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