Source/SR_MacDialog.c

/******************************************************************************
 **                                                                          **
 **     Module:     SR_MacDialog.c                                           **
 **                                                                          **
 **                                                                          **
 **     Purpose:    Modal dialog routines                                    **
 **                                                                          **
 **                                                                          **
 **                                                                          **
 **     Copyright (C) 1996-1997 Apple Computer, Inc.  All rights reserved.   **
 **                                                                          **
 **                                                                          **
 *****************************************************************************/
#include "QD3D.h"
#include "QD3DExtension.h"
 
#if defined(WINDOW_SYSTEM_MACINTOSH) && WINDOW_SYSTEM_MACINTOSH
 
#define BUILDING_FOR_SYSTEM7    1
 
#include <assert.h>
#include <Dialogs.h>
#include <Sound.h>
#include <string.h>
#include <Devices.h>
#include <ToolUtils.h>
#include <Aliases.h>
#include <Resources.h>
#include <TextUtils.h>
 
#include "SR.h"
#include "SR_MacDialog.h"
 
 
/******************************************************************************
 **                                                                          **
 **                                 Dialog ID's                              **
 **                                                                          **
 *****************************************************************************/
 
#define SR_DLOG_ID          128
#define SR_MOVABLE_DLOG_ID  129
#define SR_DLOG_OK          1
#define SR_DLOG_CANCEL      2
#define SR_DLOG_CHECKBOX    3
 
 
/******************************************************************************
 **                                                                          **
 **                             Forward Declarations                         **
 **                                                                          **
 *****************************************************************************/
 
static TQ3Status SR_ModalDialog(
    TQ3RendererObject   renderer,
    TQ3Boolean          *canceled,  
    void                *rendererPrivate);
 
static TQ3Status SR_MovableModalDialog(
    TQ3RendererObject   renderer,
    TQ3DialogAnchor     dialogAnchor, 
    TQ3Boolean          *canceled,  
    void                *rendererPrivate);
    
static DialogPtr SR_LoadDialog(
    short               dlogId,
    void                *rendererPrivate);
 
static TQ3Status HandleHit(
    DialogPtr           dialog,
    short               itemHit,
    TQ3Boolean          *done,
    TQ3RendererObject   renderer,
    TQ3Boolean          *canceled,  
    void                *rendererPrivate);
    
static TQ3Boolean HandledEvent(
    EventRecord         *event,
    DialogPtr           dialog,
    short               *itemHit,
    TQ3Boolean          *done,
    TQ3RendererObject   renderer,
    TQ3Boolean          *canceled,  
    void                *rendererPrivate);
 
static void HandleMenuClick(
    long                menuItem);
 
static void DrawDefaultButtonOutline(
    DialogPtr           dialog,
    short               item);
 
Boolean ModalDialogFilter(
    DialogPtr           dialog,
    EventRecord         *event,
    short               *itemHit);
 
void SimulateButtonClick(
    DialogPtr           dialog,
    short               item);
 
 
/******************************************************************************
 **                                                                          **
 **                                 Globals                                  **
 **                                                                          **
 *****************************************************************************/
 
static ModalFilterUPP   SRgModalFilterUPP = NULL;
 
/* 
 * The next two statics are needed to set and reset the Resource chain prior to
 * calling out to the applications Event handler
 */
static short            SRgOldResFile, 
                        SRgResFile;
 
extern AliasHandle      SRgAliasHandle;
 
 
/******************************************************************************
 **                                                                          **
 **                             Naming Routines                              **
 **                                                                          **
 *****************************************************************************/
 
/*===========================================================================*\
 *
 *  Routine:    SR_GetNameString()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
TQ3Status SR_GetNameString(
    unsigned char               *dataBuffer, 
    unsigned long               bufferSize,
    unsigned long               *actualDataSize)
{
    TQ3Status   status = kQ3Success;
    Boolean     wasChanged;
    FSSpec      fileSpec;
    OSErr       macErr;
    Str255      tempBuffer;
 
    /*
     * Get at the resource file for this renderer and open the resource
     * file, locate the renderer name string and close the res file
     * returning the string in the buffer we were passed.
     */
    SRgOldResFile = CurResFile();
    
    macErr = ResolveAlias(NULL, SRgAliasHandle, &fileSpec, &wasChanged);
    
    if (macErr == noErr) {
        SRgResFile = FSpOpenResFile(&fileSpec, fsRdPerm);
        
        if (SRgResFile != -1) {
            *actualDataSize = 0L;
            GetIndString(tempBuffer, SR_NAME_RESOURCE, 1);
            
            /* trim the buffer if necessary */
            *actualDataSize = (tempBuffer[0] > bufferSize) ? 
                                    bufferSize : tempBuffer[0];
            
            if (dataBuffer != NULL)  {
                /* copy from the pascal str returned by the res mgr */
                memcpy(
                    (char *)dataBuffer, 
                    (char *) &tempBuffer[1],
                    *actualDataSize );
                    
                /* and terminate the string */
                dataBuffer[*actualDataSize] = '\0';
            }
 
            CloseResFile(SRgResFile);
            UseResFile(SRgOldResFile);
        } else {
            Q3XMacintoshError_Post(ResError());
            status = kQ3Failure;
        }
    } else {
        Q3XMacintoshError_Post(macErr);
        status = kQ3Failure;
    }
    
    if (status == kQ3Failure || ResError()) {
        *actualDataSize = 0L;
        dataBuffer = NULL;
    }
        
                
    return (status);
}
 
 
/******************************************************************************
 **                                                                          **
 **                             Dialog Routines                              **
 **                                                                          **
 *****************************************************************************/
 
/*===========================================================================*\
 *
 *  Routine:    SR_MacModalDialog()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
TQ3Status SR_MacModalDialog(
    TQ3RendererObject   renderer,
    TQ3DialogAnchor     dialogAnchor, 
    TQ3Boolean          *canceled,  
    void                *rendererPrivate)
{
    TQ3Status   status;
    Boolean     wasChanged;
    FSSpec      fileSpec;
    OSErr       macErr;
    
    SRgOldResFile = CurResFile();
    
    macErr = ResolveAlias(NULL, SRgAliasHandle, &fileSpec, &wasChanged);
    
    if (macErr == noErr) {
        SRgResFile = FSpOpenResFile(&fileSpec, fsRdPerm);
        
        if (SRgResFile != -1) {
            if (dialogAnchor.clientEventHandler == NULL) {
                status = SR_ModalDialog(renderer, canceled, rendererPrivate);
            } else {
                status = SR_MovableModalDialog(
                            renderer, 
                            dialogAnchor, 
                            canceled, 
                            rendererPrivate);
            }
            CloseResFile(SRgResFile);
            UseResFile(SRgOldResFile);
        } else {
            Q3XMacintoshError_Post(ResError());
            return (kQ3Failure);
        }
    } else {
        Q3XMacintoshError_Post(macErr);
        status = kQ3Failure;
    }
                    
    return (status);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    SR_MacModalDialog()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
TQ3Status SR_ModalDialog(
    TQ3RendererObject           renderer,
    TQ3Boolean                  *canceled,  
    void                        *rendererPrivate)
{
    DialogPtr   dialog;
    short       itemHit;
    TQ3Boolean  done = kQ3False;
    TQ3Status   status = kQ3Success;
    
    if (SRgModalFilterUPP == NULL) {
        SRgModalFilterUPP = NewModalFilterProc(ModalDialogFilter);
    }
    
    if (SRgModalFilterUPP == NULL) {
        Q3XMacintoshError_Post(MemError());
        return (kQ3Failure);
    }
 
    dialog = SR_LoadDialog(SR_DLOG_ID, rendererPrivate);
    
    if (dialog != NULL) {
        do {
            ModalDialog(SRgModalFilterUPP, &itemHit);
            HandleHit(
                dialog, itemHit, &done, renderer, canceled, rendererPrivate);
        } while (done == kQ3False);
        
        DisposeDialog(dialog);
    } else {
        Q3XMacintoshError_Post(ResError());
        status = kQ3Failure;
    }
            
    return (status);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    ModalDialogFilter()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
Boolean ModalDialogFilter(
    DialogPtr       dialog,
    EventRecord     *event,
    short           *itemHit)
{
    Boolean     handled = kQ3False;
    char        key;
    long        menuItem;
 
    switch (event->what) {
        case keyDown: {
            key = (char) (event->message & charCodeMask);
            if (key == '\r' || key == '\3') {
                *itemHit = SR_DLOG_OK;
                handled = kQ3True;
                SimulateButtonClick(dialog, SR_DLOG_OK);
            } else if (key == '\33' || 
                       ((event->modifiers & cmdKey) && key == '.')) {
                *itemHit = SR_DLOG_CANCEL;
                handled = kQ3True;
                SimulateButtonClick(dialog, SR_DLOG_CANCEL);
            } else if (event->modifiers & cmdKey) {
                menuItem = MenuKey(key);
                if (menuItem) {
                    HandleMenuClick(menuItem);
                    handled = kQ3True;
                }
            }
            break;
        }
        case updateEvt: {
            if ((DialogPtr) (event->message) == dialog) {
                DrawDefaultButtonOutline(dialog, SR_DLOG_OK);
            }
            break;
        }
    }
    
    return (handled);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    DrawDefaultButtonOutline()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
void DrawDefaultButtonOutline(
    DialogPtr   dialog,
    short       item)
{
    short       iType;
    Rect        iRect;
    Handle      iHandle;
    GrafPtr     oldPort;
    PenState    oldPenState;
    short       buttonOval;
    
    GetDialogItem(dialog, item, &iType, &iHandle, &iRect);
    
    GetPort(&oldPort);
    SetPort((**((ControlHandle) iHandle)).contrlOwner);
    
    GetPenState(&oldPenState);
    PenNormal();
    
    InsetRect(&iRect, -4, -4);
    FrameRoundRect(&iRect, 16, 16);
    buttonOval = (iRect.bottom - iRect.top) / 2 + 2;
    
    PenSize(3, 3);
    
    FrameRoundRect(&iRect, buttonOval, buttonOval);
    
    SetPenState(&oldPenState);
    SetPort(oldPort);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    SR_MacModalDialog()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
TQ3Status SR_MovableModalDialog(
    TQ3RendererObject           renderer,
    TQ3DialogAnchor             dialogAnchor, 
    TQ3Boolean                  *canceled,  
    void                        *rendererPrivate)
{
    DialogPtr       dialog, whichDlog;
    short           itemHit;
    TQ3Boolean      done = kQ3False, handled = kQ3False;
    EventRecord     event;
 
    dialog = SR_LoadDialog(SR_MOVABLE_DLOG_ID, rendererPrivate);
    
    if (dialog == NULL) {
        Q3XMacintoshError_Post(ResError());
        return (kQ3Failure);
    }
        
    do {
        /* 
         *  If your dialog handles DiskEvents then include it in the EventMask 
         *  The extra test (event.what == nullEvent) is so we can pass on null
         *  events to the app so it can do background processing.
         */
        if (WaitNextEvent(everyEvent & ~diskMask, &event, 1, NULL) ||
            (event.what == nullEvent)) {
            if (HandledEvent(
                    &event, 
                    dialog, 
                    &itemHit, 
                    &done, 
                    renderer, 
                    canceled, 
                    rendererPrivate) == kQ3False) {
                if (IsDialogEvent(&event)) {
                    /*
                     *  This is kind of wierd. If the event is a nullevent
                     *  then we want to give the dialog and the app a shot at
                     *  handling it. Hence the next statement.
                     */
                    handled = (TQ3Boolean) (event.what != nullEvent);   
                    if (DialogSelect(&event, &whichDlog, &itemHit) && 
                            whichDlog == dialog) {
                        HandleHit(
                            dialog, 
                            itemHit, 
                            &done, 
                            renderer, 
                            canceled, 
                            rendererPrivate);
                    }
                }
                
                if (!handled) {
                    /* 
                     *  At this point we have checked for all events
                     *  that we are interested in. Give the app a shot
                     *  at the event 
                     *
                     *  Restore the old Resource file ordering 
                     */
                    UseResFile(SRgOldResFile);
                    
                    /* 
                     *  The app's event-handler 
                     */
                    (dialogAnchor.clientEventHandler)(&event);
                    
                    /* 
                     *  Call me paranoid, but just in case the app
                     * opens another resource file 
                     */
                    SRgOldResFile = CurResFile();
                    
                    /* 
                     *  Set it back to ours
                      */
                    UseResFile(SRgResFile);
                }
            }
        }
    } while (done == kQ3False);
    
    DisposeDialog(dialog);
        
    return (kQ3Success);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    SR_LoadDialog()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
DialogPtr SR_LoadDialog(
    short   dlogId,
    void    *rendererPrivate)
{
    short       iType, checkBoxVal;
    Rect        iRect;
    Handle      iHandle;
    DialogPtr   dialog;
    
    dialog = GetNewDialog(dlogId, NULL, (WindowPtr) -1);
    GetDialogItem(dialog, SR_DLOG_CHECKBOX, &iType, &iHandle, &iRect);
    
    checkBoxVal = ((TSRPrivate *) rendererPrivate)->dummyConfigData;
    SetControlValue((ControlHandle) iHandle, checkBoxVal);
    
    ShowWindow(dialog);
    
    return (dialog);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    HandledEvent()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
TQ3Boolean HandledEvent(
    EventRecord             *event,
    DialogPtr               dialog,
    short                   *itemHit,
    TQ3Boolean              *done,
    TQ3RendererObject       renderer,
    TQ3Boolean              *canceled,  
    void                    *rendererPrivate)
{
    short           part;
    WindowPtr       whichWindow;
    TQ3Boolean      handled = kQ3False;
    char            key;
    long            menuItem;
    
    switch (event->what) {
        case mouseDown:
            part = FindWindow(event->where, &whichWindow);
            switch (part) {
                case inDrag: {
                    handled = kQ3True;
                    if (whichWindow == dialog) {
                        DragWindow(
                            dialog, 
                            event->where, 
                            &((*GetGrayRgn())->rgnBBox));
                    } else {
                        SysBeep(30);
                    }
                    break;
                }
                case inContent:
                case inGrow:
                case inGoAway:
                case inZoomIn:
                case inZoomOut: {       
                    if (whichWindow != dialog) {
                        handled = kQ3True;
                        SysBeep(30);
                    }
                    break;
                }
                case inMenuBar: {
                    handled = kQ3True;
                    menuItem = MenuSelect(event->where);
                    HandleMenuClick(menuItem);
                    break;
                }               
            }
            break;
        case keyDown: {
            key = (char) (event->message & charCodeMask);
            if (key == '\r' || key == '\3') {
                *itemHit = SR_DLOG_OK;
                handled = kQ3True;
                SimulateButtonClick(dialog, SR_DLOG_OK);
                HandleHit(
                    dialog, *itemHit, done, renderer, canceled, rendererPrivate);
            } else if (key == '\33' || 
                      ((event->modifiers & cmdKey) && key == '.')) {
                *itemHit = SR_DLOG_CANCEL;
                handled = kQ3True;
                SimulateButtonClick(dialog, SR_DLOG_CANCEL);
                HandleHit(
                    dialog, *itemHit, done, renderer, canceled, rendererPrivate);               
            } else if (event->modifiers & cmdKey) {
                menuItem = MenuKey(key);
                if (menuItem) {
                    HandleMenuClick(menuItem);
                    handled = kQ3True;
                }
            }
            break;
        }
        case updateEvt: {
            if ((DialogPtr) (event->message) == dialog) {
                DrawDefaultButtonOutline(dialog, SR_DLOG_OK);
            }
            break;
        }
    }
    
    return (handled);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    HandleMenuClick()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
void HandleMenuClick(
    long    menuItem)
{
    short           item = LoWord(menuItem);
    short           menu = HiWord(menuItem);
    MenuHandle      menuH;
    Str255          itemText;
 
    if (menuItem)  {
        menuH = GetMenuHandle(menu);
        GetMenuItemText(menuH, item, itemText);
        
        /* 
         *  This is ugly.
         *  We compare names of the menus because we don't know what the
         *  application's Menu IDs are. I am using constant strings this will
         *  not work on non English systems. You should proabably load some 
         *  STR resources and compare against them so that the Renderer can be
         *  localized.
         */
        if (strncmp((char *) (**menuH).menuData, (char *) "\pEdit", 5) == 0) {
            if (strncmp((char *) itemText, (char *) "\pUndo", 5) == 0) {
                /* 
                 *  Handle Undo if supported 
                 */
            } else if (strncmp((char *) itemText, (char *) "\pCut", 4) == 0) {
                /* 
                 *  Handle Cut if supported - use DialogCut 
                 */
            } else if (strncmp((char *) itemText, (char *) "\pCopy", 5) == 0) {
                /* 
                 *  Handle Copy if supported - use DialogCopy
                 */
            } else if (strncmp((char *) itemText, (char *) "\pPaste", 6) == 0) {
                /* 
                 *  Handle Paste if supported - use DialogPaste
                 */
            } else if (strncmp((char *) itemText, (char *) "\pClear", 6) == 0) {
                /* 
                 *  Handle Clear if supported 
                 */
            } else {
                /* 
                 *  Nothing else should be selectable in the Edit Menu
                 *  The calling application should have disabled all others
                 *  SysBeep or Alert to notify user of error 
                 */
                SysBeep(30);
            }
        } else if (strncmp(
                    (char *) (**menuH).menuData, 
                    (char *) "\x01\x14", 2) == 0) {
            /* 
             *  Apple menu was selected. All application specific items
             *  in the Apple Menu such as "About..." should have been
             *  disabled by the app 
             */
            OpenDeskAcc(itemText);
        } else {
            /* 
             *  No other menu should be selectable
             *  The calling application should have disabled all others
             *  SysBeep or Alert to notify user of error 
             */
            SysBeep(30);
        }
        HiliteMenu(0);
    }
}
 
 
/*===========================================================================*\
 *
 *  Routine:    HandleHit()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
TQ3Status HandleHit(
    DialogPtr                   dialog,
    short                       itemHit,
    TQ3Boolean                  *done,
    TQ3RendererObject           renderer,
    TQ3Boolean                  *canceled,  
    void                        *rendererPrivate)
{
    short       iType, checkBoxVal;
    Rect        iRect;
    Handle      iHandle;
    
    UNUSED(renderer);
 
    switch (itemHit) {
        case SR_DLOG_OK:
            GetDialogItem(dialog, SR_DLOG_CHECKBOX, &iType, &iHandle, &iRect);
            ((TSRPrivate *) rendererPrivate)->dummyConfigData = 
                GetControlValue((ControlHandle) iHandle);
            *done = kQ3True;
            *canceled = kQ3False;
            break;
        case SR_DLOG_CANCEL:
            *done = kQ3True;
            *canceled = kQ3True;
            break;
        case SR_DLOG_CHECKBOX:
            GetDialogItem(dialog, SR_DLOG_CHECKBOX, &iType, &iHandle, &iRect);
            checkBoxVal = GetControlValue((ControlHandle) iHandle);
            SetControlValue((ControlHandle) iHandle, !checkBoxVal);
            break;          
    }
    
    return (kQ3Success);
}
 
 
/*===========================================================================*\
 *
 *  Routine:    SimulateButtonClick()
 *
 *  Comments:   
 *
\*===========================================================================*/
 
void SimulateButtonClick(
    DialogPtr   dialog,
    short       item)
{
    short       iType;
    Rect        iRect;
    Handle      iHandle;
    long        ticks;
 
    GetDialogItem(dialog, item, &iType, &iHandle, &iRect);
    HiliteControl((ControlHandle) iHandle, kControlButtonPart);
    ticks = TickCount() + 8;
    
    while (TickCount() < ticks) {
        /*
         *  Spin...
         */
    }
    HiliteControl((ControlHandle) iHandle, 0);  
}
 
#endif  /*  WINDOW_SYSTEM_MACINTOSH  */