http://www.newton-inc.com/dev/techinfo/qa/qa.htm
nil
'noBackdrop
slot to the base view will stop an application from becoming a backdrop application.viewIdleScript
. In general, applications should release the CPU now and then so the OS can do clean up operations.self
", you can speed things up by doing the lookup explicitly once before executing the loop, and using the call statement to execute the function within the body of the loop. f1 := {myFn: func() 42}; f2 := {_parent: f1}; f3 := {_parent: f2}; f4 := {_parent: f3}; f5 := {_parent: f4}; f5.test1 := func () for i:=1 to 2000 do call myFn with (); f5.test2 := func() begin local fn := myFn; for i:=1 to 2000 do call fn with (); end /* executes with a noticeable delay */ f5:test1(); /* executes noticeably faster */ f5:test2();
Use this technique only for functions that don't use inheritance or the self keyword.
Note for MacOS programmers: this trick is analogous to the MacOS programming technique of using GetTrapAddress
to get a trap's real address and calling it directly to avoid the overhead of trap dispatch.
2+3
will be replaced with 5
by the compiler.) In the meantime, you need to write your code as clearly as possible without relying too heavily on the ordering of functions inside expressions.kDebugMode
constant in your project and have in your application a statement conditioned by the value of kDebugMode
, the NTK compiler removes the entire if/then statement from your application code when the value of kDebugMode
is NIL.constant kDebugMode := true; // define in Project Dataif kDebugMode then Print(...); // in application code
When you change the value of the kDebugMode
constant to NIL, then the compiler strips out the entire if/then statement.
if getroot().|MyBaseView:MySIG| then begin getroot().|MyBaseView:MySIG|:TestThisView(); local s := getroot().|MyBaseView:MySIG|.BlahSize; end;
installScript
, then in the viewSetupFormScript
for the application's base view. In your case, you can do some math on the frame returned from GetAppParams
to see if the screen is large enough to support your application.Notify
to tell the user why your application cannot run.viewBounds
so it does not appear, useRelBounds(-10, -10, 0, 0)
so the view will be off-screen.viewChildren
and stepChildren
slots to NIL.AddDeferredSend(self, 'Close, nil)
to close the view.protoCloseBox
or protoLargeCloseBox
then your close box will automatically hide itself if your application is the backdrop application. If you also use newtStatusBar
as your status bar proto, the appropriate buttons will shift to fill the gap left by the missing close box. Note that you do not have to use the NewtApp framework to use the newtStatusBar
proto.Close
and Hide
methods so your application cannot be closed.GetUserConfig('blessedApp)
.viewChangedScript
for the view will be called each time the user does something to modify the view. For keyboards, this means the script is called each time the user taps a key. This is the only notification that is provided to indicate the view contents have changed.viewQuitScript
or other custom code to explicitly notify the target that the keyboard is going away, but we do not recommend this. (There may be a hardware keyboard attached, a system keyboard may be open, or the user may be writing into your view. It is a mistake to assume that the only way to modify your view is through your own keyboard.)viewIdleScript
(or call it from the viewIdleScript
.) In the viewChangedScript
, if the 'text
slot has changed, use :SetupIdle(<delay>)
to arrange for the viewIdleScript
to be called in a little while.:SetupIdle(<delay>)
happens again before the first delay goes by (perhaps because the user typed another key,) the idle script will be called after the new delay. The older one is ignored. SetupIdle
resets the timer each time it's called.viewIdleScript
return NIL
so it won't be called repeatedly.GetRoot():BlessApp(appSymbol)
will close the current backdrop application and open the new backdrop application if necessary. Note that appSymbol
must be a valid application symbol of a current installed and active application. BlessApp
does NOT verify that an application can become the backdrop (for instance, it doesn't check the 'noBackdrop
flag for an application). For this reason, BlessApp
must only be used on your own applications. See the Newton DTS Q&A, "How to Prevent an Application from Becoming a Backdrop" for more information about the 'noBackdrop
flag.BlessApp
from within a part's installScript
or removeScript
. If you want to do something like this, use a delayed call to use BlessApp
.stepChildren
slot of the base view of the template file. If both the exported and importing template have children, they will both have a stepChildren
slot. The result is that the stepChildren
slot of the importing prototemplate is masking the one in the exported proto. The instantiated view does not collect all the children from the entire proto chain (though NTK does do this at compile time for user proto templates).stepChildren
is to add a viewSetupChildrenScript
to either your exported proto template or the importer that collects all of the stepChildren
into a runtime stepChildren
array.// AFTER setting up stepChildren, views which "import" this proto// must call inherited:?viewSetupChildrenScript();exporter.viewSetupChildrenScript := func() begin // get the current value of the "extra" kids // ...unless the importer added NO kids, in which case, these are OURS local extraKids := stepChildren; local items := clone(extraKids); local kids; local whichFrame := self; while (whichFrame) do begin // get kids, but NOT using inheritance kids := GetSlot(whichFrame, 'stepChildren); // copy any extra stepChildren (but if NO extra kids are defined, don't copy twice!) if kids and kids <> extraKids then ArrayMunger(items, 0, 0, kids, 0, nil); // go deeper into the _proto chain (or set whichFrame to nil) whichFrame := whichFrame._proto; end; stepChildren := items; end;
Note that you will have similar problems with declared children. If you have declared children you will also need to collect the stepAllocateContext
slot.
GetRoot().buttons.soft
will be non-nil
if there is a "soft" button bar, that is, the button bar is located on the drawable screen. GetRoot():LocalBox()
returns the rectangle that encloses the screen and the tablet. GetAppParams
() returns information about the useful area of the screen, excluding the soft or hard button bar. Used together, this information will allow you to implement any combination of button bar disabling and/or button bar obscuring.KillStdButtonBar
. That API is designed for use when you want to actually remove the button bar so you can replace it - probably with a floating slip. It is a fairly expensive call and does a lot of things you don't need if all you want to do is take over the screen. We recommend avoiding that call if possible.) local params := GetAppParams(); if GetRoot().Buttons.soft then self.viewBounds := OffsetRect(UnionRect(params.appArea, params.buttonBarBounds), -params.appAreaGlobalLeft, -params.appAreaGlobalTop) else self.viewBounds := params.appArea;
If the goal is to to prevent users from accessing the buttons, then the button bar should be obscured regardless of whether or not it is on the LCD screen. On units with a hard button bar, you must take into account the fact that part of the base view will be off-screen. (For instance, don't place your close box under the silk-screened buttons.) A simple way to accomplish this is by having a child view whose bounds are the appArea
and locating the rest of the application within that child.
Note that on some Newton devices (for instance, the eMate 300), the buttons are not located in a view at all. On these devices, covering the entire tablet does not prevent the user from accessing the buttons (for instance, opening up the Extras Drawer).
Below is some sample code you can add to your base view's viewSetupFormScript
to cover the entire tablet:
local params := GetAppParams(); self.viewBounds := GetRoot():LocalBox(); if params.appAreaGlobalLeft then self.viewBounds := OffsetRect(self.viewBounds, -params.appAreaGlobalLeft, -params.appAreaGlobalTop);
'string
. Next, you need to use the global function BinaryMunger
to munge an empty string into the VBO. This will properly prepare the binary object to be used as a NewtonScript string.StrMunger
as often as needed to copy new string data into the VBO. Here is a code example:// Prepare a VBO to be the stringlocal myString := GetDefaultStore():NewVBO( 'string, Length("") );BinaryMunger( myString, 0, nil, "", 0, nil ); StrMunger( myString, StrLen( myString ), nil, "My new string", 0, nil );// Repeat with more data if necessary...
Note that unlike the C language's stdio
library function, the NewtonScript StrLen
function does not need to traverse the string to determine the string length, so you probably don't need to worry about performance hits from its usage.
Not all NewtonScript routines will necessarily "preserve" the VBO nature of large strings. For instance, if you concatenate strings using the Stringer
global function or the & or && operators, the result is currently a non-VBO string. Be aware that if you accidentally create a very large non-VBO string, the code may throw a "out of NewtonScript heap memory" evt.ex.outofmem
exception.
clEditView
every time some change is made, typing is very slow. So, when should it be saved?AddDelayedCall
or AddDelayedSend
can be used. The OS also provides AddProcrastinatedSend
and AddProcrastinatedCall
, which more or less implement the timer-resetting feature for you.viewIdleScript
. The viewIdleScript
is preferred over AddProcrastinatedCall/Send
because of better management of the event queue by the OS. When you call SetupIdle
to start an idle timer, any existing idle timer is reset. Procrastinated calls/sends aren't currently implemented by resetting an existing timer, but rather by creating a delayed event which fires for each call and then checking a flag when the timer expires to see if it's the last one.target
frame, which is usually a soup entry. The entry layer implements StartFlush
to start the timer, and EndFlush
is called when the timer expires and which should ensure that the data is saved to the soup.StartFlush
equivalent could be implemented something like this: StartFlush: func() begin self.entryDirty := TRUE; :SetupIdle(5000); // 5 second delay end;
Your viewIdleScript
would look something like this:
viewIdleScript: func() begin :EndFlush(); nil; // return NIL to stop the idler until next StartFlush end;
And your EndFlush
equivalent would look something like this:
EndFlush: func() if self.entryDirty then begin // getting data from editView may not // be necessary at this point
myEntry.editViewData := <editView/self>
.viewChildren;
EntryChangeXmit(myEntry, kAppSymbol); self.entryDirty := nil; end;
Implementing EndFlush
as a separate method rather than just putting the contents in the viewIdleScript
makes it easy to call the method from the viewQuitScript
or viewPostQuitScript
, to guarantee that changes are saved when the view is closed. (The viewIdleScript
may not have been called if the user makes a change then immediately taps the close box or overview or whatever.)
DoNotInstall
function. This function is called when the package containing the part is activated for the first time as a result of downloading to the unit, putting away to the Extras Drawer, or installation with API functions such as SuckPackageFromBinary
or SuckPackageFromEndpoint
. The function is not called when the package is installed as a result of card insertion, resetting the unit, or moving the package from one store to another using the Extras Drawer filing. This function is incorrectly mentioned in the Newton Programmer's Guide and Newton Programmer's Reference as DoNotInstallScript
. The proper name of the function is simply DoNotInstall
.nil
, the entire package will not be installed on the device. This provides a convenient way to prevent installation on devices that do not support your package. It's considered bad form to simply fail to install and provide no notification to the user. We recommend at least using GetRoot():Notify(...)
to display a message explaining why you are not installing.EnsureInternal
anything this function leaves behind to avoid the "Grip of Death" problems (the error with the alert "The package 'MyApp' still needs the card you removed...").SetPartFrameSlot
. For example, to create a package that will not install on any unit after the year 2000 (because the world will have ended anyway), do the following: SetPartFrameSlot('DoNotInstall, func() if Time() >= 50492160 then begin GetRoot():Notify(kNotifyAlert, "Millenial", "This application, and all existence, has expired."); true; // return non-nil to prevent install end);
Note that this does nothing to prevent packages that were installed before the year 2000 from continuing to function.
Because this script executes only when a package is first installed on a given device, it may be used to set a flag that can be used by other parts of your application to do things like suggest user registration, go into demo mode, show some extra help, etc. It's probably not appropriate as a way to initialize user preferences or create initial data. Operations like that are best done by checking each time the package is installed or launched, and initializing then if it hasn't been done. This is the case because a user may install your package on a card, then remove that card from one machine and insert it into a different machine that has not previously been used with your application. The part's InstallScript
will execute in this case, but the part's DoNotInstall
function will not.
SetPartFrameSlot('labels, '_extensions);
InstallScript
part method.InstallScript
for a form/application part only takes one argument, whereas the InstallScript
of an auto part takes two arguments.InstallScript
of a form/application part is EnsureInternal
'ed, an auto part's InstallScript
is not.DeletionScript
to my part, it doesn't get called when the user scrubs the package. Why is DeletionScript
not called?DeletionScript
to your part. The DeletionScript
must be defined in a manner different from the part's InstallScript
or RemoveScript
. You must explicitly set the DeletionScript
slot in your part frame using NTK's SetPartFrameSlot
global function. Here is an example:SetPartFrameSlot('DeletionScript, func() print("Howdy!") );
SetPartFrameSlot
for any part slot other than InstallScript
or RemoveScript
.protoFormulasPanel
for RegFormulas
, but there does not appear to be such a template.protoFloatNGo
as your base and add your formula elements to it. The only requirements are:overview
slot that contains the text to show in the formula's overview.viewbounds.bottom
must be the height of your panel.protoTitle
whose title
slot is the name of the formula panel.RegPrefs
function.protoPrefsRollItem
is incomplete. You must define an overview
slot which is the text to show in the overview mode. You can optionally define an icon
slot which is an icon for the title in the non-overview mode (a title icon). Note that title icons are much smaller than normal icons.SetEntryAlarm
calendar message, but the alarm is not set.SetEntryAlarm
will not find events. You need to use a new Calendar API called SetEventAlarm
. This function is provided in the Newton 2.0 Platform File. See the Platform File Notes for more information.AddLayout
method requires that the symbol in the layout is internal. This bug will be fixed in a future ROM. To work around this, do the following:local newLayout := {_proto: GetLayout("A Test Layout")};newLayout.symbol := EnsureInternal (newLayout.symbol);GetRoot().cardfile:AddLayout(newLayout);
For more information about issues for applications running from a PCMCIA card, see the article "The Newton Still Needs the Card You Removed"
CircleDistance
which takes two longitude/latitude pairs and the units to use in reporting the distance, and CircleDistance
returns the distance between the two points. NTK may give a warning about "Unknown global function 'CircleDistance'
". This warning can be safely ignored so long as you're writing a package for a Newton 2.0 OS device.CircleDistance
(firstLong, firstLat, secondLong, secondLat, units)CircleDistance
rounds the distance to the nearest ten miles or ten kilometers.firstLong
: The longitude for the first point on the Earth.firstLat
: The latitude for the first point on the Earth.secondLong
: The longitude for the second point on the Earth.secondLat
: The latitude for the second point on the Earth.units
: A symbol specifying the units in which the distance will be calculated. Currently the options are 'miles
or 'kilometers
.NewCity
. Check the section titled "Using the Time Zone Application" in the Built-In Applications and System Data chapter of the Newton Programmer's Guide for information on how to convert a longitude or latitude in degrees, minutes & seconds to an integer for CircleDistance
.GetExtraIcons
result in an undefined Query
method exception. How can I fix this?GetExtraIcons
. The code is not checking if the store has any extras information on it, so the Query
message is getting sent to a NIL
soup. The result is the exception.GetExtraIcons
: try GetExtraIcons(...) // do whatever you need to do here onexception |evt.ex.fr.intrp;type.ref.frame| do begin // check for a problem calling the Query function if currentException().data.errorCode = -48809 AND currentException().data.symbol = 'Query then begin // no extras drawer info on the store end ; else // a real error has occured, so let system handle it ReThrow() ; end ;
bcCustomFields
returns the labels and values of the custom fields used in the entry. Is there a way to get a list of all the custom fields the user has defined?'customFields
to the names soup method GetInfo
. This will return a frame of all the custom fields the user has defined. Each slot in this frame will have a frame with a 'label
slot. Each 'label
slot will be a string specified by the user. Here is an example: GetStores()[0]:GetSoup(ROM_CardFileSoupName):GetInfo('customFields)
...which returns:
{custom1: {label: "Customer Label"}, custom2: {label: "Another label"}}
owner
slot, the value of which is a frame. This slot is removed from the entry before it is sent to another Newton device. You can add slots to this frame to store them, but keep them from being sent. Be sure to append your developer signature to any slot names you add to the owner
frame.MakeTextNote
doesn't work if Notes is closed.MakeTextNote
to create the data, then add it to the soup entry using soup:AddXmit
or uSoup:AddToStoreXmit
(or one of the other soup functions.)MakeTextNote
always creates a frame with all the correct data that the Notes application requires. If the 2nd paramater (addit) is TRUE
, it will add that frame to the Notes soup and show the note on the screen. If addIt is NIL
, then the frame is returned. newNote := GetRoot().paperroll:MakeTextNote("Here is a sample note", nil); GetUnionSoup("Notes"):AddToDefaultStoreXmit(newNote, '|appSym:SIG|)
tapAction
slot but it does not work. What is missing?tapAction
slot if it does not find a 'text
slot in the partFrame
as well. The 'text
slot contains the name that will be displayed in the Extras drawer.tapAction
to a your part frame (in other words, your autopart): DefineGlobalConstant('kTapActionFn,func()begin // your code goes here! end); // part MUST have a text slot for tapAction to be used// text slot is the name seen by the user in ExtrasSetPartFrameSlot('text, kAppName) ;SetPartFrameSlot('tapAction, kTapActionFn) ;
GetAppPrefs
to get the prefs for that application for read-only purposes. Only the documented slots in that frame should be accessed. Other slots are neither documented nor supported, and their behavior may change. You should also check to ensure that the Home Page application exists on a particular unit before using any features. For example, here is a code snippet that evaluates to an array of user names, or NIL if the unit does not support multiple users or is not in multi-user mode. if GetRoot().HomePage then begin local prefs := GetAppPrefs('HomePage, '{}); if prefs.users and prefs.kMultipleUsers then foreach item in prefs.users collect item.name; end;
The Home Page preferences frame contains the following slots that may be accessed read only:
kMultipleUsers
: non-nil if multi-user mode is enabled
kRequirePassword
non-nil if passwords required in multi-user mode
kDisallowNewUsers
non-nil if new users can't be created at login
users
array of user frames or NIL.
A user frame contains the following slot that you may use as read-only data:
name
a string, the user-visible user's name
Keep in mind that new users could be created or existing users names may be changed at any time, and there is no notification when this happens. If necessary, you should check the set of users when your application launches. It is unlikely that new users will be created, deleted, or renamed while an application is open, unless this happens as a result of a new user being created at login. In this case, registering for change in the user configuration frame with RegUserConfigChange
and watching for the 'kCurrentUser
slot to change will let you catch changes to the current set of multi-user names.
RegAuxButton
, I get a -48204 error. Why am I getting this error? try RegAuxButton( kAppSymbol, {destApp: 'newtWorks, ...} ); onexception |evt.ex.fr| do nil;
AddEntryFromStationery
. For instance: if GetRoot().NewtWorks then
GetRoot().NewtWorks:AddEntryFromStationery(stationerySym);
If you want to create a new entry with data already in it, use the Newton Works method AdoptEntryFromStationery
. To create a new entry, you must add the basic Newton Works soup entry slots and then any stationery-specific slots:
(1) Create a frame with the basic Newton Works soup entry slots as shown below:
class
: Class of the item. For instance, for Draw documents, use the symbol 'drawPaper
version
: Integer, the current version of the entry
title
: String which is the document title
timeStamp
: Creation date of the entry
realModTime
: Date the entry was most recently modified
(2) Add the stationery-specific slots:
"Draw" Stationery Additional Slots
saveData
: a frame with the following slots:
shapes
: Required. An array of a single item, a picture as returned by the global function MakePict
.
selection: [], // the empty array styles: nil,
Warning: the above information describes only how to create a new Draw document. See the Q&A "Reading/Modifying Newton Works Data" for information on reading information from a Draw document. Slots in the saveData
slot of Draw documents already in Newton Works should be treated as read-only. (Do not try to modify these data structures in any way.)
"Paper" Stationery Additional Slots
SaveData
: the object returned from protoTXView:Externalize().
See the Newton 2.1 OS documentation for information about Externalize().
Note that this data must be from a protoTXView
that uses VBOs (it uses the protoTXView:SetStore(...)
method), or Newton Works can run out of NewtonScript memory when editing the document.
hiliteRange
: frame with the document's hilite range (see the protoTXView
documentation for details)
margins
: a frame with slots 'top
, 'left
, 'bottom
, 'right
, which are the document's margins in pixels. The frame can also optionally have the slots 'userTop
, 'userLeft
, 'userBottom
, and 'userRight
that will contain numbers (integer or real) with the margin sizes translated to user units (inches or centimeters.) If the userXXX
slots are missing or nil
, they will be calculated from the pixel values.
(3) Use code like the following to add the entry to the soup:
if GetRoot().NewtWorks then GetRoot().NewtWorks:AdoptEntryFromStationery(theEntry, stationerySym, GetDefaultStore());
See the Newton Programmer's Reference for more info about the
NewtApplication:AdoptEntryFromStationery(...)
method.
class
: Class of the item. For instance, for Draw documents, use the symbol 'drawPaper
version
: Integer, the current version of the entrytitle
: String which is the document titletimeStamp
: Creation date of the entryrealModTime
: Date the entry was most recently modifiedsaveData
that contains the following slots:shapes
: This is an array of shapes in the document.styles
: An array of all styles contained in the shapes array.selection
: An array containing integer indexes into the shapes array, indicating the currently selected shapes.shapes
slot is represented as an array of style/shape pairs, as returned by drawApp:GetContents()
. Each "shape" can be another array of shape and style pairs, representing grouping of shapes (these grouping can continue, so subarrays can have sub-subarrays, etc). If the shapes array contains exactly one item (its length is 1) and the class of the item is 'picture
, it is a picture that has been created/imported but not yet viewed in Newton Works. If this is the case, the individual shapes cannot be read, but the picture is the same format as the return value of the global function MakePict
.saveData
slot of Draw documents already in Newton Works should be treated as read-only. Do not try to modify these data structures in any way. Manipulating them can result in serious user problems.ShowFoundItem
method of the Works base view only as shown below. ShowFoundItem
is generally intended for internal use by the application itself. However, it provides handy access for navigating to a particular soup entry, so Works supports using it for this purpose only. Do not attempt to use ShowFoundItem
to do more than simply bring up an entry in the Works application.ShowFoundItem
may be difficult to specify because Works can use stationery provided by 3rd parties, which may have special requirements for the finder. In Works, the stationery is responsible for adding data to the finder when a find is performed, and so the stationery may rely on that data being present when ShowFoundItem
is later used. For all the stationery types that exist at the time this Q&A was written, a minimal finder frame of {findWords: [""]}
is sufficient to allow the stationery to show the item. Please note that this is NOT a fully specified finder, however it is sufficient for the FindSoupExcerpt
method, which is used widely. A full finder frame which accomplishes the same thing might look like this: {owner: GetRoot().NewtWorks, findType: 'text, findWords: [""], items: [{_proto: theEntry, title: "None"}]}
For Works stationery developers, we recommend not making any assumptions about the contents of the finder frame when implementing your ViewDef's ShowFoundItem
method. (Note that ShowFoundItem
is a Works-specific requirement of stationery. Generic ViewDefs do not require a ShowFoundItem
method.)
Here is an inspector example of navigating Works to one of each existing stationery. The example assumes a new untitled document of each type exists. (You'll want to have your own code that finds the appropriate Works soup entry to open.)
// make sure Works is open GetRoot().NewtWorks:Open(); // find an entry s := GetUnionSoup("NewtWorks"); theEntry := s:Query({text: "Untitled Paper"}):Entry(); // show it GetRoot().NewtWorks:ShowFoundItem(e, {findWords: [""]}); // the rest of them theEntry := s:Query({text: "Untitled Drawing"}):Entry(); GetRoot().NewtWorks:ShowFoundItem(e, {findWords: [""]}); theEntry := s:Query({text: "Untitled Calculations"}):Entry(); GetRoot().NewtWorks:ShowFoundItem(e, {findWords: [""]}); theEntry := s:Query({text: "Untitled Spreadsheet"}):Entry(); GetRoot().NewtWorks:ShowFoundItem(e, {findWords: [""]}); // cleanup theEntry := s := nil;
MakeTextNote
function.protoTXView
. Use the protoTXView
methods to add data to that view. When done, use the Externalize
method to get the data in a form suitable for saving in the Works soup.protoTXView
, it's imperative that you call the SetStore
method so that the data is created on the user store rather than the NS heap. Different formats are used for store-backed and heap-backed protoTXViews
, and the type of backing is carried into the Externalized
data. As a result, failure to use SetStore
would cause you to create a Works document that was not backed by the user store and which could eventually result in out-of-memory errors when the user added sufficient data to the document.SetStore
in the viewSetupFormScript
and AdoptEntryFromStationery
, or the intial text specified in the 2nd paramater to Replace
. (Notably, you may wish to provide styles for the text, see the Newton 2.1 OS documentation on the protoTXView
method Replace
.) // create and populate a dummy protoTXView local textView := BuildContext( { _proto: protoTXView, viewBounds: SetBounds(0, 0, 0, 0), viewFlags: 0, ReorientToScreen: ROM_DefRotateFunc, viewSetupFormScript: func() begin inherited:?viewSetupFormScript(); self:SetStore(GetDefaultStore()); end, }); textView:Open(); textView:Replace({first: 0, last: 0}, {text: "Some initial text"}, nil); // get the data in an external form for the Works soup local saveData := textView:Externalize(); textView:Close(); // Create a new Works document from the data GetRoot().NewtWorks:AdoptEntryFromStationery( { title: "Initial Title", saveData: saveData, hiliteRange: {first: 0, last: 0}, margins: {top: 72, left: 72, right: 72, bottom: 72}, }, 'paper, GetDefaultStore());
UnRegStamps
method, my registered stamps do not get unregistered. What is going wrong?UnRegStamps
method that causes stamps to remain registered. Use the following code to unregister your stamps:local viewDef := GetViewDefs( 'drawPaper ).default; if viewDef then begin // Call UnregStamps conditionally. If the Draw application is not // installed, GetViewDefs will return the "Missing Stationery" // stationery. viewDef:?UnregStamps( kStampRegistrySymbol ); local registry := GetViewDefs( 'drawPaper ).StampListRegistry; if registry AND HasSlot( registry, kStampRegistrySymbol ) then RemoveSlot( registry, kStampRegistrySymbol ); end;
Note that calling the UnRegStamps
method is required for future compatibility.
protoAZTabs
or protoAZVertTabs
?SetLetter
method of the AZTab protos:protoAZTabs.SetLetter(newLetter, NIL)
Set the tab to the character specified by newLetter and update the hiliting. Note that this method does not send a
pickLetterScript
message.
Example:
// set myProtoAZTabs to the letter "C"myProtoAZTabs:SetLetter($c, nil) ; protoAZVertTabs.SetLetter...see protoAZTabs.SetLetter
protoSoupOverview:HitItem(...)
? I want to call the inherited method and use the return value to determine what action the system performed.ProtoSoupOverview:HitItem(...)
returns nil if it handled the tap and non-nil
if it didn't handle the tap (the opposite meaning of the return value of protoOverview
's HitItem
method).protoSoupOverview
's HitItem
is just like protoOverview
's HitItem
method; this is a mistake in the documentation.ROM_UpArrowBitmap
in my application, and now my app appears partially invisible in the Newton 2.1 OS. What's wrong?ROM_UpArrowBitmap
and the other directional arrow constants were not intended to be supported, and the value of the magic pointer has changed in Newton 2.1 OS. The change was made to better implement the (documented and supported) scrolling protos such as protoUpDownScroller
.ROM_UpArrowBitmap
is named in the NTK Platform File defs file, and had mistakenly been mentioned in some public documentation from Apple, so you may have thought this was supported. If you have a reference to one of these magic pointers in the icon
slot of a clPictureView
, you'd have gotten an arrow graphic on the 2.0 and earlier releases of the OS, but on the Newton 2.1 OS, the changed value is not acceptable to the view system as a graphic. The result is that drawing is aborted when the OS tries to render the view with the arrow graphic, and views that would normally be drawn after the bad view will also fail to render, producing what appear to be invisible views that are otherwise functional.protoTXView
-based view as a descendent of a draggable view. When I drag the view, the protoTXView
-based view still draws its contents at the original coordinates. What is going wrong?protoTXView
prototype does not correctly update its draw origin coordinates when moved. To work around this bug, you must close the protoTXView
-based view and re-open it.protoTXView
-based view, you will need to externalize the data so that you can restore the data when you reopen the view. To do this, call the Externalize
method and store the return value somewhere (perhaps in the parent of the protoTXView
). When you reopen the view, call the Internalize
method with the stored return value of the call to Externalize
.FrameDirty
see changes to nested frames?FrameDirty
is fooled by changes to bytes within binary objects. Since strings are implemented as binary objects, this means that FrameDirty
will not see changes to individual characters in a string. Since clParagraphViews
try (as much as possible) to work by manipulating the characters in the string rather than by creating a new string, this means that FrameDirty
can be easily fooled by normal editing of string data.s := GetStores()[0]:CreateSoup("Test:DTS", []);e := s:Add({slot: 'value, string: "A test entry", nested: {slot: 'notherValue}})#4410B69 {slot: value, String: "A test entry", nested: {slot: notherValue}, _uniqueID: 0}FrameDirty(e)#2 NIL e.string[0] := $a; // modify the string w/out changing its referenceFrameDirty(e)#2 NIL EntryChange(e);e.string := "A new string"; // change the string referenceFrameDirty(e)#1A TRUE EntryChange(e);e.nested.slot := 'newValue; // nested change, FrameDirty is deep.FrameDirty(e)#1A TRUE s:RemoveFromStore() // cleanup.
evt.ex.fr.store
exceptions. These limits are for the encoded form that the data takes when written to a soup, which varies from the object's size in the NS heap.EntryFlushXMit
and EntryChangeXMit
?EntryFlushXMit
and EntryChangeXMit
is what will be done with the entry after the flush or change._proto
slots. The result is that the data will be written to the store, and a cached frame will exist. Often, this is exactly what is desired because the entry is still needed since it will soon be accessed or modified.EntryFlushXMit
is a better option; it writes the data to the soup without creating the cached entry.AddXMit
or EntryChangeXMit
. If the entry will not soon be used again (so it doesn't need to take up heap space with the cached frame), then use AddFlushedXmit
or EntryFlushXMit
.while entry dobegin entry.fooCount := entry.fooCount + 1; // nil appSymbol passed so don't broadcast EntryFlushXMit(entry, nil); entry := cursor:Next();end; // Could broadcast now foreach x in kInitialData do // if new, may not need broadcast soup:AddFlushedXmit(Clone(x), nil);
soupDef
mechanism and putting the long name (typically without appended signature) in the 'userName
slot of that data structure.WhichEnd
cursor method returns the symbols 'begin
or 'end
, depending on where the cursor is in a soup. Why does NTK complain when I try to check for these symbols?if myCursor:WhichEnd() = '|begin| then :WeAreAtBeginning();
EntryChange
on the modified entry I get a -48022 error. What is wrong?ClearVBOCache
while modifying VBOs. You can also work around the problem by putting the VBO in a soup entry and using EntryChangeXmit
or EntryFlushXmit
.ClearVBOCache
takes a reference to a VBO as an argument, and moves the dirty pages for a given VBO to the store, freeing up the system memory. Note that this function does not commit the changes to the VBO, while EntryChangeXmit
and EntryFlushXmit
do commit the changes.ClearVBOCache
. For example, modifying 32K of contiguous data, or a single byte in 32 different pages of one VBO, or even a single byte in 32 different VBOs all modify 32 total pages of VBO data. Don't do this too often, though. Calling ClearVBOCache
repeatedly for modifications to the same page of a VBO or when there are only a few modified pages will needlessly slow the machine.Stats()
is called: gc(); stats(); soup:AddToDefaultStoreXmit({ foo : "a test string"}, '|bar:SIG|); gc(); stats(); soup:AddToDefaultStoreXmit({ foo : "another test string"}, '|bar:SIG|); gc(); stats();
A: There is no leak. What's actually going on is that the Xmit versions of the soup methods do their broadcasting in deferred actions. That's good, because it means that broadcast handlers that might throw or have other side effects won't break your code. A little bit of heap memory is used to keep track of the deferred action and its arguments. This memory is released after the deferred action executes, which is typically immediately after control returns to the top level.
If you select all the test code in the NTK Inspector and press the Enter key, NTK compiles the entire selection and executes it as a single operations, so control doesn't return to the top level (thus allowing the deferred actions to execute) until after the last operation. The deferred action created by each call to the Xmitting function will still be pending, so the space won't have been released yet, and stats reflect this. If the example is executed one line at a time, you'll see that no memory is actually leaked.
If you pass nil
for the changeSym
argument to the Xmit functions, no notification occurs and the deferred action is not created. Normally, this is a bad idea, since you want other applications to know about your soup changes. However, Xmit notification may not be necessary for specialized applications that use only their own application soups and do not publish information about reading/writing soup data for extensibility.
If you are modifying a very large number of entries (for instance, creating a new soup of thousands of entries), you might pass nil
for the changeSym
to void immediate notification. Afterwards, use the XmitSoupChange
global function with the 'whatThe
symbol. (See the documentation for XmitSoupChange
for more information.)
CDPipeInit
, it returns a -28102 error (Communication tool not found). I've checked that the tool is installed properly, and the DIL sample application works fine. What's wrong?CSTR.rsrc
" to your project and see if that fixes things.SetupPortMenu
function in SoupDrink.c for an example.FDget
function return error -28801 (Out of heap memory) or -28706 (Invalid parameter)? I don't think I'm out of memory, and I don't always get this error code so my parameters must be right. What is wrong?A:={first: {left:3, right: 30, top:10, bottom:90}};A.second := A.first; // triggers the problem B:={first: {left:3, right: 30, top:10, bottom:90}};B.second := clone(B.first); // cloning avoids the problem C:={first: {left:3, right: 30, top:10, bottom:90, foo: nil}};C.second := C.first; // no problem since C.foo exists D:={first: {left:3, right: 30, top:10, bottom:1000}};D.second := D.first; // no problem since D.bottom is >255
To work around this problem, you can clone the frame (as in frame "B") or add another slot to the frame (as in frame "C") or ensure that the values are not between 0 and 255 (frame "D").
Note: this has been fixed in the 1.0.2 Windows DILs.
CDPipeListen
, but it never seems to be called. What is going wrong?CDPipeListen
, the callback function never gets called in Windows applications. You will have to use a synchronous listen, then wait for the state of the DIL pipe to change before accepting the connection. The following code shows how to properly accept a connection.anErr = CDPipeListen( gOurPipe, kDefaultTimeout, NULL, 0 ); if (!anErr) { // This code doesn't need to be executed on MacOS, but // is currently required for Windows. We need to loop, // waiting for the connection state to change to // kCDIL_ConnectPending. endTime = (GetTickCount()/1000) + 30; // to timeout in 30 seconds while ((GetTickCount()/1000) < endTime ) { if (CDGetPipeState( gOurPipe ) == kCDIL_ConnectPending) { anErr = CDPipeAccept( gOurPipe ); break; } else CDIdle( gOurPipe ); }}
Note: this has been fixed in the Windows DILs 1.0.2.
.chain
command.SetLCDContrast
, to do just that. However, changing the contrast with no end user control is not considered a good user-interface practice.ScaleShape
?MungeBitmap
, it sometimes shifts the data. How can I rotate left correctly?MungeBitmap
using these operations: 'flipHorizontal
, 'flipVertical
, and 'rotateRight
. ('rotateRight
three times will work as well, but it is less efficient bacause flips are faster than rotates.)MakeBitmap
and copy data into the bitmap.GetPICTAsBits
). If you want to create bitmaps dynamically at compile time, you can create a simple bitmap object with the following format.{ bounds: <bounds frame>, bits: <raw bitmap data>, mask: <raw bitmap data for mask - optional>} Binary object <raw bitmap data> - class 'bits bytes data-type descr 0-3 long ignored 4-5 word #bytes per row of the bitmap data (must be a multiple of 4) 6-7 word ignored 8-15 bitmap rectangle - portion of bits to use--see IM I 8-9 word top 10-11 word left 12-13 word bottom 14-15 word right 16-* bits pixel data, 1 for "on" pixel, 0 for off
The bitmap rectangle and bounds slot must be in agreement regarding the size of the bitmap.
MakeBitmap Shapes
If you want to create bitmap data at run time or extract bitmap data from a bitmap created with the MakeBitmap
global function, use the GetShapeInfo
function to get the bitmap and other slots required to interpret the meaning of the bitmap created by MakeBitmap
.
Warning: the following information applies only to bitmaps of depth 1 (black and white bitmaps) created by your application with MakeBitmap. Do not rely on GetShapeInfo
or the following slots for images created by other applications, images stored in the Newton ROM, images created with functions other than MakeBitmap
, nor images with a depth other than 1.
If you created a bitmap using MakeBitmap
of depth
1, the return value of GetShapeInfo
contains frame with information you can use to interpret the bitmap data.
This frame includes a bits
slot referencing the bitmap data for the bitmap. This bitmap data can be manipulated at run time (or copied for non-Newton use), using other slots in the return value of GetShapeInfo
to interpret the bitmap binary object: scanOffset
, bitsBounds
, and rowBytes
. For instance, the first bit of the image created with MakeBitmap
can be obtained with code like:
bitmapInfo := GetShapeInfo(theBitmap); firstByte := ExtractByte(bitmapInfo.bits, bitmapInfo.scanOffset); firstBit := firstByte >> 7; // 1 or 0, representing on or off
Note that rowBytes
will always be 32-bit aligned. For instance, for a bitmap with a bitsBounds
having width 33 pixels, rowBytes
will be 8 to indicate 8 bytes offsets per horizontal line and 31 bits of unused data at the end of every horizontal line.
view:LockScreen(nil)
message forces an "immediate update". How is this different from calling RefreshViews
?DrawShape
) the system normally renders the shape on the screen immediately. :LockScreen(true)
provides a way to "batch up" the screen updates for multiple drawing calls. Sending :LockScreen(nil)
"unplugs" the temporary block that has been placed on the screen updater, causing all the batched drawing changes to be rendered on the LCD.RefreshViews
tells the system to execute the commands needed to draw every view that has a dirty region. You can think of it as working at a level "above" the screen lock routines. When you send the message Dirty
, it does not immediately cause the system to redraw the dirtied view, instead it adds the view to the dirty area for later redrawing.SetValue
, call RefreshViews
(and not see an update) draw a few shapes, and then, when you unlock the screen, the refreshes to the dirty regions and your shapes will all appear at once.LockScreen
and RefreshViews
:LockScreen(nil)
result in a RefreshViews
?LockScreen(true)
just stops the hardware screen from updating from the offscreen buffer. LockScreen(nil)
releases that lock which usually causes the hardware screen to update soon thereafter.SetValues
draw into the offscreen buffer?SetValue
doesn't draw. Otherwise, see 1.RefreshViews
?vfFillWhite
and kRGB_0
but neither seems to work. How do I draw white text?textPattern
slot in the style frame to make the text black (kRGB_Black
) and set the transferMode
to modeBic
.kRGB_Gray1
for something that is as close to white as you can get.modeOr
- Replaces pixels under the non-white part of the source image with source pixels. If the source pixel is white, the destination pixel is unchanged.modeXor
- Inverts pixels under the non-white part of the source image. Destination pixels under the white part of the source image are unchanged. This actually XORs the values in the source and destination pixels. For example, for destination of 0xA (75% grey), source 0x0 (white) produces result 0xA (unchanged). Source 0xF (black), produces result 0x5 (25% grey, or inverted). Source pixels of other values have less utility. For example, source 0x5 (25% grey) produces result 0xF (black), while source 0xA (75% grey) produces result 0x0 (white), and source 0x3 (50% grey) produces result 0x9 (slightly less than 75% grey).modeBic
- Erases screen pixels under the non-white part of the source image, making them all white. Destination pixels under the white part of the source image are unchanged. This actually, does a bitwise NOT, so it is really only useful when source pixels are either 0 (white) or 0xF (black). With other values, weird things happen, For example, destination 0xF with source 0xA produces result 0x5. Destination 0x0 with source 0xA produces result 0x0. Destination 0x3 with source 0xA produces result 0x1.modeNotCopy
- Replaces screen pixels under the black part of the source image with white pixels. Screen pixels under the white part of the source image are made black.modeNotOr
- Screen pixels under the black part of the source image are unchanged. Screen pixels under the white part of the source image are made black.modeNotXor
- Screen pixels under the black part of the source image are unchanged. Screen pixels under the white part of the source image are inverted.GrayShrink
doing what I want it to when I use it with relatively small bitmaps?GrayShrink
was designed for rendering relatively large images such as received faxes into a moderately large part of a Newton display. It works by setting a flag in the bitmap that tells the imager to gather multiple bits from the source bitmap and turn them into a single gray pixel when drawing through a reducing transform.GrayShrink
will not modify the bitmap. The end result will be a transformed (shrunk) image with the same bit depth as the original. That is, the shrinking will still happen, but the graying won't.GrayShrink
will not work with read-only bitmaps (it is unable to set the flag.) The result will still be a transformed (shrunk) image, but pixels will not be combined to gray. There is no way to clear the flag once it has been set. After GrayShrink
has modified a bitmap, drawing it to the screen through any scaling transform that reduces the image will produce a pixel combined gray result.GrayShrink
pixel gathering algorithm produces an anomaly along the righthand side of the reduced image. When rendering large bitmaps into a reasonably large destination, this is generally uunnoticeable. However, when used with small source bitmaps or when rendering into a small area, several columns along the right side of the result may not be drawn, and the anomaly is easily seen. We recommend using GrayShrink
and the 'drawGrayScaled
setting for protoImageView
only for large source images such as incoming faxes or scanned data.MungeBitmap
to flip or rotate a grayscale image, it gets corrupted. What's wrong?MungeBitmap
does not properly handle bitmaps with a depth greater than 1. You can work around this problem by using kMungeBitmapFunc
, which has the same calling conventions and return value as MungeBitmap
. kMungeBitmapFunc
is provided in the Newton 2.1 Platform file, version 1.2b1 or later.MungeBitmap
with the 'rotateLeft
, 'rotateRight
, or 'flipHorizontal
options will trigger the bug. The 'rotate180
and 'flipVertical
arguments to MungeBitmap
work correctly with deeper bitmaps.TextBounds
and StrFontWidth
, but those values may be too small to fit.:LocalBox()
to find out) and use center justification in a style frame passed to :DrawShape(...)
.cookie := OpenRemoteControl();
Call this function once to initialize the remote control functions. It returns a magic cookie that must be passed to subsequent remote control calls, or nil if the initialization failed.
CloseRemoteControl(cookie);
Call this function once when all remote control operations are completed, passing cookie returned from
OpenRemoteControl
. Always returns nil
. cookie is invalid after this call returns.
SendRemoteControlCode(cookie, command, count);
Given the cookie returned from
OpenRemoteControl
, this function sends the remote control command (see below for format of data). The command is sent count times. count must be at least 1. Returns after the command has been sent (or after the last loop for count
> 1). (see diagram)
Each command code has the following structure:
struct IRCodeWord { unsigned long name; unsigned long timeBase; unsigned long leadIn; unsigned long repeat; unsigned long leadOut; unsigned long count; unsigned long transitions[];};
identifies the command code; set to anything you like
name
timeBase
in microseconds; sets the bit time base
leadIn
duration in timeBase units of the lead bit cell
repeat
duration in timeBase units of the last bit cell for loop commands
leadOut
duration timeBase units of the last bit cell for non-loop commands
count
one-based count of transitions following
transitions
[ ] array of transition durations in timeBase units
Note that the repeat time is used only when the code is sent multiple times.
See Remote.¹, Sony.r, RC5.r, and RemoteTypes.r files for examples. The .rsrc files have templates for ResEdit editing of the Philips and Sony resources. See Remote IR Sample code for more details.
Things To Know Before You Burn The Midnight Oil:
If the Newton goes to sleep, the IR circuits are powered down, and any subsequent sends will fail. If you want to override this, you need to have a powerOffhandler close the remote connection, and when Newton wakes up the application could re-open the connection.
If two applications are concurrently trying to use the IR port (beaming and remote control use for instance), this will cause a conflict.
Sample Code
The Remote IR Sample is part of the DTS Sample code distribution, you should find it on AppleLink and on the Internet ftp server (ftp.apple.com).
By way of a quick summary: the sample has an array of picker elements with the resource definitions bound to the index (ircode inside the application base view).
You specify the constant that is an index to the array, get the resource using the NTK function GetNamedResource
and when you send data, use the constant as the resource used.
OpenRemoteControl
is called in viewSetupFormscript
, and closeRemoteControl
is called in viewQuitScript
. Note that these are methods, not global functions; same is true of SendRemoteControlCode
.
More Information
Consult the IR samples available on ftp.apple.com (Internet) and on the Newton Developer CD-ROMs.
byteCount
slot of your input spec to the minimum packet size. In your input script, call Partial
to read in the entire packet, and then call FlushInput
to empty everything out for your next receive completion.partialScripts
with partialFrequencies
. Call the Ticks
global function if necessary to determine the exact execution time of a partialScript
.Instantiate
, Bind
or Connect
touch the hardware?{ type: 'service, label: kCMSAsyncSerial, opCode: opSetRequired}
<see diagram section A>
The CommManager task creates the appropriate CommTool task(s) and replies to the communications service request. Each CommTool task initializes itself . In response to the Bind
request the CommTool acquires access to any physical hardware it controls, such as powering up the device. The endpoint is ready-to-go.
<see diagram section B>
An endpoint may use multiple CommTool tasks, but there will be a single NewtonScript endpoint reference for them.
When the endpoint requests a connection, the CommTool interacts wih the physical hardware (or a lower level CommTool) as necessary to complete the connection, depending on the type of communications service. For example, ADSP will use the endpoint address frame to perform an NBP lookup and connection request. MNP will negotiate protocol specifications such as compression and error correction.
<see diagram section C>
The CommTool completes the connection and replies to the connection request. Note that if this is done asynchronously, the Newt task continues execution, giving the user an option to abort the connection request.
<see diagram section D>
Disconnect
functions similarly to Connect
, moving the endpoint into a disconnected state. Unbind
releases any hardware controlled by the CommTool. Dispose
deallocates the CommTool task.
kMacRomanEncoding
is the default encoding system for strings on most Newtons, you can specify it explicitly by adding one of the following encoding slots to your endpoint:encoding: kMacRomanEncoding; // Unicode<->Mac translation
kWizardEncoding
encoding: ; // Unicode<->Sharp Wizard translation
kShiftJISEncoding
encoding: ; // Unicode<->Japanese ShiftJIS translation
For kMacRomanEncoding
, the upper 128 characters of the MacOS character encoding are sparse-mapped to/from their corresponding unicode equivalents. The map table can be found in Appendix B of the NewtonScript Programming Language reference. The upper-bit translation matrix is as follows:
short gASCIIToUnicode[128] = { 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x2126, 0x00E6, 0x00F8, 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, 0x00FF, 0x0178, 0x2044, 0x00A4, 0x2039, 0x203A, 0xFB01, 0xFB02, 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, 0xF7FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7};
Connect
and Listen
methods of protoBasicEndpoint
?nil
before attempting to access the array, while others assume they will always be passed an array of options. Some also assume that the array will always contain at least one element. ep:Connect([nil], nil);
data := [....]; for item := 0 to Length(data) - 1 do ep:Output(data[ item ], nil, nil);
A: When
protoBasicEndpoint
performs a function synchronously, it creates a special kind of "sub-task" to perform the interprocess call to the comm tool task. The sub-task causes the main NewtonScript task to suspend execution until the sub-task receives the "operation completed" response from the comm tool task, at which time the sub-task returns control to the main NewtonScript task, and execution continues.
The sub-task, however, is not disposed of until control returns to the main NewtonScript event loop. In effect, each and every synchronous call is allocating memory and task execution time until control is returned to the main NewtonScript event loop! For a small number of sucessive synchronous operations, this is fine.
A fully asynchronous implementation, on the other hand, is faster, uses less machine resources, allows the user to interact at any point in the loop, and is generally very easy to implement. The above loop can be rewritten as follows:
ep.fData := [....];ep.fIndex := 0;ep.fOutSpec := { async: true, completionScript: func(ep, options, error) if ep.fIndex >= Length(ep.fData) - 1 then // indicate we're done else ep:Output(ep.fData[ ep.fIndex := ep.fIndex + 1 ], nil, ep.fOutSpec ) };ep:Output(ep.fData[ ep.fIndex ], nil, ep.fOutSpec );
Of course, you should always catch and handle any errors that may occur within the loop (completionScript
) and exit gracefully. Such code is left as an excercise for the reader.
XON/XOFF
flow control can be set with the kCMOInputFlowControlParms
and kCMOOutputFlowControlParms
options. In the case of hardware handshaking (RTS/CTS)
you should use the following options:{ label: kCMOInputFlowControlParms, type: 'option, opCode: opSetRequired, data: { arglist: [ kDefaultXonChar, kDefaultXoffChar, NIL, TRUE, 0, 0, ], typelist: ['struct, 'byte, 'byte, 'boolean, 'boolean, 'boolean, 'boolean, ], },}, { label: kCMOOutputFlowControlParms, type: 'option, opCode: opSetRequired, data: { arglist: [ kDefaultXonChar, kDefaultXoffChar, NIL, TRUE, 0, 0, ], typelist: ['struct, 'byte, 'byte, 'boolean, 'boolean, 'boolean, 'boolean, ], },}
MakeModemOption
call.MakeModemOption
when setting up options to intiailize an endpoint. So you would call MakeModemOption
to get your initial option array, then add your own custom options after that. MakeModemOption
will return correct options based on the user settings for ignore DialTone, use PC Card Modem, etc.inputSpec
of form 'string
. When its inputScript
triggers, I switch to an input form of 'binary
. When the binary inputScript
triggers, the first few bytes of the data are garbage, and sometimes the inputScript
doesn't trigger at all. The same behavior occurs when switching to the 'frame
input form. Why?endSequence
and filter
processing.byteCount
, or end-of-packet (EOP
) detection failure.{ type: 'option, label: kCMOSerialHWChipLoc, opCode: opSetRequired, form: 'template, result: nil, data: { argList: [kHWLocPCMCIASlot1, 0], // or kHWLocPCMCIASlot2 typeList: ['struct, ['array, 'char, 4], 'uLong] }
}
If you are using Newton Internet Enabler (NIE) endpoints, you can use a PC Card Modem instead of a serial PC Card, but you do not have to add any special endpoint options. NIE will handle this automatically, provided you correctly set up your modem in the Modem preferences in the Prefs application.
This should allow your endpoint code to use the PC Card (serial card or modem card) instead of the built-in serial port. Connect the NTK Inspector to the built-in serial port as you normally would. If you are using an AppleTalk endpoint, you can simultaneously use the NTK inspector connected via AppleTalk.
'char
fields in the endpoint option is preventing the correct flow control characters from being set in the serial driver. The solution is to use the 'byte
symbol rather than the 'char
symbol for these fields, thus avoiding the Unicode-to-ASCII conversion that would normally take place. The Newton Programmer's Guide is incorrect; the correct option frames are as follows:{ label: kCMOInputFlowControlParms, type: 'option, opCode: opSetRequired, result: nil, form: 'template, data: { arglist: [ unicodeDC1, // xonChar unicodeDC3, // xoffChar true, // useSoftFlowControl nil, // useHardFlowControl 0, // not needed; returned 0, ], // not needed; returned typelist: ['struct, 'byte, // XON character 'byte, // XOFF character 'boolean, // software flow control 'boolean, // hardware flow control 'boolean, // hardware flow blocked 'boolean, ], }, }, // software flow blocked { label: kCMOOutputFlowControlParms, type: 'option, opCode: opSetRequired, result: nil, form: 'template, data: { arglist: [ unicodeDC1, // xonChar unicodeDC3, // xoffChar true, // useSoftFlowControl nil, // useHardFlowControl 0, // not needed; returned 0, ], // not needed; returned typelist: ['struct, 'byte, // XON character 'byte, // XOFF character 'boolean, // software flow control 'boolean, // hardware flow control 'boolean, // hardware flow blocked 'boolean, ], }, }, // software flow blocked
Baud rate 9600 Data bits 8 Stop bits 1 Parity Odd
2 Hardware Restrictions
The IR hardware used in the Sharp Wizard series (as well as Newtons and other devices) requires a brief stablizing period when switching from transmitting mode to receiving mode. Specifically, it is not possible to receive data for two milliseconds after transmitting. Therefore, all devices should wait three milliseconds after completion of a receive before transmitting.
3 Packet Structure
There are two kinds of Packets: "Packet I" and "Packet II". Because the IR unit is unstable at the start of a data transmission, DUMMY (5 bytes of null code (0x00))
and START ID (0x96)
begin both packet types. At least two null bytes must be processed by the receiver as DUMMY
before the START ID
of a packet is considered. After this (DUMMY, START ID)
sequence the PACKET ID
is transmitted. Code 0x82
is the packet ID for a PACKET I transmission, and code 0x81
is the packet ID for a PACKET II transmission.
3.1 Packet I
This packet type is used to transmit the following control messages:
3.1.1 Request to send ENQ (0x05)
3.1.2 Clear to send SYN (0x16)
3.1.3 Completion of receiving data ACK (0x06)
3.1.4 Failed to receive data NAK (0x15)
3.1.5 Interruption of receiving data CAN (0x18)
The format of this packet type is as follows:
Byte length Set value in transmission Detection method in reception DUMMY 5 0x00 * 5 Only 2 bytes are detected when received. START ID 1 0x96PACKET ID 1 0x82DATA 1 above mentioned data
Packet I example:
DUMMY START ID PACKET ID DATA 0x00, 0x00, 0x00, 0x00 0x96 0x82 0x05
3.2 Packet II
This packet type is used to transmit data. The maximum amount of data that may be transmitted in one packet is 512 bytes. If more than 512 bytes are to be transmitted, they are sent as several consecutive 512-byte packets. The last packet need not be padded if it is less than 512 bytes and is distinguished by a BLOCK NO
value of 0xFFFF
.
The format of this packet type is as follows:
Byte length Set value in transmission Detection method in receptionDUMMY 5 0x00 * 5 Only 2 bytes are detected.START ID 1 0x96PACKET ID 1 0x81VERSION 1 0x10 Judge only bits 7-4BLOCK NO 2 (L/H) 0x0001 ~ 0xFFFFCTRL CODE 1 0x01 Don't judgeDEV CODE 1 0x40 Don't judgeID CODE 1 0xFE Don't judgeDLENGTH 2 (L/H) 0x0001 ~ 0x0200DATA 1 ~ 512CHKSUM 2 (L/H)
BLOCK NO
in last block must be set to 0xFFFF
.
CHKSUM
is the two-byte sum of all of the data bytes of DATA
where any overflow or carry is discarded immediately.
Send all two-byte integers lower byte first and upper byte second.
Packet II example:
DUMMY START ID PACKET ID VERSION BLOCK NO CTRL CODE0x00, 0x00, 0x00, 0x00 0x96 0x81 0x10 Low High 0x01
DEV CODE ID CODE DLENGTH data CHECKSUM0x40 0xFE Low High ???? Low High
4 Protocol
Data will be divided into several blocks of up to 512 bytes each. These blocks are transmitted using type I and II packets as follows:
4.1 Transmission Protocol
4.1.1 The initiating device (A) begins a session by sending an ENQ
(type I) packet. The receiving device (B) will acknowledge the ENQ
by transmitting a SYN
packet.
4.1.2 When (A) receives a SYN
packet, it goes to step 4.1.4 below.
4.1.3 When (A) receives a CAN
packet, or when 6 minutes have elapsed without a SYN
packet reply to an ENQ
packet, (A) terminates the session. If (A) receives any other packet, no packet, or an incomplete packet, it begins sending ENQ
packets every 0.5 seconds.
4.1.4 When (A) receives a SYN
packet, it transmits a single type II data packet, then awaits an ACK
packet from (B).
4.1.5 When (A) receives an ACK
packet, the transmission is considered successful.
4.1.6 If no ACK
packet is received within 1 second from completion of step 4.1.4, or if any other packet is received, (A) goes to step 4.1.1 and transmits the data again. Retransmission is attempted once. The session is terminated if the second transmission is unsuccessful.
4.2 Reception Protocol
4.2.1 The receiving device (B) begins a session by waiting for an ENQ
(type I) packet. If no ENQ
packet is received after 6 minutes (B) terminates the session.
4.2.2 When (B) receives an ENQ
packet, (B) transmits either a SYN
packet to continue the session or a CAN
packet to terminate the session.
4.2.3 When (B) receives a valid type II packet (for example, the checksum and all header fields appear to be correct), (B) transmits an ACK
packet.
4.2.4 If one or more header fields of the data packet are not correct, or if the time between data bytes is more than 1 second, (B) goes to step 4.2.1 and does not transmit the ACK
packet (this will cause (A) to retransmit the packet after a one second delay).
4.2.5 If the header fields of the data packet appear to be correct but the checksum is incorrect, (B) transmits a NAK
packet (this will cause (A) to retransmit the packet immediately).
Because of the restriction in hardware mentioned in item 2 above, it is not possible to receive data for two milliseconds after a data transmission. Please wait three milliseconds before transmitting a response to the other device.
(see diagram)
AddProcrastinatedCall
or AddProcrastinatedSend
repeatedly with the same symbol from my input specification's InputScript
method sometimes causes an out of memory exception on pre-Newton 2.1 devices. What's going wrong?InputScript
, it may not be executed until a much later time. Due to a bug in the way procrastinated actions with the same symbol are queued, it's possible to queue so many events that you run out of NewtonScript heap memory. This bug is fixed in the Newton 2.1 OS.AddDelayedCall
or AddDelayedSend
in place of a procrastinated action.'time
slot of the event frame passed to an endpoint's EventHandler
is in ticks. After some experimentation, I've discovered that it is not in ticks. What is this time value?0x3FFFFFFF
, or -536870912
decimal. Therefore, the time value will start counting at zero, count up to 536870911
, wrap to -536870912
then start to count back up to 536870911
.Ground (4) -> Ground (4) (also connect to connectors' shrouds)Transmit+ (6) -> Receive+ (8)Transmit- (3) -> Receive- (5)Receive+ (8) -> Transmit+ (6)Receive- (5) -> Transmit- (3)Data Term Ready (1) -> Clear To Send (2)Clear To Send (2) -> Data Term Ready (1)
You should use twisted pairs for 6/3, 8/5, and 1/2, to improve signal quality and reduce attenuation, especially in long cables. You can use side-by-side pairs, as in telephone hookup cable, for short cable runs.
Remember that because RS-422 uses a differential signal for transmit and receive, you always need two transmit and two receive pairs, and a break of either wire will cause communications in that direction to fail. The advantage, however, is significantly longer and more reliable cable runs than RS-232.
If you don't use hardware flow control, you can eliminate the 1/2 pair, but that's not recommended unless you know this cable will be used only in software flow control situations.
Q: What's the pin mapping on the Newton-to-PC (DIN-to-DB9) cable?
A: Here it is:
Note that the pin numbers shown are as defined above.
PC (DB9) Newton (DIN) ========================1 1 2 3 3 5 4 7,2 5 4,8 6 1 7 N/C 8 N/C 9 N/C N/C=not connected.
Apple MessagePad 100: 50 mA Apple MessagePad 110: ~160 mA Apple MessagePad 120: ~300 mA Apple MessagePad 130: ~300 mA (with backlight off) Apple MessagePad 130: (with backlight on, the maximum has not been characterized)
SafeRemovePackage()
.SuckPackageFromEndpoint()
, or the store method SuckPackageFromBinary()
depending on where the package is coming from.SafeRemovePackage
until after you verify (most likely with a deferred call) that the SuckPackageFromEndpoint
or SuckPackageFromBinary
has succeeded.SafeRemovePackage
from a function that's in the target package. You'll need to create a small function which does nothing but remove the old package, and then TotalClone that small function before executing it via a deferred call. Otherwise you'd be chopping your package's legs out from under itself, causing no end of havoc!installscript
performs whatever checks are necessary, and then conditionally calls SuckPackageFromBinary
, providing the binary object which holds the real package..pkg
file that NTK produces into an object in the NewtonScript environment in NTK. On Windows NTK, LoadDataFile
does this. On Macintosh NTK, the easiest thing to do is use a utility such as Clipboard Magician to copy the data from the .pkg
file into a resource, then use GetNamedResource
to get the data in your installer package. GetNamedResource
and LoadDataFile
are documented in the Newton Toolkit User's Guide. The MonacoTest sample code is a working example of a package installer that uses this technique.func(pkgRef)begin local thelen:=extractword(pkgRef,26) div 2 -1; local s:=" "; while strlen(s)<thelen do s:=s&s; s:=substr(s,0,thelen); BinaryMunger(s, 0, thelen*2, pkgRef, 52+(extractlong(pkgRef,48)*32)+extractword(pkgRef,24), thelen*2); s;end
Pin 1 HSKo /DTRPin 2 HSKi /CTSPin 3 TxD- /TDPin 4 GND Signal ground connected to both logic and chassis ground.Pin 5 RxD- /RD Pin 6 TxD+ (see below)Pin 7 GPi General purpose input received at SCC's DCD pin.Pin 8 RxD+ (see below)Pin 9 Power out 5V@100ma. This pin only exists on the eMate 300 and the Newton Serial Adapter.
All inputs are:
Ri 12K ohms
minimum Vih 0.2v, Vil -0.2V
maximum tolerance Vih 15V, Vil -15V
All outputs are:
Rl 450 ohms
minimum Voh 3.6V, Vol -3.6V
maximum Voh 5.5V, Vol -5.5V
No more than 40mA total can be drawn from all pins on the serial port. Pins 3 & 6 tri-state when SCC's /RTS is not asserted.
The EIA RS-422 standard modulates its data signal against an inverted (negative) copy of the same signal on another wire (twisted pairs 3/6 & 5/8 above). This differential signal is compatable with older RS-232 standards by converting to EIA standard RS-423, which involves grounding the positive side of the RS-422 receiver, and leaving the positive side of the RS-422 transmitter unconnected. Doing so, however, limits the usable cable distance to approximately 50 feet, and is somewhat less reliable.
The MessagePad 120 and the MessagePad 130 use a Linear Technology LTC902 serial line driver. This part drives +5/-5 nominally for the RS422 signals, and you can use just one half to interconnect with RS232 compatible signal levels.
The MessagePad 2000 and the eMate 300 use a Linear Technology LTC1323 serial line driver. This part drives +5/-5 nominally for the RS422 signals, and you can use just one half to interconnect with RS232 compatible signal levels.
CalibrateTablet
to open the "Align Pen" view. CalibrateTablet
takes no arguments, and does not return until the user has finished the calibration.CalibrateTablet
automatically saves the new calibration information for you.StringToDateFrame
and StringToTime
don't seem to work. StringToDateFrame
returns a frame with NIL for all the time & day slots, and StringToTime
returns NIL.StringToDateFrame
and StringToTime
: PrepareStringForDateTime := func (str) begin // str is just a time string, nothing else belongs local newStr := clone (str); local tf:= GetLocale().timeFormat; local startMin := StrPos (str, tf.timeSepStr1, 0); local startSec := StrPos (str, tf.timeSepStr2, startMin+1); // If a time seperator for seconds, then strip out seconds if startSec then
begin local skipSecSep := startSec + StrLen (tf.timeSepStr2); local remainderStr := SubStr ( str, skipSecSep, StrLen (str) - skipSecSep); local appendStr := StringFilter ( remainderStr, "1234567890", 'rejectBeginning); newStr := SubStr (str, 0, startSec) & appendStr; end; return newStr; end;
GetDateStringSpec
formats the supplied date elements in reverse order. Is this is a bug?GetDateStringSpec
uses the elements in reverse order, although some functions that use dateStringSpecs may not observe the order defined by the dateStringSpec. For instance, some functions may use the elements of the dateStringSpec, but use the element ordering defined by the locale bundle. GetDateStringSpec([[kElementYear, kFormatNumeric],[kElementMonth, kFormatNumeric],[kElementDay, kFormatNumeric] ]);
prefsView
and place a reference to the template for your preferences slip there (probably using the NTK GetLayout
function.) When the user selects "Prefs" from the Info button in your application, the NewtApp framework will create and open a view based on the template in the prefsView
slot.theApp
in the preferences view. Use this reference to call the application's GetAppPreferences
method. This method will return a frame containing your application's preferences. GetAppPreferences
is a method provided by NewtApp and should not be overidden.'|Pref1:SIG|
) or create a slot in the preferences frame using your developer signature and save all preferences in that frame. This will guarantee that you don't overwrite slots used by the NewtApp framework. preferencesSlip.viewSetupFormScript := func() begin prefs := theApp:GetAppPreferences(); if NOT HasSlot(prefs, kAppSymbol) then prefs.(kAppSymbol) := {myPref1: nil, myPref2: nil}; end;
To save the preferences, call the application's SaveAppState
method:
preferencesSlip.viewQuitScript := func() theApp:SaveAppState(); // save prefs
NewtApp currently provides one built-in preference for where to save new items. In the preferences frame there will be a slot called internalStore
. Setting this slot to true
will force the NewtApp framework to save all new items on the internal store.
aboutInfo
. Place a frame in that slot with the following slots: {tagLine: "", // A tagline for your application version: "", // The version number for the application copyright: "", // Copyright information trademarks: "", // Trademark information}
The information found in this frame will be displayed by the NewtApp framework when the user selects "About" from the Info button's popup. See the picture below for an example of what the user will see.
Alternatively, you can create your own About view. If you do this, create a slot in your application's base view called aboutView
containing a reference to a template for your about view (probably using the NTK GetLayout
function.) A view will be created from that template and opened when the user selects "About" from the Info button's popup.
NewtSoup
continues to get the FillNewSoup
message, even when the soup already exists. Am I doing something wrong?FillNewSoup
message needs to be sent. Check the "Setting the UserVisible Name With NewtSoup" Q&A for more details and a description of how to work around the problem.RegUnionSoup
?newtSoup
called MakeSoup
which you can override. The MakeSoup
method is responsible for calling RegUnionSoup
(or otherwise making a soup) and then calling the FillNewSoup
method if the soup is new/empty.MakeSoup
is called normally as part of initializing the newtSoup
object. Here is a sample MakeSoup
method that will use a newly defined slot (from the newtSoup
based template) for the user name.MakeSoup
method. In particular, MakeSoup
is used by the newtSoup
implementation to initialize the object, so it needs to set up other internal slots. It's vital that the 'appSymbol
slot in the message context be set to the passed argument, and that the 'theSoup
slot be set to the soup or unionSoup that MakeSoup
creates or gets. (Recall that RegUnionSoup
returns the union soup, whether it previously existed or not.)GetSoupList
method of union soups used in this code snippet returns an array with the member soups. It should be considered documented and supported. A newly created union will have no members, so FillNewSoup
should be called. This is an improvement over the default MakeSoup
method, which always calls FillNewSoup
if the soup on the internal store is empty.'userName
slot, which is looked up in the current context. As with soupName
, soupDescr
, etc, you should set a new userName
slot in the frame in the allSoups
frame in the newtApplication
template. MakeSoup: func(appSymbol) begin self.appSymbol := appSymbol; // just do it... self.theSoup := RegUnionSoup(appSymbol, { name: soupName, userName: userName, ownerApp: appSymbol, userDescr: soupDescr, indexes: soupIndices, }); if Length(theSoup:GetSoupList()) = 0 then :FillNewSoup(); end;
allSoups
frame, and then cause the application to refresh. The cursor that controls the sort order for the layout is built from the masterSoupSlot
slot. Both the default and the overview layouts have a masterSoupSlot
which points back to the relevant allSoups
slot in the app base view.newtAppBase.allSoups
& newtAppBase.allSoups.mySoup
are writeable. (Since the frames reside in the package, they are in protected memory.)newtAppBase.allSoups.mySoup:SetupCursor()
to create a new cursor using the new query spec.newtAppBase:RedoChildren()
to display the items in the new sort order. if IsReadOnly (newtAppBase.allSoups) then newtAppBase.allSoups := {_proto: newtAppBase.allSoups}; if IsReadOnly (newtAppBase.allSoups.mySoup) then newtAppBase.allSoups.mySoup :={ _proto: newtAppBase.allSoups.mySoup}; newtAppBase.allSoups.mySoup.soupQuery := {indexpath: newKey}; // new sort order! newtAppBase.allSoups.mySoup:SetupCursor(); newtAppBase:RedoChildren();
newtApplication
method NewtInstallScript
is normally called in the part's InstallScript
function. One thing the NewtInstallScript
does is register the viewDefs in the NewtApp base view allViewDefs
slot using the global function RegisterViewDef
.RegisterViewDef
requires that the data definition symbol be internal. If the symbol is on the card, then when the NewtRemoveScript
tries to unregister the viewDef a reference to data on the card is encountered and the above error message will be shown. This bug will be fixed in a future ROM.InstallScript
before calling NewtInstallScript
: local mainLayout := partFrame.theForm; if mainLayout.allViewDefs then foreach dataDefSym,viewDefsFrame in mainLayout.allViewDefs do foreach viewDef in viewDefsFrame do RegisterViewDef ( viewDef, EnsureInternal (dataDefSym) ); partFrame.removeFrame := mainLayout:NewtInstallScript(mainLayout);
Note that it is OK to call
RegisterViewDef
more than once with the same view definition. RegisterViewDef
will do nothing (and return NIL) if the template is already registered.
newtLabelInputLines
(and their variants) use to accomplish their work.'flavor
slot of the newtLabelInputLine
set of protos, which acts as a translator between the target data frame (or more typically a slot in that frame) and the text field which is visible to the user. For example, it's the filter for newtDateInputLines
which translates the time-in-minutes value to a string for display, and translates the string into a time-in-minutes for the target data.newtFilter
or one of the other specialized filters described in Chapter 4 of the Newton Programmer's Guide.newtLabelInputLine
is opened, a new filter object is instantiated from the template found in the 'flavor
slot for that input line. The instantiated filter can then be found in the filter
slot of the view itself. The _parent
slot of the instantiated filter will be set to the input line itself, which allows methods in the filter to get data from the current environment.inputLine
part of the field, and the rest are methods which you can override or call as appropriate.recFlags
protoLableInputLine
. This provides the 'viewFlags
settings for the inputLine
part of the proto -- the field the user interacts with.recTextFlags
'textFlags
settings for the inputLine
part of the proto.recConfig
'recConfig
settings for the inputLine
part of the proto.dictionaries
'dictionaries
slot used in recognition, Provides custom dictionaries if vCustomDictionaries
is on in the recFlags
slot.PathToText()
inputLine
needs to be updated. The function should read data out of the appropriate slot in the 'target
data frame (usually specified in the 'path
slot) and return a user-visible string form of that data. For example, for numbers the function might look like func() NumberStr(target.(path))
TextToPath(str)
inputLine
value changes. The result will be written into the appropriate slot in the 'target
data frame. The string argument is the one the user has modified from the inputLine
part of the proto. For example, for numbers the function might look like func(str) if StrFilled(str) then StringToNumber(str)
Picker()
PickActionScript
message. If the picker is cancelled, send a PickCancelledScript
message.labelCommands
array.PickActionScript( newValue )
Picker
method. If you override this method be sure to call the inherited PickActionScript
method.PickCancelledScript()
Picker
method. If you override this method be sure to call the inherited PickCancelledScript
method.InitFilter()
inputLine
that uses this filter is first opened. This method can be used to get data from the current environment (for example, the 'path
slot of the inputLine
) and adjust other settings as appropriate.newtApplication
slot called helpManual
. You should store a reference to your help book in this slot.viewHelpTopic
which you can use to dynamically change the location the help book is opened to. This slot should store the name of the topic to open to.newtEditView
or newtROEditView
, I cannot scroll through all the text of a large note. After a few pages it stops scrolling. What is going wrong?newtEditView
and newtROEditView
have a default scroll height of 2,000 pixels. To work around this limitation, you will need to add a slot called noteSize
to your newt(RO)editView
. This slot should hold an array of two elements. The first element is the scroll width. If you do not want horizontal scrolling, the scroll width should equal the view width. The second element is the scrollHeight
.noteSize
slot that you would use to create a newt(RO)EditView
with a scroll height of 20,000 pixels. { _proto: newtEditView, noteSize: [viewWidth, 20000], ...
}
newtApp
-based application which does not use stationery. If I set the forceNewEntry
slot to nil
in my layout and open the application with a nil
target, I can still see the entry view. How can I avoid this?newtApp
framework will not open the stationery if a target does not exist.newtEntry(Roll/Page)Header
proto has a PopIt
method which opens the header. You will need to override the StatScript
of your newtNewStationeryButton
and send the header a PopIt
message. Because PopIt
is not defined prior to Newton 2.1 OS, you will need to check for its existence before calling it. Here is a code example: newtNewStationeryButton. StatScript: func( theStat ) begin // Keep a copy of the inherited return value for use below local result := inherited:?StatScript( theStat ); // Pass self as a parameter for the closure. This gives us a reference // to the application so we can get the entry view. AddDeferredCall( func( context ) begin local entryView := context:GetTargetView(); // This code assumes that your header is declared to the entry // view with the name theHeaderView if entryView.theHeaderView.popIt then entryView.theHeaderView:Popit( true ); end, [self] ); result; end;
'default
in its symbol
slot. To change this behavior at run-time, you will need to override the StatScript
method of your newtNewStationeryButton
.StatScript
method, you will set two slots in your application. The first slot is the preferredViewDef
slot in your application's base view. The second is the viewDef
slot of the current layout. Both of these slots should be set to the symbol of the viewDef that you want displayed. For instance, you might have the following StatScript
:StatScript := func( theStat )begin preferredViewDef := 'myNewDefaultStationery; layout.viewDef:= 'myNewDefaultStationery; // Make sure we call the inherited method inherited:?StatScript( theStat );
end;
Note: you must not modify either the application's preferredViewDef
slot or the layout's viewDef
slot at any other time. Doing so could cause your application to not work on future versions of the Newton OS.
newtEntryPageHeader
which is declared to my newtLayout
view. Each time I change entries in my application, the header does not get properly updated. What's going wrong?newtApplication
views, they need to be declared to their parent. Declaring newtApplication
views to a grandparent can cause undefined behavior.overviewTargetClass
slot to your application (or any other layout that is a descendent of your newtOverLayout
). Set this slot's value to be the symbol that represents your data class (for example, '|myData:SIG|
), which must match the data class you use when registering your print format. The NewtApp overview will use overviewTargetClass
instead of the default overview class ('newtOverview
) supplied by newtOverLayout
.usesCursors
slot to true
indicating that it will use the value target as a multiple item target and it will iterate over it using GetTargetCursor(target)
. For an example of a print format that can handle multiple items, see the MultiRoute DTS sample.'newtOverview
) , see the Q&A "Limitations with NewtOverview Data Class".prefsCache
slot of your NewtApp-based application. This slot is defined in your application's base view by the NewtApp framework. This frame will be saved to the system soup when the application closes.prefsCache
frame, you must use your registered signature to avoid conflicting with slots that the framework may use. You can name each of your preferences with your signature, or we recommend adding a subframe in a slot named with your signature. For instance, you might have the following code:prefsCache.('|MyPrefs:MySIG|) := {pref1: 1, pref2: 2};
newtCheckAllButton
(@872
) which you can use. This proto sends the CheckAll
method to the layout. In Newton 2.1 OS, newtOverLayouts
have two new methods, CheckAll
and UncheckAll,
which implement this behavior. However, none of this is present in Newton 2.0 OS .CheckAll
and UncheckAll
methods for your overview layout (or any other layout you wish to implement check all for.)protoCheckAllButton
. These samples implement an earlier (and less useful) flavor of Check All. The old samples check all the items which are currently visible in the overview, while the Newton 2.1 OS checks all the items that are present in the currently selected folder/card filter. "Checkbook" (version 8 or later) or "WhoOwesWhom" (version 3 or later) will reflect the Newton 2.1 behavior.protoCheckAllButton
from the older sample code, since that gives the correct look and button bounds, and modify it as follows:buttonClickScript
should look something like this: func() if newtAppBase.currentLayout = 'overView then begin if layout.checkAllPrimed then layout:UnCheckAll() else layout:CheckAll(); layout.checkAllPrimed := NOT layout.checkAllPrimed; end;
The overview layout's CheckAll and UncheckAll methods should look something like this:
CheckAll: func() begin local curse := dataCursor:Clone(); curse:Reset(); hilitedIndex := nil; selected := MapCursor(curse, func(e) MakeEntryAlias(e)); AddUndoSend(layout, 'UnCheckAll, []); layout:DoRetarget(); end; UncheckAll: func() begin hilitedIndex := nil; selected := nil; layout:DoRetarget(); end
Note that these methods make use of two undocumented slots: hilitedIndex
and selected
. hilitedIndex
is used internally by newtOverLayout
to track the tapped item. You may set it to NIL
(as above) to clear the value, but do not set it to some other value or rely on its current value. selected
contains an array of aliases to soup entries representing the currently selected items, and will be used by the routing and filing buttons for processing entries. It is important to clear hilitedIndex
when modifying the selected
array in any way.
The resulting CheckAll button should be included in the menuRightButtons
array for the status bar. The older sample code puts it on the left, however user interface discussions as part of the Newton 2.1 OS effort resulted in the decision to place the button on the right.
newtApplication
. allLayouts: {
// see step 9 in the next section
default: GetLayout("default.t"),
// set step 4, overview section
overview: GetLayout("Overview.t"),
}
allSoups: { mySoup: { _proto: newtSoup, soupName: "SoupName:SIG", soupIndices: [], soupQuery: {} } } title: kAppName
4) Draw a
newtClockFolderTab
or newtFolderTab
as a child of the newtApp
.
5) Draw a newtStatusBar
as a child of the newtApp
.
6) For the newtStatusBar
set the following slots:
menuLeftButtons: [newtInfoButton] menuRightButtons: [newtActionButton, newtFilingButton]
7) Save the layout file as
"main.t"
and add it to the project.
Create the default view:
1) Create another layout file.
2) Draw a newtLayout
in the new layout file.
3) Add a viewJustify
slot to the newtLayout
and set it to parentRelativeFull
horizontal and vertical.
4) Set the viewBounds
of the newtLayout
to:
{top: 20, // leave room for the folder tab bottom: -25, // leave room for the status bar left: 0, right: 0}
5) Draw a
newtEntryView
as a child of the newtLayout
.
6) Add a viewJustify
slot and set it to parentRelativeFull
horizontal and vertical (necessary only until platform file is updated).
7) Set the viewBounds
of the newtEntryView to:
{top: 0, bottom: 0, right: 0, left: 0};
8) Draw slot views as children of the entry view to display slots from the soup entry.
For example:
a) Draw a newtLabelInputLine
as a child of the newtEntryView
.
b) Set the following slots:
label: "My Label" path: 'myTextSlot
c) Draw a
newtLabelNumInputLine
as a child of the newtEntryView
.
d) Set the following slots:
label: "Number" path: 'myNumberSlot
9) Save the layout file as
"default.t"
and add it to the project. Move it so that it is compiled before the main layout (use the Process Earlier menu item).
Add Overview support
1) Create another layout file.
2) Draw a newtOverLayout
in the new layout file.
3) Add the Abstract
slot to the newtOverLayout
, for example:
Abstract := func(item, bbox ) begin local t := item.myTextSlot & ","; if item.myNumberSlot then t := t && NumberStr(item.myNumberSlot); MakeText(t, bbox.left+18, bbox.top, bbox.right, bbox.bottom - 18); end;
4) Save the layout file as "overview.t" and add it to the project. Move it so that it is compiled before the main layout (use the Process Earlier menu item).
Add InstallScript and RemoveScript
1) Create a text file and add the following to it:
InstallScript := func(partFrame) begin partFrame.removeFrame := (partFrame.theForm):NewtInstallScript(partFrame.theForm); end; RemoveScript := func(partFrame) begin (partFrame.removeFrame): NewtRemoveScript(partFrame.removeFrame); end;
2) Save the text file and add it to the project.
callbackID
parameter to the RegSoupChange
global function. If you also use your application's symbol as the callbackID
parameter, you will overwrite the NewtApp framework's registration. There are two ways to work around this problem.callbackID
symbol in your call to RegSoupChange
.NewtSoupChangedNotify
method is called. It is passed the same four parameters as the callback function parameter of RegSoupChange
.NewtSoupChangedNotify
method, be sure to call the inherited method. application:NewtSoupChangedNotify(theName, appSym, changeType, changeData)begin // Do your stuff here inherited:?NewtSoupChangedNotify( theName, appSym, changeType, changeData );end;
packed
) to prevent aligning of the fields by the compiler. I tried to use the keyword and got an error. How can I ensure that a structure is packed?__packed
, which is probably worth a try. Using this directive may cause the compiler to stop with an internal error in some circumstances, so be prepared. You should ensure that the structure produced has the correct alignment by using the C sizeof
and offsetof
functions, since this directive does introduce a compiler dependency. Accessing elements in __packed
structures can be considerably less efficient than using non-packed structures: use them only when necessary. For example: __packed struct T { char c; int i; };
produces a structure that is 5 bytes wide. Without the
__packed
directive, it would be 8 bytes wide and the integer field would begin 4 bytes from the structure start, so that it was word aligned.
We believe the internal error in the compiler can be avoided by taking the sizeof the structure before using it. An easy way to do this is to add a dummy function right after the structure is declared. For example:
inline void dummyT() { (void)sizeof(T); }
Primitive types can also be declared __packed
, which means that the compiler will not make assumptions about the alignment of pointers to them. That is, if you know an int starts two bytes into a word-aligned data structure, the wrong thing will happen if you simply cast the pointer to int
. Instead, you can used an unaligned int
type. This generates considerably less efficient code than is needed for working with aligned values, but it's still more efficient that trying to extract the proper bytes and shift/add them into an integer youself. For example:
typedef __packed int UNALIGNED_INT; int IntAt(UNALIGNED_INT* p) { return *p; }
This directive does not work properly with bitfield specifiers. For example:
__packed struct Foo { unsigned flag1 : 1; unsigned flag2 : 1; unsigned data1 : 6; unsigned short data2;
}
will not produce what you expect. Instead, avoid the bitfield specifiers and take advantage of C++ inline functions to access the partial bytes:
__packed struct Foo { char stuff; unsigned short data2; int Flag1() { return (stuff & 0x80) != 0; } int Flag2() { return (stuff & 0x40) != 0; } int Data1() { return stuff & 0x3F; } int Data2() { return data2; } }; inline void dummyFoo() { (void)sizeof(Foo); }
The result is a 3-byte wide data structure with the bitfields easily accessible.
Note that the ProtocolGen tool (part of the DDKs) does not understand the
__packed
directive. ProtocolGen does not make use of structure sizes, so it's OK to NOP out the __packed
keyword for that tool. Here's an easy way to do that:
#ifdef PROTOCOLGEN #define __packed
#endif
AfterScript
to set the appropriate slot in the view to point to the ROM based PICT (assuming that the constant for the PICT is defined in the NTK definitions file AND documented in the Newton Programmers Guide). Use something like this in the AfterScript
:thisView.icon := ROM_RouteDeleteIcon;
protoStaticText
text slot that is in one linked layout window from a button that is in another linked layout window. I tried to allow access to the base view from both linked layouts, but this didn't help. I even tried to allow access from the base view to both layouts, but this didn't help, either. What should I do?textThatChanges
which a child of a view called changingContainer
and is declared to changingContainer
with the name textThatChanges
. ChangingContainer
is the base view for a layout which is linked into the main layout, and the link (in the main layout) is declared as changingContainerLink
. Code in the main layout can change the text of the textThatChange
view like so: SetValue(containerLink.whatToDo, 'text, "Turn and face the...")
To do the equivalent of the declare yourself:
1) In the viewSetupFormScript
script of the 'buttonThatChanges
button, set the value of the base view's slot 'theTextView
to self
, as in the following code fragment:
func() begin base.theTextView := self; end
2) In the
buttonClickScript
script of the 'buttonThatSetsText
button, use the global function SetValue
to store new text in the text slot of the 'buttonThatChanges
button, as in the following code fragment:
func() begin SetValue(base.theTextView, 'text, "Now something happened!"); end
Note that this example assumes the self-declared view called
base
. In your application, you may access your base view in a different way.
StrCompare
can return different results at compile time than it does at run time. What gives?StrCompare
on an given Newton unit. Here is one such function for English releases of the Newton OS (which assumes strings using only page 0 of the unicode table):constant kNSortTable := '[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46, 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68, 69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90, 91,92,93,94,95,96,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, 81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106, 107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122, 123,124,125,126,127,128,129,130,131,132,133,161,157,135,136,165, 149,138,137,143,141,152,159,158,144,140,170,134,146,147,148,142, 150,138,168,171,151,153,160,153,154,155,156,174,174,174,174,65, 65,145,67,175,69,175,175,176,176,176,176,162,78,177,177,177,79, 79,164,79,178,178,178,85,166,167,139,65,65,65,65,65,65,145,67,69, 69,69,69,73,73,73,73,169,78,79,79,79,79,79,163,79,85,85,85,85,172, 173,89]; // function to compare strings (only page 0 characters)// with the same order as the Newton ROM does.DefConst('kNewtonStrCompare, func(s1, s2) begin local l1 := StrLen(s1); local l2 := StrLen(s2); local l := Min(l1, l2); local i := 0; while i < l and (r := kNSortTable[ord(s1[i])] - kNSortTable[ord(s2[i])]) = 0 do i := i + 1; if i = l then l1-l2 else r; end);
Note that just because you might find a particular function to be defined at compile time, do not assume that it behaves in exactly the same way as the like-named run-time function, unless the documentation explicitly says it does. (And, of course, it might not always be defined in the compile-time environment of future NTK products if it isn't documented that way.)
call func(theFrame) begin local i := 0; foreach slot, value in theFrame do begin print(i && ': && slot); i := i + 1; end end with (<the reordered frame>)
Main Build time heap size (+/- 0.5 sec) 1250K Main heap ran out of memory... 1275K 32.7 sec 1300K 26.4 sec 1400K 22.3 sec 1500K 19.2 sec 1600K 17.5 sec 2000K 16.0 sec 3000K 15.2 sec
Experiment with Main heap size by measuring build time until you find a reasonable compromise between build time and memory requirements for your particular project.
If you are curious about GC activity, do the following:
1) Add the following line to your GlobalData
file (in the NTK folder) and restart NTK:
protoEditor:DefineKey({key: 65}, 'EvaluateSelection);
This allows you to use the period key on the numeric keypad to evaluate selected text in the Inspector window or any text file in the NTK build-time environment. (Normally the text is compiled by NTK and then evaluated by the Newton device when you hit the Enter key.) See the NTK User's Guide for details on the
GlobalData
file.
2) Type VerboseGC(TRUE)
in the Inspector window, select, and hit the keypad-period key. Each time the GC kicks in, a line will be displayed in the Inspector window. By watching the frequency of GCs, you can get some idea of how your main heap is being used.
3) Use VerboseGC(FALSE)
to turn this feature off. Please note that VerboseGC
is available only in the NTK build-time environment. The function does not exist on the Newton device itself. It should be used only for debugging and optimization.
Build Heap
The Build heap holds your package frame data during the last part of the build. Its size is set through the Toolkit Preference dialog. Changes take effect immediately.
The Build heap is allocated only when the Build Package command is issued. It is released as soon as the resulting file is written to disk. As a result Build heap allocation is a recurring issue.
The rule of thumb is to set the Build heap to the size of your package (on the MacOS computer hard disk, not on the Newton device). If the Build heap is insufficient, NTK will tell you so.
There is nothing to be gained by setting the Build heap larger than necessary.
NTK first attempts to allocate the Build heap from MultiFinder memory. If that fails, NTK tries to allocate the Build heap from NTK's partition.
To verify that you have enough memory for the Build heap you need to look at the "About This Macintosh" dialog in the Finder application just prior to issuing the build command.
1) If the "Largest Unused Block" exceeds the Build heap requested size, the Build heap will be allocated from MultiFinder memory.
2) If 1 failed and NTK's partition bar shows enough free memory to accommodate the request, the Build heap will be allocated in NTK's partition.
3) If both 1 and 2 failed, the build will fail. Try to increase MultiFinder free memory by quitting any other open application, or increase the free memory in NTK's partition by closing some or all of NTK's open windows. Then try building again.
To prevent fragmentation of MultiFinder memory launch NTK first, and DocViewer, ResEdit, etc. afterwards. Whenever possible, quit those other applications in the reverse order .
Note: You can use Balloon help to see how much memory an application is actually using. Simply select the Show Balloons menu item and position the cursor on the application partition bar in the About Macintosh dialog. This feature is missing from PowerPC-based MacOS computers.
NTK Partition Size
For NTK 1.6 the rule of thumb for the "smallest useful" partition size for small projects is:
(3500K + Main heap size) for a 680x0 MacOS computer
(5500K + Main heap size) for a PowerPC MacOS computer with Virtual Memory off.
These rules do not include space for the Build heap.
The "smallest useful" partition size is defined by the following example: Using NTK default Main and Build heaps, open the Checkbook sample. Open one browser and one layout window for each file in the project, connect the Inspector, build and download. Perform a global search on "Check" (case insensitive) producing slightly more than 200 matches. Double click on several of these matches displayed in the search results window. Build and download again.
For serious work, increase the partition size by at least 256K for small projects, more for large ones. If you routinely perform global searches that produces many matches, see the next section.
On a PowerPC-based MacOS computer with Virtual Memory on, NTK's 2.7 Meg of code (the exact number is shown in the Finder Info dialog) stays on the hard disk, reducing memory requirements at the expense of performance.
if x = 1 then dosomething else if x = 2 then doSomethingElse else if x = 3 then doYetAnotherThing else if x = 4 then doOneMoreThing else if x = 5 then doSomethingSimple else if x = 6 then doThatThing else if x = 7 then doThisThing else // x = 8 doTheOtherThing
...can be rewritten like this:
if x <= 4 then if x <= 2 then if x = 1 then doSomething else // x = 2 doSomethingElse else if x = 3 then doYetAnotherThing else // x = 4 doOneMoreThing else if x <= 6 then if x = 5 then doSomethingSimple else // x = 6 doThatThing else if x = 7 then doThisThing else // x = 8 doTheOtherThing;
Note that the if/then/else statement nesting is "unusual" to illustrate the nesting that the compiler must make each statement is nested as the compiler would process it.
Use an array of functions with integer selectors
Replace a long if-then-else statement with an array of functions. The code is more compact and readable. For a large set of alternatives, the faster direct lookup should compensate for the extra function call. This approach is most useful for a contiguous range of selector values (e.g., 11 to 65). It can accommodate a few "holes" (for example, 11 to 32, 34 to 56, 58 to 65). It is not practical for non-contiguous selectors (e.g., 31, 77, 256, 1038...)
For example, the following code:
if x = 1 then dosuchandsuch; else if x = 2 then dosomethingelse; else if x = 3 then andsoon;
...can be rewritten like this:
cmdArray := [func() dosuchandsuch, func() dosomethingelse, func() andsoon]; call cmdArray[x] with ();
Use a frame of functions with symbols for selectors
This alternative provides the flexibility of using symbols for selecting the outcome.
For example, the following code:
if x = 'foo then dosuchandsuch; else if x = 'bar then dosomethingelse; else if x = 'baz then andsoon;
...can be rewritten like this:
cmdFrame := {foo: func() dosuchandsuch, bar: func() dosomethingelse, baz: func() andsoon}; call cmdFrame.(x) with ();
Increase NTK's stack size using the ResEdit application
Open the Newton Toolkit application with ResEdit.
Double-click on the "mem!
" resource icon
Double-click on resource ID 1000
named "Additional NTK Memory Requirements"
Change the fifth (and last) value. This is an hexadecimal number. In NTK 1.6, you should see "0001 8000
" which is 98304
bytes (or 96k
) to add to the total stack size. For example, to increase this value to 128k
= 131072
bytes change the hexadecimal value to "0002 0000
".
DeclareUnit
, before it's used (imported or exported.) See the docs on DeclareUnit
below for details.DefineUnit
and specify the NS objects that are exported.UnitReference
(or UR for short.)DeclareUnit(unitName, majorVersion, minorVersion, memberIndexes)
- symbol - name of the unit
unitName
majorVersion
- integer - major version number of the unit
minorVersion
- integer - minor version number of the unit
memberIndexes
- frame - unit member name/index pairs (slot/value)
return value - unspecified
A unit must be declared by
DeclareUnit
before it's used (imported or exported.) The declaration maps the member names to their indexes. A typical declaration looks like:
DeclareUnit('|FastFourierTransforms:MathMagiks|, 1, 0, { ProtoGraph: 0, ProtoDataSet: 1, });
Typically, the declarations for a unit are provided in a file, such as "FastFourierTransforms.unit", that is added to an NTK project (similar to .h
files in C.)
When resolving imports, the name and major version specified by the importer and exporter must match exactly. The minor version does not have to match exactly. If there are units differing only in minor version, the one with the largest minor version is used.
Typically, the first version of a unit will have major version 1 and minor version 0. As bug fixes releases are made, the minor version is incremented. If a major (incompatible) change is made, then the major version number is incremented.
Note: When a unit is modified, the indexes of the existing members must remain the same. In other words, adding new members is safe as long as the indexes of the existing members don't change. If you change a member's index it will be incompatible with any existing clients (until they're recompiled with the new declaration.)
DefineUnit(unitName, members)
- symbol - name of the unit
unitName
members
- frame - unit member name/value pairs (slot/value)
return value - unspecified
DefineUnit
exports a unit and specifies the value of each member. Immediates and symbols are not allowed as member values. A typical definition looks like:
DefineUnit('|FastFourierTransforms:MathMagiks|, { ProtoGraph: GetLayout("foo.layout"), ProtoDataSet: { ... },});
A unit must be declared before it's defined. The declaration used when exporting a unit with n
members must contain n
slots with indexes 0..n-1
. The definition must specify a value for every declared member (this is important.)
UnitReference(unitName, memberName)
or
UR(unitName, memberName)
- symbol - name of a unit
unitName
memberName
- symbol - name of a member of unit
return value - a reference to the specified member
To use a unit member call UnitReference
(UR
for short) with the unit and member name.
The unit name 'ROM
can be used to refer to obects in the base ROM. For example:
UR('ROM, 'ProtoLabelInputLine)
.
Note: references to objects in the base ROM are sometimes called "magic pointers" and have traditionally been provided in NTK by constants like ProtoLabelInputLine
or ROM_SystemSoupName
.
In Newton 2.0 OS, there may also be packages in the ROM. These ROM packages may provide units. Their members are referenced just like any other unit, using UR
, the unitName, and the memberName. This is the mechanism by which licensees can provide product-specific functionality.
AliasUnit(alias, unitName)
- symbol - alternate name for unit
alias
unitName
- symbol - name of a unit
return value - unspecified
AliasUnit
provides a way to specify an alternate name for a unit. Since unit names must be unique, they tend to be long and cumbersome. For example:
AliasUnit('FFT, '|FastFourierTransforms:MathMagiks|);
...so that you could write:
local data := UR('FFT, 'ProtoDataSet):New(points);
...instead of:
local data := UR('|FastFourierTransforms:MathMagiks|, 'ProtoDataSet):New(points); AliasUnitSubset(alias, unitName, memberNames)
- symbol - alternate name for unit
alias
unitName
- symbol - name of a unit
memberNames
- array of symbols - list of unit member names
return value - unspecified
AliasUnitSubset
is similar to AliasUnit
, except that it additionally specifies a subset of the units members which can be used. This helps restrict code to using only certain members of a unit.
Unit Part Frame Methods
These methods can optionally be defined in a part frame to handle units becoming unavailable.
RemovalApproval(unitName, majorVersion, minorVersion)
- symbol - name of the unit
unitName
majorVersion
- integer - major version number of the unit
minorVersion
- integer - minor version number of the unit
return value - nil
or string
This message is sent to a part frame when an imported unit is about to be deactivated. It may a return a string to be shown to the user as a warning about the consequences of deactivating the package in use. For example:
"This operation will cause your connection to fooWorld to be dropped."
Note: do not assume that the user is removing the package. Other operations such as moving a package between stores also cause package deactivation.
This message is only a warning. The user may decide to proceed and suffer the consequences. If the user proceeds, the ImportDisabled
message (see below) will be sent.
If the removing the unit is not a problem (for example, your application is closed), then RemovalApproval
can return nil
and the user will not be bothered.
ImportDisabled(unitName, majorVersion, minorVersion)
- symbol - name of the unit
unitName
majorVersion
- integer - major version number of the unit
minorVersion
- integer - minor version number of the unit
return value - unspecified
This message is sent to a part frame after an imported unit has been deactivated. The part should deal with the situation as gracefully as possible. For example, use alternative data or put up a Notify and/or close your application.
Unit-Related Glue Functions
These functions are available in the Newton 2.0 Platform file.
MissingImports(pkgRef)
return value -
nil
or an array of frames (see below)
glue name - kMissingImportsFunc
MissingImports
lists the units used by the specified package that are not currently available. MissingImports
returns either nil, indicating there are no missing units, or an an array of frames of the form:
{ name: symbol - name of unit desired major: integer - major version number minor: integer - minor version number <other slots undocumented> }
mySoup:Query({words: "pizza"}
) don't sucessfully find the entries. Why?SetClass(SetLength("\u<hex data>"), theLength), theClass)
in Windows NTK , the binary object is not what I expect. It seems to be byte-swapped. How can I create binary objects with data in them in Windows NTK?MakeBinaryFromHex()
function; it handles the byte-swapping issues properly. This function is defined by the platform file, and only runs at build-time -- it doesn't exist on the Newton device. You may need to get a newer platform file because this function was added after Windows NTK 1.6 shipped.myEncloser := { importantSlot: 42, GetImportantSlot := func() return importantSlot, nestedSlot := { myInternalValue: 99, getTheValue := func() begin local foo; foo := :GetImportantSlot(); // WON'T WORK; can't find function foo := myEncloser:GetImportantSlot(); // MAY WORK importantSlot := 12; // WON'T WORK; will create new slot in nestedSlot myEncloser.importantSlot := 12; // MAY WORK end }}; myEncloser.nestedSlot:GetTheValue();
The proper way to accomplish this is to give the nested frame a
_parent
or _proto
slot that references the enclosing frame. Nesting the frame is not strictly necessary in this case, only the _proto
or _parent
references are used.
MyFrame:= {}; theSlotName := "Slot_1";
At this point is there a way to then create the following?... MyFrame.Slot_1
A: The function Intern
takes a string and returns a symbol. There is also a mechanism called path expressions (see the NewtonScript Reference), that allows you to specify an expression or variable to evaluate, in order to get the slot name. You can use these things to access the slots you want:
MyFrame := {x: 4}; theXSlotString := "x" ; MyFrame.(Intern(theXSlotString)) := 6 theSlotName := "Slot_1"; MyFrame.(Intern(theSlotName)) := 7; // myFrame is now {x: 6, Slot_1: 7}
call func() begin local s,v; local root := GetRoot(); local base := root.|YourApp:YourSIG|; // name of app local prot := base._proto; foreach s,v in base do begin if v and v <> root AND v <> base AND v <> prot then begin Write ("Slot:" && s & ", Value: "); Print(v); end; end; end with ()
The debugging function
TrueSize
can also be a valuable tool to determine the heap used by your applications. See the NTK User Guide for more information about TrueSize
.
thrower: func(x) begin if x then throw('|evt.ex.msg;my.exception|, "Some error occurred"); end; returner: func(x) begin if x then return -1; // some random error code, 0; // nil, true, whatever. end;
Code to throw and and handle an exception:
local s; for i := 1 to kIterations do try call thrower with (nil); onexception |evt.ex.msg;my.exception| do s := CurrentException().data.message;
Code to check the return value and handle an error:
local result; local s; for i := 1 to kIterations do if (result := call returner with (nil)) < 0 then s := ErrorMessageTable[-result];
Running the above loops 1000 times took about 45 ticks for the exception loop, and about 15 ticks for the check the return value loop. From this you might conclude that exception handling is a waste of time. However, you can often write better code if you use exceptions. A large part of the time spent in the loop is setting up the exception handler. Since we commonly want to stop processing when exceptions occur, we can rewrite the function to set up the exception handler once, like this:
local s;try for i := 1 to kIterations do call thrower with (nil); onexception |evt.ex.msg;my.exception| do s := CurrentException().data.message;
This code takes only 11 ticks for 1000 iterations, an improvement over the return value case, where we'd have to check the result after each call to the function and stop the loop if an error occurred.
Running the same loops, but passing TRUE
instead of NIL
so the "error" occurs every time was interesting. The return value loop takes about 60 ticks, mostly due to the time needed to look up the error message. The exception loop takes a whopping 850 ticks, mostly because of the overhead in the CurrentException
() call.
With exceptions, you can handle the error at any level up the call chain, without having to worry about each function checking for and returning error results for every sub-function it uses. This will produce code that performs much better, and will be easier to maintain as well.
With exceptions, you do not have to worry about the return value for successful function completion. It is occasionally very difficult to write functions that both have a return value and generate an error code. The C/C++ solution is to pass a pointer to a variable that is modified with what should otherwise be the return value of the function, which is a technique best avoided.
As in the above example, you can attach data to exceptions, so there's no need to maintain an error code to string (or whatever) mapping table, which is another boon to maintainability. (You can still use string constants and so on to aid localization efforts. Just put the constant in the throw call.)
Finally, every time an exception occurs you have an opportunity to intercept it with the NTK inspector. This is also a boon to debugging, because you know something about what's going wrong, and you can set the breakOnThrows
global to stop your code and look at why there's a problem. With result codes you have a tougher time setting break points. With a good debugger it could be argued that you can set conditional break points on the "check the return value" code, but even when you do this you'll have lost the stack frame of the function that actually had the problem. With exceptions and breakOnThrows
, all the local context at the time the exception occurred is still available for you to look at, which is an immense aid.
Conclusion: Use exceptions. The only good reason not to would be if your error handler is very local and if you expect it to be used a lot, and if that's true you should consider rewriting the function.
PrimClassOf
will return an object's primitive type.String
. A string object contains a 12-byte header plus the Unicode strings plus a null termination character. Note that Unicode characters are two-byte values. Here's an example: "Hello World!"
This string contains 12 characters, in other words it has 24 bytes. In addition we have a null termination character (24 + 2 bytes) and an object header (24 + 2 + 12 bytes), all in all the object is 38 bytes big. Note that we have not taken into account any possible savings if the string was compressed (using the NTK compression flags).
Rich Strings
Rich strings extend the string object class by embedding ink information within the object. Within the unicode, a special character kInkChar
is used to mark the position of an ink word. The ink data is stored after the null termination character. Ink size varies depending on stroke complexity.
Array Objects
Array objects have an object header (12 bytes) and additional four bytes per element which hold either the immediate value or a reference to a referenced object. To calculate the total space used by an array, you need to take into account the memory used by any referenced objects in the array.
Here's an example:
[12, $a, "Hello World!", "foo"]
We have a header (12 bytes) plus four bytes per element (12 + (4 * 4) bytes). The integer and character are immediates, so no additional space is used, but we have 2 string objects that we refer to, so the total is (12 + (4*4) + 38 + 20 bytes) 86 bytes. We have not taken into account savings concerning compression. Note that the string objects could be referred by other arrays and frames as well, so the 38 and 20 byte structures are stored only once per package.
Frame Objects
We have two kinds of frames: frames that don't have a shared map object; and frames that do have a shared map object. We take the simple case first (no shared map object).
The frame is maintained as two array-like objects. One, called the frame map, contains the slot names, and the other contains the actual slot values. A frame map has one entry per symbol, plus one additional 4 -byte value.
The frame map uses a minimum of 16 bytes. If we add the frame's object header to this, the minimal size of a frame is 28 bytes. Each slot adds 8 bytes to the storage used by the frame (two array entries.) Here's an example:
{Slot1: 42, Slot2: "hello"}
We have a header of 28 bytes, and in addition we have two slots, for a total of (28 + (2 * 8)) 48 bytes. This does not take into account the space used for each of the slot name symbols or for the string object. (The integer is an immediate, and so is stored in the array.)
Multiple similar frames (having the same slots) could share a frame map. This will save space, reducing the space used per frame (for many frames all sharing the same map) to the same as used for an array with the same number of slots. (If just a few frames share the frame map, we need to take into account the amortized map size that the frames share. So the total space for N frames sharing a map is N*28 bytes of header per frame, plus the size of the frame map, plus the size of the values for the N frames.
Here's an example of a frame that could share a map with the previous example:
{Slot1: 56, Slot2: "world"}
We have a header of 12 bytes. In addition, we have two slots (2 * 4), and additional 16 bytes for the size of a map with no slots Ñ all in all, 36 bytes. We should also take into account the shared map, which is 16 bytes, plus the space for the two symbols.
When do frames share maps?
1. When a frame is cloned, both the copy and the original frame will share the map of the original frame. A trick to make use of this is to create a common template frame, and clone this template when duplicate frames are needed.
2. Two frames created from the same frame constructor (that is, the same line of NewtonScript code) will share a frame map. This is a reason to use RelBounds
to create the viewBounds
frame, and it means there will be a single viewBounds
frame map in the part produced.
Note: These figures are for objects in their run-time state, ready for fast access. Objects in transit or in storage (packages) are compressed into smaller stream formats. Different formats are used (and different sizes apply) to objects stored in soups and to objects being streamed over a communications protocol.
if value.path = '|name.first| then ... // WRONG
A: There are several concerns.
'|name.first|
is not a path expression, it is a symbol with an escaped period. A proper path expression is either 'name.first
or [pathExpr: 'name, 'first]
. The vertical bars escape everything between them to be a single NewtonScript symbol.
The test value.path = 'name.first
will always fail, because path expressions are deep objects (essentially arrays) the equal comparison will compare references rather than contents. You will have to write your own code to deeply compare path expressions.
This code is further complicated by the fact that symbols are allowed in place of path expressions that contain only one element, but the two syntaxes produce different NewtonScript objects with different meanings. That is, 'name = [pathExpr: 'name]
will always fail, as the objects are different.
A general test is probably unnecessary in most circumstances, since you will be able to make assumptions about what you are looking for. For example, here is some code that will check if a given path value from a soup index is equivalent to 'name.first
:
if ClassOf(value.path) = 'pathExpr and Length(value.path) = 2 and value.path[0] = 'name and value.path[1] = 'first then ...
... local myFunc := func(...) ...; local futureSoupEntries := Array(10, nil); for i := 0 to 9 do futureSoupEntries[i] := { someSlots: ..., aFunction: myFunc, }; ...
A: When a function is defined within another function, the lexically enclosing scope (locals and paramaters) and message context (self) are "closed over" into the function body. When NewtonScript searches for a variable to match a symbol in a function, it first searches the local scope, then any lexically enclosing scopes, then the message context (self), then the _proto and _parent chains from the message context, then finally the global variables.
Functions constructed within another function, as in your example, will have this enclosing lexical scope, which is the locals and parameters of the function currently being executed, plus the message context (self) when the function is created. Depending on the size of this function and how it's constructed, this could be very large. (Self might be the application's base view, for example.)
A TotalClone
is made during the process of adding an entry to a soup, and this includes the function body, lexical scopes, and message context bound up within any functions in the frame. All this can take up a lot of space.
If you create the function at compile time (perhaps with DefConst('kMyFunc, func(...) ...)
) it will not have the lexically enclosing scope, and the message context at compile time is defined to be an empty frame, and so cloning such a function will take less space. You can use the constant kMyFunc
within the initializer for the frame, and each frame will still reference the same function body. (Additionally, the symbol kMyFunc
will not be included in the package, since it is only needed at compile time.)
If the soup entries are only useful when your package is installed, you might consider instead replacing the function body with a symbol when you write the entry to the soup. When the entry is read from the soup, replace the symbol with the function itself, or use a _proto
based scheme instead. Each soup entry will necessarily contain a complete copy of the function, but if you can guarantee that the function body will always be available within your application's package, it might be unnecessarily redundant to store a copy with each soup entry.
TrueSize
to get the size of a soup entry I get results like 24K or even 40K for the size. That can't be right. What's going on?TrueSize
"knows" about the underlying implementation of soup entries. A soup entry is really a special object (a fault block) that contains information about how to get an entry and can contain a cached entry frame. In the information about how to get an entry, there is a reference to the soup, and various caches in a soup contain references to the cursors, the store, and other (large) NewtonScript objects. TrueSize
is reporting the space taken up by all of these objects. (Note: calling TrueSize
on a soup entry will force the entry to be faulted in, even if it was not previously taking up space in the NewtonScript heap.)TrueSize
is not very useful when trying to find out how much space the cached frame for an entry is using. A good way to find the space used for a cached entry frame is to call gc(); stats();
record the result, then call EntryUndoChanges(entry); gc(); stats()
. The difference between the two free space reports will be the space used by the cached frame for a given entry.EntryUndoChanges(entry)
will cause any cached frame to be removed and the entry to return to the unfaulted state. Gc() then collects the space previouly used by the cached entry frame.TrueSize
breakdown of the types of objects used, you can Clone
the entry and call TrueSize
on the copy. This works because the copy is not a fault block, and so it does not reference the soups/cursors/stores.Floor
and Ceiling
seem broken. For instance, Floor(12.2900 * 10000)
returns 122899, not 122900. What's going on?Floor
or Ceiling
. This happens because of the way floating point numbers are stored, and the limitation is common to many real number representations. In the same way that 1/3 cannot accurately be represented in a finite number of digits in base 10 (it is .3333333333...), likewise 1/10 cannot be exactly represented as a fractional part in base 2. Because number printers typically round to a small number of significant digits, you don't normally notice this. The NTK inspector, for example, displays only 5 significant figures in floating point numbers. However, if you display the number with enough precision, you'll see the representation error, where the real is actually slightly larger or smaller than the intended value.FormattedNumberStr(0.1, "%.18f") -> "0.100000000000000010"
FormattedNumberStr(0.3, "%.18f") -> "0.299999999999999990"
The functions Floor
and Ceiling
are strict, and do not attempt to take this error into account. In the example, 12.29
is actually 12.2899999999999990
, which multiplied by 10000
is 122,899.999999999990
. The largest integer less than this number (Floor
) is correctly 122899
.
There are usually ways to work around this problem, depending on what you are trying to accomplish. To convert a floating point number to an integer, use RIntToL
, which rounds to the nearest integer avoiding the problems caused with round-off error and Floor
or Ceiling
. RIntToL(x)
produces the same result that Floor(Round(x))
would produce.
RIntToL(12.29*10000) -> 122900
If you need to format a number for display, use a formatting function such as FormattedNumberStr
. These functions typically round to the nearest displayable value. To display 2 decimal digits, use "%.2f":
FormattedNumberStr(12.29, "%.2f") -> "12.29"
If you're working with fixed point numbers such as dollar amounts, consider using integers instead of reals. By representing the value in pennies (or mils, or whatever) you can avoid the imprecision of reals. For example, represent $29.95
as the integer 2995
or 29950
, then divide by 100
or 1000
to display the number. If you do this, keep in mind that there is a maximum representable integer value, 0x1FFFFFFF
or 536870911
, which is sufficient to track over 5 million dollars as pennies, but can't go much over that.
If you really need to find the greatest integer less than a certain number and can't tolerate how Floor
deals with round off errors, you'll need to do some extra work keeping track of the precision of the number and the magnitude of the round off error. It's worthwhile to read a good numeric methods reference. Floating point numbers in NewtonScript are represented by IEEE 64-bit reals, which are accurate to around 15 decimal digits. The function NextAfterD
provides a handy way to see how 'close together' floating point numbers are.
FormattedNumberStr(NextAfterD(0.3, kInfinity), "%.18f"); -> "0.300000000000000040"
http://gemma.apple.com/dev/techsupport/insidemac/PPCNumerics/PPCNumerics-2.html
The Newton floating point environment is not as rich in features as the PowerPC environment, and the PowerPC numerics document is only mentioned as a useful resource for understanding floating point issues. It in no way documents API or features of the Newton floating point environment.
Briefly, numbers are represented by 1 bit of sign ("on" is negative), 11 bits of exponent, and 52 bits of fractional part. The exponent bits are stored in excess 0x3FF, that is, 0x3FF is the representation for 0, values greater than 0x3FF are positive exponents, and values less than 0x3FF are negative exponents. The 52 bits of fractional part actually provide 53 bits of accuracy, because the initial 1 bit is dropped.
For example, suppose that we want to convert 9 97/128 into IEEE 64 bit format:
1) convert to base 2
1001.1100001
2) shift number to the form of 1.yyyyyy * 2^Z
1.0011100001 * 2^3
3) add 0x3FF (excess 0x3FF) to exponent field, convert to binary.
3+0x3FF = 0x402 = 100 0000 0010
4) now put the numbers together, using only the fractional part of the number represented above, in the form of yyyyyy
0 10000000010 0011100001000000000000000000000000000000000000000000
in hex representation, this is 0x4023840000000000
5) Just to verify, try it: StrHexDump(9+97/128, 16) -> "4023840000000000"
The IEEE standard also allows for non-normal numbers. Here are the exceptions:
infinity e = 7FF, f = 0 (+ or - depending on sign bit)
NaN e = 7FF, f <> 0 (also overflow, error, etc.)
zero e = 0, f = 0 (+ or -, depending on sign bit)
subnormal e = 0, f <> 0 (these are less precise numbers, smaller than the smallest normal number)
Note that there is more than one not a number value. In fact, there are quite a large number. The IEEE spec assigns meaning to various NaN values, as well as defining signalling and quiet NaNs. NewtonScript does not distinguish between NaN values. One NaN is as good as another.
In NewtonScript, real numbers are 8-byte binary objects of class 'real
. In addition to the NewtonScript floating point literal syntax, you can use the compile time function MakeBinaryFromHex
to construct real numbers, and you must use this style for custom NaN values. The most recent platform files for Newton 2.0 and Newton 2.1 provide constants for negative zero (kNegativeZero
), positive and negative infinity (kInfinity
, kNegativeInfinity
), and a canonical NaN (kNaN
).
MakeBinaryFromHex("4023840000000000", 'real) -> 9.7578125 // = 9+97/128
protoSoupOverview
?HitItem
that gets called whenever an item is tapped. The method is defined by the overview and you should call the inherited one. Also note that HitItem
gets called regardless of where in the line a tap occurs. If the tap occurs in the checkbox, you should do nothing, otherwise you should do something.protoSoupOverview
. So, you can find the actual soup entry by cloning the cursor and moving it.HitItem
method. If the item is selected (the checkbox is not tapped) then the code will set an inherited cursor (called myCursor
) to the entry that was tapped on:func(itemIndex, x, y)begin // MUST call the inherited method for bookeeping inherited:HitItem(itemIndex, x, y); if x > selectIndent then begin // get a temporary cursor based on the cursor used // by soup overview local tCursor := cursor:Clone(); // move it to the selected item tCursor:Move(itemIndex) ; // move the inherited cursor to the selected entry myCursor:Goto(tCursor:Entry()); // usually you will close the overview and switch to // some other view self:Close(); end; // otherwise, just let them check/uncheck // which is the default behaviorend
protoSoupOverview
?protoSoupOverview
. You can draw one in a viewDrawScript
as follows: // setup a cached shape for efficiency mySoupOverview.cachedLine := nil; mySoupOverview.viewSetupDoneScript := func() begin inherited:?viewSetupDoneScript(); local bounds := :LocalBox(); cachedLine := MakeRect(selectIndent - 2, 0, selectIndent - 1, bounds.bottom); end; mySoupOverview.viewDrawScript := func() begin // MUST call inherited script inherited:?viewDrawScript(); :DrawShape(cachedLine, {penPattern: vfNone, fillPattern: vfGray}); end;
ValidationFrame
to validate and edit entries in a protoListPicker
. When I edit certains slots I get an error that a path failed. All the failures occur on items that are nested frames in my soup entry. What is going on?validationFrame
in your pickerDef, even if you have no nested entries. Instead, you can provide your own validation mechanism and editors:Validate
method in your picker definitionOpenEditor
method in your picker definitionpickerDef.Validate(nameRef, pathArray)
- nameRef to validate
nameRef
pathArray
- array of paths to validate in the nameRef
returns an array of paths that failed, or an empty array
Validate each path in pathArray
in the given nameRef. Accumulate a list of paths that are not valid and return them.
The following example assumes that pickerDef.ValidateName
and pickerDef.ValidatePager
have been implemented:
pickerDef.Validate := func(nameRef, pathArray)begin // keep track of any paths that fail local failedPaths := []; foreach index, path in pathArray do begin if path = 'name then begin // check if name validation fails if NOT :ValidateName(nameRef) then // if so, add it to array of failures AddArraySlot(failedPaths, path); end; else begin if NOT :ValidatePager(nameRef) then AddArraySlot(failedPaths, path); end; end; // return failed paths or empty array failedPaths;end; pickerDef.OpenEditor(tapInfo, context, why)
The arguments and return value are as per
OpenDefaultEditor
. However, you need to use this instead of DefaultOpenEditor
.
pickerDef.OpenEditor := func(tapInfo, context, why)begin local valid = :Validate(tapInfo.nameRef, tapInfo.editPaths) ; if (Length(valid) > 0) then // if not valid, open the editor // NOTE: returns the edit slip that is opened GetLayout("editor.t"):new(tapInfo.nameRef, tapInfo.editPaths, why, self, 'EditDone, context); else begin // the item is valid, so just toggle the selection context:Tapped('toggle); nil; // Return <nil>. end;..end;
The example above assumes that the layout "editor.t" has a
New
method that will open the editor and return the associated View.
The editor can be designed to fit your data. However, we suggest that you use a protoFloatNGo
that is a child of the root view created with the BuildContext
function. You are also likely to need a callback to the pickderDef so it can appropriately update the edited or new item. Finally, your editor will need to update your data soup uing an "Xmit" soup method so that the listPicker will update.
In the OpenEditor
example above, the last three arguments are used by the editor to send a callback to the pickerDef from the viewQuitScript
. The design of the callback function is up to you, here is an example:
pickerDef.EditDone := func(nameRef, context)begin local valid = :Validate(tapInfo.nameRef, tapInfo.editPaths) ; if (Length(valid) > 0) then begin // Something failed. Try and revert back to original if NOT :ValidatePager(nameRef) AND self.('[pathExpr: savedPagerValue, nameRef]) = nameRef then nameRef.pager := savedPagerValue.pager; context:Tapped(nil); // Remove the checkmark end; else // The nameRef is valid, so select it. context:Tapped('select); // Clear the saved value for next time. savedPagerValue := nil; end;
fixedHeight
slot. When I bring up the picker, it is not tall enough to display all the items. Worse, I cannot scroll to the extra items. What is going on?fixedHeight
slot is used for two separate things. Any given pick item can use the fixedHeight
slot to specify a different height. This works fine.fixedHeight
slot of the first pick item (in other words, pickItems[0]
) if it exists. It is as if the following code executes:local itemHeight := kDefaultItemHeight;if pickItems[0].fixedHeight then itemHeight := pickItems[0].fixedHeight;local totalHeight := itemHeight * Length(pickItems);
This total height is used to figure out if scrolling is required. As you can see, this can cause problems if your first item is not the tallest one. The solution is to make sure the first item in your
pickItems
array has a fixedHeight
slot that is sufficiently large to make scrolling work correctly. This may be fixed in future revisions of the NewtonOS.
Note that there will be similar problems if your pick items contain icons. The system will use the default height unless you specify a fixedHeight
slot in your first item. The default height is not tall enough for most icons. In other words, if you have icons in your pick items, you must have a fixedHeight
slot in the first item that is set to the height of your icon.
protoTextList
but they do not appear. How do I get columns?protoTextList
is based on a simple text view which does not support tabs. If you want scrolling selectable columns you can use shapes to represent the rows. If you need finer control, use the LayoutTable
view method.protoNumberPicker
for input. (or) I have used protoNumberPicker
and have encountered a bug/misfeature/problem. What should I use?protoNumberPicker
has several instabilities and bugs. We recommend that you use the DTS sample code "protoNumberPicker_TDS". It provides all of the features of protoNumberPicker
with none of the bugs. It also provides additional functionality that is not in protoNumberPicker
. See the sample code for more detail.protoListPicker
, protoPeoplePicker
, protoPeoplePopup
, or protoAddressPicker
?protoListPicker
. That means that the particular class of nameRef you use must include single selection. In general, this requires creating your own subclass of the particular name reference class.protoListPicker
variant will view. That data definition will include the singleSelect
slot. As an example, suppose you want to use a protoPeoplePopup
that just picks individual people. You could use the following code to bring up a protoPeoplePopup
that only allowed selecting one individual at one time: // register the modified data definition RegDataDef('|nameref.people.single:SIG|, {_proto: GetDataDefs('|nameRef.people|), singleSelect: true}); // then pop the thing protoPeoplePopup:New('|nameref.people.single:SIG|,[],self,[]); // sometime later UnRegDataDef('|nameref.people.single:SIG|);
For other types of
protoListPickers
and classes, create the appropriate subclass. For example, a transport that uses protoAddressPicker
for emails might create a subclass of '|nameRef.email|
and put that subclass symbol in the class
slot of the protoAddressPicker
.
Since many people are likely to do this, you may cut down on code in your installScript
and removeScript
by registering your dataDef only for the duration of the picker. That would mean registering the class just before you pop the picker and unregistering after the picker has closed. You can use the pickActionScript
and pickCanceledScript
methods to be notified when to unregister the dataDef.
protoListPicker
?viewFont
slot in the protoListPicker
itself and have that work (just like you can set viewLineSpacing
slot now). In the meantime, you need a piece of workaround code. Warning: you must set the viewFont of the listPicker AND include this workaround code in the viewSetupDoneScript
:func() begin if listBase exists and listBase then SetValue(listBase, 'viewFont, viewFont) ; inherited:?viewSetupDoneScript(); end;
This will set the
viewFont
slot of the listBase
view to the viewFont
of the protoListPicker
. You cannot rely on the listbase view always being there, hence the test for its existence.
Note that you can use the same code to modify the lineHeight
slot of the listPicker. Just substitute lineHeight
for viewFont
in the code snippet. The one caveat is that the lineHeight
must be at least 13 pixels.
selected
array of a protoListPicker
, it throws a -48402
error. How do I preselect items?protoSoupOverview
?selected
slot:selected
- Required. Initially set to nil
; it is modified by protoSoupOverview
as the user selects and deselects overview items.nil
or the empty array if there is no selection. For example: [[alias: NIL, 66282812, 84, "Names"], [alias: NIL, 66282812, 85, "Names"]]
protoTextList
after it is displayed. I add an item and scroll to highlight that item. However, the state of the scroll arrows does not correctly get updated. Sometimes it will indicate that there are more items to scroll when it is really at the end of the list.protoTextList
to reset the scroll distance when you update the listItems array. The workaround is to always scroll the list to the top before calling SetupList
when you add items. Then you can scroll the list to where you want it. Note that this workaround is safe to use in Newton 2.1 OS as well. In other words, if you are adding items to a protoTextList
, use this workaround unless your application is Newton 2.1 OS-specific. This method will add a single item to the protoTextList
, set the highlighted item to the new item and scroll if required. It will also make sure the item is unique. AddListItem := func(newItem) begin // Insert the item if not already in Array local index := BInsert(listItems, item, '|str<|, nil, true); // item must be in the array and index will point to the item. if NOT index then begin :Notify(kNotifyAlert, kAppName, "Duplicate entry."); return nil; end; // workaround a bug in 2.0 that causes the // scroll arrows to get out of sync // do this by scrolling to the top :DoScrollScript(-viewOriginY) ; self:SetUpList(); // Setting the selection slot will highlight the item selection := index; // scroll to show the new item if index >= viewLines then :DoScrollScript((index - viewLines + 1) * lineHeight) ; self:RedoChildren(); return true; end ;
protoPicker
that I reuse in my application. Sometimes the picker will be blank when I open it. What's happening?ProtoPicker
does not correctly reset the viewOriginY
slot if you display a list that requires scrolling, and then display one that does not require scrolling. The solution is to manually reset the viewOriginY
slot to 0 if you dynamically change the contents of the picker.protoPeoplePicker
displayed names as "last, first", but in Newton 2.1 OS it displays "first last". How can I make protoPeoplePicker
display the original way?nameRefDataDef
for people that will display the name in "last, first" format. The good news is that this workaround will work on both Newton 2.0 and Newton 2.1. The basic steps are:nameRefDataDef
that does the right thingdataClass
slot of your peoplePicker
// create a unique symbol for the the data def DefineGlobalConstant('kMyDataDefSym, Intern("nameRef.people.lastFirst:" & kAppSymbol)) ; DefineGlobalConstant('kMyGetFunc, func(item, fieldPath, format) begin // if this is a person, not a company, modify stuff local entry := EntryFromObj(item) ; if fieldPath = 'name AND format = 'text AND entry AND IsFrame(entry) AND ClassOf(entry) = 'person then begin local nameFrame := entry.name ; if nameFrame AND nameFrame.first AND nameFrame.last then return nameFrame.last & ", " & nameFrame.first ; else return inherited:Get(item, fieldPath, format) ; end else return inherited:Get(item, fieldPath, format) ; end ) ;
Put this code into the viewSetupFormScript
of the base view of your application:
// register my modified people data def RegDataDef(kMyDataDefSym, {_proto: GetDataDefs('|nameRef.people|), Get: kMyGetFunc}) ;
Put this code into the viewQuitScript
of the base view of your application:
// unregister my modified people data def UnRegDataDef(kMyDataDefSym) ;
Use the kMyDataDefSym
constant as the value for the dataClass
slot of your protoPeoplePicker
or protoPeoplePopup
protoCountryTextPicker
or default state for protoUSstatesTextPicker
?'default
to the params
frame. The slot must contain the name of the country (protoCountryTextPicker
) or state (protoUSstatesTextPicker
) as a string. For example, if you wanted the protoCountryTextPicker
to default to Canada, you could do the following in the viewSetupFormScript
: // get a writeable copy of the params frame self.params := Clone(params); params.default := "Canada";
RegGlobalKeyboard
function.GetUserSettings
, SetDefaultUserSettings
and SetUserSettings
allow you to manipulate recognition-related user preference data. These functions can allow an application to keep and manage recognition settings for multiple users. These functions only manage information about the recognition settings, and no other user preference settings. GetUserSettings()
This function returns a frame of the current user recognition settings; this frame is the argument for
SetUserSettings
. Do not modify the frame this function returns. Do not rely on any values, as the frame may change in future releases.
SetDefaultUserSettings()
This function sets recognition-related user preference settings to default values.
SetUserSettings(savedSettings)
savedSettings - Recognition preferences frame returned by
GetUserSettings
.
Sets user preferences for recognition as specified.
local correctView := GetRoot().correct;if correctView and (GetCaretBox() or GetHiliteOffsets()) then correctView:Open();
Note: An older version of this Q&A (from 12/8/95) showed using
GetKeyView
as a test to make sure a correctable view was the key view. With the changes to the OS with the Newton 2.1 release that allow any view to be a key view, this is no longer a reliable test. The corrector will fail to open (generating a -48204 "bad path" error) if the key view does not support the caret or a selection. Calling GetCaretBox
and GetHiliteOffsets
is a more reliable test to see if a correctable view is available.
GetNamedResource(..., 'picture)
routine, you can use PICT resources to be drawn in clPictureViews. MacOS PICT resources often contain multiple opcodes (instructions). For single-opcode PICTs, compression is done for the whole picture. You can check Inside Macintosh documentation for specifications of the PICT format. If you are using very large bitmaps which you will print, you should use PICT resources composed of many smaller 'bitmap copy' opcodes because they will print much faster and more reliably on PostScript printers. This is because very large PICT opcodes printed to LaserWriters must be decompressed on the printer. The printer's decompression buffer is sometimes too small if the opcodes represent large bitmaps. Check your MacOS graphics application documentation for more information on segmenting your large PICTs into smaller pieces. For some applications, you might have two versions of the PICTs, one for displaying (using GetPictAsBits
for faster screen drawing), and a large tiled PICT for printing.MonacoTest
". That includes a font which will print as the monospaced Courier font.target
variable (it will contain the "body" of the data sent; don't use fields
.body). Note that if mulitiple items are sent, the value of target
will change as the print format iterates over the list. Try to put the real "data" for the routing in the target using the view method GetTargetInfo
.GetRoot().(yourAppSymbol).theSlot
.OpenRoutingSlip
. Create a new item with the transport's NewItem
method and add routing information such as the recipient information in the toRef
slot. For the call slip, the transport symbol will be '|phoneHome:Newton|
, but this approach will work for other transports. (For transports other than the call transports, you will also provide the data to route in the item.body
slot.)toRef
slot in the item frame should contain an array of recipients in the form of nameRefs, which are the objects returned from protoPeoplePicker
and other protoListPicker
-based choosers. Each nameRef can be created from one of two forms: a cardfile soup entry, or just a frame of data with minimal slots. (The required slots vary depending on the transport. For instance, the current call transport requires only phone, name, and country.) entry := myCursor:Entry();
2. Create your own pseudo-entry:
entry := { phone:"408 555 1234", name: {first: "Glagly", last: "Wigout"}, country: "UK", };
Make the entry into a "nameRef" using the nameRef's registered datadef -- an object which describes how to manipulate nameRefs of a specific class. Note that every transport stores its preferred nameRef class symbol in its transport.addressingClass
slot. (Examples are '|nameRef.phone|
and '|nameRef.email|
).
local class := '|nameRef.phone|;local nameRef := GetDataDefs(class):MakeNameRef(myData, class);
Setting up the targetInfo Frame
Your GetTargetInfo
view method should return a targetInfo
frame, consisting of target
and targetView
slots. Alternatively, you can create a frame consisting of these slots and pass it to OpenRoutingSlip
. As a workaround to a ROM bug, you must also supply an appSymbol
slot in the targetInfo
frame containing your appSymbol. Note that targetInfo.target
could be a multiple item target (see the CreateTargetCursor
documentation for more info.)
Opening The Slip
You can use OpenRoutingSlip
to open the slip after setting up slots such as toRef
and cc
within the item. You can use code such as the following:
/* example using Call Transport */local item, entry, class, nameRef; // just for testing, get an Name...entry := GetUnionSoup("Names"):Query(nil):Entry(); item := TransportNotify('|phoneHome:Newton|, 'NewItem, [nil]);if item = 'noTransport or not item then return 'noTransport; class := '|nameRef.phone|;nameRef := GetDataDefs(class):MakeNameRef(entry, class);item.toRef := [nameRef];targetInfo := { targetView: getroot(), target: {}/* for non-CALL transports, add your data here! */, appsymbol: kAppSymbol }; // returns view (succeeded), or fails: nil or 'skipErrorMessageOpenRoutingSlip(item, targetInfo);
CreateTargetCursor
function.GetTargetInfo
method like: func(reason) begin local t := CreateTargetCursor(kDataClassSymbol, myItemArray); local tv := base; // the targetView return {target: t, targetView: tv}; end;
The first argument to CreateTargetCursor
is used as the class of the target, which is used to determine what formats and transports are available. You must register formats on that data class symbol in your part's InstallScript
function.
The item array passed to CreateTargetCursor
can contain any items, including soup entries or soup entry aliases. If you include soup entry aliases, they will automatically be resolved when accessing items using the GetTargetCursor
function.
Print formats that have their usesCursors
slot set to nil
will automatically print items on separate pages -- print formats must use the target variable to image the current item. To print multiple items, set the format usesCursors
slot to true
and use GetTargetCursor(target, nil)
to navigate through the items.
If either the format (the usesCursors
slot) or the transport (the allowsBodyCursors
slot) does not support cursors, the system will automatically split the items into separate Out Box items.
protoPrintFormat:viewSetupFormScript()
?viewSetupFormScript
.self:LocalBox()
and get the correct page size. Note that you cannot rely on the protoPrintFormat.viewBounds
slot value. To position subviews within the print format centered or "full" width or height, use view justifications like centered, right, and full, or use theEnclosingView:LocalBox()
to determine the exact size of the enclosing view.CreateTargetCursor('newtOverview, myItemArray)
in my application to simplify my code which handles overviews. Why would my print format throw an exception when I use this method?'newtOverview
symbol as your data class with CreateTargetCursor
. The biggest limitation is that it requires you to support exactly the set of of datatypes: ['frame, 'text, 'view].
In other words, you must register a protoFrameFormat
(by default, it handles 'frame
and 'text
dataTypes) and a protoPrintFormat
. However, there are two other limitations not mentioned in the final documentation: the system does not guarantee that it will call your print format's formatInitScript
method or a format's SetupItem
method.viewSetupFormScript
(or other code in the print format) assumed that the formatInitScript
has been called, it could cause errors and/or exceptions. The workaround to this would be to set a flag in the formatInitScript
; if it was not set at the beginning of viewSetupFormScript
, send your format the formatInitScript
message. Other problems could occur with SetupItem
, but you'd probably not see any errors or exceptions until you tried to beam/mail a frame to another device and then tried to Put Away the item.CreateTargetCursor
to prepare a "multiple item target", you may be able to use this special 'newtOverview
symbol as your data class. If your application prints every item on separate pages (in other words, not multiple items on one page) and you want to split beam and mail items into separate items in the Out Box, this might be useful to you. For more information, see the Newton Programmers Guide (not reference) in the Routing chapter "Using the Built-in Overview Data Class" section and the "Move It!" article in the Newton Technology Journal 2.02. Also, check out the MultiRoute DTS sample.'frame
dataTypes. Why is Beam available in my Action picker in Newton 2.1 OS?'text
dataType. If any routing formats for your data supports text export (a format.dataTypes
array includes 'text
), Beam will be available. Unfortunately, there is a bug in current Newton 2.1 OS devices such that Beam does not convert the target to text before sending it. Transports that support sending text should use the kItemToTextFunc
function (in the Newton 2.x platform files), and that function calls the format's TextScript
to convert the item to text. Since Beam does not do this, this gives the appearance that the item is being sent as 'frame
, a dataType that may not be supported by your application's routing formats.'frame
datatype. In your routing format, add 'frame
to the dataTypes
slot. This will allow all 'frame
transports, including mail transports that can send attachments, to send mail with your application. This will allow your application to avoid text-specific bugs in Beam. For the best user interface, we recommend that you write stationery for your data so that users can view the item in the In Box or Out Box. See the Newton Programmers Guide and Reference for more information about writing and registering stationery. Note that you can test your application with Put Away, using the built-in Beam transport as well as the DTS sample "Archive Transport".format:SetupItem(...)
method. If you don't support overviews or other mechanisms that use multiple item targets, change item.body
to be a new frame of the form {text: "the converted item to text", class: 'text}
. Note that this format should not also support the 'frame
dataType because you are destructively modifying the item.SetupItem
method is called. You can use the code like the following in your SetupItem
format after calling the inherited method:// get a 'cursor' to iterate over the items.// Note: this still returns a 'cursor' even if item.body wasn't reallocal cursor := GetTargetCursor(item.body, nil);local newArray := [];local entry := cursor:entry();while (entry) do begin // convert item to text in whatever way you normally do it... // For instance, you might call your format's textscript... entry := { text: "blah blah" && entry.a && entry.b, class: 'text }; AddArraySlot(newArray, entry); entry := cursor:Next(); end; item.body := CreateTargetCursor(classof(item.body), newArray); // remember to return 'item' from SetupItemitem
You might be wondering if you could route the
'frame
data by hiding the data in extra slots in item.body
. If you did that, the item would be much larger than necessary to route 'frame
data, and will not be Put Away properly because the 'class
slot is set to 'text
, not your original data class). If you actually want to support 'text
and 'frame
dataTypes, use a single protoFrameFormat
with dataTypes ['frame, 'text]
and do not convert the item.body
as illustrated above. (This is actually recommendation #1 above).
Note that 'text
stationery must be registered in order to view the item in the In Box and Out Box. Such stationery is not necessarily installed on the receiving device. Some mail transport packages may have installed 'text
stationery, but you may choose not to rely on this. If you are interested in writing text stationery, see the DTS sample "MinMail" and the Newton Programmers Guide and Reference for more information about writing and registering stationery.
:LocalBox()
in its viewSetupFormScript
, viewSetupChildrenScript
, or viewSetupDoneScript
. This is only accurate during the current print job to the printer or fax driver. You cannot determine this size during Print/Fax preview, nor during the print format's formatInitScript
, nor in your application's on-screen views.formatInitScript
can accurately determine the page height. This special format method was designed to allow time-intensive code to execute before the print job begins. For instance, fax timeouts might be less likely if some data were processed before fax connection. However, there is no supported API to find the actual final printable area that you will have when the system opens the print format view and sends the viewSetupFormScript
message. Although you may be tempted to access undocumented slots which contain information about printer and page settings, this is both unsupported and results in unreliable bounds. (For instance, the undocumented fax cover page information affects the printable area but it's not stored in the printer nor page size structures.)paperSize
and paperSizes
. You cannot use these to reliably determine the printable area, nor can you create new paperSizes
. See the Newton Programmer's Reference for more details.:LocalBox()
in the print format viewSetupFormScript
, viewSetupChildrenScript
, or viewSetupDoneScript
. (Or, use the print format pageHeight
and pageWidth
slots in viewSetupChildrenScript
or later.) Also, add new child views with appropriate center/full/right/relative justification to take advantage of varying page sizes. This will allow your application to work with any paper size, in any printer, and in any locale.modeXOR
and modeNot
will not work.modeBic
) for drawing the text.userName
slot. This slot contains a string that will show up in the alert sound picker.RegSound(soundSymbol, alertSoundFrame)
- a unique symbol identifying the sound that incorporates your signature
soundSymbol
alertSoundFrame
- see above
Registers the sound in alertSoundFrame
with the alert sound picker.
UnRegSound(soundSymbol)
- symbol of the sound to unregister
soundSymbol
Unregisters the sound frame that was registered with the symbol specified by soundSymbol
.
SoundList()
Returns an array of currently registered alert sounds that can be used to construct a popup menu. It returns an array of frames, such that each frame is of the format:
{item: soundName, soundSymbol: theSoundSymbol}
GetRegisteredSound(soundSymbol)
- symbol of the sound to return
soundSymbol
Returns a sound frame that can be passed to the sound playing functions (for example, the global function PlaySound
). soundSymbol
is the symbol used to register the sound.
height
slot, flush the data, and then do a re-target. For instance, you might have the following method in your stationery: DoResize: func( newHeight ) begin target.height := newHeight; :FlushData(); :DoRetarget();
end;
RegisterViewDef
on Newton 2.0 OS, I get the "grip of death" alert ("The package 'MyApp' still needs the card you removed...") when the card the viewDef is on is removed. I don't get the grip of death alert when using Newton 2.1 OS. How can I keep the grip of death alert from appearing?EnsureInternal
the second argument to the RegisterViewDef
global function. The RegisterViewDef
global function was changed in Newton 2.1 OS to automatically EnsureInternal
the second argument.EnsureInternal
-ing something that has been EnsureInternal
-ed is a very fast operation so you don't need to worry about checking which platform you are running on.ROM_compatibleFinder
in Newton 2.0, the overview of found items contains checkboxes for each item, allowing the user to attempt to route the found items. Since my found items are not soup items, various exceptions are thrown. How can I prevent the checkboxes?SelectItem
slot to the result frame, set to nil
. AddArraySlot(results, {_proto: ROM_compatibleFinder, owner: self, title: mytitle, SelectItem: nil, // prevents checkboxes items: myresults});
The second case is more complex. The problem is that there are many variants. The best strategy is to override the appropriate methods in your finder to gain control at appropriate points. This may be as simple of overriding
Delete
to behave correctly, or as complex as replacing GetTarget
and adding appropriate layouts. See the Newton DTS Q&A "Creating Custom Finders" for more information.
ROM_soupFinder
is not appropriate, but ROM_compatibleFinder
seems to throw many exceptions. Which should I use?soupFinder
based item you could do something like:DefConst('kMySoupFinder, { _proto: ROM_soupFinder, Delete: func() begin print("About to delete " & Length(selected) && "items") ; inherited:Delete() ; end,}) ;
Most of these routines are only callable by your code. They should not be overwritten. Those routines that can be safely overriden are specified.
Some of methods and slots are common to both types of finders:
finder.selected
An array of selected items stored in an internal format. All you can do with this array is figure out the number of selected items by taking the Length of this array.
finder:Count()
Returns an integer with the total number of found items.
finder:ReSync()
Resets the finder to the first item.
finder:ShowFoundItem(item)
Displays the item passed. item is an overview item that resides in the
overview's items array.
finder:ShowOrdinalItem(ordinal)
Display an item based on the symbol or integer passed in ordinal:
'first
- the first found item
'prev
- the previous item
'next
- the next item
<an-integer>
- display the nth item based on the integer.
Under no circumstances should you call or override:
finder:MakeFoundItem
finder:AddFoundItems
ROM_SoupFinder
SoupFinder has the following methods and slots:
All the documented items from the simple use of soupFinder as documented in the Newton Programmer's Guide 2.0.
soupFinder:Reset()
Resets the soupFinder cursor to the first found entry. In general, you
should use the ReSync method to reset a finder.
soupFinder:ZeroOneOrMore()
Returns 0 if no found entries, 1 if one found entry or another number
for more than one entry.
soupFinder:ShowEntry(entry)
causes the finding application to display entry. This may involve
opening the application and moving it to that item.
This does not close the findOverview.
soupFinder:SelectItem(item)
mark the item as selected.
If this method is set to nil
in the soupFinder proto, items will not have a checkbox in front of them (not selectable).
soupFinder:IsSelected(item)
Returns true if the item is selected.
soupFinder:ForEachSelected(callback)
Calls callback function with each selected item. The callback function has one argument, the entry from the soup cursor.
soupFinder:FileAndMove(labelsChanged, newLabel,storeChanged, newStore)
File and/or move the selected items.
newLabel
is the new label if and only if labelsChanged
is true.
is the new store if and only if
newStore storeChanged
is true.
Developers can override this, though they may want to call the inherited routine to do that actual work. Note that FileAndMove
can be called even if no items are selected. If you override this method you MUST check if there are selected items by doing:
if selected then // do the work
soupFinder:FileAs(labels)
Deprecated. Do not use.
soupFinder:MoveTo(newStore)
Deprecated. Do not use.
soupFinder:Delete()
Deletes all selected items from read/write stores.
Developer can override. Note: if you override this, the crumple effect will
still happen. There is no way to prevent the ability to delete the items or
prevent the crumple effect at this time.
soupFinder:GetTarget()
Returns a cursor used by routing.
The following methods should not be called or modified:
soupFinder.MakeFoundItemsoupFinder.AddFoundItems
ROM_CompatibleFinder
compatibleFinder:ShowFakeEntry(index)
Show the
index
'th item from the found items. Note that items will likely be an array of the found items.
ShowFakeEntry
should behave just like ShowFoundItem
. In other words, it should open the application then send a ShowFoundItem
to the application.
compatibleFinder:ConvertToSoupEntry(item)
Return a soup entry that corresponds to the item. item is an item from the found items array.
The following methods are defined to be the same as the soupFinder:
FileAs, MoveTo, Delete, IsSelected, SelectItem, ForEachSelected, GetTarget, FileAndMove
Note that this causes problems in some cases: most notably, the ForEachSelected
call is expected to return an array of soup entries. The chances are you will need to override most of those methods. See soupFinder
for a description of what the methods are supposed to do.
nil
value in the frame returned by the BatteryStatus
global function. How do I interpret these values?nil
is returned if the underlying hardware cannot determine the correct information. Some hardware is limited in the amount of information that it can return. Future hardware may fill in more slots with authoritative non-nil values.AddFolder
and RemoveFolder
to modify the folder set for a given application.AddFolder(newFolderStr, appSymbol)
- string, the name of the new folder
newFolderStr
appSymbol
- symbol, application for local folder
result
- symbol, the folder symbol of the newly added folder.
AddFolder
takes a folder name and creates a new folder for the application.
AddFolder
returns the symbol representing the tag value for the new folder. Please note that the symbol may be different from the value returned by using Intern()
on the string. In particular, folder names with non-ASCII folders are supported. If a folder with the name already exists, the symbol for the pre-existing folder is returned and a new folder is not created.
There is a limit on the number of unique folders an application can support. If the limit is exceeded, AddFolder
returns NIL
and a new folder is not added. With the Newton 2.0 OS, the current limit is twelve global folders and twelve local folders.
RemoveFolder(folderSym, appSymbol)
- symbol, the folder symbol of the folder to remove
folderSym
appSymbol
- symbol, the application for which to remove the folder
result
- undefined; do not rely on the return value of this function.
RemoveFolder
can be used to remove a folder from the available list for an application. If items exist in a folder that is removed, the only way users can see the items is by selecting "All Items" from the folder list.
protoStatusTemplate
-based view and am trying to rename the primary button through the protoStatusTemplate
's setup frame. After doing this, I get an exception when I tap on the renamed button. What am I doing wrong?protoStatusTemplate
which will cause the primary button to function incorrectly if you do not include a buttonClickScript
in the setup frame.primary
slot of the values frame of the setup, the primary button uses the text
slot and the buttonClickScript
slot of that frame to initalize itself. Unfortunately, it does not check to see if either of those slots exist before trying to use them. The result is that an exception is thrown when you tap the button.buttonClickScript
to the primary frame. From that method you will typically call your base view's CancelRequest
method.// Add a buttonClickScript method which just calls the application's CancelRequest method.local viewSetValues := { primary: { text: "Stop", buttonClickScript: func() GetRoot().(kAppSymbol):CancelRequest('userCancel) } }; local viewSet := { appSymbol: kAppSymbol, name: "The Name", values: viewSetValues }; // Setup the status templatestatusView:ViewSet( viewSet );
protoPhoneExpando
under Newton 2.0 OS. Something is going wrong in the setup1
method. Is this a known bug?protoPhoneExpando
(and the entire expando user interface) have been deprecated in the Newton 2.0 OS, and are only supported for backward compatibility. If possible, you should redesign your application to avoid the expandos.setup1
and setup2
messages to the template in the lines
array. These methods in protoPhoneExpando
rely on information that isn't created until the view is actually opened.labelCommands
slot in the template which has an array of one element, that element being the label you want to appear in the phone line. For example: labelCommands: ["phone"]
.protoPhoneExpando
doesn't use the phoneIndex
feature. If it does, you'll have problems that are harder to work around.clEditView
, and clEditViews
can contain pictures (in addition to ink, polygons, and text) in the Newton 2.0 OS.'viewChildren
slot of a clEditView
to create editable items. 'para
templates are text and ink text, 'poly
templates are drawings and sketch ink, and 'pict
templates are images.clEditView
, you need to create an appropriate template and then add it to the viewChildren
array (and open the view or call RedoChildren
) or use the AddView
method to add it to an existing view (then Dirty
the view.) See the item "Adding Editable Text to a clEditView" elsewhere in the Q&As for details.'pict
items needs to contain these slots:viewStationery
: Must have the symbol 'pict
viewBounds
: A bounds frame, like RelBounds(0,0,40,40)
icon
: A bitmap frame, see clPictureView
docsclPictureView
view class.clParagraphView
that's inside a smaller view and be able to scroll back and forth. When I try this, it always wraps at the bounds of the parent. How can I create a horizontal scrolling text view?clEditView
, leaving the rest of it off screen and unselectable.viewBounds
of the clParagraphView
are modified during creation of the view so that the view's right edge is aligned with the parent's right edge. After that, wrapping is automatic.viewFlag
vReadOnly
is set, 2) making sure vCalculateBounds
and vGesturesAllowed
, are off, and 3) not using tabs
or styles
. Lightweight text views are not editable, but you can use SetValue
to change their text
slots dynamically.clParagraphView
or if tabs or styles are required, there is another workaround. The code to check for clipping only looks one or two levels up the parent chain, so you could nest the paragraph in a couple of otherwise useless views which were large enough to prevent clipping, and let the clipping happen several layers up the parent chain.clParagraphView
view class:viewKeyDownScript
message is sent when a key is pressed.viewKeyUpScript
message is sent when a key is released.ViewKeyUpScript
and ViewKeyDownScript
are currently called using parent inheritance. Do not rely on this behavior: it may change in future ROMs.nil
. The default action for ViewKeyDownScript
is usually to insert the character into the paragraph. (There may be other default actions in the future.) If you return a non-nil value, the default action will not occur.vSingleKeyStrokes
flag in the textFlags
slot of your view for the system to send the ViewKeyDownScript
or ViewKeyUpScript
message for every key stroke. If you do not specify vSingleKeyStrokes
, keyboard input may be dropped if a lot of key strokes are coming in. ViewKeyDownScript(char, flags)
This message is sent to the key view when the user presses down on a keyboard key. This applies to a hardware keyboard or an on-screen keyboard.
char
The character that was entered on the keyboard. Note that if a modifier key is the only key pressed (for example, the Shift key), this value will be 0.
flags
An integer that specifies which modifier keys were pressed, the unmodified key value, and the keycode. The modifier key constants are shown in the section "Keyboard Modifier Keys".
ViewKeyUpScript
ViewKeyUpScript(char, flags)
This message is sent to the key view whenever the user releases a keyboard key that was depressed. This applies to a hardware keyboard or an on-screen keyboard.
char
The character that was entered on the keyboard. Note that if a modifier key is the only key pressed (for example, the Shift key), this value will be 0.
flags
An integer that specifies which modifier keys were pressed, the unmodified key value, and the keycode. The modifier key constants are shown in the section "Keyboard Modifier Keys".
Keyboard Flags Integer
Bits Description
0 to 7 The keycode.
8 to 23 Original keycode. The 16-bit character that would result if none of the
modifier keys were pressed.
24 Indicates that the key was from an on-screen keyboard. (kIsSoftKeyboard)
25 Indicates that the Command key was in effect. (kCommandModifier)
26 Indicates that the Shift key was in effect. (kShiftModifier)
27 Indicates that the Caps Lock key was in effect. (kCapsLockModifier)
28 Indicates that the Option key was in effect. (kOptionsModifier)
29 Indicates that the Control key was in effect. (kControlModifier)
Keyboard Modifier Keys
You use the keyboard modifier key constants to determine which modifier keys were in effect when a keyboard event occurs.
Constant Value
kIsSoftKeyboard
(1 << 24)
kCommandModifier
(1 << 25)
kShiftModifier
(1 << 26)
kCapsLockModifier
(1 << 27)
kOptionsModifier
(1 << 28)
kControlModifier
(1 << 29)
protoKeyboard
-based keyboard to be open at the same time as other keyboards. When my keyboard opens, it seems like any other keyboard closes. How do I keep multiple keyboards open?protoKeyboard
-based view opens, it closes the last-opened protoKeyboard
-based view. However, you need not use protoKeyboard
.protoDragger
) and use the RegisterOpenKeyboard
view message to register the keyboard with the system. Using RegisterOpenKeyboard
will ensure that the caret is set up properly and allows you to track the caret changes with the viewCaretChangedScript
view message if desired.protoKeyboardButton
-based keyboard list. Is this possible?protoKeyboardButton
has a method called SetKeyboardList
that lets you do this. SetKeyboardList
takes two arguments. The first argument is an array of keyboard symbols to add to the list. The second argument is an array of keyboard symbols to remove from the list. Note that the keyboard symbols of the built-in keyboards are listed on pages pages 8-26 and 8-27 of the Newton Programmer's Guide.keyboardSymbol
.preallocatedContext
slot with the symbol of the keyboarduserName
slot with the name that will appear in the protoKeyboardButton
popupkeyboardSymbol
slot with your keyboard's symbolpreallocatedContext
slot and the keyboardSymbol
slot must be the same symbol. Note that the keyboardSymbol
slot is required, but the preallocatedContext
slot is additionally necessary to avoid exceptions on devices prior to Newton 2.1 OS.viewSetupDoneScript
of the protoKeyboardButton
-based view, send the button a SetKeyboardList
message with your keyboard's symbol. For instance, you might have the following viewSetupDoneScript
:viewSetupDoneScript: func() begin :SetKeyboardList( [kMyKeyboardSymbol], nil ); // Be sure to call the inherited viewSetupDoneScript method! inherited:?viewSetupDoneScript();
end;
If you want to dynamically change the keyboard list, you can also override the buttonClickScript
. You must first call SetKeyboardList
, then call the inherited buttonClickScript
.
All additions and subtractions are removed from the list when your protoKeyboardButton
-based view is closed.
ViewIntoBitmap
, or the global function GetPointsArray
, or a set of functions from the Recognition chapter, particularly GetStroke
and GetStrokePointsArray
.clKeyboardView
also finds the smallest key unit specified in the keyboard and uses this to constrain the final horizontal size. It calculates a minimal pixel size for the keyboard and makes sure that the final keyboard size is an integral multiple of this value. For example, if the smallest size is 10 pixels, then the final keyboard can be 10 pixels or 20 pixels, but not 15 pixels. If the view is 15 pixels, the keyboard will be 10 pixels. m = w * (1/s)
m - minimal sizew - width of the longest keyboard row in key unitss - numeric equivelent for smallest keyboard unit specified in the keyboard: (keyHUnit = 1, keyHHalf = 0.5, keyHQuarter = 0.25, keyHEighth = 0.125)
For the built-in ASCII keyboard in current ROMs, the longest row is 14 key units, the smallest key unit used is keyHQuarter
, so the minimal width for the ASCII keyboard is:
m = 14 * (1 / 0.25) = 14 * 4 = 56 pixels.
The keyboard will always be an integral multiple of 56 pixels in width. Note that 224 pixels is exactly 4 * 56. By changing the width to 223, the keyboard now becomes 168 pixels wide.
_DoCloseButton
method to your application's base view. The _DoCloseButton
method is called when a keyboard equivalent is used to close a view. This method takes no arguments and must return true
if you handled the close, or return nil
to let the system continue to search for a close box in other applications.BuildContext
. This guarantees that it will be searched for a close box before your application is searched.keyCommand
if all I have is the keyMessage
symbol?MatchKeyMessage
that will do what you want. However, the documentation was inadvertently left out of the current version of the Newton Programmers Guide for Newton 2.1 OS. The documentation should be: MatchKeyMessage(startView, keyMessage)
Finds the keyCommand
frame for the specified message starting with the specified view.
startView
- The view from which to start searching for the message
keyMessage
- A symbol for the command message that will be searched for. This must be the same message that is specified in the keyMessage
slot of the keyCommand
frame
return value - Either nil
or a keyCommand
frame
The MatchKeyMessage
function searches for the message using the same lookup rules that are used when the system handles a key command.
protoTransportHeader
-based view?addedHeight
slot. The height of the transport header will be increased by this amount.viewSetupFormScript
method of your protoTransportHeader
view. This works around a bug with protoTransportHeader:
self.stepChildren := SetUnion( self._proto.stepChildren, self._proto._proto.stepChildren, true );
Finally, use NTK as you normally would to create the child views.
nil
for the sendPrefs
, outboxPrefs
, or inboxPrefs
slots in my transport preferences template, opening the slip throws -48204. What is going wrong?sendPrefs
, outboxPrefs
, or inboxPrefs
in your preferences dialog to set those slot to nil
. Due to a bug in the cooresponding views for those preference items, -48204 is thrown when an attempt is made to open the views. This will be fixed in a future ROM.protoAddressPicker
from remembering the last people picked?protoAddressPicker
has a slot called useMemory
that was left out of the documentation. If this slot is set to nil
, the memory mechanism will be disabled.RemoveTempItems
when items are selected, the ReceiveRequest
message sometimes has bogus request arguments (for instance, the cause
is set to 'remote
). Is this a known bug?RemoveTempItems
. The call to RemoveTempItems
does not clear out the cache of selected items correctly. This will be fixed in a future version of the Newton OS.'remote
, assume that the cause slot is actually 'user
and act accordingly. If you receive a request with its 'cause
slot set to 'remote
and your transport's communications channel is connected then perform the appropriate action for receiving remote items.ItemCompleted
. Why is this problem occuring?ItemCompleted
. To work around this, you should check to make sure the folder exists before calling ItemCompleted
. If it does not exist, then set the transport's 'outboxFiling
preference to nil
. Here is a code example:// This code assumes that the current receiver (self) is your transportif NOT GetFolderStr( :GetConfig( 'outboxFiling ) ) then :SetConfig( 'outboxFiling, nil );
inboxFiling
is nil
.AutoPutAway
method. Second, the transport configuration slot 'dontAutoPutAway
must be set to nil
(or not be present). This configuration slot is new to Newton 2.1 OS and must contain the value nil or the value 'never
. If nil
, items will be put away automatically provided the application defined by the item's appSymbol
slot has the AutoPutAway
method. If 'never
, items will not be automatically put away regardless of whether the application defined by the item's appSymbol
slot implements the AutoPutaway
method.sprintf
-like function for formatting numbers called FormattedNumberStr
. The Newton Programmer's Guide 2.0 First Edition (beta) says this function is no longer supported. How do I format my numbers?FormattedNumberStr
. Here is the FormattedNumberStr
API that is supported. FormattedNumberStr
should be considered to have undefined results if passed arguments other than those specified here.FormattedNumberStr(number, formatString)
Returns a formatted string representation of a real number.
number
A real number.
formatString
A string specifying how the number should be formatted.
This function works similar to the C function sprintf
. The formatString
specifies how the real number should be formatted; that is, whether to use decimal or exponential notation and how many places to include after the decimal point. It accepts the following format specifiers:
%f
Use decimal notation (such as "123,456.789000").
%e
Use exponential notation (such as "1.234568e+05").
%E
Use exponential notation (such as "1.234568E+05").
You can also specify a period followed by a number after the % symbol to indicate how many places to show following the decimal point. ("%.3f"
yields "123,456.789"
for example.)
Note: FormattedNumberStr
uses the current values of GetLocale().numberFormat
to get the separator and decimal characters and settings. The example strings above are for the US English locale.
Known Problems
Other specifiers
Do not use other formatStrings
. Previous releases of the documentation listed %g
and %G
as supported specifiers. The behavior of these specifiers has changed with the Newton 2.0 OS. Given the similarities to the sprintf
function, it may occur to you to try other sprintf formatting characters. Specifiers other than above have an undefined result and should be considered undocumented and unsupported.
Large numbers
FormattedNumberStr
does not work properly for numbers larger than 1.0e24
. If the number is very large the function can cause the Newton device to hang.
Small numbers or long numbers
If more than 15 characters of output would be generated, for example because you are using %f
with large number or a large number of digits following the decimal, FormattedNumberStr
has undefined results, and can cause the Newton device to hang.
Rounding
FormattedNumberStr
does not guarantee which direction it will round. In the Newton 2.0 OS, it rounds half cases down rather than up or to an even digit. If you need a precisely rounded number you should use the math functions Ceiling
, Floor
, NearbyInt
, or Round
with suitable math.
Trailing decimals
In early releases of the Newton 1.0 OS, there was a bug in FormattedNumberStr
that caused a trailing decimal character to be added when zero decimal positions was specified. That is, FormattedNumberStr(3.0, "%.0f")
resulted in "3."
not "3"
. To properly test for and remove this unwanted extra character you must be sure to use the character specified in the Locale settings and not assume the decimal character will be a period.
Gestalt
function as follows:// define this somewhere in your project// until the platform file defines it (not in 1.2d2)constant kGestalt_BackLight := '[0x02000007, [struct, boolean], 1]; local isBacklight := Gestalt(kGestalt_BackLight); if isBacklight AND isBacklight[0] then // has a backlightelse // has not got one
Status of the backlight
To find the current state of the backlight, use the following function:
BackLightStatus()
return value = nil (backlight is off) or non-nil (backlight is on)
Changing backlight status
To turn the backlight on or off, use:
BackLight(state)
return value - unspecified
state - nil (turn backlight off) or non-nil (turn backlight on)
StrPos
global function to search for a ':' character, it finds other characters such as '.' or ';'. Isn't that a bug? How can I reliably search for these characters in any locale?StrPos
and many of the other string functions are case insensitive - they treat upper and lowercase letters as being identical. In other languages, characters such as accented letters may be considered as different cases of the base letter, so they are treated as identical as well. In the Newton OS model, the concepts 'same case' and 'same position in the sorting order' are not distinguished, so all cases of a letter will sort to the same position. Going backwards, all characters that sort identically are considered to be different cases of the same letter. Well, in the Swedish sort order, many punctuation characters are defined to sort to the same place, and so the case insensitive functions in the Newton device treat the characters as identical. Many special and punctuation characters are grouped this way, but perhaps the most surprising set is ? ¡ : , . ; ¿ and !, which all sort to the same position and so are treated as identical in Swedish by StrPos
and other case insensitive functions.CharPos
function instead of StrPos
.'gmt
slot of a city entry, which can be gotten with the GetCityEntry
global function. See the "Built In Apps and System Data" chapter of the Newton Programmers Guide for details. Note that the docs incorrectly say the gmt
slot contains the offset in minutes, when it is actually specified in seconds. The current location is available in the 'location
slot of the user configuration frame. Use GetUserConfig('location)
to access it. The global function LocalTime
can be used to convert a time to the local time in a distant city.LocalTime
to compute the delta between the current city and the GMT city, then add the delta to the given GMT time. LocalTime
can be used directly to go the other way--getting the GMT time from the local time.LocalTime
(time
, where
)time
- a time in minutes in the local (Newton device) zone, for example as returned from the Time
functionwhere
- a city entry, as returned from GetCityEntry
result
- a time in minutes in the where city, adjusted as necessary for time zone and daylight savings.LocalTime
tells you the local time for the distant city, given a time in the current city. For example, to find out the time in Tokyo: Date(LocalTime(time(), GetCityEntry("Tokyo")[0])) #C427171 {year: 1997, month: 2, Date: 22, dayOfWeek: 6, hour: 8, minute: 1, second: 0, daysInMonth: 28}
Because the Newton OS doesn't have time zones, it can't keep track of daylight savings time by changing zones (for example, from Pacific Standard Time to Pacific Daylight Time). Instead, it uses a bunch of rules that tell it when to set the time ahead or back, and by how much. The global function DSTOffset
can be used to find out how much these daylight savings time rules have adjusted a given time for a given city.
DSTOffset(
time
,
where
)
time
- a time in minutes in the where city
where
- a city entry, as returned from GetCityEntry
result
- an integer, number of minutes that daylight savings adjusted that time in that city.
DSTOffset
tells you what the daylight savings component is of a given time in a given location. This component would need to be subtracted from the result of the global function Time
to get a non-daylight-adjusted time for the current location.
// it's currently 2:52 PM on 3/4/97, no DST adjustment DSTOffset(Time(), GetCityEntry("Cupertino")[0]); #0 0 // but during the summer, DST causes the clocks to "spring forward" an hour. DSTOffset(StringToDate("6/6/97 12:34"), GetCityEntry("Cupertino")[0]); #F0 60
Sqrt
with a negative number on the Newton, or use Compile
in the NTK Inspector, I get a strange result. However, if I just type sqrt(-2)
into the listener I get a different strange result. What's going on? call compile("sqrt(-2)") with () #4412F2D -1.79769e+308 sqrt(-2) #440DE05 1.00000e+999
A: There is a floating point library bug in Sqrt
on the Newton OS. When passed a negative number, the large positive value is returned instead of a not-a-number value. You can work around it using Pow(x, 0.5)
instead of Sqrt(x)
if there is no way to guarantee that the value passed to Sqrt
is non-negative, or simply check and see if the argument is less than 0 and return a not-a-number constant.
The reason sqrt(-2)
works differently when you type it into the NTK Inspector is because of a compiler process known as constant folding. Sqrt
can be evaluated at compile time if you pass it a constant argument. So what's really happening is that NTK is evaluating the Sqrt
function during the compile phase and passing the resulting floating point number (or rather, not-a-number) to the Newton device where it's promptly returned. An NTK real number formatting limitation displays non-a-number values and infinities as 1.00000e+999
rather than as some other string. You can use IsNAN
to determine if a real number is a not-a-number value.
You can avoid constant folding and force evaluation on the Newton device by using a variable. For instance:
x := -2; y := sqrt(x); #C4335B1 -1.79769e+308
Also, note that FormattedNumberStr
does not properly handle not-a-number values. (it returns "Number too small.")
ExtractByte
or ExtractWord
or possibly ExtractLong
to get the bytes out in integer form, then do something with them. However, keep in mind that NewtonScript integers are only 30 bits wide, whereas the serial number is 64 bits wide, so you'll never be able to put all the information contained in the serial number into a single integer. (3 integers would be required.)SetClass
to change it's class to 'real
, which would effectively "cast" the 8-byte object to a real number. This is a bad idea, because real numbers are interpreted using bitfields with special meanings, and it's possible for two real numbers to have different binary representations and still evaluate as equal using the '=' operator. (Any two not-a-number values will do this.)StrHexDump
is the best way to format the serial number object for humans to read. If you want to break it up to make it more easily readable, you could do something like this: local s := StrHexDump(call ROM_GetSerialNumber with (), 2); StrReplace(s, " ", "-", 3);
Which produces this string (on my unit):
"0000-0000-0154-8423 "
Please note that the serial number provided by the chip does NOT match the serial number that Apple Computer and other Newton device manufacturers may put on the outside of the case. When supporting a device, Apple and its licensees will most likely request the user-visible serial number, typically found on a sticker on the case. Please be sure that you present data from the internal chip-based serial number in such a way as to ensure the user will not be confused. (This is the reason the chip-based serial number is not displayed by any software built into the device.)
ModalConfirm
slip, because your code is paused waiting for the result. You can, however, remove an AsyncConfirm
slip. The return value from AsyncConfirm
(which is documented in the Newton Programmer's Guide as "unspecified") is actually a reference to the confirm view. Sending that view a Close
message dismisses the slip. The callback function will not be called if this slip is removed in this way, so make sure your program handles that case.clEditView
(the children paragraph, polygon, and picture views containing text, shapes, and ink) to a soup and restore it later?viewChildren
array for the clEditView
, probably in the viewQuitScript
. To restore, assign the array from the soup to the viewChildren
slot, either at viewSetupFormScript
or viewSetupChildrenScript
time; or later followed by RedoChildren
.viewChildren
array. (For example, text has optional slots for fonts and tabs, shapes have optional slots for pen width, and new optional slots may be added in future versions.) Saving the whole array also allows you to gracefully handle templates in the viewChildren
array that don't have an ink, points, or text slot. In the future, there may be children that represent other data types.clEditView
? If I drag out a clParagraphView
child in NTK, the text is not selectable even if I turn on vGesturesAllowed
.clEditViews
have special requirements. To create a text child of a clEditView
that can be selected and modified by the user (as if it had been created by the user) you need to do the following: textTemplate := { viewStationery: 'para, viewBounds: RelBounds(20, 20, 100, 20), text: "Demo Text", }; AddView(self, textTemplate);
The view must be added dynamically (with
AddView
), because the clEditView
expects to be able to modify the contents as the user edits this item. The template (textTemplate
above) should also be created at run time, because the clEditView
adds some slots to this template when creating the view. (Specifically it fills in the _proto
slot based on the viewStationery
value. The _proto
slot will be set to protoParagraph
) If you try to create too much at compile time, you will get -48214 (object is read only) errors when opening the edit view.
The minimum requirements for the template are a viewStationery
of 'para
, a text
slot, and a viewBounds
slot. You can also set viewFont
, styles
, tabs
, and other slots to make the text look as you would like.
TieViews
, do I simply remove the appropriate slots from the viewTie
array?viewChangedScript
to be called. This can happen without calling SetValue
, for example, when the user writes into a view that has recognition enabled, the viewChangedScript
will get called.UntieViews
function, and call it if it exists, but if it does not, removing the pair of elements from the tied view's viewTie
array is fine.FilterDialog
and ModalDialog
. (See the Q&A "FilterDialog and ModalDialog Limitations" for important information on those methods.)clParagraphView
's viewIdleScript
is fired off automatically. (For example, an operation which results in the creation or changing of a keyboard's input focus within the view will trigger the viewIdleScript.) Why does this happen and what can I do about it?clParagraphView
class internally uses the idle event mechanism to implement some of its features. Unfortunately, any viewIdleScript
s provided by developers also execute when the system idle events are processed. Only the "heavyweight" views do this, "lightweight" paragraph views (in other words, simple static text views) do not.viewIdleScript
. You can either accept the extra idle script calls, or use another non-clParagraphView
based view to implement your idle functions.theView:FilterDialog()
, the part of the screen that was not covered by the theView
no longer accepts any pen input. theView
is a protoFloatNGo
. Is there some trick?FilterDialog
and ModalDialog
when used to open views that are not immediate children of the root view. At this point we're not sure if we'll be able to fix the problem.FilterDialog
or ModalDialog
to open more than one non-child-of-root view at a time. Opening more than one at a time with either of these messages causes the state information from the first to be overwritten with the state information from the second. The result will be a failure to exit the modality when the views are closed.FilterDialog
.BuildContext
. This is the best solution because it avoids awkward situations when the child of an application is system-modal. (Application subviews should normally be only application-modal.)ModalDialog
message instead of FilterDialog
. ModalDialog
does not have the child-of-root bug. (FilterDialog
is preferred, since it uses fewer system resources and is faster.)view:FilterDialog();if view.modalState then begin local childOfRoot := view; while childOfRoot:Parent() <> GetRoot() do childOfRoot := childOfRoot:Parent(); childOfRoot.modalState := view.modalState; end;
This only needs to be done if the view that you send the
FilterDialog
message to is not an immediate child of the root. You can probably improve the efficiency in your applications, since the root child is ususally your application's base view, which is a "well known" view. That is, you may be able to re-write the code as follows:
view:FilterDialog();if view.modalState then base.modalState := view.modalState;
{left: -30, top: <top>, right: 0, bottom: <bottom>}
and the following view justification:
vjParentRightH + vjLeftRatio
DragAndDrop
message to a view, the bitmap for the pre-drag state is cached. Once the drag loop starts, you can not update that cached bitmap. When you "scroll" the view (probably using RefreshViews
), you update the screen, but the cached bitmap is still there and is used by the DragAndDrop
routine to update the screen as the dragged item is moved.ModalConfirm
and AsyncConfirm
, the 2.0 Newton Programmer's Reference says you may pass three types of things as the buttonList argument: a symbol ('okCancel
or 'yesNo
), and array of strings, or an array of frames with 'value
and 'text
slots.'okCancelDefaultOk
, 'okCancelDefaultCancel
, 'yesNoDefaultYes
, and 'yesNoDefaultN
o. They do the obvious thing, setting the default key as specified and the close key to Cancel or No if those aren't the default. However, using these symbols on a Newton 2.0 OS device will result in the "OK" and "Cancel" buttons always being displayed, even if you specify 'yesNoDefaultYes
or 'yesNoDefaultNo
.buttonList
argument allows an additional slot, called 'keyValue
. Supported values for this slot are the symbols 'default
and 'close
, or NIL/not present. 'default
makes the button the default key, and 'close
makes the button activate with the close key. Any other value will cause a problem in the current Newton 2.1 implementation. The keyValue
slot is ignored on the Newton 2.0 OS.'yesNoDefaultYes
and 'yesNoDefaultNo
symbols if you intend to run on both Newton 2.0 and 2.1 devices. Instead, use one of these specifiers: '[{text: "Yes", value: TRUE, keyValue: default}, {text: "No", value: NIL, keyValue: close}]
'[{text: "Yes", value: TRUE}, {text: "No", value: NIL, keyValue: default}]
ROM_DefRotateFunc
. When I rotate the screen the base view rotates properly, but the linked view closes. Do I need to add a ReorientToScreen
slot to the linked view?ReorientToScreen
slot in the view tells the OS that this view is OK, so the slot is used first as a token ("magic cookie") to tell the OS that this view knows about rotation. Later during the rotation operation, the ReorientToScreen
message is sent to your application's base view and to other views that are immediate children of the root view. That method then performs its second function, which is to completely handle resizing the view and its children for the new screen size. (Even views which are small enough so that no resizing is necessary need a ReorientToScreen
method. That method may need to move the base view to ensure that it remains on-screen after rotation.)ROM_DefRotateFunc
for this script, since it fills the magic cookie requirement and handles resizing for most views. ROM_DefRotateFunc
is very simple: it send a close and then an open message to the view. Since well-written applications take screen size into account when they open, this works fine in most cases. However, applications that keep track of internal state that isn't preserved when the app is closed can't use ROM_DefRotateFunc
, because when the app reopens on the rotated screen, it will look different. Opening a linked subview is one example of this; it doesn't usually make sense to remember that a slip is open, since it's usually closed when your application is closed.ReorientToScreen
method to your linked views wouldn't help; since they are descendents of your base view and not children of the root view, the OS wouldn't handle these views. (It's up to your application to keep its kids under control.) You could change your application so that it kept track of whether the linked views were open or closed, and restored them to the same state when it was reopened. However, this might be confusing to users who closed your app and then opened it again much later.ReorientToScreen
method, which either resizes the views so they fit on the new screen, or which closes and reopens the views such that the floaters also re-open. By using the ReorientToScreen
message to handle the special case, you get to do something different during rotation versus during opening at the user request (for example, after tapping on the Extras icon.)BuildContext
also must be handled carefully during rotation. Because they are themselves children of the root view, they'll each need their own ReorientToScreen
method or the screen may not be rotatable when they are open or they won't reopen after rotation. If you use ROM_DefRotateFunc
, the slip itself will be closed and reopened, and care may need to be taken to ensure the slip properly handles being reopened, and that its connection to its controlling application is not lost.protoTXView
text engine in Newton 2.1 OS. How can I get text, styles, pictures, etc. out of the object returned by protoTXView
's Externalize
method without either a) instantiating a protoTXView
or b) digging in the data structure?protoTXView
produces. The data structures in that object are not documented or supported. You may be tempted to do this anyway, since it looks as though the data structure is obvious. Don't, it isn't. ProtoTXView
actually uses several different data formats depending on the complexity and storage destination for the data.protoTXView
to get at the data. Here's one way: local textView := BuildContext( { _proto: protoTXView, viewBounds: SetBounds(0, 0, 0, 0), viewFlags: 0, ReorientToScreen: ROM_DefRotateFunc, }); textView:Open(); textView:Internalize(myExternalizedData);
You can now use all the
protoTXView
APIs to get the data from the textView
object. Don't forget to clean up with textView:Close()
when you're done.
protoTXView
data stored in a soup entry, for example to get the text for sending in email?protoTXView
method GetRangeData
always allocates its storage from the NewtonScript heap, so you will need to copy the data into a destination VBO in chunks. Here's some code to do that. (This code uses a 4K block size, you may wish to adjust that as necessary, add error checking, etc.) StringFilter
is used to remove the protoTXView
graphic indicator. constant kChunkSize := 0x1000; // 4K chunks local start := 0; local theText := GetDefaultStore():NewVBO('string, length("")); // make VBO into a proper string BinaryMunger(theText,0,nil, "", 0, nil); while numChars-start > kChunkSize do // strip out graphics characters StrMunger(theText,start, nil, StringFilter( textView:GetRangeData( { first: start, last: start := start + kChunkSize }, 'text), "\u2206\u", 'rejectAll), 0, nil); // copy remainder if start < numChars then StrMunger(theText,start,nil, StringFilter( textView:GetRangeData( {first: start, last: numChars}, 'text), "\u2206\u", 'rejectAll), 0, nil); // theText now holds plain text from the protoTXView
For clarity, the code above does not use
ClearVBOCache
as mentioned in the Q&A, "How to Avoid Resets When Using VBOs". If you are having problems with large VBOs during code like that mentioned above, see that Q&A for more information.
clParagraphView
and would like to find the current position of the caret within that view so that I can scroll to it. Is there a way to do this?clParagraphView
method called caretRelativeToVisibleRect
that you can use.'inbox,
'top
, 'left
, 'bottom
, 'right
, or nil
. Below is a list of what each return value signifies.paragraphView:CaretRelativeToVisibleRect( visibleBox )
'inbox
The caret is positioned inside the visible region.'top
The caret is above the visible region.'bottom
The caret is below the visible region.'left
The caret is to the left of the visible region.'right
The caret is to the right of the visible region.nil
The view has no caret.protoUpDownScroller
on an Apple MessagePad 2000 , tapping on the scroller once calls the viewScroll2DScript
two or three times. Shouldn't the scroller call the script only once per tap?viewScroll2DScript
once per tap, but rather calls it repeatedly while the pen is held down over the scroll arrow. This is normal scroller behavior, and has in fact been there all along. with the MP2000, the processor is so much faster that it can chug through many scroll operations before the pen is released.while not StrokeDone(unit) do targetView:viewScroll2dscript(....)"
. If it takes even 1 tick for the StrokeDone
function to report true, it's possible that the viewScroll2DScript
might have been called more than once.viewScroll2DScript
. Use the global function Sleep
for this, as that call will yeild the NewtonScript task and let other parts of the OS work (or sleep the processor, saving battery power.) constant kMaxScrollSpeed := 60; // ticks viewScroll2DScript: func(...) begin local t := ticks(); ... // do scrolling stuff RefreshViews(); // show scrolled result Sleep(MAX(kMaxScrollSpeed - (ticks() - t), 0)); end;
The above code should have the unit scrolling at a fairly consistent one-scroll-per-second unless the time it takes to do the scrolling stuff is more than 1 second, in which case it'll just go as fast as it can. The drawback to this approach is that the script will always delay for at least a second, so if for example the user taps the up arrow then the down arrow in less than a second, there will seem to be a delay between the up scroll and down scroll that would not necessarily need to be there.
You could certainly be more clever with your delays by counting ticks and watching the count paramater to your scroll script. This might even result in a better performing delay. However, it's probably not worth the effort it would take to implement this. The one-scroll-per-second approach works well, is simple, and it's unlikely that the user will ever notice the small extra delay. (Don't try to do one-scroll-every-5-seconds using this method though, your users may quickly grow annoyed at the extra delay!)