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