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