1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 module dfl.resources;
5 import core.sys.windows.windows;
6 
7 import dfl.base;
8 import dfl.drawing;
9 import dfl.exception;
10 import dfl.internal.dlib;
11 import dfl.internal.utf;
12 
13 version (DFL_NO_RESOURCES) {
14 } else {
15 
16    class Resources {
17 
18       this(HINSTANCE inst, WORD language = 0, bool owned = false) {
19          this.hinst = inst;
20          this.lang = language;
21          this._owned = owned;
22       }
23 
24       // Note: libName gets unloaded and may take down all its resources with it.
25       this(Dstring libName, WORD language = 0) {
26          HINSTANCE inst;
27          inst = loadLibraryEx(libName, LOAD_LIBRARY_AS_DATAFILE);
28          if (!inst) {
29             throw new DflException("Unable to load resources from '" ~ libName ~ "'");
30          }
31          this(inst, language, true); // Owned.
32       }
33 
34       /+ // Let's not depend on Application; the user can do so if they wish.
35 
36       this(WORD language = 0) {
37          this(Application.getInstance(), language);
38       }
39       +/
40 
41       void dispose() {
42          assert(_owned);
43          //if(hinst != Application.getInstance()) // ?
44          FreeLibrary(hinst);
45          hinst = null;
46       }
47 
48       final @property WORD language() {
49          return lang;
50       }
51 
52       final Icon getIcon(int id, bool defaultSize = true)
53       in {
54          assert(id >= WORD.min && id <= WORD.max);
55       }
56       body {
57          /+
58          HICON hi;
59          hi = LoadIconA(hinst, cast(LPCSTR)cast(WORD)id);
60          if(!hi) {
61             return null;
62          }
63          return Icon.fromHandle(hi);
64          +/
65          HICON hi;
66          hi = cast(HICON) LoadImageA(hinst, cast(LPCSTR) cast(WORD) id,
67             IMAGE_ICON, 0, 0, defaultSize ? (LR_DEFAULTSIZE | LR_SHARED) : 0);
68          if (!hi) {
69             return null;
70          }
71          return new Icon(hi, true); // Owned.
72       }
73 
74       final Icon getIcon(Dstring name, bool defaultSize = true) {
75          /+
76          HICON hi;
77          hi = LoadIconA(hinst, unsafeStringz(name));
78          if(!hi) {
79             return null;
80          }
81          return Icon.fromHandle(hi);
82          +/
83          HICON hi;
84          hi = cast(HICON) dfl.internal.utf.loadImage(hinst, name, IMAGE_ICON,
85             0, 0, defaultSize ? (LR_DEFAULTSIZE | LR_SHARED) : 0);
86          if (!hi) {
87             return null;
88          }
89          return new Icon(hi, true); // Owned.
90       }
91 
92       final Icon getIcon(int id, int width, int height)
93       in {
94          assert(id >= WORD.min && id <= WORD.max);
95       }
96       body {
97          // Can't have size 0 (plus causes Windows to use the actual size).
98          //if(width <= 0 || height <= 0)
99          // _noload("icon");
100          HICON hi;
101          hi = cast(HICON) LoadImageA(hinst, cast(LPCSTR) cast(WORD) id,
102             IMAGE_ICON, width, height, 0);
103          if (!hi) {
104             return null;
105          }
106          return new Icon(hi, true); // Owned.
107       }
108 
109       final Icon getIcon(Dstring name, int width, int height) {
110          // Can't have size 0 (plus causes Windows to use the actual size).
111          //if(width <= 0 || height <= 0)
112          // _noload("icon");
113          HICON hi;
114          hi = cast(HICON) dfl.internal.utf.loadImage(hinst, name, IMAGE_ICON, width,
115             height, 0);
116          if (!hi) {
117             return null;
118          }
119          return new Icon(hi, true); // Owned.
120       }
121 
122       deprecated alias loadIcon = getIcon;
123 
124       final Bitmap getBitmap(int id)
125       in {
126          assert(id >= WORD.min && id <= WORD.max);
127       }
128       body {
129          HBITMAP h;
130          h = cast(HBITMAP) LoadImageA(hinst, cast(LPCSTR) cast(WORD) id, IMAGE_BITMAP,
131             0, 0, 0);
132          if (!h) {
133             return null;
134          }
135          return new Bitmap(h, true); // Owned.
136       }
137 
138       final Bitmap getBitmap(Dstring name) {
139          HBITMAP h;
140          h = cast(HBITMAP) loadImage(hinst, name, IMAGE_BITMAP, 0, 0, 0);
141          if (!h) {
142             return null;
143          }
144          return new Bitmap(h, true); // Owned.
145       }
146 
147       deprecated alias loadBitmap = getBitmap;
148 
149       final Cursor getCursor(int id)
150       in {
151          assert(id >= WORD.min && id <= WORD.max);
152       }
153       body {
154          HCURSOR h;
155          h = cast(HCURSOR) LoadImageA(hinst, cast(LPCSTR) cast(WORD) id, IMAGE_CURSOR,
156             0, 0, 0);
157          if (!h) {
158             return null;
159          }
160          return new Cursor(h, true); // Owned.
161       }
162 
163       final Cursor getCursor(Dstring name) {
164          HCURSOR h;
165          h = cast(HCURSOR) loadImage(hinst, name, IMAGE_CURSOR, 0, 0, 0);
166          if (!h) {
167             return null;
168          }
169          return new Cursor(h, true); // Owned.
170       }
171 
172       deprecated alias loadCursor = getCursor;
173 
174       final Dstring getString(int id)
175       in {
176          assert(id >= WORD.min && id <= WORD.max);
177       }
178       body {
179          // Not casting to wDstring because a resource isn't guaranteed to be the same size.
180          wchar* ws = cast(wchar*) _getData(cast(LPCWSTR) RT_STRING,
181             cast(LPCWSTR) cast(WORD)(id / 16 + 1)).ptr;
182          Dstring result;
183          if (ws) {
184             int i;
185             for (i = 0; i < (id & 15); i++) {
186                ws += 1 + cast(size_t)*ws;
187             }
188             result = utf16stringtoUtf8string((ws + 1)[0 .. cast(size_t)*ws]);
189          }
190          return result;
191       }
192 
193       deprecated alias loadString = getString;
194 
195       // Used internally
196       // NOTE: win9x doesn't like these strings to be on the heap!
197       final void[] _getData(LPCWSTR type, LPCWSTR name) { // internal
198          HRSRC hrc;
199          hrc = FindResourceExW(hinst, type, name, lang);
200          if (!hrc) {
201             return null;
202          }
203          HGLOBAL hg = LoadResource(hinst, hrc);
204          if (!hg) {
205             return null;
206          }
207          LPVOID pv = LockResource(hg);
208          if (!pv) {
209             return null;
210          }
211          return pv[0 .. SizeofResource(hinst, hrc)];
212       }
213 
214       final void[] getData(int type, int id)
215       in {
216          assert(type >= WORD.min && type <= WORD.max);
217          assert(id >= WORD.min && id <= WORD.max);
218       }
219       body {
220          return _getData(cast(LPCWSTR) type, cast(LPCWSTR) id);
221       }
222 
223       final void[] getData(Dstring type, int id)
224       in {
225          assert(id >= WORD.min && id <= WORD.max);
226       }
227       body {
228          return _getData(utf8stringToUtf16stringz(type), cast(LPCWSTR) id);
229       }
230 
231       final void[] getData(int type, Dstring name)
232       in {
233          assert(type >= WORD.min && type <= WORD.max);
234       }
235       body {
236          return _getData(cast(LPCWSTR) type, utf8stringToUtf16stringz(name));
237       }
238 
239       final void[] getData(Dstring type, Dstring name) {
240          return _getData(utf8stringToUtf16stringz(type), utf8stringToUtf16stringz(name));
241       }
242 
243       ~this() {
244          if (_owned) {
245             dispose();
246          }
247       }
248 
249    private:
250 
251       HINSTANCE hinst;
252       WORD lang = 0;
253       bool _owned = false;
254 
255       void _noload(Dstring type) {
256          throw new DflException("Unable to load " ~ type ~ " resource");
257       }
258    }
259 }