BigEasy/BigEasyDialogs.c

/*
    File:       BigEasyDialogs.c
 
    Contains:   xxx put contents here xxx
 
    Written by: xxx put writers here xxx
 
    Copyright:  © 1992, 1994 by Apple Computer, Inc., all rights reserved.
 
    Change History (most recent first):
 
         <8>     5/27/94    dvb     This&that
        <5+>      5/5/93    dvb     Fields & controls.
         <5>      5/5/93    dvb     
         <4>      1/7/93    dvb     Improve number-dialog with +/- keys. Make it compile under mpw.
         <3>     4/13/92    dvb     Allow windows to idle behind dialogue. Pass activate/deactivates
                                    to back windows.
         <2>      4/3/92    dvb     New calls for getting text & numbers.
         <1>     1/20/92    dvb     first checked in
 
*/
 
/* file: BigEasyDialogs.c
 *
 * Started 2 Jan 1992, more or less.
 *
 */
 
 
/*--------------------------
    Inclusions
--------------------------*/
 
#include <Windows.h>
#include <Dialogs.h>
#include <Controls.h>
#include <OSUtils.h>
#include <ToolUtils.h>
#include <Resources.h>
#include <TextEdit.h>
#include <Memory.h>
#include <BDC.h>
#include <Packages.h>
 
#include <Icons.h>
#include "BigEasyDialogs.h"
#include "BigEasyTextish.h"
#include "BigEasy2.h"
 
/*--------------------------
    Constants
--------------------------*/
 
#define kButtonHeight 20
#define kButtonWindowMargin 10
#define kButtonTextMargin 20
 
#define kOSEvent app4Evt            /* event used by MultiFinder */
#define kSuspendResumeMessage 1     /* high byte of suspend/resume event message */
 
/*--------------------------
    Local Data
--------------------------*/
 
typedef struct
    {
    StringPtr header;
    StringPtr body;
    short iconID;
    } EasyDialogMessageStuff;
 
 
static EasyDialogButtonList dOkayCancelButtonList =
    {
    2,0,1,
    "\pOkay",0,
    "\pCancel",0
    };
 
static EasyDialogButtonList dSaveDiscardCancelButtonList =
    {
    3,0,2,
    "\pSave",0,
    "\pDiscard",'D',
    "\pCancel",0
    };
 
static Boolean gNumbers = false;        /* true if we're getting a number string */
 
 
/*--------------------------
    Local Prototypes
--------------------------*/
 
static void EasyDialogMessageUpdateProc(long refcon,const Rect *updateRect);
static void BlinkButton(ControlHandle buttonControl);
static void BlinkItem(DialogPtr theDialog,short item);
static pascal Boolean MyModalFilterProc(DialogPtr theDialog,
        EventRecord *theEvent, short *itemHit);
static pascal Boolean MyFieldModalFilterProc(DialogPtr theDialog,
        EventRecord *theEvent,short *itemHit);
 
/*--------------------------
    Roughage
--------------------------*/
 
long EasyDialog(Rect *windowRect,
        beDialogUpdate updateProc,
        beDialogKey keyProc,
        beDialogClick clickProc,
        beDialogIdle idleProc,
        EasyDialogButtonList *buttonList,
        long refcon)
    {
    WindowPtr w;
    Rect r,s,updateRect;
    short i;
    short biggestButton,x;
    ControlHandle ch[20];
    Rect defaultButtonRect;
    EventRecord er;
    Boolean done;
    long saveMenus;
    long result;
    long eventCount;
    #pragma unused (clickProc)
 
    HiliteMenu(0);
    r = *windowRect;
 
    if(r.left <= 0)
        {
        s = qd.screenBits.bounds;
 
        OffsetRect(&r,
                (s.left + s.right - r.left - r.right)/2,
                (s.top + s.bottom - r.top - r.bottom)/2);
        }
 
    saveMenus = DisableAllMenus();
 
    i = gSystemVersion > 0x700 ? movableDBoxProc : dBoxProc;
 
    if(gHasColor)
        w = NewCWindow(0,&r,"\p",true,i,(WindowPtr)-1,false,0);
    else
        w = NewWindow(0,&r,"\p",true,i,(WindowPtr)-1,false,0);
    SetPort(w);
 
    r.right -= r.left;
    r.bottom -= r.top;
    r.left = r.top = 0;
 
    updateRect = r;
    updateRect.bottom -= 2*kButtonWindowMargin + kButtonHeight;
 
    if((long)buttonList <= 0)
        {
        if(buttonList == kEasyDialogSaveDiscardCancel)
            buttonList = &dSaveDiscardCancelButtonList;
        else
            {   
            if(buttonList == kEasyDialogOkayCancel)
                {
                dOkayCancelButtonList.count = 2;
                dOkayCancelButtonList.defaultButton = 0;
                dOkayCancelButtonList.cancelButton = 1;
                }
            else if(buttonList == kEasyDialogCancelOkay)
                {
                dOkayCancelButtonList.count = 2;
                dOkayCancelButtonList.defaultButton = 1;
                dOkayCancelButtonList.cancelButton = 1;
                }
            else
                {
                dOkayCancelButtonList.count = 1;
                dOkayCancelButtonList.defaultButton = 0;
                dOkayCancelButtonList.cancelButton = -1;
                }
            buttonList = &dOkayCancelButtonList;
            }
        }
 
    if(buttonList->defaultButton >= buttonList->count)
        buttonList->defaultButton = -1;
    if(buttonList->cancelButton >= buttonList->count)
        buttonList->cancelButton = -1;
            
    TextFont(0);
    TextSize(12);
    biggestButton = 0;
    for(i = 0; i<buttonList->count; i++)
        {
        x = StringWidth(buttonList->button[i].name);
        if(x > biggestButton)
            biggestButton = x;
        }
 
    biggestButton += kButtonTextMargin;
 
    s.left = kButtonWindowMargin;
    s.bottom = r.bottom - kButtonWindowMargin;
    s.right = s.left + biggestButton;
    s.top = s.bottom - kButtonHeight;
 
    biggestButton += kButtonWindowMargin;
 
    for(i = 0; i<buttonList->count; i++)
        {
        ch[i] = NewControl(w,&s,buttonList->button[i].name,true,0,0,1,pushButProc,i);
        if(i == buttonList->defaultButton)
            {
            defaultButtonRect = s;
            InsetRect(&defaultButtonRect,-4,-4);
            }
        OffsetRect(&s,biggestButton,0);
        }
 
    done = false;
 
    while(!done)
        {
        if(eventCount ++ & 1)
            {
            if(idleProc)
                (*idleProc)(refcon);
            IdleWindow(-1);
            }
 
        WaitNextEvent(0xffff,&er,0,nil);
 
        switch (er.what)
            {
            case 0:
                break;
 
            case mouseDown:
                    {
                    WindowPtr clickWindow;
                    short part;
                    ControlHandle whichControl;
 
                    part = FindWindow (er.where, &clickWindow);
                    if(part == inMenuBar)
                        {
                        long p;
 
                        p = MenuSelect(er.where);
                        }
                    else if(part == inDrag
                            && (clickWindow == w || er.modifiers & cmdKey))
                        DragWindow(clickWindow,er.where,&gBigRect);
                    else if(part != inContent || clickWindow != w)
                        SysBeep(1);
                    else
                        {
                        GlobalToLocal(&er.where);
                        FindControl(er.where,w,&whichControl);
                        if(whichControl)
                            {
                            if(TrackControl(whichControl,er.where,nil))
                                {
                                done = true;
                                result = GetCRefCon(whichControl);
                                }
                            }
                        }
                    }
                break;
 
            case activateEvt:
            activateEvent:
                HandleActivateEvent(&er);
                InitCursor();
                break;
 
            case updateEvt:
                if(er.message != (long)w)
                    {
                    HandleUpdateEvent(&er);
                    SetPort(w);
                    }
                else
                    {
                    BeginUpdate(w);
                    DrawControls(w);
                    if(buttonList->defaultButton >= 0)
                        {
                        PenSize(3,3);
                        FrameRoundRect(&defaultButtonRect,16,16);
                        PenNormal();
                        }
                    PenPat(&qd.gray);
                    MoveTo(updateRect.left + kButtonWindowMargin,updateRect.bottom);
                    LineTo(updateRect.right - kButtonWindowMargin,updateRect.bottom);
                    PenNormal();
                    if(updateProc)
                        (*updateProc)(refcon,&updateRect);
                    EndUpdate(w);
                    }
                break;
 
            case keyDown:
                    {
                    er.message &= 0x000000ff;
                    if( (er.message == 13 || er.message == 3)
                            && buttonList->defaultButton >= 0)      /* enter or return */
                        {
                        BlinkButton(ch[buttonList->defaultButton]);
                        done = true;
                        result = buttonList->defaultButton;
                        }
                    else if( ((er.message == '.' && (er.modifiers & cmdKey)) || er.message == 27)
                            && buttonList->cancelButton >= 0)       /* esc or cmd-. */
                        {
                        BlinkButton(ch[buttonList->cancelButton]);
                        done = true;
                        result = buttonList->cancelButton;
                        }
                    else if (er.modifiers & cmdKey)                 /* a key-equiv? */
                        {
                        short capKey;
 
                        capKey = er.message;
                        if(capKey >= 'a' && capKey <= 'z')
                            capKey += 'A' - 'a';
                        for(i = 0; i < buttonList->count; i++)
                            if(capKey == buttonList->button[i].key)
                                {
                                BlinkButton(ch[i]);
                                done = true;
                                result = i;
                                }
                            }
                        }
 
                    if(!done)       /* someone snatch the key? */
                        if(keyProc)
                            (*keyProc)(refcon,er.message,er.modifiers);
 
 
                break;
 
            case kOSEvent:
                if( (er.message>>24) == kSuspendResumeMessage)
                    goto activateEvent;
                break;
    
            } /* switch */
        } /* while !done */
 
    DisposeWindow(w);
 
    EnableAllMenus(saveMenus);
 
    return result;
    }
 
short EasyDialogMessage(short iconID,
        StringPtr header,StringPtr body,
        EasyDialogButtonList *buttonList)
    {
    EasyDialogMessageStuff stuff;
    Rect r;
    short result;
 
    stuff.header = header;
    stuff.body = body;
    stuff.iconID = iconID;
 
    r.left = r.top = 0;
    r.right = 420;
    r.bottom = 170;
 
    result = EasyDialog(&r,EasyDialogMessageUpdateProc,nil,nil,nil,buttonList,(long)&stuff);
 
    return result;
    }
 
void EasyDialogMessageUpdateProc(long refcon,const Rect *updateRect)
    {
    EasyDialogMessageStuff *stuff;
    Rect r;
    Handle theIcon;
 
    stuff = (EasyDialogMessageStuff *) refcon;
 
    r.left = r.top = kButtonWindowMargin;
    r.bottom = r.top + 32;
 
    if(stuff->iconID != -1)
        {
        r.right = r.left + 32;
        r.top = kButtonWindowMargin;
        theIcon = GetIcon(stuff->iconID);
        if(theIcon)
            {
            PlotIcon(&r,theIcon);
            ReleaseResource(theIcon);
            }
        else
            PlotIconID(&r,atAbsoluteCenter,0,stuff->iconID);
        }
 
    r.left += 32 + kButtonWindowMargin;
    r.right = updateRect->right - kButtonWindowMargin;
 
    if(stuff->header)
        TextBox(stuff->header+1,stuff->header[0],&r,teJustLeft);
 
    r.top = r.bottom + kButtonWindowMargin;
    r.left = kButtonWindowMargin;
    r.bottom = updateRect->bottom - kButtonWindowMargin;
 
    if(stuff->body)
        TextBox(stuff->body+1,stuff->body[0],&r,teJustLeft);
    }
 
 
void BlinkButton(ControlHandle buttonControl)
    {
    long dummy;
 
    HiliteControl(buttonControl,1);
    Delay(7,&dummy);
    HiliteControl(buttonControl,0);
    }
 
 
 
 
 
Boolean EasyDialogGetString(const StringPtr dialogTitle,
        const StringPtr dialogPrompt,
        StringPtr inOutString,short maxInOutStringLength)
/*
 * The scary thing about this routine is that
 * it requires two resources: DLOG 128 and 129.
 * 128 should use a modal alert-type window,
 * and 129, a moveable-modal.
 *
 * The items should be:
 *      1 - Okay
 *      2 - Cancel
 *      3 - Title static text = "^0"
 *      4 - Prompt static text = "^1"
 *      5 - Edit text field for entry
 */
    {
    DialogPtr dp;
    short hit;
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    Boolean result;
    Str255 outString;
 
    ParamText(dialogTitle,dialogPrompt,0,0);
 
    dp = GetNewDialog(128,0,(WindowPtr)-1);         //!!! should have two kinds
    if(!dp)
        {
        SysBeep(2);
        return false;
        }
 
    GetDItem(dp,5,&itemType,&itemHandle,&itemRect);
    SetIText(itemHandle,inOutString);
    SelIText(dp,5,0,32767);
    ModalDialog(MyModalFilterProc,&hit);
    (0,&hit);
 
    if(hit == 1)        /* Okay */
        {
        GetIText(itemHandle,outString);
        if(outString[0] > maxInOutStringLength)
            outString[0] = maxInOutStringLength;
        BlockMove(outString,inOutString,maxInOutStringLength+1);
        result = true;
        }
    else
        result = false;
    DisposeDialog(dp);
 
    return result;
    }
 
 
void BlinkItem(DialogPtr theDialog,short item)
    {
    ControlHandle button;
    short itemType;
    Rect itemRect;
 
    GetDItem(theDialog,item,&itemType,(void *)&button,&itemRect);
    BlinkButton(button);
    }
 
 
pascal Boolean MyModalFilterProc(DialogPtr theDialog,
        EventRecord *theEvent,short *itemHit)
    {
    short key;
    Boolean result;
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    short bump;
    Str255 str;
    long x;
 
    IdleWindow(-1);
 
    result = false;
    switch(theEvent->what)
        {
        case keyDown:
        case autoKey:
            key = theEvent->message & 0xFF;
            if(key == 27 || (key == '.' && (theEvent->modifiers & cmdKey)))
                {
                result = true;
                *itemHit = 2;
                BlinkItem(theDialog,2);
                }
            else if(key == 3 || key == 13)
                {
                result = true;
                *itemHit = 1;
                BlinkItem(theDialog,1);
                }
            else if((key =='a' || key == 'A') && (theEvent->modifiers &cmdKey))
                {
                SelIText(theDialog,5,0,32767);
                theEvent->what = 0;
                }
            else if(gNumbers && (key == '+' || key == '=')
                    && (theEvent->modifiers & cmdKey))
                {
                bump = 1;
 
        bumpString:
                if(theEvent->modifiers & shiftKey)
                    bump *= 10;
 
                GetDItem(theDialog,5,&itemType,&itemHandle,&itemRect);
                GetIText(itemHandle,str);
                StringToNum(str,&x);
                x += bump;
                NumToString(x,str);
                SetIText(itemHandle,str);
                SelIText(theDialog,5,0,32767);
                theEvent->what = 0;
                }
            else if(gNumbers && (key == '-' || key == '_')
                    && (theEvent->modifiers & cmdKey))
                {
                bump = -1;
                goto bumpString;
                }
            break;
 
        case updateEvt:
            if(theEvent->message != (long)theDialog)
                HandleUpdateEvent(theEvent);
            else
                {
                GrafPort *oldPort;
                GetPort(&oldPort);
                SetPort(theDialog);
                GetDItem(theDialog,1,&itemType,&itemHandle,&itemRect);
                InsetRect(&itemRect,-4,-4);
                PenSize(3,3);
                FrameRoundRect(&itemRect,16,16);
                PenNormal();
                SetPort(oldPort);
                }
            break;
 
        case mouseDown:
                {
                WindowPtr clickWindow;
                short part;
                
                part = FindWindow (theEvent->where, &clickWindow);
                if(part == inDrag
                        && (clickWindow == theDialog || theEvent->modifiers & cmdKey))
                    {
                    theEvent->what = 0;
                    DragWindow(clickWindow,theEvent->where,&gBigRect);
                    }
                }
            break;
        }
 
goHome:
    return result;
    }
 
 
Boolean EasyDialogGetNumber(const StringPtr dialogTitle,
        const StringPtr dialogPrompt,
        long *inOutNumber)
    {
    Str255 numString;
    Boolean result;
 
    NumToString(*inOutNumber,numString);
 
    gNumbers = true;
    result = EasyDialogGetString(dialogTitle,dialogPrompt,numString,31);
    gNumbers = false;
 
    if(result)
        StringToNum(numString,inOutNumber);
    return result;
    }
 
static EasyFieldList *fieldList;
 
Boolean EasyFieldDialog(short dialogID,EasyFieldList *fieldList,void *data,
        StringPtr dialogTitle)
    {
    DialogPtr dp;
    short hit;
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    Boolean result;
    Str255 aString;
    EasyField *field;
    long *w;
    short j,x;
    short firstTextItem;
 
    result = true;
 
    dp = GetNewDialog(dialogID,0,(WindowPtr)-1);
    if(!dp)
        {
        SysBeep(2);
        return false;
        }
 
    if(dialogTitle)
        SetWTitle(dp,dialogTitle);
 
    w = data;
    field = fieldList->field;
    firstTextItem = 0;
 
    while(field->itemNumber)
        {
        switch(field->itemType)
            {
            case easyRadioGroup:
                for(j = 0; j < field->itemCount; j++)
                    {
                    GetDItem(dp,field->itemNumber + j,
                            &itemType,&itemHandle,&itemRect);
                    if(itemType == ctrlItem + radCtrl)
                        SetCtlValue((ControlHandle)itemHandle,
                                j+1 == *w ? 1 : 0);
                    }
                w++;
                break;
 
            case easyCheckbox:
                GetDItem(dp,field->itemNumber,
                        &itemType,&itemHandle,&itemRect);
                if(itemType == ctrlItem + chkCtrl)
                    SetCtlValue((ControlHandle)itemHandle, *w);
                w++;
                break;
 
            case easyTextNumber:
                FixedPointToPString(*w,field->general ? 4 : 0,field->general,aString);
                GetDItem(dp,field->itemNumber,
                        &itemType,&itemHandle,&itemRect);
                if(itemType == editText)
                    SetIText(itemHandle,aString);
 
                if(!firstTextItem)
                    {
                    SelIText(dp,field->itemNumber,0,32767);
                    firstTextItem = field->itemNumber;
                    }
                w++;
                break;
 
            case easyTextString:
                GetDItem(dp,field->itemNumber,
                        &itemType,&itemHandle,&itemRect);
                if(itemType == editText)
                    SetIText(itemHandle,(StringPtr)w);
 
                if(!firstTextItem)
                    {
                    SelIText(dp,field->itemNumber,0,32767);
                    firstTextItem = field->itemNumber;
                    }
                w += 64;
                break;
 
            default:
                break;
            }
        field++;
        }
 
    ShowWindow(dp);
 
    do
        {
        ModalDialog(MyModalFilterProc,&hit);
        field = fieldList->field;
        while(field->itemNumber)
            {
            if(field->itemNumber <= hit
                    && hit < field->itemNumber+field->itemCount)
                {
                switch(field->itemType)
                    {
                    case easyRadioGroup:
                        for(j = 0; j < field->itemCount; j++)
                            {
                            GetDItem(dp,field->itemNumber + j,
                                    &itemType,&itemHandle,&itemRect);
                            if(itemType == ctrlItem + radCtrl)
                                SetCtlValue((ControlHandle)itemHandle,
                                        j+field->itemNumber == hit ? 1 : 0);
                            }
                        break;
        
                    case easyCheckbox:
                        GetDItem(dp,hit,
                                &itemType,&itemHandle,&itemRect);
                        if(itemType == ctrlItem + chkCtrl)
                            SetCtlValue((ControlHandle)itemHandle,
                                    !GetCtlValue((ControlHandle)itemHandle));
                        break;
 
                    }
                goto didHit;
                }
            field++;
            }
didHit:
        if(hit == 1 || hit == fieldList->cancelButton)
            goto doneDialog;
        } while(1);
 
doneDialog:
    if(hit != 1)        /* Okay */
        result = false;
 
    if(result)
        {
        w = data;
        field = fieldList->field;
        while(field->itemNumber)
            {
            switch(field->itemType)
                {
                case easyRadioGroup:
                    for(j = 0; j < field->itemCount; j++)
                        {
                        GetDItem(dp,field->itemNumber + j,
                                &itemType,&itemHandle,&itemRect);
                        if(itemType == ctrlItem + radCtrl)
                            {
                            x = GetCtlValue((ControlHandle)itemHandle);
                            if(x)
                                *w = j+1;
                            }
                        }
                    w++;
                    break;
    
                case easyCheckbox:
                    GetDItem(dp,field->itemNumber,
                            &itemType,&itemHandle,&itemRect);
                    if(itemType == ctrlItem + chkCtrl)
                        *w = GetCtlValue((ControlHandle)itemHandle);
                    w++;
                    break;
    
                case easyTextNumber:
                    GetDItem(dp,field->itemNumber,
                            &itemType,&itemHandle,&itemRect);
                    if(itemType == editText)
                        {
                        GetIText(itemHandle,aString);
                        PStringToFixedPoint(aString,
                                field->general ? 5 : 0,field->general,w);
                        }
                    w++;
                    break;
    
                case easyTextString:
                    GetDItem(dp,field->itemNumber,
                            &itemType,&itemHandle,&itemRect);
                    if(itemType == editText)
                        GetIText(itemHandle,(StringPtr)w);
                    w += 64;
                    break;
    
                default:
                    break;
                }
            field++;
            }
        }
 
    DisposeDialog(dp);
 
 
 
    return result;
    }