[comp.sources.mac] Dialog management in Think C 3.0

blob@Apple.COM (Brian Bechtel) (09/09/88)

[Dialog management in Think C 3.0]

I picked this up from Paul DuBois' BlobMgrDemo source a while back, and
slightly enhanced it to handle the Return or Enter keys and to highlight
the default OK button.  This works under Think's LightSpeed C.  It's
public domain code.

---
 /*
    Present a dialog box with a text rectangle, a scroll bar, and an OK
    button.  This can be used as a help dialog.  Pass in a dialog
    resource number and a handle to the text.  The dialog should
    have the OK button as the first item, and user items for the
    second and third items.  The second is the rect in which the
    control is drawn, the third is the rect in which text is drawn.
    Only the OK button should be enabled.

    The text handle should be locked before calling TextDlog and
    unlocked after.

    Original author:                    dubois@uwmacc.UUCP (Paul DuBois)
    with a few changes & cleanup by:    blob@apple.com  (Brian Bechtel)
*/

#include   <EventMgr.h>
#include   <DialogMgr.h>
#include   <ControlMgr.h>


#define    nil     0L

#define ETX 03      /* Enter key */
#define CR  13      /* Return key */

enum        /* item numbers */
{
    OKButton = 1,
    scrollBarUser,
    textRectUser
};

static ControlHandle    theScroll;
static Boolean          needScroll; /* true if need scroll bar */
static int              curLine;
static int              halfPage;
static TEHandle         teHand;



/************************************************************************
 *
 *  Function:       HighLightDefault
 *
 *  Purpose:        highlight the default button in a dialog
 *
 *  Returns:        nothing
 *
 *  Side Effects:   standard box is drawn around highlight box
 *
 *  Description:    draws the heavy rounded box around the OK button,
 *                  so that the user knows that hitting enter or return
 *                  is the same as pressing the OK button.
 *
 ************************************************************************/
static void
HighLightDefault(dPtr)
DialogPtr dPtr;
{
    int unusedItemType;
    Handle unusedItemHandle;
    Rect box;
    PenState p;

    /*  This next little piece of code puts the default heavy rounded
        box around the "OK" button, so the user knows that pressing
        return is the same as hitting "OK"
    */
    SetPort(dPtr);      /* without this, can't highlite OK */
    GetDItem(dPtr, OKButton, &unusedItemType, &unusedItemHandle, &box);
    GetPenState(&p);
    PenSize(3,3);
    InsetRect(&box, -4, -4);
    FrameRoundRect(&box, 16, 16);
    PenSize(p.pnSize.h, p.pnSize.v);
}

/************************************************************************
 *
 *  Function:       DrawScroll
 *
 *  Purpose:        draw scroll bar user item
 *
 *  Returns:        nothing
 *
 *  Side Effects:   draws scroll bar user item
 *
 *  Description:    if we need to scroll, show the control.
 *
 ************************************************************************/
static pascal void DrawScroll (theDialog, itemNo)
DialogPtr   theDialog;
int         itemNo;
{
    if (needScroll)
        ShowControl (theScroll);
}


/*
    Proc for drawing text display user item.
*/


/************************************************************************
 *
 *  Function:       DrawTextRect
 *
 *  Purpose:        draw text display user item
 *
 *  Returns:        nothing
 *
 *  Side Effects:   shows the text
 *
 *  Description:    Get the rectangle for our text.  If we need to scroll,
 *                  frame the rectangle so that it will look correct.
 *                  Get the view rectangle of our text rectangle, and do
 *                  a TEUpdate so that text edit will properly handle
 *                  updating the rectangle.
 *
 ************************************************************************/
static pascal void DrawTextRect (theDialog, itemNo)
DialogPtr   theDialog;
int         itemNo;
{
Rect    r;
int     itemType;
Handle  itemHandle;

    GetDItem (theDialog, textRectUser, &itemType, &itemHandle, &r);
    if (needScroll)
        FrameRect (&r);
    r = (**teHand).viewRect;
    TEUpdate (&r, teHand);
}


/************************************************************************
 *
 *  Function:       DoScroll
 *
 *  Purpose:        scroll to correct position
 *
 *  Returns:        nothing.
 *
 *  Side Effects:   text is scrolled to new position
 *
 *  Description:
 *                  Scroll to the correct position.  lDelta is the
 *                  amount to CHANGE the current scroll setting by.
 *
 ************************************************************************/
static void
DoScroll (lDelta)
int     lDelta;
{
int newLine;

    newLine = curLine + lDelta;
    if (newLine < 0) newLine = 0;
    if (newLine > GetCtlMax (theScroll)) newLine = GetCtlMax (theScroll);
    SetCtlValue (theScroll, newLine);
    lDelta = (curLine - newLine ) * (**teHand).lineHeight;
    TEScroll (0, lDelta, teHand);
    curLine = newLine;
}



/************************************************************************
 *
 *  Function:       __TrackScroll
 *
 *  Purpose:        track the scrolling
 *
 *  Returns:        nothing
 *
 *  Side Effects:   scrolling
 *
 *  Description:    get the control part that we're in.  If we're still
 *                  in the same part, we're going to scroll.  Check what
 *                  kind of part we're in, and set our delta accordingly.
 *                  Call DoScroll to actually handle the scrolling.
 *
 ************************************************************************/
static pascal void __TrackScroll (theScroll, partCode)
ControlHandle   theScroll;
int             partCode;
{
int lDelta;

    if (partCode == GetCRefCon (theScroll)) /* still in same part? */
    {
        switch (partCode)
        {
            case inUpButton: lDelta = -1; break;
            case inDownButton: lDelta = 1; break;
            case inPageUp: lDelta = -halfPage; break;
            case inPageDown: lDelta = halfPage; break;
        }
        DoScroll (lDelta);
    }
}


/*
    Filter to handle hits in scroll bar
*/


/************************************************************************
 *
 *  Function:       TextFilter
 *
 *  Purpose:        filter to handle hits in scroll bar
 *
 *  Returns:        false (indicating we should continue within our
 *                  modal dialog)
 *
 *  Side Effects:   May scroll the text
 *
 *  Description:    We're being called as part of a ModalDialog call.
 *                  Check what kind of event activated us.  If it was a
 *                  mouseDown, handle it.  If we were in the thumb, track
 *                  the thumb, otherwise scroll the indicated amount.
 *
 ************************************************************************/
static pascal Boolean TextFilter (theDialog, theEvent, itemHit)
DialogPtr   theDialog;
EventRecord *theEvent;
int         *itemHit;
{
Point   thePoint;
int     thePart;
Boolean result;

    result = false;
    switch (theEvent->what)
    {
        case mouseDown:
            thePoint = theEvent->where;
            GlobalToLocal (&thePoint);
            thePart = TestControl (theScroll, thePoint);
            if (thePart == inThumb)
            {
                (void) TrackControl (theScroll, thePoint, nil);
                DoScroll (GetCtlValue (theScroll) - curLine);
            }
            else if (thePart != 0)
            {
                SetCRefCon (theScroll, (long) thePart);
                (void) TrackControl (theScroll, thePoint, __TrackScroll);
            }
            break;
        case keyDown:
            if ((theEvent->message & charCodeMask) == ETX)
            {
                *itemHit = 1;
                result = true;
            }
            if ((theEvent->message & charCodeMask) == CR)
            {
                *itemHit = 1;
                result = true;
            }
            break;
        case updateEvt:
        case activateEvt:
            HighLightDefault(theDialog);
            break;
    }
    return (result);
}



/************************************************************************
 *
 *  Function:       TextDialog
 *
 *  Purpose:        put up text dialog with optional scroll bar
 *
 *  Returns:        nothing
 *
 *  Side Effects:   none
 *
 *  Description:    Pass in a dialog number, a handle to the text to
 *                  display, a font number, font size, and a boolean
 *                  indicating whether you want the text to wrap or not.
 *
 *                  The dialog referenced by dlogNum should have the 
 *                  OK button as the first item, and user items for the
 *                  second and third items.  The second is the rect in
 *                  which the control is drawn, the third is the rect in
 *                  which text is drawn. Only the OK button should be
 *                  enabled.
 *
 *                  The text handle should be locked before calling us,
 *                  and unlocked afterwards.
 *
 *                  Of course, the font number and size should exist in
 *                  the current system, or it will default to something
 *                  ugly.
 *
 ************************************************************************/
void
TextDialog (dlogNum, textHandle, fontNum, fontSize, wrap)
int     dlogNum;
Handle  textHandle;
int     fontNum;
int     fontSize;
Boolean wrap;
{
GrafPtr     oldPort;
DialogPtr   theDialog;
int         itemNo;
int         itemType;
Handle      itemHandle;
Rect        r;
int         scrollLines;
int         viewLines;
Handle      oldHText;
ProcPtr     filterProc = nil;

    GetPort (&oldPort);
    theDialog = GetNewDialog (dlogNum, nil, -1L);

    GetDItem (theDialog, textRectUser, &itemType, &itemHandle, &r);
    SetDItem (theDialog, textRectUser, itemType, DrawTextRect, &r);
/*
    incorporate text into a TERec.
*/
    SetPort (theDialog);
    TextFont (fontNum);
    TextSize (fontSize);

    InsetRect (&r, 4, 4);
    teHand = TENew (&r, &r);
    if (!wrap)
        (**teHand).crOnly = -1;
    oldHText = (**teHand).hText;        /* save this.  restore later */
    (**teHand).hText = textHandle;
    TECalText (teHand);
    viewLines = (r.bottom - r.top) / (**teHand).lineHeight;
    scrollLines = (**teHand).nLines - viewLines;
    needScroll = (scrollLines > 0);
    GetDItem (theDialog, scrollBarUser, &itemType, &itemHandle, &r);
    SetDItem (theDialog, scrollBarUser, itemType, DrawScroll, &r);
    if (needScroll)
    {
        theScroll = NewControl (theDialog, &r, "\p", false, 0, 0, 0,
                        scrollBarProc, nil);
        SetCtlMax (theScroll, scrollLines);
        halfPage = viewLines / 2;
        curLine = 0;
        filterProc = (ProcPtr) TextFilter;
    }

    ShowWindow (theDialog);

    do {
        ModalDialog (filterProc, &itemNo);
    } while (itemNo != OKButton);

/*
    restore hText field of TE record before disposing of it.
*/
    (**teHand).hText = oldHText;
    TEDispose (teHand);

    if (needScroll)
        DisposeControl (theScroll);
    SetPort (oldPort);
    DisposDialog(theDialog);
}
---