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 }