summaryrefslogtreecommitdiff
path: root/blt/src/bltBind.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltBind.c')
-rw-r--r--blt/src/bltBind.c644
1 files changed, 644 insertions, 0 deletions
diff --git a/blt/src/bltBind.c b/blt/src/bltBind.c
new file mode 100644
index 00000000000..57047974b5c
--- /dev/null
+++ b/blt/src/bltBind.c
@@ -0,0 +1,644 @@
+/*
+ * bltBind.c --
+ *
+ * This module implements object binding procedures for the BLT
+ * toolkit.
+ *
+ * Copyright 1998 Lucent Technologies, Inc.
+ *
+ * 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 the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies 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 tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltInt.h"
+
+#include "bltBind.h"
+
+#if defined(__STDC__)
+static Tk_EventProc BindProc;
+#endif
+
+/*
+ * Binding table procedures.
+ */
+#define REPICK_IN_PROGRESS (1<<0)
+#define LEFT_GRABBED_ITEM (1<<1)
+
+#define ALL_BUTTONS_MASK \
+ (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)
+
+#ifndef VirtualEventMask
+#define VirtualEventMask (1L << 30)
+#endif
+
+#define ALL_VALID_EVENTS_MASK \
+ (ButtonMotionMask | Button1MotionMask | Button2MotionMask | \
+ Button3MotionMask | Button4MotionMask | Button5MotionMask | \
+ ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
+ LeaveWindowMask | KeyPressMask | KeyReleaseMask | \
+ PointerMotionMask | VirtualEventMask)
+
+static int buttonMasks[] =
+{
+ 0, /* No buttons pressed */
+ Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask,
+};
+
+/*
+ * How to make drag&drop work?
+ *
+ * Right now we generate pseudo <Enter> <Leave> events within
+ * button grab on an object. They're marked NotifyVirtual instead
+ * of NotifyAncestor. A better solution: generate new-style
+ * virtual <<DragEnter>> <<DragMotion>> <<DragLeave>> events.
+ * These virtual events don't have to exist as "real" event
+ * sequences, like virtual events do now.
+ */
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DoEvent --
+ *
+ * This procedure is called to invoke binding processing
+ * for a new event that is associated with the current item
+ * for a legend.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the bindings for the legend. A binding script
+ * could delete an entry, so callers should protect themselves
+ * with Tcl_Preserve and Tcl_Release.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+DoEvent(bindPtr, eventPtr, item)
+ struct Blt_BindTableStruct *bindPtr; /* Binding information for widget in
+ * which event occurred. */
+ XEvent *eventPtr; /* Real or simulated X event that
+ * is to be processed. */
+ ClientData item;
+{
+
+ if (bindPtr->bindingTable == NULL) {
+ return;
+ }
+ if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
+ item = bindPtr->focusItem;
+ }
+ if (item == NULL) {
+ return;
+ }
+ /*
+ * Invoke the binding system.
+ */
+ if (bindPtr->tkwin != NULL) {
+ Blt_List list;
+ ClientData *arrayPtr;
+ int nTags;
+ ClientData tags[32];
+ register Blt_ListNode node;
+
+ list = Blt_ListCreate(TCL_ONE_WORD_KEYS);
+ if (bindPtr->tagProc == NULL) {
+ Blt_ListAppend(list, Tk_GetUid("all"), 0);
+ Blt_ListAppend(list, (char *)item, 0);
+ } else {
+ (*bindPtr->tagProc) (bindPtr, item, list);
+ }
+ nTags = Blt_ListGetLength(list);
+ arrayPtr = tags;
+ if (nTags >= 32) {
+ arrayPtr = Blt_Malloc(sizeof(ClientData) * nTags);
+
+ }
+ nTags = 0;
+ for (node = Blt_ListFirstNode(list); node != NULL;
+ node = Blt_ListNextNode(node)) {
+ arrayPtr[nTags++] = Blt_ListGetKey(node);
+ }
+ Tk_BindEvent(bindPtr->bindingTable, eventPtr, bindPtr->tkwin, nTags,
+ arrayPtr);
+ if (nTags >= 32) {
+ Blt_Free(arrayPtr);
+ }
+ Blt_ListDestroy(list);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * PickCurrentItem --
+ *
+ * Find the topmost item in a legend that contains a given
+ * location and mark the the current item. If the current
+ * item has changed, generate a fake exit event on the old
+ * current item and a fake enter event on the new current
+ * item.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The current item may change. If it does, then the commands
+ * associated with item entry and exit could do just about
+ * anything. A binding script could delete the legend, so
+ * callers should protect themselves with Tcl_Preserve and
+ * Tcl_Release.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+PickCurrentItem(bindPtr, eventPtr)
+ struct Blt_BindTableStruct *bindPtr; /* Binding table information. */
+ XEvent *eventPtr; /* Event describing location of
+ * mouse cursor. Must be EnterWindow,
+ * LeaveWindow, ButtonRelease, or
+ * MotionNotify. */
+{
+ int buttonDown;
+ ClientData newItem;
+
+ /*
+ * Check whether or not a button is down. If so, we'll log entry
+ * and exit into and out of the current item, but not entry into
+ * any other item. This implements a form of grabbing equivalent
+ * to what the X server does for windows.
+ */
+ buttonDown = (bindPtr->state & ALL_BUTTONS_MASK);
+ if (!buttonDown) {
+ bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+ }
+ /*
+ * Save information about this event in the widget. The event in
+ * the widget is used for two purposes:
+ *
+ * 1. Event bindings: if the current item changes, fake events are
+ * generated to allow item-enter and item-leave bindings to trigger.
+ * 2. Reselection: if the current item gets deleted, can use the
+ * saved event to find a new current item.
+ * Translate MotionNotify events into EnterNotify events, since that's
+ * what gets reported to item handlers.
+ */
+
+ if (eventPtr != &bindPtr->pickEvent) {
+ if ((eventPtr->type == MotionNotify) ||
+ (eventPtr->type == ButtonRelease)) {
+ bindPtr->pickEvent.xcrossing.type = EnterNotify;
+ bindPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
+ bindPtr->pickEvent.xcrossing.send_event =
+ eventPtr->xmotion.send_event;
+ bindPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
+ bindPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
+ bindPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
+ bindPtr->pickEvent.xcrossing.subwindow = None;
+ bindPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
+ bindPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
+ bindPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
+ bindPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
+ bindPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
+ bindPtr->pickEvent.xcrossing.mode = NotifyNormal;
+ bindPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
+ bindPtr->pickEvent.xcrossing.same_screen
+ = eventPtr->xmotion.same_screen;
+ bindPtr->pickEvent.xcrossing.focus = False;
+ bindPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
+ } else {
+ bindPtr->pickEvent = *eventPtr;
+ }
+ }
+ bindPtr->activePick = TRUE;
+
+ /*
+ * If this is a recursive call (there's already a partially completed
+ * call pending on the stack; it's in the middle of processing a
+ * Leave event handler for the old current item) then just return;
+ * the pending call will do everything that's needed.
+ */
+ if (bindPtr->flags & REPICK_IN_PROGRESS) {
+ return;
+ }
+ /*
+ * A LeaveNotify event automatically means that there's no current
+ * item, so the check for closest item can be skipped.
+ */
+ if (bindPtr->pickEvent.type != LeaveNotify) {
+ int x, y;
+
+ x = bindPtr->pickEvent.xcrossing.x;
+ y = bindPtr->pickEvent.xcrossing.y;
+ newItem = (*bindPtr->pickProc) (bindPtr->clientData, x, y);
+ } else {
+ newItem = NULL;
+ }
+ if ((newItem == bindPtr->currentItem) &&
+ !(bindPtr->flags & LEFT_GRABBED_ITEM)) {
+ /*
+ * Nothing to do: the current item hasn't changed.
+ */
+ return;
+ }
+#ifndef FULLY_SIMULATE_GRAB
+ if ((newItem != bindPtr->currentItem) && (buttonDown)) {
+ bindPtr->flags |= LEFT_GRABBED_ITEM;
+ return;
+ }
+#endif
+ /*
+ * Simulate a LeaveNotify event on the previous current item and
+ * an EnterNotify event on the new current item. Remove the "current"
+ * tag from the previous current item and place it on the new current
+ * item.
+ */
+ if ((newItem != bindPtr->currentItem) && (bindPtr->currentItem != NULL) &&
+ !(bindPtr->flags & LEFT_GRABBED_ITEM)) {
+ XEvent event;
+
+ event = bindPtr->pickEvent;
+ event.type = LeaveNotify;
+ /*
+ * If the event's detail happens to be NotifyInferior the
+ * binding mechanism will discard the event. To be consistent,
+ * always use NotifyAncestor.
+ */
+ event.xcrossing.detail = NotifyAncestor;
+
+ bindPtr->flags |= REPICK_IN_PROGRESS;
+ DoEvent(bindPtr, &event, bindPtr->currentItem);
+ bindPtr->flags &= ~REPICK_IN_PROGRESS;
+
+ /*
+ * Note: during DoEvent above, it's possible that
+ * bindPtr->newItem got reset to NULL because the
+ * item was deleted.
+ */
+ }
+ if ((newItem != bindPtr->currentItem) && (buttonDown)) {
+ XEvent event;
+
+ bindPtr->flags |= LEFT_GRABBED_ITEM;
+ event = bindPtr->pickEvent;
+ if (newItem != bindPtr->newItem) {
+ ClientData saved;
+
+ /*
+ * Generate <Enter> and <Leave> events for objects during
+ * button grabs. This isn't standard. But for example, it
+ * allows one to provide balloon help on the individual
+ * entries of the Hierbox widget.
+ */
+ saved = bindPtr->currentItem;
+ if (bindPtr->newItem != NULL) {
+ event.type = LeaveNotify;
+ event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+ bindPtr->currentItem = bindPtr->newItem;
+ DoEvent(bindPtr, &event, bindPtr->newItem);
+ }
+ bindPtr->newItem = newItem;
+ if (newItem != NULL) {
+ event.type = EnterNotify;
+ event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+ bindPtr->currentItem = newItem;
+ DoEvent(bindPtr, &event, newItem);
+ }
+ bindPtr->currentItem = saved;
+ }
+ return;
+ }
+ /*
+ * Special note: it's possible that
+ * bindPtr->newItem == bindPtr->currentItem
+ * here. This can happen, for example, if LEFT_GRABBED_ITEM was set.
+ */
+
+ bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+ bindPtr->currentItem = bindPtr->newItem = newItem;
+ if (bindPtr->currentItem != NULL) {
+ XEvent event;
+
+ event = bindPtr->pickEvent;
+ event.type = EnterNotify;
+ event.xcrossing.detail = NotifyAncestor;
+ DoEvent(bindPtr, &event, newItem);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * BindProc --
+ *
+ * This procedure is invoked by the Tk dispatcher to handle
+ * events associated with bindings on items.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the command invoked as part of the binding
+ * (if there was any).
+ *
+ *--------------------------------------------------------------
+ */
+static void
+BindProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to widget structure. */
+ XEvent *eventPtr; /* Pointer to X event that just
+ * happened. */
+{
+ struct Blt_BindTableStruct *bindPtr = (struct Blt_BindTableStruct *)clientData;
+ int mask;
+
+ Tcl_Preserve(bindPtr->clientData);
+
+ /*
+ * This code below keeps track of the current modifier state in
+ * bindPtr->state. This information is used to defer repicks of
+ * the current item while buttons are down.
+ */
+ switch (eventPtr->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ mask = 0;
+ if ((eventPtr->xbutton.button >= Button1) &&
+ (eventPtr->xbutton.button <= Button5)) {
+ mask = buttonMasks[eventPtr->xbutton.button];
+ }
+ /*
+ * For button press events, repick the current item using the
+ * button state before the event, then process the event. For
+ * button release events, first process the event, then repick
+ * the current item using the button state *after* the event
+ * (the button has logically gone up before we change the
+ * current item).
+ */
+
+ if (eventPtr->type == ButtonPress) {
+
+ /*
+ * On a button press, first repick the current item using
+ * the button state before the event, the process the event.
+ */
+
+ bindPtr->state = eventPtr->xbutton.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ bindPtr->state ^= mask;
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+
+ } else {
+
+ /*
+ * Button release: first process the event, with the button
+ * still considered to be down. Then repick the current
+ * item under the assumption that the button is no longer down.
+ */
+ bindPtr->state = eventPtr->xbutton.state;
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+ eventPtr->xbutton.state ^= mask;
+ bindPtr->state = eventPtr->xbutton.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ eventPtr->xbutton.state ^= mask;
+ }
+ break;
+
+ case EnterNotify:
+ case LeaveNotify:
+ bindPtr->state = eventPtr->xcrossing.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ break;
+
+ case MotionNotify:
+ bindPtr->state = eventPtr->xmotion.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+ break;
+
+ case KeyPress:
+ case KeyRelease:
+ bindPtr->state = eventPtr->xkey.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem);
+ break;
+ }
+ Tcl_Release(bindPtr->clientData);
+}
+
+int
+Blt_ConfigureBindings(interp, bindPtr, item, argc, argv)
+ Tcl_Interp *interp;
+ struct Blt_BindTableStruct *bindPtr;
+ ClientData item;
+ int argc;
+ char **argv;
+{
+ char *command;
+ unsigned long mask;
+ char *seq;
+
+ if (argc == 0) {
+ Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+ return TCL_OK;
+ }
+ if (argc == 1) {
+ command = Tk_GetBinding(interp, bindPtr->bindingTable, item, argv[0]);
+ if (command == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, command, TCL_VOLATILE);
+ return TCL_OK;
+ }
+
+ seq = argv[0];
+ command = argv[1];
+
+ if (command[0] == '\0') {
+ return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ }
+
+ if (command[0] == '+') {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command + 1, TRUE);
+ } else {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command, FALSE);
+ }
+ if (mask == 0) {
+ return TCL_ERROR;
+ }
+ if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+ Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "requested illegal events; ",
+ "only key, button, motion, enter, leave, and virtual ",
+ "events may be used", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+#if (TCL_MAJOR_VERSION >= 8)
+
+int
+Blt_ConfigureBindingsFromObj(interp, bindPtr, item, objc, objv)
+ Tcl_Interp *interp;
+ struct Blt_BindTableStruct *bindPtr;
+ ClientData item;
+ int objc;
+ Tcl_Obj *CONST *objv;
+{
+ char *command;
+ unsigned long mask;
+ char *seq;
+ char *string;
+
+ if (objc == 0) {
+ Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+ return TCL_OK;
+ }
+ string = Tcl_GetString(objv[0]);
+ if (objc == 1) {
+ command = Tk_GetBinding(interp, bindPtr->bindingTable, item, string);
+ if (command == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "invalid binding event \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, command, TCL_VOLATILE);
+ return TCL_OK;
+ }
+
+ seq = string;
+ command = Tcl_GetString(objv[1]);
+
+ if (command[0] == '\0') {
+ return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ }
+
+ if (command[0] == '+') {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command + 1, TRUE);
+ } else {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command, FALSE);
+ }
+ if (mask == 0) {
+ return TCL_ERROR;
+ }
+ if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+ Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "requested illegal events; ",
+ "only key, button, motion, enter, leave, and virtual ",
+ "events may be used", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+#endif
+
+Blt_BindTable
+Blt_CreateBindingTable(interp, tkwin, clientData, pickProc, tagProc)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ ClientData clientData;
+ Blt_BindPickProc *pickProc;
+ Blt_BindTagProc *tagProc;
+{
+ unsigned int mask;
+ struct Blt_BindTableStruct *bindPtr;
+
+ bindPtr = Blt_Calloc(1, sizeof(struct Blt_BindTableStruct));
+ assert(bindPtr);
+ bindPtr->clientData = clientData;
+ bindPtr->pickProc = pickProc;
+ bindPtr->tagProc = tagProc;
+ bindPtr->tkwin = tkwin;
+ bindPtr->bindingTable = Tk_CreateBindingTable(interp);
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+ return bindPtr;
+}
+
+void
+Blt_DestroyBindingTable(bindPtr)
+ struct Blt_BindTableStruct *bindPtr;
+{
+ unsigned int mask;
+
+ Tk_DeleteBindingTable(bindPtr->bindingTable);
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+ Blt_Free(bindPtr);
+}
+
+void
+Blt_PickCurrentItem(bindPtr)
+ struct Blt_BindTableStruct *bindPtr;
+{
+ if (bindPtr->activePick) {
+ PickCurrentItem(bindPtr, &(bindPtr->pickEvent));
+ }
+}
+
+void
+Blt_DeleteBindings(bindPtr, object)
+ struct Blt_BindTableStruct *bindPtr;
+ ClientData object;
+{
+ Tk_DeleteAllBindings(bindPtr->bindingTable, object);
+
+ /*
+ * If this is the object currently picked, we need to repick one.
+ */
+ if (bindPtr->currentItem == object) {
+ bindPtr->currentItem = NULL;
+ }
+ if (bindPtr->newItem == object) {
+ bindPtr->newItem = NULL;
+ }
+ if (bindPtr->focusItem == object) {
+ bindPtr->focusItem = NULL;
+ }
+}
+
+void
+Blt_MoveBindingTable(bindPtr, tkwin)
+ struct Blt_BindTableStruct *bindPtr;
+ Tk_Window tkwin;
+{
+ unsigned int mask;
+
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ if (bindPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+ }
+ Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+ bindPtr->tkwin = tkwin;
+}