/* Set start and end times from a given seconds */ static void SetSaneTimes(UInt32 secs, TimeType *startP, TimeType *endP) { DateTimeType date_time; TimSecondsToDateTime(secs, &date_time); if (secs % 60) TimAdjust(&date_time, (60 - (secs % 60))); startP->hours = date_time.hour; startP->minutes = date_time.minute; if (startP->hours != 23) { endP->hours = startP->hours+1; endP->minutes = startP->minutes; } else { endP->hours = startP->hours; endP->minutes = 59; } }
/*********************************************************************** * * FUNCTION: SelectTimeZone * * DESCRIPTION: Display a form showing a time zone and allow the user * to select a different time zone. This is the time zone * dialog as seen in Date & Dime panel * * PARAMETERS: * ioTimeZoneP <-> pointer to time zone to change * ioLocaleInTimeZoneP <-> Ptr to locale found in time zone. * titleP -> String title for the dialog. * showTimes -> True => show current and new times * anyLocale -> True => ignore ioLocaleInTimeZoneP on entry. * * RETURNED: * true if the OK button was pressed (in which case *ioTimeZoneP and * *ioCountryInTimeZoneP might be changed). * * HISTORY: * 03/02/00 peter Created by Peter Epstein. * 04/03/00 peter Allow NULL currentTimeP. * 04/12/00 peter API changed to get rid of trigger text * 04/14/00 peter Update current & new time as time passes * 07/31/00 kwk Use SysTicksPerSecond() routine vs. sysTicksPerSecond macro. * kwk Re-wrote to use set of resources (name, offset, country), * scrollbar vs. arrows, etc. * 08/01/00 kwk Support scroll-to-key. Fixed scrollbar/list sync bugs. * 08/02/00 kwk New API w/ioCountryInTimeZoneP and anyCountry parameters. * kwk Call FrmHandleEvent _after_ our event handling code has * decided that it doesn't want to handle the event, not before. * 08/03/00 kwk Call LstSetListChoices before calling LstGetVisibleItems, * as otherwise accessing the time zone picker from the * Setup app (when <showTimes> is false) gives you a two- * line high display because LstGetVisibleItems returns 0. * 08/18/00 kwk Play error sound if user writes letter that doesn't * match any entries. * kwk Don't select item if doing scroll-to-view for entry * that matches the letter the user wrote. * 08/21/00 kwk Scroll-to-view with text entry now scrolls to the top * of the list, versus the middle. * 10/09/00 peter Get rid of scroll bar and let list do the scrolling. * 11/17/00 CS Change ioCountryInTimeZoneP to ioLocaleInTimeZoneP, * (and anyCountry to anyLocale, but that doesn't matter), * since CountryType is only a UInt8, and this may * change someday. * ***********************************************************************/ Boolean SelectTimeZone(Int16 *ioTimeZoneP, LmLocaleType* ioLocaleInTimeZoneP, const Char *titleP, Boolean showTimes, Boolean anyLocale) { FormType* originalForm; FormType* dialog; EventType event; Boolean confirmed = false; Boolean done = false; Boolean adjustTimes = false; Boolean foundLocale = false; MemHandle currentTimeHandle, newTimeHandle; ListPtr listP; Int16 oldTimeZone, newTimeZone, testTimeZone; LmLocaleType newTimeZoneLocale; Int16 delta, closestDelta, timeZoneIndex, closestIndex; DateTimeType currentTime, newTime; TimeZoneEntryType* tzArrayP; UInt16 numTimeZones; MemHandle tzNamesH; if (showTimes) { TimSecondsToDateTime(TimGetSeconds(), ¤tTime); } oldTimeZone = *ioTimeZoneP; newTimeZone = oldTimeZone; newTimeZoneLocale = *ioLocaleInTimeZoneP; originalForm = FrmGetActiveForm(); dialog = (FormType *) FrmInitForm (TimeZoneDialogForm); listP = FrmGetObjectPtr (dialog, FrmGetObjectIndex (dialog, TimeZoneDialogTimeZoneList)); if (titleP) { FrmSetTitle (dialog, (Char *) titleP); } FrmSetActiveForm (dialog); // We need to call LstSetListChoices before calling LstSetHeight below, as otherwise // LstGetVisibleItems will return 0. tzArrayP = PrvCreateTimeZoneArray(&tzNamesH, &numTimeZones); LstSetListChoices(listP, (Char**)tzArrayP, numTimeZones); if (showTimes) { currentTimeHandle = MemHandleNew(timeStringLength + 1 + dowLongDateStrLength + 1); ErrFatalDisplayIf (!currentTimeHandle, "Out of memory"); newTimeHandle = MemHandleNew(timeStringLength + 1 + dowLongDateStrLength + 1); ErrFatalDisplayIf (!newTimeHandle, "Out of memory"); PrvSetTimeField(dialog, TimeZoneDialogCurrentTimeField, currentTimeHandle, ¤tTime, false); } else { // Hide the current and new time. FrmHideObject(dialog, FrmGetObjectIndex (dialog, TimeZoneDialogCurrentTimeLabel)); FrmHideObject(dialog, FrmGetObjectIndex (dialog, TimeZoneDialogCurrentTimeField)); FrmHideObject(dialog, FrmGetObjectIndex (dialog, TimeZoneDialogNewTimeLabel)); FrmHideObject(dialog, FrmGetObjectIndex (dialog, TimeZoneDialogNewTimeField)); // Make the list show more items to take up extra the space. LstSetHeight(listP, LstGetVisibleItems(listP) + extraTimeZonesToShowWhenNoTimes); } // Find the time zone in the list closest to the current time zone, and that // matches <*ioLocaleInTimeZoneP> if <anyLocale> is false. closestDelta = hoursInMinutes * hoursPerDay; // so big that all others will be smaller for (timeZoneIndex = 0; timeZoneIndex < numTimeZones; timeZoneIndex++) { Boolean checkDelta = anyLocale; testTimeZone = tzArrayP[timeZoneIndex].tzOffset; delta = Abs(testTimeZone - oldTimeZone); if (!anyLocale) { if (tzArrayP[timeZoneIndex].tzCountry == ioLocaleInTimeZoneP->country) { // If we haven't previously found a matching locale, reset the // delta so that this entry overrides any previous best entry. if (!foundLocale) { foundLocale = true; closestDelta = hoursInMinutes * hoursPerDay; } checkDelta = true; } // If we haven't yet found a matching locale, go for the closest delta. else { checkDelta = !foundLocale; } } // If we want to check the time zone delta, do it now. if (checkDelta && (delta < closestDelta)) { closestIndex = timeZoneIndex; closestDelta = delta; } } // Scroll so that time zone is in the center of the screen and select it if it's an exact match. LstSetTopItem(listP, max(0, closestIndex - (LstGetVisibleItems(listP) / 2))); if ((closestDelta == 0) && (anyLocale || foundLocale)) { LstSetSelection(listP, closestIndex); if (showTimes) { newTime = currentTime; PrvSetTimeField(dialog, TimeZoneDialogNewTimeField, newTimeHandle, &newTime, false); } } else { LstSetSelection(listP, noListSelection); } LstSetDrawFunction(listP, PrvTimeZoneListDrawItem); FrmDrawForm (dialog); while (!done) { Boolean handled = false; EvtGetEvent(&event, SysTicksPerSecond()); // so we can update the current and new time if (SysHandleEvent ((EventType *)&event)) { continue; } if (event.eType == nilEvent) { if (showTimes) { PrvUpdateTimeFields( dialog, ¤tTime, &newTime, currentTimeHandle, newTimeHandle, TimeZoneDialogCurrentTimeField, TimeZoneDialogNewTimeField); } } else if (event.eType == ctlSelectEvent) { handled = true; switch (event.data.ctlSelect.controlID) { case TimeZoneDialogOKButton: // Set the new time zone. *ioTimeZoneP = newTimeZone; *ioLocaleInTimeZoneP = newTimeZoneLocale; done = true; confirmed = true; break; case TimeZoneDialogCancelButton: done = true; break; default: ErrNonFatalDisplay("Unknown control in form"); break; } } // User tapped on a time zone in the list. else if (event.eType == lstSelectEvent) { UInt16 localeIndex; ErrNonFatalDisplayIf(event.data.lstSelect.listID != TimeZoneDialogTimeZoneList, "Unknown list in form"); newTimeZone = tzArrayP[event.data.lstSelect.selection].tzOffset; newTimeZoneLocale.country = tzArrayP[event.data.lstSelect.selection].tzCountry; newTimeZoneLocale.language = lmAnyLanguage; if (LmLocaleToIndex(&newTimeZoneLocale, &localeIndex) == errNone) { if (LmGetLocaleSetting( localeIndex, lmChoiceLocale, &newTimeZoneLocale, sizeof(newTimeZoneLocale))) { ErrNonFatalDisplay("Can\'t get locale"); } } adjustTimes = showTimes; handled = true; } else if (event.eType == keyDownEvent) { if (!TxtCharIsHardKey(event.data.keyDown.modifiers, event.data.keyDown.chr)) { // Hard scroll buttons if (EvtKeydownIsVirtual(&event)) { if (event.data.keyDown.chr == vchrPageUp) { handled = true; LstScrollList(listP, winUp, LstGetVisibleItems(listP) - 1); } else if (event.data.keyDown.chr == vchrPageDown) { handled = true; LstScrollList(listP, winDown, LstGetVisibleItems(listP) - 1); } } else if (TxtCharIsPrint(event.data.keyDown.chr)) { Int16 index; handled = true; index = PrvSearchTimeZoneNames(tzArrayP, numTimeZones, event.data.keyDown.chr); if (index != noListSelection) { Int16 delta = index - listP->topItem; if (delta < 0) { LstScrollList(listP, winUp, -delta); } else if (delta > 0) { LstScrollList(listP, winDown, delta); } } else { SndPlaySystemSound(sndError); } } } } else if (event.eType == appStopEvent) { EvtAddEventToQueue (&event); done = true; break; } // If we didn't handle the event, give the form code a crack at it. // This simulates the "normal" method of installing an event handler // for a form, which gets called, and then if it returns false, the // FrmHandleEvent routine gets called. if (!handled) { FrmHandleEvent(dialog, &event); } // If something changed, and we need to update our time display, // do it now. if (adjustTimes) { adjustTimes = false; newTime = currentTime; TimAdjust(&newTime, (Int32)(newTimeZone - oldTimeZone) * minutesInSeconds); PrvSetTimeField(dialog, TimeZoneDialogNewTimeField, newTimeHandle, &newTime, true); } } // end while true if (showTimes) { FldSetTextHandle(FrmGetObjectPtr (dialog, FrmGetObjectIndex (dialog, TimeZoneDialogCurrentTimeField)), NULL); FldSetTextHandle(FrmGetObjectPtr (dialog, FrmGetObjectIndex (dialog, TimeZoneDialogNewTimeField)), NULL); MemHandleFree(currentTimeHandle); MemHandleFree(newTimeHandle); } FrmEraseForm (dialog); FrmDeleteForm (dialog); FrmSetActiveForm(originalForm); PrvDeleteTimeZoneArray(tzArrayP, tzNamesH); return confirmed; } // SelectTimeZone