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