1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 /// 6 module dfl.form; 7 8 private import dfl.internal.dlib; 9 10 private import dfl.control, dfl.internal.winapi, dfl.event, dfl.drawing; 11 private import dfl.application, dfl.base, dfl.internal.utf; 12 private import dfl.collections; 13 14 version(DFL_NO_MENUS) 15 { 16 } 17 else 18 { 19 private import dfl.menu; 20 } 21 22 version(NO_DFL_PARK_WINDOW) 23 { 24 } 25 else 26 { 27 version = DFL_PARK_WINDOW; 28 } 29 30 31 version = DFL_NO_ZOMBIE_FORM; 32 33 34 private extern(Windows) void _initMdiclient(); 35 36 37 /// 38 enum FormBorderStyle: ubyte //: BorderStyle 39 { 40 NONE = BorderStyle.NONE, /// 41 42 FIXED_3D = BorderStyle.FIXED_3D, /// ditto 43 FIXED_SINGLE = BorderStyle.FIXED_SINGLE, /// ditto 44 FIXED_DIALOG, /// ditto 45 SIZABLE, /// ditto 46 FIXED_TOOLWINDOW, /// ditto 47 SIZABLE_TOOLWINDOW, /// ditto 48 } 49 50 51 /// 52 deprecated enum SizeGripStyle: ubyte 53 { 54 AUTO, /// 55 HIDE, /// ditto 56 SHOW, /// ditto 57 } 58 59 60 /// 61 enum FormStartPosition: ubyte 62 { 63 CENTER_PARENT, /// 64 CENTER_SCREEN, /// ditto 65 MANUAL, /// ditto 66 DEFAULT_BOUNDS, /// ditto 67 WINDOWS_DEFAULT_BOUNDS = DEFAULT_BOUNDS, // deprecated 68 DEFAULT_LOCATION, /// ditto 69 WINDOWS_DEFAULT_LOCATION = DEFAULT_LOCATION, // deprecated 70 } 71 72 73 /// 74 enum FormWindowState: ubyte 75 { 76 MAXIMIZED, /// 77 MINIMIZED, /// ditto 78 NORMAL, /// ditto 79 } 80 81 82 /// 83 enum MdiLayout: ubyte 84 { 85 ARRANGE_ICONS, /// 86 CASCADE, /// ditto 87 TILE_HORIZONTAL, /// ditto 88 TILE_VERTICAL, /// ditto 89 } 90 91 92 /// 93 // The Form's shortcut was pressed. 94 class FormShortcutEventArgs: EventArgs 95 { 96 /// 97 this(Keys shortcut) 98 { 99 this._shortcut = shortcut; 100 } 101 102 103 /// 104 final @property Keys shortcut() // getter 105 { 106 return _shortcut; 107 } 108 109 110 private: 111 Keys _shortcut; 112 } 113 114 115 // DMD 0.93 crashes if this is placed in Form. 116 //private import dfl.button; 117 118 119 version = OLD_MODAL_CLOSE; // New version destroys control info. 120 121 122 /// 123 class Form: ContainerControl, IDialogResult // docmain 124 { 125 /// 126 final @property void acceptButton(IButtonControl btn) // setter 127 { 128 if(acceptBtn) 129 acceptBtn.notifyDefault(false); 130 131 acceptBtn = btn; 132 133 if(btn) 134 btn.notifyDefault(true); 135 } 136 137 /// ditto 138 final @property IButtonControl acceptButton() // getter 139 { 140 return acceptBtn; 141 } 142 143 144 /// 145 final @property void cancelButton(IButtonControl btn) // setter 146 { 147 cancelBtn = btn; 148 149 if(btn) 150 { 151 if(!(Application._compat & DflCompat.FORM_DIALOGRESULT_096)) 152 { 153 btn.dialogResult = DialogResult.CANCEL; 154 } 155 } 156 } 157 158 /// ditto 159 final @property IButtonControl cancelButton() // getter 160 { 161 return cancelBtn; 162 } 163 164 165 /// 166 // An exception is thrown if the shortcut was already added. 167 final void addShortcut(Keys shortcut, void delegate(Object sender, FormShortcutEventArgs ea) pressed) 168 in 169 { 170 assert(shortcut & Keys.KEY_CODE); // At least one key code. 171 assert(pressed !is null); 172 } 173 body 174 { 175 if(shortcut in _shortcuts) 176 throw new DflException("Shortcut key conflict"); 177 178 _shortcuts[shortcut] = pressed; 179 } 180 181 /// ditto 182 // Delegate parameter contravariance. 183 final void addShortcut(Keys shortcut, void delegate(Object sender, EventArgs ea) pressed) 184 { 185 return addShortcut(shortcut, cast(void delegate(Object sender, FormShortcutEventArgs ea))pressed); 186 } 187 188 /// ditto 189 final void removeShortcut(Keys shortcut) 190 { 191 //delete _shortcuts[shortcut]; 192 _shortcuts.remove(shortcut); 193 } 194 195 196 /// 197 static @property Form activeForm() // getter 198 { 199 return cast(Form)fromHandle(GetActiveWindow()); 200 } 201 202 203 /// 204 final @property Form getActiveMdiChild() // getter 205 { 206 return cast(Form)fromHandle(cast(HWND)SendMessageA(handle, WM_MDIGETACTIVE, 0, 0)); 207 } 208 209 210 protected override @property Size defaultSize() // getter 211 { 212 return Size(300, 300); 213 } 214 215 216 // Note: the following 2 functions aren't completely accurate; 217 // it sounds like it should return the center point, but it 218 // returns the point that would center the current form. 219 220 final @property Point screenCenter() // getter 221 { 222 Rect area; 223 version(DFL_MULTIPLE_SCREENS) 224 { 225 if(wparent && wparent.created) 226 { 227 area = Screen.fromControl(wparent).workingArea; 228 } 229 else 230 { 231 if(this.left != 0 && this.top != 0) 232 { 233 area = Screen.fromRectangle(this.bounds).workingArea; 234 } 235 else 236 { 237 area = Screen.fromPoint(Control.mousePosition).workingArea; 238 } 239 } 240 } 241 else 242 { 243 area = Screen.primaryScreen.workingArea; 244 } 245 246 Point pt; 247 pt.x = area.x + ((area.width - this.width) / 2); 248 pt.y = area.y + ((area.height - this.height) / 2); 249 return pt; 250 } 251 252 253 final @property Point parentCenter() // getter 254 { 255 Control cwparent; 256 if(wstyle & WS_CHILD) 257 cwparent = wparent; 258 else 259 cwparent = wowner; 260 261 if(!cwparent || !cwparent.visible) 262 return screenCenter; 263 264 Point pt; 265 pt.x = cwparent.left + ((cwparent.width - this.width) / 2); 266 pt.y = cwparent.top + ((cwparent.height - this.height) / 2); 267 return pt; 268 } 269 270 271 /// 272 final void centerToScreen() 273 { 274 location = screenCenter; 275 } 276 277 278 /// 279 final void centerToParent() 280 { 281 location = parentCenter; 282 } 283 284 285 protected override void createParams(ref CreateParams cp) 286 { 287 super.createParams(cp); 288 289 Control cwparent; 290 if(cp.style & WS_CHILD) 291 cwparent = wparent; 292 else 293 cwparent = wowner; 294 295 cp.className = FORM_CLASSNAME; 296 version(DFL_NO_MENUS) 297 { 298 cp.menu = HMENU.init; 299 } 300 else 301 { 302 cp.menu = wmenu ? wmenu.handle : HMENU.init; 303 } 304 305 //cp.parent = wparent ? wparent.handle : HWND.init; 306 //if(!(cp.style & WS_CHILD)) 307 // cp.parent = wowner ? wowner.handle : HWND.init; 308 cp.parent = cwparent ? cwparent.handle : HWND.init; 309 if(!cp.parent) 310 cp.parent = sowner; 311 version(DFL_PARK_WINDOW) 312 { 313 if(!cp.parent && !showInTaskbar) 314 cp.parent = getParkHwnd(); 315 } 316 317 if(!recreatingHandle) 318 { 319 switch(startpos) 320 { 321 case FormStartPosition.CENTER_PARENT: 322 if(cwparent && cwparent.visible) 323 { 324 cp.x = cwparent.left + ((cwparent.width - cp.width) / 2); 325 cp.y = cwparent.top + ((cwparent.height - cp.height) / 2); 326 327 // Make sure part of the form isn't off the screen. 328 RECT area; 329 SystemParametersInfoA(SPI_GETWORKAREA, 0, &area, FALSE); 330 if(cp.x < area.left) 331 cp.x = area.left; 332 else if(cp.x + cp.width > area.right) 333 cp.x = area.right - cp.width; 334 if(cp.y < area.top) 335 cp.y = area.top; 336 else if(cp.y + cp.height > area.bottom) 337 cp.y = area.bottom - cp.height; 338 break; 339 } 340 break; 341 342 case FormStartPosition.CENTER_SCREEN: 343 { 344 // TODO: map to client coords if MDI child. 345 346 RECT area; 347 SystemParametersInfoA(SPI_GETWORKAREA, 0, &area, FALSE); 348 349 cp.x = area.left + (((area.right - area.left) - cp.width) / 2); 350 cp.y = area.top + (((area.bottom - area.top) - cp.height) / 2); 351 } 352 break; 353 354 case FormStartPosition.DEFAULT_BOUNDS: 355 // WM_CREATE fixes these. 356 cp.width = CW_USEDEFAULT; 357 cp.height = CW_USEDEFAULT; 358 //break; // DEFAULT_BOUNDS assumes default location. 359 goto case FormStartPosition.DEFAULT_LOCATION; 360 361 case FormStartPosition.DEFAULT_LOCATION: 362 // WM_CREATE fixes these. 363 cp.x = CW_USEDEFAULT; 364 //cp.y = CW_USEDEFAULT; 365 cp.y = visible ? SW_SHOW : SW_HIDE; 366 break; 367 368 default: 369 } 370 } 371 } 372 373 374 protected override void createHandle() 375 { 376 // This code is reimplemented to allow some tricks. 377 378 if(isHandleCreated) 379 return; 380 381 debug 382 { 383 Dstring er; 384 } 385 if(killing) 386 { 387 /+ 388 create_err: 389 throw new DflException("Form creation failure"); 390 //throw new DflException(Object.toString() ~ " creation failure"); // ? 391 +/ 392 debug 393 { 394 er = "the form is being killed"; 395 } 396 397 debug(APP_PRINT) 398 { 399 cprintf("Creating Form handle while killing.\n"); 400 } 401 402 create_err: 403 Dstring kmsg = "Form creation failure"; 404 if(name.length) 405 kmsg ~= " (" ~ name ~ ")"; 406 debug 407 { 408 if(er.length) 409 kmsg ~= " - " ~ er; 410 } 411 throw new DflException(kmsg, __FILE__, __LINE__); 412 //throw new DflException(Object.toString() ~ " creation failure"); // ? 413 } 414 415 // Need the owner's handle to exist. 416 if(wowner) 417 // wowner.createHandle(); // DMD 0.111: class dfl.control.Control member createHandle is not accessible 418 wowner._createHandle(); 419 420 // This is here because wowner.createHandle() might create me. 421 //if(created) 422 if(isHandleCreated) 423 return; 424 425 //DWORD vis; 426 CBits vis; 427 CreateParams cp; 428 429 createParams(cp); 430 assert(!isHandleCreated); // Make sure the handle wasn't created in createParams(). 431 432 with(cp) 433 { 434 wtext = caption; 435 //wrect = Rect(x, y, width, height); // Avoid CW_USEDEFAULT problems. This gets updated in WM_CREATE. 436 wclassStyle = classStyle; 437 wexstyle = exStyle; 438 wstyle = style; 439 440 // Use local var to avoid changing -cp- at this point. 441 int ly; 442 ly = y; 443 444 // Delay setting visible. 445 //vis = wstyle; 446 vis = cbits; 447 vis |= CBits.FVISIBLE; 448 if(!(vis & CBits.VISIBLE)) 449 vis &= ~CBits.FVISIBLE; 450 if(x == CW_USEDEFAULT) 451 ly = SW_HIDE; 452 453 Application.creatingControl(this); 454 hwnd = dfl.internal.utf.createWindowEx(exStyle, className, caption, wstyle & ~WS_VISIBLE, 455 x, ly, width, height, parent, menu, inst, param); 456 if(!hwnd) 457 { 458 debug 459 { 460 er = std..string.format("CreateWindowEx failed {className=%s;exStyle=0x%X;style=0x%X;parent=0x%X;menu=0x%X;inst=0x%X;}", 461 className, exStyle, style, cast(void*)parent, cast(void*)menu, cast(void*)inst); 462 } 463 goto create_err; 464 } 465 } 466 467 if(setLayeredWindowAttributes) 468 { 469 BYTE alpha = opacityToAlpha(opa); 470 DWORD flags = 0; 471 472 if(alpha != BYTE.max) 473 flags |= LWA_ALPHA; 474 475 if(transKey != Color.empty) 476 flags |= LWA_COLORKEY; 477 478 if(flags) 479 { 480 //_exStyle(_exStyle() | WS_EX_LAYERED); // Should already be set. 481 setLayeredWindowAttributes(hwnd, transKey.toRgb(), alpha, flags); 482 } 483 } 484 485 if(!nofilter) 486 Application.addMessageFilter(mfilter); // To process IsDialogMessage(). 487 488 //createChildren(); 489 try 490 { 491 createChildren(); // Might throw. 492 } 493 catch(DThrowable e) 494 { 495 Application.onThreadException(e); 496 } 497 498 alayout(this, false); // ? 499 500 if(!recreatingHandle) // This stuff already happened if recreating... 501 { 502 if(autoScale) 503 { 504 //Application.doEvents(); // ? 505 506 _scale(); 507 508 // Scaling can goof up the centering, so fix it.. 509 switch(startpos) 510 { 511 case FormStartPosition.CENTER_PARENT: 512 centerToParent(); 513 break; 514 case FormStartPosition.CENTER_SCREEN: 515 centerToScreen(); 516 break; 517 default: 518 } 519 } 520 521 if(Application._compat & DflCompat.FORM_LOAD_096) 522 { 523 // Load before shown. 524 // Not calling if recreating handle! 525 onLoad(EventArgs.empty); 526 } 527 } 528 529 //assert(!visible); 530 //if(vis & WS_VISIBLE) 531 //if(vis & CBits.VISIBLE) 532 if(vis & CBits.FVISIBLE) 533 { 534 cbits |= CBits.VISIBLE; 535 wstyle |= WS_VISIBLE; 536 if(recreatingHandle) 537 goto show_normal; 538 // These fire onVisibleChanged as needed... 539 switch(windowState) 540 { 541 case FormWindowState.NORMAL: show_normal: 542 ShowWindow(hwnd, SW_SHOW); 543 // Possible to-do: see if non-MDI is "main form" and use SHOWNORMAL or doShow. 544 break; 545 case FormWindowState.MAXIMIZED: 546 ShowWindow(hwnd, SW_SHOWMAXIMIZED); 547 break; 548 case FormWindowState.MINIMIZED: 549 ShowWindow(hwnd, SW_SHOWMINIMIZED); 550 break; 551 default: 552 assert(0); 553 } 554 } 555 //cbits &= ~CBits.FVISIBLE; 556 } 557 558 559 /+ 560 /// 561 // Focused children are scrolled into view. 562 override @property void autoScroll(bool byes) // setter 563 { 564 super.autoScroll(byes); 565 } 566 567 /// ditto 568 override @property bool autoScroll() // getter 569 { 570 return super.autoScroll(byes); 571 } 572 +/ 573 574 575 // This only works if the windows version is 576 // set to 4.0 or higher. 577 578 /// 579 final @property void controlBox(bool byes) // setter 580 { 581 if(byes) 582 _style(_style() | WS_SYSMENU); 583 else 584 _style(_style() & ~WS_SYSMENU); 585 586 // Update taskbar button. 587 if(isHandleCreated) 588 { 589 if(visible) 590 { 591 //hide(); 592 //show(); 593 // Do it directly so that DFL code can't prevent it. 594 cbits |= CBits.RECREATING; 595 scope(exit) 596 cbits &= ~CBits.RECREATING; 597 doHide(); 598 doShow(); 599 } 600 } 601 } 602 603 /// ditto 604 final @property bool controlBox() // getter 605 { 606 return (_style() & WS_SYSMENU) != 0; 607 } 608 609 610 /// 611 final @property void desktopBounds(Rect r) // setter 612 { 613 RECT rect; 614 if(r.width < 0) 615 r.width = 0; 616 if(r.height < 0) 617 r.height = 0; 618 r.getRect(&rect); 619 620 //Control par = parent; 621 //if(par) // Convert from screen coords to parent coords. 622 // MapWindowPoints(HWND.init, par.handle, cast(POINT*)&rect, 2); 623 624 setBoundsCore(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BoundsSpecified.ALL); 625 } 626 627 /// ditto 628 final @property Rect desktopBounds() // getter 629 { 630 RECT r; 631 GetWindowRect(handle, &r); 632 return Rect(&r); 633 } 634 635 636 /// 637 final @property void desktopLocation(Point dp) // setter 638 { 639 //Control par = parent; 640 //if(par) // Convert from screen coords to parent coords. 641 // MapWindowPoints(HWND.init, par.handle, &dp.point, 1); 642 643 setBoundsCore(dp.x, dp.y, 0, 0, BoundsSpecified.LOCATION); 644 } 645 646 /// ditto 647 final @property Point desktopLocation() // getter 648 { 649 RECT r; 650 GetWindowRect(handle, &r); 651 return Point(r.left, r.top); 652 } 653 654 655 /// 656 final @property void dialogResult(DialogResult dr) // setter 657 { 658 fresult = dr; 659 660 if(!(Application._compat & DflCompat.FORM_DIALOGRESULT_096)) 661 { 662 if(modal && DialogResult.NONE != dr) 663 close(); 664 } 665 } 666 667 /// ditto 668 final @property DialogResult dialogResult() // getter 669 { 670 return fresult; 671 } 672 673 674 override @property Color backColor() // getter 675 { 676 if(Color.empty == backc) 677 return defaultBackColor; // Control's. 678 return backc; 679 } 680 681 alias Control.backColor backColor; // Overload. 682 683 684 /// 685 final @property void formBorderStyle(FormBorderStyle bstyle) // setter 686 { 687 FormBorderStyle curbstyle; 688 curbstyle = formBorderStyle; 689 if(bstyle == curbstyle) 690 return; 691 692 bool vis = false; 693 694 if(isHandleCreated && visible) 695 { 696 vis = true; 697 cbits |= CBits.RECREATING; 698 // Do it directly so that DFL code can't prevent it. 699 //doHide(); 700 ShowWindow(hwnd, SW_HIDE); 701 } 702 scope(exit) 703 cbits &= ~CBits.RECREATING; 704 705 LONG st; 706 LONG exst; 707 //Size csz; 708 st = _style(); 709 exst = _exStyle(); 710 //csz = clientSize; 711 712 enum DWORD STNOTNONE = ~(WS_BORDER | WS_THICKFRAME | WS_CAPTION | WS_DLGFRAME); 713 enum DWORD EXSTNOTNONE = ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE 714 | WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE); 715 716 // This is needed to work on Vista. 717 if(FormBorderStyle.NONE != curbstyle) 718 { 719 _style(st & STNOTNONE); 720 _exStyle(exst & EXSTNOTNONE); 721 } 722 723 final switch(bstyle) 724 { 725 case FormBorderStyle.FIXED_3D: 726 st &= ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME); 727 exst &= ~(WS_EX_TOOLWINDOW | WS_EX_STATICEDGE); 728 729 st |= WS_CAPTION; 730 exst |= WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE; 731 break; 732 733 case FormBorderStyle.FIXED_DIALOG: 734 st &= ~(WS_BORDER | WS_THICKFRAME); 735 exst &= ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); 736 737 st |= WS_CAPTION | WS_DLGFRAME; 738 exst |= WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE; 739 break; 740 741 case FormBorderStyle.FIXED_SINGLE: 742 st &= ~(WS_THICKFRAME | WS_DLGFRAME); 743 exst &= ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE | WS_EX_STATICEDGE); 744 745 st |= WS_BORDER | WS_CAPTION; 746 exst |= WS_EX_DLGMODALFRAME; 747 break; 748 749 case FormBorderStyle.FIXED_TOOLWINDOW: 750 st &= ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME); 751 exst &= ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); 752 753 st |= WS_CAPTION; 754 exst |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME; 755 break; 756 757 case FormBorderStyle.SIZABLE: 758 st &= ~(WS_BORDER | WS_DLGFRAME); 759 exst &= ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE); 760 761 st |= WS_THICKFRAME | WS_CAPTION; 762 exst |= WS_EX_WINDOWEDGE; 763 break; 764 765 case FormBorderStyle.SIZABLE_TOOLWINDOW: 766 st &= ~(WS_BORDER | WS_DLGFRAME); 767 exst &= ~(WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE); 768 769 st |= WS_THICKFRAME | WS_CAPTION; 770 exst |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE; 771 break; 772 773 case FormBorderStyle.NONE: 774 st &= STNOTNONE; 775 exst &= EXSTNOTNONE; 776 break; 777 } 778 779 _style(st); 780 _exStyle(exst); 781 //clientSize = csz; 782 783 // Update taskbar button. 784 if(isHandleCreated) 785 { 786 if(vis) 787 { 788 //hide(); 789 //show(); 790 SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE 791 | SWP_NOSIZE | SWP_NOZORDER); // Recalculate the frame while hidden. 792 _resetSystemMenu(); 793 // Do it directly so that DFL code can't prevent it. 794 doShow(); 795 invalidate(true); 796 } 797 else 798 { 799 SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE 800 | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); // Recalculate the frame. 801 _resetSystemMenu(); 802 } 803 } 804 } 805 806 /// ditto 807 final @property FormBorderStyle formBorderStyle() // getter 808 { 809 LONG st = _style(); 810 LONG exst = _exStyle(); 811 812 if(exst & WS_EX_TOOLWINDOW) 813 { 814 if(st & WS_THICKFRAME) 815 return FormBorderStyle.SIZABLE_TOOLWINDOW; 816 else 817 return FormBorderStyle.FIXED_TOOLWINDOW; 818 } 819 else 820 { 821 if(st & WS_THICKFRAME) 822 { 823 return FormBorderStyle.SIZABLE; 824 } 825 else 826 { 827 if(exst & WS_EX_CLIENTEDGE) 828 return FormBorderStyle.FIXED_3D; 829 830 if(exst & WS_EX_WINDOWEDGE) 831 return FormBorderStyle.FIXED_DIALOG; 832 833 if(st & WS_BORDER) 834 return FormBorderStyle.FIXED_SINGLE; 835 } 836 } 837 838 return FormBorderStyle.NONE; 839 } 840 841 842 /// 843 // Ignored if min and max buttons are enabled. 844 final @property void helpButton(bool byes) // setter 845 { 846 if(byes) 847 _exStyle(_exStyle() | WS_EX_CONTEXTHELP); 848 else 849 _exStyle(_exStyle() & ~WS_EX_CONTEXTHELP); 850 851 redrawEntire(); 852 } 853 854 /// ditto 855 final @property bool helpButton() // getter 856 { 857 return (_exStyle() & WS_EX_CONTEXTHELP) != 0; 858 } 859 860 861 private void _setIcon() 862 { 863 HICON hico, hicoSm; 864 865 if(wicon) 866 { 867 hico = wicon.handle; 868 869 int smx, smy; 870 smx = GetSystemMetrics(SM_CXSMICON); 871 smy = GetSystemMetrics(SM_CYSMICON); 872 hicoSm = CopyImage(hico, IMAGE_ICON, smx, smy, LR_COPYFROMRESOURCE); 873 if(!hicoSm) 874 hicoSm = CopyImage(hico, IMAGE_ICON, smx, smy, 0); 875 if(hicoSm) 876 { 877 wiconSm = new Icon(hicoSm); 878 } 879 else 880 { 881 wiconSm = null; 882 hicoSm = hico; 883 } 884 } 885 else 886 { 887 hico = HICON.init; 888 hicoSm = HICON.init; 889 890 wiconSm = null; 891 } 892 893 SendMessageA(hwnd, WM_SETICON, ICON_BIG, cast(LPARAM)hico); 894 SendMessageA(hwnd, WM_SETICON, ICON_SMALL, cast(LPARAM)hicoSm); 895 896 if(visible) 897 redrawEntire(); 898 } 899 900 901 /// 902 final @property void icon(Icon ico) // setter 903 { 904 wicon = ico; 905 906 if(isHandleCreated) 907 _setIcon(); 908 } 909 910 /// ditto 911 final @property Icon icon() // getter 912 { 913 return wicon; 914 } 915 916 917 // TODO: implement. 918 // keyPreview 919 920 921 /// 922 final @property bool isMdiChild() // getter 923 { 924 return (_exStyle() & WS_EX_MDICHILD) != 0; 925 } 926 927 928 version(NO_MDI) 929 { 930 private alias Control MdiClient; // ? 931 } 932 933 /// 934 // Note: keeping this here for NO_MDI to keep the vtable. 935 protected MdiClient createMdiClient() 936 { 937 version(NO_MDI) 938 { 939 assert(0, "MDI disabled"); 940 } 941 else 942 { 943 return new MdiClient(); 944 } 945 } 946 947 948 version(NO_MDI) {} else 949 { 950 /// 951 final @property void isMdiContainer(bool byes) // setter 952 { 953 if(mdiClient) 954 { 955 if(!byes) 956 { 957 // Remove MDI client. 958 mdiClient.dispose(); 959 //mdiClient = null; 960 _mdiClient = null; 961 } 962 } 963 else 964 { 965 if(byes) 966 { 967 // Create MDI client. 968 //mdiClient = new MdiClient; 969 //_mdiClient = new MdiClient; 970 //mdiClient = createMdiClient(); 971 _mdiClient = createMdiClient(); 972 mdiClient.parent = this; 973 } 974 } 975 } 976 977 /// ditto 978 final @property bool isMdiContainer() // getter 979 { 980 version(NO_MDI) 981 { 982 return false; 983 } 984 else 985 { 986 return !(mdiClient is null); 987 } 988 } 989 990 991 /// 992 final Form[] mdiChildren() // getter 993 { 994 version(NO_MDI) 995 { 996 return null; 997 } 998 else 999 { 1000 /+ 1001 if(!mdiClient) 1002 return null; 1003 +/ 1004 1005 return _mdiChildren; 1006 } 1007 } 1008 1009 1010 // parent is the MDI client and mdiParent is the MDI frame. 1011 1012 1013 version(NO_MDI) {} else 1014 { 1015 /// 1016 final @property void mdiParent(Form frm) // setter 1017 in 1018 { 1019 if(frm) 1020 { 1021 assert(frm.isMdiContainer); 1022 assert(!(frm.mdiClient is null)); 1023 } 1024 } 1025 /+out 1026 { 1027 if(frm) 1028 { 1029 bool found = false; 1030 foreach(Form elem; frm._mdiChildren) 1031 { 1032 if(elem is this) 1033 { 1034 found = true; 1035 break; 1036 } 1037 } 1038 assert(found); 1039 } 1040 }+/ 1041 body 1042 { 1043 if(wmdiparent is frm) 1044 return; 1045 1046 _removeFromOldOwner(); 1047 wowner = null; 1048 wmdiparent = null; // Safety in case of exception. 1049 1050 if(frm) 1051 { 1052 if(isHandleCreated) 1053 { 1054 frm.createControl(); // ? 1055 frm.mdiClient.createControl(); // Should already be done from frm.createControl(). 1056 } 1057 1058 // Copy so that old mdiChildren arrays won't get overwritten. 1059 Form[] _thisa = new Form[1]; // DMD 0.123: this can't be a static array or the append screws up. 1060 _thisa[0] = this; 1061 frm._mdiChildren = frm._mdiChildren ~ _thisa; 1062 1063 _style((_style() | WS_CHILD) & ~WS_POPUP); 1064 _exStyle(_exStyle() | WS_EX_MDICHILD); 1065 1066 wparent = frm.mdiClient; 1067 wmdiparent = frm; 1068 if(isHandleCreated) 1069 SetParent(hwnd, frm.mdiClient.hwnd); 1070 } 1071 else 1072 { 1073 _exStyle(_exStyle() & ~WS_EX_MDICHILD); 1074 _style((_style() | WS_POPUP) & ~WS_CHILD); 1075 1076 if(isHandleCreated) 1077 SetParent(hwnd, HWND.init); 1078 wparent = null; 1079 1080 //wmdiparent = null; 1081 } 1082 } 1083 } 1084 1085 /// ditto 1086 final @property Form mdiParent() // getter 1087 { 1088 version(NO_MDI) 1089 { 1090 return null; 1091 } 1092 else 1093 { 1094 //if(isMdiChild) 1095 return wmdiparent; 1096 //return null; 1097 } 1098 } 1099 } 1100 1101 1102 /// 1103 final @property void maximizeBox(bool byes) // setter 1104 { 1105 if(byes == maximizeBox) 1106 return; 1107 1108 if(byes) 1109 _style(_style() | WS_MAXIMIZEBOX); 1110 else 1111 _style(_style() & ~WS_MAXIMIZEBOX); 1112 1113 if(isHandleCreated) 1114 { 1115 redrawEntire(); 1116 1117 _resetSystemMenu(); 1118 } 1119 } 1120 1121 /// ditto 1122 final @property bool maximizeBox() // getter 1123 { 1124 return (_style() & WS_MAXIMIZEBOX) != 0; 1125 } 1126 1127 1128 /// 1129 final @property void minimizeBox(bool byes) // setter 1130 { 1131 if(byes == minimizeBox) 1132 return; 1133 1134 if(byes) 1135 _style(_style() | WS_MINIMIZEBOX); 1136 else 1137 _style(_style() & ~WS_MINIMIZEBOX); 1138 1139 if(isHandleCreated) 1140 { 1141 redrawEntire(); 1142 1143 _resetSystemMenu(); 1144 } 1145 } 1146 1147 /// ditto 1148 final @property bool minimizeBox() // getter 1149 { 1150 return (_style() & WS_MINIMIZEBOX) != 0; 1151 } 1152 1153 1154 protected override void onHandleCreated(EventArgs ea) 1155 { 1156 super.onHandleCreated(ea); 1157 1158 version(DFL_NO_MENUS) 1159 { 1160 } 1161 else 1162 { 1163 if(wmenu) 1164 wmenu._setHwnd(handle); 1165 } 1166 1167 _setIcon(); 1168 1169 //SendMessageA(handle, DM_SETDEFID, IDOK, 0); 1170 } 1171 1172 1173 protected override void onResize(EventArgs ea) 1174 { 1175 super.onResize(ea); 1176 1177 if(_isPaintingSizeGrip()) 1178 { 1179 RECT rect; 1180 _getSizeGripArea(&rect); 1181 InvalidateRect(hwnd, &rect, TRUE); 1182 } 1183 } 1184 1185 1186 private void _getSizeGripArea(RECT* rect) 1187 { 1188 rect.right = clientSize.width; 1189 rect.bottom = clientSize.height; 1190 rect.left = rect.right - GetSystemMetrics(SM_CXVSCROLL); 1191 rect.top = rect.bottom - GetSystemMetrics(SM_CYHSCROLL); 1192 } 1193 1194 1195 private bool _isPaintingSizeGrip() 1196 { 1197 if(grip) 1198 { 1199 if(wstyle & WS_THICKFRAME) 1200 { 1201 return !(wstyle & (WS_MINIMIZE | WS_MAXIMIZE | 1202 WS_VSCROLL | WS_HSCROLL)); 1203 } 1204 } 1205 return false; 1206 } 1207 1208 1209 protected override void onPaint(PaintEventArgs ea) 1210 { 1211 super.onPaint(ea); 1212 1213 if(_isPaintingSizeGrip()) 1214 { 1215 /+ 1216 RECT rect; 1217 _getSizeGripArea(&rect); 1218 DrawFrameControl(ea.graphics.handle, &rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); 1219 +/ 1220 1221 ea.graphics.drawSizeGrip(clientSize.width, clientSize.height); 1222 } 1223 } 1224 1225 1226 version(DFL_NO_MENUS) 1227 { 1228 } 1229 else 1230 { 1231 /// 1232 final @property void menu(MainMenu menu) // setter 1233 { 1234 if(isHandleCreated) 1235 { 1236 HWND hwnd; 1237 hwnd = handle; 1238 1239 if(menu) 1240 { 1241 SetMenu(hwnd, menu.handle); 1242 menu._setHwnd(hwnd); 1243 } 1244 else 1245 { 1246 SetMenu(hwnd, HMENU.init); 1247 } 1248 1249 if(wmenu) 1250 wmenu._setHwnd(HWND.init); 1251 wmenu = menu; 1252 1253 DrawMenuBar(hwnd); 1254 } 1255 else 1256 { 1257 wmenu = menu; 1258 _recalcClientSize(); 1259 } 1260 } 1261 1262 /// ditto 1263 final @property MainMenu menu() // getter 1264 { 1265 return wmenu; 1266 } 1267 1268 1269 /+ 1270 /// 1271 final @property MainMenu mergedMenu() // getter 1272 { 1273 // Return menu belonging to active MDI child if maximized ? 1274 } 1275 +/ 1276 } 1277 1278 1279 /// 1280 final @property void minimumSize(Size min) // setter 1281 { 1282 if(!min.width && !min.height) 1283 { 1284 minsz.width = 0; 1285 minsz.height = 0; 1286 return; 1287 } 1288 1289 if(maxsz.width && maxsz.height) 1290 { 1291 if(min.width > maxsz.width || min.height > maxsz.height) 1292 throw new DflException("Minimum size cannot be bigger than maximum size"); 1293 } 1294 1295 minsz = min; 1296 1297 bool ischangesz = false; 1298 Size changesz; 1299 changesz = size; 1300 1301 if(width < min.width) 1302 { 1303 changesz.width = min.width; 1304 ischangesz = true; 1305 } 1306 if(height < min.height) 1307 { 1308 changesz.height = min.height; 1309 ischangesz = true; 1310 } 1311 1312 if(ischangesz) 1313 size = changesz; 1314 } 1315 1316 /// ditto 1317 final @property Size minimumSize() // getter 1318 { 1319 return minsz; 1320 } 1321 1322 1323 /// 1324 final @property void maximumSize(Size max) // setter 1325 { 1326 if(!max.width && !max.height) 1327 { 1328 maxsz.width = 0; 1329 maxsz.height = 0; 1330 return; 1331 } 1332 1333 //if(minsz.width && minsz.height) 1334 { 1335 if(max.width < minsz.width || max.height < minsz.height) 1336 throw new DflException("Maximum size cannot be smaller than minimum size"); 1337 } 1338 1339 maxsz = max; 1340 1341 bool ischangesz = false; 1342 Size changesz; 1343 changesz = size; 1344 1345 if(width > max.width) 1346 { 1347 changesz.width = max.width; 1348 ischangesz = true; 1349 } 1350 if(height > max.height) 1351 { 1352 changesz.height = max.height; 1353 ischangesz = true; 1354 } 1355 1356 if(ischangesz) 1357 size = changesz; 1358 } 1359 1360 /// ditto 1361 final @property Size maximumSize() // getter 1362 { 1363 return maxsz; 1364 } 1365 1366 1367 /// 1368 final @property bool modal() // getter 1369 { 1370 return wmodal; 1371 } 1372 1373 1374 /// 1375 // If opacity and transparency are supported. 1376 static @property bool supportsOpacity() // getter 1377 { 1378 return setLayeredWindowAttributes != null; 1379 } 1380 1381 1382 private static BYTE opacityToAlpha(double opa) 1383 { 1384 return cast(BYTE)(opa * BYTE.max); 1385 } 1386 1387 1388 /// 1389 // 1.0 is 100%, 0.0 is 0%, 0.75 is 75%. 1390 // Does nothing if not supported. 1391 final @property void opacity(double opa) // setter 1392 { 1393 if(setLayeredWindowAttributes) 1394 { 1395 BYTE alpha; 1396 1397 if(opa >= 1.0) 1398 { 1399 this.opa = 1.0; 1400 alpha = BYTE.max; 1401 } 1402 else if(opa <= 0.0) 1403 { 1404 this.opa = 0.0; 1405 alpha = BYTE.min; 1406 } 1407 else 1408 { 1409 this.opa = opa; 1410 alpha = opacityToAlpha(opa); 1411 } 1412 1413 if(alpha == BYTE.max) // Disable 1414 { 1415 if(transKey == Color.empty) 1416 _exStyle(_exStyle() & ~WS_EX_LAYERED); 1417 else 1418 setLayeredWindowAttributes(handle, transKey.toRgb(), 0, LWA_COLORKEY); 1419 } 1420 else 1421 { 1422 _exStyle(_exStyle() | WS_EX_LAYERED); 1423 if(isHandleCreated) 1424 { 1425 //_exStyle(_exStyle() | WS_EX_LAYERED); 1426 if(transKey == Color.empty) 1427 setLayeredWindowAttributes(handle, 0, alpha, LWA_ALPHA); 1428 else 1429 setLayeredWindowAttributes(handle, transKey.toRgb(), alpha, LWA_ALPHA | LWA_COLORKEY); 1430 } 1431 } 1432 } 1433 } 1434 1435 /// ditto 1436 final @property double opacity() // getter 1437 { 1438 return opa; 1439 } 1440 1441 1442 /+ 1443 /// 1444 final @property Form[] ownedForms() // getter 1445 { 1446 // TODO: implement. 1447 } 1448 +/ 1449 1450 1451 // the "old owner" is the current -wowner- or -wmdiparent-. 1452 // If neither are set, nothing happens. 1453 private void _removeFromOldOwner() 1454 { 1455 int idx; 1456 1457 if(wmdiparent) 1458 { 1459 idx = findIsIndex!(Form)(wmdiparent._mdiChildren, this); 1460 if(-1 != idx) 1461 wmdiparent._mdiChildren = removeIndex!(Form)(wmdiparent._mdiChildren, idx); 1462 //else 1463 // assert(0); 1464 } 1465 else if(wowner) 1466 { 1467 idx = findIsIndex!(Form)(wowner._owned, this); 1468 if(-1 != idx) 1469 wowner._owned = removeIndex!(Form)(wowner._owned, idx); 1470 //else 1471 // assert(0); 1472 } 1473 } 1474 1475 1476 /// 1477 final @property void owner(Form frm) // setter 1478 /+out 1479 { 1480 if(frm) 1481 { 1482 bool found = false; 1483 foreach(Form elem; frm._owned) 1484 { 1485 if(elem is this) 1486 { 1487 found = true; 1488 break; 1489 } 1490 } 1491 assert(found); 1492 } 1493 }+/ 1494 body 1495 { 1496 if(wowner is frm) 1497 return; 1498 1499 // Remove from old owner. 1500 _removeFromOldOwner(); 1501 wmdiparent = null; 1502 wowner = null; // Safety in case of exception. 1503 _exStyle(_exStyle() & ~WS_EX_MDICHILD); 1504 _style((_style() | WS_POPUP) & ~WS_CHILD); 1505 1506 // Add to new owner. 1507 if(frm) 1508 { 1509 if(isHandleCreated) 1510 { 1511 frm.createControl(); // ? 1512 } 1513 1514 // Copy so that old ownedForms arrays won't get overwritten. 1515 Form[] _thisa = new Form[1]; // DMD 0.123: this can't be a static array or the append screws up. 1516 _thisa[0] = this; 1517 frm._owned = frm._owned ~ _thisa; 1518 1519 wowner = frm; 1520 if(isHandleCreated) 1521 { 1522 if(CCompat.DFL095 == _compat) 1523 SetParent(hwnd, frm.hwnd); 1524 else 1525 _crecreate(); 1526 } 1527 } 1528 else 1529 { 1530 if(isHandleCreated) 1531 { 1532 if(showInTaskbar || CCompat.DFL095 == _compat) 1533 SetParent(hwnd, HWND.init); 1534 else 1535 _crecreate(); 1536 } 1537 } 1538 1539 //wowner = frm; 1540 } 1541 1542 /// ditto 1543 final @property Form owner() // getter 1544 { 1545 return wowner; 1546 } 1547 1548 1549 /// 1550 // This function does not work in all cases. 1551 final @property void showInTaskbar(bool byes) // setter 1552 { 1553 if(isHandleCreated) 1554 { 1555 bool vis; 1556 vis = visible; 1557 1558 if(vis) 1559 { 1560 //hide(); 1561 // Do it directly so that DFL code can't prevent it. 1562 cbits |= CBits.RECREATING; 1563 doHide(); 1564 } 1565 scope(exit) 1566 cbits &= ~CBits.RECREATING; 1567 1568 if(byes) 1569 { 1570 _exStyle(_exStyle() | WS_EX_APPWINDOW); 1571 1572 version(DFL_PARK_WINDOW) 1573 { 1574 if(_hwPark && GetParent(handle) == _hwPark) 1575 SetParent(handle, HWND.init); 1576 } 1577 } 1578 else 1579 { 1580 _exStyle(_exStyle() & ~WS_EX_APPWINDOW); 1581 1582 version(DFL_PARK_WINDOW) 1583 { 1584 /+ // Not working, the form disappears (probably stuck as a child). 1585 if(!GetParent(handle)) 1586 { 1587 //_style((_style() | WS_POPUP) & ~WS_CHILD); 1588 1589 SetParent(handle, getParkHwnd()); 1590 } 1591 +/ 1592 _crecreate(); 1593 } 1594 } 1595 1596 if(vis) 1597 { 1598 //show(); 1599 // Do it directly so that DFL code can't prevent it. 1600 doShow(); 1601 } 1602 } 1603 else 1604 { 1605 if(byes) 1606 wexstyle |= WS_EX_APPWINDOW; 1607 else 1608 wexstyle &= ~WS_EX_APPWINDOW; 1609 } 1610 } 1611 1612 /// ditto 1613 final @property bool showInTaskbar() // getter 1614 { 1615 return (_exStyle() & WS_EX_APPWINDOW) != 0; 1616 } 1617 1618 1619 /// 1620 final @property void sizingGrip(bool byes) // setter 1621 { 1622 if(grip == byes) 1623 return; 1624 1625 this.grip = byes; 1626 1627 if(isHandleCreated) 1628 { 1629 RECT rect; 1630 _getSizeGripArea(&rect); 1631 1632 InvalidateRect(hwnd, &rect, TRUE); 1633 } 1634 } 1635 1636 /// ditto 1637 final @property bool sizingGrip() // getter 1638 { 1639 return grip; 1640 } 1641 1642 deprecated alias sizingGrip sizeGrip; 1643 1644 1645 /// 1646 final @property void startPosition(FormStartPosition startpos) // setter 1647 { 1648 this.startpos = startpos; 1649 } 1650 1651 /// ditto 1652 final @property FormStartPosition startPosition() // getter 1653 { 1654 return startpos; 1655 } 1656 1657 1658 /// 1659 final @property void topMost(bool byes) // setter 1660 { 1661 /+ 1662 if(byes) 1663 _exStyle(_exStyle() | WS_EX_TOPMOST); 1664 else 1665 _exStyle(_exStyle() & ~WS_EX_TOPMOST); 1666 +/ 1667 1668 if(isHandleCreated) 1669 { 1670 SetWindowPos(handle, byes ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 1671 } 1672 else 1673 { 1674 if(byes) 1675 wexstyle |= WS_EX_TOPMOST; 1676 else 1677 wexstyle &= ~WS_EX_TOPMOST; 1678 } 1679 } 1680 1681 /// ditto 1682 final @property bool topMost() // getter 1683 { 1684 return (_exStyle() & WS_EX_TOPMOST) != 0; 1685 } 1686 1687 1688 /// 1689 final @property void transparencyKey(Color c) // setter 1690 { 1691 if(setLayeredWindowAttributes) 1692 { 1693 transKey = c; 1694 BYTE alpha = opacityToAlpha(opa); 1695 1696 if(c == Color.empty) // Disable 1697 { 1698 if(alpha == BYTE.max) 1699 _exStyle(_exStyle() & ~WS_EX_LAYERED); 1700 else 1701 setLayeredWindowAttributes(handle, 0, alpha, LWA_ALPHA); 1702 } 1703 else 1704 { 1705 _exStyle(_exStyle() | WS_EX_LAYERED); 1706 if(isHandleCreated) 1707 { 1708 //_exStyle(_exStyle() | WS_EX_LAYERED); 1709 if(alpha == BYTE.max) 1710 setLayeredWindowAttributes(handle, c.toRgb(), 0, LWA_COLORKEY); 1711 else 1712 setLayeredWindowAttributes(handle, c.toRgb(), alpha, LWA_COLORKEY | LWA_ALPHA); 1713 } 1714 } 1715 } 1716 } 1717 1718 /// ditto 1719 final @property Color transparencyKey() // getter 1720 { 1721 return transKey; 1722 } 1723 1724 1725 /// 1726 final @property void windowState(FormWindowState state) // setter 1727 { 1728 // Not sure if visible should be checked here.. 1729 if(isHandleCreated && visible) 1730 { 1731 final switch(state) 1732 { 1733 case FormWindowState.MAXIMIZED: 1734 ShowWindow(handle, SW_MAXIMIZE); 1735 //wstyle = wstyle & ~WS_MINIMIZE | WS_MAXIMIZE; 1736 break; 1737 1738 case FormWindowState.MINIMIZED: 1739 ShowWindow(handle, SW_MINIMIZE); 1740 //wstyle = wstyle | WS_MINIMIZE & ~WS_MAXIMIZE; 1741 break; 1742 1743 case FormWindowState.NORMAL: 1744 ShowWindow(handle, SW_RESTORE); 1745 //wstyle = wstyle & ~(WS_MINIMIZE | WS_MAXIMIZE); 1746 break; 1747 } 1748 //wstyle = GetWindowLongA(hwnd, GWL_STYLE); 1749 } 1750 else 1751 { 1752 final switch(state) 1753 { 1754 case FormWindowState.MAXIMIZED: 1755 _style(_style() & ~WS_MINIMIZE | WS_MAXIMIZE); 1756 break; 1757 1758 case FormWindowState.MINIMIZED: 1759 _style(_style() | WS_MINIMIZE & ~WS_MAXIMIZE); 1760 break; 1761 1762 case FormWindowState.NORMAL: 1763 _style(_style() & ~(WS_MINIMIZE | WS_MAXIMIZE)); 1764 break; 1765 } 1766 } 1767 } 1768 1769 /// ditto 1770 final @property FormWindowState windowState() // getter 1771 { 1772 LONG wl; 1773 //wl = wstyle = GetWindowLongA(hwnd, GWL_STYLE); 1774 wl = _style(); 1775 1776 if(wl & WS_MAXIMIZE) 1777 return FormWindowState.MAXIMIZED; 1778 else if(wl & WS_MINIMIZE) 1779 return FormWindowState.MINIMIZED; 1780 else 1781 return FormWindowState.NORMAL; 1782 } 1783 1784 1785 protected override void setVisibleCore(bool byes) 1786 { 1787 if(isHandleCreated) 1788 { 1789 if(visible == byes) 1790 return; 1791 1792 version(OLD_MODAL_CLOSE) 1793 { 1794 if(!wmodal) 1795 { 1796 if(byes) 1797 { 1798 cbits &= ~CBits.NOCLOSING; 1799 } 1800 } 1801 } 1802 1803 //if(!visible) 1804 if(byes) 1805 { 1806 version(DFL_NO_ZOMBIE_FORM) 1807 { 1808 } 1809 else 1810 { 1811 nozombie(); 1812 } 1813 1814 if(wstyle & WS_MAXIMIZE) 1815 { 1816 ShowWindow(hwnd, SW_MAXIMIZE); 1817 cbits |= CBits.VISIBLE; // ? 1818 wstyle |= WS_VISIBLE; // ? 1819 onVisibleChanged(EventArgs.empty); 1820 return; 1821 } 1822 /+else if(wstyle & WS_MINIMIZE) 1823 { 1824 ShowWindow(handle, SW_MINIMIZE); 1825 onVisibleChanged(EventArgs.empty); 1826 cbits |= CBits.VISIBLE; // ? 1827 wstyle |= WS_VISIBLE; // ? 1828 return; 1829 }+/ 1830 } 1831 } 1832 1833 return super.setVisibleCore(byes); 1834 } 1835 1836 1837 protected override void onVisibleChanged(EventArgs ea) 1838 { 1839 version(OLD_MODAL_CLOSE) 1840 { 1841 if(!wmodal) 1842 { 1843 if(visible) 1844 { 1845 cbits &= ~CBits.NOCLOSING; 1846 } 1847 } 1848 } 1849 1850 if(!(Application._compat & DflCompat.FORM_LOAD_096)) 1851 { 1852 if(visible) 1853 { 1854 if(!(cbits & CBits.FORMLOADED)) 1855 { 1856 cbits |= CBits.FORMLOADED; 1857 onLoad(EventArgs.empty); 1858 } 1859 } 1860 } 1861 1862 // Ensure Control.onVisibleChanged is called AFTER onLoad, so onLoad can set the selection first. 1863 super.onVisibleChanged(ea); 1864 } 1865 1866 1867 /// 1868 final void activate() 1869 { 1870 if(!isHandleCreated) 1871 return; 1872 1873 //if(!visible) 1874 // show(); // ? 1875 1876 version(NO_MDI) 1877 { 1878 } 1879 else 1880 { 1881 if(isMdiChild) 1882 { 1883 // Good, make sure client window proc handles it too. 1884 SendMessageA(mdiParent.mdiClient.handle, WM_MDIACTIVATE, cast(WPARAM)handle, 0); 1885 return; 1886 } 1887 } 1888 1889 //SetActiveWindow(handle); 1890 SetForegroundWindow(handle); 1891 } 1892 1893 1894 override void destroyHandle() 1895 { 1896 if(!isHandleCreated) 1897 return; 1898 1899 if(isMdiChild) 1900 DefMDIChildProcA(hwnd, WM_CLOSE, 0, 0); 1901 DestroyWindow(hwnd); 1902 } 1903 1904 1905 /// 1906 final void close() 1907 { 1908 if(wmodal) 1909 { 1910 /+ 1911 if(DialogResult.NONE == fresult) 1912 { 1913 fresult = DialogResult.CANCEL; 1914 } 1915 +/ 1916 1917 version(OLD_MODAL_CLOSE) 1918 { 1919 cbits |= CBits.NOCLOSING; 1920 //doHide(); 1921 setVisibleCore(false); 1922 //if(!visible) 1923 if(!wmodal) 1924 onClosed(EventArgs.empty); 1925 } 1926 else 1927 { 1928 scope CancelEventArgs cea = new CancelEventArgs; 1929 onClosing(cea); 1930 if(!cea.cancel) 1931 { 1932 wmodal = false; // Must be false or will result in recursion. 1933 destroyHandle(); 1934 } 1935 } 1936 return; 1937 } 1938 1939 scope CancelEventArgs cea = new CancelEventArgs; 1940 onClosing(cea); 1941 if(!cea.cancel) 1942 { 1943 //destroyHandle(); 1944 dispose(); 1945 } 1946 } 1947 1948 1949 /// 1950 final void layoutMdi(MdiLayout lay) 1951 { 1952 final switch(lay) 1953 { 1954 case MdiLayout.ARRANGE_ICONS: 1955 SendMessageA(handle, WM_MDIICONARRANGE, 0, 0); 1956 break; 1957 1958 case MdiLayout.CASCADE: 1959 SendMessageA(handle, WM_MDICASCADE, 0, 0); 1960 break; 1961 1962 case MdiLayout.TILE_HORIZONTAL: 1963 SendMessageA(handle, WM_MDITILE, MDITILE_HORIZONTAL, 0); 1964 break; 1965 1966 case MdiLayout.TILE_VERTICAL: 1967 SendMessageA(handle, WM_MDITILE, MDITILE_VERTICAL, 0); 1968 break; 1969 } 1970 } 1971 1972 1973 /// 1974 final void setDesktopBounds(int x, int y, int width, int height) 1975 { 1976 desktopBounds = Rect(x, y, width, height); 1977 } 1978 1979 1980 /// 1981 final void setDesktopLocation(int x, int y) 1982 { 1983 desktopLocation = Point(x, y); 1984 } 1985 1986 1987 /// 1988 final DialogResult showDialog() 1989 { 1990 // Use active window as the owner. 1991 this.sowner = GetActiveWindow(); 1992 if(this.sowner == this.hwnd) // Possible due to fast flash? 1993 this.sowner = HWND.init; 1994 showDialog2(); 1995 return fresult; 1996 } 1997 1998 /// ditto 1999 final DialogResult showDialog(IWindow iwsowner) 2000 { 2001 //this.sowner = iwsowner ? iwsowner.handle : GetActiveWindow(); 2002 if(!iwsowner) 2003 return showDialog(); 2004 this.sowner = iwsowner.handle; 2005 showDialog2(); 2006 return fresult; 2007 } 2008 2009 2010 // Used internally. 2011 package final void showDialog2() 2012 { 2013 version(DFL_NO_ZOMBIE_FORM) 2014 { 2015 } 2016 else 2017 { 2018 nozombie(); 2019 } 2020 2021 LONG wl = _style(); 2022 sownerEnabled = false; 2023 2024 if(wl & WS_DISABLED) 2025 { 2026 debug 2027 { 2028 throw new DflException("Unable to show dialog because it is disabled"); 2029 } 2030 no_show: 2031 throw new DflException("Unable to show dialog"); 2032 } 2033 2034 if(isHandleCreated) 2035 { 2036 //if(wl & WS_VISIBLE) 2037 if(visible) 2038 { 2039 if(!wmodal && owner && sowner == owner.handle) 2040 { 2041 } 2042 else 2043 { 2044 debug 2045 { 2046 throw new DflException("Unable to show dialog because it is already visible"); 2047 } 2048 else 2049 { 2050 goto no_show; 2051 } 2052 } 2053 } 2054 2055 if(sowner == hwnd) 2056 { 2057 bad_owner: 2058 debug 2059 { 2060 throw new DflException("Invalid dialog owner"); 2061 } 2062 else 2063 { 2064 goto no_show; 2065 } 2066 } 2067 2068 //owner = null; 2069 //_exStyle(_exStyle() & ~WS_EX_MDICHILD); 2070 //_style((_style() | WS_POPUP) & ~WS_CHILD); 2071 //SetParent(hwnd, sowner); 2072 } 2073 2074 try 2075 { 2076 if(sowner) 2077 { 2078 LONG owl = GetWindowLongA(sowner, GWL_STYLE); 2079 if(owl & WS_CHILD) 2080 goto bad_owner; 2081 2082 wowner = cast(Form)fromHandle(sowner); 2083 2084 if(!(owl & WS_DISABLED)) 2085 { 2086 sownerEnabled = true; 2087 EnableWindow(sowner, false); 2088 } 2089 } 2090 2091 show(); 2092 2093 wmodal = true; 2094 for(;;) 2095 { 2096 if(!Application.doEvents()) 2097 { 2098 wmodal = false; 2099 //dialogResult = DialogResult.ABORT; // ? 2100 // Leave it at DialogResult.NONE ? 2101 break; 2102 } 2103 if(!wmodal) 2104 break; 2105 /+ 2106 //assert(visible); 2107 if(!visible) 2108 { 2109 wmodal = false; 2110 break; 2111 } 2112 +/ 2113 Application.waitForEvent(); 2114 } 2115 } 2116 finally 2117 { 2118 if(sownerEnabled) 2119 { 2120 EnableWindow(sowner, true); // In case of exception. 2121 SetActiveWindow(sowner); 2122 //SetFocus(sowner); 2123 } 2124 2125 //if(!wmodal) 2126 // DestroyWindow(hwnd); 2127 2128 wmodal = false; 2129 sowner = HWND.init; 2130 2131 //hide(); 2132 // Do it directly so that DFL code can't prevent it. 2133 doHide(); 2134 2135 version(DFL_NO_ZOMBIE_FORM) 2136 { 2137 } 2138 else 2139 { 2140 Application.doEvents(); 2141 Application.zombieHwnd(this); // Zombie; allows this to be GC'd but keep state until then. 2142 } 2143 } 2144 } 2145 2146 2147 version(DFL_NO_ZOMBIE_FORM) 2148 { 2149 } 2150 else 2151 { 2152 package final bool nozombie() 2153 { 2154 if(this.hwnd) 2155 { 2156 if(!Application.lookupHwnd(this.hwnd)) 2157 { 2158 // Zombie! 2159 Application.unzombieHwnd(this); 2160 return true; 2161 } 2162 } 2163 return false; 2164 } 2165 } 2166 2167 2168 //EventHandler activated; 2169 Event!(Form, EventArgs) activated; /// 2170 //EventHandler deactivate; 2171 Event!(Form, EventArgs) deactivate; /// 2172 //EventHandler closed; 2173 Event!(Form, EventArgs) closed; /// 2174 //CancelEventHandler closing; 2175 Event!(Form, CancelEventArgs) closing; /// 2176 //EventHandler load; 2177 Event!(Form, EventArgs) load; /// 2178 2179 2180 /// 2181 protected void onActivated(EventArgs ea) 2182 { 2183 activated(this, ea); 2184 } 2185 2186 2187 /// 2188 protected void onDeactivate(EventArgs ea) 2189 { 2190 deactivate(this, ea); 2191 } 2192 2193 2194 /+ 2195 /// 2196 protected void onInputLanguageChanged(InputLanguageChangedEventArgs ilcea) 2197 { 2198 inputLanguageChanged(this, ilcea); 2199 } 2200 2201 2202 /// 2203 protected void onInputLanguageChanging(InputLanguageChangingEventArgs ilcea) 2204 { 2205 inputLanguageChanging(this, ilcea); 2206 } 2207 +/ 2208 2209 2210 /// 2211 protected void onLoad(EventArgs ea) 2212 { 2213 load(this, ea); 2214 2215 if(!(Application._compat & DflCompat.FORM_LOAD_096)) 2216 { 2217 // Needed anyway because MDI client form needs it. 2218 HWND hwfocus = GetFocus(); 2219 if(!hwfocus || !IsChild(hwnd, hwfocus)) 2220 _selectNextControl(this, null, true, true, true, false); 2221 } 2222 } 2223 2224 2225 private void _init() 2226 { 2227 _recalcClientSize(); 2228 2229 //wicon = new Icon(LoadIconA(HINSTANCE.init, IDI_APPLICATION), false); 2230 wicon = SystemIcons.application; 2231 transKey = Color.empty; 2232 } 2233 2234 2235 this() 2236 { 2237 super(); 2238 2239 mfilter = new FormMessageFilter(this); 2240 2241 // Default border: FormBorderStyle.SIZABLE. 2242 // Default visible: false. 2243 wstyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; 2244 wexstyle = /+ WS_EX_CONTROLPARENT | +/ WS_EX_WINDOWEDGE | WS_EX_APPWINDOW; 2245 cbits |= CBits.FORM; 2246 2247 _init(); 2248 } 2249 2250 2251 /+ 2252 // Used internally. 2253 this(HWND hwnd) 2254 { 2255 super(hwnd); 2256 _init(); 2257 } 2258 +/ 2259 2260 2261 protected override void wndProc(ref Message msg) 2262 { 2263 switch(msg.msg) 2264 { 2265 case WM_COMMAND: 2266 // Don't let Control handle the WM_COMMAND if it's a default or cancel button; 2267 // otherwise, the events will be fired twice. 2268 switch(LOWORD(msg.wParam)) 2269 { 2270 case IDOK: 2271 if(acceptBtn) 2272 { 2273 if(HIWORD(msg.wParam) == BN_CLICKED) 2274 acceptBtn.performClick(); 2275 return; 2276 } 2277 break; 2278 //return; 2279 2280 case IDCANCEL: 2281 if(cancelBtn) 2282 { 2283 if(HIWORD(msg.wParam) == BN_CLICKED) 2284 cancelBtn.performClick(); 2285 return; 2286 } 2287 break; 2288 //return; 2289 2290 default: 2291 } 2292 break; 2293 2294 //case WM_CREATE: // WM_NCCREATE seems like a better choice. 2295 case WM_NCCREATE: 2296 // Make sure Windows doesn't magically change the styles. 2297 SetWindowLongA(hwnd, GWL_EXSTYLE, wexstyle); 2298 SetWindowLongA(hwnd, GWL_STYLE, wstyle & ~WS_VISIBLE); 2299 2300 SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE 2301 | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); // Recalculate the frame. 2302 2303 _setSystemMenu(); 2304 break; 2305 2306 case WM_WINDOWPOSCHANGING: 2307 { 2308 WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam; 2309 2310 if(wp.flags & SWP_HIDEWINDOW) 2311 { 2312 if(wmodal) 2313 { 2314 version(OLD_MODAL_CLOSE) 2315 { 2316 scope CancelEventArgs cea = new CancelEventArgs; 2317 onClosing(cea); 2318 if(cea.cancel) 2319 { 2320 wp.flags &= ~SWP_HIDEWINDOW; // Cancel. 2321 } 2322 } 2323 else 2324 { 2325 wp.flags &= ~SWP_HIDEWINDOW; // Don't hide because we're destroying or canceling. 2326 close(); 2327 } 2328 } 2329 } 2330 2331 version(DFL_NO_ZOMBIE_FORM) 2332 { 2333 } 2334 else 2335 { 2336 if(wp.flags & SWP_SHOWWINDOW) 2337 { 2338 nozombie(); 2339 } 2340 } 2341 } 2342 break; 2343 2344 case WM_CLOSE: 2345 if(!recreatingHandle) 2346 { 2347 // Check for this first because defWndProc() will destroy the window. 2348 /+ // Moved to close(). 2349 // version(OLD_MODAL_CLOSE) ... 2350 fresult = DialogResult.CANCEL; 2351 if(wmodal) 2352 { 2353 doHide(); 2354 } 2355 else+/ 2356 { 2357 close(); 2358 } 2359 } 2360 return; 2361 2362 default: 2363 } 2364 2365 super.wndProc(msg); 2366 2367 switch(msg.msg) 2368 { 2369 case WM_NCHITTEST: 2370 //if(msg.result == HTCLIENT || msg.result == HTBORDER) 2371 if(msg.result != HTNOWHERE && msg.result != HTERROR) 2372 { 2373 if(_isPaintingSizeGrip()) 2374 { 2375 RECT rect; 2376 _getSizeGripArea(&rect); 2377 2378 Point pt; 2379 pt.x = LOWORD(msg.lParam); 2380 pt.y = HIWORD(msg.lParam); 2381 pt = pointToClient(pt); 2382 2383 if(pt.x >= rect.left && pt.y >= rect.top) 2384 msg.result = HTBOTTOMRIGHT; 2385 } 2386 } 2387 break; 2388 2389 case WM_ACTIVATE: 2390 switch(LOWORD(msg.wParam)) 2391 { 2392 case WA_ACTIVE: 2393 case WA_CLICKACTIVE: 2394 onActivated(EventArgs.empty); 2395 break; 2396 2397 case WA_INACTIVE: 2398 onDeactivate(EventArgs.empty); 2399 break; 2400 2401 default: 2402 } 2403 break; 2404 2405 case WM_WINDOWPOSCHANGING: 2406 { 2407 WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam; 2408 2409 /+ // Moved to WM_GETMINMAXINFO. 2410 if(minsz.width && minsz.height) 2411 { 2412 if(wp.cx < minsz.width) 2413 wp.cx = minsz.width; 2414 if(wp.cy < minsz.height) 2415 wp.cy = minsz.height; 2416 } 2417 if(maxsz.width && maxsz.height) 2418 { 2419 if(wp.cx > minsz.width) 2420 wp.cx = minsz.width; 2421 if(wp.cy > minsz.height) 2422 wp.cy = minsz.height; 2423 } 2424 +/ 2425 2426 /+ 2427 if(_closingvisible) 2428 { 2429 wp.flags &= ~SWP_HIDEWINDOW; 2430 } 2431 +/ 2432 2433 if(!(wp.flags & SWP_NOSIZE)) 2434 { 2435 if(_isPaintingSizeGrip()) 2436 { 2437 // This comparison is needed to prevent some painting glitches 2438 // when moving the window... 2439 if(width != wp.cx || height != wp.cy) 2440 { 2441 RECT rect; 2442 _getSizeGripArea(&rect); 2443 InvalidateRect(hwnd, &rect, TRUE); 2444 } 2445 } 2446 } 2447 2448 if(wp.flags & SWP_HIDEWINDOW) 2449 { 2450 if(sownerEnabled) 2451 { 2452 EnableWindow(sowner, true); 2453 SetActiveWindow(sowner); 2454 //SetFocus(sowner); 2455 } 2456 2457 wmodal = false; 2458 } 2459 } 2460 break; 2461 2462 case WM_GETMINMAXINFO: 2463 { 2464 super.wndProc(msg); 2465 2466 MINMAXINFO* mmi; 2467 mmi = cast(MINMAXINFO*)msg.lParam; 2468 2469 if(minsz.width && minsz.height) 2470 { 2471 if(mmi.ptMinTrackSize.x < minsz.width) 2472 mmi.ptMinTrackSize.x = minsz.width; 2473 if(mmi.ptMinTrackSize.y < minsz.height) 2474 mmi.ptMinTrackSize.y = minsz.height; 2475 } 2476 if(maxsz.width && maxsz.height) 2477 { 2478 if(mmi.ptMaxTrackSize.x > maxsz.width) 2479 mmi.ptMaxTrackSize.x = maxsz.width; 2480 if(mmi.ptMaxTrackSize.y > maxsz.height) 2481 mmi.ptMaxTrackSize.y = maxsz.height; 2482 } 2483 2484 // Do this again so that the user's preference isn't 2485 // outside the Windows valid min/max bounds. 2486 super.wndProc(msg); 2487 } 2488 return; 2489 2490 case WM_DESTROY: 2491 /+ 2492 if(_closingvisible) 2493 { 2494 assert(wstyle & WS_VISIBLE); 2495 } 2496 +/ 2497 if(!recreatingHandle) 2498 { 2499 if(!(cbits & CBits.NOCLOSING)) 2500 { 2501 onClosed(EventArgs.empty); 2502 } 2503 } 2504 break; 2505 2506 default: 2507 } 2508 } 2509 2510 2511 package final void _setSystemMenu() 2512 { 2513 HMENU hwm; 2514 assert(isHandleCreated); 2515 hwm = GetSystemMenu(handle, FALSE); 2516 2517 switch(formBorderStyle) 2518 { 2519 case FormBorderStyle.FIXED_3D: 2520 case FormBorderStyle.FIXED_SINGLE: 2521 case FormBorderStyle.FIXED_DIALOG: 2522 case FormBorderStyle.FIXED_TOOLWINDOW: 2523 // Fall through. 2524 case FormBorderStyle.NONE: 2525 RemoveMenu(hwm, SC_SIZE, MF_BYCOMMAND); 2526 RemoveMenu(hwm, SC_MAXIMIZE, MF_BYCOMMAND); 2527 //RemoveMenu(hwm, SC_MINIMIZE, MF_BYCOMMAND); 2528 RemoveMenu(hwm, SC_RESTORE, MF_BYCOMMAND); 2529 break; 2530 2531 //case FormBorderStyle.SIZABLE: 2532 //case FormBorderStyle.SIZABLE_TOOLWINDOW: 2533 default: 2534 } 2535 2536 if(!maximizeBox) 2537 { 2538 RemoveMenu(hwm, SC_MAXIMIZE, MF_BYCOMMAND); 2539 } 2540 if(!minimizeBox) 2541 { 2542 RemoveMenu(hwm, SC_MINIMIZE, MF_BYCOMMAND); 2543 } 2544 } 2545 2546 2547 package final void _resetSystemMenu() 2548 { 2549 assert(isHandleCreated); 2550 GetSystemMenu(handle, TRUE); // Reset. 2551 _setSystemMenu(); 2552 } 2553 2554 2555 /+ package +/ override void _destroying() // package 2556 { 2557 _removeFromOldOwner(); 2558 //wowner = null; 2559 wmdiparent = null; 2560 2561 Application.removeMessageFilter(mfilter); 2562 //mfilter = null; 2563 2564 version(DFL_NO_MENUS) 2565 { 2566 } 2567 else 2568 { 2569 if(wmenu) 2570 wmenu._setHwnd(HWND.init); 2571 } 2572 2573 super._destroying(); 2574 } 2575 2576 2577 /+ package +/ /+ protected +/ override int _rtype() // package 2578 { 2579 return isMdiChild ? 2 : 0; 2580 } 2581 2582 2583 package BOOL _isNonMdiChild(HWND hw) 2584 { 2585 assert(isHandleCreated); 2586 2587 if(!hw || hw == this.hwnd) 2588 return false; 2589 2590 if(IsChild(this.hwnd, hw)) 2591 { 2592 version(NO_MDI) 2593 { 2594 } 2595 else 2596 { 2597 if(mdiClient && mdiClient.isHandleCreated) 2598 { 2599 if(IsChild(mdiClient.hwnd, hw)) 2600 return false; // ! 2601 } 2602 } 2603 return true; 2604 } 2605 return false; 2606 } 2607 2608 2609 package HWND _lastSelBtn; // Last selected button (not necessarily focused), excluding accept button! 2610 package HWND _lastSel; // Last senected and focused control. 2611 package HWND _hadfocus; // Before being deactivated. 2612 2613 2614 // Returns if there was a selection. 2615 package final bool _selbefore() 2616 { 2617 bool wasselbtn = false; 2618 if(_lastSelBtn) 2619 { 2620 wasselbtn = true; 2621 //if(IsChild(this.hwnd, _lastSelBtn)) 2622 if(_isNonMdiChild(_lastSelBtn)) 2623 { 2624 auto lastctrl = Control.fromHandle(_lastSelBtn); 2625 if(lastctrl) 2626 { 2627 auto lastibc = cast(IButtonControl)lastctrl; 2628 if(lastibc) 2629 lastibc.notifyDefault(false); 2630 } 2631 } 2632 } 2633 return wasselbtn; 2634 } 2635 2636 package final void _selafter(Control ctrl, bool wasselbtn) 2637 { 2638 _lastSelBtn = _lastSelBtn.init; 2639 auto ibc = cast(IButtonControl)ctrl; 2640 if(ibc) 2641 { 2642 if(acceptButton) 2643 { 2644 if(ibc !is acceptButton) 2645 { 2646 acceptButton.notifyDefault(false); 2647 _lastSelBtn = ctrl.hwnd; 2648 } 2649 //else don't set _lastSelBtn to accept button. 2650 } 2651 else 2652 { 2653 _lastSelBtn = ctrl.hwnd; 2654 } 2655 2656 ibc.notifyDefault(true); 2657 } 2658 else 2659 { 2660 if(wasselbtn) // Only do it if there was a different button; don't keep doing this. 2661 { 2662 if(acceptButton) 2663 acceptButton.notifyDefault(true); 2664 } 2665 } 2666 } 2667 2668 package final void _seldeactivate() 2669 { 2670 if(!_selbefore()) 2671 { 2672 if(acceptButton) 2673 acceptButton.notifyDefault(false); 2674 } 2675 //_lastSel = GetFocus(); // Not reliable, especially when minimizing. 2676 } 2677 2678 package final void _selactivate() 2679 { 2680 if(_lastSel && _isNonMdiChild(_lastSel)) 2681 { 2682 Control ctrl = Control.fromChildHandle(_lastSel); 2683 if(ctrl && ctrl._hasSelStyle()) 2684 { 2685 auto ibc = cast(IButtonControl)ctrl; 2686 if(ibc) 2687 { 2688 //ibc.notifyDefault(true); 2689 ctrl.select(); 2690 return; 2691 } 2692 ctrl.select(); 2693 } 2694 else 2695 { 2696 SetFocus(ctrl.hwnd); 2697 } 2698 } 2699 if(acceptButton) 2700 { 2701 acceptButton.notifyDefault(true); 2702 } 2703 } 2704 2705 // Child can be nested at any level. 2706 package final void _selectChild(Control ctrl) 2707 { 2708 if(ctrl.canSelect) 2709 { 2710 bool wasselbtn = _selbefore(); 2711 2712 // Need to do some things, like select-all for edit. 2713 DefDlgProcA(this.hwnd, WM_NEXTDLGCTL, cast(WPARAM)ctrl.hwnd, MAKELPARAM(true, 0)); 2714 2715 _selafter(ctrl, wasselbtn); 2716 2717 _lastSel = ctrl.hwnd; 2718 } 2719 } 2720 2721 package final void _selectChild(HWND hw) 2722 { 2723 Control ctrl = Control.fromHandle(hw); 2724 if(ctrl) 2725 _selectChild(ctrl); 2726 } 2727 2728 2729 private void _selonecontrol() 2730 { 2731 HWND hwfocus = GetFocus(); 2732 if(!hwfocus || hwfocus == hwnd) 2733 { 2734 _selectNextControl(this, null, true, true, true, false); 2735 if(!GetFocus()) 2736 select(); 2737 } 2738 } 2739 2740 2741 package alias dfl.internal.utf.defDlgProc _defFormProc; 2742 2743 protected override void defWndProc(ref Message msg) 2744 { 2745 switch(msg.msg) 2746 { 2747 /+ 2748 // Not handled by defWndProc() anymore.. 2749 2750 case WM_PAINT: 2751 case WM_PRINT: 2752 case WM_PRINTCLIENT: 2753 case WM_ERASEBKGND: 2754 // DefDlgProc() doesn't let you use a custom background 2755 // color, so call the default window proc instead. 2756 super.defWndProc(msg); 2757 break; 2758 +/ 2759 2760 case WM_SETFOCUS: 2761 /+ 2762 { 2763 bool didf = false; 2764 enumChildWindows(msg.hWnd, 2765 (HWND hw) 2766 { 2767 auto wl = GetWindowLongA(hw, GWL_STYLE); 2768 if(((WS_VISIBLE | WS_TABSTOP) == ((WS_VISIBLE | WS_TABSTOP) & wl)) 2769 && !(WS_DISABLED & wl)) 2770 { 2771 DefDlgProcA(msg.hWnd, WM_NEXTDLGCTL, cast(WPARAM)hw, MAKELPARAM(true, 0)); 2772 didf = true; 2773 return FALSE; 2774 } 2775 return TRUE; 2776 }); 2777 if(!didf) 2778 SetFocus(msg.hWnd); 2779 } 2780 +/ 2781 //_selonecontrol(); 2782 2783 version(NO_MDI) 2784 { 2785 } 2786 else 2787 { 2788 if(isMdiChild) 2789 { 2790 // ? 2791 //msg.result = DefMDIChildProcA(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2792 msg.result = dfl.internal.utf.defMDIChildProc(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2793 return; 2794 } 2795 } 2796 2797 // Prevent DefDlgProc from getting this message because it'll focus controls it shouldn't. 2798 return; 2799 2800 case WM_NEXTDLGCTL: 2801 if(LOWORD(msg.lParam)) 2802 { 2803 _selectChild(cast(HWND)msg.wParam); 2804 } 2805 else 2806 { 2807 _dlgselnext(this, GetFocus(), msg.wParam != 0); 2808 } 2809 return; 2810 2811 case WM_ENABLE: 2812 if(msg.wParam) 2813 { 2814 if(GetActiveWindow() == msg.hWnd) 2815 { 2816 _selonecontrol(); 2817 } 2818 } 2819 break; 2820 2821 case WM_ACTIVATE: 2822 switch(LOWORD(msg.wParam)) 2823 { 2824 case WA_ACTIVE: 2825 case WA_CLICKACTIVE: 2826 _selactivate(); 2827 2828 /+ 2829 version(NO_MDI) 2830 { 2831 } 2832 else 2833 { 2834 if(isMdiContainer) 2835 { 2836 auto amc = getActiveMdiChild(); 2837 if(amc) 2838 amc._selactivate(); 2839 } 2840 } 2841 +/ 2842 break; 2843 2844 case WA_INACTIVE: 2845 /+ 2846 version(NO_MDI) 2847 { 2848 } 2849 else 2850 { 2851 if(isMdiContainer) 2852 { 2853 auto amc = getActiveMdiChild(); 2854 if(amc) 2855 amc._seldeactivate(); 2856 } 2857 } 2858 +/ 2859 2860 _seldeactivate(); 2861 break; 2862 2863 default: 2864 } 2865 return; 2866 2867 // Note: WM_MDIACTIVATE here is to the MDI child forms. 2868 case WM_MDIACTIVATE: 2869 if(cast(HWND)msg.lParam == hwnd) 2870 { 2871 _selactivate(); 2872 } 2873 else if(cast(HWND)msg.wParam == hwnd) 2874 { 2875 _seldeactivate(); 2876 } 2877 goto def_def; 2878 2879 default: def_def: 2880 version(NO_MDI) 2881 { 2882 //msg.result = DefDlgProcA(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2883 msg.result = _defFormProc(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2884 } 2885 else 2886 { 2887 if(mdiClient && mdiClient.isHandleCreated && msg.msg != WM_SIZE) 2888 //msg.result = DefFrameProcA(msg.hWnd, mdiClient.handle, msg.msg, msg.wParam, msg.lParam); 2889 msg.result = dfl.internal.utf.defFrameProc(msg.hWnd, mdiClient.handle, msg.msg, msg.wParam, msg.lParam); 2890 else if(isMdiChild) 2891 //msg.result = DefMDIChildProcA(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2892 msg.result = dfl.internal.utf.defMDIChildProc(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2893 else 2894 //msg.result = DefDlgProcA(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2895 msg.result = _defFormProc(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 2896 } 2897 } 2898 } 2899 2900 2901 protected: 2902 2903 /// 2904 void onClosing(CancelEventArgs cea) 2905 { 2906 closing(this, cea); 2907 } 2908 2909 2910 /// 2911 void onClosed(EventArgs ea) 2912 { 2913 closed(this, ea); 2914 } 2915 2916 2917 override void setClientSizeCore(int width, int height) 2918 { 2919 RECT r; 2920 2921 r.left = 0; 2922 r.top = 0; 2923 r.right = width; 2924 r.bottom = height; 2925 2926 LONG wl = _style(); 2927 version(DFL_NO_MENUS) 2928 { 2929 enum hasmenu = null; 2930 } 2931 else 2932 { 2933 auto hasmenu = wmenu; 2934 } 2935 AdjustWindowRectEx(&r, wl, !(wl & WS_CHILD) && hasmenu !is null, _exStyle()); 2936 2937 setBoundsCore(0, 0, r.right - r.left, r.bottom - r.top, BoundsSpecified.SIZE); 2938 } 2939 2940 2941 protected override void setBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) 2942 { 2943 if(isHandleCreated) 2944 { 2945 super.setBoundsCore(x, y, width, height, specified); 2946 } 2947 else 2948 { 2949 if(specified & BoundsSpecified.X) 2950 wrect.x = x; 2951 if(specified & BoundsSpecified.Y) 2952 wrect.y = y; 2953 if(specified & BoundsSpecified.WIDTH) 2954 { 2955 if(width < 0) 2956 width = 0; 2957 2958 wrect.width = width; 2959 } 2960 if(specified & BoundsSpecified.HEIGHT) 2961 { 2962 if(height < 0) 2963 height = 0; 2964 2965 wrect.height = height; 2966 } 2967 2968 _recalcClientSize(); 2969 } 2970 } 2971 2972 2973 // Must be called before handle creation. 2974 protected final void noMessageFilter() // package 2975 { 2976 nofilter = true; 2977 } 2978 2979 2980 version(NO_MDI) {} else 2981 { 2982 protected final @property MdiClient mdiClient() // getter 2983 { return _mdiClient; } 2984 } 2985 2986 2987 private: 2988 IButtonControl acceptBtn, cancelBtn; 2989 bool autoscale = true; 2990 Size autoscaleBase; 2991 DialogResult fresult = DialogResult.NONE; 2992 Icon wicon, wiconSm; 2993 version(DFL_NO_MENUS) 2994 { 2995 } 2996 else 2997 { 2998 MainMenu wmenu; 2999 } 3000 Size minsz, maxsz; // {0, 0} means none. 3001 bool wmodal = false; 3002 bool sownerEnabled; 3003 HWND sowner; 3004 double opa = 1.0; // Opacity. 3005 Color transKey; 3006 bool grip = false; 3007 FormStartPosition startpos = FormStartPosition.DEFAULT_LOCATION; 3008 FormMessageFilter mfilter; 3009 //const FormMessageFilter mfilter; 3010 bool _loaded = false; 3011 void delegate(Object sender, FormShortcutEventArgs ea)[Keys] _shortcuts; 3012 Form[] _owned, _mdiChildren; // Always set because they can be created and destroyed at any time. 3013 Form wowner = null, wmdiparent = null; 3014 //bool _closingvisible; 3015 bool nofilter = false; 3016 3017 version(NO_MDI) {} else 3018 { 3019 MdiClient _mdiClient = null; // null == not MDI container. 3020 } 3021 3022 3023 package static bool wantsAllKeys(HWND hwnd) 3024 { 3025 return (SendMessageA(hwnd, WM_GETDLGCODE, 0, 0) & 3026 DLGC_WANTALLKEYS) != 0; 3027 } 3028 3029 3030 private static class FormMessageFilter: IMessageFilter 3031 { 3032 protected bool preFilterMessage(ref Message m) 3033 { 3034 version(NO_MDI) 3035 enum bool mdistuff = false; 3036 else 3037 bool mdistuff = form.mdiClient && form.mdiClient.isHandleCreated 3038 && (form.mdiClient.handle == m.hWnd || IsChild(form.mdiClient.handle, m.hWnd)); 3039 3040 if(mdistuff) 3041 { 3042 } 3043 else if(m.hWnd == form.handle || IsChild(form.handle, m.hWnd)) 3044 { 3045 { 3046 HWND hwfocus = GetFocus(); 3047 // Don't need _isNonMdiChild here; mdistuff excludes MDI stuff. 3048 if(hwfocus != form._lastSel && IsChild(form.handle, hwfocus)) 3049 form._lastSel = hwfocus; // ? 3050 } 3051 3052 switch(m.msg) 3053 { 3054 // Process shortcut keys. 3055 // This should be better than TranslateAccelerator(). 3056 case WM_SYSKEYDOWN: 3057 case WM_KEYDOWN: 3058 { 3059 void delegate(Object sender, FormShortcutEventArgs ea)* ppressed; 3060 Keys k; 3061 3062 k = cast(Keys)m.wParam | Control.modifierKeys; 3063 ppressed = k in form._shortcuts; 3064 3065 if(ppressed) 3066 { 3067 scope FormShortcutEventArgs ea = new FormShortcutEventArgs(k); 3068 (*ppressed)(form, ea); 3069 return true; // Prevent. 3070 } 3071 } 3072 break; 3073 3074 default: 3075 } 3076 3077 switch(m.msg) 3078 { 3079 case WM_KEYDOWN: 3080 case WM_KEYUP: 3081 case WM_CHAR: 3082 switch(cast(Keys)m.wParam) 3083 { 3084 case Keys.ENTER: 3085 if(form.acceptButton) 3086 { 3087 dfl.internal.utf.isDialogMessage(form.handle, &m._winMsg); 3088 return true; // Prevent. 3089 } 3090 return false; 3091 3092 case Keys.ESCAPE: 3093 if(form.cancelButton) 3094 { 3095 //dfl.internal.utf.isDialogMessage(form.handle, &m._winMsg); // Closes the parent; bad for nested controls. 3096 if(m.hWnd == form.handle || IsChild(form.handle, m.hWnd)) 3097 { 3098 if(WM_KEYDOWN == m.msg) 3099 { 3100 Message mesc; 3101 mesc.hWnd = form.handle; 3102 mesc.msg = WM_COMMAND; 3103 mesc.wParam = MAKEWPARAM(IDCANCEL, 0); 3104 //mesc.lParam = form.cancelButton.handle; // handle isn't here, isn't guaranteed to be, and doesn't matter. 3105 form.wndProc(mesc); 3106 } 3107 return true; // Prevent. 3108 } 3109 } 3110 return false; 3111 3112 case Keys.UP, Keys.DOWN, Keys.RIGHT, Keys.LEFT: 3113 //if(dfl.internal.utf.isDialogMessage(form.handle, &m._winMsg)) // Stopped working after removing controlparent. 3114 // return true; // Prevent. 3115 { 3116 LRESULT dlgc; 3117 dlgc = SendMessageA(m.hWnd, WM_GETDLGCODE, 0, 0); 3118 if(!(dlgc & (DLGC_WANTALLKEYS | DLGC_WANTARROWS))) 3119 { 3120 if(WM_KEYDOWN == m.msg) 3121 { 3122 switch(cast(Keys)m.wParam) 3123 { 3124 case Keys.UP, Keys.LEFT: 3125 // Backwards... 3126 Control._dlgselnext(form, m.hWnd, false, false, true); 3127 break; 3128 case Keys.DOWN, Keys.RIGHT: 3129 // Forwards... 3130 Control._dlgselnext(form, m.hWnd, true, false, true); 3131 break; 3132 default: 3133 assert(0); 3134 } 3135 } 3136 return true; // Prevent. 3137 } 3138 } 3139 return false; // Continue. 3140 3141 case Keys.TAB: 3142 { 3143 LRESULT dlgc; 3144 Control cc; 3145 dlgc = SendMessageA(m.hWnd, WM_GETDLGCODE, 0, 0); 3146 cc = fromHandle(m.hWnd); 3147 if(cc) 3148 { 3149 if(cc._wantTabKey()) 3150 return false; // Continue. 3151 } 3152 else 3153 { 3154 if(dlgc & DLGC_WANTALLKEYS) 3155 return false; // Continue. 3156 } 3157 //if(dlgc & (DLGC_WANTTAB | DLGC_WANTALLKEYS)) 3158 if(dlgc & DLGC_WANTTAB) 3159 return false; // Continue. 3160 if(WM_KEYDOWN == m.msg) 3161 { 3162 if(GetKeyState(VK_SHIFT) & 0x8000) 3163 { 3164 // Backwards... 3165 //DefDlgProcA(form.handle, WM_NEXTDLGCTL, 1, MAKELPARAM(FALSE, 0)); 3166 _dlgselnext(form, m.hWnd, false); 3167 } 3168 else 3169 { 3170 // Forwards... 3171 //DefDlgProcA(form.handle, WM_NEXTDLGCTL, 0, MAKELPARAM(FALSE, 0)); 3172 _dlgselnext(form, m.hWnd, true); 3173 } 3174 } 3175 } 3176 return true; // Prevent. 3177 3178 default: 3179 } 3180 break; 3181 3182 case WM_SYSCHAR: 3183 { 3184 /+ 3185 LRESULT dlgc; 3186 dlgc = SendMessageA(m.hWnd, WM_GETDLGCODE, 0, 0); 3187 /+ // Mnemonics bypass want-all-keys! 3188 if(dlgc & DLGC_WANTALLKEYS) 3189 return false; // Continue. 3190 +/ 3191 +/ 3192 3193 bool pmnemonic(HWND hw) 3194 { 3195 Control cc = Control.fromHandle(hw); 3196 //cprintf("mnemonic for "); 3197 if(!cc) 3198 { 3199 // To-do: check dlgcode for static/button and process. 3200 return false; 3201 } 3202 //cprintf("'%.*s' ", cc.name); 3203 return cc._processMnemonic(cast(dchar)m.wParam); 3204 } 3205 3206 bool foundmhw = false; 3207 bool foundmn = false; 3208 eachGoodChildHandle(form.handle, 3209 (HWND hw) 3210 { 3211 if(foundmhw) 3212 { 3213 if(pmnemonic(hw)) 3214 { 3215 foundmn = true; 3216 return false; // Break. 3217 } 3218 } 3219 else 3220 { 3221 if(hw == m.hWnd) 3222 foundmhw = true; 3223 } 3224 return true; // Continue. 3225 }); 3226 if(foundmn) 3227 return true; // Prevent. 3228 3229 if(!foundmhw) 3230 { 3231 // Didn't find current control, so go from top-to-bottom. 3232 eachGoodChildHandle(form.handle, 3233 (HWND hw) 3234 { 3235 if(pmnemonic(hw)) 3236 { 3237 foundmn = true; 3238 return false; // Break. 3239 } 3240 return true; // Continue. 3241 }); 3242 } 3243 else 3244 { 3245 // Didn't find mnemonic after current control, so go from top-to-this. 3246 eachGoodChildHandle(form.handle, 3247 (HWND hw) 3248 { 3249 if(pmnemonic(hw)) 3250 { 3251 foundmn = true; 3252 return false; // Break. 3253 } 3254 if(hw == m.hWnd) 3255 return false; // Break. 3256 return true; // Continue. 3257 }); 3258 } 3259 if(foundmn) 3260 return true; // Prevent. 3261 } 3262 break; 3263 3264 case WM_LBUTTONUP: 3265 case WM_MBUTTONUP: 3266 case WM_RBUTTONUP: 3267 if(m.hWnd != form.hwnd) 3268 { 3269 Control ctrl = Control.fromChildHandle(m.hWnd); 3270 if(ctrl.focused && ctrl.canSelect) 3271 { 3272 bool wasselbtn = form._selbefore(); 3273 form._selafter(ctrl, wasselbtn); 3274 } 3275 } 3276 break; 3277 3278 default: 3279 } 3280 } 3281 3282 return false; // Continue. 3283 } 3284 3285 3286 this(Form form) 3287 { 3288 this.form = form; 3289 } 3290 3291 3292 private: 3293 Form form; 3294 } 3295 3296 3297 /+ 3298 package final bool _dlgescape() 3299 { 3300 if(cancelBtn) 3301 { 3302 cancelBtn.performClick(); 3303 return true; 3304 } 3305 return false; 3306 } 3307 +/ 3308 3309 3310 final void _recalcClientSize() 3311 { 3312 RECT r; 3313 r.left = 0; 3314 r.right = wrect.width; 3315 r.top = 0; 3316 r.bottom = wrect.height; 3317 3318 LONG wl = _style(); 3319 version(DFL_NO_MENUS) 3320 { 3321 enum hasmenu = null; 3322 } 3323 else 3324 { 3325 auto hasmenu = wmenu; 3326 } 3327 AdjustWindowRectEx(&r, wl, hasmenu !is null && !(wl & WS_CHILD), _exStyle()); 3328 3329 // Subtract the difference. 3330 wclientsz = Size(wrect.width - ((r.right - r.left) - wrect.width), wrect.height - ((r.bottom - r.top) - wrect.height)); 3331 } 3332 } 3333 3334 3335 version(NO_MDI) {} else 3336 { 3337 /// 3338 class MdiClient: ControlSuperClass 3339 { 3340 private this() 3341 { 3342 _initMdiclient(); 3343 3344 wclassStyle = mdiclientClassStyle; 3345 wstyle |= WS_VSCROLL | WS_HSCROLL; 3346 wexstyle |= WS_EX_CLIENTEDGE /+ | WS_EX_CONTROLPARENT +/; 3347 3348 dock = DockStyle.FILL; 3349 } 3350 3351 3352 /// 3353 @property void borderStyle(BorderStyle bs) // setter 3354 { 3355 final switch(bs) 3356 { 3357 case BorderStyle.FIXED_3D: 3358 _style(_style() & ~WS_BORDER); 3359 _exStyle(_exStyle() | WS_EX_CLIENTEDGE); 3360 break; 3361 3362 case BorderStyle.FIXED_SINGLE: 3363 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 3364 _style(_style() | WS_BORDER); 3365 break; 3366 3367 case BorderStyle.NONE: 3368 _style(_style() & ~WS_BORDER); 3369 _exStyle(_exStyle() & ~WS_EX_CLIENTEDGE); 3370 break; 3371 } 3372 3373 if(isHandleCreated) 3374 { 3375 redrawEntire(); 3376 } 3377 } 3378 3379 /// ditto 3380 @property BorderStyle borderStyle() // getter 3381 { 3382 if(_exStyle() & WS_EX_CLIENTEDGE) 3383 return BorderStyle.FIXED_3D; 3384 else if(_style() & WS_BORDER) 3385 return BorderStyle.FIXED_SINGLE; 3386 return BorderStyle.NONE; 3387 } 3388 3389 3390 /// 3391 final @property void hScroll(bool byes) // setter 3392 { 3393 LONG wl = _style(); 3394 if(byes) 3395 wl |= WS_HSCROLL; 3396 else 3397 wl &= ~WS_HSCROLL; 3398 _style(wl); 3399 3400 if(isHandleCreated) 3401 redrawEntire(); 3402 } 3403 3404 3405 /// ditto 3406 final @property bool hScroll() // getter 3407 { 3408 return (_style() & WS_HSCROLL) != 0; 3409 } 3410 3411 3412 /// 3413 final @property void vScroll(bool byes) // setter 3414 { 3415 LONG wl = _style(); 3416 if(byes) 3417 wl |= WS_VSCROLL; 3418 else 3419 wl &= ~WS_VSCROLL; 3420 _style(wl); 3421 3422 if(isHandleCreated) 3423 redrawEntire(); 3424 } 3425 3426 3427 /+ 3428 override void createHandle() 3429 { 3430 //if(created) 3431 if(isHandleCreated) 3432 return; 3433 3434 if(!wowner || killing) 3435 { 3436 create_err: 3437 throw new DflException("MDI client creation failure"); 3438 } 3439 3440 CLIENTCREATESTRUCT ccs; 3441 ccs.hWindowMenu = HMENU.init; //wowner.menu ? wowner.menu.handle : HMENU.init; 3442 ccs.idFirstChild = 10000; 3443 3444 Application.creatingControl(this); 3445 hwnd = dfl.internal.utf.createWindowEx(wexstyle, MDICLIENT_CLASSNAME, wtext, wstyle, wrect.x, wrect.y, 3446 wrect.width, wrect.height, wparent.handle, HMENU.init, Application.getInstance(), &ccs); 3447 if(!hwnd) 3448 goto create_err; 3449 3450 onHandleCreated(EventArgs.empty); 3451 } 3452 +/ 3453 3454 3455 protected override void createParams(ref CreateParams cp) 3456 { 3457 if(!wparent) 3458 throw new DflException("Invalid MDI child parent"); 3459 3460 super.createParams(cp); 3461 3462 cp.className = MDICLIENT_CLASSNAME; 3463 3464 ccs.hWindowMenu = HMENU.init; //wowner.menu ? wowner.menu.handle : HMENU.init; 3465 ccs.idFirstChild = 10000; 3466 cp.param = &ccs; 3467 } 3468 3469 3470 static @property Color defaultBackColor() // getter 3471 { 3472 return Color.systemColor(COLOR_APPWORKSPACE); 3473 } 3474 3475 3476 override @property Color backColor() // getter 3477 { 3478 if(Color.empty == backc) 3479 return defaultBackColor; 3480 return backc; 3481 } 3482 3483 alias Control.backColor backColor; // Overload. 3484 3485 3486 /+ 3487 static @property Color defaultForeColor() //getter 3488 { 3489 return Color.systemColor(COLOR_WINDOWTEXT); 3490 } 3491 +/ 3492 3493 3494 protected override void prevWndProc(ref Message msg) 3495 { 3496 //msg.result = CallWindowProcA(mdiclientPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 3497 msg.result = dfl.internal.utf.callWindowProc(mdiclientPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 3498 } 3499 3500 3501 private: 3502 CLIENTCREATESTRUCT ccs; 3503 } 3504 } 3505 3506 3507 private: 3508 3509 version(DFL_PARK_WINDOW) 3510 { 3511 HWND getParkHwnd() 3512 { 3513 if(!_hwPark) 3514 { 3515 synchronized 3516 { 3517 if(!_hwPark) 3518 _makePark(); 3519 } 3520 } 3521 return _hwPark; 3522 } 3523 3524 3525 void _makePark() 3526 { 3527 WNDCLASSEXA wce; 3528 wce.cbSize = wce.sizeof; 3529 wce.style = CS_DBLCLKS; 3530 wce.lpszClassName = PARK_CLASSNAME.ptr; 3531 wce.lpfnWndProc = &DefWindowProcA; 3532 wce.hInstance = Application.getInstance(); 3533 3534 if(!RegisterClassExA(&wce)) 3535 { 3536 debug(APP_PRINT) 3537 cprintf("RegisterClassEx() failed for park class.\n"); 3538 3539 init_err: 3540 //throw new DflException("Unable to initialize forms library"); 3541 throw new DflException("Unable to create park window"); 3542 } 3543 3544 _hwPark = CreateWindowExA(WS_EX_TOOLWINDOW, PARK_CLASSNAME.ptr, "", 3545 WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, 3546 HWND.init, HMENU.init, wce.hInstance, null); 3547 if(!_hwPark) 3548 { 3549 debug(APP_PRINT) 3550 cprintf("CreateWindowEx() failed for park window.\n"); 3551 3552 goto init_err; 3553 } 3554 } 3555 3556 3557 enum PARK_CLASSNAME = "DFL_Parking"; 3558 3559 HWND _hwPark; // Don't use directly; use getParkHwnd(). 3560 } 3561