file dialogs
[vrlugburz] / tools / dunger / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <GL/glut.h>
5 #include <utk/cubertk.h>
6 #include <drawtext.h>
7 #include "level.h"
8 #include "lview.h"
9
10 static int init(void);
11 static void cleanup(void);
12 static void display(void);
13 static void reshape(int x, int y);
14 static void keyb(unsigned char key, int x, int y);
15 static void keyup(unsigned char key, int x, int y);
16 static void mouse(int bn, int st, int x, int y);
17 static void motion(int x, int y);
18
19 static void cb_new(utk_event *ev, void *data);
20 static void cb_new_ok(utk_event *ev, void *data);
21 static void cb_open(utk_event *ev, void *data);
22 static void cb_open_ok(utk_event *ev, void *data);
23 static void cb_save(utk_event *ev, void *data);
24 static void cb_save_ok(utk_event *ev, void *data);
25
26 static void cb_cancel(utk_event *ev, void *data);
27
28 static int parse_args(int argc, char **argv);
29
30
31 static void ucolor(int r, int g, int b, int a);
32 static void uclip(int x1, int y1, int x2, int y2);
33 static void uimage(int x, int y, const void *pix, int xsz, int ysz);
34 static void urect(int x1, int y1, int x2, int y2);
35 static void uline(int x1, int y1, int x2, int y2, int width);
36 static void utext(int x, int y, const char *txt, int sz);
37 static int utextspacing(void);
38 static int utextwidth(const char *txt, int sz);
39
40
41 int win_width, win_height;
42 int view_width, view_height;
43 float view_panx, view_pany, view_zoom = 1.0f;
44
45 static int bnstate[8];
46 int mousex, mousey, clickx, clicky;
47
48 static float uiscale = 1.0f;
49 #define UISPLIT 150
50 int splitx;
51
52 #define FONTSZ  16
53 static struct dtx_font *uifont;
54 static utk_widget *uiroot, *uiwin_new;
55 static utk_widget *uigrab;
56 static utk_widget *cbox_newsz;
57
58 static struct level *lvl;
59
60
61 int main(int argc, char **argv)
62 {
63         glutInit(&argc, argv);
64
65         if(parse_args(argc, argv) == -1) {
66                 return 1;
67         }
68
69         glutInitWindowSize(1280, 800);
70         glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE);
71         glutCreateWindow("dunger");
72
73         win_width = glutGet(GLUT_WINDOW_WIDTH);
74         win_height = glutGet(GLUT_WINDOW_HEIGHT);
75
76         glutDisplayFunc(display);
77         glutReshapeFunc(reshape);
78         glutKeyboardFunc(keyb);
79         glutKeyboardUpFunc(keyup);
80         glutMouseFunc(mouse);
81         glutMotionFunc(motion);
82         glutPassiveMotionFunc(motion);
83
84         if(init() == -1) {
85                 return 1;
86         }
87         atexit(cleanup);
88
89         glutMainLoop();
90         return 0;
91 }
92
93
94 static int init(void)
95 {
96         int pad;
97         utk_widget *win, *hbox, *vbox;
98
99         glEnable(GL_MULTISAMPLE);
100
101         glClearColor(0.15, 0.15, 0.15, 1);
102
103         if(!(uifont = dtx_open_font("uifont.ttf", 0))) {
104                 fprintf(stderr, "failed to open uifont.ttf\n");
105                 return -1;
106         }
107         dtx_prepare_range(uifont, FONTSZ, ' ', 'z');
108         dtx_use_font(uifont, FONTSZ);
109
110         if(!(uiroot = utk_init(win_width / uiscale, win_height / uiscale))) {
111                 fprintf(stderr, "failed to initialized ubertk\n");
112                 return -1;
113         }
114         utk_set_color_func(ucolor);
115         utk_set_clip_func(uclip);
116         utk_set_image_func(uimage);
117         utk_set_rect_func(urect);
118         utk_set_line_func(uline);
119         utk_set_text_func(utext);
120         utk_set_text_spacing_func(utextspacing);
121         utk_set_text_width_func(utextwidth);
122
123         win = utk_vbox(uiroot, 0, UTK_DEF_SPACING);
124         utk_set_pos(win, 15, 15);
125         utk_button(win, "New", 0, 0, cb_new, 0);
126         utk_button(win, "Open ...", 0, 0, cb_open, 0);
127         utk_button(win, "Save ...", 0, 0, cb_save, 0);
128
129         uiwin_new = utk_window(uiroot, (win_width - 220) / 2, (win_height - 150) / 2,
130                         220, 150, "New level");
131         vbox = utk_vbox(uiwin_new, UTK_DEF_PADDING, UTK_DEF_SPACING);
132         {
133                 const char *items[] = {"small (16x16)", "medium (24x24)", "large (32x32)"};
134                 cbox_newsz = utk_combobox_items(vbox, items, sizeof items / sizeof *items, 0, 0);
135                 utk_select(cbox_newsz, 2);
136         }
137         hbox = utk_hbox(vbox, UTK_DEF_PADDING, UTK_DEF_SPACING);
138         utk_button(hbox, "OK", 0, 0, cb_new_ok, 0);
139         utk_button(hbox, "Cancel", 0, 0, cb_cancel, uiwin_new);
140         pad = utk_get_padding(uiwin_new);
141         utk_set_size(uiwin_new, utk_get_width(vbox) + pad * 2.0f, utk_get_height(vbox) + pad * 2.0f);
142
143
144         if(!(lvl = create_level(32, 32))) {
145                 fprintf(stderr, "failed to create level\n");
146                 return -1;
147         }
148         if(init_lview(lvl) == -1) {
149                 return -1;
150         }
151
152         splitx = UISPLIT * uiscale;
153         view_width = win_width - splitx;
154         view_height = win_height;
155
156         return 0;
157 }
158
159 static void cleanup(void)
160 {
161         destroy_lview();
162         free_level(lvl);
163         dtx_close_font(uifont);
164         utk_close(uiroot);
165 }
166
167 static void display(void)
168 {
169         glClear(GL_COLOR_BUFFER_BIT);
170
171         glMatrixMode(GL_MODELVIEW);
172         glLoadIdentity();
173
174         /* draw view */
175
176         glMatrixMode(GL_PROJECTION);
177         glLoadIdentity();
178         glOrtho(0, view_width, 0, view_height, -1, 1);
179         glViewport(splitx, 0, view_width, view_height);
180
181         glBegin(GL_QUADS);
182         glColor3f(0.1, 0.1, 0.1);
183         glVertex2f(0, 0);
184         glVertex2f(view_width, 0);
185         glVertex2f(view_width, view_height);
186         glVertex2f(0, view_height);
187         glEnd();
188
189         draw_lview();
190
191         /* draw UI */
192
193         glMatrixMode(GL_PROJECTION);
194         glLoadIdentity();
195         glOrtho(0, win_width, win_height, 0, -1, 1);
196         glViewport(0, 0, win_width, win_height);
197
198         glBegin(GL_QUADS);
199         glColor3f(0.25, 0.25, 0.25);
200         glVertex2f(0, 0);
201         glVertex2f(splitx, 0);
202         glVertex2f(splitx, win_height);
203         glVertex2f(0, win_height);
204         glEnd();
205
206         glEnable(GL_BLEND);
207         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
208         utk_draw(uiroot);
209         glDisable(GL_BLEND);
210
211
212         glutSwapBuffers();
213 }
214
215 static void reshape(int x, int y)
216 {
217         win_width = x;
218         win_height = y;
219
220         if(uiroot) {
221                 utk_set_size(uiroot, x / uiscale, y / uiscale);
222         }
223
224         lview_viewport(splitx, 0, x - splitx, y);
225 }
226
227 static void keyb(unsigned char key, int x, int y)
228 {
229         switch(key) {
230         case 27:
231                 if(uigrab) {
232                         if(utk_is_dialog(uigrab)) {
233                                 utk_destroy_window(uigrab);
234                         } else {
235                                 utk_hide(uigrab);
236                         }
237                         uigrab = 0;
238                 } else {
239                         exit(0);
240                 }
241                 return;
242
243         default:
244                 break;
245         }
246
247         utk_keyboard_event(key, 1);
248         glutPostRedisplay();
249 }
250
251 static void keyup(unsigned char key, int x, int y)
252 {
253         utk_keyboard_event(key, 0);
254         glutPostRedisplay();
255 }
256
257 static void mouse(int bn, int st, int x, int y)
258 {
259         int bidx = bn - GLUT_LEFT_BUTTON;
260         int press = st == GLUT_DOWN;
261
262         bnstate[bidx] = press;
263         mousex = x;
264         mousey = y;
265
266         if(bn <= 2) {
267                 if(press) {
268                         clickx = x;
269                         clicky = y;
270                 } else {
271                         clickx = clicky = -1;
272                 }
273         } else if(bn == 3) {
274                 if(press) zoom_lview(0.1);
275         } else if(bn == 4) {
276                 if(press) zoom_lview(-0.1);
277         }
278
279         if(!uigrab) {
280                 lview_mbutton(bidx, press, x, y);
281         }
282
283         utk_mbutton_event(bidx, press, x / uiscale, y / uiscale);
284         glutPostRedisplay();
285 }
286
287 static void motion(int x, int y)
288 {
289         int dx, dy;
290         dx = x - mousex;
291         dy = y - mousey;
292         mousex = x;
293         mousey = y;
294
295         if(clickx >= splitx) {
296                 if(bnstate[1]) {
297                         pan_lview(-dx, dy);
298                 }
299         }
300
301         if(!uigrab) {
302                 lview_mouse(x, y);
303         }
304
305         utk_mmotion_event(x / uiscale, y / uiscale);
306         glutPostRedisplay();
307 }
308
309 static void cb_new(utk_event *ev, void *data)
310 {
311         utk_show(uiwin_new);
312         uigrab = uiwin_new;
313 }
314
315 static void cb_new_ok(utk_event *ev, void *data)
316 {
317         static int levsz[] = {16, 24, 32};
318         int sz;
319         struct level *newlvl;
320
321         sz = levsz[utk_get_selected(cbox_newsz)];
322
323         if(!(newlvl = create_level(sz, sz))) {
324                 utk_message_dialog("failed to create new level", UTK_MSG_TYPE_ERROR,
325                                 UTK_MSG_BN_OK, cb_cancel, 0);
326                 return;
327         }
328
329         free_level(lvl);
330         destroy_lview();
331
332         lvl = newlvl;
333         init_lview(newlvl);
334
335         utk_hide(uiwin_new);
336         uigrab = 0;
337 }
338
339 static void cb_open(utk_event *ev, void *data)
340 {
341         uigrab = utk_file_dialog(UTK_FILE_DIALOG_OPEN, 0, "Level file (*.lvl) [.lvl]", 0, cb_open_ok, 0);
342 }
343
344 static void cb_open_ok(utk_event *ev, void *data)
345 {
346         utk_widget *dlg = utk_event_widget(ev);
347         printf("selected: %s\n", utk_file_dialog_file(dlg));
348         utk_destroy_window(dlg);
349         if(uigrab == dlg) uigrab = 0;
350 }
351
352 static void cb_save(utk_event *ev, void *data)
353 {
354         uigrab = utk_file_dialog(UTK_FILE_DIALOG_SAVE, 0, "Level file (*.lvl) [.lvl]", 0, cb_save_ok, 0);
355 }
356
357 static void cb_save_ok(utk_event *ev, void *data)
358 {
359         utk_widget *dlg = utk_event_widget(ev);
360         printf("selected: %s\n", utk_file_dialog_file(dlg));
361         utk_destroy_window(dlg);
362         if(uigrab == dlg) uigrab = 0;
363 }
364
365 static void cb_cancel(utk_event *ev, void *data)
366 {
367         if(!data) data = utk_get_window(utk_event_widget(ev));
368
369         if(utk_is_dialog(data)) {
370                 utk_destroy_window(data);
371         } else {
372                 utk_hide(data);
373         }
374         uigrab = 0;
375 }
376
377 static int parse_args(int argc, char **argv)
378 {
379         int i;
380
381         for(i=1; i<argc; i++) {
382                 if(argv[i][0] == '-') {
383                         if(strcmp(argv[i], "-uiscale") == 0) {
384                                 if(!argv[++i] || !(uiscale = atoi(argv[i]))) {
385                                         fprintf(stderr, "-uiscale should be followed by a positive number\n");
386                                         return -1;
387                                 }
388                         } else if(strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "-h") == 0) {
389                                 printf("Usage: %s [options]\n", argv[0]);
390                                 printf("Options:\n");
391                                 printf(" -uiscale <scale>: UI scale factor (default: 1)\n");
392                                 printf(" -h,-help: print usage and exit\n");
393                                 exit(0);
394                         } else {
395                                 fprintf(stderr, "unknown option: %s\n", argv[i]);
396                                 return -1;
397                         }
398                 } else {
399                         fprintf(stderr, "unexpected argument: %s\n", argv[i]);
400                         return -1;
401                 }
402         }
403         return 0;
404 }
405
406 /* --- ubertk callbacks --- */
407
408 static void ucolor(int r, int g, int b, int a)
409 {
410         glColor4ub(r, g, b, a);
411 }
412
413 static void uclip(int x1, int y1, int x2, int y2)
414 {
415         if(!(x1 | y1 | x2 | y2)) {
416                 glDisable(GL_SCISSOR_TEST);
417         } else {
418                 glEnable(GL_SCISSOR_TEST);
419         }
420
421         x1 *= uiscale;
422         y1 *= uiscale;
423         x2 *= uiscale;
424         y2 *= uiscale;
425
426         glScissor(x1, win_height - y2, x2 - x1, y2 - y1);
427 }
428
429 static void uimage(int x, int y, const void *pix, int xsz, int ysz)
430 {
431         glPixelZoom(1, -1);
432         glRasterPos2f(x * uiscale, y * uiscale);
433         glDrawPixels(xsz, ysz, GL_BGRA, GL_UNSIGNED_BYTE, pix);
434 }
435
436 static void urect(int x1, int y1, int x2, int y2)
437 {
438         glRectf(x1 * uiscale, y1 * uiscale, x2 * uiscale, y2 * uiscale);
439 }
440
441 static void uline(int x1, int y1, int x2, int y2, int width)
442 {
443         glLineWidth(width);
444         glBegin(GL_LINES);
445         glVertex2f(x1 * uiscale, y1 * uiscale);
446         glVertex2f(x2 * uiscale, y2 * uiscale);
447         glEnd();
448 }
449
450 static void utext(int x, int y, const char *txt, int sz)
451 {
452         glMatrixMode(GL_PROJECTION);
453         glPushMatrix();
454         glTranslatef(x * uiscale, (y - dtx_baseline()) * uiscale, 0);
455         glScalef(uiscale, -uiscale, 1);
456
457         dtx_string(txt);
458         dtx_flush();
459
460         glPopMatrix();
461 }
462
463 static int utextspacing(void)
464 {
465         return dtx_line_height();
466 }
467
468 static int utextwidth(const char *txt, int sz)
469 {
470         return dtx_string_width(txt);
471 }