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 }