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(T)(ref T rBits, T rBit, bool set)
797 	if(is(T B == enum) && is(B == ulong))
798 	{
799 		set ? (rBits |= rBit) : (rBits &= ~rBit);
800 	}
801 
802 	protected static final bool hasBit(T)(ref T rBits, T rBit)
803 	if(is(T B == enum) && is(B == ulong))
804 	{
805 		return cast(bool)(rBits & rBit);
806 	}
807 
808 	protected final uint getExStyle()
809 	{
810 		if(this.created)
811 		{
812 			return GetWindowLongW(this._handle, GWL_EXSTYLE);
813 		}
814 
815 		return this._extendedStyle;
816 	}
817 
818 	protected final void setExStyle(uint cstyle, bool set)
819 	{
820 		if(this.created)
821 		{
822 			uint exStyle = this.getExStyle();
823 			set ? (exStyle |= cstyle) : (exStyle &= ~cstyle);
824 
825 			SetWindowLongW(this._handle, GWL_EXSTYLE, exStyle);
826 			this.redraw();
827 			this._extendedStyle = exStyle;
828 		}
829 		else
830 		{
831 			set ? (this._extendedStyle |= cstyle) : (this._extendedStyle &= ~cstyle);
832 		}
833 	}
834 
835 	protected void createControlParams(ref CreateControlParams ccp)
836 	{
837 		ClassStyles cstyle = ccp.classStyle | ClassStyles.doubleClicks;
838 
839 		WindowClass.register(ccp.className, cstyle, ccp.defaultCursor, cast(WNDPROC) /*FIXME may throw*/ &Control.msgRouter);
840 	}
841 
842 	protected uint originalWndProc(ref Message m)
843 	{
844 		return Control.defWindowProc(m);
845 	}
846 
847 	protected static uint defWindowProc(ref Message m)
848 	{
849 		if(IsWindowUnicode(m.hWnd))
850 		{
851 			m.result = DefWindowProcW(m.hWnd, m.msg, m.wParam, m.lParam);
852 		}
853 		else
854 		{
855 			m.result = DefWindowProcA(m.hWnd, m.msg, m.wParam, m.lParam);
856 		}
857 
858 		return m.result;
859 	}
860 
861 	protected void onDGuiMessage(ref Message m)
862 	{
863 		switch(m.msg)
864 		{
865 			case DGUI_REFLECTMESSAGE:
866 				Message rm = *(cast(Message*)m.wParam);
867 				this.onReflectedMessage(rm);
868 				*(cast(Message*)m.wParam) = rm; //Copy the result, so the parent can return result.
869 				//m.result = rm.result; // No result here!
870 				break;
871 
872 			case DGUI_CREATEONLY:
873 			{
874 				if(!this.created)
875 				{
876 					this.create();
877 				}
878 			}
879 			break;
880 
881 			default:
882 				m.result = 0;
883 				break;
884 		}
885 	}
886 
887 	protected void onReflectedMessage(ref Message m)
888 	{
889 		switch(m.msg)
890 		{
891 			case WM_CTLCOLOREDIT, WM_CTLCOLORBTN:
892 				SetBkColor(cast(HDC)m.wParam, this.backColor.colorref);
893 				SetTextColor(cast(HDC)m.wParam, this.foreColor.colorref);
894 				m.result = cast(LRESULT)this._backBrush;
895 				break;
896 
897 			case WM_MEASUREITEM:
898 			{
899 				MEASUREITEMSTRUCT* pMeasureItem = cast(MEASUREITEMSTRUCT*)m.lParam;
900 
901 				if(pMeasureItem.CtlType == ODT_MENU)
902 				{
903 					MenuItem mi = winCast!(MenuItem)(pMeasureItem.itemData);
904 
905 					if(mi)
906 					{
907 						if(mi.parent.handle == GetMenu(this._handle))// Check if parent of 'mi' is the menu bar
908 						{
909 							FontMetrics fm = this.font.metrics;
910 
911 							int icoSize = GetSystemMetrics(SM_CYMENU);
912 							pMeasureItem.itemWidth = icoSize + fm.maxCharWidth;
913 						}
914 						else
915 						{
916 							pMeasureItem.itemWidth = 10;
917 						}
918 					}
919 				}
920 			}
921 			break;
922 
923 			case WM_DRAWITEM:
924 			{
925 				DRAWITEMSTRUCT* pDrawItem = cast(DRAWITEMSTRUCT*)m.lParam;
926 
927 				if(pDrawItem.CtlType == ODT_MENU)
928 				{
929 					this.drawMenuItemImage(pDrawItem);
930 				}
931 			}
932 			break;
933 
934 			default:
935 				//Control.defWindowProc(m);
936 				break;
937 		}
938 	}
939 
940 	protected void onClick(EventArgs e)
941 	{
942 		this.click(this, e);
943 	}
944 
945 	protected void onKeyUp(KeyEventArgs e)
946 	{
947 		this.keyUp(this, e);
948 	}
949 
950 	protected void onKeyDown(KeyEventArgs e)
951 	{
952 		this.keyDown(this, e);
953 	}
954 
955 	protected void onKeyChar(KeyCharEventArgs e)
956 	{
957 		this.keyChar(this, e);
958 	}
959 
960 	protected void onPaint(PaintEventArgs e)
961 	{
962 		this.paint(this, e);
963 	}
964 
965 	protected void onHandleCreated(EventArgs e)
966 	{
967 		this.handleCreated(this, e);
968 	}
969 
970 	protected void onResize(EventArgs e)
971 	{
972 		this.resize(this, e);
973 	}
974 
975 	protected void onVisibleChanged(EventArgs e)
976 	{
977 		this.visibleChanged(this, e);
978 	}
979 
980 	protected void onMouseKeyDown(MouseEventArgs e)
981 	{
982 		this.mouseKeyDown(this, e);
983 	}
984 
985 	protected void onMouseKeyUp(MouseEventArgs e)
986 	{
987 		this.mouseKeyUp(this, e);
988 	}
989 
990 	protected void onDoubleClick(MouseEventArgs e)
991 	{
992 		this.doubleClick(this, e);
993 	}
994 
995 	protected void onMouseMove(MouseEventArgs e)
996 	{
997 		this.mouseMove(this, e);
998 	}
999 
1000 	protected void onMouseEnter(MouseEventArgs e)
1001 	{
1002 		this.mouseEnter(this, e);
1003 	}
1004 
1005 	protected void onMouseLeave(MouseEventArgs e)
1006 	{
1007 		this.mouseLeave(this, e);
1008 	}
1009 
1010 	protected void onFocusChanged(EventArgs e)
1011 	{
1012 		this.focusChanged(this, e);
1013 	}
1014 
1015 	protected void onControlCode(ControlCodeEventArgs e)
1016 	{
1017 		this.controlCode(this, e);
1018 	}
1019 
1020 	protected void wndProc(ref Message m)
1021 	{
1022 		switch(m.msg)
1023 		{
1024 			case WM_ERASEBKGND:
1025 				m.result = 0; // Do nothing here, handle it in WM_PAINT
1026 				break;
1027 
1028 			case WM_PAINT:
1029 			{
1030 				HDC hdc;
1031 				Rect clipRect;
1032 				PAINTSTRUCT ps;
1033 
1034 				if(!m.wParam)
1035 				{
1036 					hdc = BeginPaint(this._handle, &ps);
1037 					clipRect = Rect.fromRECT(&ps.rcPaint); //Clip Rectangle
1038 				}
1039 				else // Assume WPARAM as HDC
1040 				{
1041 					hdc = cast(HDC)m.wParam;
1042 					GetUpdateRect(this._handle, &clipRect.rect, false);
1043 				}
1044 
1045 				FillRect(hdc, &clipRect.rect, this._backBrush); //Fill with background color;
1046 
1047 				scope Canvas c = Canvas.fromHDC(hdc, false);
1048 				scope PaintEventArgs e = new PaintEventArgs(c, clipRect);
1049 				this.onPaint(e);
1050 
1051 				if(!m.wParam)
1052 				{
1053 					EndPaint(this._handle, &ps);
1054 				}
1055 
1056 				m.result = 0;
1057 			}
1058 			break;
1059 
1060 			case WM_CREATE: // Aggiornamento Font, rimuove FIXED SYS
1061 			{
1062 				this.sendMessage(WM_SETFONT, cast(WPARAM)this._defaultFont.handle, true);
1063 
1064 				if(this._ctxMenu)
1065 				{
1066 					HMENU hDefaultMenu = GetMenu(this._handle);
1067 
1068 					if(hDefaultMenu)
1069 					{
1070 						DestroyMenu(hDefaultMenu); //Destroy default menu (if exists)
1071 					}
1072 
1073 					this._ctxMenu.create();
1074 				}
1075 
1076 				this.onHandleCreated(EventArgs.empty);
1077 				m.result = 0; //Continue..
1078 			}
1079 			break;
1080 
1081 			case WM_WINDOWPOSCHANGED:
1082 			{
1083 				WINDOWPOS* pWndPos = cast(WINDOWPOS*)m.lParam;
1084 
1085 				if(!(pWndPos.flags & SWP_NOMOVE) || !(pWndPos.flags & SWP_NOSIZE))
1086 				{
1087 					/* Note: 'pWndPos' has NonClient coordinates */
1088 
1089 					if(!(pWndPos.flags & SWP_NOMOVE))
1090 					{
1091 						this._bounds.x = pWndPos.x;
1092 						this._bounds.y = pWndPos.y;
1093 					}
1094 
1095 					if(!(pWndPos.flags & SWP_NOSIZE))
1096 					{
1097 						this._bounds.width = pWndPos.cx;
1098 						this._bounds.height = pWndPos.cy;
1099 					}
1100 
1101 					if(!(pWndPos.flags & SWP_NOSIZE))
1102 					{
1103 						this.onResize(EventArgs.empty);
1104 					}
1105 				}
1106 				else if(pWndPos.flags & SWP_SHOWWINDOW || pWndPos.flags & SWP_HIDEWINDOW)
1107 				{
1108 					if(pWndPos.flags & SWP_SHOWWINDOW && this._parent)
1109 					{
1110 						this._parent.sendMessage(DGUI_DOLAYOUT, 0, 0);
1111 					}
1112 
1113 					this.onVisibleChanged(EventArgs.empty);
1114 				}
1115 
1116 				this.originalWndProc(m); //Send WM_SIZE too
1117 			}
1118 			break;
1119 
1120 			case WM_KEYDOWN:
1121 			{
1122 				scope KeyEventArgs e = new KeyEventArgs(cast(Keys)m.wParam);
1123 				this.onKeyDown(e);
1124 
1125 				if(e.handled)
1126 				{
1127 					this.originalWndProc(m);
1128 				}
1129 				else
1130 				{
1131 					m.result = 0;
1132 				}
1133 			}
1134 			break;
1135 
1136 			case WM_KEYUP:
1137 			{
1138 				scope KeyEventArgs e = new KeyEventArgs(cast(Keys)m.wParam);
1139 				this.onKeyUp(e);
1140 
1141 				if(e.handled)
1142 				{
1143 					this.originalWndProc(m);
1144 				}
1145 				else
1146 				{
1147 					m.result = 0;
1148 				}
1149 			}
1150 			break;
1151 
1152 			case WM_CHAR:
1153 			{
1154 				scope KeyCharEventArgs e = new KeyCharEventArgs(cast(Keys)m.wParam, cast(char)m.wParam);
1155 				this.onKeyChar(e);
1156 
1157 				if(e.handled)
1158 				{
1159 					this.originalWndProc(m);
1160 				}
1161 				else
1162 				{
1163 					m.result = 0;
1164 				}
1165 			}
1166 			break;
1167 
1168 			case WM_MOUSELEAVE:
1169 			{
1170 				Control.setBit(this._cBits, ControlBits.mouseEnter, false);
1171 
1172 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1173 				this.onMouseLeave(e);
1174 
1175 				this.originalWndProc(m);
1176 			}
1177 			break;
1178 
1179 			case WM_MOUSEMOVE:
1180 			{
1181 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1182 				this.onMouseMove(e);
1183 
1184 				if(!Control.hasBit(this._cBits, ControlBits.mouseEnter))
1185 				{
1186 					Control.setBit(this._cBits, ControlBits.mouseEnter, true);
1187 
1188 					TRACKMOUSEEVENT tme;
1189 
1190 					tme.cbSize = TRACKMOUSEEVENT.sizeof;
1191 					tme.dwFlags = TME_LEAVE;
1192 					tme.hwndTrack = this._handle;
1193 
1194 					TrackMouseEvent(&tme);
1195 
1196 					this.onMouseEnter(e);
1197 				}
1198 
1199 				this.originalWndProc(m);
1200 			}
1201 			break;
1202 
1203 			case WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN:
1204 			{
1205 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1206 				this.onMouseKeyDown(e);
1207 
1208 				this.originalWndProc(m);
1209 			}
1210 			break;
1211 
1212 			case WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP:
1213 			{
1214 				MouseKeys mk = MouseKeys.none;
1215 
1216 				if(GetAsyncKeyState(MK_LBUTTON))
1217 				{
1218 					mk |= MouseKeys.left;
1219 				}
1220 
1221 				if(GetAsyncKeyState(MK_MBUTTON))
1222 				{
1223 					mk |= MouseKeys.middle;
1224 				}
1225 
1226 				if(GetAsyncKeyState(MK_RBUTTON))
1227 				{
1228 					mk |= MouseKeys.right;
1229 				}
1230 
1231 				Point p = Point(LOWORD(m.lParam), HIWORD(m.lParam));
1232 				scope MouseEventArgs e = new MouseEventArgs(p, mk);
1233 				this.onMouseKeyUp(e);
1234 
1235 				Control.convertPoint(p, this, null);
1236 
1237 				if(m.msg == WM_LBUTTONUP && !Control.hasBit(this._cBits, ControlBits.ownClickMsg) && WindowFromPoint(p.point) == this._handle)
1238 				{
1239 					this.onClick(EventArgs.empty);
1240 				}
1241 
1242 				this.originalWndProc(m);
1243 			}
1244 			break;
1245 
1246 			case WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, WM_RBUTTONDBLCLK:
1247 			{
1248 				scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam);
1249 				this.onDoubleClick(e);
1250 
1251 				this.originalWndProc(m);
1252 			}
1253 			break;
1254 
1255 			case WM_SETCURSOR:
1256 			{
1257 				if(cast(HWND)m.wParam == this._handle && this._defaultCursor && cast(LONG)this._defaultCursor.handle != GetClassLongW(this._handle, GCL_HCURSOR))
1258 				{
1259 					SetClassLongW(this._handle, GCL_HCURSOR, cast(LONG)this._defaultCursor.handle);
1260 				}
1261 
1262 				this.originalWndProc(m); //Continue cursor selection
1263 			}
1264 			break;
1265 
1266 			case WM_MENUCOMMAND:
1267 				this.onMenuCommand(m.wParam, m.lParam);
1268 				break;
1269 
1270 			case WM_CONTEXTMENU:
1271 			{
1272 				if(this._ctxMenu)
1273 				{
1274 					this._ctxMenu.popupMenu(this._handle, Cursor.position);
1275 				}
1276 				else
1277 				{
1278 					this.originalWndProc(m);
1279 				}
1280 			}
1281 			break;
1282 
1283 			case WM_SETFOCUS, WM_KILLFOCUS:
1284 			{
1285 				this.onFocusChanged(EventArgs.empty);
1286 				this.originalWndProc(m);
1287 			}
1288 			break;
1289 
1290 			case WM_GETDLGCODE:
1291 			{
1292 				scope ControlCodeEventArgs e = new ControlCodeEventArgs();
1293 				this.onControlCode(e);
1294 
1295 				if(e.controlCode is ControlCode.ignore)
1296 				{
1297 					this.originalWndProc(m);
1298 				}
1299 				else
1300 				{
1301 					m.result = e.controlCode;
1302 				}
1303 			}
1304 			break;
1305 
1306 			case WM_INITMENU:
1307 			{
1308 				if(this._ctxMenu)
1309 				{
1310 					this._ctxMenu.onPopup(EventArgs.empty);
1311 				}
1312 
1313 				m.result = 0;
1314 			}
1315 			break;
1316 
1317 			default:
1318 				this.originalWndProc(m);
1319 				break;
1320 		}
1321 	}
1322 }