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