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