1 /** DGui project file.
2 
3 Copyright: Trogu Antonio Davide 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Trogu Antonio Davide
8 */
9 module dgui.core.controls.control;
10 
11 public import dgui.core.interfaces.idisposable;
12 public import dgui.core.events.controlcodeeventargs;
13 public import dgui.core.events.scrolleventargs;
14 public import dgui.core.events.mouseeventargs;
15 public import dgui.core.events.painteventargs;
16 public import dgui.core.events.keyeventargs;
17 public import dgui.core.events.event;
18 public import dgui.core.windowclass;
19 public import dgui.core.message;
20 public import dgui.core.charset;
21 public import dgui.core.winapi;
22 public import dgui.core.exception;
23 public import dgui.core.geometry;
24 public import dgui.core.collection;
25 public import dgui.core.handle;
26 public import dgui.core.utils;
27 public import dgui.core.tag;
28 public import dgui.contextmenu;
29 public import dgui.canvas;
30 
31 enum DockStyle: ubyte
32 {
33 	none 	= 0,
34 	left 	= 1,
35 	top 	= 2,
36 	right 	= 4,
37 	bottom 	= 8,
38 	fill 	= 16,
39 }
40 
41 enum PositionSpecified
42 {
43 	position = 0,
44 	size     = 1,
45 	all      = 2,
46 }
47 
48 enum ControlBits: ulong
49 {
50 	none          		= 0,
51 	erased        		= 1,
52 	mouseEnter   		= 2,
53 	canNotify   		= 4,
54 	modalControl 		= 8,   // For Modal Dialogs
55 	doubleBuffered		= 16,  // Use DGui's double buffered routine to draw components (be careful with this one!)
56 	ownClickMsg 	    = 32,  // Does the component Handles click itself?
57 	cannotAddChild	= 64,  // The child window will not be added to the parent's child controls' list
58 	useCachedText		= 128, // Does not send WM_SETTEXT / WM_GETTEXT messages, but it uses it's internal variable only.
59 }
60 
61 enum BorderStyle: ubyte
62 {
63 	none 		 = 0,
64 	manual 		 = 1, // Internal Use
65 	fixedSingle = 2,
66 	fixed3d	 = 4,
67 }
68 
69 struct CreateControlParams
70 {
71 	string className;
72 	string superclassName; //Used in Superlassing
73 	Color defaultBackColor;
74 	Color defaultForeColor;
75 	Cursor defaultCursor;
76 	ClassStyles classStyle;
77 }
78 
79 abstract class Control: Handle!(HWND), IDisposable
80 {
81 	private ContextMenu _menu;
82 	private Control _parent;
83 	private ContextMenu _ctxMenu;
84 	private Font _defaultFont;
85 	private Cursor _defaultCursor;
86 	private HBRUSH _foreBrush;
87 	private HBRUSH _backBrush;
88 	private uint _extendedStyle = 0;
89 	private uint _style = WS_VISIBLE;
90 	protected string _text;
91 	protected Rect _bounds;
92 	protected Color _foreColor;
93 	protected Color _backColor;
94 	protected DockStyle _dock = DockStyle.none;
95 	protected ControlBits _cBits = ControlBits.canNotify;
96 
97 	public Event!(Control, PaintEventArgs) paint;
98 	public Event!(Control, EventArgs) focusChanged;
99 	public Event!(Control, KeyCharEventArgs) keyChar;
100 	public Event!(Control, ControlCodeEventArgs) controlCode;
101 	public Event!(Control, KeyEventArgs) keyDown;
102 	public Event!(Control, KeyEventArgs) keyUp;
103 	public Event!(Control, MouseEventArgs) doubleClick;
104 	public Event!(Control, MouseEventArgs) mouseKeyDown;
105 	public Event!(Control, MouseEventArgs) mouseKeyUp;
106 	public Event!(Control, MouseEventArgs) mouseMove;
107 	public Event!(Control, MouseEventArgs) mouseEnter;
108 	public Event!(Control, MouseEventArgs) mouseLeave;
109 	public Event!(Control, EventArgs) visibleChanged;
110 	public Event!(Control, EventArgs) handleCreated;
111 	public Event!(Control, EventArgs) resize;
112 	public Event!(Control, EventArgs) click;
113 
114     mixin tagProperty; // Insert tag() property in Control
115 
116 	public this()
117 	{
118 
119 	}
120 
121 	public ~this()
122 	{
123 		this.dispose();
124 	}
125 
126 	public void dispose()
127 	{
128 		if(this._backBrush)
129 		{
130 			DeleteObject(this._backBrush);
131 		}
132 
133 		if(this._foreBrush)
134 		{
135 			DeleteObject(this._foreBrush);
136 		}
137 
138 		if(this._handle)
139 		{
140 			/* From MSDN: Destroys the specified window.
141 			   The function sends WM_DESTROY and WM_NCDESTROY messages to the window
142 			   to deactivate it and remove the keyboard focus from it.
143 			   The function also destroys the window's menu, flushes the thread message queue,
144 			   destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain
145 			   (if the window is at the top of the viewer chain). If the specified window is a parent
146 			   or owner window, DestroyWindow automatically destroys the associated child or owned
147 			   windows when it destroys the parent or owner window. The function first destroys child
148 			   or owned windows, and then it destroys the parent or owner window
149 			*/
150 
151 			DestroyWindow(this._handle);
152 		}
153 
154 		this._handle = null;
155 	}
156 
157 
158 	public static void convertRect(ref Rect rect, Control from, Control to)
159 	{
160 		MapWindowPoints(from ? from.handle : null, to ? to.handle : null, cast(POINT*)&rect.rect, 2);
161 	}
162 
163 	public static void convertPoint(ref Point pt, Control from, Control to)
164 	{
165 		MapWindowPoints(from ? from.handle : null, to ? to.handle : null, &pt.point, 1);
166 	}
167 
168 	public static void convertSize(ref Size sz, Control from, Control to)
169 	{
170 		MapWindowPoints(from ? from.handle : null, to ? to.handle : null, cast(POINT*)&sz.size, 1);
171 	}
172 
173 	@property public final Rect bounds()
174 	{
175 		return this._bounds;
176  	}
177 
178 	@property public void bounds(Rect rect)
179 	{
180 		this._bounds = rect;
181 
182 		if(this.created)
183 		{
184 			this.setWindowPos(rect.left, rect.top, rect.width, rect.height);
185 		}
186 	}
187 
188 	@property public final BorderStyle borderStyle()
189 	{
190 		if(this.getExStyle() & WS_EX_CLIENTEDGE)
191 		{
192 			return BorderStyle.fixed3d;
193 		}
194 		else if(this.getStyle() & WS_BORDER)
195 		{
196 			return BorderStyle.fixedSingle;
197 		}
198 
199 		return BorderStyle.none;
200 	}
201 
202 	@property public final void borderStyle(BorderStyle bs)
203 	{
204 		switch(bs)
205 		{
206 			case BorderStyle.fixed3d:
207 				this.setStyle(WS_BORDER, false);
208 				this.setExStyle(WS_EX_CLIENTEDGE, true);
209 				break;
210 
211 			case BorderStyle.fixedSingle:
212 				this.setStyle(WS_BORDER, true);
213 				this.setExStyle(WS_EX_CLIENTEDGE, false);
214 				break;
215 
216 			case BorderStyle.none:
217 				this.setStyle(WS_BORDER, false);
218 				this.setExStyle(WS_EX_CLIENTEDGE, false);
219 				break;
220 
221 			default:
222 				assert(0, "Unknown Border Style");
223 				//break;
224 		}
225 	}
226 
227 	@property public final Control parent()
228 	{
229 		return this._parent;
230 	}
231 
232 	@property public void parent(Control c)
233 	{
234 		this._parent = c;
235 
236 		if(!Control.hasBit(this._cBits, ControlBits.cannotAddChild))
237 		{
238 			c.sendMessage(DGUI_ADDCHILDCONTROL, winCast!(WPARAM)(this), 0);
239 		}
240 	}
241 
242 	@property public final Control topLevelControl()
243 	{
244 		Control topCtrl = this;
245 
246 		while(topCtrl.parent)
247 		{
248 			topCtrl = topCtrl.parent;
249 		}
250 
251 		return topCtrl;
252 	}
253 
254 	public final Canvas createCanvas()
255 	{
256 		return Canvas.fromHDC(GetDC(this._handle));
257 	}
258 
259 	public final void focus()
260 	{
261 		if(this.created)
262 		{
263 			SetFocus(this._handle);
264 		}
265 	}
266 
267 	@property public bool focused()
268 	{
269 		if(this.created)
270 		{
271 			return GetFocus() == this._handle;
272 		}
273 
274 		return false;
275 	}
276 
277 	@property public final Color backColor()
278 	{
279 		return this._backColor;
280 	}
281 
282 	@property public final void backColor(Color c)
283 	{
284 		if(this._backBrush)
285 		{
286 			DeleteObject(this._backBrush);
287 		}
288 
289 		this._backColor = c;
290 		this._backBrush = CreateSolidBrush(c.colorref);
291 
292 		if(this.created)
293 		{
294 			this.invalidate();
295 		}
296 	}
297 
298 	@property public final Color foreColor()
299 	{
300 		return this._foreColor;
301 	}
302 
303 	@property public final void foreColor(Color c)
304 	{
305 		if(this._foreBrush)
306 		{
307 			DeleteObject(this._foreBrush);
308 		}
309 
310 		this._foreColor = c;
311 		this._foreBrush = CreateSolidBrush(c.colorref);
312 
313 		if(this.created)
314 		{
315 			this.invalidate();
316 		}
317 	}
318 
319 	@property public final bool scrollBars()
320 	{
321 		return cast(bool)(this.getStyle() & (WS_VSCROLL | WS_HSCROLL));
322 	}
323 
324 	@property public final void scrollBars(bool b)
325 	{
326 		this.setStyle(WS_VSCROLL | WS_HSCROLL, true);
327 	}
328 
329 	@property public string text()
330 	{
331 		if(this.created && !Control.hasBit(this._cBits, ControlBits.useCachedText))
332 		{
333 			return getWindowText(this._handle);
334 		}
335 
336 		return this._text;
337 	}
338 
339 	@property public void text(string s) //Overwritten in TabPage
340 	{
341 		this._text = s;
342 
343 		if(this.created && !Control.hasBit(this._cBits, ControlBits.useCachedText))
344 		{
345 			Control.setBit(this._cBits, ControlBits.canNotify, false); //Do not trigger TextChanged Event
346 			setWindowText(this._handle, s);
347 			Control.setBit(this._cBits, ControlBits.canNotify, true);
348 		}
349 	}
350 
351 	@property public final Font font()
352 	{
353 		if(!this._defaultFont)
354 		{
355 			/* Font is not set, use Windows Font */
356 			this._defaultFont = SystemFonts.windowsFont;
357 		}
358 
359 		return this._defaultFont;
360 	}
361 
362 	@property public final void font(Font f)
363 	{
364 		if(this.created)
365 		{
366 			if(this._defaultFont)
367 			{
368 				this._defaultFont.dispose();
369 			}
370 
371 			this.sendMessage(WM_SETFONT, cast(WPARAM)f.handle, true);
372 		}
373 
374 		this._defaultFont = f;
375 	}
376 
377 	@property public final Point position()
378 	{
379 		return this.bounds.position;
380 	}
381 
382 	@property public final void position(Point pt)
383 	{
384 		this._bounds.position = pt;
385 
386 		if(this.created)
387 		{
388 			this.setPosition(pt.x, pt.y);
389 		}
390 	}
391 
392 	@property public final Size size()
393 	{
394 		return this._bounds.size;
395  	}
396 
397 	@property public final void size(Size sz)
398 	{
399 		this._bounds.size = sz;
400 
401 		if(this.created)
402 		{
403 			this.setSize(sz.width, sz.height);
404 		}
405 	}
406 
407 	@property public final Size clientSize()
408 	{
409 		if(this.created)
410 		{
411 			Rect r = void;
412 
413 			GetClientRect(this._handle, &r.rect);
414 			return r.size;
415 		}
416 
417 		return this.size;
418 	}
419 
420 	@property public final ContextMenu contextMenu()
421 	{
422 		return this._ctxMenu;
423 	}
424 
425 	@property public final void contextMenu(ContextMenu cm)
426 	{
427 		if(this._ctxMenu !is cm)
428 		{
429 			if(this._ctxMenu)
430 			{
431 				this._ctxMenu.dispose();
432 			}
433 
434 			this._ctxMenu = cm;
435 		}
436 	}
437 
438 	@property public final int width()
439 	{
440 		return this._bounds.width;
441 	}
442 
443 	@property public final void width(int w)
444 	{
445 		this._bounds.width = w;
446 
447 		if(this.created)
448 		{
449 			this.setSize(w, this._bounds.height);
450 		}
451 	}
452 
453 	@property public final int height()
454 	{
455 		return this._bounds.height;
456 	}
457 
458 	@property public final void height(int h)
459 	{
460 		this._bounds.height = h;
461 
462 		if(this.created)
463 		{
464 			this.setSize(this._bounds.width, h);
465 		}
466 	}
467 
468 	@property public final DockStyle dock()
469 	{
470 		return this._dock;
471 	}
472 
473 	@property public final void dock(DockStyle ds)
474 	{
475 		this._dock = ds;
476 	}
477 
478 	@property public final Cursor cursor()
479 	{
480 		if(this.created)
481 		{
482 			return Cursor.fromHCURSOR(cast(HCURSOR)GetClassLongW(this._handle, GCL_HCURSOR), false);
483 		}
484 
485 		return this._defaultCursor;
486 	}
487 
488 	@property public final void cursor(Cursor c)
489 	{
490 		if(this._defaultCursor)
491 		{
492 			this._defaultCursor.dispose();
493 		}
494 
495 		this._defaultCursor = c;
496 
497 		if(this.created)
498 		{
499 			this.sendMessage(WM_SETCURSOR, cast(WPARAM)this._handle, 0);
500 		}
501 	}
502 
503 	@property public final bool visible()
504 	{
505 		return cast(bool)(this.getStyle() & WS_VISIBLE);
506 	}
507 
508 	@property public final void visible(bool b)
509 	{
510 		b ? this.show() : this.hide();
511 	}
512 
513 	@property public final bool enabled()
514 	{
515 		return !(this.getStyle() & WS_DISABLED);
516 	}
517 
518 	@property public final void enabled(bool b)
519 	{
520 		if(this.created)
521 		{
522 			EnableWindow(this._handle, b);
523 		}
524 		else
525 		{
526 			this.setStyle(WS_DISABLED, !b);
527 		}
528 	}
529 
530 	public void show()
531 	{
532 		if(this.created)
533 		{
534 			SetWindowPos(this._handle, null, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
535 
536 			if(this._parent)
537 			{
538 				this._parent.sendMessage(DGUI_DOLAYOUT, 0, 0);
539 			}
540 		}
541 		else
542 		{
543 			this.setStyle(WS_VISIBLE, true);
544 			this.create(); //The component is not created, create it now
545 		}
546 	}
547 
548 	public final void hide()
549 	{
550 		if(this.created)
551 		{
552 			SetWindowPos(this._handle, null, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW);
553 		}
554 		else
555 		{
556 			this.setStyle(WS_VISIBLE, false);
557 		}
558 	}
559 
560 	public final void redraw()
561 	{
562 		SetWindowPos(this._handle, null, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
563 	}
564 
565 	public final void invalidate()
566 	{
567 		RedrawWindow(this._handle, null, null, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
568 	}
569 
570 	public final void sendMessage(ref Message m)
571 	{
572 		/*
573 		 * SendMessage() emulation: it allows to send messages even if the control is not created,
574 		 * it is useful in order to send custom messages to components.
575 		 */
576 
577 		if(m.msg >= DGUI_BASE) /* DGui's Custom Message Handling */
578 		{
579 			this.onDGuiMessage(m);
580 		}
581 		else /* Window Procedure Message Handling */
582 		{
583 			//Control.setBit(this._cBits, ControlBits.canNotify, false);
584 			this.wndProc(m);
585 			//Control.setBit(this._cBits, ControlBits.canNotify, true);
586 		}
587 	}
588 
589 	public final uint sendMessage(uint msg, WPARAM wParam, LPARAM lParam)
590 	{
591 		Message m = Message(this._handle, msg, wParam, lParam);
592 		this.sendMessage(m);
593 
594 		return m.result;
595 	}
596 
597 	extern(Windows) package static LRESULT msgRouter(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam)
598 	{
599 		if(msg == WM_NCCREATE)
600 		{
601 			/*
602 			 * TRICK: Id == hWnd
603 			 * ---
604 			 * Inizializzazione Componente
605 			 */
606 
607 			CREATESTRUCTW* pCreateStruct = cast(CREATESTRUCTW*)lParam;
608 			LPARAM param = cast(LPARAM)pCreateStruct.lpCreateParams;
609 			SetWindowLongW(hWnd, GWL_USERDATA, param);
610 			SetWindowLongW(hWnd, GWL_ID, cast(uint)hWnd);
611 
612 			Control theThis = winCast!(Control)(param);
613 			theThis._handle = hWnd;	//Assign handle.
614 		}
615 
616 		Control theThis = winCast!(Control)(GetWindowLongW(hWnd, GWL_USERDATA));
617 		Message m = Message(hWnd, msg, wParam, lParam);
618 
619 		if(theThis)
620 		{
621 			theThis.wndProc(m);
622 		}
623 		else
624 		{
625 			Control.defWindowProc(m);
626 		}
627 
628 		return m.result;
629 	}
630 
631 	private void onMenuCommand(WPARAM wParam, LPARAM lParam)
632 	{
633 		MENUITEMINFOW minfo;
634 
635 		minfo.cbSize = MENUITEMINFOW.sizeof;
636 		minfo.fMask = MIIM_DATA;
637 
638 		if(GetMenuItemInfoW(cast(HMENU)lParam, cast(UINT)wParam, TRUE, &minfo))
639 		{
640 			MenuItem sender = winCast!(MenuItem)(minfo.dwItemData);
641 			sender.performClick();
642 		}
643 	}
644 
645 	private void create()
646 	{
647 		CreateControlParams ccp;
648 		ccp.defaultBackColor = SystemColors.colorButtonFace;
649 		ccp.defaultForeColor = SystemColors.colorButtonText;
650 
651 		this.createControlParams(ccp);
652 
653 		this._backBrush = CreateSolidBrush(ccp.defaultBackColor.colorref);
654 		this._foreBrush = CreateSolidBrush(ccp.defaultForeColor.colorref);
655 
656 		if(ccp.defaultCursor)
657 		{
658 			this._defaultCursor = ccp.defaultCursor;
659 		}
660 
661 		if(!this._defaultFont)
662 		{
663 			this._defaultFont = SystemFonts.windowsFont;
664 		}
665 
666 		if(!this._backColor.valid) // Invalid Color
667 		{
668 			this.backColor = ccp.defaultBackColor;
669 		}
670 
671 		if(!this._foreColor.valid) // Invalid Color
672 		{
673 			this.foreColor = ccp.defaultForeColor;
674 		}
675 
676 		HWND hParent = null;
677 
678 		if(Control.hasBit(this._cBits, ControlBits.modalControl)) //Is Modal ?
679 		{
680 			hParent = GetActiveWindow();
681 			this.setStyle(WS_CHILD, false);
682 			this.setStyle(WS_POPUP, true);
683 		}
684 		else if(this._parent)
685 		{
686 			hParent = this._parent.handle;
687 
688 			/* As MSDN says:
689 			    WS_POPUP: The windows is a pop-up window. *** This style cannot be used with the WS_CHILD style. *** */
690 
691 			if(!(this.getStyle() & WS_POPUP)) //The windows doesn't have WS_POPUP style, set WS_CHILD style.
692 			{
693 				this.setStyle(WS_CHILD, true);
694 			}
695 
696 			this.setStyle(WS_CLIPSIBLINGS, true);
697 		}
698 
699 		createWindowEx(this.getExStyle(),
700 					   ccp.className,
701 					   this._text,
702 					   this.getStyle(),
703 					   this._bounds.x,
704 					   this._bounds.y,
705 					   this._bounds.width,
706 					   this._bounds.height,
707 					   hParent,
708 					   winCast!(void*)(this));
709 
710 		if(!this._handle)
711 		{
712 			throwException!(Win32Exception)("Control Creation failed: (ClassName: '%s', Text: '%s')",
713 											ccp.className, this._text);
714 		}
715 
716 		UpdateWindow(this._handle);
717 
718 		if(this._parent)
719 		{
720 			this._parent.sendMessage(DGUI_CHILDCONTROLCREATED, winCast!(WPARAM)(this), 0); //Notify the parent window
721 		}
722 	}
723 
724 	private void setPosition(int x, int y)
725 	{
726 		this.setWindowPos(x, y, 0, 0, PositionSpecified.position);
727 	}
728 
729 	private void setSize(int w, int h)
730 	{
731 		this.setWindowPos(0, 0, w, h, PositionSpecified.size);
732 	}
733 
734 	private void setWindowPos(int x, int y, int w, int h, PositionSpecified ps = PositionSpecified.all)
735 	{
736 		uint wpf = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE;
737 
738 		if(ps !is PositionSpecified.all)
739 		{
740 			if(ps is PositionSpecified.position)
741 			{
742 				wpf &= ~SWP_NOMOVE;
743 			}
744 			else //if(ps is PositionSpecified.size)
745 			{
746 				wpf &= ~SWP_NOSIZE;
747 			}
748 		}
749 		else
750 		{
751 			wpf &= ~(SWP_NOMOVE | SWP_NOSIZE);
752 		}
753 
754 		SetWindowPos(this._handle, null, x, y, w, h, wpf); //Bounds updated in WM_WINDOWPOSCHANGED
755 	}
756 
757 	private void drawMenuItemImage(DRAWITEMSTRUCT* pDrawItem)
758 	{
759 		MenuItem mi = winCast!(MenuItem)(pDrawItem.itemData);
760 
761 		if(mi)
762 		{
763 			scope Canvas c = Canvas.fromHDC(pDrawItem.hDC, false); //HDC *Not* Owned by Canvas Object
764 			int icoSize = GetSystemMetrics(SM_CYMENU);
765 			c.drawImage(mi.rootMenu.imageList.images[mi.imageIndex], Rect(0, 0, icoSize, icoSize));
766 		}
767 	}
768 
769 	protected final uint getStyle()
770 	{
771 		if(this.created)
772 		{
773 			return GetWindowLongW(this._handle, GWL_STYLE);
774 		}
775 
776 		return this._style;
777 	}
778 
779 	protected final void setStyle(uint cstyle, bool set)
780 	{
781 		if(this.created)
782 		{
783 			uint style = this.getStyle();
784 			set ? (style |= cstyle) : (style &= ~cstyle);
785 
786 			SetWindowLongW(this._handle, GWL_STYLE, style);
787 			this.redraw();
788 			this._style = style;
789 		}
790 		else
791 		{
792 			set ? (this._style |= cstyle) : (this._style &= ~cstyle);
793 		}
794 	}
795 
796 	protected static final void setBit(ref ulong rBits, ulong rBit, bool set)
797 	{
798 		set ? (rBits |= rBit) : (rBits &= ~rBit);
799 	}
800 
801 	protected static final bool hasBit(ref ulong rBits, ulong rBit)
802 	{
803 		return cast(bool)(rBits & rBit);
804 	}
805 
806 	protected final uint getExStyle()
807 	{
808 		if(this.created)
809 		{
810 			return GetWindowLongW(this._handle, GWL_EXSTYLE);
811 		}
812 
813 		return this._extendedStyle;
814 	}
815 
816 	protected final void setExStyle(uint cstyle, bool set)
817 	{
818 		if(this.created)
819 		{
820 			uint exStyle = this.getExStyle();
821 			set ? (exStyle |= cstyle) : (exStyle &= ~cstyle);
822 
823 			SetWindowLongW(this._handle, GWL_EXSTYLE, exStyle);
824 			this.redraw();
825 			this._extendedStyle = exStyle;
826 		}
827 		else
828 		{
829 			set ? (this._extendedStyle |= cstyle) : (this._extendedStyle &= ~cstyle);
830 		}
831 	}
832 
833 	protected void createControlParams(ref CreateControlParams ccp)
834 	{
835 		ClassStyles cstyle = ccp.classStyle | ClassStyles.doubleClicks;
836 
837 		WindowClass.register(ccp.className, cstyle, ccp.defaultCursor, cast(WNDPROC) /*FIXME may throw*/ &Control.msgRouter);
838 	}
839 
840 	protected uint originalWndProc(ref Message m)
841 	{
842 		return Control.defWindowProc(m);
843 	}
844 
845 	protected static uint defWindowProc(ref Message m)
846 	{
847 		if(IsWindowUnicode(m.hWnd))
848 		{
849 			m.result = DefWindowProcW(m.hWnd, m.msg, m.wParam, m.lParam);
850 		}
851 		else
852 		{
853 			m.result = DefWindowProcA(m.hWnd, m.msg, m.wParam, m.lParam);
854 		}
855 
856 		return m.result;
857 	}
858 
859 	protected void onDGuiMessage(ref Message m)
860 	{
861 		switch(m.msg)
862 		{
863 			case DGUI_REFLECTMESSAGE:
864 				Message rm = *(cast(Message*)m.wParam);
865 				this.onReflectedMessage(rm);
866 				*(cast(Message*)m.wParam) = rm; //Copy the result, so the parent can return result.
867 				//m.result = rm.result; // No result here!
868 				break;
869 
870 			case DGUI_CREATEONLY:
871 			{
872 				if(!this.created)
873 				{
874 					this.create();
875 				}
876 			}
877 			break;
878 
879 			default:
880 				m.result = 0;
881 				break;
882 		}
883 	}
884 
885 	protected void onReflectedMessage(ref Message m)
886 	{
887 		switch(m.msg)
888 		{
889 			case WM_CTLCOLOREDIT, WM_CTLCOLORBTN:
890 				SetBkColor(cast(HDC)m.wParam, this.backColor.colorref);
891 				SetTextColor(cast(HDC)m.wParam, this.foreColor.colorref);
892 				m.result = cast(LRESULT)this._backBrush;
893 				break;
894 
895 			case WM_MEASUREITEM:
896 			{
897 				MEASUREITEMSTRUCT* pMeasureItem = cast(MEASUREITEMSTRUCT*)m.lParam;
898 
899 				if(pMeasureItem.CtlType == ODT_MENU)
900 				{
901 					MenuItem mi = winCast!(MenuItem)(pMeasureItem.itemData);
902 
903 					if(mi)
904 					{
905 						if(mi.parent.handle == GetMenu(this._handle))// Check if parent of 'mi' is the menu bar
906 						{
907 							FontMetrics fm = this.font.metrics;
908 
909 							int icoSize = GetSystemMetrics(SM_CYMENU);
910 							pMeasureItem.itemWidth = icoSize + fm.maxCharWidth;
911 						}
912 						else
913 						{
914 							pMeasureItem.itemWidth = 10;
915 						}
916 					}
917 				}
918 			}
919 			break;
920 
921 			case WM_DRAWITEM:
922 			{
923 				DRAWITEMSTRUCT* pDrawItem = cast(DRAWITEMSTRUCT*)m.lParam;
924 
925 				if(pDrawItem.CtlType == ODT_MENU)
926 				{
927 					this.drawMenuItemImage(pDrawItem);
928 				}
929 			}
930 			break;
931 
932 			default:
933 				//Control.defWindowProc(m);
934 				break;
935 		}
936 	}
937 
938 	protected void onClick(EventArgs e)
939 	{
940 		this.click(this, e);
941 	}
942 
943 	protected void onKeyUp(KeyEventArgs e)
944 	{
945 		this.keyUp(this, e);
946 	}
947 
948 	protected void onKeyDown(KeyEventArgs e)
949 	{
950 		this.keyDown(this, e);
951 	}
952 
953 	protected void onKeyChar(KeyCharEventArgs e)
954 	{
955 		this.keyChar(this, e);
956 	}
957 
958 	protected void onPaint(PaintEventArgs e)
959 	{
960 		this.paint(this, e);
961 	}
962 
963 	protected void onHandleCreated(EventArgs e)
964 	{
965 		this.handleCreated(this, e);
966 	}
967 
968 	protected void onResize(EventArgs e)
969 	{
970 		this.resize(this, e);
971 	}
972 
973 	protected void onVisibleChanged(EventArgs e)
974 	{
975 		this.visibleChanged(this, e);
976 	}
977 
978 	protected void onMouseKeyDown(MouseEventArgs e)
979 	{
980 		this.mouseKeyDown(this, e);
981 	}
982 
983 	protected void onMouseKeyUp(MouseEventArgs e)
984 	{
985 		this.mouseKeyUp(this, e);
986 	}
987 
988 	protected void onDoubleClick(MouseEventArgs e)
989 	{
990 		this.doubleClick(this, e);
991 	}
992 
993 	protected void onMouseMove(MouseEventArgs e)
994 	{
995 		this.mouseMove(this, e);
996 	}
997 
998 	protected void onMouseEnter(MouseEventArgs e)
999 	{
1000 		this.mouseEnter(this, e);
1001 	}
1002 
1003 	protected void onMouseLeave(MouseEventArgs e)
1004 	{
1005 		this.mouseLeave(this, e);
1006 	}
1007 
1008 	protected void onFocusChanged(EventArgs e)
1009 	{
1010 		this.focusChanged(this, e);
1011 	}
1012 
1013 	protected void onControlCode(ControlCodeEventArgs e)
1014 	{
1015 		this.controlCode(this, e);
1016 	}
1017 
1018 	protected void wndProc(ref Message m)
1019 	{
1020 		switch(m.msg)
1021 		{
1022 			case WM_ERASEBKGND:
1023 				m.result = 0; // Do nothing here, handle it in WM_PAINT
1024 				break;
1025 
1026 			case WM_PAINT:
1027 			{
1028 				HDC hdc;
1029 				Rect clipRect;
1030 				PAINTSTRUCT ps;
1031 
1032 				if(!m.wParam)
1033 				{
1034 					hdc = BeginPaint(this._handle, &ps);
1035 					clipRect = Rect.fromRECT(&ps.rcPaint); //Clip Rectangle
1036 				}
1037 				else // Assume WPARAM as HDC
1038 				{
1039 					hdc = cast(HDC)m.wParam;
1040 					GetUpdateRect(this._handle, &clipRect.rect, false);
1041 				}
1042 
1043 				FillRect(hdc, &clipRect.rect, this._backBrush); //Fill with background color;
1044 
1045 				scope Canvas c = Canvas.fromHDC(hdc, false);
1046 				scope PaintEventArgs e = new PaintEventArgs(c, clipRect);
1047 				this.onPaint(e);
1048 
1049 				if(!m.wParam)
1050 				{
1051 					EndPaint(this._handle, &ps);
1052 				}
1053 
1054 				m.result = 0;
1055 			}
1056 			break;
1057 
1058 			case WM_CREATE: // Aggiornamento Font, rimuove FIXED SYS
1059 			{
1060 				this.sendMessage(WM_SETFONT, cast(WPARAM)this._defaultFont.handle, true);
1061 
1062 				if(this._ctxMenu)
1063 				{
1064 					HMENU hDefaultMenu = GetMenu(this._handle);
1065 
1066 					if(hDefaultMenu)
1067 					{
1068 						DestroyMenu(hDefaultMenu); //Destroy default menu (if exists)
1069 					}
1070 
1071 					this._ctxMenu.create();
1072 				}
1073 
1074 				this.onHandleCreated(EventArgs.empty);
1075 				m.result = 0; //Continue..
1076 			}
1077 			break;
1078 
1079 			case WM_WINDOWPOSCHANGED:
1080 			{
1081 				WINDOWPOS* pWndPos = cast(WINDOWPOS*)m.lParam;
1082 
1083 				if(!(pWndPos.flags & SWP_NOMOVE) || !(pWndPos.flags & SWP_NOSIZE))
1084 				{
1085 					/* Note: 'pWndPos' has NonClient coordinates */
1086 
1087 					if(!(pWndPos.flags & SWP_NOMOVE))
1088 					{
1089 						this._bounds.x = pWndPos.x;
1090 						this._bounds.y = pWndPos.y;
1091 					}
1092 
1093 					if(!(pWndPos.flags & SWP_NOSIZE))
1094 					{
1095 						this._bounds.width = pWndPos.cx;
1096 						this._bounds.height = pWndPos.cy;
1097 					}
1098 
1099 					if(!(pWndPos.flags & SWP_NOSIZE))
1100 					{
1101 						this.onResize(EventArgs.empty);
1102 					}
1103 				}
1104 				else if(pWndPos.flags & SWP_SHOWWINDOW || pWndPos.flags & SWP_HIDEWINDOW)
1105 				{
1106 					if(pWndPos.flags & SWP_SHOWWINDOW && this._parent)
1107 					{
1108 						this._parent.sendMessage(DGUI_DOLAYOUT, 0, 0);
1109 					}
1110 
1111 					this.onVisibleChanged(EventArgs.empty);
1112 				}
1113 
1114 				this.originalWndProc(m); //Send WM_SIZE too
1115 			}
1116 			break;
1117 
1118 			case WM_KEYDOWN:
1119 			{
1120 				scope KeyEventArgs e = new KeyEventArgs(cast(Keys)m.wParam);
1121 				this.onKeyDown(e);
1122 
1123 				if(e.handled)
1124 				{
1125 					this.originalWndProc(m);
1126 				}
1127 				else
1128 				{
1129 					m.result = 0;
1130 				}
1131 			}
1132 			break;
1133 
1134 			case WM_KEYUP:
1135 			{
1136 				scope KeyEventArgs e = new KeyEventArgs(cast(Keys)m.wParam);
1137 				this.onKeyUp(e);
1138 
1139 				if(e.handled)
1140 				{
1141 					this.originalWndProc(m);
1142 				}
1143 				else
1144 				{
1145 					m.result = 0;
1146 				}
1147 			}
1148 			break;
1149 
1150 			case WM_CHAR:
1151 			{
1152 				scope KeyCharEventArgs e = new KeyCharEventArgs(cast(Keys)m.wParam, cast(char)m.wParam);
1153 				this.onKeyChar(e);
1154 
1155 				if(e.handled)
1156 				{
1157 					this.originalWndProc(m);
1158 				}
1159 				else
1160 				{
1161 					m.result = 0;
1162 				}
1163 			}
1164 			break;
1165 
1166 			case WM_MOUSELEAVE:
1167 			{
1168 				Control.setBit(this._cBits, ControlBits.mouseEnter, false);
1169 
1170 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1171 				this.onMouseLeave(e);
1172 
1173 				this.originalWndProc(m);
1174 			}
1175 			break;
1176 
1177 			case WM_MOUSEMOVE:
1178 			{
1179 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1180 				this.onMouseMove(e);
1181 
1182 				if(!Control.hasBit(this._cBits, ControlBits.mouseEnter))
1183 				{
1184 					Control.setBit(this._cBits, ControlBits.mouseEnter, true);
1185 
1186 					TRACKMOUSEEVENT tme;
1187 
1188 					tme.cbSize = TRACKMOUSEEVENT.sizeof;
1189 					tme.dwFlags = TME_LEAVE;
1190 					tme.hwndTrack = this._handle;
1191 
1192 					TrackMouseEvent(&tme);
1193 
1194 					this.onMouseEnter(e);
1195 				}
1196 
1197 				this.originalWndProc(m);
1198 			}
1199 			break;
1200 
1201 			case WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN:
1202 			{
1203 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1204 				this.onMouseKeyDown(e);
1205 
1206 				this.originalWndProc(m);
1207 			}
1208 			break;
1209 
1210 			case WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP:
1211 			{
1212 				MouseKeys mk = MouseKeys.none;
1213 
1214 				if(GetAsyncKeyState(MK_LBUTTON))
1215 				{
1216 					mk |= MouseKeys.left;
1217 				}
1218 
1219 				if(GetAsyncKeyState(MK_MBUTTON))
1220 				{
1221 					mk |= MouseKeys.middle;
1222 				}
1223 
1224 				if(GetAsyncKeyState(MK_RBUTTON))
1225 				{
1226 					mk |= MouseKeys.right;
1227 				}
1228 
1229 				Point p = Point(LOWORD(m.lParam), HIWORD(m.lParam));
1230 				scope MouseEventArgs e = new MouseEventArgs(p, mk);
1231 				this.onMouseKeyUp(e);
1232 
1233 				Control.convertPoint(p, this, null);
1234 
1235 				if(m.msg == WM_LBUTTONUP && !Control.hasBit(this._cBits, ControlBits.ownClickMsg) && WindowFromPoint(p.point) == this._handle)
1236 				{
1237 					this.onClick(EventArgs.empty);
1238 				}
1239 
1240 				this.originalWndProc(m);
1241 			}
1242 			break;
1243 
1244 			case WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, WM_RBUTTONDBLCLK:
1245 			{
1246 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1247 				this.onDoubleClick(e);
1248 
1249 				this.originalWndProc(m);
1250 			}
1251 			break;
1252 
1253 			case WM_SETCURSOR:
1254 			{
1255 				if(cast(HWND)m.wParam == this._handle && this._defaultCursor && cast(LONG)this._defaultCursor.handle != GetClassLongW(this._handle, GCL_HCURSOR))
1256 				{
1257 					SetClassLongW(this._handle, GCL_HCURSOR, cast(LONG)this._defaultCursor.handle);
1258 				}
1259 
1260 				this.originalWndProc(m); //Continue cursor selection
1261 			}
1262 			break;
1263 
1264 			case WM_MENUCOMMAND:
1265 				this.onMenuCommand(m.wParam, m.lParam);
1266 				break;
1267 
1268 			case WM_CONTEXTMENU:
1269 			{
1270 				if(this._ctxMenu)
1271 				{
1272 					this._ctxMenu.popupMenu(this._handle, Cursor.position);
1273 				}
1274 
1275 				this.originalWndProc(m);
1276 			}
1277 			break;
1278 
1279 			case WM_SETFOCUS, WM_KILLFOCUS:
1280 			{
1281 				this.onFocusChanged(EventArgs.empty);
1282 				this.originalWndProc(m);
1283 			}
1284 			break;
1285 
1286 			case WM_GETDLGCODE:
1287 			{
1288 				scope ControlCodeEventArgs e = new ControlCodeEventArgs();
1289 				this.onControlCode(e);
1290 
1291 				if(e.controlCode is ControlCode.ignore)
1292 				{
1293 					this.originalWndProc(m);
1294 				}
1295 				else
1296 				{
1297 					m.result = e.controlCode;
1298 				}
1299 			}
1300 			break;
1301 
1302 			case WM_INITMENU:
1303 			{
1304 				if(this._ctxMenu)
1305 				{
1306 					this._ctxMenu.onPopup(EventArgs.empty);
1307 				}
1308 
1309 				m.result = 0;
1310 			}
1311 			break;
1312 
1313 			default:
1314 				this.originalWndProc(m);
1315 				break;
1316 		}
1317 	}
1318 }