comparison org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/widgets/Tracker.d @ 0:6dd524f61e62

add dwt win and basic java stuff
author Frank Benoit <benoit@tionex.de>
date Mon, 02 Mar 2009 14:44:16 +0100
parents
children d46287db17ed
comparison
equal deleted inserted replaced
-1:000000000000 0:6dd524f61e62
1 /*******************************************************************************
2 * Copyright (c) 2000, 2008 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
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.swt.widgets.Tracker;
14
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.SWTException;
17 import org.eclipse.swt.events.ControlListener;
18 import org.eclipse.swt.events.KeyListener;
19 import org.eclipse.swt.graphics.Cursor;
20 import org.eclipse.swt.graphics.Point;
21 import org.eclipse.swt.graphics.Rectangle;
22 import org.eclipse.swt.internal.win32.OS;
23
24 import org.eclipse.swt.widgets.Widget;
25 import org.eclipse.swt.widgets.Control;
26 import org.eclipse.swt.widgets.Composite;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.swt.widgets.TypedListener;
29 import org.eclipse.swt.widgets.Shell;
30 import org.eclipse.swt.widgets.Event;
31
32 import java.lang.all;
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 * @see <a href="http://www.eclipse.org/swt/snippets/#tracker">Tracker snippets</a>
54 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
55 */
56 public class Tracker : Widget {
57 Control parent;
58 bool tracking, cancelled, stippled;
59 Rectangle [] rectangles, proportions;
60 Rectangle bounds;
61 HCURSOR resizeCursor;
62 Cursor clientCursor;
63 int cursorOrientation = SWT.NONE;
64 bool inEvent = false;
65 HWND hwndTransparent;
66 WNDPROC oldProc;
67 int oldX, oldY;
68
69 /*
70 * The following values mirror step sizes on Windows
71 */
72 final static int STEPSIZE_SMALL = 1;
73 final static int STEPSIZE_LARGE = 9;
74
75 /**
76 * Constructs a new instance of this class given its parent
77 * and a style value describing its behavior and appearance.
78 * <p>
79 * The style value is either one of the style constants defined in
80 * class <code>SWT</code> which is applicable to instances of this
81 * class, or must be built by <em>bitwise OR</em>'ing together
82 * (that is, using the <code>int</code> "|" operator) two or more
83 * of those <code>SWT</code> style constants. The class description
84 * lists the style constants that are applicable to the class.
85 * Style bits are also inherited from superclasses.
86 * </p>
87 *
88 * @param parent a widget which will be the parent of the new instance (cannot be null)
89 * @param style the style of widget to construct
90 *
91 * @exception IllegalArgumentException <ul>
92 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
93 * </ul>
94 * @exception SWTException <ul>
95 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
96 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
97 * </ul>
98 *
99 * @see SWT#LEFT
100 * @see SWT#RIGHT
101 * @see SWT#UP
102 * @see SWT#DOWN
103 * @see SWT#RESIZE
104 * @see Widget#checkSubclass
105 * @see Widget#getStyle
106 */
107 public this (Composite parent, int style) {
108 super (parent, checkStyle (style));
109 this.parent = parent;
110 }
111
112 /**
113 * Constructs a new instance of this class given the display
114 * to create it on and a style value describing its behavior
115 * and appearance.
116 * <p>
117 * The style value is either one of the style constants defined in
118 * class <code>SWT</code> which is applicable to instances of this
119 * class, or must be built by <em>bitwise OR</em>'ing together
120 * (that is, using the <code>int</code> "|" operator) two or more
121 * of those <code>SWT</code> style constants. The class description
122 * lists the style constants that are applicable to the class.
123 * Style bits are also inherited from superclasses.
124 * </p><p>
125 * Note: Currently, null can be passed in for the display argument.
126 * This has the effect of creating the tracker on the currently active
127 * display if there is one. If there is no current display, the
128 * tracker is created on a "default" display. <b>Passing in null as
129 * the display argument is not considered to be good coding style,
130 * and may not be supported in a future release of SWT.</b>
131 * </p>
132 *
133 * @param display the display to create the tracker on
134 * @param style the style of control to construct
135 *
136 * @exception SWTException <ul>
137 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
138 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
139 * </ul>
140 *
141 * @see SWT#LEFT
142 * @see SWT#RIGHT
143 * @see SWT#UP
144 * @see SWT#DOWN
145 */
146 public this (Display display, int style) {
147 if (display is null) display = Display.getCurrent ();
148 if (display is null) display = Display.getDefault ();
149 if (!display.isValidThread ()) {
150 error (SWT.ERROR_THREAD_INVALID_ACCESS);
151 }
152 this.style = checkStyle (style);
153 this.display = display;
154 }
155
156 /**
157 * Adds the listener to the collection of listeners who will
158 * be notified when the control is moved or resized, by sending
159 * it one of the messages defined in the <code>ControlListener</code>
160 * interface.
161 *
162 * @param listener the listener which should be notified
163 *
164 * @exception IllegalArgumentException <ul>
165 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
166 * </ul>
167 * @exception SWTException <ul>
168 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
169 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
170 * </ul>
171 *
172 * @see ControlListener
173 * @see #removeControlListener
174 */
175 public void addControlListener (ControlListener listener) {
176 checkWidget ();
177 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
178 TypedListener typedListener = new TypedListener (listener);
179 addListener (SWT.Resize, typedListener);
180 addListener (SWT.Move, typedListener);
181 }
182
183 /**
184 * Adds the listener to the collection of listeners who will
185 * be notified when keys are pressed and released on the system keyboard, by sending
186 * it one of the messages defined in the <code>KeyListener</code>
187 * interface.
188 *
189 * @param listener the listener which should be notified
190 *
191 * @exception IllegalArgumentException <ul>
192 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
193 * </ul>
194 * @exception SWTException <ul>
195 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
196 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
197 * </ul>
198 *
199 * @see KeyListener
200 * @see #removeKeyListener
201 */
202 public void addKeyListener (KeyListener listener) {
203 checkWidget ();
204 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
205 TypedListener typedListener = new TypedListener (listener);
206 addListener (SWT.KeyUp,typedListener);
207 addListener (SWT.KeyDown,typedListener);
208 }
209
210 Point adjustMoveCursor () {
211 if (bounds is null) return null;
212 int newX = bounds.x + bounds.width / 2;
213 int newY = bounds.y;
214 POINT pt;
215 pt.x = newX; pt.y = newY;
216 /*
217 * Convert to screen coordinates iff needed
218 */
219 if (parent !is null) {
220 OS.ClientToScreen (parent.handle, &pt);
221 }
222 OS.SetCursorPos (pt.x, pt.y);
223 return new Point (pt.x, pt.y);
224 }
225
226 Point adjustResizeCursor () {
227 if (bounds is null) return null;
228 int newX, newY;
229
230 if ((cursorOrientation & SWT.LEFT) !is 0) {
231 newX = bounds.x;
232 } else if ((cursorOrientation & SWT.RIGHT) !is 0) {
233 newX = bounds.x + bounds.width;
234 } else {
235 newX = bounds.x + bounds.width / 2;
236 }
237
238 if ((cursorOrientation & SWT.UP) !is 0) {
239 newY = bounds.y;
240 } else if ((cursorOrientation & SWT.DOWN) !is 0) {
241 newY = bounds.y + bounds.height;
242 } else {
243 newY = bounds.y + bounds.height / 2;
244 }
245
246 POINT pt;
247 pt.x = newX; pt.y = newY;
248 /*
249 * Convert to screen coordinates iff needed
250 */
251 if (parent !is null) {
252 OS.ClientToScreen (parent.handle, &pt);
253 }
254 OS.SetCursorPos (pt.x, pt.y);
255
256 /*
257 * If the client has not provided a custom cursor then determine
258 * the appropriate resize cursor.
259 */
260 if (clientCursor is null) {
261 HCURSOR newCursor;
262 switch (cursorOrientation) {
263 case SWT.UP:
264 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZENS);
265 break;
266 case SWT.DOWN:
267 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZENS);
268 break;
269 case SWT.LEFT:
270 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZEWE);
271 break;
272 case SWT.RIGHT:
273 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZEWE);
274 break;
275 case SWT.LEFT | SWT.UP:
276 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZENWSE);
277 break;
278 case SWT.RIGHT | SWT.DOWN:
279 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZENWSE);
280 break;
281 case SWT.LEFT | SWT.DOWN:
282 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZENESW);
283 break;
284 case SWT.RIGHT | SWT.UP:
285 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZENESW);
286 break;
287 default:
288 newCursor = OS.LoadCursor (null, cast(TCHAR*)OS.IDC_SIZEALL);
289 break;
290 }
291 OS.SetCursor (newCursor);
292 if (resizeCursor !is null) {
293 OS.DestroyCursor (resizeCursor);
294 }
295 resizeCursor = newCursor;
296 }
297
298 return new Point (pt.x, pt.y);
299 }
300
301 static int checkStyle (int style) {
302 if ((style & (SWT.LEFT | SWT.RIGHT | SWT.UP | SWT.DOWN)) is 0) {
303 style |= SWT.LEFT | SWT.RIGHT | SWT.UP | SWT.DOWN;
304 }
305 return style;
306 }
307
308 /**
309 * Stops displaying the tracker rectangles. Note that this is not considered
310 * to be a cancelation by the user.
311 *
312 * @exception SWTException <ul>
313 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
314 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
315 * </ul>
316 */
317 public void close () {
318 checkWidget ();
319 tracking = false;
320 }
321
322 Rectangle computeBounds () {
323 if (rectangles.length is 0) return null;
324 int xMin = rectangles [0].x;
325 int yMin = rectangles [0].y;
326 int xMax = rectangles [0].x + rectangles [0].width;
327 int yMax = rectangles [0].y + rectangles [0].height;
328
329 for (int i = 1; i < rectangles.length; i++) {
330 if (rectangles [i].x < xMin) xMin = rectangles [i].x;
331 if (rectangles [i].y < yMin) yMin = rectangles [i].y;
332 int rectRight = rectangles [i].x + rectangles [i].width;
333 if (rectRight > xMax) xMax = rectRight;
334 int rectBottom = rectangles [i].y + rectangles [i].height;
335 if (rectBottom > yMax) yMax = rectBottom;
336 }
337
338 return new Rectangle (xMin, yMin, xMax - xMin, yMax - yMin);
339 }
340
341 Rectangle [] computeProportions (Rectangle [] rects) {
342 Rectangle [] result = new Rectangle [rects.length];
343 bounds = computeBounds ();
344 if (bounds !is null) {
345 for (int i = 0; i < rects.length; i++) {
346 int x = 0, y = 0, width = 0, height = 0;
347 if (bounds.width !is 0) {
348 x = (rects [i].x - bounds.x) * 100 / bounds.width;
349 width = rects [i].width * 100 / bounds.width;
350 } else {
351 width = 100;
352 }
353 if (bounds.height !is 0) {
354 y = (rects [i].y - bounds.y) * 100 / bounds.height;
355 height = rects [i].height * 100 / bounds.height;
356 } else {
357 height = 100;
358 }
359 result [i] = new Rectangle (x, y, width, height);
360 }
361 }
362 return result;
363 }
364
365 /**
366 * Draw the rectangles displayed by the tracker.
367 */
368 void drawRectangles (Rectangle [] rects, bool stippled) {
369 if (parent is null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
370 RECT rect1;
371 int bandWidth = stippled ? 3 : 1;
372 for (int i = 0; i < rects.length; i++) {
373 Rectangle rect = rects[i];
374 rect1.left = rect.x - bandWidth;
375 rect1.top = rect.y - bandWidth;
376 rect1.right = rect.x + rect.width + bandWidth * 2;
377 rect1.bottom = rect.y + rect.height + bandWidth * 2;
378 OS.RedrawWindow (hwndTransparent, &rect1, null, OS.RDW_INVALIDATE);
379 }
380 return;
381 }
382 int bandWidth = 1;
383 auto hwndTrack = OS.GetDesktopWindow ();
384 if (parent !is null) hwndTrack = parent.handle;
385 auto hDC = OS.GetDCEx (hwndTrack, null, OS.DCX_CACHE);
386 HBITMAP hBitmap;
387 HBRUSH hBrush, oldBrush;
388 if (stippled) {
389 bandWidth = 3;
390 byte [] bits = [-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0];
391 hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits.ptr);
392 hBrush = OS.CreatePatternBrush (hBitmap);
393 oldBrush = OS.SelectObject (hDC, hBrush);
394 }
395 for (int i=0; i<rects.length; i++) {
396 Rectangle rect = rects [i];
397 OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATINVERT);
398 OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
399 OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
400 OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATINVERT);
401 }
402 if (stippled) {
403 OS.SelectObject (hDC, oldBrush);
404 OS.DeleteObject (hBrush);
405 OS.DeleteObject (hBitmap);
406 }
407 OS.ReleaseDC (hwndTrack, hDC);
408 }
409
410 /**
411 * Returns the bounds that are being drawn, expressed relative to the parent
412 * widget. If the parent is a <code>Display</code> then these are screen
413 * coordinates.
414 *
415 * @return the bounds of the Rectangles being drawn
416 *
417 * @exception SWTException <ul>
418 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
419 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
420 * </ul>
421 */
422 public Rectangle [] getRectangles () {
423 checkWidget();
424 Rectangle [] result = new Rectangle [rectangles.length];
425 for (int i = 0; i < rectangles.length; i++) {
426 Rectangle current = rectangles [i];
427 result [i] = new Rectangle (current.x, current.y, current.width, current.height);
428 }
429 return result;
430 }
431
432 /**
433 * Returns <code>true</code> if the rectangles are drawn with a stippled line, <code>false</code> otherwise.
434 *
435 * @return the stippled effect of the rectangles
436 *
437 * @exception SWTException <ul>
438 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
439 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
440 * </ul>
441 */
442 public bool getStippled () {
443 checkWidget ();
444 return stippled;
445 }
446
447 void moveRectangles (int xChange, int yChange) {
448 if (bounds is null) return;
449 if (xChange < 0 && ((style & SWT.LEFT) is 0)) xChange = 0;
450 if (xChange > 0 && ((style & SWT.RIGHT) is 0)) xChange = 0;
451 if (yChange < 0 && ((style & SWT.UP) is 0)) yChange = 0;
452 if (yChange > 0 && ((style & SWT.DOWN) is 0)) yChange = 0;
453 if (xChange is 0 && yChange is 0) return;
454 bounds.x += xChange; bounds.y += yChange;
455 for (int i = 0; i < rectangles.length; i++) {
456 rectangles [i].x += xChange;
457 rectangles [i].y += yChange;
458 }
459 }
460
461 /**
462 * Displays the Tracker rectangles for manipulation by the user. Returns when
463 * the user has either finished manipulating the rectangles or has cancelled the
464 * Tracker.
465 *
466 * @return <code>true</code> if the user did not cancel the Tracker, <code>false</code> otherwise
467 *
468 * @exception SWTException <ul>
469 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
470 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
471 * </ul>
472 */
473 public bool open () {
474 checkWidget ();
475 cancelled = false;
476 tracking = true;
477
478 /*
479 * If exactly one of UP/DOWN is specified as a style then set the cursor
480 * orientation accordingly (the same is done for LEFT/RIGHT styles below).
481 */
482 int vStyle = style & (SWT.UP | SWT.DOWN);
483 if (vStyle is SWT.UP || vStyle is SWT.DOWN) {
484 cursorOrientation |= vStyle;
485 }
486 int hStyle = style & (SWT.LEFT | SWT.RIGHT);
487 if (hStyle is SWT.LEFT || hStyle is SWT.RIGHT) {
488 cursorOrientation |= hStyle;
489 }
490
491 /*
492 * If this tracker is being created without a mouse drag then
493 * we need to create a transparent window that fills the screen
494 * in order to get all mouse/keyboard events that occur
495 * outside of our visible windows (ie.- over the desktop).
496 */
497 //Callback newProc = null;
498 bool mouseDown = OS.GetKeyState(OS.VK_LBUTTON) < 0;
499 bool isVista = !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0);
500 if ((parent is null && isVista) || !mouseDown) {
501 int width = OS.GetSystemMetrics (OS.SM_CXSCREEN);
502 int height = OS.GetSystemMetrics (OS.SM_CYSCREEN);
503 hwndTransparent = OS.CreateWindowEx (
504 isVista ? OS.WS_EX_LAYERED | OS.WS_EX_NOACTIVATE : OS.WS_EX_TRANSPARENT,
505 display.windowClass_.ptr,
506 null,
507 OS.WS_POPUP,
508 0, 0,
509 width, height,
510 null,
511 null,
512 OS.GetModuleHandle (null),
513 null);
514 oldProc = cast(WNDPROC) OS.GetWindowLongPtr (hwndTransparent, OS.GWLP_WNDPROC);
515 //newProc = new Callback (this, "transparentProc", 4); //$NON-NLS-1$
516 //int newProcAddress = newProc.getAddress ();
517 //if (newProcAddress is 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
518 OS.SetWindowLongPtr (hwndTransparent, OS.GWLP_WNDPROC, cast(LONG_PTR) &transparentFunc );
519
520 //PORTING_FIXME: Vista version
521 version( VISTA ) if (isVista) {
522 OS.SetLayeredWindowAttributes (hwndTransparent, 0xFFFFFF, cast(byte)0xFF, OS.LWA_COLORKEY | OS.LWA_ALPHA);
523 }
524 OS.ShowWindow (hwndTransparent, OS.SW_SHOWNOACTIVATE);
525 }
526
527 update ();
528 drawRectangles (rectangles, stippled);
529 Point cursorPos = null;
530 if (mouseDown) {
531 POINT pt;
532 OS.GetCursorPos (&pt);
533 cursorPos = new Point (pt.x, pt.y);
534 } else {
535 if ((style & SWT.RESIZE) !is 0) {
536 cursorPos = adjustResizeCursor ();
537 } else {
538 cursorPos = adjustMoveCursor ();
539 }
540 }
541 if (cursorPos !is null) {
542 oldX = cursorPos.x;
543 oldY = cursorPos.y;
544 }
545
546 try {
547 /* Tracker behaves like a Dialog with its own OS event loop. */
548 MSG msg;
549 while (tracking && !cancelled) {
550 if (parent !is null && parent.isDisposed ()) break;
551 OS.GetMessage (&msg, null, 0, 0);
552 OS.TranslateMessage (&msg);
553 switch (msg.message) {
554 case OS.WM_LBUTTONUP:
555 case OS.WM_MOUSEMOVE:
556 wmMouse (msg.message, msg.wParam, msg.lParam);
557 break;
558 case OS.WM_IME_CHAR: wmIMEChar (msg.hwnd, msg.wParam, msg.lParam); break;
559 case OS.WM_CHAR: wmChar (msg.hwnd, msg.wParam, msg.lParam); break;
560 case OS.WM_KEYDOWN: wmKeyDown (msg.hwnd, msg.wParam, msg.lParam); break;
561 case OS.WM_KEYUP: wmKeyUp (msg.hwnd, msg.wParam, msg.lParam); break;
562 case OS.WM_SYSCHAR: wmSysChar (msg.hwnd, msg.wParam, msg.lParam); break;
563 case OS.WM_SYSKEYDOWN: wmSysKeyDown (msg.hwnd, msg.wParam, msg.lParam); break;
564 case OS.WM_SYSKEYUP: wmSysKeyUp (msg.hwnd, msg.wParam, msg.lParam); break;
565 default:
566 }
567 if (OS.WM_KEYFIRST <= msg.message && msg.message <= OS.WM_KEYLAST) continue;
568 if (OS.WM_MOUSEFIRST <= msg.message && msg.message <= OS.WM_MOUSELAST) continue;
569 if (!(parent is null && isVista)) {
570 if (msg.message is OS.WM_PAINT) {
571 update ();
572 drawRectangles (rectangles, stippled);
573 }
574 }
575 OS.DispatchMessage (&msg);
576 if (!(parent is null && isVista)) {
577 if (msg.message is OS.WM_PAINT) {
578 drawRectangles (rectangles, stippled);
579 }
580 }
581 }
582 if (mouseDown) OS.ReleaseCapture ();
583 if (!isDisposed()) {
584 update ();
585 drawRectangles (rectangles, stippled);
586 }
587 } finally {
588 /*
589 * Cleanup: If a transparent window was created in order to capture events then
590 * destroy it and its callback object now.
591 */
592 if (hwndTransparent !is null) {
593 OS.DestroyWindow (hwndTransparent);
594 hwndTransparent = null;
595 }
596 //if (newProc !is null) {
597 //newProc.dispose ();
598 oldProc = null;
599 //}
600 /*
601 * Cleanup: If this tracker was resizing then the last cursor that it created
602 * needs to be destroyed.
603 */
604 if (resizeCursor !is null) {
605 OS.DestroyCursor (resizeCursor);
606 resizeCursor = null;
607 }
608 }
609 tracking = false;
610 return !cancelled;
611 }
612
613 override void releaseWidget () {
614 super.releaseWidget ();
615 parent = null;
616 rectangles = proportions = null;
617 bounds = null;
618 }
619
620 /**
621 * Removes the listener from the collection of listeners who will
622 * be notified when the control is moved or resized.
623 *
624 * @param listener the listener which should no longer be notified
625 *
626 * @exception IllegalArgumentException <ul>
627 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
628 * </ul>
629 * @exception SWTException <ul>
630 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
631 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
632 * </ul>
633 *
634 * @see ControlListener
635 * @see #addControlListener
636 */
637 public void removeControlListener (ControlListener listener) {
638 checkWidget ();
639 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
640 if (eventTable is null) return;
641 eventTable.unhook (SWT.Resize, listener);
642 eventTable.unhook (SWT.Move, listener);
643 }
644
645 /**
646 * Removes the listener from the collection of listeners who will
647 * be notified when keys are pressed and released on the system keyboard.
648 *
649 * @param listener the listener which should no longer be notified
650 *
651 * @exception IllegalArgumentException <ul>
652 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
653 * </ul>
654 * @exception SWTException <ul>
655 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
656 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
657 * </ul>
658 *
659 * @see KeyListener
660 * @see #addKeyListener
661 */
662 public void removeKeyListener(KeyListener listener) {
663 checkWidget ();
664 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
665 if (eventTable is null) return;
666 eventTable.unhook (SWT.KeyUp, listener);
667 eventTable.unhook (SWT.KeyDown, listener);
668 }
669
670 void resizeRectangles (int xChange, int yChange) {
671 if (bounds is null) return;
672 /*
673 * If the cursor orientation has not been set in the orientation of
674 * this change then try to set it here.
675 */
676 if (xChange < 0 && ((style & SWT.LEFT) !is 0) && ((cursorOrientation & SWT.RIGHT) is 0)) {
677 cursorOrientation |= SWT.LEFT;
678 }
679 if (xChange > 0 && ((style & SWT.RIGHT) !is 0) && ((cursorOrientation & SWT.LEFT) is 0)) {
680 cursorOrientation |= SWT.RIGHT;
681 }
682 if (yChange < 0 && ((style & SWT.UP) !is 0) && ((cursorOrientation & SWT.DOWN) is 0)) {
683 cursorOrientation |= SWT.UP;
684 }
685 if (yChange > 0 && ((style & SWT.DOWN) !is 0) && ((cursorOrientation & SWT.UP) is 0)) {
686 cursorOrientation |= SWT.DOWN;
687 }
688
689 /*
690 * If the bounds will flip about the x or y axis then apply the adjustment
691 * up to the axis (ie.- where bounds width/height becomes 0), change the
692 * cursor's orientation accordingly, and flip each Rectangle's origin (only
693 * necessary for > 1 Rectangles)
694 */
695 if ((cursorOrientation & SWT.LEFT) !is 0) {
696 if (xChange > bounds.width) {
697 if ((style & SWT.RIGHT) is 0) return;
698 cursorOrientation |= SWT.RIGHT;
699 cursorOrientation &= ~SWT.LEFT;
700 bounds.x += bounds.width;
701 xChange -= bounds.width;
702 bounds.width = 0;
703 if (proportions.length > 1) {
704 for (int i = 0; i < proportions.length; i++) {
705 Rectangle proportion = proportions [i];
706 proportion.x = 100 - proportion.x - proportion.width;
707 }
708 }
709 }
710 } else if ((cursorOrientation & SWT.RIGHT) !is 0) {
711 if (bounds.width < -xChange) {
712 if ((style & SWT.LEFT) is 0) return;
713 cursorOrientation |= SWT.LEFT;
714 cursorOrientation &= ~SWT.RIGHT;
715 xChange += bounds.width;
716 bounds.width = 0;
717 if (proportions.length > 1) {
718 for (int i = 0; i < proportions.length; i++) {
719 Rectangle proportion = proportions [i];
720 proportion.x = 100 - proportion.x - proportion.width;
721 }
722 }
723 }
724 }
725 if ((cursorOrientation & SWT.UP) !is 0) {
726 if (yChange > bounds.height) {
727 if ((style & SWT.DOWN) is 0) return;
728 cursorOrientation |= SWT.DOWN;
729 cursorOrientation &= ~SWT.UP;
730 bounds.y += bounds.height;
731 yChange -= bounds.height;
732 bounds.height = 0;
733 if (proportions.length > 1) {
734 for (int i = 0; i < proportions.length; i++) {
735 Rectangle proportion = proportions [i];
736 proportion.y = 100 - proportion.y - proportion.height;
737 }
738 }
739 }
740 } else if ((cursorOrientation & SWT.DOWN) !is 0) {
741 if (bounds.height < -yChange) {
742 if ((style & SWT.UP) is 0) return;
743 cursorOrientation |= SWT.UP;
744 cursorOrientation &= ~SWT.DOWN;
745 yChange += bounds.height;
746 bounds.height = 0;
747 if (proportions.length > 1) {
748 for (int i = 0; i < proportions.length; i++) {
749 Rectangle proportion = proportions [i];
750 proportion.y = 100 - proportion.y - proportion.height;
751 }
752 }
753 }
754 }
755
756 // apply the bounds adjustment
757 if ((cursorOrientation & SWT.LEFT) !is 0) {
758 bounds.x += xChange;
759 bounds.width -= xChange;
760 } else if ((cursorOrientation & SWT.RIGHT) !is 0) {
761 bounds.width += xChange;
762 }
763 if ((cursorOrientation & SWT.UP) !is 0) {
764 bounds.y += yChange;
765 bounds.height -= yChange;
766 } else if ((cursorOrientation & SWT.DOWN) !is 0) {
767 bounds.height += yChange;
768 }
769
770 Rectangle [] newRects = new Rectangle [rectangles.length];
771 for (int i = 0; i < rectangles.length; i++) {
772 Rectangle proportion = proportions[i];
773 newRects[i] = new Rectangle (
774 proportion.x * bounds.width / 100 + bounds.x,
775 proportion.y * bounds.height / 100 + bounds.y,
776 proportion.width * bounds.width / 100,
777 proportion.height * bounds.height / 100);
778 }
779 rectangles = newRects;
780 }
781
782 /**
783 * Sets the <code>Cursor</code> of the Tracker. If this cursor is <code>null</code>
784 * then the cursor reverts to the default.
785 *
786 * @param newCursor the new <code>Cursor</code> to display
787 *
788 * @exception SWTException <ul>
789 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
790 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
791 * </ul>
792 */
793 public void setCursor(Cursor newCursor) {
794 checkWidget();
795 clientCursor = newCursor;
796 if (newCursor !is null) {
797 if (inEvent) OS.SetCursor (clientCursor.handle);
798 }
799 }
800
801 /**
802 * Specifies the rectangles that should be drawn, expressed relative to the parent
803 * widget. If the parent is a Display then these are screen coordinates.
804 *
805 * @param rectangles the bounds of the rectangles to be drawn
806 *
807 * @exception IllegalArgumentException <ul>
808 * <li>ERROR_NULL_ARGUMENT - if the set of rectangles contains a null rectangle</li>
809 * </ul>
810 * @exception SWTException <ul>
811 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
812 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
813 * </ul>
814 */
815 public void setRectangles (Rectangle [] rectangles) {
816 checkWidget ();
817 // SWT extension: allow null array
818 //if (rectangles is null) error (SWT.ERROR_NULL_ARGUMENT);
819 this.rectangles = new Rectangle [rectangles.length];
820 for (int i = 0; i < rectangles.length; i++) {
821 Rectangle current = rectangles [i];
822 if (current is null) error (SWT.ERROR_NULL_ARGUMENT);
823 this.rectangles [i] = new Rectangle (current.x, current.y, current.width, current.height);
824 }
825 proportions = computeProportions (rectangles);
826 }
827
828 /**
829 * Changes the appearance of the line used to draw the rectangles.
830 *
831 * @param stippled <code>true</code> if rectangle should appear stippled
832 *
833 * @exception SWTException <ul>
834 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
835 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
836 * </ul>
837 */
838 public void setStippled (bool stippled) {
839 checkWidget ();
840 this.stippled = stippled;
841 }
842
843 private static extern(Windows) int transparentFunc (HWND hwnd, int msg, int wParam, int lParam) {
844 Display d = Display.getCurrent();
845 auto t = cast(Tracker) d.findControl( hwnd );
846 return t.transparentProc( hwnd, msg, wParam, lParam );
847 }
848
849 int transparentProc (HWND hwnd, int msg, int wParam, int lParam) {
850 switch (msg) {
851 /*
852 * We typically do not want to answer that the transparent window is
853 * transparent to hits since doing so negates the effect of having it
854 * to grab events. However, clients of the tracker should not be aware
855 * of this transparent window. Therefore if there is a hit query
856 * performed as a result of client code then answer that the transparent
857 * window is transparent to hits so that its existence will not impact
858 * the client.
859 */
860 case OS.WM_NCHITTEST:
861 if (inEvent) return OS.HTTRANSPARENT;
862 break;
863 case OS.WM_SETCURSOR:
864 if (clientCursor !is null) {
865 OS.SetCursor (clientCursor.handle);
866 return 1;
867 }
868 if (resizeCursor !is null) {
869 OS.SetCursor (resizeCursor);
870 return 1;
871 }
872 break;
873 case OS.WM_PAINT:
874 if (parent is null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
875 PAINTSTRUCT ps;
876 auto hDC = OS.BeginPaint (hwnd, &ps);
877 HBITMAP hBitmap;
878 HBRUSH hBrush, oldBrush;
879 auto transparentBrush = OS.CreateSolidBrush(0xFFFFFF);
880 oldBrush = OS.SelectObject (hDC, transparentBrush);
881 OS.PatBlt (hDC, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, OS.PATCOPY);
882 OS.SelectObject (hDC, oldBrush);
883 OS.DeleteObject (transparentBrush);
884 int bandWidth = 1;
885 if (stippled) {
886 bandWidth = 3;
887 byte [] bits = [-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0];
888 hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits.ptr);
889 hBrush = OS.CreatePatternBrush (hBitmap);
890 oldBrush = OS.SelectObject (hDC, hBrush);
891 OS.SetBkColor (hDC, 0xF0F0F0);
892 } else {
893 oldBrush = OS.SelectObject (hDC, OS.GetStockObject(OS.BLACK_BRUSH));
894 }
895 Rectangle[] rects = this.rectangles;
896 for (int i=0; i<rects.length; i++) {
897 Rectangle rect = rects [i];
898 OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATCOPY);
899 OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY);
900 OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY);
901 OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATCOPY);
902 }
903 OS.SelectObject (hDC, oldBrush);
904 if (stippled) {
905 OS.DeleteObject (hBrush);
906 OS.DeleteObject (hBitmap);
907 }
908 OS.EndPaint (hwnd, &ps);
909 return 0;
910 }
911 default:
912 }
913 return OS.CallWindowProc( oldProc, hwnd, msg, wParam, lParam);
914 }
915
916 void update () {
917 if (parent is null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) return;
918 if (parent !is null) {
919 if (parent.isDisposed ()) return;
920 Shell shell = parent.getShell ();
921 shell.update (true);
922 } else {
923 display.update ();
924 }
925 }
926
927 override LRESULT wmKeyDown (HWND hwnd, int wParam, int lParam) {
928 LRESULT result = super.wmKeyDown (hwnd, wParam, lParam);
929 if (result !is null) return result;
930 bool isMirrored = parent !is null && (parent.style & SWT.MIRRORED) !is 0;
931 int stepSize = OS.GetKeyState (OS.VK_CONTROL) < 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE;
932 int xChange = 0, yChange = 0;
933 switch (wParam) {
934 case OS.VK_ESCAPE:
935 cancelled = true;
936 tracking = false;
937 break;
938 case OS.VK_RETURN:
939 tracking = false;
940 break;
941 case OS.VK_LEFT:
942 xChange = isMirrored ? stepSize : -stepSize;
943 break;
944 case OS.VK_RIGHT:
945 xChange = isMirrored ? -stepSize : stepSize;
946 break;
947 case OS.VK_UP:
948 yChange = -stepSize;
949 break;
950 case OS.VK_DOWN:
951 yChange = stepSize;
952 break;
953 default:
954 }
955 if (xChange !is 0 || yChange !is 0) {
956 Rectangle [] oldRectangles = rectangles;
957 bool oldStippled = stippled;
958 Rectangle [] rectsToErase = new Rectangle [rectangles.length];
959 for (int i = 0; i < rectangles.length; i++) {
960 Rectangle current = rectangles [i];
961 rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height);
962 }
963 Event event = new Event ();
964 event.x = oldX + xChange;
965 event.y = oldY + yChange;
966 Point cursorPos;
967 if ((style & SWT.RESIZE) !is 0) {
968 resizeRectangles (xChange, yChange);
969 inEvent = true;
970 sendEvent (SWT.Resize, event);
971 inEvent = false;
972 /*
973 * It is possible (but unlikely) that application
974 * code could have disposed the widget in the resize
975 * event. If this happens return false to indicate
976 * that the tracking has failed.
977 */
978 if (isDisposed ()) {
979 cancelled = true;
980 return LRESULT.ONE;
981 }
982 bool draw = false;
983 /*
984 * It is possible that application code could have
985 * changed the rectangles in the resize event. If this
986 * happens then only redraw the tracker if the rectangle
987 * values have changed.
988 */
989 if (rectangles !is oldRectangles) {
990 int length = rectangles.length;
991 if (length !is rectsToErase.length) {
992 draw = true;
993 } else {
994 for (int i = 0; i < length; i++) {
995 if (rectangles [i]!=/*eq*/rectsToErase [i]) {
996 draw = true;
997 break;
998 }
999 }
1000 }
1001 } else {
1002 draw = true;
1003 }
1004 if (draw) {
1005 drawRectangles (rectsToErase, oldStippled);
1006 update ();
1007 drawRectangles (rectangles, stippled);
1008 }
1009 cursorPos = adjustResizeCursor ();
1010 } else {
1011 moveRectangles (xChange, yChange);
1012 inEvent = true;
1013 sendEvent (SWT.Move, event);
1014 inEvent = false;
1015 /*
1016 * It is possible (but unlikely) that application
1017 * code could have disposed the widget in the move
1018 * event. If this happens return false to indicate
1019 * that the tracking has failed.
1020 */
1021 if (isDisposed ()) {
1022 cancelled = true;
1023 return LRESULT.ONE;
1024 }
1025 bool draw = false;
1026 /*
1027 * It is possible that application code could have
1028 * changed the rectangles in the move event. If this
1029 * happens then only redraw the tracker if the rectangle
1030 * values have changed.
1031 */
1032 if (rectangles !is oldRectangles) {
1033 int length = rectangles.length;
1034 if (length !is rectsToErase.length) {
1035 draw = true;
1036 } else {
1037 for (int i = 0; i < length; i++) {
1038 if (rectangles [i]!=/*eq*/rectsToErase [i]) {
1039 draw = true;
1040 break;
1041 }
1042 }
1043 }
1044 } else {
1045 draw = true;
1046 }
1047 if (draw) {
1048 drawRectangles (rectsToErase, oldStippled);
1049 update ();
1050 drawRectangles (rectangles, stippled);
1051 }
1052 cursorPos = adjustMoveCursor ();
1053 }
1054 if (cursorPos !is null) {
1055 oldX = cursorPos.x;
1056 oldY = cursorPos.y;
1057 }
1058 }
1059 return result;
1060 }
1061
1062 override LRESULT wmSysKeyDown (HWND hwnd, int wParam, int lParam) {
1063 LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam);
1064 if (result !is null) return result;
1065 cancelled = true;
1066 tracking = false;
1067 return result;
1068 }
1069
1070 LRESULT wmMouse (int message, int /*long*/ wParam, int /*long*/ lParam) {
1071 bool isMirrored = parent !is null && (parent.style & SWT.MIRRORED) !is 0;
1072 int newPos = OS.GetMessagePos ();
1073 int newX = OS.GET_X_LPARAM (newPos);
1074 int newY = OS.GET_Y_LPARAM (newPos);
1075 if (newX !is oldX || newY !is oldY) {
1076 Rectangle [] oldRectangles = rectangles;
1077 bool oldStippled = stippled;
1078 Rectangle [] rectsToErase = new Rectangle [rectangles.length];
1079 for (int i = 0; i < rectangles.length; i++) {
1080 Rectangle current = rectangles [i];
1081 rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height);
1082 }
1083 Event event = new Event ();
1084 event.x = newX;
1085 event.y = newY;
1086 if ((style & SWT.RESIZE) !is 0) {
1087 if (isMirrored) {
1088 resizeRectangles (oldX - newX, newY - oldY);
1089 } else {
1090 resizeRectangles (newX - oldX, newY - oldY);
1091 }
1092 inEvent = true;
1093 sendEvent (SWT.Resize, event);
1094 inEvent = false;
1095 /*
1096 * It is possible (but unlikely), that application
1097 * code could have disposed the widget in the resize
1098 * event. If this happens, return false to indicate
1099 * that the tracking has failed.
1100 */
1101 if (isDisposed ()) {
1102 cancelled = true;
1103 return LRESULT.ONE;
1104 }
1105 bool draw = false;
1106 /*
1107 * It is possible that application code could have
1108 * changed the rectangles in the resize event. If this
1109 * happens then only redraw the tracker if the rectangle
1110 * values have changed.
1111 */
1112 if (rectangles !is oldRectangles) {
1113 int length = rectangles.length;
1114 if (length !is rectsToErase.length) {
1115 draw = true;
1116 } else {
1117 for (int i = 0; i < length; i++) {
1118 if (rectangles [i]!=/*eq*/rectsToErase [i]) {
1119 draw = true;
1120 break;
1121 }
1122 }
1123 }
1124 }
1125 else {
1126 draw = true;
1127 }
1128 if (draw) {
1129 drawRectangles (rectsToErase, oldStippled);
1130 update ();
1131 drawRectangles (rectangles, stippled);
1132 }
1133 Point cursorPos = adjustResizeCursor ();
1134 if (cursorPos !is null) {
1135 newX = cursorPos.x;
1136 newY = cursorPos.y;
1137 }
1138 } else {
1139 if (isMirrored) {
1140 moveRectangles (oldX - newX, newY - oldY);
1141 } else {
1142 moveRectangles (newX - oldX, newY - oldY);
1143 }
1144 inEvent = true;
1145 sendEvent (SWT.Move, event);
1146 inEvent = false;
1147 /*
1148 * It is possible (but unlikely), that application
1149 * code could have disposed the widget in the move
1150 * event. If this happens, return false to indicate
1151 * that the tracking has failed.
1152 */
1153 if (isDisposed ()) {
1154 cancelled = true;
1155 return LRESULT.ONE;
1156 }
1157 bool draw = false;
1158 /*
1159 * It is possible that application code could have
1160 * changed the rectangles in the move event. If this
1161 * happens then only redraw the tracker if the rectangle
1162 * values have changed.
1163 */
1164 if (rectangles !is oldRectangles) {
1165 int length = rectangles.length;
1166 if (length !is rectsToErase.length) {
1167 draw = true;
1168 } else {
1169 for (int i = 0; i < length; i++) {
1170 if (rectangles [i]!=/*eq*/rectsToErase [i]) {
1171 draw = true;
1172 break;
1173 }
1174 }
1175 }
1176 } else {
1177 draw = true;
1178 }
1179 if (draw) {
1180 drawRectangles (rectsToErase, oldStippled);
1181 update ();
1182 drawRectangles (rectangles, stippled);
1183 }
1184 }
1185 oldX = newX;
1186 oldY = newY;
1187 }
1188 tracking = message !is OS.WM_LBUTTONUP;
1189 return null;
1190 }
1191
1192 }