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