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