15 struct wgt_widget *wlist;
18 int (*cb_close)(struct wgt_window*);
25 struct wgt_window *win;
26 struct wgt_widget *next;
33 wgt_callback cb_click;
34 wgt_callback cb_modify;
38 static LRESULT WINAPI event_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
39 static struct wgt_widget *find_widget(struct wgt_window *win, HWND handle);
42 void *wgt_window_handle(struct wgt_window *win)
47 void *wgt_widget_handle(struct wgt_widget *w)
52 unsigned int wgt_window_xid(struct wgt_window *win)
57 unsigned int wgt_widget_xid(struct wgt_widget *w)
63 struct wgt_window *wgt_window(const char *title, int width, int height)
65 struct wgt_window *win;
72 if(!(win = calloc(1, sizeof *win))) {
73 fprintf(stderr, "wgl_create_window: failed to allocate window structure\n");
76 win->close_quit = 1; /* by default quit when the window closes */
79 if(tlen > sizeof win->cname - 10) {
80 tlen = sizeof win->cname - 10;
82 strcpy(win->cname, "WGTCLASS-");
83 memcpy(win->cname + 9, title, tlen);
84 win->cname[tlen + 9] = 0;
86 hinst = GetModuleHandle(0);
88 memset(&wc, 0, sizeof wc);
90 wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
91 wc.hCursor = LoadCursor(0, IDC_ARROW);
92 wc.hIcon = LoadIcon(0, IDI_APPLICATION);
93 wc.lpszClassName = win->cname;
94 wc.lpfnWndProc = event_handler;
96 if(!RegisterClass(&wc)) {
97 fprintf(stderr, "wgt_create_window: failed to register window class\n");
101 if(!(win->handle = CreateWindow(win->cname, title, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
102 CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, hinst, 0))) {
103 fprintf(stderr, "wgt_create_window: failed to create window\n");
104 UnregisterClass(title, hinst);
107 win->dc = GetDC(win->handle);
109 if(!SetWindowLong(win->handle, GWL_USERDATA, (long)win) && (err = GetLastError())) {
110 fprintf(stderr, "wgt_create_window: failed to store window user data (err: %d)\n", (int)err);
111 UnregisterClass(title, hinst);
112 DestroyWindow(win->handle);
115 ShowWindow(win->handle, 1);
117 GetWindowRect(win->handle, &rect);
119 win->rect.x = rect.left;
120 win->rect.y = rect.top;
121 win->rect.width = rect.right - rect.left;
122 win->rect.height = rect.bottom - rect.top;
127 void wgt_destroy_window(struct wgt_window *win)
130 struct wgt_widget *w = win->wlist;
131 win->wlist = win->wlist->next;
132 wgt_destroy_widget(w);
137 ReleaseDC(win->handle, win->dc);
138 DestroyWindow(win->handle);
139 UnregisterClass(win->cname, GetModuleHandle(0));
143 void wgt_free_window(struct wgt_window *win)
145 wgt_destroy_window(win);
149 void wgt_destroy_widget(struct wgt_widget *w)
151 DestroyWindow(w->handle);
155 void wgt_resize_window(struct wgt_window *win, int width, int height)
157 MoveWindow(win->handle, win->rect.x, win->rect.y, width, height, TRUE);
158 win->rect.width = width;
159 win->rect.height = height;
162 void wgt_quit_on_close(struct wgt_window *win, int quit)
164 win->close_quit = quit;
167 void wgt_close_action(struct wgt_window *win, int (*func)(struct wgt_window*))
169 win->cb_close = func;
172 void wgt_enable_widget(struct wgt_widget *w)
175 EnableWindow(w->handle, 1);
178 void wgt_disable_widget(struct wgt_widget *w)
181 EnableWindow(w->handle, 0);
184 int wgt_widget_enabled(struct wgt_widget *w)
189 struct wgt_window *wgt_widget_window(struct wgt_widget *w)
194 struct wgt_rect *wgt_widget_rect(struct wgt_widget *w, struct wgt_rect *rect)
202 void wgt_move_widget(struct wgt_widget *w, int x, int y)
204 MoveWindow(w->handle, x, y, w->rect.width, w->rect.height, TRUE);
209 void wgt_resize_widget(struct wgt_widget *w, int width, int height)
211 MoveWindow(w->handle, w->rect.x, w->rect.y, width, height, TRUE);
212 w->rect.width = width;
213 w->rect.height = height;
216 int wgt_xpos_after(struct wgt_widget *w, int pad)
218 if(pad == WGT_AUTO) {
221 return w->rect.x + w->rect.width + pad;
224 int wgt_ypos_after(struct wgt_widget *w, int pad)
226 if(pad == WGT_AUTO) {
229 return w->rect.y + w->rect.height + pad;
232 int wgt_string_size(struct wgt_window *win, const char *s, struct wgt_rect *rect)
236 GetTextExtentPoint32(win->dc, (char*)s, strlen(s), &sz);
238 rect->x = rect->y = 0;
240 rect->height = sz.cy;
246 struct wgt_widget *wgt_label(struct wgt_window *win, const char *text, int x, int y)
248 struct wgt_widget *w;
249 struct wgt_rect textsz;
251 if(!(w = calloc(1, sizeof *w))) {
252 fprintf(stderr, "wgt_label: failed to allocate widget structure\n");
257 wgt_string_size(win, text, &textsz);
259 if(!(w->handle = CreateWindow("STATIC", text, WS_CHILD | WS_VISIBLE | SS_SIMPLE,
260 x, y, textsz.width, textsz.height, win->handle, 0, GetModuleHandle(0), 0))) {
261 fprintf(stderr, "wgt_label: failed to create window\n");
268 w->rect.width = textsz.width;
269 w->rect.height = textsz.height;
271 w->next = win->wlist;
276 struct wgt_widget *wgt_button(struct wgt_window *win, const char *text, int x, int y,
277 int width, int height, wgt_callback clickfunc)
279 struct wgt_widget *w;
280 struct wgt_rect textsz;
282 if(!(w = calloc(1, sizeof *w))) {
283 fprintf(stderr, "wgt_button: failed to allocate widget structure\n");
288 if(width < 0 || height < 0) {
289 wgt_string_size(win, text, &textsz);
290 if(width < 0) width = textsz.width * 5 / 3;
291 if(height < 0) height = textsz.height * 10 / 7;
294 if(!(w->handle = CreateWindow("BUTTON", text, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
295 x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) {
296 fprintf(stderr, "wgt_button: failed to create window\n");
303 w->rect.width = width;
304 w->rect.height = height;
306 w->cb_click = clickfunc;
307 w->next = win->wlist;
312 struct wgt_widget *wgt_checkbox(struct wgt_window *win, const char *text, int on,
313 int x, int y, int width, int height, wgt_callback modfunc)
315 struct wgt_widget *w;
316 struct wgt_rect textsz;
319 if(!(w = calloc(1, sizeof *w))) {
320 fprintf(stderr, "wgt_checkbox: failed to allocate widget structure\n");
325 check_width = GetSystemMetrics(SM_CXMENUCHECK);
327 if(width < 0 || height < 0) {
328 wgt_string_size(win, text, &textsz);
329 if(width < 0) width = textsz.width + check_width + 10;
330 if(height < 0) height = textsz.height;
333 if(!(w->handle = CreateWindow("BUTTON", text, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
334 x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) {
335 fprintf(stderr, "wgt_checkbox: failed to create window\n");
339 SendMessage(w->handle, BM_SETCHECK, on ? BST_CHECKED : BST_UNCHECKED, 0);
343 w->rect.width = width;
344 w->rect.height = height;
346 w->cb_click = modfunc; /* BN_CLICKED is sent for checkbox state changes */
347 w->next = win->wlist;
352 static void combosize(struct wgt_window *win, HWND cbwnd, const char **items, int num_items,
353 int width, int height, struct wgt_rect *sz)
355 int i, max_width, sum_height;
356 struct wgt_rect textsz;
358 wgt_string_size(win, "00", &textsz);
359 max_width = width < 0 && width != WGT_AUTO ? -width : textsz.width;
361 /* first get the height of the editbox */
362 sum_height = SendMessage(cbwnd, CB_GETITEMHEIGHT, -1, 0);
364 /* then add the height of the dropdown list items */
365 /* XXX I can't figure out why, but it's not sufficient to show all items
366 * without some more height, so let's double it...
369 sum_height += SendMessage(cbwnd, CB_GETITEMHEIGHT, 0, 0) * num_items;
370 sum_height += GetSystemMetrics(SM_CYEDGE) * 2;
374 for(i=0; i<num_items; i++) {
375 wgt_string_size(win, items[i], &textsz);
376 if(textsz.width > max_width) max_width = textsz.width;
378 sbar_width = GetSystemMetrics(SM_CXVSCROLL);
379 width = max_width + sbar_width + GetSystemMetrics(SM_CXEDGE) * 2;
381 if(height < 0) height = sum_height;
387 struct wgt_widget *wgt_combo(struct wgt_window *win, const char **items, int num_items,
388 int sel, int x, int y, int width, int height, wgt_callback modfunc)
390 struct wgt_widget *w;
391 struct wgt_rect rect;
393 if(!(w = calloc(1, sizeof *w))) {
394 fprintf(stderr, "wgt_combo: failed to allocate widget structure\n");
399 if(!(w->handle = CreateWindow("COMBOBOX", items[0] ? items[0] : "",
400 WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS,
401 x, y, 16, 16, win->handle, 0, GetModuleHandle(0), 0))) {
402 fprintf(stderr, "wgt_combo: failed to create window\n");
407 combosize(win, w->handle, items, num_items, width, height, &rect);
410 w->rect.width = rect.width;
411 w->rect.height = rect.y; /* single line height for layout purposes */
412 MoveWindow(w->handle, x, y, rect.width, rect.height, FALSE);
414 wgt_combo_setitems(w, items, num_items);
417 if(sel >= num_items) sel = num_items - 1;
419 SendMessage(w->handle, CB_SETCURSEL, sel, 0);
422 w->cb_modify = modfunc;
423 w->next = win->wlist;
428 struct wgt_widget *wgt_progbar(struct wgt_window *win, int x, int y, int width, int height, int p)
430 struct wgt_widget *w;
431 INITCOMMONCONTROLSEX commctrl;
433 commctrl.dwSize = sizeof commctrl;
434 commctrl.dwICC = ICC_PROGRESS_CLASS;
435 InitCommonControlsEx(&commctrl);
437 if(!(w = calloc(1, sizeof *w))) {
438 fprintf(stderr, "wgt_progbar: failed to allocate widget structure\n");
444 height = GetSystemMetrics(SM_CYVSCROLL);
447 if(!(w->handle = CreateWindow(PROGRESS_CLASS, 0, WS_CHILD | WS_VISIBLE, x, y,
448 width, height, win->handle, 0, GetModuleHandle(0), 0))) {
449 fprintf(stderr, "wgt_progbar: failed to create window\n");
455 SendMessage(w->handle, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
456 SendMessage(w->handle, PBM_SETSTEP, 1, 0);
458 SendMessage(w->handle, PBM_SETPOS, p, 0);
462 w->rect.width = width;
463 w->rect.height = height;
465 w->next = win->wlist;
470 void wgt_set_text(struct wgt_widget *w, const char *text)
472 SendMessage(w->handle, WM_SETTEXT, 0, (long)text);
474 w->rect.width = wgt_string_size(w->win, text, 0);
475 MoveWindow(w->handle, w->rect.x, w->rect.y, w->rect.width, w->rect.height, TRUE);
478 void wgt_checkbox_check(struct wgt_widget *w)
480 SendMessage(w->handle, BM_SETCHECK, BST_CHECKED, 0);
483 void wgt_checkbox_uncheck(struct wgt_widget *w)
485 SendMessage(w->handle, BM_SETCHECK, BST_UNCHECKED, 0);
488 int wgt_checkbox_checked(struct wgt_widget *w)
490 return SendMessage(w->handle, BM_GETCHECK, 0, 0) == BST_CHECKED;
493 int wgt_combo_setitems(struct wgt_widget *w, const char **items, int num_items)
497 struct wgt_rect size;
499 if(!(newit = malloc(num_items * sizeof *newit))) {
500 fprintf(stderr, "wgt_combo_setitems: failed to allocate item list\n");
503 for(i=0; i<num_items; i++) {
504 if(!(newit[i] = strdup(items[i]))) {
505 fprintf(stderr, "wgt_combo_setitems: failed to allocate item\n");
514 for(i=0; i<w->num_items; i++) {
515 free(w->itemlist[i]);
518 SendMessage(w->handle, CB_RESETCONTENT, 0, 0);
521 w->num_items = num_items;
523 for(i=0; i<num_items; i++) {
524 SendMessage(w->handle, CB_ADDSTRING, 0, (long)w->itemlist[i]);
527 combosize(w->win, w->handle, items, num_items, w->rect.width, WGT_AUTO, &size);
528 MoveWindow(w->handle, w->rect.x, w->rect.y, w->rect.width, size.height, TRUE);
530 SendMessage(w->handle, CB_SETCURSEL, 0, 0);
534 int wgt_combo_selected(struct wgt_widget *w)
536 int sel = SendMessage(w->handle, CB_GETCURSEL, 0, 0);
537 if(sel == CB_ERR) return -1;
541 const char *wgt_get_combo_item(struct wgt_widget *w, int idx)
543 if(idx < 0 || idx >= w->num_items) {
546 return w->itemlist[idx];
549 void wgt_set_progress(struct wgt_widget *w, int p)
551 SendMessage(w->handle, PBM_SETPOS, p, 0);
554 static LRESULT WINAPI event_handler(HWND wnd, unsigned int msg, WPARAM wparam, LPARAM lparam)
557 struct wgt_widget *w;
558 struct wgt_window *win;
560 win = (struct wgt_window*)GetWindowLong(wnd, GWL_USERDATA);
564 if(!win->cb_close || win->cb_close(win)) {
566 int quit = win->close_quit;
567 wgt_destroy_window(win);
568 memset(win, 0, sizeof *win);
569 win->close_quit = quit;
577 if(win && win->close_quit) {
583 if(!win || !(w = find_widget(win, (HWND)lparam))) {
586 ncode = HIWORD(wparam);
606 return DefWindowProc(wnd, msg, wparam, lparam);
611 static struct wgt_widget *find_widget(struct wgt_window *win, HWND handle)
613 struct wgt_widget *w;
617 if(w->handle == handle) return w;