1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 module dfl.application; 5 6 import core.sys.windows.windows; 7 import core.sys.windows.commctrl; 8 import core.stdc.stdlib; 9 10 import dfl.base; 11 import dfl.button; 12 import dfl.control; 13 import dfl.drawing; 14 import dfl.environment; 15 import dfl.event; 16 import dfl.exception; 17 import dfl.form; 18 import dfl.internal.clib; 19 import dfl.internal.dlib; 20 import dfl.internal.utf; 21 import dfl.internal.visualstyles; 22 import dfl.label; 23 import dfl.textbox; 24 25 26 version = DFL_NO_RESOURCES; 27 version (DFL_NO_RESOURCES) { 28 } else { 29 import dfl.resources; 30 } 31 32 version (DFL_NO_MENUS) { 33 } else { 34 import dfl.menu; 35 } 36 37 version = DFL_NO_ZOMBIE_FORM; 38 39 //debug = APP_PRINT; 40 //debug = SHOW_MESSAGE_INFO; // Slow. 41 42 debug (APP_PRINT) { 43 pragma(msg, "DFL: debug app print"); 44 45 version (DFL_LIB) static assert(0); 46 } 47 48 private extern (C) void abort(); 49 50 class ApplicationContext { 51 52 this() { 53 } 54 55 // If onMainFormClose isn't overridden, the message 56 // loop terminates when the main form is destroyed. 57 this(Form mainForm) { 58 mform = mainForm; 59 mainForm.closed ~= &onMainFormClosed; 60 } 61 62 final @property void mainForm(Form mainForm) { 63 if (mform) { 64 mform.closed.removeHandler(&onMainFormClosed); 65 } 66 67 mform = mainForm; 68 69 if (mainForm) { 70 mainForm.closed ~= &onMainFormClosed; 71 } 72 } 73 74 final @property Form mainForm() nothrow { 75 return mform; 76 } 77 78 Event!(Object, EventArgs) threadExit; 79 80 final void exitThread() { 81 exitThreadCore(); 82 } 83 84 protected: 85 86 void exitThreadCore() { 87 threadExit(this, EventArgs.empty); 88 //ExitThread(0); 89 } 90 91 void onMainFormClosed(Object sender, EventArgs args) { 92 exitThreadCore(); 93 } 94 95 private: 96 Form mform; // The context form. 97 } 98 99 private extern (Windows) nothrow { 100 alias GetTempFileNameWProc = UINT function(LPCWSTR lpPathName, 101 LPCWSTR lpPrefixString, UINT uUnique, LPWSTR lpTempFileName); 102 alias GetTempPathWProc = DWORD function(DWORD nBufferLength, LPWSTR lpBuffer); 103 alias CreateActCtxWProc = HANDLE function(PACTCTXW pActCtx); 104 alias ActivateActCtxProc = BOOL function(HANDLE hActCtx, ULONG_PTR* lpCookie); 105 } 106 107 version (NO_WINDOWS_HUNG_WORKAROUND) { 108 } else { 109 version = WINDOWS_HUNG_WORKAROUND; 110 } 111 112 // Compatibility with previous DFL versions. 113 // Set version=DFL_NO_COMPAT to disable. 114 enum DflCompat { 115 NONE = 0, 116 117 // Adding to menus is the old way. 118 MENU_092 = 0x1, 119 120 // Controls don't recreate automatically when necessary. 121 CONTROL_RECREATE_095 = 0x2, 122 123 // Nothing. 124 CONTROL_KEYEVENT_096 = 0x4, 125 126 // When a Form is in showDialog, changing the dialogResult from NONE doesn't close the form. 127 FORM_DIALOGRESULT_096 = 0x8, 128 129 // Call onLoad/load and focus a control at old time. 130 FORM_LOAD_096 = 0x10, 131 132 // Parent controls now need to be container-controls; this removes that limit. 133 CONTROL_PARENT_096 = 0x20, 134 } 135 136 final class Application { 137 private this() { 138 } 139 140 static: 141 142 // Should be called before creating any controls. 143 // This is typically the first function called in main(). 144 // Does nothing if not supported. 145 void enableVisualStyles() { 146 enum MANIFEST = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` ~ "\r\n" ~ `<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">` ~ "\r\n" ~ `<description>DFL manifest</description>` ~ "\r\n" ~ `<dependency>` ~ "\r\n" ~ `<dependentAssembly>` ~ "\r\n" ~ `<assemblyIdentity ` ~ `type="win32" ` ~ `name="Microsoft.Windows.Common-Controls" ` ~ `version="6.0.0.0" ` ~ `processorArchitecture="X86" ` ~ `publicKeyToken="6595b64144ccf1df" ` ~ `language="*" ` ~ `/>` ~ "\r\n" ~ `</dependentAssembly>` ~ "\r\n" ~ `</dependency>` ~ "\r\n" ~ `</assembly>` ~ "\r\n"; 147 148 HMODULE kernel32; 149 kernel32 = GetModuleHandleA("kernel32.dll"); 150 //if(kernel32) 151 assert(kernel32); 152 { 153 CreateActCtxWProc createActCtxW; 154 createActCtxW = cast(CreateActCtxWProc) GetProcAddress(kernel32, "CreateActCtxW"); 155 if (createActCtxW) { 156 GetTempPathWProc getTempPathW; 157 GetTempFileNameWProc getTempFileNameW; 158 ActivateActCtxProc activateActCtx; 159 160 getTempPathW = cast(GetTempPathWProc) GetProcAddress(kernel32, "GetTempPathW"); 161 assert(getTempPathW !is null); 162 getTempFileNameW = cast(GetTempFileNameWProc) GetProcAddress(kernel32, 163 "GetTempFileNameW"); 164 assert(getTempFileNameW !is null); 165 activateActCtx = cast(ActivateActCtxProc) GetProcAddress(kernel32, "ActivateActCtx"); 166 assert(activateActCtx !is null); 167 168 DWORD pathlen; 169 wchar[MAX_PATH] pathbuf = void; 170 //if(pathbuf) 171 { 172 pathlen = getTempPathW(pathbuf.length, pathbuf.ptr); 173 if (pathlen) { 174 DWORD manifestlen; 175 wchar[MAX_PATH] manifestbuf = void; 176 //if(manifestbuf) 177 { 178 manifestlen = getTempFileNameW(pathbuf.ptr, "dmf", 0, manifestbuf.ptr); 179 if (manifestlen) { 180 HANDLE hf; 181 hf = CreateFileW(manifestbuf.ptr, GENERIC_WRITE, 0, 182 null, CREATE_ALWAYS, 183 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, HANDLE.init); 184 if (hf != INVALID_HANDLE_VALUE) { 185 DWORD written; 186 if (WriteFile(hf, MANIFEST.ptr, MANIFEST.length, &written, 187 null)) { 188 CloseHandle(hf); 189 190 ACTCTXW ac; 191 HANDLE hac; 192 193 ac.cbSize = ACTCTXW.sizeof; 194 //ac.dwFlags = 4; // ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID 195 ac.dwFlags = 0; 196 ac.lpSource = manifestbuf.ptr; 197 //ac.lpAssemblyDirectory = pathbuf; // ? 198 199 hac = createActCtxW(&ac); 200 if (hac != INVALID_HANDLE_VALUE) { 201 ULONG_PTR ul; 202 activateActCtx(hac, &ul); 203 204 _initCommonControls(ICC_STANDARD_CLASSES); // Yes. 205 //InitCommonControls(); // No. Doesn't work with common controls version 6! 206 207 // Ensure the actctx is actually associated with the message queue... 208 PostMessageA(null, wmDfl, 0, 0); 209 { 210 MSG msg; 211 PeekMessageA(&msg, null, wmDfl, wmDfl, PM_REMOVE); 212 } 213 } else { 214 debug (APP_PRINT) 215 cprintf("CreateActCtxW failed.\n"); 216 } 217 } else { 218 CloseHandle(hf); 219 } 220 } 221 222 DeleteFileW(manifestbuf.ptr); 223 } 224 } 225 } 226 } 227 } 228 } 229 } 230 231 /+ 232 // 233 @property bool visualStyles() nothrow { 234 // IsAppThemed: 235 // "Do not call this function during DllMain or global objects contructors. 236 // This may cause invalid return values in Microsoft Windows Vista and may cause Windows XP to become unstable." 237 } 238 +/ 239 240 /// Path of the executable including its file name. 241 @property Dstring executablePath() { 242 return dfl.internal.utf.getModuleFileName(HMODULE.init); 243 } 244 245 /// Directory containing the executable. 246 @property Dstring startupPath() { 247 return pathGetDirName(dfl.internal.utf.getModuleFileName(HMODULE.init)); 248 } 249 250 // Used internally. 251 Dstring getSpecialPath(Dstring name) { // package 252 HKEY hk; 253 if (ERROR_SUCCESS != RegOpenKeyA(HKEY_CURRENT_USER, 254 r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders".ptr, 255 &hk)) { 256 bad_path: 257 throw new DflException("Unable to obtain " ~ name ~ " directory information"); 258 } 259 scope (exit) 260 RegCloseKey(hk); 261 Dstring result; 262 result = regQueryValueString(hk, name); 263 if (!result.length) { 264 goto bad_path; 265 } 266 return result; 267 } 268 269 /// Application data base directory path, usually `C:\Documents and Settings\<user>\Application Data`; this directory might not exist yet. 270 @property Dstring userAppDataBasePath() { 271 return getSpecialPath("AppData"); 272 } 273 274 @property bool messageLoop() nothrow { 275 return (threadFlags & TF.RUNNING) != 0; 276 } 277 278 void addMessageFilter(IMessageFilter mf) { 279 //filters ~= mf; 280 281 IMessageFilter[] fs = filters; 282 fs ~= mf; 283 filters = fs; 284 } 285 286 void removeMessageFilter(IMessageFilter mf) { 287 uint i; 288 for (i = 0; i != filters.length; i++) { 289 if (mf is filters[i]) { 290 if (!i) { 291 filters = filters[1 .. filters.length]; 292 } else if (i == filters.length - 1) { 293 filters = filters[0 .. i]; 294 } else { 295 filters = filters[0 .. i] ~ filters[i + 1 .. filters.length]; 296 } 297 break; 298 } 299 } 300 } 301 302 package bool _doEvents(bool* keep) { 303 if (threadFlags & (TF.STOP_RUNNING | TF.QUIT)) { 304 return false; 305 } 306 307 try { 308 Message msg; 309 310 //while(PeekMessageA(&msg._winMsg, HWND.init, 0, 0, PM_REMOVE)) 311 while (dfl.internal.utf.peekMessage(&msg._winMsg, HWND.init, 0, 0, PM_REMOVE)) { 312 gotMessage(msg); 313 314 if (msg.msg == WM_QUIT) { 315 threadFlags = threadFlags | TF.QUIT; 316 return false; 317 } 318 if (threadFlags & TF.STOP_RUNNING) { 319 return false; 320 } 321 if (!*keep) { 322 break; 323 } 324 } 325 326 // Execution continues after this so it's not idle. 327 } 328 catch (DThrowable e) { 329 onThreadException(e); 330 } 331 332 return (threadFlags & TF.QUIT) == 0; 333 } 334 335 /// Process all messages in the message queue. Returns false if the application should exit. 336 bool doEvents() { 337 bool keep = true; 338 return _doEvents(&keep); 339 } 340 341 bool doEvents(uint msDelay) { 342 if (msDelay <= 3) { 343 return doEvents(); 344 } 345 struct TMR { 346 public import dfl.timer; 347 } 348 349 scope tmr = new TMR.Timer(); 350 bool keep = true; 351 tmr.interval = msDelay; 352 tmr.tick ~= (TMR.Timer sender, EventArgs ea) { sender.stop(); keep = false; }; 353 tmr.start(); 354 while (keep) { 355 Application.waitForEvent(); 356 if (!_doEvents(&keep)) { 357 return false; 358 } 359 } 360 return true; 361 } 362 363 /// Run the application. 364 void run() { 365 run(new ApplicationContext); 366 } 367 368 void run(void delegate() whileIdle) { 369 run(new ApplicationContext, whileIdle); 370 } 371 372 void run(ApplicationContext appcon) { 373 void whileIdle() { 374 waitForEvent(); 375 } 376 377 run(appcon, &whileIdle); 378 } 379 380 // -whileIdle- is called repeatedly while there are no messages in the queue. 381 // Application.idle events are suppressed; however, the -whileIdle- handler 382 // may manually fire the Application.idle event. 383 void run(ApplicationContext appcon, void delegate() whileIdle) { 384 if (threadFlags & TF.RUNNING) { 385 //throw new DflException("Cannot have more than one message loop per thread"); 386 assert(0, "Cannot have more than one message loop per thread"); 387 } 388 389 if (threadFlags & TF.QUIT) { 390 assert(0, "The application is shutting down"); 391 } 392 393 version (CUSTOM_MSG_HOOK) { 394 HHOOK _msghook = SetWindowsHookExA(WH_CALLWNDPROCRET, &globalMsgHook, 395 null, GetCurrentThreadId()); 396 if (!_msghook) { 397 throw new DflException("Unable to get window messages"); 398 } 399 msghook = _msghook; 400 } 401 402 void threadJustExited(Object sender, EventArgs ea) { 403 exitThread(); 404 } 405 406 ctx = appcon; 407 ctx.threadExit ~= &threadJustExited; 408 try { 409 threadFlags = threadFlags | TF.RUNNING; 410 411 if (ctx.mainForm) { 412 //ctx.mainForm.createControl(); 413 ctx.mainForm.show(); 414 } 415 416 for (;;) { 417 try { 418 still_running: while (!(threadFlags & (TF.QUIT | TF.STOP_RUNNING))) { 419 Message msg; 420 421 //while(PeekMessageA(&msg._winMsg, HWND.init, 0, 0, PM_REMOVE)) 422 while (dfl.internal.utf.peekMessage(&msg._winMsg, HWND.init, 0, 0, 423 PM_REMOVE)) { 424 gotMessage(msg); 425 426 if (msg.msg == WM_QUIT) { 427 threadFlags = threadFlags | TF.QUIT; 428 break still_running; 429 } 430 431 if (threadFlags & (TF.QUIT | TF.STOP_RUNNING)) { 432 break still_running; 433 } 434 } 435 436 whileIdle(); 437 } 438 439 // Stopped running. 440 threadExit(typeid(Application), EventArgs.empty); 441 threadFlags = threadFlags & ~(TF.RUNNING | TF.STOP_RUNNING); 442 return; 443 } 444 catch (DThrowable e) { 445 onThreadException(e); 446 } 447 } 448 } 449 finally { 450 threadFlags = threadFlags & ~(TF.RUNNING | TF.STOP_RUNNING); 451 452 ApplicationContext tctx; 453 tctx = ctx; 454 ctx = null; 455 456 version (CUSTOM_MSG_HOOK) 457 UnhookWindowsHookEx(msghook); 458 459 tctx.threadExit.removeHandler(&threadJustExited); 460 } 461 } 462 463 // Makes the form -mainForm- visible. 464 void run(Form mainForm, void delegate() whileIdle) { 465 ApplicationContext appcon = new ApplicationContext(mainForm); 466 //mainForm.show(); // Interferes with -running-. 467 run(appcon, whileIdle); 468 } 469 470 void run(Form mainForm) { 471 ApplicationContext appcon = new ApplicationContext(mainForm); 472 //mainForm.show(); // Interferes with -running-. 473 run(appcon); 474 } 475 476 void exit() { 477 PostQuitMessage(0); 478 } 479 480 /// Exit the thread's message loop and return from run. 481 // Actually only stops the current run() loop. 482 void exitThread() { 483 threadFlags = threadFlags | TF.STOP_RUNNING; 484 } 485 486 // Will be null if not in a successful Application.run. 487 package @property ApplicationContext context() nothrow { 488 return ctx; 489 } 490 491 HINSTANCE getInstance() { 492 if (!hinst) { 493 _initInstance(); 494 } 495 return hinst; 496 } 497 498 void setInstance(HINSTANCE inst) { 499 if (hinst) { 500 if (inst != hinst) { 501 throw new DflException("Instance is already set"); 502 } 503 return; 504 } 505 506 if (inst) { 507 _initInstance(inst); 508 } else { 509 _initInstance(); // ? 510 } 511 } 512 513 // ApartmentState oleRequired() ... 514 515 private static class ErrForm : Form { 516 protected override void onLoad(EventArgs ea) { 517 okBtn.focus(); 518 } 519 520 protected override void onClosing(CancelEventArgs cea) { 521 cea.cancel = !errdone; 522 } 523 524 enum PADDING = 10; 525 526 void onOkClick(Object sender, EventArgs ea) { 527 errdone = true; 528 ctnu = true; 529 //close(); 530 dispose(); 531 } 532 533 void onCancelClick(Object sender, EventArgs ea) { 534 errdone = true; 535 ctnu = false; 536 //close(); 537 dispose(); 538 } 539 540 this(Dstring errmsg) { 541 text = "Error"; 542 clientSize = Size(340, 150); 543 startPosition = FormStartPosition.CENTER_SCREEN; 544 formBorderStyle = FormBorderStyle.FIXED_DIALOG; 545 minimizeBox = false; 546 maximizeBox = false; 547 controlBox = false; 548 549 Label label; 550 with (label = new Label) { 551 bounds = Rect(PADDING, PADDING, this.clientSize.width - PADDING * 2, 40); 552 label.text 553 = "An application exception has occured. Click Continue to allow " ~ "the application to ignore this error and attempt to continue."; 554 parent = this; 555 } 556 557 with (errBox = new TextBox) { 558 text = errmsg; 559 bounds = Rect(PADDING, 40 + PADDING, this.clientSize.width - PADDING * 2, 560 50); 561 errBox.backColor = this.backColor; 562 readOnly = true; 563 multiline = true; 564 parent = this; 565 } 566 567 with (okBtn = new Button) { 568 width = 100; 569 location = Point(this.clientSize.width - width - PADDING - width - PADDING, 570 this.clientSize.height - height - PADDING); 571 text = "&Continue"; 572 parent = this; 573 click ~= &onOkClick; 574 } 575 acceptButton = okBtn; 576 577 with (new Button) { 578 width = 100; 579 location = Point(this.clientSize.width - width - PADDING, 580 this.clientSize.height - height - PADDING); 581 text = "&Quit"; 582 parent = this; 583 click ~= &onCancelClick; 584 } 585 586 autoScale = true; 587 } 588 589 /+ 590 private int inThread2() { 591 try { 592 // Create in this thread so that it owns the handle. 593 assert(!isHandleCreated); 594 show(); 595 SetForegroundWindow(handle); 596 597 MSG msg; 598 assert(isHandleCreated); 599 // Using the unicode stuf here messes up the redrawing for some reason. 600 while(GetMessageA(&msg, HWND.init, 0, 0)) // TODO: unicode ? 601 //while(dfl.internal.utf.getMessage(&msg, HWND.init, 0, 0)) 602 { 603 if(!IsDialogMessageA(handle, &msg)) 604 //if(!dfl.internal.utf.isDialogMessage(handle, &msg)) 605 { 606 TranslateMessage(&msg); 607 DispatchMessageA(&msg); 608 //dfl.internal.utf.dispatchMessage(&msg); 609 } 610 611 if(!isHandleCreated) { 612 break; 613 } 614 } 615 } 616 finally { 617 dispose(); 618 assert(!isHandleCreated); 619 620 thread1 = null; 621 } 622 623 return 0; 624 } 625 626 private void tinThread2() { 627 inThread2(); 628 } 629 630 631 private Thread thread1; 632 633 bool doContinue() { 634 assert(!isHandleCreated); 635 636 // Need to use a separate thread so that all the main thread's messages 637 // will be there still when the exception is recovered from. 638 // This is very important for some messages, such as socket events. 639 thread1 = Thread.getThis(); // Problems with DMD 2.x 640 Thread thd; 641 thd = new Thread(&inThread2); 642 thd.start(); 643 do { 644 Sleep(200); 645 } while(thread1); 646 647 return ctnu; 648 } 649 +/ 650 651 bool doContinue() { 652 assert(!isHandleCreated); 653 654 show(); 655 656 Message msg; 657 for (;;) { 658 WaitMessage(); 659 if (PeekMessageA(&msg._winMsg, handle, 0, 0, PM_REMOVE | PM_NOYIELD)) { 660 /+ 661 //if(!IsDialogMessageA(handle, &msg._winMsg)) // Back to the old problems. 662 { 663 TranslateMessage(&msg._winMsg); 664 DispatchMessageA(&msg._winMsg); 665 } 666 +/ 667 gotMessage(msg); 668 } 669 670 if (!isHandleCreated) { 671 break; 672 } 673 } 674 675 return ctnu; 676 } 677 678 override Dstring toString() { 679 return errBox.text; 680 } 681 682 private: 683 bool errdone = false; 684 bool ctnu = false; 685 Button okBtn; 686 TextBox errBox; 687 } 688 689 bool showDefaultExceptionDialog(Object e) { 690 /+ 691 if(IDYES == MessageBoxA(null, 692 "An application exception has occured. Click Yes to allow\r\n" 693 "the application to ignore this error and attempt to continue.\r\n" 694 "Click No to quit the application.\r\n\r\n"~ 695 e.toString(), 696 null, MB_ICONWARNING | MB_TASKMODAL | MB_YESNO)) { 697 except = false; 698 return; 699 } 700 +/ 701 702 //try 703 { 704 if ((new ErrForm(getObjectString(e))).doContinue()) { 705 return true; 706 } 707 } 708 /+ 709 catch { 710 MessageBoxA(null, "Error displaying error message", "DFL", MB_ICONERROR | MB_TASKMODAL); 711 } 712 +/ 713 714 return false; 715 } 716 717 void onThreadException(DThrowable e) nothrow { 718 try { 719 static bool except = false; 720 721 version (WINDOWS_HUNG_WORKAROUND) { 722 version (WINDOWS_HUNG_WORKAROUND_NO_IGNORE) { 723 } else { 724 if (cast(WindowsHungDflException) e) { 725 return; 726 } 727 } 728 } 729 730 if (except) { 731 cprintf("Error: %.*s\n", cast(int) getObjectString(e).length, getObjectString(e).ptr); 732 733 abort(); 734 return; 735 } 736 737 except = true; 738 //if(threadException.handlers.length) 739 if (threadException.hasHandlers) { 740 threadException(typeid(Application), new ThreadExceptionEventArgs(e)); 741 except = false; 742 return; 743 } else { 744 // No thread exception handlers, display a dialog. 745 if (showDefaultExceptionDialog(e)) { 746 except = false; 747 return; 748 } 749 } 750 //except = false; 751 752 //throw e; 753 cprintf("Error: %.*s\n", cast(int) getObjectString(e).length, getObjectString(e).ptr); 754 //exitThread(); 755 Environment.exit(EXIT_FAILURE); 756 } 757 catch (DThrowable e) { 758 } 759 } 760 761 Event!(Object, EventArgs) idle; // Finished processing and is now idle. 762 763 Event!(Object, ThreadExceptionEventArgs) threadException; 764 765 Event!(Object, EventArgs) threadExit; 766 767 void addHotkey(Keys k, void delegate(Object sender, KeyEventArgs ea) dg) { 768 if (auto pkid = k in hotkeyId) { 769 immutable kid = *pkid; 770 hotkeyHandler[kid] ~= dg; 771 } else { 772 int kid = 0; 773 foreach (aak, aav; hotkeyHandler) { 774 if (!aav.hasHandlers) { 775 kid = aak; 776 break; 777 } 778 ++kid; 779 } 780 immutable mod = (k & Keys.MODIFIERS) >> 16, keycode = k & Keys.KEY_CODE; 781 if (RegisterHotKey(null, kid, mod, keycode)) { 782 hotkeyId[k] = kid; 783 if (auto h = kid in hotkeyHandler) { 784 *h ~= dg; 785 } else { 786 typeof(hotkeyHandler[kid]) e; 787 e ~= dg; 788 hotkeyHandler[kid] = e; 789 } 790 } else { 791 throw new DflException("Hotkey cannot resistered."); 792 } 793 } 794 } 795 796 void removeHotkey(Keys k, void delegate(Object sender, KeyEventArgs ea) dg) { 797 if (auto pkid = k in hotkeyId) { 798 immutable kid = *pkid; 799 hotkeyHandler[kid].removeHandler(dg); 800 if (!hotkeyHandler[kid].hasHandlers) { 801 if (UnregisterHotKey(null, kid) == 0) { 802 throw new DflException("Hotkey cannot unresistered."); 803 } 804 hotkeyHandler.remove(kid); 805 hotkeyId.remove(k); 806 } 807 } 808 } 809 810 void removeHotkey(Keys k) { 811 if (auto pkid = k in hotkeyId) { 812 immutable kid = *pkid; 813 foreach (hnd; hotkeyHandler[kid]) { 814 hotkeyHandler[kid].removeHandler(hnd); 815 } 816 assert(!hotkeyHandler[kid].hasHandlers); 817 if (UnregisterHotKey(null, kid) == 0) { 818 throw new DflException("Hotkey cannot unresistered."); 819 } 820 hotkeyHandler.remove(kid); 821 hotkeyId.remove(k); 822 } 823 } 824 825 struct HotkeyRegister { 826 static: 827 828 alias Handler = void delegate(Object c, KeyEventArgs e); 829 830 void addHandler(Keys k, Handler dg) { 831 addHotkey(k, dg); 832 } 833 834 struct IndexedCatAssigner { 835 Keys k; 836 837 void opCatAssign(Handler dg) { 838 addHandler(k, dg); 839 } 840 } 841 842 IndexedCatAssigner opIndex(Keys k) { 843 return IndexedCatAssigner(k); 844 } 845 846 void removeHandler(Keys k, Handler dg) { 847 removeHotkey(k, dg); 848 } 849 850 void removeHandler(Keys k) { 851 removeHotkey(k); 852 } 853 } 854 855 /// helper 856 HotkeyRegister hotkeys; 857 858 static ~this() { 859 foreach (key; hotkeyId.keys) { 860 removeHotkey(key); 861 } 862 hotkeyId = null; 863 } 864 865 // Returns null if not found. 866 package Control lookupHwnd(HWND hwnd) nothrow { 867 //if(hwnd in controls) 868 // return controls[hwnd]; 869 auto pc = hwnd in controls; 870 if (pc) { 871 return *pc; 872 } 873 return null; 874 } 875 876 // Also makes a great zombie. 877 package void removeHwnd(HWND hwnd) { 878 //delete controls[hwnd]; 879 controls.remove(hwnd); 880 } 881 882 version (DFL_NO_ZOMBIE_FORM) { 883 } else { 884 package enum ZOMBIE_PROP = "DFL_Zombie"; 885 886 // Doesn't do any good since the child controls still reference this control. 887 package void zombieHwnd(Control c) 888 in { 889 assert(c !is null); 890 assert(c.isHandleCreated); 891 assert(lookupHwnd(c.handle)); 892 } 893 body { 894 SetPropA(c.handle, ZOMBIE_PROP.ptr, cast(HANDLE) cast(void*) c); 895 removeHwnd(c.handle); 896 } 897 898 package void unzombieHwnd(Control c) 899 in { 900 assert(c !is null); 901 assert(c.isHandleCreated); 902 assert(!lookupHwnd(c.handle)); 903 } 904 body { 905 RemovePropA(c.handle, ZOMBIE_PROP.ptr); 906 controls[c.handle] = c; 907 } 908 909 // Doesn't need to be a zombie. 910 package void zombieKill(Control c) 911 in { 912 assert(c !is null); 913 } 914 body { 915 if (c.isHandleCreated) { 916 RemovePropA(c.handle, ZOMBIE_PROP.ptr); 917 } 918 } 919 } 920 921 version (DFL_NO_MENUS) { 922 } else { 923 // Returns its new unique menu ID. 924 package int addMenuItem(MenuItem menu) { 925 if (nmenus == END_MENU_ID - FIRST_MENU_ID) { 926 throw new DflException("Out of menus"); 927 } 928 929 typeof(menus) tempmenus; 930 931 // TODO: sort menu IDs in 'menus' so that looking for free ID is much faster. 932 933 prevMenuID++; 934 if (prevMenuID >= END_MENU_ID || prevMenuID <= FIRST_MENU_ID) { 935 prevMenuID = FIRST_MENU_ID; 936 previdloop: for (;;) { 937 for (size_t iw; iw != nmenus; iw++) { 938 MenuItem mi; 939 mi = cast(MenuItem) menus[iw]; 940 if (mi) { 941 if (prevMenuID == mi._menuID) { 942 prevMenuID++; 943 continue previdloop; 944 } 945 } 946 } 947 break; 948 } 949 } 950 tempmenus = cast(Menu*) realloc(menus, Menu.sizeof * (nmenus + 1)); 951 if (!tempmenus) { 952 //throw new OutOfMemory; 953 throw new DflException("Out of memory"); 954 } 955 menus = tempmenus; 956 957 menus[nmenus++] = menu; 958 959 return prevMenuID; 960 } 961 962 package void addContextMenu(ContextMenu menu) { 963 if (nmenus == END_MENU_ID - FIRST_MENU_ID) { 964 throw new DflException("Out of menus"); 965 } 966 967 typeof(menus) tempmenus; 968 int idx; 969 970 idx = nmenus; 971 nmenus++; 972 tempmenus = cast(Menu*) realloc(menus, Menu.sizeof * nmenus); 973 if (!tempmenus) { 974 nmenus--; 975 //throw new OutOfMemory; 976 throw new DflException("Out of memory"); 977 } 978 menus = tempmenus; 979 980 menus[idx] = menu; 981 } 982 983 package void removeMenu(Menu menu) { 984 uint idx; 985 986 for (idx = 0; idx != nmenus; idx++) { 987 if (menus[idx] is menu) { 988 goto found; 989 } 990 } 991 return; 992 993 found: 994 if (nmenus == 1) { 995 free(menus); 996 menus = null; 997 nmenus--; 998 } else { 999 if (idx != nmenus - 1) { 1000 menus[idx] = menus[nmenus - 1]; // Move last one in its place 1001 } 1002 1003 nmenus--; 1004 menus = cast(Menu*) realloc(menus, Menu.sizeof * nmenus); 1005 assert(menus != null); // Memory shrink shouldn't be a problem. 1006 } 1007 } 1008 1009 package MenuItem lookupMenuID(int menuID) { 1010 uint idx; 1011 MenuItem mi; 1012 1013 for (idx = 0; idx != nmenus; idx++) { 1014 mi = cast(MenuItem) menus[idx]; 1015 if (mi && mi._menuID == menuID) { 1016 return mi; 1017 } 1018 } 1019 return null; 1020 } 1021 1022 package Menu lookupMenu(HMENU hmenu) { 1023 uint idx; 1024 1025 for (idx = 0; idx != nmenus; idx++) { 1026 if (menus[idx].handle == hmenu) { 1027 return menus[idx]; 1028 } 1029 } 1030 return null; 1031 } 1032 } 1033 1034 package void creatingControl(Control ctrl) nothrow { 1035 TlsSetValue(tlsControl, cast(Control*) ctrl); 1036 } 1037 1038 version (DFL_NO_RESOURCES) { 1039 } else { 1040 1041 @property Resources resources() { 1042 static Resources rc = null; 1043 1044 if (!rc) { 1045 synchronized { 1046 if (!rc) { 1047 rc = new Resources(getInstance()); 1048 } 1049 } 1050 } 1051 return rc; 1052 } 1053 } 1054 1055 private UINT gctimer = 0; 1056 private DWORD gcinfo = 1; 1057 1058 @property void autoCollect(bool byes) { 1059 if (byes) { 1060 if (!autoCollect) { 1061 gcinfo = 1; 1062 } 1063 } else { 1064 if (autoCollect) { 1065 gcinfo = 0; 1066 KillTimer(HWND.init, gctimer); 1067 gctimer = 0; 1068 } 1069 } 1070 } 1071 1072 @property bool autoCollect() nothrow { 1073 return gcinfo > 0; 1074 } 1075 1076 package void _waitMsg() { 1077 if (threadFlags & (TF.STOP_RUNNING | TF.QUIT)) { 1078 return; 1079 } 1080 1081 idle(typeid(Application), EventArgs.empty); 1082 WaitMessage(); 1083 } 1084 1085 package deprecated alias waitMsg = _waitMsg; 1086 1087 // Because waiting for an event enters an idle state, 1088 // this function fires the -idle- event. 1089 void waitForEvent() { 1090 if (!autoCollect) { 1091 _waitMsg(); 1092 return; 1093 } 1094 1095 if (1 == gcinfo) { 1096 gcinfo = gcinfo.max; 1097 assert(!gctimer); 1098 gctimer = SetTimer(HWND.init, 0, 200, &_gcTimeout); 1099 } 1100 1101 _waitMsg(); 1102 1103 if (GetTickCount() > gcinfo) { 1104 gcinfo = 1; 1105 } 1106 } 1107 1108 version (DFL_NO_COMPAT) 1109 package enum _compat = DflCompat.NONE; 1110 else { 1111 package DflCompat _compat = DflCompat.NONE; 1112 } 1113 1114 deprecated void setCompat(DflCompat dflcompat) { 1115 version (DFL_NO_COMPAT) { 1116 assert(0, "Compatibility disabled"); // version=DFL_NO_COMPAT 1117 } else { 1118 if (messageLoop) { 1119 assert(0, "setCompat"); // Called too late, must enable compatibility sooner. 1120 } 1121 1122 _compat |= dflcompat; 1123 } 1124 } 1125 1126 private static size_t _doref(void* p, int by) { 1127 assert(1 == by || -1 == by); 1128 1129 size_t result; 1130 1131 synchronized { 1132 auto pref = p in _refs; 1133 if (pref) { 1134 size_t count; 1135 count = *pref; 1136 1137 assert(count || -1 != by); 1138 1139 if (-1 == by) { 1140 count--; 1141 } else { 1142 count++; 1143 } 1144 1145 if (!count) { 1146 result = 0; 1147 _refs.remove(p); 1148 } else { 1149 result = count; 1150 _refs[p] = count; 1151 } 1152 } else if (1 == by) { 1153 _refs[p] = 1; 1154 result = 1; 1155 } 1156 } 1157 1158 return result; 1159 } 1160 1161 package size_t refCountInc(void* p) { 1162 return _doref(p, 1); 1163 } 1164 1165 // Returns the new ref count. 1166 package size_t refCountDec(void* p) { 1167 return _doref(p, -1); 1168 } 1169 1170 package void ppin(void* p) { 1171 dfl.internal.dlib.gcPin(p); 1172 } 1173 1174 package void punpin(void* p) { 1175 dfl.internal.dlib.gcUnpin(p); 1176 } 1177 1178 private: 1179 static: 1180 size_t[void* ] _refs; 1181 IMessageFilter[] filters; 1182 DWORD tlsThreadFlags; 1183 DWORD tlsControl; 1184 DWORD tlsFilter; // IMessageFilter[]*. 1185 version (CUSTOM_MSG_HOOK) DWORD tlsHook; // HHOOK. 1186 Control[HWND] controls; 1187 HINSTANCE hinst; 1188 ApplicationContext ctx = null; 1189 int[Keys] hotkeyId; 1190 Event!(Object, KeyEventArgs)[int] hotkeyHandler; 1191 1192 version (DFL_NO_MENUS) { 1193 } else { 1194 // Menus. 1195 enum short FIRST_MENU_ID = 200; 1196 enum short END_MENU_ID = 10000; 1197 1198 // Controls. 1199 enum ushort FIRST_CTRL_ID = END_MENU_ID + 1; 1200 enum ushort LAST_CTRL_ID = 65500; 1201 1202 ushort prevMenuID = FIRST_MENU_ID; 1203 // malloc() is needed so the menus can be garbage collected. 1204 uint nmenus = 0; // Number of -menus-. 1205 Menu* menus = null; // WARNING: malloc()'d memory! 1206 1207 // Destroy all menu handles at program exit because Windows will not 1208 // unless it is assigned to a window. 1209 // Note that this is probably just a 16bit issue, but it still appeared in the 32bit docs. 1210 private void sdtorFreeAllMenus() { 1211 foreach (Menu m; menus[0 .. nmenus]) { 1212 DestroyMenu(m.handle); 1213 } 1214 nmenus = 0; 1215 free(menus); 1216 menus = null; 1217 } 1218 } 1219 1220 private struct TlsFilterValue { 1221 IMessageFilter[] filters; 1222 } 1223 1224 /+ 1225 @property void filters(IMessageFilter[] filters) { 1226 // The TlsFilterValue is being garbage collected! 1227 1228 TlsFilterValue* val = cast(TlsFilterValue*)TlsGetValue(tlsFilter); 1229 if(!val) { 1230 val = new TlsFilterValue; 1231 } 1232 val.filters = filters; 1233 TlsSetValue(tlsFilter, cast(LPVOID)val); 1234 } 1235 1236 1237 @property IMessageFilter[] filters() nothrow { 1238 TlsFilterValue* val = cast(TlsFilterValue*)TlsGetValue(tlsFilter); 1239 if(!val) { 1240 return null; 1241 } 1242 return val.filters; 1243 } 1244 +/ 1245 1246 version (CUSTOM_MSG_HOOK) { 1247 @property void msghook(HHOOK hhook) { 1248 TlsSetValue(tlsHook, cast(LPVOID) hhook); 1249 } 1250 1251 @property HHOOK msghook() nothrow { 1252 return cast(HHOOK) TlsGetValue(tlsHook); 1253 } 1254 } 1255 1256 Control getCreatingControl() nothrow { 1257 return cast(Control) cast(Control*) TlsGetValue(tlsControl); 1258 } 1259 1260 // Thread flags. 1261 enum TF : DWORD { 1262 RUNNING = 1, // Application.run is in affect. 1263 STOP_RUNNING = 2, 1264 QUIT = 4, // Received WM_QUIT. 1265 } 1266 1267 @property TF threadFlags() nothrow { 1268 return cast(TF) cast(DWORD) TlsGetValue(tlsThreadFlags); 1269 } 1270 1271 @property void threadFlags(TF flags) { 1272 if (!TlsSetValue(tlsThreadFlags, cast(LPVOID) cast(DWORD) flags)) { 1273 assert(0); 1274 } 1275 } 1276 1277 void gotMessage(ref Message msg) { 1278 //debug(SHOW_MESSAGE_INFO) 1279 // showMessageInfo(msg); 1280 void handleHotkey() { 1281 immutable kid = cast(int) msg.wParam, 1282 mod = cast(uint)(msg.lParam & 0x0000ffff), 1283 keycode = cast(uint)((msg.lParam & 0xffff0000) >> 16); 1284 assert(kid < hotkeyHandler.length); 1285 hotkeyHandler[kid](typeid(Application), new KeyEventArgs(cast(Keys)((mod << 16) | keycode))); 1286 } 1287 // Don't bother with this extra stuff if there aren't any filters. 1288 if (filters.length) { 1289 try { 1290 // Keep a local reference so that handlers 1291 // may be added and removed during filtering. 1292 IMessageFilter[] local = filters; 1293 1294 foreach (IMessageFilter mf; local) { 1295 // Returning true prevents dispatching. 1296 if (mf.preFilterMessage(msg)) { 1297 Control ctrl; 1298 ctrl = lookupHwnd(msg.hWnd); 1299 if (ctrl) { 1300 ctrl.mustWndProc(msg); 1301 } else if (msg.msg == WM_HOTKEY) { 1302 handleHotkey(); 1303 } 1304 return; 1305 } 1306 } 1307 } 1308 catch (DThrowable o) { 1309 Control ctrl; 1310 ctrl = lookupHwnd(msg.hWnd); 1311 if (ctrl) { 1312 ctrl.mustWndProc(msg); 1313 } 1314 throw o; 1315 } 1316 } 1317 if (msg.msg == WM_HOTKEY) { 1318 handleHotkey(); 1319 } 1320 TranslateMessage(&msg._winMsg); 1321 //DispatchMessageA(&msg._winMsg); 1322 dfl.internal.utf.dispatchMessage(&msg._winMsg); 1323 } 1324 } 1325 1326 package: 1327 1328 extern (Windows) void _gcTimeout(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) nothrow { 1329 KillTimer(hwnd, Application.gctimer); 1330 Application.gctimer = 0; 1331 1332 //cprintf("Auto-collecting\n"); 1333 dfl.internal.dlib.gcFullCollect(); 1334 1335 Application.gcinfo = GetTickCount() + 4000; 1336 } 1337 1338 // Note: phobos-only. 1339 debug (SHOW_MESSAGE_INFO) { 1340 private import std.stdio, std..string; 1341 1342 void showMessageInfo(ref Message m) { 1343 void writeWm(Dstring wmName) { 1344 writef("Message %s=%d(0x%X)\n", wmName, m.msg, m.msg); 1345 } 1346 1347 switch (m.msg) { 1348 case WM_NULL: 1349 writeWm("WM_NULL"); 1350 break; 1351 case WM_CREATE: 1352 writeWm("WM_CREATE"); 1353 break; 1354 case WM_DESTROY: 1355 writeWm("WM_DESTROY"); 1356 break; 1357 case WM_MOVE: 1358 writeWm("WM_MOVE"); 1359 break; 1360 case WM_SIZE: 1361 writeWm("WM_SIZE"); 1362 break; 1363 case WM_ACTIVATE: 1364 writeWm("WM_ACTIVATE"); 1365 break; 1366 case WM_SETFOCUS: 1367 writeWm("WM_SETFOCUS"); 1368 break; 1369 case WM_KILLFOCUS: 1370 writeWm("WM_KILLFOCUS"); 1371 break; 1372 case WM_ENABLE: 1373 writeWm("WM_ENABLE"); 1374 break; 1375 case WM_SETREDRAW: 1376 writeWm("WM_SETREDRAW"); 1377 break; 1378 case WM_SETTEXT: 1379 writeWm("WM_SETTEXT"); 1380 break; 1381 case WM_GETTEXT: 1382 writeWm("WM_GETTEXT"); 1383 break; 1384 case WM_GETTEXTLENGTH: 1385 writeWm("WM_GETTEXTLENGTH"); 1386 break; 1387 case WM_PAINT: 1388 writeWm("WM_PAINT"); 1389 break; 1390 case WM_CLOSE: 1391 writeWm("WM_CLOSE"); 1392 break; 1393 case WM_QUERYENDSESSION: 1394 writeWm("WM_QUERYENDSESSION"); 1395 break; 1396 case WM_QUIT: 1397 writeWm("WM_QUIT"); 1398 break; 1399 case WM_QUERYOPEN: 1400 writeWm("WM_QUERYOPEN"); 1401 break; 1402 case WM_ERASEBKGND: 1403 writeWm("WM_ERASEBKGND"); 1404 break; 1405 case WM_SYSCOLORCHANGE: 1406 writeWm("WM_SYSCOLORCHANGE"); 1407 break; 1408 case WM_ENDSESSION: 1409 writeWm("WM_ENDSESSION"); 1410 break; 1411 case WM_SHOWWINDOW: 1412 writeWm("WM_SHOWWINDOW"); 1413 break; 1414 //case WM_WININICHANGE: writeWm("WM_WININICHANGE"); break; 1415 case WM_SETTINGCHANGE: 1416 writeWm("WM_SETTINGCHANGE"); 1417 break; 1418 case WM_DEVMODECHANGE: 1419 writeWm("WM_DEVMODECHANGE"); 1420 break; 1421 case WM_ACTIVATEAPP: 1422 writeWm("WM_ACTIVATEAPP"); 1423 break; 1424 case WM_FONTCHANGE: 1425 writeWm("WM_FONTCHANGE"); 1426 break; 1427 case WM_TIMECHANGE: 1428 writeWm("WM_TIMECHANGE"); 1429 break; 1430 case WM_CANCELMODE: 1431 writeWm("WM_CANCELMODE"); 1432 break; 1433 case WM_SETCURSOR: 1434 writeWm("WM_SETCURSOR"); 1435 break; 1436 case WM_MOUSEACTIVATE: 1437 writeWm("WM_MOUSEACTIVATE"); 1438 break; 1439 case WM_CHILDACTIVATE: 1440 writeWm("WM_CHILDACTIVATE"); 1441 break; 1442 case WM_QUEUESYNC: 1443 writeWm("WM_QUEUESYNC"); 1444 break; 1445 case WM_GETMINMAXINFO: 1446 writeWm("WM_GETMINMAXINFO"); 1447 break; 1448 case WM_NOTIFY: 1449 writeWm("WM_NOTIFY"); 1450 break; 1451 case WM_INPUTLANGCHANGEREQUEST: 1452 writeWm("WM_INPUTLANGCHANGEREQUEST"); 1453 break; 1454 case WM_INPUTLANGCHANGE: 1455 writeWm("WM_INPUTLANGCHANGE"); 1456 break; 1457 case WM_TCARD: 1458 writeWm("WM_TCARD"); 1459 break; 1460 case WM_HELP: 1461 writeWm("WM_HELP"); 1462 break; 1463 case WM_USERCHANGED: 1464 writeWm("WM_USERCHANGED"); 1465 break; 1466 case WM_NOTIFYFORMAT: 1467 writeWm("WM_NOTIFYFORMAT"); 1468 break; 1469 case WM_CONTEXTMENU: 1470 writeWm("WM_CONTEXTMENU"); 1471 break; 1472 case WM_STYLECHANGING: 1473 writeWm("WM_STYLECHANGING"); 1474 break; 1475 case WM_STYLECHANGED: 1476 writeWm("WM_STYLECHANGED"); 1477 break; 1478 case WM_DISPLAYCHANGE: 1479 writeWm("WM_DISPLAYCHANGE"); 1480 break; 1481 case WM_GETICON: 1482 writeWm("WM_GETICON"); 1483 break; 1484 case WM_SETICON: 1485 writeWm("WM_SETICON"); 1486 break; 1487 case WM_NCCREATE: 1488 writeWm("WM_NCCREATE"); 1489 break; 1490 case WM_NCDESTROY: 1491 writeWm("WM_NCDESTROY"); 1492 break; 1493 case WM_NCCALCSIZE: 1494 writeWm("WM_NCCALCSIZE"); 1495 break; 1496 case WM_NCHITTEST: 1497 writeWm("WM_NCHITTEST"); 1498 break; 1499 case WM_NCPAINT: 1500 writeWm("WM_NCPAINT"); 1501 break; 1502 case WM_NCACTIVATE: 1503 writeWm("WM_NCACTIVATE"); 1504 break; 1505 case WM_GETDLGCODE: 1506 writeWm("WM_GETDLGCODE"); 1507 break; 1508 case WM_NCMOUSEMOVE: 1509 writeWm("WM_NCMOUSEMOVE"); 1510 break; 1511 case WM_NCLBUTTONDOWN: 1512 writeWm("WM_NCLBUTTONDOWN"); 1513 break; 1514 case WM_NCLBUTTONUP: 1515 writeWm("WM_NCLBUTTONUP"); 1516 break; 1517 case WM_NCLBUTTONDBLCLK: 1518 writeWm("WM_NCLBUTTONDBLCLK"); 1519 break; 1520 case WM_NCRBUTTONDOWN: 1521 writeWm("WM_NCRBUTTONDOWN"); 1522 break; 1523 case WM_NCRBUTTONUP: 1524 writeWm("WM_NCRBUTTONUP"); 1525 break; 1526 case WM_NCRBUTTONDBLCLK: 1527 writeWm("WM_NCRBUTTONDBLCLK"); 1528 break; 1529 case WM_NCMBUTTONDOWN: 1530 writeWm("WM_NCMBUTTONDOWN"); 1531 break; 1532 case WM_NCMBUTTONUP: 1533 writeWm("WM_NCMBUTTONUP"); 1534 break; 1535 case WM_NCMBUTTONDBLCLK: 1536 writeWm("WM_NCMBUTTONDBLCLK"); 1537 break; 1538 case WM_KEYDOWN: 1539 writeWm("WM_KEYDOWN"); 1540 break; 1541 case WM_KEYUP: 1542 writeWm("WM_KEYUP"); 1543 break; 1544 case WM_CHAR: 1545 writeWm("WM_CHAR"); 1546 break; 1547 case WM_DEADCHAR: 1548 writeWm("WM_DEADCHAR"); 1549 break; 1550 case WM_SYSKEYDOWN: 1551 writeWm("WM_SYSKEYDOWN"); 1552 break; 1553 case WM_SYSKEYUP: 1554 writeWm("WM_SYSKEYUP"); 1555 break; 1556 case WM_SYSCHAR: 1557 writeWm("WM_SYSCHAR"); 1558 break; 1559 case WM_SYSDEADCHAR: 1560 writeWm("WM_SYSDEADCHAR"); 1561 break; 1562 case WM_IME_STARTCOMPOSITION: 1563 writeWm("WM_IME_STARTCOMPOSITION"); 1564 break; 1565 case WM_IME_ENDCOMPOSITION: 1566 writeWm("WM_IME_ENDCOMPOSITION"); 1567 break; 1568 case WM_IME_COMPOSITION: 1569 writeWm("WM_IME_COMPOSITION"); 1570 break; 1571 case WM_INITDIALOG: 1572 writeWm("WM_INITDIALOG"); 1573 break; 1574 case WM_COMMAND: 1575 writeWm("WM_COMMAND"); 1576 break; 1577 case WM_SYSCOMMAND: 1578 writeWm("WM_SYSCOMMAND"); 1579 break; 1580 case WM_TIMER: 1581 writeWm("WM_TIMER"); 1582 break; 1583 case WM_HSCROLL: 1584 writeWm("WM_HSCROLL"); 1585 break; 1586 case WM_VSCROLL: 1587 writeWm("WM_VSCROLL"); 1588 break; 1589 case WM_INITMENU: 1590 writeWm("WM_INITMENU"); 1591 break; 1592 case WM_INITMENUPOPUP: 1593 writeWm("WM_INITMENUPOPUP"); 1594 break; 1595 case WM_MENUSELECT: 1596 writeWm("WM_MENUSELECT"); 1597 break; 1598 case WM_MENUCHAR: 1599 writeWm("WM_MENUCHAR"); 1600 break; 1601 case WM_ENTERIDLE: 1602 writeWm("WM_ENTERIDLE"); 1603 break; 1604 case WM_CTLCOLORMSGBOX: 1605 writeWm("WM_CTLCOLORMSGBOX"); 1606 break; 1607 case WM_CTLCOLOREDIT: 1608 writeWm("WM_CTLCOLOREDIT"); 1609 break; 1610 case WM_CTLCOLORLISTBOX: 1611 writeWm("WM_CTLCOLORLISTBOX"); 1612 break; 1613 case WM_CTLCOLORBTN: 1614 writeWm("WM_CTLCOLORBTN"); 1615 break; 1616 case WM_CTLCOLORDLG: 1617 writeWm("WM_CTLCOLORDLG"); 1618 break; 1619 case WM_CTLCOLORSCROLLBAR: 1620 writeWm("WM_CTLCOLORSCROLLBAR"); 1621 break; 1622 case WM_CTLCOLORSTATIC: 1623 writeWm("WM_CTLCOLORSTATIC"); 1624 break; 1625 case WM_MOUSEMOVE: 1626 writeWm("WM_MOUSEMOVE"); 1627 break; 1628 case WM_LBUTTONDOWN: 1629 writeWm("WM_LBUTTONDOWN"); 1630 break; 1631 case WM_LBUTTONUP: 1632 writeWm("WM_LBUTTONUP"); 1633 break; 1634 case WM_LBUTTONDBLCLK: 1635 writeWm("WM_LBUTTONDBLCLK"); 1636 break; 1637 case WM_RBUTTONDOWN: 1638 writeWm("WM_RBUTTONDOWN"); 1639 break; 1640 case WM_RBUTTONUP: 1641 writeWm("WM_RBUTTONUP"); 1642 break; 1643 case WM_RBUTTONDBLCLK: 1644 writeWm("WM_RBUTTONDBLCLK"); 1645 break; 1646 case WM_MBUTTONDOWN: 1647 writeWm("WM_MBUTTONDOWN"); 1648 break; 1649 case WM_MBUTTONUP: 1650 writeWm("WM_MBUTTONUP"); 1651 break; 1652 case WM_MBUTTONDBLCLK: 1653 writeWm("WM_MBUTTONDBLCLK"); 1654 break; 1655 case WM_PARENTNOTIFY: 1656 writeWm("WM_PARENTNOTIFY"); 1657 break; 1658 case WM_ENTERMENULOOP: 1659 writeWm("WM_ENTERMENULOOP"); 1660 break; 1661 case WM_EXITMENULOOP: 1662 writeWm("WM_EXITMENULOOP"); 1663 break; 1664 case WM_NEXTMENU: 1665 writeWm("WM_NEXTMENU"); 1666 break; 1667 case WM_SETFONT: 1668 writeWm("WM_SETFONT"); 1669 break; 1670 case WM_GETFONT: 1671 writeWm("WM_GETFONT"); 1672 break; 1673 case WM_USER: 1674 writeWm("WM_USER"); 1675 break; 1676 case WM_NEXTDLGCTL: 1677 writeWm("WM_NEXTDLGCTL"); 1678 break; 1679 case WM_CAPTURECHANGED: 1680 writeWm("WM_CAPTURECHANGED"); 1681 break; 1682 case WM_WINDOWPOSCHANGING: 1683 writeWm("WM_WINDOWPOSCHANGING"); 1684 break; 1685 case WM_WINDOWPOSCHANGED: 1686 writeWm("WM_WINDOWPOSCHANGED"); 1687 break; 1688 case WM_DRAWITEM: 1689 writeWm("WM_DRAWITEM"); 1690 break; 1691 case WM_CLEAR: 1692 writeWm("WM_CLEAR"); 1693 break; 1694 case WM_CUT: 1695 writeWm("WM_CUT"); 1696 break; 1697 case WM_COPY: 1698 writeWm("WM_COPY"); 1699 break; 1700 case WM_PASTE: 1701 writeWm("WM_PASTE"); 1702 break; 1703 case WM_MDITILE: 1704 writeWm("WM_MDITILE"); 1705 break; 1706 case WM_MDICASCADE: 1707 writeWm("WM_MDICASCADE"); 1708 break; 1709 case WM_MDIICONARRANGE: 1710 writeWm("WM_MDIICONARRANGE"); 1711 break; 1712 case WM_MDIGETACTIVE: 1713 writeWm("WM_MDIGETACTIVE"); 1714 break; 1715 case WM_MOUSEWHEEL: 1716 writeWm("WM_MOUSEWHEEL"); 1717 break; 1718 case WM_MOUSEHOVER: 1719 writeWm("WM_MOUSEHOVER"); 1720 break; 1721 case WM_MOUSELEAVE: 1722 writeWm("WM_MOUSELEAVE"); 1723 break; 1724 case WM_PRINT: 1725 writeWm("WM_PRINT"); 1726 break; 1727 case WM_PRINTCLIENT: 1728 writeWm("WM_PRINTCLIENT"); 1729 break; 1730 case WM_MEASUREITEM: 1731 writeWm("WM_MEASUREITEM"); 1732 break; 1733 1734 default: 1735 if (m.msg >= WM_USER && m.msg <= 0x7FFF) { 1736 writeWm("WM_USER+" ~ std..string.toString(m.msg - WM_USER)); 1737 } else if (m.msg >= 0xC000 && m.msg <= 0xFFFF) { 1738 writeWm("RegisterWindowMessage"); 1739 } else { 1740 writeWm("?"); 1741 } 1742 } 1743 1744 Control ctrl; 1745 ctrl = Application.lookupHwnd(m.hWnd); 1746 writef("HWND=%d(0x%X) %s WPARAM=%d(0x%X) LPARAM=%d(0x%X)\n\n", 1747 cast(size_t) m.hWnd, cast(size_t) m.hWnd, 1748 ctrl ? ("DFLname='" ~ ctrl.name ~ "'") : "<nonDFL>", m.wParam, 1749 m.wParam, m.lParam, m.lParam); 1750 1751 debug (MESSAGE_PAUSE) { 1752 Sleep(50); 1753 } 1754 } 1755 } 1756 1757 extern (Windows) LRESULT dflWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nothrow { 1758 //cprintf("HWND %p; WM %d(0x%X); WPARAM %d(0x%X); LPARAM %d(0x%X);\n", hwnd, msg, msg, wparam, wparam, lparam, lparam); 1759 1760 if (msg == wmDfl) { 1761 switch (wparam) { 1762 case WPARAM_DFL_INVOKE: { 1763 InvokeData* pinv; 1764 pinv = cast(InvokeData*) lparam; 1765 try { 1766 pinv.result = pinv.dg(pinv.args); 1767 } 1768 catch (DThrowable e) { 1769 //Application.onThreadException(e); 1770 try { 1771 pinv.exception = e; 1772 } 1773 catch (DThrowable e2) { 1774 Application.onThreadException(e2); 1775 } 1776 } 1777 } 1778 return LRESULT_DFL_INVOKE; 1779 1780 case WPARAM_DFL_INVOKE_SIMPLE: { 1781 InvokeSimpleData* pinv; 1782 pinv = cast(InvokeSimpleData*) lparam; 1783 try { 1784 pinv.dg(); 1785 } 1786 catch (DThrowable e) { 1787 //Application.onThreadException(e); 1788 try { 1789 pinv.exception = e; 1790 } 1791 catch (DThrowable e2) { 1792 Application.onThreadException(e2); 1793 } 1794 } 1795 } 1796 return LRESULT_DFL_INVOKE; 1797 1798 case WPARAM_DFL_DELAY_INVOKE: 1799 try { 1800 (cast(void function()) lparam)(); 1801 } 1802 catch (DThrowable e) { 1803 Application.onThreadException(e); 1804 } 1805 break; 1806 1807 case WPARAM_DFL_DELAY_INVOKE_PARAMS: { 1808 DflInvokeParam* p; 1809 p = cast(DflInvokeParam*) lparam; 1810 try { 1811 p.fp(Application.lookupHwnd(hwnd), p.params.ptr[0 .. p.nparams]); 1812 } 1813 catch (DThrowable e) { 1814 Application.onThreadException(e); 1815 } 1816 free(p); 1817 } 1818 break; 1819 1820 default: 1821 } 1822 } 1823 1824 Message dm = Message(hwnd, msg, wparam, lparam); 1825 Control ctrl; 1826 1827 debug (SHOW_MESSAGE_INFO) 1828 showMessageInfo(dm); 1829 1830 if (msg == WM_NCCREATE) { 1831 ctrl = Application.getCreatingControl(); 1832 if (!ctrl) { 1833 debug (APP_PRINT) 1834 cprintf("Unable to add window 0x%X.\n", hwnd); 1835 return dm.result; 1836 } 1837 Application.creatingControl(null); // Reset. 1838 1839 Application.controls[hwnd] = ctrl; 1840 ctrl.hwnd = hwnd; 1841 debug (APP_PRINT) 1842 cprintf("Added window 0x%X.\n", hwnd); 1843 1844 //ctrl.finishCreating(hwnd); 1845 goto do_msg; 1846 } 1847 1848 ctrl = Application.lookupHwnd(hwnd); 1849 1850 if (!ctrl) { 1851 // Zombie... 1852 //return 1; // Returns correctly for most messages. e.g. WM_QUERYENDSESSION, WM_NCACTIVATE. 1853 dm.result = 1; 1854 version (DFL_NO_ZOMBIE_FORM) { 1855 } else { 1856 ctrl = cast(Control) cast(void*) GetPropA(hwnd, Application.ZOMBIE_PROP.ptr); 1857 if (ctrl) { 1858 ctrl.mustWndProc(dm); 1859 } 1860 } 1861 return dm.result; 1862 } 1863 1864 if (ctrl) { 1865 do_msg: 1866 try { 1867 ctrl.mustWndProc(dm); 1868 if (!ctrl.preProcessMessage(dm)) { 1869 ctrl._wndProc(dm); 1870 } 1871 } 1872 catch (DThrowable e) { 1873 Application.onThreadException(e); 1874 } 1875 } 1876 return dm.result; 1877 } 1878 1879 version (CUSTOM_MSG_HOOK) { 1880 alias CustomMsg = CWPRETSTRUCT; 1881 1882 // Needs to be re-entrant. 1883 extern (Windows) LRESULT globalMsgHook(int code, WPARAM wparam, LPARAM lparam) { 1884 if (code == HC_ACTION) { 1885 CustomMsg* msg = cast(CustomMsg*) lparam; 1886 Control ctrl; 1887 1888 switch (msg.message) { 1889 // ... 1890 } 1891 } 1892 1893 return CallNextHookEx(Application.msghook, code, wparam, lparam); 1894 } 1895 } else { 1896 /+ 1897 struct CustomMsg { 1898 HWND hwnd; 1899 UINT message; 1900 WPARAM wParam; 1901 LPARAM lParam; 1902 } 1903 +/ 1904 } 1905 1906 enum LRESULT LRESULT_DFL_INVOKE = 0x95FADF; // Magic number. 1907 1908 struct InvokeData { 1909 Object delegate(Object[]) dg; 1910 Object[] args; 1911 Object result; 1912 DThrowable exception = null; 1913 } 1914 1915 struct InvokeSimpleData { 1916 void delegate() dg; 1917 DThrowable exception = null; 1918 } 1919 1920 UINT wmDfl; 1921 1922 enum : WPARAM { 1923 WPARAM_DFL_INVOKE = 78, 1924 WPARAM_DFL_DELAY_INVOKE = 79, 1925 WPARAM_DFL_DELAY_INVOKE_PARAMS = 80, 1926 WPARAM_DFL_INVOKE_SIMPLE = 81, 1927 } 1928 1929 struct DflInvokeParam { 1930 void function(Control, size_t[]) fp; 1931 size_t nparams; 1932 size_t[1] params; 1933 } 1934 1935 version (DFL_NO_WM_GETCONTROLNAME) { 1936 } else { 1937 UINT wmGetControlName; 1938 } 1939 1940 extern (Windows) { 1941 alias TrackMouseEventProc = BOOL function(LPTRACKMOUSEEVENT lpEventTrack); 1942 alias SetLayeredWindowAttributesProc = BOOL function(HWND, COLORREF, BYTE, DWORD); 1943 1944 alias GetWindowThemeProc = HTHEME function(HWND); 1945 alias IsThemeBackgroundPartiallyTransparentProc = BOOL function(HTHEME hTheme, 1946 int iPartId, int iStateId); 1947 alias DrawThemeParentBackgroundProc = HRESULT function(HWND hwnd, HDC hdc, RECT* prc); 1948 alias SetThemeAppPropertiesProc = void function(DWORD dwFlags); 1949 } 1950 1951 // Set version = SUPPORTS_MOUSE_TRACKING if it is guaranteed to be supported. 1952 TrackMouseEventProc trackMouseEvent; 1953 1954 // Set version = SUPPORTS_OPACITY if it is guaranteed to be supported. 1955 SetLayeredWindowAttributesProc setLayeredWindowAttributes; 1956 1957 /+ 1958 GetWindowThemeProc getWindowTheme; 1959 IsThemeBackgroundPartiallyTransparentProc isThemeBackgroundPartiallyTransparent; 1960 DrawThemeParentBackgroundProc drawThemeParentBackground; 1961 SetThemeAppPropertiesProc setThemeAppProperties; 1962 +/ 1963 1964 enum CONTROL_CLASSNAME = "DFL_Control"; 1965 enum FORM_CLASSNAME = "DFL_Form"; 1966 enum TEXTBOX_CLASSNAME = "DFL_TextBox"; 1967 enum LISTBOX_CLASSNAME = "DFL_ListBox"; 1968 //enum LABEL_CLASSNAME = "DFL_Label"; 1969 enum BUTTON_CLASSNAME = "DFL_Button"; 1970 enum MDICLIENT_CLASSNAME = "DFL_MdiClient"; 1971 enum RICHTEXTBOX_CLASSNAME = "DFL_RichTextBox"; 1972 enum COMBOBOX_CLASSNAME = "DFL_ComboBox"; 1973 enum TREEVIEW_CLASSNAME = "DFL_TreeView"; 1974 enum TABCONTROL_CLASSNAME = "DFL_TabControl"; 1975 enum LISTVIEW_CLASSNAME = "DFL_ListView"; 1976 enum STATUSBAR_CLASSNAME = "DFL_StatusBar"; 1977 enum PROGRESSBAR_CLASSNAME = "DFL_ProgressBar"; 1978 1979 WNDPROC textBoxPrevWndProc; 1980 WNDPROC listboxPrevWndProc; 1981 //WNDPROC labelPrevWndProc; 1982 WNDPROC buttonPrevWndProc; 1983 WNDPROC mdiclientPrevWndProc; 1984 WNDPROC richtextboxPrevWndProc; 1985 WNDPROC comboboxPrevWndProc; 1986 WNDPROC treeviewPrevWndProc; 1987 WNDPROC tabcontrolPrevWndProc; 1988 WNDPROC listviewPrevWndProc; 1989 WNDPROC statusbarPrevWndProc; 1990 WNDPROC progressbarPrevWndProc; 1991 1992 LONG textBoxClassStyle; 1993 LONG listboxClassStyle; 1994 //LONG labelClassStyle; 1995 LONG buttonClassStyle; 1996 LONG mdiclientClassStyle; 1997 LONG richtextboxClassStyle; 1998 LONG comboboxClassStyle; 1999 LONG treeviewClassStyle; 2000 LONG tabcontrolClassStyle; 2001 LONG listviewClassStyle; 2002 LONG statusbarClassStyle; 2003 LONG progressbarClassStyle; 2004 2005 HMODULE hmodRichtextbox; 2006 2007 // DMD 0.93: CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS is not an expression 2008 //enum UINT WNDCLASS_STYLE = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 2009 //enum UINT WNDCLASS_STYLE = 11; 2010 2011 //enum UINT WNDCLASS_STYLE = CS_DBLCLKS; 2012 // DMD 0.106: CS_DBLCLKS is not an expression 2013 enum UINT WNDCLASS_STYLE = 0x0008; 2014 2015 extern (Windows) { 2016 alias InitCommonControlsExProc = BOOL function(LPINITCOMMONCONTROLSEX lpInitCtrls); 2017 } 2018 2019 // For this to work properly on Windows 95, Internet Explorer 4.0 must be installed. 2020 void _initCommonControls(DWORD dwControls) { 2021 version (SUPPORTS_COMMON_CONTROLS_EX) { 2022 pragma(msg, "DFL: extended common controls supported at compile time"); 2023 2024 alias initProc = InitCommonControlsEx; 2025 } else { 2026 // Make sure InitCommonControlsEx() is in comctl32.dll, 2027 // otherwise use the old InitCommonControls(). 2028 2029 HMODULE hmodCommonControls; 2030 InitCommonControlsExProc initProc; 2031 2032 hmodCommonControls = LoadLibraryA("comctl32.dll"); 2033 if (!hmodCommonControls) // throw new DflException("Unable to load 'comctl32.dll'"); 2034 { 2035 goto no_comctl32; 2036 } 2037 2038 initProc = cast(InitCommonControlsExProc) GetProcAddress(hmodCommonControls, 2039 "InitCommonControlsEx"); 2040 if (!initProc) { 2041 //FreeLibrary(hmodCommonControls); 2042 no_comctl32: 2043 InitCommonControls(); 2044 return; 2045 } 2046 } 2047 2048 INITCOMMONCONTROLSEX icce; 2049 icce.dwSize = INITCOMMONCONTROLSEX.sizeof; 2050 icce.dwICC = dwControls; 2051 initProc(&icce); 2052 } 2053 2054 extern (C) { 2055 size_t C_refCountInc(void* p) { 2056 return Application._doref(p, 1); 2057 } 2058 2059 // Returns the new ref count. 2060 size_t C_refCountDec(void* p) { 2061 return Application._doref(p, -1); 2062 } 2063 } 2064 2065 static this() { 2066 dfl.internal.utf._utfinit(); 2067 2068 Application.tlsThreadFlags = TlsAlloc(); 2069 Application.tlsControl = TlsAlloc(); 2070 Application.tlsFilter = TlsAlloc(); 2071 version (CUSTOM_MSG_HOOK) 2072 Application.tlsHook = TlsAlloc(); 2073 2074 wmDfl = RegisterWindowMessageA("WM_DFL"); 2075 if (!wmDfl) { 2076 wmDfl = WM_USER + 0x7CD; 2077 } 2078 2079 version (DFL_NO_WM_GETCONTROLNAME) { 2080 } else { 2081 wmGetControlName = RegisterWindowMessageA("WM_GETCONTROLNAME"); 2082 } 2083 2084 //InitCommonControls(); // Done later. Needs to be linked with comctl32.lib. 2085 OleInitialize(null); // Needs to be linked with ole32.lib. 2086 2087 HMODULE user32 = GetModuleHandleA("user32.dll"); 2088 2089 version (SUPPORTS_MOUSE_TRACKING) { 2090 pragma(msg, "DFL: mouse tracking supported at compile time"); 2091 2092 trackMouseEvent = &TrackMouseEvent; 2093 } else { 2094 trackMouseEvent = cast(TrackMouseEventProc) GetProcAddress(user32, "TrackMouseEvent"); 2095 if (!trackMouseEvent) { // Must be Windows 95; check if common controls has it (IE 5.5). 2096 trackMouseEvent = cast(TrackMouseEventProc) GetProcAddress( 2097 GetModuleHandleA("comctl32.dll"), "_TrackMouseEvent"); 2098 } 2099 } 2100 2101 version (SUPPORTS_OPACITY) { 2102 pragma(msg, "DFL: opacity supported at compile time"); 2103 2104 setLayeredWindowAttributes = &SetLayeredWindowAttributes; 2105 } else { 2106 setLayeredWindowAttributes = cast(SetLayeredWindowAttributesProc) GetProcAddress(user32, 2107 "SetLayeredWindowAttributes"); 2108 } 2109 } 2110 2111 static ~this() { 2112 version (DFL_NO_MENUS) { 2113 } else { 2114 Application.sdtorFreeAllMenus(); 2115 } 2116 2117 if (hmodRichtextbox) { 2118 FreeLibrary(hmodRichtextbox); 2119 } 2120 } 2121 2122 void _unableToInit(Dstring what) { 2123 /+if(what.length > 4 2124 && what[0] == 'D' && what[1] == 'F' 2125 && what[2] == 'L' && what[3] == '_')+/ 2126 what = what[4 .. what.length]; 2127 throw new DflException("Unable to initialize " ~ what); 2128 } 2129 2130 void _initInstance() { 2131 return _initInstance(GetModuleHandleA(null)); 2132 } 2133 2134 void _initInstance(HINSTANCE inst) 2135 in { 2136 assert(!Application.hinst); 2137 assert(inst); 2138 } 2139 body { 2140 Application.hinst = inst; 2141 2142 dfl.internal.utf.WndClass wc; 2143 wc.wc.style = WNDCLASS_STYLE; 2144 wc.wc.hInstance = inst; 2145 wc.wc.lpfnWndProc = &dflWndProc; 2146 2147 // Control wndclass. 2148 wc.className = CONTROL_CLASSNAME; 2149 if (!dfl.internal.utf.registerClass(wc)) { 2150 _unableToInit(CONTROL_CLASSNAME); 2151 } 2152 2153 // Form wndclass. 2154 wc.wc.cbWndExtra = DLGWINDOWEXTRA; 2155 wc.className = FORM_CLASSNAME; 2156 if (!dfl.internal.utf.registerClass(wc)) { 2157 _unableToInit(FORM_CLASSNAME); 2158 } 2159 } 2160 2161 extern (Windows) { 2162 void _initTextBox() { 2163 if (!textBoxPrevWndProc) { 2164 dfl.internal.utf.WndClass info; 2165 textBoxPrevWndProc = superClass(HINSTANCE.init, "EDIT", TEXTBOX_CLASSNAME, 2166 info); 2167 if (!textBoxPrevWndProc) { 2168 _unableToInit(TEXTBOX_CLASSNAME); 2169 } 2170 textBoxClassStyle = info.wc.style; 2171 } 2172 } 2173 2174 void _initListbox() { 2175 if (!listboxPrevWndProc) { 2176 dfl.internal.utf.WndClass info; 2177 listboxPrevWndProc = superClass(HINSTANCE.init, "LISTBOX", LISTBOX_CLASSNAME, 2178 info); 2179 if (!listboxPrevWndProc) { 2180 _unableToInit(LISTBOX_CLASSNAME); 2181 } 2182 listboxClassStyle = info.wc.style; 2183 } 2184 } 2185 2186 /+ 2187 void _initLabel() { 2188 if(!labelPrevWndProc) { 2189 dfl.internal.utf.WndClass info; 2190 labelPrevWndProc = superClass(HINSTANCE.init, "STATIC", LABEL_CLASSNAME, info); 2191 if(!labelPrevWndProc) { 2192 _unableToInit(LABEL_CLASSNAME); 2193 } 2194 labelClassStyle = info.wc.style; 2195 } 2196 } 2197 +/ 2198 2199 void _initButton() { 2200 if (!buttonPrevWndProc) { 2201 dfl.internal.utf.WndClass info; 2202 buttonPrevWndProc = superClass(HINSTANCE.init, "BUTTON", BUTTON_CLASSNAME, 2203 info); 2204 if (!buttonPrevWndProc) { 2205 _unableToInit(BUTTON_CLASSNAME); 2206 } 2207 buttonClassStyle = info.wc.style; 2208 } 2209 } 2210 2211 void _initMdiclient() { 2212 if (!mdiclientPrevWndProc) { 2213 dfl.internal.utf.WndClass info; 2214 mdiclientPrevWndProc = superClass(HINSTANCE.init, "MDICLIENT", MDICLIENT_CLASSNAME, 2215 info); 2216 if (!mdiclientPrevWndProc) { 2217 _unableToInit(MDICLIENT_CLASSNAME); 2218 } 2219 mdiclientClassStyle = info.wc.style; 2220 } 2221 } 2222 2223 void _initRichtextbox() { 2224 if (!richtextboxPrevWndProc) { 2225 if (!hmodRichtextbox) { 2226 hmodRichtextbox = LoadLibraryA("riched20.dll"); 2227 if (!hmodRichtextbox) { 2228 throw new DflException("Unable to load 'riched20.dll'"); 2229 } 2230 } 2231 2232 Dstring classname; 2233 if (dfl.internal.utf.useUnicode) { 2234 classname = "RichEdit20W"; 2235 } else { 2236 classname = "RichEdit20A"; 2237 } 2238 2239 dfl.internal.utf.WndClass info; 2240 richtextboxPrevWndProc = superClass(HINSTANCE.init, classname, 2241 RICHTEXTBOX_CLASSNAME, info); 2242 if (!richtextboxPrevWndProc) { 2243 _unableToInit(RICHTEXTBOX_CLASSNAME); 2244 } 2245 richtextboxClassStyle = info.wc.style; 2246 } 2247 } 2248 2249 void _initCombobox() { 2250 if (!comboboxPrevWndProc) { 2251 dfl.internal.utf.WndClass info; 2252 comboboxPrevWndProc = superClass(HINSTANCE.init, "COMBOBOX", COMBOBOX_CLASSNAME, 2253 info); 2254 if (!comboboxPrevWndProc) { 2255 _unableToInit(COMBOBOX_CLASSNAME); 2256 } 2257 comboboxClassStyle = info.wc.style; 2258 } 2259 } 2260 2261 void _initTreeview() { 2262 if (!treeviewPrevWndProc) { 2263 _initCommonControls(ICC_TREEVIEW_CLASSES); 2264 2265 dfl.internal.utf.WndClass info; 2266 treeviewPrevWndProc = superClass(HINSTANCE.init, "SysTreeView32", 2267 TREEVIEW_CLASSNAME, info); 2268 if (!treeviewPrevWndProc) { 2269 _unableToInit(TREEVIEW_CLASSNAME); 2270 } 2271 treeviewClassStyle = info.wc.style; 2272 } 2273 } 2274 2275 void _initTabcontrol() { 2276 if (!tabcontrolPrevWndProc) { 2277 _initCommonControls(ICC_TAB_CLASSES); 2278 2279 dfl.internal.utf.WndClass info; 2280 tabcontrolPrevWndProc = superClass(HINSTANCE.init, "SysTabControl32", 2281 TABCONTROL_CLASSNAME, info); 2282 if (!tabcontrolPrevWndProc) { 2283 _unableToInit(TABCONTROL_CLASSNAME); 2284 } 2285 tabcontrolClassStyle = info.wc.style; 2286 } 2287 } 2288 2289 void _initListview() { 2290 if (!listviewPrevWndProc) { 2291 _initCommonControls(ICC_LISTVIEW_CLASSES); 2292 2293 dfl.internal.utf.WndClass info; 2294 listviewPrevWndProc = superClass(HINSTANCE.init, "SysListView32", 2295 LISTVIEW_CLASSNAME, info); 2296 if (!listviewPrevWndProc) { 2297 _unableToInit(LISTVIEW_CLASSNAME); 2298 } 2299 listviewClassStyle = info.wc.style; 2300 } 2301 } 2302 2303 void _initStatusbar() { 2304 if (!statusbarPrevWndProc) { 2305 _initCommonControls(ICC_WIN95_CLASSES); 2306 2307 dfl.internal.utf.WndClass info; 2308 statusbarPrevWndProc = superClass(HINSTANCE.init, 2309 "msctls_statusbar32", STATUSBAR_CLASSNAME, info); 2310 if (!statusbarPrevWndProc) { 2311 _unableToInit(STATUSBAR_CLASSNAME); 2312 } 2313 statusbarClassStyle = info.wc.style; 2314 } 2315 } 2316 2317 void _initProgressbar() { 2318 if (!progressbarPrevWndProc) { 2319 _initCommonControls(ICC_PROGRESS_CLASS); 2320 2321 dfl.internal.utf.WndClass info; 2322 progressbarPrevWndProc = superClass(HINSTANCE.init, 2323 "msctls_progress32", PROGRESSBAR_CLASSNAME, info); 2324 if (!progressbarPrevWndProc) { 2325 _unableToInit(PROGRESSBAR_CLASSNAME); 2326 } 2327 progressbarClassStyle = info.wc.style; 2328 } 2329 } 2330 } 2331 2332 WNDPROC _superClass(HINSTANCE hinst, Dstring className, Dstring newClassName, out WNDCLASSA getInfo) { // deprecated 2333 WNDPROC wndProc; 2334 2335 if (!GetClassInfoA(hinst, unsafeStringz(className), &getInfo)) { // TODO: unicode. 2336 throw new DflException("Unable to obtain information for window class '" ~ className ~ "'"); 2337 } 2338 2339 wndProc = getInfo.lpfnWndProc; 2340 getInfo.lpfnWndProc = &dflWndProc; 2341 2342 getInfo.style &= ~CS_GLOBALCLASS; 2343 getInfo.hCursor = HCURSOR.init; 2344 getInfo.lpszClassName = unsafeStringz(newClassName); 2345 getInfo.hInstance = Application.getInstance(); 2346 2347 if (!RegisterClassA(&getInfo)) // TODO: unicode. 2348 //throw new DflException("Unable to register window class '" ~ newClassName ~ "'"); 2349 { 2350 return null; 2351 } 2352 return wndProc; 2353 } 2354 2355 public: 2356 2357 // Returns the old wndProc. 2358 // This is the old, unsafe, unicode-unfriendly function for superclassing. 2359 deprecated WNDPROC superClass(HINSTANCE hinst, Dstring className, 2360 Dstring newClassName, out WNDCLASSA getInfo) { // package 2361 return _superClass(hinst, className, newClassName, getInfo); 2362 } 2363 2364 deprecated WNDPROC superClass(HINSTANCE hinst, Dstring className, Dstring newClassName) { // package 2365 WNDCLASSA info; 2366 return _superClass(hinst, className, newClassName, info); 2367 } 2368 2369 // Returns the old wndProc. 2370 WNDPROC superClass(HINSTANCE hinst, Dstring className, Dstring newClassName, 2371 out dfl.internal.utf.WndClass getInfo) { // package 2372 WNDPROC wndProc; 2373 2374 if (!dfl.internal.utf.getClassInfo(hinst, className, getInfo)) { 2375 throw new DflException("Unable to obtain information for window class '" ~ className ~ "'"); 2376 } 2377 2378 wndProc = getInfo.wc.lpfnWndProc; 2379 getInfo.wc.lpfnWndProc = &dflWndProc; 2380 2381 getInfo.wc.style &= ~CS_GLOBALCLASS; 2382 getInfo.wc.hCursor = HCURSOR.init; 2383 getInfo.className = newClassName; 2384 getInfo.wc.hInstance = Application.getInstance(); 2385 2386 if (!dfl.internal.utf.registerClass(getInfo)) //throw new DflException("Unable to register window class '" ~ newClassName ~ "'"); 2387 { 2388 return null; 2389 } 2390 return wndProc; 2391 }