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