Mercurial > projects > dwt-linux
annotate dwt/widgets/Tracker.d @ 255:5a30aa9820f3
removed tango.stdc.stringz imports and allow null for arrays and string arguments.
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 15 Jun 2008 22:32:20 +0200 |
parents | ce446666f5a2 |
children | c0d810de7093 |
rev | line source |
---|---|
152
17f8449522fd
overloads second walkthrough
Frank Benoit <benoit@tionex.de>
parents:
108
diff
changeset
|
1 /******************************************************************************* |
86 | 2 * Copyright (c) 2000, 2006 IBM Corporation and others. |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
108 | 10 * Port to the D programming language: |
11 * Frank Benoit <benoit@tionex.de> | |
86 | 12 *******************************************************************************/ |
13 module dwt.widgets.Tracker; | |
14 | |
15 | |
16 | |
17 import dwt.DWT; | |
18 import dwt.DWTException; | |
19 import dwt.events.ControlListener; | |
20 import dwt.events.KeyListener; | |
21 import dwt.graphics.Cursor; | |
22 import dwt.graphics.Point; | |
23 import dwt.graphics.Rectangle; | |
24 import dwt.internal.gtk.OS; | |
25 import dwt.widgets.Widget; | |
26 import dwt.widgets.Composite; | |
27 import dwt.widgets.Display; | |
28 import dwt.widgets.TypedListener; | |
29 import dwt.widgets.Event; | |
30 | |
31 import tango.core.Exception; | |
32 import tango.core.Thread; | |
33 | |
34 /** | |
35 * Instances of this class implement rubber banding rectangles that are | |
36 * drawn onto a parent <code>Composite</code> or <code>Display</code>. | |
37 * These rectangles can be specified to respond to mouse and key events | |
38 * by either moving or resizing themselves accordingly. Trackers are | |
39 * typically used to represent window geometries in a lightweight manner. | |
40 * | |
41 * <dl> | |
42 * <dt><b>Styles:</b></dt> | |
43 * <dd>LEFT, RIGHT, UP, DOWN, RESIZE</dd> | |
44 * <dt><b>Events:</b></dt> | |
45 * <dd>Move, Resize</dd> | |
46 * </dl> | |
47 * <p> | |
48 * Note: Rectangle move behavior is assumed unless RESIZE is specified. | |
49 * </p><p> | |
50 * IMPORTANT: This class is <em>not</em> intended to be subclassed. | |
51 * </p> | |
52 */ | |
53 public class Tracker : Widget { | |
54 Composite parent; | |
240 | 55 Cursor cursor; |
56 GdkCursor* lastCursor; | |
86 | 57 GdkDrawable* window; |
58 bool tracking, cancelled, grabbed, stippled; | |
59 Rectangle [] rectangles, proportions; | |
60 Rectangle bounds; | |
61 int cursorOrientation = DWT.NONE; | |
62 int oldX, oldY; | |
63 | |
64 final static int STEPSIZE_SMALL = 1; | |
65 final static int STEPSIZE_LARGE = 9; | |
66 | |
67 /** | |
68 * Constructs a new instance of this class given its parent | |
69 * and a style value describing its behavior and appearance. | |
70 * <p> | |
71 * The style value is either one of the style constants defined in | |
72 * class <code>DWT</code> which is applicable to instances of this | |
73 * class, or must be built by <em>bitwise OR</em>'ing together | |
74 * (that is, using the <code>int</code> "|" operator) two or more | |
75 * of those <code>DWT</code> style constants. The class description | |
76 * lists the style constants that are applicable to the class. | |
77 * Style bits are also inherited from superclasses. | |
78 * </p> | |
79 * | |
80 * @param parent a widget which will be the parent of the new instance (cannot be null) | |
81 * @param style the style of widget to construct | |
82 * | |
83 * @exception IllegalArgumentException <ul> | |
84 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> | |
85 * </ul> | |
86 * @exception DWTException <ul> | |
87 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> | |
88 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> | |
89 * </ul> | |
90 * | |
91 * @see DWT#LEFT | |
92 * @see DWT#RIGHT | |
93 * @see DWT#UP | |
94 * @see DWT#DOWN | |
95 * @see DWT#RESIZE | |
96 * @see Widget#checkSubclass | |
97 * @see Widget#getStyle | |
98 */ | |
99 public this (Composite parent, int style) { | |
100 super (parent, checkStyle(style)); | |
101 this.parent = parent; | |
102 } | |
103 | |
104 /** | |
105 * Constructs a new instance of this class given the display | |
106 * to create it on and a style value describing its behavior | |
107 * and appearance. | |
108 * <p> | |
109 * The style value is either one of the style constants defined in | |
110 * class <code>DWT</code> which is applicable to instances of this | |
111 * class, or must be built by <em>bitwise OR</em>'ing together | |
112 * (that is, using the <code>int</code> "|" operator) two or more | |
113 * of those <code>DWT</code> style constants. The class description | |
114 * lists the style constants that are applicable to the class. | |
115 * Style bits are also inherited from superclasses. | |
116 * </p><p> | |
117 * Note: Currently, null can be passed in for the display argument. | |
118 * This has the effect of creating the tracker on the currently active | |
119 * display if there is one. If there is no current display, the | |
120 * tracker is created on a "default" display. <b>Passing in null as | |
121 * the display argument is not considered to be good coding style, | |
122 * and may not be supported in a future release of DWT.</b> | |
123 * </p> | |
124 * | |
125 * @param display the display to create the tracker on | |
126 * @param style the style of control to construct | |
127 * | |
128 * @exception DWTException <ul> | |
129 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> | |
130 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> | |
131 * </ul> | |
132 * | |
133 * @see DWT#LEFT | |
134 * @see DWT#RIGHT | |
135 * @see DWT#UP | |
136 * @see DWT#DOWN | |
137 */ | |
138 public this (Display display, int style) { | |
139 if (display is null) display = Display.getCurrent (); | |
140 if (display is null) display = Display.getDefault (); | |
141 if (!display.isValidThread ()) { | |
142 error (DWT.ERROR_THREAD_INVALID_ACCESS); | |
143 } | |
144 this.style = checkStyle (style); | |
145 this.display = display; | |
146 } | |
147 | |
148 /** | |
149 * Adds the listener to the collection of listeners who will | |
150 * be notified when the control is moved or resized, by sending | |
151 * it one of the messages defined in the <code>ControlListener</code> | |
152 * interface. | |
153 * | |
154 * @param listener the listener which should be notified | |
155 * | |
156 * @exception IllegalArgumentException <ul> | |
157 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
158 * </ul> | |
159 * @exception DWTException <ul> | |
160 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
161 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
162 * </ul> | |
163 * | |
164 * @see ControlListener | |
165 * @see #removeControlListener | |
166 */ | |
167 public void addControlListener(ControlListener listener) { | |
168 checkWidget(); | |
169 if (listener is null) error (DWT.ERROR_NULL_ARGUMENT); | |
170 TypedListener typedListener = new TypedListener (listener); | |
171 addListener (DWT.Resize, typedListener); | |
172 addListener (DWT.Move, typedListener); | |
173 } | |
174 | |
175 /** | |
176 * Adds the listener to the collection of listeners who will | |
177 * be notified when keys are pressed and released on the system keyboard, by sending | |
178 * it one of the messages defined in the <code>KeyListener</code> | |
179 * interface. | |
180 * | |
181 * @param listener the listener which should be notified | |
182 * | |
183 * @exception IllegalArgumentException <ul> | |
184 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
185 * </ul> | |
186 * @exception DWTException <ul> | |
187 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
188 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
189 * </ul> | |
190 * | |
191 * @see KeyListener | |
192 * @see #removeKeyListener | |
193 */ | |
194 public void addKeyListener(KeyListener listener) { | |
195 checkWidget(); | |
196 if (listener is null) error (DWT.ERROR_NULL_ARGUMENT); | |
197 TypedListener typedListener = new TypedListener (listener); | |
198 addListener(DWT.KeyUp,typedListener); | |
199 addListener(DWT.KeyDown,typedListener); | |
200 } | |
201 | |
202 Point adjustMoveCursor () { | |
203 if (bounds is null) return null; | |
204 int newX = bounds.x + bounds.width / 2; | |
205 int newY = bounds.y; | |
206 | |
207 Point point = display.map (parent, null, newX, newY); | |
208 display.setCursorLocation (point); | |
209 | |
210 /* | |
211 * The call to XWarpPointer does not always place the pointer on the | |
212 * exact location that is specified, so do a query (below) to get the | |
213 * actual location of the pointer after it has been moved. | |
214 */ | |
215 int actualX, actualY, state; | |
216 OS.gdk_window_get_pointer (cast(GdkWindow*)window, &actualX, &actualY, &state); | |
217 return new Point (actualX, actualY); | |
218 } | |
219 | |
220 Point adjustResizeCursor () { | |
221 if (bounds is null) return null; | |
222 int newX, newY; | |
223 | |
224 if ((cursorOrientation & DWT.LEFT) !is 0) { | |
225 newX = bounds.x; | |
226 } else if ((cursorOrientation & DWT.RIGHT) !is 0) { | |
227 newX = bounds.x + bounds.width; | |
228 } else { | |
229 newX = bounds.x + bounds.width / 2; | |
230 } | |
231 | |
232 if ((cursorOrientation & DWT.UP) !is 0) { | |
233 newY = bounds.y; | |
234 } else if ((cursorOrientation & DWT.DOWN) !is 0) { | |
235 newY = bounds.y + bounds.height; | |
236 } else { | |
237 newY = bounds.y + bounds.height / 2; | |
238 } | |
239 | |
240 Point point = display.map (parent, null, newX, newY); | |
241 display.setCursorLocation (point); | |
242 | |
243 /* | |
244 * The call to XWarpPointer does not always place the pointer on the | |
245 * exact location that is specified, so do a query (below) to get the | |
246 * actual location of the pointer after it has been moved. | |
247 */ | |
248 int actualX, actualY, state; | |
249 OS.gdk_window_get_pointer (window, &actualX, &actualY, &state); | |
250 return new Point (actualX, actualY ); | |
251 } | |
252 | |
253 | |
254 /** | |
255 * Stops displaying the tracker rectangles. Note that this is not considered | |
256 * to be a cancelation by the user. | |
257 * | |
258 * @exception DWTException <ul> | |
259 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
260 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
261 * </ul> | |
262 */ | |
263 public void close () { | |
264 checkWidget(); | |
265 tracking = false; | |
266 } | |
267 | |
268 static int checkStyle (int style) { | |
269 if ((style & (DWT.LEFT | DWT.RIGHT | DWT.UP | DWT.DOWN)) is 0) { | |
270 style |= DWT.LEFT | DWT.RIGHT | DWT.UP | DWT.DOWN; | |
271 } | |
272 return style; | |
273 } | |
274 | |
275 Rectangle computeBounds () { | |
276 if (rectangles.length is 0) return null; | |
277 int xMin = rectangles [0].x; | |
278 int yMin = rectangles [0].y; | |
279 int xMax = rectangles [0].x + rectangles [0].width; | |
280 int yMax = rectangles [0].y + rectangles [0].height; | |
281 | |
282 for (int i = 1; i < rectangles.length; i++) { | |
283 if (rectangles [i].x < xMin) xMin = rectangles [i].x; | |
284 if (rectangles [i].y < yMin) yMin = rectangles [i].y; | |
285 int rectRight = rectangles [i].x + rectangles [i].width; | |
286 if (rectRight > xMax) xMax = rectRight; | |
287 int rectBottom = rectangles [i].y + rectangles [i].height; | |
288 if (rectBottom > yMax) yMax = rectBottom; | |
289 } | |
290 | |
291 return new Rectangle (xMin, yMin, xMax - xMin, yMax - yMin); | |
292 } | |
293 | |
294 Rectangle [] computeProportions (Rectangle [] rects) { | |
295 Rectangle [] result = new Rectangle [rects.length]; | |
296 bounds = computeBounds (); | |
297 if (bounds !is null) { | |
298 for (int i = 0; i < rects.length; i++) { | |
299 int x = 0, y = 0, width = 0, height = 0; | |
300 if (bounds.width !is 0) { | |
301 x = (rects [i].x - bounds.x) * 100 / bounds.width; | |
302 width = rects [i].width * 100 / bounds.width; | |
303 } else { | |
304 width = 100; | |
305 } | |
306 if (bounds.height !is 0) { | |
307 y = (rects [i].y - bounds.y) * 100 / bounds.height; | |
308 height = rects [i].height * 100 / bounds.height; | |
309 } else { | |
310 height = 100; | |
311 } | |
312 result [i] = new Rectangle (x, y, width, height); | |
313 } | |
314 } | |
315 return result; | |
316 } | |
317 | |
318 void drawRectangles (Rectangle [] rects) { | |
319 auto window = OS.GDK_ROOT_PARENT (); | |
320 if (parent !is null) { | |
321 window = OS.GTK_WIDGET_WINDOW (parent.paintHandle()); | |
322 } | |
323 if (window is null) return; | |
324 auto gc = OS.gdk_gc_new (window); | |
325 if (gc is null) return; | |
326 auto colormap = OS.gdk_colormap_get_system (); | |
327 GdkColor color; | |
328 OS.gdk_color_white (colormap, &color); | |
329 OS.gdk_gc_set_foreground (gc, &color); | |
330 OS.gdk_gc_set_subwindow (gc, OS.GDK_INCLUDE_INFERIORS); | |
331 OS.gdk_gc_set_function (gc, OS.GDK_XOR); | |
332 for (int i=0; i<rects.length; i++) { | |
333 Rectangle rect = rects [i]; | |
240 | 334 int x = rect.x; |
335 if (parent !is null && (parent.style & DWT.MIRRORED) !is 0) x = parent.getClientWidth () - rect.width - x; | |
336 OS.gdk_draw_rectangle (window, gc, 0, x, rect.y, rect.width, rect.height); | |
86 | 337 } |
338 OS.g_object_unref (gc); | |
339 } | |
340 | |
341 /** | |
342 * Returns the bounds that are being drawn, expressed relative to the parent | |
343 * widget. If the parent is a <code>Display</code> then these are screen | |
344 * coordinates. | |
345 * | |
346 * @return the bounds of the Rectangles being drawn | |
347 * | |
348 * @exception DWTException <ul> | |
349 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
350 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
351 * </ul> | |
352 */ | |
353 public Rectangle [] getRectangles () { | |
354 checkWidget(); | |
355 Rectangle [] result = new Rectangle [rectangles.length]; | |
356 for (int i = 0; i < rectangles.length; i++) { | |
357 Rectangle current = rectangles [i]; | |
358 result [i] = new Rectangle (current.x, current.y, current.width, current.height); | |
359 } | |
360 return result; | |
361 } | |
362 | |
363 /** | |
364 * Returns <code>true</code> if the rectangles are drawn with a stippled line, <code>false</code> otherwise. | |
365 * | |
366 * @return the stippled effect of the rectangles | |
367 * | |
368 * @exception DWTException <ul> | |
369 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
370 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
371 * </ul> | |
372 */ | |
373 public bool getStippled () { | |
374 checkWidget(); | |
375 return stippled; | |
376 } | |
377 | |
378 bool grab () { | |
240 | 379 auto cursor = this.cursor !is null ? this.cursor.handle : null; |
86 | 380 int result = OS.gdk_pointer_grab (window, false, OS.GDK_POINTER_MOTION_MASK | OS.GDK_BUTTON_RELEASE_MASK, window, cursor, OS.GDK_CURRENT_TIME); |
381 return result is OS.GDK_GRAB_SUCCESS; | |
382 } | |
383 | |
384 override int gtk_button_release_event (GtkWidget* widget, GdkEventButton* event) { | |
385 return gtk_mouse (OS.GDK_BUTTON_RELEASE, widget, event); | |
386 } | |
387 | |
388 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* keyEvent) { | |
389 auto result = super.gtk_key_press_event (widget, keyEvent); | |
390 if (result !is 0) return result; | |
391 int stepSize = ((keyEvent.state & OS.GDK_CONTROL_MASK) !is 0) ? STEPSIZE_SMALL : STEPSIZE_LARGE; | |
392 int xChange = 0, yChange = 0; | |
393 switch (keyEvent.keyval) { | |
394 case OS.GDK_Escape: | |
395 cancelled = true; | |
396 // fallthrough | |
397 case OS.GDK_Return: | |
398 tracking = false; | |
399 break; | |
400 case OS.GDK_Left: | |
401 xChange = -stepSize; | |
402 break; | |
403 case OS.GDK_Right: | |
404 xChange = stepSize; | |
405 break; | |
406 case OS.GDK_Up: | |
407 yChange = -stepSize; | |
408 break; | |
409 case OS.GDK_Down: | |
410 yChange = stepSize; | |
411 break; | |
412 default: | |
413 } | |
414 if (xChange !is 0 || yChange !is 0) { | |
415 Rectangle [] oldRectangles = rectangles; | |
416 Rectangle [] rectsToErase = new Rectangle [rectangles.length]; | |
417 for (int i = 0; i < rectangles.length; i++) { | |
418 Rectangle current = rectangles [i]; | |
419 rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); | |
420 } | |
421 Event event = new Event (); | |
422 event.x = oldX + xChange; | |
423 event.y = oldY + yChange; | |
240 | 424 if (parent !is null && (parent.style & DWT.MIRRORED) !is 0) { |
425 event.x = parent.getClientWidth () - event.width - event.x; | |
426 } | |
86 | 427 if ((style & DWT.RESIZE) !is 0) { |
428 resizeRectangles (xChange, yChange); | |
429 sendEvent (DWT.Resize, event); | |
430 /* | |
431 * It is possible (but unlikely) that application | |
432 * code could have disposed the widget in the resize | |
433 * event. If this happens return false to indicate | |
434 * that the tracking has failed. | |
435 */ | |
436 if (isDisposed ()) { | |
437 cancelled = true; | |
438 return 1; | |
439 } | |
440 bool draw = false; | |
441 /* | |
442 * It is possible that application code could have | |
443 * changed the rectangles in the resize event. If this | |
444 * happens then only redraw the tracker if the rectangle | |
445 * values have changed. | |
446 */ | |
447 if (rectangles !is oldRectangles) { | |
448 int length = rectangles.length; | |
449 if (length !is rectsToErase.length) { | |
450 draw = true; | |
451 } else { | |
452 for (int i = 0; i < length; i++) { | |
453 if (rectangles [i] !=/*eq*/ rectsToErase [i]) { | |
454 draw = true; | |
455 break; | |
456 } | |
457 } | |
458 } | |
459 } else { | |
460 draw = true; | |
461 } | |
462 if (draw) { | |
463 drawRectangles (rectsToErase); | |
464 update (); | |
465 drawRectangles (rectangles); | |
466 } | |
467 Point cursorPos = adjustResizeCursor (); | |
468 if (cursorPos !is null) { | |
469 oldX = cursorPos.x; | |
470 oldY = cursorPos.y; | |
471 } | |
472 } else { | |
473 moveRectangles (xChange, yChange); | |
474 sendEvent (DWT.Move, event); | |
475 /* | |
476 * It is possible (but unlikely) that application | |
477 * code could have disposed the widget in the move | |
478 * event. If this happens return false to indicate | |
479 * that the tracking has failed. | |
480 */ | |
481 if (isDisposed ()) { | |
482 cancelled = true; | |
483 return 1; | |
484 } | |
485 bool draw = false; | |
486 /* | |
487 * It is possible that application code could have | |
488 * changed the rectangles in the move event. If this | |
489 * happens then only redraw the tracker if the rectangle | |
490 * values have changed. | |
491 */ | |
492 if (rectangles !is oldRectangles) { | |
493 int length = rectangles.length; | |
494 if (length !is rectsToErase.length) { | |
495 draw = true; | |
496 } else { | |
497 for (int i = 0; i < length; i++) { | |
498 if (rectangles [i] !=/*eq*/ rectsToErase [i]) { | |
499 draw = true; | |
500 break; | |
501 } | |
502 } | |
503 } | |
504 } else { | |
505 draw = true; | |
506 } | |
507 if (draw) { | |
508 drawRectangles (rectsToErase); | |
509 update (); | |
510 drawRectangles (rectangles); | |
511 } | |
512 Point cursorPos = adjustMoveCursor (); | |
513 if (cursorPos !is null) { | |
514 oldX = cursorPos.x; | |
515 oldY = cursorPos.y; | |
516 } | |
517 } | |
518 } | |
519 return result; | |
520 } | |
521 | |
522 override int gtk_motion_notify_event (GtkWidget* widget, GdkEventMotion* eventPtr) { | |
240 | 523 auto cursor = this.cursor !is null ? this.cursor.handle : null; |
86 | 524 if (cursor !is lastCursor) { |
525 ungrab (); | |
526 grabbed = grab (); | |
527 lastCursor = cursor; | |
528 } | |
529 return gtk_mouse (OS.GDK_MOTION_NOTIFY, widget, eventPtr); | |
530 } | |
531 | |
532 private int gtk_mouse (int eventType, GtkWidget* widget, void* eventPtr) { | |
533 int newX, newY; | |
534 OS.gdk_window_get_pointer (window, &newX, &newY, null); | |
535 if (oldX !is newX || oldY !is newY ) { | |
536 Rectangle [] oldRectangles = rectangles; | |
537 Rectangle [] rectsToErase = new Rectangle [rectangles.length]; | |
538 for (int i = 0; i < rectangles.length; i++) { | |
539 Rectangle current = rectangles [i]; | |
540 rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); | |
541 } | |
542 Event event = new Event (); | |
543 if (parent is null) { | |
544 event.x = newX ; | |
545 event.y = newY ; | |
546 } else { | |
547 Point screenCoord = display.map (parent, null, newX , newY ); | |
548 event.x = screenCoord.x; | |
549 event.y = screenCoord.y; | |
550 } | |
551 if ((style & DWT.RESIZE) !is 0) { | |
552 resizeRectangles (newX - oldX, newY - oldY); | |
553 sendEvent (DWT.Resize, event); | |
554 /* | |
555 * It is possible (but unlikely), that application | |
556 * code could have disposed the widget in the resize | |
557 * event. If this happens, return false to indicate | |
558 * that the tracking has failed. | |
559 */ | |
560 if (isDisposed ()) { | |
561 cancelled = true; | |
562 return 1; | |
563 } | |
564 bool draw = false; | |
565 /* | |
566 * It is possible that application code could have | |
567 * changed the rectangles in the resize event. If this | |
568 * happens then only redraw the tracker if the rectangle | |
569 * values have changed. | |
570 */ | |
571 if (rectangles !is oldRectangles) { | |
572 int length = rectangles.length; | |
573 if (length !is rectsToErase.length) { | |
574 draw = true; | |
575 } else { | |
576 for (int i = 0; i < length; i++) { | |
577 if (rectangles [i] !=/*eq*/ rectsToErase [i]) { | |
578 draw = true; | |
579 break; | |
580 } | |
581 } | |
582 } | |
583 } else { | |
584 draw = true; | |
585 } | |
586 if (draw) { | |
587 drawRectangles (rectsToErase); | |
588 update (); | |
589 drawRectangles (rectangles); | |
590 } | |
591 Point cursorPos = adjustResizeCursor (); | |
592 if (cursorPos !is null) { | |
593 newX = cursorPos.x; | |
594 newY = cursorPos.y; | |
595 } | |
596 } else { | |
597 moveRectangles (newX - oldX, newY - oldY); | |
598 sendEvent (DWT.Move, event); | |
599 /* | |
600 * It is possible (but unlikely), that application | |
601 * code could have disposed the widget in the move | |
602 * event. If this happens, return false to indicate | |
603 * that the tracking has failed. | |
604 */ | |
605 if (isDisposed ()) { | |
606 cancelled = true; | |
607 return 1; | |
608 } | |
609 bool draw = false; | |
610 /* | |
611 * It is possible that application code could have | |
612 * changed the rectangles in the move event. If this | |
613 * happens then only redraw the tracker if the rectangle | |
614 * values have changed. | |
615 */ | |
616 if (rectangles !is oldRectangles) { | |
617 int length = rectangles.length; | |
618 if (length !is rectsToErase.length) { | |
619 draw = true; | |
620 } else { | |
621 for (int i = 0; i < length; i++) { | |
622 if (rectangles [i] !=/*eq*/ rectsToErase [i]) { | |
623 draw = true; | |
624 break; | |
625 } | |
626 } | |
627 } | |
628 } else { | |
629 draw = true; | |
630 } | |
631 if (draw) { | |
632 drawRectangles (rectsToErase); | |
633 update (); | |
634 drawRectangles (rectangles); | |
635 } | |
636 } | |
637 oldX = newX ; | |
638 oldY = newY ; | |
639 } | |
640 tracking = eventType !is OS.GDK_BUTTON_RELEASE; | |
641 return 0; | |
642 } | |
643 | |
644 void moveRectangles (int xChange, int yChange) { | |
645 if (bounds is null) return; | |
646 if (xChange < 0 && ((style & DWT.LEFT) is 0)) xChange = 0; | |
647 if (xChange > 0 && ((style & DWT.RIGHT) is 0)) xChange = 0; | |
648 if (yChange < 0 && ((style & DWT.UP) is 0)) yChange = 0; | |
649 if (yChange > 0 && ((style & DWT.DOWN) is 0)) yChange = 0; | |
650 if (xChange is 0 && yChange is 0) return; | |
240 | 651 if (parent !is null && (parent.style & DWT.MIRRORED) !is 0) xChange *= -1; |
86 | 652 bounds.x += xChange; bounds.y += yChange; |
653 for (int i = 0; i < rectangles.length; i++) { | |
654 rectangles [i].x += xChange; | |
655 rectangles [i].y += yChange; | |
656 } | |
657 } | |
658 | |
659 /** | |
660 * Displays the Tracker rectangles for manipulation by the user. Returns when | |
661 * the user has either finished manipulating the rectangles or has cancelled the | |
662 * Tracker. | |
663 * | |
664 * @return <code>true</code> if the user did not cancel the Tracker, <code>false</code> otherwise | |
665 * | |
666 * @exception DWTException <ul> | |
667 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
668 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
669 * </ul> | |
670 */ | |
671 public bool open () { | |
672 checkWidget(); | |
673 window = OS.GDK_ROOT_PARENT (); | |
674 if (parent !is null) { | |
675 window = OS.GTK_WIDGET_WINDOW (parent.paintHandle()); | |
676 } | |
677 if (window is null) return false; | |
678 cancelled = false; | |
679 tracking = true; | |
680 update (); | |
681 drawRectangles (rectangles); | |
682 int oldX, oldY, state; | |
683 OS.gdk_window_get_pointer (window, &oldX, &oldY, &state); | |
684 | |
685 /* | |
686 * if exactly one of UP/DOWN is specified as a style then set the cursor | |
687 * orientation accordingly (the same is done for LEFT/RIGHT styles below) | |
688 */ | |
689 int vStyle = style & (DWT.UP | DWT.DOWN); | |
690 if (vStyle is DWT.UP || vStyle is DWT.DOWN) { | |
691 cursorOrientation |= vStyle; | |
692 } | |
693 int hStyle = style & (DWT.LEFT | DWT.RIGHT); | |
694 if (hStyle is DWT.LEFT || hStyle is DWT.RIGHT) { | |
695 cursorOrientation |= hStyle; | |
696 } | |
697 | |
698 int mask = OS.GDK_BUTTON1_MASK | OS.GDK_BUTTON2_MASK | OS.GDK_BUTTON3_MASK; | |
699 bool mouseDown = (state & mask) !is 0; | |
700 if (!mouseDown) { | |
701 Point cursorPos = null; | |
702 if ((style & DWT.RESIZE) !is 0) { | |
703 cursorPos = adjustResizeCursor (); | |
704 } else { | |
705 cursorPos = adjustMoveCursor (); | |
706 } | |
707 if (cursorPos !is null) { | |
708 oldX = cursorPos.x; | |
709 oldY = cursorPos.y; | |
710 } | |
711 } | |
712 this.oldX = oldX ; | |
713 this.oldY = oldY ; | |
714 | |
715 grabbed = grab (); | |
240 | 716 lastCursor = this.cursor !is null ? this.cursor.handle : null; |
86 | 717 |
718 /* Tracker behaves like a Dialog with its own OS event loop. */ | |
719 GdkEvent* gdkEvent; | |
720 while (tracking) { | |
721 if (parent !is null && parent.isDisposed ()) break; | |
722 GdkEvent* eventPtr; | |
723 while (true) { | |
724 eventPtr = OS.gdk_event_get (); | |
725 if (eventPtr !is null) { | |
726 break; | |
727 } else { | |
158
de2578a843a7
Tango update to rev 3158, TracedException>Exception, fromUtf8z>fromStringz,Fix Bug in MenuItem Thanx to nascent for the report.
Frank Benoit <benoit@tionex.de>
parents:
152
diff
changeset
|
728 try { Thread.sleep(0.050); } catch (Exception ex) {} |
86 | 729 } |
730 } | |
731 gdkEvent = eventPtr; | |
732 auto widget = OS.gtk_get_event_widget (eventPtr); | |
733 switch (gdkEvent.type) { | |
734 case OS.GDK_MOTION_NOTIFY: gtk_motion_notify_event (widget, cast(GdkEventMotion*)eventPtr); break; | |
735 case OS.GDK_BUTTON_RELEASE: gtk_button_release_event (widget, cast(GdkEventButton*)eventPtr); break; | |
736 case OS.GDK_KEY_PRESS: gtk_key_press_event (widget, cast(GdkEventKey*)eventPtr); break; | |
737 case OS.GDK_KEY_RELEASE: gtk_key_release_event (widget, cast(GdkEventKey*)eventPtr); break; | |
738 case OS.GDK_BUTTON_PRESS: | |
739 case OS.GDK_2BUTTON_PRESS: | |
740 case OS.GDK_3BUTTON_PRESS: | |
741 case OS.GDK_ENTER_NOTIFY: | |
742 case OS.GDK_LEAVE_NOTIFY: | |
743 /* Do not dispatch these */ | |
744 break; | |
745 case OS.GDK_EXPOSE: | |
746 update (); | |
747 drawRectangles (rectangles); | |
748 OS.gtk_main_do_event (eventPtr); | |
749 drawRectangles (rectangles); | |
750 break; | |
751 default: | |
752 OS.gtk_main_do_event (eventPtr); | |
753 } | |
754 OS.gdk_event_free (eventPtr); | |
755 } | |
756 if (!isDisposed ()) { | |
757 update (); | |
758 drawRectangles (rectangles); | |
759 } | |
760 ungrab (); | |
761 window = null; | |
762 return !cancelled; | |
763 } | |
764 | |
152
17f8449522fd
overloads second walkthrough
Frank Benoit <benoit@tionex.de>
parents:
108
diff
changeset
|
765 override void releaseWidget () { |
86 | 766 super.releaseWidget (); |
767 parent = null; | |
768 rectangles = proportions = null; | |
769 bounds = null; | |
770 } | |
771 | |
772 /** | |
773 * Removes the listener from the collection of listeners who will | |
774 * be notified when the control is moved or resized. | |
775 * | |
776 * @param listener the listener which should no longer be notified | |
777 * | |
778 * @exception IllegalArgumentException <ul> | |
779 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
780 * </ul> | |
781 * @exception DWTException <ul> | |
782 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
783 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
784 * </ul> | |
785 * | |
786 * @see ControlListener | |
787 * @see #addControlListener | |
788 */ | |
789 public void removeControlListener (ControlListener listener) { | |
790 checkWidget(); | |
791 if (listener is null) error (DWT.ERROR_NULL_ARGUMENT); | |
792 if (eventTable is null) return; | |
793 eventTable.unhook (DWT.Resize, listener); | |
794 eventTable.unhook (DWT.Move, listener); | |
795 } | |
796 | |
797 /** | |
798 * Removes the listener from the collection of listeners who will | |
799 * be notified when keys are pressed and released on the system keyboard. | |
800 * | |
801 * @param listener the listener which should no longer be notified | |
802 * | |
803 * @exception IllegalArgumentException <ul> | |
804 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
805 * </ul> | |
806 * @exception DWTException <ul> | |
807 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
808 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
809 * </ul> | |
810 * | |
811 * @see KeyListener | |
812 * @see #addKeyListener | |
813 */ | |
814 public void removeKeyListener(KeyListener listener) { | |
815 checkWidget(); | |
816 if (listener is null) error (DWT.ERROR_NULL_ARGUMENT); | |
817 if (eventTable is null) return; | |
818 eventTable.unhook (DWT.KeyUp, listener); | |
819 eventTable.unhook (DWT.KeyDown, listener); | |
820 } | |
821 | |
822 void resizeRectangles (int xChange, int yChange) { | |
823 if (bounds is null) return; | |
240 | 824 if (parent !is null && (parent.style & DWT.MIRRORED) !is 0) xChange *= -1; |
86 | 825 /* |
826 * If the cursor orientation has not been set in the orientation of | |
827 * this change then try to set it here. | |
828 */ | |
829 if (xChange < 0 && ((style & DWT.LEFT) !is 0) && ((cursorOrientation & DWT.RIGHT) is 0)) { | |
830 cursorOrientation |= DWT.LEFT; | |
831 } | |
832 if (xChange > 0 && ((style & DWT.RIGHT) !is 0) && ((cursorOrientation & DWT.LEFT) is 0)) { | |
833 cursorOrientation |= DWT.RIGHT; | |
834 } | |
835 if (yChange < 0 && ((style & DWT.UP) !is 0) && ((cursorOrientation & DWT.DOWN) is 0)) { | |
836 cursorOrientation |= DWT.UP; | |
837 } | |
838 if (yChange > 0 && ((style & DWT.DOWN) !is 0) && ((cursorOrientation & DWT.UP) is 0)) { | |
839 cursorOrientation |= DWT.DOWN; | |
840 } | |
841 | |
842 /* | |
843 * If the bounds will flip about the x or y axis then apply the adjustment | |
844 * up to the axis (ie.- where bounds width/height becomes 0), change the | |
845 * cursor's orientation accordingly, and flip each Rectangle's origin (only | |
846 * necessary for > 1 Rectangles) | |
847 */ | |
848 if ((cursorOrientation & DWT.LEFT) !is 0) { | |
849 if (xChange > bounds.width) { | |
850 if ((style & DWT.RIGHT) is 0) return; | |
851 cursorOrientation |= DWT.RIGHT; | |
852 cursorOrientation &= ~DWT.LEFT; | |
853 bounds.x += bounds.width; | |
854 xChange -= bounds.width; | |
855 bounds.width = 0; | |
856 if (proportions.length > 1) { | |
857 for (int i = 0; i < proportions.length; i++) { | |
858 Rectangle proportion = proportions [i]; | |
859 proportion.x = 100 - proportion.x - proportion.width; | |
860 } | |
861 } | |
862 } | |
863 } else if ((cursorOrientation & DWT.RIGHT) !is 0) { | |
864 if (bounds.width < -xChange) { | |
865 if ((style & DWT.LEFT) is 0) return; | |
866 cursorOrientation |= DWT.LEFT; | |
867 cursorOrientation &= ~DWT.RIGHT; | |
868 xChange += bounds.width; | |
869 bounds.width = 0; | |
870 if (proportions.length > 1) { | |
871 for (int i = 0; i < proportions.length; i++) { | |
872 Rectangle proportion = proportions [i]; | |
873 proportion.x = 100 - proportion.x - proportion.width; | |
874 } | |
875 } | |
876 } | |
877 } | |
878 if ((cursorOrientation & DWT.UP) !is 0) { | |
879 if (yChange > bounds.height) { | |
880 if ((style & DWT.DOWN) is 0) return; | |
881 cursorOrientation |= DWT.DOWN; | |
882 cursorOrientation &= ~DWT.UP; | |
883 bounds.y += bounds.height; | |
884 yChange -= bounds.height; | |
885 bounds.height = 0; | |
886 if (proportions.length > 1) { | |
887 for (int i = 0; i < proportions.length; i++) { | |
888 Rectangle proportion = proportions [i]; | |
889 proportion.y = 100 - proportion.y - proportion.height; | |
890 } | |
891 } | |
892 } | |
893 } else if ((cursorOrientation & DWT.DOWN) !is 0) { | |
894 if (bounds.height < -yChange) { | |
895 if ((style & DWT.UP) is 0) return; | |
896 cursorOrientation |= DWT.UP; | |
897 cursorOrientation &= ~DWT.DOWN; | |
898 yChange += bounds.height; | |
899 bounds.height = 0; | |
900 if (proportions.length > 1) { | |
901 for (int i = 0; i < proportions.length; i++) { | |
902 Rectangle proportion = proportions [i]; | |
903 proportion.y = 100 - proportion.y - proportion.height; | |
904 } | |
905 } | |
906 } | |
907 } | |
908 | |
909 // apply the bounds adjustment | |
910 if ((cursorOrientation & DWT.LEFT) !is 0) { | |
911 bounds.x += xChange; | |
912 bounds.width -= xChange; | |
913 } else if ((cursorOrientation & DWT.RIGHT) !is 0) { | |
914 bounds.width += xChange; | |
915 } | |
916 if ((cursorOrientation & DWT.UP) !is 0) { | |
917 bounds.y += yChange; | |
918 bounds.height -= yChange; | |
919 } else if ((cursorOrientation & DWT.DOWN) !is 0) { | |
920 bounds.height += yChange; | |
921 } | |
922 | |
923 Rectangle [] newRects = new Rectangle [rectangles.length]; | |
924 for (int i = 0; i < rectangles.length; i++) { | |
925 Rectangle proportion = proportions[i]; | |
926 newRects[i] = new Rectangle ( | |
927 proportion.x * bounds.width / 100 + bounds.x, | |
928 proportion.y * bounds.height / 100 + bounds.y, | |
929 proportion.width * bounds.width / 100, | |
930 proportion.height * bounds.height / 100); | |
931 } | |
932 rectangles = newRects; | |
933 } | |
934 | |
935 /** | |
936 * Sets the <code>Cursor</code> of the Tracker. If this cursor is <code>null</code> | |
937 * then the cursor reverts to the default. | |
938 * | |
939 * @param newCursor the new <code>Cursor</code> to display | |
940 * | |
941 * @exception DWTException <ul> | |
942 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
943 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
944 * </ul> | |
945 */ | |
946 public void setCursor (Cursor value) { | |
947 checkWidget (); | |
240 | 948 cursor = value; |
86 | 949 } |
950 | |
951 /** | |
952 * Specifies the rectangles that should be drawn, expressed relative to the parent | |
953 * widget. If the parent is a Display then these are screen coordinates. | |
954 * | |
955 * @param rectangles the bounds of the rectangles to be drawn | |
956 * | |
957 * @exception IllegalArgumentException <ul> | |
255
5a30aa9820f3
removed tango.stdc.stringz imports and allow null for arrays and string arguments.
Frank Benoit <benoit@tionex.de>
parents:
240
diff
changeset
|
958 * <li>ERROR_NULL_ARGUMENT - if the set of rectangles contains a null rectangle</li> |
86 | 959 * </ul> |
960 * @exception DWTException <ul> | |
961 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
962 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
963 * </ul> | |
964 */ | |
965 public void setRectangles (Rectangle [] rectangles) { | |
966 checkWidget(); | |
255
5a30aa9820f3
removed tango.stdc.stringz imports and allow null for arrays and string arguments.
Frank Benoit <benoit@tionex.de>
parents:
240
diff
changeset
|
967 // DWT extension: allow null for zero length string |
5a30aa9820f3
removed tango.stdc.stringz imports and allow null for arrays and string arguments.
Frank Benoit <benoit@tionex.de>
parents:
240
diff
changeset
|
968 //if (rectangles is null) error (DWT.ERROR_NULL_ARGUMENT); |
86 | 969 int length = rectangles.length; |
970 this.rectangles = new Rectangle [length]; | |
971 for (int i = 0; i < length; i++) { | |
972 Rectangle current = rectangles [i]; | |
973 if (current is null) error (DWT.ERROR_NULL_ARGUMENT); | |
974 this.rectangles [i] = new Rectangle (current.x, current.y, current.width, current.height); | |
975 } | |
976 proportions = computeProportions (rectangles); | |
977 } | |
978 | |
979 /** | |
980 * Changes the appearance of the line used to draw the rectangles. | |
981 * | |
982 * @param stippled <code>true</code> if rectangle should appear stippled | |
983 * | |
984 * @exception DWTException <ul> | |
985 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
986 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
987 * </ul> | |
988 */ | |
989 public void setStippled (bool stippled) { | |
990 checkWidget(); | |
991 this.stippled = stippled; | |
992 } | |
993 | |
994 void ungrab () { | |
995 if (grabbed) OS.gdk_pointer_ungrab (OS.GDK_CURRENT_TIME); | |
996 } | |
997 | |
998 void update () { | |
999 if (parent !is null) { | |
1000 if (parent.isDisposed ()) return; | |
1001 parent.getShell ().update (); | |
1002 } else { | |
1003 display.update (); | |
1004 } | |
1005 } | |
1006 | |
1007 } |