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 result; 452 } 453