mirror of
https://github.com/Stichting-MINIX-Research-Foundation/xsrc.git
synced 2025-09-09 20:59:43 -04:00
2347 lines
64 KiB
C
2347 lines
64 KiB
C
/* $Xorg: TMstate.c,v 1.6 2001/02/09 02:03:58 xorgcvs Exp $ */
|
|
|
|
/***********************************************************
|
|
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
|
|
Copyright 1993 by Sun Microsystems, Inc. Mountain View, CA.
|
|
|
|
All Rights Reserved
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
documentation for any purpose and without fee is hereby granted,
|
|
provided that the above copyright notice appear in all copies and that
|
|
both that copyright notice and this permission notice appear in
|
|
supporting documentation, and that the names of Digital or Sun not be
|
|
used in advertising or publicity pertaining to distribution of the
|
|
software without specific, written prior permission.
|
|
|
|
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
|
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
|
|
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
|
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
SOFTWARE.
|
|
|
|
SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
|
|
NESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SUN BE LI-
|
|
ABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
|
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
******************************************************************/
|
|
/* $XFree86: xc/lib/Xt/TMstate.c,v 1.10 2004/05/05 00:07:03 dickey Exp $ */
|
|
|
|
/*
|
|
|
|
Copyright 1987, 1988, 1994, 1998 The Open Group
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
the above copyright notice appear in all copies and that both that
|
|
copyright notice and this permission notice appear in supporting
|
|
documentation.
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of The Open Group shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from The Open Group.
|
|
|
|
*/
|
|
|
|
/* TMstate.c -- maintains the state table of actions for the translation
|
|
* manager.
|
|
*/
|
|
/*LINTLIBRARY*/
|
|
|
|
#include "IntrinsicI.h"
|
|
|
|
#ifndef TM_NO_MATCH
|
|
#define TM_NO_MATCH (-2)
|
|
#endif /* TM_NO_MATCH */
|
|
|
|
/* forward definitions */
|
|
static StatePtr NewState(TMParseStateTree, TMShortCard, TMShortCard);
|
|
|
|
|
|
static String XtNtranslationError = "translationError";
|
|
|
|
#ifndef __EMX__
|
|
TMGlobalRec _XtGlobalTM; /* initialized to zero K&R */
|
|
#else
|
|
TMGlobalRec _XtGlobalTM = {0};
|
|
#endif
|
|
|
|
#define MatchIncomingEvent(tmEvent, typeMatch, modMatch) \
|
|
(typeMatch->eventType == tmEvent->event.eventType && \
|
|
(typeMatch->matchEvent != NULL) && \
|
|
(*typeMatch->matchEvent)(typeMatch, modMatch, tmEvent))
|
|
|
|
|
|
#define NumStateTrees(xlations) \
|
|
((translateData->isSimple) ? 1 : (TMComplexXlations(xlations))->numTrees)
|
|
|
|
static TMShortCard GetBranchHead(
|
|
TMParseStateTree parseTree,
|
|
TMShortCard typeIndex,
|
|
TMShortCard modIndex,
|
|
Boolean isDummy)
|
|
{
|
|
#define TM_BRANCH_HEAD_TBL_ALLOC 8
|
|
#define TM_BRANCH_HEAD_TBL_REALLOC 8
|
|
|
|
TMBranchHead branchHead = parseTree->branchHeadTbl;
|
|
TMShortCard newSize, i;
|
|
|
|
/*
|
|
* dummy is used as a place holder for later matching in old-style
|
|
* matching behavior. If there's already an entry we don't need
|
|
* another dummy.
|
|
*/
|
|
if (isDummy) {
|
|
for (i = 0; i < parseTree->numBranchHeads; i++, branchHead++) {
|
|
if ((branchHead->typeIndex == typeIndex) &&
|
|
(branchHead->modIndex == modIndex))
|
|
return i;
|
|
}
|
|
}
|
|
if (parseTree->numBranchHeads == parseTree->branchHeadTblSize)
|
|
{
|
|
if (parseTree->branchHeadTblSize == 0)
|
|
parseTree->branchHeadTblSize += TM_BRANCH_HEAD_TBL_ALLOC;
|
|
else
|
|
parseTree->branchHeadTblSize +=
|
|
TM_BRANCH_HEAD_TBL_REALLOC;
|
|
newSize = (parseTree->branchHeadTblSize * sizeof(TMBranchHeadRec));
|
|
if (parseTree->isStackBranchHeads) {
|
|
TMBranchHead oldBranchHeadTbl = parseTree->branchHeadTbl;
|
|
parseTree->branchHeadTbl = (TMBranchHead) __XtMalloc(newSize);
|
|
XtMemmove(parseTree->branchHeadTbl, oldBranchHeadTbl, newSize);
|
|
parseTree->isStackBranchHeads = False;
|
|
}
|
|
else {
|
|
parseTree->branchHeadTbl = (TMBranchHead)
|
|
XtRealloc((char *)parseTree->branchHeadTbl,
|
|
(parseTree->branchHeadTblSize *
|
|
sizeof(TMBranchHeadRec)));
|
|
}
|
|
}
|
|
#ifdef TRACE_TM
|
|
LOCK_PROCESS;
|
|
_XtGlobalTM.numBranchHeads++;
|
|
UNLOCK_PROCESS;
|
|
#endif /* TRACE_TM */
|
|
branchHead =
|
|
&parseTree->branchHeadTbl[parseTree->numBranchHeads++];
|
|
branchHead->typeIndex = typeIndex;
|
|
branchHead->modIndex = modIndex;
|
|
branchHead->more = 0;
|
|
branchHead->isSimple = True;
|
|
branchHead->hasActions = False;
|
|
branchHead->hasCycles = False;
|
|
return parseTree->numBranchHeads-1;
|
|
}
|
|
|
|
TMShortCard _XtGetQuarkIndex(
|
|
TMParseStateTree parseTree,
|
|
XrmQuark quark)
|
|
{
|
|
#define TM_QUARK_TBL_ALLOC 16
|
|
#define TM_QUARK_TBL_REALLOC 16
|
|
TMShortCard i = parseTree->numQuarks;
|
|
|
|
for (i=0; i < parseTree->numQuarks; i++)
|
|
if (parseTree->quarkTbl[i] == quark)
|
|
break;
|
|
|
|
if (i == parseTree->numQuarks)
|
|
{
|
|
if (parseTree->numQuarks == parseTree->quarkTblSize)
|
|
{
|
|
TMShortCard newSize;
|
|
|
|
if (parseTree->quarkTblSize == 0)
|
|
parseTree->quarkTblSize += TM_QUARK_TBL_ALLOC;
|
|
else
|
|
parseTree->quarkTblSize += TM_QUARK_TBL_REALLOC;
|
|
newSize = (parseTree->quarkTblSize * sizeof(XrmQuark));
|
|
|
|
if (parseTree->isStackQuarks) {
|
|
XrmQuark *oldquarkTbl = parseTree->quarkTbl;
|
|
parseTree->quarkTbl = (XrmQuark *) __XtMalloc(newSize);
|
|
XtMemmove(parseTree->quarkTbl, oldquarkTbl, newSize);
|
|
parseTree->isStackQuarks = False;
|
|
}
|
|
else {
|
|
parseTree->quarkTbl = (XrmQuark *)
|
|
XtRealloc((char *)parseTree->quarkTbl,
|
|
(parseTree->quarkTblSize *
|
|
sizeof(XrmQuark)));
|
|
}
|
|
}
|
|
parseTree->quarkTbl[parseTree->numQuarks++] = quark;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Get an entry from the parseTrees complex branchHead tbl. If there's none
|
|
* there then allocate one
|
|
*/
|
|
/*ARGSUSED*/
|
|
static TMShortCard GetComplexBranchIndex(
|
|
TMParseStateTree parseTree,
|
|
TMShortCard typeIndex,
|
|
TMShortCard modIndex)
|
|
{
|
|
#define TM_COMPLEXBRANCH_HEAD_TBL_ALLOC 8
|
|
#define TM_COMPLEXBRANCH_HEAD_TBL_REALLOC 4
|
|
|
|
if (parseTree->numComplexBranchHeads == parseTree->complexBranchHeadTblSize) {
|
|
TMShortCard newSize;
|
|
|
|
if (parseTree->complexBranchHeadTblSize == 0)
|
|
parseTree->complexBranchHeadTblSize += TM_COMPLEXBRANCH_HEAD_TBL_ALLOC;
|
|
else
|
|
parseTree->complexBranchHeadTblSize += TM_COMPLEXBRANCH_HEAD_TBL_REALLOC;
|
|
|
|
newSize = (parseTree->complexBranchHeadTblSize * sizeof(StatePtr));
|
|
|
|
if (parseTree->isStackComplexBranchHeads) {
|
|
StatePtr *oldcomplexBranchHeadTbl
|
|
= parseTree->complexBranchHeadTbl;
|
|
parseTree->complexBranchHeadTbl = (StatePtr *) __XtMalloc(newSize);
|
|
XtMemmove(parseTree->complexBranchHeadTbl,
|
|
oldcomplexBranchHeadTbl, newSize);
|
|
parseTree->isStackComplexBranchHeads = False;
|
|
}
|
|
else {
|
|
parseTree->complexBranchHeadTbl = (StatePtr *)
|
|
XtRealloc((char *)parseTree->complexBranchHeadTbl,
|
|
(parseTree->complexBranchHeadTblSize *
|
|
sizeof(StatePtr)));
|
|
}
|
|
}
|
|
parseTree->complexBranchHeadTbl[parseTree->numComplexBranchHeads++] = NULL;
|
|
return parseTree->numComplexBranchHeads-1;
|
|
}
|
|
|
|
TMShortCard _XtGetTypeIndex(
|
|
Event *event)
|
|
{
|
|
TMShortCard i, j = TM_TYPE_SEGMENT_SIZE;
|
|
TMShortCard typeIndex = 0;
|
|
TMTypeMatch typeMatch;
|
|
TMTypeMatch segment = NULL;
|
|
|
|
LOCK_PROCESS;
|
|
for (i = 0; i < _XtGlobalTM.numTypeMatchSegments; i++) {
|
|
segment = _XtGlobalTM.typeMatchSegmentTbl[i];
|
|
for (j = 0;
|
|
typeIndex < _XtGlobalTM.numTypeMatches && j < TM_TYPE_SEGMENT_SIZE;
|
|
j++, typeIndex++)
|
|
{
|
|
typeMatch = &(segment[j]);
|
|
if (event->eventType == typeMatch->eventType &&
|
|
event->eventCode == typeMatch->eventCode &&
|
|
event->eventCodeMask == typeMatch->eventCodeMask &&
|
|
event->matchEvent == typeMatch->matchEvent) {
|
|
UNLOCK_PROCESS;
|
|
return typeIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (j == TM_TYPE_SEGMENT_SIZE) {
|
|
if (_XtGlobalTM.numTypeMatchSegments == _XtGlobalTM.typeMatchSegmentTblSize) {
|
|
_XtGlobalTM.typeMatchSegmentTblSize += 4;
|
|
_XtGlobalTM.typeMatchSegmentTbl = (TMTypeMatch *)
|
|
XtRealloc((char *)_XtGlobalTM.typeMatchSegmentTbl,
|
|
(_XtGlobalTM.typeMatchSegmentTblSize * sizeof(TMTypeMatch)));
|
|
}
|
|
_XtGlobalTM.typeMatchSegmentTbl[_XtGlobalTM.numTypeMatchSegments++] =
|
|
segment = (TMTypeMatch)
|
|
__XtMalloc(TM_TYPE_SEGMENT_SIZE * sizeof(TMTypeMatchRec));
|
|
j = 0;
|
|
}
|
|
typeMatch = &segment[j];
|
|
typeMatch->eventType = event->eventType;
|
|
typeMatch->eventCode = event->eventCode;
|
|
typeMatch->eventCodeMask = event->eventCodeMask;
|
|
typeMatch->matchEvent = event->matchEvent;
|
|
_XtGlobalTM.numTypeMatches++;
|
|
UNLOCK_PROCESS;
|
|
return typeIndex;
|
|
}
|
|
|
|
static Boolean CompareLateModifiers(
|
|
LateBindingsPtr lateBind1P,
|
|
LateBindingsPtr lateBind2P)
|
|
{
|
|
LateBindingsPtr late1P = lateBind1P;
|
|
LateBindingsPtr late2P = lateBind2P;
|
|
|
|
if (late1P != NULL || late2P != NULL) {
|
|
int i = 0;
|
|
int j = 0;
|
|
if (late1P != NULL)
|
|
for (; late1P->keysym != NoSymbol; i++) late1P++;
|
|
if (late2P != NULL)
|
|
for (; late2P->keysym != NoSymbol; j++) late2P++;
|
|
if (i != j) return FALSE;
|
|
late1P--;
|
|
while (late1P >= lateBind1P) {
|
|
Boolean last = True;
|
|
for (late2P = lateBind2P + i - 1;
|
|
late2P >= lateBind2P;
|
|
late2P--) {
|
|
if (late1P->keysym == late2P->keysym
|
|
&& late1P->knot == late2P->knot) {
|
|
j--;
|
|
if (last) i--;
|
|
break;
|
|
}
|
|
last = False;
|
|
}
|
|
late1P--;
|
|
}
|
|
if (j != 0) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
TMShortCard _XtGetModifierIndex(
|
|
Event *event)
|
|
{
|
|
TMShortCard i, j = TM_MOD_SEGMENT_SIZE;
|
|
TMShortCard modIndex = 0;
|
|
TMModifierMatch modMatch;
|
|
TMModifierMatch segment = NULL;
|
|
|
|
LOCK_PROCESS;
|
|
for (i = 0; i < _XtGlobalTM.numModMatchSegments; i++) {
|
|
segment = _XtGlobalTM.modMatchSegmentTbl[i];
|
|
for (j = 0;
|
|
modIndex < _XtGlobalTM.numModMatches && j < TM_MOD_SEGMENT_SIZE;
|
|
j++, modIndex++) {
|
|
modMatch = &(segment[j]);
|
|
if (event->modifiers == modMatch->modifiers &&
|
|
event->modifierMask == modMatch->modifierMask &&
|
|
event->standard == modMatch->standard &&
|
|
((!event->lateModifiers && !modMatch->lateModifiers) ||
|
|
CompareLateModifiers(event->lateModifiers,
|
|
modMatch->lateModifiers))) {
|
|
/*
|
|
* if we found a match then we can free the parser's
|
|
* late modifiers. If there isn't a match we use the
|
|
* parser's copy
|
|
*/
|
|
if (event->lateModifiers &&
|
|
--event->lateModifiers->ref_count == 0) {
|
|
XtFree((char *)event->lateModifiers);
|
|
event->lateModifiers = NULL;
|
|
}
|
|
UNLOCK_PROCESS;
|
|
return modIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (j == TM_MOD_SEGMENT_SIZE) {
|
|
if (_XtGlobalTM.numModMatchSegments == _XtGlobalTM.modMatchSegmentTblSize) {
|
|
_XtGlobalTM.modMatchSegmentTblSize += 4;
|
|
_XtGlobalTM.modMatchSegmentTbl = (TMModifierMatch *)
|
|
XtRealloc((char *)_XtGlobalTM.modMatchSegmentTbl,
|
|
(_XtGlobalTM.modMatchSegmentTblSize * sizeof(TMModifierMatch)));
|
|
}
|
|
_XtGlobalTM.modMatchSegmentTbl[_XtGlobalTM.numModMatchSegments++] =
|
|
segment = (TMModifierMatch)
|
|
__XtMalloc(TM_MOD_SEGMENT_SIZE * sizeof(TMModifierMatchRec));
|
|
j = 0;
|
|
}
|
|
modMatch = &segment[j];
|
|
modMatch->modifiers = event->modifiers;;
|
|
modMatch->modifierMask = event->modifierMask;
|
|
modMatch->standard = event->standard;
|
|
/*
|
|
* We use the parser's copy of the late binding array
|
|
*/
|
|
#ifdef TRACE_TM
|
|
if (event->lateModifiers)
|
|
_XtGlobalTM.numLateBindings++;
|
|
#endif /* TRACE_TM */
|
|
modMatch->lateModifiers = event->lateModifiers;
|
|
_XtGlobalTM.numModMatches++;
|
|
UNLOCK_PROCESS;
|
|
return modIndex;
|
|
}
|
|
|
|
|
|
/*
|
|
* This is called from the SimpleStateHandler to match a stateTree
|
|
* entry to the event coming in
|
|
*/
|
|
static int MatchBranchHead(
|
|
TMSimpleStateTree stateTree,
|
|
int startIndex,
|
|
TMEventPtr event)
|
|
{
|
|
TMBranchHead branchHead = &stateTree->branchHeadTbl[startIndex];
|
|
int i;
|
|
|
|
LOCK_PROCESS;
|
|
for (i = startIndex;
|
|
i < (int)stateTree->numBranchHeads;
|
|
i++, branchHead++)
|
|
{
|
|
TMTypeMatch typeMatch;
|
|
TMModifierMatch modMatch;
|
|
|
|
typeMatch = TMGetTypeMatch(branchHead->typeIndex);
|
|
modMatch = TMGetModifierMatch(branchHead->modIndex);
|
|
|
|
if (MatchIncomingEvent(event, typeMatch, modMatch)) {
|
|
UNLOCK_PROCESS;
|
|
return i;
|
|
}
|
|
}
|
|
UNLOCK_PROCESS;
|
|
return (TM_NO_MATCH);
|
|
}
|
|
|
|
Boolean _XtRegularMatch(
|
|
TMTypeMatch typeMatch,
|
|
TMModifierMatch modMatch,
|
|
TMEventPtr eventSeq)
|
|
{
|
|
Modifiers computed =0;
|
|
Modifiers computedMask =0;
|
|
Boolean resolved = TRUE;
|
|
if (typeMatch->eventCode != (eventSeq->event.eventCode &
|
|
typeMatch->eventCodeMask)) return FALSE;
|
|
if (modMatch->lateModifiers != NULL)
|
|
resolved = _XtComputeLateBindings(eventSeq->xev->xany.display,
|
|
modMatch->lateModifiers,
|
|
&computed, &computedMask);
|
|
if (!resolved) return FALSE;
|
|
computed |= modMatch->modifiers;
|
|
computedMask |= modMatch->modifierMask;
|
|
|
|
return ( (computed & computedMask) ==
|
|
(eventSeq->event.modifiers & computedMask));
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
Boolean _XtMatchAtom(
|
|
TMTypeMatch typeMatch,
|
|
TMModifierMatch modMatch,
|
|
TMEventPtr eventSeq)
|
|
{
|
|
Atom atom;
|
|
|
|
atom = XInternAtom(eventSeq->xev->xany.display,
|
|
XrmQuarkToString(typeMatch->eventCode),
|
|
False);
|
|
return (atom == eventSeq->event.eventCode);
|
|
}
|
|
|
|
#define IsOn(vec,idx) ((vec)[(idx)>>3] & (1 << ((idx) & 7)))
|
|
|
|
/*
|
|
* there are certain cases where you want to ignore the event and stay
|
|
* in the same state.
|
|
*/
|
|
static Boolean Ignore(
|
|
TMEventPtr event)
|
|
{
|
|
Display *dpy;
|
|
XtPerDisplay pd;
|
|
|
|
if (event->event.eventType == MotionNotify)
|
|
return TRUE;
|
|
if (!(event->event.eventType == KeyPress ||
|
|
event->event.eventType == KeyRelease))
|
|
return FALSE;
|
|
dpy = event->xev->xany.display;
|
|
pd = _XtGetPerDisplay(dpy);
|
|
_InitializeKeysymTables(dpy, pd);
|
|
return IsOn(pd->isModifier, event->event.eventCode) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
static void XEventToTMEvent(
|
|
XEvent *event,
|
|
TMEventPtr tmEvent)
|
|
{
|
|
tmEvent->xev = event;
|
|
tmEvent->event.eventCodeMask = 0;
|
|
tmEvent->event.modifierMask = 0;
|
|
tmEvent->event.eventType = event->type;
|
|
tmEvent->event.lateModifiers = NULL;
|
|
tmEvent->event.matchEvent = NULL;
|
|
tmEvent->event.standard = FALSE;
|
|
|
|
switch (event->type) {
|
|
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
tmEvent->event.eventCode = event->xkey.keycode;
|
|
tmEvent->event.modifiers = event->xkey.state;
|
|
break;
|
|
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
tmEvent->event.eventCode = event->xbutton.button;
|
|
tmEvent->event.modifiers = event->xbutton.state;
|
|
break;
|
|
|
|
case MotionNotify:
|
|
tmEvent->event.eventCode = event->xmotion.is_hint;
|
|
tmEvent->event.modifiers = event->xmotion.state;
|
|
break;
|
|
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
tmEvent->event.eventCode = event->xcrossing.mode;
|
|
tmEvent->event.modifiers = event->xcrossing.state;
|
|
break;
|
|
|
|
case PropertyNotify:
|
|
tmEvent->event.eventCode = event->xproperty.atom;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
|
|
case SelectionClear:
|
|
tmEvent->event.eventCode = event->xselectionclear.selection;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
|
|
case SelectionRequest:
|
|
tmEvent->event.eventCode = event->xselectionrequest.selection;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
|
|
case SelectionNotify:
|
|
tmEvent->event.eventCode = event->xselection.selection;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
|
|
case ClientMessage:
|
|
tmEvent->event.eventCode = event->xclient.message_type;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
|
|
case MappingNotify:
|
|
tmEvent->event.eventCode = event->xmapping.request;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
|
|
case FocusIn:
|
|
case FocusOut:
|
|
tmEvent->event.eventCode = event->xfocus.mode;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
|
|
default:
|
|
tmEvent->event.eventCode = 0;
|
|
tmEvent->event.modifiers = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static unsigned long GetTime(
|
|
XtTM tm,
|
|
XEvent *event)
|
|
{
|
|
switch (event->type) {
|
|
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
return event->xkey.time;
|
|
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
return event->xbutton.time;
|
|
|
|
default:
|
|
return tm->lastEventTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void HandleActions(
|
|
Widget w,
|
|
XEvent *event,
|
|
TMSimpleStateTree stateTree,
|
|
Widget accelWidget,
|
|
XtActionProc *procs,
|
|
ActionRec *actions)
|
|
{
|
|
ActionHook actionHookList;
|
|
Widget bindWidget;
|
|
|
|
bindWidget = accelWidget ? accelWidget : w;
|
|
if (accelWidget && !XtIsSensitive(accelWidget) &&
|
|
(event->type == KeyPress || event->type == KeyRelease ||
|
|
event->type == ButtonPress || event->type == ButtonRelease ||
|
|
event->type == MotionNotify || event->type == EnterNotify ||
|
|
event->type == LeaveNotify || event->type == FocusIn ||
|
|
event->type == FocusOut))
|
|
return;
|
|
|
|
actionHookList = XtWidgetToApplicationContext(w)->action_hook_list;
|
|
|
|
while (actions != NULL) {
|
|
/* perform any actions */
|
|
if (procs[actions->idx] != NULL) {
|
|
if (actionHookList) {
|
|
ActionHook hook;
|
|
ActionHook next_hook;
|
|
String procName =
|
|
XrmQuarkToString(stateTree->quarkTbl[actions->idx] );
|
|
|
|
for (hook = actionHookList; hook != NULL; ) {
|
|
/*
|
|
* Need to cache hook->next because the following action
|
|
* proc may free hook via XtRemoveActionHook making
|
|
* hook->next invalid upon return from the action proc.
|
|
*/
|
|
next_hook = hook->next;
|
|
(*hook->proc)(bindWidget,
|
|
hook->closure,
|
|
procName,
|
|
event,
|
|
actions->params,
|
|
&actions->num_params
|
|
);
|
|
hook = next_hook;
|
|
}
|
|
}
|
|
(*(procs[actions->idx]))
|
|
(bindWidget, event,
|
|
actions->params, &actions->num_params );
|
|
}
|
|
actions = actions->next;
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
unsigned int isCycleStart:1;
|
|
unsigned int isCycleEnd:1;
|
|
TMShortCard typeIndex;
|
|
TMShortCard modIndex;
|
|
}MatchPairRec, *MatchPair;
|
|
|
|
typedef struct TMContextRec{
|
|
TMShortCard numMatches;
|
|
TMShortCard maxMatches;
|
|
MatchPair matches;
|
|
}TMContextRec, *TMContext;
|
|
|
|
static TMContextRec contextCache[2];
|
|
|
|
#define GetContextPtr(tm) ((TMContext *)&(tm->current_state))
|
|
|
|
#define TM_CONTEXT_MATCHES_ALLOC 4
|
|
#define TM_CONTEXT_MATCHES_REALLOC 2
|
|
|
|
static void PushContext(
|
|
TMContext *contextPtr,
|
|
StatePtr newState)
|
|
{
|
|
TMContext context = *contextPtr;
|
|
|
|
LOCK_PROCESS;
|
|
if (context == NULL)
|
|
{
|
|
if (contextCache[0].numMatches == 0)
|
|
context = &contextCache[0];
|
|
else if (contextCache[1].numMatches == 0)
|
|
context = &contextCache[1];
|
|
if (!context)
|
|
{
|
|
context = XtNew(TMContextRec);
|
|
context->matches = NULL;
|
|
context->numMatches =
|
|
context->maxMatches = 0;
|
|
}
|
|
}
|
|
if (context->numMatches &&
|
|
context->matches[context->numMatches-1].isCycleEnd)
|
|
{
|
|
TMShortCard i;
|
|
for (i = 0;
|
|
i < context->numMatches &&
|
|
!(context->matches[i].isCycleStart);
|
|
i++){};
|
|
if (i < context->numMatches)
|
|
context->numMatches = i+1;
|
|
#ifdef DEBUG
|
|
else
|
|
XtWarning("pushing cycle end with no cycle start");
|
|
#endif /* DEBUG */
|
|
}
|
|
else
|
|
{
|
|
if (context->numMatches == context->maxMatches)
|
|
{
|
|
if (context->maxMatches == 0)
|
|
context->maxMatches += TM_CONTEXT_MATCHES_ALLOC;
|
|
else
|
|
context->maxMatches += TM_CONTEXT_MATCHES_REALLOC;
|
|
context->matches = (MatchPairRec *)
|
|
XtRealloc((char *)context->matches,
|
|
context->maxMatches * sizeof(MatchPairRec));
|
|
}
|
|
context->matches[context->numMatches].isCycleStart = newState->isCycleStart;
|
|
context->matches[context->numMatches].isCycleEnd = newState->isCycleEnd;
|
|
context->matches[context->numMatches].typeIndex = newState->typeIndex;
|
|
context->matches[context->numMatches++].modIndex = newState->modIndex;
|
|
*contextPtr = context;
|
|
}
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
static void FreeContext(
|
|
TMContext *contextPtr)
|
|
{
|
|
TMContext context = NULL;
|
|
|
|
LOCK_PROCESS;
|
|
|
|
if (&contextCache[0] == *contextPtr)
|
|
context = &contextCache[0];
|
|
else if (&contextCache[1] == *contextPtr)
|
|
context = &contextCache[1];
|
|
|
|
if (context)
|
|
context->numMatches = 0;
|
|
else if (*contextPtr)
|
|
{
|
|
if ((*contextPtr)->matches)
|
|
XtFree ((char *) ((*contextPtr)->matches));
|
|
XtFree((char *)*contextPtr);
|
|
}
|
|
|
|
*contextPtr = NULL;
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
static int MatchExact(
|
|
TMSimpleStateTree stateTree,
|
|
int startIndex,
|
|
TMShortCard typeIndex,
|
|
TMShortCard modIndex)
|
|
{
|
|
TMBranchHead branchHead = &(stateTree->branchHeadTbl[startIndex]);
|
|
int i;
|
|
|
|
for (i = startIndex;
|
|
i < (int)stateTree->numBranchHeads;
|
|
i++, branchHead++)
|
|
{
|
|
if ((branchHead->typeIndex == typeIndex) &&
|
|
(branchHead->modIndex == modIndex))
|
|
return i;
|
|
}
|
|
return (TM_NO_MATCH);
|
|
}
|
|
|
|
|
|
|
|
static void HandleSimpleState(
|
|
Widget w,
|
|
XtTM tmRecPtr,
|
|
TMEventRec *curEventPtr)
|
|
{
|
|
XtTranslations xlations = tmRecPtr->translations;
|
|
TMSimpleStateTree stateTree;
|
|
TMContext *contextPtr = GetContextPtr(tmRecPtr);
|
|
TMShortCard i;
|
|
ActionRec *actions = NULL;
|
|
Boolean matchExact = False;
|
|
Boolean match = False;
|
|
StatePtr complexMatchState = NULL;
|
|
int currIndex;
|
|
TMShortCard typeIndex = 0, modIndex = 0;
|
|
int matchTreeIndex = TM_NO_MATCH;
|
|
|
|
LOCK_PROCESS;
|
|
stateTree = (TMSimpleStateTree)xlations->stateTreeTbl[0];
|
|
|
|
for (i = 0;
|
|
((!match || !complexMatchState) && (i < xlations->numStateTrees));
|
|
i++){
|
|
stateTree = (TMSimpleStateTree)xlations->stateTreeTbl[i];
|
|
currIndex = -1;
|
|
/*
|
|
* don't process this tree if we're only looking for a
|
|
* complexMatchState and there are no complex states
|
|
*/
|
|
while (!(match && stateTree->isSimple) &&
|
|
((!match || !complexMatchState) && (currIndex != TM_NO_MATCH))) {
|
|
currIndex++;
|
|
if (matchExact)
|
|
currIndex = MatchExact(stateTree,currIndex,typeIndex,modIndex);
|
|
else
|
|
currIndex = MatchBranchHead(stateTree,currIndex,curEventPtr);
|
|
if (currIndex != TM_NO_MATCH) {
|
|
TMBranchHead branchHead;
|
|
StatePtr currState;
|
|
|
|
branchHead = &stateTree->branchHeadTbl[currIndex];
|
|
if (branchHead->isSimple)
|
|
currState = NULL;
|
|
else
|
|
currState = ((TMComplexStateTree)stateTree)
|
|
->complexBranchHeadTbl[TMBranchMore(branchHead)];
|
|
|
|
/*
|
|
* first check for a complete match
|
|
*/
|
|
if (!match) {
|
|
if (branchHead->hasActions) {
|
|
if (branchHead->isSimple) {
|
|
static ActionRec dummyAction;
|
|
|
|
dummyAction.idx = TMBranchMore(branchHead);
|
|
actions = &dummyAction;
|
|
}
|
|
else
|
|
actions = currState->actions;
|
|
tmRecPtr->lastEventTime =
|
|
GetTime(tmRecPtr, curEventPtr->xev);
|
|
FreeContext((TMContext
|
|
*)&tmRecPtr->current_state);
|
|
match = True;
|
|
matchTreeIndex = i;
|
|
}
|
|
/*
|
|
* if it doesn't have actions and
|
|
* it's bc mode then it's a potential match node that is
|
|
* used to match later sequences.
|
|
*/
|
|
if (!TMNewMatchSemantics() && !matchExact) {
|
|
matchExact = True;
|
|
typeIndex = branchHead->typeIndex;
|
|
modIndex = branchHead->modIndex;
|
|
}
|
|
}
|
|
/*
|
|
* check for it being an event sequence which can be
|
|
* a future match
|
|
*/
|
|
if (!branchHead->isSimple &&
|
|
!branchHead->hasActions &&
|
|
!complexMatchState)
|
|
complexMatchState = currState;
|
|
}
|
|
}
|
|
}
|
|
if (match)
|
|
{
|
|
TMBindData bindData = (TMBindData) tmRecPtr->proc_table;
|
|
XtActionProc *procs;
|
|
Widget accelWidget;
|
|
|
|
if (bindData->simple.isComplex) {
|
|
TMComplexBindProcs bindProcs =
|
|
TMGetComplexBindEntry(bindData, matchTreeIndex);
|
|
procs = bindProcs->procs;
|
|
accelWidget = bindProcs->widget;
|
|
}
|
|
else {
|
|
TMSimpleBindProcs bindProcs =
|
|
TMGetSimpleBindEntry(bindData, matchTreeIndex);
|
|
procs = bindProcs->procs;
|
|
accelWidget = NULL;
|
|
}
|
|
HandleActions
|
|
(w,
|
|
curEventPtr->xev,
|
|
(TMSimpleStateTree)xlations->stateTreeTbl[matchTreeIndex],
|
|
accelWidget,
|
|
procs,
|
|
actions);
|
|
}
|
|
if (complexMatchState)
|
|
PushContext(contextPtr, complexMatchState);
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
static int MatchComplexBranch(
|
|
TMComplexStateTree stateTree,
|
|
int startIndex,
|
|
TMContext context,
|
|
StatePtr *leafStateRtn)
|
|
{
|
|
TMShortCard i;
|
|
|
|
LOCK_PROCESS;
|
|
for (i = startIndex; i < stateTree->numComplexBranchHeads; i++)
|
|
{
|
|
StatePtr candState;
|
|
TMShortCard numMatches = context->numMatches;
|
|
MatchPair statMatch = context->matches;
|
|
|
|
for (candState = stateTree->complexBranchHeadTbl[i];
|
|
numMatches && candState;
|
|
numMatches--, statMatch++, candState = candState->nextLevel)
|
|
{
|
|
if ((statMatch->typeIndex != candState->typeIndex) ||
|
|
(statMatch->modIndex != candState->modIndex))
|
|
break;
|
|
}
|
|
if (numMatches == 0) {
|
|
*leafStateRtn = candState;
|
|
UNLOCK_PROCESS;
|
|
return i;
|
|
}
|
|
}
|
|
*leafStateRtn = NULL;
|
|
UNLOCK_PROCESS;
|
|
return (TM_NO_MATCH);
|
|
}
|
|
|
|
static StatePtr TryCurrentTree(
|
|
TMComplexStateTree *stateTreePtr,
|
|
XtTM tmRecPtr,
|
|
TMEventRec *curEventPtr)
|
|
{
|
|
StatePtr candState = NULL, matchState = NULL;
|
|
TMContext *contextPtr = GetContextPtr(tmRecPtr);
|
|
TMTypeMatch typeMatch;
|
|
TMModifierMatch modMatch;
|
|
int currIndex = -1;
|
|
|
|
/*
|
|
* we want the first sequence that both matches and has actions.
|
|
* we keep on looking till we find both
|
|
*/
|
|
LOCK_PROCESS;
|
|
while ((currIndex =
|
|
MatchComplexBranch(*stateTreePtr,
|
|
++currIndex,
|
|
(*contextPtr),
|
|
&candState))
|
|
!= TM_NO_MATCH) {
|
|
if (candState != NULL) {
|
|
typeMatch = TMGetTypeMatch(candState->typeIndex);
|
|
modMatch = TMGetModifierMatch(candState->modIndex);
|
|
|
|
/* does this state's index match? --> done */
|
|
if (MatchIncomingEvent(curEventPtr, typeMatch, modMatch))
|
|
{
|
|
if (candState->actions) {
|
|
UNLOCK_PROCESS;
|
|
return candState;
|
|
}
|
|
else
|
|
matchState = candState;
|
|
}
|
|
/* is this an event timer? */
|
|
if (typeMatch->eventType == _XtEventTimerEventType) {
|
|
StatePtr nextState = candState->nextLevel;
|
|
|
|
/* does the succeeding state match? */
|
|
if (nextState != NULL) {
|
|
TMTypeMatch nextTypeMatch;
|
|
TMModifierMatch nextModMatch;
|
|
|
|
nextTypeMatch = TMGetTypeMatch(nextState->typeIndex);
|
|
nextModMatch = TMGetModifierMatch(nextState->modIndex);
|
|
|
|
/* is it within the timeout? */
|
|
if (MatchIncomingEvent(curEventPtr,
|
|
nextTypeMatch,
|
|
nextModMatch)) {
|
|
XEvent *xev = curEventPtr->xev;
|
|
unsigned long time = GetTime(tmRecPtr, xev);
|
|
XtPerDisplay pd = _XtGetPerDisplay(xev->xany.display);
|
|
unsigned long delta = pd->multi_click_time;
|
|
|
|
if ((tmRecPtr->lastEventTime + delta) >= time) {
|
|
if (nextState->actions) {
|
|
UNLOCK_PROCESS;
|
|
return candState;
|
|
}
|
|
else
|
|
matchState = candState;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
UNLOCK_PROCESS;
|
|
return matchState;
|
|
}
|
|
|
|
static void HandleComplexState(
|
|
Widget w,
|
|
XtTM tmRecPtr,
|
|
TMEventRec *curEventPtr)
|
|
{
|
|
XtTranslations xlations = tmRecPtr->translations;
|
|
TMContext *contextPtr = GetContextPtr(tmRecPtr);
|
|
TMShortCard i, matchTreeIndex = 0;
|
|
StatePtr matchState = NULL, candState;
|
|
TMComplexStateTree *stateTreePtr =
|
|
(TMComplexStateTree *)&xlations->stateTreeTbl[0];
|
|
|
|
LOCK_PROCESS;
|
|
for (i = 0;
|
|
i < xlations->numStateTrees;
|
|
i++, stateTreePtr++) {
|
|
/*
|
|
* some compilers sign extend Boolean bit fields so test for
|
|
* false |||
|
|
*/
|
|
if (((*stateTreePtr)->isSimple == False) &&
|
|
(candState = TryCurrentTree(stateTreePtr,
|
|
tmRecPtr,
|
|
curEventPtr))) {
|
|
if (!matchState || candState->actions) {
|
|
matchTreeIndex = i;
|
|
matchState = candState;
|
|
if (candState->actions)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (matchState == NULL){
|
|
/* couldn't find it... */
|
|
if (!Ignore(curEventPtr))
|
|
{
|
|
FreeContext(contextPtr);
|
|
HandleSimpleState(w, tmRecPtr, curEventPtr);
|
|
}
|
|
}
|
|
else {
|
|
TMBindData bindData = (TMBindData) tmRecPtr->proc_table;
|
|
XtActionProc *procs;
|
|
Widget accelWidget;
|
|
TMTypeMatch typeMatch;
|
|
|
|
typeMatch = TMGetTypeMatch(matchState->typeIndex);
|
|
|
|
PushContext(contextPtr, matchState);
|
|
if (typeMatch->eventType == _XtEventTimerEventType) {
|
|
matchState = matchState->nextLevel;
|
|
PushContext(contextPtr, matchState);
|
|
}
|
|
tmRecPtr->lastEventTime = GetTime (tmRecPtr, curEventPtr->xev);
|
|
|
|
if (bindData->simple.isComplex) {
|
|
TMComplexBindProcs bindProcs =
|
|
TMGetComplexBindEntry(bindData, matchTreeIndex);
|
|
procs = bindProcs->procs;
|
|
accelWidget = bindProcs->widget;
|
|
}
|
|
else {
|
|
TMSimpleBindProcs bindProcs =
|
|
TMGetSimpleBindEntry(bindData, matchTreeIndex);
|
|
procs = bindProcs->procs;
|
|
accelWidget = NULL;
|
|
}
|
|
HandleActions(w,
|
|
curEventPtr->xev,
|
|
(TMSimpleStateTree)
|
|
xlations->stateTreeTbl[matchTreeIndex],
|
|
accelWidget,
|
|
procs,
|
|
matchState->actions);
|
|
}
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
|
|
void _XtTranslateEvent (
|
|
Widget w,
|
|
XEvent * event)
|
|
{
|
|
XtTM tmRecPtr = &w->core.tm;
|
|
TMEventRec curEvent;
|
|
StatePtr current_state = tmRecPtr->current_state;
|
|
|
|
XEventToTMEvent (event, &curEvent);
|
|
|
|
if (! tmRecPtr->translations) {
|
|
XtAppWarningMsg(XtWidgetToApplicationContext(w),
|
|
XtNtranslationError,"nullTable",XtCXtToolkitError,
|
|
"Can't translate event through NULL table",
|
|
(String *)NULL, (Cardinal *)NULL);
|
|
return ;
|
|
}
|
|
if (current_state == NULL)
|
|
HandleSimpleState(w, tmRecPtr, &curEvent);
|
|
else
|
|
HandleComplexState(w, tmRecPtr, &curEvent);
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
static StatePtr NewState(
|
|
TMParseStateTree stateTree,
|
|
TMShortCard typeIndex,
|
|
TMShortCard modIndex)
|
|
{
|
|
StatePtr state = XtNew(StateRec);
|
|
|
|
#ifdef TRACE_TM
|
|
LOCK_PROCESS;
|
|
_XtGlobalTM.numComplexStates++;
|
|
UNLOCK_PROCESS;
|
|
#endif /* TRACE_TM */
|
|
state->typeIndex = typeIndex;
|
|
state->modIndex = modIndex;
|
|
state->nextLevel = NULL;
|
|
state->actions = NULL;
|
|
state->isCycleStart = state->isCycleEnd = False;
|
|
return state;
|
|
}
|
|
|
|
/*
|
|
* This routine is an iterator for state trees. If the func returns
|
|
* true then iteration is over.
|
|
*/
|
|
void _XtTraverseStateTree(
|
|
TMStateTree tree,
|
|
_XtTraversalProc func,
|
|
XtPointer data)
|
|
{
|
|
TMComplexStateTree stateTree = (TMComplexStateTree)tree;
|
|
TMBranchHead currBH;
|
|
TMShortCard i;
|
|
StateRec dummyStateRec, *dummyState = &dummyStateRec;
|
|
ActionRec dummyActionRec, *dummyAction = &dummyActionRec;
|
|
Boolean firstSimple = True;
|
|
StatePtr currState;
|
|
|
|
/* first traverse the complex states */
|
|
if (stateTree->isSimple == False)
|
|
for (i = 0; i < stateTree->numComplexBranchHeads; i++) {
|
|
currState = stateTree->complexBranchHeadTbl[i];
|
|
for (; currState; currState = currState->nextLevel) {
|
|
if (func(currState, data))
|
|
return;
|
|
if (currState->isCycleEnd)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* now traverse the simple ones */
|
|
for (i = 0, currBH = stateTree->branchHeadTbl;
|
|
i < stateTree->numBranchHeads;
|
|
i++, currBH++)
|
|
{
|
|
if (currBH->isSimple && currBH->hasActions)
|
|
{
|
|
if (firstSimple)
|
|
{
|
|
XtBZero((char *) dummyState, sizeof(StateRec));
|
|
XtBZero((char *) dummyAction, sizeof(ActionRec));
|
|
dummyState->actions = dummyAction;
|
|
firstSimple = False;
|
|
}
|
|
dummyState->typeIndex = currBH->typeIndex;
|
|
dummyState->modIndex = currBH->modIndex;
|
|
dummyAction->idx = currBH->more;
|
|
if (func(dummyState, data))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static EventMask EventToMask(
|
|
TMTypeMatch typeMatch,
|
|
TMModifierMatch modMatch)
|
|
{
|
|
EventMask returnMask;
|
|
unsigned long eventType = typeMatch->eventType;
|
|
|
|
if (eventType == MotionNotify) {
|
|
Modifiers modifierMask = modMatch->modifierMask;
|
|
Modifiers tempMask;
|
|
|
|
returnMask = 0;
|
|
if (modifierMask == 0) {
|
|
if (modMatch->modifiers == AnyButtonMask)
|
|
return ButtonMotionMask;
|
|
else
|
|
return PointerMotionMask;
|
|
}
|
|
tempMask = modifierMask &
|
|
(Button1Mask | Button2Mask | Button3Mask
|
|
| Button4Mask | Button5Mask);
|
|
if (tempMask == 0)
|
|
return PointerMotionMask;
|
|
if (tempMask & Button1Mask)
|
|
returnMask |= Button1MotionMask;
|
|
if (tempMask & Button2Mask)
|
|
returnMask |= Button2MotionMask;
|
|
if (tempMask & Button3Mask)
|
|
returnMask |= Button3MotionMask;
|
|
if (tempMask & Button4Mask)
|
|
returnMask |= Button4MotionMask;
|
|
if (tempMask & Button5Mask)
|
|
returnMask |= Button5MotionMask;
|
|
return returnMask;
|
|
}
|
|
returnMask = _XtConvertTypeToMask(eventType);
|
|
if (returnMask == (StructureNotifyMask|SubstructureNotifyMask))
|
|
returnMask = StructureNotifyMask;
|
|
return returnMask;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void DispatchMappingNotify(
|
|
Widget widget, /* will be NULL from _RefreshMapping */
|
|
XtPointer closure, /* real Widget */
|
|
XtPointer call_data) /* XEvent* */
|
|
{
|
|
_XtTranslateEvent( (Widget)closure, (XEvent*)call_data);
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
static void RemoveFromMappingCallbacks(
|
|
Widget widget,
|
|
XtPointer closure, /* target widget */
|
|
XtPointer call_data)
|
|
{
|
|
_XtRemoveCallback( &_XtGetPerDisplay(XtDisplay(widget))->mapping_callbacks,
|
|
DispatchMappingNotify,
|
|
closure
|
|
);
|
|
}
|
|
|
|
static Boolean AggregateEventMask(
|
|
StatePtr state,
|
|
XtPointer data)
|
|
{
|
|
LOCK_PROCESS;
|
|
*((EventMask *)data) |= EventToMask(TMGetTypeMatch(state->typeIndex),
|
|
TMGetModifierMatch(state->modIndex));
|
|
UNLOCK_PROCESS;
|
|
return False;
|
|
}
|
|
|
|
void _XtInstallTranslations(
|
|
Widget widget)
|
|
{
|
|
XtTranslations xlations;
|
|
Cardinal i;
|
|
TMStateTree stateTree;
|
|
Boolean mappingNotifyInterest = False;
|
|
|
|
xlations = widget->core.tm.translations;
|
|
if (xlations == NULL) return;
|
|
|
|
/*
|
|
* check for somebody stuffing the translations directly into the
|
|
* instance structure. We will end up being called again out of
|
|
* ComposeTranslations but we *should* have bindings by then
|
|
*/
|
|
if (widget->core.tm.proc_table == NULL) {
|
|
_XtMergeTranslations(widget, NULL, XtTableReplace);
|
|
/*
|
|
* if we're realized then we'll be called out of
|
|
* ComposeTranslations
|
|
*/
|
|
if (XtIsRealized(widget))
|
|
return;
|
|
}
|
|
|
|
xlations->eventMask = 0;
|
|
for (i = 0;
|
|
i < xlations->numStateTrees;
|
|
i++)
|
|
{
|
|
stateTree = xlations->stateTreeTbl[i];
|
|
_XtTraverseStateTree(stateTree,
|
|
AggregateEventMask,
|
|
(XtPointer)&xlations->eventMask);
|
|
mappingNotifyInterest |= stateTree->simple.mappingNotifyInterest;
|
|
}
|
|
/* double click needs to make sure that you have selected on both
|
|
button down and up. */
|
|
|
|
if (xlations->eventMask & ButtonPressMask)
|
|
xlations->eventMask |= ButtonReleaseMask;
|
|
if (xlations->eventMask & ButtonReleaseMask)
|
|
xlations->eventMask |= ButtonPressMask;
|
|
|
|
if (mappingNotifyInterest) {
|
|
XtPerDisplay pd = _XtGetPerDisplay(XtDisplay(widget));
|
|
if (pd->mapping_callbacks)
|
|
_XtAddCallbackOnce(&(pd->mapping_callbacks),
|
|
DispatchMappingNotify,
|
|
(XtPointer)widget);
|
|
else
|
|
_XtAddCallback(&(pd->mapping_callbacks),
|
|
DispatchMappingNotify,
|
|
(XtPointer)widget);
|
|
|
|
if (widget->core.destroy_callbacks != NULL)
|
|
_XtAddCallbackOnce( (InternalCallbackList *)
|
|
&widget->core.destroy_callbacks,
|
|
RemoveFromMappingCallbacks,
|
|
(XtPointer)widget
|
|
);
|
|
else
|
|
_XtAddCallback((InternalCallbackList *)
|
|
&widget->core.destroy_callbacks,
|
|
RemoveFromMappingCallbacks,
|
|
(XtPointer)widget
|
|
);
|
|
}
|
|
_XtBindActions(widget, (XtTM)&widget->core.tm);
|
|
_XtRegisterGrabs(widget);
|
|
}
|
|
|
|
void _XtRemoveTranslations(
|
|
Widget widget)
|
|
{
|
|
Cardinal i;
|
|
TMSimpleStateTree stateTree;
|
|
Boolean mappingNotifyInterest = False;
|
|
XtTranslations xlations = widget->core.tm.translations;
|
|
|
|
if (xlations == NULL)
|
|
return;
|
|
|
|
for (i = 0;
|
|
i < xlations->numStateTrees;
|
|
i++)
|
|
{
|
|
stateTree = (TMSimpleStateTree)xlations->stateTreeTbl[i];
|
|
mappingNotifyInterest |= stateTree->mappingNotifyInterest;
|
|
}
|
|
if (mappingNotifyInterest)
|
|
RemoveFromMappingCallbacks(widget, (XtPointer)widget, NULL);
|
|
}
|
|
|
|
static void _XtUninstallTranslations(
|
|
Widget widget)
|
|
{
|
|
XtTranslations xlations = widget->core.tm.translations;
|
|
|
|
_XtUnbindActions(widget,
|
|
xlations,
|
|
(TMBindData)widget->core.tm.proc_table);
|
|
_XtRemoveTranslations(widget);
|
|
widget->core.tm.translations = NULL;
|
|
FreeContext((TMContext *)&widget->core.tm.current_state);
|
|
}
|
|
|
|
void _XtDestroyTMData(
|
|
Widget widget)
|
|
{
|
|
TMComplexBindData cBindData;
|
|
|
|
_XtUninstallTranslations(widget);
|
|
|
|
if ((cBindData = (TMComplexBindData)widget->core.tm.proc_table)) {
|
|
if (cBindData->isComplex) {
|
|
ATranslations aXlations, nXlations;
|
|
|
|
nXlations = (ATranslations) cBindData->accel_context;
|
|
while (nXlations){
|
|
aXlations = nXlations;
|
|
nXlations = nXlations->next;
|
|
XtFree((char *)aXlations);
|
|
}
|
|
}
|
|
XtFree((char *)cBindData);
|
|
}
|
|
}
|
|
|
|
/*** Public procedures ***/
|
|
|
|
|
|
void XtUninstallTranslations(
|
|
Widget widget)
|
|
{
|
|
EventMask oldMask;
|
|
Widget hookobj;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
if (! widget->core.tm.translations) {
|
|
UNLOCK_APP(app);
|
|
return;
|
|
}
|
|
oldMask = widget->core.tm.translations->eventMask;
|
|
_XtUninstallTranslations(widget);
|
|
if (XtIsRealized(widget) && oldMask)
|
|
XSelectInput(XtDisplay(widget), XtWindow(widget),
|
|
XtBuildEventMask(widget));
|
|
hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
|
|
if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
|
|
XtChangeHookDataRec call_data;
|
|
|
|
call_data.type = XtHuninstallTranslations;
|
|
call_data.widget = widget;
|
|
XtCallCallbackList(hookobj,
|
|
((HookObject)hookobj)->hooks.changehook_callbacks,
|
|
(XtPointer)&call_data);
|
|
}
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
XtTranslations _XtCreateXlations(
|
|
TMStateTree *stateTrees,
|
|
TMShortCard numStateTrees,
|
|
XtTranslations first,
|
|
XtTranslations second)
|
|
{
|
|
XtTranslations xlations;
|
|
TMShortCard i;
|
|
|
|
xlations = (XtTranslations)
|
|
__XtMalloc(sizeof(TranslationData) +
|
|
(numStateTrees-1) * sizeof(TMStateTree));
|
|
#ifdef TRACE_TM
|
|
LOCK_PROCESS;
|
|
if (_XtGlobalTM.numTms == _XtGlobalTM.tmTblSize) {
|
|
_XtGlobalTM.tmTblSize += 16;
|
|
_XtGlobalTM.tmTbl = (XtTranslations *)
|
|
XtRealloc((char *)_XtGlobalTM.tmTbl,
|
|
_XtGlobalTM.tmTblSize * sizeof(XtTranslations));
|
|
}
|
|
_XtGlobalTM.tmTbl[_XtGlobalTM.numTms++] = xlations;
|
|
UNLOCK_PROCESS;
|
|
#endif /* TRACE_TM */
|
|
|
|
xlations->composers[0] = first;
|
|
xlations->composers[1] = second;
|
|
xlations->hasBindings = False;
|
|
xlations->operation = XtTableReplace;
|
|
|
|
for (i = 0;i < numStateTrees; i++)
|
|
{
|
|
xlations->stateTreeTbl[i] = (TMStateTree) stateTrees[i];
|
|
stateTrees[i]->simple.refCount++;
|
|
}
|
|
xlations->numStateTrees = numStateTrees;
|
|
xlations->eventMask = 0;
|
|
return xlations;
|
|
}
|
|
|
|
TMStateTree _XtParseTreeToStateTree(
|
|
TMParseStateTree parseTree)
|
|
{
|
|
TMSimpleStateTree simpleTree;
|
|
unsigned int tableSize;
|
|
|
|
if (parseTree->numComplexBranchHeads) {
|
|
TMComplexStateTree complexTree;
|
|
|
|
complexTree = XtNew(TMComplexStateTreeRec);
|
|
complexTree->isSimple = False;
|
|
tableSize = parseTree->numComplexBranchHeads * sizeof(StatePtr);
|
|
complexTree->complexBranchHeadTbl = (StatePtr *)
|
|
__XtMalloc(tableSize);
|
|
XtMemmove(complexTree->complexBranchHeadTbl,
|
|
parseTree->complexBranchHeadTbl, tableSize);
|
|
complexTree->numComplexBranchHeads =
|
|
parseTree->numComplexBranchHeads;
|
|
simpleTree = (TMSimpleStateTree)complexTree;
|
|
}
|
|
else {
|
|
simpleTree = XtNew(TMSimpleStateTreeRec);
|
|
simpleTree->isSimple = True;
|
|
}
|
|
simpleTree->isAccelerator = parseTree->isAccelerator;
|
|
simpleTree->refCount = 0;
|
|
simpleTree->mappingNotifyInterest = parseTree->mappingNotifyInterest;
|
|
|
|
tableSize = parseTree->numBranchHeads * sizeof(TMBranchHeadRec);
|
|
simpleTree->branchHeadTbl = (TMBranchHead)
|
|
__XtMalloc(tableSize);
|
|
XtMemmove(simpleTree->branchHeadTbl, parseTree->branchHeadTbl, tableSize);
|
|
simpleTree->numBranchHeads = parseTree->numBranchHeads;
|
|
|
|
tableSize = parseTree->numQuarks * sizeof(XrmQuark);
|
|
simpleTree->quarkTbl = (XrmQuark *) __XtMalloc(tableSize);
|
|
XtMemmove(simpleTree->quarkTbl, parseTree->quarkTbl, tableSize);
|
|
simpleTree->numQuarks = parseTree->numQuarks;
|
|
|
|
return (TMStateTree)simpleTree;
|
|
}
|
|
|
|
static void FreeActions(
|
|
ActionPtr actions)
|
|
{
|
|
ActionPtr action;
|
|
TMShortCard i;
|
|
for (action = actions; action;) {
|
|
ActionPtr nextAction = action->next;
|
|
for (i = action->num_params; i;) {
|
|
XtFree( action->params[--i] );
|
|
}
|
|
XtFree( (char*)action->params );
|
|
XtFree((char*) action);
|
|
action = nextAction;
|
|
}
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void AmbigActions(
|
|
EventSeqPtr initialEvent,
|
|
StatePtr *state,
|
|
TMParseStateTree stateTree)
|
|
{
|
|
String params[3];
|
|
Cardinal numParams = 0;
|
|
|
|
params[numParams++] = _XtPrintEventSeq(initialEvent, NULL);
|
|
params[numParams++] = _XtPrintActions((*state)->actions,
|
|
stateTree->quarkTbl);
|
|
XtWarningMsg (XtNtranslationError,"oldActions",XtCXtToolkitError,
|
|
"Previous entry was: %s %s", params, &numParams);
|
|
XtFree((char *)params[0]);
|
|
XtFree((char *)params[1]);
|
|
numParams = 0;
|
|
params[numParams++] = _XtPrintActions(initialEvent->actions,
|
|
stateTree->quarkTbl);
|
|
XtWarningMsg (XtNtranslationError,"newActions",XtCXtToolkitError,
|
|
"New actions are:%s", params, &numParams);
|
|
XtFree((char *)params[0]);
|
|
XtWarningMsg (XtNtranslationError,"ambiguousActions",
|
|
XtCXtToolkitError,
|
|
"Overriding earlier translation manager actions.",
|
|
(String *)NULL, (Cardinal *)NULL);
|
|
|
|
FreeActions((*state)->actions);
|
|
(*state)->actions = NULL;
|
|
}
|
|
|
|
|
|
void _XtAddEventSeqToStateTree(
|
|
EventSeqPtr eventSeq,
|
|
TMParseStateTree stateTree)
|
|
{
|
|
StatePtr *state;
|
|
EventSeqPtr initialEvent = eventSeq;
|
|
TMBranchHead branchHead;
|
|
TMShortCard idx, modIndex, typeIndex;
|
|
|
|
if (eventSeq == NULL) return;
|
|
|
|
/* note that all states in the event seq passed in start out null */
|
|
/* we fill them in with the matching state as we traverse the list */
|
|
|
|
/*
|
|
* We need to free the parser data structures !!!
|
|
*/
|
|
|
|
typeIndex = _XtGetTypeIndex(&eventSeq->event);
|
|
modIndex = _XtGetModifierIndex(&eventSeq->event);
|
|
idx = GetBranchHead(stateTree, typeIndex, modIndex, False);
|
|
branchHead = &stateTree->branchHeadTbl[idx];
|
|
|
|
/*
|
|
* Need to check for pre-existing actions with same lhs |||
|
|
*/
|
|
|
|
/*
|
|
* Check for optimized case. Don't assume that the eventSeq has actions.
|
|
*/
|
|
if (!eventSeq->next &&
|
|
eventSeq->actions &&
|
|
!eventSeq->actions->next &&
|
|
!eventSeq->actions->num_params)
|
|
{
|
|
if (eventSeq->event.eventType == MappingNotify)
|
|
stateTree->mappingNotifyInterest = True;
|
|
branchHead->hasActions = True;
|
|
branchHead->more = eventSeq->actions->idx;
|
|
FreeActions(eventSeq->actions);
|
|
eventSeq->actions = NULL;
|
|
return;
|
|
}
|
|
|
|
branchHead->isSimple = False;
|
|
if (!eventSeq->next)
|
|
branchHead->hasActions = True;
|
|
branchHead->more = GetComplexBranchIndex(stateTree, typeIndex, modIndex);
|
|
state = &stateTree->complexBranchHeadTbl[TMBranchMore(branchHead)];
|
|
|
|
for (;;) {
|
|
*state = NewState(stateTree, typeIndex, modIndex);
|
|
|
|
if (eventSeq->event.eventType == MappingNotify)
|
|
stateTree->mappingNotifyInterest = True;
|
|
|
|
/* *state now points at state record matching event */
|
|
eventSeq->state = *state;
|
|
|
|
if (eventSeq->actions != NULL) {
|
|
if ((*state)->actions != NULL)
|
|
AmbigActions(initialEvent, state, stateTree);
|
|
(*state)->actions = eventSeq->actions;
|
|
#ifdef TRACE_TM
|
|
LOCK_PROCESS
|
|
_XtGlobalTM.numComplexActions++;
|
|
UNLOCK_PROCESS;
|
|
#endif /* TRACE_TM */
|
|
}
|
|
|
|
if (((eventSeq = eventSeq->next) == NULL) || (eventSeq->state))
|
|
break;
|
|
|
|
state = &(*state)->nextLevel;
|
|
typeIndex = _XtGetTypeIndex(&eventSeq->event);
|
|
modIndex = _XtGetModifierIndex(&eventSeq->event);
|
|
LOCK_PROCESS;
|
|
if (!TMNewMatchSemantics()) {
|
|
/*
|
|
* force a potential empty entry into the branch head
|
|
* table in order to emulate old matching behavior
|
|
*/
|
|
(void) GetBranchHead(stateTree, typeIndex, modIndex, True);
|
|
}
|
|
UNLOCK_PROCESS;
|
|
}
|
|
|
|
if (eventSeq && eventSeq->state) {
|
|
/* we've been here before... must be a cycle in the event seq. */
|
|
branchHead->hasCycles = True;
|
|
(*state)->nextLevel = eventSeq->state;
|
|
eventSeq->state->isCycleStart = True;
|
|
(*state)->isCycleEnd = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Internal Converter for merging. Old and New must both be valid xlations
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
Boolean _XtCvtMergeTranslations(
|
|
Display *dpy,
|
|
XrmValuePtr args,
|
|
Cardinal *num_args,
|
|
XrmValuePtr from,
|
|
XrmValuePtr to,
|
|
XtPointer *closure_ret)
|
|
{
|
|
XtTranslations first, second, xlations;
|
|
TMStateTree *stateTrees, stackStateTrees[16];
|
|
TMShortCard numStateTrees, i;
|
|
|
|
if (*num_args != 0)
|
|
XtWarningMsg("invalidParameters","mergeTranslations",XtCXtToolkitError,
|
|
"MergeTM to TranslationTable needs no extra arguments",
|
|
(String *)NULL, (Cardinal *)NULL);
|
|
|
|
if (to->addr != NULL && to->size < sizeof(XtTranslations)) {
|
|
to->size = sizeof(XtTranslations);
|
|
return False;
|
|
}
|
|
|
|
first = ((TMConvertRec*)from->addr)->old;
|
|
second = ((TMConvertRec*)from->addr)->new;
|
|
|
|
numStateTrees = first->numStateTrees + second->numStateTrees;
|
|
|
|
stateTrees = (TMStateTree *)
|
|
XtStackAlloc(numStateTrees * sizeof(TMStateTree), stackStateTrees);
|
|
|
|
for (i = 0; i < first->numStateTrees; i++)
|
|
stateTrees[i] = first->stateTreeTbl[i];
|
|
for (i = 0; i < second->numStateTrees; i++)
|
|
stateTrees[i + first->numStateTrees] = second->stateTreeTbl[i];
|
|
|
|
xlations = _XtCreateXlations(stateTrees, numStateTrees, first, second);
|
|
|
|
if (to->addr != NULL) {
|
|
*(XtTranslations*)to->addr = xlations;
|
|
}
|
|
else {
|
|
static XtTranslations staticStateTable;
|
|
staticStateTable = xlations;
|
|
to->addr= (XPointer)&staticStateTable;
|
|
to->size = sizeof(XtTranslations);
|
|
}
|
|
|
|
XtStackFree((XtPointer)stateTrees, (XtPointer)stackStateTrees);
|
|
return True;
|
|
}
|
|
|
|
|
|
static XtTranslations MergeThem(
|
|
Widget dest,
|
|
XtTranslations first,
|
|
XtTranslations second)
|
|
{
|
|
XtCacheRef cache_ref;
|
|
static XrmQuark from_type = NULLQUARK, to_type;
|
|
XrmValue from, to;
|
|
TMConvertRec convert_rec;
|
|
XtTranslations newTable;
|
|
|
|
LOCK_PROCESS;
|
|
if (from_type == NULLQUARK) {
|
|
from_type = XrmPermStringToQuark(_XtRStateTablePair);
|
|
to_type = XrmPermStringToQuark(XtRTranslationTable);
|
|
}
|
|
UNLOCK_PROCESS;
|
|
from.addr = (XPointer)&convert_rec;
|
|
from.size = sizeof(TMConvertRec);
|
|
to.addr = (XPointer)&newTable;
|
|
to.size = sizeof(XtTranslations);
|
|
convert_rec.old = first;
|
|
convert_rec.new = second;
|
|
|
|
LOCK_PROCESS;
|
|
if (! _XtConvert(dest, from_type, &from, to_type, &to, &cache_ref)) {
|
|
UNLOCK_PROCESS;
|
|
return NULL;
|
|
}
|
|
UNLOCK_PROCESS;
|
|
|
|
#ifndef REFCNT_TRANSLATIONS
|
|
|
|
if (cache_ref)
|
|
XtAddCallback(dest, XtNdestroyCallback,
|
|
XtCallbackReleaseCacheRef, (XtPointer)cache_ref);
|
|
|
|
#endif
|
|
|
|
return newTable;
|
|
}
|
|
|
|
/*
|
|
* Unmerge will recursively traverse the xlation compose tree and
|
|
* generate a new xlation that is the result of all instances of
|
|
* xlations being removed. It currently doesn't differentiate between
|
|
* the potential that an xlation will be both an accelerator and
|
|
* normal. This is not supported by the spec anyway.
|
|
*/
|
|
static XtTranslations UnmergeTranslations(
|
|
Widget widget,
|
|
XtTranslations xlations,
|
|
XtTranslations unmergeXlations,
|
|
TMShortCard currIndex,
|
|
TMComplexBindProcs oldBindings,
|
|
TMShortCard numOldBindings,
|
|
TMComplexBindProcs newBindings,
|
|
TMShortCard *numNewBindingsRtn)
|
|
|
|
{
|
|
XtTranslations first, second, result;
|
|
|
|
if (!xlations || (xlations == unmergeXlations))
|
|
return NULL;
|
|
|
|
if (xlations->composers[0]) {
|
|
first = UnmergeTranslations(widget, xlations->composers[0],
|
|
unmergeXlations, currIndex,
|
|
oldBindings, numOldBindings,
|
|
newBindings, numNewBindingsRtn);
|
|
}
|
|
else
|
|
first = NULL;
|
|
|
|
if (xlations->composers[1]) {
|
|
second = UnmergeTranslations(widget, xlations->composers[1],
|
|
unmergeXlations,
|
|
currIndex +
|
|
xlations->composers[0]->numStateTrees,
|
|
oldBindings, numOldBindings,
|
|
newBindings, numNewBindingsRtn);
|
|
}
|
|
else
|
|
second = NULL;
|
|
|
|
if (first || second) {
|
|
if (first && second) {
|
|
if ((first != xlations->composers[0]) ||
|
|
(second != xlations->composers[1]))
|
|
result = MergeThem(widget, first, second);
|
|
else result = xlations;
|
|
}
|
|
else {
|
|
if (first)
|
|
result = first;
|
|
else
|
|
result = second;
|
|
}
|
|
} else { /* only update for leaf nodes */
|
|
if (numOldBindings) {
|
|
Cardinal i;
|
|
for (i = 0; i < xlations->numStateTrees; i++) {
|
|
if (xlations->stateTreeTbl[i]->simple.isAccelerator)
|
|
newBindings[*numNewBindingsRtn] =
|
|
oldBindings[currIndex + i];
|
|
(*numNewBindingsRtn)++;
|
|
}
|
|
}
|
|
result = xlations;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
typedef struct {
|
|
XtTranslations xlations;
|
|
TMComplexBindProcs bindings;
|
|
}MergeBindRec, *MergeBind;
|
|
|
|
static XtTranslations MergeTranslations(
|
|
Widget widget,
|
|
XtTranslations oldXlations,
|
|
XtTranslations newXlations,
|
|
_XtTranslateOp operation,
|
|
Widget source,
|
|
TMComplexBindProcs oldBindings,
|
|
TMComplexBindProcs newBindings,
|
|
TMShortCard *numNewRtn)
|
|
{
|
|
XtTranslations newTable = NULL, xlations;
|
|
TMComplexBindProcs bindings;
|
|
TMShortCard i, j;
|
|
TMStateTree *treePtr;
|
|
TMShortCard numNew = *numNewRtn;
|
|
MergeBindRec bindPair[2];
|
|
|
|
/* If the new translation has an accelerator context then pull it
|
|
* off and pass it and the real xlations in to the caching merge
|
|
* routine.
|
|
*/
|
|
if (newXlations->hasBindings) {
|
|
xlations = ((ATranslations) newXlations)->xlations;
|
|
bindings = (TMComplexBindProcs)
|
|
&((ATranslations) newXlations)->bindTbl[0];
|
|
}
|
|
else {
|
|
xlations = newXlations;
|
|
bindings = NULL;
|
|
}
|
|
switch(operation) {
|
|
case XtTableReplace:
|
|
newTable = bindPair[0].xlations = xlations;
|
|
bindPair[0].bindings = bindings;
|
|
bindPair[1].xlations = NULL;
|
|
bindPair[1].bindings = NULL;
|
|
break;
|
|
case XtTableAugment:
|
|
bindPair[0].xlations = oldXlations;
|
|
bindPair[0].bindings = oldBindings;
|
|
bindPair[1].xlations = xlations;
|
|
bindPair[1].bindings = bindings;
|
|
newTable = NULL;
|
|
break;
|
|
case XtTableOverride:
|
|
bindPair[0].xlations = xlations;
|
|
bindPair[0].bindings = bindings;
|
|
bindPair[1].xlations = oldXlations;
|
|
bindPair[1].bindings = oldBindings;
|
|
newTable = NULL;
|
|
break;
|
|
}
|
|
if (!newTable)
|
|
newTable = MergeThem(widget, bindPair[0].xlations, bindPair[1].xlations);
|
|
|
|
for (i = 0, numNew = 0; i < 2; i++) {
|
|
if (bindPair[i].xlations)
|
|
for (j = 0; j < bindPair[i].xlations->numStateTrees; j++, numNew++) {
|
|
if (bindPair[i].xlations->stateTreeTbl[j]->simple.isAccelerator) {
|
|
if (bindPair[i].bindings)
|
|
newBindings[numNew] = bindPair[i].bindings[j];
|
|
else {
|
|
newBindings[numNew].widget = source;
|
|
newBindings[numNew].aXlations =
|
|
bindPair[i].xlations;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*numNewRtn = numNew;
|
|
treePtr = &newTable->stateTreeTbl[0];
|
|
for (i = 0; i < newTable->numStateTrees; i++, treePtr++)
|
|
(*treePtr)->simple.refCount++;
|
|
return newTable;
|
|
}
|
|
|
|
static TMBindData MakeBindData(
|
|
TMComplexBindProcs bindings,
|
|
TMShortCard numBindings,
|
|
TMBindData oldBindData)
|
|
{
|
|
TMLongCard bytes;
|
|
TMShortCard i;
|
|
Boolean isComplex;
|
|
TMBindData bindData;
|
|
|
|
if (numBindings == 0)
|
|
return NULL;
|
|
for (i = 0; i < numBindings; i++)
|
|
if (bindings[i].widget)
|
|
break;
|
|
isComplex = (i < numBindings);
|
|
if (isComplex)
|
|
bytes = (sizeof(TMComplexBindDataRec) +
|
|
((numBindings - 1) *
|
|
sizeof(TMComplexBindProcsRec)));
|
|
else
|
|
bytes = (sizeof(TMSimpleBindDataRec) +
|
|
((numBindings - 1) *
|
|
sizeof(TMSimpleBindProcsRec)));
|
|
|
|
bindData = (TMBindData) __XtCalloc(sizeof(char), bytes);
|
|
bindData->simple.isComplex = isComplex;
|
|
if (isComplex) {
|
|
TMComplexBindData cBindData = (TMComplexBindData)bindData;
|
|
/*
|
|
* If there were any accelerator contexts in the old bindData
|
|
* then propagate them to the new one.
|
|
*/
|
|
if (oldBindData && oldBindData->simple.isComplex)
|
|
cBindData->accel_context =
|
|
((TMComplexBindData) oldBindData)->accel_context;
|
|
XtMemmove((char *)&cBindData->bindTbl[0], (char *)bindings,
|
|
numBindings * sizeof(TMComplexBindProcsRec));
|
|
}
|
|
return bindData;
|
|
}
|
|
|
|
/*
|
|
* This routine is the central clearinghouse for merging translations
|
|
* into a widget. It takes care of preping the action bindings for
|
|
* realize time and calling the converter or doing a straight merge if
|
|
* the destination is empty.
|
|
*/
|
|
static Boolean ComposeTranslations(
|
|
Widget dest,
|
|
_XtTranslateOp operation,
|
|
Widget source,
|
|
XtTranslations newXlations)
|
|
{
|
|
XtTranslations newTable, oldXlations;
|
|
XtTranslations accNewXlations;
|
|
EventMask oldMask = 0;
|
|
TMBindData bindData;
|
|
TMComplexBindProcs oldBindings = NULL;
|
|
TMShortCard numOldBindings = 0, numNewBindings = 0, numBytes;
|
|
TMComplexBindProcsRec stackBindings[16], *newBindings;
|
|
|
|
/*
|
|
* how should we be handling the refcount decrement for the
|
|
* replaced translation table ???
|
|
*/
|
|
if (!newXlations)
|
|
{
|
|
XtAppWarningMsg(XtWidgetToApplicationContext(dest),
|
|
XtNtranslationError,"nullTable",XtCXtToolkitError,
|
|
"table to (un)merge must not be null",
|
|
(String *)NULL, (Cardinal *)NULL);
|
|
return False;
|
|
}
|
|
|
|
accNewXlations = newXlations;
|
|
newXlations = ((newXlations->hasBindings)
|
|
? ((ATranslations)newXlations)->xlations
|
|
: newXlations);
|
|
|
|
if (!(oldXlations = dest->core.tm.translations))
|
|
operation = XtTableReplace;
|
|
|
|
/*
|
|
* try to avoid generation of duplicate state trees. If the source
|
|
* isn't simple (1 state Tree) then it's too much hassle
|
|
*/
|
|
if (((operation == XtTableAugment) ||
|
|
(operation == XtTableOverride)) &&
|
|
(newXlations->numStateTrees == 1)) {
|
|
Cardinal i;
|
|
for (i = 0; i < oldXlations->numStateTrees; i++)
|
|
if (oldXlations->stateTreeTbl[i] ==
|
|
newXlations->stateTreeTbl[0])
|
|
break;
|
|
if (i < oldXlations->numStateTrees) {
|
|
if (operation == XtTableAugment) {
|
|
/*
|
|
* we don't need to do anything since it's already
|
|
* there
|
|
*/
|
|
return True;
|
|
}
|
|
else {/* operation == XtTableOverride */
|
|
/*
|
|
* We'll get rid of the duplicate trees throughout the
|
|
* and leave it with a pruned translation table. This
|
|
* will only work if the same table has been merged
|
|
* into this table (or one of it's composers
|
|
*/
|
|
_XtUnmergeTranslations(dest, newXlations);
|
|
/*
|
|
* reset oldXlations so we're back in sync
|
|
*/
|
|
if (!(oldXlations = dest->core.tm.translations))
|
|
operation = XtTableReplace;
|
|
}
|
|
}
|
|
}
|
|
|
|
bindData = (TMBindData) dest->core.tm.proc_table;
|
|
if (bindData) {
|
|
numOldBindings = (oldXlations ? oldXlations->numStateTrees : 0);
|
|
if (bindData->simple.isComplex)
|
|
oldBindings = &((TMComplexBindData)bindData)->bindTbl[0];
|
|
else
|
|
oldBindings = (TMComplexBindProcs)
|
|
(&((TMSimpleBindData)bindData)->bindTbl[0]);
|
|
}
|
|
|
|
numBytes =(((oldXlations ? oldXlations->numStateTrees : 0)
|
|
+ newXlations->numStateTrees) * sizeof(TMComplexBindProcsRec));
|
|
newBindings = (TMComplexBindProcs) XtStackAlloc(numBytes, stackBindings);
|
|
XtBZero((char *)newBindings, numBytes);
|
|
|
|
if (operation == XtTableUnmerge) {
|
|
newTable = UnmergeTranslations(dest,
|
|
oldXlations,
|
|
newXlations,
|
|
0,
|
|
oldBindings, numOldBindings,
|
|
newBindings, &numNewBindings);
|
|
#ifdef DEBUG
|
|
/* check for no match for unmerge */
|
|
if (newTable == oldXlations) {
|
|
XtWarning("attempt to unmerge invalid table");
|
|
XtStackFree((char *)newBindings, (char *)stackBindings);
|
|
return(newTable != NULL);
|
|
}
|
|
#endif /* DEBUG */
|
|
}
|
|
else {
|
|
newTable = MergeTranslations(dest,
|
|
oldXlations,
|
|
accNewXlations,
|
|
operation,
|
|
source,
|
|
oldBindings,
|
|
newBindings,
|
|
&numNewBindings);
|
|
}
|
|
if (XtIsRealized(dest)) {
|
|
oldMask = 0;
|
|
if (oldXlations)
|
|
oldMask = oldXlations->eventMask;
|
|
_XtUninstallTranslations(dest);
|
|
}
|
|
|
|
dest->core.tm.proc_table =
|
|
(XtActionProc *) MakeBindData(newBindings, numNewBindings, bindData);
|
|
|
|
if (bindData) XtFree((char *)bindData);
|
|
|
|
dest->core.tm.translations = newTable;
|
|
|
|
if (XtIsRealized(dest)) {
|
|
EventMask mask = 0;
|
|
_XtInstallTranslations(dest);
|
|
if (newTable)
|
|
mask = newTable->eventMask;
|
|
if (mask != oldMask)
|
|
XSelectInput(XtDisplay(dest), XtWindow(dest),
|
|
XtBuildEventMask(dest));
|
|
}
|
|
XtStackFree((XtPointer)newBindings, (XtPointer)stackBindings);
|
|
return(newTable != NULL);
|
|
}
|
|
|
|
/*
|
|
* If a GetValues is done on a translation resource that contains
|
|
* accelerators we need to return the accelerator context in addition
|
|
* to the pure translations. Since this means returning memory that
|
|
* the client controlls but we still own, we will track the "headers"
|
|
* that we return (via a linked list pointed to from the bindData) and
|
|
* free it at destroy time.
|
|
*/
|
|
XtTranslations _XtGetTranslationValue(
|
|
Widget w)
|
|
{
|
|
XtTM tmRecPtr = (XtTM) &w->core.tm;
|
|
ATranslations *aXlationsPtr;
|
|
TMComplexBindData cBindData = (TMComplexBindData) tmRecPtr->proc_table;
|
|
XtTranslations xlations = tmRecPtr->translations;
|
|
|
|
if (!xlations || !cBindData || !cBindData->isComplex)
|
|
return xlations;
|
|
|
|
/* Walk the list looking to see if we already have generated a
|
|
* header for the currently installed translations. If we have,
|
|
* just return that header. Otherwise create a new header.
|
|
*/
|
|
for (aXlationsPtr = (ATranslations *) &cBindData->accel_context;
|
|
*aXlationsPtr && (*aXlationsPtr)->xlations != xlations;
|
|
aXlationsPtr = &(*aXlationsPtr)->next)
|
|
;
|
|
if (*aXlationsPtr)
|
|
return (XtTranslations) *aXlationsPtr;
|
|
else {
|
|
/* create a new aXlations context */
|
|
ATranslations aXlations;
|
|
Cardinal numBindings = xlations->numStateTrees;
|
|
|
|
(*aXlationsPtr) = aXlations = (ATranslations)
|
|
__XtMalloc(sizeof(ATranslationData) +
|
|
(numBindings - 1) * sizeof(TMComplexBindProcsRec));
|
|
|
|
aXlations->hasBindings = True;
|
|
aXlations->xlations = xlations;
|
|
aXlations->next = NULL;
|
|
XtMemmove((char *) &aXlations->bindTbl[0],
|
|
(char *) &cBindData->bindTbl[0],
|
|
numBindings * sizeof(TMComplexBindProcsRec));
|
|
return (XtTranslations) aXlations;
|
|
}
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
static void RemoveStateTree(
|
|
TMStateTree tree)
|
|
{
|
|
#ifdef REFCNT_TRANSLATIONS
|
|
TMComplexStateTree stateTree = (TMComplexStateTree)tree;
|
|
|
|
if (--stateTree->refCount == 0) {
|
|
/*
|
|
* should we free/refcount the match recs ?
|
|
*/
|
|
if (!stateTree->isSimple) {
|
|
StatePtr currState, nextState;
|
|
TMShortCard i;
|
|
for (i = 0; i < stateTree->numComplexBranchHeads; i++) {
|
|
currState =
|
|
nextState =
|
|
stateTree->complexBranchHeadTbl[i];
|
|
for (; nextState;){
|
|
FreeActions(currState->actions);
|
|
currState->actions = NULL;
|
|
if (!currState->isCycleEnd)
|
|
nextState = currState->nextLevel;
|
|
else
|
|
nextState = NULL;
|
|
XtFree( (char*)currState );
|
|
}
|
|
}
|
|
XtFree((char*)stateTree->complexBranchHeadTbl);
|
|
}
|
|
XtFree((char*)stateTree->branchHeadTbl);
|
|
XtFree((char*)stateTree);
|
|
}
|
|
#endif /* REFCNT_TRANSLATIONS */
|
|
}
|
|
|
|
void _XtRemoveStateTreeByIndex(
|
|
XtTranslations xlations,
|
|
TMShortCard i)
|
|
{
|
|
TMStateTree *stateTrees = xlations->stateTreeTbl;
|
|
|
|
RemoveStateTree(stateTrees[i]);
|
|
xlations->numStateTrees--;
|
|
|
|
for (; i < xlations->numStateTrees; i++)
|
|
{
|
|
stateTrees[i] = stateTrees[i+1];
|
|
}
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
void _XtFreeTranslations(
|
|
XtAppContext app,
|
|
XrmValuePtr toVal,
|
|
XtPointer closure,
|
|
XrmValuePtr args,
|
|
Cardinal *num_args)
|
|
{
|
|
XtTranslations xlations;
|
|
int i;
|
|
|
|
if (*num_args != 0)
|
|
XtAppWarningMsg(app,
|
|
"invalidParameters","freeTranslations",XtCXtToolkitError,
|
|
"Freeing XtTranslations requires no extra arguments",
|
|
(String *)NULL, (Cardinal *)NULL);
|
|
|
|
xlations = *(XtTranslations*)toVal->addr;
|
|
for (i = 0; i < (int)xlations->numStateTrees; i++)
|
|
RemoveStateTree(xlations->stateTreeTbl[i]);
|
|
XtFree((char *)xlations);
|
|
}
|
|
|
|
/* The spec is not clear on when actions specified in accelerators are bound;
|
|
* Bind them at Realize the same as translations
|
|
*/
|
|
void XtInstallAccelerators(
|
|
Widget destination, Widget source)
|
|
{
|
|
XtTranslations aXlations;
|
|
_XtTranslateOp op;
|
|
String buf;
|
|
WIDGET_TO_APPCON(destination);
|
|
|
|
/*
|
|
* test that it was parsed as an accelarator table. Even though
|
|
* there doesn't need to be a distinction it makes life easier if
|
|
* we honor the spec implication that aXlations is an accelerator
|
|
*/
|
|
LOCK_APP(app);
|
|
LOCK_PROCESS;
|
|
if ((!XtIsWidget(source)) ||
|
|
((aXlations = source->core.accelerators) == NULL) ||
|
|
(aXlations->stateTreeTbl[0]->simple.isAccelerator == False)) {
|
|
UNLOCK_PROCESS;
|
|
UNLOCK_APP(app);
|
|
return;
|
|
}
|
|
|
|
aXlations = source->core.accelerators;
|
|
op = aXlations->operation;
|
|
|
|
if (ComposeTranslations(destination, op, source, aXlations) &&
|
|
(XtClass(source)->core_class.display_accelerator != NULL)) {
|
|
|
|
buf = _XtPrintXlations(destination, aXlations, source, False);
|
|
(*(XtClass(source)->core_class.display_accelerator))(source,buf);
|
|
XtFree(buf);
|
|
}
|
|
UNLOCK_PROCESS;
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
void XtInstallAllAccelerators(
|
|
Widget destination,
|
|
Widget source)
|
|
{
|
|
Cardinal i;
|
|
CompositeWidget cw;
|
|
WIDGET_TO_APPCON(destination);
|
|
|
|
/* Recurse down normal children */
|
|
LOCK_APP(app);
|
|
LOCK_PROCESS;
|
|
if (XtIsComposite(source)) {
|
|
cw = (CompositeWidget) source;
|
|
for (i = 0; i < cw->composite.num_children; i++) {
|
|
XtInstallAllAccelerators(destination,cw->composite.children[i]);
|
|
}
|
|
}
|
|
|
|
/* Recurse down popup children */
|
|
if (XtIsWidget(source)) {
|
|
for (i = 0; i < source->core.num_popups; i++) {
|
|
XtInstallAllAccelerators(destination,source->core.popup_list[i]);
|
|
}
|
|
}
|
|
/* Finally, apply procedure to this widget */
|
|
XtInstallAccelerators(destination,source);
|
|
UNLOCK_PROCESS;
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
#if 0 /* dead code */
|
|
static _XtTranslateOp _XtGetTMOperation(
|
|
XtTranslations xlations)
|
|
{
|
|
return ((xlations->hasBindings)
|
|
? ((ATranslations)xlations)->xlations->operation
|
|
: xlations->operation);
|
|
}
|
|
#endif
|
|
|
|
void XtAugmentTranslations(
|
|
Widget widget,
|
|
XtTranslations new)
|
|
{
|
|
Widget hookobj;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
LOCK_PROCESS;
|
|
(void)ComposeTranslations(widget, XtTableAugment, (Widget)NULL, new);
|
|
hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
|
|
if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
|
|
XtChangeHookDataRec call_data;
|
|
|
|
call_data.type = XtHaugmentTranslations;
|
|
call_data.widget = widget;
|
|
XtCallCallbackList(hookobj,
|
|
((HookObject)hookobj)->hooks.changehook_callbacks,
|
|
(XtPointer)&call_data);
|
|
}
|
|
UNLOCK_PROCESS;
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
void XtOverrideTranslations(
|
|
Widget widget,
|
|
XtTranslations new)
|
|
{
|
|
Widget hookobj;
|
|
WIDGET_TO_APPCON(widget);
|
|
|
|
LOCK_APP(app);
|
|
LOCK_PROCESS;
|
|
(void) ComposeTranslations(widget, XtTableOverride, (Widget)NULL, new);
|
|
hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
|
|
if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
|
|
XtChangeHookDataRec call_data;
|
|
|
|
call_data.type = XtHoverrideTranslations;
|
|
call_data.widget = widget;
|
|
XtCallCallbackList(hookobj,
|
|
((HookObject)hookobj)->hooks.changehook_callbacks,
|
|
(XtPointer)&call_data);
|
|
}
|
|
UNLOCK_PROCESS;
|
|
UNLOCK_APP(app);
|
|
}
|
|
|
|
void _XtMergeTranslations(
|
|
Widget widget,
|
|
XtTranslations newXlations,
|
|
_XtTranslateOp op)
|
|
{
|
|
if (!newXlations){
|
|
if (!widget->core.tm.translations)
|
|
return;
|
|
else {
|
|
newXlations = widget->core.tm.translations;
|
|
widget->core.tm.translations = NULL;
|
|
}
|
|
}
|
|
(void) ComposeTranslations(widget,
|
|
op,
|
|
(Widget)NULL,
|
|
newXlations);
|
|
}
|
|
|
|
void _XtUnmergeTranslations(
|
|
Widget widget,
|
|
XtTranslations xlations)
|
|
{
|
|
ComposeTranslations(widget, XtTableUnmerge, (Widget)NULL, xlations);
|
|
}
|