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