1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 6 module dfl.control; 7 8 private import dfl.internal.dlib, dfl.internal.clib; 9 10 private import dfl.base, dfl.form, dfl.drawing; 11 private import dfl.internal.winapi, dfl.application, dfl.event, dfl.label; 12 private import dfl.internal.wincom, dfl.internal.utf, dfl.collections, dfl.internal.com; 13 private import core.memory; 14 15 version(NO_DRAG_DROP) 16 version = DFL_NO_DRAG_DROP; 17 18 version(DFL_NO_DRAG_DROP) { 19 } 20 else { 21 private import dfl.data; 22 } 23 24 version(DFL_NO_MENUS) { 25 } 26 else { 27 private import dfl.menu; 28 } 29 30 //version = RADIO_GROUP_LAYOUT; 31 version = DFL_NO_ZOMBIE_FORM; 32 33 34 35 enum AnchorStyles: ubyte { 36 NONE = 0, /// 37 TOP = 1, /// ditto 38 BOTTOM = 2, /// ditto 39 LEFT = 4, /// ditto 40 RIGHT = 8, /// ditto 41 42 /+ 43 // Extras: 44 VERTICAL = TOP | BOTTOM, 45 HORIZONTAL = LEFT | RIGHT, 46 ALL = TOP | BOTTOM | LEFT | RIGHT, 47 DEFAULT = TOP | LEFT, 48 TOP_LEFT = TOP | LEFT, 49 TOP_RIGHT = TOP | RIGHT, 50 BOTTOM_LEFT = BOTTOM | LEFT, 51 BOTTOM_RIGHT = BOTTOM | RIGHT, 52 +/ 53 } 54 55 56 /// Flags for setting control bounds. 57 enum BoundsSpecified: ubyte { 58 NONE = 0, /// 59 X = 1, /// ditto 60 Y = 2, /// ditto 61 LOCATION = 1 | 2, /// ditto 62 WIDTH = 4, /// ditto 63 HEIGHT = 8, /// ditto 64 SIZE = 4 | 8, /// ditto 65 ALL = 1 | 2 | 4 | 8, /// ditto 66 } 67 68 69 /// Layout docking style. 70 enum DockStyle: ubyte { 71 NONE, /// 72 BOTTOM, /// 73 FILL, /// 74 LEFT, /// 75 RIGHT, /// 76 TOP, /// 77 } 78 79 80 private { 81 struct GetZIndex 82 { 83 Control find; 84 int index = -1; 85 private int _tmp = 0; 86 } 87 88 89 extern(Windows) BOOL getZIndexCallback(HWND hwnd, LPARAM lparam) { 90 GetZIndex* gzi = cast(GetZIndex*)lparam; 91 if(hwnd == gzi.find.hwnd) { 92 gzi.index = gzi._tmp; 93 return FALSE; // Stop, found it. 94 } 95 96 Control ctrl; 97 ctrl = Control.fromHandle(hwnd); 98 if(ctrl && ctrl.parent is gzi.find.parent) { 99 gzi._tmp++; 100 } 101 102 return TRUE; // Keep looking. 103 } 104 } 105 106 107 /// Effect flags for drag/drop operations. 108 enum DragDropEffects: DWORD { 109 NONE = 0, /// 110 COPY = 1, /// ditto 111 MOVE = 2, /// ditto 112 LINK = 4, /// ditto 113 SCROLL = 0x80000000, /// ditto 114 ALL = COPY | MOVE | LINK | SCROLL, /// ditto 115 } 116 117 118 /// Drag/drop action. 119 enum DragAction: HRESULT { 120 CONTINUE = S_OK, /// 121 CANCEL = DRAGDROP_S_CANCEL, /// ditto 122 DROP = DRAGDROP_S_DROP, /// ditto 123 } 124 125 126 // Flags. 127 deprecated enum UICues: uint { 128 NONE = 0, 129 SHOW_FOCUS = 1, 130 SHOW_KEYBOARD = 2, 131 SHOWN = SHOW_FOCUS | SHOW_KEYBOARD, 132 CHANGE_FOCUS = 4, 133 CHANGE_KEYBOARD = 8, // Key mnemonic underline cues are on. 134 CHANGED = CHANGE_FOCUS | CHANGE_KEYBOARD, 135 } 136 137 138 // May be OR'ed together. 139 /// Style flags of a control. 140 enum ControlStyles: uint { 141 NONE = 0, /// 142 143 CONTAINER_CONTROL = 0x1, /// ditto 144 145 // TODO: implement. 146 USER_PAINT = 0x2, /// ditto 147 148 OPAQUE = 0x4, /// ditto 149 RESIZE_REDRAW = 0x10, /// ditto 150 //FIXED_WIDTH = 0x20, // TODO: implement. 151 //FIXED_HEIGHT = 0x40, // TODO: implement. 152 STANDARD_CLICK = 0x100, /// ditto 153 SELECTABLE = 0x200, /// ditto 154 155 // TODO: implement. 156 USER_MOUSE = 0x400, /// ditto 157 158 //SUPPORTS_TRANSPARENT_BACK_COLOR = 0x800, // Only if USER_PAINT and parent is derived from Control. TODO: implement. 159 STANDARD_DOUBLE_CLICK = 0x1000, /// ditto 160 ALL_PAINTING_IN_WM_PAINT = 0x2000, /// ditto 161 CACHE_TEXT = 0x4000, /// ditto 162 ENABLE_NOTIFY_MESSAGE = 0x8000, // deprecated. Calls onNotifyMessage() for every message. 163 //DOUBLE_BUFFER = 0x10000, // TODO: implement. 164 165 WANT_TAB_KEY = 0x01000000, 166 WANT_ALL_KEYS = 0x02000000, 167 } 168 169 170 /// Control creation parameters. 171 struct CreateParams { 172 Dstring className; /// 173 Dstring caption; /// ditto 174 void* param; /// ditto 175 HWND parent; /// ditto 176 HMENU menu; /// ditto 177 HINSTANCE inst; /// ditto 178 int x; /// ditto 179 int y; /// ditto 180 int width; /// ditto 181 int height; /// ditto 182 DWORD classStyle; /// ditto 183 DWORD exStyle; /// ditto 184 DWORD style; /// ditto 185 } 186 187 188 deprecated class UICuesEventArgs: EventArgs { 189 deprecated: 190 191 this(UICues uic) { 192 chg = uic; 193 } 194 195 196 final UICues changed() { // getter 197 return chg; 198 } 199 200 201 final bool changeFocus() { 202 return (chg & UICues.CHANGE_FOCUS) != 0; 203 } 204 205 206 final bool changeKeyboard() { 207 return (chg & UICues.CHANGE_KEYBOARD) != 0; 208 } 209 210 211 final bool showFocus() { 212 return (chg & UICues.SHOW_FOCUS) != 0; 213 } 214 215 216 final bool showKeyboard() { 217 return (chg & UICues.SHOW_KEYBOARD) != 0; 218 } 219 220 221 private: 222 UICues chg; 223 } 224 225 226 227 class ControlEventArgs: EventArgs { 228 229 this(Control ctrl) { 230 this.ctrl = ctrl; 231 } 232 233 234 235 final @property Control control() { // getter 236 return ctrl; 237 } 238 239 240 private: 241 Control ctrl; 242 } 243 244 245 246 class HelpEventArgs: EventArgs { 247 248 this(Point mousePos) { 249 mpos = mousePos; 250 } 251 252 253 254 final @property void handled(bool byes) { // setter 255 hand = byes; 256 } 257 258 /// ditto 259 final @property bool handled() { // getter 260 return hand; 261 } 262 263 264 265 final @property Point mousePos() { // getter 266 return mpos; 267 } 268 269 270 private: 271 Point mpos; 272 bool hand = false; 273 } 274 275 276 277 class InvalidateEventArgs: EventArgs { 278 279 this(Rect invalidRect) { 280 ir = invalidRect; 281 } 282 283 284 285 final @property Rect invalidRect() { // getter 286 return ir; 287 } 288 289 290 private: 291 Rect ir; 292 } 293 294 295 // /// 296 // New dimensions before resizing. 297 deprecated class BeforeResizeEventArgs: EventArgs { 298 deprecated: 299 300 301 this(int width, int height) { 302 this.w = width; 303 this.h = height; 304 } 305 306 307 308 void width(int cx) { // setter 309 w = cx; 310 } 311 312 /// ditto 313 int width() { // getter 314 return w; 315 } 316 317 318 319 void height(int cy) { // setter 320 h = cy; 321 } 322 323 /// ditto 324 int height() { // getter 325 return h; 326 } 327 328 329 private: 330 int w, h; 331 } 332 333 334 335 class LayoutEventArgs: EventArgs { 336 337 this(Control affectedControl) { 338 ac = affectedControl; 339 } 340 341 342 343 final @property Control affectedControl() { // getter 344 return ac; 345 } 346 347 348 private: 349 Control ac; 350 } 351 352 353 version(DFL_NO_DRAG_DROP) {} else { 354 355 class DragEventArgs: EventArgs { 356 357 this(dfl.data.IDataObject dataObj, int keyState, int x, int y, 358 DragDropEffects allowedEffect, DragDropEffects effect) { 359 _dobj = dataObj; 360 _keyState = keyState; 361 _x = x; 362 _y = y; 363 _allowedEffect = allowedEffect; 364 _effect = effect; 365 } 366 367 368 369 final @property DragDropEffects allowedEffect() { // getter 370 return _allowedEffect; 371 } 372 373 374 375 final @property void effect(DragDropEffects newEffect) { // setter 376 _effect = newEffect; 377 } 378 379 380 /// ditto 381 final @property DragDropEffects effect() { // getter 382 return _effect; 383 } 384 385 386 387 final @property dfl.data.IDataObject data() { // getter 388 return _dobj; 389 } 390 391 392 393 // State of ctrl, alt, shift, and mouse buttons. 394 final @property int keyState() { // getter 395 return _keyState; 396 } 397 398 399 400 final @property int x() { // getter 401 return _x; 402 } 403 404 405 406 final @property int y() { // getter 407 return _y; 408 } 409 410 411 private: 412 dfl.data.IDataObject _dobj; 413 int _keyState; 414 int _x, _y; 415 DragDropEffects _allowedEffect, _effect; 416 } 417 418 419 420 class GiveFeedbackEventArgs: EventArgs { 421 422 this(DragDropEffects effect, bool useDefaultCursors) { 423 _effect = effect; 424 udefcurs = useDefaultCursors; 425 } 426 427 428 429 final @property DragDropEffects effect() { // getter 430 return _effect; 431 } 432 433 434 435 final @property void useDefaultCursors(bool byes) { // setter 436 udefcurs = byes; 437 } 438 439 /// ditto 440 final @property bool useDefaultCursors() { // getter 441 return udefcurs; 442 } 443 444 445 private: 446 DragDropEffects _effect; 447 bool udefcurs; 448 } 449 450 451 452 class QueryContinueDragEventArgs: EventArgs { 453 454 this(int keyState, bool escapePressed, DragAction action) { 455 _keyState = keyState; 456 escp = escapePressed; 457 _action = action; 458 } 459 460 461 462 final @property void action(DragAction newAction) { // setter 463 _action = newAction; 464 } 465 466 /// ditto 467 final @property DragAction action() { // getter 468 return _action; 469 } 470 471 472 473 final @property bool escapePressed() { // getter 474 return escp; 475 } 476 477 478 479 // State of ctrl, alt and shift. 480 final @property int keyState() { // getter 481 return _keyState; 482 } 483 484 485 private: 486 int _keyState; 487 bool escp; 488 DragAction _action; 489 } 490 } 491 492 493 version(NO_WINDOWS_HUNG_WORKAROUND) { 494 } 495 else { 496 version = WINDOWS_HUNG_WORKAROUND; 497 } 498 debug { 499 version=_DFL_WINDOWS_HUNG_WORKAROUND; 500 } 501 version(WINDOWS_HUNG_WORKAROUND) { 502 version=_DFL_WINDOWS_HUNG_WORKAROUND; 503 } 504 505 version(_DFL_WINDOWS_HUNG_WORKAROUND) { 506 class WindowsHungDflException: DflException { 507 this(Dstring msg) { 508 super(msg); 509 } 510 } 511 } 512 513 alias BOOL delegate(HWND) EnumWindowsCallback; 514 package struct EnumWindowsCallbackData { 515 EnumWindowsCallback callback; 516 DThrowable exception; 517 } 518 519 520 // Callback for EnumWindows() and EnumChildWindows(). 521 private extern(Windows) BOOL enumingWindows(HWND hwnd, LPARAM lparam) nothrow { 522 auto cbd = *(cast(EnumWindowsCallbackData*)lparam); 523 try 524 { 525 return cbd.callback(hwnd); 526 } catch (DThrowable e) { 527 cbd.exception = e; 528 return FALSE; 529 } 530 assert(0); 531 } 532 533 534 private struct Efi { 535 HWND hwParent; 536 EnumWindowsCallbackData cbd; 537 } 538 539 540 // Callback for EnumChildWindows(). -lparam- = pointer to Efi; 541 private extern(Windows) BOOL enumingFirstWindows(HWND hwnd, LPARAM lparam) nothrow { 542 auto efi = cast(Efi*)lparam; 543 if(efi.hwParent == GetParent(hwnd)) { 544 try { 545 return efi.cbd.callback(hwnd); 546 } catch (DThrowable e) { 547 efi.cbd.exception = e; 548 return FALSE; 549 } 550 } 551 return TRUE; 552 } 553 554 555 package BOOL enumWindows(EnumWindowsCallback dg) { 556 EnumWindowsCallbackData cbd; 557 cbd.callback = dg; 558 scope (exit) if (cbd.exception) { 559 throw cbd.exception; 560 } 561 static assert((&cbd).sizeof <= LPARAM.sizeof); 562 return EnumWindows(&enumingWindows, cast(LPARAM)&cbd); 563 } 564 565 566 package BOOL enumChildWindows(HWND hwParent, EnumWindowsCallback dg) { 567 EnumWindowsCallbackData cbd; 568 cbd.callback = dg; 569 scope (exit) if (cbd.exception) { 570 throw cbd.exception; 571 } 572 static assert((&cbd).sizeof <= LPARAM.sizeof); 573 return EnumChildWindows(hwParent, &enumingWindows, cast(LPARAM)&cbd); 574 } 575 576 577 // Only the parent's children, not its children. 578 package BOOL enumFirstChildWindows(HWND hwParent, EnumWindowsCallback dg) { 579 Efi efi; 580 efi.hwParent = hwParent; 581 efi.cbd.callback = dg; 582 scope (exit) if (efi.cbd.exception) { 583 throw efi.cbd.exception; 584 } 585 return EnumChildWindows(hwParent, &enumingFirstWindows, cast(LPARAM)&efi); 586 } 587 588 589 590 enum ControlFont: ubyte { 591 COMPATIBLE, /// 592 OLD, /// ditto 593 NATIVE, /// ditto 594 } 595 596 597 debug { 598 import std.string; 599 } 600 601 602 /// Control class. 603 class Control: DObject, IWindow { // docmain 604 605 static class ControlCollection { 606 protected this(Control owner) { 607 _owner = owner; 608 } 609 610 611 deprecated alias length count; 612 613 614 @property int length() { // getter 615 if(_owner.isHandleCreated) { 616 // Inefficient :( 617 uint len = 0; 618 foreach(Control ctrl; this) { 619 len++; 620 } 621 return len; 622 } else { 623 return children.length; 624 } 625 } 626 627 628 629 @property Control opIndex(int i) { // getter 630 if(_owner.isHandleCreated) { 631 int oni = 0; 632 foreach(Control ctrl; this) { 633 if(oni == i) { 634 return ctrl; 635 } 636 oni++; 637 } 638 // Index out of bounds, bad things happen. 639 assert(0); 640 } else { 641 return children[i]; 642 } 643 } 644 645 646 647 void add(Control ctrl) { 648 ctrl.parent = _owner; 649 } 650 651 652 653 // opIn ? 654 bool contains(Control ctrl) { 655 return indexOf(ctrl) != -1; 656 } 657 658 659 660 int indexOf(Control ctrl) { 661 if(_owner.isHandleCreated) { 662 int i = 0; 663 int foundi = -1; 664 665 666 BOOL enuming(HWND hwnd) { 667 if(hwnd == ctrl.handle) { 668 foundi = i; 669 return false; // Stop. 670 } 671 672 i++; 673 return true; // Continue. 674 } 675 676 677 enumFirstChildWindows(_owner.handle, &enuming); 678 return foundi; 679 } else { 680 foreach(int i, Control onCtrl; children) { 681 if(onCtrl == ctrl) { 682 return i; 683 } 684 } 685 return -1; 686 } 687 } 688 689 690 691 void remove(Control ctrl) { 692 if(_owner.isHandleCreated) { 693 _removeCreated(ctrl.handle); 694 } else { 695 int i = indexOf(ctrl); 696 if(i != -1) { 697 _removeNotCreated(i); 698 } 699 } 700 } 701 702 703 private void _removeCreated(HWND hwnd) { 704 DestroyWindow(hwnd); // ? 705 } 706 707 708 package void _removeNotCreated(int i) { 709 if(!i) { 710 children = children[1 .. children.length]; 711 } else if(i == children.length - 1) { 712 children = children[0 .. i]; 713 } else { 714 children = children[0 .. i] ~ children[i + 1 .. children.length]; 715 } 716 } 717 718 719 720 void removeAt(int i) { 721 if(_owner.isHandleCreated) { 722 int ith = 0; 723 HWND hwndith; 724 725 726 BOOL enuming(HWND hwnd) { 727 if(ith == i) { 728 hwndith = hwnd; 729 return false; // Stop. 730 } 731 732 ith++; 733 return true; // Continue. 734 } 735 736 737 enumFirstChildWindows(_owner.handle, &enuming); 738 if(hwndith) { 739 _removeCreated(hwndith); 740 } 741 } else { 742 _removeNotCreated(i); 743 } 744 } 745 746 747 protected final @property Control owner() { // getter 748 return _owner; 749 } 750 751 752 753 int opApply(int delegate(ref Control) dg) { 754 int result = 0; 755 756 if(_owner.isHandleCreated) { 757 BOOL enuming(HWND hwnd) { 758 Control ctrl = fromHandle(hwnd); 759 if(ctrl) { 760 result = dg(ctrl); 761 if(result) { 762 return false; // Stop. 763 } 764 } 765 766 return true; // Continue. 767 } 768 769 770 enumFirstChildWindows(_owner.handle, &enuming); 771 } else { 772 foreach(Control ctrl; children) { 773 result = dg(ctrl); 774 if(result) { 775 break; 776 } 777 } 778 } 779 780 return result; 781 } 782 783 mixin OpApplyAddIndex!(opApply, Control); 784 785 786 package: 787 Control _owner; 788 Control[] children; // Only valid if -owner- isn't created yet (or is recreating). 789 790 791 /+ 792 final void _array_swap(int ifrom, int ito) { 793 if(ifrom == ito || 794 ifrom < 0 || ito < 0 || 795 ifrom >= length || ito >= length) { 796 return; 797 } 798 799 Control cto; 800 cto = children[ito]; 801 children[ito] = children[ifrom]; 802 children[ifrom] = cto; 803 } 804 +/ 805 806 807 final void _simple_front_one(int i) { 808 if(i < 0 || i >= length - 1) { 809 return; 810 } 811 812 children = children[0 .. i] ~ children[i + 1 .. i + 2] ~ children[i .. i + 1] ~ children[i + 2 .. children.length]; 813 } 814 815 816 final void _simple_front_one(Control c) { 817 return _simple_front_one(indexOf(c)); 818 } 819 820 821 final void _simple_back_one(int i) { 822 if(i <= 0 || i >= length) { 823 return; 824 } 825 826 children = children[0 .. i - 1] ~ children[i + 1 .. i + 2] ~ children[i .. i + 1] ~ children[i + 2 .. children.length]; 827 } 828 829 830 final void _simple_back_one(Control c) { 831 return _simple_back_one(indexOf(c)); 832 } 833 834 835 final void _simple_back(int i) { 836 if(i <= 0 || i >= length) { 837 return; 838 } 839 840 children = children[i .. i + 1] ~ children[0 .. i] ~ children[i + 1 .. children.length]; 841 } 842 843 844 final void _simple_back(Control c) { 845 return _simple_back(indexOf(c)); 846 } 847 848 849 final void _simple_front(int i) { 850 if(i < 0 || i >= length - 1) { 851 return; 852 } 853 854 children = children[0 .. i] ~ children[i + 1 .. children.length] ~ children[i .. i + 1]; 855 } 856 857 858 final void _simple_front(Control c) { 859 return _simple_front(indexOf(c)); 860 } 861 } 862 863 864 private void _ctrladded(ControlEventArgs cea) { 865 if(Application._compat & DflCompat.CONTROL_PARENT_096) { 866 if(!(_exStyle() & WS_EX_CONTROLPARENT)) { 867 if(!(cbits & CBits.FORM)) { 868 //if((cea.control._style() & WS_TABSTOP) || (cea.control._exStyle() & WS_EX_CONTROLPARENT)) 869 _exStyle(_exStyle() | WS_EX_CONTROLPARENT); 870 } 871 } 872 } else { 873 assert(getStyle(ControlStyles.CONTAINER_CONTROL), "Control added to non-container parent"); 874 } 875 876 onControlAdded(cea); 877 } 878 879 880 private void _ctrlremoved(ControlEventArgs cea) { 881 alayout(cea.control); 882 883 onControlRemoved(cea); 884 } 885 886 887 888 protected void onControlAdded(ControlEventArgs cea) { 889 controlAdded(this, cea); 890 } 891 892 893 894 protected void onControlRemoved(ControlEventArgs cea) { 895 controlRemoved(this, cea); 896 } 897 898 899 900 @property final HWindow handle() { // IWindow getter 901 if(!isHandleCreated) { 902 debug(APP_PRINT) 903 cprintf("Control created due to handle request.\n"); 904 905 createHandle(); 906 } 907 908 return hwnd; 909 } 910 911 912 version(DFL_NO_DRAG_DROP) {} else { 913 914 @property void allowDrop(bool byes) { // setter 915 /+ 916 if(dyes) { 917 _exStyle(_exStyle() | WS_EX_ACCEPTFILES); 918 } else { 919 _exStyle(_exStyle() & ~WS_EX_ACCEPTFILES); 920 } 921 +/ 922 923 if(byes) { 924 if(!droptarget) { 925 droptarget = new DropTarget(this); 926 if(isHandleCreated) { 927 switch(RegisterDragDrop(hwnd, droptarget)) { 928 case S_OK: 929 case DRAGDROP_E_ALREADYREGISTERED: // Hmm. 930 break; 931 932 default: 933 droptarget = null; 934 throw new DflException("Unable to register drag-drop"); 935 } 936 } 937 } 938 } else { 939 delete droptarget; 940 droptarget = null; 941 RevokeDragDrop(hwnd); 942 } 943 } 944 945 /// ditto 946 @property bool allowDrop() { // getter 947 /+ 948 return (_exStyle() & WS_EX_ACCEPTFILES) != 0; 949 +/ 950 951 return droptarget !is null; 952 } 953 } 954 955 956 /+ 957 deprecated void anchor(AnchorStyles a) { // setter 958 /+ 959 anch = a; 960 if(!(anch & (AnchorStyles.LEFT | AnchorStyles.RIGHT))) { 961 anch |= AnchorStyles.LEFT; 962 } 963 if(!(anch & (AnchorStyles.TOP | AnchorStyles.BOTTOM))) { 964 anch |= AnchorStyles.TOP; 965 } 966 +/ 967 968 sdock = DockStyle.NONE; // Can't be set at the same time. 969 } 970 971 972 deprecated AnchorStyles anchor() { // getter 973 //return anch; 974 return cast(AnchorStyles)(AnchorStyles.LEFT | AnchorStyles.TOP); 975 } 976 +/ 977 978 979 private void _propagateBackColorAmbience() { 980 Color bc; 981 bc = backColor; 982 983 984 void pa(Control pc) { 985 foreach(Control ctrl; pc.ccollection) { 986 if(Color.empty == ctrl.backc) { // If default. 987 if(bc == ctrl.backColor) { // If same default. 988 ctrl.deleteThisBackgroundBrush(); // Needs to be recreated with new color. 989 ctrl.onBackColorChanged(EventArgs.empty); 990 991 pa(ctrl); // Recursive. 992 } 993 } 994 } 995 } 996 997 998 pa(this); 999 } 1000 1001 1002 1003 protected void onBackColorChanged(EventArgs ea) { 1004 debug(EVENT_PRINT) { 1005 cprintf("{ Event: onBackColorChanged - Control %.*s }\n", name); 1006 } 1007 1008 backColorChanged(this, ea); 1009 } 1010 1011 1012 1013 @property void backColor(Color c) { // setter 1014 if(backc == c) { 1015 return; 1016 } 1017 1018 deleteThisBackgroundBrush(); // Needs to be recreated with new color. 1019 backc = c; 1020 onBackColorChanged(EventArgs.empty); 1021 1022 _propagateBackColorAmbience(); 1023 if(isHandleCreated) { 1024 invalidate(true); // Redraw! 1025 } 1026 } 1027 1028 /// ditto 1029 @property Color backColor() { // getter 1030 if(Color.empty == backc) { 1031 if(parent) { 1032 return parent.backColor; 1033 } 1034 return defaultBackColor; 1035 } 1036 return backc; 1037 } 1038 1039 1040 1041 final @property int bottom() { // getter 1042 return wrect.bottom; 1043 } 1044 1045 1046 1047 final @property void bounds(Rect r) { // setter 1048 setBoundsCore(r.x, r.y, r.width, r.height, BoundsSpecified.ALL); 1049 } 1050 1051 /// ditto 1052 final @property Rect bounds() { // getter 1053 return wrect; 1054 } 1055 1056 1057 /+ 1058 final @property Rect originalBounds() { // getter package 1059 return oldwrect; 1060 } 1061 +/ 1062 1063 1064 1065 protected void setBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { 1066 // Make sure at least one flag is set. 1067 //if(!(specified & BoundsSpecified.ALL)) 1068 if(!specified) { 1069 return; 1070 } 1071 1072 if(isHandleCreated) { 1073 UINT swpf = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE; 1074 1075 if(specified & BoundsSpecified.X) { 1076 if(!(specified & BoundsSpecified.Y)) { 1077 y = this.top(); 1078 } 1079 swpf &= ~SWP_NOMOVE; 1080 } else if(specified & BoundsSpecified.Y) { 1081 x = this.left(); 1082 swpf &= ~SWP_NOMOVE; 1083 } 1084 1085 if(specified & BoundsSpecified.WIDTH) { 1086 if(!(specified & BoundsSpecified.HEIGHT)) { 1087 height = this.height(); 1088 } 1089 swpf &= ~SWP_NOSIZE; 1090 } else if(specified & BoundsSpecified.HEIGHT) { 1091 width = this.width(); 1092 swpf &= ~SWP_NOSIZE; 1093 } 1094 1095 SetWindowPos(hwnd, HWND.init, x, y, width, height, swpf); 1096 // Window events will update -wrect-. 1097 } else { 1098 if(specified & BoundsSpecified.X) { 1099 wrect.x = x; 1100 } 1101 if(specified & BoundsSpecified.Y) { 1102 wrect.y = y; 1103 } 1104 if(specified & BoundsSpecified.WIDTH) { 1105 if(width < 0) { 1106 width = 0; 1107 } 1108 1109 wrect.width = width; 1110 wclientsz.width = width; 1111 } 1112 if(specified & BoundsSpecified.HEIGHT) { 1113 if(height < 0) { 1114 height = 0; 1115 } 1116 1117 wrect.height = height; 1118 wclientsz.height = height; 1119 } 1120 1121 //oldwrect = wrect; 1122 } 1123 } 1124 1125 1126 1127 final @property bool canFocus() { // getter 1128 /+ 1129 LONG wl = _style(); 1130 return /+ hwnd && +/ (wl & WS_VISIBLE) && !(wl & WS_DISABLED); 1131 +/ 1132 //return visible && enabled; 1133 // Don't need to check -isHandleCreated- because IsWindowVisible() will fail from a null HWND. 1134 return /+ isHandleCreated && +/ IsWindowVisible(hwnd) && IsWindowEnabled(hwnd); 1135 } 1136 1137 1138 1139 final @property bool canSelect() // getter 1140 out(result) { 1141 if(result) { 1142 assert(isHandleCreated); 1143 } 1144 } 1145 body { 1146 // All parent controls need to be visible and enabled, too. 1147 // Don't need to check -isHandleCreated- because IsWindowVisible() will fail from a null HWND. 1148 return /+ isHandleCreated && +/ (ctrlStyle & ControlStyles.SELECTABLE) && 1149 IsWindowVisible(hwnd) && IsWindowEnabled(hwnd); 1150 } 1151 1152 1153 package final bool _hasSelStyle() { 1154 return getStyle(ControlStyles.SELECTABLE); 1155 } 1156 1157 1158 1159 // Returns true if this control has the mouse capture. 1160 final @property bool capture() { // getter 1161 return isHandleCreated && hwnd == GetCapture(); 1162 } 1163 1164 /// ditto 1165 final @property void capture(bool cyes) { // setter 1166 if(cyes) { 1167 SetCapture(hwnd); 1168 } else { 1169 ReleaseCapture(); 1170 } 1171 } 1172 1173 1174 // When true, validating and validated events are fired when the control 1175 // receives focus. Typically set to false for controls such as a Help button. 1176 // Default is true. 1177 deprecated final bool causesValidation() { // getter 1178 //return cvalidation; 1179 return false; 1180 } 1181 1182 1183 deprecated protected void onCausesValidationChanged(EventArgs ea) { 1184 //causesValidationChanged(this, ea); 1185 } 1186 1187 1188 deprecated final void causesValidation(bool vyes) { // setter 1189 /+ 1190 if(cvalidation == vyes) { 1191 return; 1192 } 1193 1194 cvalidation = vyes; 1195 1196 onCausesValidationChanged(EventArgs.empty); 1197 +/ 1198 } 1199 1200 1201 1202 final @property Rect clientRectangle() { // getter 1203 return Rect(Point(0, 0), wclientsz); 1204 } 1205 1206 1207 1208 final bool contains(Control ctrl) { 1209 //return ccollection.contains(ctrl); 1210 return ctrl && ctrl.parent is this; 1211 } 1212 1213 1214 1215 final @property Size clientSize() { // getter 1216 return wclientsz; 1217 } 1218 1219 /// ditto 1220 final @property void clientSize(Size sz) { // setter 1221 setClientSizeCore(sz.width, sz.height); 1222 } 1223 1224 1225 1226 protected void setClientSizeCore(int width, int height) { 1227 /+ 1228 if(isHandleCreated) { 1229 setBoundsCore(0, 0, width, height, BoundsSpecified.SIZE); 1230 } 1231 1232 //wclientsz = Size(width, height); 1233 +/ 1234 1235 RECT r; 1236 1237 r.left = 0; 1238 r.top = 0; 1239 r.right = width; 1240 r.bottom = height; 1241 1242 AdjustWindowRectEx(&r, _style(), FALSE, _exStyle()); 1243 1244 setBoundsCore(0, 0, r.right - r.left, r.bottom - r.top, BoundsSpecified.SIZE); 1245 } 1246 1247 1248 1249 // This window or one of its children has focus. 1250 final @property bool containsFocus() { // getter 1251 if(!isHandleCreated) { 1252 return false; 1253 } 1254 1255 HWND hwfocus = GetFocus(); 1256 return hwfocus == hwnd || IsChild(hwnd, hwfocus); 1257 } 1258 1259 1260 version(DFL_NO_MENUS) { 1261 } 1262 else { 1263 1264 protected void onContextMenuChanged(EventArgs ea) { 1265 contextMenuChanged(this, ea); 1266 } 1267 1268 1269 1270 @property void contextMenu(ContextMenu menu) { // setter 1271 if(cmenu is menu) { 1272 return; 1273 } 1274 1275 cmenu = menu; 1276 1277 if(isHandleCreated) { 1278 onContextMenuChanged(EventArgs.empty); 1279 } 1280 } 1281 1282 /// ditto 1283 @property ContextMenu contextMenu() { // getter 1284 return cmenu; 1285 } 1286 } 1287 1288 1289 1290 final @property ControlCollection controls() { // getter 1291 //return new ControlCollection(this); 1292 return ccollection; 1293 } 1294 1295 1296 1297 final @property bool created() { // getter 1298 // To-do: only return true when createHandle finishes. 1299 // Will also need to update uses of created/isHandleCreated. 1300 // Return false again when disposing/killing. 1301 //return isHandleCreated; 1302 return isHandleCreated || recreatingHandle; 1303 } 1304 1305 1306 private void _propagateCursorAmbience() { 1307 Cursor cur; 1308 cur = cursor; 1309 1310 1311 void pa(Control pc) { 1312 foreach(Control ctrl; pc.ccollection) { 1313 if(ctrl.wcurs is null) { // If default. 1314 if(cur is ctrl.cursor) { // If same default. 1315 ctrl.onCursorChanged(EventArgs.empty); 1316 1317 pa(ctrl); // Recursive. 1318 } 1319 } 1320 } 1321 } 1322 1323 1324 pa(this); 1325 } 1326 1327 1328 1329 protected void onCursorChanged(EventArgs ea) { 1330 /+ 1331 debug(EVENT_PRINT) { 1332 cprintf("{ Event: onCursorChanged - Control %.*s }\n", name); 1333 } 1334 +/ 1335 1336 if(isHandleCreated) { 1337 if(visible && enabled) { 1338 Point curpt = Cursor.position; 1339 if(hwnd == WindowFromPoint(curpt.point)) { 1340 SendMessageA(hwnd, WM_SETCURSOR, cast(WPARAM)hwnd, 1341 MAKELPARAM( 1342 SendMessageA(hwnd, WM_NCHITTEST, 0, MAKELPARAM(curpt.x, curpt.y)), 1343 WM_MOUSEMOVE) 1344 ); 1345 } 1346 } 1347 } 1348 1349 cursorChanged(this, ea); 1350 } 1351 1352 1353 1354 @property void cursor(Cursor cur) { // setter 1355 if(cur is wcurs) { 1356 return; 1357 } 1358 1359 wcurs = cur; 1360 onCursorChanged(EventArgs.empty); 1361 1362 _propagateCursorAmbience(); 1363 } 1364 1365 /// ditto 1366 @property Cursor cursor() { // getter 1367 if(!wcurs) { 1368 if(parent) { 1369 return parent.cursor; 1370 } 1371 return _defaultCursor; 1372 } 1373 return wcurs; 1374 } 1375 1376 1377 1378 static @property Color defaultBackColor() { // getter 1379 return Color.systemColor(COLOR_BTNFACE); 1380 } 1381 1382 1383 1384 static @property Color defaultForeColor() { //getter 1385 return Color.systemColor(COLOR_BTNTEXT); 1386 } 1387 1388 1389 private static Font _deffont = null; 1390 1391 1392 private static Font _createOldFont() { 1393 return new Font(cast(HFONT)GetStockObject(DEFAULT_GUI_FONT), false); 1394 } 1395 1396 1397 private static Font _createCompatibleFont() { 1398 Font result; 1399 result = _createOldFont(); 1400 1401 try { 1402 OSVERSIONINFOA osi; 1403 osi.dwOSVersionInfoSize = osi.sizeof; 1404 if(GetVersionExA(&osi) && osi.dwMajorVersion >= 5) { 1405 // "MS Shell Dlg" / "MS Shell Dlg 2" not always supported. 1406 result = new Font("MS Shell Dlg 2", result.getSize(GraphicsUnit.POINT), GraphicsUnit.POINT); 1407 } 1408 } catch { 1409 } 1410 1411 //if(!result) 1412 // result = _createOldFont(); 1413 assert(result !is null); 1414 1415 return result; 1416 } 1417 1418 1419 private static Font _createNativeFont() { 1420 Font result; 1421 1422 NONCLIENTMETRICSA ncm; 1423 ncm.cbSize = ncm.sizeof; 1424 if(!SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, ncm.sizeof, &ncm, 0)) { 1425 result = _createCompatibleFont(); 1426 } else { 1427 result = new Font(&ncm.lfMessageFont, true); 1428 } 1429 1430 return result; 1431 } 1432 1433 1434 private static void _setDeffont(ControlFont cf) { 1435 synchronized { 1436 assert(_deffont is null); 1437 switch(cf) { 1438 case ControlFont.COMPATIBLE: 1439 _deffont = _createCompatibleFont(); 1440 break; 1441 case ControlFont.NATIVE: 1442 _deffont = _createNativeFont(); 1443 break; 1444 case ControlFont.OLD: 1445 _deffont = _createOldFont(); 1446 break; 1447 default: 1448 assert(0); 1449 } 1450 } 1451 } 1452 1453 1454 deprecated alias defaultFont controlFont; 1455 1456 1457 static @property void defaultFont(ControlFont cf) { // setter 1458 if(_deffont) { 1459 throw new DflException("Control font already selected"); 1460 } 1461 _setDeffont(cf); 1462 } 1463 1464 /// ditto 1465 static @property void defaultFont(Font f) { // setter 1466 if(_deffont) { 1467 throw new DflException("Control font already selected"); 1468 } 1469 _deffont = f; 1470 } 1471 1472 /// ditto 1473 static @property Font defaultFont() { // getter 1474 if(!_deffont) { 1475 _setDeffont(ControlFont.COMPATIBLE); 1476 } 1477 1478 return _deffont; 1479 } 1480 1481 1482 package static class SafeCursor: Cursor { 1483 this(HCURSOR hcur) { 1484 super(hcur, false); 1485 } 1486 1487 1488 override void dispose() { 1489 } 1490 1491 1492 /+ 1493 ~this() { 1494 super.dispose(); 1495 } 1496 +/ 1497 } 1498 1499 1500 package static @property Cursor _defaultCursor() { // getter 1501 static Cursor def = null; 1502 1503 if(!def) { 1504 synchronized { 1505 if(!def) { 1506 def = new SafeCursor(LoadCursorA(HINSTANCE.init, IDC_ARROW)); 1507 } 1508 } 1509 } 1510 1511 return def; 1512 } 1513 1514 1515 1516 @property Rect displayRectangle() { // getter 1517 return clientRectangle; 1518 } 1519 1520 1521 1522 //protected void onDockChanged(EventArgs ea) 1523 protected void onHasLayoutChanged(EventArgs ea) { 1524 if(parent) { 1525 parent.alayout(this); 1526 } 1527 1528 //dockChanged(this, ea); 1529 hasLayoutChanged(this, ea); 1530 } 1531 1532 alias onHasLayoutChanged onDockChanged; 1533 1534 1535 private final void _alreadyLayout() { 1536 throw new DflException("Control already has a layout"); 1537 } 1538 1539 1540 1541 @property DockStyle dock() { // getter 1542 return sdock; 1543 } 1544 1545 /// ditto 1546 @property void dock(DockStyle ds) { // setter 1547 if(ds == sdock) { 1548 return; 1549 } 1550 1551 DockStyle _olddock = sdock; 1552 sdock = ds; 1553 /+ 1554 anch = AnchorStyles.NONE; // Can't be set at the same time. 1555 +/ 1556 1557 if(DockStyle.NONE == ds) { 1558 if(DockStyle.NONE != _olddock) { // If it was even docking before; don't unset hasLayout for something else. 1559 hasLayout = false; 1560 } 1561 } else { 1562 // Ensure not replacing some other layout, but OK if replacing another dock. 1563 if(DockStyle.NONE == _olddock) { 1564 if(hasLayout) { 1565 _alreadyLayout(); 1566 } 1567 } 1568 hasLayout = true; 1569 } 1570 1571 /+ // Called by hasLayout. 1572 if(isHandleCreated) { 1573 onDockChanged(EventArgs.empty); 1574 } 1575 +/ 1576 } 1577 1578 1579 /// Get or set whether or not this control currently has its bounds managed. Fires onHasLayoutChanged as needed. 1580 final @property bool hasLayout() { // getter 1581 if(cbits & CBits.HAS_LAYOUT) { 1582 return true; 1583 } 1584 return false; 1585 } 1586 1587 /// ditto 1588 final @property void hasLayout(bool byes) { // setter 1589 //if(byes == hasLayout) 1590 // return; // No! setting this property again must trigger onHasLayoutChanged again. 1591 1592 if(byes) { 1593 cbits |= CBits.HAS_LAYOUT; 1594 } else { 1595 cbits &= ~CBits.HAS_LAYOUT; 1596 } 1597 1598 if(byes) { // No need if layout is removed. 1599 if(isHandleCreated) { 1600 onHasLayoutChanged(EventArgs.empty); 1601 } 1602 } 1603 } 1604 1605 1606 package final void _venabled(bool byes) { 1607 if(isHandleCreated) { 1608 EnableWindow(hwnd, byes); 1609 // Window events will update -wstyle-. 1610 } else { 1611 if(byes) { 1612 wstyle &= ~WS_DISABLED; 1613 } else { 1614 wstyle |= WS_DISABLED; 1615 } 1616 } 1617 } 1618 1619 1620 1621 final @property void enabled(bool byes) { // setter 1622 if(byes) { 1623 cbits |= CBits.ENABLED; 1624 } else { 1625 cbits &= ~CBits.ENABLED; 1626 } 1627 1628 /+ 1629 if(!byes) { 1630 _venabled(false); 1631 } else { 1632 if(!parent || parent.enabled) { 1633 _venabled(true); 1634 } 1635 } 1636 1637 _propagateEnabledAmbience(); 1638 +/ 1639 1640 _venabled(byes); 1641 } 1642 1643 1644 final @property bool enabled() { // getter 1645 /* 1646 return IsWindowEnabled(hwnd) ? true : false; 1647 */ 1648 1649 return (wstyle & WS_DISABLED) == 0; 1650 } 1651 1652 1653 private void _propagateEnabledAmbience() { 1654 /+ // Isn't working... 1655 if(cbits & CBits.FORM) { 1656 return; 1657 } 1658 1659 bool en = enabled; 1660 1661 void pa(Control pc) { 1662 foreach(Control ctrl; pc.ccollection) { 1663 if(ctrl.cbits & CBits.ENABLED) { 1664 _venabled(en); 1665 1666 pa(ctrl); 1667 } 1668 } 1669 } 1670 1671 pa(this); 1672 +/ 1673 } 1674 1675 1676 1677 final void enable() { 1678 enabled = true; 1679 } 1680 1681 /// ditto 1682 final void disable() { 1683 enabled = false; 1684 } 1685 1686 1687 1688 @property bool focused() { // getter 1689 //return isHandleCreated && hwnd == GetFocus(); 1690 return created && fromChildHandle(GetFocus()) is this; 1691 } 1692 1693 1694 1695 @property void font(Font f) { // setter 1696 if(wfont is f) { 1697 return; 1698 } 1699 1700 wfont = f; 1701 if(isHandleCreated) { 1702 SendMessageA(hwnd, WM_SETFONT, cast(WPARAM)wfont.handle, MAKELPARAM(true, 0)); 1703 } 1704 onFontChanged(EventArgs.empty); 1705 1706 _propagateFontAmbience(); 1707 } 1708 1709 /// ditto 1710 @property Font font() { // getter 1711 if(!wfont) { 1712 if(parent) { 1713 return parent.font; 1714 } 1715 return defaultFont; 1716 } 1717 return wfont; 1718 } 1719 1720 1721 private void _propagateForeColorAmbience() { 1722 Color fc; 1723 fc = foreColor; 1724 1725 1726 void pa(Control pc) { 1727 foreach(Control ctrl; pc.ccollection) { 1728 if(Color.empty == ctrl.forec) { // If default. 1729 if(fc == ctrl.foreColor) { // If same default. 1730 ctrl.onForeColorChanged(EventArgs.empty); 1731 1732 pa(ctrl); // Recursive. 1733 } 1734 } 1735 } 1736 } 1737 1738 1739 pa(this); 1740 } 1741 1742 1743 1744 protected void onForeColorChanged(EventArgs ea) { 1745 debug(EVENT_PRINT) { 1746 cprintf("{ Event: onForeColorChanged - Control %.*s }\n", name); 1747 } 1748 1749 foreColorChanged(this, ea); 1750 } 1751 1752 1753 1754 @property void foreColor(Color c) { // setter 1755 if(c == forec) { 1756 return; 1757 } 1758 1759 forec = c; 1760 onForeColorChanged(EventArgs.empty); 1761 1762 _propagateForeColorAmbience(); 1763 if(isHandleCreated) { 1764 invalidate(true); // Redraw! 1765 } 1766 } 1767 1768 /// ditto 1769 @property Color foreColor() { // getter 1770 if(Color.empty == forec) { 1771 if(parent) { 1772 return parent.foreColor; 1773 } 1774 return defaultForeColor; 1775 } 1776 return forec; 1777 } 1778 1779 1780 1781 // Doesn't cause a ControlCollection to be constructed so 1782 // it could improve performance when walking through children. 1783 final @property bool hasChildren() { // getter 1784 //return isHandleCreated && GetWindow(hwnd, GW_CHILD) != HWND.init; 1785 1786 if(isHandleCreated) { 1787 return GetWindow(hwnd, GW_CHILD) != HWND.init; 1788 } else { 1789 return ccollection.children.length != 0; 1790 } 1791 } 1792 1793 1794 1795 final @property void height(int h) { // setter 1796 /* 1797 RECT rect; 1798 GetWindowRect(hwnd, &rect); 1799 SetWindowPos(hwnd, HWND.init, 0, 0, rect.right - rect.left, h, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); 1800 */ 1801 1802 setBoundsCore(0, 0, 0, h, BoundsSpecified.HEIGHT); 1803 } 1804 1805 /// ditto 1806 final @property int height() { // getter 1807 return wrect.height; 1808 } 1809 1810 1811 1812 final @property bool isHandleCreated() { // getter 1813 return hwnd != HWND.init; 1814 } 1815 1816 1817 1818 final @property void left(int l) { // setter 1819 /* 1820 RECT rect; 1821 GetWindowRect(hwnd, &rect); 1822 SetWindowPos(hwnd, HWND.init, l, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); 1823 */ 1824 1825 setBoundsCore(l, 0, 0, 0, BoundsSpecified.X); 1826 } 1827 1828 /// ditto 1829 final @property int left() { // getter 1830 return wrect.x; 1831 } 1832 1833 1834 /// Property: get or set the X and Y location of the control. 1835 final @property void location(Point pt) { // setter 1836 /* 1837 SetWindowPos(hwnd, HWND.init, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); 1838 */ 1839 1840 setBoundsCore(pt.x, pt.y, 0, 0, BoundsSpecified.LOCATION); 1841 } 1842 1843 /// ditto 1844 final @property Point location() { // getter 1845 return wrect.location; 1846 } 1847 1848 1849 /// Currently depressed modifier keys. 1850 static @property Keys modifierKeys() { // getter 1851 // Is there a better way to do this? 1852 Keys ks = Keys.NONE; 1853 if(GetAsyncKeyState(VK_SHIFT) & 0x8000) { 1854 ks |= Keys.SHIFT; 1855 } 1856 if(GetAsyncKeyState(VK_MENU) & 0x8000) { 1857 ks |= Keys.ALT; 1858 } 1859 if(GetAsyncKeyState(VK_CONTROL) & 0x8000) { 1860 ks|= Keys.CONTROL; 1861 } 1862 return ks; 1863 } 1864 1865 1866 /// Currently depressed mouse buttons. 1867 static @property MouseButtons mouseButtons() { // getter 1868 MouseButtons result; 1869 1870 result = MouseButtons.NONE; 1871 if(GetSystemMetrics(SM_SWAPBUTTON)) { 1872 if(GetAsyncKeyState(VK_LBUTTON) & 0x8000) { 1873 result |= MouseButtons.RIGHT; // Swapped. 1874 } 1875 if(GetAsyncKeyState(VK_RBUTTON) & 0x8000) { 1876 result |= MouseButtons.LEFT; // Swapped. 1877 } 1878 } else { 1879 if(GetAsyncKeyState(VK_LBUTTON) & 0x8000) { 1880 result |= MouseButtons.LEFT; 1881 } 1882 if(GetAsyncKeyState(VK_RBUTTON) & 0x8000) { 1883 result |= MouseButtons.RIGHT; 1884 } 1885 } 1886 if(GetAsyncKeyState(VK_MBUTTON) & 0x8000) { 1887 result |= MouseButtons.MIDDLE; 1888 } 1889 1890 return result; 1891 } 1892 1893 1894 1895 static @property Point mousePosition() { // getter 1896 Point pt; 1897 GetCursorPos(&pt.point); 1898 return pt; 1899 } 1900 1901 1902 /// Property: get or set the name of this control used in code. 1903 final @property void name(Dstring txt) { // setter 1904 _ctrlname = txt; 1905 } 1906 1907 /// ditto 1908 final @property Dstring name() { // getter 1909 return _ctrlname; 1910 } 1911 1912 1913 1914 protected void onParentChanged(EventArgs ea) { 1915 debug(EVENT_PRINT) { 1916 cprintf("{ Event: onParentChanged - Control %.*s }\n", name); 1917 } 1918 1919 parentChanged(this, ea); 1920 } 1921 1922 1923 /+ 1924 1925 // ea is the new parent. 1926 protected void onParentChanging(ControlEventArgs ea) { 1927 } 1928 +/ 1929 1930 1931 1932 final Form findForm() { 1933 Form f; 1934 Control c; 1935 1936 c = this; 1937 for(;;) { 1938 f = cast(Form)c; 1939 if(f) { 1940 break; 1941 } 1942 c = c.parent; 1943 if(!c) { 1944 return null; 1945 } 1946 } 1947 return f; 1948 } 1949 1950 1951 1952 final @property void parent(Control c) { // setter 1953 if(c is wparent) { 1954 return; 1955 } 1956 1957 if(!(_style() & WS_CHILD) || (_exStyle() & WS_EX_MDICHILD)) { 1958 throw new DflException("Cannot add a top level control to a control"); 1959 } 1960 1961 //scope ControlEventArgs pcea = new ControlEventArgs(c); 1962 //onParentChanging(pcea); 1963 1964 Control oldparent; 1965 _FixAmbientOld oldinfo; 1966 1967 oldparent = wparent; 1968 1969 if(oldparent) { 1970 oldinfo.set(oldparent); 1971 1972 if(!oldparent.isHandleCreated) { 1973 int oi = oldparent.controls.indexOf(this); 1974 //assert(-1 != oi); // Fails if the parent (and thus this) handles destroyed. 1975 if(-1 != oi) { 1976 oldparent.controls._removeNotCreated(oi); 1977 } 1978 } 1979 } else { 1980 oldinfo.set(this); 1981 } 1982 1983 scope ControlEventArgs cea = new ControlEventArgs(this); 1984 1985 if(c) { 1986 wparent = c; 1987 1988 // I want the destroy notification. Don't need it anymore. 1989 //c._exStyle(c._exStyle() & ~WS_EX_NOPARENTNOTIFY); 1990 1991 if(c.isHandleCreated) { 1992 cbits &= ~CBits.NEED_INIT_LAYOUT; 1993 1994 //if(created) 1995 if(isHandleCreated) { 1996 SetParent(hwnd, c.hwnd); 1997 } else { 1998 // If the parent is created, create me! 1999 createControl(); 2000 } 2001 2002 onParentChanged(EventArgs.empty); 2003 if(oldparent) { 2004 oldparent._ctrlremoved(cea); 2005 } 2006 c._ctrladded(cea); 2007 _fixAmbient(&oldinfo); 2008 2009 initLayout(); 2010 } else { 2011 // If the parent exists and isn't created, need to add 2012 // -this- to its children array. 2013 c.ccollection.children ~= this; 2014 2015 onParentChanged(EventArgs.empty); 2016 if(oldparent) { 2017 oldparent._ctrlremoved(cea); 2018 } 2019 c._ctrladded(cea); 2020 _fixAmbient(&oldinfo); 2021 2022 cbits |= CBits.NEED_INIT_LAYOUT; 2023 } 2024 } else { 2025 assert(c is null); 2026 //wparent = c; 2027 wparent = null; 2028 2029 if(isHandleCreated) { 2030 SetParent(hwnd, HWND.init); 2031 } 2032 2033 onParentChanged(EventArgs.empty); 2034 assert(oldparent !is null); 2035 oldparent._ctrlremoved(cea); 2036 _fixAmbient(&oldinfo); 2037 } 2038 } 2039 2040 /// ditto 2041 final @property Control parent() { // getter 2042 return wparent; 2043 } 2044 2045 2046 private final Control _fetchParent() { 2047 HWND hwParent = GetParent(hwnd); 2048 return fromHandle(hwParent); 2049 } 2050 2051 2052 // TODO: check implementation. 2053 private static HRGN dupHrgn(HRGN hrgn) { 2054 HRGN rdup = CreateRectRgn(0, 0, 1, 1); 2055 CombineRgn(rdup, hrgn, HRGN.init, RGN_COPY); 2056 return rdup; 2057 } 2058 2059 2060 2061 final @property void region(Region rgn) { // setter 2062 if(isHandleCreated) { 2063 // Need to make a copy of the region. 2064 SetWindowRgn(hwnd, dupHrgn(rgn.handle), true); 2065 } 2066 2067 wregion = rgn; 2068 } 2069 2070 /// ditto 2071 final @property Region region() { // getter 2072 return wregion; 2073 } 2074 2075 2076 private final Region _fetchRegion() { 2077 HRGN hrgn = CreateRectRgn(0, 0, 1, 1); 2078 GetWindowRgn(hwnd, hrgn); 2079 return new Region(hrgn); // Owned because GetWindowRgn() gives a copy. 2080 } 2081 2082 2083 2084 final @property int right() { // getter 2085 return wrect.right; 2086 } 2087 2088 2089 /+ 2090 @property void rightToLeft(bool byes) { // setter 2091 LONG wl = _exStyle(); 2092 if(byes) { 2093 wl |= WS_EX_RTLREADING; 2094 } else { 2095 wl &= ~WS_EX_RTLREADING; 2096 } 2097 _exStyle(wl); 2098 } 2099 2100 2101 @property bool rightToLeft() { // getter 2102 return (_exStyle() & WS_EX_RTLREADING) != 0; 2103 } 2104 +/ 2105 2106 2107 deprecated @property void rightToLeft(bool byes) { // setter 2108 rightToLeft = byes ? RightToLeft.YES : RightToLeft.NO; 2109 } 2110 2111 2112 package final void _fixRtol(RightToLeft val) { 2113 switch(val) { 2114 case RightToLeft.INHERIT: 2115 if(parent && parent.rightToLeft == RightToLeft.YES) { 2116 goto case RightToLeft.YES; 2117 } 2118 goto case RightToLeft.NO; 2119 2120 case RightToLeft.YES: 2121 _exStyle(_exStyle() | WS_EX_RTLREADING); 2122 break; 2123 2124 case RightToLeft.NO: 2125 _exStyle(_exStyle() & ~WS_EX_RTLREADING); 2126 break; 2127 2128 default: 2129 assert(0); 2130 } 2131 2132 //invalidate(true); // Children too in case they inherit. 2133 invalidate(false); // Since children are enumerated. 2134 } 2135 2136 2137 private void _propagateRtolAmbience() { 2138 RightToLeft rl; 2139 rl = rightToLeft; 2140 2141 2142 void pa(Control pc) { 2143 if(RightToLeft.INHERIT == pc.rtol) { 2144 //pc._fixRtol(rtol); 2145 pc._fixRtol(rl); // Set the specific parent value so it doesn't have to look up the chain. 2146 2147 foreach(Control ctrl; pc.ccollection) { 2148 ctrl.onRightToLeftChanged(EventArgs.empty); 2149 2150 pa(ctrl); 2151 } 2152 } 2153 } 2154 2155 2156 pa(this); 2157 } 2158 2159 2160 2161 @property void rightToLeft(RightToLeft val) { // setter 2162 if(rtol != val) { 2163 rtol = val; 2164 onRightToLeftChanged(EventArgs.empty); 2165 _propagateRtolAmbience(); // Also sets the class style and invalidates. 2166 } 2167 } 2168 2169 /// ditto 2170 // Returns YES or NO; if inherited, returns parent's setting. 2171 @property RightToLeft rightToLeft() { // getter 2172 if(RightToLeft.INHERIT == rtol) { 2173 return parent ? parent.rightToLeft : RightToLeft.NO; 2174 } 2175 return rtol; 2176 } 2177 2178 2179 package struct _FixAmbientOld { 2180 Font font; 2181 Cursor cursor; 2182 Color backColor; 2183 Color foreColor; 2184 RightToLeft rightToLeft; 2185 //CBits cbits; 2186 bool enabled; 2187 2188 2189 void set(Control ctrl) { 2190 if(ctrl) { 2191 font = ctrl.font; 2192 cursor = ctrl.cursor; 2193 backColor = ctrl.backColor; 2194 foreColor = ctrl.foreColor; 2195 rightToLeft = ctrl.rightToLeft; 2196 //cbits = ctrl.cbits; 2197 enabled = ctrl.enabled; 2198 } 2199 /+else { 2200 font = null; 2201 cursor = null; 2202 backColor = Color.empty; 2203 foreColor = Color.empty; 2204 rightToLeft = RightToLeft.INHERIT; 2205 //cbits = CBits.init; 2206 enabled = true; 2207 }+/ 2208 } 2209 } 2210 2211 2212 // This is called when the inherited ambience changes. 2213 package final void _fixAmbient(_FixAmbientOld* oldinfo) { 2214 // Note: exception will screw things up. 2215 2216 _FixAmbientOld newinfo; 2217 if(parent) { 2218 newinfo.set(parent); 2219 } else { 2220 newinfo.set(this); 2221 } 2222 2223 if(RightToLeft.INHERIT == rtol) { 2224 if(newinfo.rightToLeft !is oldinfo.rightToLeft) { 2225 onRightToLeftChanged(EventArgs.empty); 2226 _propagateRtolAmbience(); 2227 } 2228 } 2229 2230 if(Color.empty == backc) { 2231 if(newinfo.backColor !is oldinfo.backColor) { 2232 onBackColorChanged(EventArgs.empty); 2233 _propagateBackColorAmbience(); 2234 } 2235 } 2236 2237 if(Color.empty == forec) { 2238 if(newinfo.foreColor !is oldinfo.foreColor) { 2239 onForeColorChanged(EventArgs.empty); 2240 _propagateForeColorAmbience(); 2241 } 2242 } 2243 2244 if(!wfont) { 2245 if(newinfo.font !is oldinfo.font) { 2246 onFontChanged(EventArgs.empty); 2247 _propagateFontAmbience(); 2248 } 2249 } 2250 2251 if(!wcurs) { 2252 if(newinfo.cursor !is oldinfo.cursor) { 2253 onCursorChanged(EventArgs.empty); 2254 _propagateCursorAmbience(); 2255 } 2256 } 2257 2258 /+ 2259 if(newinfo.enabled != oldinfo.enabled) { 2260 if(cbits & CBits.ENABLED) { 2261 _venabled(newinfo.enabled); 2262 _propagateEnabledAmbience(); 2263 } 2264 } 2265 +/ 2266 } 2267 2268 2269 /+ 2270 package final void _fixAmbientChildren() { 2271 foreach(Control ctrl; ccollection.children) { 2272 ctrl._fixAmbient(); 2273 } 2274 } 2275 +/ 2276 2277 2278 2279 final @property void size(Size sz) { // setter 2280 /* 2281 SetWindowPos(hwnd, HWND.init, 0, 0, sz.width, sz.height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); 2282 */ 2283 2284 setBoundsCore(0, 0, sz.width, sz.height, BoundsSpecified.SIZE); 2285 } 2286 2287 /// ditto 2288 final @property Size size() { // getter 2289 return wrect.size; // struct Size, not sizeof. 2290 } 2291 2292 2293 /+ 2294 final @property void tabIndex(int i) { // setter 2295 // TODO: ? 2296 } 2297 2298 2299 final @property int tabIndex() { // getter 2300 return tabidx; 2301 } 2302 +/ 2303 2304 2305 // Use -zIndex- instead. 2306 // -tabIndex- may return different values in the future. 2307 deprecated int tabIndex() { // getter 2308 return zIndex; 2309 } 2310 2311 2312 2313 final @property int zIndex() // getter 2314 out(result) { 2315 assert(result >= 0); 2316 } 2317 body { 2318 if(!parent) { 2319 return 0; 2320 } 2321 2322 if(isHandleCreated) { 2323 GetZIndex gzi; 2324 gzi.find = this; 2325 int index; 2326 int tmp; 2327 2328 BOOL getZIndexCallback(HWND hWnd) { 2329 if(hWnd is hwnd) { 2330 index = tmp; 2331 return FALSE; // Stop, found it. 2332 } 2333 2334 auto ctrl = Control.fromHandle(hWnd); 2335 if(ctrl && ctrl.parent is parent) { 2336 tmp++; 2337 } 2338 2339 return TRUE; // Keep looking. 2340 } 2341 2342 enumChildWindows(parent.hwnd, &getZIndexCallback); 2343 return index; 2344 } else 2345 { 2346 return parent.controls.indexOf(this); 2347 } 2348 } 2349 2350 2351 2352 // True if control can be tabbed to. 2353 final @property void tabStop(bool byes) { // setter 2354 LONG wl = _style(); 2355 if(byes) { 2356 wl |= WS_TABSTOP; 2357 } else { 2358 wl &= ~WS_TABSTOP; 2359 } 2360 _style(wl); 2361 } 2362 2363 /// ditto 2364 final @property bool tabStop() { // getter 2365 return (_style() & WS_TABSTOP) != 0; 2366 } 2367 2368 2369 /// Property: get or set additional data tagged onto the control. 2370 final @property void tag(Object o) { // setter 2371 otag = o; 2372 } 2373 2374 /// ditto 2375 final @property Object tag() { // getter 2376 return otag; 2377 } 2378 2379 2380 private final Dstring _fetchText() { 2381 return dfl.internal.utf.getWindowText(hwnd); 2382 } 2383 2384 2385 2386 @property void text(Dstring txt) { // setter 2387 if(isHandleCreated) { 2388 if(ctrlStyle & ControlStyles.CACHE_TEXT) { 2389 //if(wtext == txt) 2390 // return; 2391 wtext = txt; 2392 } 2393 2394 dfl.internal.utf.setWindowText(hwnd, txt); 2395 } else { 2396 wtext = txt; 2397 } 2398 } 2399 2400 /// ditto 2401 @property Dstring text() { // getter 2402 if(isHandleCreated) { 2403 if(ctrlStyle & ControlStyles.CACHE_TEXT) { 2404 return wtext; 2405 } 2406 2407 return _fetchText(); 2408 } else { 2409 return wtext; 2410 } 2411 } 2412 2413 2414 2415 final @property void top(int t) { // setter 2416 setBoundsCore(0, t, 0, 0, BoundsSpecified.Y); 2417 } 2418 2419 /// ditto 2420 final @property int top() { // getter 2421 return wrect.y; 2422 } 2423 2424 2425 /// Returns the topmost Control related to this control. 2426 // Returns the owner control that has no parent. 2427 // Returns this Control if no owner ? 2428 final @property Control topLevelControl() { // getter 2429 if(isHandleCreated) { 2430 HWND hwCurrent = hwnd; 2431 HWND hwParent; 2432 2433 for(;;) { 2434 hwParent = GetParent(hwCurrent); // This gets the top-level one, whereas the previous code jumped owners. 2435 if(!hwParent) { 2436 break; 2437 } 2438 2439 hwCurrent = hwParent; 2440 } 2441 2442 return fromHandle(hwCurrent); 2443 } else { 2444 Control ctrl; 2445 ctrl = this; 2446 while(ctrl.parent) { 2447 ctrl = ctrl.parent; // This shouldn't jump owners.. 2448 } 2449 return ctrl; 2450 } 2451 } 2452 2453 2454 /+ 2455 private DWORD _fetchVisible() { 2456 //return IsWindowVisible(hwnd) != FALSE; 2457 wstyle = GetWindowLongA(hwnd, GWL_STYLE); 2458 return wstyle & WS_VISIBLE; 2459 } 2460 +/ 2461 2462 2463 2464 final @property void visible(bool byes) { // setter 2465 setVisibleCore(byes); 2466 } 2467 2468 /// ditto 2469 final @property bool visible() { // getter 2470 //if(isHandleCreated) 2471 // wstyle = GetWindowLongA(hwnd, GWL_STYLE); // ... 2472 //return (wstyle & WS_VISIBLE) != 0; 2473 return (cbits & CBits.VISIBLE) != 0; 2474 } 2475 2476 2477 2478 final @property void width(int w) { // setter 2479 setBoundsCore(0, 0, w, 0, BoundsSpecified.WIDTH); 2480 } 2481 2482 /// ditto 2483 final @property int width() { // getter 2484 return wrect.width; 2485 } 2486 2487 2488 2489 final void sendToBack() { 2490 if(!isHandleCreated) { 2491 if(parent) { 2492 parent.ccollection._simple_front(this); 2493 } 2494 return; 2495 } 2496 2497 SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 2498 } 2499 2500 2501 2502 final void bringToFront() { 2503 if(!isHandleCreated) { 2504 if(parent) { 2505 parent.ccollection._simple_back(this); 2506 } 2507 return; 2508 } 2509 2510 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 2511 //BringWindowToTop(hwnd); 2512 } 2513 2514 2515 deprecated alias bringUpOne zIndexUp; 2516 2517 2518 // Move up one. 2519 final void bringUpOne() { 2520 if(!isHandleCreated) { 2521 if(parent) { 2522 parent.ccollection._simple_front_one(this); 2523 } 2524 return; 2525 } 2526 2527 HWND hw; 2528 2529 // Need to move back twice because the previous one already precedes this one. 2530 hw = GetWindow(hwnd, GW_HWNDPREV); 2531 if(!hw) { 2532 hw = HWND_TOP; 2533 } else { 2534 hw = GetWindow(hw, GW_HWNDPREV); 2535 if(!hw) { 2536 hw = HWND_TOP; 2537 } 2538 } 2539 2540 SetWindowPos(hwnd, hw, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 2541 } 2542 2543 2544 deprecated alias sendBackOne zIndexDown; 2545 2546 2547 // Move back one. 2548 final void sendBackOne() { 2549 if(!isHandleCreated) { 2550 if(parent) { 2551 parent.ccollection._simple_back_one(this); 2552 } 2553 return; 2554 } 2555 2556 HWND hw; 2557 2558 hw = GetWindow(hwnd, GW_HWNDNEXT); 2559 if(!hw) { 2560 hw = HWND_BOTTOM; 2561 } 2562 2563 SetWindowPos(hwnd, hw, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 2564 } 2565 2566 2567 // Note: true if no children, even if this not created. 2568 package final @property bool areChildrenCreated() { // getter 2569 return !ccollection.children.length; 2570 } 2571 2572 2573 package final void createChildren() { 2574 assert(isHandleCreated); 2575 2576 Control[] ctrls; 2577 ctrls = ccollection.children; 2578 ccollection.children = null; 2579 2580 foreach(Control ctrl; ctrls) { 2581 assert(ctrl.parent is this); 2582 assert(!(ctrl is null)); 2583 assert(ctrl); 2584 ctrl.createControl(); 2585 } 2586 } 2587 2588 2589 2590 // Force creation of the window and its child controls. 2591 final void createControl() { 2592 createHandle(); 2593 2594 // Called in WM_CREATE also. 2595 createChildren(); 2596 } 2597 2598 2599 /// Returns a new Graphics object for this control, creating the control handle if necessary. 2600 final Graphics createGraphics() { 2601 HDC hdc = GetDC(handle); // Create handle as necessary. 2602 SetTextColor(hdc, foreColor.toRgb()); 2603 return new CommonGraphics(hwnd, hdc); 2604 } 2605 2606 2607 version(DFL_NO_DRAG_DROP) {} else { 2608 private static class DropTarget: DflComObject, IDropTarget { 2609 this(Control ctrl) { 2610 this.ctrl = ctrl; 2611 } 2612 ~this() { 2613 if (dataObj) { 2614 GC.removeRoot(cast(void*)dataObj); 2615 destroy(dataObj); 2616 } 2617 } 2618 2619 2620 extern(Windows): 2621 override HRESULT QueryInterface(IID* riid, void** ppv) { 2622 if(*riid == _IID_IDropTarget) { 2623 *ppv = cast(void*)cast(IDropTarget)this; 2624 AddRef(); 2625 return S_OK; 2626 } else if(*riid == _IID_IUnknown) { 2627 *ppv = cast(void*)cast(IUnknown)this; 2628 AddRef(); 2629 return S_OK; 2630 } else { 2631 *ppv = null; 2632 return E_NOINTERFACE; 2633 } 2634 } 2635 2636 2637 HRESULT DragEnter(dfl.internal.wincom.IDataObject pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { 2638 HRESULT result; 2639 2640 try { 2641 //dataObj = new ComToDdataObject(pDataObject); 2642 ensureDataObj(pDataObject); 2643 2644 scope DragEventArgs ea = new DragEventArgs(dataObj, cast(int)grfKeyState, pt.x, pt.y, 2645 cast(DragDropEffects)*pdwEffect, DragDropEffects.NONE); // ? 2646 ctrl.onDragEnter(ea); 2647 *pdwEffect = ea.effect; 2648 2649 result = S_OK; 2650 } catch(DThrowable e) { 2651 Application.onThreadException(e); 2652 2653 result = E_UNEXPECTED; 2654 } 2655 2656 return result; 2657 } 2658 2659 2660 HRESULT DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { 2661 HRESULT result; 2662 2663 try { 2664 assert(dataObj !is null); 2665 2666 scope DragEventArgs ea = new DragEventArgs(dataObj, cast(int)grfKeyState, pt.x, pt.y, 2667 cast(DragDropEffects)*pdwEffect, DragDropEffects.NONE); // ? 2668 ctrl.onDragOver(ea); 2669 *pdwEffect = ea.effect; 2670 2671 result = S_OK; 2672 } catch(DThrowable e) { 2673 Application.onThreadException(e); 2674 2675 result = E_UNEXPECTED; 2676 } 2677 2678 return result; 2679 } 2680 2681 2682 HRESULT DragLeave() { 2683 HRESULT result; 2684 2685 try { 2686 ctrl.onDragLeave(EventArgs.empty); 2687 2688 killDataObj(); 2689 2690 result = S_OK; 2691 } catch(DThrowable e) { 2692 Application.onThreadException(e); 2693 2694 result = E_UNEXPECTED; 2695 } 2696 2697 return result; 2698 } 2699 2700 2701 HRESULT Drop(dfl.internal.wincom.IDataObject pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { 2702 HRESULT result; 2703 2704 try { 2705 //assert(dataObj !is null); 2706 ensureDataObj(pDataObject); 2707 2708 scope DragEventArgs ea = new DragEventArgs(dataObj, cast(int)grfKeyState, pt.x, pt.y, 2709 cast(DragDropEffects)*pdwEffect, DragDropEffects.NONE); // ? 2710 ctrl.onDragDrop(ea); 2711 *pdwEffect = ea.effect; 2712 2713 result = S_OK; 2714 } catch(DThrowable e) { 2715 Application.onThreadException(e); 2716 2717 result = E_UNEXPECTED; 2718 } 2719 2720 return result; 2721 } 2722 2723 2724 private: 2725 2726 Control ctrl; 2727 //dfl.data.IDataObject dataObj; 2728 ComToDdataObject dataObj; 2729 2730 2731 void ensureDataObj(dfl.internal.wincom.IDataObject pDataObject) { 2732 if(!dataObj) { 2733 dataObj = new ComToDdataObject(pDataObject); 2734 GC.addRoot(cast(void*)dataObj); 2735 } else if (!dataObj.isSameDataObject(pDataObject)) { 2736 GC.removeRoot(cast(void*)dataObj); 2737 dataObj = new ComToDdataObject(pDataObject); 2738 GC.addRoot(cast(void*)dataObj); 2739 } 2740 } 2741 2742 2743 void killDataObj() { 2744 // Can't do this because the COM object might still need to be released elsewhere. 2745 //delete dataObj; 2746 //dataObj = null; 2747 } 2748 } 2749 2750 2751 2752 protected void onDragLeave(EventArgs ea) { 2753 dragLeave(this, ea); 2754 } 2755 2756 2757 2758 protected void onDragEnter(DragEventArgs ea) { 2759 dragEnter(this, ea); 2760 } 2761 2762 2763 2764 protected void onDragOver(DragEventArgs ea) { 2765 dragOver(this, ea); 2766 } 2767 2768 2769 2770 protected void onDragDrop(DragEventArgs ea) { 2771 dragDrop(this, ea); 2772 } 2773 2774 2775 private static class DropSource: DflComObject, IDropSource { 2776 this(Control ctrl) { 2777 this.ctrl = ctrl; 2778 mbtns = Control.mouseButtons; 2779 } 2780 2781 2782 extern(Windows): 2783 override HRESULT QueryInterface(IID* riid, void** ppv) { 2784 if(*riid == _IID_IDropSource) { 2785 *ppv = cast(void*)cast(IDropSource)this; 2786 AddRef(); 2787 return S_OK; 2788 } else if(*riid == _IID_IUnknown) { 2789 *ppv = cast(void*)cast(IUnknown)this; 2790 AddRef(); 2791 return S_OK; 2792 } else { 2793 *ppv = null; 2794 return E_NOINTERFACE; 2795 } 2796 } 2797 2798 2799 HRESULT QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) { 2800 HRESULT result; 2801 2802 try { 2803 DragAction act; 2804 2805 if(fEscapePressed) { 2806 act = cast(DragAction)DragAction.CANCEL; 2807 } else { 2808 if(mbtns & MouseButtons.LEFT) { 2809 if(!(grfKeyState & MK_LBUTTON)) { 2810 act = cast(DragAction)DragAction.DROP; 2811 goto qdoit; 2812 } 2813 } else { 2814 if(grfKeyState & MK_LBUTTON) { 2815 act = cast(DragAction)DragAction.CANCEL; 2816 goto qdoit; 2817 } 2818 } 2819 if(mbtns & MouseButtons.RIGHT) { 2820 if(!(grfKeyState & MK_RBUTTON)) { 2821 act = cast(DragAction)DragAction.DROP; 2822 goto qdoit; 2823 } 2824 } else { 2825 if(grfKeyState & MK_RBUTTON) { 2826 act = cast(DragAction)DragAction.CANCEL; 2827 goto qdoit; 2828 } 2829 } 2830 if(mbtns & MouseButtons.MIDDLE) { 2831 if(!(grfKeyState & MK_MBUTTON)) { 2832 act = cast(DragAction)DragAction.DROP; 2833 goto qdoit; 2834 } 2835 } else { 2836 if(grfKeyState & MK_MBUTTON) { 2837 act = cast(DragAction)DragAction.CANCEL; 2838 goto qdoit; 2839 } 2840 } 2841 2842 act = cast(DragAction)DragAction.CONTINUE; 2843 } 2844 2845 qdoit: 2846 scope QueryContinueDragEventArgs ea = new QueryContinueDragEventArgs(cast(int)grfKeyState, 2847 fEscapePressed != FALSE, act); // ? 2848 ctrl.onQueryContinueDrag(ea); 2849 2850 result = cast(HRESULT)ea.action; 2851 } catch(DThrowable e) { 2852 Application.onThreadException(e); 2853 2854 result = E_UNEXPECTED; 2855 } 2856 2857 return result; 2858 } 2859 2860 2861 HRESULT GiveFeedback(DWORD dwEffect) { 2862 HRESULT result; 2863 2864 try { 2865 scope GiveFeedbackEventArgs ea = new GiveFeedbackEventArgs(cast(DragDropEffects)dwEffect, true); 2866 ctrl.onGiveFeedback(ea); 2867 2868 result = ea.useDefaultCursors ? DRAGDROP_S_USEDEFAULTCURSORS : S_OK; 2869 } catch(DThrowable e) { 2870 Application.onThreadException(e); 2871 2872 result = E_UNEXPECTED; 2873 } 2874 2875 return result; 2876 } 2877 2878 2879 private: 2880 Control ctrl; 2881 MouseButtons mbtns; 2882 } 2883 2884 2885 2886 protected void onQueryContinueDrag(QueryContinueDragEventArgs ea) { 2887 queryContinueDrag(this, ea); 2888 } 2889 2890 2891 2892 protected void onGiveFeedback(GiveFeedbackEventArgs ea) { 2893 giveFeedback(this, ea); 2894 } 2895 2896 2897 /// Perform a drag/drop operation. 2898 final DragDropEffects doDragDrop(dfl.data.IDataObject dataObj, DragDropEffects allowedEffects) { 2899 Object foo = cast(Object)dataObj; // Hold a reference to the Object... 2900 2901 DWORD effect; 2902 DropSource dropsrc; 2903 dfl.internal.wincom.IDataObject dropdata; 2904 2905 dropsrc = new DropSource(this); 2906 dropdata = new DtoComDataObject(dataObj); 2907 2908 // dataObj seems to be killed too early. 2909 switch(DoDragDrop(dropdata, dropsrc, cast(DWORD)allowedEffects, &effect)) { 2910 case DRAGDROP_S_DROP: // All good. 2911 break; 2912 2913 case DRAGDROP_S_CANCEL: 2914 return DragDropEffects.NONE; // ? 2915 2916 default: 2917 throw new DflException("Unable to complete drag-drop operation"); 2918 } 2919 2920 return cast(DragDropEffects)effect; 2921 } 2922 2923 /// ditto 2924 final DragDropEffects doDragDrop(Data obj, DragDropEffects allowedEffects) { 2925 dfl.data.IDataObject dd; 2926 dd = new DataObject; 2927 dd.setData(obj); 2928 return doDragDrop(dd, allowedEffects); 2929 } 2930 } 2931 2932 2933 override Dequ opEquals(Object o) { 2934 Control ctrl = cast(Control)o; 2935 if(!ctrl) { 2936 return 0; // Not equal. 2937 } 2938 return opEquals(ctrl); 2939 } 2940 2941 2942 Dequ opEquals(Control ctrl) { 2943 if(!isHandleCreated) { 2944 return super.opEquals(ctrl); 2945 } 2946 return hwnd == ctrl.hwnd; 2947 } 2948 2949 2950 override int opCmp(Object o) { 2951 Control ctrl = cast(Control)o; 2952 if(!ctrl) { 2953 return -1; 2954 } 2955 return opCmp(ctrl); 2956 } 2957 2958 2959 int opCmp(Control ctrl) { 2960 if(!isHandleCreated || hwnd != ctrl.hwnd) { 2961 return super.opCmp(ctrl); 2962 } 2963 return 0; 2964 } 2965 2966 2967 2968 final bool focus() { 2969 return SetFocus(hwnd) != HWND.init; 2970 } 2971 2972 2973 /// Returns the Control instance from one of its window handles, or null if none. 2974 // Finds controls that own more than one handle. 2975 // A combo box has several HWNDs, this would return the 2976 // correct combo box control if any of those handles are 2977 // provided. 2978 static Control fromChildHandle(HWND hwChild) { 2979 Control result; 2980 for(;;) { 2981 if(!hwChild) { 2982 return null; 2983 } 2984 2985 result = fromHandle(hwChild); 2986 if(result) { 2987 return result; 2988 } 2989 2990 hwChild = GetParent(hwChild); 2991 } 2992 } 2993 2994 2995 /// Returns the Control instance from its window handle, or null if none. 2996 static Control fromHandle(HWND hw) { 2997 return Application.lookupHwnd(hw); 2998 } 2999 3000 3001 3002 final Control getChildAtPoint(Point pt) { 3003 HWND hwChild; 3004 hwChild = ChildWindowFromPoint(hwnd, pt.point); 3005 if(!hwChild) { 3006 return null; 3007 } 3008 return fromChildHandle(hwChild); 3009 } 3010 3011 3012 3013 final void hide() { 3014 setVisibleCore(false); 3015 } 3016 3017 /// ditto 3018 final void show() { 3019 /* 3020 ShowWindow(hwnd, SW_SHOW); 3021 doShow(); 3022 */ 3023 3024 setVisibleCore(true); 3025 } 3026 3027 3028 package final void redrawEntire() { 3029 if(hwnd) { 3030 SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_DRAWFRAME | SWP_NOMOVE 3031 | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 3032 } 3033 } 3034 3035 3036 package final void recalcEntire() { 3037 if(hwnd) { 3038 SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE 3039 | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 3040 } 3041 } 3042 3043 3044 3045 final void invalidate() { 3046 if(!hwnd) { 3047 return; 3048 } 3049 3050 RedrawWindow(hwnd, null, HRGN.init, RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN); 3051 } 3052 3053 /// ditto 3054 final void invalidate(bool andChildren) { 3055 if(!hwnd) { 3056 return; 3057 } 3058 3059 RedrawWindow(hwnd, null, HRGN.init, RDW_ERASE | RDW_INVALIDATE | (andChildren ? RDW_ALLCHILDREN : RDW_NOCHILDREN)); 3060 } 3061 3062 /// ditto 3063 final void invalidate(Rect r) { 3064 if(!hwnd) { 3065 return; 3066 } 3067 3068 RECT rect; 3069 r.getRect(&rect); 3070 3071 RedrawWindow(hwnd, &rect, HRGN.init, RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN); 3072 } 3073 3074 /// ditto 3075 final void invalidate(Rect r, bool andChildren) { 3076 if(!hwnd) { 3077 return; 3078 } 3079 3080 RECT rect; 3081 r.getRect(&rect); 3082 3083 RedrawWindow(hwnd, &rect, HRGN.init, RDW_ERASE | RDW_INVALIDATE | (andChildren ? RDW_ALLCHILDREN : RDW_NOCHILDREN)); 3084 } 3085 3086 /// ditto 3087 final void invalidate(Region rgn) { 3088 if(!hwnd) { 3089 return; 3090 } 3091 3092 RedrawWindow(hwnd, null, rgn.handle, RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN); 3093 } 3094 3095 /// ditto 3096 final void invalidate(Region rgn, bool andChildren) { 3097 if(!hwnd) { 3098 return; 3099 } 3100 3101 RedrawWindow(hwnd, null, rgn.handle, RDW_ERASE | RDW_INVALIDATE | (andChildren ? RDW_ALLCHILDREN : RDW_NOCHILDREN)); 3102 } 3103 3104 3105 3106 // Redraws the entire control, including nonclient area. 3107 final void redraw() { 3108 if(!hwnd) { 3109 return; 3110 } 3111 3112 RedrawWindow(hwnd, null, HRGN.init, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); 3113 } 3114 3115 3116 /// Returns true if the window does not belong to the current thread. 3117 @property bool invokeRequired() { // getter 3118 DWORD tid = GetWindowThreadProcessId(hwnd, null); 3119 return tid != GetCurrentThreadId(); 3120 } 3121 3122 3123 private static void badInvokeHandle() { 3124 //throw new DflException("Must invoke after creating handle"); 3125 throw new DflException("Must invoke with created handle"); 3126 } 3127 3128 3129 /// Synchronously calls a delegate in this Control's thread. This function is thread safe and exceptions are propagated to the caller. 3130 // Exceptions are propagated back to the caller of invoke(). 3131 final Object invoke(Object delegate(Object[]) dg, Object[] args ...) { 3132 if(!hwnd) { 3133 badInvokeHandle(); 3134 } 3135 3136 InvokeData inv; 3137 inv.dg = dg; 3138 inv.args = args; 3139 3140 if(LRESULT_DFL_INVOKE != SendMessageA(hwnd, wmDfl, WPARAM_DFL_INVOKE, cast(LRESULT)&inv)) { 3141 throw new DflException("Invoke failure"); 3142 } 3143 if(inv.exception) { 3144 throw inv.exception; 3145 } 3146 3147 return inv.result; 3148 } 3149 3150 /// ditto 3151 final void invoke(void delegate() dg) { 3152 if(!hwnd) { 3153 badInvokeHandle(); 3154 } 3155 3156 InvokeSimpleData inv; 3157 inv.dg = dg; 3158 3159 if(LRESULT_DFL_INVOKE != SendMessageA(hwnd, wmDfl, WPARAM_DFL_INVOKE_SIMPLE, cast(LRESULT)&inv)) { 3160 throw new DflException("Invoke failure"); 3161 } 3162 if(inv.exception) { 3163 throw inv.exception; 3164 } 3165 } 3166 3167 3168 /** Asynchronously calls a function after the window message queue processes its current messages. 3169 It is generally not safe to pass references to the delayed function. 3170 Exceptions are not propagated to the caller. 3171 **/ 3172 // Extra. 3173 // Exceptions will be passed to Application.onThreadException() and 3174 // trigger the threadException event or the default exception dialog. 3175 final void delayInvoke(void function() fn) { 3176 if(!hwnd) { 3177 badInvokeHandle(); 3178 } 3179 3180 assert(!invokeRequired); 3181 3182 static assert(fn.sizeof <= LPARAM.sizeof); 3183 PostMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE, cast(LPARAM)fn); 3184 } 3185 3186 /// ditto 3187 // Extra. 3188 // Exceptions will be passed to Application.onThreadException() and 3189 // trigger the threadException event or the default exception dialog. 3190 // Copy of params are passed to fn, they do not exist after it returns. 3191 // It is unsafe to pass references to a delayed function. 3192 final void delayInvoke(void function(Control, size_t[]) fn, size_t[] params ...) { 3193 if(!hwnd) { 3194 badInvokeHandle(); 3195 } 3196 3197 assert(!invokeRequired); 3198 3199 static assert((DflInvokeParam*).sizeof <= LPARAM.sizeof); 3200 3201 DflInvokeParam* p; 3202 p = cast(DflInvokeParam*)dfl.internal.clib.malloc( 3203 (DflInvokeParam.sizeof - size_t.sizeof) 3204 + params.length * size_t.sizeof); 3205 if(!p) { 3206 throw new OomException(); 3207 } 3208 3209 p.fp = fn; 3210 p.nparams = params.length; 3211 p.params.ptr[0 .. params.length] = params[]; 3212 3213 PostMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE_PARAMS, cast(LPARAM)p); 3214 } 3215 3216 deprecated alias delayInvoke beginInvoke; 3217 3218 3219 3220 static bool isMnemonic(dchar charCode, Dstring text) { 3221 uint ui; 3222 for(ui = 0; ui != text.length; ui++) { 3223 if('&' == text[ui]) { 3224 if(++ui == text.length) { 3225 break; 3226 } 3227 if('&' == text[ui]) { // && means literal & so skip it. 3228 continue; 3229 } 3230 dchar dch; 3231 dch = utf8stringGetUtf32char(text, ui); 3232 return utf32charToLower(charCode) == utf32charToLower(dch); 3233 } 3234 } 3235 return false; 3236 } 3237 3238 3239 /// Converts a screen Point to a client Point. 3240 final Point pointToClient(Point pt) { 3241 ScreenToClient(hwnd, &pt.point); 3242 return pt; 3243 } 3244 3245 3246 /// Converts a client Point to a screen Point. 3247 final Point pointToScreen(Point pt) { 3248 ClientToScreen(hwnd, &pt.point); 3249 return pt; 3250 } 3251 3252 3253 /// Converts a screen Rectangle to a client Rectangle. 3254 final Rect rectangleToClient(Rect r) { 3255 RECT rect; 3256 r.getRect(&rect); 3257 3258 MapWindowPoints(HWND.init, hwnd, cast(POINT*)&rect, 2); 3259 return Rect(&rect); 3260 } 3261 3262 3263 /// Converts a client Rectangle to a screen Rectangle. 3264 final Rect rectangleToScreen(Rect r) { 3265 RECT rect; 3266 r.getRect(&rect); 3267 3268 MapWindowPoints(hwnd, HWND.init, cast(POINT*)&rect, 2); 3269 return Rect(&rect); 3270 } 3271 3272 3273 3274 // Return true if processed. 3275 bool preProcessMessage(ref Message msg) { 3276 return false; 3277 } 3278 3279 3280 3281 final Size getAutoScaleSize(Font f) { 3282 Size result; 3283 Graphics g; 3284 g = createGraphics(); 3285 result = g.getScaleSize(f); 3286 g.dispose(); 3287 return result; 3288 } 3289 3290 /// ditto 3291 final Size getAutoScaleSize() { 3292 return getAutoScaleSize(font); 3293 } 3294 3295 3296 3297 void refresh() { 3298 invalidate(true); 3299 } 3300 3301 3302 3303 void resetBackColor() { 3304 //backColor = defaultBackColor; 3305 backColor = Color.empty; 3306 } 3307 3308 3309 3310 void resetCursor() { 3311 //cursor = new Cursor(LoadCursorA(HINSTANCE.init, IDC_ARROW), false); 3312 cursor = null; 3313 } 3314 3315 3316 3317 void resetFont() { 3318 //font = defaultFont; 3319 font = null; 3320 } 3321 3322 3323 3324 void resetForeColor() { 3325 //foreColor = defaultForeColor; 3326 foreColor = Color.empty; 3327 } 3328 3329 3330 3331 void resetRightToLeft() { 3332 //rightToLeft = false; 3333 rightToLeft = RightToLeft.INHERIT; 3334 } 3335 3336 3337 3338 void resetText() { 3339 //text = ""; 3340 text = null; 3341 } 3342 3343 3344 3345 // Just allow layout recalc, but don't do it right now. 3346 final void resumeLayout() { 3347 //_allowLayout = true; 3348 if(_disallowLayout) { 3349 _disallowLayout--; 3350 } 3351 } 3352 3353 /// ditto 3354 // Allow layout recalc, only do it now if -byes- is true. 3355 final void resumeLayout(bool byes) { 3356 if(_disallowLayout) { 3357 _disallowLayout--; 3358 } 3359 3360 // This is correct. 3361 if(byes) { 3362 if(!_disallowLayout) { 3363 alayout(null); 3364 } 3365 } 3366 } 3367 3368 3369 3370 final void suspendLayout() { 3371 //_allowLayout = false; 3372 _disallowLayout++; 3373 } 3374 3375 3376 final void performLayout(Control affectedControl) { 3377 alayout(affectedControl, false); 3378 } 3379 3380 3381 final void performLayout() { 3382 return performLayout(this); 3383 } 3384 3385 3386 /+ 3387 // TODO: implement. 3388 3389 // Scale both height and width to -ratio-. 3390 final void scale(float ratio) { 3391 scaleCore(ratio, ratio); 3392 } 3393 3394 3395 // Scale -width- and -height- ratios. 3396 final void scale(float width, float height) { 3397 scaleCore(width, height); 3398 } 3399 3400 3401 // Also scales child controls recursively. 3402 protected void scaleCore(float width, float height) { 3403 suspendLayout(); 3404 3405 // ... 3406 3407 resumeLayout(); 3408 } 3409 +/ 3410 3411 3412 private static bool _eachild(HWND hw, bool delegate(HWND hw) callback, ref size_t xiter, bool nested) { 3413 for(; hw; hw = GetWindow(hw, GW_HWNDNEXT)) { 3414 if(!xiter) { 3415 return false; 3416 } 3417 xiter--; 3418 3419 LONG st = GetWindowLongA(hw, GWL_STYLE); 3420 if(!(st & WS_VISIBLE)) { 3421 continue; 3422 } 3423 if(st & WS_DISABLED) { 3424 continue; 3425 } 3426 3427 if(!callback(hw)) { 3428 return false; 3429 } 3430 3431 if(nested) { 3432 //LONG exst = GetWindowLongA(hw, GWL_EXSTYLE); 3433 //if(exst & WS_EX_CONTROLPARENT) // It's no longer added. 3434 { 3435 HWND hwc = GetWindow(hw, GW_CHILD); 3436 if(hwc) { 3437 //if(!_eachild(hwc, callback, xiter, nested)) 3438 if(!_eachild(hwc, callback, xiter, true)) { 3439 return false; 3440 } 3441 } 3442 } 3443 } 3444 } 3445 return true; 3446 } 3447 3448 package static void eachGoodChildHandle(HWND hwparent, bool delegate(HWND hw) callback, bool nested = true) { 3449 HWND hw = GetWindow(hwparent, GW_CHILD); 3450 size_t xiter = 2000; 3451 _eachild(hw, callback, xiter, nested); 3452 } 3453 3454 3455 private static bool _isHwndControlSel(HWND hw) { 3456 Control c = Control.fromHandle(hw); 3457 return c && c.getStyle(ControlStyles.SELECTABLE); 3458 } 3459 3460 3461 package static void _dlgselnext(Form dlg, HWND hwcursel, bool forward, 3462 bool tabStopOnly = true, bool selectableOnly = false, 3463 bool nested = true, bool wrap = true, 3464 HWND hwchildrenof = null) { 3465 //assert(cast(Form)Control.fromHandle(hwdlg) !is null); 3466 3467 if(!hwchildrenof) { 3468 hwchildrenof = dlg.handle; 3469 } 3470 if(forward) { 3471 bool foundthis = false, tdone = false; 3472 HWND hwfirst; 3473 eachGoodChildHandle(hwchildrenof, 3474 (HWND hw) { 3475 assert(!tdone); 3476 if(hw == hwcursel) { 3477 foundthis = true; 3478 } else { 3479 if(!tabStopOnly || (GetWindowLongA(hw, GWL_STYLE) & WS_TABSTOP)) { 3480 if(!selectableOnly || _isHwndControlSel(hw)) { 3481 if(foundthis) { 3482 //DefDlgProcA(dlg.handle, WM_NEXTDLGCTL, cast(WPARAM)hw, MAKELPARAM(true, 0)); 3483 dlg._selectChild(hw); 3484 tdone = true; 3485 return false; // Break. 3486 } else { 3487 if(HWND.init == hwfirst) { 3488 hwfirst = hw; 3489 } 3490 } 3491 } 3492 } 3493 } 3494 return true; // Continue. 3495 }, nested); 3496 if(!tdone && HWND.init != hwfirst) { 3497 // If it falls through without finding hwcursel, let it select the first one, even if not wrapping. 3498 if(wrap || !foundthis) { 3499 //DefDlgProcA(dlg.handle, WM_NEXTDLGCTL, cast(WPARAM)hwfirst, MAKELPARAM(true, 0)); 3500 dlg._selectChild(hwfirst); 3501 } 3502 } 3503 } else { 3504 HWND hwprev; 3505 eachGoodChildHandle(hwchildrenof, 3506 (HWND hw) { 3507 if(hw == hwcursel) { 3508 if(HWND.init != hwprev) { // Otherwise, keep looping and get last one. 3509 return false; // Break. 3510 } 3511 if(!wrap) { // No wrapping, so don't get last one. 3512 assert(HWND.init == hwprev); 3513 return false; // Break. 3514 } 3515 } 3516 if(!tabStopOnly || (GetWindowLongA(hw, GWL_STYLE) & WS_TABSTOP)) { 3517 if(!selectableOnly || _isHwndControlSel(hw)) { 3518 hwprev = hw; 3519 } 3520 } 3521 return true; // Continue. 3522 }, nested); 3523 // If it falls through without finding hwcursel, let it select the last one, even if not wrapping. 3524 if(HWND.init != hwprev) 3525 //DefDlgProcA(dlg.handle, WM_NEXTDLGCTL, cast(WPARAM)hwprev, MAKELPARAM(true, 0)); 3526 { 3527 dlg._selectChild(hwprev); 3528 } 3529 } 3530 } 3531 3532 3533 package final void _selectNextControl(Form ctrltoplevel, 3534 Control ctrl, bool forward, bool tabStopOnly, bool nested, bool wrap) { 3535 if(!created) { 3536 return; 3537 } 3538 3539 assert(ctrltoplevel !is null); 3540 assert(ctrltoplevel.isHandleCreated); 3541 3542 _dlgselnext(ctrltoplevel, 3543 (ctrl && ctrl.isHandleCreated) ? ctrl.handle : null, 3544 forward, tabStopOnly, !tabStopOnly, nested, wrap, 3545 this.handle); 3546 } 3547 3548 3549 package final void _selectThisControl() { 3550 3551 } 3552 3553 3554 // Only considers child controls of this control. 3555 final void selectNextControl(Control ctrl, bool forward, bool tabStopOnly, bool nested, bool wrap) { 3556 if(!created) { 3557 return; 3558 } 3559 3560 auto ctrltoplevel = findForm(); 3561 if(ctrltoplevel) { 3562 return _selectNextControl(ctrltoplevel, ctrl, forward, tabStopOnly, nested, wrap); 3563 } 3564 } 3565 3566 3567 3568 final void select() { 3569 select(false, false); 3570 } 3571 3572 /// ditto 3573 // If -directed- is true, -forward- is used; otherwise, selects this control. 3574 // If -forward- is true, the next control in the tab order is selected, 3575 // otherwise the previous control in the tab order is selected. 3576 // Controls without style ControlStyles.SELECTABLE are skipped. 3577 void select(bool directed, bool forward) { 3578 if(!created) { 3579 return; 3580 } 3581 3582 auto ctrltoplevel = findForm(); 3583 if(ctrltoplevel && ctrltoplevel !is this) { 3584 /+ // Old... 3585 // Even if directed, ensure THIS one is selected first. 3586 if(!directed || hwnd != GetFocus()) { 3587 DefDlgProcA(ctrltoplevel.handle, WM_NEXTDLGCTL, cast(WPARAM)hwnd, MAKELPARAM(true, 0)); 3588 } 3589 3590 if(directed) { 3591 DefDlgProcA(ctrltoplevel.handle, WM_NEXTDLGCTL, !forward, MAKELPARAM(false, 0)); 3592 } 3593 +/ 3594 3595 if(directed) { 3596 _dlgselnext(ctrltoplevel, this.handle, forward); 3597 } else { 3598 ctrltoplevel._selectChild(this); 3599 } 3600 } else { 3601 focus(); // This must be a form so just focus it ? 3602 } 3603 } 3604 3605 3606 3607 final void setBounds(int x, int y, int width, int height) { 3608 setBoundsCore(x, y, width, height, BoundsSpecified.ALL); 3609 } 3610 3611 /// ditto 3612 final void setBounds(int x, int y, int width, int height, BoundsSpecified specified) { 3613 setBoundsCore(x, y, width, height, specified); 3614 } 3615 3616 3617 override Dstring toString() { 3618 return text; 3619 } 3620 3621 3622 3623 final void update() { 3624 if(!created) { 3625 return; 3626 } 3627 3628 UpdateWindow(hwnd); 3629 } 3630 3631 3632 3633 // If mouseEnter, mouseHover and mouseLeave events are supported. 3634 // Returns true on Windows 95 with IE 5.5, Windows 98+ or Windows NT 4.0+. 3635 static @property bool supportsMouseTracking() { // getter 3636 return trackMouseEvent != null; 3637 } 3638 3639 3640 package final Rect _fetchBounds() { 3641 RECT r; 3642 GetWindowRect(hwnd, &r); 3643 HWND hwParent = GetParent(hwnd); 3644 if(hwParent && (_style() & WS_CHILD)) { 3645 MapWindowPoints(HWND.init, hwParent, cast(POINT*)&r, 2); 3646 } 3647 return Rect(&r); 3648 } 3649 3650 3651 package final Size _fetchClientSize() { 3652 RECT r; 3653 GetClientRect(hwnd, &r); 3654 return Size(r.right, r.bottom); 3655 } 3656 3657 3658 deprecated protected void onInvalidated(InvalidateEventArgs iea) { 3659 //invalidated(this, iea); 3660 } 3661 3662 3663 3664 protected void onPaint(PaintEventArgs pea) { 3665 paint(this, pea); 3666 } 3667 3668 3669 protected void onMoving(MovingEventArgs cea) { 3670 moving(this, cea); 3671 } 3672 3673 3674 protected void onMove(EventArgs ea) { 3675 move(this, ea); 3676 } 3677 3678 3679 /+ 3680 protected void onLocationChanged(EventArgs ea) { 3681 locationChanged(this, ea); 3682 } 3683 +/ 3684 alias onMove onLocationChanged; 3685 3686 3687 protected void onSizing(SizingEventArgs cea) { 3688 sizing(this, cea); 3689 } 3690 3691 3692 protected void onResize(EventArgs ea) { 3693 resize(this, ea); 3694 } 3695 3696 3697 /+ 3698 protected void onSizeChanged(EventArgs ea) { 3699 sizeChanged(this, ea); 3700 } 3701 +/ 3702 alias onResize onSizeChanged; 3703 3704 3705 /+ 3706 // /// 3707 // Allows comparing before and after dimensions, and also allows modifying the new dimensions. 3708 deprecated protected void onBeforeResize(BeforeResizeEventArgs ea) { 3709 } 3710 +/ 3711 3712 3713 3714 protected void onMouseEnter(MouseEventArgs mea) { 3715 mouseEnter(this, mea); 3716 } 3717 3718 3719 3720 protected void onMouseMove(MouseEventArgs mea) { 3721 mouseMove(this, mea); 3722 } 3723 3724 3725 3726 protected void onKeyDown(KeyEventArgs kea) { 3727 keyDown(this, kea); 3728 } 3729 3730 3731 3732 protected void onKeyPress(KeyPressEventArgs kea) { 3733 keyPress(this, kea); 3734 } 3735 3736 3737 3738 protected void onKeyUp(KeyEventArgs kea) { 3739 keyUp(this, kea); 3740 } 3741 3742 3743 3744 protected void onMouseWheel(MouseEventArgs mea) { 3745 mouseWheel(this, mea); 3746 } 3747 3748 3749 3750 protected void onMouseHover(MouseEventArgs mea) { 3751 mouseHover(this, mea); 3752 } 3753 3754 3755 3756 protected void onMouseLeave(MouseEventArgs mea) { 3757 mouseLeave(this, mea); 3758 } 3759 3760 3761 3762 protected void onMouseDown(MouseEventArgs mea) { 3763 mouseDown(this, mea); 3764 } 3765 3766 3767 3768 protected void onMouseUp(MouseEventArgs mea) { 3769 mouseUp(this, mea); 3770 } 3771 3772 3773 3774 protected void onClick(EventArgs ea) { 3775 click(this, ea); 3776 } 3777 3778 3779 3780 protected void onDoubleClick(EventArgs ea) { 3781 doubleClick(this, ea); 3782 } 3783 3784 3785 3786 protected void onGotFocus(EventArgs ea) { 3787 gotFocus(this, ea); 3788 } 3789 3790 3791 /+ 3792 deprecated protected void onEnter(EventArgs ea) { 3793 //enter(this, ea); 3794 } 3795 3796 3797 deprecated protected void onLeave(EventArgs ea) { 3798 //leave(this, ea); 3799 } 3800 3801 3802 deprecated protected void onValidated(EventArgs ea) { 3803 //validated(this, ea); 3804 } 3805 3806 3807 deprecated protected void onValidating(CancelEventArgs cea) { 3808 /+ 3809 foreach(CancelEventHandler.Handler handler; validating.handlers()) { 3810 handler(this, cea); 3811 3812 if(cea.cancel) { 3813 return; // Not validated. 3814 } 3815 } 3816 3817 onValidated(EventArgs.empty); 3818 +/ 3819 } 3820 +/ 3821 3822 3823 3824 protected void onLostFocus(EventArgs ea) { 3825 lostFocus(this, ea); 3826 } 3827 3828 3829 3830 protected void onEnabledChanged(EventArgs ea) { 3831 enabledChanged(this, ea); 3832 } 3833 3834 3835 3836 protected void onTextChanged(EventArgs ea) { 3837 textChanged(this, ea); 3838 } 3839 3840 3841 private void _propagateFontAmbience() { 3842 Font fon; 3843 fon = font; 3844 3845 3846 void pa(Control pc) { 3847 foreach(Control ctrl; pc.ccollection) { 3848 if(!ctrl.wfont) { // If default. 3849 if(fon is ctrl.font) { // If same default. 3850 if(ctrl.isHandleCreated) { 3851 SendMessageA(ctrl.hwnd, WM_SETFONT, cast(WPARAM)fon.handle, MAKELPARAM(true, 0)); 3852 } 3853 ctrl.onFontChanged(EventArgs.empty); 3854 3855 pa(ctrl); // Recursive. 3856 } 3857 } 3858 } 3859 } 3860 3861 3862 pa(this); 3863 } 3864 3865 3866 3867 protected void onFontChanged(EventArgs ea) { 3868 debug(EVENT_PRINT) { 3869 cprintf("{ Event: onFontChanged - Control %.*s }\n", name); 3870 } 3871 3872 fontChanged(this, ea); 3873 } 3874 3875 3876 3877 protected void onRightToLeftChanged(EventArgs ea) { 3878 debug(EVENT_PRINT) { 3879 cprintf("{ Event: onRightToLeftChanged - Control %.*s }\n", name); 3880 } 3881 3882 rightToLeftChanged(this, ea); 3883 } 3884 3885 3886 3887 protected void onVisibleChanged(EventArgs ea) { 3888 if(wparent) { 3889 wparent.vchanged(); 3890 suspendLayout(); // Note: exception could cause failure to restore. 3891 wparent.alayout(this); 3892 resumeLayout(false); 3893 } 3894 if(visible) { 3895 alayout(this); 3896 } 3897 3898 visibleChanged(this, ea); 3899 3900 if(visible) { 3901 // If no focus or the focused control is hidden, try to select something... 3902 HWND hwfocus = GetFocus(); 3903 if(!hwfocus 3904 || (hwfocus == hwnd && !getStyle(ControlStyles.SELECTABLE)) 3905 || !IsWindowVisible(hwfocus)) { 3906 selectNextControl(null, true, true, true, false); 3907 } 3908 } 3909 } 3910 3911 3912 3913 protected void onHelpRequested(HelpEventArgs hea) { 3914 debug(EVENT_PRINT) { 3915 cprintf("{ Event: onHelpRequested - Control %.*s }\n", name); 3916 } 3917 3918 helpRequested(this, hea); 3919 } 3920 3921 3922 3923 protected void onSystemColorsChanged(EventArgs ea) { 3924 debug(EVENT_PRINT) { 3925 cprintf("{ Event: onSystemColorsChanged - Control %.*s }\n", name); 3926 } 3927 3928 systemColorsChanged(this, ea); 3929 } 3930 3931 3932 3933 protected void onHandleCreated(EventArgs ea) { 3934 if(!(cbits & CBits.VSTYLE)) { 3935 _disableVisualStyle(); 3936 } 3937 3938 Font fon; 3939 fon = font; 3940 if(fon) { 3941 SendMessageA(hwnd, WM_SETFONT, cast(WPARAM)fon.handle, 0); 3942 } 3943 3944 if(wregion) { 3945 // Need to make a copy of the region. 3946 SetWindowRgn(hwnd, dupHrgn(wregion.handle), true); 3947 } 3948 3949 version(DFL_NO_DRAG_DROP) {} else { 3950 if(droptarget) { 3951 if(S_OK != RegisterDragDrop(hwnd, droptarget)) { 3952 droptarget = null; 3953 throw new DflException("Unable to register drag-drop"); 3954 } 3955 } 3956 } 3957 3958 debug { 3959 _handlecreated = true; 3960 } 3961 } 3962 3963 3964 3965 protected void onHandleDestroyed(EventArgs ea) { 3966 handleDestroyed(this, ea); 3967 } 3968 3969 3970 3971 protected void onPaintBackground(PaintEventArgs pea) { 3972 RECT rect; 3973 pea.clipRectangle.getRect(&rect); 3974 FillRect(pea.graphics.handle, &rect, hbrBg); 3975 } 3976 3977 3978 private static MouseButtons wparamMouseButtons(WPARAM wparam) { 3979 MouseButtons result; 3980 if(wparam & MK_LBUTTON) { 3981 result |= MouseButtons.LEFT; 3982 } 3983 if(wparam & MK_RBUTTON) { 3984 result |= MouseButtons.RIGHT; 3985 } 3986 if(wparam & MK_MBUTTON) { 3987 result |= MouseButtons.MIDDLE; 3988 } 3989 return result; 3990 } 3991 3992 3993 package final void prepareDc(HDC hdc) { 3994 //SetBkMode(hdc, TRANSPARENT); // ? 3995 //SetBkMode(hdc, OPAQUE); // ? 3996 SetBkColor(hdc, backColor.toRgb()); 3997 SetTextColor(hdc, foreColor.toRgb()); 3998 } 3999 4000 4001 // Message copy so it cannot be modified. 4002 deprecated protected void onNotifyMessage(Message msg) { 4003 } 4004 4005 4006 /+ 4007 /+package+/ LRESULT customMsg(ref CustomMsg msg) { // package 4008 return 0; 4009 } 4010 +/ 4011 4012 4013 4014 protected void onReflectedMessage(ref Message m) { 4015 switch(m.msg) { 4016 case WM_CTLCOLORSTATIC: 4017 case WM_CTLCOLORLISTBOX: 4018 case WM_CTLCOLOREDIT: 4019 case WM_CTLCOLORSCROLLBAR: 4020 case WM_CTLCOLORBTN: 4021 //case WM_CTLCOLORDLG: // ? 4022 //case 0x0019: //WM_CTLCOLOR; obsolete. 4023 prepareDc(cast(HDC)m.wParam); 4024 //assert(GetObjectA(hbrBg, 0, null)); 4025 m.result = cast(LRESULT)hbrBg; 4026 break; 4027 4028 default: 4029 } 4030 } 4031 4032 4033 // ChildWindowFromPoint includes both hidden and disabled. 4034 // This includes disabled windows, but not hidden. 4035 // Here is a point in this control, see if it's over a visible child. 4036 // Returns null if not even in this control's client. 4037 final HWND pointOverVisibleChild(Point pt) { // package 4038 if(pt.x < 0 || pt.y < 0) { 4039 return HWND.init; 4040 } 4041 if(pt.x > wclientsz.width || pt.y > wclientsz.height) { 4042 return HWND.init; 4043 } 4044 4045 // Note: doesn't include non-DFL windows... TO-DO: fix. 4046 foreach(Control ctrl; ccollection) { 4047 if(!ctrl.visible) { 4048 continue; 4049 } 4050 if(!ctrl.isHandleCreated) { // Shouldn't.. 4051 continue; 4052 } 4053 if(ctrl.bounds.contains(pt)) { 4054 return ctrl.hwnd; 4055 } 4056 } 4057 4058 return hwnd; // Just over this control. 4059 } 4060 4061 4062 version(_DFL_WINDOWS_HUNG_WORKAROUND) { 4063 DWORD ldlgcode = 0; 4064 } 4065 4066 4067 4068 protected void wndProc(ref Message msg) { 4069 //if(ctrlStyle & ControlStyles.ENABLE_NOTIFY_MESSAGE) 4070 // onNotifyMessage(msg); 4071 4072 switch(msg.msg) { 4073 case WM_PAINT: { 4074 // This can't be done in BeginPaint() becuase part might get 4075 // validated during this event ? 4076 //RECT uprect; 4077 //GetUpdateRect(hwnd, &uprect, true); 4078 //onInvalidated(new InvalidateEventArgs(Rect(&uprect))); 4079 4080 PAINTSTRUCT ps; 4081 BeginPaint(msg.hWnd, &ps); 4082 try { 4083 //onInvalidated(new InvalidateEventArgs(Rect(&uprect))); 4084 4085 scope PaintEventArgs pea = new PaintEventArgs(new Graphics(ps.hdc, false), Rect(&ps.rcPaint)); 4086 4087 // Probably because ControlStyles.ALL_PAINTING_IN_WM_PAINT. 4088 if(ps.fErase) { 4089 prepareDc(ps.hdc); 4090 onPaintBackground(pea); 4091 } 4092 4093 prepareDc(ps.hdc); 4094 onPaint(pea); 4095 } 4096 finally { 4097 EndPaint(hwnd, &ps); 4098 } 4099 } 4100 return; 4101 4102 case WM_ERASEBKGND: 4103 if(ctrlStyle & ControlStyles.OPAQUE) { 4104 msg.result = 1; // Erased. 4105 } else if(!(ctrlStyle & ControlStyles.ALL_PAINTING_IN_WM_PAINT)) { 4106 RECT uprect; 4107 /+ 4108 GetUpdateRect(hwnd, &uprect, false); 4109 +/ 4110 uprect.left = 0; 4111 uprect.top = 0; 4112 uprect.right = clientSize.width; 4113 uprect.bottom = clientSize.height; 4114 4115 prepareDc(cast(HDC)msg.wParam); 4116 scope PaintEventArgs pea = new PaintEventArgs(new Graphics(cast(HDC)msg.wParam, false), Rect(&uprect)); 4117 onPaintBackground(pea); 4118 msg.result = 1; // Erased. 4119 } 4120 return; 4121 4122 case WM_PRINTCLIENT: 4123 prepareDc(cast(HDC)msg.wParam); 4124 scope PaintEventArgs pea = new PaintEventArgs(new Graphics(cast(HDC)msg.wParam, false), Rect(Point(0, 0), wclientsz)); 4125 onPaint(pea); 4126 return; 4127 4128 case WM_CTLCOLORSTATIC: 4129 case WM_CTLCOLORLISTBOX: 4130 case WM_CTLCOLOREDIT: 4131 case WM_CTLCOLORSCROLLBAR: 4132 case WM_CTLCOLORBTN: 4133 //case WM_CTLCOLORDLG: // ? 4134 //case 0x0019: //WM_CTLCOLOR; obsolete. 4135 { 4136 Control ctrl = fromChildHandle(cast(HWND)msg.lParam); 4137 if(ctrl) { 4138 //ctrl.prepareDc(cast(HDC)msg.wParam); 4139 //msg.result = cast(LRESULT)ctrl.hbrBg; 4140 ctrl.onReflectedMessage(msg); 4141 return; 4142 } 4143 } 4144 break; 4145 4146 case WM_WINDOWPOSCHANGED: { 4147 WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam; 4148 bool needLayout = false; 4149 4150 //if(!wp.hwndInsertAfter) 4151 // wp.flags |= SWP_NOZORDER; // ? 4152 4153 bool didvis = false; 4154 if(wp.flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW)) { 4155 needLayout = true; // Only if not didvis / if not recreating. 4156 if(!recreatingHandle) { // Note: suppresses onVisibleChanged 4157 if(wp.flags & SWP_HIDEWINDOW) { // Hiding. 4158 _clicking = false; 4159 } 4160 onVisibleChanged(EventArgs.empty); 4161 didvis = true; 4162 //break; // Showing min/max includes other flags. 4163 } 4164 } 4165 4166 if(!(wp.flags & SWP_NOZORDER) /+ || (wp.flags & SWP_SHOWWINDOW) +/) { 4167 if(wparent) { 4168 wparent.vchanged(); 4169 } 4170 } 4171 4172 if(!(wp.flags & SWP_NOMOVE)) { 4173 onMove(EventArgs.empty); 4174 } 4175 4176 if(!(wp.flags & SWP_NOSIZE)) { 4177 if(szdraw) { 4178 invalidate(true); 4179 } 4180 4181 onResize(EventArgs.empty); 4182 4183 needLayout = true; 4184 } 4185 4186 // Frame change results in a new client size. 4187 if(wp.flags & SWP_FRAMECHANGED) { 4188 if(szdraw) { 4189 invalidate(true); 4190 } 4191 4192 needLayout = true; 4193 } 4194 4195 if(!didvis) { // onVisibleChanged already triggers layout. 4196 if(/+ (wp.flags & SWP_SHOWWINDOW) || +/ !(wp.flags & SWP_NOSIZE) || 4197 !(wp.flags & SWP_NOZORDER)) { // z-order determines what is positioned first. 4198 suspendLayout(); // Note: exception could cause failure to restore. 4199 if(wparent) { 4200 wparent.alayout(this); 4201 } 4202 resumeLayout(false); 4203 needLayout = true; 4204 } 4205 4206 if(needLayout) { 4207 alayout(this); 4208 } 4209 } 4210 } 4211 break; 4212 4213 case WM_WINDOWPOSCHANGING: { 4214 WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam; 4215 4216 if (!(wp.flags & SWP_NOMOVE) 4217 && (location.x != wp.x || location.y != wp.y)) { 4218 scope e = new MovingEventArgs(Point(wp.x, wp.y)); 4219 onMoving(e); 4220 wp.x = e.x; 4221 wp.y = e.y; 4222 } 4223 if (!(wp.flags & SWP_NOSIZE) 4224 && (width != wp.cx || height != wp.cy)) { 4225 scope e = new SizingEventArgs(Size(wp.cx, wp.cy)); 4226 onSizing(e); 4227 wp.cx = e.width; 4228 wp.cy = e.height; 4229 } 4230 } 4231 break; 4232 4233 case WM_MOUSEMOVE: 4234 if(_clicking) { 4235 if(!(msg.wParam & MK_LBUTTON)) { 4236 _clicking = false; 4237 } 4238 } 4239 4240 if(trackMouseEvent) { // Requires Windows 95 with IE 5.5, 98 or NT4. 4241 if(!menter) { 4242 menter = true; 4243 4244 POINT pt; 4245 GetCursorPos(&pt); 4246 MapWindowPoints(HWND.init, hwnd, &pt, 1); 4247 scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, pt.x, pt.y, 0); 4248 onMouseEnter(mea); 4249 4250 TRACKMOUSEEVENT tme; 4251 tme.cbSize = TRACKMOUSEEVENT.sizeof; 4252 tme.dwFlags = TME_HOVER | TME_LEAVE; 4253 tme.hwndTrack = msg.hWnd; 4254 tme.dwHoverTime = HOVER_DEFAULT; 4255 trackMouseEvent(&tme); 4256 } 4257 } 4258 4259 onMouseMove(new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0)); 4260 break; 4261 4262 case WM_SETCURSOR: 4263 // Just update it so that Control.defWndProc() can set it correctly. 4264 if(cast(HWND)msg.wParam == hwnd) { 4265 Cursor cur; 4266 cur = cursor; 4267 if(cur) { 4268 if(cast(HCURSOR)GetClassLongA(hwnd, GCL_HCURSOR) != cur.handle) { 4269 SetClassLongA(hwnd, GCL_HCURSOR, cast(LONG)cur.handle); 4270 } 4271 } else { 4272 if(cast(HCURSOR)GetClassLongA(hwnd, GCL_HCURSOR) != HCURSOR.init) { 4273 SetClassLongA(hwnd, GCL_HCURSOR, cast(LONG)cast(HCURSOR)null); 4274 } 4275 } 4276 Control.defWndProc(msg); 4277 return; 4278 } 4279 break; 4280 4281 /+ 4282 case WM_NEXTDLGCTL: 4283 if(!LOWORD(msg.lParam)) { 4284 select(true, msg.wParam != 0); 4285 return; 4286 } 4287 break; 4288 +/ 4289 4290 case WM_KEYDOWN: 4291 case WM_KEYUP: 4292 case WM_CHAR: 4293 case WM_SYSKEYDOWN: 4294 case WM_SYSKEYUP: 4295 case WM_SYSCHAR: 4296 //case WM_IMECHAR: 4297 /+ 4298 if(processKeyEventArgs(msg)) { 4299 // The key was processed. 4300 msg.result = 0; 4301 return; 4302 } 4303 msg.result = 1; // The key was not processed. 4304 break; 4305 +/ 4306 msg.result = !processKeyEventArgs(msg); 4307 return; 4308 4309 case WM_MOUSEWHEEL: { // Requires Windows 98 or NT4. 4310 scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(LOWORD(msg.wParam)), 0, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), cast(short)HIWORD(msg.wParam)); 4311 onMouseWheel(mea); 4312 } 4313 break; 4314 4315 case WM_MOUSEHOVER: { // Requires Windows 95 with IE 5.5, 98 or NT4. 4316 scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4317 onMouseHover(mea); 4318 } 4319 break; 4320 4321 case WM_MOUSELEAVE: { // Requires Windows 95 with IE 5.5, 98 or NT4. 4322 menter = false; 4323 4324 POINT pt; 4325 GetCursorPos(&pt); 4326 MapWindowPoints(HWND.init, hwnd, &pt, 1); 4327 scope MouseEventArgs mea = new MouseEventArgs(wparamMouseButtons(msg.wParam), 0, pt.x, pt.y, 0); 4328 onMouseLeave(mea); 4329 } 4330 break; 4331 4332 case WM_LBUTTONDOWN: { 4333 _clicking = true; 4334 4335 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.LEFT, 1, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4336 onMouseDown(mea); 4337 4338 //if(ctrlStyle & ControlStyles.SELECTABLE) 4339 // SetFocus(hwnd); // No, this goofs up stuff, including the ComboBox dropdown. 4340 } 4341 break; 4342 4343 case WM_RBUTTONDOWN: { 4344 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.RIGHT, 1, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4345 onMouseDown(mea); 4346 } 4347 break; 4348 4349 case WM_MBUTTONDOWN: { 4350 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.MIDDLE, 1, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4351 onMouseDown(mea); 4352 } 4353 break; 4354 4355 case WM_LBUTTONUP: { 4356 if(msg.lParam == -1) { 4357 break; 4358 } 4359 4360 // Use temp in case of exception. 4361 bool wasClicking = _clicking; 4362 _clicking = false; 4363 4364 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.LEFT, 1, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4365 onMouseUp(mea); 4366 4367 if(wasClicking && (ctrlStyle & ControlStyles.STANDARD_CLICK)) { 4368 // See if the mouse up was over the control. 4369 if(Rect(0, 0, wclientsz.width, wclientsz.height).contains(mea.x, mea.y)) { 4370 // Now make sure there's no child in the way. 4371 //if(ChildWindowFromPoint(hwnd, Point(mea.x, mea.y).point) == hwnd) // Includes hidden windows. 4372 if(pointOverVisibleChild(Point(mea.x, mea.y)) == hwnd) { 4373 onClick(EventArgs.empty); 4374 } 4375 } 4376 } 4377 } 4378 break; 4379 4380 version(CUSTOM_MSG_HOOK) { 4381 } 4382 else { 4383 case WM_DRAWITEM: { 4384 Control ctrl; 4385 4386 DRAWITEMSTRUCT* dis = cast(DRAWITEMSTRUCT*)msg.lParam; 4387 if(dis.CtlType == ODT_MENU) { 4388 // dis.hwndItem is the HMENU. 4389 } else { 4390 ctrl = Control.fromChildHandle(dis.hwndItem); 4391 if(ctrl) { 4392 //msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg)); 4393 ctrl.onReflectedMessage(msg); 4394 return; 4395 } 4396 } 4397 } 4398 break; 4399 4400 case WM_MEASUREITEM: { 4401 Control ctrl; 4402 4403 MEASUREITEMSTRUCT* mis = cast(MEASUREITEMSTRUCT*)msg.lParam; 4404 if(!(mis.CtlType == ODT_MENU)) { 4405 ctrl = Control.fromChildHandle(cast(HWND)mis.CtlID); 4406 if(ctrl) { 4407 //msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg)); 4408 ctrl.onReflectedMessage(msg); 4409 return; 4410 } 4411 } 4412 } 4413 break; 4414 4415 case WM_COMMAND: { 4416 /+ 4417 switch(LOWORD(msg.wParam)) { 4418 case IDOK: 4419 case IDCANCEL: 4420 if(parent) { 4421 parent.wndProc(msg); 4422 } 4423 //break; 4424 return; // ? 4425 4426 default: 4427 } 4428 +/ 4429 4430 Control ctrl; 4431 4432 ctrl = Control.fromChildHandle(cast(HWND)msg.lParam); 4433 if(ctrl) { 4434 //msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg)); 4435 ctrl.onReflectedMessage(msg); 4436 return; 4437 } else { 4438 version(DFL_NO_MENUS) { 4439 } 4440 else { 4441 MenuItem m; 4442 4443 m = cast(MenuItem)Application.lookupMenuID(LOWORD(msg.wParam)); 4444 if(m) { 4445 //msg.result = m.customMsg(*(cast(CustomMsg*)&msg)); 4446 m._reflectMenu(msg); 4447 //return; // ? 4448 } 4449 } 4450 } 4451 } 4452 break; 4453 4454 case WM_NOTIFY: { 4455 Control ctrl; 4456 NMHDR* nmh; 4457 nmh = cast(NMHDR*)msg.lParam; 4458 4459 ctrl = Control.fromChildHandle(nmh.hwndFrom); 4460 if(ctrl) { 4461 //msg.result = ctrl.customMsg(*(cast(CustomMsg*)&msg)); 4462 ctrl.onReflectedMessage(msg); 4463 return; 4464 } 4465 } 4466 break; 4467 4468 version(DFL_NO_MENUS) { 4469 } 4470 else { 4471 case WM_MENUSELECT: { 4472 UINT mflags; 4473 UINT uitem; 4474 int mid; 4475 MenuItem m; 4476 4477 mflags = HIWORD(msg.wParam); 4478 uitem = LOWORD(msg.wParam); // Depends on the flags. 4479 4480 if(mflags & MF_SYSMENU) { 4481 break; 4482 } 4483 4484 if(mflags & MF_POPUP) { 4485 // -uitem- is an index. 4486 mid = GetMenuItemID(cast(HMENU)msg.lParam, uitem); 4487 } else { 4488 // -uitem- is the item identifier. 4489 mid = uitem; 4490 } 4491 4492 m = cast(MenuItem)Application.lookupMenuID(mid); 4493 if(m) { 4494 //msg.result = m.customMsg(*(cast(CustomMsg*)&msg)); 4495 m._reflectMenu(msg); 4496 //return; 4497 } 4498 } 4499 break; 4500 4501 case WM_INITMENUPOPUP: 4502 if(HIWORD(msg.lParam)) { 4503 // System menu. 4504 } else { 4505 MenuItem m; 4506 4507 //m = cast(MenuItem)Application.lookupMenuID(GetMenuItemID(cast(HMENU)msg.wParam, LOWORD(msg.lParam))); 4508 m = cast(MenuItem)Application.lookupMenu(cast(HMENU)msg.wParam); 4509 if(m) { 4510 //msg.result = m.customMsg(*(cast(CustomMsg*)&msg)); 4511 m._reflectMenu(msg); 4512 //return; 4513 } 4514 } 4515 break; 4516 4517 case WM_INITMENU: { 4518 ContextMenu m; 4519 4520 m = cast(ContextMenu)Application.lookupMenu(cast(HMENU)msg.wParam); 4521 if(m) { 4522 //msg.result = m.customMsg(*(cast(CustomMsg*)&msg)); 4523 m._reflectMenu(msg); 4524 //return; 4525 } 4526 } 4527 break; 4528 } 4529 } 4530 4531 case WM_RBUTTONUP: { 4532 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.RIGHT, 1, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4533 onMouseUp(mea); 4534 } 4535 break; 4536 4537 case WM_MBUTTONUP: { 4538 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.MIDDLE, 1, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4539 onMouseUp(mea); 4540 } 4541 break; 4542 4543 case WM_LBUTTONDBLCLK: { 4544 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.LEFT, 2, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4545 onMouseDown(mea); 4546 4547 if((ctrlStyle & (ControlStyles.STANDARD_CLICK | ControlStyles.STANDARD_DOUBLE_CLICK)) 4548 == (ControlStyles.STANDARD_CLICK | ControlStyles.STANDARD_DOUBLE_CLICK)) { 4549 onDoubleClick(EventArgs.empty); 4550 } 4551 } 4552 break; 4553 4554 case WM_RBUTTONDBLCLK: { 4555 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.RIGHT, 2, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4556 onMouseDown(mea); 4557 } 4558 break; 4559 4560 case WM_MBUTTONDBLCLK: { 4561 scope MouseEventArgs mea = new MouseEventArgs(MouseButtons.MIDDLE, 2, cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam), 0); 4562 onMouseDown(mea); 4563 } 4564 break; 4565 4566 case WM_SETFOCUS: 4567 _wmSetFocus(); 4568 // defWndProc* Form focuses a child. 4569 break; 4570 4571 case WM_KILLFOCUS: 4572 _wmKillFocus(); 4573 break; 4574 4575 case WM_ENABLE: 4576 onEnabledChanged(EventArgs.empty); 4577 4578 // defWndProc* 4579 break; 4580 4581 /+ 4582 case WM_NEXTDLGCTL: 4583 if(msg.wParam && !LOWORD(msg.lParam)) { 4584 HWND hwf; 4585 hwf = GetFocus(); 4586 if(hwf) { 4587 Control hwc; 4588 hwc = Control.fromHandle(hwf); 4589 if(hwc) { 4590 if(hwc._rtype() & 0x20) { // TabControl 4591 hwf = GetWindow(hwf, GW_CHILD); 4592 if(hwf) { 4593 // Can't do this because it could be modifying someone else's memory. 4594 //msg.wParam = cast(WPARAM)hwf; 4595 //msg.lParam = MAKELPARAM(1, 0); 4596 msg.result = DefWindowProcA(msg.hWnd, WM_NEXTDLGCTL, cast(WPARAM)hwf, MAKELPARAM(TRUE, 0)); 4597 return; 4598 } 4599 } 4600 } 4601 } 4602 } 4603 break; 4604 +/ 4605 4606 case WM_SETTEXT: 4607 defWndProc(msg); 4608 4609 // Need to fetch it because cast(char*)lparam isn't always accessible ? 4610 // Should this go in _wndProc()? Need to defWndProc() first ? 4611 if(ctrlStyle & ControlStyles.CACHE_TEXT) { 4612 wtext = _fetchText(); 4613 } 4614 4615 onTextChanged(EventArgs.empty); 4616 return; 4617 4618 case WM_SETFONT: 4619 // Don't replace -wfont- if it's the same one, beacuse the old Font 4620 // object will get garbage collected and probably delete the HFONT. 4621 4622 //onFontChanged(EventArgs.empty); 4623 4624 // defWndProc* 4625 return; 4626 4627 /+ 4628 case WM_STYLECHANGED: { 4629 //defWndProc(msg); 4630 4631 STYLESTRUCT* ss = cast(STYLESTRUCT*)msg.lParam; 4632 DWORD changed = ss.styleOld ^ ss.styleNew; 4633 4634 if(msg.wParam == GWL_EXSTYLE) { 4635 //if(changed & WS_EX_RTLREADING) 4636 // onRightToLeftChanged(EventArgs.empty); 4637 } 4638 } 4639 break; 4640 +/ 4641 4642 case WM_ACTIVATE: 4643 switch(LOWORD(msg.wParam)) { 4644 case WA_INACTIVE: 4645 _clicking = false; 4646 break; 4647 4648 default: 4649 } 4650 break; 4651 4652 version(DFL_NO_MENUS) { 4653 } 4654 else { 4655 case WM_CONTEXTMENU: 4656 if(hwnd == cast(HWND)msg.wParam) { 4657 if(cmenu) { 4658 // Shift+F10 causes xPos and yPos to be -1. 4659 4660 Point point; 4661 4662 if(msg.lParam == -1) { 4663 point = pointToScreen(Point(0, 0)); 4664 } else { 4665 point = Point(cast(short)LOWORD(msg.lParam), cast(short)HIWORD(msg.lParam)); 4666 } 4667 4668 SetFocus(handle); // ? 4669 cmenu.show(this, point); 4670 4671 return; 4672 } 4673 } 4674 break; 4675 } 4676 4677 case WM_HELP: { 4678 HELPINFO* hi = cast(HELPINFO*)msg.lParam; 4679 4680 scope HelpEventArgs hea = new HelpEventArgs(Point(hi.MousePos.x, hi.MousePos.y)); 4681 onHelpRequested(hea); 4682 if(hea.handled) { 4683 msg.result = TRUE; 4684 return; 4685 } 4686 } 4687 break; 4688 4689 case WM_SYSCOLORCHANGE: 4690 onSystemColorsChanged(EventArgs.empty); 4691 4692 // Need to send the message to children for some common controls to update properly. 4693 foreach(Control ctrl; ccollection) { 4694 SendMessageA(ctrl.handle, WM_SYSCOLORCHANGE, msg.wParam, msg.lParam); 4695 } 4696 break; 4697 4698 case WM_SETTINGCHANGE: 4699 // Send the message to children. 4700 foreach(Control ctrl; ccollection) { 4701 SendMessageA(ctrl.handle, WM_SETTINGCHANGE, msg.wParam, msg.lParam); 4702 } 4703 break; 4704 4705 case WM_PALETTECHANGED: 4706 /+ 4707 if(cast(HWND)msg.wParam != hwnd) { 4708 // Realize palette. 4709 } 4710 +/ 4711 4712 // Send the message to children. 4713 foreach(Control ctrl; ccollection) { 4714 SendMessageA(ctrl.handle, WM_PALETTECHANGED, msg.wParam, msg.lParam); 4715 } 4716 break; 4717 4718 //case WM_QUERYNEWPALETTE: // Send this message to children ? 4719 4720 /+ 4721 // Moved this stuff to -parent-. 4722 case WM_PARENTNOTIFY: 4723 switch(LOWORD(msg.wParam)) { 4724 case WM_DESTROY: 4725 Control ctrl = fromChildHandle(cast(HWND)msg.lParam); 4726 if(ctrl) { 4727 _ctrlremoved(new ControlEventArgs(ctrl)); 4728 4729 // ? 4730 vchanged(); 4731 //alayout(ctrl); // This is already being called from somewhere else.. 4732 } 4733 break; 4734 4735 /+ 4736 case WM_CREATE: 4737 initLayout(); 4738 break; 4739 +/ 4740 4741 default: 4742 } 4743 break; 4744 +/ 4745 4746 case WM_CREATE: 4747 /+ 4748 if(wparent) { 4749 initLayout(); // ? 4750 } 4751 +/ 4752 if(cbits & CBits.NEED_INIT_LAYOUT) { 4753 if(visible) { 4754 if(wparent) { 4755 wparent.vchanged(); 4756 suspendLayout(); // Note: exception could cause failure to restore. 4757 wparent.alayout(this); 4758 resumeLayout(false); 4759 } 4760 alayout(this); 4761 } 4762 } 4763 break; 4764 4765 case WM_DESTROY: 4766 onHandleDestroyed(EventArgs.empty); 4767 break; 4768 4769 case WM_GETDLGCODE: { 4770 version(_DFL_WINDOWS_HUNG_WORKAROUND) { 4771 /+ 4772 if(ctrlStyle & ControlStyles.CONTAINER_CONTROL) { 4773 if(!(_exStyle & WS_EX_CONTROLPARENT)) { 4774 assert(0); 4775 } 4776 } 4777 +/ 4778 4779 DWORD dw; 4780 dw = GetTickCount(); 4781 if(ldlgcode < dw - 1020) { 4782 ldlgcode = dw - 1000; 4783 } else { 4784 ldlgcode += 50; 4785 if(ldlgcode > dw) { 4786 // Probably a problem with WS_EX_CONTROLPARENT and WS_TABSTOP. 4787 if(ldlgcode >= ldlgcode.max - 10_000) { 4788 ldlgcode = 0; 4789 throw new WindowsHungDflException("Windows hung"); 4790 } 4791 //msg.result |= 0x0004 | 0x0002 | 0x0001; //DLGC_WANTALLKEYS | DLGC_WANTTAB | DLGC_WANTARROWS; 4792 ldlgcode = ldlgcode.max - 10_000; 4793 return; 4794 } 4795 } 4796 } 4797 4798 /+ 4799 if(msg.lParam) { 4800 Message m; 4801 m._winMsg = *cast(MSG*)msg.lParam; 4802 if(processKeyEventArgs(m)) { 4803 return; 4804 } 4805 } 4806 +/ 4807 4808 defWndProc(msg); 4809 4810 if(ctrlStyle & ControlStyles.WANT_ALL_KEYS) { 4811 msg.result |= DLGC_WANTALLKEYS; 4812 } 4813 4814 // Only want chars if ALT isn't down, because it would break mnemonics. 4815 if(!(GetKeyState(VK_MENU) & 0x8000)) { 4816 msg.result |= DLGC_WANTCHARS; 4817 } 4818 4819 } 4820 return; 4821 4822 case WM_CLOSE: 4823 /+ { 4824 if(parent) { 4825 Message mp; 4826 mp = msg; 4827 mp.hWnd = parent.handle; 4828 parent.wndProc(mp); // Pass to parent so it can decide what to do. 4829 } 4830 }+/ 4831 return; // Prevent defWndProc from destroying the window! 4832 4833 case 0: // WM_NULL 4834 // Don't confuse with failed RegisterWindowMessage(). 4835 break; 4836 4837 default: 4838 //defWndProc(msg); 4839 version(DFL_NO_WM_GETCONTROLNAME) { 4840 } 4841 else { 4842 if(msg.msg == wmGetControlName) { 4843 //cprintf("WM_GETCONTROLNAME: %.*s; wparam: %d\n", cast(uint)name.length, name.ptr, msg.wParam); 4844 if(msg.wParam && this.name.length) { 4845 OSVERSIONINFOA osver; 4846 osver.dwOSVersionInfoSize = OSVERSIONINFOA.sizeof; 4847 if(GetVersionExA(&osver)) { 4848 try { 4849 if(osver.dwPlatformId <= VER_PLATFORM_WIN32_WINDOWS) { 4850 if(dfl.internal.utf.useUnicode) { 4851 } else { 4852 // ANSI. 4853 Dstring ansi; 4854 ansi = dfl.internal.utf.toAnsi(this.name); 4855 if(msg.wParam <= ansi.length) { 4856 ansi = ansi[0 .. msg.wParam - 1]; 4857 } 4858 (cast(char*)msg.lParam)[0 .. ansi.length] = ansi[]; 4859 (cast(char*)msg.lParam)[ansi.length] = 0; 4860 msg.result = ansi.length + 1; 4861 } 4862 } else { 4863 // Unicode. 4864 Dwstring uni; 4865 uni = dfl.internal.utf.toUnicode(this.name); 4866 if(msg.wParam <= uni.length) { 4867 uni = uni[0 .. msg.wParam - 1]; 4868 } 4869 (cast(wchar*)msg.lParam)[0 .. uni.length] = uni[]; 4870 (cast(wchar*)msg.lParam)[uni.length] = 0; 4871 msg.result = uni.length + 1; 4872 } 4873 } catch { 4874 } 4875 return; 4876 } 4877 } 4878 } 4879 } 4880 } 4881 4882 defWndProc(msg); 4883 4884 if(msg.msg == WM_CREATE) { 4885 EventArgs ea; 4886 ea = EventArgs.empty; 4887 onHandleCreated(ea); 4888 4889 debug { 4890 assert(_handlecreated, "If overriding onHandleCreated(), be sure to call super.onHandleCreated()!"); 4891 } 4892 handleCreated(this, ea); 4893 debug { 4894 _handlecreated = false; // Reset. 4895 } 4896 } 4897 } 4898 4899 4900 package final void _wmSetFocus() { 4901 //onEnter(EventArgs.empty); 4902 4903 onGotFocus(EventArgs.empty); 4904 4905 // defWndProc* Form focuses a child. 4906 } 4907 4908 4909 package final void _wmKillFocus() { 4910 _clicking = false; 4911 4912 //onLeave(EventArgs.empty); 4913 4914 //if(cvalidation) 4915 // onValidating(new CancelEventArgs); 4916 4917 onLostFocus(EventArgs.empty); 4918 } 4919 4920 4921 4922 protected void defWndProc(ref Message msg) { 4923 //msg.result = DefWindowProcA(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 4924 msg.result = dfl.internal.utf.defWindowProc(msg.hWnd, msg.msg, msg.wParam, msg.lParam); 4925 } 4926 4927 4928 // Always called right when destroyed, before doing anything else. 4929 // hwnd is cleared after this step. 4930 void _destroying() { // package 4931 //wparent = null; // ? 4932 } 4933 4934 4935 // This function must be called FIRST for EVERY message to this 4936 // window in order to keep the correct window state. 4937 // This function must not throw exceptions. 4938 package final void mustWndProc(ref Message msg) { 4939 if(needCalcSize) { 4940 needCalcSize = false; 4941 RECT crect; 4942 GetClientRect(msg.hWnd, &crect); 4943 wclientsz.width = crect.right; 4944 wclientsz.height = crect.bottom; 4945 } 4946 4947 switch(msg.msg) { 4948 case WM_NCCALCSIZE: 4949 needCalcSize = true; 4950 break; 4951 4952 case WM_WINDOWPOSCHANGED: { 4953 WINDOWPOS* wp = cast(WINDOWPOS*)msg.lParam; 4954 4955 if(!recreatingHandle) { 4956 //wstyle = GetWindowLongA(hwnd, GWL_STYLE); // ..WM_SHOWWINDOW. 4957 if(wp.flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW)) { 4958 //wstyle = GetWindowLongA(hwnd, GWL_STYLE); 4959 cbits |= CBits.VISIBLE; 4960 wstyle |= WS_VISIBLE; 4961 if(wp.flags & SWP_HIDEWINDOW) { // Hiding. 4962 cbits &= ~CBits.VISIBLE; 4963 wstyle &= ~WS_VISIBLE; 4964 } 4965 //break; // Showing min/max includes other flags. 4966 } 4967 } 4968 4969 //if(!(wp.flags & SWP_NOMOVE)) 4970 // wrect.location = Point(wp.x, wp.y); 4971 if(!(wp.flags & SWP_NOSIZE) || !(wp.flags & SWP_NOMOVE) || (wp.flags & SWP_FRAMECHANGED)) { 4972 //wrect = _fetchBounds(); 4973 wrect = Rect(wp.x, wp.y, wp.cx, wp.cy); 4974 wclientsz = _fetchClientSize(); 4975 } 4976 4977 if((wp.flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW)) || !(wp.flags & SWP_NOSIZE)) { 4978 DWORD rstyle; 4979 rstyle = GetWindowLongA(msg.hWnd, GWL_STYLE); 4980 rstyle &= WS_MAXIMIZE | WS_MINIMIZE; 4981 wstyle &= ~(WS_MAXIMIZE | WS_MINIMIZE); 4982 wstyle |= rstyle; 4983 } 4984 } 4985 break; 4986 4987 /+ 4988 case WM_WINDOWPOSCHANGING: 4989 //oldwrect = wrect; 4990 break; 4991 +/ 4992 4993 /+ 4994 case WM_SETFONT: 4995 //wfont = _fetchFont(); 4996 break; 4997 +/ 4998 4999 case WM_STYLECHANGED: { 5000 STYLESTRUCT* ss = cast(STYLESTRUCT*)msg.lParam; 5001 5002 if(msg.wParam == GWL_STYLE) { 5003 wstyle = ss.styleNew; 5004 } else if(msg.wParam == GWL_EXSTYLE) { 5005 wexstyle = ss.styleNew; 5006 } 5007 5008 /+ 5009 wrect = _fetchBounds(); 5010 wclientsz = _fetchClientSize(); 5011 +/ 5012 } 5013 break; 5014 5015 /+ 5016 // NOTE: this is sent even if the parent is shown. 5017 case WM_SHOWWINDOW: 5018 if(!msg.lParam) { 5019 /+ { 5020 cbits &= ~(CBits.SW_SHOWN | CBits.SW_HIDDEN); 5021 DWORD rstyle; 5022 rstyle = GetWindowLongA(msg.hWnd, GWL_STYLE); 5023 if(cast(BOOL)msg.wParam) { 5024 //wstyle |= WS_VISIBLE; 5025 if(!(WS_VISIBLE & wstyle) && (WS_VISIBLE & rstyle)) { 5026 wstyle = rstyle; 5027 cbits |= CBits.SW_SHOWN; 5028 5029 try { 5030 createChildren(); // Might throw. 5031 } catch(DThrowable e) { 5032 Application.onThreadException(e); 5033 } 5034 } 5035 wstyle = rstyle; 5036 } else 5037 { 5038 //wstyle &= ~WS_VISIBLE; 5039 if((WS_VISIBLE & wstyle) && !(WS_VISIBLE & rstyle)) { 5040 wstyle = rstyle; 5041 cbits |= CBits.SW_HIDDEN; 5042 } 5043 wstyle = rstyle; 5044 } 5045 } 5046 +/ 5047 wstyle = GetWindowLongA(msg.hWnd, GWL_STYLE); 5048 //if(cbits & CBits.FVISIBLE) 5049 // wstyle |= WS_VISIBLE; 5050 } 5051 break; 5052 +/ 5053 5054 case WM_ENABLE: 5055 /+ 5056 //if(IsWindowEnabled(hwnd)) 5057 if(cast(BOOL)msg.wParam) { 5058 wstyle &= ~WS_DISABLED; 5059 } else { 5060 wstyle |= WS_DISABLED; 5061 } 5062 +/ 5063 wstyle = GetWindowLongA(hwnd, GWL_STYLE); 5064 break; 5065 5066 /+ 5067 case WM_PARENTNOTIFY: 5068 switch(LOWORD(msg.wParam)) { 5069 case WM_DESTROY: 5070 // ... 5071 break; 5072 5073 default: 5074 } 5075 break; 5076 +/ 5077 5078 case WM_NCCREATE: { 5079 //hwnd = msg.hWnd; 5080 5081 /+ 5082 // Not using CREATESTRUCT for window bounds because it can contain 5083 // CW_USEDEFAULT and other magic values. 5084 5085 CREATESTRUCTA* cs; 5086 cs = cast(CREATESTRUCTA*)msg.lParam; 5087 5088 //wrect = Rect(cs.x, cs.y, cs.cx, cs.cy); 5089 +/ 5090 5091 wrect = _fetchBounds(); 5092 //oldwrect = wrect; 5093 wclientsz = _fetchClientSize(); 5094 } 5095 break; 5096 5097 case WM_CREATE: 5098 try { 5099 cbits |= CBits.CREATED; 5100 5101 //hwnd = msg.hWnd; 5102 5103 CREATESTRUCTA* cs; 5104 cs = cast(CREATESTRUCTA*)msg.lParam; 5105 /+ 5106 // Done in WM_NCCREATE now. 5107 //wrect = _fetchBounds(); 5108 wrect = Rect(cs.x, cs.y, cs.cx, cs.cy); 5109 wclientsz = _fetchClientSize(); 5110 +/ 5111 5112 // If class style was changed, update. 5113 if(_fetchClassLong() != wclassStyle) { 5114 SetClassLongA(hwnd, GCL_STYLE, wclassStyle); 5115 } 5116 5117 // Need to update clientSize in case of styles in createParams(). 5118 wclientsz = _fetchClientSize(); 5119 5120 //finishCreating(msg.hWnd); 5121 5122 if(!(ctrlStyle & ControlStyles.CACHE_TEXT)) { 5123 wtext = null; 5124 } 5125 5126 /+ 5127 // Gets created on demand instead. 5128 if(Color.empty != backc) { 5129 hbrBg = backc.createBrush(); 5130 } 5131 +/ 5132 5133 /+ 5134 // ? 5135 wstyle = cs.style; 5136 wexstyle = cs.dwExStyle; 5137 +/ 5138 5139 createChildren(); // Might throw. Used to be commented-out. 5140 5141 if(recreatingHandle) { 5142 // After existing messages and functions are done. 5143 delayInvoke(function(Control cthis, size_t[] params) { 5144 cthis.cbits &= ~CBits.RECREATING; 5145 }); 5146 } 5147 } catch(DThrowable e) { 5148 Application.onThreadException(e); 5149 } 5150 break; 5151 5152 case WM_DESTROY: 5153 cbits &= ~CBits.CREATED; 5154 if(!recreatingHandle) { 5155 cbits &= ~CBits.FORMLOADED; 5156 } 5157 _destroying(); 5158 //if(!killing) 5159 if(recreatingHandle) { 5160 fillRecreationData(); 5161 } 5162 break; 5163 5164 case WM_NCDESTROY: 5165 Application.removeHwnd(hwnd); 5166 hwnd = HWND.init; 5167 break; 5168 5169 default: 5170 /+ 5171 if(msg.msg == wmDfl) { 5172 switch(msg.wParam) { 5173 case WPARAM_DFL_: 5174 5175 default: 5176 } 5177 } 5178 +/ 5179 } 5180 } 5181 5182 5183 package final void _wndProc(ref Message msg) { 5184 //mustWndProc(msg); // Done in dflWndProc() now. 5185 wndProc(msg); 5186 } 5187 5188 5189 package final void _defWndProc(ref Message msg) { 5190 defWndProc(msg); 5191 } 5192 5193 5194 package final void doShow() { 5195 if(wparent) { // Exclude owner. 5196 SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER); 5197 } else { 5198 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); 5199 } 5200 } 5201 5202 5203 package final void doHide() { 5204 SetWindowPos(hwnd, HWND.init, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOZORDER); 5205 } 5206 5207 5208 //EventHandler backColorChanged; 5209 Event!(Control, EventArgs) backColorChanged; /// 5210 // EventHandler backgroundImageChanged; 5211 /+ 5212 deprecated EventHandler causesValidationChanged; 5213 deprecated InvalidateEventHandler invalidated; 5214 deprecated EventHandler validated; 5215 deprecated CancelEventHandler validating; // Once cancel is true, remaining events are suppressed (including validated). 5216 deprecated EventHandler enter; // Cascades up. TODO: fix implementation. 5217 deprecated EventHandler leave; // Cascades down. TODO: fix implementation. 5218 deprecated UICuesEventHandler changeUICues; // TODO: properly fire. 5219 +/ 5220 //EventHandler click; 5221 Event!(Control, EventArgs) click; /// 5222 version(DFL_NO_MENUS) { 5223 } 5224 else { 5225 //EventHandler contextMenuChanged; 5226 Event!(Control, EventArgs) contextMenuChanged; /// 5227 } 5228 //ControlEventHandler controlAdded; 5229 Event!(Control, ControlEventArgs) controlAdded; /// 5230 //ControlEventHandler controlRemoved; 5231 Event!(Control, ControlEventArgs) controlRemoved; /// 5232 //EventHandler cursorChanged; 5233 Event!(Control, EventArgs) cursorChanged; /// 5234 //EventHandler disposed; 5235 Event!(Control, EventArgs) disposed; /// 5236 //EventHandler dockChanged; 5237 //Event!(Control, EventArgs) dockChanged; /// 5238 Event!(Control, EventArgs) hasLayoutChanged; /// 5239 alias hasLayoutChanged dockChanged; 5240 //EventHandler doubleClick; 5241 Event!(Control, EventArgs) doubleClick; /// 5242 //EventHandler enabledChanged; 5243 Event!(Control, EventArgs) enabledChanged; /// 5244 //EventHandler fontChanged; 5245 Event!(Control, EventArgs) fontChanged; /// 5246 //EventHandler foreColorChanged; 5247 Event!(Control, EventArgs) foreColorChanged; /// 5248 //EventHandler gotFocus; // After enter. 5249 Event!(Control, EventArgs) gotFocus; /// 5250 //EventHandler handleCreated; 5251 Event!(Control, EventArgs) handleCreated; /// 5252 //EventHandler handleDestroyed; 5253 Event!(Control, EventArgs) handleDestroyed; /// 5254 //HelpEventHandler helpRequested; 5255 Event!(Control, HelpEventArgs) helpRequested; /// 5256 //KeyEventHandler keyDown; 5257 Event!(Control, KeyEventArgs) keyDown; /// 5258 //KeyEventHandler keyPress; 5259 Event!(Control, KeyPressEventArgs) keyPress; /// 5260 //KeyEventHandler keyUp; 5261 Event!(Control, KeyEventArgs) keyUp; /// 5262 //LayoutEventHandler layout; 5263 Event!(Control, LayoutEventArgs) layout; /// 5264 //EventHandler lostFocus; 5265 Event!(Control, EventArgs) lostFocus; /// 5266 //MouseEventHandler mouseDown; 5267 Event!(Control, MouseEventArgs) mouseDown; /// 5268 //MouseEventHandler mouseEnter; 5269 Event!(Control, MouseEventArgs) mouseEnter; /// 5270 //MouseEventHandler mouseHover; 5271 Event!(Control, MouseEventArgs) mouseHover; /// 5272 //MouseEventHandler mouseLeave; 5273 Event!(Control, MouseEventArgs) mouseLeave; /// 5274 //MouseEventHandler mouseMove; 5275 Event!(Control, MouseEventArgs) mouseMove; /// 5276 //MouseEventHandler mouseUp; 5277 Event!(Control, MouseEventArgs) mouseUp; /// 5278 //MouseEventHandler mouseWheel; 5279 Event!(Control, MouseEventArgs) mouseWheel; /// 5280 //EventHandler moving; 5281 Event!(Control, MovingEventArgs) moving; /// 5282 //EventHandler move; 5283 Event!(Control, EventArgs) move; /// 5284 //EventHandler locationChanged; 5285 alias move locationChanged; 5286 //PaintEventHandler paint; 5287 Event!(Control, PaintEventArgs) paint; /// 5288 //EventHandler parentChanged; 5289 Event!(Control, EventArgs) parentChanged; /// 5290 //EventHandler sizing; 5291 Event!(Control, SizingEventArgs) sizing; /// 5292 //EventHandler resize; 5293 Event!(Control, EventArgs) resize; /// 5294 //EventHandler sizeChanged; 5295 alias resize sizeChanged; 5296 //EventHandler rightToLeftChanged; 5297 Event!(Control, EventArgs) rightToLeftChanged; /// 5298 // EventHandler styleChanged; 5299 //EventHandler systemColorsChanged; 5300 Event!(Control, EventArgs) systemColorsChanged; /// 5301 // EventHandler tabIndexChanged; 5302 // EventHandler tabStopChanged; 5303 //EventHandler textChanged; 5304 Event!(Control, EventArgs) textChanged; /// 5305 //EventHandler visibleChanged; 5306 Event!(Control, EventArgs) visibleChanged; /// 5307 5308 version(DFL_NO_DRAG_DROP) {} else { 5309 //DragEventHandler dragDrop; 5310 Event!(Control, DragEventArgs) dragDrop; /// 5311 //DragEventHandler dragEnter; 5312 Event!(Control, DragEventArgs) dragEnter; /// 5313 //EventHandler dragLeave; 5314 Event!(Control, EventArgs) dragLeave; /// 5315 //DragEventHandler dragOver; 5316 Event!(Control, DragEventArgs) dragOver; /// 5317 //GiveFeedbackEventHandler giveFeedback; 5318 Event!(Control, GiveFeedbackEventArgs) giveFeedback; /// 5319 //QueryContinueDragEventHandler queryContinueDrag; 5320 Event!(Control, QueryContinueDragEventArgs) queryContinueDrag; /// 5321 } 5322 5323 5324 /// Construct a new Control instance. 5325 this() { 5326 //name = DObject.toString(); // ? 5327 5328 wrect.size = defaultSize; 5329 //oldwrect = wrect; 5330 5331 /+ 5332 backc = defaultBackColor; 5333 forec = defaultForeColor; 5334 wfont = defaultFont; 5335 wcurs = new Cursor(LoadCursorA(HINSTANCE.init, IDC_ARROW), false); 5336 +/ 5337 backc = Color.empty; 5338 forec = Color.empty; 5339 wfont = null; 5340 wcurs = null; 5341 5342 ccollection = createControlsInstance(); 5343 } 5344 5345 /// ditto 5346 this(Dstring text) { 5347 this(); 5348 wtext = text; 5349 5350 ccollection = createControlsInstance(); 5351 } 5352 5353 /// ditto 5354 this(Control cparent, Dstring text) { 5355 this(); 5356 wtext = text; 5357 parent = cparent; 5358 5359 ccollection = createControlsInstance(); 5360 } 5361 5362 /// ditto 5363 this(Dstring text, int left, int top, int width, int height) { 5364 this(); 5365 wtext = text; 5366 wrect = Rect(left, top, width, height); 5367 5368 ccollection = createControlsInstance(); 5369 } 5370 5371 /// ditto 5372 this(Control cparent, Dstring text, int left, int top, int width, int height) { 5373 this(); 5374 wtext = text; 5375 wrect = Rect(left, top, width, height); 5376 parent = cparent; 5377 5378 ccollection = createControlsInstance(); 5379 } 5380 5381 5382 /+ 5383 // Used internally. 5384 this(HWND hwnd) 5385 in { 5386 assert(hwnd); 5387 } 5388 body { 5389 this.hwnd = hwnd; 5390 owned = false; 5391 5392 ccollection = new ControlCollection(this); 5393 } 5394 +/ 5395 5396 5397 ~this() { 5398 debug(APP_PRINT) 5399 cprintf("~Control %p\n", cast(void*)this); 5400 5401 version(DFL_NO_ZOMBIE_FORM) { 5402 } 5403 else { 5404 Application.zombieKill(this); // Does nothing if not zombie. 5405 } 5406 5407 //dispose(false); 5408 destroyHandle(); 5409 deleteThisBackgroundBrush(); 5410 } 5411 5412 5413 /+ package +/ /+ protected +/ int _rtype() { // package 5414 return 0; 5415 } 5416 5417 5418 5419 void dispose() { 5420 dispose(true); 5421 } 5422 5423 /// ditto 5424 protected void dispose(bool disposing) { 5425 if(disposing) { 5426 killing = true; 5427 5428 version(DFL_NO_MENUS) { 5429 } 5430 else { 5431 cmenu = cmenu.init; 5432 } 5433 _ctrlname = _ctrlname.init; 5434 otag = otag.init; 5435 wcurs = wcurs.init; 5436 wfont = wfont.init; 5437 wparent = wparent.init; 5438 wregion = wregion.init; 5439 wtext = wtext.init; 5440 deleteThisBackgroundBrush(); 5441 //ccollection.children = null; // Not GC-safe in dtor. 5442 //ccollection = null; // ? Causes bad things. Leaving it will do just fine. 5443 } 5444 5445 if(!isHandleCreated) { 5446 return; 5447 } 5448 5449 destroyHandle(); 5450 /+ 5451 //assert(hwnd == HWND.init); // Zombie trips this. (Not anymore with the hwnd-prop) 5452 if(hwnd) { 5453 assert(!IsWindow(hwnd)); 5454 hwnd = HWND.init; 5455 } 5456 +/ 5457 assert(hwnd == HWND.init); 5458 5459 onDisposed(EventArgs.empty); 5460 } 5461 5462 5463 protected: 5464 5465 5466 @property Size defaultSize() { // getter 5467 return Size(0, 0); 5468 } 5469 5470 5471 /+ 5472 // TODO: implement. 5473 @property EventHandlerList events() { // getter 5474 } 5475 +/ 5476 5477 5478 /+ 5479 // TODO: implement. Is this worth implementing? 5480 5481 // Set to -1 to reset cache. 5482 final @property void fontHeight(int fh) { // setter 5483 5484 } 5485 5486 5487 final @property int fontHeight() { // getter 5488 return fonth; 5489 } 5490 +/ 5491 5492 5493 5494 //final void resizeRedraw(bool byes) // setter 5495 public final @property void resizeRedraw(bool byes) { // setter 5496 /+ 5497 // These class styles get lost sometimes so don't rely on them. 5498 LONG cl = _classStyle(); 5499 if(byes) { 5500 cl |= CS_HREDRAW | CS_VREDRAW; 5501 } else { 5502 cl &= ~(CS_HREDRAW | CS_VREDRAW); 5503 } 5504 5505 _classStyle(cl); 5506 +/ 5507 szdraw = byes; 5508 } 5509 5510 /// ditto 5511 final @property bool resizeRedraw() { // getter 5512 //return (_classStyle() & (CS_HREDRAW | CS_VREDRAW)) != 0; 5513 return szdraw; 5514 } 5515 5516 5517 /+ 5518 // /// 5519 // I don't think this is reliable. 5520 final bool hasVisualStyle() { // getter 5521 bool result = false; 5522 HWND hw = handle; // Always reference handle. 5523 HMODULE huxtheme = GetModuleHandleA("uxtheme.dll"); 5524 //HMODULE huxtheme = LoadLibraryA("uxtheme.dll"); 5525 if(huxtheme) { 5526 auto getwintheme = cast(typeof(&GetWindowTheme))GetProcAddress(huxtheme, "GetWindowTheme"); 5527 if(getwintheme) { 5528 result = getwintheme(hw) != null; 5529 } 5530 //FreeLibrary(huxtheme); 5531 } 5532 return result; 5533 } 5534 +/ 5535 5536 5537 package final void _disableVisualStyle() { 5538 assert(isHandleCreated); 5539 5540 HMODULE hmuxt; 5541 hmuxt = GetModuleHandleA("uxtheme.dll"); 5542 if(hmuxt) { 5543 auto setWinTheme = cast(typeof(&SetWindowTheme))GetProcAddress(hmuxt, "SetWindowTheme"); 5544 if(setWinTheme) { 5545 setWinTheme(hwnd, " "w.ptr, " "w.ptr); // Clear the theme. 5546 } 5547 } 5548 } 5549 5550 5551 5552 public final void disableVisualStyle(bool byes = true) { 5553 if(!byes) { 5554 if(cbits & CBits.VSTYLE) { 5555 return; 5556 } 5557 cbits |= CBits.VSTYLE; 5558 5559 if(isHandleCreated) { 5560 _crecreate(); 5561 } 5562 } else { 5563 if(!(cbits & CBits.VSTYLE)) { 5564 return; 5565 } 5566 cbits &= ~CBits.VSTYLE; 5567 5568 if(isHandleCreated) { 5569 _disableVisualStyle(); 5570 } 5571 } 5572 } 5573 5574 deprecated public final void enableVisualStyle(bool byes = true) { 5575 return disableVisualStyle(!byes); 5576 } 5577 5578 5579 5580 ControlCollection createControlsInstance() { 5581 return new ControlCollection(this); 5582 } 5583 5584 5585 deprecated package final void createClassHandle(Dstring className) { 5586 if(!wparent || !wparent.handle || killing) { 5587 create_err: 5588 throw new DflException("Control creation failure"); 5589 } 5590 5591 // This is here because referencing wparent.handle might create me. 5592 //if(created) 5593 if(isHandleCreated) { 5594 return; 5595 } 5596 5597 Application.creatingControl(this); 5598 hwnd = dfl.internal.utf.createWindowEx(wexstyle, className, wtext, wstyle, wrect.x, wrect.y, 5599 wrect.width, wrect.height, wparent.handle, HMENU.init, Application.getInstance(), null); 5600 if(!hwnd) { 5601 goto create_err; 5602 } 5603 } 5604 5605 5606 5607 // Override to change the creation parameters. 5608 // Be sure to call super.createParams() or all the create params will need to be filled. 5609 protected void createParams(ref CreateParams cp) { 5610 with(cp) { 5611 className = CONTROL_CLASSNAME; 5612 caption = wtext; 5613 param = null; 5614 //parent = wparent.handle; 5615 parent = wparent ? wparent.handle : HWND.init; 5616 menu = HMENU.init; 5617 inst = Application.getInstance(); 5618 x = wrect.x; 5619 y = wrect.y; 5620 width = wrect.width; 5621 height = wrect.height; 5622 classStyle = wclassStyle; 5623 exStyle = wexstyle; 5624 wstyle |= WS_VISIBLE; 5625 if(!(cbits & CBits.VISIBLE)) { 5626 wstyle &= ~WS_VISIBLE; 5627 } 5628 style = wstyle; 5629 } 5630 } 5631 5632 5633 5634 protected void createHandle() { 5635 // Note: if modified, Form.createHandle() should be modified as well. 5636 5637 if(isHandleCreated) { 5638 return; 5639 } 5640 5641 //createClassHandle(CONTROL_CLASSNAME); 5642 5643 /+ 5644 if(!wparent || !wparent.handle || killing) { 5645 create_err: 5646 //throw new DflException("Control creation failure"); 5647 throw new DflException(Object.toString() ~ " creation failure"); // ? 5648 } 5649 +/ 5650 5651 debug { 5652 Dstring er; 5653 } 5654 if(killing) { 5655 debug { 5656 er = "the control is being disposed"; 5657 } 5658 5659 debug(APP_PRINT) { 5660 cprintf("Creating Control handle while disposing.\n"); 5661 } 5662 5663 create_err: 5664 Dstring kmsg = "Control creation failure"; 5665 if(name.length) { 5666 kmsg ~= " (" ~ name ~ ")"; 5667 } 5668 debug { 5669 if(er.length) { 5670 kmsg ~= " - " ~ er; 5671 } 5672 } 5673 throw new DflException(kmsg); 5674 //throw new DflException(Object.toString() ~ " creation failure"); // ? 5675 } 5676 5677 // Need the parent's handle to exist. 5678 if(wparent) { 5679 wparent.createHandle(); 5680 } 5681 5682 // This is here because wparent.createHandle() might create me. 5683 //if(created) 5684 if(isHandleCreated) { 5685 return; 5686 } 5687 5688 CreateParams cp; 5689 /+ 5690 DWORD prevClassStyle; 5691 prevClassStyle = wclassStyle; 5692 +/ 5693 5694 createParams(cp); 5695 assert(!isHandleCreated); // Make sure the handle wasn't created in createParams(). 5696 5697 with(cp) { 5698 wtext = caption; 5699 //wrect = Rect(x, y, width, height); // This gets updated in WM_CREATE. 5700 wclassStyle = classStyle; 5701 wexstyle = exStyle; 5702 wstyle = style; 5703 5704 //if(style & WS_CHILD) // Breaks context-help. 5705 if((ctrlStyle & ControlStyles.CONTAINER_CONTROL) && (style & WS_CHILD)) { 5706 exStyle |= WS_EX_CONTROLPARENT; 5707 } 5708 5709 bool vis = (style & WS_VISIBLE) != 0; 5710 5711 Application.creatingControl(this); 5712 hwnd = dfl.internal.utf.createWindowEx(exStyle, className, caption, (style & ~WS_VISIBLE), x, y, 5713 width, height, parent, menu, inst, param); 5714 if(!hwnd) { 5715 debug(APP_PRINT) { 5716 cprintf("CreateWindowEx failed." 5717 " (exStyle=0x%X, className=`%.*s`, caption=`%.*s`, style=0x%X, x=%d, y=%d, width=%d, height=%d," 5718 " parent=0x%X, menu=0x%X, inst=0x%X, param=0x%X)\n", 5719 exStyle, className.ptr, caption.ptr, style, x, y, width, height, 5720 parent, menu, inst, param); 5721 } 5722 5723 debug { 5724 er = std..string.format("CreateWindowEx failed {className=%s;exStyle=0x%X;style=0x%X;parent=0x%X;menu=0x%X;inst=0x%X;}", 5725 className, exStyle, style, cast(void*)parent, cast(void*)menu, cast(void*)inst); 5726 } 5727 5728 goto create_err; 5729 } 5730 5731 if(vis) { 5732 doShow(); // Properly fires onVisibleChanged. 5733 } 5734 } 5735 5736 //onHandleCreated(EventArgs.empty); // Called in WM_CREATE now. 5737 } 5738 5739 5740 package final void _createHandle() { 5741 createHandle(); 5742 } 5743 5744 5745 5746 public final @property bool recreatingHandle() { // getter 5747 if(cbits & CBits.RECREATING) { 5748 return true; 5749 } 5750 return false; 5751 } 5752 5753 5754 private void _setAllRecreating() { 5755 cbits |= CBits.RECREATING; 5756 foreach(Control cc; controls) { 5757 cc._setAllRecreating(); 5758 } 5759 } 5760 5761 5762 5763 protected void recreateHandle() 5764 in { 5765 assert(!recreatingHandle); 5766 } 5767 body { 5768 if(!isHandleCreated) { 5769 return; 5770 } 5771 5772 if(recreatingHandle) { 5773 return; 5774 } 5775 5776 bool hfocus = focused; 5777 HWND prevHwnd = GetWindow(hwnd, GW_HWNDPREV); 5778 5779 _setAllRecreating(); 5780 //scope(exit) 5781 // cbits &= ~CBits.RECREATING; // Now done from WM_CREATE. 5782 5783 destroyHandle(); 5784 createHandle(); 5785 5786 if(prevHwnd) { 5787 SetWindowPos(hwnd, prevHwnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 5788 } else 5789 { SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); } 5790 if(hfocus) { 5791 select(); 5792 } 5793 } 5794 5795 5796 5797 void destroyHandle() { 5798 if(!isHandleCreated) { 5799 return; 5800 } 5801 5802 DestroyWindow(hwnd); 5803 5804 // This stuff is done in WM_DESTROY because DestroyWindow() could be called elsewhere.. 5805 //hwnd = HWND.init; // Done in WM_DESTROY. 5806 //onHandleDestroyed(EventArgs.empty); // Done in WM_DESTROY. 5807 } 5808 5809 5810 private final void fillRecreationData() { 5811 //cprintf(" { fillRecreationData %.*s }\n", name); 5812 5813 if(!(ctrlStyle & ControlStyles.CACHE_TEXT)) { 5814 wtext = _fetchText(); 5815 } 5816 5817 //wclassStyle = _fetchClassLong(); // ? 5818 5819 // Fetch children. 5820 Control[] ccs; 5821 foreach(Control cc; controls) { 5822 ccs ~= cc; 5823 } 5824 ccollection.children = ccs; 5825 } 5826 5827 5828 5829 protected void onDisposed(EventArgs ea) { 5830 disposed(this, ea); 5831 } 5832 5833 5834 5835 protected final bool getStyle(ControlStyles flag) { 5836 return (ctrlStyle & flag) != 0; 5837 } 5838 5839 /// ditto 5840 protected final void setStyle(ControlStyles flag, bool value) { 5841 if(flag & ControlStyles.CACHE_TEXT) { 5842 if(value) { 5843 wtext = _fetchText(); 5844 } else { 5845 wtext = null; 5846 } 5847 } 5848 5849 if(value) { 5850 ctrlStyle |= flag; 5851 } else { 5852 ctrlStyle &= ~flag; 5853 } 5854 } 5855 5856 5857 5858 // Only for setStyle() styles that are part of hwnd and wndclass styles. 5859 protected final void updateStyles() { 5860 LONG newClassStyles = _classStyle(); 5861 LONG newWndStyles = _style(); 5862 5863 if(ctrlStyle & ControlStyles.STANDARD_DOUBLE_CLICK) { 5864 newClassStyles |= CS_DBLCLKS; 5865 } else { 5866 newClassStyles &= ~CS_DBLCLKS; 5867 } 5868 5869 /+ 5870 if(ctrlStyle & ControlStyles.RESIZE_REDRAW) { 5871 newClassStyles |= CS_HREDRAW | CS_VREDRAW; 5872 } else { 5873 newClassStyles &= ~(CS_HREDRAW | CS_VREDRAW); 5874 } 5875 +/ 5876 5877 /+ 5878 if(ctrlStyle & ControlStyles.SELECTABLE) { 5879 newWndStyles |= WS_TABSTOP; 5880 } else { 5881 newWndStyles &= ~WS_TABSTOP; 5882 } 5883 +/ 5884 5885 _classStyle(newClassStyles); 5886 _style(newWndStyles); 5887 } 5888 5889 5890 5891 final bool getTopLevel() { 5892 // return GetParent(hwnd) == HWND.init; 5893 return wparent is null; 5894 } 5895 5896 5897 package final void alayout(Control ctrl, bool vcheck = true) { 5898 if(vcheck && !visible) { 5899 return; 5900 } 5901 5902 if(cbits & CBits.IN_LAYOUT) { 5903 return; 5904 } 5905 5906 //if(_allowLayout) 5907 if(!_disallowLayout) { 5908 //cprintf("alayout\n"); 5909 scope LayoutEventArgs lea = new LayoutEventArgs(ctrl); 5910 onLayout(lea); 5911 } 5912 } 5913 5914 5915 // Z-order of controls has changed. 5916 package final void vchanged() { 5917 // Z-order can't change if it's not created or invisible. 5918 //if(!isHandleCreated || !visible) 5919 // return; 5920 5921 version(RADIO_GROUP_LAYOUT) { 5922 //cprintf("vchanged\n"); 5923 5924 bool foundRadio = false; 5925 5926 foreach(Control ctrl; ccollection) { 5927 if(!ctrl.visible) { 5928 continue; 5929 } 5930 5931 if(ctrl._rtype() & 1) { // Radio type. 5932 LONG wlg; 5933 wlg = ctrl._style(); 5934 if(foundRadio) { 5935 if(wlg & WS_GROUP) 5936 //ctrl._style(wlg & ~WS_GROUP); 5937 { 5938 ctrl._style(wlg & ~(WS_GROUP | WS_TABSTOP)); 5939 } 5940 } else { 5941 foundRadio = true; 5942 5943 if(!(wlg & WS_GROUP)) 5944 //ctrl._style(wlg | WS_GROUP); 5945 { 5946 ctrl._style(wlg | WS_GROUP | WS_TABSTOP); 5947 } 5948 } 5949 } else { 5950 // Found non-radio so reset group. 5951 // Update: only reset group if found ctrl with WS_EX_CONTROLPARENT. 5952 // TODO: check if correct implementation. 5953 if(ctrl._exStyle() & WS_EX_CONTROLPARENT) { 5954 foundRadio = false; 5955 } 5956 } 5957 } 5958 } 5959 } 5960 5961 5962 5963 // Called after adding the control to a container. 5964 protected void initLayout() { 5965 assert(wparent !is null); 5966 if(visible && created) { // ? 5967 wparent.vchanged(); 5968 wparent.alayout(this); 5969 } 5970 } 5971 5972 5973 5974 protected void onLayout(LayoutEventArgs lea) { 5975 // Note: exception could cause failure to restore. 5976 //suspendLayout(); 5977 cbits |= CBits.IN_LAYOUT; 5978 5979 debug(EVENT_PRINT) { 5980 cprintf("{ Event: onLayout - Control %.*s }\n", name); 5981 } 5982 5983 Rect area; 5984 area = displayRectangle; 5985 5986 foreach(Control ctrl; ccollection) { 5987 if(!ctrl.visible || !ctrl.created) { 5988 continue; 5989 } 5990 if(ctrl._rtype() & (2 | 4)) { // Mdichild | Tabpage 5991 continue; 5992 } 5993 5994 //Rect prevctrlbounds; 5995 //prevctrlbounds = ctrl.bounds; 5996 //ctrl.suspendLayout(); // Note: exception could cause failure to restore. 5997 switch(ctrl.sdock) { 5998 case DockStyle.NONE: 5999 /+ 6000 if(ctrl.anch & (AnchorStyles.RIGHT | AnchorStyles.BOTTOM)) { // If none of these are set, no point in doing any anchor code. 6001 Rect newb; 6002 newb = ctrl.bounds; 6003 if(ctrl.anch & AnchorStyles.RIGHT) { 6004 if(ctrl.anch & AnchorStyles.LEFT) { 6005 newb.width += bounds.width - originalBounds.width; 6006 } else { 6007 newb.x += bounds.width - originalBounds.width; 6008 } 6009 } 6010 if(ctrl.anch & AnchorStyles.BOTTOM) { 6011 if(ctrl.anch & AnchorStyles.LEFT) { 6012 newb.height += bounds.height - originalBounds.height; 6013 } else { 6014 newb.y += bounds.height - originalBounds.height; 6015 } 6016 } 6017 if(newb != ctrl.bounds) { 6018 ctrl.bounds = newb; 6019 } 6020 } 6021 +/ 6022 break; 6023 6024 case DockStyle.LEFT: 6025 ctrl.setBoundsCore(area.x, area.y, 0, area.height, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.HEIGHT)); 6026 area.x = area.x + ctrl.width; 6027 area.width = area.width - ctrl.width; 6028 break; 6029 6030 case DockStyle.TOP: 6031 ctrl.setBoundsCore(area.x, area.y, area.width, 0, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.WIDTH)); 6032 area.y = area.y + ctrl.height; 6033 area.height = area.height - ctrl.height; 6034 break; 6035 6036 case DockStyle.FILL: 6037 //ctrl.bounds(Rect(area.x, area.y, area.width, area.height)); 6038 ctrl.bounds = area; 6039 // area = ? 6040 break; 6041 6042 case DockStyle.BOTTOM: 6043 ctrl.setBoundsCore(area.x, area.bottom - ctrl.height, area.width, 0, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.WIDTH)); 6044 area.height = area.height - ctrl.height; 6045 break; 6046 6047 case DockStyle.RIGHT: 6048 ctrl.setBoundsCore(area.right - ctrl.width, area.y, 0, area.height, cast(BoundsSpecified)(BoundsSpecified.LOCATION | BoundsSpecified.HEIGHT)); 6049 area.width = area.width - ctrl.width; 6050 break; 6051 6052 default: 6053 assert(0); 6054 } 6055 //ctrl.resumeLayout(true); 6056 //ctrl.resumeLayout(prevctrlbounds != ctrl.bounds); 6057 } 6058 6059 layout(this, lea); 6060 6061 //resumeLayout(false); 6062 cbits &= ~CBits.IN_LAYOUT; 6063 } 6064 6065 6066 /+ 6067 // Not sure what to do here. 6068 deprecated bool isInputChar(char charCode) { 6069 return false; 6070 } 6071 +/ 6072 6073 6074 6075 void setVisibleCore(bool byes) { 6076 if(isHandleCreated) { 6077 //wstyle = GetWindowLongA(hwnd, GWL_STYLE); 6078 if(visible == byes) { 6079 return; 6080 } 6081 6082 //ShowWindow(hwnd, byes ? SW_SHOW : SW_HIDE); 6083 if(byes) { 6084 doShow(); 6085 } else { 6086 doHide(); 6087 } 6088 } else { 6089 if(byes) { 6090 cbits |= CBits.VISIBLE; 6091 wstyle |= WS_VISIBLE; 6092 createControl(); 6093 } else { 6094 cbits &= ~CBits.VISIBLE; 6095 wstyle &= ~WS_VISIBLE; 6096 return; // Not created and being hidden.. 6097 } 6098 } 6099 } 6100 6101 6102 package final bool _wantTabKey() { 6103 if(ctrlStyle & ControlStyles.WANT_TAB_KEY) { 6104 return true; 6105 } 6106 return false; 6107 } 6108 6109 6110 6111 // Return true if processed. 6112 protected bool processKeyEventArgs(ref Message msg) { 6113 switch(msg.msg) { 6114 case WM_KEYDOWN: { 6115 scope KeyEventArgs kea = new KeyEventArgs(cast(Keys)(msg.wParam | modifierKeys)); 6116 6117 ushort repeat = msg.lParam & 0xFFFF; // First 16 bits. 6118 for(; repeat; repeat--) { 6119 //kea.handled = false; 6120 onKeyDown(kea); 6121 } 6122 6123 if(kea.handled) { 6124 return true; 6125 } 6126 } 6127 break; 6128 6129 case WM_KEYUP: { 6130 // Repeat count is always 1 for key up. 6131 scope KeyEventArgs kea = new KeyEventArgs(cast(Keys)(msg.wParam | modifierKeys)); 6132 onKeyUp(kea); 6133 if(kea.handled) { 6134 return true; 6135 } 6136 } 6137 break; 6138 6139 case WM_CHAR: { 6140 scope KeyPressEventArgs kpea = new KeyPressEventArgs(cast(dchar)msg.wParam, modifierKeys); 6141 onKeyPress(kpea); 6142 if(kpea.handled) { 6143 return true; 6144 } 6145 } 6146 break; 6147 6148 default: 6149 } 6150 6151 defWndProc(msg); 6152 return !msg.result; 6153 } 6154 6155 6156 package final bool _processKeyEventArgs(ref Message msg) { 6157 return processKeyEventArgs(msg); 6158 } 6159 6160 6161 /+ 6162 bool processKeyPreview(ref Message m) { 6163 if(wparent) { 6164 return wparent.processKeyPreview(m); 6165 } 6166 return false; 6167 } 6168 6169 6170 protected bool processDialogChar(dchar charCode) { 6171 if(wparent) { 6172 return wparent.processDialogChar(charCode); 6173 } 6174 return false; 6175 } 6176 +/ 6177 6178 6179 6180 protected bool processMnemonic(dchar charCode) { 6181 return false; 6182 } 6183 6184 6185 package bool _processMnemonic(dchar charCode) { 6186 return processMnemonic(charCode); 6187 } 6188 6189 6190 // Retain DFL 0.9.5 compatibility. 6191 public deprecated void setDFL095() { 6192 version(SET_DFL_095) { 6193 pragma(msg, "DFL: DFL 0.9.5 compatibility set at compile time"); 6194 } 6195 else { 6196 //_compat = CCompat.DFL095; 6197 Application.setCompat(DflCompat.CONTROL_RECREATE_095); 6198 } 6199 } 6200 6201 private enum CCompat: ubyte { 6202 NONE = 0, 6203 DFL095 = 1, 6204 } 6205 6206 version(SET_DFL_095) 6207 package enum _compat = CCompat.DFL095; 6208 else version(DFL_NO_COMPAT) 6209 package enum _compat = CCompat.NONE; 6210 else 6211 package @property CCompat _compat() { // getter 6212 if(Application._compat & DflCompat.CONTROL_RECREATE_095) { 6213 return CCompat.DFL095; 6214 } return CCompat.NONE; 6215 } 6216 6217 6218 package final void _crecreate() { 6219 if(CCompat.DFL095 != _compat) { 6220 if(!recreatingHandle) { 6221 recreateHandle(); 6222 } 6223 } 6224 } 6225 6226 6227 package: 6228 HWND hwnd; 6229 //AnchorStyles anch = cast(AnchorStyles)(AnchorStyles.TOP | AnchorStyles.LEFT); 6230 //bool cvalidation = true; 6231 version(DFL_NO_MENUS) { 6232 } 6233 else { 6234 ContextMenu cmenu; 6235 } 6236 DockStyle sdock = DockStyle.NONE; 6237 Dstring _ctrlname; 6238 Object otag; 6239 Color backc, forec; 6240 Rect wrect; 6241 //Rect oldwrect; 6242 Size wclientsz; 6243 Cursor wcurs; 6244 Font wfont; 6245 Control wparent; 6246 Region wregion; 6247 ControlCollection ccollection; 6248 Dstring wtext; // After creation, this isn't used unless ControlStyles.CACHE_TEXT. 6249 ControlStyles ctrlStyle = ControlStyles.STANDARD_CLICK | ControlStyles.STANDARD_DOUBLE_CLICK /+ | ControlStyles.RESIZE_REDRAW +/ ; 6250 HBRUSH _hbrBg; 6251 RightToLeft rtol = RightToLeft.INHERIT; 6252 uint _disallowLayout = 0; 6253 6254 version(DFL_NO_DRAG_DROP) {} else { 6255 DropTarget droptarget = null; 6256 } 6257 6258 // Note: WS_VISIBLE is not reliable. 6259 LONG wstyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; // Child, visible and enabled by default. 6260 LONG wexstyle; 6261 LONG wclassStyle = WNDCLASS_STYLE; 6262 6263 6264 enum CBits: uint { 6265 NONE = 0x0, 6266 MENTER = 0x1, // Is mouse entered? Only valid if -trackMouseEvent- is non-null. 6267 KILLING = 0x2, 6268 OWNED = 0x4, 6269 //ALLOW_LAYOUT = 0x8, 6270 CLICKING = 0x10, 6271 NEED_CALC_SIZE = 0x20, 6272 SZDRAW = 0x40, 6273 OWNEDBG = 0x80, 6274 HANDLE_CREATED = 0x100, // debug only 6275 SW_SHOWN = 0x200, 6276 SW_HIDDEN = 0x400, 6277 CREATED = 0x800, 6278 NEED_INIT_LAYOUT = 0x1000, 6279 IN_LAYOUT = 0x2000, 6280 FVISIBLE = 0x4000, 6281 VISIBLE = 0x8000, 6282 NOCLOSING = 0x10000, 6283 ASCROLL = 0x20000, 6284 ASCALE = 0x40000, 6285 FORM = 0x80000, 6286 RECREATING = 0x100000, 6287 HAS_LAYOUT = 0x200000, 6288 VSTYLE = 0x400000, // If not forced off. 6289 FORMLOADED = 0x800000, // If not forced off. 6290 ENABLED = 0x1000000, // Enabled state, not considering the parent. 6291 } 6292 6293 //CBits cbits = CBits.ALLOW_LAYOUT; 6294 //CBits cbits = CBits.NONE; 6295 CBits cbits = CBits.VISIBLE | CBits.VSTYLE | CBits.ENABLED; 6296 6297 6298 final: 6299 6300 @property void menter(bool byes) { // setter 6301 if(byes) { 6302 cbits |= CBits.MENTER; 6303 } else { 6304 cbits &= ~CBits.MENTER; 6305 } 6306 } 6307 @property bool menter() { // getter 6308 return (cbits & CBits.MENTER) != 0; 6309 } 6310 6311 @property void killing(bool byes) // setter 6312 //{ if(byes) cbits |= CBits.KILLING; else cbits &= ~CBits.KILLING; } 6313 { 6314 assert(byes); 6315 if(byes) { 6316 cbits |= CBits.KILLING; 6317 } 6318 } 6319 @property bool killing() { // getter 6320 return (cbits & CBits.KILLING) != 0; 6321 } 6322 6323 @property void owned(bool byes) { // setter 6324 if(byes) { 6325 cbits |= CBits.OWNED; 6326 } else { 6327 cbits &= ~CBits.OWNED; 6328 } 6329 } 6330 @property bool owned() { // getter 6331 return (cbits & CBits.OWNED) != 0; 6332 } 6333 6334 /+ 6335 void _allowLayout(bool byes) { // setter 6336 if(byes) { 6337 cbits |= CBits.ALLOW_LAYOUT; 6338 } else { 6339 cbits &= ~CBits.ALLOW_LAYOUT; 6340 } 6341 } 6342 bool _allowLayout() { // getter 6343 return (cbits & CBits.ALLOW_LAYOUT) != 0; 6344 } 6345 +/ 6346 6347 @property void _clicking(bool byes) { // setter 6348 if(byes) { 6349 cbits |= CBits.CLICKING; 6350 } else { 6351 cbits &= ~CBits.CLICKING; 6352 } 6353 } 6354 @property bool _clicking() { // getter 6355 return (cbits & CBits.CLICKING) != 0; 6356 } 6357 6358 @property void needCalcSize(bool byes) { // setter 6359 if(byes) { 6360 cbits |= CBits.NEED_CALC_SIZE; 6361 } else { 6362 cbits &= ~CBits.NEED_CALC_SIZE; 6363 } 6364 } 6365 @property bool needCalcSize() { // getter 6366 return (cbits & CBits.NEED_CALC_SIZE) != 0; 6367 } 6368 6369 @property void szdraw(bool byes) { // setter 6370 if(byes) { 6371 cbits |= CBits.SZDRAW; 6372 } else { 6373 cbits &= ~CBits.SZDRAW; 6374 } 6375 } 6376 @property bool szdraw() { // getter 6377 return (cbits & CBits.SZDRAW) != 0; 6378 } 6379 6380 @property void ownedbg(bool byes) { // setter 6381 if(byes) { 6382 cbits |= CBits.OWNEDBG; 6383 } else { 6384 cbits &= ~CBits.OWNEDBG; 6385 } 6386 } 6387 @property bool ownedbg() { // getter 6388 return (cbits & CBits.OWNEDBG) != 0; 6389 } 6390 6391 debug { 6392 @property void _handlecreated(bool byes) { // setter 6393 if(byes) { 6394 cbits |= CBits.HANDLE_CREATED; 6395 } else { 6396 cbits &= ~CBits.HANDLE_CREATED; 6397 } 6398 } 6399 @property bool _handlecreated() { // getter 6400 return (cbits & CBits.HANDLE_CREATED) != 0; 6401 } 6402 } 6403 6404 6405 @property LONG _exStyle() { 6406 // return GetWindowLongA(hwnd, GWL_EXSTYLE); 6407 return wexstyle; 6408 } 6409 6410 6411 @property void _exStyle(LONG wl) { 6412 if(isHandleCreated) { 6413 SetWindowLongA(hwnd, GWL_EXSTYLE, wl); 6414 } 6415 6416 wexstyle = wl; 6417 } 6418 6419 6420 @property LONG _style() { 6421 // return GetWindowLongA(hwnd, GWL_STYLE); 6422 return wstyle; 6423 } 6424 6425 6426 @property void _style(LONG wl) { 6427 if(isHandleCreated) { 6428 SetWindowLongA(hwnd, GWL_STYLE, wl); 6429 } 6430 6431 wstyle = wl; 6432 } 6433 6434 6435 @property HBRUSH hbrBg() { // getter 6436 if(_hbrBg) { 6437 return _hbrBg; 6438 } 6439 if(backc == Color.empty && parent && backColor == parent.backColor) { 6440 ownedbg = false; 6441 _hbrBg = parent.hbrBg; 6442 return _hbrBg; 6443 } 6444 hbrBg = backColor.createBrush(); // Call hbrBg's setter and set ownedbg. 6445 return _hbrBg; 6446 } 6447 6448 6449 @property void hbrBg(HBRUSH hbr) // setter 6450 in { 6451 if(hbr) { 6452 assert(!_hbrBg); 6453 } 6454 } 6455 body { 6456 _hbrBg = hbr; 6457 ownedbg = true; 6458 } 6459 6460 6461 void deleteThisBackgroundBrush() { 6462 if(_hbrBg) { 6463 if(ownedbg) { 6464 DeleteObject(_hbrBg); 6465 } 6466 _hbrBg = HBRUSH.init; 6467 } 6468 } 6469 6470 6471 LRESULT defwproc(UINT msg, WPARAM wparam, LPARAM lparam) { 6472 //return DefWindowProcA(hwnd, msg, wparam, lparam); 6473 return dfl.internal.utf.defWindowProc(hwnd, msg, wparam, lparam); 6474 } 6475 6476 6477 LONG _fetchClassLong() { 6478 return GetClassLongA(hwnd, GCL_STYLE); 6479 } 6480 6481 6482 LONG _classStyle() { 6483 // return GetClassLongA(hwnd, GCL_STYLE); 6484 // return wclassStyle; 6485 6486 if(isHandleCreated) { 6487 // Always fetch because it's not guaranteed to be accurate. 6488 wclassStyle = _fetchClassLong(); 6489 } 6490 6491 return wclassStyle; 6492 } 6493 6494 6495 package void _classStyle(LONG cl) { 6496 if(isHandleCreated) { 6497 SetClassLongA(hwnd, GCL_STYLE, cl); 6498 } 6499 6500 wclassStyle = cl; 6501 } 6502 } 6503 6504 6505 package abstract class ControlSuperClass: Control { // dapi.d 6506 // Call previous wndProc(). 6507 abstract protected void prevWndProc(ref Message msg); 6508 6509 6510 protected override void wndProc(ref Message msg) { 6511 switch(msg.msg) { 6512 case WM_PAINT: { 6513 RECT uprect; 6514 //GetUpdateRect(hwnd, &uprect, true); 6515 //onInvalidated(new InvalidateEventArgs(Rect(&uprect))); 6516 6517 //if(!msg.wParam) 6518 GetUpdateRect(hwnd, &uprect, false); // Preserve. 6519 6520 prevWndProc(msg); 6521 6522 // Now fake a normal paint event... 6523 6524 scope Graphics gpx = new CommonGraphics(hwnd, GetDC(hwnd)); 6525 //scope Graphics gpx = new CommonGraphics(hwnd, msg.wParam ? cast(HDC)msg.wParam : GetDC(hwnd), msg.wParam ? false : true); 6526 HRGN hrgn; 6527 6528 hrgn = CreateRectRgnIndirect(&uprect); 6529 SelectClipRgn(gpx.handle, hrgn); 6530 DeleteObject(hrgn); 6531 6532 scope PaintEventArgs pea = new PaintEventArgs(gpx, Rect(&uprect)); 6533 6534 // Can't erase the background now, Windows just painted.. 6535 //if(ps.fErase) 6536 //{ 6537 // prepareDc(gpx.handle); 6538 // onPaintBackground(pea); 6539 //} 6540 6541 prepareDc(gpx.handle); 6542 onPaint(pea); 6543 } 6544 break; 6545 6546 case WM_PRINTCLIENT: { 6547 prevWndProc(msg); 6548 6549 scope Graphics gpx = new CommonGraphics(hwnd, GetDC(hwnd)); 6550 scope PaintEventArgs pea = new PaintEventArgs(gpx, 6551 Rect(Point(0, 0), wclientsz)); 6552 6553 prepareDc(pea.graphics.handle); 6554 onPaint(pea); 6555 } 6556 break; 6557 6558 case WM_PRINT: 6559 Control.defWndProc(msg); 6560 break; 6561 6562 case WM_ERASEBKGND: 6563 Control.wndProc(msg); 6564 break; 6565 6566 case WM_NCACTIVATE: 6567 case WM_NCCALCSIZE: 6568 case WM_NCCREATE: 6569 case WM_NCPAINT: 6570 prevWndProc(msg); 6571 break; 6572 6573 case WM_KEYDOWN: 6574 case WM_KEYUP: 6575 case WM_CHAR: 6576 case WM_SYSKEYDOWN: 6577 case WM_SYSKEYUP: 6578 case WM_SYSCHAR: 6579 //case WM_IMECHAR: 6580 super.wndProc(msg); 6581 return; 6582 6583 default: 6584 prevWndProc(msg); 6585 super.wndProc(msg); 6586 } 6587 } 6588 6589 6590 override void defWndProc(ref Message m) { 6591 switch(m.msg) { 6592 case WM_KEYDOWN: 6593 case WM_KEYUP: 6594 case WM_CHAR: 6595 case WM_SYSKEYDOWN: 6596 case WM_SYSKEYUP: 6597 case WM_SYSCHAR: 6598 //case WM_IMECHAR: // ? 6599 prevWndProc(m); 6600 break; 6601 6602 default: 6603 } 6604 } 6605 6606 6607 protected override void onPaintBackground(PaintEventArgs pea) { 6608 Message msg; 6609 6610 msg.hWnd = handle; 6611 msg.msg = WM_ERASEBKGND; 6612 msg.wParam = cast(WPARAM)pea.graphics.handle; 6613 6614 prevWndProc(msg); 6615 6616 // Don't paint the background twice. 6617 //super.onPaintBackground(pea); 6618 6619 // Event ? 6620 //paintBackground(this, pea); 6621 } 6622 } 6623 6624 6625 6626 class ScrollableControl: Control { // docmain 6627 // /// 6628 deprecated void autoScroll(bool byes) { // setter 6629 if(byes) { 6630 cbits |= CBits.ASCROLL; 6631 } else { 6632 cbits &= ~CBits.ASCROLL; 6633 } 6634 } 6635 6636 // /// ditto 6637 deprecated bool autoScroll() { // getter 6638 return (cbits & CBits.ASCROLL) == CBits.ASCROLL; 6639 } 6640 6641 6642 // /// 6643 deprecated final void autoScrollMargin(Size sz) { // setter 6644 //scrollmargin = sz; 6645 } 6646 6647 // /// ditto 6648 deprecated final Size autoScrollMargin() { // getter 6649 //return scrollmargin; 6650 return Size(0, 0); 6651 } 6652 6653 6654 // /// 6655 deprecated final void autoScrollMinSize(Size sz) { // setter 6656 //scrollmin = sz; 6657 } 6658 6659 // /// ditto 6660 deprecated final Size autoScrollMinSize() { // getter 6661 //return scrollmin; 6662 return Size(0, 0); 6663 } 6664 6665 6666 // /// 6667 deprecated final void autoScrollPosition(Point pt) { // setter 6668 //autoscrollpos = pt; 6669 } 6670 6671 // /// ditto 6672 deprecated final Point autoScrollPosition() { // getter 6673 //return autoscrollpos; 6674 return Point(0, 0); 6675 } 6676 6677 6678 6679 final @property Size autoScaleBaseSize() { // getter 6680 return autossz; 6681 } 6682 6683 /// ditto 6684 final @property void autoScaleBaseSize(Size newSize) // setter 6685 in { 6686 assert(newSize.width > 0); 6687 assert(newSize.height > 0); 6688 } 6689 body { 6690 autossz = newSize; 6691 } 6692 6693 6694 6695 final @property void autoScale(bool byes) { // setter 6696 if(byes) { 6697 cbits |= CBits.ASCALE; 6698 } else { 6699 cbits &= ~CBits.ASCALE; 6700 } 6701 } 6702 6703 /// ditto 6704 final @property bool autoScale() { // getter 6705 return (cbits & CBits.ASCALE) == CBits.ASCALE; 6706 } 6707 6708 6709 final @property Point scrollPosition() { // getter 6710 return Point(xspos, yspos); 6711 } 6712 6713 6714 static Size calcScale(Size area, Size toScale, Size fromScale) // package 6715 in { 6716 assert(fromScale.width); 6717 assert(fromScale.height); 6718 } 6719 body { 6720 area.width = cast(int)(cast(float)area.width / cast(float)fromScale.width * cast(float)toScale.width); 6721 area.height = cast(int)(cast(float)area.height / cast(float)fromScale.height * cast(float)toScale.height); 6722 return area; 6723 } 6724 6725 6726 Size calcScale(Size area, Size toScale) { // package 6727 return calcScale(area, toScale, DEFAULT_SCALE); 6728 } 6729 6730 6731 final void _scale(Size toScale) { // package 6732 bool first = true; 6733 6734 // Note: doesn't get to-scale for nested scrollable-controls. 6735 void xscale(Control c, Size fromScale) { 6736 c.suspendLayout(); 6737 6738 if(first) { 6739 first = false; 6740 c.size = calcScale(c.size, toScale, fromScale); 6741 } else { 6742 Point pt; 6743 Size sz; 6744 sz = calcScale(Size(c.left, c.top), toScale, fromScale); 6745 pt = Point(sz.width, sz.height); 6746 sz = calcScale(c.size, toScale, fromScale); 6747 c.bounds = Rect(pt, sz); 6748 } 6749 6750 if(c.hasChildren) { 6751 ScrollableControl scc; 6752 foreach(Control cc; c.controls) { 6753 scc = cast(ScrollableControl)cc; 6754 if(scc) { 6755 if(scc.autoScale) { // ? 6756 xscale(scc, scc.autoScaleBaseSize); 6757 scc.autoScaleBaseSize = toScale; 6758 } 6759 } else { 6760 xscale(cc, fromScale); 6761 } 6762 } 6763 } 6764 6765 //c.resumeLayout(true); 6766 c.resumeLayout(false); // Should still be perfectly proportionate if it was properly laid out before scaling. 6767 } 6768 6769 6770 xscale(this, autoScaleBaseSize); 6771 autoScaleBaseSize = toScale; 6772 } 6773 6774 6775 final void _scale() { // package 6776 return _scale(getAutoScaleSize()); 6777 } 6778 6779 6780 protected override void onControlAdded(ControlEventArgs ea) { 6781 super.onControlAdded(ea); 6782 6783 if(created) // ? 6784 if(isHandleCreated) { 6785 auto sc = cast(ScrollableControl)ea.control; 6786 if(sc) { 6787 if(sc.autoScale) { 6788 sc._scale(); 6789 } 6790 } else { 6791 if(autoScale) { 6792 _scale(); 6793 } 6794 } 6795 } 6796 } 6797 6798 6799 //override final Rect displayRectangle() // getter 6800 override @property Rect displayRectangle() { // getter 6801 Rect result = clientRectangle; 6802 6803 // Subtract dock padding. 6804 result.x = result.x + dpad.left; 6805 result.width = result.width - dpad.right - dpad.left; 6806 result.y = result.y + dpad.top; 6807 result.height = result.height - dpad.bottom - dpad.top; 6808 6809 // Add scroll width. 6810 if(scrollSize.width > clientSize.width) { 6811 result.width = result.width + (scrollSize.width - clientSize.width); 6812 } 6813 if(scrollSize.height > clientSize.height) { 6814 result.height = result.height + (scrollSize.height - clientSize.height); 6815 } 6816 6817 // Adjust scroll position. 6818 result.location = Point(result.location.x - scrollPosition.x, result.location.y - scrollPosition.y); 6819 6820 return result; 6821 } 6822 6823 6824 6825 final @property void scrollSize(Size sz) { // setter 6826 scrollsz = sz; 6827 6828 _fixScrollBounds(); // Implies _adjustScrollSize(). 6829 } 6830 6831 /// ditto 6832 final @property Size scrollSize() { // getter 6833 return scrollsz; 6834 } 6835 6836 6837 6838 class DockPaddingEdges { 6839 private: 6840 6841 int _left, _top, _right, _bottom; 6842 int _all; 6843 //package void delegate() changed; 6844 6845 6846 final: 6847 6848 void changed() { 6849 dpadChanged(); 6850 } 6851 6852 6853 public: 6854 6855 6856 @property void all(int x) { // setter 6857 _bottom = _right = _top = _left = _all = x; 6858 6859 changed(); 6860 } 6861 6862 /// ditto 6863 final @property int all() { // getter 6864 return _all; 6865 } 6866 6867 /// ditto 6868 @property void left(int x) { // setter 6869 _left = x; 6870 6871 changed(); 6872 } 6873 6874 /// ditto 6875 @property int left() { // getter 6876 return _left; 6877 } 6878 6879 /// ditto 6880 @property void top(int x) { // setter 6881 _top = x; 6882 6883 changed(); 6884 } 6885 6886 /// ditto 6887 @property int top() { // getter 6888 return _top; 6889 } 6890 6891 /// ditto 6892 @property void right(int x) { // setter 6893 _right = x; 6894 6895 changed(); 6896 } 6897 6898 /// ditto 6899 @property int right() { // getter 6900 return _right; 6901 } 6902 6903 /// ditto 6904 @property void bottom(int x) { // setter 6905 _bottom = x; 6906 6907 changed(); 6908 } 6909 6910 /// ditto 6911 @property int bottom() { // getter 6912 return _bottom; 6913 } 6914 } 6915 6916 6917 6918 final @property DockPaddingEdges dockPadding() { // getter 6919 return dpad; 6920 } 6921 6922 6923 deprecated final void setAutoScrollMargin(int x, int y) { 6924 // 6925 } 6926 6927 6928 this() { 6929 super(); 6930 _init(); 6931 } 6932 6933 6934 enum DEFAULT_SCALE = Size(5, 13); 6935 6936 6937 final @property void hScroll(bool byes) { // setter 6938 LONG wl = _style(); 6939 if(byes) { 6940 wl |= WS_HSCROLL; 6941 } else { 6942 wl &= ~WS_HSCROLL; 6943 } 6944 _style(wl); 6945 6946 if(isHandleCreated) { 6947 redrawEntire(); 6948 } 6949 } 6950 6951 6952 /// ditto 6953 final @property bool hScroll() { // getter 6954 return (_style() & WS_HSCROLL) != 0; 6955 } 6956 6957 6958 6959 final @property void vScroll(bool byes) { // setter 6960 LONG wl = _style(); 6961 if(byes) { 6962 wl |= WS_VSCROLL; 6963 } else { 6964 wl &= ~WS_VSCROLL; 6965 } 6966 _style(wl); 6967 6968 if(isHandleCreated) { 6969 redrawEntire(); 6970 } 6971 } 6972 6973 /// ditto 6974 final @property bool vScroll() { // getter 6975 return (_style() & WS_VSCROLL) != 0; 6976 } 6977 6978 6979 protected: 6980 6981 6982 /+ 6983 override void onLayout(LayoutEventArgs lea) { 6984 // ... 6985 super.onLayout(lea); 6986 } 6987 +/ 6988 6989 6990 /+ 6991 override void scaleCore(float width, float height) { 6992 // Might not want to call super.scaleCore(). 6993 } 6994 +/ 6995 6996 6997 override void wndProc(ref Message m) { 6998 switch(m.msg) { 6999 case WM_VSCROLL: { 7000 SCROLLINFO si = void; 7001 si.cbSize = SCROLLINFO.sizeof; 7002 si.fMask = SIF_ALL; 7003 if(GetScrollInfo(m.hWnd, SB_VERT, &si)) { 7004 int delta, maxp; 7005 maxp = scrollSize.height - clientSize.height; 7006 switch(LOWORD(m.wParam)) { 7007 case SB_LINEDOWN: 7008 if(yspos >= maxp) { 7009 return; 7010 } 7011 delta = maxp - yspos; 7012 if(autossz.height < delta) { 7013 delta = autossz.height; 7014 } 7015 break; 7016 case SB_LINEUP: 7017 if(yspos <= 0) { 7018 return; 7019 } 7020 delta = yspos; 7021 if(autossz.height < delta) { 7022 delta = autossz.height; 7023 } 7024 delta = -delta; 7025 break; 7026 case SB_PAGEDOWN: 7027 if(yspos >= maxp) { 7028 return; 7029 } 7030 if(yspos >= maxp) { 7031 return; 7032 } 7033 delta = maxp - yspos; 7034 if(clientSize.height < delta) { 7035 delta = clientSize.height; 7036 } 7037 break; 7038 case SB_PAGEUP: 7039 if(yspos <= 0) { 7040 return; 7041 } 7042 delta = yspos; 7043 if(clientSize.height < delta) { 7044 delta = clientSize.height; 7045 } 7046 delta = -delta; 7047 break; 7048 case SB_THUMBTRACK: 7049 case SB_THUMBPOSITION: 7050 //delta = cast(int)HIWORD(m.wParam) - yspos; // Limited to 16-bits. 7051 delta = si.nTrackPos - yspos; 7052 break; 7053 case SB_BOTTOM: 7054 delta = maxp - yspos; 7055 break; 7056 case SB_TOP: 7057 delta = -yspos; 7058 break; 7059 default: 7060 } 7061 yspos += delta; 7062 SetScrollPos(m.hWnd, SB_VERT, yspos, TRUE); 7063 ScrollWindow(m.hWnd, 0, -delta, null, null); 7064 } 7065 } 7066 break; 7067 7068 case WM_HSCROLL: { 7069 SCROLLINFO si = void; 7070 si.cbSize = SCROLLINFO.sizeof; 7071 si.fMask = SIF_ALL; 7072 if(GetScrollInfo(m.hWnd, SB_HORZ, &si)) { 7073 int delta, maxp; 7074 maxp = scrollSize.width - clientSize.width; 7075 switch(LOWORD(m.wParam)) { 7076 case SB_LINERIGHT: 7077 if(xspos >= maxp) { 7078 return; 7079 } 7080 delta = maxp - xspos; 7081 if(autossz.width < delta) { 7082 delta = autossz.width; 7083 } 7084 break; 7085 case SB_LINELEFT: 7086 if(xspos <= 0) { 7087 return; 7088 } 7089 delta = xspos; 7090 if(autossz.width < delta) { 7091 delta = autossz.width; 7092 } 7093 delta = -delta; 7094 break; 7095 case SB_PAGERIGHT: 7096 if(xspos >= maxp) { 7097 return; 7098 } 7099 if(xspos >= maxp) { 7100 return; 7101 } 7102 delta = maxp - xspos; 7103 if(clientSize.width < delta) { 7104 delta = clientSize.width; 7105 } 7106 break; 7107 case SB_PAGELEFT: 7108 if(xspos <= 0) { 7109 return; 7110 } 7111 delta = xspos; 7112 if(clientSize.width < delta) { 7113 delta = clientSize.width; 7114 } 7115 delta = -delta; 7116 break; 7117 case SB_THUMBTRACK: 7118 case SB_THUMBPOSITION: 7119 //delta = cast(int)HIWORD(m.wParam) - xspos; // Limited to 16-bits. 7120 delta = si.nTrackPos - xspos; 7121 break; 7122 case SB_RIGHT: 7123 delta = maxp - xspos; 7124 break; 7125 case SB_LEFT: 7126 delta = -xspos; 7127 break; 7128 default: 7129 } 7130 xspos += delta; 7131 SetScrollPos(m.hWnd, SB_HORZ, xspos, TRUE); 7132 ScrollWindow(m.hWnd, -delta, 0, null, null); 7133 } 7134 } 7135 break; 7136 7137 default: 7138 } 7139 7140 super.wndProc(m); 7141 } 7142 7143 7144 override void onMouseWheel(MouseEventArgs ea) { 7145 int maxp = scrollSize.height - clientSize.height; 7146 int delta; 7147 7148 UINT wlines; 7149 if(!SystemParametersInfoA(SPI_GETWHEELSCROLLLINES, 0, &wlines, 0)) { 7150 wlines = 3; 7151 } 7152 7153 if(ea.delta < 0) { 7154 if(yspos < maxp) { 7155 delta = maxp - yspos; 7156 if(autossz.height * wlines < delta) { 7157 delta = autossz.height * wlines; 7158 } 7159 7160 yspos += delta; 7161 SetScrollPos(hwnd, SB_VERT, yspos, TRUE); 7162 ScrollWindow(hwnd, 0, -delta, null, null); 7163 } 7164 } else { 7165 if(yspos > 0) { 7166 delta = yspos; 7167 if(autossz.height * wlines < delta) { 7168 delta = autossz.height * wlines; 7169 } 7170 delta = -delta; 7171 7172 yspos += delta; 7173 SetScrollPos(hwnd, SB_VERT, yspos, TRUE); 7174 ScrollWindow(hwnd, 0, -delta, null, null); 7175 } 7176 } 7177 7178 super.onMouseWheel(ea); 7179 } 7180 7181 7182 override void onHandleCreated(EventArgs ea) { 7183 xspos = 0; 7184 yspos = 0; 7185 7186 super.onHandleCreated(ea); 7187 7188 //_adjustScrollSize(FALSE); 7189 if(hScroll || vScroll) { 7190 _adjustScrollSize(FALSE); 7191 recalcEntire(); // Need to recalc frame. 7192 } 7193 } 7194 7195 7196 override void onVisibleChanged(EventArgs ea) { 7197 if(visible) { 7198 _adjustScrollSize(FALSE); 7199 } 7200 7201 super.onVisibleChanged(ea); 7202 } 7203 7204 7205 private void _fixScrollBounds() { 7206 if(hScroll || vScroll) { 7207 int ydiff = 0, xdiff = 0; 7208 7209 if(yspos > scrollSize.height - clientSize.height) { 7210 ydiff = (clientSize.height + yspos) - scrollSize.height; 7211 yspos -= ydiff; 7212 if(yspos < 0) { 7213 ydiff += yspos; 7214 yspos = 0; 7215 } 7216 } 7217 7218 if(xspos > scrollSize.width - clientSize.width) { 7219 xdiff = (clientSize.width + xspos) - scrollSize.width; 7220 xspos -= xdiff; 7221 if(xspos < 0) { 7222 xdiff += xspos; 7223 xspos = 0; 7224 } 7225 } 7226 7227 if(isHandleCreated) { 7228 if(xdiff || ydiff) { 7229 ScrollWindow(hwnd, xdiff, ydiff, null, null); 7230 } 7231 7232 _adjustScrollSize(); 7233 } 7234 } 7235 } 7236 7237 7238 override void onResize(EventArgs ea) { 7239 super.onResize(ea); 7240 7241 _fixScrollBounds(); 7242 } 7243 7244 7245 private: 7246 //Size scrollmargin, scrollmin; 7247 //Point autoscrollpos; 7248 DockPaddingEdges dpad; 7249 Size autossz = DEFAULT_SCALE; 7250 Size scrollsz = Size(0, 0); 7251 int xspos = 0, yspos = 0; 7252 7253 7254 void _init() { 7255 dpad = new DockPaddingEdges; 7256 //dpad.changed = &dpadChanged; 7257 } 7258 7259 7260 void dpadChanged() { 7261 alayout(this); 7262 } 7263 7264 7265 void _adjustScrollSize(BOOL fRedraw = TRUE) { 7266 assert(isHandleCreated); 7267 7268 if(!hScroll && !vScroll) { 7269 return; 7270 } 7271 7272 SCROLLINFO si; 7273 //if(vScroll) 7274 { 7275 si.cbSize = SCROLLINFO.sizeof; 7276 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; 7277 si.nPos = yspos; 7278 si.nMin = 0; 7279 si.nMax = clientSize.height; 7280 si.nPage = clientSize.height; 7281 if(scrollSize.height > clientSize.height) { 7282 si.nMax = scrollSize.height; 7283 } 7284 if(si.nMax) { 7285 si.nMax--; 7286 } 7287 SetScrollInfo(hwnd, SB_VERT, &si, fRedraw); 7288 } 7289 //if(hScroll) 7290 { 7291 si.cbSize = SCROLLINFO.sizeof; 7292 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; 7293 si.nPos = xspos; 7294 si.nMin = 0; 7295 si.nMax = clientSize.width; 7296 si.nPage = clientSize.width; 7297 if(scrollSize.width > clientSize.width) { 7298 si.nMax = scrollSize.width; 7299 } 7300 if(si.nMax) { 7301 si.nMax--; 7302 } 7303 SetScrollInfo(hwnd, SB_HORZ, &si, fRedraw); 7304 } 7305 } 7306 } 7307 7308 7309 7310 interface IContainerControl { // docmain 7311 7312 @property Control activeControl(); // getter 7313 7314 deprecated void activeControl(Control); // setter 7315 7316 deprecated bool activateControl(Control); 7317 } 7318 7319 7320 7321 class ContainerControl: ScrollableControl, IContainerControl { // docmain 7322 7323 @property Control activeControl() { // getter 7324 /+ 7325 HWND hwfocus, hw; 7326 hw = hwfocus = GetFocus(); 7327 while(hw) { 7328 if(hw == this.hwnd) { 7329 return Control.fromChildHandle(hwfocus); 7330 } 7331 hw = GetParent(hw); 7332 } 7333 return null; 7334 +/ 7335 Control ctrlfocus, ctrl; 7336 ctrl = ctrlfocus = Control.fromChildHandle(GetFocus()); 7337 while(ctrl) { 7338 if(ctrl is this) { 7339 return ctrlfocus; 7340 } 7341 ctrl = ctrl.parent; 7342 } 7343 return null; 7344 } 7345 7346 /// ditto 7347 @property void activeControl(Control ctrl) { // setter 7348 if(!activateControl(ctrl)) { 7349 throw new DflException("Unable to activate control"); 7350 } 7351 } 7352 7353 7354 7355 // Returns true if successfully activated. 7356 final bool activateControl(Control ctrl) { 7357 // Not sure if this is correct. 7358 7359 if(!ctrl.canSelect) { 7360 return false; 7361 } 7362 //if(!SetActiveWindow(ctrl.handle)) 7363 // return false; 7364 ctrl.select(); 7365 return true; 7366 } 7367 7368 7369 7370 final @property Form parentForm() { // getter 7371 Control par; 7372 Form f; 7373 7374 for(par = parent; par; par = par.parent) { 7375 f = cast(Form)par; 7376 if(f) { 7377 return f; 7378 } 7379 } 7380 7381 return null; 7382 } 7383 7384 7385 /+ 7386 final bool validate() { 7387 // ... 7388 } 7389 +/ 7390 7391 7392 this() { 7393 super(); 7394 _init(); 7395 } 7396 7397 7398 /+ 7399 // Used internally. 7400 this(HWND hwnd) { 7401 super(hwnd); 7402 _init(); 7403 } 7404 +/ 7405 7406 7407 private void _init() { 7408 //wexstyle |= WS_EX_CONTROLPARENT; 7409 ctrlStyle |= ControlStyles.CONTAINER_CONTROL; 7410 } 7411 7412 7413 protected: 7414 /+ 7415 override bool processDialogChar(char charCode) { 7416 // Not sure if this is correct. 7417 return false; 7418 } 7419 +/ 7420 7421 7422 /+ 7423 deprecated protected override bool processMnemonic(dchar charCode) { 7424 return false; 7425 } 7426 7427 7428 bool processTabKey(bool forward) { 7429 if(isHandleCreated) { 7430 //SendMessageA(hwnd, WM_NEXTDLGCTL, !forward, 0); 7431 //return true; 7432 select(true, forward); 7433 } 7434 return false; 7435 } 7436 +/ 7437 } 7438 7439 7440 import std.traits, std.typecons; 7441 private template hasLocalAliasing(T...) { 7442 static if( !T.length ) { 7443 enum hasLocalAliasing = false; 7444 } else 7445 enum hasLocalAliasing = std.traits.hasLocalAliasing!(T[0]) || 7446 dfl.control.hasLocalAliasing!(T[1 .. $]); 7447 } 7448 7449 7450 shared class SharedControl { 7451 private: 7452 Control _ctrl; 7453 7454 LPARAM makeParam(ARGS...)(void function(Control, ARGS) fn, Tuple!(ARGS)* args) 7455 if (ARGS.length) { 7456 static assert((DflInvokeParam*).sizeof <= LPARAM.sizeof); 7457 static struct InvokeParam { 7458 void function(Control, ARGS) fn; 7459 ARGS args; 7460 } 7461 alias dfl.internal.clib.malloc malloc; 7462 alias dfl.internal.clib.free free; 7463 7464 auto param = cast(InvokeParam*)malloc(InvokeParam.sizeof); 7465 param.fn = fn; 7466 param.args = args.field; 7467 7468 if (!param) { 7469 throw new OomException(); 7470 } 7471 7472 auto p = cast(DflInvokeParam*)malloc(DflInvokeParam.sizeof); 7473 7474 if (!p) { 7475 throw new OomException(); 7476 } 7477 7478 7479 static void fnentry(Control c, size_t[] p) { 7480 auto param = cast(InvokeParam*)p[0]; 7481 param.fn(c, param.args); 7482 free(param); 7483 } 7484 7485 p.fp = &fnentry; 7486 p.nparams = 1; 7487 p.params[0] = cast(size_t)param; 7488 7489 return cast(LPARAM)p; 7490 } 7491 7492 7493 LPARAM makeParamNoneArgs(void function(Control) fn) { 7494 static assert((DflInvokeParam*).sizeof <= LPARAM.sizeof); 7495 alias dfl.internal.clib.malloc malloc; 7496 alias dfl.internal.clib.free free; 7497 7498 auto p = cast(DflInvokeParam*)malloc(DflInvokeParam.sizeof); 7499 7500 if (!p) { 7501 throw new OomException(); 7502 } 7503 7504 static void fnentry(Control c, size_t[] p) { 7505 auto fn = cast(void function(Control))p[0]; 7506 fn(c); 7507 } 7508 7509 p.fp = &fnentry; 7510 p.nparams = 1; 7511 p.params[0] = cast(size_t)fn; 7512 7513 return cast(LPARAM)p; 7514 } 7515 7516 7517 7518 public: 7519 7520 this(Control ctrl) { 7521 assert(ctrl); 7522 _ctrl = cast(shared)ctrl; 7523 } 7524 7525 7526 void invoke(ARGS...)(void function(Control, ARGS) fn, ARGS args) 7527 if (ARGS.length && !hasLocalAliasing!(ARGS)) { 7528 auto ctrl = cast(Control)_ctrl; 7529 auto hwnd = ctrl.handle; 7530 7531 if(!hwnd) { 7532 Control.badInvokeHandle(); 7533 } 7534 7535 auto t = tuple(args); 7536 auto p = makeParam(fn, &t); 7537 SendMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE_PARAMS, p); 7538 } 7539 7540 7541 void invoke(ARGS...)(void function(Control, ARGS) fn, ARGS args) 7542 if (!ARGS.length) { 7543 auto ctrl = cast(Control)_ctrl; 7544 auto hwnd = ctrl.handle; 7545 7546 if(!hwnd) { 7547 Control.badInvokeHandle(); 7548 } 7549 7550 auto p = makeParamNoneArgs(fn); 7551 SendMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE_PARAMS, p); 7552 } 7553 7554 7555 void delayInvoke(ARGS...)(void function(Control, ARGS) fn, ARGS args) 7556 if (ARGS.length && !hasLocalAliasing!(ARGS)) { 7557 auto ctrl = cast(Control)_ctrl; 7558 auto hwnd = ctrl.handle; 7559 7560 if(!hwnd) { 7561 Control.badInvokeHandle(); 7562 } 7563 7564 auto t = tuple(args); 7565 auto p = makeParam(fn, &t); 7566 PostMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE_PARAMS, p); 7567 } 7568 7569 7570 void delayInvoke(ARGS...)(void function(Control, ARGS) fn, ARGS args) 7571 if (!ARGS.length) { 7572 auto ctrl = cast(Control)_ctrl; 7573 auto hwnd = ctrl.handle; 7574 7575 if(!hwnd) { 7576 Control.badInvokeHandle(); 7577 } 7578 7579 auto p = makeParamNoneArgs(fn); 7580 PostMessageA(hwnd, wmDfl, WPARAM_DFL_DELAY_INVOKE_PARAMS, p); 7581 } 7582 }