1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.fontdialog;
7 
8 private import dfl.base, dfl.commondialog, dfl.internal.winapi, dfl.application,
9 	dfl.control, dfl.drawing, dfl.event, dfl.internal.utf,
10 	dfl.internal.dlib;
11 
12 
13 private extern(Windows) nothrow
14 {
15 	alias BOOL function(LPCHOOSEFONTW lpcf) ChooseFontWProc;
16 }
17 
18 
19 ///
20 class FontDialog: CommonDialog
21 {
22 	this()
23 	{
24 		Application.ppin(cast(void*)this);
25 		
26 		cf.lStructSize = cf.sizeof;
27 		cf.Flags = INIT_FLAGS;
28 		cf.lpLogFont = cast(typeof(cf.lpLogFont))&lfw;
29 		cf.lCustData = cast(typeof(cf.lCustData))cast(void*)this;
30 		cf.lpfnHook = &fondHookProc;
31 		cf.rgbColors = 0;
32 	}
33 	
34 	
35 	override void reset()
36 	{
37 		_fon = null;
38 		cf.Flags = INIT_FLAGS;
39 		cf.rgbColors = 0;
40 		cf.nSizeMin = 0;
41 		cf.nSizeMax = 0;
42 	}
43 	
44 	
45 	///
46 	final @property void allowSimulations(bool byes) // setter
47 	{
48 		if(byes)
49 			cf.Flags &= ~CF_NOSIMULATIONS;
50 		else
51 			cf.Flags |= CF_NOSIMULATIONS;
52 	}
53 	
54 	/// ditto
55 	final @property bool allowSimulations() // getter
56 	{
57 		if(cf.Flags & CF_NOSIMULATIONS)
58 			return false;
59 		return true;
60 	}
61 	
62 	
63 	///
64 	final @property void allowVectorFonts(bool byes) // setter
65 	{
66 		if(byes)
67 			cf.Flags &= ~CF_NOVECTORFONTS;
68 		else
69 			cf.Flags |= CF_NOVECTORFONTS;
70 	}
71 	
72 	/// ditto
73 	final bool allowVectorFonts() // getter
74 	{
75 		if(cf.Flags & CF_NOVECTORFONTS)
76 			return false;
77 		return true;
78 	}
79 	
80 	
81 	///
82 	final @property void allowVerticalFonts(bool byes) // setter
83 	{
84 		if(byes)
85 			cf.Flags &= ~CF_NOVERTFONTS;
86 		else
87 			cf.Flags |= CF_NOVERTFONTS;
88 	}
89 	
90 	/// ditto
91 	final @property bool allowVerticalFonts() // getter
92 	{
93 		if(cf.Flags & CF_NOVERTFONTS)
94 			return false;
95 		return true;
96 	}
97 	
98 	
99 	///
100 	final @property void color(Color c) // setter
101 	{
102 		cf.rgbColors = c.toRgb();
103 	}
104 	
105 	/// ditto
106 	final @property Color color() // getter
107 	{
108 		return Color.fromRgb(cf.rgbColors);
109 	}
110 	
111 	
112 	///
113 	final @property void fixedPitchOnly(bool byes) // setter
114 	{
115 		if(byes)
116 			cf.Flags |= CF_FIXEDPITCHONLY;
117 		else
118 			cf.Flags &= ~CF_FIXEDPITCHONLY;
119 	}
120 	
121 	/// ditto
122 	final @property bool fixedPitchOnly() // getter
123 	{
124 		if(cf.Flags & CF_FIXEDPITCHONLY)
125 			return true;
126 		return false;
127 	}
128 	
129 	
130 	///
131 	final @property void font(Font f) // setter
132 	{
133 		_fon = f;
134 	}
135 	
136 	/// ditto
137 	final @property Font font() // getter
138 	{
139 		if(!_fon)
140 			_fon = Control.defaultFont; // ?
141 		return _fon;
142 	}
143 	
144 	
145 	///
146 	final @property void fontMustExist(bool byes) // setter
147 	{
148 		if(byes)
149 			cf.Flags |= CF_FORCEFONTEXIST;
150 		else
151 			cf.Flags &= ~CF_FORCEFONTEXIST;
152 	}
153 	
154 	/// ditto
155 	final @property bool fontMustExist() // getter
156 	{
157 		if(cf.Flags & CF_FORCEFONTEXIST)
158 			return true;
159 		return false;
160 	}
161 	
162 	
163 	///
164 	final @property void maxSize(int max) // setter
165 	{
166 		if(max > 0)
167 		{
168 			if(max > cf.nSizeMin)
169 				cf.nSizeMax = max;
170 			cf.Flags |= CF_LIMITSIZE;
171 		}
172 		else
173 		{
174 			cf.Flags &= ~CF_LIMITSIZE;
175 			cf.nSizeMax = 0;
176 			cf.nSizeMin = 0;
177 		}
178 	}
179 	
180 	/// ditto
181 	final @property int maxSize() // getter
182 	{
183 		if(cf.Flags & CF_LIMITSIZE)
184 			return cf.nSizeMax;
185 		return 0;
186 	}
187 	
188 	
189 	///
190 	final @property void minSize(int min) // setter
191 	{
192 		if(min > cf.nSizeMax)
193 			cf.nSizeMax = min;
194 		cf.nSizeMin = min;
195 		cf.Flags |= CF_LIMITSIZE;
196 	}
197 	
198 	/// ditto
199 	final @property int minSize() // getter
200 	{
201 		if(cf.Flags & CF_LIMITSIZE)
202 			return cf.nSizeMin;
203 		return 0;
204 	}
205 	
206 	
207 	///
208 	final @property void scriptsOnly(bool byes) // setter
209 	{
210 		if(byes)
211 			cf.Flags |= CF_SCRIPTSONLY;
212 		else
213 			cf.Flags &= ~CF_SCRIPTSONLY;
214 	}
215 	
216 	/// ditto
217 	final @property bool scriptsOnly() // getter
218 	{
219 		if(cf.Flags & CF_SCRIPTSONLY)
220 			return true;
221 		return false;
222 	}
223 	
224 	
225 	///
226 	final @property void showApply(bool byes) // setter
227 	{
228 		if(byes)
229 			cf.Flags |= CF_APPLY;
230 		else
231 			cf.Flags &= ~CF_APPLY;
232 	}
233 	
234 	/// ditto
235 	final @property bool showApply() // getter
236 	{
237 		if(cf.Flags & CF_APPLY)
238 			return true;
239 		return false;
240 	}
241 	
242 	
243 	///
244 	final @property void showHelp(bool byes) // setter
245 	{
246 		if(byes)
247 			cf.Flags |= CF_SHOWHELP;
248 		else
249 			cf.Flags &= ~CF_SHOWHELP;
250 	}
251 	
252 	/// ditto
253 	final @property bool showHelp() // getter
254 	{
255 		if(cf.Flags & CF_SHOWHELP)
256 			return true;
257 		return false;
258 	}
259 	
260 	
261 	///
262 	final @property void showEffects(bool byes) // setter
263 	{
264 		if(byes)
265 			cf.Flags |= CF_EFFECTS;
266 		else
267 			cf.Flags &= ~CF_EFFECTS;
268 	}
269 	
270 	/// ditto
271 	final @property bool showEffects() // getter
272 	{
273 		if(cf.Flags & CF_EFFECTS)
274 			return true;
275 		return false;
276 	}
277 	
278 	
279 	override DialogResult showDialog()
280 	{
281 		return runDialog(GetActiveWindow()) ?
282 			DialogResult.OK : DialogResult.CANCEL;
283 	}
284 	
285 	
286 	override DialogResult showDialog(IWindow owner)
287 	{
288 		return runDialog(owner ? owner.handle : GetActiveWindow()) ?
289 			DialogResult.OK : DialogResult.CANCEL;
290 	}
291 	
292 	
293 	///
294 	EventHandler apply;
295 	
296 	
297 	protected override LRESULT hookProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
298 	{
299 		switch(msg)
300 		{
301 			case WM_COMMAND:
302 				switch(LOWORD(wparam))
303 				{
304 					case CF_APPLY: // ?
305 						_update();
306 						onApply(EventArgs.empty);
307 						break;
308 					
309 					default:
310 				}
311 				break;
312 			
313 			default:
314 		}
315 		
316 		return super.hookProc(hwnd, msg, wparam, lparam);
317 	}
318 	
319 	
320 	protected override bool runDialog(HWND owner)
321 	{
322 		if(!_runDialog(owner))
323 		{
324 			if(!CommDlgExtendedError())
325 				return false;
326 			_cantrun();
327 		}
328 		return true;
329 	}
330 	
331 	
332 	private BOOL _runDialog(HWND owner)
333 	{
334 		BOOL result = FALSE;
335 		
336 		cf.hwndOwner = owner;
337 		
338 		if(dfl.internal.utf.useUnicode)
339 		{
340 			font._info(&lfw); // -font- gets default font if not set.
341 			
342 			enum NAME = "ChooseFontW";
343 			static ChooseFontWProc proc = null;
344 			
345 			if(!proc)
346 			{
347 				proc = cast(ChooseFontWProc)GetProcAddress(GetModuleHandleA("comdlg32.dll"), NAME.ptr);
348 				if(!proc)
349 					throw new Exception("Unable to load procedure " ~ NAME ~ ".");
350 			}
351 			
352 			result = proc(&cfw);
353 		}
354 		else
355 		{
356 			font._info(&lfa); // -font- gets default font if not set.
357 			
358 			result = ChooseFontA(&cfa);
359 		}
360 		
361 		if(result)
362 		{
363 			_update();
364 			return result;
365 		}
366 		return FALSE;
367 	}
368 	
369 	
370 	private void _update()
371 	{
372 		LogFont lf;
373 		
374 		if(dfl.internal.utf.useUnicode)
375 			Font.LOGFONTWtoLogFont(lf, &lfw);
376 		else
377 			Font.LOGFONTAtoLogFont(lf, &lfa);
378 		
379 		_fon = new Font(Font._create(lf), true);
380 	}
381 	
382 	
383 	///
384 	protected void onApply(EventArgs ea)
385 	{
386 		apply(this, ea);
387 	}
388 	
389 	
390 	private:
391 	
392 	union
393 	{
394 		CHOOSEFONTW cfw;
395 		CHOOSEFONTA cfa;
396 		alias cfw cf;
397 		
398 		static assert(CHOOSEFONTW.sizeof == CHOOSEFONTA.sizeof);
399 		static assert(CHOOSEFONTW.Flags.offsetof == CHOOSEFONTA.Flags.offsetof);
400 		static assert(CHOOSEFONTW.nSizeMax.offsetof == CHOOSEFONTA.nSizeMax.offsetof);
401 	}
402 	
403 	union
404 	{
405 		LOGFONTW lfw;
406 		LOGFONTA lfa;
407 		
408 		static assert(LOGFONTW.lfFaceName.offsetof == LOGFONTA.lfFaceName.offsetof);
409 	}
410 	
411 	Font _fon;
412 	
413 	
414 	enum UINT INIT_FLAGS = CF_EFFECTS | CF_ENABLEHOOK | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
415 }
416 
417 
418 // WM_CHOOSEFONT_SETFLAGS to update flags after dialog creation ... ?
419 
420 
421 private extern(Windows) UINT fondHookProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nothrow
422 {
423 	enum PROP_STR = "DFL_FontDialog";
424 	FontDialog fd;
425 	LRESULT result = 0;
426 	
427 	try
428 	{
429 		if(msg == WM_INITDIALOG)
430 		{
431 			CHOOSEFONTA* cf;
432 			cf = cast(CHOOSEFONTA*)lparam;
433 			SetPropA(hwnd, PROP_STR.ptr, cast(HANDLE)cf.lCustData);
434 			fd = cast(FontDialog)cast(void*)cf.lCustData;
435 		}
436 		else
437 		{
438 			fd = cast(FontDialog)cast(void*)GetPropA(hwnd, PROP_STR.ptr);
439 		}
440 		
441 		if(fd)
442 		{
443 			result = fd.hookProc(hwnd, msg, wparam, lparam);
444 		}
445 	}
446 	catch(DThrowable e)
447 	{
448 		Application.onThreadException(e);
449 	}
450 	
451 	return cast(uint)result;
452 }
453