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 }