1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
4
5 /**
6 * @file toolkit.c
7 *
8 * @brief Handles windows and widgets.
9 */
10
11
12 #include "toolkit.h"
13
14 #include "naev.h"
15
16 #include <stdarg.h>
17
18 #include "tk/toolkit_priv.h"
19
20 #include "log.h"
21 #include "pause.h"
22 #include "opengl.h"
23 #include "input.h"
24 #include "nstd.h"
25 #include "dialogue.h"
26 #include "conf.h"
27
28
29 #define INPUT_DELAY conf.repeat_delay /**< Delay before starting to repeat. */
30 #define INPUT_FREQ conf.repeat_freq /**< Interval between repetition. */
31
32
33 static unsigned int genwid = 0; /**< Generates unique window ids, > 0 */
34
35
36 static int toolkit_open = 0; /**< 1 if toolkit is in use, 0 else. */
37 static int toolkit_delayCounter = 0; /**< Horrible hack around secondary loop. */
38
39
40 /*
41 * window stuff
42 */
43 #define MIN_WINDOWS 3 /**< Minimum windows to prealloc. */
44 static Window *windows = NULL; /**< Window linked list, not to be confused with MS windows. */
45 static int window_dead = 0; /**< There are dead windows lying around. */
46
47
48 /*
49 * simulate keypresses when holding
50 */
51 static SDLKey input_key = 0; /**< Current pressed key. */
52 static SDLMod input_mod = 0; /**< Current pressed modifier. */
53 static unsigned int input_keyTime = 0; /**< Tick pressed. */
54 static int input_keyCounter = 0; /**< Number of repetitions. */
55 static char input_text = 0; /**< Current character. */
56
57
58 /*
59 * default outline colours
60 */
61 const glColour* toolkit_colLight = &cGrey90; /**< Light outline colour. */
62 const glColour* toolkit_col = &cGrey70; /**< Normal outline colour. */
63 const glColour* toolkit_colDark = &cGrey30; /**< Dark outline colour. */
64
65
66 /*
67 * VBO
68 */
69 static gl_vbo *toolkit_vbo; /**< Toolkit VBO. */
70 static GLsizei toolkit_vboColourOffset; /**< Colour offset. */
71
72
73 /*
74 * static prototypes
75 */
76 /* input */
77 static int toolkit_mouseEvent( Window *w, SDL_Event* event );
78 static int toolkit_mouseEventWidget( Window *w, Widget *wgt,
79 SDL_Event *event, int x, int y, int rx, int ry );
80 static int toolkit_keyEvent( Window *wdw, SDL_Event* event );
81 #if SDL_VERSION_ATLEAST(2,0,0)
82 static int toolkit_textEvent( Window *wdw, SDL_Event* event );
83 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
84 /* Focus */
85 static int toolkit_isFocusable( Widget *wgt );
86 static Widget* toolkit_getFocus( Window *wdw );
87 static void toolkit_expose( Window *wdw, int expose );
88 /* render */
89 static void window_renderBorder( Window* w );
90 /* Death. */
91 static void widget_kill( Widget *wgt );
92 static void window_kill( Window *wdw );
93 static void toolkit_purgeDead (void);
94
95
96 /**
97 * @brief Checks to see if the toolkit is open.
98 *
99 * @return 1 if the toolkit is open.
100 */
toolkit_isOpen(void)101 int toolkit_isOpen (void)
102 {
103 return !!toolkit_open;
104 }
105
106
107 /**
108 * @brief Delays the toolkit purge by an iteration, useful for dialogues.
109 */
toolkit_delay(void)110 void toolkit_delay (void)
111 {
112 toolkit_delayCounter++;
113 }
114
115
116 /**
117 * @brief Sets the internal widget position.
118 *
119 * @param wdw Window to which the widget belongs.
120 * @param wgt Widget to set position of.
121 * @param x X position to use.
122 * @param y Y position to use.
123 */
toolkit_setPos(Window * wdw,Widget * wgt,int x,int y)124 void toolkit_setPos( Window *wdw, Widget *wgt, int x, int y )
125 {
126 /* X position. */
127 if (x < 0)
128 wgt->x = wdw->w - wgt->w + x;
129 else
130 wgt->x = (double) x;
131
132 /* Y position. */
133 if (y < 0)
134 wgt->y = wdw->h - wgt->h + y;
135 else
136 wgt->y = (double) y;
137 }
138
139
140 /**
141 * @brief Moves a window to the specified coordinates.
142 *
143 * @param x X position.
144 * @param y Y position.
145 */
toolkit_setWindowPos(Window * wdw,int x,int y)146 void toolkit_setWindowPos( Window *wdw, int x, int y )
147 {
148 wdw->xrel = -1.;
149 wdw->yrel = -1.;
150
151 /* x pos */
152 if (x == -1) { /* Center */
153 wdw->x = (SCREEN_W - wdw->w)/2.;
154 wdw->xrel = .5;
155 }
156 else if (x < 0)
157 wdw->x = SCREEN_W - wdw->w + (double) x;
158 else
159 wdw->x = (double) x;
160
161 /* y pos */
162 if (y == -1) { /* Center */
163 wdw->y = (SCREEN_H - wdw->h)/2.;
164 wdw->yrel = .5;
165 }
166 else if (y < 0)
167 wdw->y = SCREEN_H - wdw->h + (double) y;
168 else
169 wdw->y = (double) y;
170 }
171
172
173 /**
174 * @brief Moves a window to the specified coordinates.
175 *
176 * @param x X position.
177 * @param y Y position.
178 */
window_move(unsigned int wid,int x,int y)179 void window_move( unsigned int wid, int x, int y )
180 {
181 Window *wdw;
182
183 /* Get the window. */
184 wdw = window_wget(wid);
185 if (wdw == NULL)
186 return;
187
188 toolkit_setWindowPos( wdw, x, y );
189 }
190
191
192 /**
193 * @brief Allocates room for a new widget.
194 *
195 * @param w Window to create widget in.
196 * @return Newly allocated widget.
197 */
window_newWidget(Window * w,const char * name)198 Widget* window_newWidget( Window* w, const char *name )
199 {
200 Widget *wgt, *wlast, *wtmp;
201 char *saved_name = NULL;
202
203 /* NULL protection. */
204 if (w==NULL)
205 return NULL;
206
207 /* Try to find one with the same name first. */
208 wlast = NULL;
209 for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next) {
210
211 /* Must match name. */
212 if (strcmp(name, wgt->name)!=0) {
213 wlast = wgt;
214 continue;
215 }
216
217 /* Should be destroyed. */
218 if (!wgt_isFlag( wgt, WGT_FLAG_KILL )) {
219 WARN("Trying to create widget '%s' over existing one that hasn't been destroyed",
220 name );
221 return NULL;
222 }
223
224 /* Relink. */
225 if (wlast==NULL)
226 w->widgets = wgt->next;
227 else
228 wlast->next = wgt->next;
229
230 /* Prepare and return this widget. */
231 saved_name = wgt->name;
232 wgt->name = NULL;
233 widget_cleanup(wgt);
234 break;
235 }
236
237 /* Must grow widgets. */
238 if (wgt == NULL)
239 wgt = malloc( sizeof(Widget) );
240
241 /* Sane defaults. */
242 memset( wgt, 0, sizeof(Widget) );
243 wgt->type = WIDGET_NULL;
244 wgt->status = WIDGET_STATUS_NORMAL;
245 wgt->wdw = w->id;
246 if (saved_name != NULL) /* Hack to avoid frees so _getFocus works in the same frame. */
247 wgt->name = saved_name;
248 else
249 wgt->name = strdup(name);
250 wgt->id = ++w->idgen;
251
252 /* Set up. */
253 wlast = NULL;
254 for (wtmp=w->widgets; wtmp!=NULL; wtmp=wtmp->next)
255 wlast = wtmp;
256 if (wlast == NULL)
257 w->widgets = wgt;
258 else
259 wlast->next = wgt;
260
261 return wgt;
262 }
263
264
265 /**
266 * @brief Gets a Window by ID.
267 *
268 * @param wid ID of the window to get.
269 * @return Window matching wid.
270 */
window_wget(const unsigned int wid)271 Window* window_wget( const unsigned int wid )
272 {
273 Window *w;
274 if (windows == NULL)
275 return NULL;
276 for (w = windows; w != NULL; w = w->next)
277 if (w->id == wid)
278 return w;
279 return NULL;
280 }
281
282
283 /**
284 * @brief Gets a widget from window id and widgetname.
285 *
286 * @param wid ID of the window to get widget from.
287 * @param name Name of the widget to get.
288 * @return Widget matching name in the window.
289 */
window_getwgt(const unsigned int wid,const char * name)290 Widget* window_getwgt( const unsigned int wid, const char* name )
291 {
292 Window *wdw;
293 Widget *wgt;
294
295 /* Get the window. */
296 wdw = window_wget(wid);
297 if (wdw == NULL)
298 return NULL;
299
300 /* Find the widget. */
301 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
302 if (strcmp(wgt->name, name)==0)
303 return wgt;
304
305 WARN("Widget '%s' not found in window '%u'!", name, wid );
306 return NULL;
307 }
308
309
310 /**
311 * @brief Gets the dimensions of a window.
312 *
313 * @param wid ID of the window to get dimension of.
314 * @param[out] w Width of the window or -1 on error.
315 * @param[out] h Height of the window or -1 on error.
316 */
window_dimWindow(const unsigned int wid,int * w,int * h)317 void window_dimWindow( const unsigned int wid, int *w, int *h )
318 {
319 Window *wdw;
320
321 /* Get the window. */
322 wdw = window_wget(wid);
323 if (wdw == NULL) {
324 *w = -1;
325 *h = -1;
326 return;
327 }
328
329 /* Set dimensions. */
330 *w = wdw->w;
331 *h = wdw->h;
332 }
333
334
335 /**
336 * @brief Gets the dimensions of a widget.
337 *
338 * @param wid ID of the window that contains the widget.
339 * @param name Name of the widget to get dimensions of.
340 * @param[out] w Width of the widget or -1 on error.
341 * @param[out] h Height of the widget or -1 on error.
342 */
window_dimWidget(const unsigned int wid,char * name,int * w,int * h)343 void window_dimWidget( const unsigned int wid, char *name, int *w, int *h )
344 {
345 Widget *wgt;
346
347 /* Get widget. */
348 wgt = window_getwgt(wid, name);
349 if (wgt == NULL) {
350 *w = -1;
351 *h = -1;
352 return;
353 }
354
355 *w = wgt->w;
356 *h = wgt->h;
357 }
358
359
360 /**
361 * @brief Gets a widget's position.
362 *
363 * @param wid ID of the window to get widget from.
364 * @param name Name of the widget to get position of.
365 * @param[out] x X position of the widget.
366 * @param[out] y Y position of the widget.
367 */
window_posWidget(const unsigned int wid,char * name,int * x,int * y)368 void window_posWidget( const unsigned int wid,
369 char* name, int *x, int *y )
370 {
371 Widget *wgt;
372
373 /* Get widget. */
374 wgt = window_getwgt(wid,name);
375 if (wgt == NULL)
376 return;
377
378 /* Return position. */
379 if (x != NULL)
380 (*x) = wgt->x;
381 if (y != NULL)
382 (*y) = wgt->y;
383 }
384
385
386 /**
387 * @brief Moves a widget.
388 *
389 * @param wid ID of the window to get widget from.
390 * @param name Name of the widget to set position to.
391 * @param x New X position to set widget to.
392 * @param y New Y position to set widget to.
393 */
window_moveWidget(const unsigned int wid,char * name,int x,int y)394 void window_moveWidget( const unsigned int wid,
395 char* name, int x, int y )
396 {
397 Window *wdw;
398 Widget *wgt;
399
400 /* Get window. */
401 wdw = window_wget(wid);
402 if (wdw == NULL)
403 return;
404
405 /* Get widget. */
406 wgt = window_getwgt(wid,name);
407 if (wgt == NULL)
408 return;
409
410 /* Set position. */
411 toolkit_setPos( wdw, wgt, x, y );
412 }
413
414
415 /**
416 * @brief Checks to see if a window exists.
417 *
418 * @param wdwname Name of the window to check.
419 * @return 1 if it exists, 0 if it doesn't.
420 */
window_exists(const char * wdwname)421 int window_exists( const char* wdwname )
422 {
423 Window *w;
424 if (windows == NULL)
425 return 0;
426 for (w = windows; w != NULL; w = w->next)
427 if ((strcmp(w->name,wdwname)==0) && !window_isFlag(w, WINDOW_KILL))
428 return 1;
429 return 0; /* doesn't exist */
430 }
431
432
433 /**
434 * @brief Checks to see if a window with a certain ID exists.
435 *
436 * @param wdwname Name of the window to check.
437 * @return 1 if it exists, 0 if it doesn't.
438 */
window_existsID(const unsigned int wid)439 int window_existsID( const unsigned int wid )
440 {
441 Window *w;
442 if (windows == NULL)
443 return 0;
444 for (w = windows; w != NULL; w = w->next)
445 if ((w->id==wid) && !window_isFlag(w, WINDOW_KILL))
446 return 1;
447 return 0; /* doesn't exist */
448 }
449
450
451 /**
452 * @brief Gets the ID of a window.
453 *
454 * @param wdwname Name of the window to get ID of.
455 * @return ID of the window.
456 */
window_get(const char * wdwname)457 unsigned int window_get( const char* wdwname )
458 {
459 Window *w;
460 if (windows == NULL)
461 return 0;
462 for (w = windows; w != NULL; w = w->next)
463 if ((strcmp(w->name,wdwname)==0) && !window_isFlag(w, WINDOW_KILL))
464 return w->id;
465 return 0;
466 }
467
468
469 /**
470 * @brief Creates a window.
471 *
472 * @param name Name of the window to create.
473 * @param x X position of the window (-1 centers).
474 * @param y Y position of the window (-1 centers).
475 * @param w Width of the window (-1 fullscreen).
476 * @param h Height of the window (-1 fullscreen).
477 * @return Newly created window's ID.
478 */
window_create(const char * name,const int x,const int y,const int w,const int h)479 unsigned int window_create( const char* name,
480 const int x, const int y, const int w, const int h )
481 {
482 return window_createFlags( name, x, y, w, h, 0 );
483 }
484
485
486 /**
487 * @brief Creates a window.
488 *
489 * @param name Name of the window to create.
490 * @param x X position of the window (-1 centers).
491 * @param y Y position of the window (-1 centers).
492 * @param w Width of the window (-1 fullscreen).
493 * @param h Height of the window (-1 fullscreen).
494 * @param flags Initial flags to set.
495 * @return Newly created window's ID.
496 */
window_createFlags(const char * name,const int x,const int y,const int w,const int h,unsigned int flags)497 unsigned int window_createFlags( const char* name,
498 const int x, const int y, const int w, const int h, unsigned int flags )
499 {
500 Window *wcur, *wlast, *wdw;
501
502 /* Allocate memory. */
503 wdw = calloc( 1, sizeof(Window) );
504
505 const int wid = (++genwid); /* unique id */
506
507 /* Create the window. */
508
509 wdw->id = wid;
510 wdw->name = strdup(name);
511
512 /* Sane defaults. */
513 wdw->idgen = -1;
514 wdw->focus = -1;
515 wdw->xrel = -1.;
516 wdw->yrel = -1.;
517 wdw->flags = flags;
518 wdw->exposed = !window_isFlag(wdw, WINDOW_NOFOCUS);
519
520 /* Dimensions. */
521 wdw->w = (w == -1) ? SCREEN_W : (double) w;
522 wdw->h = (h == -1) ? SCREEN_H : (double) h;
523 if ((w == -1) && (h == -1)) {
524 window_setFlag( wdw, WINDOW_FULLSCREEN );
525 wdw->x = 0.;
526 wdw->y = 0.;
527 }
528 else
529 toolkit_setWindowPos( wdw, x, y );
530
531 if (toolkit_open==0) { /* toolkit is on */
532 input_mouseShow();
533 toolkit_open = 1; /* enable toolkit */
534 pause_game();
535 gl_defViewport(); /* Reset the default viewport */
536 }
537
538 /* Clear key repeat. */
539 toolkit_clearKey();
540
541 /* Add to list. */
542 wdw->next = NULL;
543 if (windows == NULL)
544 windows = wdw;
545 else {
546 /* Take focus from the old window. */
547 if (wdw->exposed) {
548 wcur = toolkit_getActiveWindow();
549 if (wcur != NULL)
550 toolkit_expose( wcur, 0 ); /* wcur is hidden */
551 }
552
553 wlast = NULL;
554 for (wcur = windows; wcur != NULL; wcur = wcur->next) {
555 if ((strcmp(wcur->name,name)==0) && !window_isFlag(wcur, WINDOW_KILL) &&
556 !window_isFlag(wcur, WINDOW_NOFOCUS))
557 WARN("Window with name '%s' already exists!",wcur->name);
558 wlast = wcur;
559 }
560
561 if (wlast != NULL)
562 wlast->next = wdw;
563 }
564
565 return wid;
566 }
567
568
569 /**
570 * @brief Sets a window as a window's parent.
571 *
572 * When a window's parent closes, it closes the window also.
573 *
574 * @param wid Window to set as child.
575 * @param parent Window to set as parent.
576 */
window_setParent(unsigned int wid,unsigned int parent)577 void window_setParent( unsigned int wid, unsigned int parent )
578 {
579 Window *wdw;
580
581 /* Get the window. */
582 wdw = window_wget( wid );
583 if (wdw == NULL)
584 return;
585
586 /* Set the parent. */
587 wdw->parent = parent;
588 }
589
590
591 /**
592 * @brief Gets the window's parent.
593 *
594 * @param wid Window to get parent of.
595 * @return Parent of the window or 0 on error.
596 */
window_getParent(unsigned int wid)597 unsigned int window_getParent( unsigned int wid )
598 {
599 Window *wdw;
600
601 /* Get the window. */
602 wdw = window_wget( wid );
603 if (wdw == NULL)
604 return 0;
605
606 /* Get the parent. */
607 return wdw->parent;
608 }
609
610
611 /**
612 * @brief Sets the default close function of the window.
613 *
614 * This function is called when the window is closed.
615 *
616 * @param wid Window to set close function of.
617 * @param Function to trigger when window is closed, parameter is window id
618 * and name.
619 */
window_onClose(unsigned int wid,void (* fptr)(unsigned int,char *))620 void window_onClose( unsigned int wid, void (*fptr)(unsigned int,char*) )
621 {
622 Window *wdw;
623
624 /* Get the window. */
625 wdw = window_wget( wid );
626 if (wdw == NULL)
627 return;
628
629 /* Set the close function. */
630 wdw->close_fptr = fptr;
631 }
632
633
634 /**
635 * @brief Sets the default accept function of the window.
636 *
637 * This function is called whenever 'enter' is pressed and the current widget
638 * does not catch it. NULL disables the accept function.
639 *
640 * @param wid ID of the window to set the accept function.
641 * @param accept Function to trigger when window is "accepted". Parameter
642 * passed is window name.
643 */
window_setAccept(const unsigned int wid,void (* accept)(unsigned int,char *))644 void window_setAccept( const unsigned int wid, void (*accept)(unsigned int,char*) )
645 {
646 Window *wdw;
647
648 /* Get the window. */
649 wdw = window_wget( wid );
650 if (wdw == NULL)
651 return;
652
653 /* Set the accept function. */
654 wdw->accept_fptr = accept;
655 }
656
657
658 /**
659 * @brief Sets the default cancel function of the window.
660 *
661 * This function is called whenever 'escape' is hit and the current widget
662 * does not catch it. NULL disables the cancel function.
663 *
664 * @param wid ID of the window to set cancel function.
665 * @param cancel Function to trigger when window is "cancelled". Parameter
666 * passed is window name.
667 */
window_setCancel(const unsigned int wid,void (* cancel)(unsigned int,char *))668 void window_setCancel( const unsigned int wid, void (*cancel)(unsigned int,char*) )
669 {
670 Window *wdw;
671
672 /* Get the window. */
673 wdw = window_wget( wid );
674 if (wdw == NULL)
675 return;
676
677 /* Set the cancel function. */
678 wdw->cancel_fptr = cancel;
679 }
680
681
682 /**
683 * @brief Sets custom data for a window.
684 *
685 * @param wid Window to set custom data of.
686 * @param data Data to set.
687 */
window_setData(unsigned int wid,void * data)688 void window_setData( unsigned int wid, void *data )
689 {
690 Window *wdw;
691
692 /* Get the window. */
693 wdw = window_wget( wid );
694 if (wdw == NULL)
695 return;
696
697 /* Set data. */
698 wdw->udata = data;
699 }
700
701
702 /**
703 * @brief Gets the custom data of a window.
704 *
705 * @param wid Window to get custom data of.
706 * @return The custom data or NULL if not applicable.
707 */
window_getData(unsigned int wid)708 void* window_getData( unsigned int wid )
709 {
710 Window *wdw;
711
712 /* Get the window. */
713 wdw = window_wget( wid );
714 if (wdw == NULL)
715 return NULL;
716
717 /* Get data. */
718 return wdw->udata;
719 }
720
721
722 /**
723 * @brief Sets or removes the border of a window.
724 *
725 * Default is enabled.
726 *
727 * @param wid ID of the window to enable/disable border.
728 * @param enable Whether or not to enable rendering of the border.
729 */
window_setBorder(unsigned int wid,int enable)730 void window_setBorder( unsigned int wid, int enable )
731 {
732 Window *wdw;
733
734 /* Get the window. */
735 wdw = window_wget( wid );
736 if (wdw == NULL)
737 return;
738
739 if (enable)
740 window_rmFlag( wdw, WINDOW_NOBORDER );
741 else
742 window_setFlag( wdw, WINDOW_NOBORDER );
743 }
744
745
746 /**
747 * @brief Sets the key handler for the window.
748 *
749 * This function is only called if neither the active widget nor the window
750 * itself grabs the input.
751 */
window_handleKeys(const unsigned int wid,int (* keyhandler)(unsigned int,SDLKey,SDLMod))752 void window_handleKeys( const unsigned int wid,
753 int (*keyhandler)(unsigned int,SDLKey,SDLMod) )
754 {
755 Window *wdw;
756
757 /* Get the window. */
758 wdw = window_wget( wid );
759 if (wdw == NULL)
760 return;
761
762 /* Set key event handler function. */
763 wdw->keyevent = keyhandler;
764 }
765
766
767 /**
768 * @brief Sets the event handler for the window.
769 *
770 * This function is called every time the window receives input.
771 */
window_handleEvents(const unsigned int wid,int (* eventhandler)(unsigned int,SDL_Event *))772 void window_handleEvents( const unsigned int wid,
773 int (*eventhandler)(unsigned int,SDL_Event*) )
774 {
775 Window *wdw;
776
777 /* Get the window. */
778 wdw = window_wget( wid );
779 if (wdw == NULL)
780 return;
781
782 /* Set key event handler function. */
783 wdw->eventevent = eventhandler;
784 }
785
786
787 /**
788 * @brief Destroys a widget.
789 *
790 * @param widget Widget to destroy.
791 */
widget_cleanup(Widget * widget)792 void widget_cleanup( Widget *widget )
793 {
794 /* Type specific clean up. */
795 if (widget->cleanup != NULL)
796 widget->cleanup(widget);
797
798 /* General freeing. */
799 free(widget->name);
800 }
801
802
803 /**
804 * @brief Helper function to automatically close the window calling it.
805 *
806 * @param wid Window to close.
807 * @param str Unused.
808 */
window_close(unsigned int wid,char * str)809 void window_close( unsigned int wid, char *str )
810 {
811 (void) str;
812
813 window_destroy( wid );
814 }
815
816
817 /**
818 * @brief Kills the window.
819 *
820 * @param wid ID of window to destroy.
821 * @return 1 if windows still need killing.
822 */
window_destroy(const unsigned int wid)823 void window_destroy( const unsigned int wid )
824 {
825 Window *wdw, *w;
826 if (windows == NULL)
827 return;
828 /* Destroy the window */
829 for (wdw = windows; wdw != NULL; wdw = wdw->next) {
830
831 /* Not the window we're looking for. */
832 if (wdw->id != wid)
833 continue;
834
835 /* Already being killed, skip. */
836 if (window_isFlag( wdw, WINDOW_KILL ))
837 continue;
838
839 /* Mark children for death. */
840 for (w = windows; w != NULL; w = w->next)
841 if (w->parent == wid)
842 window_destroy( w->id );
843
844 /* Mark for death. */
845 window_setFlag( wdw, WINDOW_KILL );
846 window_dead = 1;
847
848 /* Run the close function first. */
849 if (wdw->close_fptr != NULL)
850 wdw->close_fptr( wdw->id, wdw->name );
851 wdw->close_fptr = NULL;
852
853 /* Disable text input, etc. */
854 toolkit_focusClear( wdw );
855
856 w = toolkit_getActiveWindow();
857 if (w == NULL)
858 break;
859
860 toolkit_expose( w, 1 );
861 break;
862 }
863 }
864
865
866 /**
867 * @brief Kills the window.
868 *
869 * @param wid ID of window to destroy.
870 */
window_kill(Window * wdw)871 static void window_kill( Window *wdw )
872 {
873 Widget *wgt, *wgtkill;
874
875 /* Run the close function first. */
876 if (wdw->close_fptr != NULL)
877 wdw->close_fptr( wdw->id, wdw->name );
878 wdw->close_fptr = NULL;
879
880 /* Destroy the window. */
881 if (wdw->name)
882 free(wdw->name);
883 wgt = wdw->widgets;
884 while (wgt != NULL) {
885 wgtkill = wgt;
886 wgt = wgtkill->next;
887 widget_kill(wgtkill);
888 }
889 free(wdw);
890
891 /* Clear key repeat, since toolkit could miss the keyup event. */
892 toolkit_clearKey();
893 }
894
895
896 /**
897 * @brief Checks to see if a widget exists.
898 *
899 * @param wid Window to check widget in.
900 * @param wgtname Name of the widget to check;
901 * @return 1 if the widget exists.
902 */
widget_exists(const unsigned int wid,const char * wgtname)903 int widget_exists( const unsigned int wid, const char* wgtname )
904 {
905 Window *w = window_wget(wid);
906 Widget *wgt;
907
908 /* Get window. */
909 if (w==NULL) {
910 WARN("window '%d' does not exist", wid);
911 return 0;
912 }
913
914 /* Check for widget. */
915 for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next)
916 if (strcmp(wgtname, wgt->name)==0)
917 return !wgt_isFlag(wgt, WGT_FLAG_KILL);
918
919 return 0;
920 }
921
922
923 /**
924 * @brief Destroys a widget in a window.
925 *
926 * @param wid Window to destroy widget in.
927 * @param wgtname Name of the widget to destroy.
928 */
window_destroyWidget(unsigned int wid,const char * wgtname)929 void window_destroyWidget( unsigned int wid, const char* wgtname )
930 {
931 Window *wdw;
932 Widget *wgt;
933
934 /* Get the window. */
935 wdw = window_wget( wid );
936 if (wdw == NULL)
937 return;
938
939 /* Get the widget. */
940 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
941 if (strcmp(wgt->name, wgtname)==0)
942 break;
943
944 if (wgt == NULL) {
945 WARN("Widget '%s' not found in window '%s'", wgtname, wdw->name );
946 return;
947 }
948
949 /* Defocus. */
950 if (wdw->focus == wgt->id)
951 toolkit_defocusWidget( wdw, wgt );
952
953 /* There's dead stuff now. */
954 window_dead = 1;
955 wgt_rmFlag( wgt, WGT_FLAG_FOCUSED );
956 wgt_setFlag( wgt, WGT_FLAG_KILL );
957 }
958
959
960 /**
961 * @brief Destroy a widget really.
962 */
widget_kill(Widget * wgt)963 static void widget_kill( Widget *wgt )
964 {
965 /* Clean up. */
966 widget_cleanup(wgt);
967 free(wgt);
968 }
969
970
971 /**
972 * @brief Draws an outline.
973 *
974 * If lc is NULL, colour will be flat.
975 *
976 * @param x X position to draw at.
977 * @param y Y position to draw at.
978 * @param w Width.
979 * @param h Height.
980 * @param b Border width.
981 * @param thick Thickness of the border.
982 * @param c Colour.
983 * @param lc Light colour.
984 */
toolkit_drawOutlineThick(int x,int y,int w,int h,int b,int thick,const glColour * c,const glColour * lc)985 void toolkit_drawOutlineThick( int x, int y, int w, int h, int b,
986 int thick, const glColour* c, const glColour* lc )
987 {
988 GLshort tri[5][4];
989 glColour colours[10];
990
991 /* Set shade model. */
992 glShadeModel( (lc==NULL) ? GL_FLAT : GL_SMOOTH );
993
994 x -= b - thick;
995 w += 2 * (b - thick);
996 y -= b - thick;
997 h += 2 * (b - thick);
998 lc = lc ? lc : c;
999
1000 /* Left-up. */
1001 tri[0][0] = x; /* Inner */
1002 tri[0][1] = y;
1003 tri[0][2] = x-thick; /* Outer */
1004 tri[0][3] = y-thick;
1005 colours[0] = *lc;
1006 colours[1] = *lc;
1007
1008 /* Left-down. */
1009 tri[1][0] = x; /* Inner. */
1010 tri[1][1] = y + h;
1011 tri[1][2] = x-thick; /* Outer. */
1012 tri[1][3] = y + h+thick;
1013 colours[2] = *c;
1014 colours[3] = *c;
1015
1016 /* Right-down. */
1017 tri[2][0] = x + w; /* Inner. */
1018 tri[2][1] = y + h;
1019 tri[2][2] = x + w+thick; /* Outer. */
1020 tri[2][3] = y + h+thick;
1021 colours[4] = *c;
1022 colours[5] = *c;
1023
1024 /* Right-up. */
1025 tri[3][0] = x + w; /* Inner. */
1026 tri[3][1] = y;
1027 tri[3][2] = x + w+thick; /* Outer. */
1028 tri[3][3] = y-thick;
1029 colours[6] = *lc;
1030 colours[7] = *lc;
1031
1032 /* Left-up. */
1033 tri[4][0] = x; /* Inner */
1034 tri[4][1] = y;
1035 tri[4][2] = x-thick; /* Outer */
1036 tri[4][3] = y-thick;
1037 colours[8] = *lc;
1038 colours[9] = *lc;
1039
1040 /* Upload to the VBO. */
1041 gl_vboSubData( toolkit_vbo, 0, sizeof(tri), tri );
1042 gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset, sizeof(colours), colours );
1043
1044 /* Set up the VBO. */
1045 gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 );
1046 gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1047 toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1048
1049 /* Draw the VBO. */
1050 glDrawArrays( GL_TRIANGLE_STRIP, 0, 10 );
1051
1052 /* Deactivate VBO. */
1053 gl_vboDeactivate();
1054 }
1055
1056
1057 /**
1058 * @brief Draws an outline.
1059 *
1060 * If lc is NULL, colour will be flat.
1061 *
1062 * @param x X position to draw at.
1063 * @param y Y position to draw at.
1064 * @param w Width.
1065 * @param h Height.
1066 * @param b Border width.
1067 * @param c Colour.
1068 * @param lc Light colour.
1069 */
toolkit_drawOutline(int x,int y,int w,int h,int b,const glColour * c,const glColour * lc)1070 void toolkit_drawOutline( int x, int y, int w, int h, int b,
1071 const glColour* c, const glColour* lc )
1072 {
1073 GLshort lines[4][2];
1074 glColour colours[4];
1075
1076 /* Set shade model. */
1077 glShadeModel( (lc==NULL) ? GL_FLAT : GL_SMOOTH );
1078
1079 x -= b, w += 2 * b;
1080 y -= b, h += 2 * b;
1081 lc = lc ? lc : c;
1082
1083 /* Lines. */
1084 lines[0][0] = x - 1; /* left-up */
1085 lines[0][1] = y;
1086 colours[0] = *lc;
1087
1088 lines[1][0] = x; /* left-down */
1089 lines[1][1] = y + h;
1090 colours[1] = *c;
1091
1092 lines[2][0] = x + w; /* right-down */
1093 lines[2][1] = y + h;
1094 colours[2] = *c;
1095
1096 lines[3][0] = x + w; /* right-up */
1097 lines[3][1] = y;
1098 colours[3] = *lc;
1099
1100 /* Upload to the VBO. */
1101 gl_vboSubData( toolkit_vbo, 0, sizeof(lines), lines );
1102 gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset, sizeof(colours), colours );
1103
1104 /* Set up the VBO. */
1105 gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 );
1106 gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1107 toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1108
1109 /* Draw the VBO. */
1110 glDrawArrays( GL_LINE_LOOP, 0, 4 );
1111
1112 /* Deactivate VBO. */
1113 gl_vboDeactivate();
1114 }
1115 /**
1116 * @brief Draws a rectangle.
1117 *
1118 * If lc is NULL, colour will be flat.
1119 *
1120 * @param x X position to draw at.
1121 * @param y Y position to draw at.
1122 * @param w Width.
1123 * @param h Height.
1124 * @param c Colour.
1125 * @param lc Light colour.
1126 */
toolkit_drawRect(int x,int y,int w,int h,const glColour * c,const glColour * lc)1127 void toolkit_drawRect( int x, int y, int w, int h,
1128 const glColour* c, const glColour* lc )
1129 {
1130 GLshort vertex[4][2];
1131 glColour colours[4];
1132
1133 /* Set shade model. */
1134 glShadeModel( (lc) ? GL_SMOOTH : GL_FLAT );
1135
1136 lc = lc == NULL ? c : lc;
1137
1138 /* Set up vertices and colours. */
1139 vertex[0][0] = x; /* left-up */
1140 vertex[0][1] = y;
1141 colours[0] = *c;
1142
1143 vertex[1][0] = x; /* left-down */
1144 vertex[1][1] = y + h;
1145 colours[1] = *lc;
1146
1147 vertex[2][0] = x + w; /* right-up */
1148 vertex[2][1] = y;
1149 colours[2] = *c;
1150
1151 vertex[3][0] = x + w; /* right-down */
1152 vertex[3][1] = y + h;
1153 colours[3] = *lc;
1154
1155 /* Upload to the VBO. */
1156 gl_vboSubData( toolkit_vbo, 0, sizeof(vertex), vertex );
1157 gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset, sizeof(colours), colours );
1158
1159 /* Set up the VBO. */
1160 gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY,
1161 0, 2, GL_SHORT, 0 );
1162 gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1163 toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1164
1165 /* Draw the VBO. */
1166 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1167
1168 /* Deactivate VBO. */
1169 gl_vboDeactivate();
1170 }
1171 /**
1172 * @brief Draws an alt text.
1173 *
1174 * @param bx X position to draw at.
1175 * @param by Y position to draw at.
1176 * @param alt Text to draw.
1177 */
toolkit_drawAltText(int bx,int by,const char * alt)1178 void toolkit_drawAltText( int bx, int by, const char *alt )
1179 {
1180 double w, h;
1181 double x, y, o;
1182 glColour c;
1183 glColour c2;
1184 int i, l;
1185 char *buf;
1186
1187 /* Find the first newline. */
1188 i = 0;
1189 while (alt[i] != '\0' && alt[i] != '\n')
1190 i++;
1191
1192 buf = malloc(i + 1);
1193 strncpy(buf, alt, i);
1194 buf[i] = '\0'; /* Null-terminate. */
1195
1196 l = gl_printWidthRaw( &gl_smallFont, buf );
1197 free(buf);
1198
1199 /* Get dimensions, rounding width up to nearest 20 px increment. */
1200 w = CLAMP(160., 240., ceil( l / 20. ) * 20.);
1201 h = gl_printHeightRaw( &gl_smallFont, w, alt );
1202
1203 /* One check to make bigger. */
1204 if (h > 160. && w < 200.) {
1205 w = 200;
1206 h = gl_printHeightRaw( &gl_smallFont, w, alt );
1207 }
1208
1209 /* Choose position. */
1210 x = bx + 10.;
1211 y = by - h - gl_smallFont.h - 10.;
1212 if (y < -SCREEN_H/2+10) {
1213 o = -(SCREEN_H/2 + y) + 10;
1214 y += o;
1215 }
1216
1217 /* Set colours. */
1218 c.r = cGrey80.r;
1219 c.g = cGrey80.g;
1220 c.b = cGrey80.b;
1221 c.a = 0.8;
1222 c2.r = cGrey30.r;
1223 c2.g = cGrey30.g;
1224 c2.b = cGrey30.b;
1225 c2.a = 0.7;
1226 toolkit_drawRect( x-1, y-5, w+6, h+6, &c2, NULL );
1227 toolkit_drawRect( x-3, y-3, w+6, h+6, &c, NULL );
1228 gl_printTextRaw( &gl_smallFont, w, h, x, y, &cBlack, alt );
1229 }
1230
1231
1232 /**
1233 * @brief Renders a window border.
1234 *
1235 * @param w Window to render
1236 */
window_renderBorder(Window * w)1237 static void window_renderBorder( Window* w )
1238 {
1239 int i;
1240 GLshort cx, cy;
1241 double x, y;
1242 const glColour *lc, *c, *dc, *oc;
1243 GLshort vertex[31*4];
1244 GLfloat colours[31*4];
1245
1246 /* position */
1247 x = w->x;
1248 y = w->y;
1249
1250 /* colours */
1251 lc = &cGrey90;
1252 c = &cGrey70;
1253 dc = &cGrey50;
1254 oc = &cGrey30;
1255
1256 /*
1257 * Case fullscreen.
1258 */
1259 if (window_isFlag( w, WINDOW_FULLSCREEN )) {
1260 /* Background. */
1261 toolkit_drawRect( x, y, w->w, 0.6*w->h, dc, c );
1262 toolkit_drawRect( x, y+0.6*w->h, w->w, 0.4*w->h, c, NULL );
1263 /* Name. */
1264 gl_printMidRaw( &gl_defFont, w->w,
1265 x,
1266 y + w->h - 20.,
1267 &cBlack, w->name );
1268 return;
1269 }
1270
1271 /*
1272 * window shaded bg
1273 */
1274 /* main body */
1275 toolkit_drawRect( x+21, y, w->w-42., 0.6*w->h, dc, c );
1276 toolkit_drawRect( x+21, y+0.6*w->h, w->w-42., 0.4*w->h, c, NULL );
1277
1278 glShadeModel(GL_SMOOTH);
1279 /* Both sides. */
1280 gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1281 toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1282 gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 );
1283 /* Colour is shared. */
1284 colours[0] = c->r;
1285 colours[1] = c->g;
1286 colours[2] = c->b;
1287 colours[3] = c->a;
1288 for (i=0; i<7; i++) {
1289 colours[4 + 4*i + 0] = dc->r;
1290 colours[4 + 4*i + 1] = dc->g;
1291 colours[4 + 4*i + 2] = dc->b;
1292 colours[4 + 4*i + 3] = dc->a;
1293 }
1294 for (i=0; i<8; i++) {
1295 colours[32 + 4*i + 0] = c->r;
1296 colours[32 + 4*i + 1] = c->g;
1297 colours[32 + 4*i + 2] = c->b;
1298 colours[32 + 4*i + 3] = c->a;
1299 }
1300 gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset,
1301 sizeof(GLfloat) * 4*16, colours );
1302 /* Left side vertex. */
1303 cx = x;
1304 cy = y;
1305 vertex[0] = cx + 21;
1306 vertex[1] = cy + 0.6*w->h;
1307 vertex[2] = cx + 21;
1308 vertex[3] = cy;
1309 vertex[4] = cx + 15;
1310 vertex[5] = cy + 1;
1311 vertex[6] = cx + 10;
1312 vertex[7] = cy + 3;
1313 vertex[8] = cx + 6;
1314 vertex[9] = cy + 6;
1315 vertex[10] = cx + 3;
1316 vertex[11] = cy + 10;
1317 vertex[12] = cx + 1;
1318 vertex[13] = cy + 15;
1319 vertex[14] = cx;
1320 vertex[15] = cy + 21;
1321 vertex[16] = cx;
1322 vertex[17] = cy + 0.6*w->h;
1323 vertex[18] = cx;
1324 cy = y + w->h;
1325 vertex[19] = cy - 21;
1326 vertex[20] = cx + 1;
1327 vertex[21] = cy - 15;
1328 vertex[22] = cx + 3;
1329 vertex[23] = cy - 10;
1330 vertex[24] = cx + 6;
1331 vertex[25] = cy - 6;
1332 vertex[26] = cx + 10;
1333 vertex[27] = cy - 3;
1334 vertex[28] = cx + 15;
1335 vertex[29] = cy - 1;
1336 vertex[30] = cx + 21;
1337 vertex[31] = cy;
1338 gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*16, vertex );
1339 glDrawArrays( GL_TRIANGLE_FAN, 0, 16 );
1340 /* Right side vertex. */
1341 cx = x + w->w;
1342 cy = y;
1343 vertex[0] = cx - 21;
1344 vertex[1] = cy + 0.6*w->h;
1345 vertex[2] = cx - 21;
1346 vertex[3] = cy;
1347 vertex[4] = cx - 15;
1348 vertex[5] = cy + 1;
1349 vertex[6] = cx - 10;
1350 vertex[7] = cy + 3;
1351 vertex[8] = cx - 6;
1352 vertex[9] = cy + 6;
1353 vertex[10] = cx - 3;
1354 vertex[11] = cy + 10;
1355 vertex[12] = cx - 1;
1356 vertex[13] = cy + 15;
1357 vertex[14] = cx;
1358 vertex[15] = cy + 21;
1359 vertex[16] = cx;
1360 vertex[17] = cy + 0.6*w->h;
1361 vertex[18] = cx;
1362 cy = y + w->h;
1363 vertex[19] = cy - 21;
1364 vertex[20] = cx - 1;
1365 vertex[21] = cy - 15;
1366 vertex[22] = cx - 3;
1367 vertex[23] = cy - 10;
1368 vertex[24] = cx - 6;
1369 vertex[25] = cy - 6;
1370 vertex[26] = cx - 10;
1371 vertex[27] = cy - 3;
1372 vertex[28] = cx - 15;
1373 vertex[29] = cy - 1;
1374 vertex[30] = cx - 21;
1375 vertex[31] = cy;
1376 gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*16, vertex );
1377 glDrawArrays( GL_TRIANGLE_FAN, 0, 16 );
1378
1379
1380 /*
1381 * inner outline
1382 */
1383 /* Colour. */
1384 for (i=0; i<7; i++) {
1385 colours[4*i + 0] = c->r;
1386 colours[4*i + 1] = c->g;
1387 colours[4*i + 2] = c->b;
1388 colours[4*i + 3] = c->a;
1389 }
1390 for (; i<7+16; i++) {
1391 colours[4*i + 0] = lc->r;
1392 colours[4*i + 1] = lc->g;
1393 colours[4*i + 2] = lc->b;
1394 colours[4*i + 3] = lc->a;
1395 }
1396 for (; i<7+16+8; i++) {
1397 colours[4*i + 0] = c->r;
1398 colours[4*i + 1] = c->g;
1399 colours[4*i + 2] = c->b;
1400 colours[4*i + 3] = c->a;
1401 }
1402 gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset,
1403 sizeof(GLfloat) * 4*31, colours );
1404 /* Vertex. */
1405 /* Left side. */
1406 cx = x + 1;
1407 cy = y + 1;
1408 vertex[0] = cx + 21;
1409 vertex[1] = cy;
1410 vertex[2] = cx + 15;
1411 vertex[3] = cy + 1;
1412 vertex[4] = cx + 10;
1413 vertex[5] = cy + 3;
1414 vertex[6] = cx + 6;
1415 vertex[7] = cy + 6;
1416 vertex[8] = cx + 3;
1417 vertex[9] = cy + 10;
1418 vertex[10] = cx + 1;
1419 vertex[11] = cy + 15;
1420 vertex[12] = cx;
1421 vertex[13] = cy + 21;
1422 vertex[14] = cx;
1423 vertex[15] = cy + 0.6*w->h - 1;
1424 cy = y + w->h - 1;
1425 vertex[16] = cx;
1426 vertex[17] = cy - 21;
1427 vertex[18] = cx + 1;
1428 vertex[19] = cy - 15;
1429 vertex[20] = cx + 3;
1430 vertex[21] = cy - 10;
1431 vertex[22] = cx + 6;
1432 vertex[23] = cy - 6;
1433 vertex[24] = cx + 10;
1434 vertex[25] = cy - 3;
1435 vertex[26] = cx + 15;
1436 vertex[27] = cy - 1;
1437 vertex[28] = cx + 21;
1438 vertex[29] = cy;
1439 /* Right side via top. */
1440 cx = x + w->w - 1;
1441 cy = y + w->h - 1;
1442 vertex[30] = cx - 21;
1443 vertex[31] = cy;
1444 vertex[32] = cx - 15;
1445 vertex[33] = cy - 1;
1446 vertex[34] = cx - 10;
1447 vertex[35] = cy - 3;
1448 vertex[36] = cx - 6;
1449 vertex[37] = cy - 6;
1450 vertex[38] = cx - 3;
1451 vertex[39] = cy - 10;
1452 vertex[40] = cx - 1;
1453 vertex[41] = cy - 15;
1454 vertex[42] = cx;
1455 vertex[43] = cy - 21;
1456 cy = y + 1;
1457 vertex[44] = cx;
1458 vertex[45] = cy + 0.6*w->h - 1;
1459 vertex[46] = cx;
1460 vertex[47] = cy + 21;
1461 vertex[48] = cx - 1;
1462 vertex[49] = cy + 15;
1463 vertex[50] = cx - 3;
1464 vertex[51] = cy + 10;
1465 vertex[52] = cx - 6;
1466 vertex[53] = cy + 6;
1467 vertex[54] = cx - 10;
1468 vertex[55] = cy + 3;
1469 vertex[56] = cx - 15;
1470 vertex[57] = cy + 1;
1471 vertex[58] = cx - 21;
1472 vertex[59] = cy;
1473 cx = x + 1;
1474 cy = y + 1;
1475 vertex[60] = cx + 21;
1476 vertex[61] = cy;
1477 gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*31, vertex );
1478 glDrawArrays( GL_LINE_LOOP, 0, 31 );
1479
1480
1481 /*
1482 * outer outline
1483 */
1484 glShadeModel(GL_FLAT);
1485 /* Colour. */
1486 for (i=0; i<31; i++) {
1487 colours[4*i + 0] = oc->r;
1488 colours[4*i + 1] = oc->g;
1489 colours[4*i + 2] = oc->b;
1490 colours[4*i + 3] = oc->a;
1491 }
1492 gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset,
1493 sizeof(GLfloat) * 4*31, colours );
1494 /* Vertex. */
1495 /* Left side. */
1496 cx = x;
1497 cy = y;
1498 vertex[0] = cx + 21;
1499 vertex[1] = cy;
1500 vertex[2] = cx + 15;
1501 vertex[3] = cy + 1;
1502 vertex[4] = cx + 10;
1503 vertex[5] = cy + 3;
1504 vertex[6] = cx + 6;
1505 vertex[7] = cy + 6;
1506 vertex[8] = cx + 3;
1507 vertex[9] = cy + 10;
1508 vertex[10] = cx + 1;
1509 vertex[11] = cy + 15;
1510 vertex[12] = cx;
1511 vertex[13] = cy + 21;
1512 vertex[14] = cx;
1513 vertex[15] = cy + 0.6*w->h;
1514 cy = y + w->h;
1515 vertex[16] = cx;
1516 vertex[17] = cy - 21;
1517 vertex[18] = cx + 1;
1518 vertex[19] = cy - 15;
1519 vertex[20] = cx + 3;
1520 vertex[21] = cy - 10;
1521 vertex[22] = cx + 6;
1522 vertex[23] = cy - 6;
1523 vertex[24] = cx + 10;
1524 vertex[25] = cy - 3;
1525 vertex[26] = cx + 15;
1526 vertex[27] = cy - 1;
1527 vertex[28] = cx + 21;
1528 vertex[29] = cy;
1529 /* Right side via top. */
1530 cx = x + w->w;
1531 cy = y + w->h;
1532 vertex[30] = cx - 21;
1533 vertex[31] = cy;
1534 vertex[32] = cx - 15;
1535 vertex[33] = cy - 1;
1536 vertex[34] = cx - 10;
1537 vertex[35] = cy - 3;
1538 vertex[36] = cx - 6;
1539 vertex[37] = cy - 6;
1540 vertex[38] = cx - 3;
1541 vertex[39] = cy - 10;
1542 vertex[40] = cx - 1;
1543 vertex[41] = cy - 15;
1544 vertex[42] = cx;
1545 vertex[43] = cy - 21;
1546 cy = y;
1547 vertex[44] = cx;
1548 vertex[45] = cy + 0.6*w->h;
1549 vertex[46] = cx;
1550 vertex[47] = cy + 21;
1551 vertex[48] = cx - 1;
1552 vertex[49] = cy + 15;
1553 vertex[50] = cx - 3;
1554 vertex[51] = cy + 10;
1555 vertex[52] = cx - 6;
1556 vertex[53] = cy + 6;
1557 vertex[54] = cx - 10;
1558 vertex[55] = cy + 3;
1559 vertex[56] = cx - 15;
1560 vertex[57] = cy + 1;
1561 vertex[58] = cx - 21;
1562 vertex[59] = cy;
1563 cx = x;
1564 cy = y;
1565 vertex[60] = cx + 21;
1566 vertex[61] = cy;
1567 gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*31, vertex );
1568 glDrawArrays( GL_LINE_LOOP, 0, 31 );
1569
1570 /* Clean up. */
1571 gl_vboDeactivate();
1572
1573 /*
1574 * render window name
1575 */
1576 gl_printMidRaw( &gl_defFont, w->w,
1577 x,
1578 y + w->h - 20.,
1579 &cBlack, w->name );
1580 }
1581
1582
1583 /**
1584 * @brief Renders a window.
1585 *
1586 * @param w Window to render.
1587 */
window_render(Window * w)1588 void window_render( Window *w )
1589 {
1590 double x, y, wid, hei;
1591 Widget *wgt;
1592
1593 /* Do not render dead windows. */
1594 if (window_isFlag( w, WINDOW_KILL ))
1595 return;
1596
1597 /* position */
1598 x = w->x;
1599 y = w->y;
1600
1601 /* See if needs border. */
1602 if (!window_isFlag( w, WINDOW_NOBORDER ))
1603 window_renderBorder(w);
1604
1605 /*
1606 * widgets
1607 */
1608 for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next)
1609 if (wgt->render != NULL)
1610 wgt->render( wgt, x, y );
1611
1612 /*
1613 * focused widget
1614 */
1615 if (w->focus != -1) {
1616 wgt = toolkit_getFocus( w );
1617 if (wgt == NULL)
1618 return;
1619 x += wgt->x;
1620 y += wgt->y;
1621 wid = wgt->w;
1622 hei = wgt->h;
1623 toolkit_drawOutline( x, y, wid, hei, 3, &cBlack, NULL );
1624 }
1625 }
1626
1627
1628 /**
1629 * @brief Renders the window overlays.
1630 *
1631 * @param w Window to render overlays of.
1632 */
window_renderOverlay(Window * w)1633 void window_renderOverlay( Window *w )
1634 {
1635 double x, y;
1636 Widget *wgt;
1637
1638 /* Do not render dead windows. */
1639 if (window_isFlag( w, WINDOW_KILL ))
1640 return;
1641
1642 /* position */
1643 x = w->x;
1644 y = w->y;
1645
1646 /*
1647 * overlays
1648 */
1649 for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next)
1650 if (wgt->renderOverlay != NULL)
1651 wgt->renderOverlay( wgt, x, y );
1652 }
1653
1654
1655 /**
1656 * @brief Draws a scrollbar.
1657 *
1658 * @param x X position of scrollbar.
1659 * @param y Y position of scrollbar.
1660 * @param w Width of the scrollbar.
1661 * @param h Height of the scrollbar.
1662 * @param pos Position at [0:1].
1663 */
toolkit_drawScrollbar(int x,int y,int w,int h,double pos)1664 void toolkit_drawScrollbar( int x, int y, int w, int h, double pos )
1665 {
1666 double sy;
1667
1668 /* scrollbar background */
1669 toolkit_drawRect( x, y, w, h, toolkit_colDark, toolkit_col );
1670 /* toolkit_drawOutline( x, y, w, h, 0., toolkit_colDark, NULL ); */
1671 /* toolkit_drawOutline( x, y, w, h, 0., toolkit_colLight, toolkit_col ); */
1672
1673 /* Bar itself. */
1674 sy = y + (h - 30.) * (1.-pos);
1675 toolkit_drawRect( x, sy, w, 30., toolkit_colLight, toolkit_col );
1676 toolkit_drawOutline( x + 1, sy, w - 1, 30., 0., toolkit_colDark, NULL );
1677 }
1678
1679
1680 /**
1681 * @brief Renders the windows.
1682 */
toolkit_render(void)1683 void toolkit_render (void)
1684 {
1685 Window *w;
1686
1687 /* Render base. */
1688 for (w = windows; w!=NULL; w = w->next) {
1689 if (!window_isFlag(w, WINDOW_NORENDER) &&
1690 !window_isFlag(w, WINDOW_KILL)) {
1691 window_render(w);
1692 window_renderOverlay(w);
1693 }
1694 }
1695 }
1696
1697
1698 /**
1699 * @brief Toolkit input handled here.
1700 *
1701 * @param event Event to handle.
1702 * @return 1 if input was used, 0 if it wasn't.
1703 */
toolkit_input(SDL_Event * event)1704 int toolkit_input( SDL_Event* event )
1705 {
1706 Window *wdw;
1707
1708 /* Get window that can be focused. */
1709 wdw = toolkit_getActiveWindow();
1710 if (wdw == NULL)
1711 return 0;
1712
1713 /* Pass event to window. */
1714 return toolkit_inputWindow( wdw, event, 1 );
1715 }
1716
1717
1718 /**
1719 * @brief Toolkit window input is handled here.
1720 */
toolkit_inputWindow(Window * wdw,SDL_Event * event,int purge)1721 int toolkit_inputWindow( Window *wdw, SDL_Event *event, int purge )
1722 {
1723 int ret;
1724 Widget *wgt;
1725 ret = 0;
1726
1727 /* See if widget needs event. */
1728 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
1729 if (wgt_isFlag( wgt, WGT_FLAG_RAWINPUT )) {
1730 if (wgt->rawevent != NULL) {
1731 ret = wgt->rawevent( wgt, event );
1732 if (ret != 0)
1733 return ret;
1734 }
1735 }
1736 }
1737
1738 /* Event handler. */
1739 if (wdw->eventevent != NULL)
1740 wdw->eventevent( wdw->id, event );
1741
1742 /* Hack in case window got destroyed in eventevent. */
1743 if (!window_isFlag(wdw, WINDOW_KILL)) {
1744
1745 /* Pass it on. */
1746 switch (event->type) {
1747 case SDL_MOUSEMOTION:
1748 case SDL_MOUSEBUTTONDOWN:
1749 case SDL_MOUSEBUTTONUP:
1750 #if SDL_VERSION_ATLEAST(2,0,0)
1751 case SDL_MOUSEWHEEL:
1752 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1753 ret |= toolkit_mouseEvent(wdw, event);
1754 break;
1755
1756 case SDL_KEYDOWN:
1757 case SDL_KEYUP:
1758 ret |= toolkit_keyEvent(wdw, event);
1759 break;
1760
1761 #if SDL_VERSION_ATLEAST(2,0,0)
1762 case SDL_TEXTINPUT:
1763 ret |= toolkit_textEvent(wdw, event);
1764 break;
1765 case SDL_TEXTEDITING:
1766 break;
1767 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1768 }
1769 }
1770
1771 /* Clean up the dead if needed. */
1772 if (purge && !dialogue_isOpen()) { /* Hack, since dialogues use secondary loop. */
1773 if (toolkit_delayCounter > 0)
1774 toolkit_delayCounter--;
1775 else
1776 toolkit_purgeDead();
1777 }
1778
1779 return ret; /* don't block input */
1780 }
1781
1782
1783 /**
1784 * @brief Translates the mouse coordinates.
1785 *
1786 * @param w Window to translate coords for.
1787 * @param event Event to translate coords.
1788 * @param[out] x Resulting X coord in window space.
1789 * @param[out] y Resulting Y coord in window space.
1790 * @param[out] rx Relative X movement (only valid for motion).
1791 * @param[out] ry Relative Y movement (only valid for motion).
1792 * @return The type of the event.
1793 */
toolkit_inputTranslateCoords(Window * w,SDL_Event * event,int * x,int * y,int * rx,int * ry)1794 Uint32 toolkit_inputTranslateCoords( Window *w, SDL_Event *event,
1795 int *x, int *y, int *rx, int *ry )
1796 {
1797 /* Extract the position as event. */
1798 if (event->type==SDL_MOUSEMOTION) {
1799 *x = event->motion.x;
1800 *y = event->motion.y;
1801 }
1802 else if ((event->type==SDL_MOUSEBUTTONDOWN) || (event->type==SDL_MOUSEBUTTONUP)) {
1803 *x = event->button.x;
1804 *y = event->button.y;
1805 }
1806 #if SDL_VERSION_ATLEAST(2,0,0)
1807 else if (event->type == SDL_MOUSEWHEEL)
1808 SDL_GetMouseState( x, y );
1809 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1810
1811 /* Translate offset. */
1812 gl_windowToScreenPos( x, y, *x, *y );
1813
1814 /* Transform to relative to window. */
1815 *x -= w->x;
1816 *y -= w->y;
1817
1818 /* Relative only matter if mouse motion. */
1819 if (event->type==SDL_MOUSEMOTION) {
1820 *ry = (double)event->motion.yrel * gl_screen.mxscale;
1821 *rx = (double)event->motion.xrel * gl_screen.myscale;
1822 }
1823 else {
1824 *ry = 0;
1825 *rx = 0;
1826 }
1827
1828 return event->type;
1829 }
1830
1831
1832 /**
1833 * @brief Handles the mouse events.
1834 *
1835 * @param wdw Window receiving the mouse event.
1836 * @param event Mouse event to handle.
1837 */
toolkit_mouseEvent(Window * w,SDL_Event * event)1838 static int toolkit_mouseEvent( Window *w, SDL_Event* event )
1839 {
1840 Widget *wgt;
1841 int x, y, rx, ry, ret;
1842
1843 /* Translate mouse coords. */
1844 toolkit_inputTranslateCoords( w, event, &x, &y, &rx, &ry );
1845
1846 /* Check each widget. */
1847 ret = 0;
1848 for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next) {
1849
1850 /* custom widgets take it from here */
1851 if (wgt->type==WIDGET_CUST) {
1852 if (wgt->dat.cst.mouse)
1853 ret |= wgt->dat.cst.mouse( w->id, event, x-wgt->x, y-wgt->y, wgt->w, wgt->h,
1854 wgt->dat.cst.userdata );
1855 }
1856 else
1857 ret |= toolkit_mouseEventWidget( w, wgt, event, x, y, rx, ry );
1858 }
1859
1860 return ret;
1861 }
1862
1863
1864 /**
1865 * @brief Handle widget mouse input.
1866 *
1867 * @param w Window to which widget belongs.
1868 * @param wgt Widget receiving event.
1869 * @param event Event received by the window.
1870 */
toolkit_mouseEventWidget(Window * w,Widget * wgt,SDL_Event * event,int x,int y,int rx,int ry)1871 static int toolkit_mouseEventWidget( Window *w, Widget *wgt,
1872 SDL_Event *event, int x, int y, int rx, int ry )
1873 {
1874 int ret, inbounds;
1875 Uint8 button;
1876
1877 /* Widget translations. */
1878 x -= wgt->x;
1879 y -= wgt->y;
1880
1881 /* Handle mouse event. */
1882 if (event->type == SDL_MOUSEMOTION)
1883 button = event->motion.state;
1884 else
1885 button = event->button.button;
1886
1887 /* Check inbounds. */
1888 inbounds = !((x < 0) || (x >= wgt->w) || (y < 0) || (y >= wgt->h));
1889
1890 /* Regular widgets. */
1891 ret = 0;
1892 switch (event->type) {
1893 case SDL_MOUSEMOTION:
1894 /* Change the status of the widget if mouse isn't down. */
1895
1896 /* Not scrolling. */
1897 if (wgt->status != WIDGET_STATUS_SCROLLING) {
1898 if (inbounds) {
1899 if (wgt->status != WIDGET_STATUS_MOUSEDOWN)
1900 wgt->status = WIDGET_STATUS_MOUSEOVER;
1901 }
1902 else
1903 wgt->status = WIDGET_STATUS_NORMAL;
1904 }
1905 else
1906 inbounds = 1; /* Scrolling is always inbounds. */
1907
1908 /* If always gets the event. */
1909 if (wgt_isFlag( wgt, WGT_FLAG_ALWAYSMMOVE ))
1910 inbounds = 1;
1911
1912 /* Try to give the event to the widget. */
1913 if (inbounds && (wgt->mmoveevent != NULL))
1914 ret |= (*wgt->mmoveevent)( wgt, x, y, rx, ry );
1915
1916 break;
1917
1918 #if SDL_VERSION_ATLEAST(2,0,0)
1919 case SDL_MOUSEWHEEL:
1920 if (!inbounds)
1921 break;
1922
1923 /* Try to give the event to the widget. */
1924 if (wgt->mwheelevent != NULL)
1925 ret |= (*wgt->mwheelevent)( wgt, event->wheel );
1926
1927 break;
1928 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1929
1930 case SDL_MOUSEBUTTONDOWN:
1931 if (!inbounds)
1932 break;
1933
1934 /* Update the status. */
1935 if (button == SDL_BUTTON_LEFT)
1936 wgt->status = WIDGET_STATUS_MOUSEDOWN;
1937
1938 if (toolkit_isFocusable(wgt)) {
1939 toolkit_focusClear( w );
1940 toolkit_focusWidget( w, wgt );
1941 }
1942
1943 /* Try to give the event to the widget. */
1944 if (wgt->mclickevent != NULL)
1945 ret |= (*wgt->mclickevent)( wgt, button, x, y );
1946 break;
1947
1948 case SDL_MOUSEBUTTONUP:
1949 /* Since basically only buttons are handled here, we ignore
1950 * it all except the left mouse button. */
1951 if (button != SDL_BUTTON_LEFT)
1952 break;
1953
1954 if (wgt->status==WIDGET_STATUS_MOUSEDOWN) {
1955 /* Soft-disabled buttons will run anyway. */
1956 if ((wgt->type==WIDGET_BUTTON) && ((wgt->dat.btn.disabled==0) ||
1957 (wgt->dat.btn.softdisable))) {
1958 if (wgt->dat.btn.fptr==NULL)
1959 DEBUG("Toolkit: Button '%s' of Window '%s' "
1960 "doesn't have a function trigger",
1961 wgt->name, w->name );
1962 else {
1963 (*wgt->dat.btn.fptr)(w->id, wgt->name);
1964 ret = 1;
1965 }
1966 }
1967 }
1968
1969 /* Signal scroll done if necessary. */
1970 if ((wgt->status == WIDGET_STATUS_SCROLLING) && (wgt->scrolldone != NULL))
1971 wgt->scrolldone( wgt );
1972
1973 /* Always goes normal unless is below mouse. */
1974 if (inbounds)
1975 wgt->status = WIDGET_STATUS_MOUSEOVER;
1976 else
1977 wgt->status = WIDGET_STATUS_NORMAL;
1978
1979 break;
1980 }
1981
1982 return ret;
1983 }
1984
1985
1986 /**
1987 * @brief Maps modifier keysyms (ctrl, alt, shift) to SDLMods.
1988 *
1989 * @param key Key to convert.
1990 * @return The SDLMod corresponding to the key, or 0 if none correspond.
1991 */
toolkit_mapMod(SDLKey key)1992 static SDLMod toolkit_mapMod( SDLKey key )
1993 {
1994 switch(key) {
1995 case SDLK_LCTRL:
1996 return KMOD_LCTRL;
1997 case SDLK_RCTRL:
1998 return KMOD_RCTRL;
1999 case SDLK_LALT:
2000 return KMOD_LALT;
2001 case SDLK_RALT:
2002 return KMOD_RALT;
2003 case SDLK_LSHIFT:
2004 return KMOD_LSHIFT;
2005 case SDLK_RSHIFT:
2006 return KMOD_RSHIFT;
2007 default:
2008 return 0;
2009 }
2010 }
2011
2012 /**
2013 * @brief Registers a key as down (for key repetition).
2014 *
2015 * @param key Key to register as down.
2016 */
toolkit_regKey(SDLKey key,SDLKey c)2017 static void toolkit_regKey( SDLKey key, SDLKey c )
2018 {
2019 SDLMod mod;
2020
2021 /* See if our key is in fact a modifier key, and if it is, convert it to a mod.
2022 * If it is indeed a mod, do not register a new key but add the modifier to the mod mask instead.
2023 */
2024 mod = toolkit_mapMod(key);
2025 if (mod)
2026 input_mod |= mod;
2027 /* Don't reset values on repeat keydowns. */
2028 else if (input_key != key) {
2029 input_key = key;
2030 input_keyTime = SDL_GetTicks();
2031 input_keyCounter = 0;
2032 input_text = nstd_checkascii(c) ? c : 0;
2033 }
2034 }
2035
2036 /**
2037 * @brief Unregisters a key.
2038 *
2039 * @param key Key to unregister.
2040 */
toolkit_unregKey(SDLKey key)2041 static void toolkit_unregKey( SDLKey key )
2042 {
2043 SDLMod mod;
2044
2045 /* See if our key is in fact a modifier key, and if it is, convert it to a mod.
2046 * If it is indeed a mod, do not unregister the key but subtract the modifier from the mod mask instead.
2047 */
2048 mod = toolkit_mapMod(key);
2049 if (mod)
2050 input_mod &= ~mod;
2051 else
2052 toolkit_clearKey();
2053 }
2054
2055 /**
2056 * @brief Clears the registered keys.
2057 */
toolkit_clearKey(void)2058 void toolkit_clearKey (void)
2059 {
2060 input_key = 0;
2061 input_keyTime = 0;
2062 input_keyCounter = 0;
2063 input_text = 0;
2064 }
2065 /**
2066 * @brief Handles keyboard events.
2067 *
2068 * @param wdw Window receiving the key event.
2069 * @param event Keyboard event to handle.
2070 * @return 1 if the event is used, 0 if it isn't.
2071 */
toolkit_keyEvent(Window * wdw,SDL_Event * event)2072 static int toolkit_keyEvent( Window *wdw, SDL_Event* event )
2073 {
2074 Widget *wgt;
2075 SDLKey key;
2076 SDLMod mod;
2077 int handled = 0;
2078 #if !SDL_VERSION_ATLEAST(2,0,0)
2079 char buf[2];
2080 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
2081
2082 /* Event info. */
2083 key = event->key.keysym.sym;
2084 mod = event->key.keysym.mod;
2085
2086 /* Hack to simulate key repetition */
2087 if (event->type == SDL_KEYDOWN)
2088 #if SDL_VERSION_ATLEAST(2,0,0)
2089 toolkit_regKey(key, key);
2090 #else /* SDL_VERSION_ATLEAST(2,0,0) */
2091 toolkit_regKey(key, event->key.keysym.unicode);
2092 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
2093 else if (event->type == SDL_KEYUP)
2094 toolkit_unregKey(key);
2095
2096 /* See if window is valid. */
2097 if (wdw == NULL)
2098 return 0;
2099
2100 /* Get widget. */
2101 wgt = toolkit_getFocus( wdw );
2102
2103 /* We only want keydown from now on. */
2104 if (event->type != SDL_KEYDOWN)
2105 return 0;
2106
2107 /* Trigger event function if exists. */
2108 if (wgt != NULL) {
2109 if (wgt->keyevent != NULL) {
2110 if (wgt->keyevent( wgt, input_key, input_mod ))
2111 return 1;
2112 }
2113 #if !SDL_VERSION_ATLEAST(2,0,0)
2114 if (wgt->textevent != NULL) {
2115 buf[0] = key & 0x7f;
2116 buf[0] = event->key.keysym.unicode & 0x7f;
2117 buf[1] = '\0';
2118 if ((*wgt->textevent)( wgt, buf ))
2119 return 1;
2120 }
2121 #endif /* !SDL_VERSION_ATLEAST(2,0,0) */
2122 }
2123
2124 /* Handle button hotkeys. */
2125 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
2126 if ((wgt->type == WIDGET_BUTTON) && (wgt->dat.btn.key != 0) &&
2127 (wgt->dat.btn.key == input_key))
2128 return (wgt->keyevent( wgt, SDLK_RETURN, input_mod ));
2129
2130 /* Handle other cases where event might be used by the window. */
2131 switch (key) {
2132 case SDLK_RETURN:
2133 case SDLK_KP_ENTER:
2134 if (wdw->accept_fptr != NULL) {
2135 wdw->accept_fptr( wdw->id, wdw->name );
2136 return 1;
2137 }
2138 break;
2139
2140 case SDLK_ESCAPE:
2141 if (wdw->cancel_fptr != NULL) {
2142 wdw->cancel_fptr( wdw->id, wdw->name );
2143 return 1;
2144 }
2145 break;
2146
2147 default:
2148 break;
2149 }
2150
2151 /* Finally the stuff gets passed to the custom key handler if it's defined. */
2152 if (wdw->keyevent != NULL)
2153 handled = (*wdw->keyevent)( wdw->id, input_key, input_mod );
2154
2155 /* Placed here so it can be overriden in console for tab completion. */
2156 if (!handled && key == SDLK_TAB) {
2157 if (mod & (KMOD_LSHIFT | KMOD_RSHIFT))
2158 toolkit_prevFocus( wdw );
2159 else
2160 toolkit_nextFocus( wdw );
2161 }
2162
2163 return 0;
2164 }
2165 #if SDL_VERSION_ATLEAST(2,0,0)
toolkit_textEvent(Window * wdw,SDL_Event * event)2166 static int toolkit_textEvent( Window *wdw, SDL_Event* event )
2167 {
2168 Widget *wgt;
2169
2170 /* See if window is valid. */
2171 if (wdw == NULL)
2172 return 0;
2173
2174 /* Get widget. */
2175 wgt = toolkit_getFocus( wdw );
2176
2177 /* Trigger event function if exists. */
2178 if ((wgt != NULL) && (wgt->textevent != NULL)) {
2179 if ((*wgt->textevent)( wgt, event->text.text ))
2180 return 1;
2181 }
2182
2183 return 0;
2184 }
2185 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
2186
2187
2188 /**
2189 * @brief Purges the dead windows.
2190 */
toolkit_purgeDead(void)2191 static void toolkit_purgeDead (void)
2192 {
2193 Window *wdw, *wlast, *wkill;
2194 Widget *wgt, *wgtlast, *wgtkill;
2195
2196 /* Only clean up if necessary. */
2197 if (!window_dead)
2198 return;
2199
2200 /* Must be windows. */
2201 if (windows == NULL)
2202 return;
2203
2204 /* Destroy what is needed. */
2205 wlast = NULL;
2206 wdw = windows;
2207 while (wdw != NULL) {
2208 if (window_isFlag( wdw, WINDOW_KILL )) {
2209 /* Save target. */
2210 wkill = wdw;
2211 /* Reattach linked list. */
2212 if (wlast == NULL)
2213 windows = wdw->next;
2214 else
2215 wlast->next = wdw->next;
2216 wdw = wlast;
2217 /* Kill target. */
2218 wkill->next = NULL;
2219 window_kill( wkill );
2220 }
2221 else {
2222 wgtlast = NULL;
2223 wgt = wdw->widgets;
2224 while (wgt != NULL) {
2225 if (wgt_isFlag( wgt, WGT_FLAG_KILL )) {
2226 /* Save target. */
2227 wgtkill = wgt;
2228 /* Reattach linked list. */
2229 if (wgtlast == NULL)
2230 wdw->widgets = wgt->next;
2231 else
2232 wgtlast->next = wgt->next;
2233 wgt = wgtlast;
2234 /* Kill target. */
2235 wgtkill->next = NULL;
2236 widget_kill( wgtkill );
2237 }
2238 /* Save position. */
2239 wgtlast = wgt;
2240 if (wgt == NULL)
2241 wgt = wdw->widgets;
2242 else
2243 wgt = wgt->next;
2244 }
2245 }
2246 /* Save position. */
2247 wlast = wdw;
2248 if (wdw == NULL)
2249 wdw = windows;
2250 else
2251 wdw = wdw->next;
2252 }
2253
2254 /* Nothing left to purge. */
2255 window_dead = 0;
2256 }
2257
2258
2259 /**
2260 * @brief Updates the toolkit input for repeating keys.
2261 */
toolkit_update(void)2262 void toolkit_update (void)
2263 {
2264 #if !SDL_VERSION_ATLEAST(2,0,0)
2265 unsigned int t;
2266 Window *wdw;
2267 Widget *wgt;
2268 char buf[2];
2269 SDL_Event event;
2270 int ret;
2271 #endif /* !SDL_VERSION_ATLEAST(2,0,0) */
2272
2273 /* Clean up the dead if needed. */
2274 if (!dialogue_isOpen()) { /* Hack, since dialogues use secondary loop. */
2275 if (toolkit_delayCounter > 0)
2276 toolkit_delayCounter--;
2277 else
2278 toolkit_purgeDead();
2279 }
2280
2281 /* Killed all the windows. */
2282 if (windows == NULL) {
2283 input_mouseHide();
2284 toolkit_open = 0; /* disable toolkit */
2285 if (paused && !player_paused)
2286 unpause_game();
2287 return; /* No need to handle anything else. */
2288 }
2289
2290 #if !SDL_VERSION_ATLEAST(2,0,0)
2291 /* Must have a key pressed. */
2292 if (input_key == 0 && input_mod == 0)
2293 return;
2294
2295 t = SDL_GetTicks();
2296
2297 /* Should be repeating. */
2298 if (input_keyTime + INPUT_DELAY + input_keyCounter*INPUT_FREQ > t)
2299 return;
2300
2301 /* Increment counter. */
2302 input_keyCounter++;
2303
2304 /* Get the window. */
2305 wdw = toolkit_getActiveWindow();
2306 if (wdw == NULL)
2307 return;
2308
2309 /* See if widget needs event. */
2310 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2311 if (wgt_isFlag( wgt, WGT_FLAG_RAWINPUT )) {
2312 if (wgt->rawevent != NULL) {
2313 event.type = SDL_KEYDOWN;
2314 event.key.state = SDL_PRESSED;
2315 event.key.keysym.sym = input_key;
2316 event.key.keysym.mod = input_mod;
2317 event.key.keysym.unicode = (uint8_t)input_text;
2318 ret = wgt->rawevent( wgt, &event );
2319 if (ret != 0)
2320 return;
2321 }
2322 }
2323 }
2324
2325 /* Handle the focused widget. */
2326 wgt = toolkit_getFocus( wdw );
2327 if ((wgt != NULL) && (wgt->keyevent != NULL))
2328 wgt->keyevent( wgt, input_key, input_mod );
2329
2330 if ((input_text != 0) && (wgt != NULL) && (wgt->textevent != NULL)) {
2331 buf[0] = input_text;
2332 buf[1] = '\0';
2333 wgt->textevent( wgt, buf );
2334 }
2335 #endif /* !SDL_VERSION_ATLEAST(2,0,0) */
2336 }
2337
2338
2339 /**
2340 * @brief Exposes or hides a window and notifies its widgets.
2341 *
2342 * @param wgt Widget to change exposure of.
2343 * @param expose Whether exposing or hiding.
2344 */
toolkit_expose(Window * wdw,int expose)2345 static void toolkit_expose( Window *wdw, int expose )
2346 {
2347 Widget *wgt;
2348
2349 if (expose == wdw->exposed)
2350 return;
2351 else
2352 wdw->exposed = expose;
2353
2354 if (expose)
2355 toolkit_focusSanitize( wdw );
2356 else
2357 toolkit_focusClear( wdw );
2358
2359 if (wdw->focus != -1)
2360 return;
2361
2362 /* Notify widgets (for tabbed children, etc.) */
2363 for (wgt = wdw->widgets; wgt != NULL; wgt = wgt->next)
2364 if (wgt->exposeevent != NULL)
2365 wgt->exposeevent( wgt, expose );
2366 }
2367
2368
2369 /**
2370 * @brief Clears the window focus.
2371 */
toolkit_focusClear(Window * wdw)2372 void toolkit_focusClear( Window *wdw )
2373 {
2374 Widget *wgt;
2375
2376 if (wdw->focus == -1)
2377 return;
2378
2379 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2380 if (wdw->focus == wgt->id)
2381 toolkit_defocusWidget( wdw, wgt );
2382
2383 wgt_rmFlag( wgt, WGT_FLAG_FOCUSED );
2384 }
2385 }
2386
2387
2388 /**
2389 * @brief Sanitizes the focus of a window.
2390 *
2391 * Makes sure the window has a focusable widget focused.
2392 */
toolkit_focusSanitize(Window * wdw)2393 void toolkit_focusSanitize( Window *wdw )
2394 {
2395 Widget *wgt;
2396
2397 /* Clear focus. */
2398 toolkit_focusClear( wdw );
2399
2400 /* No focus is always sane. */
2401 if (wdw->focus == -1)
2402 return;
2403
2404 /* Check focused widget. */
2405 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2406 if (wdw->focus == wgt->id) {
2407 /* Not focusable. */
2408 if (!toolkit_isFocusable(wgt)) {
2409 wdw->focus = -1;
2410 toolkit_nextFocus( wdw ); /* Get first focus. */
2411 }
2412 else
2413 toolkit_focusWidget( wdw, wgt );
2414
2415 return;
2416 }
2417 }
2418 }
2419
2420
2421 /**
2422 * @brief Focus next widget.
2423 */
toolkit_nextFocus(Window * wdw)2424 void toolkit_nextFocus( Window *wdw )
2425 {
2426 Widget *wgt;
2427 int next;
2428
2429 /* Clear focus. */
2430 toolkit_focusClear( wdw );
2431
2432 /* See what to focus. */
2433 next = (wdw->focus == -1);
2434 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2435 if (!toolkit_isFocusable(wgt))
2436 continue;
2437
2438 if (next) {
2439 toolkit_focusWidget( wdw, wgt );
2440 return;
2441 }
2442 else if (wdw->focus == wgt->id) {
2443 next = 1;
2444 }
2445 }
2446
2447 /* Focus nothing. */
2448 wdw->focus = -1;
2449 return;
2450 }
2451
2452
2453 /**
2454 * @brief Focus previous widget.
2455 */
toolkit_prevFocus(Window * wdw)2456 void toolkit_prevFocus( Window *wdw )
2457 {
2458 Widget *wgt, *prev;
2459
2460 /* Clear focus. */
2461 toolkit_focusClear( wdw );
2462
2463 /* See what to focus. */
2464 prev = NULL;
2465 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2466 if (!toolkit_isFocusable(wgt))
2467 continue;
2468
2469 /* See if we found the current one. */
2470 if (wdw->focus == wgt->id) {
2471 if (prev == NULL)
2472 wdw->focus = -1;
2473 else
2474 toolkit_focusWidget( wdw, prev );
2475
2476 return;
2477 }
2478
2479 /* Store last focusable widget. */
2480 prev = wgt;
2481 }
2482
2483 /* Focus nothing. */
2484 if (prev == NULL)
2485 wdw->focus = -1;
2486 else
2487 toolkit_focusWidget( wdw, prev );
2488
2489 return;
2490 }
2491
2492
2493 /**
2494 * @brief Focuses a widget in a window.
2495 */
toolkit_focusWidget(Window * wdw,Widget * wgt)2496 void toolkit_focusWidget( Window *wdw, Widget *wgt )
2497 {
2498 if (!toolkit_isFocusable(wgt))
2499 return;
2500
2501 wdw->focus = wgt->id;
2502 wgt_setFlag( wgt, WGT_FLAG_FOCUSED );
2503 if (wgt->focusGain != NULL)
2504 wgt->focusGain( wgt );
2505 }
2506
2507
2508 /**
2509 * @brief Defocuses the focused widget in a window.
2510 */
toolkit_defocusWidget(Window * wdw,Widget * wgt)2511 void toolkit_defocusWidget( Window *wdw, Widget *wgt )
2512 {
2513 if (wdw->focus != wgt->id)
2514 return;
2515
2516 wgt_rmFlag( wgt, WGT_FLAG_FOCUSED );
2517 if (wgt->focusLose != NULL)
2518 wgt->focusLose( wgt );
2519 }
2520
2521
2522 /**
2523 * @brief Checks to see if a widget is focusable.
2524 *
2525 * @param wgt Widget to check if is focusable.
2526 * @return 1 if it's focusable, 0 if it isn't.
2527 */
toolkit_isFocusable(Widget * wgt)2528 static int toolkit_isFocusable( Widget *wgt )
2529 {
2530 if (wgt==NULL)
2531 return 0;
2532
2533 return wgt_isFlag(wgt, WGT_FLAG_CANFOCUS);
2534 }
2535
2536
2537 /**
2538 * @brief Gets the active window in the toolkit.
2539 *
2540 * @return The active window in the toolkit.
2541 */
toolkit_getActiveWindow(void)2542 Window* toolkit_getActiveWindow (void)
2543 {
2544 Window *wdw, *wlast;
2545
2546 /* Get window that can be focused. */
2547 wlast = NULL;
2548 for (wdw = windows; wdw!=NULL; wdw = wdw->next)
2549 if (!window_isFlag(wdw, WINDOW_NOFOCUS) &&
2550 !window_isFlag(wdw, WINDOW_KILL))
2551 wlast = wdw;
2552 return wlast;
2553 }
2554
2555
2556 /**
2557 * @brief Gets the focused widget in a window.
2558 *
2559 * @param wdw The window to get the focused widget from.
2560 * @return The focused widget.
2561 */
toolkit_getFocus(Window * wdw)2562 static Widget* toolkit_getFocus( Window *wdw )
2563 {
2564 Widget *wgt;
2565
2566 /* No focus. */
2567 if (wdw->focus == -1)
2568 return NULL;
2569
2570 /* Find focus. */
2571 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
2572 if (wdw->focus == wgt->id)
2573 return wgt;
2574
2575 /* Not found. */
2576 toolkit_focusClear( wdw );
2577 wdw->focus = -1;
2578 return NULL;
2579 }
2580
2581
2582 /**
2583 * @brief Sets the focused widget in a window.
2584 *
2585 * @param wid ID of the window to get widget from.
2586 * @param name Name of the widget to set focus to.
2587 */
window_setFocus(const unsigned int wid,const char * wgtname)2588 void window_setFocus( const unsigned int wid, const char* wgtname )
2589 {
2590 Window *wdw;
2591 Widget *wgt;
2592
2593 /* Get window. */
2594 wdw = window_wget(wid);
2595 if (wdw == NULL)
2596 return;
2597
2598 /* Get widget. */
2599 wgt = window_getwgt(wid,wgtname);
2600 if (wgt == NULL)
2601 return;
2602
2603 toolkit_focusClear( wdw );
2604 toolkit_focusWidget( wdw, wgt );
2605 }
2606
2607
2608 /**
2609 * @brief Gets the focused widget in a window.
2610 *
2611 * @param wid ID of the window to get widget from.
2612 * @return The focused widget's name.
2613 */
window_getFocus(const unsigned int wid)2614 char* window_getFocus( const unsigned int wid )
2615 {
2616 Window *wdw;
2617 Widget *wgt;
2618
2619 /* Get window. */
2620 wdw = window_wget(wid);
2621 if (wdw == NULL)
2622 return NULL;
2623
2624 /* Find focused widget. */
2625 for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
2626 if (wgt->id == wdw->focus)
2627 return wgt->name;
2628
2629 return NULL;
2630 }
2631
2632
2633 /**
2634 * @brief Raises a window (causes all other windows to appear below it).
2635 *
2636 * @param wid Window to raise.
2637 */
window_raise(unsigned int wid)2638 void window_raise( unsigned int wid )
2639 {
2640 Window *wdw, *wtmp, *wprev, *wlast;
2641
2642 wdw = window_wget(wid);
2643
2644 /* Not found, or already top of the stack. */
2645 if (wdw == NULL || wdw->next == NULL)
2646 return;
2647
2648 wprev = NULL;
2649 wlast = NULL;
2650
2651 for (wtmp = windows; wtmp != NULL; wtmp = wtmp->next)
2652 if (wtmp->next == wdw)
2653 wprev = wtmp;
2654 else if (wtmp->next == NULL)
2655 wlast = wtmp;
2656
2657 if (wprev != NULL)
2658 wprev->next = wdw->next; /* wdw-1 links to wdw+1 */
2659
2660 if (wlast != NULL)
2661 wlast->next = wdw; /* last links to wdw */
2662
2663 wdw->next = NULL; /* wdw becomes new last window */
2664
2665 wtmp = toolkit_getActiveWindow();
2666
2667 /* No active window, or window is the same. */
2668 if (wtmp == NULL || wtmp == wdw)
2669 return;
2670
2671 toolkit_expose( wtmp, 0 ); /* wtmp is hidden */
2672 toolkit_expose( wdw, 1 ); /* wdw is visible */
2673 }
2674
2675
2676 /**
2677 * @brief Lowers a window (causes all other windows to appear above it).
2678 *
2679 * @param wid Window to lower.
2680 */
window_lower(unsigned int wid)2681 void window_lower( unsigned int wid )
2682 {
2683 Window *wdw, *wtmp, *wprev;
2684
2685 wdw = window_wget(wid);
2686
2687 /* Not found, or already bottom of the stack. */
2688 if (wdw == NULL || wdw == windows)
2689 return;
2690
2691 wprev = NULL;
2692 for (wtmp = windows; wtmp != NULL; wtmp = wtmp->next)
2693 if (wtmp->next == wdw)
2694 wprev = wtmp;
2695
2696 if (wprev != NULL)
2697 wprev->next = wdw->next; /* wdw-1 links to wdw+1 */
2698
2699 wdw->next = windows; /* wdw links to first window */
2700 windows = wdw; /* wdw becomes new first window */
2701
2702 wtmp = toolkit_getActiveWindow();
2703
2704 /* No active window, or window is the same. */
2705 if (wtmp == NULL || wtmp == wdw)
2706 return;
2707
2708 toolkit_expose( wtmp, 1 ); /* wtmp is visible */
2709 toolkit_expose( wdw, 0 ); /* wdw is hidden */
2710 }
2711
2712
2713 /**
2714 * @brief Repositions windows and their children if resolution changes.
2715 */
toolkit_reposition(void)2716 void toolkit_reposition (void)
2717 {
2718 Window *w, *wtmp;
2719 Widget *wgt;
2720 int i, xorig, yorig, xdiff, ydiff;
2721
2722 for (w = windows; w != NULL; w = w->next) {
2723 /* Fullscreen windows must always be full size, though their widgets
2724 * don't auto-scale. */
2725 if (window_isFlag( w, WINDOW_FULLSCREEN )) {
2726 w->w = SCREEN_W;
2727 w->h = SCREEN_H;
2728 continue;
2729 }
2730
2731 /* Skip if position is fixed. */
2732 if (w->xrel == -1. && w->yrel == -1.)
2733 continue;
2734
2735 xdiff = 0.;
2736 ydiff = 0.;
2737
2738 if (w->xrel != -1.) {
2739 xorig = w->x;
2740 w->x = (SCREEN_W - w->w) * w->xrel;
2741 xdiff = w->x - xorig;
2742 }
2743
2744 if (w->yrel != -1.) {
2745 yorig = w->y;
2746 w->y = (SCREEN_H - w->h) * w->yrel;
2747 ydiff = w->y - yorig;
2748 }
2749
2750 /* Tabwin children aren't in the stack and must be manually updated. */
2751 for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next) {
2752 if (wgt->type != WIDGET_TABBEDWINDOW)
2753 continue;
2754
2755 for (i=0; i<wgt->dat.tab.ntabs; i++) {
2756 wtmp = window_wget( wgt->dat.tab.windows[i] );
2757 wtmp->x += xdiff;
2758 wtmp->y += ydiff;
2759 }
2760 }
2761 }
2762 }
2763
2764
2765 /**
2766 * @brief Initializes the toolkit.
2767 *
2768 * @return 0 on success.
2769 */
toolkit_init(void)2770 int toolkit_init (void)
2771 {
2772 GLsizei size;
2773
2774 /* Create the VBO. */
2775 toolkit_vboColourOffset = sizeof(GLshort) * 2 * 31;
2776 size = (sizeof(GLshort)*2 + sizeof(GLfloat)*4) * 31;
2777 toolkit_vbo = gl_vboCreateStream( size, NULL );
2778
2779 /* Disable the cursor. */
2780 input_mouseHide();
2781
2782 return 0;
2783 }
2784
2785
2786 /**
2787 * @brief Exits the toolkit.
2788 */
toolkit_exit(void)2789 void toolkit_exit (void)
2790 {
2791 Window *wdw;
2792
2793 /* Destroy the windows. */
2794 while (windows!=NULL) {
2795 wdw = windows;
2796 windows = windows->next;
2797 window_kill(wdw);
2798 }
2799 free(windows);
2800
2801 /* Free the VBO. */
2802 gl_vboDestroy( toolkit_vbo );
2803 toolkit_vbo = NULL;
2804 }
2805
2806