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