1 /*
2  * bltBind.c --
3  *
4  *	This module implements object binding procedures for the BLT
5  *	toolkit.
6  *
7  * Copyright 1998 Lucent Technologies, Inc.
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that the copyright notice and warranty
13  * disclaimer appear in supporting documentation, and that the names
14  * of Lucent Technologies any of their entities not be used in
15  * advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission.
17  *
18  * Lucent Technologies disclaims all warranties with regard to this
19  * software, including all implied warranties of merchantability and
20  * fitness.  In no event shall Lucent Technologies be liable for any
21  * special, indirect or consequential damages or any damages
22  * whatsoever resulting from loss of use, data or profits, whether in
23  * an action of contract, negligence or other tortuous action, arising
24  * out of or in connection with the use or performance of this
25  * software.
26  */
27 
28 #include "bltInt.h"
29 #include "bltBind.h"
30 
31 static Tk_EventProc BindProc;
32 
33 /*
34  * Binding table procedures.
35  */
36 #define REPICK_IN_PROGRESS (1<<0)
37 #define LEFT_GRABBED_ITEM  (1<<1)
38 
39 #define ALL_BUTTONS_MASK \
40 	(Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)
41 
42 #ifndef VirtualEventMask
43 #define VirtualEventMask    (1L << 30)
44 #endif
45 
46 #define ALL_VALID_EVENTS_MASK \
47 	(ButtonMotionMask | Button1MotionMask | Button2MotionMask | \
48 	 Button3MotionMask | Button4MotionMask | Button5MotionMask | \
49 	 ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
50 	 LeaveWindowMask | KeyPressMask | KeyReleaseMask | \
51 	 PointerMotionMask | VirtualEventMask)
52 
53 static int buttonMasks[] =
54 {
55     0,				/* No buttons pressed */
56     Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask,
57 };
58 
59 
60 /*
61  * How to make drag&drop work?
62  *
63  *	Right now we generate pseudo <Enter> <Leave> events within
64  *	button grab on an object.  They're marked NotifyVirtual instead
65  *	of NotifyAncestor.  A better solution: generate new-style
66  *	virtual <<DragEnter>> <<DragMotion>> <<DragLeave>> events.
67  *	These virtual events don't have to exist as "real" event
68  *	sequences, like virtual events do now.
69  */
70 
71 /*
72  *--------------------------------------------------------------
73  *
74  * DoEvent --
75  *
76  *	This procedure is called to invoke binding processing
77  *	for a new event that is associated with the current item
78  *	for a legend.
79  *
80  * Results:
81  *	None.
82  *
83  * Side effects:
84  *	Depends on the bindings for the legend.  A binding script
85  *	could delete an entry, so callers should protect themselves
86  *	with Tcl_Preserve and Tcl_Release.
87  *
88  *--------------------------------------------------------------
89  */
90 static void
DoEvent(bindPtr,eventPtr,item,context)91 DoEvent(bindPtr, eventPtr, item, context)
92     struct Blt_BindTableStruct *bindPtr; /* Binding information for widget in
93 				 * which event occurred. */
94     XEvent *eventPtr;		/* Real or simulated X event that
95 				 * is to be processed. */
96     ClientData item;		/* Item picked. */
97     ClientData context;		/* Context of item.  */
98 {
99     Blt_List bindIds;
100     int nIds;
101 
102     if ((bindPtr->tkwin == NULL) || (bindPtr->bindingTable == NULL)) {
103 	return;
104      }
105      if (Tcl_InterpDeleted(bindPtr->interp)) {
106          return;
107      }
108 
109     if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
110 	item = bindPtr->focusItem;
111 	context = bindPtr->focusContext;
112     }
113     if (item == NULL) {
114 	return;
115     }
116     /*
117      * Invoke the binding system.
118      */
119 
120     bindIds = Blt_ListCreate(BLT_ONE_WORD_KEYS);
121     if (bindPtr->tagProc == NULL) {
122 	Blt_ListAppend(bindIds, (char *)Tk_GetUid("all"), 0);
123 	Blt_ListAppend(bindIds, (char *)item, 0);
124     } else {
125 	(*bindPtr->tagProc) (bindPtr, item, context, bindIds);
126     }
127     nIds = Blt_ListGetLength(bindIds);
128     if (nIds > 0) {
129 	ClientData *idArray;
130 	ClientData tags[32];
131 	register Blt_ListNode node;
132 
133 	idArray = tags;
134 	if (nIds >= 32) {
135 	    idArray = Blt_Malloc(sizeof(ClientData) * nIds);
136 
137 	}
138 	nIds = 0;
139 	for (node = Blt_ListFirstNode(bindIds); node != NULL;
140 	     node = Blt_ListNextNode(node)) {
141 	    idArray[nIds++] = (ClientData)Blt_ListGetKey(node);
142 	}
143 	Tk_BindEvent(bindPtr->bindingTable, eventPtr, bindPtr->tkwin, nIds,
144 		idArray);
145 	if (nIds >= 32) {
146 	    Blt_Free(idArray);
147 	}
148     }
149     Blt_ListDestroy(bindIds);
150 }
151 
152 /*
153  *--------------------------------------------------------------
154  *
155  * PickCurrentItem --
156  *
157  *	Find the topmost item in a legend that contains a given
158  *	location and mark the the current item.  If the current
159  *	item has changed, generate a fake exit event on the old
160  *	current item and a fake enter event on the new current
161  *	item.
162  *
163  * Results:
164  *	None.
165  *
166  * Side effects:
167  *	The current item may change.  If it does, then the commands
168  *	associated with item entry and exit could do just about
169  *	anything.  A binding script could delete the legend, so
170  *	callers should protect themselves with Tcl_Preserve and
171  *	Tcl_Release.
172  *
173  *--------------------------------------------------------------
174  */
175 static void
PickCurrentItem(bindPtr,eventPtr)176 PickCurrentItem(bindPtr, eventPtr)
177     struct Blt_BindTableStruct *bindPtr;	/* Binding table information. */
178     XEvent *eventPtr;		/* Event describing location of
179 				 * mouse cursor.  Must be EnterWindow,
180 				 * LeaveWindow, ButtonRelease, or
181 				 * MotionNotify. */
182 {
183     int buttonDown;
184     ClientData newItem;
185     ClientData newContext;
186 
187     if (Tcl_InterpDeleted(bindPtr->interp)) {
188         return;
189     }
190     /*
191      * Check whether or not a button is down.  If so, we'll log entry
192      * and exit into and out of the current item, but not entry into
193      * any other item.  This implements a form of grabbing equivalent
194      * to what the X server does for windows.
195      */
196     buttonDown = (bindPtr->state & ALL_BUTTONS_MASK);
197     if (!buttonDown) {
198 	bindPtr->flags &= ~LEFT_GRABBED_ITEM;
199     }
200     /*
201      * Save information about this event in the widget.  The event in
202      * the widget is used for two purposes:
203      *
204      * 1. Event bindings: if the current item changes, fake events are
205      *    generated to allow item-enter and item-leave bindings to trigger.
206      * 2. Reselection: if the current item gets deleted, can use the
207      *    saved event to find a new current item.
208      * Translate MotionNotify events into EnterNotify events, since that's
209      * what gets reported to item handlers.
210      */
211 
212     if (eventPtr != &bindPtr->pickEvent) {
213 	if ((eventPtr->type == MotionNotify) ||
214 	    (eventPtr->type == ButtonRelease)) {
215 	    bindPtr->pickEvent.xcrossing.type = EnterNotify;
216 	    bindPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
217 	    bindPtr->pickEvent.xcrossing.send_event =
218 		eventPtr->xmotion.send_event;
219 	    bindPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
220 	    bindPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
221 	    bindPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
222 	    bindPtr->pickEvent.xcrossing.subwindow = None;
223 	    bindPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
224 	    bindPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
225 	    bindPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
226 	    bindPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
227 	    bindPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
228 	    bindPtr->pickEvent.xcrossing.mode = NotifyNormal;
229 	    bindPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
230 	    bindPtr->pickEvent.xcrossing.same_screen
231 		= eventPtr->xmotion.same_screen;
232 	    bindPtr->pickEvent.xcrossing.focus = False;
233 	    bindPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
234 	} else {
235 	    bindPtr->pickEvent = *eventPtr;
236 	}
237     }
238     bindPtr->activePick = TRUE;
239 
240     /*
241      * If this is a recursive call (there's already a partially completed
242      * call pending on the stack;  it's in the middle of processing a
243      * Leave event handler for the old current item) then just return;
244      * the pending call will do everything that's needed.
245      */
246     if (bindPtr->flags & REPICK_IN_PROGRESS) {
247 	return;
248     }
249     /*
250      * A LeaveNotify event automatically means that there's no current
251      * item, so the check for closest item can be skipped.
252      */
253     newContext = NULL;
254     if (bindPtr->pickEvent.type != LeaveNotify) {
255 	int x, y;
256 
257 	x = bindPtr->pickEvent.xcrossing.x;
258 	y = bindPtr->pickEvent.xcrossing.y;
259 	newItem = (*bindPtr->pickProc) (bindPtr->clientData, x, y, &newContext);
260     } else {
261 	newItem = NULL;
262     }
263     if (((newItem == bindPtr->currentItem) &&
264 	 (newContext == bindPtr->currentContext)) &&
265 	(!(bindPtr->flags & LEFT_GRABBED_ITEM))) {
266 	/*
267 	 * Nothing to do:  the current item hasn't changed.
268 	 */
269 	return;
270     }
271 #ifndef FULLY_SIMULATE_GRAB
272     if (((newItem != bindPtr->currentItem) ||
273 	 (newContext != bindPtr->currentContext)) &&
274 	(buttonDown)) {
275 	bindPtr->flags |= LEFT_GRABBED_ITEM;
276 	return;
277     }
278 #endif
279     /*
280      * Simulate a LeaveNotify event on the previous current item and
281      * an EnterNotify event on the new current item.  Remove the "current"
282      * tag from the previous current item and place it on the new current
283      * item.
284      */
285     if ((bindPtr->currentItem != NULL) &&
286 	((newItem != bindPtr->currentItem) ||
287 	 (newContext != bindPtr->currentContext)) &&
288 	!(bindPtr->flags & LEFT_GRABBED_ITEM)) {
289 	XEvent event;
290 
291 	event = bindPtr->pickEvent;
292 	event.type = LeaveNotify;
293 	/*
294 	 * If the event's detail happens to be NotifyInferior the
295 	 * binding mechanism will discard the event.  To be consistent,
296 	 * always use NotifyAncestor.
297 	 */
298 	event.xcrossing.detail = NotifyAncestor;
299 
300 	bindPtr->flags |= REPICK_IN_PROGRESS;
301 	DoEvent(bindPtr, &event, bindPtr->currentItem, bindPtr->currentContext);
302 	bindPtr->flags &= ~REPICK_IN_PROGRESS;
303 
304 	/*
305 	 * Note:  during DoEvent above, it's possible that
306 	 * bindPtr->newItem got reset to NULL because the
307 	 * item was deleted.
308 	 */
309     }
310     if (((newItem != bindPtr->currentItem) ||
311 	 (newContext != bindPtr->currentContext)) &&
312 	(buttonDown)) {
313 	XEvent event;
314 
315 	bindPtr->flags |= LEFT_GRABBED_ITEM;
316 	event = bindPtr->pickEvent;
317 	if ((newItem != bindPtr->newItem) ||
318 	    (newContext != bindPtr->newContext)) {
319 	    ClientData savedItem;
320 	    ClientData savedContext;
321 
322 	    /*
323 	     * Generate <Enter> and <Leave> events for objects during
324 	     * button grabs.  This isn't standard. But for example, it
325 	     * allows one to provide balloon help on the individual
326 	     * entries of the Hierbox widget.
327 	     */
328 	    savedItem = bindPtr->currentItem;
329 	    savedContext = bindPtr->currentContext;
330 	    if (bindPtr->newItem != NULL) {
331 		event.type = LeaveNotify;
332 		event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
333 		bindPtr->currentItem = bindPtr->newItem;
334 		DoEvent(bindPtr, &event, bindPtr->newItem, bindPtr->newContext);
335 	    }
336 	    bindPtr->newItem = newItem;
337 	    bindPtr->newContext = newContext;
338 	    if (newItem != NULL) {
339 		event.type = EnterNotify;
340 		event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
341 		bindPtr->currentItem = newItem;
342 		DoEvent(bindPtr, &event, newItem, newContext);
343 	    }
344 	    bindPtr->currentItem = savedItem;
345 	    bindPtr->currentContext = savedContext;
346 	}
347 	return;
348     }
349     /*
350      * Special note:  it's possible that
351      *		bindPtr->newItem == bindPtr->currentItem
352      * here.  This can happen, for example, if LEFT_GRABBED_ITEM was set.
353      */
354 
355     bindPtr->flags &= ~LEFT_GRABBED_ITEM;
356     bindPtr->currentItem = bindPtr->newItem = newItem;
357     bindPtr->currentContext = bindPtr->newContext = newContext;
358     if (bindPtr->currentItem != NULL) {
359 	XEvent event;
360 
361 	event = bindPtr->pickEvent;
362 	event.type = EnterNotify;
363 	event.xcrossing.detail = NotifyAncestor;
364 	DoEvent(bindPtr, &event, newItem, newContext);
365     }
366 }
367 
368 /*
369  *--------------------------------------------------------------
370  *
371  * BindProc --
372  *
373  *	This procedure is invoked by the Tk dispatcher to handle
374  *	events associated with bindings on items.
375  *
376  * Results:
377  *	None.
378  *
379  * Side effects:
380  *	Depends on the command invoked as part of the binding
381  *	(if there was any).
382  *
383  *--------------------------------------------------------------
384  */
385 static void
BindProc(clientData,eventPtr)386 BindProc(clientData, eventPtr)
387     ClientData clientData;	/* Pointer to widget structure. */
388     XEvent *eventPtr;		/* Pointer to X event that just
389 					 * happened. */
390 {
391     struct Blt_BindTableStruct *bindPtr = clientData;
392     int mask;
393 
394     if (Tcl_InterpDeleted(bindPtr->interp)) {
395         return;
396     }
397     Tcl_Preserve(bindPtr->clientData);
398 
399     /*
400      * This code below keeps track of the current modifier state in
401      * bindPtr->state.  This information is used to defer repicks of
402      * the current item while buttons are down.
403      */
404     switch (eventPtr->type) {
405     case ButtonPress:
406     case ButtonRelease:
407 	mask = 0;
408 	if ((eventPtr->xbutton.button >= Button1) &&
409 	    (eventPtr->xbutton.button <= Button5)) {
410 	    mask = buttonMasks[eventPtr->xbutton.button];
411 	}
412 	/*
413 	 * For button press events, repick the current item using the
414 	 * button state before the event, then process the event.  For
415 	 * button release events, first process the event, then repick
416 	 * the current item using the button state *after* the event
417 	 * (the button has logically gone up before we change the
418 	 * current item).
419 	 */
420 
421 	if (eventPtr->type == ButtonPress) {
422 
423 	    /*
424 	     * On a button press, first repick the current item using
425 	     * the button state before the event, the process the event.
426 	     */
427 
428 	    bindPtr->state = eventPtr->xbutton.state;
429 	    PickCurrentItem(bindPtr, eventPtr);
430 	    bindPtr->state ^= mask;
431 	    DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
432 		bindPtr->currentContext);
433 
434 	} else {
435 
436 	    /*
437 	     * Button release: first process the event, with the button
438 	     * still considered to be down.  Then repick the current
439 	     * item under the assumption that the button is no longer down.
440 	     */
441 	    bindPtr->state = eventPtr->xbutton.state;
442 	    DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
443 		bindPtr->currentContext);
444 	    eventPtr->xbutton.state ^= mask;
445 	    bindPtr->state = eventPtr->xbutton.state;
446 	    PickCurrentItem(bindPtr, eventPtr);
447 	    eventPtr->xbutton.state ^= mask;
448 	}
449 	break;
450 
451     case EnterNotify:
452     case LeaveNotify:
453 	bindPtr->state = eventPtr->xcrossing.state;
454 	PickCurrentItem(bindPtr, eventPtr);
455 	break;
456 
457     case MotionNotify:
458 	bindPtr->state = eventPtr->xmotion.state;
459 	PickCurrentItem(bindPtr, eventPtr);
460 	DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
461 		bindPtr->currentContext);
462 	break;
463 
464     case KeyPress:
465     case KeyRelease:
466 	bindPtr->state = eventPtr->xkey.state;
467 	PickCurrentItem(bindPtr, eventPtr);
468 	DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
469 		bindPtr->currentContext);
470 	break;
471     }
472     Tcl_Release(bindPtr->clientData);
473 }
474 
475 int
Blt_ConfigureBindings(interp,bindPtr,item,argc,argv)476 Blt_ConfigureBindings(interp, bindPtr, item, argc, argv)
477     Tcl_Interp *interp;
478     struct Blt_BindTableStruct *bindPtr;
479     ClientData item;
480     int argc;
481     char **argv;
482 {
483     char *command;
484     unsigned long mask;
485     char *seq;
486 
487     if (argc == 0) {
488 	Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
489 	return TCL_OK;
490     }
491     if (argc == 1) {
492 	command = Tk_GetBinding(interp, bindPtr->bindingTable, item, argv[0]);
493 	if (command == NULL) {
494 	    return TCL_ERROR;
495 	}
496 	Tcl_SetResult(interp, command, TCL_VOLATILE);
497 	return TCL_OK;
498     }
499 
500     seq = argv[0];
501     command = argv[1];
502 
503     if (command[0] == '\0') {
504 	return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
505     }
506 
507     if (command[0] == '+') {
508 	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
509 		command + 1, TRUE);
510     } else {
511 	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
512 		command, FALSE);
513     }
514     if (mask == 0) {
515 	return TCL_ERROR;
516     }
517     if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
518 	Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
519 	Tcl_ResetResult(interp);
520 	Tcl_AppendResult(interp, "requested illegal events; ",
521 		 "only key, button, motion, enter, leave, and virtual ",
522 		 "events may be used", (char *)NULL);
523 	return TCL_ERROR;
524     }
525     return TCL_OK;
526 }
527 
528 
529 #if (TCL_MAJOR_VERSION >= 8)
530 
531 int
Blt_ConfigureBindingsFromObj(interp,bindPtr,item,objc,objv)532 Blt_ConfigureBindingsFromObj(interp, bindPtr, item, objc, objv)
533     Tcl_Interp *interp;
534     struct Blt_BindTableStruct *bindPtr;
535     ClientData item;
536     int objc;
537     Tcl_Obj *CONST *objv;
538 {
539     char *command;
540     unsigned long mask;
541     char *seq;
542     char *string;
543 
544     if (objc == 0) {
545 	Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
546 	return TCL_OK;
547     }
548     string = Tcl_GetString(objv[0]);
549     if (objc == 1) {
550 	command = Tk_GetBinding(interp, bindPtr->bindingTable, item, string);
551 	if (command == NULL) {
552 	    Tcl_ResetResult(interp);
553 	    Tcl_AppendResult(interp, "invalid binding event \"", string, "\"",
554 		(char *)NULL);
555 	    return TCL_ERROR;
556 	}
557 	Tcl_SetResult(interp, command, TCL_VOLATILE);
558 	return TCL_OK;
559     }
560 
561     seq = string;
562     command = Tcl_GetString(objv[1]);
563 
564     if (command[0] == '\0') {
565 	return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
566     }
567 
568     if (command[0] == '+') {
569 	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
570 		command + 1, TRUE);
571     } else {
572 	mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
573 		command, FALSE);
574     }
575     if (mask == 0) {
576 	return TCL_ERROR;
577     }
578     if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
579 	Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
580 	Tcl_ResetResult(interp);
581 	Tcl_AppendResult(interp, "requested illegal events; ",
582 		 "only key, button, motion, enter, leave, and virtual ",
583 		 "events may be used", (char *)NULL);
584 	return TCL_ERROR;
585     }
586     return TCL_OK;
587 }
588 #endif
589 
590 Blt_BindTable
Blt_CreateBindingTable(interp,tkwin,clientData,pickProc,tagProc)591 Blt_CreateBindingTable(interp, tkwin, clientData, pickProc, tagProc)
592     Tcl_Interp *interp;
593     Tk_Window tkwin;
594     ClientData clientData;
595     Blt_BindPickProc *pickProc;
596     Blt_BindTagProc *tagProc;
597 {
598     unsigned int mask;
599     struct Blt_BindTableStruct *bindPtr;
600 
601     bindPtr = Blt_Calloc(1, sizeof(struct Blt_BindTableStruct));
602     assert(bindPtr);
603     bindPtr->interp = interp;
604     bindPtr->clientData = clientData;
605     bindPtr->pickProc = pickProc;
606     bindPtr->tagProc = tagProc;
607     bindPtr->tkwin = tkwin;
608     bindPtr->bindingTable = Tk_CreateBindingTable(interp);
609     mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
610 	ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
611 	PointerMotionMask);
612     Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
613     return bindPtr;
614 }
615 
616 void
Blt_DestroyBindingTable(bindPtr)617 Blt_DestroyBindingTable(bindPtr)
618     struct Blt_BindTableStruct *bindPtr;
619 {
620     unsigned int mask;
621 
622     Tk_DeleteBindingTable(bindPtr->bindingTable);
623     mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
624 	ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
625 	PointerMotionMask);
626     Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
627     Blt_Free(bindPtr);
628 }
629 
630 void
Blt_PickCurrentItem(bindPtr)631 Blt_PickCurrentItem(bindPtr)
632     struct Blt_BindTableStruct *bindPtr;
633 {
634     if (bindPtr->activePick) {
635 	PickCurrentItem(bindPtr, &(bindPtr->pickEvent));
636     }
637 }
638 
639 void
Blt_DeleteBindings(bindPtr,object)640 Blt_DeleteBindings(bindPtr, object)
641     struct Blt_BindTableStruct *bindPtr;
642     ClientData object;
643 {
644     Tk_DeleteAllBindings(bindPtr->bindingTable, object);
645 
646     /*
647      * If this is the object currently picked, we need to repick one.
648      */
649     if (bindPtr->currentItem == object) {
650 	bindPtr->currentItem = NULL;
651 	bindPtr->currentContext = NULL;
652     }
653     if (bindPtr->newItem == object) {
654 	bindPtr->newItem = NULL;
655 	bindPtr->newContext = NULL;
656     }
657     if (bindPtr->focusItem == object) {
658 	bindPtr->focusItem = NULL;
659 	bindPtr->focusContext = NULL;
660     }
661 }
662 
663 void
Blt_MoveBindingTable(bindPtr,tkwin)664 Blt_MoveBindingTable(bindPtr, tkwin)
665     struct Blt_BindTableStruct *bindPtr;
666     Tk_Window tkwin;
667 {
668     unsigned int mask;
669 
670     mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
671 	ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
672 	PointerMotionMask);
673     if (bindPtr->tkwin != NULL) {
674 	Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
675     }
676     Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
677     bindPtr->tkwin = tkwin;
678 }
679