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