SourceForge.net Logo
/******************************************************************************
 *
 * Copyright (c) 1995-2000 Palm, Inc. or its subsidiaries.
 * All rights reserved.
 *
 * File: Datebook.c
 *
 * Release: Palm OS SDK 4.0 (63220)
 *
 * Description:
 *  This is the Datebook application's main module.  This module
 *   starts the application, dispatches events, and stops
 *   the application.
 *
 * History:
 *June 12, 1995Created by Art Lamb
 *NameDateDescription
 *-------------------
 *???????Initial Revision
 *frigino970909Added alarmSoundUniqueRecID to DatebookPreferenceType
 *to remember the alarm sound to play. Moved
 *DatebookPreferenceType out of this file.
 *grant3/5/99Removed dependece on MemDeref and MemoryPrv.h.
 *DetailsH was a handle that was always left locked;
 *replaced by a pointer DetailsP.
 *rbb4/9/99Removed time bar and end-time for zero-duration appts
 *rbb4/22/99Added snooze
 *rbb6/10/99Removed obsoleted code that worked around
 *single-segment linker limitation
 *grant6/28/99New global - RepeatDetailsP.  When editing an event's details,
 *there is one details info block that is pointed to by either
 *DetailsP or RepeatDetailsP but not both.  When the "Details"
 *form is active, then DetailsP is valid and RepeatDetailsP
 *should be NULL.  And vice versa for the "Repeat" form.
 *
 *****************************************************************************/

#include <PalmOS.h>

#include <PalmUtils.h>

#include "Datebook.h"

extern ECApptDBValidate (DmOpenRef dbP);


/***********************************************************************
 *
 *Global variables, declarded in DateGlobals.c.  Because of a bug in
 *  the Metrowerks compiler, we must compile the globals separately with
 * PC-relative strings turned off.
 *
 ***********************************************************************/

externUInt16TopVisibleAppt;
extern privateRecordViewEnumCurrentRecordVisualStatus;// applies to current record
extern privateRecordViewEnumPrivateRecordVisualStatus;// applies to all other records

// The following global variables are used to keep track of the edit
// state of the application.
extern UInt16CurrentRecord;// record being edited
extern BooleanItemSelected;// true if a day view item is selected
extern UInt16DayEditPosition;// position of the insertion point in the desc field
externUInt16DayEditSelectionLength;// length of the current selection.
externBooleanRecordDirty;// true if a record has been modified


/***********************************************************************
 *
 *Internal Functions
 *
 ***********************************************************************/

static void EventLoop (void);


/***********************************************************************
 *
 *Test Code
 *
 ***********************************************************************/

// Useful structure field offset and size macros
#define prvFieldOffset(type, field)((UInt32)(&((type*)0)->field))
#define prvFieldSize(type, field)(sizeof(((type*)0)->field))



/***********************************************************************
 *
 * FUNCTION:     SetDBBackupBit
 *
 * DESCRIPTION:  This routine sets the backup bit on the given database.
 *  This is to aid syncs with non Palm software.
 *  If no DB is given, open the app's default database and set
 *  the backup bit on it.
 *
 * PARAMETERS:   dbP -the database to set backup bit,
 *can be NULL to indicate app's default database
 *
 * RETURNED:     nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *grant4/1/99Initial Revision
 *
 ***********************************************************************/
static void SetDBBackupBit(DmOpenRef dbP)
{
  DmOpenRef localDBP;
  LocalID dbID;
  UInt16 cardNo;
  UInt16 attributes;

  // Open database if necessary. If it doesn't exist, simply exit (don't create it).
  if (dbP == NULL)
    {
      localDBP = DmOpenDatabaseByTypeCreator (datebookDBType, sysFileCDatebook, dmModeReadWrite);
      if (localDBP == NULL)  return;
    }
  else
    {
      localDBP = dbP;
    }
  
  // now set the backup bit on localDBP
  DmOpenDatabaseInfo(localDBP, &dbID, NULL, NULL, &cardNo, NULL);
  DmDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
		 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  attributes |= dmHdrAttrBackup;
  DmSetDatabaseInfo(cardNo, dbID, NULL, &attributes, NULL, NULL,
		    NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  
  // close database if necessary
  if (dbP == NULL) 
    {
      DmCloseDatabase(localDBP);
    }
}


/***********************************************************************
 *
 * FUNCTION:RegisterData
 *
 * DESCRIPTION:Register with the Exchange Manager to receive .vcs and
 *text/x-vCalendar.
 *
 * PARAMETERS:none
 *
 * RETURNED:nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *dje7/28/00Initial Revision
 *
 ***********************************************************************/
static void RegisterData(void)
{
  MemHandle resH = DmGetResource(strRsc, exgDescriptionStrID);
  void *desc = MemHandleLock(resH);
  
  ExgRegisterDatatype(sysFileCDatebook, exgRegExtensionID, dateExtension, (Char *)desc, 0);
  ExgRegisterDatatype(sysFileCDatebook, exgRegTypeID, dateMIMEType, (Char *)desc, 0);
  MemHandleUnlock(resH);
  DmReleaseResource(resH);
}


/***********************************************************************
 *
 * FUNCTION:     DateGetDatabase
 *
 * DESCRIPTION:  Get the application's database.  Open the database if it
 * exists, create it if neccessary.
 *
 * PARAMETERS:   *dbPP - pointer to a database ref (DmOpenRef) to be set
 *  mode - how to open the database (dmModeReadWrite)
 *
 * RETURNED:     Err - zero if no error, else the error
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *roger12/3/97Initial Revision
 *
 ***********************************************************************/
static Err DateGetDatabase (DmOpenRef *dbPP, UInt16 mode)
{
  Err error = 0;
  DmOpenRef dbP;
  UInt16 cardNo;
  LocalID dbID;
  
  
  *dbPP = 0;
  dbP = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, mode);
  if (! dbP)
    {
      error = DmCreateDatabase (0, datebookDBName, sysFileCDatebook,
				datebookDBType, false);
      if (error) return error;
      
      dbP = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, mode);
      if (! dbP) return ~0;

      // Set the backup bit.  This is to aid syncs with non Palm software.
      SetDBBackupBit(dbP);
      
      error = ApptAppInfoInit (dbP);
      if (error) 
	{
	  DmOpenDatabaseInfo(dbP, &dbID, NULL, NULL, &cardNo, NULL);
	  DmCloseDatabase(dbP);
	  DmDeleteDatabase(cardNo, dbID);
	  return error;
	}
    }
  
  *dbPP = dbP;
  return 0;
}


/***********************************************************************
 *
 * FUNCTION:     StartApplication
 *
 * DESCRIPTION:  This routine opens the application's database, loads the 
 *               saved-state information and initializes global variables.
 *
 * PARAMETERS:   nothing
 *
 * RETURNED:     nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art6/12/95Initial Revision
 *trm8/5/97adds variable alarm sound settings
 *frigino9/9/97Add AlarmSoundUniqueRecID initialization
 *vmk12/9/97Call DatebookLoadPrefs() to load and fixup prefs
 *rbb6/20/99Removed multi-segment workaround
 *
 ***********************************************************************/
static UInt16 StartApplication (void)
{
  Err err = 0;
  UInt16 mode;
  DateTimeType dateTime;
  DatebookPreferenceType prefs;
  Int16 prefsVersion;
  
  
  // Determime if secret record should be shown.
  PrivateRecordVisualStatus = CurrentRecordVisualStatus =
    (privateRecordViewEnum)PrefGetPreference(prefShowPrivateRecords);
  mode = (PrivateRecordVisualStatus == hidePrivateRecords) ?
    dmModeReadWrite : (dmModeReadWrite | dmModeShowSecret);
  

  // Get the time formats from the system preferences.
  TimeFormat = (TimeFormatType)PrefGetPreference(prefTimeFormat);

  // Get the date formats from the system preferences.
  LongDateFormat = (DateFormatType)PrefGetPreference(prefLongDateFormat);
  ShortDateFormat = (DateFormatType)PrefGetPreference(prefDateFormat);

  // Get the starting day of the week from the system preferences.
  StartDayOfWeek = PrefGetPreference(prefWeekStartDay);
  

  // Get today's date.
  TimSecondsToDateTime (TimGetSeconds (), &dateTime);
  Date.year = dateTime.year - firstYear;
  Date.month = dateTime.month;
  Date.day = dateTime.day;


  // Find the application's data file.  If it don't exist create it.
  err = DateGetDatabase (&ApptDB, mode);
  if (err)return err;
  
  
  // Read the preferences / saved-state information (will fix up incompatible versions).
  prefsVersion = DatebookLoadPrefs (&prefs);
  DayStartHour = prefs.dayStartHour;
  DayEndHour = prefs.dayEndHour;
  AlarmPreset = prefs.alarmPreset;
  NoteFont = prefs.noteFont;
  SaveBackup = prefs.saveBackup;
  ShowTimeBars = prefs.showTimeBars;
  CompressDayView = prefs.compressDayView;
  ShowTimedAppts = prefs.showTimedAppts;
  ShowUntimedAppts = prefs.showUntimedAppts;
  ShowDailyRepeatingAppts = prefs.showDailyRepeatingAppts;
  
  AlarmSoundRepeatCount = prefs.alarmSoundRepeatCount;
  AlarmSoundRepeatInterval = prefs.alarmSoundRepeatInterval;
  AlarmSoundUniqueRecID = prefs.alarmSoundUniqueRecID;
  ApptDescFont = prefs.apptDescFont;
  
  AlarmSnooze = prefs.alarmSnooze;

  // The first time this app starts register to handle vCard data.
  if (prefsVersion != datebookPrefsVersionNum)
    RegisterData();

  TopVisibleAppt = 0;
  CurrentRecord = noRecordSelected;
  
  return 0;
}


/***********************************************************************
 *
 * FUNCTION:    StopApplication
 *
 * DESCRIPTION: This routine closes the application's database
 *              and saves the current state of the application.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art6/12/95Initial Revision
 *trm8/5/97adds variable alarm sound settings
 *frigino9/9/97Add saving of AlarmSoundUniqueRecID
 *vmk12/9/97Call DatebookSavePrefs() to write out the prefs
 *rbb6/20/99Removed multi-segment workaround
 *
 ***********************************************************************/
static void StopApplication (void)
{

  DatebookSavePrefs();

  // Send a frmSave event to all the open forms.
  FrmSaveAllForms ();
  
  // Close all the open forms.
  FrmCloseAllForms ();

  // Close the application's data file.
  DmCloseDatabase (ApptDB);
}

/***********************************************************************
 *
 * FUNCTION:DatebookLoadPrefs
 *
 * DESCRIPTION:Loads app's preferences and fixes them up if they didn't exist
 *or were of the wrong version.
 *
 * PARAMETERS:prefsP-- ptr to preferences structure to fill in
 *
 * RETURNED:the version of preferences from which values were read
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *vmk12/9/97Initial version
 *vmk12/11/97Fix up note font
 *rbb4/23/99Added alarmSnooze
 *
 ***********************************************************************/
Int16 DatebookLoadPrefs (DatebookPreferenceType* prefsP)
{
  UInt16prefsSize;
  Int16prefsVersion = noPreferenceFound;
  Boolean haveDefaultFont = false;
  UInt32 defaultFont;
  
  ErrNonFatalDisplayIf(!prefsP, "null prefP arg");
  
  
  // Read the preferences / saved-state information.  Fix-up if no prefs or older/newer version
  prefsSize = sizeof (DatebookPreferenceType);
  prefsVersion = PrefGetAppPreferences (sysFileCDatebook, datebookPrefID, prefsP, &prefsSize, true);
  
  // If the preferences version is from a future release (as can happen when going back
  // and syncing to an older version of the device), treat it the same as "not found" because
  // it could be significantly different
  if ( prefsVersion > datebookPrefsVersionNum )
    prefsVersion = noPreferenceFound;
  
  if ( prefsVersion == noPreferenceFound )
    {
      // Version 1 and 2 preferences
      prefsP->dayStartHour = defaultDayStartHour;
      prefsP->dayEndHour = defaultDayEndHour;
      prefsP->alarmPreset.advance = defaultAlarmPresetAdvance;
      prefsP->alarmPreset.advanceUnit = defaultAlarmPresetUnit;
      prefsP->saveBackup = defaultSaveBackup;
      prefsP->showTimeBars = defaultShowTimeBars;
      prefsP->compressDayView = defaultCompressDayView;
      prefsP->showTimedAppts = defaultShowTimedAppts;
      prefsP->showUntimedAppts = defaultShowUntimedAppts;
      prefsP->showDailyRepeatingAppts = defaultShowDailyRepeatingAppts;
      
      // We need to set up the note font with a default value for the system.
      FtrGet(sysFtrCreator, sysFtrDefaultFont, &defaultFont);
      haveDefaultFont = true;
      
      prefsP->v20NoteFont = (FontID)defaultFont;
    }
  
  if ((prefsVersion == noPreferenceFound) || (prefsVersion < datebookPrefsVersionNum))
    {
      // Version 3 preferences
      prefsP->alarmSoundRepeatCount = defaultAlarmSoundRepeatCount;
      prefsP->alarmSoundRepeatInterval = defaultAlarmSoundRepeatInterval;
      prefsP->alarmSoundUniqueRecID = defaultAlarmSoundUniqueRecID;
      prefsP->noteFont = prefsP->v20NoteFont;// 2.0 compatibility (BGT)
      
      // Fix up the note font if we copied from older preferences.
      if ((prefsVersion != noPreferenceFound) && (prefsP->noteFont == largeFont))
	prefsP->noteFont = largeBoldFont;

      if (!haveDefaultFont)
	FtrGet(sysFtrCreator, sysFtrDefaultFont, &defaultFont);
      
      prefsP->apptDescFont = (FontID)defaultFont;
    }
  
  if ((prefsVersion == noPreferenceFound) || (prefsVersion < datebookPrefsVersionNum))
    {
      // Version 4 preferences
      prefsP->alarmSnooze = defaultAlarmSnooze;
    }

  return prefsVersion;
}


/***********************************************************************
 *
 * FUNCTION:    DatebookSavePrefs
 *
 * DESCRIPTION: Saves the current preferences of the application.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * CALLED:from DatePref.c and Datebook.c
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *vmk12/9/97Initial version
 *rbb4/23/99Added alarmSnooze
 *
 ***********************************************************************/
void DatebookSavePrefs (void)
{
  DatebookPreferenceType prefs;
  

  // Write the preferences / saved-state information.
  prefs.dayStartHour = DayStartHour;
  prefs.dayEndHour = DayEndHour;
  prefs.alarmPreset = AlarmPreset;
  prefs.saveBackup = SaveBackup;
  prefs.showTimeBars = ShowTimeBars;
  prefs.compressDayView = CompressDayView;
  prefs.showTimedAppts = ShowTimedAppts;
  prefs.showUntimedAppts = ShowUntimedAppts;
  prefs.showDailyRepeatingAppts = ShowDailyRepeatingAppts;
  prefs.alarmSoundRepeatCount = AlarmSoundRepeatCount;
  prefs.alarmSoundRepeatInterval = AlarmSoundRepeatInterval;
  prefs.alarmSoundUniqueRecID = AlarmSoundUniqueRecID;
  prefs.apptDescFont = ApptDescFont;
  prefs.noteFont = NoteFont;
  prefs.alarmSnooze = AlarmSnooze;

  // Clear reserved field so prefs don't look "different" just from stack garbage!
  prefs.reserved = 0;

  // Handle 2.0 backwards compatibility for fonts.(BGT)
  prefs.v20NoteFont = stdFont;


  // Write the state information.
  PrefSetAppPreferences (sysFileCDatebook, datebookPrefID, datebookPrefsVersionNum, 
			 &prefs, sizeof (DatebookPreferenceType), true);
}


/***********************************************************************
 *
 * FUNCTION:    InitDatabase
 *
 * DESCRIPTION: This routine initializes the datebook database.
 *
 * PARAMETERS: datebase
 *
 * RETURNED: nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art10/19/95Initial Revision
 *grant6/23/99Set the backup bit.
 *
 ***********************************************************************/
static void InitDatabase (DmOpenRef dbP)
{
  ApptAppInfoInit (dbP);
  
  // Set the backup bit.  This is to aid syncs with non-Palm software.
  SetDBBackupBit(dbP);
}


/***********************************************************************
 *
 * FUNCTION:    SyncNotification
 *
 * DESCRIPTION: This routine is called when the datebook database is 
 *              synchronized.  This routine will sort the database
 *              and schedule the next alarm.
 *
 * PARAMETERS: nothing
 *
 * RETURNED: nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art6/6/95Initial Revision
 *
 ***********************************************************************/
static void SyncNotification (void)
{
  UInt16 mode;
  DmOpenRef dbP;

  // Find the application's data file.
  mode = dmModeReadWrite;
  dbP = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, mode);
  if (!dbP) return;

  // Resort the appointment database.
  ApptSort (dbP);

  // Remove any alarms from the attention manager queue
  // that might be associated with items deleted during the hotsync.
  UpdatePostedAlarms(PostHotsyncVerification);
  
  // Reschedule the next alarm.
  RescheduleAlarms (dbP);

  DmCloseDatabase (dbP);
}

/***********************************************************************
 *
 * FUNCTION:    SearchDraw
 *
 * DESCRIPTION: This routine draws the description, date, and time of
 *              an appointment found by the text search routine.
 *
 * PARAMETERS: apptRecP - pointer to an appointment record.
 *              x        - draw position
 *              y        - draw position
 *              width    - maximum width to draw.
 *
 * RETURNED: nothing
 *
 *HISTORY:
 *04/18/95artCreated by Art Lamb
 *08/03/99kwkUse WinDrawTruncChars to handle truncation.
 *11/14/00CSUse PrefGetPreference instead of PrefGetPreferences.
 *
 ***********************************************************************/

// DOLATER - these should be based on the screen width, not hard coded.

#define maxSearchRepeatDescWidth100
#define maxSearchDateWidth42
#define maxSearchTimeWidth39

static void SearchDraw (ApptDBRecordPtr apptRecP, Int16 x, Int16 y, 
			Int16 width)
{
  UInt16 i;
  Char timeStr [timeStringLength];
  Char dateStr [dateStringLength];
  UInt16 len;
  UInt16 maxDescWidth;
  Coord drawX;
  Char* ptr;
  Char* desc;
  Char* rscP;
  MemHandle rscH;
  DateFormatType dateFormat;
  TimeFormatType timeFormat;
  
  
  if (apptRecP->repeat)
    maxDescWidth = maxSearchRepeatDescWidth;
  else
    maxDescWidth = width - maxSearchDateWidth - maxSearchTimeWidth;

  // Draw the appointment's desciption.
  desc = apptRecP->description;
  ptr = StrChr(desc, linefeedChr);
  len = (ptr == NULL ? StrLen(desc) : (UInt16)(ptr - desc));
  WinDrawTruncChars (desc, len, x, y, maxDescWidth);

  // If the event is repeating, draw the repeat type.
  if (apptRecP->repeat)
    {
      rscH = DmGetResource (strRsc, repeatTypesStrID);
      rscP = MemHandleLock (rscH);
      for (i = 0; i < apptRecP->repeat->repeatType; i++)
	rscP += StrLen(rscP) + 1;
      x += (width - FntCharsWidth (rscP, StrLen (rscP)));
      WinDrawChars (rscP, StrLen (rscP), x, y);
      MemHandleUnlock (rscH);
    }
  
  // Draw the appointment's date and time.
  else 
    {
      // Get time and date formats from the system preferences.
      dateFormat = (DateFormatType)PrefGetPreference(prefDateFormat);
      timeFormat = (TimeFormatType)PrefGetPreference(prefTimeFormat);

      if (TimeToInt (apptRecP->when->startTime) != apptNoTime)
	{
	  TimeToAscii (apptRecP->when->startTime.hours, 
		       apptRecP->when->startTime.minutes, timeFormat, timeStr);
	  len = StrLen (timeStr);
	  drawX = x + (width - FntCharsWidth (timeStr, len));
	  WinDrawChars (timeStr, len, drawX, y);
	}

      DateToAscii (apptRecP->when->date.month, 
		   apptRecP->when->date.day, 
		   apptRecP->when->date.year + firstYear, 
		   dateFormat, dateStr);
      len = StrLen (dateStr);
      drawX = x + (width - FntCharsWidth (dateStr, len) - maxSearchTimeWidth);
      WinDrawChars (dateStr, len, drawX, y);
    }
}


/***********************************************************************
 *
 * FUNCTION:    Search
 *
 * DESCRIPTION: This routine searchs the datebook database for records 
 *              containing the string passed. 
 *
 * PARAMETERS: findParams - text search parameter block
 *
 * RETURNED: nothing
 *
 *HISTORY:
 *04/18/95artCreated by Art Lamb.
 *05/27/99jaqTwo-pass process (future first)
 *08/03/99kwkUse TxtFindString for source match length result.
 *10/21/99jmpMade this routine a bit more like everyone else's.
 *
 ***********************************************************************/
static void Search (FindParamsPtr findParams)
{
  Errerr;
  UInt16 cardNo = 0;
  UInt16fieldNum;
  UInt16 recordNum;
  Char* desc;
  Char* note;
  Char* header;
  Boolean done;
  Boolean match;
  MemHandleheaderStringH;
  LocalID dbID;
  DateTypedate;
  MemHandle recordH;
  DmOpenRef dbP;
  RectangleType r;
  ApptDBRecordTypeapptRec;
  DmSearchStateType searchInfo;
  UInt16matchLength;
  UInt32matchPos;

  // Display the heading line.
  headerStringH = DmGetResource(strRsc, findDatebookHeaderStrID);
  header = MemHandleLock(headerStringH);
  done = FindDrawHeader(findParams, header);
  MemHandleUnlock(headerStringH);   
  DmReleaseResource(headerStringH);   
  if (done) 
    return;

  // Find the application's data file.
  err =  DmGetNextDatabaseByTypeCreator (true, &searchInfo, datebookDBType, 
					 sysFileCDatebook, true, &cardNo, &dbID);
  if (err)
    {
      findParams->more = false;
      return;
    }

  // Open the appointment database.
  dbP = DmOpenDatabase(cardNo, dbID, findParams->dbAccesMode);
  if (!dbP) 
    {
      findParams->more = false;
      return;
    }

  
  // Search the description and note fields for the "find" string.
  recordNum = findParams->recordNum;
  while (true)
    {
      // Because applications can take a long time to finish a find when
      // the result may be on the screen or for other reasons, users like
      // to be able to stop the find.  Stop the find if an event is pending.
      // This stops if the user does something with the device.  Because
      // this call slows down the search we perform it every so many 
      // records instead of every record.  The response time should still
      // be short without introducing much extra work to the search.
      
      // Note that in the implementation below, if the next 16th record is  
      // secret the check doesn't happen.  Generally this shouldn't be a 
      // problem since if most of the records are secret then the search 
      // won't take long anyways!
      if ((recordNum & 0x000f) == 0 &&// every 16th record
	  EvtSysEventAvail(true))
	{
	  // Stop the search process.
	  findParams->more = true;
	  break;
	}
      
      recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories);

      //Should we wrap around to repeating and past events?
      if (! recordH)
	{
	  findParams->more = false;
	  break;
	}

      ApptGetRecord (dbP, recordNum, &apptRec, &recordH);
       
      // Search the description field,  if a match is not found search the
      // note field.
      fieldNum = descSeacrchFieldNum;
      desc = apptRec.description;
      match = TxtFindString (desc, findParams->strToFind, &matchPos, &matchLength);
      if (! match)
	{
	  note = apptRec.note;
	  if (note)
	    {
	      fieldNum = noteSeacrchFieldNum;
	      match = TxtFindString (note, findParams->strToFind, &matchPos, &matchLength);
	    }
	}

      // If a match occurred in a repeating event,  make sure there is 
      // a displayable occurrence of the event.
      if (match && apptRec.repeat)
	{
	  date = apptRec.when->date;
	  match = ApptNextRepeat (&apptRec, &date, true);
	}

      if (match)
	{
	  // Add the match to the find paramter block,  if there is no room to
	  // display the match the following function will return true.
	  done = FindSaveMatch (findParams, recordNum, matchPos, fieldNum, matchLength, cardNo, dbID);
	  if (done) 
	    {
	      MemHandleUnlock (recordH);
	      break;
	    }

	  // Get the bounds of the region where we will draw the results.
	  FindGetLineBounds (findParams, &r);
	  
	  // Display the appointment info.
	  SearchDraw (&apptRec, r.topLeft.x+1, r.topLeft.y, r.extent.x-2);

	  findParams->lineNumber++;
	}

      MemHandleUnlock (recordH);
      recordNum++;
    }
  
 Exit:
  DmCloseDatabase (dbP);
}


/***********************************************************************
 *
 * FUNCTION:    GoToItem
 *
 * DESCRIPTION: This routine is called as the result of hitting the 
 *              "Go to" button in the text search dialog. 
 *
 * PARAMETERS: goToParams   - where to go to.
 *              launchingApp - true if the application is being launched
 *
 * RETURNED: nothing
 *
 *HISTORY:
 *08/23/95artCreated by Art Lamb.
 *08/03/99kwkUse custom param to set up match length.
 *04/12/00rywFixed MemSet parameter ordering.
 *
 ***********************************************************************/
void GoToItem (GoToParamsPtr goToParams, Boolean launchingApp)
{
  UInt16 formID;
  UInt16 recordNum;
  EventType event;
  UInt32 uniqueID;

  recordNum = goToParams->recordNum;
  
  if (!DmQueryRecord(ApptDB, recordNum))
    {
      // Record isn't accessible. This can happen when receiving a beam with an
      // empty record. This prevents a fatal alert, but doesn't fix the off-by-one
      // error. (See DOLATER in sysAppLaunchCmdExgReceiveData case.)
      if (!SeekCurrentRecord(ApptDB, &recordNum, 0, dmSeekBackward))
	if (!SeekCurrentRecord(ApptDB, &recordNum, 0, dmSeekForward))
	  {
	    FrmAlert(secGotoInvalidRecordAlert);
	    FrmGotoForm(DayView);
	    return;
	  }
    }

  // If the application is already running, destroy the UI, this may 
  // change the index of record we're going to.
  if (! launchingApp)
    {
      DmRecordInfo (ApptDB, recordNum, NULL, &uniqueID, NULL); 
      FrmCloseAllForms ();
      ClearEditState ();
      DmFindRecordByID (ApptDB, uniqueID, &recordNum);
    }
  
  // Go to day view or note view.
  if (goToParams->matchFieldNum == noteSeacrchFieldNum)
    formID = NewNoteView;
  else
    formID = DayView;

  MemSet (&event, sizeof(EventType), 0);

  // Send an event to load the form we want to goto.
  event.eType = frmLoadEvent;
  event.data.frmLoad.formID = formID;
  EvtAddEventToQueue (&event);
 

  // Send an event to goto a form and select the matching text.
  event.eType = frmGotoEvent;
  event.data.frmGoto.formID = formID;
  event.data.frmGoto.recordNum = recordNum;
  event.data.frmGoto.matchPos = goToParams->matchPos;
  event.data.frmGoto.matchLen = goToParams->matchCustom;
  event.data.frmGoto.matchFieldNum = goToParams->matchFieldNum;
  EvtAddEventToQueue (&event);
}


/***********************************************************************
 *
 * FUNCTION:    GoToAlarmItem
 *
 * DESCRIPTION: This routine is called as the result of hitting the 
 *              "Go to" button in the attention manager dialog.
 *
 * PARAMETERS: UniqueID   - uniqueID of the item to go to.
 *
 * RETURNED: nothing
 *
 *HISTORY:
 *09/07/00 gapInitial reviesion.
 *
 ***********************************************************************/
void GoToAlarmItem (UInt32 uniqueID)
{
  UInt16attributes;
  UInt16recordNum;
  Errerr;
  EventTypeevent;
  UInt16cardNo;
  LocalIDdbID;
  DmSearchStateType searchInfo;



#if EMULATION_LEVEL != EMULATION_NONE
  FrmCloseAllForms ();
  ClearEditState ();
#endif

  // remove the item from the attention manager queue
  DmGetNextDatabaseByTypeCreator (true, &searchInfo, sysFileTApplication, sysFileCDatebook, true, &cardNo, &dbID);
  AttnForgetIt(cardNo, dbID, uniqueID);
  
  // verify that the specified uniqueID is still in the Date Book's database
  // one reason this needs to be done is to prevent crashing should the alarm
  // have been on an untitled event with no note attached.  in the process of 
  // executing the goto, the original record may have been deleted as all 
  // untitled events w/o a note are as soon as the item is deselected.
  err = FindRecordByID (ApptDB, uniqueID, &recordNum);
  if (err)
    {
      FrmGotoForm(DayView);
      return;
    }
  
  // Get the attribute information for the record
  DmRecordInfo (ApptDB, recordNum, &attributes, NULL, NULL);


  // if the user does a "go to" from an attention manager dialog while the
  // private items are hidden and the selected item happens to have the 
  // private bit set, the user must submit his password so that the 
  // security level can be changed from hide to show before the goto to the 
  // specified record occurs
  if ( (attributes & dmRecAttrSecret) && (PrivateRecordVisualStatus == hidePrivateRecords) )
    {
      if (SecVerifyPW(showPrivateRecords))
	{
	  DmCloseDatabase (ApptDB);
	  ApptDB = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, (dmModeReadWrite | dmModeShowSecret));
	  PrivateRecordVisualStatus = CurrentRecordVisualStatus = showPrivateRecords;
	}
      else
	{
	  FrmGotoForm(DayView);
	  return;
	}
    }
  
  MemSet (&event, sizeof(EventType), 0);

  // Send an event to load the form we want to goto.
  event.eType = frmLoadEvent;
  event.data.frmLoad.formID = DayView;
  EvtAddEventToQueue (&event);


  // Send an event to goto a form and select the matching text.
  event.eType = frmGotoEvent;
  event.data.frmGoto.formID = DayView;
  event.data.frmGoto.recordNum = recordNum;
  event.data.frmGoto.matchPos = 0;
  event.data.frmGoto.matchLen = 0;
  event.data.frmGoto.matchFieldNum = 0;
  EvtAddEventToQueue (&event);
}


/***********************************************************************
 *
 * FUNCTION:    DrawTime
 *
 * DESCRIPTION: Draws the given time. Used by the custom draw routines
 * for the date columns of the agenda and day views.
 *
 * PARAMETERS:  inTime- the time
 *              inFormat- time-to-ascii conversion format
 * inBoundsP- drawing area
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *rbb6/4/99Initial Revision
 *
 ***********************************************************************/
void DrawTime (
	       TimeTypeinTime,
	       TimeFormatTypeinFormat,
	       FontIDinFont,
	       JustificationTypeinJustification,
	       RectangleType*inBoundsP )
{
  FontIDcurFont;
  chartimeStr [timeStringLength];
  TimeFormatTypeformat;
  UInt16len;
  UInt16width;
  Int16x;

  // No-time appointment?
  if (TimeToInt(inTime) == apptNoTime)
    {
      // Show a centered diamond symbol, overriding the font params
      inFont = symbolFont;
      inJustification  = centerAlign;

      timeStr[0] = symbolNoTime;
      timeStr[1] = '\0';
    }
  else
    {
      if (inFormat == tfColonAMPM)
	format = tfColon;
      else if (inFormat == tfDotAMPM)
	format = tfDot;
       else
	 format = inFormat;

      TimeToAscii (inTime.hours, inTime.minutes, format, timeStr);

    }
  
  // Use the string width and alignment to compute its starting point
  len = StrLen (timeStr);
  width = FntCharsWidth (timeStr, len);
  x = inBoundsP->topLeft.x;
  switch (inJustification)
    {
    case rightAlign:
      x += inBoundsP->extent.x - width;
      break;
      
    case centerAlign:
      x += (inBoundsP->extent.x - width) / 2;
      break;
    }
  
  x = max (inBoundsP->topLeft.x, x);
  
  // Draw the time
  curFont = FntSetFont (inFont);
  WinDrawChars (timeStr, len, x, inBoundsP->topLeft.y);
  FntSetFont (curFont);
}


/***********************************************************************
 *
 * FUNCTION:    GetObjectPtr
 *
 * DESCRIPTION: This routine returns a pointer to an object in the current
 *              form.
 *
 * PARAMETERS:  formId - id of the form to display
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art2/21/95Initial Revision
 *
 ***********************************************************************/
void* GetObjectPtr (UInt16 objectID)
{
  FormPtr frm;
  
  frm = FrmGetActiveForm ();
  return (FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, objectID)));

}


/***********************************************************************
 *
 * FUNCTION:    DirtyRecord
 *
 * DESCRIPTION: Mark a record dirty (modified).  Record marked dirty 
 *              will be synchronized.
 *
 * PARAMETERS:  record index
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art4/15/95Initial Revision
 *
 ***********************************************************************/
void DirtyRecord (DmOpenRef dbP, UInt16 index)
{
  UInt16attr;
  Errerr;

  err = DmRecordInfo (dbP, index, &attr, NULL, NULL);
  ErrFatalDisplayIf (err, "Invalid index");
  
  attr |= dmRecAttrDirty;
  DmSetRecordInfo (dbP, index, &attr, NULL);
}


/***********************************************************************
 *
 * FUNCTION:    ClearEditState
 *
 * DESCRIPTION: This routine take the application out of edit mode.
 *              The edit state of the current record is remember until
 *              this routine is called.  
 *
 *              If the current record is empty its deleted by this
 *              routine.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    true is current record is deleted by this routine.
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art7/28/95Initial Revision
 *gap8/01/00add attention manager support
 *
 ***********************************************************************/
Boolean ClearEditState (void)
{
  UInt16 recordNum;
  Boolean empty;
  Boolean hasAlarm;
  MemHandle recordH;
  ApptDBRecordType apptRec;

  if ( ! ItemSelected)
    {
      CurrentRecord = noRecordSelected;
      return (false);
    }
  
  recordNum = CurrentRecord;

  // Clear the global variables that keep track of the edit state of the
  // current record.
  ItemSelected = false;
  CurrentRecord = noRecordSelected;
  DayEditPosition = 0;
  DayEditSelectionLength = 0;
  
  // If the description field is empty there is no note, delete
  // the record.
  ApptGetRecord (ApptDB, recordNum, &apptRec, &recordH);
  hasAlarm = (apptRec.alarm != NULL);
  empty = true;
  if (apptRec.description && *apptRec.description)
    empty = false;
  else if (apptRec.note && *apptRec.note)
    empty = false;
  MemHandleUnlock (recordH);


  if (empty)
    {
      UInt32alarmRef;// Ignored. Needed for AlarmGetTrigger()

      // if the event to be delete had an alarm, be sure to remove it
      // from the posted alarm queue before the event is deleted.
      if (hasAlarm)
	DeleteAlarmIfPosted(recordNum);
      
      // If the record was not modified, and the description and 
      // note fields are empty, remove the record from the database.
      // This can occur when a new empty record is deleted.
      if (RecordDirty)
	{
	  DmDeleteRecord (ApptDB, recordNum);
	  DmMoveRecord (ApptDB, recordNum, DmNumRecords (ApptDB));
	}
      else
	DmRemoveRecord (ApptDB, recordNum);
      
      // If the appointment had the currently scheduled alarm, reschedule the next alarm
      if (hasAlarm)
	{
	  UInt32 alarmTrigger = AlarmGetTrigger (&alarmRef);
	  
	  if (alarmTrigger && (alarmTrigger == ApptGetAlarmTime (&apptRec, TimGetSeconds(), true)))
	    {
	      RescheduleAlarms (ApptDB);
	    }
	}

      return (true);
    }

  return (false);
}


// Branch island to get to the glue code, linked in first.

Char* DateParamString(const Char* inTemplate, const Char* param0,
		      const Char* param1, const Char* param2, const Char* param3)
{
  return(TxtParamString(inTemplate, param0, param1, param2, param3));
}


#pragma mark ----------------
/***********************************************************************
 *
 * FUNCTION:    PilotMain
 *
 * DESCRIPTION: This is the main entry point for the Datebook
 *              application.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art6/12/95Initial Revision
 *vmk10/22/97Moved MIDI db creation to general panel
 *art1/7/98Save current record before receiving a beamed record
 *rbb4/22/99Moved code to link w/ 16-bit jumps
 *rbb6/20/99Removed multi-segment workaround
 *jmp10/02/99 Made the support for the sysAppLaunchCmdExgReceiveData
 *launch code more like its counterparts in Address,
 *Memo, and ToDo.
 *jmp10/18/99Note that Datebook doesn't attempt to create an empty
 *database if a default ("demo") database doesn't exist.
 *jmp11/04/99Eliminate extraneous FrmSaveAllForms() call from sysAppLaunchCmdExgAskUser
 *since it was already being done in sysAppLaunchCmdExgReceiveData if
 *the user affirmed sysAppLaunchCmdExgAskUser.  Also, in sysAppLaunchCmdExgReceiveData
 *prevent call FrmSaveAllForms() if we're being call back through
 *PhoneNumberLookup() as the two tasks are incompatible with each other.
 *rbb11/12/99Added recentFormFeature to pick default view on launch
 *
 ***********************************************************************/
UInt32PilotMain (UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags)
{
  UInt16 error;
  UInt32 defaultForm;
  DmOpenRef dbP;
       
  
  // The only place the following define is used is in the Jamfile for this application.
  // It's sole purpose is to provide a way to check if cross segment calls are being made
  // when application globals are unavailable.
#ifndef SINGLE_SEGMENT_CHECK
  Boolean launched;

  // Launch code sent by the launcher or the datebook button.
  if (cmd == sysAppLaunchCmdNormalLaunch)
    {
      error = StartApplication ();
      if (error) return (error);
      
      // If the user previously left the Datebook while viewing the agenda,
      // return there. Otherwise, go to the day view
      error = FtrGet (sysFileCDatebook, recentFormFeature, &defaultForm);
      if (error)
	{
	  defaultForm = defaultRecentForm;
	}
      
      FrmGotoForm (defaultForm);
      EventLoop ();
      StopApplication ();
    }


  // This action code is a DateBook specific custom launch code.
  // It will always require that the app launches as it is a result
  // of a SysUIAppSwitch call.
  else if (cmd == appLaunchCmdAlarmEventGoto)
    {
      error = StartApplication ();
      if (error) return (error);

      GoToAlarmItem (*((UInt32*)cmdPBP));

      EventLoop ();
      StopApplication ();
    }

  

  // This action code might be sent to the app when it's already running
  // if the use hits the "Go To" button in the Find Results dialog box.
  else if (cmd == sysAppLaunchCmdGoTo)
    {
      launched = launchFlags & sysAppLaunchFlagNewGlobals;
      if (launched)
	{
	  error = StartApplication ();
	  if (error) return (error);

	  GoToItem ((GoToParamsPtr) cmdPBP, launched);

	  EventLoop ();
	  StopApplication ();
	}
      else
	GoToItem ((GoToParamsPtr) cmdPBP, launched);
    }
#else
  if (false) {}
#endif

  //////////////////////////////////////////////////////////////////////////////////
  //
  // WARNING! Launch codes below this point can be called from a context where
  // application globals are unavailable.
  //
  //////////////////////////////////////////////////////////////////////////////////

  // Launch code sent to running app before sysAppLaunchCmdFind
  // or other action codes that will cause data searches or manipulation.
  // We don't need to respond to this launch code because events are
  // edited in place.
  //else if (cmd == sysAppLaunchCmdSaveData)
  //{
  //FrmSaveAllForms ();
  //}
  

  // Launch code sent by text search.
  else if (cmd == sysAppLaunchCmdFind)
    {
      Search ((FindParamsPtr)cmdPBP);
    }
  

  // Launch code sent by sync application to notify the datebook 
  // application that its database was been synced.
  else if (cmd == sysAppLaunchCmdSyncNotify)
    {
      SyncNotification ();
    }
  

  // Launch code sent by Alarm Manager to notify the datebook 
  // application that an alarm has triggered.
  else if (cmd == sysAppLaunchCmdAlarmTriggered)
    {
      AlarmTriggered ((SysAlarmTriggeredParamType *)cmdPBP);
    }
  

  // Launch code sent when the system time is changed.
  else if (cmd == sysAppLaunchCmdTimeChange)
    {
      // reset the trigger for the next alarm to fire
      AlarmReset (false);
      
      // Remove any "future" alarms from the attention manager queue
      // (ie alarms that will trigger after the new time setting)
      UpdatePostedAlarms(DeviceTimeChanged);
    }
  

  // This action code is sent after the system is reset.  We use this time
  // to create our default database.  Note:  Unlike several other built-in
  // apps, we do not attempt to create an empty database if the default
  // database image doesn't exist.
  else if (cmd == sysAppLaunchCmdSystemReset) 
    {
      if (((SysAppLaunchCmdSystemResetType*)cmdPBP)->createDefaultDB)
	{
	  MemHandle resH;
	  
	  resH = DmGet1Resource(sysResTDefaultDB, sysResIDDefaultDB);
	  if (resH) 
	    {
	      DmCreateDatabaseFromImage(MemHandleLock(resH));
	      MemHandleUnlock(resH);
	      DmReleaseResource(resH);
	      
	      // set the backup bit on the new database
	      SetDBBackupBit(NULL);
	    }

	  // Register to receive vcs files on hard reset.
	  RegisterData();
	}

      if (! ((SysAppLaunchCmdSystemResetType*)cmdPBP)->hardReset)
	{
	  AlarmReset (false);// Find and install next upcoming alarm
	}
    }


  // Receive the record.  The app will parse the data and add it to the database.
  // This data should be displayed by the app.
  else if (cmd == sysAppLaunchCmdExgReceiveData) 
    {
      UInt32 currentUID;
      
      // if our app is not active, we need to open the database 
      // the subcall flag is used here since this call can be made without launching the app
      if (!(launchFlags & sysAppLaunchFlagSubCall))
	{
	  error = DateGetDatabase(&dbP, dmModeReadWrite);
	}
      else
	{
	  dbP = ApptDB;
	  
	  // We don't delete the current record if it's empty because the user
	  // could cancel the beam receive.
	  
	  // DateReceiveData() inserts the received record in sorted order. This may may change
	  // the index of the current record. So we remember its UID here, and refresh our copy
	  // of its index afterwards.
	  if (CurrentRecord != noRecordSelected)
	    DmRecordInfo(dbP, CurrentRecord, NULL, &currentUID, NULL);
	}
      
      if (dbP != NULL)
	{
	  error = DateReceiveData(dbP, (ExgSocketPtr) cmdPBP);
	  
	  if (launchFlags & sysAppLaunchFlagSubCall)
	    {
	      if (CurrentRecord != noRecordSelected)
		{
		  if (DmFindRecordByID(dbP, currentUID, &CurrentRecord) != 0)
		    CurrentRecord = noRecordSelected;// Can't happen, but...
		  
		  // DOLATER dje -
		  //To fix the off-by-one error, we can decrement exgSocketP->goToParams.recordNum
		  //if it's after the current empty record in order to compensate for the
		  //current empty record getting deleted when we exit before the goto launch.
		}
	    }
	        
	  // The received event may have an alarm, reschedule the next alarm.
	  RescheduleAlarms (dbP);

	  if (!(launchFlags & sysAppLaunchFlagSubCall))
	    DmCloseDatabase(dbP);
	}
      else
	error = exgErrAppError;// DOLATER dje - use a new error code - "try again after switching apps"
      
      // If we can't open our database, return the error since it wasn't passed to ExgDisconnect
      return error;
    }

  else if (cmd == sysAppLaunchCmdExgPreview)
    {
      DateTransferPreview((ExgPreviewInfoType *)cmdPBP);
    }      
  // This action code is sent by the DesktopLink server when it create 
  // a new database.  We will initializes the new database.
  else if (cmd == sysAppLaunchCmdInitDatabase)
    {
      InitDatabase (((SysAppLaunchCmdInitDatabaseType*)cmdPBP)->dbP);
    }
  
  else if (cmd == sysAppLaunchCmdAttention)
    {
      AttentionBottleNeckProc((AttnLaunchCodeArgsType*)cmdPBP);
    }
  

  return (0);
}


#pragma mark ----------------
/***********************************************************************
 *
 * FUNCTION:    TimeToWait
 *
 * DESCRIPTION:This routine calculates the number of ticks until the
 *time display should be hidden.  If the time is not being
 *displayed, we return evtWaitForever - a good default for
 *EvtGetEvent.
 *
 *If the agenda view is on display, return a delay of one
 *second, allowing the title bar clock to update.
 *
 * PARAMETERS:  none
 *
 * RETURNED:    number of ticks to wait
 *== evtWaitForever if we are not showing the time
 *== 0 if time is up
 *
 *NOTES:Uses the global variables TimeDisplayed and TimeDisplayTick.
 *TimeDisplayed is true when some temporary information is on
 *display.  In that case, TimeDisplayTick is the time when the
 *temporary display should go away.
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *grant2/2/99Initial Revision
 *rbb11/15/99Added timeout for the agenda view 
 *
 ***********************************************************************/
Int32 TimeToWait()
{
  Int32 timeRemaining;
  
  if (FrmGetActiveFormID () == AgendaView)
    {
      return sysTicksPerSecond;
    }
  
  if (!TimeDisplayed)
    {
      return evtWaitForever;
    }
  
  timeRemaining = TimeDisplayTick - TimGetTicks();
  if (timeRemaining < 0)
    {
      timeRemaining = 0;
    }
  
  return timeRemaining;
}


/***********************************************************************
 *
 * FUNCTION:    ApplicationHandleEvent
 *
 * DESCRIPTION: This routine loads form resources and set the event
 *              handler for the form loaded.
 *
 * PARAMETERS:  event  - a pointer to an EventType structure
 *
 * RETURNED:    true if the event has handle and should not be passed
 *              to a higher level handler.
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art9/11/95Initial Revision
 *jmp9/17/99Use NewNoteVie instead of NoteView.
 *rbb11/12/99Added recentFormFeature to pick default view on launch
 *
 ***********************************************************************/
static Boolean ApplicationHandleEvent (EventType* event)
{
  UInt16 formId;
  FormPtr frm;

  if (event->eType == frmLoadEvent)
    {
      // Load the form resource.
      formId = event->data.frmLoad.formID;
      frm = FrmInitForm (formId);
      FrmSetActiveForm (frm);
      
      // Set the event handler for the form.  The handler of the currently
      // active form is called by FrmDispatchEvent each time is receives an
      // event.
      if (formId == DayView)
	FrmSetEventHandler (frm, DayViewHandleEvent);
      
      else if (formId == WeekView)
	FrmSetEventHandler (frm, WeekViewHandleEvent);
      
      else if (formId == MonthView)
	FrmSetEventHandler (frm, MonthViewHandleEvent);
      
      else if (formId == AgendaView)
	FrmSetEventHandler (frm, AgendaViewHandleEvent);
      
      else if (formId == NewNoteView)
	FrmSetEventHandler (frm, NoteViewHandleEvent);
      
      else if (formId == DetailsDialog)
	FrmSetEventHandler (frm, DetailsHandleEvent);

      else if (formId == RepeatDialog)
	FrmSetEventHandler (frm, RepeatHandleEvent);
      
      else if (formId == PreferencesDialog)
	FrmSetEventHandler (frm, PreferencesHandleEvent);

      else if (formId == DisplayDialog)
	FrmSetEventHandler (frm, DisplayOptionsHandleEvent);
      
      FtrSet (sysFileCDatebook, recentFormFeature,
	      formId == AgendaView ? AgendaView : DayView);

      return (true);
    }

  return (false);
}


/***********************************************************************
 *
 * FUNCTION:    PreprocessEvent
 *
 * DESCRIPTION: This routine handles special event processing that
 *              needs to occur before the System Event Handler get 
 *              and event.
 *
 * PARAMETERS:  event  - a pointer to an EventType structure
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art1/10/97Initial Revision
 *CSS06/22/99Standardized keyDownEvent handling
 *(TxtCharIsHardKey, commandKeyMask, etc.)
 *
 ***********************************************************************/
static void PreprocessEvent (EventType* event)
{

  // We want to allow the datebook event handled see the command keys
  // before the any other event handler get them.  Some of the databook
  // views display UI that dispappears automaticial (the time in the 
  // title, or an event's descripition in the week view).  This UI
  // needs to be dismissed before the System Event Handler or the 
  // Menu Event Handler displays any UI objects.  
  if (event->eType == keyDownEvent) 
    {
      if((!TxtCharIsHardKey(event->data.keyDown.modifiers,
			    event->data.keyDown.chr))
	 &&(EvtKeydownIsVirtual(event))
	 &&(event->data.keyDown.chr != vchrPageUp)
	 &&(event->data.keyDown.chr != vchrPageDown)
	 &&(event->data.keyDown.chr != vchrSendData))
	{
	  FrmDispatchEvent (event);
	}
    }
}


/***********************************************************************
 *
 * FUNCTION:    EventLoop
 *
 * DESCRIPTION: This routine is the event loop for the Datebook
 *              aplication.  
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *NameDateDescription
 *-------------------
 *art6/12/95Initial Revision
 *grant 2/2/99Use TimeToWait() for time to wait for next event
 *
 ***********************************************************************/
static void EventLoop (void)
{
  UInt16 error;
  EventType event;

  do
    {
      EvtGetEvent (&event, TimeToWait());
      
      PreprocessEvent (&event);
      
      if (! SysHandleEvent (&event))
	
	if (! MenuHandleEvent (NULL, &event, &error))
	  
	  if (! ApplicationHandleEvent (&event))
	    
	    FrmDispatchEvent (&event); 

      //#ifEMULATION_LEVEL != EMULATION_NONE
      //ECApptDBValidate (ApptDB);
      //#endif
    }
  while (event.eType != appStopEvent);
}



Tutorials

Linux System Admin Tips: There are over 200 Linux tips and tricks in this article. That is over 100 pages covering everything from NTP, setting up 2 IP address on one NIC, sharing directories among several users, putting running jobs in the background, find out who is doing what on your system by examining open sockets and the ps command, how to watch a file, how to prevent even root from deleting a file, tape commands, setting up cron jobs, using rsync, using screen conveniently with emacs, how to kill every process for a user, security tips and a lot more. These tip grow weekly. The above link will download the text version for easy grep searching. There is also an html version here.

Breaking Firewalls with OpenSSH and PuTTY: If the system administrator deliberately filters out all traffic except port 22 (ssh), to a single server, it is very likely that you can still gain access other computers behind the firewall. This article shows how remote Linux and Windows users can gain access to firewalled samba, mail, and http servers. In essence, it shows how openSSH and Putty can be used as a VPN solution for your home or workplace.

MySQL Tips and Tricks: Find out who is doing what in MySQL and how to kill the process, create binary log files, connect, create and select with Perl and Java, remove duplicates in a table with the index command, rollback and how to apply, merging several tables into one, updating foreign keys, monitor port 3306 with the tcpdump command, creating a C API, complex selects, and much more.

Create a Live Linux CD - BusyBox and OpenSSH Included: These steps will show you how to create a functioning Linux system, with the latest 2.6 kernel compiled from source, and how to integrate the BusyBox utilities including the installation of DHCP. Plus, how to compile in the OpenSSH package on this CD based system. On system boot-up a filesystem will be created and the contents from the CD will be uncompressed and completely loaded into RAM -- the CD could be removed at this point for boot-up on a second computer. The remaining functioning system will have full ssh capabilities. You can take over any PC assuming, of course, you have configured the kernel with the appropriate drivers and the PC can boot from a CD. This tutorial steps you through the whole processes.

SQLite Tutorial : This article explores the power and simplicity of sqlite3, first by starting with common commands and triggers, then the attach statement with the union operation is introduced in a way that allows multiple tables, in separate databases, to be combined as one virtual table, without the overhead of copying or moving data. Next, the simple sign function and the amazingly powerful trick of using this function in SQL select statements to solve complex queries with a single pass through the data is demonstrated, after making a brief mathematical case for how the sign function defines the absolute value and IF conditions.

The Lemon Parser Tutorial: This article explains how to build grammars and programs using the lemon parser, which is faster than yacc. And, unlike yacc, it is thread safe.

How to Compile the 2.6 kernel for Red Hat 9 and 8.0 and get Fedora Updates: This is a step by step tutorial on how to compile the 2.6 kernel from source.

Virtual Filesystem: Building A Linux Filesystem From An Ordinary File. You can take a disk file, format it as ext2, ext3, or reiser filesystem and then mount it, just like a physical drive. Yes, it then possible to read and write files to this newly mounted device. You can also copy the complete filesystem, since it is just a file, to another computer. If security is an issue, read on. This article will show you how to encrypt the filesystem, and mount it with ACL (Access Control Lists), which give you rights beyond the traditional read (r) write (w) and execute (x) for the 3 user groups file, owner and other.

Working With Time: What? There are 61 seconds in a minute? We can go back in time? We still tell time by the sun?



Chirico img Mike Chirico, a father of triplets (all girls) lives outside of Philadelphia, PA, USA. He has worked with Linux since 1996, has a Masters in Computer Science and Mathematics from Villanova University, and has worked in computer-related jobs from Wall Street to the University of Pennsylvania. His hero is Paul Erdos, a brilliant number theorist who was known for his open collaboration with others.


Mike's notes page is souptonuts. For open source consulting needs, please send an email to mchirico@gmail.com. All consulting work must include a donation to SourceForge.net.

SourceForge.net Logo


SourceForge.net Logo