Mercurial > projects > dwt-addons
annotate dwtx/jface/fieldassist/ControlDecoration.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 46a6e0e6ccd4 |
children |
rev | line source |
---|---|
29 | 1 /******************************************************************************* |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
2 * Copyright (c) 2006, 2008 IBM Corporation and others. |
29 | 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 dwtx.jface.fieldassist.ControlDecoration; | |
14 | |
15 | |
16 import dwt.DWT; | |
17 import dwt.events.DisposeEvent; | |
18 import dwt.events.DisposeListener; | |
19 import dwt.events.FocusEvent; | |
20 import dwt.events.FocusListener; | |
21 import dwt.events.MenuDetectEvent; | |
22 import dwt.events.MenuDetectListener; | |
23 import dwt.events.MouseAdapter; | |
24 import dwt.events.MouseEvent; | |
25 import dwt.events.MouseMoveListener; | |
26 import dwt.events.MouseTrackListener; | |
27 import dwt.events.PaintEvent; | |
28 import dwt.events.PaintListener; | |
29 import dwt.events.SelectionEvent; | |
30 import dwt.events.SelectionListener; | |
31 import dwt.graphics.GC; | |
32 import dwt.graphics.Image; | |
33 import dwt.graphics.Point; | |
34 import dwt.graphics.Rectangle; | |
35 import dwt.graphics.Region; | |
36 import dwt.widgets.Composite; | |
37 import dwt.widgets.Control; | |
38 import dwt.widgets.Display; | |
39 import dwt.widgets.Event; | |
40 import dwt.widgets.Listener; | |
41 import dwt.widgets.Shell; | |
42 import dwt.widgets.Widget; | |
43 import dwtx.core.runtime.ListenerList; | |
44 | |
45 import dwt.dwthelper.utils; | |
46 import tango.io.Stdout; | |
47 | |
48 /** | |
49 * ControlDecoration renders an image decoration near a control. It allows | |
50 * clients to specify an image and a position for the image relative to the | |
51 * control. A ControlDecoration may be assigned description text, which can | |
52 * optionally be shown when the user hovers over the image. Clients can decorate | |
53 * any kind of control. | |
54 * <p> | |
55 * Decoration images always appear on the left or right side of the field, never | |
56 * above or below it. Decorations can be positioned at the top, center, or | |
57 * bottom of either side of the control. Future implementations may provide | |
58 * additional positioning options for decorations. | |
59 * <p> | |
60 * ControlDecoration renders the image adjacent to the specified (already | |
61 * created) control, with no guarantee that it won't be clipped or otherwise | |
62 * obscured or overlapped by adjacent controls, including another | |
63 * ControlDecoration placed in the same location. Clients should ensure that | |
64 * there is adequate space adjacent to the control to show the decoration | |
65 * properly. | |
66 * <p> | |
67 * Clients using ControlDecoration should typically ensure that enough margin | |
68 * space is reserved for a decoration by altering the layout data margins, | |
69 * although this is not assumed or required by the ControlDecoration | |
70 * implementation. | |
71 * <p> | |
72 * This class is intended to be instantiated and used by clients. It is not | |
73 * intended to be subclassed by clients. | |
74 * | |
75 * @since 3.3 | |
76 * | |
77 * @see FieldDecoration | |
78 * @see FieldDecorationRegistry | |
79 */ | |
80 public class ControlDecoration { | |
81 /** | |
82 * Debug flag for tracing | |
83 */ | |
84 private static bool DEBUG = false; | |
85 | |
86 /** | |
87 * Cached platform flags for dealing with platform-specific issues. | |
88 */ | |
34 | 89 private static bool CARBON(){ |
90 return "carbon".equals(DWT.getPlatform()); //$NON-NLS-1$ | |
91 } | |
29 | 92 |
93 /** | |
94 * The associated control | |
95 */ | |
96 private Control control; | |
97 | |
98 /** | |
99 * The composite on which to render the decoration and hook mouse events, or | |
100 * null if we are hooking all parent composites. | |
101 */ | |
102 private Composite composite; | |
103 | |
104 /** | |
105 * The associated image. | |
106 */ | |
107 private Image image; | |
108 | |
109 /** | |
110 * The associated description text. | |
111 */ | |
112 private String descriptionText; | |
113 /** | |
114 * The position of the decoration. | |
115 */ | |
116 private int position; | |
117 | |
118 /** | |
119 * The decoration's visibility flag | |
120 */ | |
121 private bool visible = true; | |
122 | |
123 /** | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
124 * Boolean indicating whether the decoration should only be shown when the |
29 | 125 * control has focus |
126 */ | |
127 private bool showOnlyOnFocus = false; | |
128 | |
129 /** | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
130 * Boolean indicating whether the decoration should show its description |
29 | 131 * text in a hover when the user hovers over the decoration. |
132 */ | |
133 private bool showHover = true; | |
134 | |
135 /** | |
136 * Margin width used between the decorator and the control. | |
137 */ | |
138 private int marginWidth = 0; | |
139 | |
140 /** | |
141 * Registered selection listeners. | |
142 */ | |
143 private ListenerList selectionListeners; | |
144 | |
145 /** | |
146 * Registered menu detect listeners. | |
147 */ | |
148 private ListenerList menuDetectListeners; | |
149 | |
150 /** | |
151 * The focus listener | |
152 */ | |
153 private FocusListener focusListener; | |
154 | |
155 /** | |
156 * The dispose listener | |
157 */ | |
158 private DisposeListener disposeListener; | |
159 | |
160 /** | |
161 * The paint listener installed for drawing the decoration | |
162 */ | |
163 private PaintListener paintListener; | |
164 | |
165 /** | |
166 * The mouse listener installed for tracking the hover | |
167 */ | |
168 private MouseTrackListener mouseTrackListener; | |
169 | |
170 /** | |
171 * The mouse move listener installed for tracking the hover | |
172 */ | |
173 private MouseMoveListener mouseMoveListener; | |
174 | |
175 /** | |
176 * The untyped listener installed for notifying external listeners | |
177 */ | |
178 private Listener compositeListener; | |
179 | |
180 /** | |
181 * Control that we last installed a move listener on. We only want one at a | |
182 * time. | |
183 */ | |
184 private Control moveListeningTarget = null; | |
185 | |
186 /** | |
187 * Debug counter used to match add and remove listeners | |
188 */ | |
189 private int listenerInstalls = 0; | |
190 | |
191 /** | |
192 * The current rectangle used for tracking mouse moves | |
193 */ | |
194 private Rectangle decorationRectangle; | |
195 | |
196 /** | |
197 * An internal flag tracking whether we have focus. We use this rather than | |
198 * isFocusControl() so that we can set the flag as soon as we get the focus | |
199 * callback, rather than having to do an asyncExec in the middle of a focus | |
200 * callback to ensure that isFocusControl() represents the outcome of the | |
201 * event. | |
202 */ | |
203 private bool hasFocus = false; | |
204 | |
205 /** | |
206 * The hover used for showing description text | |
207 */ | |
208 private Hover hover; | |
209 | |
210 /** | |
211 * The hover used to show a decoration image's description. | |
212 */ | |
213 class Hover { | |
214 private static const String EMPTY = ""; //$NON-NLS-1$ | |
215 | |
216 /** | |
217 * Offset of info hover arrow from the left or right side. | |
218 */ | |
219 private int hao = 10; | |
220 | |
221 /** | |
222 * Width of info hover arrow. | |
223 */ | |
224 private int haw = 8; | |
225 | |
226 /** | |
227 * Height of info hover arrow. | |
228 */ | |
229 private int hah = 10; | |
230 | |
231 /** | |
232 * Margin around info hover text. | |
233 */ | |
234 private int hm = 2; | |
235 | |
236 /** | |
237 * This info hover's shell. | |
238 */ | |
239 Shell hoverShell; | |
240 | |
241 /** | |
242 * The info hover text. | |
243 */ | |
244 String text = EMPTY; | |
245 | |
246 /** | |
247 * The region used to manage the shell shape | |
248 */ | |
249 Region region; | |
250 | |
251 /** | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
252 * Boolean indicating whether the last computed polygon location had an |
29 | 253 * arrow on left. (true if left, false if right). |
254 */ | |
255 bool arrowOnLeft = true; | |
256 | |
257 /* | |
258 * Create a hover parented by the specified shell. | |
259 */ | |
260 this(Shell parent) { | |
261 Display display = parent.getDisplay(); | |
262 hoverShell = new Shell(parent, DWT.NO_TRIM | DWT.ON_TOP | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
263 | DWT.NO_FOCUS | DWT.TOOL); |
29 | 264 hoverShell.setBackground(display |
265 .getSystemColor(DWT.COLOR_INFO_BACKGROUND)); | |
266 hoverShell.setForeground(display | |
267 .getSystemColor(DWT.COLOR_INFO_FOREGROUND)); | |
268 hoverShell.addPaintListener(new class PaintListener { | |
269 public void paintControl(PaintEvent pe) { | |
270 pe.gc.drawText(text, hm, hm); | |
271 if (!CARBON) { | |
272 pe.gc.drawPolygon(getPolygon(true)); | |
273 } | |
274 } | |
275 }); | |
276 hoverShell.addMouseListener(new class MouseAdapter { | |
277 public void mouseDown(MouseEvent e) { | |
278 hideHover(); | |
279 } | |
280 }); | |
281 } | |
282 | |
283 /* | |
284 * Compute a polygon that represents a hover with an arrow pointer. If | |
285 * border is true, compute the polygon inset by 1-pixel border. Consult | |
286 * the arrowOnLeft flag to determine which side the arrow is on. | |
287 */ | |
288 int[] getPolygon(bool border) { | |
289 Point e = getExtent(); | |
290 int b = border ? 1 : 0; | |
291 if (arrowOnLeft) { | |
292 return [ 0, 0, e.x - b, 0, e.x - b, e.y - b, | |
293 hao + haw, e.y - b, hao + haw / 2, e.y + hah - b, hao, | |
294 e.y - b, 0, e.y - b, 0, 0 ]; | |
295 } | |
296 return [ 0, 0, e.x - b, 0, e.x - b, e.y - b, | |
297 e.x - hao - b, e.y - b, e.x - hao - haw / 2, e.y + hah - b, | |
298 e.x - hao - haw, e.y - b, 0, e.y - b, 0, 0 ]; | |
299 } | |
300 | |
301 /* | |
302 * Dispose the hover, it is no longer needed. Dispose any resources | |
303 * allocated by the hover. | |
304 */ | |
305 void dispose() { | |
306 if (!hoverShell.isDisposed()) { | |
307 hoverShell.dispose(); | |
308 } | |
309 if (region !is null) { | |
310 region.dispose(); | |
311 } | |
312 } | |
313 | |
314 /* | |
315 * Set the visibility of the hover. | |
316 */ | |
317 void setVisible(bool visible) { | |
318 if (visible) { | |
319 if (!hoverShell.isVisible()) { | |
320 hoverShell.setVisible(true); | |
321 } | |
322 } else { | |
323 if (hoverShell.isVisible()) { | |
324 hoverShell.setVisible(false); | |
325 } | |
326 } | |
327 } | |
328 | |
329 /* | |
330 * Set the text of the hover to the specified text. Recompute the size | |
331 * and location of the hover to hover near the decoration rectangle, | |
332 * pointing the arrow toward the target control. | |
333 */ | |
334 void setText(String t, Rectangle decorationRectangle, | |
335 Control targetControl) { | |
336 if (t is null) { | |
337 t = EMPTY; | |
338 } | |
339 if (!t.equals(text)) { | |
340 Point oldSize = getExtent(); | |
341 text = t; | |
342 hoverShell.redraw(); | |
343 Point newSize = getExtent(); | |
344 if (!oldSize.opEquals(newSize)) { | |
345 // set a flag that indicates the direction of arrow | |
346 arrowOnLeft = decorationRectangle.x <= targetControl | |
347 .getLocation().x; | |
348 setNewShape(); | |
349 } | |
350 } | |
351 | |
352 Point extent = getExtent(); | |
353 int y = -extent.y - hah + 1; | |
354 int x = arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw / 2; | |
355 | |
356 hoverShell.setLocation(control.getParent().toDisplay( | |
357 decorationRectangle.x + x, decorationRectangle.y + y)); | |
358 } | |
359 | |
360 /* | |
361 * Return whether or not the hover (shell) is visible. | |
362 */ | |
363 bool isVisible() { | |
364 return hoverShell.isVisible(); | |
365 } | |
366 | |
367 /* | |
368 * Compute the extent of the hover for the current text. | |
369 */ | |
370 Point getExtent() { | |
371 GC gc = new GC(hoverShell); | |
372 Point e = gc.textExtent(text); | |
373 gc.dispose(); | |
374 e.x += hm * 2; | |
375 e.y += hm * 2; | |
376 return e; | |
377 } | |
378 | |
379 /* | |
380 * Compute a new shape for the hover shell. | |
381 */ | |
382 void setNewShape() { | |
383 Region oldRegion = region; | |
384 region = new Region(); | |
385 region.add(getPolygon(false)); | |
386 hoverShell.setRegion(region); | |
387 if (oldRegion !is null) { | |
388 oldRegion.dispose(); | |
389 } | |
390 | |
391 } | |
392 } | |
393 | |
394 /** | |
395 * Construct a ControlDecoration for decorating the specified control at the | |
396 * specified position relative to the control. Render the decoration on top | |
397 * of any Control that happens to appear at the specified location. | |
398 * <p> | |
399 * DWT constants are used to specify the position of the decoration relative | |
400 * to the control. The position should include style bits describing both | |
401 * the vertical and horizontal orientation. <code>DWT.LEFT</code> and | |
402 * <code>DWT.RIGHT</code> describe the horizontal placement of the | |
403 * decoration relative to the control, and the constants | |
404 * <code>DWT.TOP</code>, <code>DWT.CENTER</code>, and | |
405 * <code>DWT.BOTTOM</code> describe the vertical alignment of the | |
406 * decoration relative to the control. Decorations always appear on either | |
407 * the left or right side of the control, never above or below it. For | |
408 * example, a decoration appearing on the left side of the field, at the | |
409 * top, is specified as DWT.LEFT | DWT.TOP. If no position style bits are | |
410 * specified, the control decoration will be positioned to the left and | |
411 * center of the control (<code>DWT.LEFT | DWT.CENTER</code>). | |
412 * </p> | |
413 * | |
414 * @param control | |
415 * the control to be decorated | |
416 * @param position | |
417 * bit-wise or of position constants (<code>DWT.TOP</code>, | |
418 * <code>DWT.BOTTOM</code>, <code>DWT.LEFT</code>, | |
419 * <code>DWT.RIGHT</code>, and <code>DWT.CENTER</code>). | |
420 */ | |
421 public this(Control control, int position) { | |
422 this(control, position, null); | |
423 | |
424 } | |
425 | |
426 /** | |
427 * Construct a ControlDecoration for decorating the specified control at the | |
428 * specified position relative to the control. Render the decoration only on | |
429 * the specified Composite or its children. The decoration will be clipped | |
430 * if it does not appear within the visible bounds of the composite or its | |
431 * child composites. | |
432 * <p> | |
433 * DWT constants are used to specify the position of the decoration relative | |
434 * to the control. The position should include style bits describing both | |
435 * the vertical and horizontal orientation. <code>DWT.LEFT</code> and | |
436 * <code>DWT.RIGHT</code> describe the horizontal placement of the | |
437 * decoration relative to the control, and the constants | |
438 * <code>DWT.TOP</code>, <code>DWT.CENTER</code>, and | |
439 * <code>DWT.BOTTOM</code> describe the vertical alignment of the | |
440 * decoration relative to the control. Decorations always appear on either | |
441 * the left or right side of the control, never above or below it. For | |
442 * example, a decoration appearing on the left side of the field, at the | |
443 * top, is specified as DWT.LEFT | DWT.TOP. If no position style bits are | |
444 * specified, the control decoration will be positioned to the left and | |
445 * center of the control (<code>DWT.LEFT | DWT.CENTER</code>). | |
446 * </p> | |
447 * | |
448 * @param control | |
449 * the control to be decorated | |
450 * @param position | |
451 * bit-wise or of position constants (<code>DWT.TOP</code>, | |
452 * <code>DWT.BOTTOM</code>, <code>DWT.LEFT</code>, | |
453 * <code>DWT.RIGHT</code>, and <code>DWT.CENTER</code>). | |
454 * @param composite | |
455 * The DWT composite within which the decoration should be | |
456 * rendered. The decoration will be clipped to this composite, | |
457 * but it may be rendered on a child of the composite. The | |
458 * decoration will not be visible if the specified composite or | |
459 * its child composites are not visible in the space relative to | |
460 * the control, where the decoration is to be rendered. If this | |
461 * value is <code>null</code>, then the decoration will be | |
462 * rendered on whichever composite (or composites) are located in | |
463 * the specified position. | |
464 */ | |
465 public this(Control control, int position, Composite composite) { | |
466 selectionListeners = new ListenerList(); | |
467 menuDetectListeners = new ListenerList(); | |
468 this.position = position; | |
469 this.control = control; | |
470 this.composite = composite; | |
471 | |
472 addControlListeners(); | |
473 | |
474 } | |
475 | |
476 /** | |
477 * Adds the listener to the collection of listeners who will be notified | |
478 * when the platform-specific context menu trigger has occurred, by sending | |
479 * it one of the messages defined in the <code>MenuDetectListener</code> | |
480 * interface. | |
481 * <p> | |
482 * The <code>widget</code> field in the SelectionEvent will contain the | |
483 * Composite on which the decoration is rendered that received the click. | |
484 * The <code>x</code> and <code>y</code> fields will be in coordinates | |
485 * relative to the display. The <code>data</code> field will contain the | |
486 * decoration that received the event. | |
487 * </p> | |
488 * | |
489 * @param listener | |
490 * the listener which should be notified | |
491 * | |
492 * @see dwt.events.MenuDetectListener | |
493 * @see dwt.events.MenuDetectEvent | |
494 * @see #removeMenuDetectListener | |
495 */ | |
496 public void addMenuDetectListener(MenuDetectListener listener) { | |
497 menuDetectListeners.add(cast(Object)listener); | |
498 } | |
499 | |
500 /** | |
501 * Removes the listener from the collection of listeners who will be | |
502 * notified when the platform-specific context menu trigger has occurred. | |
503 * | |
504 * @param listener | |
505 * the listener which should no longer be notified. This message | |
506 * has no effect if the listener was not previously added to the | |
507 * receiver. | |
508 * | |
509 * @see dwt.events.MenuDetectListener | |
510 * @see #addMenuDetectListener | |
511 */ | |
512 public void removeMenuDetectListener(MenuDetectListener listener) { | |
513 menuDetectListeners.remove(cast(Object)listener); | |
514 } | |
515 | |
516 /** | |
517 * Adds the listener to the collection of listeners who will be notified | |
518 * when the decoration is selected, by sending it one of the messages | |
519 * defined in the <code>SelectionListener</code> interface. | |
520 * <p> | |
521 * <code>widgetSelected</code> is called when the decoration is selected | |
522 * (by mouse click). <code>widgetDefaultSelected</code> is called when the | |
523 * decoration is double-clicked. | |
524 * </p> | |
525 * <p> | |
526 * The <code>widget</code> field in the SelectionEvent will contain the | |
527 * Composite on which the decoration is rendered that received the click. | |
528 * The <code>x</code> and <code>y</code> fields will be in coordinates | |
529 * relative to that widget. The <code>data</code> field will contain the | |
530 * decoration that received the event. | |
531 * </p> | |
532 * | |
533 * @param listener | |
534 * the listener which should be notified | |
535 * | |
536 * @see dwt.events.SelectionListener | |
537 * @see dwt.events.SelectionEvent | |
538 * @see #removeSelectionListener | |
539 */ | |
540 public void addSelectionListener(SelectionListener listener) { | |
541 selectionListeners.add(cast(Object)listener); | |
542 } | |
543 | |
544 /** | |
545 * Removes the listener from the collection of listeners who will be | |
546 * notified when the decoration is selected. | |
547 * | |
548 * @param listener | |
549 * the listener which should no longer be notified. This message | |
550 * has no effect if the listener was not previously added to the | |
551 * receiver. | |
552 * | |
553 * @see dwt.events.SelectionListener | |
554 * @see #addSelectionListener | |
555 */ | |
556 public void removeSelectionListener(SelectionListener listener) { | |
557 selectionListeners.remove(cast(Object)listener); | |
558 } | |
559 | |
560 /** | |
561 * Dispose this ControlDecoration. Unhook any listeners that have been | |
562 * installed on the target control. This method has no effect if the | |
563 * receiver is already disposed. | |
564 */ | |
565 public void dispose() { | |
566 if (control is null) { | |
567 return; | |
568 } | |
569 if (hover !is null) { | |
570 hover.dispose(); | |
571 hover = null; | |
572 } | |
573 removeControlListeners(); | |
574 control = null; | |
575 } | |
576 | |
577 /** | |
578 * Get the control that is decorated by the receiver. | |
579 * | |
580 * @return the Control decorated by the receiver. May be <code>null</code> | |
581 * if the control has been uninstalled. | |
582 */ | |
583 public Control getControl() { | |
584 return control; | |
585 } | |
586 | |
587 /** | |
588 * Add any listeners needed on the target control and on the composite where | |
589 * the decoration is to be rendered. | |
590 */ | |
591 private void addControlListeners() { | |
592 disposeListener = new class DisposeListener { | |
593 public void widgetDisposed(DisposeEvent event) { | |
594 dispose(); | |
595 } | |
596 }; | |
597 printAddListener(control, "DISPOSE"); //$NON-NLS-1$ | |
598 control.addDisposeListener(disposeListener); | |
599 | |
600 focusListener = new class FocusListener { | |
601 public void focusGained(FocusEvent event) { | |
602 hasFocus = true; | |
603 if (showOnlyOnFocus) { | |
604 update(); | |
605 } | |
606 } | |
607 | |
608 public void focusLost(FocusEvent event) { | |
609 hasFocus = false; | |
610 if (showOnlyOnFocus) { | |
611 update(); | |
612 } | |
613 } | |
614 }; | |
615 printAddListener(control, "FOCUS"); //$NON-NLS-1$ | |
616 control.addFocusListener(focusListener); | |
617 | |
618 // Listener for painting the decoration | |
619 paintListener = new class PaintListener { | |
620 public void paintControl(PaintEvent event) { | |
621 Control control = cast(Control) event.widget; | |
622 Rectangle rect = getDecorationRectangle(control); | |
623 if (shouldShowDecoration()) { | |
624 event.gc.drawImage(getImage(), rect.x, rect.y); | |
625 } | |
626 } | |
627 }; | |
628 | |
629 // Listener for tracking the end of a hover. Only installed | |
630 // after a hover begins. | |
631 mouseMoveListener = new class MouseMoveListener { | |
632 public void mouseMove(MouseEvent event) { | |
633 if (showHover) { | |
634 if (!decorationRectangle.contains(event.x, event.y)) { | |
635 hideHover(); | |
636 // No need to listen any longer | |
637 printRemoveListener(event.widget, "MOUSEMOVE"); //$NON-NLS-1$ | |
638 (cast(Control) event.widget) | |
639 .removeMouseMoveListener(mouseMoveListener); | |
640 moveListeningTarget = null; | |
641 } | |
642 } | |
643 } | |
644 }; | |
645 | |
646 // Listener for tracking the beginning of a hover. Always installed. | |
647 mouseTrackListener = new class MouseTrackListener { | |
648 public void mouseExit(MouseEvent event) { | |
649 // Just in case we didn't catch it before. | |
650 Control target = cast(Control) event.widget; | |
651 if (target is moveListeningTarget) { | |
652 printRemoveListener(target, "MOUSEMOVE"); //$NON-NLS-1$ | |
653 target.removeMouseMoveListener(mouseMoveListener); | |
654 moveListeningTarget = null; | |
655 } | |
656 hideHover(); | |
657 } | |
658 | |
659 public void mouseHover(MouseEvent event) { | |
660 if (showHover) { | |
661 decorationRectangle = getDecorationRectangle(cast(Control) event.widget); | |
662 if (decorationRectangle.contains(event.x, event.y)) { | |
663 showHoverText(getDescriptionText()); | |
664 Control target = cast(Control) event.widget; | |
665 if (moveListeningTarget is null) { | |
666 printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$ | |
667 target.addMouseMoveListener(mouseMoveListener); | |
668 moveListeningTarget = target; | |
669 } else if (target !is moveListeningTarget) { | |
670 printRemoveListener(moveListeningTarget, | |
671 "MOUSEMOVE"); //$NON-NLS-1$ | |
672 moveListeningTarget | |
673 .removeMouseMoveListener(mouseMoveListener); | |
674 printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$ | |
675 target.addMouseMoveListener(mouseMoveListener); | |
676 moveListeningTarget = target; | |
677 } else { | |
678 // It is already installed on this control. | |
679 } | |
680 } | |
681 } | |
682 } | |
683 | |
684 public void mouseEnter(MouseEvent event) { | |
685 // Nothing to do until a hover occurs. | |
686 } | |
687 }; | |
688 | |
689 compositeListener = new class Listener { | |
690 public void handleEvent(Event event) { | |
691 // Don't forward events if decoration is not showing | |
692 if (!visible) { | |
693 return; | |
694 } | |
695 // Notify listeners if any are registered. | |
696 switch (event.type) { | |
697 case DWT.MouseDown: | |
698 if (!selectionListeners.isEmpty()) | |
699 notifySelectionListeners(event); | |
700 break; | |
701 case DWT.MouseDoubleClick: | |
702 if (!selectionListeners.isEmpty()) | |
703 notifySelectionListeners(event); | |
704 break; | |
705 case DWT.MenuDetect: | |
706 if (!menuDetectListeners.isEmpty()) | |
707 notifyMenuDetectListeners(event); | |
708 break; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
70
diff
changeset
|
709 default: |
29 | 710 } |
711 } | |
712 }; | |
713 | |
714 // We do not know which parent in the control hierarchy | |
715 // is providing the decoration space, so hook all the way up, until | |
716 // the shell or the specified parent composite is reached. | |
717 Composite c = control.getParent(); | |
718 while (c !is null) { | |
719 installCompositeListeners(c); | |
720 if (composite !is null && composite is c) { | |
721 // We just installed on the specified composite, so stop. | |
722 c = null; | |
723 } else if (cast(Shell)c ) { | |
724 // We just installed on a shell, so don't go further | |
725 c = null; | |
726 } else { | |
727 c = c.getParent(); | |
728 } | |
729 } | |
730 // force a redraw of the decoration area so our paint listener | |
731 // is notified. | |
732 update(); | |
733 } | |
734 | |
735 /* | |
736 * Install the listeners used to paint and track mouse events on the | |
737 * composite. | |
738 */ | |
739 private void installCompositeListeners(Composite c) { | |
740 if (!c.isDisposed()) { | |
741 printAddListener(c, "PAINT"); //$NON-NLS-1$ | |
742 c.addPaintListener(paintListener); | |
743 printAddListener(c, "MOUSETRACK"); //$NON-NLS-1$ | |
744 c.addMouseTrackListener(mouseTrackListener); | |
745 printAddListener(c, "DWT.MenuDetect"); //$NON-NLS-1$ | |
746 c.addListener(DWT.MenuDetect, compositeListener); | |
747 printAddListener(c, "DWT.MouseDown"); //$NON-NLS-1$ | |
748 c.addListener(DWT.MouseDown, compositeListener); | |
749 printAddListener(c, "DWT.MouseDoubleClick"); //$NON-NLS-1$ | |
750 c.addListener(DWT.MouseDoubleClick, compositeListener); | |
751 } | |
752 } | |
753 | |
754 /* | |
755 * Remove the listeners used to paint and track mouse events on the | |
756 * composite. | |
757 */ | |
758 private void removeCompositeListeners(Composite c) { | |
759 if (!c.isDisposed()) { | |
760 printRemoveListener(c, "PAINT"); //$NON-NLS-1$ | |
761 c.removePaintListener(paintListener); | |
762 printRemoveListener(c, "MOUSETRACK"); //$NON-NLS-1$ | |
763 c.removeMouseTrackListener(mouseTrackListener); | |
764 printRemoveListener(c, "DWT.MenuDetect"); //$NON-NLS-1$ | |
765 c.removeListener(DWT.MenuDetect, compositeListener); | |
766 printRemoveListener(c, "DWT.MouseDown"); //$NON-NLS-1$ | |
767 c.removeListener(DWT.MouseDown, compositeListener); | |
768 printRemoveListener(c, "DWT.MouseDoubleClick"); //$NON-NLS-1$ | |
769 c.removeListener(DWT.MouseDoubleClick, compositeListener); | |
770 } | |
771 } | |
772 | |
773 private void notifySelectionListeners(Event event) { | |
774 if (!(cast(Control)event.widget )) { | |
775 return; | |
776 } | |
777 if (getDecorationRectangle(cast(Control) event.widget).contains(event.x, | |
778 event.y)) { | |
779 SelectionEvent clientEvent = new SelectionEvent(event); | |
780 clientEvent.data = this; | |
781 if (getImage() !is null) { | |
782 clientEvent.height = getImage().getBounds().height; | |
783 clientEvent.width = getImage().getBounds().width; | |
784 } | |
785 Object[] listeners; | |
786 switch (event.type) { | |
787 case DWT.MouseDoubleClick: | |
788 if (event.button is 1) { | |
789 listeners = selectionListeners.getListeners(); | |
790 for (int i = 0; i < listeners.length; i++) { | |
791 (cast(SelectionListener) listeners[i]) | |
792 .widgetDefaultSelected(clientEvent); | |
793 } | |
794 } | |
795 break; | |
796 case DWT.MouseDown: | |
797 if (event.button is 1) { | |
798 listeners = selectionListeners.getListeners(); | |
799 for (int i = 0; i < listeners.length; i++) { | |
800 (cast(SelectionListener) listeners[i]) | |
801 .widgetSelected(clientEvent); | |
802 } | |
803 } | |
804 break; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
70
diff
changeset
|
805 default: |
29 | 806 } |
807 } | |
808 } | |
809 | |
810 private void notifyMenuDetectListeners(Event event) { | |
811 if (getDecorationRectangle(null).contains(event.x, event.y)) { | |
812 MenuDetectEvent clientEvent = new MenuDetectEvent(event); | |
813 clientEvent.data = this; | |
814 Object[] listeners = menuDetectListeners.getListeners(); | |
815 for (int i = 0; i < listeners.length; i++) { | |
816 (cast(MenuDetectListener) listeners[i]).menuDetected(clientEvent); | |
817 | |
818 } | |
819 } | |
820 } | |
821 | |
822 /** | |
823 * Show the specified text using the same hover dialog as is used to show | |
824 * decorator descriptions. When {@link #setShowHover(bool)} has been set | |
825 * to <code>true</code>, a decoration's description text will be shown in | |
826 * an info hover over the field's control whenever the mouse hovers over the | |
827 * decoration. This method can be used to show a decoration's description | |
828 * text at other times (such as when the control receives focus), or to show | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
829 * other text associated with the field. The hover will not be shown if the |
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
830 * decoration is hidden. |
29 | 831 * |
832 * @param text | |
833 * the text to be shown in the info hover, or <code>null</code> | |
834 * if no text should be shown. | |
835 */ | |
836 public void showHoverText(String text) { | |
837 if (control is null) { | |
838 return; | |
839 } | |
840 showHoverText(text, control); | |
841 } | |
842 | |
843 /** | |
844 * Hide any hover popups that are currently showing on the control. When | |
845 * {@link #setShowHover(bool)} has been set to <code>true</code>, a | |
846 * decoration's description text will be shown in an info hover over the | |
847 * field's control as long as the mouse hovers over the decoration, and will | |
848 * be hidden when the mouse exits the decoration. This method can be used to | |
849 * hide a hover, whether it was shown explicitly using | |
850 * {@link #showHoverText(String)}, or was showing because the user was | |
851 * hovering in the decoration. | |
852 * <p> | |
853 * This message has no effect if there is no current hover. | |
854 * | |
855 */ | |
856 public void hideHover() { | |
857 if (hover !is null) { | |
858 hover.setVisible(false); | |
859 } | |
860 } | |
861 | |
862 /** | |
863 * Show the control decoration. This message has no effect if the decoration | |
864 * is already showing. If {@link #setShowOnlyOnFocus(bool)} is set to | |
865 * <code>true</code>, the decoration will only be shown if the control | |
866 * has focus. | |
867 */ | |
868 public void show() { | |
869 if (!visible) { | |
870 visible = true; | |
871 update(); | |
872 } | |
873 } | |
874 | |
875 /** | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
876 * Hide the control decoration and any associated hovers. This message has |
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
877 * no effect if the decoration is already hidden. |
29 | 878 */ |
879 public void hide() { | |
880 if (visible) { | |
881 visible = false; | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
882 hideHover(); |
29 | 883 update(); |
884 } | |
885 } | |
886 | |
887 /** | |
888 * Get the description text that may be shown in a hover for this | |
889 * decoration. | |
890 * | |
891 * @return the text to be shown as a description for the decoration, or | |
892 * <code>null</code> if none has been set. | |
893 */ | |
894 public String getDescriptionText() { | |
895 return descriptionText; | |
896 } | |
897 | |
898 /** | |
899 * Set the image shown in this control decoration. Update the rendered | |
900 * decoration. | |
901 * | |
902 * @param text | |
903 * the text to be shown as a description for the decoration, or | |
904 * <code>null</code> if none has been set. | |
905 */ | |
906 public void setDescriptionText(String text) { | |
907 this.descriptionText = text; | |
908 update(); | |
909 } | |
910 | |
911 /** | |
912 * Get the image shown in this control decoration. | |
913 * | |
914 * @return the image to be shown adjacent to the control, or | |
915 * <code>null</code> if one has not been set. | |
916 */ | |
917 public Image getImage() { | |
918 return image; | |
919 } | |
920 | |
921 /** | |
922 * Set the image shown in this control decoration. Update the rendered | |
923 * decoration. | |
924 * | |
925 * @param image | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
926 * the image to be shown adjacent to the control. Should never be |
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
927 * <code>null</code>. |
29 | 928 */ |
929 public void setImage(Image image) { | |
930 this.image = image; | |
931 update(); | |
932 } | |
933 | |
934 /** | |
935 * Get the bool that controls whether the decoration is shown only when | |
936 * the control has focus. The default value of this setting is | |
937 * <code>false</code>. | |
938 * | |
939 * @return <code>true</code> if the decoration should only be shown when | |
940 * the control has focus, and <code>false</code> if it should | |
941 * always be shown. Note that if the control is not capable of | |
942 * receiving focus (<code>DWT.NO_FOCUS</code>), then the | |
943 * decoration will never show when this value is <code>true</code>. | |
944 */ | |
945 public bool getShowOnlyOnFocus() { | |
946 return showOnlyOnFocus; | |
947 } | |
948 | |
949 /** | |
950 * Set the bool that controls whether the decoration is shown only when | |
951 * the control has focus. The default value of this setting is | |
952 * <code>false</code>. | |
953 * | |
954 * @param showOnlyOnFocus | |
955 * <code>true</code> if the decoration should only be shown | |
956 * when the control has focus, and <code>false</code> if it | |
957 * should always be shown. Note that if the control is not | |
958 * capable of receiving focus (<code>DWT.NO_FOCUS</code>), | |
959 * then the decoration will never show when this value is | |
960 * <code>true</code>. | |
961 */ | |
962 public void setShowOnlyOnFocus(bool showOnlyOnFocus) { | |
963 this.showOnlyOnFocus = showOnlyOnFocus; | |
964 update(); | |
965 } | |
966 | |
967 /** | |
968 * Get the bool that controls whether the decoration's description text | |
969 * should be shown in a hover when the user hovers over the decoration. The | |
970 * default value of this setting is <code>true</code>. | |
971 * | |
972 * @return <code>true</code> if a hover popup containing the decoration's | |
973 * description text should be shown when the user hovers over the | |
974 * decoration, and <code>false</code> if a hover should not be | |
975 * shown. | |
976 */ | |
977 public bool getShowHover() { | |
978 return showHover; | |
979 } | |
980 | |
981 /** | |
982 * Set the bool that controls whether the decoration's description text | |
983 * should be shown in a hover when the user hovers over the decoration. The | |
984 * default value of this setting is <code>true</code>. | |
985 * | |
986 * @param showHover | |
987 * <code>true</code> if a hover popup containing the | |
988 * decoration's description text should be shown when the user | |
989 * hovers over the decoration, and <code>false</code> if a | |
990 * hover should not be shown. | |
991 */ | |
992 public void setShowHover(bool showHover) { | |
993 this.showHover = showHover; | |
994 update(); | |
995 } | |
996 | |
997 /** | |
998 * Get the margin width in pixels that should be used between the decorator | |
999 * and the horizontal edge of the control. The default value of this setting | |
1000 * is <code>0</code>. | |
1001 * | |
1002 * @return the number of pixels that should be reserved between the | |
1003 * horizontal edge of the control and the adjacent edge of the | |
1004 * decoration. | |
1005 */ | |
1006 public int getMarginWidth() { | |
1007 return marginWidth; | |
1008 } | |
1009 | |
1010 /** | |
1011 * Set the margin width in pixels that should be used between the decorator | |
1012 * and the horizontal edge of the control. The default value of this setting | |
1013 * is <code>0</code>. | |
1014 * | |
1015 * @param marginWidth | |
1016 * the number of pixels that should be reserved between the | |
1017 * horizontal edge of the control and the adjacent edge of the | |
1018 * decoration. | |
1019 */ | |
1020 public void setMarginWidth(int marginWidth) { | |
1021 this.marginWidth = marginWidth; | |
1022 update(); | |
1023 } | |
1024 | |
1025 /** | |
1026 * Something has changed, requiring redraw. Redraw the decoration and update | |
1027 * the hover text if appropriate. | |
1028 */ | |
1029 protected void update() { | |
1030 if (control is null || control.isDisposed()) { | |
1031 return; | |
1032 } | |
1033 Rectangle rect = getDecorationRectangle(control.getShell()); | |
1034 // Redraw this rectangle in all children | |
1035 control.getShell() | |
1036 .redraw(rect.x, rect.y, rect.width, rect.height, true); | |
1037 control.getShell().update(); | |
1038 if (hover !is null && getDescriptionText() !is null) { | |
1039 hover.setText(getDescriptionText(), getDecorationRectangle(control | |
1040 .getParent()), control); | |
1041 } | |
1042 } | |
1043 | |
1044 /* | |
1045 * Show the specified text in the hover, positioning the hover near the | |
1046 * specified control. | |
1047 */ | |
1048 private void showHoverText(String text, Control hoverNear) { | |
1049 // If we aren't to show a hover, don't do anything. | |
1050 if (!showHover) { | |
1051 return; | |
1052 } | |
70
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
1053 |
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
1054 // If we are not visible, don't show the hover. |
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
1055 if (!visible) { |
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
1056 return; |
46a6e0e6ccd4
Merge with d-fied sources of 3.4M7
Frank Benoit <benoit@tionex.de>
parents:
34
diff
changeset
|
1057 } |
29 | 1058 // If there is no text, don't do anything. |
1059 if (text is null) { | |
1060 hideHover(); | |
1061 return; | |
1062 } | |
1063 | |
1064 // If there is no control, nothing to do | |
1065 if (control is null) { | |
1066 return; | |
1067 } | |
1068 // Create the hover if it's not showing | |
1069 if (hover is null) { | |
1070 hover = new Hover(hoverNear.getShell()); | |
1071 } | |
1072 hover.setText(text, getDecorationRectangle(control.getParent()), | |
1073 control); | |
1074 hover.setVisible(true); | |
1075 } | |
1076 | |
1077 /* | |
1078 * Remove any listeners installed on the controls. | |
1079 */ | |
1080 private void removeControlListeners() { | |
1081 if (control is null) { | |
1082 return; | |
1083 } | |
1084 printRemoveListener(control, "FOCUS"); //$NON-NLS-1$ | |
1085 control.removeFocusListener(focusListener); | |
1086 focusListener = null; | |
1087 | |
1088 printRemoveListener(control, "DISPOSE"); //$NON-NLS-1$ | |
1089 control.removeDisposeListener(disposeListener); | |
1090 disposeListener = null; | |
1091 | |
1092 Composite c = control.getParent(); | |
1093 while (c !is null) { | |
1094 removeCompositeListeners(c); | |
1095 if (composite !is null && composite is c) { | |
1096 // We previously installed listeners only to the specified | |
1097 // composite, so stop. | |
1098 c = null; | |
1099 } else if (cast(Shell)c ) { | |
1100 // We previously installed listeners only up to the first Shell | |
1101 // encountered, so stop. | |
1102 c = null; | |
1103 } else { | |
1104 c = c.getParent(); | |
1105 } | |
1106 } | |
1107 paintListener = null; | |
1108 mouseTrackListener = null; | |
1109 compositeListener = null; | |
1110 | |
1111 // We may have a remaining mouse move listener installed | |
1112 if (moveListeningTarget !is null) { | |
1113 printRemoveListener(moveListeningTarget, "MOUSEMOVE"); //$NON-NLS-1$ | |
1114 moveListeningTarget.removeMouseMoveListener(mouseMoveListener); | |
1115 moveListeningTarget = null; | |
1116 mouseMoveListener = null; | |
1117 } | |
1118 if (DEBUG) { | |
1119 if (listenerInstalls > 0) { | |
1120 Stdout.formatln("LISTENER LEAK>>>CHECK TRACE ABOVE"); //$NON-NLS-1$ | |
1121 } else if (listenerInstalls < 0) { | |
1122 Stdout.formatln("REMOVED UNREGISTERED LISTENERS>>>CHECK TRACE ABOVE"); //$NON-NLS-1$ | |
1123 } else { | |
1124 Stdout.formatln("ALL INSTALLED LISTENERS WERE REMOVED."); //$NON-NLS-1$ | |
1125 } | |
1126 } | |
1127 } | |
1128 | |
1129 /** | |
1130 * Return the rectangle in which the decoration should be rendered, in | |
1131 * coordinates relative to the specified control. If the specified control | |
1132 * is null, return the rectangle in display coordinates. | |
1133 * | |
1134 * @param targetControl | |
1135 * the control whose coordinates should be used | |
1136 * @return the rectangle in which the decoration should be rendered | |
1137 */ | |
1138 protected Rectangle getDecorationRectangle(Control targetControl) { | |
1139 if (getImage() is null || control is null) { | |
1140 return new Rectangle(0, 0, 0, 0); | |
1141 } | |
1142 // Compute the bounds first relative to the control's parent. | |
1143 Rectangle imageBounds = getImage().getBounds(); | |
1144 Rectangle controlBounds = control.getBounds(); | |
1145 int x, y; | |
1146 // Compute x | |
1147 if ((position & DWT.RIGHT) is DWT.RIGHT) { | |
1148 x = controlBounds.x + controlBounds.width + marginWidth; | |
1149 } else { | |
1150 // default is left | |
1151 x = controlBounds.x - imageBounds.width - marginWidth; | |
1152 } | |
1153 // Compute y | |
1154 if ((position & DWT.TOP) is DWT.TOP) { | |
1155 y = controlBounds.y; | |
1156 } else if ((position & DWT.BOTTOM) is DWT.BOTTOM) { | |
1157 y = controlBounds.y + control.getBounds().height | |
1158 - imageBounds.height; | |
1159 } else { | |
1160 // default is center | |
1161 y = controlBounds.y | |
1162 + (control.getBounds().height - imageBounds.height) / 2; | |
1163 } | |
1164 | |
1165 // Now convert to coordinates relative to the target control. | |
1166 Point globalPoint = control.getParent().toDisplay(x, y); | |
1167 Point targetPoint; | |
1168 if (targetControl is null) { | |
1169 targetPoint = globalPoint; | |
1170 } else { | |
1171 targetPoint = targetControl.toControl(globalPoint); | |
1172 } | |
1173 return new Rectangle(targetPoint.x, targetPoint.y, imageBounds.width, | |
1174 imageBounds.height); | |
1175 } | |
1176 | |
1177 /* | |
1178 * Return true if the decoration should be shown, false if it should not. | |
1179 */ | |
1180 private bool shouldShowDecoration() { | |
1181 if (!visible) { | |
1182 return false; | |
1183 } | |
1184 if (control is null || control.isDisposed() || getImage() is null) { | |
1185 return false; | |
1186 } | |
1187 | |
1188 if (!control.isVisible()) { | |
1189 return false; | |
1190 } | |
1191 if (showOnlyOnFocus) { | |
1192 return hasFocus; | |
1193 } | |
1194 return true; | |
1195 } | |
1196 | |
1197 /* | |
1198 * If in debug mode, print info about adding the specified listener. | |
1199 */ | |
1200 private void printAddListener(Widget widget, String listenerType) { | |
1201 listenerInstalls++; | |
1202 if (DEBUG) { | |
1203 Stdout.formatln("Added listener>>>{} to>>>{}", listenerType, widget); //$NON-NLS-1$//$NON-NLS-2$ | |
1204 } | |
1205 } | |
1206 | |
1207 /* | |
1208 * If in debug mode, print info about adding the specified listener. | |
1209 */ | |
1210 private void printRemoveListener(Widget widget, String listenerType) { | |
1211 listenerInstalls--; | |
1212 if (DEBUG) { | |
1213 Stdout.formatln("Removed listener>>>{} from>>>{}", listenerType, widget); //$NON-NLS-1$//$NON-NLS-2$ | |
1214 } | |
1215 } | |
1216 } |