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
86TMGlobalRec _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
96static TMShortCard
97GetBranchHead(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
160TMShortCard
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 */
205static TMShortCard
206GetComplexBranchIndex(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
244TMShortCard
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
294static Boolean
295CompareLateModifiers(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
334TMShortCard
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 */
407static int
408MatchBranchHead(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
430Boolean
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
455Boolean
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 */
474static Boolean
475Ignore(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
492static void
493XEventToTMEvent(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
571static unsigned long
572GetTime(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
591static void
592HandleActions(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
644typedef struct {
645    unsigned int isCycleStart:1;
646    unsigned int isCycleEnd:1;
647    TMShortCard typeIndex;
648    TMShortCard modIndex;
649} MatchPairRec, *MatchPair;
650
651typedef struct TMContextRec {
652    TMShortCard numMatches;
653    TMShortCard maxMatches;
654    MatchPair matches;
655} TMContextRec, *TMContext;
656
657static 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
664static void
665PushContext(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
721static void
722FreeContext(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
744static int
745MatchExact(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
761static void
762HandleSimpleState(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
873static int
874MatchComplexBranch(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
906static StatePtr
907TryCurrentTree(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
977static void
978HandleComplexState(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
1044void
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
1065static StatePtr
1066NewState(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 */
1089void
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
1131static EventMask
1132EventToMask(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
1171static void
1172DispatchMappingNotify(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
1179static void
1180RemoveFromMappingCallbacks(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
1188static Boolean
1189AggregateEventMask(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
1198void
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
1266void
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
1287static 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
1298void
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
1322void
1323XtUninstallTranslations(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
1353XtTranslations
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
1393TMStateTree
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
1436static void
1437FreeActions(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
1454static void
1455AmbigActions(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
1483void
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 */
1581Boolean
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
1635static XtTranslations
1636MergeThem(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 */
1682static XtTranslations
1683UnmergeTranslations(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
1750typedef struct {
1751    XtTranslations xlations;
1752    TMComplexBindProcs bindings;
1753} MergeBindRec, *MergeBind;
1754
1755static XtTranslations
1756MergeTranslations(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
1832static TMBindData
1833MakeBindData(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 */
1882static Boolean
1883ComposeTranslations(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 */
2028XtTranslations
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
2068static void
2069RemoveStateTree(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
2102void
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
2115void
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 */
2141void
2142XtInstallAccelerators(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
2178void
2179XtInstallAllAccelerators(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 */
2209static _XtTranslateOp
2210_XtGetTMOperation(XtTranslations xlations)
2211{
2212    return ((xlations->hasBindings)
2213            ? ((ATranslations) xlations)->xlations->operation
2214            : xlations->operation);
2215}
2216#endif
2217
2218void
2219XtAugmentTranslations(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
2242void
2243XtOverrideTranslations(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
2266void
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
2282void
2283_XtUnmergeTranslations(Widget widget, XtTranslations xlations)
2284{
2285    ComposeTranslations(widget, XtTableUnmerge, (Widget) NULL, xlations);
2286}
2287