KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > forms > widgets > ExpandableComposite


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.forms.widgets;
12
13 import org.eclipse.core.runtime.Assert;
14 import org.eclipse.core.runtime.ListenerList;
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.events.FocusEvent;
17 import org.eclipse.swt.events.FocusListener;
18 import org.eclipse.swt.events.KeyAdapter;
19 import org.eclipse.swt.events.KeyEvent;
20 import org.eclipse.swt.events.PaintEvent;
21 import org.eclipse.swt.events.PaintListener;
22 import org.eclipse.swt.events.TraverseEvent;
23 import org.eclipse.swt.events.TraverseListener;
24 import org.eclipse.swt.graphics.Color;
25 import org.eclipse.swt.graphics.Font;
26 import org.eclipse.swt.graphics.FontMetrics;
27 import org.eclipse.swt.graphics.GC;
28 import org.eclipse.swt.graphics.Point;
29 import org.eclipse.swt.graphics.Rectangle;
30 import org.eclipse.swt.widgets.Canvas;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.Event;
34 import org.eclipse.swt.widgets.Label;
35 import org.eclipse.swt.widgets.Layout;
36 import org.eclipse.swt.widgets.Listener;
37 import org.eclipse.swt.widgets.Menu;
38 import org.eclipse.ui.forms.events.ExpansionEvent;
39 import org.eclipse.ui.forms.events.HyperlinkAdapter;
40 import org.eclipse.ui.forms.events.HyperlinkEvent;
41 import org.eclipse.ui.forms.events.IExpansionListener;
42 import org.eclipse.ui.internal.forms.widgets.FormUtil;
43 import org.eclipse.ui.internal.forms.widgets.FormsResources;
44
45 /**
46  * This composite is capable of expanding or collapsing a single client that is
47  * its direct child. The composite renders an expansion toggle affordance
48  * (according to the chosen style), and a title that also acts as a hyperlink
49  * (can be selected and is traversable). The client is layed out below the title
50  * when expanded, or hidden when collapsed.
51  * <p>
52  * The widget can be instantiated as-is, or subclassed to modify some aspects of
53  * it. *
54  * <p>
55  * Since 3.1, left/right arrow keys can be used to control the expansion state.
56  * If several expandable composites are created in the same parent, up/down
57  * arrow keys can be used to traverse between them. Expandable text accepts
58  * mnemonics and mnemonic activation will toggle the expansion state.
59  *
60  * <p>
61  * While expandable composite recognize that different styles can be used to
62  * render the title bar, and even defines the constants for these styles (<code>TITLE_BAR</code>
63  * and <code>SHORT_TITLE_BAR</code> the actual painting is done in the
64  * subclasses.
65  *
66  * @see Section
67  * @since 3.0
68  */

69 public class ExpandableComposite extends Canvas {
70     /**
71      * If this style is used, a twistie will be used to render the expansion
72      * toggle.
73      */

74     public static final int TWISTIE = 1 << 1;
75
76     /**
77      * If this style is used, a tree node with either + or - signs will be used
78      * to render the expansion toggle.
79      */

80     public static final int TREE_NODE = 1 << 2;
81
82     /**
83      * If this style is used, the title text will be rendered as a hyperlink
84      * that can individually accept focus. Otherwise, it will still act like a
85      * hyperlink, but only the toggle control will accept focus.
86      */

87     public static final int FOCUS_TITLE = 1 << 3;
88
89     /**
90      * If this style is used, the client origin will be vertically aligned with
91      * the title text. Otherwise, it will start at x = 0.
92      */

93     public static final int CLIENT_INDENT = 1 << 4;
94
95     /**
96      * If this style is used, computed size of the composite will take the
97      * client width into consideration only in the expanded state. Otherwise,
98      * client width will always be taken into acount.
99      */

100     public static final int COMPACT = 1 << 5;
101
102     /**
103      * If this style is used, the control will be created in the expanded state.
104      * This state can later be changed programmatically or by the user if
105      * TWISTIE or TREE_NODE style is used.
106      */

107     public static final int EXPANDED = 1 << 6;
108
109     /**
110      * If this style is used, title bar decoration will be painted behind the
111      * text.
112      */

113     public static final int TITLE_BAR = 1 << 8;
114
115     /**
116      * If this style is used, a short version of the title bar decoration will
117      * be painted behind the text. This style is useful when a more descrete
118      * option is needed for the title bar.
119      *
120      * @since 3.1
121      */

122     public static final int SHORT_TITLE_BAR = 1 << 9;
123
124     /**
125      * If this style is used, title will not be rendered.
126      */

127     public static final int NO_TITLE = 1 << 12;
128
129     /**
130      * By default, text client is right-aligned. If this style is used, it will
131      * be positioned after the text control and vertically centered with it.
132      */

133     public static final int LEFT_TEXT_CLIENT_ALIGNMENT = 1 << 13;
134
135     /**
136      * Width of the margin that will be added around the control (default is 0).
137      */

138     public int marginWidth = 0;
139
140     /**
141      * Height of the margin that will be added around the control (default is
142      * 0).
143      */

144     public int marginHeight = 0;
145
146     /**
147      * Vertical spacing between the title area and the composite client control
148      * (default is 3).
149      */

150     public int clientVerticalSpacing = 3;
151
152     /**
153      * Vertical spacing between the title area and the description control
154      * (default is 0). The description control is normally placed at the new
155      * line as defined in the font used to render it. This value will be added
156      * to it.
157      *
158      * @since 3.3
159      */

160     public int descriptionVerticalSpacing = 0;
161
162     /**
163      * Horizontal margin around the inside of the title bar area when TITLE_BAR
164      * or SHORT_TITLE_BAR style is used. This variable is not used otherwise.
165      *
166      * @since 3.3
167      */

168     public int titleBarTextMarginWidth = 6;
169
170     /**
171      * The toggle widget used to expand the composite.
172      */

173     protected ToggleHyperlink toggle;
174
175     /**
176      * The text label for the title.
177      */

178     protected Control textLabel;
179
180     /**
181      * @deprecated this variable was left as protected by mistake. It will be
182      * turned into static and hidden in the future versions. Do not
183      * use them and do not change its value.
184      */

185     protected int VGAP = 3;
186     /**
187      * @deprecated this variable was left as protected by mistake. It will be
188      * turned into static and hidden in the future versions. Do not
189      * use it and do not change its value.
190      */

191     protected int GAP = 4;
192
193     static final int IGAP = 4;
194     static final int IVGAP = 3;
195
196     private static final Point NULL_SIZE = new Point(0, 0);
197
198     private static final int VSPACE = 3;
199
200     private static final int SEPARATOR_HEIGHT = 2;
201
202     private int expansionStyle = TWISTIE | FOCUS_TITLE | EXPANDED;
203
204     private boolean expanded;
205
206     private Control textClient;
207
208     private Control client;
209
210     private ListenerList listeners = new ListenerList();
211
212     private Color titleBarForeground;
213
214     private class ExpandableLayout extends Layout implements ILayoutExtension {
215
216         private SizeCache toggleCache = new SizeCache();
217
218         private SizeCache textClientCache = new SizeCache();
219
220         private SizeCache textLabelCache = new SizeCache();
221
222         private SizeCache descriptionCache = new SizeCache();
223
224         private SizeCache clientCache = new SizeCache();
225
226         private void initCache(boolean shouldFlush) {
227             toggleCache.setControl(toggle);
228             textClientCache.setControl(textClient);
229             textLabelCache.setControl(textLabel);
230             descriptionCache.setControl(getDescriptionControl());
231             clientCache.setControl(client);
232
233             if (shouldFlush) {
234                 toggleCache.flush();
235                 textClientCache.flush();
236                 textLabelCache.flush();
237                 descriptionCache.flush();
238                 clientCache.flush();
239             }
240         }
241
242         protected void layout(Composite parent, boolean changed) {
243             initCache(changed);
244
245             Rectangle clientArea = parent.getClientArea();
246             int thmargin = 0;
247             int tvmargin = 0;
248
249             if (hasTitleBar()) {
250                 thmargin = titleBarTextMarginWidth;
251                 tvmargin = IVGAP;
252             }
253             int x = marginWidth + thmargin;
254             int y = marginHeight + tvmargin;
255             Point tsize = NULL_SIZE;
256             Point tcsize = NULL_SIZE;
257             if (toggle != null)
258                 tsize = toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
259             int twidth = clientArea.width - marginWidth - marginWidth
260                     - thmargin - thmargin;
261             if (tsize.x > 0)
262                 twidth -= tsize.x + IGAP;
263             if (textClient != null)
264                 tcsize = textClientCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
265             if (tcsize.x > 0)
266                 twidth -= tcsize.x + IGAP;
267             Point size = NULL_SIZE;
268             if (textLabel != null)
269                 size = textLabelCache.computeSize(twidth, SWT.DEFAULT);
270             if (textLabel instanceof Label) {
271                 Point defSize = textLabelCache.computeSize(SWT.DEFAULT,
272                         SWT.DEFAULT);
273                 if (defSize.y == size.y) {
274                     // One line - pick the smaller of the two widths
275
size.x = Math.min(defSize.x, size.x);
276                 }
277             }
278             if (toggle != null) {
279                 GC gc = new GC(ExpandableComposite.this);
280                 gc.setFont(getFont());
281                 FontMetrics fm = gc.getFontMetrics();
282                 int textHeight = fm.getHeight();
283                 gc.dispose();
284                 if (textClient != null
285                         && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) != 0) {
286                     textHeight = Math.max(textHeight, tcsize.y);
287                 }
288                 int ty = textHeight / 2 - tsize.y / 2 + 1;
289                 ty = Math.max(ty, 0);
290                 ty += marginHeight + tvmargin;
291                 toggle.setLocation(x, ty);
292                 toggle.setSize(tsize);
293                 x += tsize.x + IGAP;
294             }
295             if (textLabel != null) {
296                 int ty = y;
297                 if (textClient != null
298                         && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) != 0) {
299                     if (size.y < tcsize.y)
300                         ty = tcsize.y / 2 - size.y / 2 + marginHeight
301                                 + tvmargin;
302                 }
303                 textLabelCache.setBounds(x, ty, size.x, size.y);
304             }
305             if (textClient != null) {
306                 int tcx;
307                 if ((expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) != 0) {
308                     tcx = x + size.x + GAP;
309                 } else {
310                     tcx = clientArea.width - tcsize.x - marginWidth - thmargin;
311                 }
312                 textClientCache.setBounds(tcx, y, tcsize.x, tcsize.y);
313             }
314             int tbarHeight = 0;
315             if (size.y > 0)
316                 tbarHeight = size.y;
317             if (tcsize.y > 0)
318                 tbarHeight = Math.max(tbarHeight, tcsize.y);
319             y += tbarHeight;
320             if (hasTitleBar())
321                 y += tvmargin;
322             if (getSeparatorControl() != null) {
323                 y += VSPACE;
324                 getSeparatorControl().setBounds(marginWidth, y,
325                         clientArea.width - marginWidth - marginWidth,
326                         SEPARATOR_HEIGHT);
327                 y += SEPARATOR_HEIGHT;
328                 if (expanded)
329                     y += VSPACE;
330             }
331             if (expanded) {
332                 int areaWidth = clientArea.width - marginWidth - marginWidth
333                         - thmargin - thmargin;
334                 int cx = marginWidth + thmargin;
335                 if ((expansionStyle & CLIENT_INDENT) != 0) {
336                     cx = x;
337                     areaWidth -= x;
338                 }
339                 if (client != null) {
340                     Point dsize = null;
341                     Control desc = getDescriptionControl();
342                     if (desc != null) {
343                         dsize = descriptionCache.computeSize(areaWidth,
344                                 SWT.DEFAULT);
345                         y += descriptionVerticalSpacing;
346                         descriptionCache.setBounds(cx, y, areaWidth, dsize.y);
347                         y += dsize.y + clientVerticalSpacing;
348                     } else {
349                         y += clientVerticalSpacing;
350                         if (getSeparatorControl() != null)
351                             y -= VSPACE;
352                     }
353                     int cwidth = areaWidth;
354                     int cheight = clientArea.height - marginHeight
355                             - marginHeight - y;
356                     clientCache.setBounds(cx, y, cwidth, cheight);
357                 }
358             }
359         }
360
361         protected Point computeSize(Composite parent, int wHint, int hHint,
362                 boolean changed) {
363             initCache(changed);
364
365             int width = 0, height = 0;
366             Point tsize = NULL_SIZE;
367             int twidth = 0;
368             if (toggle != null) {
369                 tsize = toggleCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
370                 twidth = tsize.x + IGAP;
371             }
372             int thmargin = 0;
373             int tvmargin = 0;
374
375             if (hasTitleBar()) {
376                 thmargin = titleBarTextMarginWidth;
377                 tvmargin = IVGAP;
378             }
379             int innerwHint = wHint;
380             if (innerwHint != SWT.DEFAULT)
381                 innerwHint -= twidth + marginWidth + marginWidth + thmargin
382                         + thmargin;
383
384             int innertHint = innerwHint;
385
386             Point tcsize = NULL_SIZE;
387             if (textClient != null) {
388                 tcsize = textClientCache.computeSize(SWT.DEFAULT, SWT.DEFAULT);
389                 if (innertHint != SWT.DEFAULT)
390                     innertHint -= IGAP + tcsize.x;
391             }
392             Point size = NULL_SIZE;
393
394             if (textLabel != null)
395                 size = textLabelCache.computeSize(innertHint, SWT.DEFAULT);
396             if (textLabel instanceof Label) {
397                 Point defSize = textLabelCache.computeSize(SWT.DEFAULT,
398                         SWT.DEFAULT);
399                 if (defSize.y == size.y) {
400                     // One line - pick the smaller of the two widths
401
size.x = Math.min(defSize.x, size.x);
402                 }
403             }
404             if (size.x > 0)
405                 width = size.x;
406             if (tcsize.x > 0)
407                 width += IGAP + tcsize.x;
408             height = tcsize.y > 0 ? Math.max(tcsize.y, size.y) : size.y;
409             if (getSeparatorControl() != null) {
410                 height += VSPACE + SEPARATOR_HEIGHT;
411                 if (expanded && client != null)
412                     height += VSPACE;
413             }
414             // if (hasTitleBar())
415
// height += VSPACE;
416
if ((expanded || (expansionStyle & COMPACT) == 0) && client != null) {
417                 int cwHint = wHint;
418
419                 if (cwHint != SWT.DEFAULT) {
420                     cwHint -= marginWidth + marginWidth + thmargin + thmargin;
421                     if ((expansionStyle & CLIENT_INDENT) != 0)
422                         if (tcsize.x > 0)
423                             cwHint -= twidth;
424                 }
425                 Point dsize = null;
426                 Point csize = clientCache.computeSize(FormUtil.getWidthHint(
427                         cwHint, client), SWT.DEFAULT);
428                 if (getDescriptionControl() != null) {
429                     int dwHint = cwHint;
430                     if (dwHint == SWT.DEFAULT) {
431                         dwHint = csize.x;
432                         if ((expansionStyle & CLIENT_INDENT) != 0)
433                             dwHint -= twidth;
434                     }
435                     dsize = descriptionCache.computeSize(dwHint, SWT.DEFAULT);
436                 }
437                 if (dsize != null) {
438                     width = Math.max(width, dsize.x);
439                     if (expanded)
440                         height += descriptionVerticalSpacing + dsize.y
441                                 + clientVerticalSpacing;
442                 } else {
443                     height += clientVerticalSpacing;
444                     if (getSeparatorControl() != null)
445                         height -= VSPACE;
446                 }
447                 width = Math.max(width, csize.x);
448                 if (expanded)
449                     height += csize.y;
450             }
451             if (toggle != null) {
452                 height = height - size.y + Math.max(size.y, tsize.y);
453                 width += twidth;
454             }
455
456             Point result = new Point(width + marginWidth + marginWidth
457                     + thmargin + thmargin, height + marginHeight + marginHeight
458                     + tvmargin + tvmargin);
459             return result;
460         }
461
462         public int computeMinimumWidth(Composite parent, boolean changed) {
463             return computeSize(parent, 0, SWT.DEFAULT, changed).x;
464         }
465
466         /*
467          * (non-Javadoc)
468          *
469          * @see org.eclipse.ui.forms.parts.ILayoutExtension#computeMinimumWidth(org.eclipse.swt.widgets.Composite,
470          * boolean)
471          */

472         public int computeMaximumWidth(Composite parent, boolean changed) {
473             return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
474         }
475     }
476
477     /**
478      * Creates an expandable composite using a TWISTIE toggle.
479      *
480      * @param parent
481      * the parent composite
482      * @param style
483      * SWT style bits
484      */

485     public ExpandableComposite(Composite parent, int style) {
486         this(parent, style, TWISTIE);
487     }
488
489     /**
490      * Creates the expandable composite in the provided parent.
491      *
492      * @param parent
493      * the parent
494      * @param style
495      * the control style (as expected by SWT subclass)
496      * @param expansionStyle
497      * the style of the expansion widget (TREE_NODE, TWISTIE,
498      * CLIENT_INDENT, COMPACT, FOCUS_TITLE,
499      * LEFT_TEXT_CLIENT_ALIGNMENT, NO_TITLE)
500      */

501     public ExpandableComposite(Composite parent, int style, int expansionStyle) {
502         super(parent, style);
503         this.expansionStyle = expansionStyle;
504         if ((expansionStyle & TITLE_BAR) != 0)
505             setBackgroundMode(SWT.INHERIT_DEFAULT);
506         super.setLayout(new ExpandableLayout());
507         if (hasTitleBar()) {
508             this.addPaintListener(new PaintListener() {
509                 public void paintControl(PaintEvent e) {
510                     onPaint(e);
511                 }
512             });
513         }
514         if ((expansionStyle & TWISTIE) != 0)
515             toggle = new Twistie(this, SWT.NULL);
516         else if ((expansionStyle & TREE_NODE) != 0)
517             toggle = new TreeNode(this, SWT.NULL);
518         else
519             expanded = true;
520         if ((expansionStyle & EXPANDED) != 0)
521             expanded = true;
522         if (toggle != null) {
523             toggle.setExpanded(expanded);
524             toggle.addHyperlinkListener(new HyperlinkAdapter() {
525                 public void linkActivated(HyperlinkEvent e) {
526                     toggleState();
527                 }
528             });
529             toggle.addPaintListener(new PaintListener() {
530                 public void paintControl(PaintEvent e) {
531                     if (textLabel instanceof Label && !isFixedStyle())
532                         textLabel.setForeground(toggle.hover ? toggle
533                                 .getHoverDecorationColor()
534                                 : getTitleBarForeground());
535                 }
536             });
537             toggle.addKeyListener(new KeyAdapter() {
538                 public void keyPressed(KeyEvent e) {
539                     if (e.keyCode == SWT.ARROW_UP) {
540                         verticalMove(false);
541                         e.doit = false;
542                     } else if (e.keyCode == SWT.ARROW_DOWN) {
543                         verticalMove(true);
544                         e.doit = false;
545                     }
546                 }
547             });
548             if ((getExpansionStyle()&FOCUS_TITLE)==0) {
549                 toggle.paintFocus=false;
550                 toggle.addFocusListener(new FocusListener() {
551                     public void focusGained(FocusEvent e) {
552                         textLabel.redraw();
553                     }
554
555                     public void focusLost(FocusEvent e) {
556                         textLabel.redraw();
557                     }
558                 });
559             }
560         }
561         if ((expansionStyle & FOCUS_TITLE) != 0) {
562             Hyperlink link = new Hyperlink(this, SWT.WRAP);
563             link.addHyperlinkListener(new HyperlinkAdapter() {
564                 public void linkActivated(HyperlinkEvent e) {
565                     programmaticToggleState();
566                 }
567             });
568             textLabel = link;
569         } else if ((expansionStyle & NO_TITLE) == 0) {
570             final Label label = new Label(this, SWT.WRAP);
571             if (!isFixedStyle()) {
572                 label.setCursor(FormsResources.getHandCursor());
573                 Listener listener = new Listener() {
574                     public void handleEvent(Event e) {
575                         switch (e.type) {
576                         case SWT.MouseDown:
577                             if (toggle != null)
578                                 toggle.setFocus();
579                             break;
580                         case SWT.MouseUp:
581                             label.setCursor(FormsResources.getBusyCursor());
582                             programmaticToggleState();
583                             label.setCursor(FormsResources.getHandCursor());
584                             break;
585                         case SWT.MouseEnter:
586                             if (toggle != null) {
587                                 label.setForeground(toggle
588                                         .getHoverDecorationColor());
589                                 toggle.hover = true;
590                                 toggle.redraw();
591                             }
592                             break;
593                         case SWT.MouseExit:
594                             if (toggle != null) {
595                                 label.setForeground(getTitleBarForeground());
596                                 toggle.hover = false;
597                                 toggle.redraw();
598                             }
599                             break;
600                         case SWT.Paint:
601                             if (toggle != null) {
602                                 paintTitleFocus(e.gc);
603                             }
604                             break;
605                         }
606                     }
607                 };
608                 label.addListener(SWT.MouseDown, listener);
609                 label.addListener(SWT.MouseUp, listener);
610                 label.addListener(SWT.MouseEnter, listener);
611                 label.addListener(SWT.MouseExit, listener);
612                 label.addListener(SWT.Paint, listener);
613             }
614             textLabel = label;
615         }
616         if (textLabel != null) {
617             textLabel.setMenu(getMenu());
618             textLabel.addTraverseListener(new TraverseListener() {
619                 public void keyTraversed(TraverseEvent e) {
620                     if (e.detail == SWT.TRAVERSE_MNEMONIC) {
621                         // steal the mnemonic
622
if (!isVisible() || !isEnabled())
623                             return;
624                         if (FormUtil.mnemonicMatch(getText(), e.character)) {
625                             e.doit = false;
626                             programmaticToggleState();
627                             setFocus();
628                         }
629                     }
630                 }
631             });
632         }
633     }
634
635     /**
636      * Overrides 'super' to pass the menu to the text label.
637      *
638      * @param menu
639      * the menu from the parent to attach to this control.
640      */

641
642     public void setMenu(Menu menu) {
643         if (textLabel != null)
644             textLabel.setMenu(menu);
645         super.setMenu(menu);
646     }
647
648     /**
649      * Prevents assignment of the layout manager - expandable composite uses its
650      * own layout.
651      */

652     public final void setLayout(Layout layout) {
653     }
654
655     /**
656      * Sets the background of all the custom controls in the expandable.
657      */

658     public void setBackground(Color bg) {
659         super.setBackground(bg);
660         if ((getExpansionStyle() & TITLE_BAR) == 0) {
661             if (textLabel != null)
662                 textLabel.setBackground(bg);
663             if (toggle != null)
664                 toggle.setBackground(bg);
665         }
666     }
667
668     /**
669      * Sets the foreground of all the custom controls in the expandable.
670      */

671     public void setForeground(Color fg) {
672         super.setForeground(fg);
673         if (textLabel != null)
674             textLabel.setForeground(fg);
675         if (toggle != null)
676             toggle.setForeground(fg);
677     }
678
679     /**
680      * Sets the color of the toggle control.
681      *
682      * @param c
683      * the color object
684      */

685     public void setToggleColor(Color c) {
686         if (toggle != null)
687             toggle.setDecorationColor(c);
688     }
689
690     /**
691      * Sets the active color of the toggle control (when the mouse enters the
692      * toggle area).
693      *
694      * @param c
695      * the active color object
696      */

697     public void setActiveToggleColor(Color c) {
698         if (toggle != null)
699             toggle.setHoverDecorationColor(c);
700     }
701
702     /**
703      * Sets the fonts of all the custom controls in the expandable.
704      */

705     public void setFont(Font font) {
706         super.setFont(font);
707         if (textLabel != null)
708             textLabel.setFont(font);
709         if (toggle != null)
710             toggle.setFont(font);
711     }
712
713     /*
714      * (non-Javadoc)
715      *
716      * @see org.eclipse.swt.widgets.Control#setEnabled(boolean)
717      */

718
719     public void setEnabled(boolean enabled) {
720         if (textLabel != null)
721             textLabel.setEnabled(enabled);
722         if (toggle != null)
723             toggle.setEnabled(enabled);
724         super.setEnabled(enabled);
725     }
726
727     /**
728      * Sets the client of this expandable composite. The client must not be
729      * <samp>null </samp> and must be a direct child of this container.
730      *
731      * @param client
732      * the client that will be expanded or collapsed
733      */

734     public void setClient(Control client) {
735         Assert.isTrue(client != null && client.getParent().equals(this));
736         this.client = client;
737     }
738
739     /**
740      * Returns the current expandable client.
741      *
742      * @return the client control
743      */

744     public Control getClient() {
745         return client;
746     }
747
748     /**
749      * Sets the title of the expandable composite. The title will act as a
750      * hyperlink and activating it will toggle the client between expanded and
751      * collapsed state.
752      *
753      * @param title
754      * the new title string
755      * @see #getText()
756      */

757     public void setText(String JavaDoc title) {
758         if (textLabel instanceof Label)
759             ((Label) textLabel).setText(title);
760         else if (textLabel instanceof Hyperlink)
761             ((Hyperlink) textLabel).setText(title);
762     }
763
764     /**
765      * Returns the title string.
766      *
767      * @return the title string
768      * @see #setText(String)
769      */

770     public String JavaDoc getText() {
771         if (textLabel instanceof Label)
772             return ((Label) textLabel).getText();
773         else if (textLabel instanceof Hyperlink)
774             return ((Hyperlink) textLabel).getText();
775         else
776             return ""; //$NON-NLS-1$
777
}
778
779     /**
780      * Tests the expanded state of the composite.
781      *
782      * @return <samp>true </samp> if expanded, <samp>false </samp> if collapsed.
783      */

784     public boolean isExpanded() {
785         return expanded;
786     }
787
788     /**
789      * Returns the bitwise-ORed style bits for the expansion control.
790      *
791      * @return the bitwise-ORed style bits for the expansion control
792      */

793     public int getExpansionStyle() {
794         return expansionStyle;
795     }
796
797     /**
798      * Programmatically changes expanded state.
799      *
800      * @param expanded
801      * the new expanded state
802      */

803     public void setExpanded(boolean expanded) {
804         internalSetExpanded(expanded);
805         if (toggle != null)
806             toggle.setExpanded(expanded);
807     }
808
809     /**
810      * Performs the expansion state change for the expandable control.
811      *
812      * @param expanded
813      * the expansion state
814      */

815     protected void internalSetExpanded(boolean expanded) {
816         if (this.expanded != expanded) {
817             this.expanded = expanded;
818             if (getDescriptionControl() != null)
819                 getDescriptionControl().setVisible(expanded);
820             if (client != null)
821                 client.setVisible(expanded);
822             layout();
823         }
824     }
825
826     /**
827      * Adds the listener that will be notified when the expansion state changes.
828      *
829      * @param listener
830      * the listener to add
831      */

832     public void addExpansionListener(IExpansionListener listener) {
833         listeners.add(listener);
834     }
835
836     /**
837      * Removes the expansion listener.
838      *
839      * @param listener
840      * the listner to remove
841      */

842     public void removeExpansionListener(IExpansionListener listener) {
843         listeners.remove(listener);
844     }
845
846     /**
847      * If TITLE_BAR or SHORT_TITLE_BAR style is used, title bar decoration will
848      * be painted behind the text in this method. The default implementation
849      * does nothing - subclasses are responsible for rendering the title area.
850      *
851      * @param e
852      * the paint event
853      */

854     protected void onPaint(PaintEvent e) {
855     }
856
857     /**
858      * Returns description control that will be placed under the title if
859      * present.
860      *
861      * @return the description control or <samp>null </samp> if not used.
862      */

863     protected Control getDescriptionControl() {
864         return null;
865     }
866
867     /**
868      * Returns the separator control that will be placed between the title and
869      * the description if present.
870      *
871      * @return the separator control or <samp>null </samp> if not used.
872      */

873     protected Control getSeparatorControl() {
874         return null;
875     }
876
877     /**
878      * Computes the size of the expandable composite.
879      *
880      * @see org.eclipse.swt.widgets.Composite#computeSize
881      */

882     public Point computeSize(int wHint, int hHint, boolean changed) {
883         checkWidget();
884         Point size;
885         ExpandableLayout layout = (ExpandableLayout) getLayout();
886         if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
887             size = layout.computeSize(this, wHint, hHint, changed);
888         } else {
889             size = new Point(wHint, hHint);
890         }
891         Rectangle trim = computeTrim(0, 0, size.x, size.y);
892         return new Point(trim.width, trim.height);
893     }
894
895     /**
896      * Returns <samp>true </samp> if the composite is fixed i.e. cannot be
897      * expanded or collapsed. Fixed control will still contain the title,
898      * separator and description (if present) as well as the client, but will be
899      * in the permanent expanded state and the toggle affordance will not be
900      * shown.
901      *
902      * @return <samp>true </samp> if the control is fixed in the expanded state,
903      * <samp>false </samp> if it can be collapsed.
904      */

905     protected boolean isFixedStyle() {
906         return (expansionStyle & TWISTIE) == 0
907                 && (expansionStyle & TREE_NODE) == 0;
908     }
909
910     /**
911      * Returns the text client control.
912      *
913      * @return Returns the text client control if specified, or
914      * <code>null</code> if not.
915      */

916     public Control getTextClient() {
917         return textClient;
918     }
919
920     /**
921      * Sets the text client control. Text client is a control that is a child of
922      * the expandable composite and is placed to the right of the text. It can
923      * be used to place small image hyperlinks. If more than one control is
924      * needed, use Composite to hold them. Care should be taken that the height
925      * of the control is comparable to the height of the text.
926      *
927      * @param textClient
928      * the textClient to set or <code>null</code> if not needed any
929      * more.
930      */

931     public void setTextClient(Control textClient) {
932         if (this.textClient != null)
933             this.textClient.dispose();
934         this.textClient = textClient;
935     }
936
937     /**
938      * Returns the difference in height between the text and the text client (if
939      * set). This difference can cause vertical alignment problems when two
940      * expandable composites are placed side by side, one with and one without
941      * the text client. Use this method obtain the value to add to either
942      * <code>descriptionVerticalSpacing</code> (if you have description) or
943      * <code>clientVerticalSpacing</code> to correct the alignment of the
944      * expandable without the text client.
945      *
946      * @return the difference in height between the text and the text client or
947      * 0 if no corrective action is needed.
948      * @since 3.3
949      */

950     public int getTextClientHeightDifference() {
951         if (textClient == null || textLabel == null)
952             return 0;
953         int theight = textLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
954         int tcheight = textClient.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
955         return Math.max(tcheight - theight, 0);
956     }
957
958     /**
959      * Tests if this expandable composite renders a title bar around the text.
960      *
961      * @return <code>true</code> for <code>TITLE_BAR</code> or
962      * <code>SHORT_TITLE_BAR</code> styles, <code>false</code>
963      * otherwise.
964      */

965     protected boolean hasTitleBar() {
966         return (getExpansionStyle() & TITLE_BAR) != 0
967                 || (getExpansionStyle() & SHORT_TITLE_BAR) != 0;
968     }
969
970     /**
971      * Sets the color of the title bar foreground when TITLE_BAR style is used.
972      *
973      * @param color
974      * the title bar foreground
975      */

976     public void setTitleBarForeground(Color color) {
977         titleBarForeground = color;
978         textLabel.setForeground(color);
979     }
980
981     /**
982      * Returns the title bar foreground when TITLE_BAR style is used.
983      *
984      * @return the title bar foreground
985      */

986     public Color getTitleBarForeground() {
987         return titleBarForeground;
988     }
989
990     // end of APIs
991

992     private void toggleState() {
993         boolean newState = !isExpanded();
994         fireExpanding(newState, true);
995         internalSetExpanded(newState);
996         fireExpanding(newState, false);
997         if (newState)
998             FormUtil.ensureVisible(this);
999     }
1000
1001    private void fireExpanding(boolean state, boolean before) {
1002        int size = listeners.size();
1003        if (size == 0)
1004            return;
1005        ExpansionEvent e = new ExpansionEvent(this, state);
1006        Object JavaDoc [] listenerList = listeners.getListeners();
1007        for (int i = 0; i < size; i++) {
1008            IExpansionListener listener = (IExpansionListener) listenerList[i];
1009            if (before)
1010                listener.expansionStateChanging(e);
1011            else
1012                listener.expansionStateChanged(e);
1013        }
1014    }
1015
1016    private void verticalMove(boolean down) {
1017        Composite parent = getParent();
1018        Control[] children = parent.getChildren();
1019        for (int i = 0; i < children.length; i++) {
1020            Control child = children[i];
1021            if (child == this) {
1022                ExpandableComposite sibling = getSibling(children, i, down);
1023                if (sibling != null && sibling.toggle != null) {
1024                    sibling.setFocus();
1025                }
1026                break;
1027            }
1028        }
1029    }
1030
1031    private ExpandableComposite getSibling(Control[] children, int index,
1032            boolean down) {
1033        int loc = down ? index + 1 : index - 1;
1034        while (loc >= 0 && loc < children.length) {
1035            Control c = children[loc];
1036            if (c instanceof ExpandableComposite && c.isVisible())
1037                return (ExpandableComposite) c;
1038            loc = down ? loc + 1 : loc - 1;
1039        }
1040        return null;
1041    }
1042
1043    private void programmaticToggleState() {
1044        if (toggle != null)
1045            toggle.setExpanded(!toggle.isExpanded());
1046        toggleState();
1047    }
1048    
1049    private void paintTitleFocus(GC gc) {
1050        Point size = textLabel.getSize();
1051        gc.setBackground(textLabel.getBackground());
1052        gc.setForeground(textLabel.getForeground());
1053        if (toggle.isFocusControl())
1054            gc.drawFocus(0, 0, size.x, size.y);
1055    }
1056}
Popular Tags