contacts.c

来自「CD_《Palm OS编程实践》」· C语言 代码 · 共 583 行

C
583
字号
// 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	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 );

// 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.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.5 If there are no records, create one
	if( numRecords == 0 )
		newRecord();

	// CH.4 Go to our starting page
	FrmGotoForm( ContactDetailForm );

	// 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;
			}				
			FrmSetActiveForm( form );
		}
			
		// CH.2 Handle form events
		FrmDispatchEvent( &event );
		
	// CH.2 If it's a stop event, exit
	} while( event.eType != appStopEvent );

	// 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

	// 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.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.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.5 Create the database record and get a handle to it
	hrecord = DmNewRecord( contactsDB, &cursor, DB_RECORD_SIZE );
	
	// 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.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 );
	precord = MemHandleLock( hrecord );
		
	// 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
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.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.5 Unlock the record
		MemHandleUnlock( hrecord );
	}

	// 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
		MemHandleResize( hfield, StrLen( text ) + 1 );
	}
	
	else
	// CH.5 Allocate a handle for the string
		hfield = MemHandleNew( StrLen( text ) + 1 );
	
	// 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;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?