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