root/dwt/widgets/ExpandBar.d

Revision 246:fd9c62a2998e, 26.9 kB (checked in by Frank Benoit <benoit@tionex.de>, 5 months ago)

Updater SWT 3.4M7 to 3.4

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.widgets.ExpandBar;
14
15
16 import dwt.DWT;
17 import dwt.DWTException;
18 import dwt.events.ExpandAdapter;
19 import dwt.events.ExpandEvent;
20 import dwt.events.ExpandListener;
21 import dwt.graphics.Font;
22 import dwt.graphics.GC;
23 import dwt.graphics.GCData;
24 import dwt.graphics.Point;
25 import dwt.graphics.Rectangle;
26 import dwt.graphics.Drawable;
27 import dwt.internal.win32.OS;
28
29 import dwt.widgets.Composite;
30 import dwt.widgets.Control;
31 import dwt.widgets.ExpandItem;
32 import dwt.widgets.ScrollBar;
33 import dwt.widgets.ExpandItem;
34 import dwt.widgets.TypedListener;
35 import dwt.widgets.Event;
36
37 import dwt.dwthelper.utils;
38 import tango.util.log.Trace;
39 void trc( long line ){
40     //Trace.formatln( "ExpandBar {}", line );
41 }
42
43 /**
44  * Instances of this class support the layout of selectable
45  * expand bar items.
46  * <p>
47  * The item children that may be added to instances of this class
48  * must be of type <code>ExpandItem</code>.
49  * </p><p>
50  * <dl>
51  * <dt><b>Styles:</b></dt>
52  * <dd>V_SCROLL</dd>
53  * <dt><b>Events:</b></dt>
54  * <dd>Expand, Collapse</dd>
55  * </dl>
56  * </p><p>
57  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
58  * </p>
59  *
60  * @see ExpandItem
61  * @see ExpandEvent
62  * @see ExpandListener
63  * @see ExpandAdapter
64  * @see <a href="http://www.eclipse.org/swt/snippets/#expandbar">ExpandBar snippets</a>
65  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: ControlExample</a>
66  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
67  *
68  * @since 3.2
69  */
70 public class ExpandBar : Composite {
71
72     alias Composite.computeSize computeSize;
73     alias Composite.windowProc windowProc;
74
75     ExpandItem[] items;
76     int itemCount;
77     ExpandItem focusItem;
78     int spacing = 4;
79     int yCurrentScroll;
80     HFONT hFont;
81
82
83 /**
84  * Constructs a new instance of this class given its parent
85  * and a style value describing its behavior and appearance.
86  * <p>
87  * The style value is either one of the style constants defined in
88  * class <code>DWT</code> which is applicable to instances of this
89  * class, or must be built by <em>bitwise OR</em>'ing together
90  * (that is, using the <code>int</code> "|" operator) two or more
91  * of those <code>DWT</code> style constants. The class description
92  * lists the style constants that are applicable to the class.
93  * Style bits are also inherited from superclasses.
94  * </p>
95  *
96  * @param parent a composite control which will be the parent of the new instance (cannot be null)
97  * @param style the style of control to construct
98  *
99  * @exception IllegalArgumentException <ul>
100  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
101  * </ul>
102  * @exception DWTException <ul>
103  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
104  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
105  * </ul>
106  *
107  * @see Widget#checkSubclass
108  * @see Widget#getStyle
109  */
110 public this (Composite parent, int style) {
111     super (parent, checkStyle (style));
112 }
113
114 /**
115  * Adds the listener to the collection of listeners who will
116  * be notified when an item in the receiver is expanded or collapsed
117  * by sending it one of the messages defined in the <code>ExpandListener</code>
118  * interface.
119  *
120  * @param listener the listener which should be notified
121  *
122  * @exception IllegalArgumentException <ul>
123  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
124  * </ul>
125  * @exception DWTException <ul>
126  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
127  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
128  * </ul>
129  *
130  * @see ExpandListener
131  * @see #removeExpandListener
132  */
133 public void addExpandListener (ExpandListener listener) {
134     checkWidget ();
135     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
136     TypedListener typedListener = new TypedListener (listener);
137     addListener (DWT.Expand, typedListener);
138     addListener (DWT.Collapse, typedListener);
139 }
140
141 override int callWindowProc (HWND hwnd, int msg, int wParam, int lParam) {
142     if (handle is null) return 0;
143     return OS.DefWindowProc (hwnd, msg, wParam, lParam);
144 }
145
146 override protected void checkSubclass () {
147     if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
148 }
149
150 static int checkStyle (int style) {
151     style &= ~DWT.H_SCROLL;
152     return style | DWT.NO_BACKGROUND;
153 }
154
155 override public Point computeSize (int wHint, int hHint, bool changed) {
156     checkWidget ();
157     int height = 0, width = 0;
158     if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) {
159         if (itemCount > 0) {
160             auto hDC = OS.GetDC (handle);
161             HTHEME hTheme;
162             if (isAppThemed ()) {
163                 hTheme = display.hExplorerBarTheme ();
164             }
165             HFONT hCurrentFont, oldFont;
166             if (hTheme is null) {
167                 if (hFont !is null) {
168                     hCurrentFont = hFont;
169                 } else {
170                     static if (!OS.IsWinCE) {
171                         NONCLIENTMETRICS info;
172                         info.cbSize = NONCLIENTMETRICS.sizeof;
173                         if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, &info, 0)) {
174                             LOGFONT* logFont = &info.lfCaptionFont;
175                             hCurrentFont = OS.CreateFontIndirect (logFont);
176                         }
177                     }
178                 }
179                 if (hCurrentFont !is null) {
180                     oldFont = OS.SelectObject (hDC, hCurrentFont);
181                 }
182             }
183             height += spacing;
184             for (int i = 0; i < itemCount; i++) {
185                 ExpandItem item = items [i];
186                 height += item.getHeaderHeight ();
187                 if (item.expanded) height += item.height;
188                 height += spacing;
189                 width = Math.max (width, item.getPreferredWidth (hTheme, hDC));
190             }
191             if (hCurrentFont !is null) {
192                 OS.SelectObject (hDC, oldFont);
193                 if (hCurrentFont !is hFont) OS.DeleteObject (hCurrentFont);
194             }
195             OS.ReleaseDC (handle, hDC);
196         }
197     }
198     if (width is 0) width = DEFAULT_WIDTH;
199     if (height is 0) height = DEFAULT_HEIGHT;
200     if (wHint !is DWT.DEFAULT) width = wHint;
201     if (hHint !is DWT.DEFAULT) height = hHint;
202     Rectangle trim = computeTrim (0, 0, width, height);
203     return new Point (trim.width, trim.height);
204 }
205
206 override void createHandle () {
207     super.createHandle ();
208     state &= ~CANVAS;
209     state |= TRACK_MOUSE;
210 }
211
212 void createItem (ExpandItem item, int style, int index) {
213     if (!(0 <= index && index <= itemCount)) error (DWT.ERROR_INVALID_RANGE);
214     if (itemCount is items.length) {
215         ExpandItem [] newItems = new ExpandItem [itemCount + 4];
216         System.arraycopy (items, 0, newItems, 0, items.length);
217         items = newItems;
218     }
219     System.arraycopy (items, index, items, index + 1, itemCount - index);
220     items [index] = item;
221     itemCount++;
222     if (focusItem is null) focusItem = item;
223
224     RECT rect;
225     OS.GetWindowRect (handle, &rect);
226     item.width = Math.max (0, rect.right - rect.left - spacing * 2);
227     layoutItems (index, true);
228 }
229
230 override void createWidget () {
231     super.createWidget ();
232     items = new ExpandItem [4];
233     if (!isAppThemed ()) {
234         backgroundMode = DWT.INHERIT_DEFAULT;
235     }
236 }
237
238 override int defaultBackground() {
239     if (!isAppThemed ()) {
240         return OS.GetSysColor (OS.COLOR_WINDOW);
241     }
242     return super.defaultBackground();
243 }
244
245 void destroyItem (ExpandItem item) {
246     int index = 0;
247     while (index < itemCount) {
248         if (items [index] is item) break;
249         index++;
250     }
251     if (index is itemCount) return;
252     if (item is focusItem) {
253         int focusIndex = index > 0 ? index - 1 : 1;
254         if (focusIndex < itemCount) {
255             focusItem = items [focusIndex];
256             focusItem.redraw (true);
257         } else {
258             focusItem = null;
259         }
260     }
261     System.arraycopy (items, index + 1, items, index, --itemCount - index);
262     items [itemCount] = null;
263     item.redraw (true);
264     layoutItems (index, true);
265 }
266
267 override void drawThemeBackground (HDC hDC, HWND hwnd, RECT* rect) {
268     RECT rect2;
269     OS.GetClientRect (handle, &rect2);
270     OS.MapWindowPoints (handle, hwnd, cast(POINT*) &rect2, 2);
271     OS.DrawThemeBackground (display.hExplorerBarTheme (), hDC, OS.EBP_NORMALGROUPBACKGROUND, 0, &rect2, null);
272 }
273
274 void drawWidget (GC gc, RECT* clipRect) {
275     HTHEME hTheme;
276     if (isAppThemed ()) {
277         hTheme = display.hExplorerBarTheme ();
278     }
279     if (hTheme !is null) {
280         RECT rect;
281         OS.GetClientRect (handle, &rect);
282         OS.DrawThemeBackground (hTheme, gc.handle, OS.EBP_HEADERBACKGROUND, 0, &rect, clipRect);
283     } else {
284         drawBackground (gc.handle);
285     }
286     bool drawFocus = false;
287     if (handle is OS.GetFocus ()) {
288         int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
289         drawFocus = (uiState & OS.UISF_HIDEFOCUS) is 0;
290     }
291     HFONT hCurrentFont, oldFont;
292     if (hTheme is null) {
293         if (hFont !is null) {
294             hCurrentFont = hFont;
295         } else {
296             if (!OS.IsWinCE) {
297                 NONCLIENTMETRICS info;
298                 info.cbSize = NONCLIENTMETRICS.sizeof;
299                 if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, &info, 0)) {
300                     LOGFONT* logFont = &info.lfCaptionFont;
301                     hCurrentFont = OS.CreateFontIndirect (logFont);
302                 }
303             }
304         }
305         if (hCurrentFont !is null) {
306             oldFont = OS.SelectObject (gc.handle, hCurrentFont);
307         }
308         if (foreground !is -1) {
309             OS.SetTextColor (gc.handle, foreground);
310         }
311     }
312     for (int i = 0; i < itemCount; i++) {
313         ExpandItem item = items[i];
314         item.drawItem (gc, hTheme, clipRect, item is focusItem && drawFocus);
315     }
316     if (hCurrentFont !is null) {
317         OS.SelectObject (gc.handle, oldFont);
318         if (hCurrentFont !is hFont) OS.DeleteObject (hCurrentFont);
319     }
320 }
321
322 override Control findBackgroundControl () {
323     Control control = super.findBackgroundControl ();
324     if (!isAppThemed ()) {
325         if (control is null) control = this;
326     }
327     return control;
328 }
329
330 override Control findThemeControl () {
331     return isAppThemed () ? this : super.findThemeControl ();
332 }
333
334 int getBandHeight () {
335     if (hFont is null) return ExpandItem.CHEVRON_SIZE;
336     auto hDC = OS.GetDC (handle);
337     auto oldHFont = OS.SelectObject (hDC, hFont);
338     TEXTMETRIC lptm;
339     OS.GetTextMetrics (hDC, &lptm);
340     OS.SelectObject (hDC, oldHFont);
341     OS.ReleaseDC (handle, hDC);
342     return Math.max (ExpandItem.CHEVRON_SIZE, lptm.tmHeight + 4);
343 }
344
345 /**
346  * Returns the item at the given, zero-relative index in the
347  * receiver. Throws an exception if the index is out of range.
348  *
349  * @param index the index of the item to return
350  * @return the item at the given index
351  *
352  * @exception IllegalArgumentException <ul>
353  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
354  * </ul>
355  * @exception DWTException <ul>
356  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
357  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
358  * </ul>
359  */
360 public ExpandItem getItem (int index) {
361     checkWidget ();
362     if (!(0 <= index && index < itemCount)) error (DWT.ERROR_INVALID_RANGE);
363     return items [index];
364 }
365
366 /**
367  * Returns the number of items contained in the receiver.
368  *
369  * @return the number of items
370  *
371  * @exception DWTException <ul>
372  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
373  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
374  * </ul>
375  */
376 public int getItemCount () {
377     checkWidget ();
378     return itemCount;
379 }
380
381 /**
382  * Returns an array of <code>ExpandItem</code>s which are the items
383  * in the receiver.
384  * <p>
385  * Note: This is not the actual structure used by the receiver
386  * to maintain its list of items, so modifying the array will
387  * not affect the receiver.
388  * </p>
389  *
390  * @return the items in the receiver
391  *
392  * @exception DWTException <ul>
393  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
394  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
395  * </ul>
396  */
397 public ExpandItem [] getItems () {
398     checkWidget ();
399     ExpandItem [] result = new ExpandItem [itemCount];
400     System.arraycopy (items, 0, result, 0, itemCount);
401     return result;
402 }
403
404 /**
405  * Returns the receiver's spacing.
406  *
407  * @return the spacing
408  *
409  * @exception DWTException <ul>
410  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
411  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
412  * </ul>
413  */
414 public int getSpacing () {
415     checkWidget ();
416     return spacing;
417 }
418
419 /**
420  * Searches the receiver's list starting at the first item
421  * (index 0) until an item is found that is equal to the
422  * argument, and returns the index of that item. If no item
423  * is found, returns -1.
424  *
425  * @param item the search item
426  * @return the index of the item
427  *
428  * @exception IllegalArgumentException <ul>
429  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
430  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
431  * </ul>
432  * @exception DWTException <ul>
433  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
434  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
435  * </ul>
436  */
437 public int indexOf (ExpandItem item) {
438     checkWidget ();
439     if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
440     for (int i = 0; i < itemCount; i++) {
441         if (items [i] is item) return i;
442     }
443     return -1;
444 }
445
446 bool isAppThemed () {
447     if (background !is -1) return false;
448     if (foreground !is -1) return false;
449     if (hFont !is null) return false;
450     return OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ();
451 }
452
453 void layoutItems (int index, bool setScrollbar_) {
454     if (index < itemCount) {
455         int y = spacing - yCurrentScroll;
456         for (int i = 0; i < index; i++) {
457             ExpandItem item = items [i];
458             if (item.expanded) y += item.height;
459             y += item.getHeaderHeight () + spacing;
460         }
461         for (int i = index; i < itemCount; i++) {
462             ExpandItem item = items [i];
463             item.setBounds (spacing, y, 0, 0, true, false);
464             if (item.expanded) y += item.height;
465             y += item.getHeaderHeight () + spacing;
466         }
467     }
468     if (setScrollbar_) setScrollbar ();
469 }
470
471 override void releaseChildren (bool destroy) {
472     if (items !is null) {
473         for (int i=0; i<items.length; i++) {
474             ExpandItem item = items [i];
475             if (item !is null && !item.isDisposed ()) {
476                 item.release (false);
477             }
478         }
479         items = null;
480     }
481     focusItem = null;
482     super.releaseChildren (destroy);
483 }
484
485 /**
486  * Removes the listener from the collection of listeners who will
487  * be notified when items in the receiver are expanded or collapsed.
488  *
489  * @param listener the listener which should no longer be notified
490  *
491  * @exception IllegalArgumentException <ul>
492  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
493  * </ul>
494  * @exception DWTException <ul>
495  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
496  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
497  * </ul>
498  *
499  * @see ExpandListener
500  * @see #addExpandListener
501  */
502 public void removeExpandListener (ExpandListener listener) {
503     checkWidget ();
504     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
505     if (eventTable is null) return;
506     eventTable.unhook (DWT.Expand, listener);
507     eventTable.unhook (DWT.Collapse, listener);
508 }
509
510 override void setBackgroundPixel (int pixel) {
511     super.setBackgroundPixel (pixel);
512     static if (!OS.IsWinCE) {
513         int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
514         OS.RedrawWindow (handle, null, null, flags);
515     }
516 }
517
518 override public void setFont (Font font) {
519     super.setFont (font);
520     hFont = font !is null ? font.handle : null;
521     layoutItems (0, true);
522 }
523
524 override void setForegroundPixel (int pixel) {
525     super.setForegroundPixel (pixel);
526     static if (!OS.IsWinCE) {
527         int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
528         OS.RedrawWindow (handle, null, null, flags);
529     }
530 }
531
532 void setScrollbar () {
533     if (itemCount is 0) return;
534     if ((style & DWT.V_SCROLL) is 0) return;
535     RECT rect;
536     OS.GetClientRect (handle, &rect);
537     int height = rect.bottom - rect.top;
538     ExpandItem item = items [itemCount - 1];
539     int maxHeight = item.y + getBandHeight () + spacing;
540     if (item.expanded) maxHeight += item.height;
541
542     //claim bottom free space
543     if (yCurrentScroll > 0 && height > maxHeight) {
544         yCurrentScroll = Math.max (0, yCurrentScroll + maxHeight - height);
545         layoutItems (0, false);
546     }
547     maxHeight += yCurrentScroll;
548
549     SCROLLINFO info;
550     info.cbSize = SCROLLINFO.sizeof;
551     info.fMask = OS.SIF_RANGE | OS.SIF_PAGE | OS.SIF_POS;
552     info.nMin = 0;
553     info.nMax = maxHeight;
554     info.nPage = height;
555     info.nPos = Math.min (yCurrentScroll, info.nMax);
556     if (info.nPage !is 0) info.nPage++;
557     OS.SetScrollInfo (handle, OS.SB_VERT, &info, true);
558 }
559
560 /**
561  * Sets the receiver's spacing. Spacing specifies the number of pixels allocated around
562  * each item.
563  *
564  * @param spacing the spacing around each item
565  *
566  * @exception DWTException <ul>
567  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
568  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
569  * </ul>
570  */
571 public void setSpacing (int spacing) {
572     checkWidget ();
573     if (spacing < 0) return;
574     if (spacing is this.spacing) return;
575     this.spacing = spacing;
576     RECT rect;
577     OS.GetClientRect (handle, &rect);
578     int width = Math.max (0, (rect.right - rect.left) - spacing * 2);
579     for (int i = 0; i < itemCount; i++) {
580         ExpandItem item = items[i];
581         if (item.width !is width) item.setBounds (0, 0, width, item.height, false, true);
582     }
583     layoutItems (0, true);
584     OS.InvalidateRect (handle, null, true);
585 }
586
587 void showItem (ExpandItem item) {
588     Control control = item.control;
589     if (control !is null && !control.isDisposed ()) {
590         control.setVisible (item.expanded);
591     }
592     item.redraw (true);