The "Controls" sample program

The "Controls" sample program

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.

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


And here are the resources:


#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 application:

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.

Handling the title form

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.

Handling the "buttons" form

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.

Triggers and Trigger Events

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.

Fields

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.

The "memory" form

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.

Powered by Zope