|
|
1.1 root 1: /*
2: * tkShare.c --
3: *
4: * This module implements a simple mechanism for sharing
5: * mouse- and button-related events among collections of
6: * windows. It is used primarily for menus. For example,
7: * if one menu is posted and mouse moves over the menu button
8: * for a different menu, then the menubutton needs to see the
9: * event so that it can post itself and unpost the first menu.
10: *
11: * Copyright 1990-1992 Regents of the University of California
12: * Permission to use, copy, modify, and distribute this
13: * software and its documentation for any purpose and without
14: * fee is hereby granted, provided that the above copyright
15: * notice appear in all copies. The University of California
16: * makes no representations about the suitability of this
17: * software for any purpose. It is provided "as is" without
18: * express or implied warranty.
19: */
20:
21: #ifndef lint
22: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkShare.c,v 1.10 92/05/31 16:20:12 ouster Exp $ SPRITE (Berkeley)";
23: #endif /* not lint */
24:
25: #include "tkconfig.h"
26: #include "tk.h"
27:
28: /*
29: * the global variable below is used to tell TkPointerEvent
30: * not to do any processing on an event that we're forwarding from one
31: * window to another. This is really ugly. Eventually this file and
32: * tkGrab.c need to get merged together to produce something cleaner.
33: */
34:
35: XEvent *tkShareEventPtr = NULL;
36:
37: /*
38: * Sharing is implemented in terms of groups of windows, where events
39: * are shared among all the windows in a group. One of the following
40: * structures exists for each group.
41: */
42:
43: typedef struct Group {
44: Tk_Uid groupId; /* Identifies group uniquely among all
45: * share groups. */
46: Tk_Window *windows; /* Pointer to array of windows in
47: * this group. Malloc'ed. */
48: int numWindows; /* Number of windows currently in
49: * this group. */
50: Tk_Window lastWindow; /* Last window found that contained
51: * an event. Needed in order to
52: * notify window when mouse moves out
53: * of it. NULL means nobody to
54: * notify. */
55: XEvent *activeEvent; /* If non-NULL, means that a recursive
56: * call to Tk_HandleEvent is in
57: * progress for this share group, and
58: * identifies event. NULL means no
59: * recursive call in progress. Used
60: * to avoid infinite recursion. */
61: struct Group *nextPtr; /* Next in list of all share groups. */
62: } Group;
63:
64: static Group *groupList = NULL; /* First in list of all share groups
65: * currently defined. */
66:
67: /*
68: * Forward declarations for procedures defined later in this file:
69: */
70:
71: static void DeleteGroup _ANSI_ARGS_((Group *groupPtr));
72: static void ShareEventProc _ANSI_ARGS_((ClientData clientData,
73: XEvent *eventPtr));
74:
75: /*
76: *----------------------------------------------------------------------
77: *
78: * Tk_ShareEvents --
79: *
80: * Add tkwin to a group of windows sharing events.
81: *
82: * Results:
83: * None.
84: *
85: * Side effects:
86: * In the future, if a button- or mouse-related event occurs for
87: * any window in the same group as tkwin, but the mouse is actually
88: * in tkwin (the event went to a different window because of a
89: * grab) then a synthetic event will be generated with tkwin as
90: * window and adjusted coordinates.
91: *
92: *----------------------------------------------------------------------
93: */
94:
95: void
96: Tk_ShareEvents(tkwin, groupId)
97: Tk_Window tkwin; /* Token for window. */
98: Tk_Uid groupId; /* Identifier for group among which
99: * events are to be shared. */
100: {
101: register Group *groupPtr;
102:
103: /*
104: * See if this group exists. If so, add the window to the group.
105: */
106:
107: for (groupPtr = groupList; groupPtr != NULL;
108: groupPtr = groupPtr->nextPtr) {
109: Tk_Window *new;
110:
111: if (groupPtr->groupId != groupId) {
112: continue;
113: }
114: new = (Tk_Window *) ckalloc((unsigned)
115: (groupPtr->numWindows+1) * sizeof(Tk_Window *));
116: memcpy((VOID *) (new+1), (VOID *) groupPtr->windows,
117: (groupPtr->numWindows * sizeof(Tk_Window *)));
118: ckfree((char *) groupPtr->windows);
119: groupPtr->windows = new;
120: groupPtr->windows[0] = tkwin;
121: groupPtr->numWindows++;
122: break;
123: }
124:
125: if (groupPtr == NULL) {
126: /*
127: * Group doesn't exist. Make a new one.
128: */
129:
130: groupPtr = (Group *) ckalloc(sizeof(Group));
131: groupPtr->groupId = groupId;
132: groupPtr->windows = (Tk_Window *) ckalloc(sizeof (Tk_Window *));
133: groupPtr->windows[0] = tkwin;
134: groupPtr->numWindows = 1;
135: groupPtr->lastWindow = NULL;
136: groupPtr->activeEvent = NULL;
137: groupPtr->nextPtr = groupList;
138: groupList = groupPtr;
139: }
140:
141: /*
142: * Create an event handler so we find out about relevant events
143: * that are directed to tkwin.
144: */
145:
146: Tk_CreateEventHandler(tkwin,
147: ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
148: ShareEventProc, (ClientData) groupPtr);
149: }
150:
151: /*
152: *----------------------------------------------------------------------
153: *
154: * Tk_UnshareEvents --
155: *
156: * Remove tkwin from a group of windows sharing events.
157: *
158: * Results:
159: * None.
160: *
161: * Side effects:
162: * Tkwin will no longer participate in event-sharing for the
163: * given group, either as source of events or as destination.
164: *
165: *----------------------------------------------------------------------
166: */
167:
168: void
169: Tk_UnshareEvents(tkwin, groupId)
170: Tk_Window tkwin; /* Token for window. */
171: Tk_Uid groupId; /* Identifier for group. */
172: {
173: register Group *groupPtr;
174: int i;
175:
176: for (groupPtr = groupList; groupPtr != NULL;
177: groupPtr = groupPtr->nextPtr) {
178: if (groupPtr->groupId != groupId) {
179: continue;
180: }
181: if (groupPtr->lastWindow == tkwin) {
182: groupPtr->lastWindow = NULL;
183: }
184: for (i = 0; i < groupPtr->numWindows; i++) {
185: if (groupPtr->windows[i] != tkwin) {
186: continue;
187: }
188: if ((i+1) < groupPtr->numWindows) {
189: memcpy((VOID *) (groupPtr->windows + i),
190: (VOID *) (groupPtr->windows + i + 1),
191: (groupPtr->numWindows - (i+1))*sizeof(Tk_Window *));
192: }
193: groupPtr->numWindows--;
194: Tk_DeleteEventHandler(tkwin,
195: ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
196: ShareEventProc, (ClientData) groupPtr);
197: if (groupPtr->numWindows == 0) {
198: DeleteGroup(groupPtr);
199: }
200: return;
201: }
202: }
203: }
204:
205: /*
206: *----------------------------------------------------------------------
207: *
208: * DeleteGroup --
209: *
210: * This procedure is called when a group has no more members.
211: * It deletes the group from the list of existing groups.
212: *
213: * Results:
214: * None.
215: *
216: * Side effects:
217: * Memory gets freed.
218: *
219: *----------------------------------------------------------------------
220: */
221:
222: static void
223: DeleteGroup(groupPtr)
224: Group *groupPtr; /* Group to delete. */
225: {
226: if (groupList == groupPtr) {
227: groupList = groupPtr->nextPtr;
228: } else {
229: register Group *prevPtr;
230:
231: for (prevPtr = groupList; ; prevPtr = prevPtr->nextPtr) {
232: if (prevPtr == NULL) {
233: panic("DeleteGroup couldn't find group on shareList");
234: }
235: if (prevPtr->nextPtr == groupPtr) {
236: prevPtr->nextPtr = groupPtr->nextPtr;
237: break;
238: }
239: }
240: }
241: ckfree((char *) groupPtr->windows);
242: ckfree((char *) groupPtr);
243: }
244:
245: /*
246: *----------------------------------------------------------------------
247: *
248: * ShareEventProc --
249: *
250: * This procedure is invoked by the Tk dispatcher when an event
251: * occurs for which we need to implement sharing.
252: *
253: * Results:
254: * None.
255: *
256: * Side effects:
257: * If the mouse is actually in a window other than the one for
258: * which the event occurred, generate a new event translated to
259: * that window.
260: *
261: *----------------------------------------------------------------------
262: */
263:
264: static void
265: ShareEventProc(clientData, eventPtr)
266: ClientData clientData; /* Information about share group. */
267: register XEvent *eventPtr; /* Event that just occurred. */
268: {
269: register Group *groupPtr = (Group *) clientData;
270: register Tk_Window tkwin;
271: Window window;
272: XEvent newEvent, *savedActive, *savedShareEventPtr;
273: int i, x, y;
274: Tk_Uid savedId;
275: register Group *grpPtr;
276:
277: /*
278: * If this event was a synthetic one that we generated, then
279: * don't bother to process it again.
280: */
281:
282: if (groupPtr->activeEvent == eventPtr) {
283: return;
284: }
285: savedActive = groupPtr->activeEvent;
286: groupPtr->activeEvent = &newEvent;
287: savedId = groupPtr->groupId;
288:
289: savedShareEventPtr = tkShareEventPtr;
290: tkShareEventPtr = &newEvent;
291:
292: /*
293: * Scan through all of the windows for this group to find the
294: * first one (if any) that contains the event.
295: */
296:
297: tkwin = NULL; /* Not needed, but stops compiler warning. */
298: for (i = 0; i < groupPtr->numWindows; i++) {
299: Tk_Window tkwin2;
300:
301: tkwin = groupPtr->windows[i];
302: Tk_GetRootCoords(tkwin, &x, &y);
303: x = eventPtr->xmotion.x_root - x - Tk_Changes(tkwin)->border_width;
304: y = eventPtr->xmotion.y_root - y - Tk_Changes(tkwin)->border_width;
305: if ((x < 0) || (y < 0) || (x >= Tk_Width(tkwin))
306: || (y >= Tk_Height(tkwin))) {
307: continue;
308: }
309: for (tkwin2 = tkwin; ; tkwin2 = Tk_Parent(tkwin2)) {
310: if (tkwin2 == NULL) {
311: goto foundWindow;
312: }
313: if (!Tk_IsMapped(tkwin2)) {
314: break;
315: }
316: if (((Tk_FakeWin *) (tkwin2))->flags & TK_TOP_LEVEL) {
317: goto foundWindow;
318: }
319: }
320: }
321:
322: foundWindow:
323: window = None; /* Not really needed but stops compiler warning. */
324: if (i >= groupPtr->numWindows) {
325: tkwin = NULL;
326: } else {
327: window = Tk_WindowId(tkwin);
328: }
329:
330: /*
331: * SPECIAL NOTE: it is possible that any or all of the information
332: * in groupPtr could be modified as part of the processing of the
333: * events that we generate and hand to Tk_HandleEvent below. For this
334: * to work smoothly, it is imperative that we extract any information
335: * we need from groupPtr (and from tkwin's, since they could be
336: * deleted) before the first call to Tk_HandleEvent below. The code
337: * below may potentially pass an X window identifier to Tk_HandleEvent
338: * after the window has been deleted, but as long as identifiers
339: * aren't recycled Tk_HandleEvent will simply discard the event if
340: * this occurs.
341: */
342:
343: /*
344: * If the pointer is in a different window now than the last time
345: * we were invoked, send a LeaveNotify event to the old window and
346: * an EnterNotify event to the new window.
347: */
348:
349: newEvent = *eventPtr;
350: newEvent.xany.send_event = True;
351: if (tkwin != groupPtr->lastWindow) {
352: newEvent = *eventPtr;
353: newEvent.xany.send_event = True;
354: newEvent.xcrossing.mode = TK_NOTIFY_SHARE;
355: newEvent.xcrossing.detail = NotifyAncestor;
356: newEvent.xcrossing.same_screen = True;
357: newEvent.xcrossing.state = eventPtr->xmotion.state;
358: if (groupPtr->lastWindow != NULL) {
359: newEvent.xcrossing.type = LeaveNotify;
360: newEvent.xcrossing.window = Tk_WindowId(groupPtr->lastWindow);
361: Tk_GetRootCoords(groupPtr->lastWindow, &newEvent.xcrossing.x,
362: &newEvent.xcrossing.y);
363: newEvent.xcrossing.x = eventPtr->xmotion.x_root
364: - newEvent.xcrossing.x
365: - Tk_Changes(groupPtr->lastWindow)->border_width;
366: newEvent.xcrossing.y = eventPtr->xmotion.y_root
367: - newEvent.xcrossing.y
368: - Tk_Changes(groupPtr->lastWindow)->border_width;
369: Tk_HandleEvent(&newEvent);
370: }
371: if (tkwin != NULL) {
372: newEvent.xcrossing.type = EnterNotify;
373: newEvent.xcrossing.window = window;
374: newEvent.xcrossing.x = x;
375: newEvent.xcrossing.y = y;
376: Tk_HandleEvent(&newEvent);
377: }
378: groupPtr->lastWindow = tkwin;
379: }
380:
381: /*
382: * If the pointer is in the window to which the event was sent,
383: * then we needn't do any forwarding at all. Ditto if the pointer
384: * isn't in any window at all.
385: */
386:
387: if ((tkwin != NULL) && (Tk_WindowId(tkwin) != eventPtr->xmotion.window)) {
388: newEvent = *eventPtr;
389: newEvent.xmotion.send_event = True;
390: newEvent.xmotion.window = window;
391: newEvent.xmotion.x = x;
392: newEvent.xmotion.y = y;
393: Tk_HandleEvent(&newEvent);
394: }
395:
396: /*
397: * Only restore the activeEvent if the group still exists.
398: * (It could be deleted as a side effect of processing the event.)
399: */
400:
401: for (grpPtr = groupList; grpPtr != NULL; grpPtr = grpPtr->nextPtr) {
402: if (grpPtr->groupId == savedId) {
403: groupPtr->activeEvent = savedActive;
404: break;
405: }
406: }
407:
408: tkShareEventPtr = savedShareEventPtr;
409: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.