Welcome to this, my second article on Palm development. In the meantime I see Jason Henderson has submitted an article on Palm, and I'm certainly presuming to build off his article as well as my own. In particular, my previous article does not cover the vagaries of the Palm event loops, so this time I will presume that you're familiar with them from reading Jason's excellent article.
In this article we will develop our first form based application, and explore some of the GUI components available to us. More complex components such as tables and lists will be covered separately at a later date.
Forms and Alerts
As has been previously mentioned: the Palm regards what we would call a window to be a form. What the Windows world would call a MessageBox is known as an alert. The library which handles these things is the forms library, and therefore the functions found within it are all prefixed by Frm. This is actually incredibly helpful when using the help index provided with your compiler. We do cheat a little in this example to minimise the amount of code required, but where we do this I will be sure to point it out to you.
The project provided with this article is for the Falch.net developer studio IDE, because that is what I use. It uses the PilRC resource compiler, and GCC. Therefore if you're using these things directly, you're unlikely to have any trouble. If you are using CodeWarrior I am not sure if it can work with textbased resources. If not, I am afraid you will have to create them within your compiler.
PilotMain
Traditionally a Palm application has specific points of operation. Generally speaking, the PilotMain will in part look something like this.
Err error;
switch (cmd)
{
case sysAppLaunchCmdNormalLaunch:
error = StartApplication();
if (error)
return error;
EventLoop();
StopApplication();
break;
}
The Err type in one which is often seen in that it is returned by great many functions, and the API also provides constants for predefined return values. When an application receives a normal launch command, it's typical for initialisation to occur within a function called StartApplication, the main event loop to be a function called EventLoop, and then for clean up to occur within a function called StopApplication. The StartApplication function simply calls FrmGotoForm in order to initialise our main form, and the EventLoop is boilerplate code which looks like this:
static void EventLoop(void)
{
Err error;
EventType event;
do
{ EvtGetEvent(&event, evtWaitForever);
if (!SysHandleEvent(&event))
{
if (!MenuHandleEvent(0, &event, &error))
{
if (!ApplicationHandleEvent(&event))
FrmDispatchEvent(&event);
}
}
} while (event.eType != appStopEvent);
}
This is the core message pump for the application, and it gives first the system, then our menu, and finally our application the opportunity to handle an event. Should all these functions return false, then the event is sent to our own event handler for that specific form. The Palm is strictly a single threaded environment, and so if any other applications started, the appStopEvent event is sent, our event loop terminates, and our program ends.
Controls
Once we have created a form within our IDE, we need to populate it. The Palm has a number of UI controls that you will be familiar with, and a few that are not so similar to the ones offered by the Windows environment.
Labels |
A label is basically a static text control. Naturally we can programmatically set its value, however it does not open itself for editing by the end user. |
Fields |
A field is an edit control, where the user is able to enter text. It may be multi-line, or single, it can be underlined, and it may be limited to accept only numbers. The one in the demo application is single line, and underlined so that you can see where it is. |
Slider |
The Palm offers a horizontal slider for setting values within a range. |
Scrollbar |
Although some input controls offer scrollbars automatically, it's also possible to set up a scroll bar and respond to it manually. |
Checkbox |
Just like in Windows, a checkbox control contains text to explain its purpose, and a box within which a checkmark is toggled by clicking on it. |
Button |
It is almost obvious that a GUI will offer buttons which can be pressed in order to trigger desired actions. |
SelectorTrigger |
A selectorTrigger works a little like a button. Both buttons and selectorTriggers can display an image rather than text. |
PushButton |
A pushbutton is a button that stays down when it is pressed. These are typically presented in a sequence, so the user can visibly select from a list of options. |
PopupTrigger |
A little like a combobox, a pop-up trigger has text and an arrow. Pressing the arrow brings up a list of options, and selecting an option changes the text on the trigger. |
GraffitiStateIndicator |
When entering text using the graffiti system, there are a couple of notations which instead of entering text, notify the Palm of the nature of text to follow. This component simply provides graphical information regarding the state of the graffiti system. |
These are the controls which will appear on our form for this example, but this does not represent an exhaustive list. In particular, lists and tables will be covered in a future article.
Responding to GUI events
Our main event handler, ApplicationHandleEvent, handles a number of things, including setting the event handler function for our form. The code which does this is as follows:
switch (event->eType)
{
case frmLoadEvent: formID = event->data.frmLoad.formID;
form = FrmInitForm(formID);
FrmSetActiveForm(form);
switch (formID)
{
case frmMain: FrmSetEventHandler(form, (FormEventHandlerPtr)frmMain_HandleEvent);
break;
default:
break;
}
handled = true;
break;
default:
break;
}
As a result of this, the frmMain_HandleEvent function is the function which is called by events which have been rejected by the system, the menu, and this main event loop. Within the handle event function, we must handle the frmOpenEvent event, with some code such as this:
switch (event->eType)
{
case frmOpenEvent: form = FrmGetActiveForm();
FrmDrawForm(form);
handled = true;
break;
This code simply makes sure our form is drawn, a bit like a Windows paint event. To respond to controls, we need to add something like this:
case ctlSelectEvent:
switch (event->data.ctlSelect.controlID)
{
case btnAlert:
FrmAlert(altHelloWorld);
break;
case trgr:
ShowSecondForm();
break;
}
break;
In this case, I have named our button btnAlert, defined an alert called HelloWorld, and so when the button is pressed, our alert is shown. Alerts can also be very useful for debugging, with the FrmCustomAlert being able to show a dialog with information that we pass in. This function takes the id of an alert, and three string values, which then replace the values ^1, ^2 and ^3 within the text of the alert. Note the title is not parsed, only the body. It is necessary to pass an empty string rather than null for unwanted values with this function.
Our selection trigger is named trgr, and it serves to show a second form, which merely has a button on it. We show this form modally, and pressing any button automatically will close it, so no code is needed. Rather than add more code to this example, we cheat and use the same event loop for this form as for our main form. The ShowSecondForm function looks like this:
void ShowSecondForm()
{
FormPtr frm = FrmInitForm(frmSecond);
FrmSetEventHandler(frm, frmMain_HandleEvent);
FrmDoDialog(frm);
FrmDeleteForm(frm);
}
Finally, we need some code to set up our popup. We actually do this entirely within the resource file, although it can be done programatically. We need to associate the popup with a list, and populate that list. In PilRC, it looks like this:
POPUPTRIGGER "Select" ID popup AT (11 61 42 11)
POPUPLIST ID popup popList
LIST "Ozzy" "Sharon" "Aimee" "Kelly" "Jack" ID popList AT (11 61 44 0) NONUSABLE DISABLED FONT 0
I've set up a list that contains the names of the Osbourne family, positioned it at the same location as the trigger, with 0 height so that it will work it out itself, and made it invisible. The POPUPLIST item simply links the trigger to the list.
Conclusion
Well, I hope you find this information useful. We've covered a fair bit of ground, in a fairly whirlwind manner. Next up we will talk about databases, and how to persist information to the Palm, then we will be in a position to discuss lists and tables. Once we have that basic information under our belt, we can start to build a real application. I welcome any feedback, there are areas I am being vague because I do not want to favour one IDE. If however the information is hard to follow as a result, I will make changes, although they will of necessity favour the IDE I am using.