Hi

I am working through a table example and it compiles fine, but at run time I
get the following error.

Table.c Line:2153, Invalid Parameter

The program is not called Table.c  and the code only contains 1566 lines.
Where is this error coming from?

Below is my code minus the resource file.

------------------------------------

// CH.2 The super-include for the Palm OS
#include <Pilot.h>

// CH.5 Added for the call to GrfSetState()
#include <Graffiti.h>

// CH.3 Our resource file
#include "Contacts_res.h"

// CH.4 Prototypes for our event handler functions
static Boolean  contactDetailHandleEvent( EventPtr event );
static Boolean  aboutHandleEvent( EventPtr event );
static Boolean  enterTimeHandleEvent( EventPtr event );
static Boolean  contactListHandleEvent( EventPtr event );
static Boolean  menuEventHandler( EventPtr event );

// CH.4 Constants for ROM revision
#define ROM_VERSION_2   0x02003000
#define ROM_VERSION_MIN ROM_VERSION_2

// CH.5 Prototypes for utility functions
static void             newRecord( void );
static VoidPtr  getObject( FormPtr, Word );
static void             setFields( void );
static void             getFields( void );
static void             setText( FieldPtr, CharPtr );
static void             getText( FieldPtr, VoidPtr, Word );
static void             setDateTrigger( void );
static void             setTimeTrigger( void );
static void             setTimeControls( void );
static Int              sortFunc( CharPtr, CharPtr, Int );
static void             drawTable( void );
static void             drawCell( VoidPtr table, Word row, Word column,
                                RectanglePtr bounds );

// CH.5 Our open database reference
static DmOpenRef        contactsDB;
static ULong            numRecords;
static UInt                     cursor;
static Boolean          isDirty;
static VoidHand         hrecord;

// CH.5 Constants that define the database record
#define DB_ID_START                             0
#define DB_ID_SIZE                              (sizeof( ULong ))
#define DB_DATE_TIME_START              (DB_ID_START +\
                                                                DB_ID_SIZE)
#define DB_DATE_TIME_SIZE               (sizeof( DateTimeType ))
#define DB_FIRST_NAME_START             (DB_DATE_TIME_START +\
        
DB_DATE_TIME_SIZE)
#define DB_FIRST_NAME_SIZE              16
#define DB_LAST_NAME_START              (DB_FIRST_NAME_START +\
        
DB_FIRST_NAME_SIZE)
#define DB_LAST_NAME_SIZE               16
#define DB_PHONE_NUMBER_START   (DB_LAST_NAME_START +\
        
DB_LAST_NAME_SIZE)
#define DB_PHONE_NUMBER_SIZE    16
#define DB_RECORD_SIZE                  (DB_PHONE_NUMBER_START +\
        
DB_PHONE_NUMBER_SIZE)

// CH.6 Storage for the record's date and time in expanded form
static DateTimeType     dateTime;
static Word                     timeSelect;
#define NO_DATE         0
#define NO_TIME         0x7fff

// CH.7 The error exit macro
#define errorExit(alert) { ErrThrow( alert ); }

// CH.7 The sort order variable and constants
static Int sortBy;
// CH.7 NOTE: These items match the popup list entries!
#define SORTBY_DATE_TIME        0
#define SORTBY_FIRST_NAME       1
#define SORTBY_LAST_NAME        2

// CH.8 Table constants
#define TABLE_NUM_COLUMNS       3
#define TABLE_NUM_ROWS          11

#define TABLE_COLUMN_DATE       0
#define TABLE_COLUMN_TIME       1
#define TABLE_COLUMN_NAME       2
#define BLACK_UP_ARROW          "\x01"
#define BLACK_DOWN_ARROW        "\x02"
#define GRAY_UP_ARROW           "\x03"
#define GRAY_DOWN_ARROW         "\x04"

// CH.2 The main entry point
DWord PilotMain( Word cmd, Ptr, Word )
{
    DWord               romVersion;     // CH.4 ROM version
        FormPtr         form;   // CH.2 A pointer to our form structure
        EventType       event;  // CH.2 Our event structure
        Word            error;  // CH.3 Error word

    // CH.4 Get the ROM version
    romVersion = 0;
    FtrGet( sysFtrCreator, sysFtrNumROMVersion, &romVersion );

        // CH.4 If we are below our minimum acceptable ROM revision
    if( romVersion < ROM_VERSION_MIN )
    {
        // CH.4 Display the alert
        FrmAlert( LowROMVersionErrorAlert );

        // CH.4 PalmOS 1.0 will continuously re-launch this app
        // unless we switch to another safe one
        if( romVersion < ROM_VERSION_2 )
        {
            AppLaunchWithCommand( sysFileCDefaultApp,
                    sysAppLaunchCmdNormalLaunch, NULL );
        }
        return( 0 );
    }

        // CH.2 If this is not a normal launch, don't launch
        if( cmd != sysAppLaunchCmdNormalLaunch )
                return( 0 );

        // CH.5 Create a new database in case there isn't one
        if( ((error = DmCreateDatabase( 0, "ContactsDB-PPGU", 'PPGU',
'ctct',
                        false )) != dmErrAlreadyExists) && (error != 0) )
        {
                // CH.5 Handle db creation error
                FrmAlert( DBCreationErrorAlert );
                return( 0 );
        }
        
        // CH.5 Open the database
        contactsDB = DmOpenDatabaseByTypeCreator( 'ctct', 'PPGU',
                        dmModeReadWrite );

        // CH.5 Get the number of records in the database
        numRecords = DmNumRecords( contactsDB );

        // CH.5 Initialize the record number
        cursor = 0;

        // CH.7 Choose our starting page
        // CH.5 If there are no records, create one
        if( numRecords == 0 )
        {
                newRecord();
                FrmGotoForm( ContactDetailForm );       
        }
        else
                FrmGotoForm( ContactListForm );

        // CH.7 Begin the try block
        ErrTry {

        // CH.2 Our event loop
        do
        {
                // CH.2 Get the next event
                EvtGetEvent( &event, -1 );

                // CH.2 Handle system events
                if( SysHandleEvent( &event ) )
                        continue;

                // CH.3 Handle menu events
                if( MenuHandleEvent( NULL, &event, &error ) )
                        continue;

                // CH.4 Handle form load events
                if( event.eType == frmLoadEvent )
                {
                        // CH.4 Initialize our form
                        switch( event.data.frmLoad.formID )
                        {
                                // CH.4 Contact Detail form
                                case ContactDetailForm:
                                        form = FrmInitForm(
ContactDetailForm );
                                        FrmSetEventHandler( form,
contactDetailHandleEvent );
                                break;
                                
                                // CH.4 About form
                                case AboutForm:
                                        form = FrmInitForm( AboutForm );
                                        FrmSetEventHandler( form,
aboutHandleEvent );
                                break;

                                // CH.6 Enter Time form
                                case EnterTimeForm:
                                        form = FrmInitForm( EnterTimeForm );
                                        FrmSetEventHandler( form,
enterTimeHandleEvent );
                                break;
                                
                                // CH.7 Contact List form
                                case ContactListForm:
                                        form = FrmInitForm( ContactListForm
);
                                        FrmSetEventHandler( form,
contactListHandleEvent );
                                break;
                        }                               
                        FrmSetActiveForm( form );
                }
                        
                // CH.2 Handle form events
                FrmDispatchEvent( &event );
                
        // CH.2 If it's a stop event, exit
        } while( event.eType != appStopEvent );

        // CH.7 End the try block and do the catch block
        }
        ErrCatch( errorAlert )
        {
                // CH.7 Display the appropriate alert
                FrmAlert( errorAlert );
        } ErrEndCatch

        // CH.5 Close all open forms
        FrmCloseAllForms();
        
        // CH.5 Close the database
        DmCloseDatabase( contactsDB );
        
        // CH.2 We're done
        return( 0 );
}

// CH.4 Our Contact Detail form handler function
static Boolean contactDetailHandleEvent( EventPtr event )
{
        FormPtr         form;           // CH.3 A pointer to our form
structure
        VoidPtr         precord;        // CH.6 Points to a database record

        // CH.3 Get our form pointer
        form = FrmGetActiveForm();

        // CH.4 Parse events
        switch( event->eType )
        {
                // CH.4 Form open event
                case frmOpenEvent:
                {
                        // CH.2 Draw the form
                        FrmDrawForm( form );

                        // CH.5 Draw the database fields
                        setFields();
                }
                break;
        
                // CH.5 Form close event
                case frmCloseEvent:
                {
                        // CH.5 Store away any modified fields
                        getFields();
                }
                break;
                                
                // CH.5 Parse the button events
                case ctlSelectEvent:
                {
                        // CH.5 Store any field changes
                        getFields();
                        
                        switch( event->data.ctlSelect.controlID )
                        {
                                // CH.5 First button
                                case ContactDetailFirstButton:
                                {
                                        // CH.5 Set the cursor to the first
record
                                        if( cursor > 0 )
                                                cursor = 0;
                                }
                                break;
                                
                                // CH.5 Previous button
                                case ContactDetailPrevButton:
                                {
                                        // CH.5 Move the cursor back one
record
                                        if( cursor > 0 )
                                                cursor--;
                                }
                                break;
                                
                                // CH.5 Next button
                                case ContactDetailNextButton:
                                {
                                        // CH.5 Move the cursor up one
record
                                        if( cursor < (numRecords - 1) )
                                                cursor++;
                                }
                                break;
                                
                                // CH.5 Last button
                                case ContactDetailLastButton:
                                {
                                        // CH.5 Move the cursor to the last
record
                                        if( cursor < (numRecords - 1) )
                                                cursor = numRecords - 1;
                                }
                                break;
                                
                                // CH.5 Delete button
                                case ContactDetailDeleteButton:
                                {
                                        // CH.5 Remove the record from the
database
                                        DmRemoveRecord( contactsDB, cursor
);
                                        
                                        // CH.5 Decrease the number of
records
                                        numRecords--;
                                        
                                        // CH.5 Place the cursor at the
first record
                                        cursor = 0;

                                        // CH.5 If there are no records
left, create one
                                        if( numRecords == 0 )
                                                newRecord();
                                }
                                break;                                  
                                        
                                // CH.5 New button
                                case ContactDetailNewButton:
                                {
                                        // CH.5 Create a new record
                                        newRecord();
                                }
                                break;
                                
                                // CH.7 Done button
                                case ContactDetailDoneButton:
                                {
                                        // CH.7 Load the contact list
                                        FrmGotoForm( ContactListForm );
                                }
                                break;
                                
                                // CH.6 Date selector trigger
                                case ContactDetailDateSelTrigger:
                                {
                                        // CH.6 Initialize the date if
necessary
                                        if( dateTime.year == NO_DATE )
                                        {
                                                DateTimeType currentDate;
                                                
                                                // CH.6 Get the current date
                                                TimSecondsToDateTime(
TimGetSeconds(),
                                                                &currentDate
);
                                                                
                                                // CH.6 Copy it
                                                dateTime.year =
currentDate.year;
                                                dateTime.month =
currentDate.month;
                                                dateTime.day =
currentDate.day;
                                        }
                                        
                                        // CH.6 Pop up the system date
selection form
                                        SelectDay( selectDayByDay,
&(dateTime.month),
                                                        &(dateTime.day),
&(dateTime.year),
                                                        "Enter Date" );

                                        // CH.6 Get the record
                                        hrecord = DmQueryRecord( contactsDB,
cursor );
                                        
                                        // CH.6 Lock it down
                                        precord = MemHandleLock( hrecord );
                                        
                                        // CH.6 Write the date time field
                                        DmWrite( precord,
DB_DATE_TIME_START, &dateTime,
                                                        sizeof( DateTimeType
) );
                                        
                                        // CH.6 Unlock the record
                                        MemHandleUnlock( hrecord );
                                        
                                        // CH.6 Mark the record dirty
                                        isDirty = true;
                                }
                                break;
                                
                                // CH.6 Time selector trigger
                                case ContactDetailTimeSelTrigger:
                                {
                                        // CH.6 Pop up our selection form
                                        FrmPopupForm( EnterTimeForm );

                                }
                                break;
                        }
                        
                        // CH.5 Sync the current record to the fields
                        setFields();
                }
                break;
                
                // CH.5 Respond to field tap
                case fldEnterEvent:
                        isDirty = true;
                break;
                
                // CH.3 Parse menu events
                case menuEvent:
                        return( menuEventHandler( event ) );
                break;
        }

        // CH.2 We're done
        return( false );
}

// CH.4 Our About form event handler function
static Boolean aboutHandleEvent( EventPtr event )
{
        FormPtr         form;   // CH.4 A pointer to our form structure

        // CH.4 Get our form pointer
        form = FrmGetActiveForm();

        // CH.4 Respond to the Open event
        if( event->eType == frmOpenEvent )
        {
                // CH.4 Draw the form
                FrmDrawForm( form );
        }
        
        // CH.4 Return to the calling form
    if( event->eType == ctlSelectEvent )
    {
                FrmReturnToForm( 0 );
                
                // CH.4 Always return true in this case
                return( true );
        }
        
        // CH.4 We're done
        return( false );
}

// CH.6 Our Enter Time form event handler function
static Boolean enterTimeHandleEvent( EventPtr event )
{
        FormPtr                         form;           // CH.6 A form
structure pointer
        static DateTimeType     oldTime;        // CH.6 The original time

        // CH.6 Get our form pointer
        form = FrmGetActiveForm();

        // CH.6 Switch on the event
        switch( event->eType )
        {
                // CH.6 Initialize the form
                case frmOpenEvent:
                {
                        // CH.6 Store the time value
                        oldTime = dateTime;
                        
                        // CH.6 Draw it
                        FrmDrawForm( form );
                        
                        // CH.6 Set the time controls
                        setTimeControls();
                }
                break;
                
                // CH.6 If a button was repeated
                case ctlRepeatEvent:
                // CH.6 If a button was pushed
                case ctlSelectEvent:
                {
                        Word    buttonID;       // CH.6 The ID of the button
                
                        // CH.6 Set the ID
                        buttonID = event->data.ctlSelect.controlID;
                
                        // CH.6 Switch on button ID
                        switch( buttonID )
                        {
                                // CH.6 Hours button
                                case EnterTimeHoursPushButton:
                                // CH.6 Minute Tens button
                                case EnterTimeMinuteTensPushButton:
                                // CH.6 Minute Ones button
                                case EnterTimeMinuteOnesPushButton:
                                {
                                        // CH.6 If no time was set
                                        if( dateTime.hour == NO_TIME )
                                        {
                                                // CH.6 Set the time to 12
PM
                                                dateTime.hour = 12;
                                                dateTime.minute = 0;
                                                
                                                // CH.6 Set the controls
                                                setTimeControls();
                                        }       
                                        
                                        // CH.6 Clear the old selection if
any
                                        if( timeSelect )
                                                CtlSetValue( getObject(
form, timeSelect ),
                                                                false );
                                
                                        // CH.6 Set the new selection
                                        CtlSetValue( getObject( form,
buttonID ), true );
                                        timeSelect = buttonID;
                                }       
                                break;
                                
                                // CH.6 Up button
                                case EnterTimeTimeUpRepeating:
                                {
                                        // CH.6 If there's no time, do
nothing
                                        if( dateTime.hour == NO_TIME )
                                                break;
                                        
                                        // CH.6 Based on what push button is
selected
                                        switch( timeSelect )
                                        {
                                                // CH.6 Increase hours
                                                case
EnterTimeHoursPushButton:
                                                {
                                                        // CH.6 Increment
hours
                                                        dateTime.hour++;
                                                        
                                                        // CH.6 If it was 11
AM, make it 12 AM
                                                        if( dateTime.hour ==
12 )
        
dateTime.hour = 0;
                                                                
                                                        // CH.6 If it was 11
PM, make it 12 PM
                                                        if( dateTime.hour ==
24 )
        
dateTime.hour = 12;
                                                }
                                                break;
                                                
                                                // CH.6 Increase tens of
minutes
                                                case
EnterTimeMinuteTensPushButton:
                                                {
                                                        // CH.6 Increment
minutes
                                                        dateTime.minute +=
10;
                                                        
                                                        // CH.6 If it was
5X, roll over
                                                        if( dateTime.minute
> 59 )
        
dateTime.minute -= 60;
                                                }
                                                break;
                                                
                                                // CH.6 Increase minutes
                                                case
EnterTimeMinuteOnesPushButton:
                                                {
                                                        // CH.6 Increment
minutes
                                                        dateTime.minute++;
                                                        
                                                        // CH.6 If it is
zero, subtract ten
                                                        if( (dateTime.minute
% 10) == 0 )
        
dateTime.minute -= 10;
                                                }
                                                break;
                                        }
                                        
                                        // Revise the controls
                                        setTimeControls();
                                }
                                break;
                                
                                // CH.6 Down button
                                case EnterTimeTimeDownRepeating:
                                {
                                        
                                        // CH.6 If there's no time, do
nothing
                                        if( dateTime.hour == NO_TIME )
                                                break;
                                        
                                        // CH.6 Based on what push button is
selected
                                        switch( timeSelect )
                                        {
                                                // CH.6 Decrease hours
                                                case
EnterTimeHoursPushButton:
                                                {
                                                        // CH.6 Decrement
hours
                                                        dateTime.hour--;
                                                        
                                                        // CH.6 If it was 12
AM, make it 11 AM
                                                        if( dateTime.hour ==
-1 )
        
dateTime.hour = 11;
                                                                
                                                        // CH.6 If it was 12
PM, make it 11 PM
                                                        if( dateTime.hour ==
11 )
        
dateTime.hour = 23;
                                                }
                                                break;
                                                
                                                // CH.6 Decrease tens of
minutes
                                                case
EnterTimeMinuteTensPushButton:
                                                {
                                                        // CH.6 Decrement
minutes
                                                        dateTime.minute -=
10;
                                                        
                                                        // CH.6 If it was
0X, roll over
                                                        if( dateTime.minute
< 0 )
        
dateTime.minute += 60;
                                                }
                                                break;
                                                
                                                // CH.6 Decrease minutes
                                                case
EnterTimeMinuteOnesPushButton:
                                                {
                                                        // CH.6 Decrement
minutes
                                                        dateTime.minute--;
                                                        
                                                        // CH.6 If it is 9,
add ten
                                                        if( (dateTime.minute
% 10) == 9 )
        
dateTime.minute += 10;
                                                                
                                                        // CH.6 If less than
zero, make it 9
                                                        if( dateTime.minute
< 0 )
        
dateTime.minute = 9;
                                                }
                                                break;
                                        }
                                        
                                        // CH.6 Revise the controls
                                        setTimeControls();
                                }
                                break;
                                                                
                                // CH.6 AM button
                                case EnterTimeAMPushButton:
                                {
                                        // CH.6 If no time was set
                                        if( dateTime.hour == NO_TIME )
                                        {
                                                // CH.6 Set the time to 12
AM
                                                dateTime.hour = 0;
                                                dateTime.minute = 0;
                                                
                                                // CH.6 Set the controls
                                                setTimeControls();
                                        }       
                                        
                                        // CH.6 If it is PM
                                        if( dateTime.hour > 11 )
                                        {
                                                // CH.6 Change to AM
                                                dateTime.hour -= 12;
                                                
                                                // CH.6 Set the controls
                                                setTimeControls();
                                        }
                                }
                                break;
                                
                                // CH.6 PM button
                                case EnterTimePMPushButton:
                                {
                                        // CH.6 If no time was set
                                        if( dateTime.hour == NO_TIME )
                                        {
                                                // CH.6 Set the time to 12
PM
                                                dateTime.hour = 12;
                                                dateTime.minute = 0;
                                                
                                                // CH.6 Set the controls
                                                setTimeControls();
                                        }       
                                        
                                        // CH.6 If it is AM
                                        if( dateTime.hour < 12 )
                                        {
                                                // CH.6 Change to PM
                                                dateTime.hour += 12;
                                                
                                                // CH.6 Set the controls
                                                setTimeControls();
                                        }
                                }
                                break;
                                
                                // CH.6 No Time checkbox
                                case EnterTimeNoTimeCheckbox:
                                {
                                        // CH.6 If we are unchecking the box
                                        if( dateTime.hour == NO_TIME )
                                        {
                                                // CH.6 Set the time to 12
PM
                                                dateTime.hour = 12;
                                                dateTime.minute = 0;
                                                
                                                // CH.6 Set the controls
                                                setTimeControls();

                                                // CH.6 Set the new
selection
                                                timeSelect =
EnterTimeHoursPushButton;
                                                CtlSetValue( getObject(
form, timeSelect ),
                                                                true );
                                        }
                                        
                                        else
                                        // CH.6 If we are checking the box
                                                dateTime.hour = NO_TIME;
                                        
                                        // CH.6 Set the controls
                                        setTimeControls();
                                }
                                break;
                                
                                // CH.6 Cancel button
                                case EnterTimeCancelButton:
                                {
                                        // CH.6 Restore time
                                        dateTime = oldTime;
                                        
                                        // CH.6 Return to calling form
                                        FrmReturnToForm( 0 );
                                }
                                // CH.6 Always return true
                                return( true );
                                        
                                // CH.6 OK button
                                case EnterTimeOKButton:
                                {
                                        VoidPtr precord;        // CH.6
Points to the record
                        
                                        // CH.6 Lock it down
                                        precord = MemHandleLock( hrecord );
                                        
                                        // CH.6 Write the date time field
                                        DmWrite( precord,
DB_DATE_TIME_START, &dateTime,
                                                        sizeof( DateTimeType
) );
                                
                                        // CH.6 Unlock the record
                                        MemHandleUnlock( hrecord );
                                        
                                        // CH.6 Mark the record dirty
                                        isDirty = true;
                        
                                        // CH.6 Return to the Contact
Details form
                                        FrmReturnToForm( 0 );

                                        // CH.6 Update the field
                                        setTimeTrigger();
                                }
                                // CH.6 Always return true
                                return( true );
                        }
                }
                break;
        }
        
        // CH.6 We're done
        return( false );
}
                
// CH.7 Our Contact List form event handler function
static Boolean contactListHandleEvent( EventPtr event )
{
        FormPtr form;           // CH.7 A form structure pointer

        // CH.7 Get our form pointer
        form = FrmGetActiveForm();

        // CH.7 Parse events 
        switch( event->eType )
        {
                // CH.7 Form open event
                case frmOpenEvent:
                {
                        // CH.7 Draw the form   
                        FrmDrawForm( form );
                        
                        // CH.8 Populate and draw the table
                        drawTable();                    
                }
                break;
        
                // CH.7 Respond to a list selection
                case tblSelectEvent:
                {
                        // CH.7 Set the database cursor to the selected
contact
                        cursor += event->data.tblSelect.row;
                        
                        // CH.7 Go to contact details
                        FrmGotoForm( ContactDetailForm );
                }
                break;
                
                // CH.7 Respond to a menu event
                case menuEvent:
                        return( menuEventHandler( event ) );

                // CH.7 Respond to the popup trigger
                case popSelectEvent:
                {
                        // CH.7 If there is no change, we're done
                        if( sortBy == event->data.popSelect.selection )
                                return( true );
                        
                        // CH.7 Modify sort order variable
                        sortBy = event->data.popSelect.selection;
                        
                        // CH.7 Sort the contact database by the new
criteria
                        DmQuickSort( contactsDB, (DmComparF*)sortFunc,
sortBy );

                        // CH.8 Rebuild the table
                        drawTable();
                }
                break;
                
                // CH.8 Respond to arrows
                case ctlRepeatEvent:
                {
                        switch( event->data.ctlRepeat.controlID )
                        {
                                // CH.8 Up arrow
                                case ContactListRecordUpRepeating:
                                        if( cursor > 0 )
                                                cursor--;
                                break;
                                
                                // CH.8 Down arrow
                                case ContactListRecordDownRepeating:
                                        if( (numRecords > TABLE_NUM_ROWS) &&
                                                        (cursor < numRecords
- TABLE_NUM_ROWS) )
                                                cursor++;
                                break;
                        }
                        
                        // CH.8 Now refresh the table
                        drawTable();
                }
                return( true );
                
                // CH.8 Respond to up and down arrow hard keys
                case keyDownEvent:
                {
                        switch( event->data.keyDown.chr )
                        {
                                // CH.8 Up arrow hard key
                                case pageUpChr:
                                        if( cursor > TABLE_NUM_ROWS - 1 )
                                                cursor -= TABLE_NUM_ROWS -
1;
                                        else
                                                cursor = 0;
                                break;
                                
                                // CH.8 Down arrow hard key
                                case pageDownChr:
                                        if( (numRecords > 2 * TABLE_NUM_ROWS
- 1) &&
                                                        (cursor < numRecords
-
                                                        2 * TABLE_NUM_ROWS -
1) )
                                                cursor += TABLE_NUM_ROWS -
1;
                                        else
                                                cursor = numRecords -
TABLE_NUM_ROWS;
                                break;
                        }
                        
                        // CH.8 Now refresh the table
                        drawTable();
                }
                break;
                
                // CH.8 Respond to scrollbar events
                case sclRepeatEvent:
                        cursor = event->data.sclExit.newValue;
                        drawTable();
                break;
                
        }       // CH.7 End of the event switch statement
        
        // CH.7 We're done
        return( false );
}

// CH.3 Handle menu events
Boolean menuEventHandler( EventPtr event )
{
        FormPtr         form;           // CH.3 A pointer to our form
structure
        Word            index;          // CH.3 A general purpose control
index
        FieldPtr        field;          // CH.3 Used for manipulating fields

        // CH.3 Get our form pointer
        form = FrmGetActiveForm();

        // CH.3 Erase the menu status from the display
        MenuEraseStatus( NULL );
                        
        // CH.4 Handle options menu
        if( event->data.menu.itemID == OptionsAboutContacts )
        {
                // CH.4 Pop up the About form as a Dialog
                FrmPopupForm( AboutForm );
                return( true );
        }
                
        // CH.3 Handle graffiti help
        if( event->data.menu.itemID == EditGraffitiHelp )
        {
                // CH.3 Pop up the graffiti reference based on
                // the graffiti state
                SysGraffitiReferenceDialog( referenceDefault );
                return( true );
        }

        // CH.3 Get the index of our field
        index = FrmGetFocus( form );
                        
        // CH.3 If there is no field selected, we're done
        if( index == noFocus )
                return( false );
                        
        // CH.3 Get the pointer of our field
        field = FrmGetObjectPtr( form, index );
                        
        // CH.3 Do the edit command
        switch( event->data.menu.itemID )
        {
                // CH.3 Undo
                case EditUndo:
                        FldUndo( field );
                break;
                
                // CH.3 Cut
                case EditCut:
                        FldCut( field );
                break;
                                
                // CH.3 Copy
                case EditCopy:
                        FldCopy( field );
                break;
                        
                // CH.3 Paste
                case EditPaste:
                        FldPaste( field );
                break;
                                
                // CH.3 Select All
                case EditSelectAll:
                {
                        // CH.3 Get the length of the string in the field
                        Word length = FldGetTextLength( field );
                                
                        // CH.3 Sound an error if appropriate
                        if( length == 0 )
                        {
                                SndPlaySystemSound( sndError );
                                return( false );
                        }
                        
                        // CH.3 Select the whole string
                        FldSetSelection( field, 0, length );

                }
                break;
                                
                // CH.3 Bring up the keyboard tool
                case EditKeyboard:
                        SysKeyboardDialogV10();
                break;
        }
        
        // CH.3 We're done      
        return( true );
}

// CH.5 This function creates and initializes a new record
static void newRecord( void )
{
        VoidPtr         precord;        // CH.5 Pointer to the record
        
        // CH.7 Create the database record and get a handle to it
        if( (hrecord = DmNewRecord( contactsDB, &cursor,
                        DB_RECORD_SIZE )) == NULL )
                errorExit( MemoryErrorAlert );
        
        // CH.5 Lock down the record to modify it
        precord = MemHandleLock( hrecord );
        
        // CH.5 Clear the record
        DmSet( precord, 0, DB_RECORD_SIZE, 0 );
        
        // CH.6 Initialize the date and time
        MemSet( &dateTime, sizeof( dateTime ), 0 );
        dateTime.year = NO_DATE;
        dateTime.hour = NO_TIME;
        DmWrite( precord, DB_DATE_TIME_START, &dateTime,
                        sizeof( DateTimeType ) );
        
        // CH.5 Unlock the record
        MemHandleUnlock( hrecord );
        
        // CH.5 Clear the busy bit and set the dirty bit
        DmReleaseRecord( contactsDB, cursor, true );
        
        // CH.5 Increment the total record count
        numRecords++;

        // CH.5 Set the dirty bit
        isDirty = true;
        
        // CH.5 We're done
        return;
}

// CH.5 A time saver: Gets object pointers based on their ID
static VoidPtr getObject( FormPtr form, Word objectID )
{
        Word    index;  // CH.5 The object index
        
        // CH.5 Get the index
        index = FrmGetObjectIndex( form, objectID );
        
        // CH.5 Return the pointer
        return( FrmGetObjectPtr( form, index ) );
}

// CH.5 Gets the current database record and displays it
// in the detail fields
static void setFields( void )
{
        FormPtr         form;           // CH.5 The contact detail form
        CharPtr         precord;        // CH.5 A record pointer
        Word            index;          // CH.5 The object index
        
        // CH.5 Get the contact detail form pointer
        form = FrmGetActiveForm();
                
        // CH.5 Get the current record
        hrecord = DmQueryRecord( contactsDB, cursor );
                
        // CH.6 Initialize the date and time variable
        precord = MemHandleLock( hrecord );
        MemMove( &dateTime, precord + DB_DATE_TIME_START,
                        sizeof( dateTime ) );

        // CH.6 Initialize the date control
        setDateTrigger();
                
        // CH.6 Initialize the time control
        setTimeTrigger();
        
        // CH.5 Set the text for the First Name field
        setText( getObject( form, ContactDetailFirstNameField ),
                        precord + DB_FIRST_NAME_START );
                
        // CH.5 Set the text for the Last Name field
        setText( getObject( form, ContactDetailLastNameField ),
                        precord + DB_LAST_NAME_START );
                
        // CH.5 Set the text for the Phone Number field
        setText( getObject( form, ContactDetailPhoneNumberField ),
                        precord + DB_PHONE_NUMBER_START );
        MemHandleUnlock( hrecord );

        // CH.5 If the record is already dirty, it's new, so set focus
        if( isDirty )
        {
                // CH.3 Get the index of our field
                index = FrmGetObjectIndex( form, ContactDetailFirstNameField
);

                // CH.3 Set the focus to the First Name field
                FrmSetFocus( form, index );
                
                // CH.5 Set upper shift on
                GrfSetState( false, false, true );                      
        }
        
        // CH.5 We're done
        return;
}

// CH.5 Puts any field changes in the record
static void getFields( void )
{
        FormPtr         form;           // CH.5 The contact detail form
        
        // CH.5 Get the contact detail form pointer
        form = FrmGetActiveForm();
                
        // CH.5 Turn off focus
        FrmSetFocus( form, -1 );
                        
        // CH.5 If the record has been modified
        if( isDirty )
        {
                CharPtr precord;        // CH.5 Points to the DB record
        
                // CH.7 Detach the record from the database
                DmDetachRecord( contactsDB, cursor, &hrecord );
                
                // CH.5 Lock the record
                precord = MemHandleLock( hrecord );
                
                // CH.5 Get the text for the First Name field
                getText( getObject( form, ContactDetailFirstNameField ),
                                precord, DB_FIRST_NAME_START );
                
                // CH.5 Get the text for the Last Name field
                getText( getObject( form, ContactDetailLastNameField ),
                                precord, DB_LAST_NAME_START );
                
                // CH.5 Get the text for the Phone Number field
                getText( getObject( form, ContactDetailPhoneNumberField ),
                                precord, DB_PHONE_NUMBER_START );
                
                // CH.7 Find the proper position
                cursor = DmFindSortPosition( contactsDB, precord, NULL,
                                (DmComparF*)sortFunc, sortBy );
                
                // CH.5 Unlock the record
                MemHandleUnlock( hrecord );
                
                // CH.7 Reattach the record
                DmAttachRecord( contactsDB, &cursor, hrecord, NULL );
        }

        // CH.5 Reset the dirty bit
        isDirty = false;
                                
        // CH.5 We're done
        return;
}

// CH.5 Set the text in a field
static void setText( FieldPtr field, CharPtr text )
{
        VoidHand        hfield; // CH.5 Handle of field text
        CharPtr         pfield; // CH.5 Pointer to field text
        
        // CH.5 Get the current field handle
        hfield = FldGetTextHandle( field );
        
        // CH.5 If we have a handle
        if( hfield != NULL )
        {
                // CH.5 Resize it
                if( MemHandleResize( hfield, StrLen( text ) + 1 ) != 0 )
                        errorExit( MemoryErrorAlert );
        }
        
        else
        // CH.5 Allocate a handle for the string
        {
                hfield = MemHandleNew( StrLen( text ) + 1 );
                if( hfield == NULL )
                        errorExit( MemoryErrorAlert );
        }
        
        // CH.5 Lock it
        pfield = MemHandleLock( hfield );
        
        // CH.5 Copy the string
        StrCopy( pfield, text );
        
        // CH.5 Unlock it
        MemHandleUnlock( hfield );
        
        // CH.5 Give it to the field
        FldSetTextHandle( field, hfield );
        
        // CH.5 Draw the field
        FldDrawField( field );
        
        // CH.5 We're done
        return;
}       

// CH.5 Get the text from a field
static void getText( FieldPtr field, VoidPtr precord, Word offset )
{
        CharPtr         pfield; // CH.5 Pointer to field text

        // CH.5 Get the text pointer
        pfield = FldGetTextPtr( field );
        
        // CH.5 Copy it
        DmWrite( precord, offset, pfield, StrLen( pfield ) );
        
        // CH.5 We're done
        return;
}

// CH.6 Set the Contact Detail date selector trigger
static void setDateTrigger( void )
{
        FormPtr         form;           // CH.5 The contact detail form
        
        // CH.6 Get the contact detail form pointer
        form = FrmGetActiveForm();
        
        // CH.6 If there is no date
        if( dateTime.year == NO_DATE )
        {
                CtlSetLabel( getObject( form, ContactDetailDateSelTrigger ),
                                "          " );
        }
        
        else
        // CH.6 If there is a date
        {
                Char dateString[dateStringLength];
                
                // CH.6 Get the date string
                DateToAscii( dateTime.month, dateTime.day, dateTime.year,
                                (DateFormatType)PrefGetPreference(
prefDateFormat ), dateString );
                
                // CH.6 Set the selector trigger label
                CtlSetLabel( getObject( form, ContactDetailDateSelTrigger ),
                                dateString );
                                
        }
        
        // CH.6 We're done
        return;
}

// CH.6 Set the Contact Detail time selector trigger
static void setTimeTrigger( void )
{
        FormPtr         form;           // CH.5 The contact detail form
        
        // CH.6 Get the contact detail form pointer
        form = FrmGetActiveForm();
        
        // CH.6 If there's no time
        if( dateTime.hour == NO_TIME )
        {
                CtlSetLabel( getObject( form, ContactDetailTimeSelTrigger ),
                                "          " );
        }
        
        else
        // CH.6 If there is a time
        {
                Char timeString[timeStringLength];
                
                // CH.6 Get the time string
                TimeToAscii( dateTime.hour, dateTime.minute,
                                (TimeFormatType)PrefGetPreference(
prefTimeFormat ), timeString );
                
                // CH.6 Set the selector trigger label
                CtlSetLabel( getObject( form, ContactDetailTimeSelTrigger ),
                                timeString );
                                
        }

        // CH.6 We're done
        return;
}

// CH.6 Set the controls in the Enter Time form based on dateTime
static void setTimeControls( void )
{
        FormPtr         form;
        ControlPtr      hourButton;
        ControlPtr      minuteTensButton;
        ControlPtr      minuteOnesButton;
        ControlPtr      amButton;
        ControlPtr      pmButton;
        ControlPtr      noTimeCheckbox;
        Char            labelString[3];
        SWord           hour;
        
        // CH.6 Get the form
        form = FrmGetActiveForm();
        
        // CH.6 Get the control pointers
        hourButton = getObject( form, EnterTimeHoursPushButton );
        minuteTensButton = getObject( form,
                        EnterTimeMinuteTensPushButton );
        minuteOnesButton = getObject( form,
                        EnterTimeMinuteOnesPushButton );
        amButton = getObject( form, EnterTimeAMPushButton );
        pmButton = getObject( form, EnterTimePMPushButton );
        noTimeCheckbox = getObject( form, EnterTimeNoTimeCheckbox );
        
        // CH.6 If there is a time
        if( dateTime.hour != NO_TIME )
        {
                // CH.6 Update the hour
                hour = dateTime.hour % 12;
                if( hour == 0 )
                        hour = 12;
                CtlSetLabel( hourButton,
                                StrIToA( labelString, hour ) );

                // CH.6 Update the minute tens
                CtlSetLabel( minuteTensButton,
                                StrIToA( labelString, dateTime.minute / 10 )
);
        
                // CH.6 Update the minute ones
                CtlSetLabel( minuteOnesButton,
                                StrIToA( labelString, dateTime.minute % 10 )
);
        
                // CH.6 Update AM
                CtlSetValue( amButton, (dateTime.hour < 12) );
                                
                // CH.6 Update PM
                CtlSetValue( pmButton, (dateTime.hour > 11) );

                // CH.6 Uncheck the no time checkbox
                CtlSetValue( noTimeCheckbox, false );
        }
        
        else
        // If there is no time
        {
                // CH.6 Update the hour
                CtlSetValue( hourButton, false );
                CtlSetLabel( hourButton, "" );
                
                // CH.6 Update the minute tens
                CtlSetValue( minuteTensButton, false );
                CtlSetLabel( minuteTensButton, "" );
                
                // CH.6 Update the minute ones
                CtlSetValue( minuteOnesButton, false );
                CtlSetLabel( minuteOnesButton, "" );
                
                // CH.6 Update AM
                CtlSetValue( amButton, false );
                                
                // CH.6 Update PM
                CtlSetValue( pmButton, false );

                // CH.6 Uncheck the no time checkbox
                CtlSetValue( noTimeCheckbox, true );
        }
        
        // CH.6 We're done
        return;
}

// CH.7 This function is called by Palm OS to sort records
static Int sortFunc( CharPtr precord1, CharPtr precord2, Int sortBy )
{
        Int sortResult;
        
        // CH.7 Switch based on sort criteria
        switch( sortBy )
        {
                // CH.7 Sort by date and time
                case SORTBY_DATE_TIME:
                {
                        DateTimePtr pdateTime1;
                        DateTimePtr pdateTime2;
                        Long lDiff;
                        
                        pdateTime1 = (DateTimePtr)(precord1 +
DB_DATE_TIME_START);
                        pdateTime2 = (DateTimePtr)(precord2 +
DB_DATE_TIME_START);
                        
                        // CH.7 Compare the dates and times
                        lDiff = (Long)(TimDateTimeToSeconds( pdateTime1 ) /
60 ) -
                                        (Long)(TimDateTimeToSeconds(
pdateTime2 ) / 60 );
                        
                        // CH.7 Date/time #1 is later
                        if( lDiff > 0 )
                                sortResult = 1;
                        
                        else    
                        // CH.7 Date/time #2 is later
                        if( lDiff < 0 )
                                sortResult = -1;
                                
                        else
                        // CH.7 They are equal
                                sortResult = 0;
                }
                break;          

                // CH.7 Sort by first name
                case SORTBY_FIRST_NAME:
                {
                        sortResult = StrCompare( precord1 +
DB_FIRST_NAME_START,
                                        precord2 + DB_FIRST_NAME_START );
                }
                break;
                
                // CH.7 Sort by last name
                case SORTBY_LAST_NAME:
                {
                        sortResult = StrCompare( precord1 +
DB_LAST_NAME_START,
                                        precord2 + DB_LAST_NAME_START );
                }
                break;          
        }
        
        // CH.7 We're done
        return( sortResult );
}

// CH.8 Draw our list of choices using a table object
static void drawTable( void )
{
        FormPtr         form;
        TablePtr        table;
        Int                     column;
        Int                     count;
        ControlPtr      upArrow;
        ControlPtr      downArrow;
        
        // CH.8 Get the form pointer
        form = FrmGetActiveForm();
        
        // CH.8 Get the table pointer
        table = getObject( form, ContactListTableTable );
        
        // CH.8 For all columns
        for( column = 0; column < TABLE_NUM_COLUMNS; column++ )
        {
                // CH.8 Set the draw routine
                TblSetCustomDrawProcedure( table, column, drawCell );
                
                // CH.8 Make the column visible
                TblSetColumnUsable( table, column, true );
        }
        
        // CH.8 Initialize the table styles
        for( count = 0; count < TABLE_NUM_ROWS; count++ )
        {
                // CH.8 If there is data
                if( count < numRecords )
                {
                        // CH.8 Show the row
                        TblSetRowUsable( table, count, true );
                
                        // CH.8 Set the cell styles     
                        for( column = 0; column < TABLE_NUM_COLUMNS;
column++ )
                                TblSetItemStyle( table, count, column,
customTableItem );
                }
                
                else
                // CH.8 Hide unused rows if any
                        TblSetRowUsable( table, count, false );
        }
                
        // CH.8 Draw the table
        TblDrawTable( table );

        // CH.8 Get pointers to the arrow buttons
        upArrow = getObject( form, ContactListRecordUpRepeating );
        downArrow = getObject( form, ContactListRecordDownRepeating );

        // CH.8 Update the arrow buttons and scrollbars
        if( numRecords > TABLE_NUM_ROWS )
        {
                // CH.8 Show the up arrow
                if( cursor > 0 )
                {
                        CtlSetLabel( upArrow, BLACK_UP_ARROW );
                        CtlSetEnabled( upArrow, true );
                }
                else
                {
                        CtlSetLabel( upArrow, GRAY_UP_ARROW );
                        CtlSetEnabled( upArrow, false );
                }
                CtlShowControl( upArrow );
                
                // CH.8 Show the down arrow
                if( cursor >= numRecords - TABLE_NUM_ROWS )
                {
                        CtlSetLabel( downArrow, GRAY_DOWN_ARROW );
                        CtlSetEnabled( downArrow, false );
                }
                else
                {
                        CtlSetLabel( downArrow, BLACK_DOWN_ARROW );
                        CtlSetEnabled( downArrow, true );
                }
                CtlShowControl( downArrow );
                
                // CH.8 Show the scrollbar
                FrmShowObject( form, FrmGetObjectIndex( form,
                                ContactListScrollbarScrollBar ) );
                SclSetScrollBar( getObject( form,
                                ContactListScrollbarScrollBar ), cursor, 0,
                                numRecords - TABLE_NUM_ROWS, TABLE_NUM_ROWS
);
        }
        else
        {
                // CH.8 Hide the arrows
                CtlHideControl( upArrow );
                CtlHideControl( downArrow );
                
                // CH.8 Hide the scrollbar
                FrmHideObject( form, FrmGetObjectIndex( form,
                                ContactListScrollbarScrollBar ) );
        }
        
        // CH.8 We're done
        return;
}

// CH.8 The custom drawing routine for a table cell
static void drawCell( VoidPtr table, Word row, Word column,
                RectanglePtr bounds )
{
        Int             record;
        CharPtr precord;
        Char    string[DB_FIRST_NAME_SIZE + DB_LAST_NAME_SIZE];
        SWord   width;
        SWord   len;
        Boolean noFit;
        
        // CH.8 Calculate our record
        record = cursor + row;

        // CH.8 Get our record
        hrecord = DmQueryRecord( contactsDB, record );
        precord = MemHandleLock( hrecord );

        // CH.8 Get the date and time
        MemMove( &dateTime, precord + DB_DATE_TIME_START,
                        sizeof( dateTime ) );
                
        // CH.8 Switch on the column
        switch( column )
        {
                // CH.8 Handle dates
                case TABLE_COLUMN_DATE:
                {
                        if( dateTime.year != NO_DATE )
                        {
                                DateToAscii( dateTime.month, dateTime.day,
                                                dateTime.year,
        
(DateFormatType)PrefGetPreference(
                                                prefDateFormat ), string );
                        }
                        else
                                StrCopy( string, "-" );
                }
                break;

                // CH.8 Handle times
                case TABLE_COLUMN_TIME:
                {
                        if( dateTime.hour != NO_TIME )
                        {
                                TimeToAscii( dateTime.hour, dateTime.minute,
        
(TimeFormatType)PrefGetPreference(
                                                prefTimeFormat ), string );
                        }
                        else
                                StrCopy( string, "-" );
                }
                break;

                // CH.8 Handle names
                case TABLE_COLUMN_NAME:
                {
                        StrCopy( string, precord + DB_FIRST_NAME_START );
                        StrCat( string, " " );
                        StrCat( string, precord + DB_LAST_NAME_START );
                }
                break;
        }

        // CH.8 Unlock the record
        MemHandleUnlock( hrecord );

        // CH.8 Set the text mode
        WinSetUnderlineMode( noUnderline );
        FntSetFont( stdFont );
        
        // CH.8 Truncate the string if necessary
        width = bounds->extent.x;
        len = StrLen( string );
        noFit = false;
        FntCharsInWidth( string, &width, &len, &noFit );
        
        // CH.8 Draw the cell
        WinEraseRectangle( bounds, 0 );
        WinDrawChars( string, len, bounds->topLeft.x, bounds->topLeft.y );
        
        // CH.8 We're done
        return;
}

-- 
For information on using the Palm Developer Forums, or to unsubscribe, please see 
http://www.palmos.com/dev/tech/support/forums/

Reply via email to