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 Data
if 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 string
local 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 Extras
SetPartFrameSlot('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 reference
FrameDirty(e)
#2 NIL
EntryChange(e);
e.string := "A new string"; // change the string reference
FrameDirty(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 do
begin
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 0x96
PACKET ID 1 0x82
DATA 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 reception
DUMMY 5 0x00 * 5 Only 2 bytes are detected.
START ID 1 0x96
PACKET ID 1 0x81
VERSION 1 0x10 Judge only bits 7-4
BLOCK NO 2 (L/H) 0x0001 ~ 0xFFFF
CTRL CODE 1 0x01 Don't judge
DEV CODE 1 0x40 Don't judge
ID CODE 1 0xFE Don't judge
DLENGTH 2 (L/H) 0x0001 ~ 0x0200
DATA 1 ~ 512
CHKSUM 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 CODE
0x00, 0x00, 0x00, 0x00 0x96 0x81 0x10 Low High 0x01
DEV CODE ID CODE DLENGTH data CHECKSUM
0x40 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 /DTR
Pin 2 HSKi /CTS
Pin 3 TxD- /TD
Pin 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 behavior
end
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 'skipErrorMessage
OpenRoutingSlip(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 real
local 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 SetupItem
item
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.MakeFoundItem
soupFinder.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 template
statusView: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 size
w - width of the longest keyboard row in key units
s - 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 transport
if 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 backlight
else
// 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!)