/[dotgnu-pnet]/pnetlib/Xsharp/GrabWindow.cs
ViewVC logotype

Contents of /pnetlib/Xsharp/GrabWindow.cs

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations) (download)
Tue Jun 24 07:52:49 2003 UTC (20 years, 10 months ago) by rweather
Branch: MAIN
Changes since 1.1: +2 -1 lines

Add the internal "PopupControl" class to Forms, to support popup menus and
combox box drop-down lists.

1 /*
2 * GrabWindow.cs - Window that captures all events during an active grab.
3 *
4 * Copyright (C) 2003 Southern Storm Software, Pty Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 namespace Xsharp
22 {
23
24 using System;
25 using System.Runtime.InteropServices;
26 using Xsharp.Events;
27
28 // When a "PopupWindow" instance is displayed on-screen, all mouse
29 // and keyboard events are routed to the grab window. From there,
30 // they are dispatched to the approriate popup window. The grab
31 // ends when the last popup window is unmapped.
32
33 internal class GrabWindow : OverrideWindow
34 {
35 // Internal state.
36 private PopupWindow[] list;
37 private PopupWindow lastEntered;
38 private PopupWindow lastButton;
39 private IntPtr keyBuffer;
40
41 // Constructor.
42 public GrabWindow(Widget parent)
43 : base(parent, -1, -1, 1, 1)
44 {
45 // Create the initial list of popup windows.
46 list = new PopupWindow [0];
47
48 // The grab window is always mapped, but just off-screen
49 // so that it isn't visible to the user.
50 Map();
51 }
52
53 // Destructor.
54 ~GrabWindow()
55 {
56 if(keyBuffer != IntPtr.Zero)
57 {
58 Marshal.FreeHGlobal(keyBuffer);
59 keyBuffer = IntPtr.Zero;
60 }
61 }
62
63 // Grab control of the mouse and keyboard to manage popups.
64 private void Grab()
65 {
66 try
67 {
68 IntPtr display = dpy.Lock();
69 Xlib.Window handle = GetWidgetHandle();
70 Xlib.XGrabKeyboard
71 (display, handle, Xlib.Bool.False,
72 1 /* GrabModeAsync */, 1 /* GrabModeAsync */,
73 dpy.knownEventTime);
74 Xlib.XGrabPointer
75 (display, handle, Xlib.Bool.False,
76 (uint)(EventMask.ButtonPressMask |
77 EventMask.ButtonReleaseMask |
78 EventMask.PointerMotionMask),
79 1 /* GrabModeAsync */, 1 /* GrabModeAsync */,
80 Xlib.Window.Zero,
81 dpy.GetCursor(CursorType.XC_left_ptr),
82 dpy.knownEventTime);
83 Xlib.XFlush(display);
84 }
85 finally
86 {
87 dpy.Unlock();
88 }
89 }
90
91 // Ungrab the mouse and keyboard because there are no more popups.
92 private void Ungrab()
93 {
94 try
95 {
96 IntPtr display = dpy.Lock();
97 Xlib.XUngrabPointer(display, dpy.knownEventTime);
98 Xlib.XUngrabKeyboard(display, dpy.knownEventTime);
99 Xlib.XFlush(display);
100 }
101 finally
102 {
103 dpy.Unlock();
104 }
105 }
106
107 // Add a popup to the top of the mapped list.
108 public void AddPopup(PopupWindow popup)
109 {
110 int index;
111 lock(this)
112 {
113 // See if the popup is already in the list.
114 for(index = 0; index < list.Length; ++index)
115 {
116 if(list[index] == popup)
117 {
118 while(index < (list.Length - 1))
119 {
120 list[index] = list[index + 1];
121 ++index;
122 }
123 list[index] = popup;
124 return;
125 }
126 }
127
128 // Re-allocate the list and add the new item.
129 PopupWindow[] newList = new PopupWindow [list.Length + 1];
130 Array.Copy(list, 0, newList, 0, list.Length);
131 newList[list.Length] = popup;
132 list = newList;
133
134 // If the list now contains one item, then grab.
135 if(list.Length == 1)
136 {
137 Grab();
138 }
139 }
140 }
141
142 // Remove a popup from the mapped list.
143 public void RemovePopup(PopupWindow popup)
144 {
145 int index;
146 lock(this)
147 {
148 for(index = list.Length - 1; index >= 0; --index)
149 {
150 if(list[index] == popup)
151 {
152 // Remove the item from the list.
153 PopupWindow[] newList;
154 newList = new PopupWindow [list.Length - 1];
155 Array.Copy(list, 0, newList, 0, index);
156 Array.Copy(list, index + 1, newList, index,
157 list.Length - index - 1);
158 list = newList;
159
160 // If this was the entered window, then
161 // send it a fake "LeaveNotify" event.
162 if(lastEntered == popup)
163 {
164 lastEntered = null;
165 FakeLeave(popup);
166 }
167
168 // If this was the button window, then clear it.
169 if(lastButton == popup)
170 {
171 lastButton = null;
172 }
173
174 // If the list is now empty, then ungrab.
175 if(list.Length == 0)
176 {
177 Ungrab();
178 }
179 return;
180 }
181 }
182 }
183 }
184
185 // Lower a popup to the bottom of the mapped list.
186 public void LowerPopup(PopupWindow popup)
187 {
188 int index;
189 lock(this)
190 {
191 for(index = list.Length - 1; index >= 0; --index)
192 {
193 if(list[index] == popup)
194 {
195 while(index > 0)
196 {
197 list[index] = list[index - 1];
198 --index;
199 }
200 list[0] = popup;
201 return;
202 }
203 }
204 }
205 }
206
207 // Send a fake "EnterNotify" event to a popup window.
208 private static void FakeEnter(PopupWindow popup)
209 {
210 if(popup != null)
211 {
212 XEvent xevent = new XEvent();
213 xevent.xany.type__ =
214 (Xlib.Xint)(int)(EventType.EnterNotify);
215 popup.DispatchEvent(ref xevent);
216 }
217 }
218
219 // Send a fake "LeaveNotify" event to a popup window.
220 private static void FakeLeave(PopupWindow popup)
221 {
222 if(popup != null)
223 {
224 XEvent xevent = new XEvent();
225 xevent.xany.type__ =
226 (Xlib.Xint)(int)(EventType.LeaveNotify);
227 popup.DispatchEvent(ref xevent);
228 }
229 }
230
231 // Change the "lastEntered" window.
232 private void ChangeEntered(PopupWindow popup)
233 {
234 PopupWindow before = null;
235 PopupWindow after = null;
236 lock(this)
237 {
238 if(lastEntered != popup)
239 {
240 before = lastEntered;
241 after = popup;
242 lastEntered = popup;
243 }
244 }
245 if(before != null)
246 {
247 FakeLeave(before);
248 }
249 if(after != null)
250 {
251 FakeEnter(after);
252 }
253 }
254
255 // Find the popup window that contains a particular mouse position.
256 private PopupWindow Find(int x, int y, bool defaultIsTop)
257 {
258 int index;
259 PopupWindow popup;
260 for(index = 0; index < list.Length; ++index)
261 {
262 popup = list[index];
263 if(x >= popup.x && x < (popup.x + popup.width) &&
264 y >= popup.y && y < (popup.y + popup.height))
265 {
266 return popup;
267 }
268 }
269 if(defaultIsTop && list.Length > 0)
270 {
271 return list[list.Length - 1];
272 }
273 else
274 {
275 return null;
276 }
277 }
278
279 // Dispatch an event to this widget.
280 internal override void DispatchEvent(ref XEvent xevent)
281 {
282 Xlib.KeySym keysym;
283 PopupWindow popup;
284
285 switch(xevent.type)
286 {
287 case EventType.ButtonPress:
288 {
289 // A mouse button was pressed during the grab.
290 lock(this)
291 {
292 if(lastButton != null)
293 {
294 // We currently have a button window, so
295 // all mouse events should go to it.
296 popup = lastButton;
297 }
298 else
299 {
300 // Determine which popup contains the mouse.
301 // If nothing contains, then use the top.
302 popup = Find(xevent.xbutton.x_root,
303 xevent.xbutton.y_root, true);
304 }
305 lastButton = popup;
306 }
307 ChangeEntered(popup);
308 if(popup != null)
309 {
310 // Adjust the co-ordinates and re-dispatch.
311 xevent.xbutton.x__ =
312 (Xlib.Xint)(xevent.xbutton.x_root - popup.x);
313 xevent.xbutton.y__ =
314 (Xlib.Xint)(xevent.xbutton.y_root - popup.y);
315 popup.DispatchEvent(ref xevent);
316 }
317 }
318 break;
319
320 case EventType.ButtonRelease:
321 {
322 // A mouse button was released during the grab.
323 lock(this)
324 {
325 popup = lastButton;
326 if(popup != null)
327 {
328 // Reset "lastButton" if this is the last
329 // button to be released.
330 ModifierMask mask = ModifierMask.AllButtons;
331 mask &= ~(ModifierMask.Button1Mask <<
332 (((int)(xevent.xbutton.button__)) - 1));
333 if((xevent.xbutton.state & mask) == 0)
334 {
335 lastButton = null;
336 }
337 }
338 }
339 ChangeEntered(popup);
340 if(popup != null)
341 {
342 // Adjust the co-ordinates and re-dispatch.
343 xevent.xbutton.x__ =
344 (Xlib.Xint)(xevent.xbutton.x_root - popup.x);
345 xevent.xbutton.y__ =
346 (Xlib.Xint)(xevent.xbutton.y_root - popup.y);
347 popup.DispatchEvent(ref xevent);
348 }
349 }
350 break;
351
352 case EventType.MotionNotify:
353 {
354 // The mouse pointer was moved during the grab.
355 lock(this)
356 {
357 // If there is a last button window, then use
358 // that, otherwise find the one under the mouse.
359 popup = lastButton;
360 if(popup == null)
361 {
362 popup = Find(xevent.xmotion.x_root,
363 xevent.xmotion.y_root, false);
364 }
365 }
366 ChangeEntered(popup);
367 if(popup != null)
368 {
369 // Adjust the co-ordinates and re-dispatch.
370 xevent.xmotion.x__ =
371 (Xlib.Xint)(xevent.xmotion.x_root - popup.x);
372 xevent.xmotion.y__ =
373 (Xlib.Xint)(xevent.xmotion.y_root - popup.y);
374 popup.DispatchEvent(ref xevent);
375 }
376 }
377 break;
378
379 case EventType.KeyPress:
380 {
381 // Convert the event into a symbol and a string.
382 if(keyBuffer == IntPtr.Zero)
383 {
384 keyBuffer = Marshal.AllocHGlobal(32);
385 }
386 keysym = 0;
387 int len = Xlib.XLookupString
388 (ref xevent.xkey, keyBuffer, 32,
389 ref keysym, IntPtr.Zero);
390 String str;
391 if(len > 0)
392 {
393 str = Marshal.PtrToStringAnsi(keyBuffer, len);
394 }
395 else
396 {
397 str = null;
398 }
399
400 // Dispatch the event to the top-most popup.
401 lock(this)
402 {
403 if(list.Length > 0)
404 {
405 popup = list[list.Length - 1];
406 }
407 else
408 {
409 popup = null;
410 }
411 }
412 if(popup != null)
413 {
414 popup.DispatchKeyEvent
415 ((KeyName)keysym, xevent.xkey.state, str);
416 }
417 }
418 break;
419
420 case EventType.KeyRelease:
421 {
422 // Convert the event into a symbol and a string.
423 if(keyBuffer == IntPtr.Zero)
424 {
425 keyBuffer = Marshal.AllocHGlobal(32);
426 }
427 keysym = 0;
428 int len = Xlib.XLookupString
429 (ref xevent.xkey, keyBuffer, 32,
430 ref keysym, IntPtr.Zero);
431
432 // Dispatch the event to the top-most popup.
433 lock(this)
434 {
435 if(list.Length > 0)
436 {
437 popup = list[list.Length - 1];
438 }
439 else
440 {
441 popup = null;
442 }
443 }
444 if(popup != null)
445 {
446 popup.DispatchKeyReleaseEvent
447 ((KeyName)keysym, xevent.xkey.state);
448 }
449 }
450 break;
451
452 default:
453 {
454 // Everything else is handled normally.
455 base.DispatchEvent(ref xevent);
456 }
457 break;
458 }
459 }
460
461 } // class GrabWindow
462
463 } // namespace Xsharp

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26