Monday, February 1, 1999

Playing the field

.KEYWORD field
.FLYINGHEAD PROGRAMMING POWER
.TITLE Playing the field
.DEPT
.SUMMARY Programming technology editor Alan Jay Weiner is back — and better than ever before. In this fine continuation of our well-respected Programming Power series, Alan tells you about the trials, tribulations, and triumphs of programming with fields.
.AUTHOR Alan Jay Weiner
Since this is my first column for 1999, let me start by saying Happy New Year to all of you. I’d also like to thank PalmPower readers and staff for letting my take January off and get some sorely needed "rest and recovery" (which looked an awful lot like catching up on some other work…).

[Shhh… Alan, don’t let it get out that we let our writers off the hook. It’ll be bad for my reputation as a tough, unrelenting, merciless, Editor-in-Chief. — DG]

In any case, back to work…

Almost any application that uses text entry will use fields. Fields are the standard Palm OS user-interface element for text entry. The system knows how to handle attributes such as automatically capitalizing the first letter, word-wrapping, expanding to multiple lines and so forth. Certainly that’s easier than writing our own text-entry routines — especially if you need multiple lines with word-wrapping!

Shown in Figure A, this month’s sample program, Infield, provides some basics of field usage, along with a couple of special functions: only allowing digits and forcing text to upper case. As usual, it’s based on the original "Hello, world" program, so most of it will be familiar.

.FIG A Infield is a simple little program that shows off field operations.

.H1 Fields are complex little beasts
First, let’s look at how the field itself works. You define fields along with forms in whichever tool you use to build the resources — Constructor if you’re using the Metrowerks compiler, or PilRC if you’re using the GCC tools.

Fields have many attributes, including the typical positioning information (upper-left corner’s X and Y coordinates, and the width and height), and usability (enabled or not). They also have names and ID numbers like any other Palm OS user interface (UI) element.

Beyond these attributes, the field can be underlined or not, limited to a single line, editable or not, numeric only. Other attributes include wild-and-wooly things like dynamic sizing and scroll bars. To really cover fields entirely will take many, many columns; they don’t always work as you’d expect. For this column we’ll just stay with some basics and a couple of tricks to keep things interesting.

.H1 The simple stuff
Most of the field activity is handled by the default form handler. When your EventLoop function calls FrmDispatchEvent, the event goes to the form handler you’ve set. If your form handler doesn’t handle the event, it then goes on to the default handler. Usually your form handler doesn’t do anything special with field events. The default handler does all the right stuff, given the attributes you’ve defined for the field. At least, it mostly does — there are times where things don’t work as you think they should. We’ll look at some of those later on.

When a field is tapped, that field is selected. From that point on, until the form is closed or you select another field, any Graffiti or keyboard characters go into that field.

.H1 Looking at the Infield sample
As I mentioned, Infield began as the "Hello, world" program. I added:

.BEGIN_LIST
.BULLET changes to the user interface; the field itself and several push-buttons;
.BULLET several "library" routines in the beginning;
.BULLET handlers for the push-buttons (ctlSelectEvent);
.BULLET keyDownEvent handler for "special effects";
.BULLET nilEvent handler to do some post-field change processing;
.BULLET EventLoop changed to get nilEvents;
.BULLET change to form’s frmOpenEvent to set default push-button setting and select the field.
.END_LIST

Let’s look at each of these. You can see a listing at http://www.component-net.com/pp-extras/field.html.

.H2 User interface changes
Well, obviously if we’re going to use a field, we need to have one — so I added the field itself. I defined it as a maximum of 20 characters, underlined, usable, editable, and a single line.

I also defined a group of three push-buttons. These allow the user to filter out characters, setting the field to allow any characters, forcing alphabetic characters to upper case, or only allowing numeric characters.

.H2 Included "library" routines
For convenience, I’ve kept everything in a single source file. As these samples are small, I find it convenient to deal with just one listing. In reality, the routines such as toupper, isnumeric, and isprintable are in a library. These routines do just what you’d expect — at least when using English characters; they take the US-centric view of the ASCII character set — and I apologize to our international readers.

.H2 Handling the Push-buttons
Tapping the push-buttons generate (amongst other events) a ctlSelectEvent. This indicates the particular control has been selected. I added ctlSelectEvent handlers for the push-buttons to MainFormHandleEvent. Each push-button control sets or clears the two global boolean flags, fForceUpper and fNumericOnly. The keyDownEvent handler uses these flags to force characters to upper case or filter non-numerics.

.H2 The keyDownEvent handler
This is a bit unusual — fields normally do keyDownEvent handling on their own. I put it in to demonstrate some additional techniques. We can modify the event before the default handler gets it, as we do when fForceUpper is set. When we’re forcing upper-case characters, we check the character in the keyDownEvent structure. If it’s an alphabetic character, we force it to upper case, then allow the default handler to process it. If the fNumericOnly flag is set, we do similar checking for numeric characters. In this case, if the character is not a number, we return "handled" — so the default handler won’t process the event at all.

This technique isn’t perfect, though. It works for characters entered by Graffiti, but if the user enters characters via the on-screen keyboard, those characters are stuffed right into the field’s data — they don’t go through the keyDownEvent processing. I have neither Jot, T9, nor the GoType! keyboard, so I couldn’t check if they will work with keyDownEvent processing or not.

There is a field attribute that says "numeric only." When that attribute is set, the field’s default handler prevents non-numeric characters from being entered. This is easier than using keyDownEvent processing, unless you want some additional characters to be allowed. It’s also more reliable, since the keyboard respects this attribute and won’t put non-numeric characters into the field. However, it only works on Palm OS 2 and 3 (PalmPilots and PalmIIIs) — it doesn’t work on Palm OS 1, so Pilot 1000s and 5000s will put non-numeric characters into the field.

.H2 Processing after the field changes
Infield displays a running count of the number of characters in the field’s text buffer. We want to update this count after the field has changed. Unfortunately, we don’t get an event to tell us that the field has changed. There is a fldChangedEvent, but that only happens when the field scrolls due to dragging and selecting text; we don’t get that when entering or deleting characters.

When there are no other events in the event queue, we can ask EvtGetEvent (in our event loop) to give us a nilEvent. So Infield does its post-field-change processing when a nilEvent comes in. To trigger the nilEvents, the keyDownEvent processing sets a boolean flag, fUpdateScreen. When EventLoop calls EvtGetEvent, it uses fUpdateScreen to select how long to wait for an event — either forever (if fUpdateScreen is not set) or 0 (zero) ticks (if fUpdateScreen is set). Once the event queue is empty, and after the timeout occurs (immediately in the case of 0), EvtGetEvent will return a nilEvent. So immediately after processing all the events to put a character into the field, EvtGetEvent will give us the nilEvent, so we update the character count.

In actuality, there’s a side-effect of being in a field — your application gets constant nilEvents. So I could’ve skipped all the handling with fUpdateScreen and setting the EvtGetEvent timeout. However, there’s a delay before that nilEvent comes in; the character-count doesn’t update immediately, so I kept the fUpdateScreen flag to trigger a 0-length timeout in EvtGetEvent. I prefer things happening because I said to do them and not counting on undocumented features like the nilEvents in fields. It also shows how to do something like this, just in case you have a similar need in some other application.

Originally, I used the fUpdateScreen flag in a conditional block so we wouldn’t keep updating the screen with the same character count. However, if you enter characters using the on-screen keyboard, fUpdateScreen never got set, so the count wasn’t updated after using the keyboard. So I left it doing the character count calculation with each nilEvent, but I only update the screen when the count changes.

This routine also demonstrates getting text from the field. In this case, we get a pointer to the text and just get the text’s length. However, we could just as easily copy that text into a data record, or do other processing with it.

.H2 EventLoop changes
As just described, the call to EvtGetEvent sets the timeout to either forever or zero, based on the fUpdateScreen flag.

.H2 Form’s frmOpenEvent changes
I made two minor changes to frmOpenEvent. First, it calls CtlSetValue to enable the "All Chars" push-button. Otherwise, the form initially draws with all three push-buttons displayed as "off" and it simply looks different from tapping the "All Chars" button.

The other change was to set the focus to the field. This allows the user to just go ahead and enter characters; if we didn’t set the focus, any characters would be ignored until the user tapped the field itself to set the focus.

.H1 Field funnies
When you start manipulating fields, such as inserting text programmatically, things can get weird. In fact, lots of people have had strange problems when doing complex things with fields. The Palm Developer’s mailing list includes several discussions of unexpected behaviors.

Some of the oddities include:

.BEGIN_LIST
.BULLET If you insert end-of-line characters, they should be "&#92012" (an octal 12 which is a decimal 10). If you use "&#92r" or "&#92n" it may work on the real machine, but not in the Macintosh Simulator; apparently the characters are mapped differently.
.END_LIST

.BEGIN_LIST
.BULLET Gremlins doesn’t go through keyDownEvents to insert keys into fields. Expect gremlins to throw all kinds of bizarre stuff into your fields.
.END_LIST

.BEGIN_LIST
.BULLET In fact, Gremlins have been known to put more characters into a field than the maximum value indicated by the field settings. Be sure that you don’t crash if that happens (especially if you go for Platinum certification!). This may only happen if the field is limited to two characters, but that’s not certain.
.END_LIST

.BEGIN_LIST
.BULLET Palm OS 1 works differently from Palm OS 2 which works differently from Palm OS 3. Test on all three devices. Why should programming ever be easy?
.END_LIST

.H1 Conclusion
Even some of the best programmers have complained about fields — especially when manipulating the field’s memory buffer. They’re quite useful, but not often intuitive. If you run into troubles, a great place to ask for help is the Palm Developer’s mailing list. In fact, anyone doing Palm development should read that list religiously.

Fields are very powerful, and we’ve just had a taste of them here. We’ll visit them again from time to time.

.BEGIN_SIDEBAR
.H1 Product availability and resources
Source code to Infield is at Alan Jay Weiner’s web site at http://www.ajw.com/PalmPower/ProgrammingPower/Feb99/Infield.zip.

The Palm Developer’s mailing list is at http://www.palm.com/devzone/mailinglists.html.
.END_SIDEBAR

.BIO
.DISCUSS http://powerboards.zatz.com/cgi-bin/webx?13@@.ee6c9e2