MacCalendarSetup.c

/*
    File:       MacCalendarSetup.c
 
    Contains:   Application for configuring the control strip.
 
    Written by: Martin Minow
 
    Copyright:  © 1994-1997 by Apple Computer, Inc., all rights reserved.
 
    Change History (most recent first):
 
    You may incorporate this sample code into your applications without
    restriction, though the sample code has been provided "AS IS" and the
    responsibility for its operation is 100% yours.  However, what you are
    not permitted to do is to redistribute the source as "DSC Sample Code"
    after having made changes. If you're going to re-distribute the source,
    we require that you make it clear in the source that the code was
    descended from Apple Sample Code, but that you've made changes.
 
    Sets the configuration parameters for MacCalendar
 
    File Type           APPL
    File Creator        SCCF    -- registered with DTS
 */
 
/////////////////////////////////////////////////////////////////////////
 
// Pick up resource constants common to both control strip
// module and application.
 
#include "MacCalendarCommon.h"
 
/////////////////////////////////////////////////////////////////////////
 
// Pick up resource constants common to both C and Rez for
// the application.
 
#include "MacCalendarSetup.h"
 
/////////////////////////////////////////////////////////////////////////
 
// Pick up prototype for the core drawing code.
 
#include "DrawCalendar.h"
 
/////////////////////////////////////////////////////////////////////////
 
#include <Dialogs.h>
#include <Errors.h>
#include <Fonts.h>
#include <Memory.h>
#include <Menus.h>
#include <Quickdraw.h>
#include <Resources.h>
#include <ToolUtils.h>
#include <Types.h>
#include <Gestalt.h>
#include <Processes.h>
 
/////////////////////////////////////////////////////////////////////////
 
// Various globals and constants.
 
// Indices into the STRN_Messages STR# resource.
 
enum {
    kMsgSystemError = 1
};
 
/*
 * Items in the setup dialog ('DITL' ID=DLOG_Setup).
 *
 * Note: kOKButton is also used for Alerts.
 */
enum {
    kOKButton = 1,
    kCancelButton,
    kCalendarUserItem,
    kFontNamePopupItem,
    kFontSizePopupItem,
    kFirstDayPopupItem,
    kDayNameUserItem,
    kSundayText,            /* Day names must be in order   */
    kMondayText,
    kTuesdayText,
    kWednesdayText,
    kThursdayText,
    kFridayText,
    kSaturdayText,
#if DEBUG
    kPrevMonth,
    kNextMonth,
#endif
    kDummyLastEnumBecauseWeCare 
};
 
DialogPtr           gSetupDialog;
short               gAppResourceFile;
 
SavedSettingsHandle gSettingHandle = NULL;
 
DateTimeRec         gNow;
 
/////////////////////////////////////////////////////////////////////////
 
// Boiler plate error dialogs.
 
static void
ErrorAlert(
        short                   alertID,
        OSErr                   errorStatus,
        short                   errorMsgID
    )
{
        Handle                  errorTextHdl;
        Str15                   errorStatusText;
        short                   userChoice;
        Str255                  errorMsgText;
        Str255                  errorText;
        short                   saveResFile;
        
        saveResFile = CurResFile();
        UseResFile(gAppResourceFile);
        GetIndString(errorMsgText, STRN_Messages, errorMsgID);
        NumToString(errorStatus, errorStatusText);
        errorTextHdl = GetResource('Estr', errorStatus);
        if (errorTextHdl != NULL)
            pstrcpy(errorText, *errorTextHdl);
        else {
            GetIndString(errorText, STRN_Messages, kMsgSystemError);
        }
        ParamText(errorStatusText, errorText, errorMsgText, "\p");
        userChoice = StopAlert(alertID, NULL);
        UseResFile(saveResFile);
        if (alertID == ALRT_FatalError || userChoice == kOKButton)
            ExitToShell();
}
 
static void
NonFatalError(
        OSErr                   errorStatus,
        short                   errorMsgID
    )
{
        ErrorAlert(ALRT_NonFatalError, errorStatus, errorMsgID);
}
 
static void
FatalError(
        OSErr                   errorStatus,
        short                   errorMsgID
    )
{
        ErrorAlert(ALRT_FatalError, errorStatus, errorMsgID);
}
 
/////////////////////////////////////////////////////////////////////////
 
static UserItemUPP gDrawDayNameUserItemUPP;
 
static pascal void
DrawDayNameUserItem(
        DialogPtr               theDialog,
        short                   itemNumber
    )
{
        short                   itemType;
        Handle                  itemHandle;
        Rect                    itemRect;
        Rect                    dayNameRect;
        short                   i;
        Str255                  work;
        Intl1Rec                **itlHandle;
        GrafPtr                 savePort;
        short                   saveFontNumber;
        short                   saveFontSize;
        
        GetPort(&savePort);
        SetPort(theDialog);
        saveFontNumber = theDialog->txFont;
        saveFontSize = theDialog->txSize;
        GetDialogItem(
            theDialog,
            itemNumber,
            &itemType,
            &itemHandle,
            &dayNameRect
        );
        itlHandle = (Intl1Rec **) GetIntlResource(1);
        for (i = 0; i < 7; i++) {
            /*
             * Get the string, abbreviate it, and draw it in the
             * day name rectangle, tabbed over the associated
             *Êedit text area. Messy, but it looks nicer.
             */
            pstrcpy(work, (**itlHandle).days[i]);
            GetDialogItem(
                theDialog,
                kSundayText + i,
                &itemType,
                &itemHandle,
                &itemRect
            );
            itemRect.top = dayNameRect.top;
            itemRect.bottom = dayNameRect.bottom;
            TETextBox(
                &work[1],
                (**itlHandle).abbrLen,
                &itemRect,
                teJustLeft
            );
        }
        TextFont(saveFontNumber);
        TextSize(saveFontSize);
        SetPort(savePort);
}
 
/////////////////////////////////////////////////////////////////////////
 
static void
AddThisDayName(
        short                   dayNumber
    )
{
        short                   itemType;
        Handle                  itemHandle;
        Rect                    itemRect;
        Str255                  work;
 
        GetDialogItem(
            gSetupDialog,
            dayNumber,
            &itemType,
            &itemHandle,
            &itemRect
        );
        GetDialogItemText(itemHandle, work);
        BlockMoveData(
            work,
            &(**gSettingHandle).dayNameString[(**gSettingHandle).dayNameString[0] + 1],
            work[0] + 1
        );
        (**gSettingHandle).dayNameString[0] += (work[0] + 1);
}
 
/////////////////////////////////////////////////////////////////////////
 
static void
RebuildDayNameText(void)
{
        short                       i;
        
        (**gSettingHandle).dayNameString[0] = 0;
        if ((**gSettingHandle).firstDayOfWeek == kFirstIsMonday) {
            for (i = kMondayText; i <= kSaturdayText; i++)
                AddThisDayName(i);
            AddThisDayName(kSundayText);
        }
        else {
            for (i = kSundayText; i <= kSaturdayText; i++)
                AddThisDayName(i);
        }
        (**gSettingHandle).dayNameString[++(**gSettingHandle).dayNameString[0]] = 0;
}
 
/////////////////////////////////////////////////////////////////////////
 
static UserItemUPP gDrawCalendarUserItemUPP;
 
static pascal void
DrawCalendarUserItem(
        DialogPtr               theDialog,
        short                   itemNumber
    )
{
        short                   itemType;
        Handle                  itemHandle;
        Rect                    itemRect;
        short                   fontNumber;
        GrafPtr                 savePort;
        short                   saveFontNumber;
        short                   saveFontSize;
        RgnHandle               saveRgn;
        
        RebuildDayNameText();
        GetPort(&savePort);
        SetPort(theDialog);
        saveFontNumber = theDialog->txFont;
        saveFontSize = theDialog->txSize;
        GetDialogItem(
            theDialog,
            itemNumber,
            &itemType,
            &itemHandle,
            &itemRect
        );
        EraseRect(&itemRect);
        FrameRect(&itemRect);
        GetFNum((**gSettingHandle).fontName, &fontNumber);
        saveRgn = NewRgn();
        GetClip(saveRgn);
        ClipRect(&itemRect);
        DrawCalendar(
            gSettingHandle,
            gNow.year,
            gNow.month,
            &itemRect
        );
        SetClip(saveRgn);
        DisposeRgn(saveRgn);
        TextFont(saveFontNumber);
        TextSize(saveFontSize);
        SetPort(savePort);
}
 
/////////////////////////////////////////////////////////////////////////
 
static void
SetThisDayName(
        short                   itemNumber,
        ConstStr255Param        itemText
    )
{
        short               itemType;
        Handle              itemHandle;
        Rect                itemRect;
 
        GetDialogItem(
            gSetupDialog,
            itemNumber,
            &itemType,
            &itemHandle,
            &itemRect
        );
        SetDialogItemText(itemHandle, itemText);
}
 
/////////////////////////////////////////////////////////////////////////
 
static void
SetupDialog(void)
{
        short               nMenuItems;
        short               i;
        Str255              work;
        Str255              fontSizeText;
        short               itemType;
        Handle              itemHandle;
        Rect                itemRect;
        StringPtr           dayName;
 
        SetDialogDefaultItem(gSetupDialog, kOKButton);
        SetDialogCancelItem(gSetupDialog, kCancelButton);
        /*
         * Set the font name popup menu
         */
        nMenuItems = CountMItems(GetMenu(MENU_Font));
        for (i = 1; i <= nMenuItems; i++) {
            GetMenuItemText(GetMenu(MENU_Font), i, work);
            if (EqualString(work, (**gSettingHandle).fontName, FALSE, FALSE))
                break;
        }
        if (i <= nMenuItems) {
            GetDialogItem(
                gSetupDialog,
                kFontNamePopupItem,
                &itemType,
                &itemHandle,
                &itemRect
            );
            SetControlValue((ControlHandle) itemHandle, i);
        }
        /*
         * Set the font size popup menu
         */
        nMenuItems = CountMItems(GetMenu(MENU_FontSize));
        NumToString((**gSettingHandle).fontSize, fontSizeText);
        for (i = 1; i <= nMenuItems; i++) {
            GetMenuItemText(GetMenu(MENU_FontSize), i, work);
            if (EqualString(work, fontSizeText, FALSE, FALSE))
                break;
        }
        if (i <= nMenuItems) {
            GetDialogItem(
                gSetupDialog,
                kFontSizePopupItem,
                &itemType,
                &itemHandle,
                &itemRect
            );
            SetControlValue((ControlHandle) itemHandle, i);
        }
        /*
         * Set the first day popup menu
         */
        GetDialogItem(
            gSetupDialog,
            kFirstDayPopupItem,
            &itemType,
            &itemHandle,
            &itemRect
        );
        SetControlValue(
            (ControlHandle) itemHandle,
            (**gSettingHandle).firstDayOfWeek
        );
        /*
         * Set the calendar drawing procedure
         */
        GetDialogItem(
            gSetupDialog,
            kCalendarUserItem,
            &itemType,
            &itemHandle,
            &itemRect
        );
        SetDialogItem(
            gSetupDialog,
            kCalendarUserItem,
            itemType,
            (Handle) gDrawCalendarUserItemUPP,
            &itemRect
        );
        /*
         * Setup the day name user item
         */
        GetDialogItem(
            gSetupDialog,
            kDayNameUserItem,
            &itemType,
            &itemHandle,
            &itemRect
        );
        SetDialogItem(
            gSetupDialog,
            kDayNameUserItem,
            itemType,
            (Handle) gDrawDayNameUserItemUPP,
            &itemRect
        );
        /*
         * Move the current date string into the dialog edit records.
         */
        dayName = (StringPtr) &(**gSettingHandle).dayNameString[1];
        i = ((**gSettingHandle).firstDayOfWeek == kFirstIsSunday)
            ? kSundayText : kMondayText;
        for (; i <= kSaturdayText; i++) {
            SetThisDayName(i, dayName);
            dayName = (StringPtr) &dayName[dayName[0] + 1];
        }
        if ((**gSettingHandle).firstDayOfWeek == kFirstIsMonday)
            SetThisDayName(kSundayText, dayName);
}
 
/////////////////////////////////////////////////////////////////////////
 
static OSErr
ReadCurrentParameters(void)
{
        OSErr                   result;
 
        result = Gestalt(kControlStripCreator, (long *) &gSettingHandle);
        if (result == noErr
             && gSettingHandle != nil
             && GetHandleSize((Handle) gSettingHandle) == sizeof (SavedSettings)
             && (**gSettingHandle).signature == kControlStripCreator
             && (**gSettingHandle).prefVersion == kPrefVersion) {
            result = HandToHand( (Handle *) &gSettingHandle );
        } else {
            if (result == noErr) {
                result = gestaltUndefSelectorErr;
            }
        }
        
        if (gSettingHandle != nil) {
            HLock( (Handle) gSettingHandle);
        }
        
        return (result);
}
 
/////////////////////////////////////////////////////////////////////////
 
static void
SaveNewParameters(void)
{
        OSErr                   status;
        SavedSettingsHandle     controlStripSettings;
 
        // Locate the control strip's settings handle.
        
        status = Gestalt(kControlStripCreator, (long *) &controlStripSettings);
        QAssert(status == noErr);
        QAssert(controlStripSettings != nil);
        QAssert(GetHandleSize( (Handle) controlStripSettings) == sizeof (SavedSettings));
        QAssert((**controlStripSettings).signature == kControlStripCreator);
        QAssert((**controlStripSettings).prefVersion == kPrefVersion);
        
        // Copy the new settings into the control strip's preferences.
        
        pstrcpy( (**controlStripSettings).dayNameString , (**gSettingHandle).dayNameString );
        pstrcpy( (**controlStripSettings).fontName , (**gSettingHandle).fontName );
        (**controlStripSettings).fontSize = (**gSettingHandle).fontSize;
        (**controlStripSettings).firstDayOfWeek = (**gSettingHandle).firstDayOfWeek;
        
        // Bump the modification count so that the control strip knows we
        // want it to save the settings.
        (**controlStripSettings).modCount += 1;
}
 
/////////////////////////////////////////////////////////////////////////
 
static void
InitApplication(void)
{
        Handle                  menuBarHdl;
        
        InitGraf(&qd.thePort);
        InitFonts();
        InitWindows();
        InitMenus();
        TEInit();
        InitDialogs(0);
        MaxApplZone();
 
        menuBarHdl = GetNewMBar(MBAR_MenuBar);
        SetMenuBar(menuBarHdl);
        AppendResMenu(GetMenuHandle(MENU_Apple), 'DRVR');
        AppendResMenu(GetMenu(MENU_Font), 'FONT');
 
        DrawMenuBar();
 
        gAppResourceFile = CurResFile();
 
        gDrawCalendarUserItemUPP = NewUserItemProc(DrawCalendarUserItem);
        gDrawDayNameUserItemUPP = NewUserItemProc(DrawDayNameUserItem);
 
        InitCursor();
}
 
/////////////////////////////////////////////////////////////////////////
 
static Boolean
DoDialog(void)
{
        short               itemHit;
        short               itemType;
        Handle              itemHandle;
        Rect                itemRect;
        short               i;
        Str255              work;
        Boolean             redraw;
        long                newFontSize;
        
        ShowWindow(gSetupDialog);
        do {
            ModalDialog(NULL, &itemHit);
            GetDialogItem(
                gSetupDialog,
                itemHit,
                &itemType,
                &itemHandle,
                &itemRect
            );
            redraw = FALSE;
            switch (itemHit) {
            case kFontNamePopupItem:
                i = GetControlValue((ControlHandle) itemHandle);
                GetMenuItemText(GetMenu(MENU_Font), i, work);
                if (EqualString((**gSettingHandle).fontName, work, FALSE, FALSE) == FALSE) {
                    pstrcpy((**gSettingHandle).fontName, work);
                    redraw = TRUE;
                }
                break;
            case kFontSizePopupItem:
                i = GetControlValue((ControlHandle) itemHandle);
                GetMenuItemText(GetMenu(MENU_FontSize), i, work);
                StringToNum(work, &newFontSize);
                if (newFontSize != (**gSettingHandle).fontSize) {
                    (**gSettingHandle).fontSize = newFontSize;
                    redraw = TRUE;
                }
                break;
            case kFirstDayPopupItem:
                i = GetControlValue((ControlHandle) itemHandle);
                if (i != (**gSettingHandle).firstDayOfWeek) {
                    (**gSettingHandle).firstDayOfWeek = i;
                    redraw = TRUE;
                }
                break;
#if DEBUG
            /*
             * This lets us test the drawing algorithm without rebuilding the
             * actual status bar and rebooting the machine.
             */
            case kPrevMonth:
                if (--gNow.month < 1) {
                    gNow.month = 12;
                    --gNow.year;
                }
                redraw = TRUE;
                break;
            case kNextMonth:
                if (++gNow.month > 12) {
                    gNow.month = 1;
                    ++gNow.year;
                }
                redraw = TRUE;
                break;
#endif
            default:
                if (itemHit >= kSundayText && itemHit <= kSaturdayText)
                    redraw = TRUE;
                break;
            }
            if (redraw)
                DrawCalendarUserItem(gSetupDialog, kCalendarUserItem);
        } while (itemHit != kOKButton && itemHit != kCancelButton);
        return (itemHit == kOKButton);
}
 
/////////////////////////////////////////////////////////////////////////
 
void
main(void)
{
        unsigned long           nowSeconds;
        Boolean                 updateNeeded;
        
        InitApplication();
        GetDateTime(&nowSeconds);
        SecondsToDate(nowSeconds, &gNow);
        gSetupDialog = GetNewDialog(DLOG_Setup, NULL, (WindowPtr) -1L);
        if (gSetupDialog != NULL) {
            if (ReadCurrentParameters() != noErr) {
                NoteAlert(ALRT_NoPreferences, NULL);
            } else {
                SetupDialog();
                updateNeeded = DoDialog();
                if (updateNeeded) {
                    SaveNewParameters();
                }
            }
            DisposeDialog(gSetupDialog);
        }
        ExitToShell();
}