The first sample program will be fairly simple: the program fills the screen with a form entitled "Hello World!" and a button; when pressed, the button brings up an alert box with the words "Goodnight moon!". A simple menu that displays a few choices which the user can select.
This is the sample application from Palm Programming: The Developer's Guide beginning on page 67 of the first edition, and we will walk through it in a similar fashion.
hello_world.rcp
and hello_world_resource.h
The first task is to describe the forms and menus to the PalmOS
development environment. We use the pilrc
program from Ardiri
software for this, available at
www.ardiri.com.
First, we define the constants we'll be using to describe all the
resources, and list them in the C-style header file
'hello_world_resource.h':
#define HelloWorldForm 1000 #define HelloWorldButtonButton 1003 #define GoodnightMoonAlert 1101 #define GoodnightMoonOK 0 #define DebugAlert 1102 #define DebugOK 0 #define HelloWorldMenuBar 1000 #define FirstMenu 1010 #define FirstBeep 1010 #define SecondMenu 1000 #define SecondBeepmore 1000
Good so far. These numbers represent internal integer codes for options the user can choose. Note that there are duplicates--OK is generally represented by the value 0, and there are no real problems with objects of different types holding similar IDs in most cases.
Now we must represent the forms and menus themselves, in the 'pilrc'-compatible file 'hello_world.rcp':
#include "hello_world_resource.h" MENU ID HelloWorldMenuBar BEGIN PULLDOWN "First" BEGIN MENUITEM "Beep" ID FirstBeep END PULLDOWN "Second" BEGIN MENUITEM "Beep more" ID SecondBeepmore END END FORM ID HelloWorldForm AT (0 0 160 160) MENUID 1000 BEGIN TITLE "Hello World" BUTTON "Button" ID HelloWorldButtonButton AT (59 91 36 12) LEFTANCHOR FRAME FONT 0 END ALERT ID GoodnightMoonAlert INFORMATION BEGIN TITLE "An alert" MESSAGE "Goodnight moon!" BUTTONS "OK" END ALERT ID DebugAlert INFORMATION BEGIN TITLE "Debug info" MESSAGE "bk file: ^1, line: ^2" BUTTONS "OK" END VERSION ID 1000 "1.0"
Note that the textual descriptions of resource values are used
here; pilrc
will take the numerical values from
hello_world_resource.h
and use them in the generated binary
resource. We are defining a number of resources here: a menu bar,
a form, and an alert.
We're not quite done, though--we must still generate an Eiffel
class representing the resource constants, or we can't use them in
our programs. The easiest way to do this is establish a
Makefile
target using the make_constants.rb
ruby script
provided in the tools
subdirectory of the distribution:
$(PROGNAME)_constants.e : $(RUBY) $(EPALM_TOOLS)/make_constants.rb $(PROGNAME)_constants $(PROGNAME)_resource.h
...where $(RUBY)
is set to point to the ruby executable,
$(EPALM_TOOLS)
is the path to the ePalm tools directory, and
$(PROGNAME)
is the name of the program--in this case,
hello_world
.
Every ePalm application inherits from the EPALM_APPLICATION
class, and then customizes a few of its deferred features. Here's
the MAIN
class from 'hello_world':
class MAIN inherit FORM_DISPATCHER_CLIENT HELLO_WORLD_CONSTANTS PALM_APPLICATION redefine handle_event, start_application end creation pilot_main feature {ANY} --redefined application feature start_application is --opens the "hello world" form do form_manager.go_to_form( Hello_world_form ) end handle_event( event : EVENT ) is --handles events local form : FORM do inspect event.type when Frm_load_event then create form.from_form_id( event.data_frmload_formid ) form.set_as_active_form create form_handler form.set_event_handler( form_handler ) last_event_was_handled := True else last_event_was_handled := False end end form_handler : MAIN_FORM_EVENT_HANDLER end
Two features are overridden: start_application
invokes the
form_manager
singleton object of FORM_MANAGER_CLIENT
to open
our starting form, Hello_world_form
. It does nothing else.
The handle_event
feature takes a bit more explanation; it
handles high-level events such as those requesting forms be
opened, etc. In this case, it looks form a Frm_load_event
event
type, which indicates that another form should be opened. When
the OS requests that a form be opened, we have a pretty good idea
what form it is (since we've only defined one). We create a
form
object from the form id noted in the event
(event.data_frmload_formid
), and set it as the active form. We
then create form_handler
(our application's form event handler,
of type MAIN_FORM_EVENT_HANDLER
and set the form's event handler
to be that object, and exit.
The OS does almost everything else. The only thing we haven't covered is how our program responds to button presses.
The answer is in our final Eiffel class,
MAIN_FORM_EVENT_HANDLER
. When PalmOS sees an event, it tries to
handle it at the lowest possible level; if it's a form-type event,
it passes it off to the current active form handler for the form
in question. MAIN_FORM_EVENT_HANDLER
inherits from
STANDARD_EVENT_HANDLER
and overrides a number of its event
handlers:
class MAIN_FORM_EVENT_HANDLER inherit FORM_MANAGER_CLIENT HELLO_WORLD_CONSTANTS SOUND_MANAGER_CLIENT SOUND_MANAGER_CONSTANTS STANDARD_EVENT_HANDLER redefine handle_frm_open_event, handle_ctl_select_event, handle_menu_event end feature {ANY} handle_frm_open_event( event : EVENT ) is do form_manager.active_form.draw last_event_was_handled := True end handle_ctl_select_event( event : EVENT ) is do form_manager.alert( Goodnight_moon_alert ) last_event_was_handled := True end handle_menu_event( event : EVENT ) is do if event.data_menu_item_id = First_beep then sound_manager.play_system_sound( Snd_info ); else sound_manager.play_system_sound( Snd_start_up ); end last_event_was_handled := True end end
Looking at each of these in turn is at least somewhat illustrative.
The handle_frm_open_event
feature handles the low-level event of
a form actually being opened (this is different from
Frm_load_event
, which is handled at the application level and
requests that a form be loaded from memory; the Frm_open_event
event type represents a form about to be opened to the screen).
Its action is simple; draw the active form and record that the
event was handled properly. Setting last_event_was_handled
to
True
ensures that no further handling of this event will occur;
if it is left as False
, then the OS will try and handle the
event further and further up the chain of command.
The handle_ctl_select_event
feature handles any events in which
the user selects a control on the page; in this case, we have only
one control on the page, the button; when pressed, this feature
brings up an alert dialog (our 'Goodnight_moon_alert), which is
handled by the OS, and then records that the event was handled
properly.
Finally, handle_menu_event
handles any events created by the
menu selections. The event.data_menu_item_id
feature holds the
ID of the menu item selected; this feature simply branches based
on that value and plays a sound.
cecil.se
You will have to write a custom cecil.se
file to alert
SmartEiffel that certain Eiffel functions will be called from C
code. The cecil.se
file here is quite simple and only uses some
FORM_DISPATCHER
and FORM_DISPATCHER_CLIENT
features:
--the feature you want to call from C FORM_DISPATCHER_dispatch_event_from_pointer FORM_DISPATCHER dispatch_event_from_pointer FORM_DISPATCHER_CLIENT_form_dispatcher FORM_DISPATCHER_CLIENT form_dispatcher
.ace
file
All of this can now be bound into an .ace
file, so:
system hello_world root MAIN : "pilot_main" default -- section of the system assertion (boost); debug (no); trace (no); collect (yes); case_insensitive (no); no_style_warning (no); no_warning (no); verbose (no); manifest_string_trace (yes); high_memory_compiler (no); cluster -- section hello_world : "." --these directories will point to your ePalm distribution epalm_application : "../../src/application" epalm_constants : "../../src/constants" epalm_structs : "../../src/structs" --these directories point to your SmartEiffel distribution "/home/vputz/bin/SmartEiffel/lib/kernel"; "/home/vputz/bin/SmartEiffel/lib/base"; "/home/vputz/bin/SmartEiffel/lib/io"; external -- section for elements written in another language cecil ("cecil.se") generate -- section no_strip(no); no_split(no); clean(no); c_mode: palmos end
As you can see, the basics of a simple ePalm application are just that: simple. Define the forms and resources your application will use, define an application which will request your title-page form and also handle high-level "load form" events, and then for each form define an event handler which will handle the low-level events produced by the user.
It should be noted that Makefiles can automate a great deal for the
user, particularly generation of source code. also, some environment
variables should be set; for example, SmartEiffel
should point to
the smalleiffel_sys/system.se
file in the ePalm distribution, and
for proper operation of many Makefile targets and tools, the EPALM
variable should point to the root directory of your ePalm
distribution.