Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Your first Palm app - covering GUI components, alerts and forms.

0.00/5 (No votes)
5 Nov 2002 1  
Building on previous articles, we develop a simple application and discuss some of the components available for Palm GUI.

Sample Image - Palm.jpg

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: 
		// Application start 
		error = StartApplication(); 
		if (error) 
			return error; 
		// Maintain event loop 
		EventLoop(); 
		// Stop application 
		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; 
	// Main event loop 
	do 
	{ // Get next event 
		EvtGetEvent(&event, evtWaitForever); 
		// Handle event 
		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:

// Application event loop 
switch (event->eType)
{ 
	case frmLoadEvent: //Handle form load events 
		formID = event->data.frmLoad.formID; 
		form = FrmInitForm(formID); 
		FrmSetActiveForm(form); 
		
		switch (formID) 
		{ 
			case frmMain: // Set event handler for 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: // Repaint form on open 
		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() 
{ 
	// Create form 
	FormPtr frm = FrmInitForm(frmSecond); 
	// Cheat - use our event handler, as it won't be called upon to do anything anyhow 
	FrmSetEventHandler(frm, frmMain_HandleEvent); 
	// Show as a dialog - this returns the ID of the button pressed, 
	// just like windows, but in this case we don't care 
	FrmDoDialog(frm); 
	// Clean up 
	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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here