root/dwt/widgets/Label.d

Revision 246:fd9c62a2998e, 26.1 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.Label;
14
15
16 import dwt.DWT;
17 import dwt.DWTException;
18 import dwt.graphics.GC;
19 import dwt.graphics.GCData;
20 import dwt.graphics.Image;
21 import dwt.graphics.Point;
22 import dwt.graphics.Rectangle;
23 import dwt.internal.win32.OS;
24
25 import dwt.widgets.Control;
26 import dwt.widgets.Composite;
27 import dwt.widgets.Display;
28 import dwt.widgets.Event;
29
30 import dwt.dwthelper.utils;
31
32 /**
33  * Instances of this class represent a non-selectable
34  * user interface object that displays a string or image.
35  * When SEPARATOR is specified, displays a single
36  * vertical or horizontal line.
37  * <p>
38  * Shadow styles are hints and may not be honoured
39  * by the platform.  To create a separator label
40  * with the default shadow style for the platform,
41  * do not specify a shadow style.
42  * </p>
43  * <dl>
44  * <dt><b>Styles:</b></dt>
45  * <dd>SEPARATOR, HORIZONTAL, VERTICAL</dd>
46  * <dd>SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd>
47  * <dd>CENTER, LEFT, RIGHT, WRAP</dd>
48  * <dt><b>Events:</b></dt>
49  * <dd>(none)</dd>
50  * </dl>
51  * <p>
52  * Note: Only one of SHADOW_IN, SHADOW_OUT and SHADOW_NONE may be specified.
53  * SHADOW_NONE is a HINT. Only one of HORIZONTAL and VERTICAL may be specified.
54  * Only one of CENTER, LEFT and RIGHT may be specified.
55  * </p><p>
56  * IMPORTANT: This class is intended to be subclassed <em>only</em>
57  * within the DWT implementation.
58  * </p>
59  *
60  * @see <a href="http://www.eclipse.org/swt/snippets/#label">Label snippets</a>
61  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: ControlExample</a>
62  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
63  */
64 public class Label : Control {
65
66     alias Control.computeSize computeSize;
67     alias Control.windowProc windowProc;
68
69     String text = "";
70     Image image;
71     static const int MARGIN = 4;
72     static const bool IMAGE_AND_TEXT = false;
73     private static /+const+/ WNDPROC LabelProc;
74     static const TCHAR[] LabelClass = "STATIC\0";
75
76     private static bool static_this_completed = false;
77     private static void static_this() {
78         if( static_this_completed ){
79             return;
80         }
81         synchronized {
82             if( static_this_completed ){
83                 return;
84             }
85             WNDCLASS lpWndClass;
86             OS.GetClassInfo (null, LabelClass.ptr, &lpWndClass);
87             LabelProc = lpWndClass.lpfnWndProc;
88             static_this_completed = true;
89         }
90     }
91
92
93 /**
94  * Constructs a new instance of this class given its parent
95  * and a style value describing its behavior and appearance.
96  * <p>
97  * The style value is either one of the style constants defined in
98  * class <code>DWT</code> which is applicable to instances of this
99  * class, or must be built by <em>bitwise OR</em>'ing together
100  * (that is, using the <code>int</code> "|" operator) two or more
101  * of those <code>DWT</code> style constants. The class description
102  * lists the style constants that are applicable to the class.
103  * Style bits are also inherited from superclasses.
104  * </p>
105  *
106  * @param parent a composite control which will be the parent of the new instance (cannot be null)
107  * @param style the style of control to construct
108  *
109  * @exception IllegalArgumentException <ul>
110  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
111  * </ul>
112  * @exception DWTException <ul>
113  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
114  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
115  * </ul>
116  *
117  * @see DWT#SEPARATOR
118  * @see DWT#HORIZONTAL
119  * @see DWT#VERTICAL
120  * @see DWT#SHADOW_IN
121  * @see DWT#SHADOW_OUT
122  * @see DWT#SHADOW_NONE
123  * @see DWT#CENTER
124  * @see DWT#LEFT
125  * @see DWT#RIGHT
126  * @see DWT#WRAP
127  * @see Widget#checkSubclass
128  * @see Widget#getStyle
129  */
130 public this (Composite parent, int style) {
131     static_this();
132     super (parent, checkStyle (style));
133 }
134
135 override int callWindowProc (HWND hwnd, int msg, int wParam, int lParam) {
136     if (handle is null) return 0;
137     return OS.CallWindowProc (LabelProc, hwnd, msg, wParam, lParam);
138 }
139
140 static int checkStyle (int style) {
141     style |= DWT.NO_FOCUS;
142     if ((style & DWT.SEPARATOR) !is 0) {
143         style = checkBits (style, DWT.VERTICAL, DWT.HORIZONTAL, 0, 0, 0, 0);
144         return checkBits (style, DWT.SHADOW_OUT, DWT.SHADOW_IN, DWT.SHADOW_NONE, 0, 0, 0);
145     }
146     return checkBits (style, DWT.LEFT, DWT.CENTER, DWT.RIGHT, 0, 0, 0);
147 }
148
149 override public Point computeSize (int wHint, int hHint, bool changed) {
150     checkWidget ();
151     int width = 0, height = 0, border = getBorderWidth ();
152     if ((style & DWT.SEPARATOR) !is 0) {
153         int lineWidth = OS.GetSystemMetrics (OS.SM_CXBORDER);
154         if ((style & DWT.HORIZONTAL) !is 0) {
155             width = DEFAULT_WIDTH;  height = lineWidth * 2;
156         } else {
157             width = lineWidth * 2; height = DEFAULT_HEIGHT;
158         }
159         if (wHint !is DWT.DEFAULT) width = wHint;
160         if (hHint !is DWT.DEFAULT) height = hHint;
161         width += border * 2; height += border * 2;
162         return new Point (width, height);
163     }
164     int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
165     bool drawText = true;
166     bool drawImage = (bits & OS.SS_OWNERDRAW) is OS.SS_OWNERDRAW;
167     if (drawImage) {
168         if (image !is null) {
169             Rectangle rect = image.getBounds();
170             width += rect.width;
171             height += rect.height;
172             if (IMAGE_AND_TEXT) {
173                 if (text.length !is 0) width += MARGIN;
174             } else {
175                 drawText = false;
176             }
177         }
178     }
179     if (drawText) {
180         auto hDC = OS.GetDC (handle);
181         auto newFont = cast(HFONT) OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
182         auto oldFont = OS.SelectObject (hDC, newFont);
183         int length = OS.GetWindowTextLength (handle);
184         if (length is 0) {
185             TEXTMETRIC tm;
186             OS.GetTextMetrics (hDC, &tm);
187             height = Math.max (height, tm.tmHeight);
188         } else {
189             RECT rect;
190             int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_EXPANDTABS;
191             if ((style & DWT.WRAP) !is 0 && wHint !is DWT.DEFAULT) {
192                 flags |= OS.DT_WORDBREAK;
193                 rect.right = Math.max (0, wHint - width);
194             }
195             TCHAR[] buffer = new TCHAR [/+getCodePage (),+/ length + 1];
196             OS.GetWindowText (handle, buffer.ptr, length + 1);
197             OS.DrawText (hDC, buffer.ptr, length, &rect, flags);
198             width += rect.right - rect.left;
199             height = Math.max (height, rect.bottom - rect.top);
200         }
201         if (newFont !is null) OS.SelectObject (hDC, oldFont);
202         OS.ReleaseDC (handle, hDC);
203     }
204     if (wHint !is DWT.DEFAULT) width = wHint;
205     if (hHint !is DWT.DEFAULT) height = hHint;
206     width += border * 2;
207     height += border * 2;
208     /*
209     * Feature in WinCE PPC.  Text labels have a trim
210     * of one pixel wide on the right and left side.
211     * The fix is to increase the width to include
212     * this trim.
213     */
214     if (OS.IsWinCE && !drawImage) width += 2;
215     return new Point (width, height);
216 }
217
218 override void createHandle () {
219     super.createHandle ();
220     state |= THEME_BACKGROUND;
221 }
222
223 /**
224  * Returns a value which describes the position of the
225  * text or image in the receiver. The value will be one of
226  * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
227  * unless the receiver is a <code>SEPARATOR</code> label, in
228  * which case, <code>NONE</code> is returned.
229  *
230  * @return the alignment
231  *
232  * @exception DWTException <ul>
233  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
234  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
235  * </ul>
236  */
237 public int getAlignment () {
238     checkWidget ();
239     if ((style & DWT.SEPARATOR) !is 0) return 0;
240     if ((style & DWT.LEFT) !is 0) return DWT.LEFT;
241     if ((style & DWT.CENTER) !is 0) return DWT.CENTER;
242     if ((style & DWT.RIGHT) !is 0) return DWT.RIGHT;
243     return DWT.LEFT;
244 }
245
246 /**
247  * Returns the receiver's image if it has one, or null
248  * if it does not.
249  *
250  * @return the receiver's image
251  *
252  * @exception DWTException <ul>
253  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
254  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
255  * </ul>
256  */
257 public Image getImage () {
258     checkWidget ();
259     return image;
260 }
261
262 override String getNameText () {
263     return getText ();
264 }
265
266 /**
267  * Returns the receiver's text, which will be an empty
268  * string if it has never been set or if the receiver is
269  * a <code>SEPARATOR</code> label.
270  *
271  * @return the receiver's text
272  *
273  * @exception DWTException <ul>
274  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
275  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
276  * </ul>
277  */
278 public String getText () {
279     checkWidget ();
280     if ((style & DWT.SEPARATOR) !is 0) return "";
281     return text;
282 }
283
284 override bool mnemonicHit (wchar key) {
285     Composite control = this.parent;
286     while (control !is null) {
287         Control [] children = control._getChildren ();
288         int index = 0;
289         while (index < children.length) {
290             if (children [index] is this) break;
291             index++;
292         }
293         index++;
294         if (index < children.length) {
295             if (children [index].setFocus ()) return true;
296         }
297         control = control.parent;
298     }
299     return false;
300 }
301
302 override bool mnemonicMatch (wchar key) {
303     wchar mnemonic = findMnemonic (getText ());
304     if (mnemonic is '\0') return false;
305     return CharacterToUpper (key) is CharacterToUpper (mnemonic);
306 }
307
308 override void releaseWidget () {
309     super.releaseWidget ();
310     text = null;
311     image = null;
312 }
313
314 /**
315  * Controls how text and images will be displayed in the receiver.
316  * The argument should be one of <code>LEFT</code>, <code>RIGHT</code>
317  * or <code>CENTER</code>.  If the receiver is a <code>SEPARATOR</code>
318  * label, the argument is ignored and the alignment is not changed.
319  *
320  * @param alignment the new alignment
321  *
322  * @exception DWTException <ul>
323  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
324  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
325  * </ul>
326  */
327 public void setAlignment (int alignment) {
328     checkWidget ();
329     if ((style & DWT.SEPARATOR) !is 0) return;
330     if ((alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER)) is 0) return;
331     style &= ~(DWT.LEFT | DWT.RIGHT | DWT.CENTER);
332     style |= alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER);
333     int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
334     if ((bits & OS.SS_OWNERDRAW) !is OS.SS_OWNERDRAW) {
335         bits &= ~(OS.SS_LEFTNOWORDWRAP | OS.SS_CENTER | OS.SS_RIGHT);
336         if ((style & DWT.LEFT) !is 0) {
337             if ((style & DWT.WRAP) !is 0) {
338                 bits |= OS.SS_LEFT;
339             } else {
340                 bits |= OS.SS_LEFTNOWORDWRAP;
341             }
342         }
343         if ((style & DWT.CENTER) !is 0) bits |= OS.SS_CENTER;
344         if ((style & DWT.RIGHT) !is 0) bits |= OS.SS_RIGHT;
345         OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
346     }
347     OS.InvalidateRect (handle, null, true);
348 }
349
350 /**
351  * Sets the receiver's image to the argument, which may be
352  * null indicating that no image should be displayed.
353  *
354  * @param image the image to display on the receiver (may be null)
355  *
356  * @exception IllegalArgumentException <ul>
357  *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
358  * </ul>
359  * @exception DWTException <ul>
360  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
361  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
362  * </ul>
363  */
364 public void setImage (Image image) {
365     checkWidget ();
366     if ((style & DWT.SEPARATOR) !is 0) return;
367     if (image !is null && image.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
368     this.image = image;
369     int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
370     if ((bits & OS.SS_OWNERDRAW) !is OS.SS_OWNERDRAW) {
371         bits &= ~(OS.SS_LEFTNOWORDWRAP | OS.SS_CENTER | OS.SS_RIGHT);
372         bits |= OS.SS_OWNERDRAW;
373         OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
374     }
375     OS.InvalidateRect (handle, null, true);
376 }
377
378 /**
379  * Sets the receiver's text.
380  * <p>
381  * This method sets the widget label.  The label may include
382  * the mnemonic character and line delimiters.
383  * </p>
384  * <p>
385  * Mnemonics are indicated by an '&amp;' that causes the next
386  * character to be the mnemonic.  When the user presses a
387  * key sequence that matches the mnemonic, focus is assigned
388  * to the control that follows the label. On most platforms,
389  * the mnemonic appears underlined but may be emphasised in a
390  * platform specific manner.  The mnemonic indicator character
391  * '&amp;' can be escaped by doubling it in the string, causing
392  * a single '&amp;' to be displayed.
393  * </p>
394  *
395  * @param string the new text
396  *
397  * @exception DWTException <ul>
398  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
399  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
400  * </ul>
401  */
402 public void setText (String string) {
403     checkWidget ();
404     // DWT extensions allow null argument
405     //if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
406     if ((style & DWT.SEPARATOR) !is 0) return;
407     /*
408     * Feature in Windows.  For some reason, SetWindowText() for
409     * static controls redraws the control, even when the text has
410     * has not changed.  The fix is to check for this case and do
411     * nothing.
412     */
413     if (string.equals(text)) return;
414     text = string;
415     if (image is null || !IMAGE_AND_TEXT) {
416         int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
417         newBits &= ~OS.SS_OWNERDRAW;
418         if ((style & DWT.LEFT) !is 0) {
419             if ((style & DWT.WRAP) !is 0) {
420                 newBits |= OS.SS_LEFT;
421             } else {
422                 newBits |= OS.SS_LEFTNOWORDWRAP;
423             }
424         }
425         if ((style & DWT.CENTER) !is 0) newBits |= OS.SS_CENTER;
426         if ((style & DWT.RIGHT) !is 0) newBits |= OS.SS_RIGHT;
427         if (oldBits !is newBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
428     }
429     string = Display.withCrLf (string);
430     TCHAR* buffer = StrToTCHARz ( getCodePage (), string);
431     OS.SetWindowText (handle, buffer);
432     /*
433     * Bug in Windows.  For some reason, the HBRUSH that
434     * is returned from WM_CTRLCOLOR is misaligned when
435     * the label uses it to draw.  If the brush is a solid
436     * color, this does not matter.  However, if the brush
437     * contains an image, the image is misaligned.  The
438     * fix is to draw the background in WM_ERASEBKGND.
439     */
440     if (OS.COMCTL32_MAJOR < 6) {
441         if (findImageControl () !is null) OS.InvalidateRect (handle, null, true);
442     }
443 }
444
445 override int widgetExtStyle () {
446     int bits = super.widgetExtStyle () & ~OS.WS_EX_CLIENTEDGE;
447     if ((style & DWT.BORDER) !is 0) return bits | OS.WS_EX_STATICEDGE;
448     return bits;
449 }
450
451 override int widgetStyle () {
452     int bits = super.widgetStyle () | OS.SS_NOTIFY;
453     if ((style & DWT.SEPARATOR) !is 0) return bits | OS.SS_OWNERDRAW;
454     if (OS.WIN32_VERSION >= OS.VERSION (5, 0)) {
455         if ((style & DWT.WRAP) !is 0) bits |= OS.SS_EDITCONTROL;
456     }
457     if ((style & DWT.CENTER) !is 0) return bits | OS.SS_CENTER;
458     if ((style & DWT.RIGHT) !is 0) return bits | OS.SS_RIGHT;
459     if ((style & DWT.WRAP) !is 0) return bits | OS.SS_LEFT;
460     return bits | OS.SS_LEFTNOWORDWRAP;
461 }
462
463 override String windowClass () {
464     return TCHARsToStr( LabelClass );
465 }
466
467 override int windowProc () {
468     return cast(int) LabelProc;
469 }
470
471 override LRESULT WM_ERASEBKGND (int wParam, int lParam) {
472     LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
473     if (result !is null) return result;
474     int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
475     if ((bits & OS.SS_OWNERDRAW) is OS.SS_OWNERDRAW) {
476         return LRESULT.ONE;
477     }
478     /*
479     * Bug in Windows.  For some reason, the HBRUSH that
480     * is returned from WM_CTRLCOLOR is misaligned when
481     * the label uses it to draw.  If the brush is a solid
482     * color, this does not matter.  However, if the brush
483     * contains an image, the image is misaligned.  The
484     * fix is to draw the background in WM_ERASEBKGND.
485     */
486     if (OS.COMCTL32_MAJOR < 6) {
487         if (findImageControl () !is null) {
488             drawBackground (cast(HANDLE)wParam);
489             return LRESULT.ONE;
490         }
491     }
492     return result;
493 }
494
495 override LRESULT WM_SIZE (int wParam, int lParam) {
496     LRESULT result = super.WM_SIZE (wParam, lParam);
497     if (isDisposed ()) return result;
498     if ((style & DWT.SEPARATOR) !is 0) {
499         OS.InvalidateRect (handle, null, true);
500         return result;
501     }
502     int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
503     if ((bits & OS.SS_OWNERDRAW) is OS.SS_OWNERDRAW) {
504         OS.InvalidateRect (handle, null, true);
505         return result;
506     }
507     /*
508     * Bug in Windows.  For some reason, a label with
509     * style SS_LEFT, SS_CENTER or SS_RIGHT does not
510     * redraw the text in the new position when resized.
511     * Note that SS_LEFTNOWORDWRAP does not have the
512     * problem.  The fix is to force the redraw.
513     */
514     if ((bits & OS.SS_LEFTNOWORDWRAP) !is OS.SS_LEFTNOWORDWRAP) {
515         OS.InvalidateRect (handle, null, true);
516         return result;
517     }
518     return result;
519 }
520
521 override LRESULT WM_UPDATEUISTATE (int wParam, int lParam) {
522     LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
523     if (result !is null) return result;
524     /*
525     * Feature in Windows.  When WM_UPDATEUISTATE is sent to
526     * a static control, it sends WM_CTLCOLORSTATIC to get the
527     * foreground and background.  If any drawing happens in
528     * WM_CTLCOLORSTATIC, it overwrites the contents of the control.
529     * The fix is draw the static without drawing the background
530     * and avoid the static window proc.
531     */
532     bool redraw = findImageControl () !is null;
533     if (!redraw) {
534         if ((state & THEME_BACKGROUND) !is 0) {
535             if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
536                 redraw = findThemeControl () !is null;
537             }
538         }
539     }
540     if (redraw) {
541         OS.InvalidateRect (handle, null, false);
542         int /*long*/ code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam);
543         return new LRESULT (code);
544     }
545     return result;
546 }
547
548 override LRESULT wmColorChild (int wParam, int lParam) {
549     /*
550     * Bug in Windows.  For some reason, the HBRUSH that
551     * is returned from WM_CTRLCOLOR is misaligned when
552     * the label uses it to draw.  If the brush is a solid
553     * color, this does not matter.  However, if the brush
554     * contains an image, the image is misaligned.  The
555     * fix is to draw the background in WM_ERASEBKGND.
556     */
557     LRESULT result = super.wmColorChild (wParam, lParam);
558     if (OS.COMCTL32_MAJOR < 6) {
559         int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
560         if ((bits & OS.SS_OWNERDRAW) !is OS.SS_OWNERDRAW) {
561             if (findImageControl () !is null) {
562                 OS.SetBkMode ( cast(HANDLE) wParam, OS.TRANSPARENT);
563                 return new LRESULT ( cast(int)OS.GetStockObject (OS.NULL_BRUSH));
564             }
565         }
566     }
567     return result;
568 }
569
570 override LRESULT WM_PAINT (int wParam, int lParam) {
571     static if (OS.IsWinCE) {
572         bool drawImage = image !is null;
573         bool drawSeparator = (style & DWT.SEPARATOR) !is 0 && (style & DWT.SHADOW_NONE) is 0;
574         if (drawImage || drawSeparator) {
575             LRESULT result = null;
576             PAINTSTRUCT ps;
577             GCData