kcapApp.c

/*
    File:       kcapApp.c
 
    Contains:       demonstration keyboard drawing from KCAP resource
 
    Written by: Greg Robbins    
 
    Copyright:  Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved.
 
                            You may incorporate this Apple sample source code into your program(s) without
                            restriction. This Apple sample source code has been provided "AS IS" and the
                            responsibility for its operation is yours. You are not permitted to redistribute
                            this Apple sample source code as "Apple sample source 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 source
                            code, but that you've made changes.
 
    Change History (most recent first):
                            8/6/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                            2/92                                        uses RGetResource rather than GetResource for KCAP;
                                                        gets KCHR number from script manager
                                                                            adds menu of KCAP IDs, displays virtual keycodes
*/
                  
#include <String.h>
#include <Dialogs.h>
#include <Desk.h>
#include <ToolUtils.h>
#include <LowMem.h>
#include <TextUtils.h>
    
/* Constants */
#define appleID             1000            /* resource IDs/menu IDs for Apple, */
#define fileID              1001            /*  File and */
#define editID              1002            /*  Edit menus */
#define displayID           1003
 
#define appleM              0               /* Index for each menu in myMenus (array of menu handles) */
#define fileM               1
#define editM               2
#define displayM        3
 
#define menuCount           4               /* Total number of menus */
 
#define windowID            1000            /* Resource ID for main window */
#define aboutMeDLOG         1000            /* And Resource ID for About box dialog. */
 
#define quitItem            1               /* Quit in the File menu */
 
#define aboutMeCommand      1               /* Menu item in apple menu for About */
    
    
/* Globals */
MenuHandle myMenus[menuCount];
Rect dragRect;
Boolean doneFlag;
EventRecord myEvent;
WindowPtr   myWindow, whichWindow;
char theChar;
 
Boolean gKeycodesFlag = false;
short   gKcapNum, gCurrDisplayItem;
 
void SetUpMenus();
void DoCommand(long int mResult);
void DrawKeyCaps(short modifiers, short kcharNum, short kcapNum, Boolean keycodesFlag);
void ShowAboutMeDialog();
 
void main(void)
{
    Str255 tempStr, tempStr2;
    StringHandle tempStrHandle;
 
    InitGraf(&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(nil);
    InitCursor();
 
    SetRect(&dragRect, 4, 24, qd.screenBits.bounds.right - 4, qd.screenBits.bounds.bottom - 4);
    doneFlag = false;                           /* flag to detect when Quit command is chosen */
 
    myWindow = GetNewWindow(windowID, nil, (WindowPtr) -1);
    SetPort(myWindow);
 
    SetUpMenus();
    
    /*
    **  Main Event Loop
    */
    do {
        if (WaitNextEvent(everyEvent, &myEvent, 600, nil)) {
            switch (myEvent.what) {             /* case on event type */
 
                case mouseDown:
                    switch (FindWindow(myEvent.where, &whichWindow)) {
 
                        case inSysWindow:       /* desk accessory window: call Desk Manager to handle it */
                            SystemClick(&myEvent, whichWindow);
                            break;
 
                        case inMenuBar:         /* Menu bar: learn which command, then execute it. */
                            DoCommand(MenuSelect(myEvent.where));
                            break;
 
                        case inDrag:            /* title bar: call Window Manager to drag */
                            DragWindow(whichWindow, myEvent.where, &dragRect);
                            break;
 
                        case inContent:         /* body of application window: */
                            if (whichWindow != FrontWindow())
                                SelectWindow(whichWindow); /* and make it active if not */
                            break;
                    }
                    break;
 
                case updateEvt:                 /* Update the window. */
                    if ((WindowPtr) myEvent.message == myWindow) {
                        BeginUpdate((WindowPtr) myEvent.message);
                        
                        /* draw keyboard using current keyboard modifiers, 
                           current KCHR, current keyboard KCAP 
                             
                             see Tech Note 263 for a better way to get the
                             KCHR data under System 7 */
                             
                        DrawKeyCaps(myEvent.modifiers,
                            GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptKeys),
                            gKcapNum, gKeycodesFlag);
 
                        EndUpdate((WindowPtr) myEvent.message);
 
                        /* set window title */
                        NumToString(gKcapNum, tempStr);
                        
                        if (gKeycodesFlag) {
                            /* set window title to "Virtual Keycodes for KCAP ID _" */
                            tempStrHandle = GetString(128);
                            if (tempStrHandle) {
                                p2cstr(*tempStrHandle);
                                strcpy((char*)tempStr2, (const char*)*tempStrHandle);
                                strcat((char*)tempStr2, p2cstr(tempStr));
                                SetWTitle(myWindow, c2pstr((char*)tempStr2));
                                ReleaseResource((Handle)tempStrHandle);
                            }
                        }
                        else {
                            tempStrHandle = GetString(129);
                            if (tempStrHandle) {
                            
                                /* set window title to "Key Labels for KCAP ID _ and KCHR _" */
                                p2cstr(*tempStrHandle);
                                strcpy((char*)tempStr2, (const char*)*tempStrHandle);  /* string */
                                strcat((char*)tempStr2, p2cstr(tempStr)); /* KCAP # */
                                ReleaseResource((Handle)tempStrHandle);
                                tempStrHandle = GetString(130);
                                if (tempStrHandle) {
                                    p2cstr(*tempStrHandle);
                                    strcat((char*)tempStr2, (const char*)*tempStrHandle); /* string */
                                    
                                    /* get current KCHR number */
                                    NumToString(GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptKeys), tempStr);
                                    strcat((char*)tempStr2, p2cstr(tempStr)); /* KCHR # */
                                    SetWTitle(myWindow, c2pstr((char*)tempStr2));
                                    ReleaseResource((Handle)tempStrHandle);
                                }
                            }
                        }
                    }
                    break;
                            
                case keyDown:
                case autoKey:                   /* key pressed once or held down to repeat */
                    if (myWindow == FrontWindow()) {
                        theChar = (myEvent.message & charCodeMask); /* get the char */
                        /* 
                        **  If Command key down, do it as a Menu Command.
                        */
                        if (myEvent.modifiers & cmdKey)
                            DoCommand(MenuKey(theChar));
                        else {
                            EraseRect(&myWindow->portRect);
                            InvalRect(&myWindow->portRect);
                        }
                    }
                    break;
 
            }
        }
 
    } while (!doneFlag);
 
    DisposeWindow (myWindow);
}
 
 
void SetUpMenus()
{
    short i, kcapMaxCount, tempInt;
    Handle kcapHandle;
    Str255 tempStr, kcapStr;
    ResType tempResType;
 
    myMenus[appleM] = GetMenu(appleID);         /* read Apple menu from resource file */
    AppendResMenu(myMenus[appleM], 'DRVR');     /* add desk accessory names to Apple menu */
    myMenus[fileM] = GetMenu(fileID);           /* read file menu from resource file */
    myMenus[editM] = GetMenu(editID);           /* read edit menu from resource file */
    DisableItem(myMenus[editM], 0);
    myMenus[displayM] = GetMenu(displayID);         /* read display menu from resource file */
 
    /* add IDs of KCAP resources to display menu */
    gKcapNum = LMGetKbdType();  /* KCAP ID of current keyboard */
 
    LMSetROMMapInsert (0x0100);  /* use ROM resources, SetResLoad(false) */
 
    kcapMaxCount = CountResources('KCAP');
    if (kcapMaxCount > 0) AppendMenu(myMenus[displayM], "\p(-!");
 
    /* add KCAP menu items */
    for (i = 1; i <= kcapMaxCount; i++) {
        LMSetROMMapInsert (0x0100);
        kcapHandle = GetIndResource('KCAP', i);
        if (kcapHandle) {
            GetResInfo(kcapHandle, &tempInt, &tempResType, tempStr);
            NumToString(tempInt, tempStr);
            AppendMenu(myMenus[displayM],
                c2pstr(strcat(strcpy((char*)kcapStr, "KCAP "), p2cstr(tempStr))));
            ReleaseResource(kcapHandle);
            
            if (tempInt == gKcapNum) {
                CheckItem(myMenus[displayM], i+2, true);
                gCurrDisplayItem = i+2;
            }
        }
    }
    gKeycodesFlag = false;                  /* not displaying keycodes on keys initially */
    CheckItem(myMenus[displayM], 1, !gKeycodesFlag); /* mark that key labels are displayed */
    
    for (i = 0; i < menuCount; i++) 
        InsertMenu(myMenus[i], 0);              /* install menus in menu bar */
    
    
    DrawMenuBar();                              /* and draw menu bar */
}
 
 
void ShowAboutMeDialog()
{
    DialogPtr   theDialog;
    short       itemHit;
 
    theDialog = GetNewDialog(aboutMeDLOG, nil, (WindowPtr) -1);
    ModalDialog(nil, &itemHit);
    DisposeDialog(theDialog);
}
 
 
/* DrawKeyCaps draws the key caps, given a set of
   modifiers (in the high byte of the short) and
   KCHR and KCAP resource IDs, using the current pen in
   the current GrafPort 
     
     if keycodesFlag is true, the virtual keycodes are drawn
     rather than the key labels.
     
   kcapPtr just bounces along the resource data as we 
     parse it 
*/
     
/* see resource type definition on p. 14-101 of IM VI */
     
void DrawKeyCaps(short modifiers, short kchrNum, short kcapNum, Boolean keycodesFlag)
{
    typedef struct {
        char modifierMask;
        char keyCode;
        short deltaV;
        short deltaH;
    } KeyEntryRec;
    
    #define SHAPEMAXPTS 10  /* hopefully, fewer than 10 points per shape */
        
    Rect tempRect;
    Point penPoint, currPoint, swapPoint;
    Point shapePoint[SHAPEMAXPTS];
    Handle kcapResHandle;
    Ptr kcapPtr;
    
    KeyEntryRec thisKeyEntryRec;
    short mainIndex, shapeIndex, keyIndex, shapeTotal, shapeCount;
    short modifiedKeyCode;
    
    FontInfo theFontInfo;
    short fontHeight;
    Point charLoc;
    char theChar;
    
    RgnHandle keyshapeRgnHandle;
    
    Handle kchrResHandle;
    long state;
    
    Str255 keycodeStr;
    short saveTextSize;
 
    GetFontInfo(&theFontInfo);
    fontHeight = theFontInfo.ascent + theFontInfo.descent;
    saveTextSize = myWindow->txSize;
    
    kchrResHandle = RGetResource('KCHR', kchrNum);
    if (ResError() == noErr && kchrResHandle != nil) {
        state = 0;
    
        kcapResHandle = RGetResource('KCAP', kcapNum);
        if (ResError() == noErr && kcapResHandle != nil) {
        
            keyshapeRgnHandle = NewRgn();
            
            MoveHHi(kcapResHandle);
            HLock(kcapResHandle);
            kcapPtr = *kcapResHandle;
            
            /* draw boundary from origin */
            tempRect = *((Rect *) kcapPtr); 
            OffsetRect(&tempRect, - tempRect.left, -tempRect.top);
            EraseRect(&tempRect);                           
            FrameRect(&tempRect);                           
            kcapPtr += sizeof(Rect);
            
            /* draw textedit area */
            tempRect = *((Rect *) kcapPtr); 
            FrameRect(&tempRect);                           
            kcapPtr += sizeof(Rect);
            
            /* loop through main array */
            mainIndex = *((short *) kcapPtr);
            kcapPtr += sizeof(short);
            for ( ; mainIndex>0; mainIndex--) {
            
                /* loop through shape array - build array of points for this shape */
                shapeIndex = *((short *) kcapPtr);
                kcapPtr += sizeof(short);
 
                shapeTotal = (shapeIndex < SHAPEMAXPTS ? shapeIndex + 1 : SHAPEMAXPTS);
                for (shapeCount=0; shapeIndex>-1; shapeIndex--, shapeCount++) {
                    shapePoint[shapeCount] = *((Point *) kcapPtr);
                    kcapPtr += sizeof(Point);
                }
                
                /* start drawing keys of this shape from 0,0 */
                MoveTo(0,0);
                
                /* loop through key array */
                keyIndex = *((short *) kcapPtr);
                kcapPtr += sizeof(short);
                for ( ; keyIndex>-1; keyIndex--) {
                
                    /* get modifier mask, keyCode, and offset from previous key */                  
                    thisKeyEntryRec = *((KeyEntryRec *) kcapPtr);
                    kcapPtr += sizeof(KeyEntryRec);
                    
                    /* move the pen to the start of the key */
                    Move(thisKeyEntryRec.deltaH, thisKeyEntryRec.deltaV);
                    
                    /* draw the key, composed of one or more rects */
                    SetPt(&currPoint, 0, 0);
                    OpenRgn();
                    
                    for (shapeCount=0, shapeIndex=shapeTotal; shapeIndex; 
                                shapeIndex--, shapeCount++) {
 
                        /* set the rect, then reverse coordinates if necessary 
                           to ensure it is not empty */
                        SetRect(&tempRect, currPoint.h, currPoint.v, 
                            shapePoint[shapeCount].h, shapePoint[shapeCount].v);
                            
                        if (tempRect.top > tempRect.bottom) {
                            swapPoint.v = tempRect.top;
                            tempRect.top = tempRect.bottom;
                            tempRect.bottom = swapPoint.v;
                        }
                        if (tempRect.left > tempRect.right) {
                            swapPoint.h = tempRect.left;
                            tempRect.left = tempRect.right;
                            tempRect.right = swapPoint.h;
                        }
                        
                        /* move the rect to the pen location and add it to the region */
                        currPoint = shapePoint[shapeCount];
                        GetPen(&penPoint);
                        OffsetRect(&tempRect, penPoint.h, penPoint.v);  
                        FrameRect(&tempRect);
                    }
                    
                    /* draw the key frame */
                    CloseRgn(keyshapeRgnHandle);
                    FrameRgn(keyshapeRgnHandle);
                    SetEmptyRgn(keyshapeRgnHandle);
                    
                    /* convert the keyCode to a character code */
                    /* mask out high bit of keyCode and add masked modifiers;
                       KeyTranslate stroke bit taken from modifier parameter */
                         
                    if (thisKeyEntryRec.keyCode & 0x80) 
                        modifiers &= (((short) thisKeyEntryRec.modifierMask) << 8);
                    else modifiers |= (((short) thisKeyEntryRec.modifierMask) << 8);
                    
                    modifiedKeyCode =
                        ((thisKeyEntryRec.keyCode & 0x007F) | (modifiers & 0xFF80));
                        
                    theChar = KeyTranslate(*kchrResHandle, modifiedKeyCode, (UInt32*)&state);
        
                    if (!keycodesFlag) {
                        /* center and draw the character */
                        charLoc.v = 
                            ((tempRect.top + tempRect.bottom) / 2) - (fontHeight / 2);
                        charLoc.v += theFontInfo.ascent;
                        charLoc.h = 
                            ((tempRect.left + tempRect.right) / 2) - (CharWidth(theChar) / 2);
                        MoveTo(charLoc.h, charLoc.v);
                        DrawChar(theChar);
                    } else {
                        /* draw the virtual keycode instead of the key label */
                        TextSize(9);
                        NumToString(modifiedKeyCode & 0x7F, keycodeStr);
                        charLoc.v = 
                            ((tempRect.top + tempRect.bottom) / 2) - (fontHeight / 2);
                        charLoc.v += theFontInfo.ascent;
                        charLoc.h = 
                            ((tempRect.left + tempRect.right) / 2) - (StringWidth(keycodeStr) / 2);
                        MoveTo(charLoc.h, charLoc.v);
                        DrawString(keycodeStr);
                    }
                    
                    /* reposition pen for next key */
                    MoveTo(penPoint.h, penPoint.v);
                }
            }
            HUnlock(kcapResHandle);
            DisposeRgn(keyshapeRgnHandle);
            
            /* release the KCAP and KCHR unless they're System resources */
            if (HomeResFile(kcapResHandle) > 1) ReleaseResource(kcapResHandle);
        }   
        if (HomeResFile(kchrResHandle) > 1) ReleaseResource(kchrResHandle);
        
        TextSize(saveTextSize);
    }
}
 
 
void DoCommand(long int mResult)
{
    short   theItem,                            /* menu item number from mResult low-order word */
    theMenu;                            /* menu number from mResult high-order word */
    Str255  name;                               /* desk accessory name */
    int     tempInt;
    ResType tempResType;
    Str255 tempStr;
    
    Handle kcapHandle;
    theItem = LoWord(mResult);                  /* call Toolbox Utility routines to */
    theMenu = HiWord(mResult);                  /* set menu item number and menu */
 
    switch (theMenu) {                          /* switch on menu ID */
 
        case appleID:
            if (theItem == aboutMeCommand)
                ShowAboutMeDialog();
            else {
                    GetMenuItemText(myMenus[appleM], theItem, name);
                    tempInt = OpenDeskAcc(name);
                    SetPort(myWindow);
            }
            break;
 
        case fileID:
            if (theItem == quitItem)
                doneFlag = true;
            break;
 
        case editID:
            if (!SystemEdit(theItem - 1)) { /* Pass the command on to the Desk Manager. */
                ; /* do nothing */
            };
            break;
        
        case displayID:
                
                if (gCurrDisplayItem != theItem) {
                
                    if (theItem == 1) {
                        
                        /* display key labels */
                        gKeycodesFlag = !gKeycodesFlag;
                        CheckItem(myMenus[displayM], 1, !gKeycodesFlag);
                        
                    } else {
    
                        LMSetROMMapInsert (0x0100);  /* use ROM resources, SetResLoad(false) */
                        kcapHandle = GetIndResource('KCAP', theItem - 2);
    
                        if (kcapHandle) {
                            /* find the ID of the chosen KCAP */
                            GetResInfo(kcapHandle, &gKcapNum, &tempResType, tempStr);
                            ReleaseResource(kcapHandle);
    
                            CheckItem(myMenus[displayM], gCurrDisplayItem, false);
                            CheckItem(myMenus[displayM], theItem, true);
    
                            gCurrDisplayItem = theItem;
                        }
                    }
                    
                    
                    EraseRect(&myWindow->portRect);
                    InvalRect(&myWindow->portRect);
                }
                                                
            break;
    }
 
    HiliteMenu(0);                              /* Unhighlight menu title */
                                                /* (highlighted by MenuSelect) */
}