Mercurial > projects > dwt-addons
annotate dwtx/ui/forms/widgets/ExpandableComposite.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 56fea7e5f0f9 |
children |
rev | line source |
---|---|
75 | 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 * Kai Nacke - Fix for Bug 202382 | |
11 * Port to the D programming language: | |
12 * Frank Benoit <benoit@tionex.de> | |
13 *******************************************************************************/ | |
14 module dwtx.ui.forms.widgets.ExpandableComposite; | |
15 | |
16 import dwtx.ui.forms.widgets.ToggleHyperlink; | |
17 import dwtx.ui.forms.widgets.ILayoutExtension; | |
18 import dwtx.ui.forms.widgets.SizeCache; | |
19 import dwtx.ui.forms.widgets.Twistie; | |
20 import dwtx.ui.forms.widgets.TreeNode; | |
21 import dwtx.ui.forms.widgets.Hyperlink; | |
22 | |
23 import dwt.DWT; | |
24 import dwt.events.FocusEvent; | |
25 import dwt.events.FocusListener; | |
26 import dwt.events.KeyAdapter; | |
27 import dwt.events.KeyEvent; | |
28 import dwt.events.PaintEvent; | |
29 import dwt.events.PaintListener; | |
30 import dwt.events.TraverseEvent; | |
31 import dwt.events.TraverseListener; | |
32 import dwt.graphics.Color; | |
33 import dwt.graphics.Font; | |
34 import dwt.graphics.FontMetrics; | |
35 import dwt.graphics.GC; | |
36 import dwt.graphics.Point; | |
37 import dwt.graphics.Rectangle; | |
38 import dwt.widgets.Canvas; | |
39 import dwt.widgets.Composite; | |
40 import dwt.widgets.Control; | |
41 import dwt.widgets.Event; | |
42 import dwt.widgets.Label; | |
43 import dwt.widgets.Layout; | |
44 import dwt.widgets.Listener; | |
45 import dwt.widgets.Menu; | |
46 import dwtx.core.runtime.Assert; | |
47 import dwtx.core.runtime.ListenerList; | |
48 import dwtx.ui.forms.events.ExpansionEvent; | |
49 import dwtx.ui.forms.events.HyperlinkAdapter; | |
50 import dwtx.ui.forms.events.HyperlinkEvent; | |
51 import dwtx.ui.forms.events.IExpansionListener; | |
52 import dwtx.ui.internal.forms.widgets.FormUtil; | |
53 import dwtx.ui.internal.forms.widgets.FormsResources; | |
54 | |
55 import dwt.dwthelper.utils; | |
56 | |
57 /** | |
58 * This composite is capable of expanding or collapsing a single client that is | |
59 * its direct child. The composite renders an expansion toggle affordance | |
60 * (according to the chosen style), and a title that also acts as a hyperlink | |
61 * (can be selected and is traversable). The client is layed out below the title | |
62 * when expanded, or hidden when collapsed. | |
63 * <p> | |
64 * The widget can be instantiated as-is, or subclassed to modify some aspects of | |
65 * it. * | |
66 * <p> | |
67 * Since 3.1, left/right arrow keys can be used to control the expansion state. | |
68 * If several expandable composites are created in the same parent, up/down | |
69 * arrow keys can be used to traverse between them. Expandable text accepts | |
70 * mnemonics and mnemonic activation will toggle the expansion state. | |
71 * | |
72 * <p> | |
73 * While expandable composite recognize that different styles can be used to | |
74 * render the title bar, and even defines the constants for these styles (<code>TITLE_BAR</code> | |
75 * and <code>SHORT_TITLE_BAR</code> the actual painting is done in the | |
76 * subclasses. | |
77 * | |
78 * @see Section | |
79 * @since 3.0 | |
80 */ | |
81 public class ExpandableComposite : Canvas { | |
82 /** | |
83 * If this style is used, a twistie will be used to render the expansion | |
84 * toggle. | |
85 */ | |
86 public static const int TWISTIE = 1 << 1; | |
87 | |
88 /** | |
89 * If this style is used, a tree node with either + or - signs will be used | |
90 * to render the expansion toggle. | |
91 */ | |
92 public static const int TREE_NODE = 1 << 2; | |
93 | |
94 /** | |
95 * If this style is used, the title text will be rendered as a hyperlink | |
96 * that can individually accept focus. Otherwise, it will still act like a | |
97 * hyperlink, but only the toggle control will accept focus. | |
98 */ | |
99 public static const int FOCUS_TITLE = 1 << 3; | |
100 | |
101 /** | |
102 * If this style is used, the client origin will be vertically aligned with | |
103 * the title text. Otherwise, it will start at x = 0. | |
104 */ | |
105 public static const int CLIENT_INDENT = 1 << 4; | |
106 | |
107 /** | |
108 * If this style is used, computed size of the composite will take the | |
109 * client width into consideration only in the expanded state. Otherwise, | |
110 * client width will always be taken into acount. | |
111 */ | |
112 public static const int COMPACT = 1 << 5; | |
113 | |
114 /** | |
115 * If this style is used, the control will be created in the expanded state. | |
116 * This state can later be changed programmatically or by the user if | |
117 * TWISTIE or TREE_NODE style is used. | |
118 */ | |
119 public static const int EXPANDED = 1 << 6; | |
120 | |
121 /** | |
122 * If this style is used, title bar decoration will be painted behind the | |
123 * text. | |
124 */ | |
125 public static const int TITLE_BAR = 1 << 8; | |
126 | |
127 /** | |
128 * If this style is used, a short version of the title bar decoration will | |
129 * be painted behind the text. This style is useful when a more descrete | |
130 * option is needed for the title bar. | |
131 * | |
132 * @since 3.1 | |
133 */ | |
134 public static const int SHORT_TITLE_BAR = 1 << 9; | |
135 | |
136 /** | |
137 * If this style is used, title will not be rendered. | |
138 */ | |
139 public static const int NO_TITLE = 1 << 12; | |
140 | |
141 /** | |
142 * By default, text client is right-aligned. If this style is used, it will | |
143 * be positioned after the text control and vertically centered with it. | |
144 */ | |
145 public static const int LEFT_TEXT_CLIENT_ALIGNMENT = 1 << 13; | |
146 | |
147 /** | |
148 * Width of the margin that will be added around the control (default is 0). | |
149 */ | |
150 public int marginWidth = 0; | |
151 | |
152 /** | |
153 * Height of the margin that will be added around the control (default is | |
154 * 0). | |
155 */ | |
156 public int marginHeight = 0; | |
157 | |
158 /** | |
159 * Vertical spacing between the title area and the composite client control | |
160 * (default is 3). | |
161 */ | |
162 public int clientVerticalSpacing = 3; | |
163 | |
164 /** | |
165 * Vertical spacing between the title area and the description control | |
166 * (default is 0). The description control is normally placed at the new | |
167 * line as defined in the font used to render it. This value will be added | |
168 * to it. | |
169 * | |
170 * @since 3.3 | |
171 */ | |
172 public int descriptionVerticalSpacing = 0; | |
173 | |
174 /** | |
175 * Horizontal margin around the inside of the title bar area when TITLE_BAR | |
176 * or SHORT_TITLE_BAR style is used. This variable is not used otherwise. | |
177 * | |
178 * @since 3.3 | |
179 */ | |
180 public int titleBarTextMarginWidth = 6; | |
181 | |
182 /** | |
183 * The toggle widget used to expand the composite. | |
184 */ | |
185 protected ToggleHyperlink toggle; | |
186 package ToggleHyperlink toggle_package(){ | |
187 return toggle; | |
188 } | |
189 package ToggleHyperlink toggle_package(ToggleHyperlink t){ | |
190 return (toggle = t); | |
191 } | |
192 | |
193 /** | |
194 * The text label for the title. | |
195 */ | |
196 protected Control textLabel; | |
197 package Control textLabel_package(){ | |
198 return textLabel; | |
199 } | |
200 package Control textLabel_package(Control t){ | |
201 return (textLabel = t); | |
202 } | |
203 | |
204 /** | |
205 * @deprecated this variable was left as protected by mistake. It will be | |
206 * turned into static and hidden in the future versions. Do not | |
207 * use them and do not change its value. | |
208 */ | |
209 protected int VGAP = 3; | |
210 /** | |
211 * @deprecated this variable was left as protected by mistake. It will be | |
212 * turned into static and hidden in the future versions. Do not | |
213 * use it and do not change its value. | |
214 */ | |
215 protected int GAP = 4; | |
216 | |
217 static const int IGAP = 4; | |
218 static const int IVGAP = 3; | |
219 | |
220 private static Point NULL_SIZE_; | |
221 private static Point NULL_SIZE(){ | |
222 if( NULL_SIZE_ is null ){ | |
223 synchronized(ExpandableComposite.classinfo ){ | |
224 if( NULL_SIZE_ is null ){ | |
225 NULL_SIZE_ = new Point(0, 0); | |
226 } | |
227 } | |
228 } | |
229 return NULL_SIZE_; | |
230 } | |
231 | |
232 private static const int VSPACE = 3; | |
233 | |
234 private static const int SEPARATOR_HEIGHT = 2; | |
235 | |
236 private int expansionStyle = TWISTIE | FOCUS_TITLE | EXPANDED; | |
237 | |
238 private bool expanded; | |
239 | |
240 private Control textClient; | |
241 | |
242 private Control client; | |
243 | |
244 private ListenerList listeners; | |
245 | |
246 private Color titleBarForeground; | |
247 | |
248 private class ExpandableLayout : Layout, ILayoutExtension { | |
249 | |
250 private SizeCache toggleCache; | |
251 | |
252 private SizeCache textClientCache; | |
253 | |
254 private SizeCache textLabelCache; | |
255 | |
256 private SizeCache descriptionCache; | |
257 | |
258 private SizeCache clientCache; | |
259 | |
260 this(){ | |
261 toggleCache = new SizeCache(); | |
262 textClientCache = new SizeCache(); | |
263 textLabelCache = new SizeCache(); | |
264 descriptionCache = new SizeCache(); | |
265 clientCache = new SizeCache(); | |
266 } | |
267 private void initCache(bool shouldFlush) { | |
268 toggleCache.setControl(toggle); | |
269 textClientCache.setControl(textClient); | |
270 textLabelCache.setControl(textLabel); | |
271 descriptionCache.setControl(getDescriptionControl()); | |
272 clientCache.setControl(client); | |
273 | |
274 if (shouldFlush) { | |
275 toggleCache.flush(); | |
276 textClientCache.flush(); | |
277 textLabelCache.flush(); | |
278 descriptionCache.flush(); | |
279 clientCache.flush(); | |
280 } | |
281 } | |
282 | |
283 protected void layout(Composite parent, bool changed) { | |
284 initCache(changed); | |
285 | |
286 Rectangle clientArea = parent.getClientArea(); | |
287 int thmargin = 0; | |
288 int tvmargin = 0; | |
289 | |
290 if (hasTitleBar()) { | |
291 thmargin = titleBarTextMarginWidth; | |
292 tvmargin = IVGAP; | |
293 } | |
294 int x = marginWidth + thmargin; | |
295 int y = marginHeight + tvmargin; | |
296 Point tsize = NULL_SIZE; | |
297 Point tcsize = NULL_SIZE; | |
298 if (toggle !is null) | |
299 tsize = toggleCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); | |
300 int twidth = clientArea.width - marginWidth - marginWidth | |
301 - thmargin - thmargin; | |
302 if (tsize.x > 0) | |
303 twidth -= tsize.x + IGAP; | |
304 if (textClient !is null) { | |
305 tcsize = textClientCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); | |
306 } | |
307 Point size = NULL_SIZE; | |
308 if (textLabel !is null) { | |
309 if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) { | |
310 size = textLabelCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); | |
311 if (twidth < size.x + IGAP + tcsize.x) { | |
312 twidth -= IGAP; | |
313 if (null !is cast(Label)textLabel ) | |
314 size = FormUtil.computeWrapSize(new GC(textLabel), (cast(Label)textLabel).getText(), cast(int) Math.round(twidth*(size.x/cast(float)(size.x+tcsize.x)))); | |
315 else | |
316 size = textLabelCache.computeSize(cast(int) Math.round(twidth*(size.x/cast(float)(size.x+tcsize.x))), DWT.DEFAULT); | |
317 tcsize = textClientCache.computeSize(twidth-size.x, DWT.DEFAULT); | |
318 } | |
319 } | |
320 else { | |
321 if (tcsize.x > 0) | |
322 twidth -= tcsize.x + IGAP; | |
323 size = textLabelCache.computeSize(twidth, DWT.DEFAULT); | |
324 } | |
325 } | |
326 if (null !is cast(Label)textLabel ) { | |
327 Point defSize = textLabelCache.computeSize(DWT.DEFAULT, | |
328 DWT.DEFAULT); | |
329 if (defSize.y is size.y) { | |
330 // One line - pick the smaller of the two widths | |
331 size.x = Math.min(defSize.x, size.x); | |
332 } | |
333 } | |
334 if (toggle !is null) { | |
335 GC gc = new GC(this.outer); | |
336 gc.setFont(getFont()); | |
337 FontMetrics fm = gc.getFontMetrics(); | |
338 int textHeight = fm.getHeight(); | |
339 gc.dispose(); | |
340 if (textClient !is null | |
341 && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) { | |
342 textHeight = Math.max(textHeight, tcsize.y); | |
343 } | |
344 int ty = textHeight / 2 - tsize.y / 2 + 1; | |
345 ty = Math.max(ty, 0); | |
346 ty += marginHeight + tvmargin; | |
347 toggle.setLocation(x, ty); | |
348 toggle.setSize(tsize); | |
349 x += tsize.x + IGAP; | |
350 } | |
351 if (textLabel !is null) { | |
352 int ty = y; | |
353 if (textClient !is null | |
354 && (expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) { | |
355 if (size.y < tcsize.y) | |
356 ty = tcsize.y / 2 - size.y / 2 + marginHeight | |
357 + tvmargin; | |
358 } | |
359 textLabelCache.setBounds(x, ty, size.x, size.y); | |
360 } | |
361 if (textClient !is null) { | |
362 int tcx; | |
363 if ((expansionStyle & LEFT_TEXT_CLIENT_ALIGNMENT) !is 0) { | |
364 tcx = x + size.x + GAP; | |
365 } else { | |
366 tcx = clientArea.width - tcsize.x - marginWidth - thmargin; | |
367 } | |
368 textClientCache.setBounds(tcx, y, tcsize.x, tcsize.y); | |
369 } | |
370 int tbarHeight = 0; | |
371 if (size.y > 0) | |
372 tbarHeight = size.y; | |
373 if (tcsize.y > 0) | |
374 tbarHeight = Math.max(tbarHeight, tcsize.y); | |
375 y += tbarHeight; | |
376 if (hasTitleBar()) | |
377 y += tvmargin; | |
378 if (getSeparatorControl() !is null) { | |
379 y += VSPACE; | |
380 getSeparatorControl().setBounds(marginWidth, y, | |
381 clientArea.width - marginWidth - marginWidth, | |
382 SEPARATOR_HEIGHT); | |
383 y += SEPARATOR_HEIGHT; | |
384 if (expanded) | |
385 y += VSPACE; | |
386 } | |
387 if (expanded) { | |
388 int areaWidth = clientArea.width - marginWidth - marginWidth | |
389 - thmargin - thmargin; | |
390 int cx = marginWidth + thmargin; | |
391 if ((expansionStyle & CLIENT_INDENT) !is 0) { | |
392 cx = x; | |
393 areaWidth -= x; | |
394 } | |
395 if (client !is null) { | |
396 Point dsize = null; | |
397 Control desc = getDescriptionControl(); | |
398 if (desc !is null) { | |
399 dsize = descriptionCache.computeSize(areaWidth, | |
400 DWT.DEFAULT); | |
401 y += descriptionVerticalSpacing; | |
402 descriptionCache.setBounds(cx, y, areaWidth, dsize.y); | |
403 y += dsize.y + clientVerticalSpacing; | |
404 } else { | |
405 y += clientVerticalSpacing; | |
406 if (getSeparatorControl() !is null) | |
407 y -= VSPACE; | |
408 } | |
409 int cwidth = areaWidth; | |
410 int cheight = clientArea.height - marginHeight | |
411 - marginHeight - y; | |
412 clientCache.setBounds(cx, y, cwidth, cheight); | |
413 } | |
414 } | |
415 } | |
416 | |
417 protected Point computeSize(Composite parent, int wHint, int hHint, | |
418 bool changed) { | |
419 initCache(changed); | |
420 | |
421 int width = 0, height = 0; | |
422 Point tsize = NULL_SIZE; | |
423 int twidth = 0; | |
424 if (toggle !is null) { | |
425 tsize = toggleCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); | |
426 twidth = tsize.x + IGAP; | |
427 } | |
428 int thmargin = 0; | |
429 int tvmargin = 0; | |
430 | |
431 if (hasTitleBar()) { | |
432 thmargin = titleBarTextMarginWidth; | |
433 tvmargin = IVGAP; | |
434 } | |
435 int innerwHint = wHint; | |
436 if (innerwHint !is DWT.DEFAULT) | |
437 innerwHint -= twidth + marginWidth + marginWidth + thmargin | |
438 + thmargin; | |
439 | |
440 int innertHint = innerwHint; | |
441 | |
442 Point tcsize = NULL_SIZE; | |
443 if (textClient !is null) { | |
444 tcsize = textClientCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); | |
445 } | |
446 Point size = NULL_SIZE; | |
447 | |
448 if (textLabel !is null) { | |
449 if (tcsize.x > 0 && FormUtil.isWrapControl(textClient)) { | |
450 size = textLabelCache.computeSize(DWT.DEFAULT, DWT.DEFAULT); | |
451 if (innertHint !is DWT.DEFAULT && innertHint < size.x + IGAP + tcsize.x) { | |
452 innertHint -= IGAP; | |
453 if (null !is cast(Label)textLabel ) | |
454 size = FormUtil.computeWrapSize(new GC(textLabel), (cast(Label)textLabel).getText(), cast(int) Math.round(innertHint*(size.x/cast(float)(size.x+tcsize.x)))); | |
455 else | |
456 size = textLabelCache.computeSize(cast(int) Math.round(innertHint*(size.x/cast(float)(size.x+tcsize.x))), DWT.DEFAULT); | |
457 tcsize = textClientCache.computeSize(innertHint-size.x, DWT.DEFAULT); | |
458 } | |
459 } else { | |
460 if (innertHint !is DWT.DEFAULT && tcsize.x > 0) | |
461 innertHint -= IGAP + tcsize.x; | |
462 size = textLabelCache.computeSize(innertHint, DWT.DEFAULT); | |
463 } | |
464 } | |
465 if (null !is cast(Label)textLabel ) { | |
466 Point defSize = textLabelCache.computeSize(DWT.DEFAULT, | |
467 DWT.DEFAULT); | |
468 if (defSize.y is size.y) { | |
469 // One line - pick the smaller of the two widths | |
470 size.x = Math.min(defSize.x, size.x); | |
471 } | |
472 } | |
473 if (size.x > 0) | |
474 width = size.x; | |
475 if (tcsize.x > 0) | |
476 width += IGAP + tcsize.x; | |
477 if (toggle !is null) | |
478 width += twidth; | |
479 height = tcsize.y > 0 ? Math.max(tcsize.y, size.y) : size.y; | |
480 if (getSeparatorControl() !is null) { | |
481 height += VSPACE + SEPARATOR_HEIGHT; | |
482 if (expanded && client !is null) | |
483 height += VSPACE; | |
484 } | |
485 // if (hasTitleBar()) | |
486 // height += VSPACE; | |
487 if ((expanded || (expansionStyle & COMPACT) is 0) && client !is null) { | |
488 int cwHint = wHint; | |
489 int clientIndent = 0; | |
490 if ((expansionStyle & CLIENT_INDENT) !is 0) | |
491 clientIndent = twidth; | |
492 | |
493 if (cwHint !is DWT.DEFAULT) { | |
494 cwHint -= marginWidth + marginWidth + thmargin + thmargin; | |
495 if ((expansionStyle & CLIENT_INDENT) !is 0) | |
496 if (tcsize.x > 0) | |
497 cwHint -= twidth; | |
498 } | |
499 Point dsize = null; | |
500 Point csize = clientCache.computeSize(FormUtil.getWidthHint( | |
501 cwHint, client), DWT.DEFAULT); | |
502 if (getDescriptionControl() !is null) { | |
503 int dwHint = cwHint; | |
504 if (dwHint is DWT.DEFAULT) { | |
505 dwHint = csize.x; | |
506 if ((expansionStyle & CLIENT_INDENT) !is 0) | |
507 dwHint -= twidth; | |
508 } | |
509 dsize = descriptionCache.computeSize(dwHint, DWT.DEFAULT); | |
510 } | |
511 if (dsize !is null) { | |
512 width = Math.max(width, dsize.x + clientIndent); | |
513 if (expanded) | |
514 height += descriptionVerticalSpacing + dsize.y | |
515 + clientVerticalSpacing; | |
516 } else { | |
517 height += clientVerticalSpacing; | |
518 if (getSeparatorControl() !is null) | |
519 height -= VSPACE; | |
520 } | |
521 width = Math.max(width, csize.x + clientIndent); | |
522 if (expanded) | |
523 height += csize.y; | |
524 } | |
525 if (toggle !is null) | |
526 height = height - size.y + Math.max(size.y, tsize.y); | |
527 | |
528 Point result = new Point(width + marginWidth + marginWidth | |
529 + thmargin + thmargin, height + marginHeight + marginHeight | |
530 + tvmargin + tvmargin); | |
531 return result; | |
532 } | |
533 | |
534 public int computeMinimumWidth(Composite parent, bool changed) { | |
535 return computeSize(parent, 0, DWT.DEFAULT, changed).x; | |
536 } | |
537 | |
538 /* | |
539 * (non-Javadoc) | |
540 * | |
541 * @see dwtx.ui.forms.parts.ILayoutExtension#computeMinimumWidth(dwt.widgets.Composite, | |
542 * bool) | |
543 */ | |
544 public int computeMaximumWidth(Composite parent, bool changed) { | |
545 return computeSize(parent, DWT.DEFAULT, DWT.DEFAULT, changed).x; | |
546 } | |
547 } | |
548 | |
549 /** | |
550 * Creates an expandable composite using a TWISTIE toggle. | |
551 * | |
552 * @param parent | |
553 * the parent composite | |
554 * @param style | |
555 * DWT style bits | |
556 */ | |
557 public this(Composite parent, int style) { | |
558 this(parent, style, TWISTIE); | |
559 } | |
560 | |
561 /** | |
562 * Creates the expandable composite in the provided parent. | |
563 * | |
564 * @param parent | |
565 * the parent | |
566 * @param style | |
567 * the control style (as expected by DWT subclass) | |
568 * @param expansionStyle | |
569 * the style of the expansion widget (TREE_NODE, TWISTIE, | |
570 * CLIENT_INDENT, COMPACT, FOCUS_TITLE, | |
571 * LEFT_TEXT_CLIENT_ALIGNMENT, NO_TITLE) | |
572 */ | |
573 public this(Composite parent, int style, int expansionStyle) { | |
574 listeners = new ListenerList(); | |
575 super(parent, style); | |
576 this.expansionStyle = expansionStyle; | |
577 if ((expansionStyle & TITLE_BAR) !is 0) | |
578 setBackgroundMode(DWT.INHERIT_DEFAULT); | |
579 super.setLayout(new ExpandableLayout()); | |
580 if (hasTitleBar()) { | |
581 this.addPaintListener(new class PaintListener { | |
582 public void paintControl(PaintEvent e) { | |
583 onPaint(e); | |
584 } | |
585 }); | |
586 } | |
587 if ((expansionStyle & TWISTIE) !is 0) | |
588 toggle = new Twistie(this, DWT.NULL); | |
589 else if ((expansionStyle & TREE_NODE) !is 0) | |
590 toggle = new TreeNode(this, DWT.NULL); | |
591 else | |
592 expanded = true; | |
593 if ((expansionStyle & EXPANDED) !is 0) | |
594 expanded = true; | |
595 if (toggle !is null) { | |
596 toggle.setExpanded(expanded); | |
597 toggle.addHyperlinkListener(new class HyperlinkAdapter { | |
598 public void linkActivated(HyperlinkEvent e) { | |
599 toggleState(); | |
600 } | |
601 }); | |
602 toggle.addPaintListener(new class PaintListener { | |
603 public void paintControl(PaintEvent e) { | |
604 if (null !is cast(Label)textLabel && !isFixedStyle()) | |
605 textLabel.setForeground(toggle.hover_package ? toggle | |
606 .getHoverDecorationColor() | |
607 : getTitleBarForeground()); | |
608 } | |
609 }); | |
610 toggle.addKeyListener(new class KeyAdapter { | |
611 public void keyPressed(KeyEvent e) { | |
612 if (e.keyCode is DWT.ARROW_UP) { | |
613 verticalMove(false); | |
614 e.doit = false; | |
615 } else if (e.keyCode is DWT.ARROW_DOWN) { | |
616 verticalMove(true); | |
617 e.doit = false; | |
618 } | |
619 } | |
620 }); | |
621 if ((getExpansionStyle()&FOCUS_TITLE) is 0) { | |
622 toggle.paintFocus=false; | |
623 toggle.addFocusListener(new class FocusListener { | |
624 public void focusGained(FocusEvent e) { | |
625 textLabel.redraw(); | |
626 } | |
627 | |
628 public void focusLost(FocusEvent e) { | |
629 textLabel.redraw(); | |
630 } | |
631 }); | |
632 } | |
633 } | |
634 if ((expansionStyle & FOCUS_TITLE) !is 0) { | |
635 Hyperlink link = new Hyperlink(this, DWT.WRAP); | |
636 link.addHyperlinkListener(new class HyperlinkAdapter { | |
637 public void linkActivated(HyperlinkEvent e) { | |
638 programmaticToggleState(); | |
639 } | |
640 }); | |
641 textLabel = link; | |
642 } else if ((expansionStyle & NO_TITLE) is 0) { | |
643 final Label label = new Label(this, DWT.WRAP); | |
644 if (!isFixedStyle()) { | |
645 label.setCursor(FormsResources.getHandCursor()); | |
85 | 646 // DWT FIXME: workaround for DMD anonymous class, nested function problem |
647 Listener listener = dgListener( &tst, label ); | |
75 | 648 label.addListener(DWT.MouseDown, listener); |
649 label.addListener(DWT.MouseUp, listener); | |
650 label.addListener(DWT.MouseEnter, listener); | |
651 label.addListener(DWT.MouseExit, listener); | |
652 label.addListener(DWT.Paint, listener); | |
653 } | |
654 textLabel = label; | |
655 } | |
656 if (textLabel !is null) { | |
657 textLabel.setMenu(getMenu()); | |
658 textLabel.addTraverseListener(new class TraverseListener { | |
659 public void keyTraversed(TraverseEvent e) { | |
660 if (e.detail is DWT.TRAVERSE_MNEMONIC) { | |
661 // steal the mnemonic | |
662 if (!isVisible() || !isEnabled()) | |
663 return; | |
664 if (FormUtil.mnemonicMatch(getText(), e.character)) { | |
665 e.doit = false; | |
666 if (!isFixedStyle()) { | |
667 programmaticToggleState(); | |
668 } | |
669 setFocus(); | |
670 } | |
671 } | |
672 } | |
673 }); | |
674 } | |
675 } | |
676 | |
85 | 677 // DWT FIXME: workaround for DMD anonymous class, nested function problem |
678 void tst(Event e, Label label_) { | |
679 switch (e.type) { | |
680 case DWT.MouseDown: | |
681 if (toggle !is null) | |
682 toggle.setFocus(); | |
683 break; | |
684 case DWT.MouseUp: | |
685 label_.setCursor(FormsResources.getBusyCursor()); | |
686 programmaticToggleState(); | |
687 label_.setCursor(FormsResources.getHandCursor()); | |
688 break; | |
689 case DWT.MouseEnter: | |
690 if (toggle !is null) { | |
691 label_.setForeground(toggle | |
692 .getHoverDecorationColor()); | |
693 toggle.hover_package = true; | |
694 toggle.redraw(); | |
695 } | |
696 break; | |
697 case DWT.MouseExit: | |
698 if (toggle !is null) { | |
699 label_.setForeground(getTitleBarForeground()); | |
700 toggle.hover_package = false; | |
701 toggle.redraw(); | |
702 } | |
703 break; | |
704 case DWT.Paint: | |
705 if (toggle !is null) { | |
706 paintTitleFocus(e.gc); | |
707 } | |
708 break; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
709 default: |
85 | 710 } |
711 } | |
75 | 712 /* (non-Javadoc) |
713 * @see dwt.widgets.Control#forceFocus() | |
714 */ | |
715 public bool forceFocus() { | |
716 return false; | |
717 } | |
718 | |
719 /** | |
720 * Overrides 'super' to pass the menu to the text label. | |
721 * | |
722 * @param menu | |
723 * the menu from the parent to attach to this control. | |
724 */ | |
725 | |
726 public void setMenu(Menu menu) { | |
727 if (textLabel !is null) | |
728 textLabel.setMenu(menu); | |
729 super.setMenu(menu); | |
730 } | |
731 | |
732 /** | |
733 * Prevents assignment of the layout manager - expandable composite uses its | |
734 * own layout. | |
735 */ | |
736 public final void setLayout(Layout layout) { | |
737 } | |
738 | |
739 /** | |
740 * Sets the background of all the custom controls in the expandable. | |
741 */ | |
742 public void setBackground(Color bg) { | |
743 super.setBackground(bg); | |
744 if ((getExpansionStyle() & TITLE_BAR) is 0) { | |
745 if (textLabel !is null) | |
746 textLabel.setBackground(bg); | |
747 if (toggle !is null) | |
748 toggle.setBackground(bg); | |
749 } | |
750 } | |
751 | |
752 /** | |
753 * Sets the foreground of all the custom controls in the expandable. | |
754 */ | |
755 public void setForeground(Color fg) { | |
756 super.setForeground(fg); | |
757 if (textLabel !is null) | |
758 textLabel.setForeground(fg); | |
759 if (toggle !is null) | |
760 toggle.setForeground(fg); | |
761 } | |
762 | |
763 /** | |
764 * Sets the color of the toggle control. | |
765 * | |
766 * @param c | |
767 * the color object | |
768 */ | |
769 public void setToggleColor(Color c) { | |
770 if (toggle !is null) | |
771 toggle.setDecorationColor(c); | |
772 } | |
773 | |
774 /** | |
775 * Sets the active color of the toggle control (when the mouse enters the | |
776 * toggle area). | |
777 * | |
778 * @param c | |
779 * the active color object | |
780 */ | |
781 public void setActiveToggleColor(Color c) { | |
782 if (toggle !is null) | |
783 toggle.setHoverDecorationColor(c); | |
784 } | |
785 | |
786 /** | |
787 * Sets the fonts of all the custom controls in the expandable. | |
788 */ | |
789 public void setFont(Font font) { | |
790 super.setFont(font); | |
791 if (textLabel !is null) | |
792 textLabel.setFont(font); | |
793 if (toggle !is null) | |
794 toggle.setFont(font); | |
795 } | |
796 | |
797 /* | |
798 * (non-Javadoc) | |
799 * | |
800 * @see dwt.widgets.Control#setEnabled(bool) | |
801 */ | |
802 | |
803 public void setEnabled(bool enabled) { | |
804 if (textLabel !is null) | |
805 textLabel.setEnabled(enabled); | |
806 if (toggle !is null) | |
807 toggle.setEnabled(enabled); | |
808 super.setEnabled(enabled); | |
809 } | |
810 | |
811 /** | |
812 * Sets the client of this expandable composite. The client must not be | |
813 * <samp>null </samp> and must be a direct child of this container. | |
814 * | |
815 * @param client | |
816 * the client that will be expanded or collapsed | |
817 */ | |
818 public void setClient(Control client) { | |
819 Assert.isTrue(client !is null && client.getParent().opEquals(this)); | |
820 this.client = client; | |
821 } | |
822 | |
823 /** | |
824 * Returns the current expandable client. | |
825 * | |
826 * @return the client control | |
827 */ | |
828 public Control getClient() { | |
829 return client; | |
830 } | |
831 | |
832 /** | |
833 * Sets the title of the expandable composite. The title will act as a | |
834 * hyperlink and activating it will toggle the client between expanded and | |
835 * collapsed state. | |
836 * | |
837 * @param title | |
838 * the new title string | |
839 * @see #getText() | |
840 */ | |
841 public void setText(String title) { | |
842 if (null !is cast(Label)textLabel ) | |
843 (cast(Label) textLabel).setText(title); | |
844 else if (null !is cast(Hyperlink)textLabel ) | |
845 (cast(Hyperlink) textLabel).setText(title); | |
846 } | |
847 | |
848 /** | |
849 * Returns the title string. | |
850 * | |
851 * @return the title string | |
852 * @see #setText(String) | |
853 */ | |
854 public String getText() { | |
855 if (null !is cast(Label)textLabel ) | |
856 return (cast(Label) textLabel).getText(); | |
857 else if (null !is cast(Hyperlink)textLabel ) | |
858 return (cast(Hyperlink) textLabel).getText(); | |
859 else | |
860 return ""; //$NON-NLS-1$ | |
861 } | |
862 | |
863 /** | |
864 * Tests the expanded state of the composite. | |
865 * | |
866 * @return <samp>true </samp> if expanded, <samp>false </samp> if collapsed. | |
867 */ | |
868 public bool isExpanded() { | |
869 return expanded; | |
870 } | |
871 | |
872 /** | |
873 * Returns the bitwise-ORed style bits for the expansion control. | |
874 * | |
875 * @return the bitwise-ORed style bits for the expansion control | |
876 */ | |
877 public int getExpansionStyle() { | |
878 return expansionStyle; | |
879 } | |
880 | |
881 /** | |
882 * Programmatically changes expanded state. | |
883 * | |
884 * @param expanded | |
885 * the new expanded state | |
886 */ | |
887 public void setExpanded(bool expanded) { | |
888 internalSetExpanded(expanded); | |
889 if (toggle !is null) | |
890 toggle.setExpanded(expanded); | |
891 } | |
892 | |
893 /** | |
894 * Performs the expansion state change for the expandable control. | |
895 * | |
896 * @param expanded | |
897 * the expansion state | |
898 */ | |
899 protected void internalSetExpanded(bool expanded) { | |
900 if (this.expanded !is expanded) { | |
901 this.expanded = expanded; | |
902 if (getDescriptionControl() !is null) | |
903 getDescriptionControl().setVisible(expanded); | |
904 if (client !is null) | |
905 client.setVisible(expanded); | |
906 layout(); | |
907 } | |
908 } | |
909 | |
910 /** | |
911 * Adds the listener that will be notified when the expansion state changes. | |
912 * | |
913 * @param listener | |
914 * the listener to add | |
915 */ | |
916 public void addExpansionListener(IExpansionListener listener) { | |
917 listeners.add(cast(Object)listener); | |
918 } | |
919 | |
920 /** | |
921 * Removes the expansion listener. | |
922 * | |
923 * @param listener | |
924 * the listner to remove | |
925 */ | |
926 public void removeExpansionListener(IExpansionListener listener) { | |
927 listeners.remove(cast(Object)listener); | |
928 } | |
929 | |
930 /** | |
931 * If TITLE_BAR or SHORT_TITLE_BAR style is used, title bar decoration will | |
932 * be painted behind the text in this method. The default implementation | |
933 * does nothing - subclasses are responsible for rendering the title area. | |
934 * | |
935 * @param e | |
936 * the paint event | |
937 */ | |
938 protected void onPaint(PaintEvent e) { | |
939 } | |
940 | |
941 /** | |
942 * Returns description control that will be placed under the title if | |
943 * present. | |
944 * | |
945 * @return the description control or <samp>null </samp> if not used. | |
946 */ | |
947 protected Control getDescriptionControl() { | |
948 return null; | |
949 } | |
950 | |
951 /** | |
952 * Returns the separator control that will be placed between the title and | |
953 * the description if present. | |
954 * | |
955 * @return the separator control or <samp>null </samp> if not used. | |
956 */ | |
957 protected Control getSeparatorControl() { | |
958 return null; | |
959 } | |
960 | |
961 /** | |
962 * Computes the size of the expandable composite. | |
963 * | |
964 * @see dwt.widgets.Composite#computeSize | |
965 */ | |
966 public Point computeSize(int wHint, int hHint, bool changed) { | |
967 checkWidget(); | |
968 Point size; | |
969 ExpandableLayout layout = cast(ExpandableLayout) getLayout(); | |
970 if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) { | |
971 size = layout.computeSize(this, wHint, hHint, changed); | |
972 } else { | |
973 size = new Point(wHint, hHint); | |
974 } | |
975 Rectangle trim = computeTrim(0, 0, size.x, size.y); | |
976 return new Point(trim.width, trim.height); | |
977 } | |
978 | |
979 /** | |
980 * Returns <samp>true </samp> if the composite is fixed i.e. cannot be | |
981 * expanded or collapsed. Fixed control will still contain the title, | |
982 * separator and description (if present) as well as the client, but will be | |
983 * in the permanent expanded state and the toggle affordance will not be | |
984 * shown. | |
985 * | |
986 * @return <samp>true </samp> if the control is fixed in the expanded state, | |
987 * <samp>false </samp> if it can be collapsed. | |
988 */ | |
989 protected bool isFixedStyle() { | |
990 return (expansionStyle & TWISTIE) is 0 | |
991 && (expansionStyle & TREE_NODE) is 0; | |
992 } | |
993 | |
994 /** | |
995 * Returns the text client control. | |
996 * | |
997 * @return Returns the text client control if specified, or | |
998 * <code>null</code> if not. | |
999 */ | |
1000 public Control getTextClient() { | |
1001 return textClient; | |
1002 } | |
1003 | |
1004 /** | |
1005 * Sets the text client control. Text client is a control that is a child of | |
1006 * the expandable composite and is placed to the right of the text. It can | |
1007 * be used to place small image hyperlinks. If more than one control is | |
1008 * needed, use Composite to hold them. Care should be taken that the height | |
1009 * of the control is comparable to the height of the text. | |
1010 * | |
1011 * @param textClient | |
1012 * the textClient to set or <code>null</code> if not needed any | |
1013 * more. | |
1014 */ | |
1015 public void setTextClient(Control textClient) { | |
1016 if (this.textClient !is null) | |
1017 this.textClient.dispose(); | |
1018 this.textClient = textClient; | |
1019 } | |
1020 | |
1021 /** | |
1022 * Returns the difference in height between the text and the text client (if | |
1023 * set). This difference can cause vertical alignment problems when two | |
1024 * expandable composites are placed side by side, one with and one without | |
1025 * the text client. Use this method obtain the value to add to either | |
1026 * <code>descriptionVerticalSpacing</code> (if you have description) or | |
1027 * <code>clientVerticalSpacing</code> to correct the alignment of the | |
1028 * expandable without the text client. | |
1029 * | |
1030 * @return the difference in height between the text and the text client or | |
1031 * 0 if no corrective action is needed. | |
1032 * @since 3.3 | |
1033 */ | |
1034 public int getTextClientHeightDifference() { | |
1035 if (textClient is null || textLabel is null) | |
1036 return 0; | |
1037 int theight = textLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT).y; | |
1038 int tcheight = textClient.computeSize(DWT.DEFAULT, DWT.DEFAULT).y; | |
1039 return Math.max(tcheight - theight, 0); | |
1040 } | |
1041 | |
1042 /** | |
1043 * Tests if this expandable composite renders a title bar around the text. | |
1044 * | |
1045 * @return <code>true</code> for <code>TITLE_BAR</code> or | |
1046 * <code>SHORT_TITLE_BAR</code> styles, <code>false</code> | |
1047 * otherwise. | |
1048 */ | |
1049 protected bool hasTitleBar() { | |
1050 return (getExpansionStyle() & TITLE_BAR) !is 0 | |
1051 || (getExpansionStyle() & SHORT_TITLE_BAR) !is 0; | |
1052 } | |
1053 | |
1054 /** | |
1055 * Sets the color of the title bar foreground when TITLE_BAR style is used. | |
1056 * | |
1057 * @param color | |
1058 * the title bar foreground | |
1059 */ | |
1060 public void setTitleBarForeground(Color color) { | |
1061 titleBarForeground = color; | |
1062 textLabel.setForeground(color); | |
1063 } | |
1064 | |
1065 /** | |
1066 * Returns the title bar foreground when TITLE_BAR style is used. | |
1067 * | |
1068 * @return the title bar foreground | |
1069 */ | |
1070 public Color getTitleBarForeground() { | |
1071 return titleBarForeground; | |
1072 } | |
1073 | |
1074 // end of APIs | |
1075 | |
1076 private void toggleState() { | |
1077 bool newState = !isExpanded(); | |
1078 fireExpanding(newState, true); | |
1079 internalSetExpanded(newState); | |
1080 fireExpanding(newState, false); | |
1081 if (newState) | |
1082 FormUtil.ensureVisible(this); | |
1083 } | |
1084 | |
1085 private void fireExpanding(bool state, bool before) { | |
1086 int size = listeners.size(); | |
1087 if (size is 0) | |
1088 return; | |
1089 ExpansionEvent e = new ExpansionEvent(this, state); | |
1090 Object [] listenerList = listeners.getListeners(); | |
1091 for (int i = 0; i < size; i++) { | |
1092 IExpansionListener listener = cast(IExpansionListener) listenerList[i]; | |
1093 if (before) | |
1094 listener.expansionStateChanging(e); | |
1095 else | |
1096 listener.expansionStateChanged(e); | |
1097 } | |
1098 } | |
1099 | |
1100 private void verticalMove(bool down) { | |
1101 Composite parent = getParent(); | |
1102 Control[] children = parent.getChildren(); | |
1103 for (int i = 0; i < children.length; i++) { | |
1104 Control child = children[i]; | |
1105 if (child is this) { | |
1106 ExpandableComposite sibling = getSibling(children, i, down); | |
1107 if (sibling !is null && sibling.toggle !is null) { | |
1108 sibling.setFocus(); | |
1109 } | |
1110 break; | |
1111 } | |
1112 } | |
1113 } | |
1114 | |
1115 private ExpandableComposite getSibling(Control[] children, int index, | |
1116 bool down) { | |
1117 int loc = down ? index + 1 : index - 1; | |
1118 while (loc >= 0 && loc < children.length) { | |
1119 Control c = children[loc]; | |
1120 if (null !is cast(ExpandableComposite)c && c.isVisible()) | |
1121 return cast(ExpandableComposite) c; | |
1122 loc = down ? loc + 1 : loc - 1; | |
1123 } | |
1124 return null; | |
1125 } | |
1126 | |
1127 private void programmaticToggleState() { | |
1128 if (toggle !is null) | |
1129 toggle.setExpanded(!toggle.isExpanded()); | |
1130 toggleState(); | |
1131 } | |
1132 | |
1133 private void paintTitleFocus(GC gc) { | |
1134 Point size = textLabel.getSize(); | |
1135 gc.setBackground(textLabel.getBackground()); | |
1136 gc.setForeground(textLabel.getForeground()); | |
1137 if (toggle.isFocusControl()) | |
1138 gc.drawFocus(0, 0, size.x, size.y); | |
1139 } | |
1140 } |