KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > widgets > Tracker


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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  *******************************************************************************/

11 package org.eclipse.swt.widgets;
12
13  
14 import org.eclipse.swt.internal.*;
15 import org.eclipse.swt.internal.win32.*;
16 import org.eclipse.swt.graphics.*;
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.events.*;
19
20 /**
21  * Instances of this class implement rubber banding rectangles that are
22  * drawn onto a parent <code>Composite</code> or <code>Display</code>.
23  * These rectangles can be specified to respond to mouse and key events
24  * by either moving or resizing themselves accordingly. Trackers are
25  * typically used to represent window geometries in a lightweight manner.
26  *
27  * <dl>
28  * <dt><b>Styles:</b></dt>
29  * <dd>LEFT, RIGHT, UP, DOWN, RESIZE</dd>
30  * <dt><b>Events:</b></dt>
31  * <dd>Move, Resize</dd>
32  * </dl>
33  * <p>
34  * Note: Rectangle move behavior is assumed unless RESIZE is specified.
35  * </p><p>
36  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
37  * </p>
38  */

39 public class Tracker extends Widget {
40     Control parent;
41     boolean tracking, cancelled, stippled;
42     Rectangle [] rectangles = new Rectangle [0], proportions = rectangles;
43     Rectangle bounds;
44     int resizeCursor, clientCursor, cursorOrientation = SWT.NONE;
45     boolean inEvent = false;
46     int hwndTransparent, oldProc, oldX, oldY;
47
48     /*
49     * The following values mirror step sizes on Windows
50     */

51     final static int STEPSIZE_SMALL = 1;
52     final static int STEPSIZE_LARGE = 9;
53
54 /**
55  * Constructs a new instance of this class given its parent
56  * and a style value describing its behavior and appearance.
57  * <p>
58  * The style value is either one of the style constants defined in
59  * class <code>SWT</code> which is applicable to instances of this
60  * class, or must be built by <em>bitwise OR</em>'ing together
61  * (that is, using the <code>int</code> "|" operator) two or more
62  * of those <code>SWT</code> style constants. The class description
63  * lists the style constants that are applicable to the class.
64  * Style bits are also inherited from superclasses.
65  * </p>
66  *
67  * @param parent a widget which will be the parent of the new instance (cannot be null)
68  * @param style the style of widget to construct
69  *
70  * @exception IllegalArgumentException <ul>
71  * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
72  * </ul>
73  * @exception SWTException <ul>
74  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
75  * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
76  * </ul>
77  *
78  * @see SWT#LEFT
79  * @see SWT#RIGHT
80  * @see SWT#UP
81  * @see SWT#DOWN
82  * @see SWT#RESIZE
83  * @see Widget#checkSubclass
84  * @see Widget#getStyle
85  */

86 public Tracker (Composite parent, int style) {
87     super (parent, checkStyle (style));
88     this.parent = parent;
89 }
90
91 /**
92  * Constructs a new instance of this class given the display
93  * to create it on and a style value describing its behavior
94  * and appearance.
95  * <p>
96  * The style value is either one of the style constants defined in
97  * class <code>SWT</code> which is applicable to instances of this
98  * class, or must be built by <em>bitwise OR</em>'ing together
99  * (that is, using the <code>int</code> "|" operator) two or more
100  * of those <code>SWT</code> style constants. The class description
101  * lists the style constants that are applicable to the class.
102  * Style bits are also inherited from superclasses.
103  * </p><p>
104  * Note: Currently, null can be passed in for the display argument.
105  * This has the effect of creating the tracker on the currently active
106  * display if there is one. If there is no current display, the
107  * tracker is created on a "default" display. <b>Passing in null as
108  * the display argument is not considered to be good coding style,
109  * and may not be supported in a future release of SWT.</b>
110  * </p>
111  *
112  * @param display the display to create the tracker on
113  * @param style the style of control to construct
114  *
115  * @exception SWTException <ul>
116  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
117  * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
118  * </ul>
119  *
120  * @see SWT#LEFT
121  * @see SWT#RIGHT
122  * @see SWT#UP
123  * @see SWT#DOWN
124  */

125 public Tracker (Display display, int style) {
126     if (display == null) display = Display.getCurrent ();
127     if (display == null) display = Display.getDefault ();
128     if (!display.isValidThread ()) {
129         error (SWT.ERROR_THREAD_INVALID_ACCESS);
130     }
131     this.style = checkStyle (style);
132     this.display = display;
133 }
134
135 /**
136  * Adds the listener to the collection of listeners who will
137  * be notified when the control is moved or resized, by sending
138  * it one of the messages defined in the <code>ControlListener</code>
139  * interface.
140  *
141  * @param listener the listener which should be notified
142  *
143  * @exception IllegalArgumentException <ul>
144  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
145  * </ul>
146  * @exception SWTException <ul>
147  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
148  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
149  * </ul>
150  *
151  * @see ControlListener
152  * @see #removeControlListener
153  */

154 public void addControlListener (ControlListener listener) {
155     checkWidget ();
156     if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
157     TypedListener typedListener = new TypedListener (listener);
158     addListener (SWT.Resize, typedListener);
159     addListener (SWT.Move, typedListener);
160 }
161
162 /**
163  * Adds the listener to the collection of listeners who will
164  * be notified when keys are pressed and released on the system keyboard, by sending
165  * it one of the messages defined in the <code>KeyListener</code>
166  * interface.
167  *
168  * @param listener the listener which should be notified
169  *
170  * @exception IllegalArgumentException <ul>
171  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
172  * </ul>
173  * @exception SWTException <ul>
174  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
175  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
176  * </ul>
177  *
178  * @see KeyListener
179  * @see #removeKeyListener
180  */

181 public void addKeyListener (KeyListener listener) {
182     checkWidget ();
183     if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
184     TypedListener typedListener = new TypedListener (listener);
185     addListener (SWT.KeyUp,typedListener);
186     addListener (SWT.KeyDown,typedListener);
187 }
188
189 Point adjustMoveCursor () {
190     if (bounds == null) return null;
191     int newX = bounds.x + bounds.width / 2;
192     int newY = bounds.y;
193     POINT pt = new POINT ();
194     pt.x = newX; pt.y = newY;
195     /*
196      * Convert to screen coordinates iff needed
197      */

198     if (parent != null) {
199         OS.ClientToScreen (parent.handle, pt);
200     }
201     OS.SetCursorPos (pt.x, pt.y);
202     return new Point (pt.x, pt.y);
203 }
204
205 Point adjustResizeCursor () {
206     if (bounds == null) return null;
207     int newX, newY;
208
209     if ((cursorOrientation & SWT.LEFT) != 0) {
210         newX = bounds.x;
211     } else if ((cursorOrientation & SWT.RIGHT) != 0) {
212         newX = bounds.x + bounds.width;
213     } else {
214         newX = bounds.x + bounds.width / 2;
215     }
216
217     if ((cursorOrientation & SWT.UP) != 0) {
218         newY = bounds.y;
219     } else if ((cursorOrientation & SWT.DOWN) != 0) {
220         newY = bounds.y + bounds.height;
221     } else {
222         newY = bounds.y + bounds.height / 2;
223     }
224
225     POINT pt = new POINT ();
226     pt.x = newX; pt.y = newY;
227     /*
228      * Convert to screen coordinates iff needed
229      */

230     if (parent != null) {
231         OS.ClientToScreen (parent.handle, pt);
232     }
233     OS.SetCursorPos (pt.x, pt.y);
234
235     /*
236     * If the client has not provided a custom cursor then determine
237     * the appropriate resize cursor.
238     */

239     if (clientCursor == 0) {
240         int newCursor = 0;
241         switch (cursorOrientation) {
242             case SWT.UP:
243                 newCursor = OS.LoadCursor (0, OS.IDC_SIZENS);
244                 break;
245             case SWT.DOWN:
246                 newCursor = OS.LoadCursor (0, OS.IDC_SIZENS);
247                 break;
248             case SWT.LEFT:
249                 newCursor = OS.LoadCursor (0, OS.IDC_SIZEWE);
250                 break;
251             case SWT.RIGHT:
252                 newCursor = OS.LoadCursor (0, OS.IDC_SIZEWE);
253                 break;
254             case SWT.LEFT | SWT.UP:
255                 newCursor = OS.LoadCursor (0, OS.IDC_SIZENWSE);
256                 break;
257             case SWT.RIGHT | SWT.DOWN:
258                 newCursor = OS.LoadCursor (0, OS.IDC_SIZENWSE);
259                 break;
260             case SWT.LEFT | SWT.DOWN:
261                 newCursor = OS.LoadCursor (0, OS.IDC_SIZENESW);
262                 break;
263             case SWT.RIGHT | SWT.UP:
264                 newCursor = OS.LoadCursor (0, OS.IDC_SIZENESW);
265                 break;
266             default:
267                 newCursor = OS.LoadCursor (0, OS.IDC_SIZEALL);
268                 break;
269         }
270         OS.SetCursor (newCursor);
271         if (resizeCursor != 0) {
272             OS.DestroyCursor (resizeCursor);
273         }
274         resizeCursor = newCursor;
275     }
276         
277     return new Point (pt.x, pt.y);
278 }
279
280 static int checkStyle (int style) {
281     if ((style & (SWT.LEFT | SWT.RIGHT | SWT.UP | SWT.DOWN)) == 0) {
282         style |= SWT.LEFT | SWT.RIGHT | SWT.UP | SWT.DOWN;
283     }
284     return style;
285 }
286
287 /**
288  * Stops displaying the tracker rectangles. Note that this is not considered
289  * to be a cancelation by the user.
290  *
291  * @exception SWTException <ul>
292  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
293  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
294  * </ul>
295  */

296 public void close () {
297     checkWidget ();
298     tracking = false;
299 }
300
301 Rectangle computeBounds () {
302     if (rectangles.length == 0) return null;
303     int xMin = rectangles [0].x;
304     int yMin = rectangles [0].y;
305     int xMax = rectangles [0].x + rectangles [0].width;
306     int yMax = rectangles [0].y + rectangles [0].height;
307     
308     for (int i = 1; i < rectangles.length; i++) {
309         if (rectangles [i].x < xMin) xMin = rectangles [i].x;
310         if (rectangles [i].y < yMin) yMin = rectangles [i].y;
311         int rectRight = rectangles [i].x + rectangles [i].width;
312         if (rectRight > xMax) xMax = rectRight;
313         int rectBottom = rectangles [i].y + rectangles [i].height;
314         if (rectBottom > yMax) yMax = rectBottom;
315     }
316     
317     return new Rectangle (xMin, yMin, xMax - xMin, yMax - yMin);
318 }
319
320 Rectangle [] computeProportions (Rectangle [] rects) {
321     Rectangle [] result = new Rectangle [rects.length];
322     bounds = computeBounds ();
323     if (bounds != null) {
324         for (int i = 0; i < rects.length; i++) {
325             int x = 0, y = 0, width = 0, height = 0;
326             if (bounds.width != 0) {
327                 x = (rects [i].x - bounds.x) * 100 / bounds.width;
328                 width = rects [i].width * 100 / bounds.width;
329             } else {
330                 width = 100;
331             }
332             if (bounds.height != 0) {
333                 y = (rects [i].y - bounds.y) * 100 / bounds.height;
334                 height = rects [i].height * 100 / bounds.height;
335             } else {
336                 height = 100;
337             }
338             result [i] = new Rectangle (x, y, width, height);
339         }
340     }
341     return result;
342 }
343
344 /**
345  * Draw the rectangles displayed by the tracker.
346  */

347 void drawRectangles (Rectangle [] rects, boolean stippled) {
348     if (parent == null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
349         RECT rect1 = new RECT();
350         int bandWidth = stippled ? 3 : 1;
351         for (int i = 0; i < rects.length; i++) {
352             Rectangle rect = rects[i];
353             rect1.left = rect.x - bandWidth;
354             rect1.top = rect.y - bandWidth;
355             rect1.right = rect.x + rect.width + bandWidth * 2;
356             rect1.bottom = rect.y + rect.height + bandWidth * 2;
357             OS.RedrawWindow (hwndTransparent, rect1, 0, OS.RDW_INVALIDATE);
358         }
359         return;
360     }
361     int bandWidth = 1;
362     int hwndTrack = OS.GetDesktopWindow ();
363     if (parent != null) hwndTrack = parent.handle;
364     int hDC = OS.GetDCEx (hwndTrack, 0, OS.DCX_CACHE);
365     int hBitmap = 0, hBrush = 0, oldBrush = 0;
366     if (stippled) {
367         bandWidth = 3;
368         byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0};
369         hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits);
370         hBrush = OS.CreatePatternBrush (hBitmap);
371         oldBrush = OS.SelectObject (hDC, hBrush);
372     }
373     for (int i=0; i<rects.length; i++) {
374         Rectangle rect = rects [i];
375         OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATINVERT);
376         OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
377         OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
378         OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATINVERT);
379     }
380     if (stippled) {
381         OS.SelectObject (hDC, oldBrush);
382         OS.DeleteObject (hBrush);
383         OS.DeleteObject (hBitmap);
384     }
385     OS.ReleaseDC (hwndTrack, hDC);
386 }
387
388 /**
389  * Returns the bounds that are being drawn, expressed relative to the parent
390  * widget. If the parent is a <code>Display</code> then these are screen
391  * coordinates.
392  *
393  * @return the bounds of the Rectangles being drawn
394  *
395  * @exception SWTException <ul>
396  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
397  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
398  * </ul>
399  */

400 public Rectangle [] getRectangles () {
401     checkWidget();
402     Rectangle [] result = new Rectangle [rectangles.length];
403     for (int i = 0; i < rectangles.length; i++) {
404         Rectangle current = rectangles [i];
405         result [i] = new Rectangle (current.x, current.y, current.width, current.height);
406     }
407     return result;
408 }
409
410 /**
411  * Returns <code>true</code> if the rectangles are drawn with a stippled line, <code>false</code> otherwise.
412  *
413  * @return the stippled effect of the rectangles
414  *
415  * @exception SWTException <ul>
416  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
417  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
418  * </ul>
419  */

420 public boolean getStippled () {
421     checkWidget ();
422     return stippled;
423 }
424
425 void moveRectangles (int xChange, int yChange) {
426     if (bounds == null) return;
427     if (xChange < 0 && ((style & SWT.LEFT) == 0)) xChange = 0;
428     if (xChange > 0 && ((style & SWT.RIGHT) == 0)) xChange = 0;
429     if (yChange < 0 && ((style & SWT.UP) == 0)) yChange = 0;
430     if (yChange > 0 && ((style & SWT.DOWN) == 0)) yChange = 0;
431     if (xChange == 0 && yChange == 0) return;
432     bounds.x += xChange; bounds.y += yChange;
433     for (int i = 0; i < rectangles.length; i++) {
434         rectangles [i].x += xChange;
435         rectangles [i].y += yChange;
436     }
437 }
438
439 /**
440  * Displays the Tracker rectangles for manipulation by the user. Returns when
441  * the user has either finished manipulating the rectangles or has cancelled the
442  * Tracker.
443  *
444  * @return <code>true</code> if the user did not cancel the Tracker, <code>false</code> otherwise
445  *
446  * @exception SWTException <ul>
447  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
448  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
449  * </ul>
450  */

451 public boolean open () {
452     checkWidget ();
453     cancelled = false;
454     tracking = true;
455
456     /*
457     * If exactly one of UP/DOWN is specified as a style then set the cursor
458     * orientation accordingly (the same is done for LEFT/RIGHT styles below).
459     */

460     int vStyle = style & (SWT.UP | SWT.DOWN);
461     if (vStyle == SWT.UP || vStyle == SWT.DOWN) {
462         cursorOrientation |= vStyle;
463     }
464     int hStyle = style & (SWT.LEFT | SWT.RIGHT);
465     if (hStyle == SWT.LEFT || hStyle == SWT.RIGHT) {
466         cursorOrientation |= hStyle;
467     }
468
469     /*
470     * If this tracker is being created without a mouse drag then
471     * we need to create a transparent window that fills the screen
472     * in order to get all mouse/keyboard events that occur
473     * outside of our visible windows (ie.- over the desktop).
474     */

475     Callback newProc = null;
476     boolean mouseDown = OS.GetKeyState(OS.VK_LBUTTON) < 0;
477     boolean isVista = !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0);
478     if ((parent == null && isVista) || !mouseDown) {
479         int width = OS.GetSystemMetrics (OS.SM_CXSCREEN);
480         int height = OS.GetSystemMetrics (OS.SM_CYSCREEN);
481         hwndTransparent = OS.CreateWindowEx (
482             isVista ? OS.WS_EX_LAYERED | OS.WS_EX_NOACTIVATE : OS.WS_EX_TRANSPARENT,
483             display.windowClass,
484             null,
485             OS.WS_POPUP,
486             0, 0,
487             width, height,
488             0,
489             0,
490             OS.GetModuleHandle (null),
491             null);
492         oldProc = OS.GetWindowLong (hwndTransparent, OS.GWL_WNDPROC);
493         newProc = new Callback (this, "transparentProc", 4); //$NON-NLS-1$
494
int newProcAddress = newProc.getAddress ();
495         if (newProcAddress == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
496         OS.SetWindowLong (hwndTransparent, OS.GWL_WNDPROC, newProcAddress);
497         if (isVista) {
498             OS.SetLayeredWindowAttributes (hwndTransparent, 0xFFFFFF, (byte)0xFF, OS.LWA_COLORKEY | OS.LWA_ALPHA);
499         }
500         OS.ShowWindow (hwndTransparent, OS.SW_SHOWNOACTIVATE);
501     }
502
503     update ();
504     drawRectangles (rectangles, stippled);
505     Point cursorPos = null;
506     if (mouseDown) {
507         POINT pt = new POINT ();
508         OS.GetCursorPos (pt);
509         cursorPos = new Point (pt.x, pt.y);
510     } else {
511         if ((style & SWT.RESIZE) != 0) {
512             cursorPos = adjustResizeCursor ();
513         } else {
514             cursorPos = adjustMoveCursor ();
515         }
516     }
517     if (cursorPos != null) {
518         oldX = cursorPos.x;
519         oldY = cursorPos.y;
520     }
521
522     try {
523         /* Tracker behaves like a Dialog with its own OS event loop. */
524         MSG msg = new MSG ();
525         while (tracking && !cancelled) {
526             if (parent != null && parent.isDisposed ()) break;
527             OS.GetMessage (msg, 0, 0, 0);
528             OS.TranslateMessage (msg);
529             switch (msg.message) {
530                 case OS.WM_LBUTTONUP:
531                 case OS.WM_MOUSEMOVE:
532                     wmMouse (msg.message, msg.wParam, msg.lParam);
533                     break;
534                 case OS.WM_IME_CHAR: wmIMEChar (msg.hwnd, msg.wParam, msg.lParam); break;
535                 case OS.WM_CHAR: wmChar (msg.hwnd, msg.wParam, msg.lParam); break;
536                 case OS.WM_KEYDOWN: wmKeyDown (msg.hwnd, msg.wParam, msg.lParam); break;
537                 case OS.WM_KEYUP: wmKeyUp (msg.hwnd, msg.wParam, msg.lParam); break;
538                 case OS.WM_SYSCHAR: wmSysChar (msg.hwnd, msg.wParam, msg.lParam); break;
539                 case OS.WM_SYSKEYDOWN: wmSysKeyDown (msg.hwnd, msg.wParam, msg.lParam); break;
540                 case OS.WM_SYSKEYUP: wmSysKeyUp (msg.hwnd, msg.wParam, msg.lParam); break;
541             }
542             if (OS.WM_KEYFIRST <= msg.message && msg.message <= OS.WM_KEYLAST) continue;
543             if (OS.WM_MOUSEFIRST <= msg.message && msg.message <= OS.WM_MOUSELAST) continue;
544             if (!(parent == null && isVista)) {
545                 if (msg.message == OS.WM_PAINT) {
546                     update ();
547                     drawRectangles (rectangles, stippled);
548                 }
549             }
550             OS.DispatchMessage (msg);
551             if (!(parent == null && isVista)) {
552                 if (msg.message == OS.WM_PAINT) {
553                     drawRectangles (rectangles, stippled);
554                 }
555             }
556         }
557         if (mouseDown) OS.ReleaseCapture ();
558         if (!isDisposed()) {
559             update ();
560             drawRectangles (rectangles, stippled);
561         }
562     } finally {
563         /*
564         * Cleanup: If a transparent window was created in order to capture events then
565         * destroy it and its callback object now.
566         */

567         if (hwndTransparent != 0) {
568             OS.DestroyWindow (hwndTransparent);
569             hwndTransparent = 0;
570         }
571         if (newProc != null) {
572             newProc.dispose ();
573             oldProc = 0;
574         }
575         /*
576         * Cleanup: If this tracker was resizing then the last cursor that it created
577         * needs to be destroyed.
578         */

579         if (resizeCursor != 0) {
580             OS.DestroyCursor (resizeCursor);
581             resizeCursor = 0;
582         }
583     }
584     tracking = false;
585     return !cancelled;
586 }
587
588 void releaseWidget () {
589     super.releaseWidget ();
590     parent = null;
591     rectangles = proportions = null;
592     bounds = null;
593 }
594
595 /**
596  * Removes the listener from the collection of listeners who will
597  * be notified when the control is moved or resized.
598  *
599  * @param listener the listener which should no longer be notified
600  *
601  * @exception IllegalArgumentException <ul>
602  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
603  * </ul>
604  * @exception SWTException <ul>
605  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
606  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
607  * </ul>
608  *
609  * @see ControlListener
610  * @see #addControlListener
611  */

612 public void removeControlListener (ControlListener listener) {
613     checkWidget ();
614     if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
615     if (eventTable == null) return;
616     eventTable.unhook (SWT.Resize, listener);
617     eventTable.unhook (SWT.Move, listener);
618 }
619
620 /**
621  * Removes the listener from the collection of listeners who will
622  * be notified when keys are pressed and released on the system keyboard.
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 KeyListener
635  * @see #addKeyListener
636  */

637 public void removeKeyListener(KeyListener listener) {
638     checkWidget ();
639     if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
640     if (eventTable == null) return;
641     eventTable.unhook (SWT.KeyUp, listener);
642     eventTable.unhook (SWT.KeyDown, listener);
643 }
644
645 void resizeRectangles (int xChange, int yChange) {
646     if (bounds == null) return;
647     /*
648     * If the cursor orientation has not been set in the orientation of
649     * this change then try to set it here.
650     */

651     if (xChange < 0 && ((style & SWT.LEFT) != 0) && ((cursorOrientation & SWT.RIGHT) == 0)) {
652         cursorOrientation |= SWT.LEFT;
653     }
654     if (xChange > 0 && ((style & SWT.RIGHT) != 0) && ((cursorOrientation & SWT.LEFT) == 0)) {
655         cursorOrientation |= SWT.RIGHT;
656     }
657     if (yChange < 0 && ((style & SWT.UP) != 0) && ((cursorOrientation & SWT.DOWN) == 0)) {
658         cursorOrientation |= SWT.UP;
659     }
660     if (yChange > 0 && ((style & SWT.DOWN) != 0) && ((cursorOrientation & SWT.UP) == 0)) {
661         cursorOrientation |= SWT.DOWN;
662     }
663     
664     /*
665      * If the bounds will flip about the x or y axis then apply the adjustment
666      * up to the axis (ie.- where bounds width/height becomes 0), change the
667      * cursor's orientation accordingly, and flip each Rectangle's origin (only
668      * necessary for > 1 Rectangles)
669      */

670     if ((cursorOrientation & SWT.LEFT) != 0) {
671         if (xChange > bounds.width) {
672             if ((style & SWT.RIGHT) == 0) return;
673             cursorOrientation |= SWT.RIGHT;
674             cursorOrientation &= ~SWT.LEFT;
675             bounds.x += bounds.width;
676             xChange -= bounds.width;
677             bounds.width = 0;
678             if (proportions.length > 1) {
679                 for (int i = 0; i < proportions.length; i++) {
680                     Rectangle proportion = proportions [i];
681                     proportion.x = 100 - proportion.x - proportion.width;
682                 }
683             }
684         }
685     } else if ((cursorOrientation & SWT.RIGHT) != 0) {
686         if (bounds.width < -xChange) {
687             if ((style & SWT.LEFT) == 0) return;
688             cursorOrientation |= SWT.LEFT;
689             cursorOrientation &= ~SWT.RIGHT;
690             xChange += bounds.width;
691             bounds.width = 0;
692             if (proportions.length > 1) {
693                 for (int i = 0; i < proportions.length; i++) {
694                     Rectangle proportion = proportions [i];
695                     proportion.x = 100 - proportion.x - proportion.width;
696                 }
697             }
698         }
699     }
700     if ((cursorOrientation & SWT.UP) != 0) {
701         if (yChange > bounds.height) {
702             if ((style & SWT.DOWN) == 0) return;
703             cursorOrientation |= SWT.DOWN;
704             cursorOrientation &= ~SWT.UP;
705             bounds.y += bounds.height;
706             yChange -= bounds.height;
707             bounds.height = 0;
708             if (proportions.length > 1) {
709                 for (int i = 0; i < proportions.length; i++) {
710                     Rectangle proportion = proportions [i];
711                     proportion.y = 100 - proportion.y - proportion.height;
712                 }
713             }
714         }
715     } else if ((cursorOrientation & SWT.DOWN) != 0) {
716         if (bounds.height < -yChange) {
717             if ((style & SWT.UP) == 0) return;
718             cursorOrientation |= SWT.UP;
719             cursorOrientation &= ~SWT.DOWN;
720             yChange += bounds.height;
721             bounds.height = 0;
722             if (proportions.length > 1) {
723                 for (int i = 0; i < proportions.length; i++) {
724                     Rectangle proportion = proportions [i];
725                     proportion.y = 100 - proportion.y - proportion.height;
726                 }
727             }
728         }
729     }
730     
731     // apply the bounds adjustment
732
if ((cursorOrientation & SWT.LEFT) != 0) {
733         bounds.x += xChange;
734         bounds.width -= xChange;
735     } else if ((cursorOrientation & SWT.RIGHT) != 0) {
736         bounds.width += xChange;
737     }
738     if ((cursorOrientation & SWT.UP) != 0) {
739         bounds.y += yChange;
740         bounds.height -= yChange;
741     } else if ((cursorOrientation & SWT.DOWN) != 0) {
742         bounds.height += yChange;
743     }
744     
745     Rectangle [] newRects = new Rectangle [rectangles.length];
746     for (int i = 0; i < rectangles.length; i++) {
747         Rectangle proportion = proportions[i];
748         newRects[i] = new Rectangle (
749             proportion.x * bounds.width / 100 + bounds.x,
750             proportion.y * bounds.height / 100 + bounds.y,
751             proportion.width * bounds.width / 100,
752             proportion.height * bounds.height / 100);
753     }
754     rectangles = newRects;
755 }
756
757 /**
758  * Sets the <code>Cursor</code> of the Tracker. If this cursor is <code>null</code>
759  * then the cursor reverts to the default.
760  *
761  * @param newCursor the new <code>Cursor</code> to display
762  *
763  * @exception SWTException <ul>
764  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
765  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
766  * </ul>
767  */

768 public void setCursor(Cursor newCursor) {
769     checkWidget();
770     clientCursor = 0;
771     if (newCursor != null) {
772         clientCursor = newCursor.handle;
773         if (inEvent) OS.SetCursor (clientCursor);
774     }
775 }
776
777 /**
778  * Specifies the rectangles that should be drawn, expressed relative to the parent
779  * widget. If the parent is a Display then these are screen coordinates.
780  *
781  * @param rectangles the bounds of the rectangles to be drawn
782  *
783  * @exception IllegalArgumentException <ul>
784  * <li>ERROR_NULL_ARGUMENT - if the set of rectangles is null or contains a null rectangle</li>
785  * </ul>
786  * @exception SWTException <ul>
787  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
788  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
789  * </ul>
790  */

791 public void setRectangles (Rectangle [] rectangles) {
792     checkWidget ();
793     if (rectangles == null) error (SWT.ERROR_NULL_ARGUMENT);
794     this.rectangles = new Rectangle [rectangles.length];
795     for (int i = 0; i < rectangles.length; i++) {
796         Rectangle current = rectangles [i];
797         if (current == null) error (SWT.ERROR_NULL_ARGUMENT);
798         this.rectangles [i] = new Rectangle (current.x, current.y, current.width, current.height);
799     }
800     proportions = computeProportions (rectangles);
801 }
802
803 /**
804  * Changes the appearance of the line used to draw the rectangles.
805  *
806  * @param stippled <code>true</code> if rectangle should appear stippled
807  *
808  * @exception SWTException <ul>
809  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
810  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
811  * </ul>
812  */

813 public void setStippled (boolean stippled) {
814     checkWidget ();
815     this.stippled = stippled;
816 }
817
818 int transparentProc (int hwnd, int msg, int wParam, int lParam) {
819     switch (msg) {
820         /*
821         * We typically do not want to answer that the transparent window is
822         * transparent to hits since doing so negates the effect of having it
823         * to grab events. However, clients of the tracker should not be aware
824         * of this transparent window. Therefore if there is a hit query
825         * performed as a result of client code then answer that the transparent
826         * window is transparent to hits so that its existence will not impact
827         * the client.
828         */

829         case OS.WM_NCHITTEST:
830             if (inEvent) return OS.HTTRANSPARENT;
831             break;
832         case OS.WM_SETCURSOR:
833             if (clientCursor != 0) {
834                 OS.SetCursor (clientCursor);
835                 return 1;
836             }
837             if (resizeCursor != 0) {
838                 OS.SetCursor (resizeCursor);
839                 return 1;
840             }
841             break;
842         case OS.WM_PAINT:
843             if (parent == null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
844                 PAINTSTRUCT ps = new PAINTSTRUCT();
845                 int hDC = OS.BeginPaint (hwnd, ps);
846                 int hBitmap = 0, hBrush = 0, oldBrush = 0;
847                 int transparentBrush = OS.CreateSolidBrush(0xFFFFFF);
848                 oldBrush = OS.SelectObject (hDC, transparentBrush);
849                 OS.PatBlt (hDC, ps.left, ps.top, ps.right - ps.left, ps.bottom - ps.top, OS.PATCOPY);
850                 OS.SelectObject (hDC, oldBrush);
851                 OS.DeleteObject (transparentBrush);
852                 int bandWidth = 1;
853                 if (stippled) {
854                     bandWidth = 3;
855                     byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0};
856                     hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits);
857                     hBrush = OS.CreatePatternBrush (hBitmap);
858                     oldBrush = OS.SelectObject (hDC, hBrush);
859                     OS.SetBkColor (hDC, 0xF0F0F0);
860                 } else {
861                     oldBrush = OS.SelectObject (hDC, OS.GetStockObject(OS.BLACK_BRUSH));
862                 }
863                 Rectangle[] rects = this.rectangles;
864                 for (int i=0; i<rects.length; i++) {
865                     Rectangle rect = rects [i];
866                     OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATCOPY);
867                     OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY);
868                     OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY);
869                     OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATCOPY);
870                 }
871                 OS.SelectObject (hDC, oldBrush);
872                 if (stippled) {
873                     OS.DeleteObject (hBrush);
874                     OS.DeleteObject (hBitmap);
875                 }
876                 OS.EndPaint (hwnd, ps);
877                 return 0;
878             }
879     }
880     return OS.CallWindowProc (oldProc, hwnd, msg, wParam, lParam);
881 }
882
883 void update () {
884     if (parent == null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) return;
885     if (parent != null) {
886         if (parent.isDisposed ()) return;
887         Shell shell = parent.getShell ();
888         shell.update (true);
889     } else {
890         display.update ();
891     }
892 }
893
894 LRESULT wmKeyDown (int hwnd, int wParam, int lParam) {
895     LRESULT result = super.wmKeyDown (hwnd, wParam, lParam);
896     if (result != null) return result;
897     boolean isMirrored = parent != null && (parent.style & SWT.MIRRORED) != 0;
898     int stepSize = OS.GetKeyState (OS.VK_CONTROL) < 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE;
899     int xChange = 0, yChange = 0;
900     switch (wParam) {
901         case OS.VK_ESCAPE:
902             cancelled = true;
903             tracking = false;
904             break;
905         case OS.VK_RETURN:
906             tracking = false;
907             break;
908         case OS.VK_LEFT:
909             xChange = isMirrored ? stepSize : -stepSize;
910             break;
911         case OS.VK_RIGHT:
912             xChange = isMirrored ? -stepSize : stepSize;
913             break;
914         case OS.VK_UP:
915             yChange = -stepSize;
916             break;
917         case OS.VK_DOWN:
918             yChange = stepSize;
919             break;
920     }
921     if (xChange != 0 || yChange != 0) {
922         Rectangle [] oldRectangles = rectangles;
923         boolean oldStippled = stippled;
924         Rectangle [] rectsToErase = new Rectangle [rectangles.length];
925         for (int i = 0; i < rectangles.length; i++) {
926             Rectangle current = rectangles [i];
927             rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height);
928         }
929         Event event = new Event ();
930         event.x = oldX + xChange;
931         event.y = oldY + yChange;
932         Point cursorPos;
933         if ((style & SWT.RESIZE) != 0) {
934             resizeRectangles (xChange, yChange);
935             inEvent = true;
936             sendEvent (SWT.Resize, event);
937             inEvent = false;
938             /*
939             * It is possible (but unlikely) that application
940             * code could have disposed the widget in the resize
941             * event. If this happens return false to indicate
942             * that the tracking has failed.
943             */

944             if (isDisposed ()) {
945                 cancelled = true;
946                 return LRESULT.ONE;
947             }
948             boolean draw = false;
949             /*
950              * It is possible that application code could have
951              * changed the rectangles in the resize event. If this
952              * happens then only redraw the tracker if the rectangle
953              * values have changed.
954              */

955             if (rectangles != oldRectangles) {
956                 int length = rectangles.length;
957                 if (length != rectsToErase.length) {
958                     draw = true;
959                 } else {
960                     for (int i = 0; i < length; i++) {
961                         if (!rectangles [i].equals (rectsToErase [i])) {
962                             draw = true;
963                             break;
964                         }
965                     }
966                 }
967             } else {
968                 draw = true;
969             }
970             if (draw) {
971                 drawRectangles (rectsToErase, oldStippled);
972                 update ();
973                 drawRectangles (rectangles, stippled);
974             }
975             cursorPos = adjustResizeCursor ();
976         } else {
977             moveRectangles (xChange, yChange);
978             inEvent = true;
979             sendEvent (SWT.Move, event);
980             inEvent = false;
981             /*
982             * It is possible (but unlikely) that application
983             * code could have disposed the widget in the move
984             * event. If this happens return false to indicate
985             * that the tracking has failed.
986             */

987             if (isDisposed ()) {
988                 cancelled = true;
989                 return LRESULT.ONE;
990             }
991             boolean draw = false;
992             /*
993              * It is possible that application code could have
994              * changed the rectangles in the move event. If this
995              * happens then only redraw the tracker if the rectangle
996              * values have changed.
997              */

998             if (rectangles != oldRectangles) {
999                 int length = rectangles.length;
1000                if (length != rectsToErase.length) {
1001                    draw = true;
1002                } else {
1003                    for (int i = 0; i < length; i++) {
1004                        if (!rectangles [i].equals (rectsToErase [i])) {
1005                            draw = true;
1006                            break;
1007                        }
1008                    }
1009                }
1010            } else {
1011                draw = true;
1012            }
1013            if (draw) {
1014                drawRectangles (rectsToErase, oldStippled);
1015                update ();
1016                drawRectangles (rectangles, stippled);
1017            }
1018            cursorPos = adjustMoveCursor ();
1019        }
1020        if (cursorPos != null) {
1021            oldX = cursorPos.x;
1022            oldY = cursorPos.y;
1023        }
1024    }
1025    return result;
1026}
1027
1028LRESULT wmSysKeyDown (int hwnd, int wParam, int lParam) {
1029    LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam);
1030    if (result != null) return result;
1031    cancelled = true;
1032    tracking = false;
1033    return result;
1034}
1035
1036LRESULT wmMouse (int message, int wParam, int lParam) {
1037    boolean isMirrored = parent != null && (parent.style & SWT.MIRRORED) != 0;
1038    int newPos = OS.GetMessagePos ();
1039    int newX = (short) (newPos & 0xFFFF);
1040    int newY = (short) (newPos >> 16);
1041    if (newX != oldX || newY != oldY) {
1042        Rectangle [] oldRectangles = rectangles;
1043        boolean oldStippled = stippled;
1044        Rectangle [] rectsToErase = new Rectangle [rectangles.length];
1045        for (int i = 0; i < rectangles.length; i++) {
1046            Rectangle current = rectangles [i];
1047            rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height);
1048        }
1049        Event event = new Event ();
1050        event.x = newX;
1051        event.y = newY;
1052        if ((style & SWT.RESIZE) != 0) {
1053            if (isMirrored) {
1054               resizeRectangles (oldX - newX, newY - oldY);
1055            } else {
1056               resizeRectangles (newX - oldX, newY - oldY);
1057            }
1058            inEvent = true;
1059            sendEvent (SWT.Resize, event);
1060            inEvent = false;
1061            /*
1062            * It is possible (but unlikely), that application
1063            * code could have disposed the widget in the resize
1064            * event. If this happens, return false to indicate
1065            * that the tracking has failed.
1066            */

1067            if (isDisposed ()) {
1068                cancelled = true;
1069                return LRESULT.ONE;
1070            }
1071            boolean draw = false;
1072            /*
1073             * It is possible that application code could have
1074             * changed the rectangles in the resize event. If this
1075             * happens then only redraw the tracker if the rectangle
1076             * values have changed.
1077             */

1078            if (rectangles != oldRectangles) {
1079                int length = rectangles.length;
1080                if (length != rectsToErase.length) {
1081                    draw = true;
1082                } else {
1083                    for (int i = 0; i < length; i++) {
1084                        if (!rectangles [i].equals (rectsToErase [i])) {
1085                            draw = true;
1086                            break;
1087                        }
1088                    }
1089                }
1090            }
1091            else {
1092                draw = true;
1093            }
1094            if (draw) {
1095                drawRectangles (rectsToErase, oldStippled);
1096                update ();
1097                drawRectangles (rectangles, stippled);
1098            }
1099            Point cursorPos = adjustResizeCursor ();
1100            if (cursorPos != null) {
1101                newX = cursorPos.x;
1102                newY = cursorPos.y;
1103            }
1104        } else {
1105            if (isMirrored) {
1106                moveRectangles (oldX - newX, newY - oldY);
1107            } else {
1108                moveRectangles (newX - oldX, newY - oldY);
1109            }
1110            inEvent = true;
1111            sendEvent (SWT.Move, event);
1112            inEvent = false;
1113            /*
1114            * It is possible (but unlikely), that application
1115            * code could have disposed the widget in the move
1116            * event. If this happens, return false to indicate
1117            * that the tracking has failed.
1118            */

1119            if (isDisposed ()) {
1120                cancelled = true;
1121                return LRESULT.ONE;
1122            }
1123            boolean draw = false;
1124            /*
1125             * It is possible that application code could have
1126             * changed the rectangles in the move event. If this
1127             * happens then only redraw the tracker if the rectangle
1128             * values have changed.
1129             */

1130            if (rectangles != oldRectangles) {
1131                int length = rectangles.length;
1132                if (length != rectsToErase.length) {
1133                    draw = true;
1134                } else {
1135                    for (int i = 0; i < length; i++) {
1136                        if (!rectangles [i].equals (rectsToErase [i])) {
1137                            draw = true;
1138                            break;
1139                        }
1140                    }
1141                }
1142            } else {
1143                draw = true;
1144            }
1145            if (draw) {
1146                drawRectangles (rectsToErase, oldStippled);
1147                update ();
1148                drawRectangles (rectangles, stippled);
1149            }
1150        }
1151        oldX = newX;
1152        oldY = newY;
1153    }
1154    tracking = message != OS.WM_LBUTTONUP;
1155    return null;
1156}
1157
1158}
1159
Popular Tags