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