KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > AbstractInformationControlManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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  * Sean Montgomery, sean_montgomery@comcast.net - https://bugs.eclipse.org/bugs/show_bug.cgi?id=45095
11  *******************************************************************************/

12
13 package org.eclipse.jface.text;
14
15
16 import org.eclipse.swt.SWT;
17 import org.eclipse.swt.events.DisposeEvent;
18 import org.eclipse.swt.events.DisposeListener;
19 import org.eclipse.swt.graphics.GC;
20 import org.eclipse.swt.graphics.Point;
21 import org.eclipse.swt.graphics.Rectangle;
22 import org.eclipse.swt.widgets.Control;
23 import org.eclipse.swt.widgets.Display;
24 import org.eclipse.swt.widgets.Monitor;
25
26 import org.eclipse.core.runtime.Assert;
27
28 import org.eclipse.jface.dialogs.IDialogSettings;
29 import org.eclipse.jface.util.Geometry;
30
31
32 /**
33  * Manages the life cycle, visibility, layout, and contents of an
34  * {@link org.eclipse.jface.text.IInformationControl}. This manager can be
35  * installed on and removed from a control, referred to as the subject control,
36  * i.e. the one from which the subject of the information to be shown is
37  * retrieved. Also a manager can be enabled or disabled. An installed and
38  * enabled manager can be forced to show information in its information control
39  * using <code>showInformation</code>. An information control manager uses an
40  * <code>IInformationControlCloser</code> to define the behavior when a
41  * presented information control must be closed. The disposal of the subject and
42  * the information control are internally handled by the information control
43  * manager and are not the responsibility of the information control closer.
44  *
45  * @see org.eclipse.jface.text.IInformationControl
46  * @since 2.0
47  */

48 abstract public class AbstractInformationControlManager {
49
50     /**
51      * Interface of an information control closer. An information control closer
52      * monitors its information control and its subject control and closes the
53      * information control if necessary.
54      * <p>
55      * Clients must implement this interface in order to equip an information
56      * control manager accordingly.
57      */

58     public interface IInformationControlCloser {
59
60         /**
61          * Sets the closer's subject control. This is the control that parents
62          * the information control and from which the subject of the information
63          * to be shown is retrieved. <p>
64          * Must be called before <code>start</code>. May again be called
65          * between <code>start</code> and <code>stop</code>.
66          *
67          * @param subject the subject control
68          */

69         public void setSubjectControl(Control subject);
70
71         /**
72          * Sets the closer's information control, the one to close if necessary. <p>
73          * Must be called before <code>start</code>. May again be called
74          * between <code>start</code> and <code>stop</code>.
75          *
76          * @param control the information control
77          */

78         public void setInformationControl(IInformationControl control);
79
80         /**
81          * Tells this closer to start monitoring the subject and the information
82          * control. The presented information is considered valid for the given
83          * area of the subject control's display.
84          *
85          * @param subjectArea the area for which the presented information is valid
86          */

87         public void start(Rectangle subjectArea);
88
89         /**
90          * Tells this closer to stop monitoring the subject and the information control.
91          */

92         public void stop();
93     }
94
95
96
97     /**
98      * Constitutes entities to enumerate anchors for the layout of the information control.
99      */

100     public static final class Anchor {
101         private final int fFlag;
102         private Anchor(int flag) {
103             fFlag= flag;
104         }
105         /**
106          * Returns the SWT direction flag. One of {@link SWT#BOTTOM}, {@link SWT#TOP},
107          * {@link SWT#LEFT}, {@link SWT#RIGHT}, {@link SWT#CENTER},
108          *
109          * @return the SWT direction flag
110          * @since 3.3
111          */

112         int getSWTFlag() {
113             return fFlag;
114         }
115     }
116
117     /** Internal anchor list. */
118     private final static Anchor[] ANCHORS= { new Anchor(SWT.TOP), new Anchor(SWT.BOTTOM), new Anchor(SWT.LEFT), new Anchor(SWT.RIGHT) };
119
120     /** Anchor representing the top of the information area */
121     public final static Anchor ANCHOR_TOP= ANCHORS[0];
122     /** Anchor representing the bottom of the information area */
123     public final static Anchor ANCHOR_BOTTOM= ANCHORS[1];
124     /** Anchor representing the left side of the information area */
125     public final static Anchor ANCHOR_LEFT= ANCHORS[2];
126     /** Anchor representing the right side of the information area */
127     public final static Anchor ANCHOR_RIGHT= ANCHORS[3];
128     /**
129      * Anchor representing the middle of the subject control
130      * @since 2.1
131      */

132     public final static Anchor ANCHOR_GLOBAL= new Anchor(SWT.CENTER);
133
134     /**
135      * Dialog store constant for the location's x-coordinate.
136      * @since 3.0
137      */

138     public static final String JavaDoc STORE_LOCATION_X= "location.x"; //$NON-NLS-1$
139
/**
140      * Dialog store constant for the location's y-coordinate.
141      * @since 3.0
142      */

143     public static final String JavaDoc STORE_LOCATION_Y= "location.y"; //$NON-NLS-1$
144
/**
145      * Dialog store constant for the size's width.
146      * @since 3.0
147      */

148     public static final String JavaDoc STORE_SIZE_WIDTH= "size.width"; //$NON-NLS-1$
149
/**
150      * Dialog store constant for the size's height.
151      * @since 3.0
152      */

153     public static final String JavaDoc STORE_SIZE_HEIGHT= "size.height"; //$NON-NLS-1$
154

155
156     /** The subject control of the information control */
157     private Control fSubjectControl;
158
159     /** The display area for which the information to be presented is valid */
160     private Rectangle fSubjectArea;
161
162     /** The information to be presented */
163     private Object JavaDoc fInformation;
164
165     /** Indicates whether the information control takes focus when visible */
166     private boolean fTakesFocusWhenVisible= false;
167
168     /** The information control */
169     protected IInformationControl fInformationControl;
170
171     /** The information control creator */
172     protected IInformationControlCreator fInformationControlCreator;
173
174     /** The information control closer */
175     protected IInformationControlCloser fInformationControlCloser;
176
177     /** Indicates that the information control has been disposed */
178     protected boolean fDisposed= false;
179
180     /** Indicates the enable state of this manager */
181     private boolean fEnabled= false;
182
183     /** Cached, computed size constraints of the information control in points */
184     private Point fSizeConstraints;
185
186     /** The vertical margin when laying out the information control */
187     private int fMarginY= 5;
188
189     /** The horizontal margin when laying out the information control */
190     private int fMarginX= 5;
191
192     /** The width constraint of the information control in characters */
193     private int fWidthConstraint= 60;
194
195     /** The height constraint of the information control in characters */
196     private int fHeightConstraint= 6;
197
198     /** Indicates whether the size constraints should be enforced as minimal control size */
199     private boolean fEnforceAsMinimalSize= false;
200
201     /** Indicates whether the size constraints should be enforced as maximal control size */
202     private boolean fEnforceAsMaximalSize= false;
203
204     /** The anchor for laying out the information control in relation to the subject control */
205     private Anchor fAnchor= ANCHOR_BOTTOM;
206
207     /**
208      * The anchor sequence used to layout the information control if the original anchor
209      * can not be used because the information control would not fit in the display client area.
210      * <p>
211      * The fallback anchor for a given anchor is the one that comes directly after the given anchor or
212      * is the first one in the sequence if the given anchor is the last one in the sequence.
213      * <p>
214      * </p>
215      * Note: This sequence is ignored if the original anchor is not contained in this sequence.
216      * </p>
217      *
218      * @see #fAnchor
219      */

220     private Anchor[] fFallbackAnchors= ANCHORS;
221
222     /**
223      * The custom information control creator.
224      * @since 3.0
225      */

226     private volatile IInformationControlCreator fCustomInformationControlCreator;
227
228     /**
229      * Tells whether a custom information control is in use.
230      * @since 3.0
231      */

232     private boolean fIsCustomInformationControl= false;
233
234     /**
235      * The dialog settings for the control's bounds.
236      * @since 3.0
237      */

238     private IDialogSettings fDialogSettings;
239
240     /**
241      * Tells whether the control's location should be read
242      * from the dialog settings and whether the last
243      * valid control's size is stored back into the settings.
244      *
245      * @since 3.0
246      */

247     private boolean fIsRestoringLocation;
248
249     /**
250      * Tells whether the control's size should be read
251      * from the dialog settings and whether the last
252      * valid control's size is stored back into the settings.
253      *
254      * @since 3.0
255      */

256     private boolean fIsRestoringSize;
257
258     /**
259      * The dispose listener on the subject control.
260      *
261      * @since 3.1
262      */

263     private DisposeListener fSubjectControlDisposeListener;
264
265
266     /**
267      * Creates a new information control manager using the given information control creator.
268      * By default the following configuration is given:
269      * <ul>
270      * <li> enabled == false
271      * <li> horizontal margin == 5 points
272      * <li> vertical margin == 5 points
273      * <li> width constraint == 60 characters
274      * <li> height constraint == 6 characters
275      * <li> enforce constraints as minimal size == false
276      * <li> enforce constraints as maximal size == false
277      * <li> layout anchor == ANCHOR_BOTTOM
278      * <li> fall back anchors == { ANCHOR_TOP, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_RIGHT, ANCHOR_GLOBAL }
279      * <li> takes focus when visible == false
280      * </ul>
281      *
282      * @param creator the information control creator
283      */

284     protected AbstractInformationControlManager(IInformationControlCreator creator) {
285         Assert.isNotNull(creator);
286         fInformationControlCreator= creator;
287     }
288
289     /**
290      * Computes the information to be displayed and the area in which the computed
291      * information is valid. Implementation of this method must finish their computation
292      * by setting the computation results using <code>setInformation</code>.
293      */

294     abstract protected void computeInformation();
295
296     /**
297      * Sets the parameters of the information to be displayed. These are the information itself and
298      * the area for which the given information is valid. This so called subject area is a graphical
299      * region of the information control's subject control. This method calls <code>presentInformation()</code>
300      * to trigger the presentation of the computed information.
301      *
302      * @param information the information
303      * @param subjectArea the subject area
304      */

305     protected final void setInformation(String JavaDoc information, Rectangle subjectArea) {
306         fInformation= information;
307         fSubjectArea= subjectArea;
308         presentInformation();
309     }
310
311     /**
312      * Sets the parameters of the information to be displayed. These are the information itself and
313      * the area for which the given information is valid. This so called subject area is a graphical
314      * region of the information control's subject control. This method calls <code>presentInformation()</code>
315      * to trigger the presentation of the computed information.
316      *
317      * @param information the information
318      * @param subjectArea the subject area
319      * @since 2.1
320      */

321     protected final void setInformation(Object JavaDoc information, Rectangle subjectArea) {
322         fInformation= information;
323         fSubjectArea= subjectArea;
324         presentInformation();
325     }
326
327     /**
328      * Sets the information control closer for this manager.
329      *
330      * @param closer the information control closer for this manager
331      */

332     protected void setCloser(IInformationControlCloser closer) {
333         fInformationControlCloser= closer;
334     }
335
336     /**
337      * Sets the horizontal and vertical margin to be used when laying out the information control
338      * relative to the subject control.
339      *
340      * @param xMargin the x-margin
341      * @param yMargin the y-Margin
342      */

343     public void setMargins(int xMargin, int yMargin) {
344         fMarginX= xMargin;
345         fMarginY= yMargin;
346     }
347
348     /**
349      * Sets the width- and height constraints of the information control.
350      *
351      * @param widthInChar the width constraint in number of characters
352      * @param heightInChar the height constrain in number of characters
353      * @param enforceAsMinimalSize indicates whether the constraints describe the minimal allowed size of the control
354      * @param enforceAsMaximalSize indicates whether the constraints describe the maximal allowed size of the control
355      */

356     public void setSizeConstraints(int widthInChar, int heightInChar, boolean enforceAsMinimalSize, boolean enforceAsMaximalSize) {
357         fSizeConstraints= null;
358         fWidthConstraint= widthInChar;
359         fHeightConstraint= heightInChar;
360         fEnforceAsMinimalSize= enforceAsMinimalSize;
361         fEnforceAsMaximalSize= enforceAsMaximalSize;
362
363     }
364
365     /**
366      * Tells this information control manager to open the information
367      * control with the values contained in the given dialog settings
368      * and to store the control's last valid size in the given dialog
369      * settings.
370      * <p>
371      * Note: This API is only valid if the information control implements
372      * {@link IInformationControlExtension3}. Not following this restriction
373      * will later result in an {@link UnsupportedOperationException}.
374      * </p>
375      * <p>
376      * The constants used to store the values are:
377      * <ul>
378      * <li>{@link AbstractInformationControlManager#STORE_LOCATION_X}</li>
379      * <li>{@link AbstractInformationControlManager#STORE_LOCATION_Y}</li>
380      * <li>{@link AbstractInformationControlManager#STORE_SIZE_WIDTH}</li>
381      * <li>{@link AbstractInformationControlManager#STORE_SIZE_HEIGHT}</li>
382      * </ul>
383      * </p>
384      *
385      * @param dialogSettings
386      * @param restoreLocation <code>true</code> iff the location is must be (re-)stored
387      * @param restoreSize <code>true</code>iff the size is (re-)stored
388      * @since 3.0
389      */

390     public void setRestoreInformationControlBounds(IDialogSettings dialogSettings, boolean restoreLocation, boolean restoreSize) {
391         Assert.isTrue(dialogSettings != null && (restoreLocation || restoreSize));
392         fDialogSettings= dialogSettings;
393         fIsRestoringLocation= restoreLocation;
394         fIsRestoringSize= restoreSize;
395     }
396
397     /**
398      * Sets the anchor used for laying out the information control relative to the
399      * subject control. E.g, using <code>ANCHOR_TOP</code> indicates that the
400      * information control is position above the area for which the information to
401      * be displayed is valid.
402      *
403      * @param anchor the layout anchor
404      */

405     public void setAnchor(Anchor anchor) {
406         fAnchor= anchor;
407     }
408
409     /**
410      * Sets the anchors fallback sequence used to layout the information control if the original
411      * anchor can not be used because the information control would not fit in the display client
412      * area.
413      * <p>
414      * The fallback anchor for a given anchor is the one that comes directly after the given anchor or
415      * is the first one in the sequence if the given anchor is the last one in the sequence.
416      * <p>
417      * </p>
418      * Note: This sequence is ignored if the original anchor is not contained in this list.
419      * </p>
420      *
421      * @param fallbackAnchors the array with the anchor fallback sequence
422      * @see #setAnchor(AbstractInformationControlManager.Anchor)
423      */

424     public void setFallbackAnchors(Anchor[] fallbackAnchors) {
425         if (fallbackAnchors != null) {
426             fFallbackAnchors= new Anchor[fallbackAnchors.length];
427             System.arraycopy(fallbackAnchors, 0, fFallbackAnchors, 0, fallbackAnchors.length);
428         } else
429             fFallbackAnchors= null;
430     }
431
432     /**
433      * Sets the temporary custom control creator, overriding this manager's default information control creator.
434      *
435      * @param informationControlCreator the creator, possibly <code>null</code>
436      * @since 3.0
437      */

438     protected void setCustomInformationControlCreator(IInformationControlCreator informationControlCreator) {
439         if (informationControlCreator != null && fCustomInformationControlCreator instanceof IInformationControlCreatorExtension) {
440             IInformationControlCreatorExtension extension= (IInformationControlCreatorExtension) fCustomInformationControlCreator;
441             if (extension.canReplace(informationControlCreator))
442                 return;
443         }
444         fCustomInformationControlCreator= informationControlCreator;
445     }
446
447     /**
448      * Tells the manager whether it should set the focus to the information control when made visible.
449      *
450      * @param takesFocus <code>true</code> if information control should take focus when made visible
451      */

452     public void takesFocusWhenVisible(boolean takesFocus) {
453         fTakesFocusWhenVisible= takesFocus;
454     }
455
456     /**
457      * Handles the disposal of the subject control. By default, the information control
458      * is disposed by calling <code>disposeInformationControl</code>. Subclasses may extend
459      * this method.
460      */

461     protected void handleSubjectControlDisposed() {
462         disposeInformationControl();
463     }
464
465     /**
466      * Installs this manager on the given control. The control is now taking the role of
467      * the subject control. This implementation sets the control also as the information
468      * control closer's subject control and automatically enables this manager.
469      *
470      * @param subjectControl the subject control
471      */

472     public void install(Control subjectControl) {
473         if (fSubjectControl != null && !fSubjectControl.isDisposed() && fSubjectControlDisposeListener != null)
474             fSubjectControl.removeDisposeListener(fSubjectControlDisposeListener);
475
476         fSubjectControl= subjectControl;
477
478         if (fSubjectControl != null)
479             fSubjectControl.addDisposeListener(getSubjectControlDisposeListener());
480
481         if (fInformationControlCloser != null)
482             fInformationControlCloser.setSubjectControl(subjectControl);
483
484         setEnabled(true);
485         fDisposed= false;
486     }
487     
488     /**
489      * Returns the dispose listener which gets added
490      * to the subject control.
491      *
492      * @return the dispose listener
493      * @since 3.1
494      */

495     private DisposeListener getSubjectControlDisposeListener() {
496         if (fSubjectControlDisposeListener == null) {
497             fSubjectControlDisposeListener= new DisposeListener() {
498                 public void widgetDisposed(DisposeEvent e) {
499                     handleSubjectControlDisposed();
500                 }
501             };
502         }
503         return fSubjectControlDisposeListener;
504     }
505
506     /**
507      * Returns the subject control of this manager/information control.
508      *
509      * @return the subject control
510      */

511     protected Control getSubjectControl() {
512         return fSubjectControl;
513     }
514
515     /**
516      * Returns the actual subject area.
517      *
518      * @return the actual subject area
519      */

520     protected Rectangle getSubjectArea() {
521         return fSubjectArea;
522     }
523
524     /**
525      * Sets the enable state of this manager.
526      *
527      * @param enabled the enable state
528      * @deprecated visibility will be changed to protected
529      */

530     public void setEnabled(boolean enabled) {
531         fEnabled= enabled;
532     }
533
534     /**
535      * Returns whether this manager is enabled or not.
536      *
537      * @return <code>true</code> if this manager is enabled otherwise <code>false</code>
538      */

539     protected boolean isEnabled() {
540         return fEnabled;
541     }
542
543     /**
544      * Computes the size constraints of the information control in points based on the
545      * default font of the given subject control as well as the size constraints in character
546      * width.
547      *
548      * @param subjectControl the subject control
549      * @param informationControl the information control whose size constraints are computed
550      * @return the computed size constraints in points
551      */

552     protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
553
554         if (fSizeConstraints == null) {
555
556             if (subjectControl == null)
557                 return null;
558
559             GC gc= new GC(subjectControl);
560             gc.setFont(subjectControl.getFont());
561             int width= gc.getFontMetrics().getAverageCharWidth();
562             int height = gc.getFontMetrics().getHeight();
563             gc.dispose();
564
565             fSizeConstraints= new Point (fWidthConstraint * width, fHeightConstraint * height);
566         }
567
568         return new Point(fSizeConstraints.x, fSizeConstraints.y);
569     }
570
571     /**
572      * Computes the size constraints of the information control in points.
573      *
574      * @param subjectControl the subject control
575      * @param subjectArea the subject area
576      * @param informationControl the information control whose size constraints are computed
577      * @return the computed size constraints in points
578      * @since 3.0
579      */

580     protected Point computeSizeConstraints(Control subjectControl, Rectangle subjectArea, IInformationControl informationControl) {
581         return computeSizeConstraints(subjectControl, informationControl);
582     }
583
584     /**
585      * Handles the disposal of the information control. By default, the information
586      * control closer is stopped.
587      */

588     protected void handleInformationControlDisposed() {
589
590         storeInformationControlBounds();
591
592         fInformationControl= null;
593         if (fInformationControlCloser != null) {
594             fInformationControlCloser.setInformationControl(null);
595             fInformationControlCloser.stop();
596         }
597     }
598
599     /**
600      * Returns the information control. If the information control has not been created yet,
601      * it is automatically created.
602      *
603      * @return the information control
604      */

605     protected IInformationControl getInformationControl() {
606
607         if (fDisposed)
608             return fInformationControl;
609
610         IInformationControlCreator creator= null;
611
612         if (fCustomInformationControlCreator == null) {
613             creator= fInformationControlCreator;
614             if (fIsCustomInformationControl && fInformationControl != null) {
615                 fInformationControl.dispose();
616                 fInformationControl= null;
617             }
618             fIsCustomInformationControl= false;
619
620         } else {
621
622             creator= fCustomInformationControlCreator;
623             if (creator instanceof IInformationControlCreatorExtension) {
624                 IInformationControlCreatorExtension extension= (IInformationControlCreatorExtension) creator;
625                 if (fInformationControl != null && extension.canReuse(fInformationControl))
626                     return fInformationControl;
627             }
628             if (fInformationControl != null) {
629                 fInformationControl.dispose();
630                 fInformationControl= null;
631             }
632             fIsCustomInformationControl= true;
633         }
634
635         if (fInformationControl == null) {
636             fInformationControl= creator.createInformationControl(fSubjectControl.getShell());
637             fInformationControl.addDisposeListener(new DisposeListener() {
638                 public void widgetDisposed(DisposeEvent e) {
639                     handleInformationControlDisposed();
640                 }
641             });
642
643             if (fInformationControlCloser != null)
644                 fInformationControlCloser.setInformationControl(fInformationControl);
645         }
646
647         return fInformationControl;
648     }
649
650     /**
651      * Computes the display location of the information control. The location is computed
652      * considering the given subject area, the anchor at the subject area, and the
653      * size of the information control. This method does not care about whether the information
654      * control would be completely visible when placed at the result location.
655      *
656      * @param subjectArea the subject area
657      * @param controlSize the size of the information control
658      * @param anchor the anchor at the subject area
659      * @return the display location of the information control
660      */

661     protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) {
662         int xShift= 0;
663         int yShift= 0;
664
665         switch (anchor.getSWTFlag()) {
666             case SWT.CENTER:
667                 Point subjectControlSize= fSubjectControl.getSize();
668                 Point location= new Point(subjectControlSize.x / 2, subjectControlSize.y / 2);
669                 location.x -= (controlSize.x / 2);
670                 location.y -= (controlSize.y / 2);
671                 return fSubjectControl.toDisplay(location);
672             case SWT.BOTTOM:
673                 yShift= subjectArea.height + fMarginY;
674                 break;
675             case SWT.RIGHT:
676                 xShift= fMarginX + subjectArea.width;
677                 break;
678             case SWT.TOP:
679                 yShift= -controlSize.y - fMarginY;
680                 break;
681             case SWT.LEFT:
682                 xShift= -controlSize.x - fMarginX;
683                 break;
684         }
685
686         boolean isRTL= fSubjectControl != null && (fSubjectControl.getStyle() & SWT.RIGHT_TO_LEFT) != 0;
687         if (isRTL)
688             xShift += controlSize.x;
689
690         return fSubjectControl.toDisplay(new Point(subjectArea.x + xShift, subjectArea.y + yShift));
691     }
692     
693     /**
694      * Computes the area available for an information control given an anchor and the subject area
695      * within <code>bounds</code>.
696      *
697      * @param subjectArea the subject area
698      * @param bounds the bounds
699      * @param anchor the anchor at the subject area
700      * @return the area available at the given anchor relative to the subject area, confined to the
701      * monitor's client area
702      * @since 3.3
703      */

704     protected Rectangle computeAvailableArea(Rectangle subjectArea, Rectangle bounds, Anchor anchor) {
705         Rectangle area;
706         switch (anchor.getSWTFlag()) {
707             case SWT.CENTER:
708                 area= bounds;
709                 break;
710             case SWT.BOTTOM:
711                 int y= subjectArea.y + subjectArea.height + fMarginY;
712                 area= new Rectangle(bounds.x, y, bounds.width, bounds.y + bounds.height - y);
713                 break;
714             case SWT.RIGHT:
715                 int x= subjectArea.x + subjectArea.width + fMarginX;
716                 area= new Rectangle(x, bounds.y, bounds.x + bounds.width - x, bounds.height);
717                 break;
718             case SWT.TOP:
719                 area= new Rectangle(bounds.x, bounds.y, bounds.width, subjectArea.y - bounds.y - fMarginY);
720                 break;
721             case SWT.LEFT:
722                 area= new Rectangle(bounds.x, bounds.y, subjectArea.x - bounds.x - fMarginX, bounds.height);
723                 break;
724             default:
725                 Assert.isLegal(false);
726                 return null;
727         }
728
729         // Don't return negative areas if the subjectArea overlaps with the monitor bounds.
730
area.intersect(bounds);
731         return area;
732     }
733     
734     /**
735      * Checks whether a control of the given size at the given location would be completely visible
736      * in the given display area when laid out by using the given anchor. If not, this method tries
737      * to shift the control orthogonal to the direction given by the anchor to make it visible. If possible
738      * it updates the location.<p>
739      * This method returns <code>true</code> if the potentially updated position results in a
740      * completely visible control, or <code>false</code> otherwise.
741      *
742      *
743      * @param location the location of the control
744      * @param size the size of the control
745      * @param displayArea the display area in which the control should be visible
746      * @param anchor anchor for lying out the control
747      * @return <code>true</code>if the updated location is useful
748      */

749     protected boolean updateLocation(Point location, Point size, Rectangle displayArea, Anchor anchor) {
750
751         int displayLowerRightX= displayArea.x + displayArea.width;
752         int displayLowerRightY= displayArea.y + displayArea.height;
753         int lowerRightX= location.x + size.x;
754         int lowerRightY= location.y + size.y;
755
756         if (ANCHOR_BOTTOM == anchor || ANCHOR_TOP == anchor) {
757
758             if (ANCHOR_BOTTOM == anchor) {
759                 if (lowerRightY > displayLowerRightY)
760                     return false;
761             } else {
762                 if (location.y < displayArea.y)
763                     return false;
764             }
765
766             if (lowerRightX > displayLowerRightX)
767                 location.x= location.x - (lowerRightX - displayLowerRightX);
768
769             return (location.x >= displayArea.x && location.y >= displayArea.y);
770
771         } else if (ANCHOR_RIGHT == anchor || ANCHOR_LEFT == anchor) {
772
773             if (ANCHOR_RIGHT == anchor) {
774                 if (lowerRightX > displayLowerRightX)
775                     return false;
776             } else {
777                 if (location.x < displayArea.x)
778                     return false;
779             }
780
781             if (lowerRightY > displayLowerRightY)
782                 location.y= location.y - (lowerRightY - displayLowerRightY);
783
784             return (location.x >= displayArea.x && location.y >= displayArea.y);
785
786         } else if (ANCHOR_GLOBAL == anchor) {
787
788             if (lowerRightX > displayLowerRightX)
789                 location.x= location.x - (lowerRightX - displayLowerRightX);
790
791             if (lowerRightY > displayLowerRightY)
792                 location.y= location.y - (lowerRightY - displayLowerRightY);
793
794             return (location.x >= displayArea.x && location.y >= displayArea.y);
795         }
796
797         return false;
798     }
799
800     /**
801      * Returns the next fallback anchor as specified by this manager's
802      * fallback anchor sequence.
803      * <p>
804      * The fallback anchor for the given anchor is the one that comes directly after
805      * the given anchor or is the first one in the sequence if the given anchor is the
806      * last one in the sequence.
807      * </p>
808      * <p>
809      * Note: It is the callers responsibility to prevent an endless loop i.e. to test
810      * whether a given anchor has already been used once.
811      * then
812      * </p>
813      *
814      * @param anchor the current anchor
815      * @return the next fallback anchor or <code>null</code> if no fallback anchor is available
816      */

817     protected Anchor getNextFallbackAnchor(Anchor anchor) {
818
819         if (anchor == null || fFallbackAnchors == null)
820             return null;
821
822         for (int i= 0; i < fFallbackAnchors.length; i++) {
823             if (fFallbackAnchors[i] == anchor)
824                 return fFallbackAnchors[i + 1 == fFallbackAnchors.length ? 0 : i + 1];
825         }
826
827         return null;
828     }
829
830     /**
831      * Computes the location of the information control depending on the
832      * subject area and the size of the information control. This method attempts
833      * to find a location at which the information control lies completely in the display's
834      * client area while honoring the manager's default anchor. If this isn't possible using the
835      * default anchor, the fallback anchors are tried out.
836      *
837      * @param subjectArea the information area
838      * @param controlSize the size of the information control
839      * @return the computed location of the information control
840      */

841     protected Point computeInformationControlLocation(Rectangle subjectArea, Point controlSize) {
842         Rectangle subjectAreaDisplayRelative= Geometry.toDisplay(fSubjectControl, subjectArea);
843
844         Point upperLeft;
845         Anchor testAnchor= fAnchor;
846         Rectangle bestBounds= null;
847         int bestArea= Integer.MIN_VALUE;
848         Anchor bestAnchor= null;
849         do {
850
851             upperLeft= computeLocation(subjectArea, controlSize, testAnchor);
852             Monitor monitor= getClosestMonitor(subjectAreaDisplayRelative, testAnchor);
853             if (updateLocation(upperLeft, controlSize, monitor.getClientArea(), testAnchor))
854                 return upperLeft;
855             
856             // compute available area for this anchor and update if better than best
857
Rectangle available= computeAvailableArea(subjectAreaDisplayRelative, monitor.getClientArea(), testAnchor);
858             Rectangle proposed= new Rectangle(upperLeft.x, upperLeft.y, controlSize.x, controlSize.y);
859             available.intersect(proposed);
860             int area= available.width * available.height;
861             if (area > bestArea) {
862                 bestArea= area;
863                 bestBounds= available;
864                 bestAnchor= testAnchor;
865             }
866             
867             testAnchor= getNextFallbackAnchor(testAnchor);
868
869         } while (testAnchor != fAnchor && testAnchor != null);
870         
871         // no anchor is perfect - select the one with larges area and set the size to not overlap with the subjectArea
872
if (bestAnchor != ANCHOR_GLOBAL)
873             Geometry.set(controlSize, Geometry.getSize(bestBounds));
874         return Geometry.getLocation(bestBounds);
875     }
876     
877     /**
878      * Gets the closest monitor given an anchor and the subject area.
879      *
880      * @param area the subject area
881      * @param anchor the anchor
882      * @return the monitor closest to the edge of <code>area</code> defined by
883      * <code>anchor</code>
884      * @since 3.3
885      */

886     private Monitor getClosestMonitor(Rectangle area, Anchor anchor) {
887         Point center;
888         if (ANCHOR_GLOBAL == anchor)
889             center= Geometry.centerPoint(area);
890         else
891             center= Geometry.centerPoint(Geometry.getExtrudedEdge(area, 0, anchor.getSWTFlag()));
892         return getClosestMonitor(fSubjectControl.getDisplay(), Geometry.createRectangle(center, new Point(0, 0)));
893     }
894
895     /**
896      * Copied from org.eclipse.jface.window.Window. Returns the monitor whose client area contains
897      * the given point. If no monitor contains the point, returns the monitor that is closest to the
898      * point. If this is ever made public, it should be moved into a separate utility class.
899      *
900      * @param display the display to search for monitors
901      * @param rectangle the rectangle to find the closest monitor for (display coordinates)
902      * @return the montor closest to the given point
903      * @since 3.3
904      */

905     private Monitor getClosestMonitor(Display display, Rectangle rectangle) {
906         int closest = Integer.MAX_VALUE;
907
908         Point toFind= Geometry.centerPoint(rectangle);
909         Monitor[] monitors = display.getMonitors();
910         Monitor result = monitors[0];
911
912         for (int idx = 0; idx < monitors.length; idx++) {
913             Monitor current = monitors[idx];
914
915             Rectangle clientArea = current.getClientArea();
916
917             if (clientArea.contains(toFind)) {
918                 return current;
919             }
920
921             int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
922             if (distance < closest) {
923                 closest = distance;
924                 result = current;
925             }
926         }
927
928         return result;
929     }
930
931     /**
932      * Computes information to be displayed as well as the subject area
933      * and initiates that this information is presented in the information control.
934      * This happens only if this controller is enabled.
935      */

936     public void showInformation() {
937         if (fEnabled)
938             doShowInformation();
939     }
940
941     /**
942      * Computes information to be displayed as well as the subject area
943      * and initiates that this information is presented in the information control.
944      */

945     protected void doShowInformation() {
946         fSubjectArea= null;
947         fInformation= null;
948         computeInformation();
949     }
950
951     /**
952      * Presents the information in the information control or hides the information
953      * control if no information should be presented. The information has previously
954      * been set using <code>setInformation</code>.
955      */

956     protected void presentInformation() {
957         boolean hasContents= false;
958         if (fInformation instanceof String JavaDoc)
959             hasContents= ((String JavaDoc)fInformation).trim().length() > 0;
960         else
961             hasContents= (fInformation != null);
962
963         if (fSubjectArea != null && hasContents)
964             internalShowInformationControl(fSubjectArea, fInformation);
965         else
966             hideInformationControl();
967     }
968
969     /**
970      * Opens the information control with the given information and the specified
971      * subject area. It also activates the information control closer.
972      *
973      * @param subjectArea the information area
974      * @param information the information
975      */

976     private void internalShowInformationControl(Rectangle subjectArea, Object JavaDoc information) {
977
978         IInformationControl informationControl= getInformationControl();
979         if (informationControl != null) {
980
981             Point sizeConstraints= computeSizeConstraints(fSubjectControl, fSubjectArea, informationControl);
982             informationControl.setSizeConstraints(sizeConstraints.x, sizeConstraints.y);
983
984             if (informationControl instanceof IInformationControlExtension2)
985                 ((IInformationControlExtension2)informationControl).setInput(information);
986             else
987                 informationControl.setInformation(information.toString());
988
989             if (informationControl instanceof IInformationControlExtension) {
990                 IInformationControlExtension extension= (IInformationControlExtension)informationControl;
991                 if (!extension.hasContents())
992                     return;
993             }
994
995             Point size= null;
996             Point location= null;
997             Rectangle bounds= restoreInformationControlBounds();
998
999             if (bounds != null) {
1000                if (bounds.x > -1 && bounds.y > -1)
1001                    location= Geometry.getLocation(bounds);
1002
1003                if (bounds.width > -1 && bounds.height > -1)
1004                    size= Geometry.getSize(bounds);
1005            }
1006
1007            if (size == null)
1008                size= informationControl.computeSizeHint();
1009
1010            if (fEnforceAsMinimalSize)
1011                size= Geometry.max(size, sizeConstraints);
1012            if (fEnforceAsMaximalSize)
1013                size= Geometry.min(size, sizeConstraints);
1014
1015            if (location == null)
1016                location= computeInformationControlLocation(subjectArea, size);
1017
1018            // Make sure it fits on the screen
1019
Rectangle controlBounds= Geometry.createRectangle(location, size);
1020            Rectangle monitorBounds= getClosestMonitor(fSubjectControl.getDisplay(), controlBounds).getClientArea();
1021            controlBounds.intersect(monitorBounds);
1022            
1023            informationControl.setLocation(location);
1024            informationControl.setSize(size.x, size.y);
1025            
1026            showInformationControl(subjectArea);
1027        }
1028    }
1029
1030    /**
1031     * Hides the information control and stops the information control closer.
1032     */

1033    protected void hideInformationControl() {
1034        if (fInformationControl != null) {
1035            storeInformationControlBounds();
1036            fInformationControl.setVisible(false);
1037            if (fInformationControlCloser != null)
1038                fInformationControlCloser.stop();
1039        }
1040    }
1041
1042    /**
1043     * Shows the information control and starts the information control closer.
1044     * This method may not be called by clients.
1045     *
1046     * @param subjectArea the information area
1047     */

1048    protected void showInformationControl(Rectangle subjectArea) {
1049        fInformationControl.setVisible(true);
1050
1051        if (fTakesFocusWhenVisible)
1052            fInformationControl.setFocus();
1053
1054        if (fInformationControlCloser != null)
1055            fInformationControlCloser.start(subjectArea);
1056    }
1057
1058    /**
1059     * Disposes this manager's information control.
1060     */

1061    public void disposeInformationControl() {
1062        if (fInformationControl != null) {
1063            fInformationControl.dispose();
1064            handleInformationControlDisposed();
1065        }
1066    }
1067
1068    /**
1069     * Disposes this manager and if necessary all dependent parts such as
1070     * the information control. For symmetry it first disables this manager.
1071     */

1072    public void dispose() {
1073        if (!fDisposed) {
1074
1075            fDisposed= true;
1076
1077            setEnabled(false);
1078            disposeInformationControl();
1079            
1080            if (fSubjectControl != null && !fSubjectControl.isDisposed() && fSubjectControlDisposeListener != null)
1081                fSubjectControl.removeDisposeListener(fSubjectControlDisposeListener);
1082            fSubjectControl= null;
1083            fSubjectControlDisposeListener= null;
1084
1085            fIsCustomInformationControl= false;
1086            fCustomInformationControlCreator= null;
1087            fInformationControlCreator= null;
1088            fInformationControlCloser= null;
1089        }
1090    }
1091
1092    // ------ control's size handling dialog settings ------
1093

1094    /**
1095     * Stores the information control's bounds.
1096     *
1097     * @since 3.0
1098     */

1099    protected void storeInformationControlBounds() {
1100        if (fDialogSettings == null || fInformationControl == null || !(fIsRestoringLocation || fIsRestoringSize))
1101            return;
1102
1103        if (!(fInformationControl instanceof IInformationControlExtension3))
1104            throw new UnsupportedOperationException JavaDoc();
1105
1106        boolean controlRestoresSize= ((IInformationControlExtension3)fInformationControl).restoresSize();
1107        boolean controlRestoresLocation= ((IInformationControlExtension3)fInformationControl).restoresLocation();
1108
1109        Rectangle bounds= ((IInformationControlExtension3)fInformationControl).getBounds();
1110        if (bounds == null)
1111            return;
1112
1113        if (fIsRestoringSize && controlRestoresSize) {
1114            fDialogSettings.put(STORE_SIZE_WIDTH, bounds.width);
1115            fDialogSettings.put(STORE_SIZE_HEIGHT, bounds.height);
1116        }
1117        if (fIsRestoringLocation && controlRestoresLocation) {
1118            fDialogSettings.put(STORE_LOCATION_X, bounds.x);
1119            fDialogSettings.put(STORE_LOCATION_Y, bounds.y);
1120        }
1121    }
1122    /**
1123     * Restores the information control's bounds.
1124     *
1125     * @return the stored bounds
1126     * @since 3.0
1127     */

1128    protected Rectangle restoreInformationControlBounds() {
1129        if (fDialogSettings == null || !(fIsRestoringLocation || fIsRestoringSize))
1130            return null;
1131
1132        if (!(fInformationControl instanceof IInformationControlExtension3))
1133            throw new UnsupportedOperationException JavaDoc();
1134
1135        boolean controlRestoresSize= ((IInformationControlExtension3)fInformationControl).restoresSize();
1136        boolean controlRestoresLocation= ((IInformationControlExtension3)fInformationControl).restoresLocation();
1137
1138        Rectangle bounds= new Rectangle(-1, -1, -1, -1);
1139
1140        if (fIsRestoringSize && controlRestoresSize) {
1141            try {
1142                bounds.width= fDialogSettings.getInt(STORE_SIZE_WIDTH);
1143                bounds.height= fDialogSettings.getInt(STORE_SIZE_HEIGHT);
1144            } catch (NumberFormatException JavaDoc ex) {
1145                bounds.width= -1;
1146                bounds.height= -1;
1147            }
1148        }
1149
1150        if (fIsRestoringLocation && controlRestoresLocation) {
1151            try {
1152                bounds.x= fDialogSettings.getInt(STORE_LOCATION_X);
1153                bounds.y= fDialogSettings.getInt(STORE_LOCATION_Y);
1154            } catch (NumberFormatException JavaDoc ex) {
1155                bounds.x= -1;
1156                bounds.y= -1;
1157            }
1158        }
1159
1160        // sanity check
1161
if (bounds.x == -1 && bounds.y == -1 && bounds.width == -1 && bounds.height == -1)
1162            return null;
1163
1164        Rectangle maxBounds= null;
1165        if (fSubjectControl != null && !fSubjectControl.isDisposed())
1166            maxBounds= fSubjectControl.getDisplay().getBounds();
1167        else {
1168            // fallback
1169
Display display= Display.getCurrent();
1170            if (display == null)
1171                display= Display.getDefault();
1172            if (display != null && !display.isDisposed())
1173                maxBounds= display.getBounds();
1174        }
1175
1176
1177        if (bounds.width > -1 && bounds.height > -1) {
1178            if (maxBounds != null) {
1179                bounds.width= Math.min(bounds.width, maxBounds.width);
1180                bounds.height= Math.min(bounds.height, maxBounds.height);
1181            }
1182
1183            // Enforce an absolute minimal size
1184
bounds.width= Math.max(bounds.width, 30);
1185            bounds.height= Math.max(bounds.height, 30);
1186        }
1187
1188        if (bounds.x > -1 && bounds.y > -1 && maxBounds != null) {
1189            bounds.x= Math.max(bounds.x, maxBounds.x);
1190            bounds.y= Math.max(bounds.y, maxBounds.y);
1191
1192            if (bounds .width > -1 && bounds.height > -1) {
1193                bounds.x= Math.min(bounds.x, maxBounds.width - bounds.width);
1194                bounds.y= Math.min(bounds.y, maxBounds.height - bounds.height);
1195            }
1196        }
1197
1198        return bounds;
1199    }
1200}
1201
Popular Tags