For a sample copy of PDA Developers, contact Creative Digital at info© cdpubs.com or visit their web site at http://www.cdpubs.com.
NS BASIC CORNER - RIE
John Schettino
GTE Laboratories, Inc.
js12© gte.com
Recent items are a handy addition to Newton, but they can become annoying when they contain useless or outdated information. The recent item Editor (RIE) is one of those handy little programs that NS BASIC is so good for. Since I've already covered the technical details of how recent items are stored and how you edit them in PDA Developers 4.3, this time I focus on creating a usable interface. (The previous article used a text-based interface). When I'm done you'll have a hard time distinguishing this application from one written in NewtonScript.
The program I describe here only works with Newton 2.0. It modifies system information. As with any programming project, you can delete something unintentionally due to a programming error. Always be sure to have a recent backup that you are comfortable with. The program also uses NS BASIC 3.5 features such as block statements and the new WIDGETDEF statement. If you are using an older version of NS BASIC, you need to modify the program somewhat. You can replace block statements with GOTOs. The WIDGETDEF statement lets you group several widget specifications into one statement. You need to break them into several individual statements.
THE RIE INTERFACE
The RIE needs to show the user two lists. The initial list contains all
recent
items found in the Newton (see Figure 1). The user makes a single
selection from
the list, and then chooses to either edit the individual recent items for
the
selected item or to delete the entire set of recent items for the
selection. If
the user chooses to edit the item, a new list appears that contains the
recent
elements stored for the selected item (see Figure 2). The user can then
delete
all recent items, choose one or more recent items to delete, or exit back
to the
main display.
NS BASIC has three widgets that I use to implement this interface. I start with the APP widget to create the application background. It has a status bar, including a clock and close box, and a title. On top of this widget I display the two screens. The main display is a text-list widget showing all the recent items in the Newton. It supports scrolling and a single selection. On top of the status bar I add two windows that act as Edit and Delete buttons. The user selects a recent item from the text list and then taps one of these two buttons.
(If you were writing this application for others, you would probably want to add to this window an info button containing a pop-up with About and Help choices. You would also want to display a confirmation window before deleting a recent item, since Undo is not supported in this version of the program.)
Once the user makes their selection and taps the Edit button, the recent-item screen appears: the text list and buttons from the main display are hidden and a text list that contains the elements for the selected item appears. Over top of the new text list is a text widget that displays the selected item, and on the status bar three buttons appear: Del All, Del Selected, and Done. The user checks off the desired elements, taps Del Selected, and they are deleted.
PROGRAM STARTUP
When you first run the program, it needs to do some preparation work and
then
build the main display:
10 REM RIE - recent items Editor 20 REM Version 2.0 by John Schettino 30 OPEN ch, "System", tag 40 screenSize := GETAPPPARAMS() 50 buttonTops = screenSize.appAreaHeight - 19 60 buttonBottoms = buttonTops + 13 70 buttonFormat = 4*vfRound +2*vfPen + vfFrameBlack + vfFillWhite 80 buttonFont := {family: 'geneva, face: 1, size:10} 90 mainApp := [ {widgetType: "APP", GOTO:280, title: "recent item Editor"}, {GOTO: 560, text: "Edit", viewBounds: SETBOUNDS(30, buttonTops, 66, buttonBottoms), viewFont: buttonFont, viewJustify: 2, viewFormat: buttonFormat}, {GOTO: 430, text: "Delete", viewBounds: SETBOUNDS(74, buttonTops, 120, buttonBottoms), viewFont: buttonFont, viewJustify: 2, viewFormat: buttonFormat}] 100 WINDOW w1, mainApp 105 SHOW W1 110 WIDGETDEF mainScreen :=[ {widgetType:"textList", viewBounds: {left:4, top:27, right:237, bottom:258}, ListItems:[], useScrollers:TRUE, useMultipleSelections:NIL, ScrollAmounts:[1,3,20]}] 120 WINDOW w2, mainScreen 130 GOSUB 0320 //Get all recent items into an arrayLine 30 opens the "System" file using the tag key. This is where the recent items are stored. Lines 40-80 create four variables that are used for all of the button specifications. This gives the program a uniform visual interface. Line 40 gets the current Newton screen bounds. Lines 50 and 60 compute the top and bottom of a button located on the status bar. Line 70 establishes the correct format to mimic a NewtonScript button, and line 80 selects the correct font for buttons.
Line 90 creates the APP widget and the Edit and Delete button window specifications. This is a new feature in NS BASIC 3.5 that I really like: by combining all the window specs into one array, you can see the relationships between the screens. I use SETBOUNDS() to place the two buttons on the status bar, using the top and bottom numbers computed before. Lines 100 and 105 create and show these items.
Line 110 is a WIDGETDEF statement. If you are creating this program from scratch, you start with:
110 WIDGETDEF windowspec := []Then you edit the line (either by tapping on it or by using the EDIT command) to activate the Visual Designer (see Figure 3). You create as many widgets as you need by selecting them from the New pop-up and then dragging them to the desired location. You fine tune each widget by using the Edit Properties floater. This floater has a pop-up list of widget properties. Figure 3 shows the ListItems property for the empty list.
Tap the close box to return to NS BASIC. The actual values listed in line 110 appear if you set the LISTWIDGETS environment variable to true as I do. You can re-edit a WIDGETDEF as often as you like until you get the screen exactly right.
Line 120 creates the window for the widget defined in the WIDGETDEF, and line 130 calls the subroutine that looks up all the recent items in the Newton.
140 REM Show main view 143 SHOW w1[1], w1[2], w2 145 IF LENGTH(itemArray) > 0 THEN 150 mainScreen[0].listItems := itemArray 183 U.mainScreen[0]:SETUPLIST() 186 U.mainScreen[0]:REDOCHILDREN() 190 WAIT 1000 200 GOTO 190 210 ELSE 220 spec := [{text: "No recent items Found", viewBounds: SETBOUNDS(30, 100, 210, 180), viewFont: {family: 'espy, face: 1, size: 10}, viewJustify: 2, viewFormat: vfPen + vfFrameBlack + vfFillWhite + 2*vfShadow}, {GOTO: 280, text: "OK", viewBounds: SETBOUNDS(184, 159, 204, 174), viewFont: buttonFont, viewJustify: 2, viewFormat: buttonFormat}] 230 WINDOW Dialog, spec 240 SHOW Dialog 250 WAIT 1000 260 GOTO 250 270 END IF 280 REM Clean up 290 HIDE 300 CLOSE ch 310 END 320 REM Get all recent items into an array 330 itemArray := [] 340 tagArray := [] 350 GET ch, riFrame, "_M_" 360 DO WHILE FSTAT <> 1 AND STREQUAL(SUBSTR(riFrame.tag,0,3), "_M_") 370 numItems = 0 380 IF HASSLOT(riFrame, 'items) THEN numItems = LENGTH(riFrame.items) 390 ADDARRAYSLOT(tagArray, riFrame.tag) 395 ADDARRAYSLOT(itemArray, SUBSTR( riFrame.tag, 3, STRLEN(riFrame.tag)) & " (" & numItems & " items)") 400 GET ch, riFrame 410 LOOP 420 RETURNLines 140-270 display the main screen. Line 143 shows the two buttons in the main display, and the text list. Lines 150-200 update the list items in the text list with the array of all recent item names created in the get-all-recent-items subroutine. It uses :SETUPLIST() and :REDOCHILDREN() to update the displayed list. Finally, a WAIT/GOTO loop pauses the program until the user taps a button or the close box. Lines 220-260 display a dialog window if there are no more recent items.
Lines 280-310 hide all windows and close the open file when the user taps the close box. The program ends in line 310. This is the only way out of the program, so I need to take care not to cover or hide the APP widget while the program is running.
Lines 320-420 extract the names and tags of all recent items into a pair of arrays. A DO WHILE loop iterates over all the recent items, building the two arrays.
DELETING A RECENT ITEM
Although the Edit button is first, the Delete action is a bit simpler to
describe. Note that the GOTO: field in the Delete button windowSpec (line
90) is
430. That's where we go when the button is tapped:
0430 REM delete from main display 0440 GOSUB 0460 //Delete the selected item 0450 GOTO 0140 //Show main view 0460 REM Delete the selected item 0490 riToDelete := tagArray[mainScreen[0].selection] 0500 GET ch, riFrame, riToDelete 0510 DEL ch, riFrame 0520 ARRAYREMOVECOUNT(tagArray, mainScreen[0].selection,1) 0530 ARRAYREMOVECOUNT(itemArray, mainScreen[0].selection,1) 0550 RETURNThe Delete logic is split into two subroutines, because there's a Delete All choice on the recent items screen as well. Line 440 calls the real delete subroutine and line 450 branches back to the main screen display code. We do this so that the main display's list of recent items is updated to reflect the deletion.
Lines 490-550 actually delete the currently selected item. Line 490 gets the selected tag from the array of tags. The mainScreen[0].-selection expression gets the value of the selection field from the windowSpec for the text-list widget. This field contains a single integer value representing the selected item in the list. I then locate and delete the recent item from the System file, and remove it from both arrays.
EDITING A RECENT ITEM
When the user taps the Edit button the program branches to line 560. I
have to
display a new screen at this point.
0560 REM Edit the selected item 0570 HIDE w1[1], w1[2], w2 0580 WIDGETDEF riScreen :=[ {widgetType:"textList", viewBounds:{left:6,top:52,right:234,bottom:291}, ListItems:[],useScrollers:TRUE, useMultipleSelections:TRUE, ScrollAmounts:[1,3,20], widgetName:"List Box"}, {widgetType:"text",viewClass:81, viewBounds:{left:7,top:19,right:227,bottom:45}, viewFlags:2,viewFormat:257,viewJustify:0, viewLineSpacing:16,text:"", viewFont:{family:'espy,face:0,size:10}, tabs:[20,40,60,80,120,140,160,180], widgetName:"text label"}]Line 570 hides the main screen except for the APP widget. Line 580 is another WIDGETDEF statement. As before, you use the Visual Designer to lay out the two widgets on this screen (see Figure 4).
Notice how easy it is to customize complex options like the viewFormat. These settings make the text widget display without lines or a border frame.
0590 GET ch, riFrame, tagArray[mainScreen[0].selection] 0610 recentItems := [] 0615 IF HASSLOT(riFrame, 'items) THEN 0620 FOR i = 0 TO LENGTH(riFrame.items) -1 0630 ADDARRAYSLOT(recentItems,riFrame.items[i].item) 0640 NEXT i 0645 END IF 0650 riScreen[0].listItems := recentItems 0660 WINDOW itemSel, riScreen 0665 SETVALUE(riScreen[1], 'text, "recent items for: " & itemArray[mainScreen[0].selection]) 0670 U.riScreen[0]:SETUPLIST() 0680 U.riScreen[0]:REDOCHILDREN() 0690 spec := [ {GOTO: 740, text: "Del All", viewBounds: SETBOUNDS(30, buttonTops, 70, buttonBottoms), viewFont: buttonFont, viewJustify: 2, viewFormat: buttonFormat}, {GOSUB: 820, text: "Del Selected", viewBounds: SETBOUNDS(76, buttonTops, 160, buttonBottoms), viewFont: buttonFont, viewJustify: 2, viewFormat: buttonFormat}, {GOTO: 920, text: "Done", viewBounds: SETBOUNDS(166, buttonTops, 204, buttonBottoms), viewFont: buttonFont, viewJustify: 2, viewFormat: buttonFormat}] 0700 WINDOW itemSelBtns, spec 0710 SHOW itemSel, itemSelBtns 0720 WAIT 1000 0730 GOTO 0720Lines 590-645 extract all the elements for the selected recent item. Lines 650-680 set up the list-items field for the text list like I did for the main display. One thing to note about this text list is that useMultipleSelections is set to true. This causes the check boxes next to each item to appear so that the user can make multiple selections.
Lines 690-730 complete the screen. Line 690 defines three buttons. All the windows are created and displayed (700-710) and then the program waits for the user to make a selection.
If the user taps Del All this small subroutine is called:
0740 REM delete RI, return to main display 0750 GOSUB 0460 //Delete the selected item 0770 HIDE itemSel, itemSelBtns 0780 GOTO 0140 //Show main viewIt deletes the selected item and then hides the recent item screen. Finally, it branches back to the main screen display.
If the user taps Del Selected this subroutine is executed:
0820 REM delete the selected items 0822 numDel = LENGTH(riScreen[0].selectedItems) 0824 IF numDel = LENGTH(riFrame.items) THEN GOTO 740 0830 FOR i = 0 TO numDel -1 0840 ARRAYREMOVECOUNT(riFrame.items, riScreen[0].selectedItems[i]-i, 1) 0850 NEXT i 0860 PUT ch, riFrame 0865 itemArray[mainScreen[0].selection] := SUBSTR(riFrame.tag,3,STRLEN(riFrame.tag)) & " (" & LENGTH(riFrame.items) & " items)" 0870 recentItems := [] 0880 FOR i = 0 TO LENGTH(riFrame.items) -1 0890 ADDARRAYSLOT(recentItems, riFrame.items[i].item) 0900 NEXT i 0910 riScreen[0].listItems := recentItems 0911 riScreen[0].selectedItems := [] 0912 U.riScreen[0]:SETUPLIST() 0914 U.riScreen[0]:REDOCHILDREN() 0915 SETVALUE(riScreen[1], 'text, "recent items for: " & itemArray[mainScreen[0].selection]) 0916 RETURNI first determine if they selected all the items in lines 822-824. If they did I just branch to the Del All routine. If they didn't, I iterate over the list of selection numbers in riScreen[0].selectedItems. This array has one element for each selected item. I remove each selected element from the recent item frame in lines 830-850. The edited frame is stored back to the file in line 860. Lines 865-914 update the recent items display to reflect the new list. Line 865 updates the item text shown in the main screen, line 870 clears the selections from the text list, and lines 880-915 update the text list to remove the deleted items.
When the user taps Done I hide the recent items screen and go back to the main screen display:
0920 REM done with RI review 0930 HIDE itemSel, itemSelBtns 0940 GOTO 0140 //Show main viewTEST DRIVE You've already seen the displays produced by the both versions of this program. If you built both, you'll notice that the visual version is noticeably faster. It's snappy to use, incorporates familiar user-interface elements, and is very functional. The version with the text-based interface is cumbersome to use, slow, and not very Newton-like. Note that both versions are still missing some niceties, such as confirmation dialogs before deletion.
I jumped into visual programs with both feet this time. Next time I plan on discussing some of the little details I glossed over this time, like stacking order, updating, extracting values from widgets, and more. The RIE program contains most of these features, so I'll probably revisit it next time.
The RIE source code can be found on this month's source code disk.
ENVIRONMENTAL UPDATE
The NS BASIC development environment is now split into several parts that
you
can selectively install. If you need a feature, then you install its
component.
The components are: NS BASIC, the interpreter; MakePackage, the
stand-alone,
package-building tool; Visual Designer, a tool that lets you lay out most
of the
visual components of your application directly on your Newton; and the NS
BASIC
runtime for distributing "skinny" packages.
The interpreter is now a svelte 124K installed, down from almost 200K in the previous version. One great new feature of this version is the Enable Break check box. When enabled, you get a nice big Stop button that you can tap to halt the currently running program. Now you don't have to reach for the reset button just because your program is looping forever. This version of NS BASIC also implements a new storage approach for programs that reduces their size a bit, and provides a boost to performance as well.
The MakePackage part is for creating stand-alone packages from your programs. This feature has been expanded from version 3.0. You can now make skinny or fat packages. Skinny packages must be distributed with the NS BASIC runtime package, but they are very small. Fat packages include the runtime package, which makes them much bigger. For example, skinny RIE is 14K; fat RIE is 102K
VISUAL DESIGNER
The Visual Designer is directly integrated into the NS BASIC environment.
You
access it by entering and then editing using the new WIDGETDEF statement.
You
create new widgets by tapping New, then selecting the desired widget from
the
pop-up menu. You then can move , re-size, and set the initial property
values
for the widget. This makes most programs that use widgets a breeze to
create. No
more trial and error (or tedious work with graph paper) to get the
viewBounds
correct for each widget.
You still need to do some work outside of the Visual Designer. Plain windows (like those used for buttons) and the APP widget are not currently supported in the Visual Designer. The idea really is to lay out the guts of each screen in the Visual Designer and then SHOW/HIDE them as needed on top of a single APP widget that is always visible.
LANGUAGE FEATURES
There are some changes to the language to support the Visual Designer. The
WIDGETDEF statement is one. Another is that window specs can now contain
the
name of the widget. By using this feature you can combine several window
specs
in a single array and then create them all with a single WINDOW
statement. The
SHOW and HIDE statements have also been expanded to accept arrays as well
as
single window IDs in a list. Your widget programming should be much
easier with
these enhancements.
This version also introduces statement labels in addition to line numbers. You can add a label before any statement and then use that label instead of a line number, as the target for a GOTO, GOSUB, or windowSpec field for instance. I had some problems using labels in the RIE so I took them out, but they look like a great addition to the language. We're one step closer to getting rid of those pesky line numbers.