📄 tkbind.c
字号:
Tcl_DString ds; hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object); if (hPtr == NULL) { return; } Tcl_DStringInit(&ds); for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL; psPtr = psPtr->nextObjPtr) { /* * For each binding, output information about each of the * patterns in its sequence. */ Tcl_DStringSetLength(&ds, 0); GetPatternString(psPtr, &ds); Tcl_AppendElement(interp, Tcl_DStringValue(&ds)); } Tcl_DStringFree(&ds);}/* *-------------------------------------------------------------- * * Tk_DeleteAllBindings -- * * Remove all bindings associated with a given object in a * given binding table. * * Results: * All bindings associated with object are removed from * bindingTable. * * Side effects: * None. * *-------------------------------------------------------------- */voidTk_DeleteAllBindings(bindingTable, object) Tk_BindingTable bindingTable; /* Table in which to delete * bindings. */ ClientData object; /* Token for object. */{ BindingTable *bindPtr = (BindingTable *) bindingTable; PatSeq *psPtr, *prevPtr; PatSeq *nextPtr; Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object); if (hPtr == NULL) { return; } for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL; psPtr = nextPtr) { nextPtr = psPtr->nextObjPtr; /* * Be sure to remove each binding from its hash chain in the * pattern table. If this is the last pattern in the chain, * then delete the hash entry too. */ prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr); if (prevPtr == psPtr) { if (psPtr->nextSeqPtr == NULL) { Tcl_DeleteHashEntry(psPtr->hPtr); } else { Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr); } } else { for ( ; ; prevPtr = prevPtr->nextSeqPtr) { if (prevPtr == NULL) { panic("Tk_DeleteAllBindings couldn't find on hash chain"); } if (prevPtr->nextSeqPtr == psPtr) { prevPtr->nextSeqPtr = psPtr->nextSeqPtr; break; } } } psPtr->flags |= MARKED_DELETED; if (psPtr->refCount == 0) { if (psPtr->freeProc != NULL) { (*psPtr->freeProc)(psPtr->clientData); } ckfree((char *) psPtr); } } Tcl_DeleteHashEntry(hPtr);}/* *--------------------------------------------------------------------------- * * Tk_BindEvent -- * * This procedure is invoked to process an X event. The * event is added to those recorded for the binding table. * Then each of the objects at *objectPtr is checked in * order to see if it has a binding that matches the recent * events. If so, the most specific binding is invoked for * each object. * * Results: * None. * * Side effects: * Depends on the command associated with the matching binding. * * All Tcl bindings scripts for each object are accumulated before * the first binding is evaluated. If the action of a Tcl binding * is to change or delete a binding, or delete the window associated * with the binding, all the original Tcl binding scripts will still * fire. Contrast this with C binding procedures. If a pending C * binding (one that hasn't fired yet, but is queued to be fired for * this window) is deleted, it will not be called, and if it is * changed, then the new binding procedure will be called. If the * window itself is deleted, no further C binding procedures will be * called for this window. When both Tcl binding scripts and C binding * procedures are interleaved, the above rules still apply. * *--------------------------------------------------------------------------- */voidTk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr) Tk_BindingTable bindingTable; /* Table in which to look for * bindings. */ XEvent *eventPtr; /* What actually happened. */ Tk_Window tkwin; /* Window on display where event * occurred (needed in order to * locate display information). */ int numObjects; /* Number of objects at *objectPtr. */ ClientData *objectPtr; /* Array of one or more objects * to check for a matching binding. */{ BindingTable *bindPtr; TkDisplay *dispPtr; BindInfo *bindInfoPtr; TkDisplay *oldDispPtr; ScreenInfo *screenPtr; XEvent *ringPtr; PatSeq *vMatchDetailList, *vMatchNoDetailList; int flags, oldScreen, i, deferModal; unsigned int matchCount, matchSpace; Tcl_Interp *interp; Tcl_DString scripts, savedResult; Detail detail; char *p, *end; PendingBinding *pendingPtr; PendingBinding staticPending; TkWindow *winPtr = (TkWindow *)tkwin; PatternTableKey key; /* * Ignore events on windows that don't have names: these are windows * like wrapper windows that shouldn't be visible to the * application. */ if (winPtr->pathName == NULL) { return; } /* * Ignore the event completely if it is an Enter, Leave, FocusIn, * or FocusOut event with detail NotifyInferior. The reason for * ignoring these events is that we don't want transitions between * a window and its children to visible to bindings on the parent: * this would cause problems for mega-widgets, since the internal * structure of a mega-widget isn't supposed to be visible to * people watching the parent. */ if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) { if (eventPtr->xcrossing.detail == NotifyInferior) { return; } } if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) { if (eventPtr->xfocus.detail == NotifyInferior) { return; } } bindPtr = (BindingTable *) bindingTable; dispPtr = ((TkWindow *) tkwin)->dispPtr; bindInfoPtr = (BindInfo *) winPtr->mainPtr->bindInfo; /* * Add the new event to the ring of saved events for the * binding table. Two tricky points: * * 1. Combine consecutive MotionNotify events. Do this by putting * the new event *on top* of the previous event. * 2. If a modifier key is held down, it auto-repeats to generate * continuous KeyPress and KeyRelease events. These can flush * the event ring so that valuable information is lost (such * as repeated button clicks). To handle this, check for the * special case of a modifier KeyPress arriving when the previous * two events are a KeyRelease and KeyPress of the same key. * If this happens, mark the most recent event (the KeyRelease) * invalid and put the new event on top of the event before that * (the KeyPress). */ if ((eventPtr->type == MotionNotify) && (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) { /* * Don't advance the ring pointer. */ } else if (eventPtr->type == KeyPress) { int i; for (i = 0; ; i++) { if (i >= dispPtr->numModKeyCodes) { goto advanceRingPointer; } if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) { break; } } ringPtr = &bindPtr->eventRing[bindPtr->curEvent]; if ((ringPtr->type != KeyRelease) || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) { goto advanceRingPointer; } if (bindPtr->curEvent <= 0) { i = EVENT_BUFFER_SIZE - 1; } else { i = bindPtr->curEvent - 1; } ringPtr = &bindPtr->eventRing[i]; if ((ringPtr->type != KeyPress) || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) { goto advanceRingPointer; } bindPtr->eventRing[bindPtr->curEvent].type = -1; bindPtr->curEvent = i; } else { advanceRingPointer: bindPtr->curEvent++; if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) { bindPtr->curEvent = 0; } } ringPtr = &bindPtr->eventRing[bindPtr->curEvent]; memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent)); detail.clientData = 0; flags = flagArray[ringPtr->type]; if (flags & KEY) { detail.keySym = GetKeySym(dispPtr, ringPtr); if (detail.keySym == NoSymbol) { detail.keySym = 0; } } else if (flags & BUTTON) { detail.button = ringPtr->xbutton.button; } else if (flags & VIRTUAL) { detail.name = ((XVirtualEvent *) ringPtr)->name; } bindPtr->detailRing[bindPtr->curEvent] = detail; /* * Find out if there are any virtual events that correspond to this * physical event (or sequence of physical events). */ vMatchDetailList = NULL; vMatchNoDetailList = NULL; memset(&key, 0, sizeof(key)); if (ringPtr->type != VirtualEvent) { Tcl_HashTable *veptPtr; Tcl_HashEntry *hPtr; veptPtr = &bindInfoPtr->virtualEventTable.patternTable; key.object = NULL; key.type = ringPtr->type; key.detail = detail; hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key); if (hPtr != NULL) { vMatchDetailList = (PatSeq *) Tcl_GetHashValue(hPtr); } if (key.detail.clientData != 0) { key.detail.clientData = 0; hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key); if (hPtr != NULL) { vMatchNoDetailList = (PatSeq *) Tcl_GetHashValue(hPtr); } } } /* * Loop over all the binding tags, finding the binding script or * callback for each one. Append all of the binding scripts, with * %-sequences expanded, to "scripts", with null characters separating * the scripts for each object. Append all the callbacks to the array * of pending callbacks. */ pendingPtr = &staticPending; matchCount = 0; matchSpace = sizeof(staticPending.matchArray) / sizeof(PatSeq *); Tcl_DStringInit(&scripts); for ( ; numObjects > 0; numObjects--, objectPtr++) { PatSeq *matchPtr, *sourcePtr; Tcl_HashEntry *hPtr; matchPtr = NULL; sourcePtr = NULL; /* * Match the new event against those recorded in the pattern table, * saving the longest matching pattern. For events with details * (button and key events), look for a binding for the specific * key or button. First see if the event matches a physical event * that the object is interested in, then look for a virtual event. */ key.object = *objectPtr; key.type = ringPtr->type; key.detail = detail; hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key); if (hPtr != NULL) { matchPtr = MatchPatterns(dispPtr, bindPtr, (PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL, &sourcePtr); } if (vMatchDetailList != NULL) { matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchDetailList, matchPtr, objectPtr, &sourcePtr); } /* * If no match was found, look for a binding for all keys or buttons * (detail of 0). Again, first match on a virtual event. */ if ((detail.clientData != 0) && (matchPtr == NULL)) { key.detail.clientData = 0; hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key); if (hPtr != NULL) { matchPtr = MatchPatterns(dispPtr, bindPtr, (PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL, &sourcePtr); } if (vMatchNoDetailList != NULL) { matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchNoDetailList, matchPtr, objectPtr, &sourcePtr); } } if (matchPtr != NULL) { if (sourcePtr->eventProc == NULL) { panic("Tk_BindEvent: missing command"); } if (sourcePtr->eventProc == EvalTclBinding) { ExpandPercents(winPtr, (char *) sourcePtr->clientData, eventPtr, detail.keySym, &scripts); } else { if (matchCount >= matchSpace) { PendingBinding *new; unsigned int oldSize, newSize; oldSize = sizeof(staticPending) - sizeof(staticPending.matchArray) + matchSpace * sizeof(PatSeq*); matchSpace *= 2; newSize = sizeof(staticPending) - sizeof(staticPending.matchArray) + matchSpace * sizeof(PatSeq*); new = (PendingBinding *) ckalloc(newSize); memcpy((VOID *) new, (VOID *) pendingPtr, oldSize); if (pendingPtr != &staticPending) { ckfree((char *) pendingPtr); } pendingPtr = new; } sourcePtr->refCount++; pendingPtr->matchArray[matchCount] = sourcePtr; matchCount++; } /* * A "" is added to the scripts string to separate the * various scripts that should be invoked. */ Tcl_DStringAppend(&scripts, "", 1); } } if (Tcl_DStringLength(&scripts) == 0) { return; } /* * Now go back through and evaluate the binding for each object, * in order, dealing with "break" and "continue" exceptions * appropriately. * * There are two tricks here: * 1. Bindings can be invoked from in the middle of Tcl commands, * where interp->result is significant (for example, a widget * might be deleted because of an error in creating it, so the * result contains an error message that is eventually going to * be returned by the creating command). To preserve the result,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -