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