17
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2005, 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 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module dwtx.jface.fieldassist.DecoratedField;
|
|
14
|
|
15 import dwtx.jface.fieldassist.FieldDecoration;
|
|
16 import dwtx.jface.fieldassist.FieldDecorationRegistry;
|
|
17 import dwtx.jface.fieldassist.IControlCreator;
|
|
18
|
|
19 import dwt.DWT;
|
|
20 import dwt.events.DisposeEvent;
|
|
21 import dwt.events.DisposeListener;
|
|
22 import dwt.events.FocusEvent;
|
|
23 import dwt.events.FocusListener;
|
|
24 import dwt.events.MouseAdapter;
|
|
25 import dwt.events.MouseEvent;
|
|
26 import dwt.events.MouseTrackListener;
|
|
27 import dwt.events.PaintEvent;
|
|
28 import dwt.events.PaintListener;
|
|
29 import dwt.graphics.GC;
|
|
30 import dwt.graphics.Image;
|
|
31 import dwt.graphics.Point;
|
|
32 import dwt.graphics.Region;
|
|
33 import dwt.layout.FormAttachment;
|
|
34 import dwt.layout.FormData;
|
|
35 import dwt.layout.FormLayout;
|
|
36 import dwt.widgets.Composite;
|
|
37 import dwt.widgets.Control;
|
|
38 import dwt.widgets.Display;
|
|
39 import dwt.widgets.Label;
|
|
40 import dwt.widgets.Shell;
|
|
41 import dwtx.core.runtime.Assert;
|
|
42
|
|
43 import dwt.dwthelper.utils;
|
|
44
|
|
45 /**
|
|
46 * DecoratedField manages image decorations around a control. It allows clients
|
|
47 * to specify an image decoration and a position for the decoration relative to
|
|
48 * the field. Decorations may be assigned descriptions, which are shown when the
|
|
49 * user hovers over the decoration. Clients can decorate any kind of control by
|
|
50 * supplying a {@link IControlCreator} to create the control that is decorated.
|
|
51 * <p>
|
|
52 * Decorations always appear on either horizontal side of the field, never above
|
|
53 * or below it. Decorations can be positioned at the top or bottom of either
|
|
54 * side. Future implementations may provide additional positioning options for
|
|
55 * decorations.
|
|
56 * <p>
|
|
57 * By default, DecoratedField will consult the {@link FieldDecorationRegistry}
|
|
58 * to determine how much space should be reserved for each decoration. This
|
|
59 * allows fields with decorations from different sources to align properly on
|
|
60 * the same dialog, since the registry tracks the size of all decorations
|
|
61 * registered. Therefore, it is recommended, but not required, that clients of
|
|
62 * DecoratedField register the decorations used. In cases where alignment
|
|
63 * between different fields is not a concern, clients can use
|
|
64 * <code>setUseMaximumDecorationWidth(false)</code> and need not register
|
|
65 * their decorations.
|
|
66 * <p>
|
|
67 * This class is not intended to be subclassed.
|
|
68 *
|
|
69 * @since 3.2
|
|
70 * @deprecated As of 3.3, clients should use {@link ControlDecoration} instead.
|
|
71 */
|
|
72 public class DecoratedField {
|
|
73
|
|
74 /**
|
|
75 * Cached platform flags for dealing with platform-specific issues.
|
|
76 */
|
35
|
77 private static bool CARBON(){
|
|
78 return "carbon".equals(DWT.getPlatform()); //$NON-NLS-1$
|
|
79 }
|
17
|
80
|
|
81 /**
|
|
82 * Constants describing the array indices used to hold the decorations in
|
|
83 * array slots.
|
|
84 */
|
|
85
|
|
86 private static const int LEFT_TOP = 0;
|
|
87
|
|
88 private static const int LEFT_BOTTOM = 1;
|
|
89
|
|
90 private static const int RIGHT_TOP = 2;
|
|
91
|
|
92 private static const int RIGHT_BOTTOM = 3;
|
|
93
|
|
94 private static const int DECORATION_SLOTS = 4;
|
|
95
|
|
96 /**
|
|
97 * Simple data structure class for specifying the internals for a field
|
|
98 * decoration. This class contains data specific to the implementation of
|
|
99 * field decorations as labels attached to the field. Clients should use
|
|
100 * <code>FieldDecoration</code> for specifying a decoration.
|
|
101 */
|
|
102 private class FieldDecorationData {
|
|
103
|
|
104 /* Package */FieldDecoration decoration;
|
|
105
|
|
106 /* Package */Label label;
|
|
107
|
|
108 /* Package */FormData data;
|
|
109
|
|
110 /* Package */bool showOnFocus;
|
|
111
|
|
112 /* Package */bool visible = true;
|
|
113
|
|
114 /**
|
|
115 * Create a decoration data representing the specified decoration, using
|
|
116 * the specified label and form data for its representation.
|
|
117 *
|
|
118 * @param decoration
|
|
119 * the decoration whose data is kept.
|
|
120 * @param label
|
|
121 * the label used to represent the decoration.
|
|
122 * @param formData
|
|
123 * the form data used to attach the decoration to its field.
|
|
124 * @param showOnFocus
|
|
125 * a bool specifying whether the decoration should only be
|
|
126 * shown when the field has focus.
|
|
127 */
|
|
128 this(FieldDecoration decoration, Label label,
|
|
129 FormData formData, bool showOnFocus) {
|
|
130 this.decoration = decoration;
|
|
131 this.label = label;
|
|
132 this.data = formData;
|
|
133 this.showOnFocus = showOnFocus;
|
|
134 }
|
|
135 }
|
|
136
|
|
137 /**
|
|
138 * Decorations keyed by position.
|
|
139 */
|
|
140 private FieldDecorationData[] decDatas;
|
|
141
|
|
142 /**
|
|
143 * The associated control
|
|
144 */
|
|
145 private Control control;
|
|
146
|
|
147 /**
|
|
148 * The composite with form layout used to manage decorations.
|
|
149 */
|
|
150 private Composite form;
|
|
151
|
|
152 /**
|
|
153 * The bool that indicates whether the maximum decoration width is used
|
|
154 * when allocating space for decorations.
|
|
155 */
|
|
156 private bool useMaxDecorationWidth = true;
|
|
157
|
|
158 /**
|
|
159 * The hover used for showing description text
|
|
160 */
|
|
161 private Hover hover;
|
|
162
|
|
163 /**
|
|
164 * The hover used to show a decoration image's description.
|
|
165 */
|
|
166 class Hover {
|
|
167 private static const String EMPTY = ""; //$NON-NLS-1$
|
|
168
|
|
169 /**
|
|
170 * Offset of info hover arrow from the left or right side.
|
|
171 */
|
|
172 private int hao = 10;
|
|
173
|
|
174 /**
|
|
175 * Width of info hover arrow.
|
|
176 */
|
|
177 private int haw = 8;
|
|
178
|
|
179 /**
|
|
180 * Height of info hover arrow.
|
|
181 */
|
|
182 private int hah = 10;
|
|
183
|
|
184 /**
|
|
185 * Margin around info hover text.
|
|
186 */
|
|
187 private int hm = 2;
|
|
188
|
|
189 /**
|
|
190 * This info hover's shell.
|
|
191 */
|
|
192 Shell hoverShell;
|
|
193
|
|
194 /**
|
|
195 * The info hover text.
|
|
196 */
|
|
197 String text = EMPTY;
|
|
198
|
|
199 /**
|
|
200 * The region used to manage the shell shape
|
|
201 */
|
|
202 Region region;
|
|
203
|
|
204 /**
|
|
205 * bool indicating whether the last computed polygon location had an
|
|
206 * arrow on left. (true if left, false if right).
|
|
207 */
|
|
208 bool arrowOnLeft = true;
|
|
209
|
|
210 /*
|
|
211 * Create a hover parented by the specified shell.
|
|
212 */
|
|
213 this(Shell parent) {
|
|
214 final Display display = parent.getDisplay();
|
|
215 hoverShell = new Shell(parent, DWT.NO_TRIM | DWT.ON_TOP
|
|
216 | DWT.NO_FOCUS);
|
|
217 hoverShell.setBackground(display
|
|
218 .getSystemColor(DWT.COLOR_INFO_BACKGROUND));
|
|
219 hoverShell.setForeground(display
|
|
220 .getSystemColor(DWT.COLOR_INFO_FOREGROUND));
|
|
221 hoverShell.addPaintListener(new class PaintListener {
|
|
222 public void paintControl(PaintEvent pe) {
|
|
223 pe.gc.drawString(text, hm, hm);
|
|
224 if (!CARBON) {
|
|
225 pe.gc.drawPolygon(getPolygon(true));
|
|
226 }
|
|
227 }
|
|
228 });
|
|
229 hoverShell.addMouseListener(new class MouseAdapter {
|
|
230 public void mouseDown(MouseEvent e) {
|
|
231 hideHover();
|
|
232 }
|
|
233 });
|
|
234 }
|
|
235
|
|
236 /*
|
|
237 * Compute a polygon that represents a hover with an arrow pointer. If
|
|
238 * border is true, compute the polygon inset by 1-pixel border. Consult
|
|
239 * the arrowOnLeft flag to determine which side the arrow is on.
|
|
240 */
|
|
241 int[] getPolygon(bool border) {
|
|
242 Point e = getExtent();
|
|
243 int b = border ? 1 : 0;
|
|
244 if (arrowOnLeft) {
|
|
245 return [ 0, 0, e.x - b, 0, e.x - b, e.y - b,
|
|
246 hao + haw, e.y - b, hao + haw / 2, e.y + hah - b, hao,
|
|
247 e.y - b, 0, e.y - b, 0, 0 ];
|
|
248 }
|
|
249 return [ 0, 0, e.x - b, 0, e.x - b, e.y - b,
|
|
250 e.x - hao - b, e.y - b, e.x - hao - haw / 2, e.y + hah - b,
|
|
251 e.x - hao - haw, e.y - b, 0, e.y - b, 0, 0 ];
|
|
252 }
|
|
253
|
|
254 /*
|
|
255 * Dispose the hover, it is no longer needed. Dispose any resources
|
|
256 * allocated by the hover.
|
|
257 */
|
|
258 void dispose() {
|
|
259 if (!hoverShell.isDisposed()) {
|
|
260 hoverShell.dispose();
|
|
261 }
|
|
262 if (region !is null) {
|
|
263 region.dispose();
|
|
264 }
|
|
265 }
|
|
266
|
|
267 /*
|
|
268 * Set the visibility of the hover.
|
|
269 */
|
|
270 void setVisible(bool visible) {
|
|
271 if (visible) {
|
|
272 if (!hoverShell.isVisible()) {
|
|
273 hoverShell.setVisible(true);
|
|
274 }
|
|
275 } else {
|
|
276 if (hoverShell.isVisible()) {
|
|
277 hoverShell.setVisible(false);
|
|
278 }
|
|
279 }
|
|
280 }
|
|
281
|
|
282 /*
|
|
283 * Set the text of the hover to the specified text. Recompute the size
|
|
284 * and location of the hover to hover near the specified control,
|
|
285 * pointing the arrow toward the target control.
|
|
286 */
|
|
287 void setText(String t, Control hoverNear, Control targetControl) {
|
|
288 if (t is null) {
|
|
289 t = EMPTY;
|
|
290 }
|
|
291 if (!t.equals(text)) {
|
|
292 Point oldSize = getExtent();
|
|
293 text = t;
|
|
294 hoverShell.redraw();
|
|
295 Point newSize = getExtent();
|
|
296 if (!oldSize.opEquals(newSize)) {
|
|
297 // set a flag that indicates the direction of arrow
|
|
298 arrowOnLeft = hoverNear.getLocation().x <= targetControl
|
|
299 .getLocation().x;
|
|
300 setNewShape();
|
|
301 }
|
|
302 }
|
|
303
|
|
304 if (hoverNear !is null) {
|
|
305 Point extent = getExtent();
|
|
306 int y = -extent.y - hah + 1;
|
|
307 int x = arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw
|
|
308 / 2;
|
|
309
|
|
310 hoverShell.setLocation(hoverNear.toDisplay(x, y));
|
|
311 }
|
|
312
|
|
313 }
|
|
314
|
|
315 /*
|
|
316 * Return whether or not the hover (shell) is visible.
|
|
317 */
|
|
318 bool isVisible() {
|
|
319 return hoverShell.isVisible();
|
|
320 }
|
|
321
|
|
322 /*
|
|
323 * Compute the extent of the hover for the current text.
|
|
324 */
|
|
325 Point getExtent() {
|
|
326 GC gc = new GC(hoverShell);
|
|
327 Point e = gc.textExtent(text);
|
|
328 gc.dispose();
|
|
329 e.x += hm * 2;
|
|
330 e.y += hm * 2;
|
|
331 return e;
|
|
332 }
|
|
333
|
|
334 /*
|
|
335 * Compute a new shape for the hover shell.
|
|
336 */
|
|
337 void setNewShape() {
|
|
338 Region oldRegion = region;
|
|
339 region = new Region();
|
|
340 region.add(getPolygon(false));
|
|
341 hoverShell.setRegion(region);
|
|
342 if (oldRegion !is null) {
|
|
343 oldRegion.dispose();
|
|
344 }
|
|
345
|
|
346 }
|
|
347 }
|
|
348
|
|
349 /**
|
|
350 * Construct a decorated field which is parented by the specified composite
|
|
351 * and has the given style bits. Use the controlCreator to create the
|
|
352 * specific kind of control that is decorated inside the field.
|
|
353 *
|
|
354 * @param parent
|
|
355 * the parent of the decorated field.
|
|
356 * @param style
|
|
357 * the desired style bits for the field.
|
|
358 * @param controlCreator
|
|
359 * the IControlCreator used to specify the specific kind of
|
|
360 * control that is to be decorated.
|
|
361 *
|
|
362 * @see IControlCreator
|
|
363 */
|
|
364 public this(Composite parent, int style,
|
|
365 IControlCreator controlCreator) {
|
|
366 decDatas = new FieldDecorationData[DECORATION_SLOTS];
|
|
367 this.form = createForm(parent);
|
|
368 this.control = controlCreator.createControl(form, style);
|
|
369
|
|
370 addControlListeners();
|
|
371 form.setTabList([ control ]);
|
|
372
|
|
373 // Set up the initial layout data.
|
|
374 FormData data = new FormData();
|
|
375 data.left = new FormAttachment(0, 0);
|
|
376 data.top = new FormAttachment(0, 0);
|
|
377 data.right = new FormAttachment(100, 0);
|
|
378 data.bottom = new FormAttachment(100, 0);
|
|
379 control.setLayoutData(data);
|
|
380
|
|
381 }
|
|
382
|
|
383 /**
|
|
384 * Adds an image decoration to the field.
|
|
385 *
|
|
386 * @param decoration
|
|
387 * A FieldDecoration describing the image and description for the
|
|
388 * decoration
|
|
389 *
|
|
390 * @param position
|
|
391 * The DWT constant indicating the position of the decoration
|
|
392 * relative to the field's control. The position should include
|
|
393 * style bits describing both the vertical and horizontal
|
|
394 * orientation. <code>DWT.LEFT</code> and
|
|
395 * <code>DWT.RIGHT</code> describe the horizontal placement of
|
|
396 * the decoration relative to the field, and the constants
|
|
397 * <code>DWT.TOP</code> and <code>DWT.BOTTOM</code> describe
|
|
398 * the vertical alignment of the decoration relative to the
|
|
399 * field. Decorations always appear on either horizontal side of
|
|
400 * the field, never above or below it. For example, a decoration
|
|
401 * appearing on the left side of the field, at the top, is
|
|
402 * specified as DWT.LEFT | DWT.TOP. If an image decoration
|
|
403 * already exists in the specified position, it will be replaced
|
|
404 * by the one specified.
|
|
405 * @param showOnFocus
|
|
406 * <code>true</code> if the decoration should only be shown
|
|
407 * when the associated control has focus, <code>false</code> if
|
|
408 * it should always be shown.
|
|
409 *
|
|
410 */
|
|
411 public void addFieldDecoration(FieldDecoration decoration, int position,
|
|
412 bool showOnFocus) {
|
|
413 Label label;
|
|
414 FormData formData;
|
|
415 int i = indexForPosition(position);
|
|
416 if (decDatas[i] is null) {
|
|
417 formData = createFormDataForIndex(i, decoration.getImage());
|
|
418 label = new Label(form, DWT.HORIZONTAL | DWT.VERTICAL | DWT.CENTER);
|
|
419 label.addMouseTrackListener(new class MouseTrackListener {
|
|
420 Label label_;
|
|
421 this(){
|
|
422 label_=label;
|
|
423 }
|
|
424 public void mouseHover(MouseEvent event) {
|
|
425 FieldDecorationData decData = cast(FieldDecorationData) event.widget
|
|
426 .getData();
|
|
427 String desc = decData.decoration.getDescription();
|
|
428 if (desc !is null) {
|
|
429 showHoverText(desc, label_);
|
|
430 }
|
|
431 }
|
|
432
|
|
433 public void mouseEnter(MouseEvent event) {
|
|
434 }
|
|
435
|
|
436 public void mouseExit(MouseEvent event) {
|
|
437 hideHover();
|
|
438 }
|
|
439 });
|
|
440 decDatas[i] = new FieldDecorationData(decoration, label, formData,
|
|
441 showOnFocus);
|
|
442 } else {
|
|
443 label = decDatas[i].label;
|
|
444 formData = decDatas[i].data;
|
|
445 decDatas[i].decoration = decoration;
|
|
446 decDatas[i].showOnFocus = showOnFocus;
|
|
447 }
|
|
448 label.setImage(decDatas[i].decoration.getImage());
|
|
449 label.setData(decDatas[i]);
|
|
450 label.setLayoutData(formData);
|
|
451 label.setVisible(!showOnFocus);
|
|
452
|
|
453 // Since sizes may have changed or there could be a new position
|
|
454 // defined, we need to update layout data on the control.
|
|
455 updateControlAttachments(i, decDatas[i]);
|
|
456 }
|
|
457
|
|
458 /*
|
|
459 * A decoration at the specified index has been added. Update the control's
|
|
460 * attachments if it has not previously been attached on that side or if it
|
|
461 * was attached to a decoration with a lesser width.
|
|
462 */
|
|
463 private void updateControlAttachments(int index, FieldDecorationData decData) {
|
|
464 FormData formData = cast(FormData) control.getLayoutData();
|
|
465 int newWidth = widthOf(decData.decoration.getImage());
|
|
466 // opposing represents the location of the decoration above or below
|
|
467 // the one in question.
|
|
468 int opposing;
|
|
469
|
|
470 switch (index) {
|
|
471 case LEFT_TOP:
|
|
472 case LEFT_BOTTOM:
|
|
473 if (index is LEFT_TOP) {
|
|
474 opposing = LEFT_BOTTOM;
|
|
475 } else {
|
|
476 opposing = LEFT_TOP;
|
|
477 }
|
|
478 if (decDatas[opposing] is null) {
|
|
479 // No decorator on the opposing side.
|
|
480 // Attach the control to this decorator
|
|
481 formData.left = new FormAttachment(decData.label);
|
|
482 } else if (decDatas[opposing].data.width < newWidth) {
|
|
483 // Decorator on opposing side is the smaller one. Attach
|
|
484 // control to the new one.
|
|
485 formData.left = new FormAttachment(decData.label);
|
|
486 // Center align the smaller one relative to the larger one.
|
|
487 decDatas[opposing].data.left.alignment = DWT.CENTER;
|
|
488 decDatas[opposing].data.left.control = decData.label;
|
|
489 } else {
|
|
490 // The new decorator is the smaller one. Keep the
|
|
491 // control attached to the opposing one.
|
|
492 formData = null;
|
|
493 // Horizontally center the smaller one relative to the larger
|
|
494 // one.
|
|
495 decData.data.left.alignment = DWT.CENTER;
|
|
496 decData.data.left.control = decDatas[opposing].label;
|
|
497 }
|
|
498 break;
|
|
499 /*
|
|
500 * The only real difference in right side cases is that we are attaching
|
|
501 * the right side of the control to the wider decoration rather than the
|
|
502 * left side of the control. Other concerns (horizontally aligning the
|
|
503 * smaller decoration relative to the larger one) are the same.
|
|
504 */
|
|
505 case RIGHT_TOP:
|
|
506 case RIGHT_BOTTOM:
|
|
507 if (index is RIGHT_TOP) {
|
|
508 opposing = RIGHT_BOTTOM;
|
|
509 } else {
|
|
510 opposing = RIGHT_TOP;
|
|
511 }
|
|
512 if (decDatas[opposing] is null) {
|
|
513 // No decorator on the opposing side.
|
|
514 // Attach the control to this decorator.
|
|
515 formData.right = new FormAttachment(decData.label);
|
|
516 } else if (decDatas[opposing].data.width < newWidth) {
|
|
517 // Decorator on opposing side is the smaller one. Attach
|
|
518 // control to the new one.
|
|
519 formData.right = new FormAttachment(decData.label);
|
|
520 // Center align the smaller one to the larger one.
|
|
521 // Note that this could be done using the left or right
|
|
522 // attachment, we use the right since it is already
|
|
523 // created for all right-side decorations.
|
|
524 decDatas[opposing].data.right.alignment = DWT.CENTER;
|
|
525 decDatas[opposing].data.right.control = decData.label;
|
|
526 } else {
|
|
527 // The new decorator is the smaller one. Keep the
|
|
528 // control attached to the opposing one.
|
|
529 formData = null;
|
|
530 // Horizontally center align the smaller one to the
|
|
531 // larger one.
|
|
532 decData.data.right.alignment = DWT.CENTER;
|
|
533 decData.data.right.control = decDatas[opposing].label;
|
|
534 }
|
|
535 break;
|
|
536 default:
|
|
537 return;
|
|
538 }
|
|
539 if (formData !is null) {
|
|
540 // Form data was updated.
|
|
541 control.setLayoutData(formData);
|
|
542 form.layout();
|
|
543 }
|
|
544 }
|
|
545
|
|
546 /**
|
|
547 * Get the control that is decorated by the receiver.
|
|
548 *
|
|
549 * @return the Control decorated by the receiver, or <code>null</code> if
|
|
550 * none has been created yet.
|
|
551 */
|
|
552 public Control getControl() {
|
|
553 return control;
|
|
554 }
|
|
555
|
|
556 /**
|
|
557 * Get the control that represents the decorated field. This composite
|
|
558 * should be used to lay out the field within its parent.
|
|
559 *
|
|
560 * @return the Control that should be layed out in the field's parent's
|
|
561 * layout. This is typically not the control itself, since
|
|
562 * additional controls are used to represent the decorations.
|
|
563 */
|
|
564 public Control getLayoutControl() {
|
|
565 return form;
|
|
566 }
|
|
567
|
|
568 /**
|
|
569 * Create the parent composite and a form layout that will be used to manage
|
|
570 * decorations.
|
|
571 */
|
|
572 private Composite createForm(Composite parent) {
|
|
573 Composite composite = new Composite(parent, DWT.NO_FOCUS);
|
|
574 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=126553
|
|
575 composite.setBackgroundMode(DWT.INHERIT_DEFAULT);
|
|
576 composite.setLayout(new FormLayout());
|
|
577 return composite;
|
|
578 }
|
|
579
|
|
580 /**
|
|
581 * Add any listeners needed on the target control.
|
|
582 */
|
|
583 private void addControlListeners() {
|
|
584 control.addDisposeListener(new class DisposeListener {
|
|
585 public void widgetDisposed(DisposeEvent event) {
|
|
586 if (hover !is null) {
|
|
587 hover.dispose();
|
|
588 }
|
|
589 }
|
|
590 });
|
|
591 control.addFocusListener(new class FocusListener {
|
|
592 public void focusGained(FocusEvent event) {
|
|
593 controlFocusGained();
|
|
594 }
|
|
595
|
|
596 public void focusLost(FocusEvent event) {
|
|
597 controlFocusLost();
|
|
598 }
|
|
599
|
|
600 });
|
|
601 }
|
|
602
|
|
603 /*
|
|
604 * Return the index in the array of decoration datas that represents the
|
|
605 * specified DWT position.
|
|
606 *
|
|
607 * @param position The DWT constant indicating the position of the
|
|
608 * decoration relative to the field's control. The position should include
|
|
609 * style bits describing both the vertical and horizontal orientation.
|
|
610 * <code>DWT.LEFT</code> and <code>DWT.RIGHT</code> describe the
|
|
611 * horizontal placement of the decoration relative to the field, and the
|
|
612 * constants <code>DWT.TOP</code> and <code>DWT.BOTTOM</code> describe
|
|
613 * the vertical alignment of the decoration relative to the field.
|
|
614 * Decorations always appear on either horizontal side of the field, never
|
|
615 * above or below it. For example, a decoration appearing on the left side
|
|
616 * of the field, at the top, is specified as DWT.LEFT | DWT.TOP.
|
|
617 *
|
|
618 * @return index the index in the array of decorations that represents the
|
|
619 * specified DWT position. If the position is not an expected position, the
|
|
620 * index representing the top left position will be returned.
|
|
621 *
|
|
622 */
|
|
623 private int indexForPosition(int position) {
|
|
624 switch (position) {
|
|
625 case DWT.LEFT | DWT.BOTTOM:
|
|
626 return LEFT_BOTTOM;
|
|
627 case DWT.RIGHT | DWT.TOP:
|
|
628 return RIGHT_TOP;
|
|
629 case DWT.RIGHT | DWT.BOTTOM:
|
|
630 return RIGHT_BOTTOM;
|
|
631 default:
|
|
632 return LEFT_TOP;
|
|
633 }
|
|
634 }
|
|
635
|
|
636 /*
|
|
637 * Create a form data that will place the decoration at the specified
|
|
638 * position.
|
|
639 *
|
|
640 * @param index the index in the decDatas describing the position of the
|
|
641 * decoration.
|
|
642 *
|
|
643 * @param image the image shown in the decoration.
|
|
644 *
|
|
645 */
|
|
646 private FormData createFormDataForIndex(int index, Image image) {
|
|
647 Assert.isTrue(index >= 0 && index < DECORATION_SLOTS,
|
|
648 "Index out of range"); //$NON-NLS-1$
|
|
649
|
|
650 FormData data = new FormData();
|
|
651 switch (index) {
|
|
652 case LEFT_TOP:
|
|
653 data.left = new FormAttachment(0, 0);
|
|
654 data.top = new FormAttachment(0, 0);
|
|
655 break;
|
|
656 case LEFT_BOTTOM:
|
|
657 data.left = new FormAttachment(0, 0);
|
|
658 data.bottom = new FormAttachment(100, 0);
|
|
659 break;
|
|
660 case RIGHT_TOP:
|
|
661 data.right = new FormAttachment(100, 0);
|
|
662 data.top = new FormAttachment(0, 0);
|
|
663 break;
|
|
664 case RIGHT_BOTTOM:
|
|
665 data.right = new FormAttachment(100, 0);
|
|
666 data.bottom = new FormAttachment(100, 0);
|
|
667 break;
|
|
668 }
|
|
669 data.width = widthOf(image);
|
|
670 data.height = DWT.DEFAULT;
|
|
671
|
|
672 return data;
|
|
673 }
|
|
674
|
|
675 /**
|
|
676 * Show the specified text using the same hover dialog as is used to show
|
|
677 * decorator descriptions. Normally, a decoration's description text will be
|
|
678 * shown in an info hover over the field's control whenever the mouse hovers
|
|
679 * over the decoration. This method can be used to show a decoration's
|
|
680 * description text at other times (such as when the control receives
|
|
681 * focus), or to show other text associated with the field.
|
|
682 *
|
|
683 * <p>
|
|
684 * If there is currently a hover visible, the hover's text will be replaced
|
|
685 * with the specified text.
|
|
686 *
|
|
687 * @param text
|
|
688 * the text to be shown in the info hover, or <code>null</code>
|
|
689 * if no text should be shown.
|
|
690 */
|
|
691 public void showHoverText(String text) {
|
|
692 showHoverText(text, control);
|
|
693 }
|
|
694
|
|
695 /**
|
|
696 * Hide any hover popups that are currently showing on the control.
|
|
697 * Normally, a decoration's description text will be shown in an info hover
|
|
698 * over the field's control as long as the mouse hovers over the decoration,
|
|
699 * and will be hidden when the mouse exits the control. This method can be
|
|
700 * used to hide a hover that was shown using <code>showHoverText</code>,
|
|
701 * or to programatically hide the current decoration hover.
|
|
702 *
|
|
703 * <p>
|
|
704 * This message has no effect if there is no current hover.
|
|
705 *
|
|
706 */
|
|
707 public void hideHover() {
|
|
708 if (hover !is null) {
|
|
709 hover.setVisible(false);
|
|
710 }
|
|
711 }
|
|
712
|
|
713 /*
|
|
714 * The target control gained focus. Any decorations that should show only
|
|
715 * when they have the focus should be shown here.
|
|
716 */
|
|
717 private void controlFocusGained() {
|
|
718 for (int i = 0; i < DECORATION_SLOTS; i++) {
|
|
719 if (decDatas[i] !is null && decDatas[i].showOnFocus) {
|
|
720 setVisible(decDatas[i], true);
|
|
721 }
|
|
722 }
|
|
723 }
|
|
724
|
|
725 /*
|
|
726 * The target control lost focus. Any decorations that should show only when
|
|
727 * they have the focus should be hidden here.
|
|
728 */
|
|
729 private void controlFocusLost() {
|
|
730 for (int i = 0; i < DECORATION_SLOTS; i++) {
|
|
731 if (decDatas[i] !is null && decDatas[i].showOnFocus) {
|
|
732 setVisible(decDatas[i], false);
|
|
733 }
|
|
734 }
|
|
735 }
|
|
736
|
|
737 /**
|
|
738 * Show the specified decoration. This message has no effect if the
|
|
739 * decoration is already showing, or was not already added to the field
|
|
740 * using <code>addFieldDecoration</code>.
|
|
741 *
|
|
742 * @param decoration
|
|
743 * the decoration to be shown.
|
|
744 */
|
|
745 public void showDecoration(FieldDecoration decoration) {
|
|
746 FieldDecorationData data = getDecorationData(decoration);
|
|
747 if (data is null) {
|
|
748 return;
|
|
749 }
|
|
750 // record the fact that client would like it to be visible
|
|
751 data.visible = true;
|
|
752 // even if it is supposed to be shown, if the field does not have focus,
|
|
753 // do not show it (yet)
|
|
754 if (!data.showOnFocus || control.isFocusControl()) {
|
|
755 setVisible(data, true);
|
|
756 }
|
|
757 }
|
|
758
|
|
759 /**
|
|
760 * Hide the specified decoration. This message has no effect if the
|
|
761 * decoration is already hidden, or was not already added to the field using
|
|
762 * <code>addFieldDecoration</code>.
|
|
763 *
|
|
764 * @param decoration
|
|
765 * the decoration to be hidden.
|
|
766 */
|
|
767 public void hideDecoration(FieldDecoration decoration) {
|
|
768 FieldDecorationData data = getDecorationData(decoration);
|
|
769 if (data is null) {
|
|
770 return;
|
|
771 }
|
|
772 // Store the desired visibility in the decData. We remember the
|
|
773 // client's instructions so that changes in visibility caused by
|
|
774 // field focus changes won't violate the client's visibility setting.
|
|
775 data.visible = false;
|
|
776 setVisible(data, false);
|
|
777 }
|
|
778
|
|
779 /**
|
|
780 * Update the specified decoration. This message should be used if the image
|
|
781 * or description in the decoration have changed. This message has no
|
|
782 * immediate effect if the decoration is not visible, and no effect at all
|
|
783 * if the decoration was not previously added to the field.
|
|
784 *
|
|
785 * @param decoration
|
|
786 * the decoration to be hidden.
|
|
787 */
|
|
788 public void updateDecoration(FieldDecoration decoration) {
|
|
789 FieldDecorationData data = getDecorationData(decoration);
|
|
790 if (data is null) {
|
|
791 return;
|
|
792 }
|
|
793 if (data.label !is null) {
|
|
794 data.label.setImage(decoration.getImage());
|
|
795 // If the decoration is being shown, and a hover is active,
|
|
796 // update the hover text to display the new description.
|
|
797 if (data.label.getVisible() is true && hover !is null) {
|
|
798 showHoverText(decoration.getDescription(), data.label);
|
|
799 }
|
|
800 }
|
|
801 }
|
|
802
|
|
803 /*
|
|
804 * Set the visibility of the specified decoration data. This method does not
|
|
805 * change the visibility value stored in the decData, but instead consults
|
|
806 * it to determine how the visibility should be changed. This method is
|
|
807 * called any time visibility of a decoration might change, whether by
|
|
808 * client API or focus changes.
|
|
809 */
|
|
810 private void setVisible(FieldDecorationData decData, bool visible) {
|
|
811 // Check the decData visibility flag, since it contains the client's
|
|
812 // instructions for visibility.
|
|
813 if (visible && decData.visible) {
|
|
814 decData.label.setVisible(true);
|
|
815 } else {
|
|
816 decData.label.setVisible(false);
|
|
817 }
|
|
818 }
|
|
819
|
|
820 /*
|
|
821 * Get the FieldDecorationData that corresponds to the given decoration.
|
|
822 */
|
|
823 private FieldDecorationData getDecorationData(FieldDecoration dec) {
|
|
824 for (int i = 0; i < DECORATION_SLOTS; i++) {
|
|
825 if (decDatas[i] !is null && dec is decDatas[i].decoration
|
|
826 && decDatas[i].label !is null
|
|
827 && !decDatas[i].label.isDisposed()) {
|
|
828 return decDatas[i];
|
|
829 }
|
|
830 }
|
|
831 return null;
|
|
832 }
|
|
833
|
|
834 /*
|
|
835 * Show the specified text in the hover, positioning the hover near the
|
|
836 * specified control.
|
|
837 */
|
|
838 private void showHoverText(String text, Control hoverNear) {
|
|
839 if (text is null) {
|
|
840 hideHover();
|
|
841 return;
|
|
842 }
|
|
843
|
|
844 if (hover is null) {
|
|
845 hover = new Hover(hoverNear.getShell());
|
|
846 }
|
|
847 hover.setText(text, hoverNear, control);
|
|
848 hover.setVisible(true);
|
|
849 }
|
|
850
|
|
851 /**
|
|
852 * Set a bool that indicates whether the receiver should use the
|
|
853 * decoration registry's maximum decoration width when allocating space for
|
|
854 * decorations. The default value is <code>true</code>. Using the maximum
|
|
855 * decoration width is useful so that decorated fields on the same dialog
|
|
856 * that have different decoration widths will all align. This also allows
|
|
857 * client dialogs to align non-decorated fields with decorated fields by
|
|
858 * consulting the maximum decoration width.
|
|
859 * </p>
|
|
860 * <p>
|
|
861 * Clients may wish to set this value to <code>false</code> in cases where
|
|
862 * space usage is more important than alignment of fields. This value must
|
|
863 * be set before the decorations are added in order to ensure proper
|
|
864 * alignment.
|
|
865 * </p>
|
|
866 *
|
|
867 * @param useMaximumWidth
|
|
868 * <code>true</code> if the maximum decoration width should be
|
|
869 * used as the size for all decorations, <code>false</code> if
|
|
870 * only the decoration size should be used.
|
|
871 *
|
|
872 * @see FieldDecorationRegistry#getMaximumDecorationWidth()
|
|
873 */
|
|
874 public void setUseMaximumDecorationWidth(bool useMaximumWidth) {
|
|
875 useMaxDecorationWidth = useMaximumWidth;
|
|
876 }
|
|
877
|
|
878 /*
|
|
879 * Return the width appropriate for the specified decoration image.
|
|
880 */
|
|
881 private int widthOf(Image image) {
|
|
882 if (image is null) {
|
|
883 return 0;
|
|
884 }
|
|
885 return useMaxDecorationWidth ? FieldDecorationRegistry.getDefault()
|
|
886 .getMaximumDecorationWidth() : image.getBounds().width;
|
|
887 }
|
|
888 }
|