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