1 // napWritten by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 module dfl.base; 5 6 import core.sys.windows.windows; 7 import core.sys.windows.winuser; 8 //import core.sys.wndows.winerror; 9 10 import core.stdc.stdlib : alloca; 11 import core.stdc..string : strcpy; 12 13 import dfl.internal.dlib; 14 static import dfl.internal.utf; 15 import dfl.internal.clib; 16 17 // FIX: import dfl.internal.winapi; 18 import dfl.drawing; 19 import dfl.event; 20 import dfl.exception; 21 22 alias HWindow = HWND; 23 24 interface IWindow { 25 @property HWindow handle(); 26 } 27 28 alias IWin32Window = IWindow; // deprecated 29 30 alias DflThrowable = DThrowable; 31 32 class StringObject : DObject { 33 Dstring value; 34 this(Dstring str) pure nothrow { 35 this.value = str; 36 } 37 38 override Dstring toString() { 39 return value; 40 } 41 42 override Dequ opEquals(Object o) { 43 return value == getObjectString(o); // ? 44 } 45 46 Dequ opEquals(StringObject s) { 47 return value == s.value; 48 } 49 50 override int opCmp(Object o) { 51 return stringICmp(value, getObjectString(o)); // ? 52 } 53 54 int opCmp(StringObject s) { 55 return stringICmp(value, s.value); 56 } 57 } 58 59 enum Keys : uint { 60 NONE = 0, /// No keys specified. 61 62 SHIFT = 0x10000, /// Modifier keys. 63 CONTROL = 0x20000, 64 ALT = 0x40000, 65 WINDOWS = 0x80000, 66 67 A = 'A', /// Letters. 68 B = 'B', 69 C = 'C', 70 D = 'D', 71 E = 'E', 72 F = 'F', 73 G = 'G', 74 H = 'H', 75 I = 'I', 76 J = 'J', 77 K = 'K', 78 L = 'L', 79 M = 'M', 80 N = 'N', 81 O = 'O', 82 P = 'P', 83 Q = 'Q', 84 R = 'R', 85 S = 'S', 86 T = 'T', 87 U = 'U', 88 V = 'V', 89 W = 'W', 90 X = 'X', 91 Y = 'Y', 92 Z = 'Z', 93 94 D0 = '0', /// Digits. 95 D1 = '1', 96 D2 = '2', 97 D3 = '3', 98 D4 = '4', 99 D5 = '5', 100 D6 = '6', 101 D7 = '7', 102 D8 = '8', 103 D9 = '9', 104 105 F1 = 112, /// F - function keys. 106 F2 = 113, 107 F3 = 114, 108 F4 = 115, 109 F5 = 116, 110 F6 = 117, 111 F7 = 118, 112 F8 = 119, 113 F9 = 120, 114 F10 = 121, 115 F11 = 122, 116 F12 = 123, 117 F13 = 124, 118 F14 = 125, 119 F15 = 126, 120 F16 = 127, 121 F17 = 128, 122 F18 = 129, 123 F19 = 130, 124 F20 = 131, 125 F21 = 132, 126 F22 = 133, 127 F23 = 134, 128 F24 = 135, 129 130 NUM_PAD0 = 96, /// Numbers on keypad. 131 NUM_PAD1 = 97, 132 NUM_PAD2 = 98, 133 NUM_PAD3 = 99, 134 NUM_PAD4 = 100, 135 NUM_PAD5 = 101, 136 NUM_PAD6 = 102, 137 NUM_PAD7 = 103, 138 NUM_PAD8 = 104, 139 NUM_PAD9 = 105, 140 141 ADD = 107, 142 APPS = 93, /// Application. 143 ATTN = 246, 144 BACK = 8, /// Backspace. 145 CANCEL = 3, 146 CAPITAL = 20, 147 CAPS_LOCK = 20, 148 CLEAR = 12, 149 CONTROL_KEY = 17, 150 CRSEL = 247, 151 DECIMAL = 110, 152 DEL = 46, 153 DELETE = DEL, 154 PERIOD = 190, 155 DOT = PERIOD, 156 DIVIDE = 111, 157 DOWN = 40, /// Down arrow. 158 END = 35, 159 ENTER = 13, 160 ERASE_EOF = 249, 161 ESCAPE = 27, 162 EXECUTE = 43, 163 EXSEL = 248, 164 FINAL_MODE = 4, /// IME final mode. 165 HANGUL_MODE = 21, /// IME Hangul mode. 166 HANGUEL_MODE = 21, 167 HANJA_MODE = 25, /// IME Hanja mode. 168 HELP = 47, 169 HOME = 36, 170 IME_ACCEPT = 30, 171 IME_CONVERT = 28, 172 IME_MODE_CHANGE = 31, 173 IME_NONCONVERT = 29, 174 INSERT = 45, 175 JUNJA_MODE = 23, 176 KANA_MODE = 21, 177 KANJI_MODE = 25, 178 LEFT_CONTROL = 162, /// Left Ctrl. 179 LEFT = 37, /// Left arrow. 180 LINE_FEED = 10, 181 LEFT_MENU = 164, /// Left Alt. 182 LEFT_SHIFT = 160, 183 LEFT_WIN = 91, /// Left Windows logo. 184 MENU = 18, /// Alt. 185 MULTIPLY = 106, 186 NEXT = 34, /// Page down. 187 NO_NAME = 252, // Reserved for future use. 188 NUM_LOCK = 144, 189 OEM8 = 223, // OEM specific. 190 OEM_CLEAR = 254, 191 PA1 = 253, 192 PAGE_DOWN = 34, 193 PAGE_UP = 33, 194 PAUSE = 19, 195 PLAY = 250, 196 PRINT = 42, 197 PRINT_SCREEN = 44, 198 PROCESS_KEY = 229, 199 RIGHT_CONTROL = 163, /// Right Ctrl. 200 RETURN = 13, 201 RIGHT = 39, /// Right arrow. 202 RIGHT_MENU = 165, /// Right Alt. 203 RIGHT_SHIFT = 161, 204 RIGHT_WIN = 92, /// Right Windows logo. 205 SCROLL = 145, /// Scroll lock. 206 SELECT = 41, 207 SEPARATOR = 108, 208 SHIFT_KEY = 16, 209 SNAPSHOT = 44, /// Print screen. 210 SPACE = 32, 211 SPACEBAR = SPACE, // Extra. 212 SUBTRACT = 109, 213 TAB = 9, 214 UP = 38, /// Up arrow. 215 ZOOM = 251, 216 217 // Windows 2000+ 218 BROWSER_BACK = 166, 219 BROWSER_FAVORITES = 171, 220 BROWSER_FORWARD = 167, 221 BROWSER_HOME = 172, 222 BROWSER_REFRESH = 168, 223 BROWSER_SEARCH = 170, 224 BROWSER_STOP = 169, 225 LAUNCH_APPLICATION1 = 182, 226 LAUNCH_APPLICATION2 = 183, 227 LAUNCH_MAIL = 180, 228 MEDIA_NEXT_TRACK = 176, 229 MEDIA_PLAY_PAUSE = 179, 230 MEDIA_PREVIOUS_TRACK = 177, 231 MEDIA_STOP = 178, 232 OEM_BACKSLASH = 226, // OEM angle bracket or backslash. 233 OEM_CLOSE_BRACKETS = 221, 234 OEM_COMMA = 188, 235 OEM_MINUS = 189, 236 OEM_OPEN_BRACKETS = 219, 237 OEM_PERIOD = 190, 238 OEM_PIPE = 220, 239 OEM_PLUS = 187, 240 OEM_QUESTION = 191, 241 OEM_QUOTES = 222, 242 OEM_SEMICOLON = 186, 243 OEM_TILDE = 192, 244 SELECT_MEDIA = 181, 245 VOLUME_DOWN = 174, 246 VOLUME_MUTE = 173, 247 VOLUME_UP = 175, 248 249 /// Bit mask to extract key code from key value. 250 KEY_CODE = 0xFFFF, 251 252 /// Bit mask to extract modifiers from key value. 253 MODIFIERS = 0xFFFF0000, 254 } 255 256 enum MouseButtons : uint { 257 /// No mouse buttons specified. 258 NONE = 0, 259 260 LEFT = 0x100000, 261 RIGHT = 0x200000, 262 MIDDLE = 0x400000,// Windows 2000+ 263 //XBUTTON1 = 0x800000, 264 //XBUTTON2 = 0x1000000, 265 } 266 267 enum CheckState : ubyte { 268 UNCHECKED = BST_UNCHECKED, 269 CHECKED = BST_CHECKED, 270 INDETERMINATE = BST_INDETERMINATE, 271 } 272 273 struct Message { 274 union { 275 struct { 276 HWND hWnd; 277 UINT msg; 278 WPARAM wParam; 279 LPARAM lParam; 280 } 281 282 package MSG _winMsg; // .time and .pt are not always valid. 283 } 284 285 LRESULT result; 286 287 /// Construct a Message struct. 288 this(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) pure nothrow { 289 this.hWnd = hWnd; 290 this.msg = msg; 291 this.wParam = wParam; 292 this.lParam = lParam; 293 result = 0; 294 } 295 } 296 297 interface IMessageFilter { 298 // Return false to allow the message to be dispatched. 299 // Filter functions cannot modify messages. 300 bool preFilterMessage(ref Message m); 301 } 302 303 abstract class WaitHandle { 304 // FIX: enum WAIT_TIMEOUT = WAIT_TIMEOUT; // DMD 1.028: needs fqn, otherwise conflicts with std.thread 305 enum INVALID_HANDLE = INVALID_HANDLE_VALUE; 306 307 this() { 308 h = INVALID_HANDLE; 309 } 310 311 // Used internally. 312 this(HANDLE h, bool owned = true) { 313 this.h = h; 314 this.owned = owned; 315 } 316 317 @property HANDLE handle() nothrow { 318 return h; 319 } 320 321 @property void handle(HANDLE h) { 322 this.h = h; 323 } 324 325 void close() { 326 CloseHandle(h); 327 h = INVALID_HANDLE; 328 } 329 330 ~this() { 331 if (owned) { 332 close(); 333 } 334 } 335 336 private static DWORD _wait(WaitHandle[] handles, BOOL waitall, DWORD msTimeout) { 337 DWORD result; 338 HANDLE* hs; 339 // Some implementations fail with > 64 handles, but that will return WAIT_FAILED; 340 // all implementations fail with >= 128 handles due to WAIT_ABANDONED_0 being 128. 341 if (handles.length >= 128) { 342 goto fail; 343 } 344 345 //hs = new HANDLE[handles.length]; 346 hs = cast(HANDLE*) alloca(HANDLE.sizeof * handles.length); 347 348 foreach (size_t i, WaitHandle wh; handles) { 349 hs[i] = wh.handle; 350 } 351 352 result = WaitForMultipleObjects(handles.length, hs, waitall, msTimeout); 353 if (WAIT_FAILED == result) { 354 fail: 355 throw new DflException("Wait failure"); 356 } 357 return result; 358 } 359 360 static void waitAll(WaitHandle[] handles) { 361 return waitAll(handles, INFINITE); 362 } 363 364 static void waitAll(WaitHandle[] handles, DWORD msTimeout) { 365 _wait(handles, true, msTimeout); 366 } 367 368 static int waitAny(WaitHandle[] handles) { 369 return waitAny(handles, INFINITE); 370 } 371 372 static int waitAny(WaitHandle[] handles, DWORD msTimeout) { 373 DWORD result; 374 result = _wait(handles, false, msTimeout); 375 return cast(int) result; // Same return info. 376 } 377 378 void waitOne() { 379 return waitOne(INFINITE); 380 } 381 382 void waitOne(DWORD msTimeout) { 383 DWORD result; 384 result = WaitForSingleObject(handle, msTimeout); 385 if (WAIT_FAILED == result) { 386 throw new DflException("Wait failure"); 387 } 388 } 389 390 private HANDLE h; 391 private bool owned = true; 392 } 393 394 interface IAsyncResult { 395 @property WaitHandle asyncWaitHandle(); 396 397 // Usually just returns false. 398 @property bool completedSynchronously(); 399 400 // When true, it is safe to release its resources. 401 @property bool isCompleted(); 402 } 403 404 /+ 405 class AsyncResult: IAsyncResult { 406 } 407 +/ 408 409 interface IButtonControl { 410 @property DialogResult dialogResult(); 411 412 @property void dialogResult(DialogResult); 413 414 void notifyDefault(bool); // True if default button. 415 void performClick(); // Raise click event. 416 } 417 418 enum DialogResult : ubyte { 419 NONE, 420 421 ABORT = IDABORT, 422 CANCEL = IDCANCEL, 423 IGNORE = IDIGNORE, 424 NO = IDNO, 425 OK = IDOK, 426 RETRY = IDRETRY, 427 YES = IDYES, 428 429 // Extra. 430 CLOSE = IDCLOSE, 431 HELP = IDHELP, 432 } 433 434 interface IDialogResult { 435 // 436 @property DialogResult dialogResult(); 437 // 438 @property void dialogResult(DialogResult); 439 } 440 441 enum SortOrder : ubyte { 442 NONE, 443 444 ASCENDING, 445 DESCENDING, 446 } 447 448 enum View : ubyte { 449 LARGE_ICON, 450 SMALL_ICON, 451 LIST, 452 DETAILS, 453 } 454 455 enum ItemBoundsPortion : ubyte { 456 ENTIRE, 457 ICON, 458 ITEM_ONLY, /// Excludes other stuff like check boxes. 459 LABEL, /// Item's text. 460 } 461 462 enum ItemActivation : ubyte { 463 STANDARD, 464 ONE_CLICK, 465 TWO_CLICK, 466 } 467 468 enum ColumnHeaderStyle : ubyte { 469 CLICKABLE, 470 NONCLICKABLE, 471 NONE, /// No column header. 472 } 473 474 enum BorderStyle : ubyte { 475 NONE, 476 477 FIXED_3D, 478 FIXED_SINGLE, 479 } 480 481 enum FlatStyle : ubyte { 482 STANDARD, 483 FLAT, 484 POPUP, 485 SYSTEM, 486 } 487 488 enum Appearance : ubyte { 489 NORMAL, 490 BUTTON, 491 } 492 493 enum ContentAlignment : ubyte { 494 TOP_LEFT, 495 BOTTOM_CENTER, 496 BOTTOM_LEFT, 497 BOTTOM_RIGHT, 498 MIDDLE_CENTER, 499 MIDDLE_LEFT, 500 MIDDLE_RIGHT, 501 TOP_CENTER, 502 TOP_RIGHT, 503 } 504 505 enum CharacterCasing : ubyte { 506 NORMAL, 507 LOWER, 508 UPPER, 509 } 510 511 // Not flags. 512 enum ScrollBars : ubyte { 513 NONE, 514 515 HORIZONTAL, 516 VERTICAL, 517 BOTH, 518 } 519 520 enum HorizontalAlignment : ubyte { 521 LEFT, 522 RIGHT, 523 CENTER, 524 } 525 526 enum DrawMode : ubyte { 527 NORMAL, 528 OWNER_DRAW_FIXED, 529 OWNER_DRAW_VARIABLE, 530 } 531 532 enum DrawItemState : uint { 533 NONE = 0, 534 SELECTED = 1, 535 DISABLED = 2, 536 CHECKED = 8, 537 FOCUS = 0x10, 538 DEFAULT = 0x20, 539 HOT_LIGHT = 0x40, 540 NO_ACCELERATOR = 0x80, 541 INACTIVE = 0x100, 542 NO_FOCUS_RECT = 0x200, 543 COMBO_BOX_EDIT = 0x1000, 544 } 545 546 enum RightToLeft : ubyte { 547 INHERIT = 2, 548 YES = 1, 549 NO = 0, 550 } 551 552 enum ColorDepth : ubyte { 553 DEPTH_4BIT = 0x04, 554 DEPTH_8BIT = 0x08, 555 DEPTH_16BIT = 0x10, 556 DEPTH_24BIT = 0x18, 557 DEPTH_32BIT = 0x20, 558 } 559 560 class PaintEventArgs : EventArgs { 561 562 this(Graphics graphics, Rect clipRect) pure nothrow { 563 g = graphics; 564 cr = clipRect; 565 } 566 567 final @property Graphics graphics() pure nothrow { 568 return g; 569 } 570 571 final @property Rect clipRectangle() pure nothrow { 572 return cr; 573 } 574 575 private: 576 Graphics g; 577 Rect cr; 578 } 579 580 class CancelEventArgs : EventArgs { 581 582 // Initialize cancel to false. 583 this() pure nothrow { 584 cncl = false; 585 } 586 587 this(bool cancel) pure nothrow { 588 cncl = cancel; 589 } 590 591 final @property void cancel(bool byes) pure nothrow { 592 cncl = byes; 593 } 594 595 final @property bool cancel() pure nothrow { 596 return cncl; 597 } 598 599 private: 600 bool cncl; 601 } 602 603 class SizingEventArgs : EventArgs { 604 605 // Initialize cancel to false. 606 this(Size sz) pure nothrow { 607 _sz = sz; 608 } 609 610 @property Size size() pure nothrow { 611 return _sz; 612 } 613 614 @property void size(Size sz) pure nothrow { 615 _sz = sz; 616 } 617 618 @property void width(int w) pure nothrow { 619 _sz.width = w; 620 } 621 622 @property int width() pure nothrow { 623 return _sz.width; 624 } 625 626 @property void height(int h) pure nothrow { 627 _sz.height = h; 628 } 629 630 @property int height() pure nothrow { 631 return _sz.height; 632 } 633 634 private: 635 Size _sz; 636 } 637 638 class MovingEventArgs : EventArgs { 639 640 // Initialize cancel to false. 641 this(Point loc) pure nothrow { 642 _loc = loc; 643 } 644 645 @property Point location() pure nothrow { 646 return _loc; 647 } 648 649 @property void location(Point loc) pure nothrow { 650 _loc = loc; 651 } 652 653 @property void x(int posX) pure nothrow { 654 _loc.x = posX; 655 } 656 657 @property int x() pure nothrow { 658 return _loc.x; 659 } 660 661 @property void y(int posY) pure nothrow { 662 _loc.y = posY; 663 } 664 665 @property int y() pure nothrow { 666 return _loc.y; 667 } 668 669 private: 670 Point _loc; 671 } 672 673 class KeyEventArgs : EventArgs { 674 675 this(Keys keys) pure nothrow { 676 ks = keys; 677 } 678 679 final @property bool alt() pure nothrow { 680 return (ks & Keys.ALT) != 0; 681 } 682 683 final @property bool control() pure nothrow { 684 return (ks & Keys.CONTROL) != 0; 685 } 686 687 final @property void handled(bool byes) pure nothrow { 688 hand = byes; 689 } 690 691 final @property bool handled() pure nothrow { 692 return hand; 693 } 694 695 final @property Keys keyCode() pure nothrow { 696 return ks & Keys.KEY_CODE; 697 } 698 699 final @property Keys keyData() pure nothrow { 700 return ks; 701 } 702 703 // -keyData- as an int. 704 final @property int keyValue() pure nothrow { 705 return cast(int) ks; 706 } 707 708 final @property Keys modifiers() pure nothrow { 709 return ks & Keys.MODIFIERS; 710 } 711 712 final @property bool shift() pure nothrow { 713 return (ks & Keys.SHIFT) != 0; 714 } 715 716 final @property bool windows() pure nothrow { 717 return (ks & Keys.WINDOWS) != 0; 718 } 719 720 private: 721 Keys ks; 722 bool hand = false; 723 } 724 725 class KeyPressEventArgs : KeyEventArgs { 726 private dchar _keych; 727 this(dchar ch) { 728 this(ch, (ch >= 'A' && ch <= 'Z') ? Keys.SHIFT : Keys.NONE); 729 } 730 731 this(dchar ch, Keys modifiers) 732 in { 733 assert((modifiers & Keys.MODIFIERS) == modifiers, 734 "modifiers parameter can only contain modifiers"); 735 } 736 body { 737 _keych = ch; 738 739 int vk; 740 if (dfl.internal.utf.useUnicode) { 741 vk = 0xFF & VkKeyScanW(cast(WCHAR) ch); 742 } else { 743 vk = 0xFF & VkKeyScanA(cast(char) ch); 744 } 745 746 super(cast(Keys)(vk | modifiers)); 747 } 748 749 final @property dchar keyChar() { 750 return _keych; 751 } 752 } 753 754 class MouseEventArgs : EventArgs { 755 private MouseButtons btn; 756 private int clks; 757 private int _x, _y; 758 private int dlt; 759 // -delta- is mouse wheel rotations. 760 this(MouseButtons button, int clicks, int x, int y, int delta) pure nothrow { 761 btn = button; 762 clks = clicks; 763 _x = x; 764 _y = y; 765 dlt = delta; 766 } 767 768 final @property MouseButtons button() pure nothrow { 769 return btn; 770 } 771 772 final @property int clicks() pure nothrow { 773 return clks; 774 } 775 776 final @property int delta() pure nothrow { 777 return dlt; 778 } 779 780 final @property int x() pure nothrow { 781 return _x; 782 } 783 784 final @property int y() pure nothrow { 785 return _y; 786 } 787 } 788 789 /+ 790 791 class LabelEditEventArgs: EventArgs { 792 793 this(int index) { 794 795 } 796 797 798 this(int index, Dstring labelText) { 799 this.idx = index; 800 this.ltxt = labelText; 801 } 802 803 804 805 final @property void cancelEdit(bool byes) { 806 cancl = byes; 807 } 808 809 810 final @property bool cancelEdit() { 811 return cancl; 812 } 813 814 815 816 // The text of the label's edit. 817 final @property Dstring label() { 818 return ltxt; 819 } 820 821 822 823 // Gets the item's index. 824 final @property int item() { 825 return idx; 826 } 827 828 829 private: 830 int idx; 831 Dstring ltxt; 832 bool cancl = false; 833 } 834 +/ 835 836 class ColumnClickEventArgs : EventArgs { 837 838 private int col; 839 this(int col) pure nothrow { 840 this.col = col; 841 } 842 843 final @property int column() pure nothrow { 844 return col; 845 } 846 } 847 848 class DrawItemEventArgs : EventArgs { 849 private Graphics gpx; 850 private Font fnt; // Suggestion; the parent's font. 851 private Rect rect; 852 private int idx; 853 private DrawItemState distate; 854 private Color fcolor, bcolor; // Suggestion; depends on item state. 855 this(Graphics g, Font f, Rect r, int i, DrawItemState dis) pure nothrow { 856 this(g, f, r, i, dis, Color.empty, Color.empty); 857 } 858 859 this(Graphics g, Font f, Rect r, int i, DrawItemState dis, Color fc, Color bc) pure nothrow { 860 gpx = g; 861 fnt = f; 862 rect = r; 863 idx = i; 864 distate = dis; 865 fcolor = fc; 866 bcolor = bc; 867 } 868 869 final @property Color backColor() pure nothrow { 870 return bcolor; 871 } 872 873 final @property Rect bounds() pure nothrow { 874 return rect; 875 } 876 877 final @property Font font() pure nothrow { 878 return fnt; 879 } 880 881 final @property Color foreColor() pure nothrow { 882 return fcolor; 883 } 884 885 final @property Graphics graphics() pure nothrow { 886 return gpx; 887 } 888 889 final @property int index() pure nothrow { 890 return idx; 891 } 892 893 final @property DrawItemState state() pure nothrow { 894 return distate; 895 } 896 897 void drawBackground() { 898 /+ 899 HBRUSH hbr; 900 RECT _rect; 901 902 hbr = bcolor.createBrush(); 903 try { 904 rect.getRect(&_rect); 905 FillRect(gpx.handle, &_rect, hbr); 906 } 907 finally { 908 DeleteObject(hbr); 909 } 910 +/ 911 912 gpx.fillRectangle(bcolor, rect); 913 } 914 915 void drawFocusRectangle() { 916 if (distate & DrawItemState.FOCUS) { 917 RECT _rect; 918 rect.getRect(&_rect); 919 DrawFocusRect(gpx.handle, &_rect); 920 } 921 } 922 } 923 924 class MeasureItemEventArgs : EventArgs { 925 926 this(Graphics g, int index, int itemHeight) { 927 gpx = g; 928 idx = index; 929 iheight = itemHeight; 930 } 931 932 this(Graphics g, int index) { 933 this(g, index, 0); 934 } 935 936 final @property Graphics graphics() { 937 return gpx; 938 } 939 940 final @property int index() { 941 return idx; 942 } 943 944 final @property void itemHeight(int height) { 945 iheight = height; 946 } 947 948 final @property int itemHeight() { 949 return iheight; 950 } 951 952 final @property void itemWidth(int width) { 953 iwidth = width; 954 } 955 956 final @property int itemWidth() { 957 return iwidth; 958 } 959 960 private: 961 Graphics gpx; 962 int idx, iheight, iwidth = 0; 963 } 964 965 class Cursor { 966 private static Cursor _cur; 967 968 // Used internally. 969 this(HCURSOR hcur, bool owned = true) { 970 this.hcur = hcur; 971 this.owned = owned; 972 } 973 974 ~this() { 975 if (owned) { 976 dispose(); 977 } 978 } 979 980 void dispose() { 981 assert(owned); 982 DestroyCursor(hcur); 983 hcur = HCURSOR.init; 984 } 985 986 static @property void current(Cursor cur) { 987 // Keep a reference so that it doesn't get garbage collected until set again. 988 _cur = cur; 989 990 SetCursor(cur ? cur.hcur : HCURSOR.init); 991 } 992 993 static @property Cursor current() { 994 HCURSOR hcur = GetCursor(); 995 return hcur ? new Cursor(hcur, false) : null; 996 } 997 998 static @property void clip(Rect r) { 999 RECT rect; 1000 r.getRect(&rect); 1001 ClipCursor(&rect); 1002 } 1003 1004 static @property Rect clip() { 1005 RECT rect; 1006 GetClipCursor(&rect); 1007 return Rect(&rect); 1008 } 1009 1010 final @property HCURSOR handle() { 1011 return hcur; 1012 } 1013 1014 /+ 1015 // TODO: 1016 final @property Size size() { 1017 Size result; 1018 ICONINFO iinfo; 1019 1020 if(GetIconInfo(hcur, &iinfo)) { 1021 1022 } 1023 1024 return result; 1025 } 1026 +/ 1027 1028 // Uses the actual size. 1029 final void draw(Graphics g, Point pt) { 1030 DrawIconEx(g.handle, pt.x, pt.y, hcur, 0, 0, 0, HBRUSH.init, DI_NORMAL); 1031 } 1032 1033 /+ 1034 1035 // Should not stretch if bigger, but should crop if smaller. 1036 final void draw(Graphics g, Rect r) { 1037 } 1038 +/ 1039 1040 final void drawStretched(Graphics g, Rect r) { 1041 // DrawIconEx operates differently if the width or height is zero 1042 // so bail out if zero and pretend the zero size cursor was drawn. 1043 int width = r.width; 1044 if (!width) { 1045 return; 1046 } 1047 int height = r.height; 1048 if (!height) { 1049 return; 1050 } 1051 1052 DrawIconEx(g.handle, r.x, r.y, hcur, width, height, 0, HBRUSH.init, DI_NORMAL); 1053 } 1054 1055 override Dequ opEquals(Object o) { 1056 Cursor cur = cast(Cursor) o; 1057 if (!cur) { 1058 return 0; // Not equal. 1059 } 1060 return opEquals(cur); 1061 } 1062 1063 Dequ opEquals(Cursor cur) { 1064 return hcur == cur.hcur; 1065 } 1066 1067 /// Show/hide the current mouse cursor; reference counted. 1068 // show/hide are ref counted. 1069 static void hide() { 1070 ShowCursor(false); 1071 } 1072 1073 // show/hide are ref counted. 1074 static void show() { 1075 ShowCursor(true); 1076 } 1077 1078 /// The position of the current mouse cursor. 1079 static @property void position(Point pt) { 1080 SetCursorPos(pt.x, pt.y); 1081 } 1082 1083 static @property Point position() { 1084 Point pt; 1085 GetCursorPos(&pt.point); 1086 return pt; 1087 } 1088 1089 private: 1090 HCURSOR hcur; 1091 bool owned = true; 1092 } 1093 1094 class Cursors { 1095 private this() { 1096 } 1097 1098 static { 1099 1100 @property Cursor appStarting() { 1101 return new Cursor(LoadCursor(HINSTANCE.init, IDC_APPSTARTING), false); 1102 } 1103 1104 @property Cursor arrow() { 1105 return new Cursor(LoadCursor(HINSTANCE.init, IDC_ARROW), false); 1106 } 1107 1108 @property Cursor cross() { 1109 return new Cursor(LoadCursor(HINSTANCE.init, IDC_CROSS), false); 1110 } 1111 1112 //@property Cursor default() 1113 @property Cursor defaultCursor() { 1114 return arrow; 1115 } 1116 1117 @property Cursor hand() { 1118 version (SUPPORTS_HAND_CURSOR) { // Windows 98+ 1119 return new Cursor(LoadCursor(HINSTANCE.init, IDC_HAND), false); 1120 } else { 1121 static HCURSOR hcurHand; 1122 1123 if (!hcurHand) { 1124 hcurHand = LoadCursor(HINSTANCE.init, IDC_HAND); 1125 if (!hcurHand) { // Must be Windows 95, so load the cursor from winhlp32.exe. 1126 UINT len; 1127 char[MAX_PATH] winhlppath = void; 1128 1129 len = GetWindowsDirectoryA(winhlppath.ptr, winhlppath.length - 16); 1130 if (!len || len > winhlppath.length - 16) { 1131 load_failed: 1132 return arrow; // Just fall back to a normal arrow. 1133 } 1134 strcpy(winhlppath.ptr + len, "\\winhlp32.exe"); 1135 1136 HINSTANCE hinstWinhlp; 1137 hinstWinhlp = LoadLibraryExA(winhlppath.ptr, HANDLE.init, 1138 LOAD_LIBRARY_AS_DATAFILE); 1139 if (!hinstWinhlp) { 1140 goto load_failed; 1141 } 1142 1143 HCURSOR hcur; 1144 hcur = LoadCursorA(hinstWinhlp, cast(char*) 106); 1145 if (!hcur) { // No such cursor resource. 1146 FreeLibrary(hinstWinhlp); 1147 goto load_failed; 1148 } 1149 hcurHand = CopyCursor(hcur); 1150 if (!hcurHand) { 1151 FreeLibrary(hinstWinhlp); 1152 //throw new DflException("Unable to copy cursor resource"); 1153 goto load_failed; 1154 } 1155 1156 FreeLibrary(hinstWinhlp); 1157 } 1158 } 1159 1160 assert(hcurHand); 1161 // Copy the cursor and own it here so that it's safe to dispose it. 1162 return new Cursor(CopyCursor(hcurHand)); 1163 } 1164 } 1165 1166 @property Cursor help() { 1167 HCURSOR hcur; 1168 hcur = LoadCursor(HINSTANCE.init, IDC_HELP); 1169 if (!hcur) { // IDC_HELP might not be supported on Windows 95, so fall back to a normal arrow. 1170 return arrow; 1171 } 1172 return new Cursor(hcur); 1173 } 1174 1175 @property Cursor hSplit() { 1176 // ... 1177 return sizeNS; 1178 } 1179 1180 @property Cursor vSplit() { 1181 // ... 1182 return sizeWE; 1183 } 1184 1185 @property Cursor iBeam() { 1186 return new Cursor(LoadCursor(HINSTANCE.init, IDC_IBEAM), false); 1187 } 1188 1189 @property Cursor no() { 1190 return new Cursor(LoadCursor(HINSTANCE.init, IDC_NO), false); 1191 } 1192 1193 @property Cursor sizeAll() { 1194 return new Cursor(LoadCursor(HINSTANCE.init, IDC_SIZEALL), false); 1195 } 1196 1197 @property Cursor sizeNESW() { 1198 return new Cursor(LoadCursor(HINSTANCE.init, IDC_SIZENESW), false); 1199 } 1200 1201 @property Cursor sizeNS() { 1202 return new Cursor(LoadCursor(HINSTANCE.init, IDC_SIZENS), false); 1203 } 1204 1205 @property Cursor sizeNWSE() { 1206 return new Cursor(LoadCursor(HINSTANCE.init, IDC_SIZENWSE), false); 1207 } 1208 1209 @property Cursor sizeWE() { 1210 return new Cursor(LoadCursor(HINSTANCE.init, IDC_SIZEWE), false); 1211 } 1212 1213 /* 1214 1215 // Insertion point. 1216 @property Cursor upArrow() { 1217 } 1218 */ 1219 1220 @property Cursor waitCursor() { 1221 return new Cursor(LoadCursor(HINSTANCE.init, IDC_WAIT), false); 1222 } 1223 } 1224 }