1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 
6 module dfl.label;
7 
8 private import dfl.base, dfl.control, dfl.internal.winapi, dfl.application,
9         dfl.event, dfl.drawing, dfl.internal.dlib;
10 
11 
12 
13 class Label: Control { // docmain
14    this() {
15       resizeRedraw = true; // Word wrap and center correctly.
16 
17       tfmt = new TextFormat(TextFormatFlags.WORD_BREAK | TextFormatFlags.LINE_LIMIT);
18    }
19 
20 
21 
22    @property void borderStyle(BorderStyle bs) { // setter
23       final switch(bs) {
24          case BorderStyle.FIXED_3D:
25             _style(_style() & ~WS_BORDER);
26             _exStyle(_exStyle() | WS_EX_CLIENTEDGE);
27             break;
28 
29          case BorderStyle.FIXED_SINGLE:
30             _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
31             _style(_style() | WS_BORDER);
32             break;
33 
34          case BorderStyle.NONE:
35             _style(_style() & ~WS_BORDER);
36             _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
37             break;
38       }
39 
40       if(isHandleCreated) {
41          redrawEntire();
42       }
43    }
44 
45    /// ditto
46    @property BorderStyle borderStyle() { // getter
47       if(_exStyle() & WS_EX_CLIENTEDGE) {
48          return BorderStyle.FIXED_3D;
49       } else if(_style() & WS_BORDER) {
50          return BorderStyle.FIXED_SINGLE;
51       }
52       return BorderStyle.NONE;
53    }
54 
55 
56 
57    final @property void useMnemonic(bool byes) { // setter
58       if(byes) {
59          tfmt.formatFlags = tfmt.formatFlags & ~TextFormatFlags.NO_PREFIX;
60          _style(_style() & ~SS_NOPREFIX);
61       } else {
62          tfmt.formatFlags = tfmt.formatFlags | TextFormatFlags.NO_PREFIX;
63          _style(_style() | SS_NOPREFIX);
64       }
65 
66       if(isHandleCreated) {
67          invalidate();
68       }
69    }
70 
71    /// ditto
72    final @property bool useMnemonic() { // getter
73       return (tfmt.formatFlags & TextFormatFlags.NO_PREFIX) == 0;
74    }
75 
76 
77 
78    @property Size preferredSize() { // getter
79       Size result;
80       Graphics g;
81       g = isHandleCreated ? createGraphics() : Graphics.getScreen();
82       result = g.measureText(text, font, tfmt);
83       g.dispose();
84       return result;
85    }
86 
87 
88    private void doAutoSize(Dstring text) {
89       //if(isHandleCreated)
90       {
91          clientSize = preferredSize;
92       }
93    }
94 
95 
96    override @property void text(Dstring newText) { // setter
97       super.text = newText;
98 
99       if(autosz) {
100          doAutoSize(newText);
101       }
102 
103       invalidate(false);
104    }
105 
106    alias Control.text text; // Overload.
107 
108 
109 
110    @property void autoSize(bool byes) { // setter
111       if(byes != autosz) {
112          autosz = byes;
113 
114          if(byes) {
115             doAutoSize(text);
116          }
117       }
118    }
119 
120    /// ditto
121    @property bool autoSize() { // getter
122       return autosz;
123    }
124 
125 
126 
127    @property void textAlign(ContentAlignment calign) { // setter
128       final switch(calign) {
129          case ContentAlignment.TOP_LEFT:
130             tfmt.alignment = TextAlignment.TOP | TextAlignment.LEFT;
131             break;
132 
133          case ContentAlignment.BOTTOM_CENTER:
134             tfmt.alignment = TextAlignment.BOTTOM | TextAlignment.CENTER;
135             break;
136 
137          case ContentAlignment.BOTTOM_LEFT:
138             tfmt.alignment = TextAlignment.BOTTOM | TextAlignment.LEFT;
139             break;
140 
141          case ContentAlignment.BOTTOM_RIGHT:
142             tfmt.alignment = TextAlignment.BOTTOM | TextAlignment.RIGHT;
143             break;
144 
145          case ContentAlignment.MIDDLE_CENTER:
146             tfmt.alignment = TextAlignment.MIDDLE | TextAlignment.CENTER;
147             break;
148 
149          case ContentAlignment.MIDDLE_LEFT:
150             tfmt.alignment = TextAlignment.MIDDLE | TextAlignment.LEFT;
151             break;
152 
153          case ContentAlignment.MIDDLE_RIGHT:
154             tfmt.alignment = TextAlignment.MIDDLE | TextAlignment.RIGHT;
155             break;
156 
157          case ContentAlignment.TOP_CENTER:
158             tfmt.alignment = TextAlignment.TOP | TextAlignment.CENTER;
159             break;
160 
161          case ContentAlignment.TOP_RIGHT:
162             tfmt.alignment = TextAlignment.TOP | TextAlignment.RIGHT;
163             break;
164       }
165 
166       invalidate(); // ?
167    }
168 
169    /// ditto
170    @property ContentAlignment textAlign() { // getter
171       TextAlignment ta;
172       ta = tfmt.alignment;
173 
174       if(ta & TextAlignment.BOTTOM) {
175          if(ta & TextAlignment.RIGHT) {
176             return ContentAlignment.BOTTOM_RIGHT;
177          } else if(ta & TextAlignment.CENTER) {
178             return ContentAlignment.BOTTOM_CENTER;
179          } else { // Left.
180             return ContentAlignment.BOTTOM_LEFT;
181          }
182       } else if(ta & TextAlignment.MIDDLE) {
183          if(ta & TextAlignment.RIGHT) {
184             return ContentAlignment.MIDDLE_RIGHT;
185          } else if(ta & TextAlignment.CENTER) {
186             return ContentAlignment.MIDDLE_CENTER;
187          } else { // Left.
188             return ContentAlignment.MIDDLE_LEFT;
189          }
190       } else { // Top.
191          if(ta & TextAlignment.RIGHT) {
192             return ContentAlignment.TOP_RIGHT;
193          } else if(ta & TextAlignment.CENTER) {
194             return ContentAlignment.TOP_CENTER;
195          } else { // Left.
196             return ContentAlignment.TOP_LEFT;
197          }
198       }
199    }
200 
201 
202    protected override @property Size defaultSize() { // getter
203       return Size(100, 23);
204    }
205 
206 
207    protected override void onPaint(PaintEventArgs ea) {
208       int x, y, w, h;
209       Dstring text;
210 
211       text = this.text;
212 
213       if(tfmt.alignment & TextAlignment.MIDDLE) {
214          // Graphics.drawText() does not support middle alignment
215          // if the text is multiline, so need to do extra work.
216          Size sz;
217          sz = ea.graphics.measureText(text, font, tfmt);
218          x = 0;
219          //if(sz.height >= this.clientSize.height)
220          // y = 0;
221          //else
222          y = (this.clientSize.height - sz.height) / 2;
223          w = clientSize.width;
224          h = sz.height;
225       } else if(tfmt.alignment & TextAlignment.BOTTOM) {
226          // Graphics.drawText() does not support bottom alignment
227          // if the text is multiline, so need to do extra work.
228          Size sz;
229          sz = ea.graphics.measureText(text, font, tfmt);
230          x = 0;
231          //if(sz.height >= this.clientSize.height)
232          // y = 0;
233          //else
234          y = this.clientSize.height - sz.height;
235          w = clientSize.width;
236          h = sz.height;
237       } else {
238          x = 0;
239          y = 0;
240          w = clientSize.width;
241          h = clientSize.height;
242       }
243 
244       Color c;
245       //c = foreColor;
246       c = foreColor.solidColor(backColor);
247 
248       if(enabled) {
249          ea.graphics.drawText(text, font, c, Rect(x, y, w, h), tfmt);
250       } else {
251          version(LABEL_GRAYSTRING) {
252             // GrayString() is pretty ugly.
253             GrayStringA(ea.graphics.handle, null, &_disabledOutputProc,
254                         cast(LPARAM)cast(void*)this, -1, x, y, w, h);
255          }
256          else {
257             ea.graphics.drawTextDisabled(text, font, c, backColor, Rect(x, y, w, h), tfmt);
258          }
259       }
260 
261       super.onPaint(ea);
262    }
263 
264 
265    /+
266    protected override void onHandleCreated(EventArgs ea) {
267       super.onHandleCreated(ea);
268 
269       /+
270       if(autosz) {
271          doAutoSize(text);
272       }
273       +/
274    }
275    +/
276 
277 
278    protected override void onEnabledChanged(EventArgs ea) {
279       invalidate(false);
280 
281       super.onEnabledChanged(ea);
282    }
283 
284 
285    protected override void onFontChanged(EventArgs ea) {
286       if(autosz) {
287          doAutoSize(text);
288       }
289 
290       invalidate(false);
291 
292       super.onFontChanged(ea);
293    }
294 
295 
296    protected override void wndProc(ref Message m) {
297       switch(m.msg) {
298          case WM_GETDLGCODE:
299             super.wndProc(m);
300             //if(useMnemonic)
301             m.result |= DLGC_STATIC;
302             break;
303 
304          default:
305             super.wndProc(m);
306       }
307    }
308 
309 
310    protected override bool processMnemonic(dchar charCode) {
311       if(visible && enabled) {
312          if(isMnemonic(charCode, text)) {
313             select(true, true);
314             return true;
315          }
316       }
317       return false;
318    }
319 
320 
321  private:
322    TextFormat _tfmt;
323    bool autosz = false;
324 
325 
326    final @property void tfmt(TextFormat tf) { // setter
327       _tfmt = tf;
328    }
329 
330 
331    final @property TextFormat tfmt() { // getter
332       /+
333       // This causes it to invert.
334       if(rightToLeft) {
335          _tfmt.formatFlags = _tfmt.formatFlags | TextFormatFlags.DIRECTION_RIGHT_TO_LEFT;
336       } else {
337          _tfmt.formatFlags = _tfmt.formatFlags & ~TextFormatFlags.DIRECTION_RIGHT_TO_LEFT;
338       }
339       +/
340 
341       return _tfmt;
342    }
343 }
344 
345 
346 version(LABEL_GRAYSTRING) {
347    private extern(Windows) BOOL _disabledOutputProc(HDC hdc, LPARAM lpData, int cchData) {
348       BOOL result = TRUE;
349       try {
350          scope Graphics g = new Graphics(hdc, false);
351          Label l;
352          with(l = cast(Label)cast(void*)lpData) {
353             g.drawText(text, font, foreColor,
354                        Rect(0, 0, clientSize.width, clientSize.height), tfmt);
355          }
356       } catch(DThrowable e) {
357          Application.onThreadException(e);
358          result = FALSE;
359       }
360       return result;
361    }
362 }
363