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 }