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