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