Home | History | Annotate | Line # | Download | only in src
      1 /***********************************************************
      2 Copyright (c) 1993, Oracle and/or its affiliates.
      3 
      4 Permission is hereby granted, free of charge, to any person obtaining a
      5 copy of this software and associated documentation files (the "Software"),
      6 to deal in the Software without restriction, including without limitation
      7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 and/or sell copies of the Software, and to permit persons to whom the
      9 Software is furnished to do so, subject to the following conditions:
     10 
     11 The above copyright notice and this permission notice (including the next
     12 paragraph) shall be included in all copies or substantial portions of the
     13 Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21 DEALINGS IN THE SOFTWARE.
     22 
     23 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
     24 
     25                         All Rights Reserved
     26 
     27 Permission to use, copy, modify, and distribute this software and its
     28 documentation for any purpose and without fee is hereby granted,
     29 provided that the above copyright notice appear in all copies and that
     30 both that copyright notice and this permission notice appear in
     31 supporting documentation, and that the name of Digital not be
     32 used in advertising or publicity pertaining to distribution of the
     33 software without specific, written prior permission.
     34 
     35 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     36 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     37 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     38 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     39 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     40 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     41 SOFTWARE.
     42 
     43 ******************************************************************/
     44 
     45 /*
     46 
     47 Copyright 1987, 1988, 1994, 1998  The Open Group
     48 
     49 Permission to use, copy, modify, distribute, and sell this software and its
     50 documentation for any purpose is hereby granted without fee, provided that
     51 the above copyright notice appear in all copies and that both that
     52 copyright notice and this permission notice appear in supporting
     53 documentation.
     54 
     55 The above copyright notice and this permission notice shall be included in
     56 all copies or substantial portions of the Software.
     57 
     58 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     59 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     60 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     61 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     62 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     63 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     64 
     65 Except as contained in this notice, the name of The Open Group shall not be
     66 used in advertising or otherwise to promote the sale, use or other dealings
     67 in this Software without prior written authorization from The Open Group.
     68 
     69 */
     70 
     71 /* TMstate.c -- maintains the state table of actions for the translation
     72  *              manager.
     73  */
     74 #ifdef HAVE_CONFIG_H
     75 #include <config.h>
     76 #endif
     77 #include "IntrinsicI.h"
     78 #ifndef TM_NO_MATCH
     79 #define TM_NO_MATCH (-2)
     80 #endif                          /* TM_NO_MATCH */
     81 /* forward definitions */
     82 static StatePtr NewState(TMParseStateTree, TMShortCard, TMShortCard);
     83 
     84 static String XtNtranslationError = "translationError";
     85 
     86 TMGlobalRec _XtGlobalTM;        /* initialized to zero K&R */
     87 
     88 #define MatchIncomingEvent(tmEvent, typeMatch, modMatch) \
     89   (typeMatch->eventType == tmEvent->event.eventType && \
     90    (typeMatch->matchEvent != NULL) && \
     91    (*typeMatch->matchEvent)(typeMatch, modMatch, tmEvent))
     92 
     93 #define NumStateTrees(xlations) \
     94   ((translateData->isSimple) ? 1 : (TMComplexXlations(xlations))->numTrees)
     95 
     96 static TMShortCard
     97 GetBranchHead(TMParseStateTree parseTree,
     98               TMShortCard typeIndex,
     99               TMShortCard modIndex,
    100               Boolean isDummy)
    101 {
    102 #define TM_BRANCH_HEAD_TBL_ALLOC        ((TMShortCard) 8)
    103 #define TM_BRANCH_HEAD_TBL_REALLOC      ((TMShortCard) 8)
    104 
    105     TMBranchHead branchHead = parseTree->branchHeadTbl;
    106 
    107     /*
    108      * dummy is used as a place holder for later matching in old-style
    109      * matching behavior. If there's already an entry we don't need
    110      * another dummy.
    111      */
    112     if (isDummy) {
    113         TMShortCard i;
    114 
    115         for (i = 0; i < parseTree->numBranchHeads; i++, branchHead++) {
    116             if ((branchHead->typeIndex == typeIndex) &&
    117                 (branchHead->modIndex == modIndex))
    118                 return i;
    119         }
    120     }
    121     if (parseTree->numBranchHeads == parseTree->branchHeadTblSize) {
    122 
    123         if (parseTree->branchHeadTblSize == 0)
    124             parseTree->branchHeadTblSize = TM_BRANCH_HEAD_TBL_ALLOC;
    125         else
    126             parseTree->branchHeadTblSize += TM_BRANCH_HEAD_TBL_REALLOC;
    127 
    128         if (parseTree->isStackBranchHeads) {
    129             TMBranchHead oldBranchHeadTbl = parseTree->branchHeadTbl;
    130 
    131             parseTree->branchHeadTbl =
    132                 XtMallocArray((Cardinal) parseTree->branchHeadTblSize,
    133                               (Cardinal) sizeof(TMBranchHeadRec));
    134             memcpy(parseTree->branchHeadTbl, oldBranchHeadTbl,
    135                    parseTree->branchHeadTblSize * sizeof(TMBranchHeadRec));
    136             parseTree->isStackBranchHeads = False;
    137         }
    138         else {
    139             parseTree->branchHeadTbl = (TMBranchHead)
    140                 XtReallocArray(parseTree->branchHeadTbl,
    141                                (Cardinal) parseTree->branchHeadTblSize,
    142                                (Cardinal) sizeof(TMBranchHeadRec));
    143         }
    144     }
    145 #ifdef TRACE_TM
    146     LOCK_PROCESS;
    147     _XtGlobalTM.numBranchHeads++;
    148     UNLOCK_PROCESS;
    149 #endif                          /* TRACE_TM */
    150     branchHead = &parseTree->branchHeadTbl[parseTree->numBranchHeads++];
    151     branchHead->typeIndex = typeIndex;
    152     branchHead->modIndex = modIndex;
    153     branchHead->more = 0;
    154     branchHead->isSimple = True;
    155     branchHead->hasActions = False;
    156     branchHead->hasCycles = False;
    157     return (TMShortCard) (parseTree->numBranchHeads - 1);
    158 }
    159 
    160 TMShortCard
    161 _XtGetQuarkIndex(TMParseStateTree parseTree, XrmQuark quark)
    162 {
    163 #define TM_QUARK_TBL_ALLOC      ((TMShortCard) 16)
    164 #define TM_QUARK_TBL_REALLOC    ((TMShortCard) 16)
    165     TMShortCard i;
    166 
    167     for (i = 0; i < parseTree->numQuarks; i++)
    168         if (parseTree->quarkTbl[i] == quark)
    169             break;
    170 
    171     if (i == parseTree->numQuarks) {
    172         if (parseTree->numQuarks == parseTree->quarkTblSize) {
    173 
    174             if (parseTree->quarkTblSize == 0)
    175                 parseTree->quarkTblSize = TM_QUARK_TBL_ALLOC;
    176             else
    177                 parseTree->quarkTblSize += TM_QUARK_TBL_REALLOC;
    178 
    179             if (parseTree->isStackQuarks) {
    180                 XrmQuark *oldquarkTbl = parseTree->quarkTbl;
    181 
    182                 parseTree->quarkTbl =
    183                     XtMallocArray((Cardinal) parseTree->quarkTblSize,
    184                                   (Cardinal) sizeof(XrmQuark));
    185                 memcpy(parseTree->quarkTbl, oldquarkTbl,
    186                        parseTree->quarkTblSize * sizeof(XrmQuark));
    187                 parseTree->isStackQuarks = False;
    188             }
    189             else {
    190                 parseTree->quarkTbl = (XrmQuark *)
    191                     XtReallocArray(parseTree->quarkTbl,
    192                                    (Cardinal) parseTree->quarkTblSize,
    193                                    (Cardinal) sizeof(XrmQuark));
    194             }
    195         }
    196         parseTree->quarkTbl[parseTree->numQuarks++] = quark;
    197     }
    198     return i;
    199 }
    200 
    201 /*
    202  * Get an entry from the parseTrees complex branchHead tbl. If there's none
    203  * there then allocate one
    204  */
    205 static TMShortCard
    206 GetComplexBranchIndex(TMParseStateTree parseTree,
    207                       TMShortCard typeIndex _X_UNUSED,
    208                       TMShortCard modIndex _X_UNUSED)
    209 {
    210 #define TM_COMPLEXBRANCH_HEAD_TBL_ALLOC 8
    211 #define TM_COMPLEXBRANCH_HEAD_TBL_REALLOC 4
    212 
    213     if (parseTree->numComplexBranchHeads == parseTree->complexBranchHeadTblSize) {
    214         if (parseTree->complexBranchHeadTblSize == 0)
    215             parseTree->complexBranchHeadTblSize =
    216                 (TMShortCard) (parseTree->complexBranchHeadTblSize +
    217                                TM_COMPLEXBRANCH_HEAD_TBL_ALLOC);
    218         else
    219             parseTree->complexBranchHeadTblSize =
    220                 (TMShortCard) (parseTree->complexBranchHeadTblSize +
    221                                TM_COMPLEXBRANCH_HEAD_TBL_REALLOC);
    222 
    223         if (parseTree->isStackComplexBranchHeads) {
    224             StatePtr *oldcomplexBranchHeadTbl = parseTree->complexBranchHeadTbl;
    225 
    226             parseTree->complexBranchHeadTbl =
    227                 XtMallocArray((Cardinal) parseTree->complexBranchHeadTblSize,
    228                               (Cardinal) sizeof(StatePtr));
    229             memcpy(parseTree->complexBranchHeadTbl, oldcomplexBranchHeadTbl,
    230                    parseTree->complexBranchHeadTblSize * sizeof(StatePtr));
    231             parseTree->isStackComplexBranchHeads = False;
    232         }
    233         else {
    234             parseTree->complexBranchHeadTbl = (StatePtr *)
    235                 XtReallocArray(parseTree->complexBranchHeadTbl,
    236                                (Cardinal) parseTree->complexBranchHeadTblSize,
    237                                (Cardinal) sizeof(StatePtr));
    238         }
    239     }
    240     parseTree->complexBranchHeadTbl[parseTree->numComplexBranchHeads++] = NULL;
    241     return (TMShortCard) (parseTree->numComplexBranchHeads - 1);
    242 }
    243 
    244 TMShortCard
    245 _XtGetTypeIndex(Event *event)
    246 {
    247     TMShortCard i, j = TM_TYPE_SEGMENT_SIZE;
    248     TMShortCard typeIndex = 0;
    249     TMTypeMatch typeMatch;
    250     TMTypeMatch segment = NULL;
    251 
    252     LOCK_PROCESS;
    253     for (i = 0; i < _XtGlobalTM.numTypeMatchSegments; i++) {
    254         segment = _XtGlobalTM.typeMatchSegmentTbl[i];
    255         for (j = 0;
    256              typeIndex < _XtGlobalTM.numTypeMatches && j < TM_TYPE_SEGMENT_SIZE;
    257              j++, typeIndex++) {
    258             typeMatch = &(segment[j]);
    259             if (event->eventType == typeMatch->eventType &&
    260                 event->eventCode == typeMatch->eventCode &&
    261                 event->eventCodeMask == typeMatch->eventCodeMask &&
    262                 event->matchEvent == typeMatch->matchEvent) {
    263                 UNLOCK_PROCESS;
    264                 return typeIndex;
    265             }
    266         }
    267     }
    268 
    269     if (j == TM_TYPE_SEGMENT_SIZE) {
    270         if (_XtGlobalTM.numTypeMatchSegments ==
    271             _XtGlobalTM.typeMatchSegmentTblSize) {
    272             _XtGlobalTM.typeMatchSegmentTblSize =
    273                 (TMShortCard) (_XtGlobalTM.typeMatchSegmentTblSize + 4);
    274             _XtGlobalTM.typeMatchSegmentTbl = (TMTypeMatch *)
    275                 XtReallocArray(_XtGlobalTM.typeMatchSegmentTbl,
    276                                (Cardinal) _XtGlobalTM.typeMatchSegmentTblSize,
    277                                (Cardinal) sizeof(TMTypeMatch));
    278         }
    279         _XtGlobalTM.typeMatchSegmentTbl[_XtGlobalTM.numTypeMatchSegments++] =
    280             segment = XtMallocArray(TM_TYPE_SEGMENT_SIZE,
    281                                     (Cardinal) sizeof(TMTypeMatchRec));
    282         j = 0;
    283     }
    284     typeMatch = &segment[j];
    285     typeMatch->eventType = event->eventType;
    286     typeMatch->eventCode = event->eventCode;
    287     typeMatch->eventCodeMask = event->eventCodeMask;
    288     typeMatch->matchEvent = event->matchEvent;
    289     _XtGlobalTM.numTypeMatches++;
    290     UNLOCK_PROCESS;
    291     return typeIndex;
    292 }
    293 
    294 static Boolean
    295 CompareLateModifiers(LateBindingsPtr lateBind1P, LateBindingsPtr lateBind2P)
    296 {
    297     LateBindingsPtr late1P = lateBind1P;
    298     LateBindingsPtr late2P = lateBind2P;
    299 
    300     if (late1P != NULL || late2P != NULL) {
    301         int i = 0;
    302         int j = 0;
    303 
    304         if (late1P != NULL)
    305             for (; late1P->keysym != NoSymbol; i++)
    306                 late1P++;
    307         if (late2P != NULL)
    308             for (; late2P->keysym != NoSymbol; j++)
    309                 late2P++;
    310         if (i != j)
    311             return FALSE;
    312         late1P--;
    313         while (late1P >= lateBind1P) {
    314             Boolean last = True;
    315 
    316             for (late2P = lateBind2P + i - 1; late2P >= lateBind2P; late2P--) {
    317                 if (late1P->keysym == late2P->keysym
    318                     && late1P->knot == late2P->knot) {
    319                     j--;
    320                     if (last)
    321                         i--;
    322                     break;
    323                 }
    324                 last = False;
    325             }
    326             late1P--;
    327         }
    328         if (j != 0)
    329             return FALSE;
    330     }
    331     return TRUE;
    332 }
    333 
    334 TMShortCard
    335 _XtGetModifierIndex(Event *event)
    336 {
    337     TMShortCard i, j = TM_MOD_SEGMENT_SIZE;
    338     TMShortCard modIndex = 0;
    339     TMModifierMatch modMatch;
    340     TMModifierMatch segment = NULL;
    341 
    342     LOCK_PROCESS;
    343     for (i = 0; i < _XtGlobalTM.numModMatchSegments; i++) {
    344         segment = _XtGlobalTM.modMatchSegmentTbl[i];
    345         for (j = 0;
    346              modIndex < _XtGlobalTM.numModMatches && j < TM_MOD_SEGMENT_SIZE;
    347              j++, modIndex++) {
    348             modMatch = &(segment[j]);
    349             if (event->modifiers == modMatch->modifiers &&
    350                 event->modifierMask == modMatch->modifierMask &&
    351                 event->standard == modMatch->standard &&
    352                 ((!event->lateModifiers && !modMatch->lateModifiers) ||
    353                  CompareLateModifiers(event->lateModifiers,
    354                                       modMatch->lateModifiers))) {
    355                 /*
    356                  * if we found a match then we can free the parser's
    357                  * late modifiers. If there isn't a match we use the
    358                  * parser's copy
    359                  */
    360                 if (event->lateModifiers &&
    361                     --event->lateModifiers->ref_count == 0) {
    362                     XtFree((char *) event->lateModifiers);
    363                     event->lateModifiers = NULL;
    364                 }
    365                 UNLOCK_PROCESS;
    366                 return modIndex;
    367             }
    368         }
    369     }
    370 
    371     if (j == TM_MOD_SEGMENT_SIZE) {
    372         if (_XtGlobalTM.numModMatchSegments ==
    373             _XtGlobalTM.modMatchSegmentTblSize) {
    374             _XtGlobalTM.modMatchSegmentTblSize =
    375                 (TMShortCard) (_XtGlobalTM.modMatchSegmentTblSize + 4);
    376             _XtGlobalTM.modMatchSegmentTbl = (TMModifierMatch *)
    377                 XtReallocArray(_XtGlobalTM.modMatchSegmentTbl,
    378                                (Cardinal) _XtGlobalTM.modMatchSegmentTblSize,
    379                                (Cardinal) sizeof(TMModifierMatch));
    380         }
    381         _XtGlobalTM.modMatchSegmentTbl[_XtGlobalTM.numModMatchSegments++] =
    382             segment = XtMallocArray(TM_MOD_SEGMENT_SIZE,
    383                                     (Cardinal) sizeof(TMModifierMatchRec));
    384         j = 0;
    385     }
    386     modMatch = &segment[j];
    387     modMatch->modifiers = event->modifiers;
    388     modMatch->modifierMask = event->modifierMask;
    389     modMatch->standard = event->standard;
    390     /*
    391      * We use the parser's copy of the late binding array
    392      */
    393 #ifdef TRACE_TM
    394     if (event->lateModifiers)
    395         _XtGlobalTM.numLateBindings++;
    396 #endif                          /* TRACE_TM */
    397     modMatch->lateModifiers = event->lateModifiers;
    398     _XtGlobalTM.numModMatches++;
    399     UNLOCK_PROCESS;
    400     return modIndex;
    401 }
    402 
    403 /*
    404  * This is called from the SimpleStateHandler to match a stateTree
    405  * entry to the event coming in
    406  */
    407 static int
    408 MatchBranchHead(TMSimpleStateTree stateTree, int startIndex, TMEventPtr event)
    409 {
    410     TMBranchHead branchHead = &stateTree->branchHeadTbl[startIndex];
    411     int i;
    412 
    413     LOCK_PROCESS;
    414     for (i = startIndex; i < (int) stateTree->numBranchHeads; i++, branchHead++) {
    415         TMTypeMatch typeMatch;
    416         TMModifierMatch modMatch;
    417 
    418         typeMatch = TMGetTypeMatch(branchHead->typeIndex);
    419         modMatch = TMGetModifierMatch(branchHead->modIndex);
    420 
    421         if (MatchIncomingEvent(event, typeMatch, modMatch)) {
    422             UNLOCK_PROCESS;
    423             return i;
    424         }
    425     }
    426     UNLOCK_PROCESS;
    427     return (TM_NO_MATCH);
    428 }
    429 
    430 Boolean
    431 _XtRegularMatch(TMTypeMatch typeMatch,
    432                 TMModifierMatch modMatch,
    433                 TMEventPtr eventSeq)
    434 {
    435     Modifiers computed = 0;
    436     Modifiers computedMask = 0;
    437     Boolean resolved = TRUE;
    438 
    439     if (typeMatch->eventCode != (eventSeq->event.eventCode &
    440                                  typeMatch->eventCodeMask))
    441         return FALSE;
    442     if (modMatch->lateModifiers != NULL)
    443         resolved = _XtComputeLateBindings(eventSeq->xev->xany.display,
    444                                           modMatch->lateModifiers,
    445                                           &computed, &computedMask);
    446     if (!resolved)
    447         return FALSE;
    448     computed = (Modifiers) (computed | modMatch->modifiers);
    449     computedMask = (Modifiers) (computedMask | modMatch->modifierMask);
    450 
    451     return ((computed & computedMask) ==
    452             (eventSeq->event.modifiers & computedMask));
    453 }
    454 
    455 Boolean
    456 _XtMatchAtom(TMTypeMatch typeMatch,
    457              TMModifierMatch modMatch _X_UNUSED,
    458              TMEventPtr eventSeq)
    459 {
    460     Atom atom;
    461 
    462     atom = XInternAtom(eventSeq->xev->xany.display,
    463                        XrmQuarkToString((XrmQuark) (typeMatch->eventCode)),
    464                        False);
    465     return (atom == eventSeq->event.eventCode);
    466 }
    467 
    468 #define IsOn(vec,idx) ((vec)[(idx)>>3] & (1 << ((idx) & 7)))
    469 
    470 /*
    471  * there are certain cases where you want to ignore the event and stay
    472  * in the same state.
    473  */
    474 static Boolean
    475 Ignore(TMEventPtr event)
    476 {
    477     Display *dpy;
    478     XtPerDisplay pd;
    479 
    480     if (event->event.eventType == MotionNotify)
    481         return TRUE;
    482     if (!(event->event.eventType == KeyPress ||
    483           event->event.eventType == KeyRelease))
    484         return FALSE;
    485     dpy = event->xev->xany.display;
    486 
    487     pd = _XtGetPerDisplay(dpy);
    488     _InitializeKeysymTables(dpy, pd);
    489     return IsOn(pd->isModifier, event->event.eventCode) ? TRUE : FALSE;
    490 }
    491 
    492 static void
    493 XEventToTMEvent(XEvent *event, TMEventPtr tmEvent)
    494 {
    495     tmEvent->xev = event;
    496     tmEvent->event.eventCodeMask = 0;
    497     tmEvent->event.modifierMask = 0;
    498     tmEvent->event.eventType = (TMLongCard) event->type;
    499     tmEvent->event.lateModifiers = NULL;
    500     tmEvent->event.matchEvent = NULL;
    501     tmEvent->event.standard = FALSE;
    502 
    503     switch (event->type) {
    504 
    505     case KeyPress:
    506     case KeyRelease:
    507         tmEvent->event.eventCode = event->xkey.keycode;
    508         tmEvent->event.modifiers = event->xkey.state;
    509         break;
    510 
    511     case ButtonPress:
    512     case ButtonRelease:
    513         tmEvent->event.eventCode = event->xbutton.button;
    514         tmEvent->event.modifiers = event->xbutton.state;
    515         break;
    516 
    517     case MotionNotify:
    518         tmEvent->event.eventCode = (TMLongCard) event->xmotion.is_hint;
    519         tmEvent->event.modifiers = event->xmotion.state;
    520         break;
    521 
    522     case EnterNotify:
    523     case LeaveNotify:
    524         tmEvent->event.eventCode = (TMLongCard) event->xcrossing.mode;
    525         tmEvent->event.modifiers = event->xcrossing.state;
    526         break;
    527 
    528     case PropertyNotify:
    529         tmEvent->event.eventCode = event->xproperty.atom;
    530         tmEvent->event.modifiers = 0;
    531         break;
    532 
    533     case SelectionClear:
    534         tmEvent->event.eventCode = event->xselectionclear.selection;
    535         tmEvent->event.modifiers = 0;
    536         break;
    537 
    538     case SelectionRequest:
    539         tmEvent->event.eventCode = event->xselectionrequest.selection;
    540         tmEvent->event.modifiers = 0;
    541         break;
    542 
    543     case SelectionNotify:
    544         tmEvent->event.eventCode = event->xselection.selection;
    545         tmEvent->event.modifiers = 0;
    546         break;
    547 
    548     case ClientMessage:
    549         tmEvent->event.eventCode = event->xclient.message_type;
    550         tmEvent->event.modifiers = 0;
    551         break;
    552 
    553     case MappingNotify:
    554         tmEvent->event.eventCode = (TMLongCard) event->xmapping.request;
    555         tmEvent->event.modifiers = 0;
    556         break;
    557 
    558     case FocusIn:
    559     case FocusOut:
    560         tmEvent->event.eventCode = (TMLongCard) event->xfocus.mode;
    561         tmEvent->event.modifiers = 0;
    562         break;
    563 
    564     default:
    565         tmEvent->event.eventCode = 0;
    566         tmEvent->event.modifiers = 0;
    567         break;
    568     }
    569 }
    570 
    571 static unsigned long
    572 GetTime(XtTM tm, XEvent *event)
    573 {
    574     switch (event->type) {
    575 
    576     case KeyPress:
    577     case KeyRelease:
    578         return event->xkey.time;
    579 
    580     case ButtonPress:
    581     case ButtonRelease:
    582         return event->xbutton.time;
    583 
    584     default:
    585         return tm->lastEventTime;
    586 
    587     }
    588 
    589 }
    590 
    591 static void
    592 HandleActions(Widget w,
    593               XEvent *event,
    594               TMSimpleStateTree stateTree,
    595               Widget accelWidget,
    596               XtActionProc *procs,
    597               ActionRec *actions)
    598 {
    599     ActionHook actionHookList;
    600     Widget bindWidget;
    601 
    602     bindWidget = accelWidget ? accelWidget : w;
    603     if (accelWidget && !XtIsSensitive(accelWidget) &&
    604         (event->type == KeyPress || event->type == KeyRelease ||
    605          event->type == ButtonPress || event->type == ButtonRelease ||
    606          event->type == MotionNotify || event->type == EnterNotify ||
    607          event->type == LeaveNotify || event->type == FocusIn ||
    608          event->type == FocusOut))
    609         return;
    610 
    611     actionHookList = XtWidgetToApplicationContext(w)->action_hook_list;
    612 
    613     while (actions != NULL) {
    614         /* perform any actions */
    615         if (procs[actions->idx] != NULL) {
    616             if (actionHookList) {
    617                 ActionHook hook;
    618                 ActionHook next_hook;
    619                 String procName =
    620                     XrmQuarkToString(stateTree->quarkTbl[actions->idx]);
    621 
    622                 for (hook = actionHookList; hook != NULL;) {
    623                     /*
    624                      * Need to cache hook->next because the following action
    625                      * proc may free hook via XtRemoveActionHook making
    626                      * hook->next invalid upon return from the action proc.
    627                      */
    628                     next_hook = hook->next;
    629                     (*hook->proc) (bindWidget,
    630                                    hook->closure,
    631                                    procName,
    632                                    event,
    633                                    actions->params, &actions->num_params);
    634                     hook = next_hook;
    635                 }
    636             }
    637             (*(procs[actions->idx]))
    638                 (bindWidget, event, actions->params, &actions->num_params);
    639         }
    640         actions = actions->next;
    641     }
    642 }
    643 
    644 typedef struct {
    645     unsigned int isCycleStart:1;
    646     unsigned int isCycleEnd:1;
    647     TMShortCard typeIndex;
    648     TMShortCard modIndex;
    649 } MatchPairRec, *MatchPair;
    650 
    651 typedef struct TMContextRec {
    652     TMShortCard numMatches;
    653     TMShortCard maxMatches;
    654     MatchPair matches;
    655 } TMContextRec, *TMContext;
    656 
    657 static TMContextRec contextCache[2];
    658 
    659 #define GetContextPtr(tm) ((TMContext *)&(tm->current_state))
    660 
    661 #define TM_CONTEXT_MATCHES_ALLOC 4
    662 #define TM_CONTEXT_MATCHES_REALLOC 2
    663 
    664 static void
    665 PushContext(TMContext *contextPtr, StatePtr newState)
    666 {
    667     TMContext context = *contextPtr;
    668 
    669     LOCK_PROCESS;
    670     if (context == NULL) {
    671         if (contextCache[0].numMatches == 0)
    672             context = &contextCache[0];
    673         else if (contextCache[1].numMatches == 0)
    674             context = &contextCache[1];
    675         if (!context) {
    676             context = XtNew(TMContextRec);
    677             context->matches = NULL;
    678             context->numMatches = context->maxMatches = 0;
    679         }
    680     }
    681     if (context->numMatches &&
    682         context->matches[context->numMatches - 1].isCycleEnd) {
    683         TMShortCard i;
    684 
    685         for (i = 0;
    686              i < context->numMatches &&
    687              !(context->matches[i].isCycleStart); i++) {
    688         };
    689         if (i < context->numMatches)
    690             context->numMatches = (TMShortCard) (i + 1);
    691 #ifdef DEBUG
    692         else
    693             XtWarning("pushing cycle end with no cycle start");
    694 #endif                          /* DEBUG */
    695     }
    696     else {
    697         if (context->numMatches == context->maxMatches) {
    698             if (context->maxMatches == 0)
    699                 context->maxMatches =
    700                     (TMShortCard) (context->maxMatches +
    701                                    TM_CONTEXT_MATCHES_ALLOC);
    702             else
    703                 context->maxMatches =
    704                     (TMShortCard) (context->maxMatches +
    705                                    TM_CONTEXT_MATCHES_REALLOC);
    706             context->matches = (MatchPairRec *)
    707                 XtReallocArray(context->matches,
    708                                (Cardinal) context->maxMatches,
    709                                sizeof(MatchPairRec));
    710         }
    711         context->matches[context->numMatches].isCycleStart =
    712             newState->isCycleStart;
    713         context->matches[context->numMatches].isCycleEnd = newState->isCycleEnd;
    714         context->matches[context->numMatches].typeIndex = newState->typeIndex;
    715         context->matches[context->numMatches++].modIndex = newState->modIndex;
    716         *contextPtr = context;
    717     }
    718     UNLOCK_PROCESS;
    719 }
    720 
    721 static void
    722 FreeContext(TMContext *contextPtr)
    723 {
    724     TMContext context = NULL;
    725 
    726     LOCK_PROCESS;
    727 
    728     if (&contextCache[0] == *contextPtr)
    729         context = &contextCache[0];
    730     else if (&contextCache[1] == *contextPtr)
    731         context = &contextCache[1];
    732 
    733     if (context)
    734         context->numMatches = 0;
    735     else if (*contextPtr) {
    736         XtFree((char *) ((*contextPtr)->matches));
    737         XtFree((char *) *contextPtr);
    738     }
    739 
    740     *contextPtr = NULL;
    741     UNLOCK_PROCESS;
    742 }
    743 
    744 static int
    745 MatchExact(TMSimpleStateTree stateTree,
    746            int startIndex,
    747            TMShortCard typeIndex,
    748            TMShortCard modIndex)
    749 {
    750     TMBranchHead branchHead = &(stateTree->branchHeadTbl[startIndex]);
    751     int i;
    752 
    753     for (i = startIndex; i < (int) stateTree->numBranchHeads; i++, branchHead++) {
    754         if ((branchHead->typeIndex == typeIndex) &&
    755             (branchHead->modIndex == modIndex))
    756             return i;
    757     }
    758     return (TM_NO_MATCH);
    759 }
    760 
    761 static void
    762 HandleSimpleState(Widget w, XtTM tmRecPtr, TMEventRec *curEventPtr)
    763 {
    764     XtTranslations xlations = tmRecPtr->translations;
    765     TMContext *contextPtr = GetContextPtr(tmRecPtr);
    766     TMShortCard i;
    767     ActionRec *actions = NULL;
    768     Boolean matchExact = False;
    769     Boolean match = False;
    770     StatePtr complexMatchState = NULL;
    771     TMShortCard typeIndex = 0, modIndex = 0;
    772     int matchTreeIndex = TM_NO_MATCH;
    773 
    774     LOCK_PROCESS;
    775     for (i = 0;
    776          ((!match || !complexMatchState) && (i < xlations->numStateTrees));
    777          i++) {
    778         int currIndex = -1;
    779         TMSimpleStateTree stateTree =
    780             (TMSimpleStateTree) xlations->stateTreeTbl[i];
    781 
    782         /*
    783          * don't process this tree if we're only looking for a
    784          * complexMatchState and there are no complex states
    785          */
    786         while (!(match && stateTree->isSimple) &&
    787                ((!match || !complexMatchState) && (currIndex != TM_NO_MATCH))) {
    788             currIndex++;
    789             if (matchExact)
    790                 currIndex =
    791                     MatchExact(stateTree, currIndex, typeIndex, modIndex);
    792             else
    793                 currIndex = MatchBranchHead(stateTree, currIndex, curEventPtr);
    794             if (currIndex != TM_NO_MATCH) {
    795                 TMBranchHead branchHead;
    796                 StatePtr currState;
    797 
    798                 branchHead = &stateTree->branchHeadTbl[currIndex];
    799                 if (branchHead->isSimple)
    800                     currState = NULL;
    801                 else
    802                     currState = ((TMComplexStateTree) stateTree)
    803                         ->complexBranchHeadTbl[TMBranchMore(branchHead)];
    804 
    805                 /*
    806                  * first check for a complete match
    807                  */
    808                 if (!match) {
    809                     if (branchHead->hasActions) {
    810                         if (branchHead->isSimple) {
    811                             static ActionRec dummyAction;
    812 
    813                             dummyAction.idx = TMBranchMore(branchHead);
    814                             actions = &dummyAction;
    815                         }
    816                         else
    817                             actions = currState->actions;
    818                         tmRecPtr->lastEventTime =
    819                             GetTime(tmRecPtr, curEventPtr->xev);
    820                         FreeContext((TMContext *) &tmRecPtr->current_state);
    821                         match = True;
    822                         matchTreeIndex = i;
    823                     }
    824                     /*
    825                      * if it doesn't have actions and
    826                      * it's bc mode then it's a potential match node that is
    827                      * used to match later sequences.
    828                      */
    829                     if (!TMNewMatchSemantics() && !matchExact) {
    830                         matchExact = True;
    831                         typeIndex = branchHead->typeIndex;
    832                         modIndex = branchHead->modIndex;
    833                     }
    834                 }
    835                 /*
    836                  * check for it being an event sequence which can be
    837                  * a future match
    838                  */
    839                 if (!branchHead->isSimple &&
    840                     !branchHead->hasActions && !complexMatchState)
    841                     complexMatchState = currState;
    842             }
    843         }
    844     }
    845     if (match) {
    846         TMBindData bindData = (TMBindData) tmRecPtr->proc_table;
    847         XtActionProc *procs;
    848         Widget accelWidget;
    849 
    850         if (bindData->simple.isComplex) {
    851             TMComplexBindProcs bindProcs =
    852                 TMGetComplexBindEntry(bindData, matchTreeIndex);
    853             procs = bindProcs->procs;
    854             accelWidget = bindProcs->widget;
    855         }
    856         else {
    857             TMSimpleBindProcs bindProcs =
    858                 TMGetSimpleBindEntry(bindData, matchTreeIndex);
    859             procs = bindProcs->procs;
    860             accelWidget = NULL;
    861         }
    862         HandleActions
    863             (w,
    864              curEventPtr->xev,
    865              (TMSimpleStateTree) xlations->stateTreeTbl[matchTreeIndex],
    866              accelWidget, procs, actions);
    867     }
    868     if (complexMatchState)
    869         PushContext(contextPtr, complexMatchState);
    870     UNLOCK_PROCESS;
    871 }
    872 
    873 static int
    874 MatchComplexBranch(TMComplexStateTree stateTree,
    875                    int startIndex,
    876                    TMContext context,
    877                    StatePtr *leafStateRtn)
    878 {
    879     TMShortCard i;
    880 
    881     LOCK_PROCESS;
    882     for (i = (TMShortCard) startIndex; i < stateTree->numComplexBranchHeads;
    883          i++) {
    884         StatePtr candState;
    885         TMShortCard numMatches = context->numMatches;
    886         MatchPair statMatch = context->matches;
    887 
    888         for (candState = stateTree->complexBranchHeadTbl[i];
    889              numMatches && candState;
    890              numMatches--, statMatch++, candState = candState->nextLevel) {
    891             if ((statMatch->typeIndex != candState->typeIndex) ||
    892                 (statMatch->modIndex != candState->modIndex))
    893                 break;
    894         }
    895         if (numMatches == 0) {
    896             *leafStateRtn = candState;
    897             UNLOCK_PROCESS;
    898             return i;
    899         }
    900     }
    901     *leafStateRtn = NULL;
    902     UNLOCK_PROCESS;
    903     return (TM_NO_MATCH);
    904 }
    905 
    906 static StatePtr
    907 TryCurrentTree(TMComplexStateTree *stateTreePtr,
    908                XtTM tmRecPtr,
    909                TMEventRec *curEventPtr)
    910 {
    911     StatePtr candState = NULL, matchState = NULL;
    912     TMContext *contextPtr = GetContextPtr(tmRecPtr);
    913     TMTypeMatch typeMatch;
    914     TMModifierMatch modMatch;
    915     int currIndex = -1;
    916 
    917     /*
    918      * we want the first sequence that both matches and has actions.
    919      * we keep on looking till we find both
    920      */
    921     LOCK_PROCESS;
    922     while ((currIndex =
    923             MatchComplexBranch(*stateTreePtr,
    924                                ++currIndex, (*contextPtr), &candState))
    925            != TM_NO_MATCH) {
    926         if (candState != NULL) {
    927             typeMatch = TMGetTypeMatch(candState->typeIndex);
    928             modMatch = TMGetModifierMatch(candState->modIndex);
    929 
    930             /* does this state's index match? --> done */
    931             if (MatchIncomingEvent(curEventPtr, typeMatch, modMatch)) {
    932                 if (candState->actions) {
    933                     UNLOCK_PROCESS;
    934                     return candState;
    935                 }
    936                 else
    937                     matchState = candState;
    938             }
    939             /* is this an event timer? */
    940             if (typeMatch->eventType == _XtEventTimerEventType) {
    941                 StatePtr nextState = candState->nextLevel;
    942 
    943                 /* does the succeeding state match? */
    944                 if (nextState != NULL) {
    945                     TMTypeMatch nextTypeMatch;
    946                     TMModifierMatch nextModMatch;
    947 
    948                     nextTypeMatch = TMGetTypeMatch(nextState->typeIndex);
    949                     nextModMatch = TMGetModifierMatch(nextState->modIndex);
    950 
    951                     /* is it within the timeout? */
    952                     if (MatchIncomingEvent(curEventPtr,
    953                                            nextTypeMatch, nextModMatch)) {
    954                         XEvent *xev = curEventPtr->xev;
    955                         unsigned long time = GetTime(tmRecPtr, xev);
    956                         XtPerDisplay pd = _XtGetPerDisplay(xev->xany.display);
    957                         unsigned long delta =
    958                             (unsigned long) pd->multi_click_time;
    959 
    960                         if ((tmRecPtr->lastEventTime + delta) >= time) {
    961                             if (nextState->actions) {
    962                                 UNLOCK_PROCESS;
    963                                 return candState;
    964                             }
    965                             else
    966                                 matchState = candState;
    967                         }
    968                     }
    969                 }
    970             }
    971         }
    972     }
    973     UNLOCK_PROCESS;
    974     return matchState;
    975 }
    976 
    977 static void
    978 HandleComplexState(Widget w, XtTM tmRecPtr, TMEventRec *curEventPtr)
    979 {
    980     XtTranslations xlations = tmRecPtr->translations;
    981     TMContext *contextPtr = GetContextPtr(tmRecPtr);
    982     TMShortCard i, matchTreeIndex = 0;
    983     StatePtr matchState = NULL, candState;
    984     TMComplexStateTree *stateTreePtr =
    985         (TMComplexStateTree *) &xlations->stateTreeTbl[0];
    986 
    987     LOCK_PROCESS;
    988     for (i = 0; i < xlations->numStateTrees; i++, stateTreePtr++) {
    989         /*
    990          * some compilers sign extend Boolean bit fields so test for
    991          * false |||
    992          */
    993         if (((*stateTreePtr)->isSimple == False) &&
    994             (candState = TryCurrentTree(stateTreePtr, tmRecPtr, curEventPtr))) {
    995             if (!matchState || candState->actions) {
    996                 matchTreeIndex = i;
    997                 matchState = candState;
    998                 if (candState->actions)
    999                     break;
   1000             }
   1001         }
   1002     }
   1003     if (matchState == NULL) {
   1004         /* couldn't find it... */
   1005         if (!Ignore(curEventPtr)) {
   1006             FreeContext(contextPtr);
   1007             HandleSimpleState(w, tmRecPtr, curEventPtr);
   1008         }
   1009     }
   1010     else {
   1011         TMBindData bindData = (TMBindData) tmRecPtr->proc_table;
   1012         XtActionProc *procs;
   1013         Widget accelWidget;
   1014         TMTypeMatch typeMatch;
   1015 
   1016         typeMatch = TMGetTypeMatch(matchState->typeIndex);
   1017 
   1018         PushContext(contextPtr, matchState);
   1019         if (typeMatch->eventType == _XtEventTimerEventType) {
   1020             matchState = matchState->nextLevel;
   1021             PushContext(contextPtr, matchState);
   1022         }
   1023         tmRecPtr->lastEventTime = GetTime(tmRecPtr, curEventPtr->xev);
   1024 
   1025         if (bindData->simple.isComplex) {
   1026             TMComplexBindProcs bindProcs =
   1027                 TMGetComplexBindEntry(bindData, matchTreeIndex);
   1028             procs = bindProcs->procs;
   1029             accelWidget = bindProcs->widget;
   1030         }
   1031         else {
   1032             TMSimpleBindProcs bindProcs =
   1033                 TMGetSimpleBindEntry(bindData, matchTreeIndex);
   1034             procs = bindProcs->procs;
   1035             accelWidget = NULL;
   1036         }
   1037         HandleActions(w, curEventPtr->xev, (TMSimpleStateTree)
   1038                       xlations->stateTreeTbl[matchTreeIndex],
   1039                       accelWidget, procs, matchState->actions);
   1040     }
   1041     UNLOCK_PROCESS;
   1042 }
   1043 
   1044 void
   1045 _XtTranslateEvent(Widget w, XEvent *event)
   1046 {
   1047     XtTM tmRecPtr = &w->core.tm;
   1048     TMEventRec curEvent;
   1049     StatePtr current_state = tmRecPtr->current_state;
   1050 
   1051     XEventToTMEvent(event, &curEvent);
   1052 
   1053     if (!tmRecPtr->translations) {
   1054         XtAppWarningMsg(XtWidgetToApplicationContext(w),
   1055                         XtNtranslationError, "nullTable", XtCXtToolkitError,
   1056                         "Can't translate event through NULL table", NULL, NULL);
   1057         return;
   1058     }
   1059     if (current_state == NULL)
   1060         HandleSimpleState(w, tmRecPtr, &curEvent);
   1061     else
   1062         HandleComplexState(w, tmRecPtr, &curEvent);
   1063 }
   1064 
   1065 static StatePtr
   1066 NewState(TMParseStateTree stateTree _X_UNUSED,
   1067          TMShortCard typeIndex,
   1068          TMShortCard modIndex)
   1069 {
   1070     StatePtr state = XtNew(StateRec);
   1071 
   1072 #ifdef TRACE_TM
   1073     LOCK_PROCESS;
   1074     _XtGlobalTM.numComplexStates++;
   1075     UNLOCK_PROCESS;
   1076 #endif                          /* TRACE_TM */
   1077     state->typeIndex = typeIndex;
   1078     state->modIndex = modIndex;
   1079     state->nextLevel = NULL;
   1080     state->actions = NULL;
   1081     state->isCycleStart = state->isCycleEnd = False;
   1082     return state;
   1083 }
   1084 
   1085 /*
   1086  * This routine is an iterator for state trees. If the func returns
   1087  * true then iteration is over.
   1088  */
   1089 void
   1090 _XtTraverseStateTree(TMStateTree tree, _XtTraversalProc func, XtPointer data)
   1091 {
   1092     TMComplexStateTree stateTree = (TMComplexStateTree) tree;
   1093     TMBranchHead currBH;
   1094     TMShortCard i;
   1095     StateRec dummyStateRec, *dummyState = &dummyStateRec;
   1096     ActionRec dummyActionRec, *dummyAction = &dummyActionRec;
   1097     Boolean firstSimple = True;
   1098     StatePtr currState;
   1099 
   1100     /* first traverse the complex states */
   1101     if (stateTree->isSimple == False)
   1102         for (i = 0; i < stateTree->numComplexBranchHeads; i++) {
   1103             currState = stateTree->complexBranchHeadTbl[i];
   1104             for (; currState; currState = currState->nextLevel) {
   1105                 if (func(currState, data))
   1106                     return;
   1107                 if (currState->isCycleEnd)
   1108                     break;
   1109             }
   1110         }
   1111 
   1112     /* now traverse the simple ones */
   1113     for (i = 0, currBH = stateTree->branchHeadTbl;
   1114          i < stateTree->numBranchHeads; i++, currBH++) {
   1115         if (currBH->isSimple && currBH->hasActions) {
   1116             if (firstSimple) {
   1117                 XtBZero((char *) dummyState, sizeof(StateRec));
   1118                 XtBZero((char *) dummyAction, sizeof(ActionRec));
   1119                 dummyState->actions = dummyAction;
   1120                 firstSimple = False;
   1121             }
   1122             dummyState->typeIndex = currBH->typeIndex;
   1123             dummyState->modIndex = currBH->modIndex;
   1124             dummyAction->idx = currBH->more;
   1125             if (func(dummyState, data))
   1126                 return;
   1127         }
   1128     }
   1129 }
   1130 
   1131 static EventMask
   1132 EventToMask(TMTypeMatch typeMatch, TMModifierMatch modMatch)
   1133 {
   1134     EventMask returnMask;
   1135     unsigned long eventType = typeMatch->eventType;
   1136 
   1137     if (eventType == MotionNotify) {
   1138         Modifiers modifierMask = (Modifiers) modMatch->modifierMask;
   1139         Modifiers tempMask;
   1140 
   1141         returnMask = 0;
   1142         if (modifierMask == 0) {
   1143             if (modMatch->modifiers == AnyButtonMask)
   1144                 return ButtonMotionMask;
   1145             else
   1146                 return PointerMotionMask;
   1147         }
   1148         tempMask = modifierMask &
   1149             (Button1Mask | Button2Mask | Button3Mask
   1150              | Button4Mask | Button5Mask);
   1151         if (tempMask == 0)
   1152             return PointerMotionMask;
   1153         if (tempMask & Button1Mask)
   1154             returnMask |= Button1MotionMask;
   1155         if (tempMask & Button2Mask)
   1156             returnMask |= Button2MotionMask;
   1157         if (tempMask & Button3Mask)
   1158             returnMask |= Button3MotionMask;
   1159         if (tempMask & Button4Mask)
   1160             returnMask |= Button4MotionMask;
   1161         if (tempMask & Button5Mask)
   1162             returnMask |= Button5MotionMask;
   1163         return returnMask;
   1164     }
   1165     returnMask = _XtConvertTypeToMask((int) eventType);
   1166     if (returnMask == (StructureNotifyMask | SubstructureNotifyMask))
   1167         returnMask = StructureNotifyMask;
   1168     return returnMask;
   1169 }
   1170 
   1171 static void
   1172 DispatchMappingNotify(Widget widget _X_UNUSED,  /* will be NULL from _RefreshMapping */
   1173                       XtPointer closure,        /* real Widget */
   1174                       XtPointer call_data)      /* XEvent* */
   1175 {
   1176     _XtTranslateEvent((Widget) closure, (XEvent *) call_data);
   1177 }
   1178 
   1179 static void
   1180 RemoveFromMappingCallbacks(Widget widget,
   1181                            XtPointer closure,    /* target widget */
   1182                            XtPointer call_data _X_UNUSED)
   1183 {
   1184     _XtRemoveCallback(&_XtGetPerDisplay(XtDisplay(widget))->mapping_callbacks,
   1185                       DispatchMappingNotify, closure);
   1186 }
   1187 
   1188 static Boolean
   1189 AggregateEventMask(StatePtr state, XtPointer data)
   1190 {
   1191     LOCK_PROCESS;
   1192     *((EventMask *) data) |= EventToMask(TMGetTypeMatch(state->typeIndex),
   1193                                          TMGetModifierMatch(state->modIndex));
   1194     UNLOCK_PROCESS;
   1195     return False;
   1196 }
   1197 
   1198 void
   1199 _XtInstallTranslations(Widget widget)
   1200 {
   1201     XtTranslations xlations;
   1202     Cardinal i;
   1203     Boolean mappingNotifyInterest = False;
   1204 
   1205     xlations = widget->core.tm.translations;
   1206     if (xlations == NULL)
   1207         return;
   1208 
   1209     /*
   1210      * check for somebody stuffing the translations directly into the
   1211      * instance structure. We will end up being called again out of
   1212      * ComposeTranslations but we *should* have bindings by then
   1213      */
   1214     if (widget->core.tm.proc_table == NULL) {
   1215         _XtMergeTranslations(widget, NULL, XtTableReplace);
   1216         /*
   1217          * if we're realized then we'll be called out of
   1218          * ComposeTranslations
   1219          */
   1220         if (XtIsRealized(widget))
   1221             return;
   1222     }
   1223 
   1224     xlations->eventMask = 0;
   1225     for (i = 0; i < xlations->numStateTrees; i++) {
   1226         TMStateTree stateTree = xlations->stateTreeTbl[i];
   1227 
   1228         _XtTraverseStateTree(stateTree,
   1229                              AggregateEventMask,
   1230                              (XtPointer) &xlations->eventMask);
   1231         mappingNotifyInterest =
   1232             (Boolean) (mappingNotifyInterest |
   1233                        stateTree->simple.mappingNotifyInterest);
   1234     }
   1235     /* double click needs to make sure that you have selected on both
   1236        button down and up. */
   1237 
   1238     if (xlations->eventMask & ButtonPressMask)
   1239         xlations->eventMask |= ButtonReleaseMask;
   1240     if (xlations->eventMask & ButtonReleaseMask)
   1241         xlations->eventMask |= ButtonPressMask;
   1242 
   1243     if (mappingNotifyInterest) {
   1244         XtPerDisplay pd = _XtGetPerDisplay(XtDisplay(widget));
   1245 
   1246         if (pd->mapping_callbacks)
   1247             _XtAddCallbackOnce(&(pd->mapping_callbacks),
   1248                                DispatchMappingNotify, (XtPointer) widget);
   1249         else
   1250             _XtAddCallback(&(pd->mapping_callbacks),
   1251                            DispatchMappingNotify, (XtPointer) widget);
   1252 
   1253         if (widget->core.destroy_callbacks != NULL)
   1254             _XtAddCallbackOnce((InternalCallbackList *)
   1255                                &widget->core.destroy_callbacks,
   1256                                RemoveFromMappingCallbacks, (XtPointer) widget);
   1257         else
   1258             _XtAddCallback((InternalCallbackList *)
   1259                            &widget->core.destroy_callbacks,
   1260                            RemoveFromMappingCallbacks, (XtPointer) widget);
   1261     }
   1262     _XtBindActions(widget, (XtTM) &widget->core.tm);
   1263     _XtRegisterGrabs(widget);
   1264 }
   1265 
   1266 void
   1267 _XtRemoveTranslations(Widget widget)
   1268 {
   1269     Cardinal i;
   1270     Boolean mappingNotifyInterest = False;
   1271     XtTranslations xlations = widget->core.tm.translations;
   1272 
   1273     if (xlations == NULL)
   1274         return;
   1275 
   1276     for (i = 0; i < xlations->numStateTrees; i++) {
   1277         TMSimpleStateTree stateTree =
   1278             (TMSimpleStateTree) xlations->stateTreeTbl[i];
   1279         mappingNotifyInterest =
   1280             (Boolean) (mappingNotifyInterest |
   1281                        stateTree->mappingNotifyInterest);
   1282     }
   1283     if (mappingNotifyInterest)
   1284         RemoveFromMappingCallbacks(widget, (XtPointer) widget, NULL);
   1285 }
   1286 
   1287 static void
   1288 _XtUninstallTranslations(Widget widget)
   1289 {
   1290     XtTranslations xlations = widget->core.tm.translations;
   1291 
   1292     _XtUnbindActions(widget, xlations, (TMBindData) widget->core.tm.proc_table);
   1293     _XtRemoveTranslations(widget);
   1294     widget->core.tm.translations = NULL;
   1295     FreeContext((TMContext *) &widget->core.tm.current_state);
   1296 }
   1297 
   1298 void
   1299 _XtDestroyTMData(Widget widget)
   1300 {
   1301     TMComplexBindData cBindData;
   1302 
   1303     _XtUninstallTranslations(widget);
   1304 
   1305     if ((cBindData = (TMComplexBindData) widget->core.tm.proc_table)) {
   1306         if (cBindData->isComplex) {
   1307             ATranslations nXlations = (ATranslations) cBindData->accel_context;
   1308 
   1309             while (nXlations) {
   1310                 ATranslations aXlations = nXlations;
   1311 
   1312                 nXlations = nXlations->next;
   1313                 XtFree((char *) aXlations);
   1314             }
   1315         }
   1316         XtFree((char *) cBindData);
   1317     }
   1318 }
   1319 
   1320 /*** Public procedures ***/
   1321 
   1322 void
   1323 XtUninstallTranslations(Widget widget)
   1324 {
   1325     EventMask oldMask;
   1326     Widget hookobj;
   1327 
   1328     WIDGET_TO_APPCON(widget);
   1329 
   1330     LOCK_APP(app);
   1331     if (!widget->core.tm.translations) {
   1332         UNLOCK_APP(app);
   1333         return;
   1334     }
   1335     oldMask = widget->core.tm.translations->eventMask;
   1336     _XtUninstallTranslations(widget);
   1337     if (XtIsRealized(widget) && oldMask)
   1338         XSelectInput(XtDisplay(widget), XtWindow(widget),
   1339                      (long) XtBuildEventMask(widget));
   1340     hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
   1341     if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
   1342         XtChangeHookDataRec call_data;
   1343 
   1344         call_data.type = XtHuninstallTranslations;
   1345         call_data.widget = widget;
   1346         XtCallCallbackList(hookobj,
   1347                            ((HookObject) hookobj)->hooks.changehook_callbacks,
   1348                            (XtPointer) &call_data);
   1349     }
   1350     UNLOCK_APP(app);
   1351 }
   1352 
   1353 XtTranslations
   1354 _XtCreateXlations(TMStateTree *stateTrees,
   1355                   TMShortCard numStateTrees,
   1356                   XtTranslations first,
   1357                   XtTranslations second)
   1358 {
   1359     XtTranslations xlations;
   1360     TMShortCard i;
   1361 
   1362     xlations = (XtTranslations)
   1363         __XtMalloc((Cardinal) (sizeof(TranslationData) +
   1364                                (size_t) (numStateTrees -
   1365                                          1) * sizeof(TMStateTree)));
   1366 #ifdef TRACE_TM
   1367     LOCK_PROCESS;
   1368     if (_XtGlobalTM.numTms == _XtGlobalTM.tmTblSize) {
   1369         _XtGlobalTM.tmTblSize = (TMShortCard) (_XtGlobalTM.tmTblSize + 16);
   1370         _XtGlobalTM.tmTbl = (XtTranslations *)
   1371             XtReallocArray(_XtGlobalTM.tmTbl,
   1372                            (Cardinal) _XtGlobalTM.tmTblSize,
   1373                            (Cardinal) sizeof(XtTranslations));
   1374     }
   1375     _XtGlobalTM.tmTbl[_XtGlobalTM.numTms++] = xlations;
   1376     UNLOCK_PROCESS;
   1377 #endif                          /* TRACE_TM */
   1378 
   1379     xlations->composers[0] = first;
   1380     xlations->composers[1] = second;
   1381     xlations->hasBindings = False;
   1382     xlations->operation = XtTableReplace;
   1383 
   1384     for (i = 0; i < numStateTrees; i++) {
   1385         xlations->stateTreeTbl[i] = (TMStateTree) stateTrees[i];
   1386         stateTrees[i]->simple.refCount++;
   1387     }
   1388     xlations->numStateTrees = numStateTrees;
   1389     xlations->eventMask = 0;
   1390     return xlations;
   1391 }
   1392 
   1393 TMStateTree
   1394 _XtParseTreeToStateTree(TMParseStateTree parseTree)
   1395 {
   1396     TMSimpleStateTree simpleTree;
   1397 
   1398     if (parseTree->numComplexBranchHeads) {
   1399         TMComplexStateTree complexTree;
   1400 
   1401         complexTree = XtNew(TMComplexStateTreeRec);
   1402         complexTree->isSimple = False;
   1403         complexTree->complexBranchHeadTbl =
   1404             XtMallocArray((Cardinal) parseTree->numComplexBranchHeads,
   1405                           (Cardinal) sizeof(StatePtr));
   1406         memcpy(complexTree->complexBranchHeadTbl,
   1407                parseTree->complexBranchHeadTbl,
   1408                parseTree->numComplexBranchHeads * sizeof(StatePtr));
   1409         complexTree->numComplexBranchHeads = parseTree->numComplexBranchHeads;
   1410         simpleTree = (TMSimpleStateTree) complexTree;
   1411     }
   1412     else {
   1413         simpleTree = XtNew(TMSimpleStateTreeRec);
   1414         simpleTree->isSimple = True;
   1415     }
   1416     simpleTree->isAccelerator = parseTree->isAccelerator;
   1417     simpleTree->refCount = 0;
   1418     simpleTree->mappingNotifyInterest = parseTree->mappingNotifyInterest;
   1419 
   1420     simpleTree->branchHeadTbl =
   1421         XtMallocArray((Cardinal) parseTree->numBranchHeads,
   1422                       (Cardinal) sizeof(TMBranchHeadRec));
   1423     memcpy(simpleTree->branchHeadTbl, parseTree->branchHeadTbl,
   1424            parseTree->numBranchHeads * sizeof(TMBranchHeadRec));
   1425     simpleTree->numBranchHeads = parseTree->numBranchHeads;
   1426 
   1427     simpleTree->quarkTbl = XtMallocArray((Cardinal) parseTree->numQuarks,
   1428                                          (Cardinal) sizeof(XrmQuark));
   1429     memcpy(simpleTree->quarkTbl, parseTree->quarkTbl,
   1430            parseTree->numQuarks * sizeof(XrmQuark));
   1431     simpleTree->numQuarks = parseTree->numQuarks;
   1432 
   1433     return (TMStateTree) simpleTree;
   1434 }
   1435 
   1436 static void
   1437 FreeActions(ActionPtr actions)
   1438 {
   1439     ActionPtr action;
   1440     TMShortCard i;
   1441 
   1442     for (action = actions; action;) {
   1443         ActionPtr nextAction = action->next;
   1444 
   1445         for (i = (TMShortCard) action->num_params; i;) {
   1446             XtFree((_XtString) action->params[--i]);
   1447         }
   1448         XtFree((char *) action->params);
   1449         XtFree((char *) action);
   1450         action = nextAction;
   1451     }
   1452 }
   1453 
   1454 static void
   1455 AmbigActions(EventSeqPtr initialEvent,
   1456              StatePtr *state,
   1457              TMParseStateTree stateTree)
   1458 {
   1459     String params[3];
   1460     Cardinal numParams = 0;
   1461 
   1462     params[numParams++] = _XtPrintEventSeq(initialEvent, NULL);
   1463     params[numParams++] = _XtPrintActions((*state)->actions,
   1464                                           stateTree->quarkTbl);
   1465     XtWarningMsg(XtNtranslationError, "oldActions", XtCXtToolkitError,
   1466                  "Previous entry was: %s %s", params, &numParams);
   1467     XtFree((char *) params[0]);
   1468     XtFree((char *) params[1]);
   1469     numParams = 0;
   1470     params[numParams++] = _XtPrintActions(initialEvent->actions,
   1471                                           stateTree->quarkTbl);
   1472     XtWarningMsg(XtNtranslationError, "newActions", XtCXtToolkitError,
   1473                  "New actions are:%s", params, &numParams);
   1474     XtFree((char *) params[0]);
   1475     XtWarningMsg(XtNtranslationError, "ambiguousActions",
   1476                  XtCXtToolkitError,
   1477                  "Overriding earlier translation manager actions.", NULL, NULL);
   1478 
   1479     FreeActions((*state)->actions);
   1480     (*state)->actions = NULL;
   1481 }
   1482 
   1483 void
   1484 _XtAddEventSeqToStateTree(EventSeqPtr eventSeq, TMParseStateTree stateTree)
   1485 {
   1486     StatePtr *state;
   1487     EventSeqPtr initialEvent = eventSeq;
   1488     TMBranchHead branchHead;
   1489     TMShortCard idx, modIndex, typeIndex;
   1490 
   1491     if (eventSeq == NULL)
   1492         return;
   1493 
   1494     /* note that all states in the event seq passed in start out null */
   1495     /* we fill them in with the matching state as we traverse the list */
   1496 
   1497     /*
   1498      * We need to free the parser data structures !!!
   1499      */
   1500 
   1501     typeIndex = _XtGetTypeIndex(&eventSeq->event);
   1502     modIndex = _XtGetModifierIndex(&eventSeq->event);
   1503     idx = GetBranchHead(stateTree, typeIndex, modIndex, False);
   1504     branchHead = &stateTree->branchHeadTbl[idx];
   1505 
   1506     /*
   1507      * Need to check for pre-existing actions with same lhs |||
   1508      */
   1509 
   1510     /*
   1511      * Check for optimized case. Don't assume that the eventSeq has actions.
   1512      */
   1513     if (!eventSeq->next &&
   1514         eventSeq->actions &&
   1515         !eventSeq->actions->next && !eventSeq->actions->num_params) {
   1516         if (eventSeq->event.eventType == MappingNotify)
   1517             stateTree->mappingNotifyInterest = True;
   1518         branchHead->hasActions = True;
   1519         XtSetBits(branchHead->more, eventSeq->actions->idx, 13);
   1520         FreeActions(eventSeq->actions);
   1521         eventSeq->actions = NULL;
   1522         return;
   1523     }
   1524 
   1525     branchHead->isSimple = False;
   1526     if (!eventSeq->next)
   1527         branchHead->hasActions = True;
   1528     XtSetBits(branchHead->more,
   1529               GetComplexBranchIndex(stateTree, typeIndex, modIndex), 13);
   1530     state = &stateTree->complexBranchHeadTbl[TMBranchMore(branchHead)];
   1531 
   1532     for (;;) {
   1533         *state = NewState(stateTree, typeIndex, modIndex);
   1534 
   1535         if (eventSeq->event.eventType == MappingNotify)
   1536             stateTree->mappingNotifyInterest = True;
   1537 
   1538         /* *state now points at state record matching event */
   1539         eventSeq->state = *state;
   1540 
   1541         if (eventSeq->actions != NULL) {
   1542             if ((*state)->actions != NULL)
   1543                 AmbigActions(initialEvent, state, stateTree);
   1544             (*state)->actions = eventSeq->actions;
   1545 #ifdef TRACE_TM
   1546             LOCK_PROCESS;
   1547             _XtGlobalTM.numComplexActions++;
   1548             UNLOCK_PROCESS;
   1549 #endif                          /* TRACE_TM */
   1550         }
   1551 
   1552         if (((eventSeq = eventSeq->next) == NULL) || (eventSeq->state))
   1553             break;
   1554 
   1555         state = &(*state)->nextLevel;
   1556         typeIndex = _XtGetTypeIndex(&eventSeq->event);
   1557         modIndex = _XtGetModifierIndex(&eventSeq->event);
   1558         LOCK_PROCESS;
   1559         if (!TMNewMatchSemantics()) {
   1560             /*
   1561              * force a potential empty entry into the branch head
   1562              * table in order to emulate old matching behavior
   1563              */
   1564             (void) GetBranchHead(stateTree, typeIndex, modIndex, True);
   1565         }
   1566         UNLOCK_PROCESS;
   1567     }
   1568 
   1569     if (eventSeq && eventSeq->state) {
   1570         /* we've been here before... must be a cycle in the event seq. */
   1571         branchHead->hasCycles = True;
   1572         (*state)->nextLevel = eventSeq->state;
   1573         eventSeq->state->isCycleStart = True;
   1574         (*state)->isCycleEnd = TRUE;
   1575     }
   1576 }
   1577 
   1578 /*
   1579  * Internal Converter for merging. Old and New must both be valid xlations
   1580  */
   1581 Boolean
   1582 _XtCvtMergeTranslations(Display *dpy _X_UNUSED,
   1583                         XrmValuePtr args _X_UNUSED,
   1584                         Cardinal *num_args,
   1585                         XrmValuePtr from,
   1586                         XrmValuePtr to,
   1587                         XtPointer *closure_ret _X_UNUSED)
   1588 {
   1589     XtTranslations first, second, xlations;
   1590     TMStateTree *stateTrees, stackStateTrees[16];
   1591     TMShortCard numStateTrees, i;
   1592 
   1593     if (*num_args != 0)
   1594         XtWarningMsg("invalidParameters", "mergeTranslations",
   1595                      XtCXtToolkitError,
   1596                      "MergeTM to TranslationTable needs no extra arguments",
   1597                      NULL, NULL);
   1598 
   1599     if (to->addr != NULL && to->size < sizeof(XtTranslations)) {
   1600         to->size = sizeof(XtTranslations);
   1601         return False;
   1602     }
   1603 
   1604     first = ((TMConvertRec *) from->addr)->old;
   1605     second = ((TMConvertRec *) from->addr)->new;
   1606 
   1607     numStateTrees =
   1608         (TMShortCard) (first->numStateTrees + second->numStateTrees);
   1609 
   1610     stateTrees = (TMStateTree *)
   1611         XtStackAlloc(numStateTrees * sizeof(TMStateTree), stackStateTrees);
   1612 
   1613     for (i = 0; i < first->numStateTrees; i++)
   1614         stateTrees[i] = first->stateTreeTbl[i];
   1615     for (i = 0; i < second->numStateTrees; i++)
   1616         stateTrees[i + first->numStateTrees] = second->stateTreeTbl[i];
   1617 
   1618     xlations = _XtCreateXlations(stateTrees, numStateTrees, first, second);
   1619 
   1620     if (to->addr != NULL) {
   1621         *(XtTranslations *) to->addr = xlations;
   1622     }
   1623     else {
   1624         static XtTranslations staticStateTable;
   1625 
   1626         staticStateTable = xlations;
   1627         to->addr = (XPointer) &staticStateTable;
   1628         to->size = sizeof(XtTranslations);
   1629     }
   1630 
   1631     XtStackFree((XtPointer) stateTrees, (XtPointer) stackStateTrees);
   1632     return True;
   1633 }
   1634 
   1635 static XtTranslations
   1636 MergeThem(Widget dest, XtTranslations first, XtTranslations second)
   1637 {
   1638     XtCacheRef cache_ref;
   1639     static XrmQuark from_type = NULLQUARK, to_type;
   1640     XrmValue from, to;
   1641     TMConvertRec convert_rec;
   1642     XtTranslations newTable;
   1643 
   1644     LOCK_PROCESS;
   1645     if (from_type == NULLQUARK) {
   1646         from_type = XrmPermStringToQuark(_XtRStateTablePair);
   1647         to_type = XrmPermStringToQuark(XtRTranslationTable);
   1648     }
   1649     UNLOCK_PROCESS;
   1650     from.addr = (XPointer) &convert_rec;
   1651     from.size = sizeof(TMConvertRec);
   1652     to.addr = (XPointer) &newTable;
   1653     to.size = sizeof(XtTranslations);
   1654     convert_rec.old = first;
   1655     convert_rec.new = second;
   1656 
   1657     LOCK_PROCESS;
   1658     if (!_XtConvert(dest, from_type, &from, to_type, &to, &cache_ref)) {
   1659         UNLOCK_PROCESS;
   1660         return NULL;
   1661     }
   1662     UNLOCK_PROCESS;
   1663 
   1664 #ifndef REFCNT_TRANSLATIONS
   1665 
   1666     if (cache_ref)
   1667         XtAddCallback(dest, XtNdestroyCallback,
   1668                       XtCallbackReleaseCacheRef, (XtPointer) cache_ref);
   1669 
   1670 #endif
   1671 
   1672     return newTable;
   1673 }
   1674 
   1675 /*
   1676  * Unmerge will recursively traverse the xlation compose tree and
   1677  * generate a new xlation that is the result of all instances of
   1678  * xlations being removed. It currently doesn't differentiate between
   1679  * the potential that an xlation will be both an accelerator and
   1680  * normal. This is not supported by the spec anyway.
   1681  */
   1682 static XtTranslations
   1683 UnmergeTranslations(Widget widget,
   1684                     XtTranslations xlations,
   1685                     XtTranslations unmergeXlations,
   1686                     TMShortCard currIndex,
   1687                     TMComplexBindProcs oldBindings,
   1688                     TMShortCard numOldBindings,
   1689                     TMComplexBindProcs newBindings,
   1690                     TMShortCard *numNewBindingsRtn)
   1691 {
   1692     XtTranslations first, second, result;
   1693 
   1694     if (!xlations || (xlations == unmergeXlations))
   1695         return NULL;
   1696 
   1697     if (xlations->composers[0]) {
   1698         first = UnmergeTranslations(widget, xlations->composers[0],
   1699                                     unmergeXlations, currIndex,
   1700                                     oldBindings, numOldBindings,
   1701                                     newBindings, numNewBindingsRtn);
   1702     }
   1703     else
   1704         first = NULL;
   1705 
   1706     if (xlations->composers[0]
   1707         && xlations->composers[1]) {
   1708         second = UnmergeTranslations(widget, xlations->composers[1],
   1709                                      unmergeXlations, (TMShortCard)
   1710                                      (currIndex +
   1711                                       xlations->composers[0]->numStateTrees),
   1712                                      oldBindings,
   1713                                      numOldBindings, newBindings,
   1714                                      numNewBindingsRtn);
   1715     }
   1716     else
   1717         second = NULL;
   1718 
   1719     if (first || second) {
   1720         if (first && second) {
   1721             if ((first != xlations->composers[0]) ||
   1722                 (second != xlations->composers[1]))
   1723                 result = MergeThem(widget, first, second);
   1724             else
   1725                 result = xlations;
   1726         }
   1727         else {
   1728             if (first)
   1729                 result = first;
   1730             else
   1731                 result = second;
   1732         }
   1733     }
   1734     else {                      /* only update for leaf nodes */
   1735         if (numOldBindings) {
   1736             Cardinal i;
   1737 
   1738             for (i = 0; i < xlations->numStateTrees; i++) {
   1739                 if (xlations->stateTreeTbl[i]->simple.isAccelerator)
   1740                     newBindings[*numNewBindingsRtn] =
   1741                         oldBindings[currIndex + i];
   1742                 (*numNewBindingsRtn)++;
   1743             }
   1744         }
   1745         result = xlations;
   1746     }
   1747     return result;
   1748 }
   1749 
   1750 typedef struct {
   1751     XtTranslations xlations;
   1752     TMComplexBindProcs bindings;
   1753 } MergeBindRec, *MergeBind;
   1754 
   1755 static XtTranslations
   1756 MergeTranslations(Widget widget,
   1757                   XtTranslations oldXlations,
   1758                   XtTranslations newXlations,
   1759                   _XtTranslateOp operation,
   1760                   Widget source,
   1761                   TMComplexBindProcs oldBindings,
   1762                   TMComplexBindProcs newBindings,
   1763                   TMShortCard *numNewRtn)
   1764 {
   1765     XtTranslations newTable = NULL, xlations;
   1766     TMComplexBindProcs bindings;
   1767     TMShortCard i, j;
   1768     TMStateTree *treePtr;
   1769     TMShortCard numNew;
   1770     MergeBindRec bindPair[2];
   1771 
   1772     /* If the new translation has an accelerator context then pull it
   1773      * off and pass it and the real xlations in to the caching merge
   1774      * routine.
   1775      */
   1776     if (newXlations->hasBindings) {
   1777         xlations = ((ATranslations) newXlations)->xlations;
   1778         bindings = (TMComplexBindProcs)
   1779             &((ATranslations) newXlations)->bindTbl[0];
   1780     }
   1781     else {
   1782         xlations = newXlations;
   1783         bindings = NULL;
   1784     }
   1785     switch (operation) {
   1786     default:
   1787     case XtTableReplace:
   1788         newTable = bindPair[0].xlations = xlations;
   1789         bindPair[0].bindings = bindings;
   1790         bindPair[1].xlations = NULL;
   1791         bindPair[1].bindings = NULL;
   1792         break;
   1793     case XtTableAugment:
   1794         bindPair[0].xlations = oldXlations;
   1795         bindPair[0].bindings = oldBindings;
   1796         bindPair[1].xlations = xlations;
   1797         bindPair[1].bindings = bindings;
   1798         newTable = NULL;
   1799         break;
   1800     case XtTableOverride:
   1801         bindPair[0].xlations = xlations;
   1802         bindPair[0].bindings = bindings;
   1803         bindPair[1].xlations = oldXlations;
   1804         bindPair[1].bindings = oldBindings;
   1805         newTable = NULL;
   1806         break;
   1807     }
   1808     if (!newTable)
   1809         newTable =
   1810             MergeThem(widget, bindPair[0].xlations, bindPair[1].xlations);
   1811 
   1812     for (i = 0, numNew = 0; i < 2; i++) {
   1813         if (bindPair[i].xlations)
   1814             for (j = 0; j < bindPair[i].xlations->numStateTrees; j++, numNew++) {
   1815                 if (bindPair[i].xlations->stateTreeTbl[j]->simple.isAccelerator) {
   1816                     if (bindPair[i].bindings)
   1817                         newBindings[numNew] = bindPair[i].bindings[j];
   1818                     else {
   1819                         newBindings[numNew].widget = source;
   1820                         newBindings[numNew].aXlations = bindPair[i].xlations;
   1821                     }
   1822                 }
   1823             }
   1824     }
   1825     *numNewRtn = numNew;
   1826     treePtr = &newTable->stateTreeTbl[0];
   1827     for (i = 0; i < newTable->numStateTrees; i++, treePtr++)
   1828         (*treePtr)->simple.refCount++;
   1829     return newTable;
   1830 }
   1831 
   1832 static TMBindData
   1833 MakeBindData(TMComplexBindProcs bindings,
   1834              TMShortCard numBindings,
   1835              TMBindData oldBindData)
   1836 {
   1837     TMLongCard bytes;
   1838     TMShortCard i;
   1839     Boolean isComplex;
   1840     TMBindData bindData;
   1841 
   1842     if (numBindings == 0)
   1843         return NULL;
   1844     for (i = 0; i < numBindings; i++)
   1845         if (bindings[i].widget)
   1846             break;
   1847     isComplex = (i < numBindings);
   1848     if (isComplex)
   1849         bytes = (sizeof(TMComplexBindDataRec) +
   1850                  ((TMLongCard) (numBindings - 1) *
   1851                   sizeof(TMComplexBindProcsRec)));
   1852     else
   1853         bytes = (sizeof(TMSimpleBindDataRec) +
   1854                  ((TMLongCard) (numBindings - 1) *
   1855                   sizeof(TMSimpleBindProcsRec)));
   1856 
   1857     bindData =
   1858         (TMBindData) __XtCalloc((Cardinal) sizeof(char), (Cardinal) bytes);
   1859     XtSetBit(bindData->simple.isComplex, isComplex);
   1860     if (isComplex) {
   1861         TMComplexBindData cBindData = (TMComplexBindData) bindData;
   1862 
   1863         /*
   1864          * If there were any accelerator contexts in the old bindData
   1865          * then propagate them to the new one.
   1866          */
   1867         if (oldBindData && oldBindData->simple.isComplex)
   1868             cBindData->accel_context =
   1869                 ((TMComplexBindData) oldBindData)->accel_context;
   1870         memcpy(&cBindData->bindTbl[0], bindings,
   1871                numBindings * sizeof(TMComplexBindProcsRec));
   1872     }
   1873     return bindData;
   1874 }
   1875 
   1876 /*
   1877  * This routine is the central clearinghouse for merging translations
   1878  * into a widget. It takes care of preping the action bindings for
   1879  * realize time and calling the converter or doing a straight merge if
   1880  * the destination is empty.
   1881  */
   1882 static Boolean
   1883 ComposeTranslations(Widget dest,
   1884                     _XtTranslateOp operation,
   1885                     Widget source,
   1886                     XtTranslations newXlations)
   1887 {
   1888     XtTranslations newTable, oldXlations;
   1889     XtTranslations accNewXlations;
   1890     EventMask oldMask = 0;
   1891     TMBindData bindData;
   1892     TMComplexBindProcs oldBindings = NULL;
   1893     TMShortCard numOldBindings = 0, numNewBindings = 0, numBytes;
   1894     TMComplexBindProcsRec stackBindings[16], *newBindings;
   1895 
   1896     /*
   1897      * how should we be handling the refcount decrement for the
   1898      * replaced translation table ???
   1899      */
   1900     if (!newXlations) {
   1901         XtAppWarningMsg(XtWidgetToApplicationContext(dest),
   1902                         XtNtranslationError, "nullTable", XtCXtToolkitError,
   1903                         "table to (un)merge must not be null", NULL, NULL);
   1904         return False;
   1905     }
   1906 
   1907     accNewXlations = newXlations;
   1908     newXlations = ((newXlations->hasBindings)
   1909                    ? ((ATranslations) newXlations)->xlations : newXlations);
   1910 
   1911     if (!(oldXlations = dest->core.tm.translations))
   1912         operation = XtTableReplace;
   1913 
   1914     /*
   1915      * try to avoid generation of duplicate state trees. If the source
   1916      * isn't simple (1 state Tree) then it's too much hassle
   1917      */
   1918     if (((operation == XtTableAugment) ||
   1919          (operation == XtTableOverride)) && (newXlations->numStateTrees == 1)) {
   1920         Cardinal i;
   1921 
   1922         for (i = 0; i < oldXlations->numStateTrees; i++)
   1923             if (oldXlations->stateTreeTbl[i] == newXlations->stateTreeTbl[0])
   1924                 break;
   1925         if (i < oldXlations->numStateTrees) {
   1926             if (operation == XtTableAugment) {
   1927                 /*
   1928                  * we don't need to do anything since it's already
   1929                  * there
   1930                  */
   1931                 return True;
   1932             }
   1933             else {              /* operation == XtTableOverride */
   1934                 /*
   1935                  * We'll get rid of the duplicate trees throughout the
   1936                  * and leave it with a pruned translation table. This
   1937                  * will only work if the same table has been merged
   1938                  * into this table (or one of it's composers
   1939                  */
   1940                 _XtUnmergeTranslations(dest, newXlations);
   1941                 /*
   1942                  * reset oldXlations so we're back in sync
   1943                  */
   1944                 if (!(oldXlations = dest->core.tm.translations))
   1945                     operation = XtTableReplace;
   1946             }
   1947         }
   1948     }
   1949 
   1950     bindData = (TMBindData) dest->core.tm.proc_table;
   1951     if (bindData) {
   1952         numOldBindings = (oldXlations ? oldXlations->numStateTrees : 0);
   1953         if (bindData->simple.isComplex)
   1954             oldBindings = &((TMComplexBindData) bindData)->bindTbl[0];
   1955         else
   1956             oldBindings = (TMComplexBindProcs)
   1957                 (&((TMSimpleBindData) bindData)->bindTbl[0]);
   1958     }
   1959 
   1960     numBytes =
   1961         (TMShortCard) ((size_t) ((oldXlations ? oldXlations->numStateTrees : 0)
   1962                                  +
   1963                                  newXlations->numStateTrees) *
   1964                        sizeof(TMComplexBindProcsRec));
   1965     newBindings = (TMComplexBindProcs) XtStackAlloc(numBytes, stackBindings);
   1966     XtBZero((char *) newBindings, numBytes);
   1967 
   1968     if (operation == XtTableUnmerge) {
   1969         newTable = UnmergeTranslations(dest,
   1970                                        oldXlations,
   1971                                        newXlations,
   1972                                        0,
   1973                                        oldBindings, numOldBindings,
   1974                                        newBindings, &numNewBindings);
   1975 #ifdef DEBUG
   1976         /* check for no match for unmerge */
   1977         if (newTable == oldXlations) {
   1978             XtWarning("attempt to unmerge invalid table");
   1979             XtStackFree((char *) newBindings, (char *) stackBindings);
   1980             return (newTable != NULL);
   1981         }
   1982 #endif                          /* DEBUG */
   1983     }
   1984     else {
   1985         newTable = MergeTranslations(dest,
   1986                                      oldXlations,
   1987                                      accNewXlations,
   1988                                      operation,
   1989                                      source,
   1990                                      oldBindings, newBindings, &numNewBindings);
   1991     }
   1992     if (XtIsRealized(dest)) {
   1993         oldMask = 0;
   1994         if (oldXlations)
   1995             oldMask = oldXlations->eventMask;
   1996         _XtUninstallTranslations(dest);
   1997     }
   1998 
   1999     dest->core.tm.proc_table =
   2000         (XtActionProc *) MakeBindData(newBindings, numNewBindings, bindData);
   2001 
   2002     XtFree((char *) bindData);
   2003 
   2004     dest->core.tm.translations = newTable;
   2005 
   2006     if (XtIsRealized(dest)) {
   2007         EventMask mask = 0;
   2008 
   2009         _XtInstallTranslations(dest);
   2010         if (newTable)
   2011             mask = newTable->eventMask;
   2012         if (mask != oldMask)
   2013             XSelectInput(XtDisplay(dest), XtWindow(dest),
   2014                          (long) XtBuildEventMask(dest));
   2015     }
   2016     XtStackFree((XtPointer) newBindings, (XtPointer) stackBindings);
   2017     return (newTable != NULL);
   2018 }
   2019 
   2020 /*
   2021  * If a GetValues is done on a translation resource that contains
   2022  * accelerators we need to return the accelerator context in addition
   2023  * to the pure translations.  Since this means returning memory that
   2024  * the client controls but we still own, we will track the "headers"
   2025  * that we return (via a linked list pointed to from the bindData) and
   2026  * free it at destroy time.
   2027  */
   2028 XtTranslations
   2029 _XtGetTranslationValue(Widget w)
   2030 {
   2031     XtTM tmRecPtr = (XtTM) &w->core.tm;
   2032     ATranslations *aXlationsPtr;
   2033     TMComplexBindData cBindData = (TMComplexBindData) tmRecPtr->proc_table;
   2034     XtTranslations xlations = tmRecPtr->translations;
   2035 
   2036     if (!xlations || !cBindData || !cBindData->isComplex)
   2037         return xlations;
   2038 
   2039     /* Walk the list looking to see if we already have generated a
   2040      * header for the currently installed translations.  If we have,
   2041      * just return that header.  Otherwise create a new header.
   2042      */
   2043     for (aXlationsPtr = (ATranslations *) &cBindData->accel_context;
   2044          *aXlationsPtr && (*aXlationsPtr)->xlations != xlations;
   2045          aXlationsPtr = &(*aXlationsPtr)->next);
   2046     if (*aXlationsPtr)
   2047         return (XtTranslations) *aXlationsPtr;
   2048     else {
   2049         /* create a new aXlations context */
   2050         ATranslations aXlations;
   2051         Cardinal numBindings = xlations->numStateTrees;
   2052 
   2053         (*aXlationsPtr) = aXlations = (ATranslations)
   2054             __XtMalloc((Cardinal) (sizeof(ATranslationData) +
   2055                                    (numBindings -
   2056                                     1) * sizeof(TMComplexBindProcsRec)));
   2057 
   2058         aXlations->hasBindings = True;
   2059         aXlations->xlations = xlations;
   2060         aXlations->next = NULL;
   2061         memcpy(&aXlations->bindTbl[0],
   2062                &cBindData->bindTbl[0],
   2063                numBindings * sizeof(TMComplexBindProcsRec));
   2064         return (XtTranslations) aXlations;
   2065     }
   2066 }
   2067 
   2068 static void
   2069 RemoveStateTree(TMStateTree tree _X_UNUSED)
   2070 {
   2071 #ifdef REFCNT_TRANSLATIONS
   2072     TMComplexStateTree stateTree = (TMComplexStateTree) tree;
   2073 
   2074     if (--stateTree->refCount == 0) {
   2075         /*
   2076          * should we free/refcount the match recs ?
   2077          */
   2078         if (!stateTree->isSimple) {
   2079             StatePtr currState, nextState;
   2080             TMShortCard i;
   2081 
   2082             for (i = 0; i < stateTree->numComplexBranchHeads; i++) {
   2083                 currState = nextState = stateTree->complexBranchHeadTbl[i];
   2084                 for (; nextState;) {
   2085                     FreeActions(currState->actions);
   2086                     currState->actions = NULL;
   2087                     if (!currState->isCycleEnd)
   2088                         nextState = currState->nextLevel;
   2089                     else
   2090                         nextState = NULL;
   2091                     XtFree((char *) currState);
   2092                 }
   2093             }
   2094             XtFree((char *) stateTree->complexBranchHeadTbl);
   2095         }
   2096         XtFree((char *) stateTree->branchHeadTbl);
   2097         XtFree((char *) stateTree);
   2098     }
   2099 #endif                          /* REFCNT_TRANSLATIONS */
   2100 }
   2101 
   2102 void
   2103 _XtRemoveStateTreeByIndex(XtTranslations xlations, TMShortCard i)
   2104 {
   2105     TMStateTree *stateTrees = xlations->stateTreeTbl;
   2106 
   2107     RemoveStateTree(stateTrees[i]);
   2108     xlations->numStateTrees--;
   2109 
   2110     for (; i < xlations->numStateTrees; i++) {
   2111         stateTrees[i] = stateTrees[i + 1];
   2112     }
   2113 }
   2114 
   2115 void
   2116 _XtFreeTranslations(XtAppContext app,
   2117                     XrmValuePtr toVal,
   2118                     XtPointer closure _X_UNUSED,
   2119                     XrmValuePtr args _X_UNUSED,
   2120                     Cardinal *num_args)
   2121 {
   2122     XtTranslations xlations;
   2123     int i;
   2124 
   2125     if (*num_args != 0)
   2126         XtAppWarningMsg(app,
   2127                         "invalidParameters", "freeTranslations",
   2128                         XtCXtToolkitError,
   2129                         "Freeing XtTranslations requires no extra arguments",
   2130                         NULL, NULL);
   2131 
   2132     xlations = *(XtTranslations *) toVal->addr;
   2133     for (i = 0; i < (int) xlations->numStateTrees; i++)
   2134         RemoveStateTree(xlations->stateTreeTbl[i]);
   2135     XtFree((char *) xlations);
   2136 }
   2137 
   2138 /*  The spec is not clear on when actions specified in accelerators are bound;
   2139  *  Bind them at Realize the same as translations
   2140  */
   2141 void
   2142 XtInstallAccelerators(Widget destination, Widget source)
   2143 {
   2144     XtTranslations aXlations;
   2145     _XtTranslateOp op;
   2146 
   2147     WIDGET_TO_APPCON(destination);
   2148 
   2149     /*
   2150      * test that it was parsed as an accelarator table. Even though
   2151      * there doesn't need to be a distinction it makes life easier if
   2152      * we honor the spec implication that aXlations is an accelerator
   2153      */
   2154     LOCK_APP(app);
   2155     LOCK_PROCESS;
   2156     if ((!XtIsWidget(source)) ||
   2157         ((aXlations = source->core.accelerators) == NULL) ||
   2158         (aXlations->stateTreeTbl[0]->simple.isAccelerator == False)) {
   2159         UNLOCK_PROCESS;
   2160         UNLOCK_APP(app);
   2161         return;
   2162     }
   2163 
   2164     aXlations = source->core.accelerators;
   2165     op = aXlations->operation;
   2166 
   2167     if (ComposeTranslations(destination, op, source, aXlations) &&
   2168         (XtClass(source)->core_class.display_accelerator != NULL)) {
   2169         _XtString buf = _XtPrintXlations(destination, aXlations, source, False);
   2170 
   2171         (*(XtClass(source)->core_class.display_accelerator)) (source, buf);
   2172         XtFree(buf);
   2173     }
   2174     UNLOCK_PROCESS;
   2175     UNLOCK_APP(app);
   2176 }
   2177 
   2178 void
   2179 XtInstallAllAccelerators(Widget destination, Widget source)
   2180 {
   2181     Cardinal i;
   2182 
   2183     WIDGET_TO_APPCON(destination);
   2184 
   2185     /* Recurse down normal children */
   2186     LOCK_APP(app);
   2187     LOCK_PROCESS;
   2188     if (XtIsComposite(source)) {
   2189         CompositeWidget cw = (CompositeWidget) source;
   2190 
   2191         for (i = 0; i < cw->composite.num_children; i++) {
   2192             XtInstallAllAccelerators(destination, cw->composite.children[i]);
   2193         }
   2194     }
   2195 
   2196     /* Recurse down popup children */
   2197     if (XtIsWidget(source)) {
   2198         for (i = 0; i < source->core.num_popups; i++) {
   2199             XtInstallAllAccelerators(destination, source->core.popup_list[i]);
   2200         }
   2201     }
   2202     /* Finally, apply procedure to this widget */
   2203     XtInstallAccelerators(destination, source);
   2204     UNLOCK_PROCESS;
   2205     UNLOCK_APP(app);
   2206 }
   2207 
   2208 #if 0                           /* dead code */
   2209 static _XtTranslateOp
   2210 _XtGetTMOperation(XtTranslations xlations)
   2211 {
   2212     return ((xlations->hasBindings)
   2213             ? ((ATranslations) xlations)->xlations->operation
   2214             : xlations->operation);
   2215 }
   2216 #endif
   2217 
   2218 void
   2219 XtAugmentTranslations(Widget widget, XtTranslations new)
   2220 {
   2221     Widget hookobj;
   2222 
   2223     WIDGET_TO_APPCON(widget);
   2224 
   2225     LOCK_APP(app);
   2226     LOCK_PROCESS;
   2227     (void) ComposeTranslations(widget, XtTableAugment, (Widget) NULL, new);
   2228     hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
   2229     if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
   2230         XtChangeHookDataRec call_data;
   2231 
   2232         call_data.type = XtHaugmentTranslations;
   2233         call_data.widget = widget;
   2234         XtCallCallbackList(hookobj,
   2235                            ((HookObject) hookobj)->hooks.changehook_callbacks,
   2236                            (XtPointer) &call_data);
   2237     }
   2238     UNLOCK_PROCESS;
   2239     UNLOCK_APP(app);
   2240 }
   2241 
   2242 void
   2243 XtOverrideTranslations(Widget widget, XtTranslations new)
   2244 {
   2245     Widget hookobj;
   2246 
   2247     WIDGET_TO_APPCON(widget);
   2248 
   2249     LOCK_APP(app);
   2250     LOCK_PROCESS;
   2251     (void) ComposeTranslations(widget, XtTableOverride, (Widget) NULL, new);
   2252     hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
   2253     if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
   2254         XtChangeHookDataRec call_data;
   2255 
   2256         call_data.type = XtHoverrideTranslations;
   2257         call_data.widget = widget;
   2258         XtCallCallbackList(hookobj,
   2259                            ((HookObject) hookobj)->hooks.changehook_callbacks,
   2260                            (XtPointer) &call_data);
   2261     }
   2262     UNLOCK_PROCESS;
   2263     UNLOCK_APP(app);
   2264 }
   2265 
   2266 void
   2267 _XtMergeTranslations(Widget widget,
   2268                      XtTranslations newXlations,
   2269                      _XtTranslateOp op)
   2270 {
   2271     if (!newXlations) {
   2272         if (!widget->core.tm.translations)
   2273             return;
   2274         else {
   2275             newXlations = widget->core.tm.translations;
   2276             widget->core.tm.translations = NULL;
   2277         }
   2278     }
   2279     (void) ComposeTranslations(widget, op, (Widget) NULL, newXlations);
   2280 }
   2281 
   2282 void
   2283 _XtUnmergeTranslations(Widget widget, XtTranslations xlations)
   2284 {
   2285     ComposeTranslations(widget, XtTableUnmerge, (Widget) NULL, xlations);
   2286 }
   2287