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