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