8ed6d890874cb823e8051feac3550e056bfbbd7a
[instimg] / src / widgets.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <windows.h>
6 #include "widgets.h"
7
8 struct wgt_window {
9         HWND handle;
10         struct wgt_rect rect;
11
12         HDC dc;
13         char cname[32];
14         struct wgt_widget *wlist;
15 };
16
17 struct wgt_widget {
18         HWND handle;
19         struct wgt_rect rect;
20
21         struct wgt_window *win;
22         struct wgt_widget *next;
23
24         char **itemlist;
25         int num_items;
26
27         wgt_callback cb_click;
28         wgt_callback cb_modify;
29 };
30
31
32 static LRESULT WINAPI event_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
33 static struct wgt_widget *find_widget(struct wgt_window *win, HWND handle);
34
35 struct wgt_window *wgt_window(const char *title, int width, int height)
36 {
37         struct wgt_window *win;
38         int tlen;
39         WNDCLASS wc;
40         HINSTANCE hinst;
41         DWORD err;
42         RECT rect;
43
44         if(!(win = malloc(sizeof *win))) {
45                 fprintf(stderr, "wgl_create_window: failed to allocate window structure\n");
46                 return 0;
47         }
48         win->wlist = 0;
49
50         tlen = strlen(title);
51         if(tlen > sizeof win->cname - 10) {
52                 tlen = sizeof win->cname - 10;
53         }
54         strcpy(win->cname, "WGTCLASS-");
55         memcpy(win->cname + 9, title, tlen);
56         win->cname[tlen + 9] = 0;
57
58         hinst = GetModuleHandle(0);
59
60         memset(&wc, 0, sizeof wc);
61         wc.hInstance = hinst;
62         wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
63         wc.hCursor = LoadCursor(0, IDC_ARROW);
64         wc.hIcon = LoadIcon(0, IDI_APPLICATION);
65         wc.lpszClassName = win->cname;
66         wc.lpfnWndProc = event_handler;
67         wc.cbWndExtra = 4;
68         if(!RegisterClass(&wc)) {
69                 fprintf(stderr, "wgt_create_window: failed to register window class\n");
70                 return 0;
71         }
72
73         if(!(win->handle = CreateWindow(win->cname, title, WS_OVERLAPPEDWINDOW,
74                         CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, hinst, 0))) {
75                 fprintf(stderr, "wgt_create_window: failed to create window\n");
76                 UnregisterClass(title, hinst);
77                 return 0;
78         }
79         win->dc = GetDC(win->handle);
80         SetLastError(0);
81         if(!SetWindowLong(win->handle, GWL_USERDATA, (long)win) && (err = GetLastError())) {
82                 fprintf(stderr, "wgt_create_window: failed to store window user data (err: %d)\n", (int)err);
83                 UnregisterClass(title, hinst);
84                 DestroyWindow(win->handle);
85                 return 0;
86         }
87         ShowWindow(win->handle, 1);
88
89         GetWindowRect(win->handle, &rect);
90
91         win->rect.x = rect.left;
92         win->rect.y = rect.top;
93         win->rect.width = rect.right - rect.left;
94         win->rect.height = rect.bottom - rect.top;
95
96         return win;
97 }
98
99 void wgt_destroy_window(struct wgt_window *win)
100 {
101         while(win->wlist) {
102                 struct wgt_widget *w = win->wlist;
103                 win->wlist = win->wlist->next;
104                 wgt_destroy_widget(w);
105                 free(w);
106         }
107
108         ReleaseDC(win->handle, win->dc);
109         DestroyWindow(win->handle);
110         UnregisterClass(win->cname, GetModuleHandle(0));
111
112         free(win);
113 }
114
115 void wgt_destroy_widget(struct wgt_widget *w)
116 {
117         DestroyWindow(w->handle);
118         free(w->itemlist);
119 }
120
121 struct wgt_window *wgt_widget_window(struct wgt_widget *w)
122 {
123         return w->win;
124 }
125
126 struct wgt_rect *wgt_widget_rect(struct wgt_widget *w, struct wgt_rect *rect)
127 {
128         if(rect) {
129                 *rect = w->rect;
130         }
131         return &w->rect;
132 }
133
134 int wgt_xpos_after(struct wgt_widget *w, int pad)
135 {
136         if(pad == WGT_AUTO) {
137                 pad = 8;
138         }
139         return w->rect.x + w->rect.width + pad;
140 }
141
142 int wgt_ypos_after(struct wgt_widget *w, int pad)
143 {
144         if(pad == WGT_AUTO) {
145                 pad = 8;
146         }
147         return w->rect.y + w->rect.height + pad;
148 }
149
150 int wgt_string_size(struct wgt_window *win, const char *s, struct wgt_rect *rect)
151 {
152         SIZE sz;
153
154         GetTextExtentPoint32(win->dc, (char*)s, strlen(s), &sz);
155         if(rect) {
156                 rect->x = rect->y = 0;
157                 rect->width = sz.cx;
158                 rect->height = sz.cy;
159         }
160         return sz.cx;
161 }
162
163
164 struct wgt_widget *wgt_label(struct wgt_window *win, const char *text, int x, int y)
165 {
166         struct wgt_widget *w;
167         struct wgt_rect textsz;
168
169         if(!(w = calloc(1, sizeof *w))) {
170                 fprintf(stderr, "wgt_label: failed to allocate widget structure\n");
171                 return 0;
172         }
173         w->win = win;
174
175         wgt_string_size(win, text, &textsz);
176
177         if(!(w->handle = CreateWindow("STATIC", text, WS_CHILD | WS_VISIBLE | SS_SIMPLE,
178                         x, y, textsz.width, textsz.height, win->handle, 0, GetModuleHandle(0), 0))) {
179                 fprintf(stderr, "wgt_label: failed to create window\n");
180                 free(w);
181                 return 0;
182         }
183
184         w->rect.x = x;
185         w->rect.y = y;
186         w->rect.width = textsz.width;
187         w->rect.height = textsz.height;
188
189         w->next = win->wlist;
190         win->wlist = w;
191         return w;
192 }
193
194 struct wgt_widget *wgt_button(struct wgt_window *win, const char *text, int x, int y,
195                         int width, int height, wgt_callback clickfunc)
196 {
197         struct wgt_widget *w;
198         struct wgt_rect textsz;
199
200         if(!(w = calloc(1, sizeof *w))) {
201                 fprintf(stderr, "wgt_button: failed to allocate widget structure\n");
202                 return 0;
203         }
204         w->win = win;
205
206         if(width < 0 || height < 0) {
207                 wgt_string_size(win, text, &textsz);
208                 if(width < 0) width = textsz.width * 5 / 3;
209                 if(height < 0) height = textsz.height * 10 / 7;
210         }
211
212         if(!(w->handle = CreateWindow("BUTTON", text, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
213                         x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) {
214                 fprintf(stderr, "wgt_button: failed to create window\n");
215                 free(w);
216                 return 0;
217         }
218
219         w->rect.x = x;
220         w->rect.y = y;
221         w->rect.width = width;
222         w->rect.height = height;
223
224         w->cb_click = clickfunc;
225         w->next = win->wlist;
226         win->wlist = w;
227         return w;
228 }
229
230 struct wgt_widget *wgt_checkbox(struct wgt_window *win, const char *text, int on,
231                         int x, int y, int width, int height, wgt_callback modfunc)
232 {
233         struct wgt_widget *w;
234         struct wgt_rect textsz;
235
236         if(!(w = calloc(1, sizeof *w))) {
237                 fprintf(stderr, "wgt_checkbox: failed to allocate widget structure\n");
238                 return 0;
239         }
240         w->win = win;
241
242         if(width < 0 || height < 0) {
243                 wgt_string_size(win, text, &textsz);
244                 if(width < 0) width = textsz.width + 32;        /* XXX */
245                 if(height < 0) height = textsz.height;
246         }
247
248         if(!(w->handle = CreateWindow("BUTTON", text, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
249                         x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) {
250                 fprintf(stderr, "wgt_checkbox: failed to create window\n");
251                 free(w);
252                 return 0;
253         }
254
255         w->rect.x = x;
256         w->rect.y = y;
257         w->rect.width = width;
258         w->rect.height = height;
259
260         w->cb_modify = modfunc;
261         w->next = win->wlist;
262         win->wlist = w;
263         return w;
264 }
265
266 struct wgt_widget *wgt_combo(struct wgt_window *win, const char **items, int num_items,
267                         int sel, int x, int y, int width, int height, wgt_callback modfunc)
268 {
269         int i, max_width, max_height, sum_height, res;
270         struct wgt_widget *w;
271         struct wgt_rect textsz;
272
273         if(!(w = calloc(1, sizeof *w))) {
274                 fprintf(stderr, "wgt_combo: failed to allocate widget structure\n");
275                 return 0;
276         }
277         w->win = win;
278
279         if(!(w->itemlist = malloc(num_items * sizeof *w->itemlist))) {
280                 fprintf(stderr, "wgt_combo: failed to allocate item list\n");
281                 free(w);
282                 return 0;
283         }
284
285         max_width = max_height = sum_height = 0;
286
287         for(i=0; i<num_items; i++) {
288                 wgt_string_size(win, items[i], &textsz);
289                 if(textsz.width > max_width) max_width = textsz.width;
290                 if(textsz.height > max_height) max_height = textsz.height;
291                 sum_height += textsz.height;
292         }
293         if(width < 0) width = max_width * 3 / 2;
294         if(height < 0) height = sum_height * 2;
295
296
297         if(!(w->handle = CreateWindow("COMBOBOX", items[0],
298                         WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS,
299                         x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) {
300                 fprintf(stderr, "wgt_combo: failed to create window\n");
301                 free(w);
302                 return 0;
303         }
304
305         for(i=0; i<num_items; i++) {
306                 if(!(w->itemlist[i] = strdup(items[i]))) {
307                         fprintf(stderr, "wgt_combo: failed to allocate item\n");
308                         while(--i >= 0) free(w->itemlist[i]);
309                         DestroyWindow(w->handle);
310                         free(w->itemlist);
311                         free(w);
312                         return 0;
313                 }
314                 if((res = SendMessage(w->handle, CB_ADDSTRING, 0, (long)items[i])) != i) {
315                         fprintf(stderr, "wgt_combo: failed to add item\n");
316                 }
317         }
318         w->num_items = num_items;
319
320         if(sel < 0) sel = 0;
321         if(sel >= num_items) sel = num_items - 1;
322         SendMessage(w->handle, CB_SETCURSEL, sel, 0);
323
324         w->rect.x = x;
325         w->rect.y = y;
326         w->rect.width = width;
327         w->rect.height = max_height;
328
329         w->cb_modify = modfunc;
330         w->next = win->wlist;
331         win->wlist = w;
332         return w;
333 }
334
335 int wgt_combo_selected(struct wgt_widget *w)
336 {
337         int sel = SendMessage(w->handle, CB_GETCURSEL, 0, 0);
338         if(sel == CB_ERR) return -1;
339         return sel;
340 }
341
342 const char *wgt_get_combo_item(struct wgt_widget *w, int idx)
343 {
344         if(idx < 0 || idx >= w->num_items) {
345                 return 0;
346         }
347         return w->itemlist[idx];
348 }
349
350 static LRESULT WINAPI event_handler(HWND wnd, unsigned int msg, WPARAM wparam, LPARAM lparam)
351 {
352         int ncode;
353         struct wgt_widget *w;
354         struct wgt_window *win;
355
356         win = (struct wgt_window*)GetWindowLong(wnd, GWL_USERDATA);
357
358         switch(msg) {
359         case WM_CLOSE:
360         case WM_DESTROY:
361                 PostQuitMessage(0);
362                 break;
363
364         case WM_COMMAND:
365                 if(!win || !(w = find_widget(win, (HWND)lparam))) {
366                         break;
367                 }
368                 ncode = HIWORD(wparam);
369
370                 switch(ncode) {
371                 case BN_CLICKED:
372                         if(w->cb_click) {
373                                 w->cb_click(w);
374                         }
375                         break;
376
377                 case CBN_SELCHANGE:
378                         if(w->cb_modify) {
379                                 w->cb_modify(w);
380                         }
381                         break;
382
383                 default:
384                         break;
385                 }
386         
387         default:
388                 return DefWindowProc(wnd, msg, wparam, lparam);
389         }
390         return 0;
391 }
392
393 static struct wgt_widget *find_widget(struct wgt_window *win, HWND handle)
394 {
395         struct wgt_widget *w;
396
397         w = win->wlist;
398         while(w) {
399                 if(w->handle == handle) return w;
400                 w = w->next;
401         }
402         return 0;
403 }