/* $Id: ckmkey.c,v 1.4 91/12/15 23:17:43 rick Exp $ * $Source: /uw/mackermit/RCS/ckmkey.c,v $ *------------------------------------------------------------------ * $Log: ckmkey.c,v $ * Revision 1.4 91/12/15 23:17:43 rick * ut9 * * Revision 1.3 91/10/03 12:43:08 rick * UT(5) * *------------------------------------------------------------------ * $Endlog$ */ /* * CKMKEY.C * * This file contains all procedures and data related to the handling * of key macros. * * Matthias Aebi, ECOFIN Research and Consulting, Ltd., Oct 1987 * * Chaged 12/11/87 Paul Placeway @ Ohio State University: changed the * internal storage of a key macro from a C string to a Pascal string * (so that I could bind Control-Space to send ASCII NUL (0x00) * * Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New * York. Permission is granted to any individual or institution to use this * software as long as it is not sold for profit. This copyright notice must * be retained. This software may not be included in commercial products * without written permission of Columbia University. */ #include "ckcdeb.h" /* Kermit definitions */ #include "ckcker.h" /* Kermit definitions */ #include "ckmdef.h" /* Common Mac module definitions */ #include "ckmres.h" /* resource defs */ #include "ckmptp.h" /* ckm* Prototypes */ /* KSET This structure tells for which key / modifier combinations */ /* there is a special macro declaration. */ /* MSET This structure holds all macrokey definitons in a packed */ /* form. To be useful it has to be unpacked into a macrodefs */ /* structure. */ hmacrodefs macroshdl; char keytable[512]; extern Handle gethdl (); /****************************************************************************/ /* Look for a macrodefinition for theCode and return its table index if */ /* found or -1 if not found */ /****************************************************************************/ short FindMacro (short theCode) { short i; short num; macrodefs *macros; macros = *macroshdl; num = macros->numOfMacros; for (i = 0; (i < num) && (macros->mcr[i].code != theCode); i++); if (i >= num) return (-1); else return (i); } /* FindMacro */ /****************************************************************************/ /* Set theCode at theIndex to -1 which means this entry is empty. Release */ /* the memory block occupied to save the macrostring if necessary */ /****************************************************************************/ DeleteMacro (short theIndex) { macrodefs *macros; macros = *macroshdl; macros->mcr[theIndex].code = -1; if (macros->mcr[theIndex].len > 4) /* release memory block if there is * one */ DisposPtr ((Ptr) macros->mcr[theIndex].macro); } /* DeleteMacro */ /****************************************************************************/ /* Add a new macro table entry at the end and return its index */ /****************************************************************************/ short NewMacro () { macrodefs *macros; long hsize; short num; macros = *macroshdl; macros->numOfMacros++; num = macros->numOfMacros; hsize = GetHandleSize ((Handle) macroshdl); if ((num * sizeof (macrorec) + MacroBaseSize) > hsize) { /* allocate room for 10 more definitions */ HUnlock ((Handle) macroshdl); SetHandleSize ((Handle) macroshdl, hsize + 10 * sizeof (macrorec)); HLock ((Handle) macroshdl); } /* init the code field to -1 (empty) */ macros = *macroshdl; macros->mcr[--num].code = -1; return (num); } /* NewMacro */ /****************************************************************************/ /* Enter theCode and theStr into the (empty!) entry at tabIndex. SetMacro */ /* does not release a previously existing string. Do this with DeleteMacro */ /* first. */ /****************************************************************************/ SetMacro (short theIndex, short theCode, char *theFlags, StringPtr theStr) /* PWP: note: theStr is really a Pascal string */ { macrodefs *macros; short slen; Ptr sptr; macros = *macroshdl; if (macros->mcr[theIndex].code != -1) /* check for free entry */ printerr ("snh SetMacro", 0); macros->mcr[theIndex].code = theCode; macros->mcr[theIndex].flags = *theFlags; if (*theFlags) { /* (PWP) save a bit of space: if flags, then no string */ macros->mcr[theIndex].macro = 0; macros->mcr[theIndex].len = 1; } else { slen = theStr[0] + 1; /* PWP: was ... = strlen(theStr); */ if (slen > 4) { sptr = NewPtr (slen); BlockMove (theStr, sptr, slen); /* PWP: yes, we save the length too */ macros->mcr[theIndex].macro = (long) sptr; } else { BlockMove (theStr, (Ptr) ¯os->mcr[theIndex].macro, slen); } macros->mcr[theIndex].len = slen; } } /* SetMacro */ /****************************************************************************/ /* Save theStr as macrostring for the code. Reuse any empty table entires */ /****************************************************************************/ InsertMacro (short theCode, char *theFlags, StringPtr theStr) /* PWP: theStr is really a Pascal string */ { short idx; HLock ((Handle) macroshdl); if (FindMacro (theCode) >= 0) /* does the entry exist already */ printerr ("snh InsertMacro", 0); idx = FindMacro (-1); /* look for a free entry */ if (idx < 0) /* create a new free entry if none is * available */ idx = NewMacro (); SetMacro (idx, theCode, theFlags, theStr); HUnlock ((Handle) macroshdl); } /* InsertMacro */ /****************************************************************************/ /* Remove the macro definition from the table and mark its entry as empty */ /****************************************************************************/ RemoveMacro (short theCode) { short idx; HLock ((Handle) macroshdl); idx = FindMacro (theCode); /* look for the entry */ if (idx >= 0) DeleteMacro (idx); /* delete it if we found it */ else printerr ("snh RemoveMacro", 0); HUnlock ((Handle) macroshdl); } /* RemoveMacro */ /****************************************************************************/ /* Replace the macro definition in the table */ /****************************************************************************/ ReplaceMacro (short theCode, char *theFlags, StringPtr theStr) { short idx; HLock ((Handle) macroshdl); idx = FindMacro (theCode); /* look for the entry */ if (idx >= 0) { DeleteMacro (idx); /* delete it if we found it */ /* reuse it immediately */ SetMacro (idx, theCode, theFlags, theStr); } else printerr ("snh ReplaceMacro", 0); HUnlock ((Handle) macroshdl); } /* ReplaceMacro */ /****************************************************************************/ /* Get the macro string for theCode from the table */ /****************************************************************************/ GetMacro (short theCode, char *theFlags, StringPtr theStr) { short idx; macrodefs *macros; short slen; Ptr sptr; HLock ((Handle) macroshdl); macros = *macroshdl; idx = FindMacro (theCode); /* look for the entry */ if (idx >= 0) { slen = macros->mcr[idx].len; if (slen > 4) sptr = (Ptr) macros->mcr[idx].macro; else sptr = (Ptr) ¯os->mcr[idx].macro; BlockMove (sptr, theStr, slen); /* *(theStr + slen) = '\0'; */ *theFlags = macros->mcr[idx].flags; } else printerr ("snh GetMacro", theCode); HUnlock ((Handle) macroshdl); } /* GetMacro */ /****************************************************************************/ /* dipose all macro strings */ /****************************************************************************/ DisposeMacros () { short i; short num; macrodefs *macros; HLock ((Handle) macroshdl); macros = *macroshdl; num = macros->numOfMacros; for (i = 0; i < num; i++) if (macros->mcr[i].code != -1) DeleteMacro (i); macros->numOfMacros = 0; HUnlock ((Handle) macroshdl); } /* DisposeMacros */ /****************************************************************************/ /* compress '\' expressions */ /****************************************************************************/ EncodeString (char *s, char *flags) /* PWP: takes a C string, returns a Pascal string. */ { register char *t, *b; register int v, i; char buf[256]; *flags = '\0'; /* no flags set */ if ((strcmp (s, "\\break") == 0) || (strcmp (s, "\\shortbreak") == 0)) { *flags = shortBreak; return; } else if (strcmp (s, "\\longbreak") == 0) { *flags = longBreak; return; } else if (strcmp (s, "\\leftarrow") == 0) { *flags = leftArrowKey; return; } else if (strcmp (s, "\\rightarrow") == 0) { *flags = rightArrowKey; return; } else if (strcmp (s, "\\uparrow") == 0) { *flags = upArrowKey; return; } else if (strcmp (s, "\\downarrow") == 0) { *flags = downArrowKey; return; } else if (strcmp (s, "\\pf1") == 0) { *flags = keypf1; return; } else if (strcmp (s, "\\pf2") == 0) { *flags = keypf2; return; } else if (strcmp (s, "\\pf3") == 0) { *flags = keypf3; return; } else if (strcmp (s, "\\pf4") == 0) { *flags = keypf4; return; } else if (strcmp (s, "\\enter") == 0) { *flags = keyenter; return; } else if ((strncmp (s, "\\keypad", 7) == 0) && (s[8] == '\0')) { if ((s[7] >= ',') && (s[7] <= '9')) { *flags = keycomma + (s[7] - ','); } return; } *(s + 255) = '\0'; b = s; t = buf; while (*s != '\0') { if (*s != '\\') *t = *s++; else if isdigit(*++s) { /* if \digits */ /* the current char was a backslash */ for (i = 0, v = 0; i < 3; i++) { /* only do the first 3 digits: \0335 -> ^[5 */ if (!isdigit(*s)) break; v = (8 * v) + (int) *s++ - (int) '0'; } *t = (char) v % 256; } else { switch (*s) { case 'b': /* backspace */ *t = '\010'; break; case 't': /* tab */ *t = '\011'; break; case 'n': /* newline -- linefeed */ *t = '\012'; break; case 'f': /* formfeed */ *t = '\014'; break; case 'r': /* return */ *t = '\015'; break; case '^': /* \^c --> control-C */ s++; if (*s == '?') *t = '\177';/* special case */ else *t = (*s & 037); break; default: *t = *s; } s++; } t++; } b[0] = (char) (t - buf); /* PWP: the length */ BlockMove (buf, &b[1], b[0]); /* copy the new string in */ } /* EncodeString */ /****************************************************************************/ /* Decode the pascal string into a C string with '\' notation */ /****************************************************************************/ DecodeString (char *s, char flags) /* takes a Pascal string, returns a C string */ /* PWP: note! flags is NOT a pointer here */ { register unsigned char ch; register char *tp; char t[256]; /* PWP: actually, this probably won't * overflow 256, but be safe */ register int i, j; switch (flags) { case shortBreak: strcpy (s, "\\break"); return; case longBreak: strcpy (s, "\\longbreak"); return; case leftArrowKey: strcpy (s, "\\leftarrow"); return; case rightArrowKey: strcpy (s, "\\rightarrow"); return; case upArrowKey: strcpy (s, "\\uparrow"); return; case downArrowKey: strcpy (s, "\\downarrow"); return; case keypf1: case keypf2: case keypf3: case keypf4: strcpy (s, "\\pf"); s[3] = flags - keypf1 + '1'; s[4] = '\0'; return; case keyenter: strcpy (s, "\\enter"); return; case keycomma: case keyminus: case keyperiod: /* there is no keyslash */ case key0: case key1: case key2: case key3: case key4: case key5: case key6: case key7: case key8: case key9: strcpy (s, "\\keypad"); s[7] = flags - keycomma + ','; s[8] = '\0'; return; } tp = t; for (i = 1; i <= s[0]; i++) { /* PWP: step through a Pascal string */ ch = s[i]; if ((ch < ' ') || (ch > 126)) { *tp++ = '\\'; j = (long) ch & 0377; /* mask of sign extension */ *tp++ = (j / 0100) + '0'; /* 64s digit */ j &= 077; *tp++ = (j / 010) + '0'; /* 8s digit */ j &= 07; *tp++ = j + '0'; /* 1s digit */ } else if (ch == '\\') { *tp++ = '\\'; *tp++ = '\\'; } else *tp++ = ch; } *tp = '\0'; t[255] = '\0'; /* be extra safe */ strcpy (s, t); /* copy it into place */ } /* DecodeString */ #define KeyPressed 2 /* dummy item. returned when a key is pressed */ #define myKeyCodeMask 0x7F00 #define keyModifierMask 0x1F00 short lastCode; /****************************************************************************/ /* return KeyPressed and TRUE if a keyevent happened */ /****************************************************************************/ pascal Boolean keyfilter (DialogPtr theDialog, EventRecord *theEvent, short *itemHit) { Boolean retVal; char modstr[256]; char theChar[3]; char codeStr[5]; retVal = (theEvent->what == keyDown); if (retVal) { *itemHit = KeyPressed; /* show modifiers pressed */ *modstr = '\0'; if (theEvent->modifiers & shiftKey) strcat (modstr, " shift"); if (theEvent->modifiers & alphaLock) strcat (modstr, " lock"); if (theEvent->modifiers & optionKey) strcat (modstr, " option"); #ifndef controlKey #define controlKey 4096 /* PWP: hack for beta MPW C */ #endif if (theEvent->modifiers & controlKey) strcat (modstr, " control"); if (theEvent->modifiers & cmdKey) strcat (modstr, " command"); strcpy (theChar, " "); *(theChar + 1) = (theEvent->message & charCodeMask); strcat (modstr, theChar); lastCode = ((theEvent->message & myKeyCodeMask) >> 8) + ((theEvent->modifiers & keyModifierMask) >> 1); NumToString (lastCode, codeStr); p2cstr(codeStr); strcat (modstr, " ("); strcat (modstr, codeStr); strcat (modstr, ")"); if (BitTst (keytable, lastCode)) strcat (modstr, " bound to:"); else strcat (modstr, " [unbound]"); c2pstr(modstr); SetIText (gethdl (KY_MODIFIER, theDialog), modstr); } return (retVal); } /* keyfilter */ #define HelpBtn 3 /****************************************************************************/ /* runs the set key macros dialog */ /****************************************************************************/ keymacros () { DialogPtr macrodlg; DialogPtr macro2dlg; short itemhit; Str255 keystr; char flags; macrodlg = GetNewDialog (KEYBOXID, NILPTR, (WindowPtr) - 1); for (;;) { SetIText (gethdl(KY_MODIFIER, macrodlg), "\pPress the key to program"); ModalDialog (keyfilter, &itemhit); switch (itemhit) { case OKBtn: /* finish up */ DisposDialog (macrodlg); /* finished with the dialog */ return; /* return */ case KeyPressed: /* fall in from above */ macro2dlg = GetNewDialog (KEY2BOXID, NILPTR, (WindowPtr) - 1); circleOK(macro2dlg); /* display the current macrostring if there is one */ if (BitTst (keytable, lastCode)) { GetMacro (lastCode, &flags, keystr); DecodeString ((char *) keystr, flags); /* decode invisible characters */ c2pstr(keystr); SetIText (gethdl (KY_TEXT, macro2dlg), keystr); SelIText(macro2dlg, KY_TEXT, 0, 32767); } itemhit = 0; while (itemhit == 0) { ModalDialog ((ModalFilterProcPtr) NILPROC, &itemhit); switch (itemhit) { case OKBtn: /* finish up */ GetIText (gethdl (KY_TEXT, macro2dlg), keystr); p2cstr(keystr); EncodeString ((char *) keystr, &flags); /* encode '\' expressions */ if (BitTst (keytable, lastCode)) if (keystr[0] != 0) /* if (strlen (keystr) > 0) */ ReplaceMacro (lastCode, &flags, keystr); else { RemoveMacro (lastCode); BitClr (keytable, lastCode); } else if (keystr[0] != 0) { InsertMacro (lastCode, &flags, keystr); BitSet (keytable, lastCode); } case QuitBtn: DisposDialog (macro2dlg); /* finished with the dialog */ break; case KY_HELP: itemhit = Alert (ALERT_HELP, (ModalFilterProcPtr) NILPROC); default: itemhit = 0; } } } } } /* keymacros */ modrec modtable[NUMOFMODS]; /****************************************************************************/ /* handle the modifier dialog */ /****************************************************************************/ keymoddialog () { DialogPtr moddlg; short itemhit; modrec tmodtable[NUMOFMODS];/* temporary copy of modtable */ short i; short ignType; Handle ignHdl; Rect box; char theStr[256]; char dummy; GrafPtr savePort; GetPort (&savePort); moddlg = GetNewDialog (MODBOXID, NILPTR, (WindowPtr) - 1); circleOK(moddlg); SetPort (moddlg); for (i = 0; i < NUMOFMODS; i++) tmodtable[i] = modtable[i]; /* make a temporary copy */ /* draw the gray lines */ for (i = MOD_LIN1; i <= MOD_LINL; i++) { GetDItem (moddlg, i, &ignType, &ignHdl, &box); FillRect (&box, qd.gray); } /* set the texts in the edit fields */ for (i = 0; i < NUMOFMODS; i++) { /* PWP: these are saved as pascal strings now... */ theStr[0] = 0; /* be double extra safe */ BlockMove (tmodtable[i].prefix, theStr, (tmodtable[i].prefix[0] + 1)); DecodeString (theStr, (char) 0); c2pstr(theStr); SetIText (gethdl (i + MOD_PRF1, moddlg), theStr); } /* set the checkboxes according to the bits set */ for (i = MOD_CHK1; i <= MOD_CHKL; i++) SetCtlValue (getctlhdl (i, moddlg), (tmodtable[(i - MOD_CHK1) / 9].modbits & (1 << ((i - MOD_CHK1) % 9))) ? btnOn : btnOff); /* loop till ok or cancel is pressed */ for (;;) { ModalDialog ((ModalFilterProcPtr) NILPROC, &itemhit); if (itemhit == OKBtn) { for (i = 0; i < NUMOFMODS; i++) { GetIText (gethdl (i + MOD_PRF1, moddlg), theStr); p2cstr(theStr); EncodeString (theStr, &dummy); if ((unsigned) (theStr[0]) > 19) theStr[0] = 19; /* Limit the length of the thing */ BlockMove (theStr, tmodtable[i].prefix, 20); } /* write the temporary copy back */ for (i = 0; i < NUMOFMODS; i++) modtable[i] = tmodtable[i]; UpdateOptKey(1); /* make Option key processing right */ } if ((itemhit == OKBtn) || (itemhit == QuitBtn)) { DisposDialog (moddlg); /* finished with the dialog */ SetPort (savePort); return; } if (itemhit == MOD_HELP) { itemhit = Alert (ALERT_MODHELP, (ModalFilterProcPtr) NILPROC); /* draw the gray lines again */ for (i = MOD_LIN1; i <= MOD_LINL; i++) { GetDItem (moddlg, i, &ignType, &ignHdl, &box); FillRect (&box, qd.gray); } } if (itemhit <= MOD_CHKL) { /* is it a check box ? */ tmodtable[(itemhit - MOD_CHK1) / 9].modbits ^= (1 << ((itemhit - MOD_CHK1) % 9)); SetCtlValue (getctlhdl (itemhit, moddlg), (tmodtable[(itemhit - MOD_CHK1) / 9].modbits & (1 << ((itemhit - MOD_CHK1) % 9))) ? btnOn : btnOff); } } } /* keymoddialog */ /****************************************************************************/ /* load and unpack the key macro tables */ /****************************************************************************/ loadkset () { Handle ktab; char *k; int i; THz curZone; /* load the bit table */ ktab = GetResource (KSET_TYPE, KSVER); if (ktab == (Handle) NIL) printerr ("Could not load the key macros (KSET) [old version?]", 0); if ((ktab == (Handle) NIL) || (GetHandleSize (ktab) == 0)) { /* init the keytable with zeroes if ktab is empty or not available */ /* ktab is empty in the resource fork of the program itself */ k = keytable; for (i = 1; i <= sizeof (keytable); i++) *k++ = '\0'; return; } HLock (ktab); BlockMove (*ktab, keytable, sizeof (keytable)); HUnlock (ktab); curZone = GetZone(); /* as per John Norstad's (Disinfectant) */ SetZone(HandleZone(ktab)); /* "Toolbox Gotchas" */ ReleaseResource(ktab); SetZone(curZone); } /* loadkset */ /****************************************************************************/ /* load and unpack the key macro table */ /****************************************************************************/ loadmset () { short i; short idx; short num; short theCode; Handle mtab; char *src; char flags; THz curZone; DisposeMacros (); /* release all macro strings */ /* load the bit table */ mtab = GetResource (MSET_TYPE, KMVER); if (mtab == (Handle) NIL) { printerr ("Could not load the key macros (MSET) [old version?]", 0); return; } HLock (mtab); HLock ((Handle) macroshdl); src = *mtab; /* load the modifier information */ BlockMove (src, (Ptr) modtable, sizeof (modtable)); src += sizeof (modtable); UpdateOptKey(1); /* make Option key processing right */ /* get the number of macro key definitions */ BlockMove (src, (Ptr) &num, sizeof (num)); src += sizeof (num); for (i = 0; i < num; i++) { /* Get the code */ BlockMove (src, (Ptr) &theCode, sizeof (theCode)); src += sizeof (theCode); /* Get the flags */ flags = *src++; /* Get the string */ /* p2cstr(src); -- PWP: it allready is a pascal string! */ idx = NewMacro (); /* create a new free entry */ SetMacro (idx, theCode, &flags, (StringPtr) src); src += src[0] + 1; /* PWP: was strlen */ } HUnlock ((Handle) macroshdl); HUnlock (mtab); curZone = GetZone(); /* as per John Norstad's (Disinfectant) */ SetZone(HandleZone(mtab)); /* "Toolbox Gotchas" */ ReleaseResource(mtab); SetZone(curZone); } /* loadmset */ /****************************************************************************/ /* save the key macro bit table */ /****************************************************************************/ savekset () { Handle ktab; ktab = NewHandle (sizeof (keytable)); if (ktab == (Handle) NIL) { printerr ("Could not save the key macros (KSET) (out of memory)", 0); return; } HLock (ktab); BlockMove (keytable, *ktab, sizeof (keytable)); HUnlock (ktab); AddResource (ktab, KSET_TYPE, KSVER, ""); } /* savekset */ /****************************************************************************/ /* pack and save the key macro table */ /****************************************************************************/ savemset () { short i; short num; short leng; short count; short theCode; int totalLen; char *dest; Ptr src; Handle mtab; macrodefs *macros; HLock ((Handle) macroshdl); macros = *macroshdl; num = macros->numOfMacros; totalLen = 0; count = 0; /* calculate the sum of the string lengths of all active table entries */ for (i = 0; i < num; i++) if (macros->mcr[i].code != -1) { totalLen += macros->mcr[i].len; count++; }; /* add the length for keycode and length */ /* information and the number of entries */ totalLen += count * (sizeof (macrorec) - sizeof (long)) + MacroBaseSize; mtab = NewHandle (totalLen); if (mtab == (Handle) NIL) { printerr ("Could not save the key macros (MSET) (out of memory)", 0); return; } HLock (mtab); dest = *mtab; /* save the modifier information */ BlockMove ((Ptr) modtable, dest, sizeof (modtable)); dest += sizeof (modtable); /* save the number of key macros */ BlockMove ((Ptr) &num, dest, sizeof (num)); dest += sizeof (num); /* save the whole rest of the table */ for (i = 0; i < num; i++) { theCode = macros->mcr[i].code; if (theCode != -1) { /* save the code number */ BlockMove ((Ptr) &theCode, dest, sizeof (theCode)); dest += sizeof (theCode); /* save the flags */ leng = macros->mcr[i].len; *dest++ = macros->mcr[i].flags; /* save the string length (1 byte!) */ /* PWP: nope! it's allready a Pascal string */ /* *dest++ = macros->mcr[i].len; */ /* save the macro string */ if (leng > 4) src = (Ptr) macros->mcr[i].macro; /* the address is stored here */ else src = (Ptr) ¯os->mcr[i].macro; BlockMove (src, dest, leng); dest += leng; } } HUnlock (mtab); HUnlock ((Handle) macroshdl); AddResource (mtab, MSET_TYPE, KMVER, ""); } /* savemset */