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