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