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