The hello_world
program was an excellent introduction into PalmOS
programming using ePalm, but was of course a fairly limited example
since only one control was available and it did virtually nothing.
The controls
example is a bit more rigorous and shows virtually
all of the simple controls available in older versions of PalmOS
(newer versions have introduced some controls which were not
represented in ePalm at the time of this writing).
We'll follow the same framework we did with the hello_world
application: first, the forms and resources.
This will be a somewhat more tricky application, with a title form
and several forms underneath that. Each of those forms will have
several control resources embedded in them, so the
controls_resource.h
and controls.rcp
files will be somewhat
more complex. Here are the constants:
#define Controls_main_form 1000 #define Controls_button_demo_button 1001 #define Controls_trigger_demo_button 1002 #define Controls_checkbox_demo_button 1003 #define Controls_slider_demo_button 1004 #define Controls_field_demo_button 1005 #define Controls_memory_demo_button 1006 #define Controls_main_menu_bar 1010 #define Controls_menu_button_demo 1011 #define Controls_menu_trigger_demo 1012 #define Controls_menu_checkbox_demo 1013 #define Controls_menu_field_demo 1014 #define Controls_menu_memory_demo 1015 #define Button_demo_form 1100 #define Button_demo_reg_dec_button 1101 #define Button_demo_reg_inc_button 1102 #define Button_demo_reg_label 1103 #define Button_demo_pushbutton_a 1104 #define Button_demo_pushbutton_b 1105 #define Button_demo_pushbutton_c 1106 #define Button_demo_pushbutton_d 1107 #define Button_demo_pushbutton_e 1108 #define Button_demo_pushbutton_label 1109 #define Button_demo_rpt_dec_button 1110 #define Button_demo_rpt_inc_button 1111 #define Button_demo_rpt_label 1112 #define Button_demo_back_button 1113 #define Trigger_demo_form 1200 #define Trigger_demo_form_popup_trigger 1201 #define Trigger_demo_form_selector_trigger 1202 #define Trigger_demo_form_popup_list 1203 #define Trigger_demo_form_regular_list 1204 #define Trigger_demo_form_popup_field 1205 #define Trigger_demo_form_regular_list_field 1206 #define Trigger_demo_back_button 1207 #define Field_demo_form 1400 #define Field_demo_dec_field 1401 #define Field_demo_hex_field 1402 #define Field_demo_convert_button 1403 #define Field_demo_reverse_field 1404 #define Field_demo_reverse_button 1405 #define Field_demo_big_field 1406 #define Field_demo_scrollbar 1407 #define Field_demo_back_button 1408 #define Memory_demo_form 1500 #define Memory_demo_heap_id_field 1501 #define Memory_demo_free_bytes_field 1502 #define Memory_demo_allocate_size_field 1503 #define Memory_demo_allocate_button 1504 #define Memory_demo_collect_button 1505 #define Memory_demo_back_button 1506 #define Generic_alert 1600 #define Debug_alert 1700
#include "controls_resource.h" MENU ID Controls_main_menu_bar BEGIN PULLDOWN "Demo" BEGIN MENUITEM "Buttons" ID Controls_menu_button_demo "B" MENUITEM "Triggers/Lists" ID Controls_menu_trigger_demo "T" MENUITEM "Fields" ID Controls_menu_field_demo "F" MENUITEM "Memory" ID Controls_menu_memory_demo "M" END END FORM ID Controls_main_form AT (0 0 160 160) MENUID Controls_main_menu_bar BEGIN TITLE "ePalm Controls Demo" FORMBITMAP AT (5 15) BITMAP 1 BUTTON "Buttons demo" ID Controls_button_demo_button AT (55 40 90 15) LEFTANCHOR FRAME FONT 0 BUTTON "Trigger demo" ID Controls_trigger_demo_button AT (PREVLEFT PREVBOTTOM+3 PREVWIDTH PREVHEIGHT) LEFTANCHOR FRAME FONT 0 BUTTON "Field demo" ID Controls_field_demo_button AT (PREVLEFT PREVBOTTOM+3 PREVWIDTH PREVHEIGHT) LEFTANCHOR FRAME FONT 0 BUTTON "Memory demo" ID Controls_memory_demo_button AT (PREVLEFT PREVBOTTOM+4 PREVWIDTH PREVHEIGHT) LEFTANCHOR FRAME FONT 0 END BITMAP ID 1 "tower.bmp" FORM ID Button_demo_form AT (0 0 160 160) BEGIN TITLE "Buttons demo" LABEL "Regular buttons simply allow one" AUTOID AT (0 16) LABEL "event per keypress." AUTOID AT (PREVLEFT PREVBOTTOM) BUTTON "Dec" ID Button_demo_reg_dec_button AT (10 PREVBOTTOM+3 20 AUTO) LEFTANCHOR FRAME FONT 0 FIELD ID Button_demo_reg_label AT (70 PREVTOP 20 AUTO) DISABLED NUMERIC MAXCHARS 6 BUTTON "Inc" ID Button_demo_reg_inc_button AT (130 PREVTOP 20 AUTO) LEFTANCHOR FRAME FONT 0 LABEL "Pushbuttons allow a single value from" AUTOID AT (0 PREVBOTTOM+3) LABEL "a set of predefined values." AUTOID AT (PREVLEFT PREVBOTTOM) PUSHBUTTON "1" ID Button_demo_pushbutton_a AT (20 PREVBOTTOM+3 12 AUTO) GROUP 1 PUSHBUTTON "2" ID Button_demo_pushbutton_b AT (PREVRIGHT+1 PREVTOP PREVWIDTH PREVHEIGHT) GROUP 1 PUSHBUTTON "3" ID Button_demo_pushbutton_c AT (PREVRIGHT+1 PREVTOP PREVWIDTH PREVHEIGHT) GROUP 1 PUSHBUTTON "4" ID Button_demo_pushbutton_d AT (PREVRIGHT+1 PREVTOP PREVWIDTH PREVHEIGHT) GROUP 1 PUSHBUTTON "5" ID Button_demo_pushbutton_e AT (PREVRIGHT+1 PREVTOP PREVWIDTH PREVHEIGHT) GROUP 1 FIELD ID Button_demo_pushbutton_label AT (PREVRIGHT+10 PREVTOP 20 AUTO) DISABLED NUMERIC LABEL "Repeating buttons generate events" AUTOID AT (0 PREVBOTTOM+3) LABEL "as long as they are pressed." AUTOID AT (PREVLEFT PREVBOTTOM) REPEATBUTTON "Dec" ID Button_demo_rpt_dec_button AT (10 PREVBOTTOM+3 20 AUTO) LEFTANCHOR FRAME FONT 0 FIELD ID Button_demo_rpt_label AT (70 PREVTOP 20 AUTO) DISABLED NUMERIC REPEATBUTTON "Inc" ID Button_demo_rpt_inc_button AT (130 PREVTOP 20 AUTO) LEFTANCHOR FRAME FONT 0 BUTTON "Back" ID Button_demo_back_button AT (5 PREVBOTTOM+5 AUTO AUTO) LEFTANCHOR FRAME FONT 0 END FORM ID Trigger_demo_form AT (0 0 160 160) BEGIN TITLE "Triggers and Lists" LABEL "Triggers allow popup access to lists," AUTOID AT (0 16) LABEL "which provide a simple way to make" AUTOID AT (PREVLEFT PREVBOTTOM) LABEL "simple multiple-choice selections" AUTOID AT (PREVLEFT PREVBOTTOM) POPUPTRIGGER "Language" ID Trigger_demo_form_popup_trigger AT (PREVLEFT+5 PREVBOTTOM+5 AUTO AUTO) LEFTANCHOR NOFRAME FONT 0 POPUPLIST ID Trigger_demo_form_popup_trigger Trigger_demo_form_popup_list LIST "Eiffel" "Sather" "Objective-C" "Python" "Ruby" "Haskell" "C" "Java" "C++" ID Trigger_demo_form_popup_list AT (PREVLEFT+5 PREVTOP+13 PREVWIDTH 55) NONUSABLE DISABLED FONT 0 LIST "Unix" "Windows" "AmigaOS" "PalmOS" ID Trigger_demo_form_regular_list AT (PREVRIGHT+30 PREVTOP PREVWIDTH PREVHEIGHT - 10) FONT 0 LABEL "Platform" AUTOID AT (PREVLEFT PREVTOP-13) LABEL "Language: " AUTOID AT (0 120) FIELD ID Trigger_demo_form_popup_field AT (PREVRIGHT+5 PREVTOP 60 AUTO) DISABLED LABEL "Platform: " AUTOID AT (0 PREVBOTTOM) FIELD ID Trigger_demo_form_regular_list_field AT (PREVRIGHT+5 PREVTOP 60 AUTO) DISABLED BUTTON "Back" ID Trigger_demo_back_button AT (5 PREVBOTTOM AUTO AUTO) LEFTANCHOR FRAME FONT 0 END FORM ID Field_demo_form AT (0 0 160 160) BEGIN TITLE "Fields" LABEL "Fields allow users to write in" AUTOID AT (0 16) LABEL "or display complex information." AUTOID AT (PREVLEFT PREVBOTTOM) LABEL "A decimal-to-hex converter: " AUTOID AT (PREVLEFT PREVBOTTOM+5) LABEL "dec: " AUTOID AT (PREVLEFT+5 PREVBOTTOM+2) FIELD ID Field_demo_dec_field AT (PREVRIGHT PREVTOP 30 AUTO) UNDERLINED NUMERIC MAXCHARS 6 BUTTON "Convert" ID Field_demo_convert_button AT (PREVRIGHT+5 PREVTOP AUTO AUTO) LEFTANCHOR FRAME FONT 0 LABEL "hex: " AUTOID AT (PREVRIGHT+5 PREVTOP) FIELD ID Field_demo_hex_field AT (PREVRIGHT+5 PREVTOP 25 AUTO) DISABLED LABEL "A string reverser:" AUTOID AT (0 PREVBOTTOM+2) FIELD ID Field_demo_reverse_field AT (PREVLEFT+5 PREVBOTTOM+2 70 AUTO) UNDERLINED MAXCHARS 20 BUTTON "Reverse" ID Field_demo_reverse_button AT (PREVRIGHT+5 PREVTOP AUTO AUTO) LEFTANCHOR FRAME FONT 0 LABEL "A scrollable field:" AUTOID AT (0 PREVBOTTOM+2) FIELD ID Field_demo_big_field AT (5 PREVBOTTOM 130 30) UNDERLINED MULTIPLELINES MAXCHARS 255 HASSCROLLBAR SCROLLBAR ID Field_demo_scrollbar AT (PREVRIGHT+2 PREVTOP 7 PREVHEIGHT) VALUE 0 MIN 0 MAX 10 PAGESIZE 5 BUTTON "Back" ID Field_demo_back_button AT (5 PREVBOTTOM AUTO AUTO) LEFTANCHOR FRAME FONT 0 END FORM ID Memory_demo_form AT (0 0 160 160) BEGIN TITLE "Memory" LABEL "Test to see if the garbage collector" AUTOID AT (0 16) LABEL "is working correctly. Press the" AUTOID AT (0 PREVBOTTOM-1) LABEL "'allocate' button to allocate a block" AUTOID AT (0 PREVBOTTOM-1) LABEL "of memory which is 'lost' to the" AUTOID AT (0 PREVBOTTOM-1) LABEL "application, and 'collect' to force" AUTOID AT (0 PREVBOTTOM-1) LABEL "a garbage collection cycle" AUTOID AT (0 PREVBOTTOM-1) LABEL "Heap ID: " AUTOID AT (0 PREVBOTTOM+3) FIELD ID Memory_demo_heap_id_field AT (PREVRIGHT+5 PREVTOP 60 AUTO) DISABLED LABEL "Free bytes: " AUTOID AT (0 PREVBOTTOM) FIELD ID Memory_demo_free_bytes_field AT (PREVRIGHT+5 PREVTOP 60 AUTO) DISABLED LABEL "Allocate " AUTOID AT (0 PREVBOTTOM+3) FIELD ID Memory_demo_allocate_size_field AT (PREVRIGHT PREVTOP 50 AUTO) UNDERLINED NUMERIC MAXCHARS 6 LABEL " bytes" AUTOID AT (PREVRIGHT PREVTOP) BUTTON "Allocate" ID Memory_demo_allocate_button AT (30 PREVBOTTOM+3 AUTO AUTO) LEFTANCHOR FRAME FONT 0 BUTTON "Collect" ID Memory_demo_collect_button AT (PREVRIGHT+5 PREVTOP AUTO AUTO) LEFTANCHOR FRAME FONT 0 BUTTON "Back" ID Memory_demo_back_button AT (5 PREVBOTTOM+3 AUTO AUTO) LEFTANCHOR FRAME FONT 0 END ALERT ID Generic_alert INFORMATION BEGIN TITLE "Not yet!" MESSAGE "The desired functionality has yet not been implemented." BUTTONS "OK" END ALERT ID Debug_alert INFORMATION BEGIN TITLE "Debug info" MESSAGE "msg1: ^1, msg2: ^2, msg3: ^3" BUTTONS "OK" END VERSION ID 1000 "1.0"
Take a look at all the different controls we are putting in; this
program is at least nominally more tricky than hello_world
.
The basic structure of the root class is no different:
class CONTROLS inherit FORM_DISPATCHER_CLIENT FORM_MANAGER_CLIENT CONTROLS_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 create main_form_event_handler create button_demo_form_event_handler create trigger_demo_form_event_handler create field_demo_form_event_handler create memory_demo_form_event_handler form_manager.go_to_form( Controls_main_form ) end handle_event( event : EVENT ) is --handles events local form : FORM do inspect event.type when Frm_load_event then inspect event.data_frmload_formid when Controls_main_form then create form.from_form_id( Controls_main_form ) form.set_as_active_form form.set_event_handler( main_form_event_handler ) when Button_demo_form then create form.from_form_id( Button_demo_form ) form.set_event_handler( button_demo_form_event_handler ) form.set_as_active_form when Trigger_demo_form then create form.from_form_id( Trigger_demo_form ) form.set_event_handler( trigger_demo_form_event_handler ) form.set_as_active_form when Field_demo_form then create form.from_form_id( Field_demo_form ) form.set_event_handler( field_demo_form_event_handler ) form.set_as_active_form when Memory_demo_form then create form.from_form_id( Memory_demo_form ) form.set_event_handler( memory_demo_form_event_handler ) form.set_as_active_form end last_event_was_handled := True else last_event_was_handled := False end end main_form_event_handler : MAIN_FORM_EVENT_HANDLER button_demo_form_event_handler : BUTTON_DEMO_FORM_EVENT_HANDLER trigger_demo_form_event_handler : TRIGGER_DEMO_FORM_EVENT_HANDLER field_demo_form_event_handler : FIELD_DEMO_FORM_EVENT_HANDLER memory_demo_form_event_handler : MEMORY_DEMO_FORM_EVENT_HANDLER end
Again, only start_application
and handle_event
are redefined;
in fact, they are virtually the only features in this class aside
from the event handlers. And their functions are identical to
those performed in 'hello_world'; start_application
initializes
the object's event handlers and then requests that the title form
be opened; handle_event
merely intercepts requests for various
forms, initializes the FORM
objects and event handlers, and
passes control back to the PalmOS. So fundamentally this is not
different from hello_world
except in scale.
For each of the forms we can open, we must set up an individual form. The title form is handled with some degree of ease, since it only has buttons:
class MAIN_FORM_EVENT_HANDLER inherit FORM_MANAGER_CLIENT CONTROLS_CONSTANTS STANDARD_EVENT_HANDLER redefine handle_frm_open_event, handle_menu_event, handle_ctl_select_event end feature {ANY} handle_frm_open_event( event : EVENT ) is do form_manager.active_form.draw last_event_was_handled := True end handle_menu_event( event : EVENT ) is do inspect event.data_menu_item_id when Controls_menu_button_demo then form_manager.popup_form( Button_demo_form ) else form_manager.alert( Generic_alert ) end last_event_was_handled := True; end handle_ctl_select_event( event : EVENT ) is do inspect event.data_ctlselect_controlid when Controls_button_demo_button then form_manager.popup_form( Button_demo_form ) when Controls_trigger_demo_button then form_manager.popup_form( Trigger_demo_form ) when Controls_field_demo_button then form_manager.popup_form( Field_demo_form ) when Controls_memory_demo_button then form_manager.popup_form( Memory_demo_form ) else form_manager.alert( Generic_alert ) end last_event_was_handled := True end end
Note the repeated use of 'form_manager.popup_form'; this feature "pops up" a form in front of the one already displayed. Until the "popped" form is closed, it remains the active form and control will be handled by its event handler.
The buttons form event handler,
button_demo_form_event_handler.e
, will also look quite similar,
because it mostly has buttons:
class BUTTON_DEMO_FORM_EVENT_HANDLER inherit FORM_MANAGER_CLIENT CONTROLS_CONSTANTS STANDARD_EVENT_HANDLER redefine handle_frm_open_event, handle_ctl_select_event, handle_ctl_repeat_event end feature {ANY} handle_frm_open_event( event : EVENT ) is local form : FORM do form := form_manager.active_form form.draw set_label_as_integer( form, Button_demo_reg_label, 50 ) set_label_as_integer( form, Button_demo_rpt_label, 50 ) set_label_as_integer( form, Button_demo_pushbutton_label, 1 ) form.set_control_group_selection_by_id( 1, Button_demo_pushbutton_a ) last_event_was_handled := True end handle_ctl_select_event( event : EVENT ) is do inspect event.data_ctlselect_controlid when Button_demo_reg_dec_button then decrement_reg_label( form_manager.active_form ) when Button_demo_reg_inc_button then increment_reg_label( form_manager.active_form ) when Button_demo_pushbutton_a then reset_pushbutton_group( form_manager.active_form ) when Button_demo_pushbutton_b then reset_pushbutton_group( form_manager.active_form ) when Button_demo_pushbutton_c then reset_pushbutton_group( form_manager.active_form ) when Button_demo_pushbutton_d then reset_pushbutton_group( form_manager.active_form ) when Button_demo_pushbutton_e then reset_pushbutton_group( form_manager.active_form ) when Button_demo_back_button then form_manager.return_to_form( Controls_main_form ) else end last_event_was_handled := True end handle_ctl_repeat_event( event : EVENT ) is do inspect event.data_ctlrepeat_controlid when Button_demo_rpt_dec_button then decrement_rpt_label( form_manager.active_form ) when Button_demo_rpt_inc_button then increment_rpt_label( form_manager.active_form ) else end end reset_pushbutton_group( form : FORM ) is do set_label_as_integer( form, Button_demo_pushbutton_label, form.control_group_selection( 1 ) - 7) end label_as_integer( form : FORM; label_id : INTEGER_16 ) : INTEGER_16 is local field : FORM_FIELD do field := form.field_from_id( label_id ); Result := field.contents.to_integer.to_integer_16 end set_label_as_integer( form : FORM; label_id, new_label : INTEGER_16 ) is local field : FORM_FIELD do field := form.field_from_id( label_id ) field.set_text_to_string( new_label.to_string ) field.draw end decrement_reg_label( form : FORM ) is do set_label_as_integer( form, Button_demo_reg_label, (label_as_integer( form, Button_demo_reg_label) - 1).max( 0 ) ) end increment_reg_label( form : FORM ) is do set_label_as_integer( form, Button_demo_reg_label, (label_as_integer( form, Button_demo_reg_label) + 1).min(99)) end decrement_rpt_label( form : FORM ) is do set_label_as_integer( form, Button_demo_rpt_label, (label_as_integer( form, Button_demo_rpt_label) - 1).max( 0 ) ) end increment_rpt_label( form : FORM ) is do set_label_as_integer( form, Button_demo_rpt_label, (label_as_integer( form, Button_demo_rpt_label) + 1).min(99)) end end
Note particularly the new redefined feature 'handle_ctl_repeat_event'; this is used for particular cases where controls are allowed to repeat themselves. Back when we were describing resources for this application, we used these definitions:
REPEATBUTTON "Dec" ID Button_demo_rpt_dec_button AT (10 PREVBOTTOM+3 20 AUTO) LEFTANCHOR FRAME FONT 0 FIELD ID Button_demo_rpt_label AT (70 PREVTOP 20 AUTO) DISABLED NUMERIC REPEATBUTTON "Inc" ID Button_demo_rpt_inc_button AT (130 PREVTOP 20 AUTO) LEFTANCHOR FRAME FONT 0
By declaring those two buttons as REPEATBUTTON
buttons, they
will continue to generate repeat events as long as they are held
down. This can be useful for a number of different applications,
but in this case holding them only decrements or increments a
number on the screen.
The "triggers" form has a few more event handlers that we haven't seen before:
class TRIGGER_DEMO_FORM_EVENT_HANDLER inherit FORM_MANAGER_CLIENT CONTROLS_CONSTANTS STANDARD_EVENT_HANDLER redefine handle_frm_open_event, handle_ctl_select_event, handle_pop_select_event, handle_lst_select_event end feature {ANY} handle_frm_open_event( event : EVENT ) is do form_manager.active_form.field_from_id( Trigger_demo_form_regular_list_field ).set_text_to_string( "Unix" ) form_manager.active_form.draw last_event_was_handled := True end handle_ctl_select_event( event : EVENT ) is do if event.data_ctlselect_controlid = Trigger_demo_back_button then form_manager.return_to_form( Controls_main_form ) last_event_was_handled := True end end handle_pop_select_event( event : EVENT ) is do inspect event.data_popselect_controlid when Trigger_demo_form_popup_trigger then set_popup_trigger_field( form_manager.active_form, event.data_popselect_list, event.data_popselect_selection ) last_event_was_handled := True else end end handle_lst_select_event( event : EVENT ) is do if event.data_listselect_listid = Trigger_demo_form_regular_list then set_regular_list_field( form_manager.active_form, event.data_listselect_list, event.data_listselect_selection ) last_event_was_handled := True end end set_popup_trigger_field( form : FORM; list : FORM_LIST; selection : INTEGER_16 ) is local field : FORM_FIELD do field := form.field_from_id( Trigger_demo_form_popup_field ) field.set_text_to_string( list.selection_text( selection ) ) field.draw end set_regular_list_field( form : FORM; list : FORM_LIST; selection : INTEGER_16 ) is local field : FORM_FIELD do field := form.field_from_id( Trigger_demo_form_regular_list_field ) field.set_text_to_string( list.selection_text( selection ) ) field.draw end end
Popup triggers are the usual "drop-down lists" users have come to
expect from GUI applications these days. The
handle_pop_select_event
feature handles events generated by the
user selecting a choice from the popup (or drop-down) menu. Here,
it simply identifies the control in question through the
event.data_popselect_controlid
feature and, if it's the expected
trigger, sets the field. The Pop_select_event
event holds
values for data_popselect_list
(the id of the list object
holding possible choices) and data_popselect_selection
(the
index of the chosen choice). Control then transfers to the
set_popup_trigger_field
feature, which creates a FORM_FIELD
object from the Trigger_demo_form_popup_field
id, and sets its
text to the string chosen by the user.
A near-identical process is used to handle list selection events,
and in fact handle_lst_select_event
and set_regular_list_field
are extremely similar to their popup variants.
The "fields" form also introduces some new ideas:
class FIELD_DEMO_FORM_EVENT_HANDLER inherit PALM_CHARS FORM_MANAGER_CLIENT CONTROLS_CONSTANTS STANDARD_EVENT_HANDLER redefine handle_frm_open_event, handle_ctl_select_event, handle_fld_changed_event, handle_scl_repeat_event, handle_key_down_event end feature {ANY} handle_frm_open_event( event : EVENT ) is do form_manager.active_form.draw update_big_field(form_manager.active_form) last_event_was_handled := True end handle_ctl_select_event( event : EVENT ) is do inspect event.data_ctlselect_controlid when Field_demo_convert_button then convert_dec_to_hex( form_manager.active_form ) last_event_was_handled := True when Field_demo_reverse_button then reverse_field( form_manager.active_form ) last_event_was_handled := True when Field_demo_back_button then form_manager.return_to_form( Controls_main_form ) last_event_was_handled := True else last_event_was_handled := False end end handle_fld_changed_event( event : EVENT ) is do if event.data_fldchanged_fieldid = Field_demo_big_field then update_big_field( form_manager.active_form ) last_event_was_handled := True; else last_event_was_handled := False end end handle_scl_repeat_event( event : EVENT ) is do if event.data_sclrepeat_scrollbarid = Field_demo_scrollbar then scroll_big_field( form_manager.active_form, event.data_sclrepeat_newvalue - event.data_sclrepeat_value ) end end handle_key_down_event( event : EVENT ) is do if event.data_keydown_chr = Page_up_chr then form_manager.active_form.field_from_id( Field_demo_big_field ).scroll_pages( -1 ) update_big_field( form_manager.active_form ) last_event_was_handled := True; elseif event.data_keydown_chr = Page_down_chr then form_manager.active_form.field_from_id( Field_demo_big_field ).scroll_pages( 1 ) update_big_field( form_manager.active_form ) last_event_was_handled := True; end end scroll_big_field( form : FORM; lines_to_scroll : INTEGER ) is do form.field_from_id( Field_demo_big_field ).scroll( lines_to_scroll.to_integer_16 ) update_big_field( form ) end update_big_field( form : FORM ) is local field : FORM_FIELD scrollbar : FORM_SCROLLBAR do field := form.field_from_id( Field_demo_big_field ) scrollbar := form.scrollbar_from_id( Field_demo_scrollbar ) scrollbar.set_scrollbar_values( field.scrollbar_values ) end convert_dec_to_hex( form: FORM ) is local new_contents : STRING source_field, target_field : FORM_FIELD do source_field := form.field_from_id( Field_demo_dec_field ) target_field := form.field_from_id( Field_demo_hex_field ) new_contents := source_field.contents.to_integer.to_hexadecimal target_field.set_text_to_string( new_contents ) target_field.draw end reverse_field( form : FORM ) is local contents : STRING field : FORM_FIELD do field := form.field_from_id( Field_demo_reverse_field ) contents := field.contents contents.reverse field.set_text_to_string( contents ) field.draw end end
The handle_ctl_select_event
feature intercepts button events
and passes control to convert_dec_to_hex
(which attempts a
conversion of the field contents (using field.contents
) from
decimal to hexadecimal and sets the on-screen field's contents
using the set_text_to_string
feature) or reverse_field
which
reverses the contents of the given field and again uses
set_text_to_string
to assign the field's contents.
The handle_fld_changed_event
feature is triggered whenever a
field's contents have been changed by the user. In this case, it
passes control to the update_big_field
feature if the big field
on the form has been changed; the update_big_field
feature sets
the new scrollbar values for the field's scrollbar. This is worth
noting; scrollbars attached to large scrolling fields must be
manually updated; you cannot simply rely on PalmOS to do that for
you!
Scroll repeat events (handle_scl_repeat_event
) are generated
when the scrollbar attached to a field is scrolled; again, these
must manually be responded to; in this case, by calling the
scroll_big_field
feature, which scrolls the field (using the
scroll
feature) and updates it, resetting the scrollbar again.
Finally, handle_key_down
handles "key" events--those generated
by pressing the buttons on the PalmOS device itself. In this
case, only two are handled, and those simply scroll the big field
a page up or down--but it's worth noting how key events are
handled.
One more form is left, which shows briefly some elements of memory management (this was done originally to demonstrate the garbage collector):
class MEMORY_DEMO_FORM_EVENT_HANDLER inherit MEMORY FORM_MANAGER_CLIENT CONTROLS_CONSTANTS STANDARD_EVENT_HANDLER redefine handle_frm_open_event, handle_ctl_select_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 inspect event.data_ctlselect_controlid when Memory_demo_allocate_button then allocate_memory( form_manager.active_form ) last_event_was_handled := True when Memory_demo_collect_button then collect( form_manager.active_form ) last_event_was_handled := True when Memory_demo_back_button then form_manager.return_to_form( Controls_main_form ) last_event_was_handled := True else last_event_was_handled := False end end last_heap_id : INTEGER_16 allocate_memory( form : FORM ) is local new_handle : MEM_HANDLE do create new_handle.allocate( form.field_from_id( Memory_demo_allocate_size_field ).contents.to_integer, True ) show_heap_status( form, last_heap_id ) end show_heap_status( form : FORM; heap_id : INTEGER_16 ) is local id_field : FORM_FIELD free_field : FORM_FIELD heap : MEM_HEAP do last_heap_id := heap_id id_field := form.field_from_id( Memory_demo_heap_id_field ) id_field.set_text_to_string( heap_id.to_string ) id_field.draw create heap.from_id( heap_id ) free_field := form.field_from_id( Memory_demo_free_bytes_field ) free_field.set_text_to_string( heap.free_bytes.to_string ) free_field.draw end collect( form : FORM ) is do full_collect show_heap_status( form, last_heap_id ) end end
The allocate_memory
feature demonstrates how blocks of memory
are allocated in PalmOS--by using MEM_HANDLE
objects, or handles
to blocks of memory. They are simple enough, using the allocate
creation feature of the MEM_HANDLE
class to allocate a block of
memory (see the MEM_HANDLE
short description for more details).
The 'show_heap_status feature simply updates a field showing how
much memory remains for allocation, and collect
simply calls the
MEMORY.full_collect
feature to force collection of garbage.
This form allows the user to create and allocate a MEM_HANDLE
and then drops the reference; when garbage is collected using
full_collect
, the memory used by those objects is freed using
their dispose
feature. You can see how not all the memory is
freed; SmartEiffel's linear allocation blocks are reused even if
memory owned by the objects therein is released.
It's also worth noting that this will happily allow the user to continue requesting blocks of memory; the allocation request will simply fail without warning! Obviously this is untenable for serious applications, and deserves some work in the future.