1 #include <stdlib.h>
2 #include <math.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <SDL.h>
6 #include <SDL_keycode.h>
7 #include "widgets.h"
8 #include "asc.h"
9 #include "chat.h"
10 #include "context_menu.h"
11 #include "cursors.h"
12 #include "elconfig.h"
13 #include "elwindows.h"
14 #include "gamewin.h"
15 #include "interface.h"
16 #include "misc.h"
17 #include "multiplayer.h"
18 #include "paste.h"
19 #include "tabs.h"
20 #include "textures.h"
21 #include "translate.h"
22 #ifdef OPENGL_TRACE
23 #include "gl_init.h"
24 #endif
25 #include "sound.h"
26
27 static size_t cm_edit_id = CM_INIT_VALUE;
28
29 typedef struct {
30 char text[256];
31 }label;
32
33 typedef struct {
34 float u1,v1,u2,v2, alpha;
35 int id;
36 }image;
37
38 typedef struct {
39 int *checked;
40 }checkbox;
41
42 typedef struct {
43 unsigned char text[256];
44 Uint16 fixed_width;
45 Uint16 fixed_height;
46 int center_offset;
47 }button;
48
49 typedef struct {
50 float progress;
51 GLfloat colors[12];
52 }progressbar;
53
54 typedef struct {
55 unsigned char* password;
56 int status;
57 int max_chars;
58 int cursor_pos;
59 int draw_begin, draw_end;
60 int sel_begin, sel_end;
61 int drag_begin;
62 int mouseover;
63 float shadow_r, shadow_g, shadow_b;
64 Uint16 fixed_height;
65 } password_entry;
66
67 typedef struct {
68 unsigned char text[256];
69 Uint16 x;
70 Uint16 y;
71 int width;
72 int height;
73 int value;
74 }multiselect_button;
75
76 typedef struct {
77 int nr_buttons;
78 int selected_button;
79 int max_buttons;
80 int next_value;
81 multiselect_button *buttons;
82 /* Scrollbar related vars */
83 Uint16 max_height;
84 Uint16 actual_height;
85 Uint32 scrollbar;
86 int scrollbar_width;
87 int win_id;
88 float highlighted_red;
89 float highlighted_green;
90 float highlighted_blue;
91 }multiselect;
92
93 Uint32 widget_id = 0x0000FFFF;
94
95 int ReadXMLWindow(xmlNode * a_node);
96 int ParseWindow (xmlNode *node);
97 int ParseWidget (xmlNode *node, int winid);
98 int ParseTab (xmlNode *node, int winid, int colid);
99 int GetWidgetType (const char *w);
100 int disable_double_click = 0;
101
102 // Forward declarations for widget types;
103 static int label_resize (widget_list *w, int width, int height);
104 static int free_widget_info (widget_list *widget);
105 static int checkbox_click(widget_list *W, int mx, int my, Uint32 flags);
106 static int button_draw(widget_list *w);
107 static int square_button_draw(widget_list *w);
108 static int button_change_font(widget_list *W, font_cat cat);
109 static int vscrollbar_click(widget_list *W, int mx, int my, Uint32 flags);
110 static int vscrollbar_drag(widget_list *W, int x, int y, Uint32 flags, int dx, int dy);
111 static int tab_collection_click(widget_list *W, int x, int y, Uint32 flags);
112 static int tab_collection_keypress(widget_list *W, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod);
113 static int free_tab_collection(widget_list *widget);
114 static int tab_collection_change_font(widget_list *w, font_cat font);
115 static int text_field_click(widget_list *w, int mx, int my, Uint32 flags);
116 static int text_field_drag(widget_list *w, int mx, int my, Uint32 flags, int dx, int dy);
117 static int text_field_resize (widget_list *w, int width, int height);
118 static int text_field_destroy(widget_list *w);
119 static int text_field_move(widget_list *w, int pos_x, int pos_y);
120 static int text_field_change_font(widget_list *w, font_cat cat);
121 static int text_field_paste(widget_list *w, const char* text);
122 static int text_field_set_color(widget_list *widget, float r, float g, float b);
123 static int pword_field_mouseover(widget_list *w, int mx, int my);
124 static int pword_field_click(widget_list *w, int mx, int my, Uint32 flags);
125 static int pword_field_drag(widget_list *w, int mx, int my, Uint32 flags, int dx, int dy);
126 static int pword_field_keypress(widget_list *w, int mx, int my, SDL_Keycode key_code,
127 Uint32 key_unicode, Uint16 key_mod);
128 static int pword_field_draw(widget_list *w);
129 static int pword_field_paste(widget_list *w, const char* text);
130 static int multiselect_draw(widget_list *widget);
131 static int multiselect_click(widget_list *widget, int mx, int my, Uint32 flags);
132 static int multiselect_set_color(widget_list *widget, float r, float g, float b);
133 static int free_multiselect(widget_list *widget);
134 static int spinbutton_draw(widget_list *widget);
135 static int spinbutton_click(widget_list *widget, int mx, int my, Uint32 flags);
136 static int spinbutton_keypress(widget_list *widget, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod);
137
138 static const struct WIDGET_TYPE label_type = { NULL, label_draw, NULL, NULL, NULL, label_resize, NULL, free_widget_info, NULL, NULL, NULL, NULL };
139 static const struct WIDGET_TYPE image_type = { NULL, image_draw, NULL, NULL, NULL, NULL, NULL, free_widget_info, NULL, NULL, NULL, NULL };
140 static const struct WIDGET_TYPE checkbox_type = { NULL, checkbox_draw, checkbox_click, NULL, NULL, NULL, NULL, free_widget_info, NULL, NULL, NULL, NULL };
141 static const struct WIDGET_TYPE round_button_type = { NULL, button_draw, NULL, NULL, NULL, NULL, NULL, free_widget_info, NULL, button_change_font, NULL, NULL };
142 static const struct WIDGET_TYPE square_button_type = { NULL, square_button_draw, NULL, NULL, NULL, NULL, NULL, free_widget_info, NULL, button_change_font, NULL, NULL };
143 static const struct WIDGET_TYPE progressbar_type = { NULL, progressbar_draw, NULL, NULL, NULL, NULL, NULL, free_widget_info, NULL, NULL, NULL, NULL };
144 static const struct WIDGET_TYPE vscrollbar_type = { NULL, vscrollbar_draw, vscrollbar_click, vscrollbar_drag, NULL, NULL, NULL, free_widget_info, NULL, NULL, NULL, NULL };
145 static const struct WIDGET_TYPE tab_collection_type = { NULL, tab_collection_draw, tab_collection_click, NULL, NULL, tab_collection_resize, (int (*)())tab_collection_keypress, free_tab_collection, NULL, tab_collection_change_font, NULL, NULL };
146 static const struct WIDGET_TYPE text_field_type = { NULL, text_field_draw, text_field_click, text_field_drag, NULL, text_field_resize, (int (*)())text_field_keypress, text_field_destroy, text_field_move, text_field_change_font, text_field_paste, text_field_set_color };
147 static const struct WIDGET_TYPE pword_field_type = { NULL, pword_field_draw, pword_field_click, pword_field_drag, pword_field_mouseover, NULL, (int (*)())pword_field_keypress, free_widget_info, NULL, NULL, pword_field_paste, NULL };
148 static const struct WIDGET_TYPE multiselect_type = { NULL, multiselect_draw, multiselect_click, NULL, NULL, NULL, NULL, free_multiselect, NULL, NULL, NULL, multiselect_set_color };
149 static const struct WIDGET_TYPE spinbutton_type = { NULL, spinbutton_draw, spinbutton_click, spinbutton_click, NULL, NULL, (int (*)())spinbutton_keypress, free_multiselect, NULL, NULL, NULL, NULL };
150
151 // <--- Common widget functions ---
widget_find(int window_id,Uint32 widget_id)152 widget_list * widget_find(int window_id, Uint32 widget_id)
153 {
154 widget_list *w;
155
156 if (window_id < 0 || window_id >= windows_list.num_windows) return NULL;
157 if (windows_list.window[window_id].window_id != window_id) return NULL;
158
159 w = windows_list.window[window_id].widgetlist;
160 while(w != NULL)
161 {
162 if(w->id == widget_id)
163 return w;
164 w = w->next;
165 }
166
167 return NULL;
168 }
169
widget_destroy(int window_id,Uint32 widget_id)170 int widget_destroy (int window_id, Uint32 widget_id)
171 {
172 widget_list *w, *n;
173
174 if (window_id < 0 || window_id >= windows_list.num_windows) return 0;
175 if (windows_list.window[window_id].window_id != window_id) return 0;
176
177 n = windows_list.window[window_id].widgetlist;
178 if (n == NULL)
179 {
180 // shouldn't happen
181 return 0;
182 }
183
184 if (n->id == widget_id)
185 {
186 if (n->OnDestroy != NULL)
187 {
188 if (n->spec != NULL)
189 n->OnDestroy (n, n->spec);
190 else
191 n->OnDestroy (n);
192 }
193 if (n->type != NULL)
194 if (n->type->destroy != NULL)
195 n->type->destroy (n);
196 windows_list.window[window_id].widgetlist = n->next;
197 free (n);
198 return 1;
199 }
200 else
201 {
202 while (1)
203 {
204 w = n;
205 n = n->next;
206 if (n == NULL)
207 {
208 // shouldn't happen
209 return 0;
210 }
211 if (n->id == widget_id)
212 {
213 // unlink n first, otherwise recursive calls
214 // to widget_destroy (e.g. widgets destroying
215 // their scrollbar) may set a pointer to the
216 // widget being deleted here.
217 w->next = n->next;
218
219 if (n->OnDestroy != NULL)
220 {
221 if(n->spec != NULL)
222 n->OnDestroy (n, n->spec);
223 else
224 n->OnDestroy (n);
225 }
226 if (n->type != NULL)
227 if (n->type->destroy != NULL)
228 n->type->destroy (n);
229 free (n);
230 return 1;
231 }
232 }
233 }
234
235 // shouldn't get here
236 return 0;
237 }
238
widget_set_OnDraw(int window_id,Uint32 widget_id,int (* handler)())239 int widget_set_OnDraw(int window_id, Uint32 widget_id, int (*handler)())
240 {
241 widget_list *w = widget_find(window_id, widget_id);
242 if(w){
243 w->OnDraw = handler;
244 return 1;
245 }
246 return 0;
247 }
248
widget_set_OnClick(int window_id,Uint32 widget_id,int (* handler)())249 int widget_set_OnClick(int window_id, Uint32 widget_id, int (*handler)())
250 {
251 widget_list *w = widget_find(window_id, widget_id);
252 if(w){
253 w->OnClick = handler;
254 return 1;
255 }
256 return 0;
257 }
258
widget_set_OnDrag(int window_id,Uint32 widget_id,int (* handler)())259 int widget_set_OnDrag(int window_id, Uint32 widget_id, int (*handler)())
260 {
261 widget_list *w = widget_find(window_id, widget_id);
262 if(w){
263 w->OnDrag = handler;
264 return 1;
265 }
266 return 0;
267 }
268
widget_set_OnMouseover(int window_id,Uint32 widget_id,int (* handler)())269 int widget_set_OnMouseover(int window_id, Uint32 widget_id, int (*handler)())
270 {
271 widget_list *w = widget_find(window_id, widget_id);
272 if(w){
273 w->OnMouseover = handler;
274 return 1;
275 }
276 return 0;
277 }
278
widget_set_OnKey(int window_id,Uint32 widget_id,int (* handler)())279 int widget_set_OnKey ( int window_id, Uint32 widget_id, int (*handler)() )
280 {
281 widget_list *w = widget_find (window_id, widget_id);
282 if (w)
283 {
284 w->OnKey = handler;
285 return 1;
286 }
287 return 0;
288 }
289
widget_set_OnDestroy(int window_id,Uint32 widget_id,int (* handler)())290 int widget_set_OnDestroy (int window_id, Uint32 widget_id, int (*handler)())
291 {
292 widget_list *w = widget_find(window_id, widget_id);
293 if(w)
294 {
295 w->OnDestroy = handler;
296 return 1;
297 }
298 return 0;
299 }
300
widget_move(int window_id,Uint32 widget_id,Uint16 x,Uint16 y)301 int widget_move(int window_id, Uint32 widget_id, Uint16 x, Uint16 y)
302 {
303 widget_list *w = widget_find(window_id, widget_id);
304 if(w){
305 int res = 1;
306 w->pos_x = x;
307 w->pos_y = y;
308 if (w->type != NULL)
309 if (w->type->move != NULL)
310 res = w->type->move (w, x, y);
311 return res;
312 }
313 return 0;
314 }
315
widget_move_win(int window_id,Uint32 widget_id,int new_win_id)316 Uint32 widget_move_win(int window_id, Uint32 widget_id, int new_win_id)
317 {
318 widget_list *w;
319 widget_list *prev_w = NULL;
320
321 if (window_id < 0 || window_id >= windows_list.num_windows
322 || new_win_id < 0 || new_win_id >= windows_list.num_windows) {
323 return 0;
324 }
325 if (windows_list.window[window_id].window_id != window_id
326 || windows_list.window[new_win_id].window_id != new_win_id
327 || window_id == new_win_id) {
328 return 0;
329 }
330
331 w = windows_list.window[window_id].widgetlist;
332 /* Find the widget */
333 while(w != NULL) {
334 if(w->id == widget_id) {
335 break;
336 } else {
337 prev_w = w;
338 w = w->next;
339 }
340 }
341 if(w != NULL) {
342 Uint32 new_id = 0;
343 widget_list *target_w = windows_list.window[new_win_id].widgetlist;
344 /* Remove the widget from the old list */
345 if(prev_w) {
346 prev_w->next = w->next;
347 } else {
348 windows_list.window[window_id].widgetlist = NULL;
349 }
350 /* Put it in the new list */
351 if(target_w == NULL) {
352 /* List is empty, start a new one */
353 windows_list.window[new_win_id].widgetlist = w;
354 } else {
355 /* Find the end of the list */
356 while(target_w->next != NULL) {
357 if(target_w->id > new_id) {
358 new_id = target_w->id;
359 }
360 target_w = target_w->next;
361 }
362 target_w->next = w;
363 }
364 w->next = NULL;
365 w->window_id = new_win_id;
366 if(new_id > 0xFFFF) {
367 new_id = widget_id++;
368 } else {
369 new_id++;
370 }
371 w->id = new_id;
372 /* if removing a (possibly none existant) context menu for the widget works, we need to replace it */
373 if (cm_remove_widget(window_id, widget_id))
374 cm_add_widget(cm_edit_id, new_win_id, new_id);
375 return w->id;
376 } else {
377 return 0;
378 }
379 }
380
widget_move_rel(int window_id,Uint32 widget_id,Sint16 dx,Sint16 dy)381 int widget_move_rel (int window_id, Uint32 widget_id, Sint16 dx, Sint16 dy)
382 {
383 widget_list *w = widget_find (window_id, widget_id);
384 if(w)
385 {
386 w->pos_x += dx;
387 w->pos_y += dy;
388 return 1;
389 }
390 return 0;
391 }
392
widget_resize(int window_id,Uint32 widget_id,Uint16 x,Uint16 y)393 int widget_resize(int window_id, Uint32 widget_id, Uint16 x, Uint16 y)
394 {
395 widget_list *w = widget_find (window_id, widget_id);
396 int res = 0;
397
398 if (w)
399 {
400 w->len_x = x;
401 w->len_y = y;
402 if (w->type != NULL)
403 if (w->type->resize != NULL)
404 res = w->type->resize (w, x, y);
405 if (w->OnResize && res != -1)
406 {
407 if(w->spec != NULL) res |= w->OnResize (w, x, y, w->spec);
408 else res |= w->OnResize (w, x, y);
409 }
410
411 return res > -1 ? res : 0;
412 }
413 return 0;
414 }
415
widget_set_flags(int window_id,Uint32 widget_id,Uint32 f)416 int widget_set_flags(int window_id, Uint32 widget_id, Uint32 f)
417 {
418 widget_list *w = widget_find(window_id, widget_id);
419 if(w){
420 w->Flags = f;
421 return 1;
422 }
423 return 0;
424 }
425
widget_unset_flags(int window_id,Uint32 widget_id,Uint32 f)426 int widget_unset_flags (int window_id, Uint32 widget_id, Uint32 f)
427 {
428 widget_list *w = widget_find(window_id, widget_id);
429 if (w)
430 {
431 w->Flags &= ~f;
432 return 1;
433 }
434 return 0;
435 }
436
widget_set_size(int window_id,Uint32 widget_id,float size)437 int widget_set_size(int window_id, Uint32 widget_id, float size)
438 {
439 widget_list *w = widget_find (window_id, widget_id);
440 if (w)
441 {
442 w->size = size;
443 return 1;
444 }
445 return 0;
446 }
447
widget_set_color(int window_id,Uint32 widget_id,float r,float g,float b)448 int widget_set_color(int window_id, Uint32 widget_id, float r, float g, float b)
449 {
450 widget_list *w = widget_find(window_id, widget_id);
451 if(!w)
452 return 0;
453
454 w->r = r;
455 w->g = g;
456 w->b = b;
457 if (w->type->color_change)
458 w->type->color_change(w, r, g, b);
459 return 1;
460 }
461
widget_set_font_cat(int window_id,int widget_id,font_cat cat)462 int widget_set_font_cat(int window_id, int widget_id, font_cat cat)
463 {
464 widget_list *w = widget_find(window_id, widget_id);
465 if (!w)
466 return 0;
467
468 w->fcat = cat;
469 widget_handle_font_change(w, cat);
470
471 return 1;
472 }
473
widget_get_width(int window_id,Uint32 widget_id)474 int widget_get_width (int window_id, Uint32 widget_id)
475 {
476 widget_list *w = widget_find(window_id, widget_id);
477 if (w != NULL)
478 {
479 return w->len_x;
480 }
481 return -1;
482 }
483
widget_get_height(int window_id,Uint32 widget_id)484 int widget_get_height (int window_id, Uint32 widget_id)
485 {
486 widget_list *w = widget_find(window_id, widget_id);
487 if (w != NULL)
488 {
489 return w->len_y;
490 }
491 return -1;
492 }
493
free_widget_info(widget_list * widget)494 static int free_widget_info (widget_list *widget)
495 {
496 free (widget->widget_info);
497 return 1;
498 }
499
widget_set_args(int window_id,Uint32 widget_id,void * spec)500 int widget_set_args (int window_id, Uint32 widget_id, void *spec)
501 {
502 widget_list *w = widget_find(window_id, widget_id);
503 if(w){
504 w->spec = spec;
505 return 1;
506 }
507 return 0;
508 }
509
510 // Create a generic widget
widget_add(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,float size,const struct WIDGET_TYPE * type,void * T,void * S)511 Uint32 widget_add (int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly,
512 Uint32 Flags, float size, const struct WIDGET_TYPE *type, void *T, void *S)
513 {
514 window_info *win = &windows_list.window[window_id];
515 widget_list *W = (widget_list *) malloc(sizeof(widget_list));
516 widget_list *w = win->widgetlist;
517
518 // Clearing everything
519 memset(W,0,sizeof(widget_list));
520
521 // Filling the widget info
522 W->widget_info = T;
523
524 // Copy the information
525 W->id = wid;
526 W->window_id = window_id;
527 W->Flags = Flags;
528 W->pos_x = x;
529 W->pos_y = y;
530 W->size = size;
531 W->r = gui_color[0];
532 W->g = gui_color[1];
533 W->b = gui_color[2];
534 W->fcat = win->font_category;
535 W->len_y = ly;
536 W->len_x = lx;
537
538 W->spec = S;
539
540 // Generic Handling Functions
541 W->OnInit = OnInit;
542 W->type = type;
543
544 // Check if we need to initialize it
545 if(W->type != NULL)
546 if(W->type->init != NULL)
547 W->type->init(W);
548 if(W->OnInit != NULL)
549 {
550 if(W->spec != NULL) W->OnInit (W, W->spec);
551 else W->OnInit (W);
552 }
553
554 // Adding the widget to the list
555 if (w == NULL)
556 {
557 win->widgetlist = W;
558 }
559 else
560 {
561 while(w->next != NULL)
562 w = w->next;
563 w->next = W;
564 }
565
566 return W->id;
567 }
568
569
widget_handle_mouseover(widget_list * widget,int mx,int my)570 int widget_handle_mouseover (widget_list *widget, int mx, int my)
571 {
572 int res = 0;
573
574 if (widget->type != NULL) {
575 if (widget->type->mouseover != NULL) {
576 res = widget->type->mouseover (widget, mx, my);
577 }
578 }
579
580 if (widget->OnMouseover != NULL && res != -1) {
581 if(widget->spec != NULL) {
582 res |= widget->OnMouseover (widget, mx, my, widget->spec);
583 } else {
584 res |= widget->OnMouseover (widget, mx, my);
585 }
586 }
587
588 return res > -1 ? res : 0;
589 }
590
widget_handle_click(widget_list * widget,int mx,int my,Uint32 flags)591 int widget_handle_click (widget_list *widget, int mx, int my, Uint32 flags)
592 {
593 int res = 0;
594 /* widget might get destroyed by handler so check for sound type now */
595 int play_sound = (widget->type == &round_button_type) ?1 :0;
596
597 if (widget->type != NULL) {
598 if (widget->type->click != NULL) {
599 res = widget->type->click (widget, mx, my, flags);
600 }
601 }
602
603 if (widget->OnClick != NULL && res != -1)
604 {
605 if(widget->spec != NULL) {
606 res |= widget->OnClick (widget, mx, my, flags, widget->spec);
607 } else {
608 res |= widget->OnClick (widget, mx, my, flags);
609 }
610 }
611
612 if (play_sound && res > -1)
613 do_click_sound();
614
615 return res > -1 ? res : 0;
616 }
617
widget_handle_drag(widget_list * widget,int mx,int my,Uint32 flags,int dx,int dy)618 int widget_handle_drag (widget_list *widget, int mx, int my, Uint32 flags, int dx, int dy)
619 {
620 int res = 0;
621
622 if (widget->type != NULL && widget->type->drag != NULL) {
623 res = widget->type->drag (widget, mx, my, flags, dx, dy);
624 }
625
626 if (widget->OnDrag != NULL && res != -1)
627 {
628 if(widget->spec != NULL) {
629 res |= widget->OnDrag (widget, mx, my, flags, dx, dy, widget->spec);
630 } else {
631 res |= widget->OnDrag (widget, mx, my, flags, dx, dy);
632 }
633 }
634 return res > -1 ? res : 0;
635 }
636
widget_handle_keypress(widget_list * widget,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)637 int widget_handle_keypress (widget_list *widget, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
638 {
639 int res = 0;
640
641 if (widget->type != NULL) {
642 if (widget->type->key != NULL) {
643 res = widget->type->key (widget, mx, my, key_code, key_unicode, key_mod);
644 }
645 }
646
647 if (widget->OnKey != NULL && res != -1)
648 {
649 if(widget->spec != NULL) {
650 res |= widget->OnKey (widget, mx, my, key_code, key_unicode, key_mod, widget->spec);
651 } else {
652 res |= widget->OnKey (widget, mx, my, key_code, key_unicode, key_mod);
653 }
654 }
655 return res > -1 ? res : 0;
656 }
657
widget_handle_font_change(widget_list * widget,font_cat cat)658 int widget_handle_font_change(widget_list *widget, font_cat cat)
659 {
660 int res = 0;
661
662 if (cat != widget->fcat)
663 return 0;
664
665 if (widget->type && widget->type->font_change)
666 {
667 res = widget->type->font_change(widget, cat);
668 }
669 if (widget->OnFontChange)
670 {
671 if (widget->spec)
672 {
673 res |= widget->OnFontChange(widget, cat, widget->spec);
674 }
675 else
676 {
677 res |= widget->OnFontChange(widget, cat);
678 }
679 }
680
681 return res;
682 }
683
widget_handle_paste(widget_list * widget,const char * text)684 int widget_handle_paste(widget_list *widget, const char* text)
685 {
686 int res = 0;
687 if (widget->type && widget->type->paste)
688 {
689 res = widget->type->paste(widget, text);
690 }
691
692 // MAYBE FIXME? Add object-specific paste handlers?
693
694 return res;
695 }
696
697 // --- End Common Widget Functions --->
698
699 // Label
label_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint32 Flags,float size,const char * text)700 int label_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint32 Flags, float size, const char *text)
701 {
702 window_info *win = &windows_list.window[window_id];
703
704 Uint16 len_x = get_string_width_zoom((const unsigned char*)text, win->font_category, size);
705 Uint16 len_y = get_line_height(win->font_category, size);
706
707 label *T = (label *) calloc (1, sizeof(label));
708 safe_strncpy(T->text, text, sizeof(T->text));
709
710 return widget_add (window_id, wid, OnInit, x, y, len_x, len_y, Flags, size, &label_type, (void *)T, NULL);
711 }
712
label_add(int window_id,int (* OnInit)(),const char * text,Uint16 x,Uint16 y)713 int label_add(int window_id, int (*OnInit)(), const char *text, Uint16 x, Uint16 y)
714 {
715 return label_add_extended (window_id, widget_id++, OnInit, x, y, 0, 1.0, text);
716 }
717
label_draw(widget_list * W)718 int label_draw(widget_list *W)
719 {
720 label *l = (label *)W->widget_info;
721 if(W->r != -1.0) {
722 glColor3f(W->r,W->g,W->b);
723 }
724 draw_string_zoomed_width_font(W->pos_x, W->pos_y, (const unsigned char *)l->text,
725 window_width, 1, W->fcat, W->size);
726 return 1;
727 }
728
label_resize(widget_list * w,int width,int height)729 static int label_resize (widget_list *w, int width, int height)
730 {
731 label *l;
732 if (!w || !(l = (label*)w->widget_info))
733 return 0;
734
735 w->len_x = (width > 0) ? width : get_string_width_zoom((const unsigned char*)l->text,
736 w->fcat, w->size);
737 w->len_y = (height > 0) ? height : get_line_height(w->fcat, w->size);
738 return 1;
739 }
740
label_set_text(int window_id,Uint32 widget_id,const char * text)741 int label_set_text(int window_id, Uint32 widget_id, const char *text)
742 {
743 widget_list *w = widget_find(window_id, widget_id);
744 if(w){
745 label *l = (label *) w->widget_info;
746 safe_snprintf(l->text, sizeof(l->text), "%s", text);
747 return 1;
748 }
749 return 0;
750 }
751
752 // Image
753
image_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,float size,int id,float u1,float v1,float u2,float v2,float alpha)754 int image_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint32 Flags, float size, int id, float u1, float v1, float u2, float v2, float alpha)
755 {
756 image *T = calloc (1, sizeof (image));
757 T->u1 = u1;
758 T->u2 = u2;
759 T->v1 = -v1;
760 T->v2 = -v2;
761 T->id = id;
762 T->alpha = alpha;
763
764 return widget_add (window_id, wid, OnInit, x, y, lx, ly, Flags, size, &image_type, T, NULL);
765 }
766
image_add(int window_id,int (* OnInit)(),int id,Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,float u1,float v1,float u2,float v2)767 int image_add(int window_id, int (*OnInit)(), int id, Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, float u1, float v1, float u2, float v2)
768 {
769 return image_add_extended(window_id, widget_id++, OnInit, x, y, lx, ly, 0, 1.0, id, u1, v1, u2, v2, -1);
770 }
771
image_draw(widget_list * W)772 int image_draw(widget_list *W)
773 {
774 image *i = (image *)W->widget_info;
775 bind_texture(i->id);
776 glColor3f(W->r, W->g, W->b);
777 if (i->alpha > -1) {
778 glEnable(GL_ALPHA_TEST);
779 glAlphaFunc(GL_GREATER, i->alpha);
780 }
781 glBegin(GL_QUADS);
782 draw_2d_thing(i->u1, i->v1, i->u2, i->v2, W->pos_x, W->pos_y, W->pos_x + (W->len_x * W->size), W->pos_y + (W->len_y * W->size));
783 glEnd();
784 if (i->alpha > -1) {
785 glDisable(GL_ALPHA_TEST);
786 }
787 #ifdef OPENGL_TRACE
788 CHECK_GL_ERRORS();
789 #endif //OPENGL_TRACE
790 return 1;
791 }
792
image_set_id(int window_id,Uint32 widget_id,int id)793 int image_set_id(int window_id, Uint32 widget_id, int id)
794 {
795 widget_list *w = widget_find(window_id, widget_id);
796 if(w){
797 image *l = (image *) w->widget_info;
798 l->id = id;
799 return 1;
800 }
801 return 0;
802 }
803
image_set_uv(int window_id,Uint32 widget_id,float u1,float v1,float u2,float v2)804 int image_set_uv(int window_id, Uint32 widget_id, float u1, float v1, float u2, float v2)
805 {
806 widget_list *w = widget_find(window_id, widget_id);
807 if(w){
808 image *l = (image *) w->widget_info;
809 l->u1 = u1;
810 l->u2 = u2;
811 l->v1 = -v1;
812 l->v2 = -v2;
813 return 1;
814 }
815 return 0;
816 }
817
818
819 // Checkbox
checkbox_draw(widget_list * W)820 int checkbox_draw(widget_list *W)
821 {
822 checkbox *c = (checkbox *)W->widget_info;
823 glDisable(GL_TEXTURE_2D);
824 if(W->r!=-1.0)
825 glColor3f(W->r, W->g, W->b);
826 glBegin(*c->checked ? GL_QUADS: GL_LINE_LOOP);
827 glVertex3i(W->pos_x,W->pos_y,0);
828 glVertex3i(W->pos_x + W->len_x,W->pos_y,0);
829 glVertex3i(W->pos_x + W->len_x,W->pos_y + W->len_y,0);
830 glVertex3i(W->pos_x,W->pos_y + W->len_y,0);
831 glEnd();
832 glEnable(GL_TEXTURE_2D);
833 #ifdef OPENGL_TRACE
834 CHECK_GL_ERRORS();
835 #endif //OPENGL_TRACE
836 return 1;
837 }
838
checkbox_click(widget_list * W,int mx,int my,Uint32 flags)839 static int checkbox_click(widget_list *W, int mx, int my, Uint32 flags)
840 {
841 checkbox *c = (checkbox *)W->widget_info;
842
843 // only handle mouse button clicks, not scroll wheels moves
844 if ( (flags & ELW_MOUSE_BUTTON) == 0)
845 return 0;
846
847 *c->checked = !*c->checked;
848
849 do_click_sound();
850
851 return 1;
852 }
853
checkbox_get_checked(int window_id,Uint32 widget_id)854 int checkbox_get_checked(int window_id, Uint32 widget_id)
855 {
856 widget_list *w = widget_find(window_id, widget_id);
857 if(w){
858 checkbox *c = (checkbox *)w->widget_info;
859 return *c->checked;
860 }
861 return -1;
862 }
863
checkbox_set_checked(int window_id,Uint32 widget_id,int checked)864 int checkbox_set_checked(int window_id, Uint32 widget_id, int checked)
865 {
866 widget_list *w = widget_find(window_id, widget_id);
867 if(w){
868 checkbox *c = (checkbox *)w->widget_info;
869 *c->checked = checked;
870 return 1;
871 }
872 return 0;
873 }
874
checkbox_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,float size,int * checked)875 int checkbox_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint32 Flags, float size, int *checked)
876 {
877 checkbox *T = calloc (1, sizeof (checkbox));
878 T->checked = checked;
879
880 return widget_add (window_id, wid, OnInit, x, y, lx, ly, Flags, size, &checkbox_type, T, NULL);
881 }
882
checkbox_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,int * checked)883 int checkbox_add(int window_id, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, int *checked)
884 {
885 if(checked == NULL)
886 {
887 checked = calloc(1,sizeof(*checked));
888 }
889 return checkbox_add_extended(window_id, widget_id++, NULL, x, y, lx, ly, 0, 1.0, checked);
890 }
891
892 // Button
safe_button_click(Uint32 * last_click)893 int safe_button_click(Uint32 *last_click)
894 {
895 int retvalue = 0;
896 if (disable_double_click || ((SDL_GetTicks() - *last_click) < 500))
897 retvalue = 1;
898 *last_click = SDL_GetTicks();
899 return retvalue;
900 }
901
calc_button_width(const unsigned char * label,font_cat cat,float size)902 int calc_button_width(const unsigned char* label, font_cat cat, float size)
903 {
904 return get_string_width_zoom(label, cat, size) + (int)(0.5 + 2*size*BUTTONRADIUS);
905 }
906
button_change_font(widget_list * W,font_cat cat)907 static int button_change_font(widget_list *W, font_cat cat)
908 {
909 button *T;
910 Uint16 len_x, len_y;
911
912 if (!W || !(T = W->widget_info))
913 return 0;
914
915 len_x = T->fixed_width ? T->fixed_width : calc_button_width(T->text, W->fcat, W->size);
916 if (T->fixed_height)
917 {
918 len_y = T->fixed_height;
919 }
920 else
921 {
922 // FIXME? Even if the font height changes, the button frame that is drawn only depends on
923 // the size. So at least for now, stick to the button frame for the height.
924 // int min_len_y = (int)(2 * BUTTONRADIUS * W->size + 0.5);
925 // len_y = get_line_height(W->fcat, W->size) + (int)(12 * W->size + 0.5);
926 // if (len_y < min_len_y)
927 // len_y = min_len_y;
928 len_y = (int)(2 * BUTTONRADIUS * W->size + 0.5);
929 }
930
931 if (W->Flags & BUTTON_VCENTER_CONTENT)
932 T->center_offset = get_center_offset(T->text, strlen((const char*)T->text), W->fcat, W->size);
933
934 return widget_resize(W->window_id, W->id, len_x, len_y);
935 }
936
button_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,float size,const char * text)937 int button_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint32 Flags, float size, const char *text)
938 {
939 window_info *win = &windows_list.window[window_id];
940
941 Uint16 len_x, len_y;
942 const struct WIDGET_TYPE *type = (Flags & BUTTON_SQUARE) ? &square_button_type : &round_button_type;
943
944 button *T = calloc (1, sizeof(button));
945 safe_strncpy((char*)T->text, text, sizeof(T->text));
946 T->fixed_width = lx;
947 T->fixed_height = ly;
948
949 if (Flags & BUTTON_VCENTER_CONTENT)
950 {
951 T->center_offset = get_center_offset((const unsigned char*)text, strlen(text),
952 win->font_category, size);
953 }
954
955 len_x = lx ? lx : calc_button_width(T->text, win->font_category, size);
956 len_y = ly ? ly : get_line_height(win->font_category, size) + (int)(12 * size + 0.5);
957
958 return widget_add (window_id, wid, OnInit, x, y, len_x, len_y, Flags, size, type, T, NULL);
959 }
960
button_resize(int window_id,Uint32 wid,Uint16 lx,Uint16 ly,float size)961 int button_resize(int window_id, Uint32 wid, Uint16 lx, Uint16 ly, float size)
962 {
963 widget_list *w = widget_find(window_id, wid);
964 button *T;
965 if (!w || !(T = w->widget_info))
966 return 0;
967
968 if (lx) T->fixed_width = lx;
969 if (ly) T->fixed_height = ly;
970 w->size = size;
971
972 return button_change_font(w, w->fcat);
973 }
974
button_add(int window_id,int (* OnInit)(),const char * text,Uint16 x,Uint16 y)975 int button_add(int window_id, int (*OnInit)(), const char *text, Uint16 x, Uint16 y)
976 {
977 return button_add_extended(window_id, widget_id++, NULL, x, y, 0, 0, 0, 1.0, text);
978 }
979
button_draw(widget_list * W)980 static int button_draw(widget_list *W)
981 {
982 button *l = (button *)W->widget_info;
983 int text_width = W->len_x - 2*BUTTONRADIUS*W->size;
984 draw_smooth_button(NULL, W->fcat, W->size, W->pos_x, W->pos_y, text_width,
985 1, W->r, W->g, W->b, W->Flags & BUTTON_ACTIVE, gui_invert_color[0], gui_invert_color[1], gui_invert_color[2], 0.0f);
986 draw_text(W->pos_x + W->len_x/2, W->pos_y + W->len_y/2 - l->center_offset, l->text,
987 strlen((const char*)l->text), W->fcat, TDO_MAX_WIDTH, text_width, TDO_ALIGNMENT, CENTER,
988 TDO_VERTICAL_ALIGNMENT, CENTER_LINE, TDO_ZOOM, W->size, TDO_SHRINK_TO_FIT, 1, TDO_END);
989 return 1;
990 }
991
square_button_draw(widget_list * W)992 static int square_button_draw(widget_list *W)
993 {
994 button *l = (button *)W->widget_info;
995
996 glDisable(GL_TEXTURE_2D);
997
998 if(W->r != -1.0)
999 glColor3f(W->r,W->g,W->b);
1000
1001 glBegin(GL_LINE_LOOP);
1002 glVertex3i(W->pos_x,W->pos_y,0);
1003 glVertex3i(W->pos_x + W->len_x,W->pos_y,0);
1004 glVertex3i(W->pos_x + W->len_x,W->pos_y + W->len_y,0);
1005 glVertex3i(W->pos_x,W->pos_y + W->len_y,0);
1006 glEnd();
1007
1008 glEnable(GL_TEXTURE_2D);
1009 draw_text(W->pos_x + W->len_x/2, W->pos_y + W->len_y/2 - l->center_offset, l->text,
1010 strlen((const char*)l->text), W->fcat, TDO_ALIGNMENT, CENTER, TDO_VERTICAL_ALIGNMENT, CENTER_LINE,
1011 TDO_ZOOM, W->size, TDO_END);
1012 #ifdef OPENGL_TRACE
1013 CHECK_GL_ERRORS();
1014 #endif //OPENGL_TRACE
1015
1016 return 1;
1017 }
1018
draw_smooth_button(const unsigned char * str,font_cat fcat,float size,int x,int y,int w,int lines,float r,float g,float b,int highlight,float hr,float hg,float hb,float ha)1019 void draw_smooth_button(const unsigned char* str, font_cat fcat, float size,
1020 int x, int y, int w, int lines, float r, float g, float b,
1021 int highlight, float hr, float hg, float hb, float ha)
1022 {
1023 int radius=lines*BUTTONRADIUS*size;
1024
1025 glDisable(GL_TEXTURE_2D);
1026
1027 if(r>=0.0f)
1028 glColor3f(r, g, b);
1029
1030 #ifdef OSX
1031 if (square_buttons) {
1032 glBegin(GL_LINE_LOOP);
1033 glVertex3i(x,y,0);
1034 glVertex3i(x + w + radius*2,y,0);
1035 glVertex3i(x + w + radius*2,y + radius*2,0);
1036 glVertex3i(x,y + radius*2,0);
1037 glEnd();
1038
1039 if(highlight) {
1040 if(hr>=0.0f)
1041 glColor4f(hr,hg,hb,ha);
1042 glBegin(GL_POLYGON);
1043 glVertex3i(x+1,y+1,0);
1044 glVertex3i(x + w + radius*2 -1,y+1,0);
1045 glVertex3i(x + w + radius*2 -1,y + radius*2 -1,0);
1046 glVertex3i(x+1,y + radius*2 -1,0);
1047 glEnd();
1048 }
1049
1050 glEnable(GL_TEXTURE_2D);
1051 } else {
1052 #endif
1053 glBegin(GL_LINE_LOOP);
1054 draw_circle_ext(x, y, radius, 10, 90, 270);
1055 draw_circle_ext(x+w, y, radius, 10, -90, 90);
1056 glEnd();
1057 if(highlight) {
1058 if(hr>=0.0f)
1059 glColor4f(hr,hg,hb,ha);
1060 glBegin(GL_POLYGON);
1061 draw_circle_ext(x+1, y+1, radius-1, 10, 90, 270);
1062 draw_circle_ext(x+w+1, y+1, radius-1, 10, -90, 90);
1063 glEnd();
1064 }
1065 glEnable(GL_TEXTURE_2D);
1066
1067 #ifdef OSX
1068 } // to close off square_buttons conditional
1069 #endif
1070
1071 if(highlight) {
1072 glColor3f(r, g, b);
1073 }
1074
1075 if (str)
1076 {
1077 draw_text(x + radius + w/2, y + radius, str, strlen((const char*)str), fcat,
1078 TDO_MAX_WIDTH, w, TDO_ALIGNMENT, CENTER, TDO_VERTICAL_ALIGNMENT, CENTER_LINE,
1079 TDO_ZOOM, size, TDO_SHRINK_TO_FIT, 1, TDO_END);
1080 }
1081 #ifdef OPENGL_TRACE
1082 CHECK_GL_ERRORS();
1083 #endif //OPENGL_TRACE
1084 }
1085
1086
button_set_text(int window_id,Uint32 widget_id,const char * text)1087 int button_set_text(int window_id, Uint32 widget_id, const char *text)
1088 {
1089 widget_list *w = widget_find(window_id, widget_id);
1090 if(w){
1091 button *l = (button *) w->widget_info;
1092 safe_strncpy((char*)l->text, text, sizeof(l->text));
1093 return 1;
1094 }
1095 return 0;
1096 }
1097
1098 // Progressbar
progressbar_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly)1099 int progressbar_add(int window_id, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly)
1100 {
1101 return progressbar_add_extended(window_id, widget_id++, OnInit, x, y, lx, ly, 0, 1.0, 0.0f, NULL);
1102 }
1103
progressbar_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,float size,float progress,const float * colors)1104 int progressbar_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint32 Flags, float size, float progress, const float * colors)
1105 {
1106 progressbar *T = calloc (1, sizeof(progressbar));
1107 T->progress = progress;
1108 if (colors) {
1109 memcpy(T->colors, colors, sizeof(T->colors));
1110 } else {
1111 T->colors[0] = -1.0f;
1112 }
1113
1114 return widget_add (window_id, wid, OnInit, x, y, lx, ly, Flags, size, &progressbar_type, T, NULL);
1115 }
1116
progressbar_draw(widget_list * W)1117 int progressbar_draw(widget_list *W)
1118 {
1119 progressbar *b = (progressbar *)W->widget_info;
1120 int pixels = (b->progress/100) * W->len_x;
1121
1122 glDisable(GL_TEXTURE_2D);
1123
1124 if (pixels > 0) {
1125 const char have_bar_colors = (b->colors[0] > -0.5f);
1126 GLfloat right_colors[6];
1127
1128 if (have_bar_colors) {
1129 const float progress = b->progress/100.0f, inv_progress = 1.0f - progress;
1130 int i;
1131
1132 for (i=0; i<3; i++) {
1133 right_colors[i+0] = progress * b->colors[i+3] + inv_progress * b->colors[i+0];
1134 right_colors[i+3] = progress * b->colors[i+6] + inv_progress * b->colors[i+9];
1135 }
1136 }
1137
1138 glBegin(GL_QUADS);
1139 if (have_bar_colors) glColor3fv(&b->colors[0]);
1140 glVertex3i(W->pos_x, W->pos_y + 0, 0);//LabRat: fix unfilled pixels in progress bar
1141 if (have_bar_colors) glColor3fv(&right_colors[0]);
1142 glVertex3i(W->pos_x + pixels,W->pos_y + 0,0);
1143 if (have_bar_colors) glColor3fv(&right_colors[3]);
1144 glVertex3i(W->pos_x + pixels, W->pos_y + W->len_y, 0);
1145 if (have_bar_colors) glColor3fv(&b->colors[9]);
1146 glVertex3i(W->pos_x, W->pos_y + W->len_y, 0);//LabRat: fix unfilled pixels in progress bar
1147 glColor3fv(gui_color);
1148 glEnd();
1149 }
1150 if(W->r != -1.0)
1151 glColor3f(W->r,W->g,W->b);
1152 else
1153 glColor3fv(gui_color);
1154
1155 //LabRat: Draw bounding box after progress bar
1156 glBegin(GL_LINE_LOOP);
1157 glVertex3i(W->pos_x,W->pos_y,0);
1158 glVertex3i(W->pos_x + W->len_x,W->pos_y,0);
1159 glVertex3i(W->pos_x + W->len_x,W->pos_y + W->len_y,0);
1160 glVertex3i(W->pos_x,W->pos_y + W->len_y,0);
1161 glEnd();
1162
1163 glEnable(GL_TEXTURE_2D);
1164 #ifdef OPENGL_TRACE
1165 CHECK_GL_ERRORS();
1166 #endif //OPENGL_TRACE
1167 return 0;
1168 }
1169
progressbar_get_progress(int window_id,Uint32 widget_id)1170 float progressbar_get_progress(int window_id, Uint32 widget_id)
1171 {
1172 widget_list *w = widget_find(window_id, widget_id);
1173 if(w){
1174 progressbar *c = (progressbar *)w->widget_info;
1175 return c->progress;
1176 }
1177 return -1;
1178 }
1179
progressbar_set_progress(int window_id,Uint32 widget_id,float progress)1180 int progressbar_set_progress(int window_id, Uint32 widget_id, float progress)
1181 {
1182 widget_list *w = widget_find(window_id, widget_id);
1183 if(w){
1184 progressbar *c = (progressbar *)w->widget_info;
1185 c->progress = progress;
1186 return 1;
1187 }
1188 return 0;
1189 }
1190
1191
1192 // Vertical scrollbar
vscrollbar_draw(widget_list * W)1193 int vscrollbar_draw(widget_list *W)
1194 {
1195 int drawn_bar_len = 0;
1196 vscrollbar *c = (vscrollbar *)W->widget_info;
1197 int arrow_size = (int)(0.5 + (float)(W->len_x) / 4.0f);
1198
1199 glDisable(GL_TEXTURE_2D);
1200 if(W->r!=-1.0)
1201 glColor3f(W->r, W->g, W->b);
1202
1203 // scrollbar border
1204 glBegin(GL_LINE_LOOP);
1205 glVertex3i(W->pos_x,W->pos_y,0);
1206 glVertex3i(W->pos_x + W->len_x,W->pos_y,0);
1207 glVertex3i(W->pos_x + W->len_x,W->pos_y + W->len_y,0);
1208 glVertex3i(W->pos_x,W->pos_y + W->len_y,0);
1209 glEnd ();
1210
1211 // scrollbar arrows
1212 glBegin (GL_LINES);
1213 glVertex3i(W->pos_x + arrow_size, W->pos_y + 2 * arrow_size,0);
1214 glVertex3i(W->pos_x + 2 * arrow_size, W->pos_y + arrow_size,0);
1215 glVertex3i(W->pos_x + 2 * arrow_size, W->pos_y + arrow_size,0);
1216 glVertex3i(W->pos_x + 3 * arrow_size, W->pos_y + 2 * arrow_size,0);
1217 glVertex3i(W->pos_x + arrow_size, W->pos_y + W->len_y - 2 * arrow_size,0);
1218 glVertex3i(W->pos_x + 2 * arrow_size, W->pos_y + W->len_y - arrow_size,0);
1219 glVertex3i(W->pos_x + 2 * arrow_size, W->pos_y + W->len_y - arrow_size,0);
1220 glVertex3i(W->pos_x + 3 * arrow_size, W->pos_y + W->len_y - 2 * arrow_size,0);
1221 glEnd();
1222
1223 if (c->bar_len > 0)
1224 drawn_bar_len = c->bar_len;
1225 else
1226 {
1227 drawn_bar_len = 1;
1228 if(W->r!=-1.0)
1229 glColor3f(W->r/3, W->g/3, W->b/3);
1230 }
1231 glBegin(GL_QUADS);
1232 glVertex3i(W->pos_x + 2 * arrow_size - (int)(0.5 + (float)arrow_size / 1.5f), W->pos_y + 3 * arrow_size + (c->pos * ((float)(W->len_y -11 * arrow_size) / drawn_bar_len)), 0);
1233 glVertex3i(W->pos_x + 2 * arrow_size + (int)(0.5 + (float)arrow_size / 1.5f), W->pos_y + 3 * arrow_size + (c->pos * ((float)(W->len_y -11 * arrow_size) / drawn_bar_len)), 0);
1234 glVertex3i(W->pos_x + 2 * arrow_size + (int)(0.5 + (float)arrow_size / 1.5f), W->pos_y + 8 * arrow_size + (c->pos * ((float)(W->len_y -11 * arrow_size) / drawn_bar_len)), 0);
1235 glVertex3i(W->pos_x + 2 * arrow_size - (int)(0.5 + (float)arrow_size / 1.5f), W->pos_y + 8 * arrow_size + (c->pos * ((float)(W->len_y -11 * arrow_size) / drawn_bar_len)), 0);
1236 glEnd();
1237
1238 glEnable(GL_TEXTURE_2D);
1239 #ifdef OPENGL_TRACE
1240 CHECK_GL_ERRORS();
1241 #endif //OPENGL_TRACE
1242 return 0;
1243 }
1244
vscrollbar_click(widget_list * W,int mx,int my,Uint32 flags)1245 int vscrollbar_click(widget_list *W, int mx, int my, Uint32 flags)
1246 {
1247 int arrow_size = (int)(0.5 + (float)(W->len_x) / 4.0f);
1248 vscrollbar *b = (vscrollbar *)W->widget_info;
1249 if ( my < 3*arrow_size || (flags & ELW_WHEEL_UP) )
1250 {
1251 b->pos -= b->pos_inc;
1252 }
1253 else if (my > W->len_y - 3*arrow_size || (flags & ELW_WHEEL_DOWN) )
1254 {
1255 b->pos += b->pos_inc;
1256 }
1257 else
1258 {
1259 b->pos = (my - 5*arrow_size)/((float)(W->len_y-11*arrow_size)/b->bar_len);
1260 }
1261
1262 if (b->pos < 0) b->pos = 0;
1263 if (b->pos > b->bar_len) b->pos = b->bar_len;
1264
1265 return 1;
1266 }
1267
vscrollbar_set_pos_inc(int window_id,Uint32 widget_id,int pos_inc)1268 int vscrollbar_set_pos_inc(int window_id, Uint32 widget_id, int pos_inc)
1269 {
1270 widget_list *w = widget_find(window_id, widget_id);
1271 if(w){
1272 vscrollbar *c = (vscrollbar *)w->widget_info;
1273 c->pos_inc = pos_inc;
1274 return 1;
1275 }
1276
1277 return 0;
1278 }
1279
vscrollbar_set_pos(int window_id,Uint32 widget_id,int pos)1280 int vscrollbar_set_pos(int window_id, Uint32 widget_id, int pos)
1281 {
1282 widget_list *w = widget_find(window_id, widget_id);
1283 if(w){
1284 vscrollbar *c = (vscrollbar *)w->widget_info;
1285 if (pos < 0)
1286 c->pos = 0;
1287 else if (pos > c->bar_len)
1288 c->pos = c->bar_len;
1289 else
1290 c->pos = pos;
1291 return 1;
1292 }
1293
1294 return 0;
1295 }
1296
vscrollbar_scroll_up(int window_id,Uint32 widget_id)1297 int vscrollbar_scroll_up(int window_id, Uint32 widget_id)
1298 {
1299 widget_list *w = widget_find(window_id, widget_id);
1300 if(w){
1301 vscrollbar *scrollbar = w->widget_info;
1302 return vscrollbar_set_pos(window_id, widget_id, scrollbar->pos - scrollbar->pos_inc);
1303 }
1304 return 0;
1305 }
1306
vscrollbar_scroll_down(int window_id,Uint32 widget_id)1307 int vscrollbar_scroll_down(int window_id, Uint32 widget_id)
1308 {
1309 widget_list *w = widget_find(window_id, widget_id);
1310 if(w){
1311 vscrollbar *scrollbar = w->widget_info;
1312 return vscrollbar_set_pos(window_id, widget_id, scrollbar->pos + scrollbar->pos_inc);
1313 }
1314 return 0;
1315 }
1316
vscrollbar_set_bar_len(int window_id,Uint32 widget_id,int bar_len)1317 int vscrollbar_set_bar_len (int window_id, Uint32 widget_id, int bar_len)
1318 {
1319 widget_list *w = widget_find(window_id, widget_id);
1320 if(w){
1321 vscrollbar *c = (vscrollbar *)w->widget_info;
1322 c->bar_len = bar_len >= 0 ? bar_len : 1;
1323 if (c->pos > c->bar_len)
1324 c->pos = c->bar_len;
1325 return 1;
1326 }
1327
1328 return 0;
1329 }
1330
vscrollbar_drag(widget_list * W,int x,int y,Uint32 flags,int dx,int dy)1331 static int vscrollbar_drag(widget_list *W, int x, int y, Uint32 flags, int dx, int dy)
1332 {
1333 window_info *win;
1334
1335 if (!W)
1336 return 0;
1337
1338 vscrollbar_click(W, x, y, flags);
1339
1340 /* the drag event can happen multiple times for each redraw as its done in the event loop
1341 * so update the scroll bar position each time to avoid positions glitches */
1342 win = &windows_list.window[W->window_id];
1343 if(win!=NULL && win->flags&ELW_SCROLLABLE && win->scroll_id==W->id)
1344 {
1345 int pos = vscrollbar_get_pos(win->window_id, win->scroll_id);
1346 int offset = win->scroll_yoffset + ((win->flags&ELW_CLOSE_BOX) ? ELW_BOX_SIZE : 0);
1347 widget_move(win->window_id, win->scroll_id, win->len_x-ELW_BOX_SIZE, pos+offset);
1348 }
1349
1350 return 1;
1351 }
1352
vscrollbar_get_pos(int window_id,Uint32 widget_id)1353 int vscrollbar_get_pos(int window_id, Uint32 widget_id)
1354 {
1355 widget_list *w = widget_find(window_id, widget_id);
1356 if(w){
1357 vscrollbar *c = (vscrollbar *)w->widget_info;
1358 return c->pos;
1359 }
1360 return -1;
1361 }
1362
vscrollbar_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,float size,int pos,int pos_inc,int bar_len)1363 int vscrollbar_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint32 Flags, float size, int pos, int pos_inc, int bar_len)
1364 {
1365 vscrollbar *T = calloc (1, sizeof(vscrollbar));
1366 T->pos_inc = pos_inc;
1367 T->pos = pos;
1368 T->bar_len = bar_len > 0 ? bar_len : 0;
1369
1370 return widget_add (window_id, wid, OnInit, x, y, lx, ly, Flags, size, &vscrollbar_type, T, NULL);
1371 }
1372
vscrollbar_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly)1373 int vscrollbar_add(int window_id, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly)
1374 {
1375 return vscrollbar_add_extended(window_id, widget_id++, OnInit, x, y, lx, ly, 0, 1.0, 0, 1, ly);
1376 }
1377
1378 // Tab collection
tab_collection_get_tab(int window_id,Uint32 widget_id)1379 int tab_collection_get_tab (int window_id, Uint32 widget_id)
1380 {
1381 widget_list *w = widget_find (window_id, widget_id);
1382 if (w)
1383 {
1384 tab_collection *col = (tab_collection *) w->widget_info;
1385 return col->cur_tab;
1386 }
1387 return -1;
1388 }
1389
tab_collection_get_tab_id(int window_id,Uint32 widget_id)1390 int tab_collection_get_tab_id (int window_id, Uint32 widget_id)
1391 {
1392 widget_list *w = widget_find (window_id, widget_id);
1393 if (w)
1394 {
1395 int tab;
1396 tab_collection *col = (tab_collection *) w->widget_info;
1397 tab = col->cur_tab;
1398 if (tab >= 0 && tab < col->nr_tabs)
1399 return col->tabs[tab].content_id;
1400 }
1401 return -1;
1402 }
1403
tab_collection_get_tab_nr(int window_id,Uint32 col_id,int tab_id)1404 int tab_collection_get_tab_nr (int window_id, Uint32 col_id, int tab_id)
1405 {
1406 widget_list *w = widget_find (window_id, col_id);
1407 if (w != NULL)
1408 {
1409 int tab;
1410 tab_collection *col = (tab_collection *) w->widget_info;
1411 for (tab = 0; tab < col->nr_tabs; tab++)
1412 if (col->tabs[tab].content_id == tab_id)
1413 return tab;
1414 }
1415 return -1;
1416 }
1417
tab_collection_get_nr_tabs(int window_id,Uint32 widget_id)1418 int tab_collection_get_nr_tabs (int window_id, Uint32 widget_id)
1419 {
1420 widget_list *w = widget_find (window_id, widget_id);
1421 if (w)
1422 {
1423 tab_collection *t = (tab_collection *) w->widget_info;
1424 return t->nr_tabs;
1425 }
1426 return -1;
1427 }
1428
tab_collection_calc_tab_height(font_cat cat,float size)1429 int tab_collection_calc_tab_height(font_cat cat, float size)
1430 {
1431 return 2 * get_line_height(cat, size);
1432 }
1433
tab_set_label_color_by_id(int window_id,Uint32 col_id,int tab_id,float r,float g,float b)1434 int tab_set_label_color_by_id (int window_id, Uint32 col_id, int tab_id, float r, float g, float b)
1435 {
1436 widget_list *w = widget_find (window_id, col_id);
1437
1438 if (w)
1439 {
1440 int itab;
1441 tab_collection *col = (tab_collection *) w->widget_info;
1442
1443 for (itab = 0; itab < col->nr_tabs; itab++)
1444 {
1445 if (col->tabs[itab].content_id == tab_id)
1446 {
1447 col->tabs[itab].label_r = r;
1448 col->tabs[itab].label_g = g;
1449 col->tabs[itab].label_b = b;
1450 return tab_id;
1451 }
1452 }
1453 }
1454 return -1;
1455 }
1456
tab_collection_select_tab(int window_id,Uint32 widget_id,int tab)1457 int tab_collection_select_tab (int window_id, Uint32 widget_id, int tab)
1458 {
1459 widget_list *w = widget_find (window_id, widget_id);
1460 if (w)
1461 {
1462 tab_collection *col = (tab_collection *) w->widget_info;
1463 if (tab >= 0 && tab < col->nr_tabs)
1464 {
1465 if (tab != col->cur_tab)
1466 hide_window (col->tabs[col->cur_tab].content_id);
1467 col->cur_tab = tab;
1468
1469 // Don't show the tab, because the parent window might
1470 // be hidden. The widget drawing code will take care
1471 // of it.
1472 //show_window (col->tabs[tab].content_id);
1473 //select_window (col->tabs[tab].content_id);
1474 return tab;
1475 }
1476 }
1477 return -1;
1478 }
1479
_tab_collection_close_tab_real(tab_collection * col,int tab)1480 int _tab_collection_close_tab_real (tab_collection* col, int tab)
1481 {
1482 if (col != NULL && tab >= 0 && tab < col->nr_tabs)
1483 {
1484 int i;
1485
1486 destroy_window (col->tabs[tab].content_id);
1487 for (i = tab+1; i < col->nr_tabs; i++)
1488 col->tabs[i-1] = col->tabs[i];
1489
1490 col->nr_tabs--;
1491 if (tab < col->cur_tab || (tab == col->cur_tab && tab >= col->nr_tabs))
1492 col->cur_tab--;
1493 if (tab < col->tab_offset || (tab == col->tab_offset && tab >= col->nr_tabs))
1494 col->tab_offset--;
1495
1496 return col->cur_tab;
1497 }
1498
1499 return -1;
1500 }
1501
tab_collection_close_tab(int window_id,Uint32 widget_id,int tab)1502 int tab_collection_close_tab (int window_id, Uint32 widget_id, int tab)
1503 {
1504 widget_list *w = widget_find (window_id, widget_id);
1505 if (w)
1506 return _tab_collection_close_tab_real (w->widget_info, tab);
1507 return -1;
1508 }
1509
free_tab_collection(widget_list * widget)1510 static int free_tab_collection(widget_list *widget)
1511 {
1512 tab_collection *col = (tab_collection *) widget->widget_info;
1513 free (col->tabs);
1514 free (col);
1515 return 1;
1516 }
1517
tab_collection_draw(widget_list * w)1518 int tab_collection_draw (widget_list *w)
1519 {
1520 tab_collection *col;
1521 int itab, ytagtop, ytagbot, xstart, xend, xmax;
1522 int btn_size, arw_width;
1523 int cur_start, cur_end;
1524 int h;
1525 int xtxt, ytxt;
1526 float tab_corner;
1527 int right_margin = 0;
1528
1529 if (!w) return 0;
1530
1531 col = (tab_collection *) w->widget_info;
1532
1533 h = col->tag_height;
1534 ytagtop = w->pos_y;
1535 ytagbot = w->pos_y + h;
1536 xstart = w->pos_x;
1537 btn_size = col->button_size;
1538 arw_width = btn_size / 4;
1539 cur_start = cur_end = xstart;
1540 tab_corner = h / 5.0f;
1541
1542 if ((w->window_id >= 0) && (w->window_id < windows_list.num_windows))
1543 right_margin = col->tabs_right_margin * windows_list.window[w->window_id].current_scale;
1544
1545 glDisable(GL_TEXTURE_2D);
1546
1547 if (col->tab_offset > 0)
1548 {
1549 // draw a "move left" button
1550 if(w->r!=-1.0)
1551 glColor3f(w->r, w->g, w->b);
1552
1553 // button outline
1554 glBegin (GL_LINE_STRIP);
1555 glVertex3i (xstart, ytagtop, 0);
1556 glVertex3i (xstart, ytagtop+btn_size, 0);
1557 glVertex3i (xstart+btn_size, ytagtop+btn_size, 0);
1558 glVertex3i (xstart+btn_size, ytagtop, 0);
1559 glVertex3i (xstart, ytagtop, 0);
1560 glEnd ();
1561
1562 // left arrows
1563 glBegin (GL_LINE_STRIP);
1564 glVertex3i (xstart+2*arw_width, ytagtop+btn_size/2-arw_width, 0);
1565 glVertex3i (xstart+arw_width, ytagtop+btn_size/2, 0);
1566 glVertex3i (xstart+2*arw_width, ytagtop+btn_size/2+arw_width, 0);
1567 glEnd ();
1568
1569 glBegin (GL_LINE_STRIP);
1570 glVertex3i (xstart+3*arw_width, ytagtop+btn_size/2-arw_width, 0);
1571 glVertex3i (xstart+2*arw_width, ytagtop+btn_size/2, 0);
1572 glVertex3i (xstart+3*arw_width, ytagtop+btn_size/2+arw_width, 0);
1573 glEnd ();
1574
1575 xstart += h;
1576 }
1577
1578 // draw the tags
1579 for (itab = col->tab_offset; itab < col->nr_tabs; itab++)
1580 {
1581 xend = xstart + col->tabs[itab].tag_width;
1582 xmax = w->pos_x + w->len_x - right_margin;
1583 if (itab < col->nr_tabs - 1)
1584 xmax -= h;
1585
1586 // Check if there's still room for this tab, but always
1587 // draw at least one tab
1588 if (itab > col->tab_offset && xend > xmax)
1589 {
1590 // this tab doesn't fit. Simply extend the top line to
1591 // the end of the available width
1592 glBegin (GL_LINES);
1593 glVertex3f (xstart - 2 * tab_corner, ytagtop, 0);
1594 glVertex3f (w->pos_x + w->len_x - h - right_margin, ytagtop, 0);
1595 glEnd ();
1596 break;
1597 }
1598
1599 if (itab == col->cur_tab)
1600 {
1601 cur_start = xstart;
1602 cur_end = xend;
1603 }
1604
1605 if(w->r!=-1.0)
1606 glColor3f(w->r, w->g, w->b);
1607
1608 if(col->cur_tab == itab){
1609 // current
1610 glBegin(GL_LINE_STRIP);
1611 glVertex3f(xstart, ytagbot, 0);
1612 glVertex3f(xstart, ytagtop + tab_corner, 0);
1613 glVertex3f(xstart + tab_corner, ytagtop, 0);
1614 glVertex3f(xend - tab_corner, ytagtop, 0);
1615 glVertex3f(xend, ytagtop + tab_corner, 0);
1616 glVertex3f(xend, ytagbot, 0);
1617 glEnd();
1618 } else if(col->cur_tab>itab){
1619 // left of current
1620 glBegin (GL_LINE_STRIP);
1621 glVertex3f(xstart, ytagbot, 0);
1622 glVertex3f(xstart, ytagtop + tab_corner, 0);
1623 glVertex3f(xstart + tab_corner, ytagtop, 0);
1624 glVertex3f(xend + tab_corner, ytagtop, 0);
1625 glEnd ();
1626 } else {
1627 // right of current
1628 glBegin (GL_LINE_STRIP);
1629 glVertex3f(xend, ytagbot, 0);
1630 glVertex3f(xend, ytagtop + tab_corner, 0);
1631 glVertex3f(xend - tab_corner, ytagtop, 0);
1632 glVertex3f (xstart - tab_corner, ytagtop, 0);
1633 glEnd ();
1634 }
1635
1636 // draw a close box if necessary
1637 if (col->tabs[itab].closable)
1638 {
1639 glBegin (GL_LINE_LOOP);
1640 glVertex3i (xstart + tab_corner, ytagbot - tab_corner, 0);
1641 glVertex3i (xstart + tab_corner, ytagtop + tab_corner, 0);
1642 glVertex3i (xstart + h - tab_corner, ytagtop + tab_corner, 0);
1643 glVertex3i (xstart + h - tab_corner, ytagbot - tab_corner, 0);
1644 glEnd ();
1645
1646 glBegin (GL_LINES);
1647 glVertex3i (xstart + tab_corner, ytagbot - tab_corner, 0);
1648 glVertex3i (xstart + h - tab_corner, ytagtop + tab_corner, 0);
1649 glVertex3i (xstart + tab_corner, ytagtop + tab_corner, 0);
1650 glVertex3i (xstart + h - tab_corner, ytagbot - tab_corner, 0);
1651 glEnd ();
1652 }
1653
1654 glEnable(GL_TEXTURE_2D);
1655
1656 if (col->tabs[itab].label_r >= 0.0f)
1657 glColor3f (col->tabs[itab].label_r, col->tabs[itab].label_g, col->tabs[itab].label_b);
1658
1659 xtxt = xstart + (w->size * DEFAULT_FIXED_FONT_WIDTH) / 2;
1660 ytxt = ytagtop + h/2;
1661 if (col->tabs[itab].closable)
1662 xtxt += h;
1663 draw_text(xtxt, ytxt, col->tabs[itab].label, strlen((const char*)col->tabs[itab].label),
1664 w->fcat, TDO_ZOOM, w->size, TDO_VERTICAL_ALIGNMENT, CENTER_LINE, TDO_END);
1665
1666 glDisable(GL_TEXTURE_2D);
1667
1668 xstart = xend;
1669 }
1670
1671 col->tab_last_visible = itab-1;
1672 if (itab < col->nr_tabs)
1673 {
1674 // draw a "move right" button
1675 xstart = w->pos_x + w->len_x - right_margin - btn_size;
1676
1677 if(w->r!=-1.0)
1678 glColor3f(w->r, w->g, w->b);
1679
1680 // button outline
1681 glBegin (GL_LINE_STRIP);
1682 glVertex3i (xstart, ytagtop, 0);
1683 glVertex3i (xstart, ytagtop+btn_size, 0);
1684 glVertex3i (xstart+btn_size, ytagtop+btn_size, 0);
1685 glVertex3i (xstart+btn_size, ytagtop, 0);
1686 glVertex3i (xstart, ytagtop, 0);
1687 glEnd ();
1688
1689 // right arrows
1690 glBegin (GL_LINE_STRIP);
1691 glVertex3i (xstart+arw_width, ytagtop+btn_size/2-arw_width, 0);
1692 glVertex3i (xstart+2*arw_width, ytagtop+btn_size/2, 0);
1693 glVertex3i (xstart+arw_width, ytagtop+btn_size/2+arw_width, 0);
1694 glEnd ();
1695
1696 glBegin (GL_LINE_STRIP);
1697 glVertex3i (xstart+2*arw_width, ytagtop+btn_size/2-arw_width, 0);
1698 glVertex3i (xstart+3*arw_width, ytagtop+btn_size/2, 0);
1699 glVertex3i (xstart+2*arw_width, ytagtop+btn_size/2+arw_width, 0);
1700 glEnd ();
1701 }
1702
1703 if(w->r!=-1.0)
1704 glColor3f(w->r, w->g, w->b);
1705
1706 // draw the rest of the frame around the tab
1707 glBegin (GL_LINE_STRIP);
1708 glVertex3i (cur_end, ytagbot, 0);
1709 glVertex3i (w->pos_x + w->len_x, ytagbot, 0);
1710 glVertex3i (w->pos_x + w->len_x, w->pos_y + w->len_y, 0);
1711 glVertex3i (w->pos_x, w->pos_y + w->len_y, 0);
1712 glVertex3i (w->pos_x, ytagbot, 0);
1713 glVertex3i (cur_start, ytagbot, 0);
1714 glEnd ();
1715
1716 glEnable(GL_TEXTURE_2D);
1717
1718 // show the content of the current tab
1719 if (col->nr_tabs > 0)
1720 show_window (col->tabs[col->cur_tab].content_id);
1721 #ifdef OPENGL_TRACE
1722 CHECK_GL_ERRORS();
1723 #endif //OPENGL_TRACE
1724
1725 return 1;
1726 }
1727
tab_collection_click(widget_list * W,int x,int y,Uint32 flags)1728 static int tab_collection_click(widget_list *W, int x, int y, Uint32 flags)
1729 {
1730 tab_collection *col = (tab_collection *) W->widget_info;
1731 int right_margin = 0;
1732
1733 // only handle mouse button clicks, not scroll wheels moves
1734 if ( (flags & ELW_MOUSE_BUTTON) == 0) return 0;
1735
1736 // Check if we clicked a tab scroll button
1737 if (col->tab_offset > 0 && x >= 0 && x <= col->button_size && y >= 0 && y <= col->button_size)
1738 {
1739 col->tab_offset--;
1740 return 1;
1741 }
1742
1743 if ((W->window_id >= 0) && (W->window_id < windows_list.num_windows))
1744 right_margin = col->tabs_right_margin * windows_list.window[W->window_id].current_scale;
1745
1746 if ((col->tab_last_visible < col->nr_tabs - 1) &&
1747 (x >= W->len_x - col->button_size - right_margin) &&
1748 (x <= W->len_x - right_margin) &&
1749 (y >= 0) && (y <= col->button_size))
1750 {
1751 if (col->tab_offset < col->nr_tabs-1)
1752 col->tab_offset++;
1753 return 1;
1754 }
1755
1756 if (y < col->tag_height)
1757 {
1758 int x_start = col->tab_offset > 0 ? col->tag_height : 0;
1759 int itag, ctag = col->cur_tab;
1760
1761 // find which tag was clicked
1762 for (itag = col->tab_offset; itag <= col->tab_last_visible; itag++)
1763 {
1764 int x_end = x_start + col->tabs[itag].tag_width;
1765 if (x >= x_start && x < x_end)
1766 break;
1767 x_start = x_end;
1768 }
1769
1770 if (itag <= col->tab_last_visible)
1771 {
1772 int tab_corner = col->tag_height / 5;
1773
1774 // check if close box was clicked
1775 if (col->tabs[itag].closable && x > x_start + tab_corner &&
1776 x < x_start + col->tag_height - tab_corner &&
1777 y > tab_corner && y < col->tag_height - tab_corner)
1778 {
1779 do_click_sound();
1780 _tab_collection_close_tab_real (col, itag);
1781 }
1782 // check if a new tab is selected
1783 else if (itag != ctag)
1784 {
1785 col->cur_tab = itag;
1786 hide_window (col->tabs[ctag].content_id);
1787 show_window (col->tabs[itag].content_id);
1788 //select_window (col->tabs[itag].content_id);
1789 do_click_sound();
1790 }
1791 return 1;
1792 }
1793 }
1794
1795 return 0;
1796 }
1797
calc_tag_width(const unsigned char * label,font_cat fcat,float size,int close_width)1798 static int calc_tag_width(const unsigned char* label, font_cat fcat, float size, int close_width)
1799 {
1800 return size * DEFAULT_FIXED_FONT_WIDTH
1801 + get_string_width_zoom(label, fcat, size)
1802 + close_width;
1803 }
1804
tab_collection_change_font(widget_list * w,font_cat font)1805 static int tab_collection_change_font(widget_list *w, font_cat font)
1806 {
1807 tab_collection *col;
1808 int itab;
1809
1810 if (!w || !(col = (tab_collection *)w->widget_info))
1811 return 0;
1812
1813 col->tag_height = tab_collection_calc_tab_height(w->fcat, w->size);
1814 col->button_size = (9 * col->tag_height) / 10;
1815
1816 for (itab = 0; itab < col->nr_tabs; ++itab)
1817 {
1818 Uint16 width = calc_tag_width(col->tabs[itab].label, w->fcat, w->size,
1819 col->tabs[itab].closable ? col->tag_height : 0);
1820 if (width > col->tabs[itab].min_tag_width)
1821 col->tabs[itab].tag_width = width;
1822 }
1823
1824 return 1;
1825 }
1826
tab_collection_resize(widget_list * w,Uint32 width,Uint32 height)1827 int tab_collection_resize (widget_list *w, Uint32 width, Uint32 height)
1828 {
1829 tab_collection *col;
1830 int itab;
1831
1832 if (w == NULL || (col = (tab_collection *) w->widget_info) == NULL)
1833 return 0;
1834
1835 tab_collection_change_font(w, w->fcat);
1836
1837 for (itab = 0; itab < col->nr_tabs; itab++)
1838 resize_window (col->tabs[itab].content_id, width, height);
1839
1840 return 1;
1841 }
1842
tab_collection_move(widget_list * W,Uint32 pos_x,Uint32 pos_y)1843 int tab_collection_move (widget_list *W, Uint32 pos_x, Uint32 pos_y)
1844 {
1845 tab_collection *col;
1846 int itab;
1847
1848 if (W == NULL || (col = (tab_collection *) W->widget_info) == NULL)
1849 return 0;
1850
1851 for (itab = 0; itab < col->nr_tabs; itab++)
1852 move_window(col->tabs[itab].content_id, W->window_id, 0, pos_x, pos_y);
1853
1854 return 1;
1855 }
1856
_tab_collection_make_cur_visible(widget_list * W)1857 void _tab_collection_make_cur_visible (widget_list *W)
1858 {
1859 tab_collection *col;
1860
1861 if (W == NULL)
1862 return;
1863
1864 col = W->widget_info;
1865 if (col == NULL)
1866 return;
1867
1868 if (col->cur_tab < col->tab_offset)
1869 {
1870 col->tab_offset = col->cur_tab;
1871 }
1872 else if (col->cur_tab > col->tab_last_visible)
1873 {
1874 // find a tab offset such that the selected tab is the
1875 // rightmost visible tab
1876 int max_width = W->len_x - col->tag_height;
1877 if (col->cur_tab < col->nr_tabs-1)
1878 max_width -= col->tag_height;
1879
1880 while (col->tab_offset < col->cur_tab)
1881 {
1882 int w = 0, i;
1883
1884 col->tab_offset++;
1885 for (i = col->tab_offset; i <= col->cur_tab; i++)
1886 w+= col->tabs[i].tag_width;
1887
1888 if (w < max_width)
1889 break;
1890 }
1891 }
1892 }
1893
tab_collection_keypress(widget_list * W,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)1894 static int tab_collection_keypress(widget_list *W, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
1895 {
1896 int shift_on = key_mod & KMOD_SHIFT;
1897 tab_collection *col = (tab_collection *) W->widget_info;
1898
1899 if (col->nr_tabs <= 0) return 0;
1900
1901 if (shift_on && KEY_DEF_CMP(K_ROTATERIGHT, key_code, key_mod))
1902 {
1903 if (col->nr_tabs == 1) return 1;
1904 hide_window (col->tabs[col->cur_tab].content_id);
1905 if (++col->cur_tab >= col->nr_tabs)
1906 col->cur_tab = 0;
1907 _tab_collection_make_cur_visible (W);
1908 return 1;
1909 }
1910 else if (shift_on && KEY_DEF_CMP(K_ROTATELEFT, key_code, key_mod))
1911 {
1912 if (col->nr_tabs == 1) return 1;
1913 hide_window (col->tabs[col->cur_tab].content_id);
1914 if (--col->cur_tab < 0)
1915 col->cur_tab = col->nr_tabs - 1;
1916 _tab_collection_make_cur_visible (W);
1917 return 1;
1918 }
1919
1920 return 0;
1921 }
1922
tab_collection_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,float size,int max_tabs,int right_margin)1923 int tab_collection_add_extended (int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint32 Flags, float size, int max_tabs, int right_margin)
1924 {
1925 int itab;
1926 window_info *win = &windows_list.window[window_id];
1927 tab_collection *T;
1928
1929 T = calloc (1, sizeof (tab_collection));
1930 T->max_tabs = max_tabs <= 0 ? 2 : max_tabs;
1931 T->tabs = calloc (T->max_tabs, sizeof (tab));
1932 // initialize all tabs content ids to -1 (unitialized window_
1933 for (itab = 0; itab < T->max_tabs; itab++)
1934 T->tabs[itab].content_id = -1;
1935 T->nr_tabs = 0;
1936 T->tag_height = tab_collection_calc_tab_height(win->font_category, size);
1937 T->button_size = (9 * T->tag_height) / 10;
1938 T->tabs_right_margin = right_margin;
1939 T->cur_tab = 0;
1940 T->tab_offset = 0;
1941 T->tab_last_visible = 0;
1942
1943 return widget_add (window_id, wid, OnInit, x, y, lx, ly, Flags, size, &tab_collection_type, T, NULL);
1944 }
1945
tab_collection_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly)1946 int tab_collection_add (int window_id, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly)
1947 {
1948 return tab_collection_add_extended (window_id, widget_id++, OnInit, x, y, lx, ly, 0, 1.0, 0, 0);
1949 }
1950
tab_add(int window_id,Uint32 col_id,const char * label,Uint16 tag_width,int closable,Uint32 flags)1951 int tab_add (int window_id, Uint32 col_id, const char *label, Uint16 tag_width, int closable, Uint32 flags)
1952 {
1953 widget_list *w = widget_find(window_id, col_id);
1954 tab_collection *col;
1955 int nr;
1956
1957 if (w == NULL || (col = (tab_collection *) w->widget_info) == NULL)
1958 return 0;
1959
1960 nr = col->nr_tabs++;
1961 if (nr >= col->max_tabs)
1962 {
1963 // shoot, we allocated too few tabs
1964 int old_max = col->max_tabs, new_max = 2 * old_max;
1965 int itab;
1966
1967 col->tabs = realloc ( col->tabs, new_max * sizeof (tab) );
1968 memset ( &(col->tabs[old_max]), 0, (new_max-old_max) * sizeof (tab) );
1969 for (itab = old_max; itab < new_max; itab++)
1970 col->tabs[itab].content_id = -1;
1971 col->max_tabs = new_max;
1972 }
1973
1974 safe_strncpy((char*)col->tabs[nr].label, label, sizeof (col->tabs[nr].label));
1975 col->tabs[nr].content_id = create_window ("", window_id, 0, w->pos_x, w->pos_y + col->tag_height, w->len_x, w->len_y - col->tag_height, ELW_TITLE_NONE|flags);
1976 col->tabs[nr].closable = closable ? 1 : 0;
1977
1978 if (tag_width > 0)
1979 {
1980 col->tabs[nr].tag_width = col->tabs[nr].min_tag_width = tag_width;
1981 }
1982 else
1983 {
1984 // compute tag width from label width
1985 col->tabs[nr].min_tag_width = 0;
1986 col->tabs[nr].tag_width = calc_tag_width(col->tabs[nr].label, w->fcat, w->size,
1987 col->tabs[nr].closable ? col->tag_height : 0);
1988 }
1989
1990 // set label color to default values
1991 col->tabs[nr].label_r = -1.0f;
1992 col->tabs[nr].label_g = -1.0f;
1993 col->tabs[nr].label_b = -1.0f;
1994
1995 return col->tabs[nr].content_id;
1996 }
1997
1998 // text field
_text_field_set_nr_visible_lines(widget_list * w)1999 static void _text_field_set_nr_visible_lines (widget_list *w)
2000 {
2001 text_field* tf = w->widget_info;
2002
2003 if (tf != NULL/* && (w->Flags & TEXT_FIELD_EDITABLE)*/)
2004 {
2005 tf->nr_visible_lines = get_max_nr_lines(w->len_y - 2*tf->y_space, w->fcat,
2006 tf->buffer[tf->msg].wrap_zoom);
2007 if (tf->nr_visible_lines < 0)
2008 tf->nr_visible_lines = 0;
2009 }
2010 }
2011
_text_field_set_nr_lines(widget_list * w,int nr_lines)2012 static void _text_field_set_nr_lines (widget_list *w, int nr_lines)
2013 {
2014 text_field* tf = w->widget_info;
2015 if (tf != NULL)
2016 {
2017 tf->nr_lines = nr_lines;
2018 if (tf->scroll_id != -1)
2019 {
2020 int bar_len = nr_lines >= tf->nr_visible_lines ? nr_lines - tf->nr_visible_lines : 0;
2021 vscrollbar_set_bar_len (w->window_id, tf->scroll_id, bar_len);
2022 tf->update_bar = 0;
2023 }
2024 }
2025 }
2026
_text_field_find_cursor_line(text_field * tf)2027 static void _text_field_find_cursor_line(text_field* tf)
2028 {
2029 int i, line = 0;
2030 const text_message* msg = &tf->buffer[tf->msg];
2031 for (i = 0; i < msg->len; i++)
2032 {
2033 if (i == tf->cursor) tf->cursor_line = line;
2034 if (msg->data[i] == '\n' || msg->data[i] == '\r') line++;
2035 }
2036 tf->nr_lines = line + 1; // we'll call _text_field_set_nr_lines later;
2037 if (tf->cursor >= msg->len) tf->cursor_line = line;
2038 tf->update_bar = 1;
2039 }
2040
_text_field_set_cursor_line_only(text_field * tf)2041 static void _text_field_set_cursor_line_only(text_field* tf)
2042 {
2043 int i;
2044 const text_message* msg = &tf->buffer[tf->msg];
2045 tf->cursor_line = 0;
2046 for (i = 0; i < tf->cursor; ++i)
2047 {
2048 if (msg->data[i] == '\n' || msg->data[i] == '\r')
2049 ++tf->cursor_line;
2050 }
2051 }
2052
text_field_get_selected_text(const text_field * tf)2053 char* text_field_get_selected_text (const text_field* tf)
2054 {
2055 int sm, sc, em, ec;
2056 const select_info* select;
2057 int len, max_len;
2058 char* text = NULL;
2059
2060 max_len = 0;
2061 len = 0;
2062 select = &tf->select;
2063 if (TEXT_FIELD_SELECTION_EMPTY(select)) return NULL;
2064
2065 if ((select->em > select->sm) || ((select->em == select->sm) && (select->ec >= select->sc)))
2066 {
2067 sm = select->sm;
2068 sc = select->sc;
2069 em = select->em;
2070 ec = select->ec;
2071 }
2072 else
2073 {
2074 sm = select->em;
2075 sc = select->ec;
2076 em = select->sm;
2077 ec = select->sc;
2078 }
2079 for (; (sm <= em) && (sm < tf->buf_size); sm++)
2080 {
2081 if (skip_message(&tf->buffer[sm], tf->chan_nr)) continue;
2082 while (sc < tf->buffer[sm].len)
2083 {
2084 char ch;
2085 if ((sm == em) && (sc > ec))
2086 {
2087 append_char(&text, '\0', &len, &max_len);
2088 return text;
2089 }
2090 ch = tf->buffer[sm].data[sc];
2091 if (ch == '\0')
2092 {
2093 break;
2094 }
2095 else if (ch == '\n')
2096 {
2097 append_char(&text, '\n', &len, &max_len);
2098 }
2099 else if (is_printable(ch))
2100 {
2101 append_char(&text, ch, &len, &max_len);
2102 }
2103 sc++;
2104 }
2105 append_char (&text, '\n', &len, &max_len);
2106 sc = 0;
2107 }
2108 append_char(&text, '\0', &len, &max_len);
2109 return text;
2110 }
2111
text_field_remove_selection(text_field * tf)2112 void text_field_remove_selection(text_field* tf)
2113 {
2114 int sm, sc, em, ec;
2115 text_message* msg;
2116 select_info* select;
2117
2118 select = &tf->select;
2119 if (TEXT_FIELD_SELECTION_EMPTY(select)) return;
2120
2121 if ((select->em > select->sm) || ((select->em == select->sm) && (select->ec >= select->sc)))
2122 {
2123 sm = select->sm;
2124 sc = select->sc;
2125 em = select->em;
2126 ec = select->ec;
2127 }
2128 else
2129 {
2130 sm = select->em;
2131 sc = select->ec;
2132 em = select->sm;
2133 ec = select->sc;
2134 }
2135
2136 // XXX Grum: should we remove messages we delete entirely?
2137 for (; (sm <= em) && (sm < tf->buf_size); sm++)
2138 {
2139 if (skip_message(&tf->buffer[sm], tf->chan_nr)) continue;
2140 msg = &tf->buffer[sm];
2141 if (em > sm)
2142 {
2143 if (sc == 0)
2144 {
2145 msg->data[0] = '\0';
2146 msg->len = 0;
2147 }
2148 else
2149 {
2150 memmove (msg->data, &(msg->data[sc]), msg->len-sc+1);
2151 msg->len -= sc;
2152 }
2153 }
2154 else
2155 {
2156 if (ec >= msg->len) ec = msg->len - 1;
2157 memmove(&msg->data[sc], &msg->data[ec + 1], msg->len - ec);
2158 msg->len -= ec - sc + 1;
2159 if (tf->cursor > sc) tf->cursor = sc;
2160 }
2161 sc = 0;
2162 }
2163 _text_field_find_cursor_line(tf);
2164 }
2165
_text_field_scroll_to_cursor(widget_list * w)2166 void _text_field_scroll_to_cursor (widget_list *w)
2167 {
2168 text_field *tf = w->widget_info;
2169
2170 if (tf == NULL || tf->scroll_id == -1)
2171 return;
2172
2173 // scroll only if the cursor is currently not visible
2174 if (tf->cursor_line < tf->line_offset)
2175 vscrollbar_set_pos (w->window_id, tf->scroll_id, tf->cursor_line);
2176 else if (tf->cursor_line >= tf->line_offset + tf->nr_visible_lines)
2177 vscrollbar_set_pos (w->window_id, tf->scroll_id, tf->cursor_line - tf->nr_visible_lines + 1);
2178 }
2179
_text_field_cursor_left(widget_list * w,int skipword)2180 void _text_field_cursor_left(widget_list *w, int skipword)
2181 {
2182 text_field *tf = w->widget_info;
2183 text_message* msg;
2184 int i;
2185
2186 if (tf == NULL || tf->cursor <= 0)
2187 return;
2188
2189 msg = &(tf->buffer[tf->msg]);
2190 i = tf->cursor - 1;
2191 if (i > 0 && msg->data[i] == '\r')
2192 --i;
2193 if (skipword)
2194 {
2195 while (i > 0 && isspace(msg->data[i]))
2196 --i;
2197 while (i > 0 && !isspace(msg->data[i-1]))
2198 --i;
2199 }
2200 tf->cursor = i;
2201 _text_field_set_cursor_line_only(tf);
2202
2203 if (tf->scroll_id != -1)
2204 _text_field_scroll_to_cursor (w);
2205 }
2206
_text_field_cursor_right(widget_list * w,int skipword)2207 void _text_field_cursor_right(widget_list *w, int skipword)
2208 {
2209 text_field *tf = w->widget_info;
2210 text_message* msg;
2211 int i;
2212
2213 if (tf == NULL)
2214 return;
2215
2216 msg = &(tf->buffer[tf->msg]);
2217 if (tf->cursor >= msg->len)
2218 return;
2219
2220 i = tf->cursor + 1;
2221 if (i < msg->len && msg->data[i] == '\r')
2222 ++i;
2223 if (skipword)
2224 {
2225 while (i < msg->len && !isspace(msg->data[i]))
2226 ++i;
2227 while (i < msg->len && isspace(msg->data[i]))
2228 ++i;
2229 }
2230 tf->cursor = i;
2231 _text_field_set_cursor_line_only(tf);
2232
2233 if (tf->scroll_id != -1)
2234 _text_field_scroll_to_cursor (w);
2235 }
2236
_text_field_cursor_up(widget_list * w)2237 void _text_field_cursor_up (widget_list *w)
2238 {
2239 text_field *tf = w->widget_info;
2240 text_message *msg;
2241 int line_start; // The beginning of the line we're processing
2242 int prev_line_start; // Beginning of the line before the line with the cursor
2243 int cursor_offset; // Position of the cursor on this line
2244 int prev_line_end; // 1 past last character of previous line
2245
2246 if (tf == NULL || tf->cursor_line <= 0)
2247 return;
2248
2249 // find where the cursor is on this line
2250 msg = &(tf->buffer[tf->msg]);
2251 for (line_start = tf->cursor; line_start > 0; line_start--)
2252 if (msg->data[line_start-1] == '\r' || msg->data[line_start-1] == '\n')
2253 break;
2254 if (line_start == 0)
2255 // shouldn't happen
2256 return;
2257
2258 cursor_offset = tf->cursor - line_start;
2259 prev_line_end = msg->data[line_start-1] == '\r' ? line_start - 1 : line_start;
2260
2261 // Now find where the previous line starts
2262 for (prev_line_start = line_start-1; prev_line_start > 0; prev_line_start--)
2263 if (msg->data[prev_line_start-1] == '\r' || msg->data[prev_line_start-1] == '\n')
2264 break;
2265
2266 tf->cursor = min2i(prev_line_start + cursor_offset, prev_line_end - 1);
2267 tf->cursor_line--;
2268 if (tf->scroll_id != -1)
2269 _text_field_scroll_to_cursor (w);
2270 }
2271
_text_field_cursor_down(widget_list * w)2272 void _text_field_cursor_down (widget_list *w)
2273 {
2274 text_field *tf = w->widget_info;
2275 text_message *msg;
2276 int line_start; // The beginning of the line we're processing
2277 int next_line_start; // Beginning of the line after the line with the cursor
2278 int next_line_end; // Index of last character on line after the line with the cursor
2279 int cursor_offset; // Position of the cursor on this line
2280
2281 if (tf == NULL || tf->cursor_line >= tf->nr_lines-1)
2282 return;
2283
2284 // find where the cursor is on this line
2285 msg = &(tf->buffer[tf->msg]);
2286 for (line_start = tf->cursor; line_start > 0; line_start--)
2287 if (msg->data[line_start-1] == '\r' || msg->data[line_start-1] == '\n')
2288 break;
2289 cursor_offset = tf->cursor - line_start;
2290
2291 // Now find where the next line starts
2292 for (next_line_start = tf->cursor; next_line_start < msg->len; next_line_start++)
2293 if (msg->data[next_line_start] == '\r' || msg->data[next_line_start] == '\n')
2294 break;
2295 if (next_line_start >= msg->len)
2296 // shouldn't happen
2297 return;
2298
2299 // skip newline
2300 ++next_line_start;
2301 // Find where the next line ends
2302 for (next_line_end = next_line_start; next_line_end < msg->len; next_line_end++)
2303 {
2304 if (msg->data[next_line_end] == '\r')
2305 {
2306 --next_line_end;
2307 break;
2308 }
2309 if (msg->data[next_line_end] == '\n')
2310 break;
2311 }
2312
2313 tf->cursor = min2i(next_line_start + cursor_offset, next_line_end);
2314 tf->cursor_line++;
2315 if (tf->scroll_id != -1)
2316 _text_field_scroll_to_cursor (w);
2317 }
2318
_text_field_cursor_home(widget_list * w)2319 void _text_field_cursor_home (widget_list *w)
2320 {
2321 text_field *tf = w->widget_info;
2322 text_message *msg;
2323 int i;
2324
2325 if (tf == NULL)
2326 return;
2327
2328 msg = &(tf->buffer[tf->msg]);
2329 for (i = tf->cursor; i > 0; i--)
2330 if (msg->data[i-1] == '\r' || msg->data[i-1] == '\n')
2331 break;
2332
2333 tf->cursor = i;
2334 // tf->cursor_line doesn't change
2335 }
2336
_text_field_cursor_end(widget_list * w)2337 void _text_field_cursor_end (widget_list *w)
2338 {
2339 text_field *tf = w->widget_info;
2340 text_message *msg;
2341 int i;
2342
2343 if (tf == NULL)
2344 return;
2345
2346 msg = &(tf->buffer[tf->msg]);
2347 for (i = tf->cursor; i < msg->len; ++i)
2348 {
2349 if (msg->data[i] == '\n' || (i+1 < msg->len && msg->data[i+1] == '\r'))
2350 break;
2351 }
2352
2353 tf->cursor = i;
2354 // tf->cursor_line doesn't change
2355 }
2356
_text_field_cursor_page_up(widget_list * w)2357 void _text_field_cursor_page_up (widget_list *w)
2358 {
2359 text_field *tf = w->widget_info;
2360
2361 if (tf == NULL)
2362 return;
2363
2364 if (tf->nr_visible_lines > tf->cursor_line)
2365 {
2366 tf->cursor = 0;
2367 tf->cursor_line = 0;
2368 }
2369 else
2370 {
2371 int i, nr_lines;
2372 text_message *msg = &(tf->buffer[tf->msg]);
2373
2374 for (i = tf->cursor, nr_lines = tf->nr_visible_lines; i > 0; i--)
2375 {
2376 if (msg->data[i-1] == '\n' || msg->data[i-1] == '\r')
2377 if (--nr_lines == 0) break;
2378 }
2379 tf->cursor = i;
2380 tf->cursor_line -= tf->nr_visible_lines - 1;
2381 }
2382
2383 if (tf->scroll_id != -1)
2384 _text_field_scroll_to_cursor (w);
2385 }
2386
_text_field_cursor_page_down(widget_list * w)2387 void _text_field_cursor_page_down (widget_list *w)
2388 {
2389 text_field *tf = w->widget_info;
2390 text_message *msg;
2391
2392 if (tf == NULL)
2393 return;
2394
2395 msg = &(tf->buffer[tf->msg]);
2396 if (tf->cursor == msg->len || tf->nr_visible_lines <= 1)
2397 {
2398 return;
2399 }
2400 if (tf->nr_visible_lines >= tf->nr_lines - tf->cursor_line)
2401 {
2402 tf->cursor = msg->len;
2403 tf->cursor_line = tf->nr_lines - 1;
2404 }
2405 else
2406 {
2407 int i, nr_lines;
2408
2409 for (i = tf->cursor+1, nr_lines = tf->nr_visible_lines-1; i < msg->len; i++)
2410 {
2411 if (msg->data[i-1] == '\n' || msg->data[i-1] == '\r')
2412 if (--nr_lines == 0) break;
2413 }
2414 tf->cursor = i;
2415 tf->cursor_line += tf->nr_visible_lines - 1;
2416 }
2417
2418 if (tf->scroll_id != -1)
2419 _text_field_scroll_to_cursor (w);
2420 }
2421
_text_field_delete_backward(widget_list * w)2422 void _text_field_delete_backward(widget_list * w)
2423 {
2424 text_field *tf = w->widget_info;
2425 text_message *msg;
2426 int ni, nr_lines;
2427
2428 if (tf == NULL)
2429 return;
2430
2431 msg = &(tf->buffer[tf->msg]);
2432 ni = tf->cursor - 1;
2433 while (ni > 0 && msg->data[ni] == '\r')
2434 --ni;
2435
2436 memmove(msg->data + ni, msg->data + tf->cursor, msg->len - tf->cursor + 1);
2437 msg->len -= tf->cursor - ni;
2438 tf->cursor = ni;
2439
2440 // set invalid width to force rewrap
2441 msg->wrap_width = 0;
2442 nr_lines = rewrap_message(msg, w->fcat, w->size, w->len_x - 2*tf->x_space - tf->scrollbar_width,
2443 &tf->cursor);
2444 _text_field_set_cursor_line_only(tf);
2445 _text_field_set_nr_lines(w, nr_lines);
2446
2447 if (tf->scroll_id != -1)
2448 _text_field_scroll_to_cursor (w);
2449
2450 }
2451
_text_field_delete_forward(widget_list * w)2452 void _text_field_delete_forward(widget_list *w)
2453 {
2454 text_field *tf = w->widget_info;
2455 text_message *msg;
2456 int ni, nr_lines;
2457
2458 if (tf == NULL)
2459 return;
2460
2461 msg = &(tf->buffer[tf->msg]);
2462 ni = tf->cursor + 1;
2463 while (ni < msg->len && msg->data[ni] == '\r')
2464 ++ni;
2465
2466 memmove(msg->data + tf->cursor, msg->data + ni, msg->len - ni + 1);
2467 msg->len -= ni - tf->cursor;
2468
2469 // set invalid width to force rewrap
2470 msg->wrap_width = 0;
2471 nr_lines = rewrap_message(msg, w->fcat, w->size, w->len_x - 2*tf->x_space - tf->scrollbar_width,
2472 &tf->cursor);
2473 _text_field_set_cursor_line_only(tf);
2474 _text_field_set_nr_lines (w, nr_lines);
2475 }
2476
_text_field_insert_char(widget_list * w,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)2477 void _text_field_insert_char (widget_list *w, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
2478 {
2479 Uint8 ch = key_to_char (key_unicode);
2480 text_field *tf = w->widget_info;
2481 text_message *msg;
2482 int nr_lines;
2483
2484 if (tf == NULL)
2485 return;
2486
2487 msg = &(tf->buffer[tf->msg]);
2488
2489 if (key_code == SDLK_RETURN || key_code == SDLK_KP_ENTER)
2490 ch = '\n';
2491
2492 // keep one position free, so that we can always introduce a
2493 // soft line break if necessary.
2494 if (msg->len >= msg->size-2)
2495 {
2496 if (w->Flags & TEXT_FIELD_CAN_GROW)
2497 {
2498 msg->size *= 2;
2499 msg->data = realloc (msg->data, msg->size * sizeof (char) );
2500 }
2501 }
2502 tf->cursor += put_char_in_buffer (msg, ch, tf->cursor);
2503
2504 // set invalid width to force rewrap
2505 msg->wrap_width = 0;
2506 nr_lines = rewrap_message(msg, w->fcat, w->size, w->len_x - 2*tf->x_space - tf->scrollbar_width,
2507 &tf->cursor);
2508 _text_field_set_cursor_line_only(tf);
2509 _text_field_set_nr_lines (w, nr_lines);
2510
2511 // XXX FIXME: Grum: is the following even possible?
2512 while (msg->data[tf->cursor] == '\r') {
2513 tf->cursor++;
2514 tf->cursor_line++;
2515 }
2516
2517 if (tf->scroll_id != -1)
2518 _text_field_scroll_to_cursor (w);
2519 }
2520
_text_field_copy_to_clipboard(text_field * tf)2521 void _text_field_copy_to_clipboard(text_field *tf)
2522 {
2523 char* text = text_field_get_selected_text(tf);
2524 if (text != NULL)
2525 {
2526 copy_to_clipboard(text);
2527 free(text);
2528 }
2529 }
2530
2531 #if !defined OSX && !defined WINDOWS
2532 #ifdef MIDDLE_MOUSE_PASTE
_text_field_copy_to_primary(text_field * tf)2533 void _text_field_copy_to_primary(text_field *tf)
2534 {
2535 char* text = text_field_get_selected_text(tf);
2536 if (text)
2537 {
2538 copy_to_primary(text);
2539 free(text);
2540 }
2541 }
2542 #endif
2543 #endif
2544
2545 static void update_cursor_selection(widget_list* w, int flag);
2546
text_field_resize(widget_list * w,int width,int height)2547 static int text_field_resize (widget_list *w, int width, int height)
2548 {
2549 text_field *tf = w->widget_info;
2550
2551 if (tf != NULL)
2552 {
2553 if (tf->scroll_id != -1)
2554 {
2555 tf->scrollbar_width = w->size * ELW_BOX_SIZE;
2556 widget_resize (w->window_id, tf->scroll_id, tf->scrollbar_width, height);
2557 widget_move (w->window_id, tf->scroll_id, w->pos_x + width - tf->scrollbar_width, w->pos_y);
2558 }
2559 if (tf->buffer != NULL)
2560 {
2561 int i, nr_lines = 0, nr_vis = tf->nr_visible_lines;
2562
2563 _text_field_set_nr_visible_lines (w);
2564 if (tf->nr_visible_lines != nr_vis)
2565 tf->select.lines = realloc (tf->select.lines, tf->nr_visible_lines * sizeof (text_field_line));
2566 tf->select.sm = tf->select.em = tf->select.sc = tf->select.ec = -1;
2567
2568 for (i = 0; i < tf->buf_size; i++)
2569 {
2570 int *cursor = i == tf->msg ? &(tf->cursor) : NULL;
2571 nr_lines += rewrap_message(tf->buffer+i, w->fcat, w->size,
2572 width - tf->scrollbar_width, cursor);
2573 }
2574 _text_field_set_nr_lines (w, nr_lines);
2575
2576 _text_field_find_cursor_line (tf);
2577 }
2578 }
2579
2580 return 1;
2581 }
2582
text_field_move(widget_list * w,int pos_x,int pos_y)2583 static int text_field_move(widget_list *w, int pos_x, int pos_y)
2584 {
2585 if (w)
2586 {
2587 text_field *tf = w->widget_info;
2588 if (tf && tf->scroll_id != -1)
2589 widget_move (w->window_id, tf->scroll_id, w->pos_x + w->len_x - tf->scrollbar_width, w->pos_y);
2590 }
2591 return 1;
2592 }
2593
2594 static int insert_window_id = -1;
2595 static int insert_widget_id = -1;
2596
2597 /* insert the given text string into the text widget */
text_widget_insert(const char * thestring)2598 static void text_widget_insert(const char *thestring)
2599 {
2600 widget_list* w = widget_find (insert_window_id, insert_widget_id);
2601 if (w != NULL)
2602 {
2603 Uint32 saved_flag = w->Flags & TEXT_FIELD_NO_KEYPRESS;
2604 if (w->Flags & TEXT_FIELD_MOUSE_EDITABLE)
2605 w->Flags &= ~TEXT_FIELD_NO_KEYPRESS;
2606 widget_unset_flags(insert_window_id, insert_widget_id, WIDGET_DISABLED);
2607 text_field_paste(w, thestring);
2608 w->Flags |= saved_flag;
2609 }
2610 insert_window_id = insert_widget_id = -1;
2611 }
2612
2613
2614 /* the edit context menu callback */
context_edit_handler(window_info * win,int widget_id,int mx,int my,int option)2615 static int context_edit_handler(window_info *win, int widget_id, int mx, int my, int option)
2616 {
2617 widget_list* w = NULL;
2618 Uint32 saved_flag;
2619
2620 if (win == NULL)
2621 return 0;
2622 w = widget_find (win->window_id, widget_id);
2623 if (w == NULL)
2624 return 0;
2625
2626 saved_flag = w->Flags & TEXT_FIELD_NO_KEYPRESS;
2627 if (w->Flags & TEXT_FIELD_MOUSE_EDITABLE)
2628 w->Flags &= ~TEXT_FIELD_NO_KEYPRESS;
2629 switch (option)
2630 {
2631 case 0: text_field_keypress(w, 0, 0, K_CUT.key_code, 0, K_CUT.key_mod); break;
2632 case 1: text_field_keypress(w, 0, 0, K_COPY.key_code, 0, K_COPY.key_mod); break;
2633 case 2:
2634 if (!text_field_keypress(w, 0, 0, K_PASTE.key_code, 0, K_PASTE.key_mod))
2635 start_paste(NULL);
2636 break;
2637 case 4:
2638 {
2639 insert_window_id = win->window_id;
2640 insert_widget_id = widget_id;
2641 get_date(text_widget_insert);
2642 }
2643 break;
2644 case 5:
2645 {
2646 char str[20];
2647 safe_snprintf(str, sizeof(str), "%1d:%02d:%02d", real_game_minute/60, real_game_minute%60, real_game_second);
2648 widget_unset_flags(win->window_id, widget_id, WIDGET_DISABLED);
2649 text_field_paste(w, str);
2650 }
2651 break;
2652 case 6:
2653 {
2654 actor *me = get_our_actor ();
2655 if (me != NULL)
2656 {
2657 char str[20];
2658 safe_snprintf(str, sizeof(str), "%d,%d", me->x_tile_pos, me->y_tile_pos);
2659 widget_unset_flags(win->window_id, widget_id, WIDGET_DISABLED);
2660 text_field_paste(w, str);
2661 }
2662 }
2663 break;
2664 }
2665 w->Flags |= saved_flag;
2666 return 1;
2667 }
2668
2669 /* the edit context menu pre show callback */
context_edit_pre_show_handler(window_info * win,int widget_id,int mx,int my,window_info * cm_win)2670 static void context_edit_pre_show_handler(window_info *win, int widget_id, int mx, int my, window_info *cm_win)
2671 {
2672 widget_list* w = NULL;
2673 text_field *tf;
2674 int is_grey = 0;
2675
2676 if (win == NULL)
2677 return;
2678 w = widget_find (win->window_id, widget_id);
2679 if (w == NULL)
2680 return;
2681
2682 tf = w->widget_info;
2683 is_grey = TEXT_FIELD_SELECTION_EMPTY(&tf->select);
2684 cm_grey_line(cm_edit_id, 1, is_grey);
2685
2686 is_grey = is_grey || !(w->Flags & TEXT_FIELD_EDITABLE)
2687 || ((w->Flags & TEXT_FIELD_NO_KEYPRESS) && !(w->Flags & TEXT_FIELD_MOUSE_EDITABLE));
2688 cm_grey_line(cm_edit_id, 0, is_grey);
2689
2690 is_grey = !((w->Flags & TEXT_FIELD_EDITABLE) || (w->Flags & TEXT_FIELD_MOUSE_EDITABLE));
2691 cm_grey_line(cm_edit_id, 2, is_grey);
2692 cm_grey_line(cm_edit_id, 4, is_grey);
2693 cm_grey_line(cm_edit_id, 5, is_grey);
2694 cm_grey_line(cm_edit_id, 6, is_grey);
2695 }
2696
text_field_keypress(widget_list * w,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)2697 int text_field_keypress(widget_list *w, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
2698 {
2699 Uint8 ch = key_to_char(key_unicode);
2700 text_field *tf;
2701 text_message *msg;
2702 int alt_on = key_mod & KMOD_ALT, ctrl_on = key_mod & KMOD_CTRL;
2703 int shift_on = key_mod & KMOD_SHIFT;
2704
2705 if (w == NULL) return 0;
2706 tf = w->widget_info;
2707
2708 if (KEY_DEF_CMP(K_COPY, key_code, key_mod) || KEY_DEF_CMP(K_COPY_ALT, key_code, key_mod))
2709 {
2710 _text_field_copy_to_clipboard (tf);
2711 return 1;
2712 }
2713
2714 if ( !(w->Flags & TEXT_FIELD_EDITABLE) ) return 0;
2715 if (w->Flags & TEXT_FIELD_NO_KEYPRESS) return 0;
2716
2717 msg = &(tf->buffer[tf->msg]);
2718
2719 if (is_printable (ch) || key_code == SDLK_UP || key_code == SDLK_DOWN ||
2720 key_code == SDLK_LEFT || key_code == SDLK_RIGHT || key_code == SDLK_HOME ||
2721 key_code == SDLK_END || key_code == SDLK_BACKSPACE || key_code == SDLK_DELETE)
2722 {
2723 /* Stop blinking on input */
2724 tf->next_blink = cur_time + TF_BLINK_DELAY;
2725 }
2726
2727 if ((key_code == SDLK_LEFT) || (key_code == SDLK_RIGHT) || (key_code == SDLK_UP) || (key_code == SDLK_DOWN) ||
2728 (key_code == SDLK_HOME) || (key_code == SDLK_END))
2729 {
2730 if (shift_on)
2731 {
2732 if (TEXT_FIELD_SELECTION_EMPTY(&tf->select))
2733 update_cursor_selection(w, 0);
2734 }
2735 else
2736 {
2737 TEXT_FIELD_CLEAR_SELECTION(&tf->select);
2738 }
2739 }
2740
2741 if (key_code == SDLK_LEFT)
2742 {
2743 _text_field_cursor_left (w, ctrl_on);
2744 if (shift_on) update_cursor_selection(w, 1);
2745 return 1;
2746 }
2747 else if (key_code == SDLK_RIGHT)
2748 {
2749 _text_field_cursor_right (w, ctrl_on);
2750 if (shift_on) update_cursor_selection(w, 1);
2751 return 1;
2752 }
2753 else if (key_code == SDLK_UP && !ctrl_on && !alt_on && tf->cursor >= 0)
2754 {
2755 _text_field_cursor_up (w);
2756 if (shift_on) update_cursor_selection(w, 1);
2757 return 1;
2758 }
2759 else if (key_code == SDLK_DOWN && !ctrl_on && !alt_on && tf->cursor >= 0)
2760 {
2761 _text_field_cursor_down (w);
2762 if (shift_on) update_cursor_selection(w, 1);
2763 return 1;
2764 }
2765 else if (key_code == SDLK_HOME)
2766 {
2767 if (ctrl_on)
2768 {
2769 tf->cursor = 0;
2770 tf->cursor_line = 0;
2771 if (tf->scroll_id != -1)
2772 _text_field_scroll_to_cursor (w);
2773 }
2774 else
2775 {
2776 _text_field_cursor_home (w);
2777 }
2778 if (shift_on) update_cursor_selection(w, 1);
2779 return 1;
2780 }
2781 else if (key_code == SDLK_END)
2782 {
2783 if (ctrl_on)
2784 {
2785 tf->cursor = msg->len;
2786 tf->cursor_line = tf->nr_lines - 1;
2787 if (tf->scroll_id != -1)
2788 _text_field_scroll_to_cursor (w);
2789 }
2790 else
2791 {
2792 _text_field_cursor_end (w);
2793 }
2794 if (shift_on) update_cursor_selection(w, 1);
2795 return 1;
2796 }
2797 else if (key_code == SDLK_PAGEUP)
2798 {
2799 _text_field_cursor_page_up (w);
2800 if (shift_on) update_cursor_selection(w, 1);
2801 return 1;
2802 }
2803 else if (key_code == SDLK_PAGEDOWN)
2804 {
2805 _text_field_cursor_page_down (w);
2806 if (shift_on) update_cursor_selection(w, 1);
2807 return 1;
2808 }
2809 else if ((key_code == SDLK_BACKSPACE || key_code == SDLK_DELETE
2810 #ifdef OSX
2811 || ch == 127
2812 #endif
2813 ) && !TEXT_FIELD_SELECTION_EMPTY(&tf->select))
2814 {
2815 text_field_remove_selection(tf);
2816 TEXT_FIELD_CLEAR_SELECTION(&tf->select);
2817 return 1;
2818 }
2819 #ifdef OSX
2820 else if (key_code == SDLK_BACKSPACE || ch == 127)
2821 #else
2822 else if (key_code == SDLK_BACKSPACE)
2823 #endif
2824 {
2825 if (tf->cursor > 0)
2826 _text_field_delete_backward (w);
2827 return 1;
2828 }
2829 else if (key_code == SDLK_DELETE)
2830 {
2831 if (tf->cursor < msg->len)
2832 _text_field_delete_forward (w);
2833 return 1;
2834 }
2835 else if (KEY_DEF_CMP(K_CUT, key_code, key_mod))
2836 {
2837 _text_field_copy_to_clipboard(tf);
2838 if (!TEXT_FIELD_SELECTION_EMPTY(&tf->select))
2839 {
2840 text_field_remove_selection(tf);
2841 TEXT_FIELD_CLEAR_SELECTION(&tf->select);
2842 }
2843 return 1;
2844 }
2845 else if (KEY_DEF_CMP(K_PASTE, key_code, key_mod) || KEY_DEF_CMP(K_PASTE_ALT, key_code, key_mod))
2846 {
2847 if (!TEXT_FIELD_SELECTION_EMPTY(&tf->select))
2848 {
2849 text_field_remove_selection(tf);
2850 TEXT_FIELD_CLEAR_SELECTION(&tf->select);
2851 }
2852 start_paste(w);
2853 return 1;
2854 }
2855 else if (!alt_on && !ctrl_on && ( is_printable (ch)
2856 || ((key_code == SDLK_RETURN || key_code == SDLK_KP_ENTER) && !(w->Flags&TEXT_FIELD_IGNORE_RETURN)) ) && ch != '`' )
2857 {
2858 if (!TEXT_FIELD_SELECTION_EMPTY(&tf->select))
2859 {
2860 text_field_remove_selection(tf);
2861 TEXT_FIELD_CLEAR_SELECTION(&tf->select);
2862 }
2863 _text_field_insert_char (w, key_code, key_unicode, key_mod);
2864 return 1;
2865 }
2866 return 0;
2867 }
2868
_set_edit_pos(text_field * tf,int x,int y,font_cat fcat)2869 void _set_edit_pos (text_field* tf, int x, int y, font_cat fcat)
2870 {
2871 unsigned int i = tf->offset;
2872 unsigned int nrlines = 0, line = 0;
2873 int px = 0;
2874 text_message* msg = &(tf->buffer[tf->msg]);
2875 int line_skip = get_line_skip(fcat, msg->wrap_zoom);
2876
2877 if (msg->len == 0)
2878 return; // nothing to do, there is no string
2879
2880 nrlines = y / line_skip;
2881 for (; line < nrlines && i < msg->len; i++) {
2882 switch (msg->data[i]) {
2883 case '\r':
2884 case '\n':
2885 ++line;
2886 break;
2887 case '\0':
2888 tf->cursor = i;
2889 tf->cursor_line = tf->line_offset + line;
2890 return;
2891 }
2892 }
2893
2894 tf->cursor_line = tf->line_offset + nrlines;
2895 for (; i < msg->len; i++) {
2896 switch (msg->data[i]) {
2897 case '\r':
2898 tf->cursor = i-1;
2899 return;
2900 case '\n':
2901 case '\0':
2902 tf->cursor = i;
2903 return;
2904 default:
2905 px += get_char_width_zoom(msg->data[i], fcat, msg->wrap_zoom);
2906 if (px >= x)
2907 {
2908 tf->cursor = i;
2909 return;
2910 }
2911 }
2912 }
2913 tf->cursor = msg->len;
2914 }
2915
update_selection(int x,int y,widget_list * w,int drag)2916 void update_selection(int x, int y, widget_list* w, int drag)
2917 {
2918 int line, col;
2919 int cx = 0;
2920 text_field* tf;
2921 text_message* msg;
2922
2923 tf = w->widget_info;
2924 if (tf == NULL) return;
2925
2926 line = y / get_line_skip(w->fcat, w->size);
2927 if (line < 0 || line >= tf->nr_visible_lines || tf->select.lines[line].msg == -1)
2928 {
2929 // Invalid position, if we were dragging keep the selection
2930 // intact, but if this was a click, clear it
2931 if (!drag)
2932 tf->select.sm = tf->select.sc = tf->select.em = tf->select.ec = -1;
2933 return;
2934 }
2935
2936 msg = &tf->buffer[tf->select.lines[line].msg];
2937 for (col = tf->select.lines[line].chr; col < msg->len; col++)
2938 {
2939 if (msg->data[col] == '\r' || msg->data[col] == '\n' || msg->data[col] == '\0')
2940 break;
2941 cx += get_char_width_zoom(msg->data[col], w->fcat, w->size);
2942 if (cx >= x)
2943 break;
2944 }
2945 if (!drag || tf->select.sm == -1 || tf->select.sc == -1)
2946 {
2947 // click (or selection still empty), set the start position
2948 tf->select.sm = tf->select.lines[line].msg;
2949 tf->select.sc = col;
2950 tf->select.em = -1;
2951 tf->select.ec = -1;
2952 }
2953 else
2954 {
2955 // drag, set the end position
2956 tf->select.em = tf->select.lines[line].msg;
2957 tf->select.ec = col;
2958 #if !defined OSX && !defined WINDOWS
2959 #ifdef MIDDLE_MOUSE_PASTE
2960 _text_field_copy_to_primary(tf);
2961 #endif
2962 #endif
2963 }
2964 }
2965
update_cursor_selection(widget_list * w,int update_end)2966 static void update_cursor_selection(widget_list* w, int update_end)
2967 {
2968 text_field* tf;
2969 int line;
2970
2971 tf = w->widget_info;
2972 if (tf == NULL) return;
2973
2974 line = tf->cursor_line - tf->line_offset;
2975 if (line < 0)
2976 line = 0;
2977 else if (line >= tf->nr_visible_lines)
2978 line = tf->nr_visible_lines - 1;
2979
2980 if (!update_end)
2981 {
2982 tf->select.sm = tf->select.lines[line].msg;
2983 tf->select.sc = tf->cursor;
2984 tf->select.em = -1;
2985 tf->select.ec = -1;
2986 }
2987 else
2988 {
2989 tf->select.em = tf->select.lines[line].msg;
2990 tf->select.ec = tf->cursor;
2991 #if !defined OSX && !defined WINDOWS
2992 #ifdef MIDDLE_MOUSE_PASTE
2993 _text_field_copy_to_primary(tf);
2994 #endif
2995 #endif
2996 }
2997 }
2998
text_field_click(widget_list * w,int mx,int my,Uint32 flags)2999 static int text_field_click(widget_list *w, int mx, int my, Uint32 flags)
3000 {
3001 Uint32 buttons;
3002 text_field *tf;
3003 int em_before;
3004
3005 tf = w->widget_info;
3006 if (tf == NULL)
3007 return 0;
3008
3009 // send scroll wheel moves to the scrollbar
3010 if ((flags & ELW_WHEEL) != 0 && tf->scroll_id != -1)
3011 {
3012 widget_list* sbw = widget_find (w->window_id, tf->scroll_id);
3013 return vscrollbar_click (sbw, mx, my, flags);
3014 }
3015
3016 // if no scrollbar, only handle mouse button clicks,
3017 // not scroll wheels moves
3018 buttons = flags & ELW_MOUSE_BUTTON;
3019 if (!buttons)
3020 return 0;
3021
3022 #if !defined OSX && !defined WINDOWS
3023 #ifdef MIDDLE_MOUSE_PASTE
3024 // Don't handle middle button clicks (paste) if the text field is not editable
3025 if (buttons == ELW_MID_MOUSE && !(w->Flags & TEXT_FIELD_EDITABLE))
3026 return 0;
3027 #endif
3028 #endif
3029
3030 em_before = tf->select.em;
3031 update_selection(mx, my, w, 0);
3032 if (em_before != -1 && tf->select.em == -1)
3033 /* We deselected some text, click was handled */
3034 return 1;
3035
3036 if ( (w->Flags & TEXT_FIELD_EDITABLE) == 0)
3037 return 0;
3038
3039 _set_edit_pos(tf, mx, my, w->fcat);
3040
3041 #if !defined OSX && !defined WINDOWS
3042 #ifdef MIDDLE_MOUSE_PASTE
3043 if (flags & ELW_MID_MOUSE)
3044 start_paste_from_primary(w);
3045 #endif // MIDDLE_MOUSE_PASTE
3046 #endif
3047
3048 return 1;
3049 }
3050
text_field_drag(widget_list * w,int mx,int my,Uint32 flags,int dx,int dy)3051 static int text_field_drag(widget_list *w, int mx, int my, Uint32 flags, int dx, int dy)
3052 {
3053 update_selection(mx, my, w, 1);
3054 return 1;
3055 }
3056
text_field_destroy(widget_list * w)3057 static int text_field_destroy(widget_list *w)
3058 {
3059 text_field *tf = w->widget_info;
3060 if (tf != NULL)
3061 {
3062 /* remove the context menu */
3063 cm_remove_widget(w->window_id, w->id);
3064 if (tf->scroll_id != -1)
3065 widget_destroy (w->window_id, tf->scroll_id);
3066 if (tf->select.lines != NULL) free(tf->select.lines);
3067 free (tf);
3068 }
3069
3070 return 1;
3071 }
3072
text_field_set_color(widget_list * widget,float r,float g,float b)3073 static int text_field_set_color(widget_list *widget, float r, float g, float b)
3074 {
3075 text_field *tf = widget->widget_info;
3076 if (!tf)
3077 return 0;
3078
3079 if (tf->scroll_id != -1)
3080 widget_set_color(widget->window_id, tf->scroll_id, r, g, b);
3081 return 1;
3082 }
3083
text_field_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint32 Flags,font_cat fcat,float size,text_message * buf,int buf_size,Uint8 chan_filt,int x_space,int y_space)3084 int text_field_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y,
3085 Uint16 lx, Uint16 ly, Uint32 Flags, font_cat fcat, float size, text_message *buf, int buf_size,
3086 Uint8 chan_filt, int x_space, int y_space)
3087 {
3088 int res;
3089
3090 text_field *T = calloc (1, sizeof (text_field));
3091 T->x_space = x_space,
3092 T->y_space = y_space,
3093 T->msg = 0;
3094 T->offset = 0;
3095 T->buffer = buf;
3096 T->buf_size = buf_size;
3097 T->nr_lines = 1; //We'll always have one line in the text field.
3098 T->chan_nr = chan_filt;
3099 T->cursor = (Flags & TEXT_FIELD_EDITABLE) ? 0 : -1;
3100 T->cursor_line = T->cursor;
3101 T->next_blink = TF_BLINK_DELAY;
3102 if (Flags & TEXT_FIELD_SCROLLBAR)
3103 {
3104 T->scrollbar_width = size * ELW_BOX_SIZE;
3105 T->scroll_id = vscrollbar_add_extended (window_id, widget_id++, NULL, x + lx-T->scrollbar_width, y, T->scrollbar_width, ly, 0, size, 0, 1, 1);
3106 }
3107 else
3108 {
3109 T->scroll_id = -1;
3110 T->scrollbar_width = 0;
3111 }
3112
3113 res = widget_add (window_id, wid, OnInit, x, y, lx, ly, Flags, size, &text_field_type, T, NULL);
3114 // In addition to setting the font category, the call to widget_set_font_cat() also sets
3115 // the number of visible lines, and allocates T->select.lines with the correct number of lines.
3116 widget_set_font_cat(window_id, wid, fcat);
3117 T->select.sm = T->select.em = T->select.sc = T->select.ec = -1;
3118
3119 /* on the first occurance create the editting context menu */
3120 /* maintain a activation entry for each widget so they can be removed or modified */
3121 if (!cm_valid(cm_edit_id))
3122 {
3123 cm_edit_id = cm_create(cm_textedit_menu_str, context_edit_handler);
3124 cm_set_pre_show_handler(cm_edit_id, context_edit_pre_show_handler);
3125 }
3126 /* assign to the new widget */
3127 cm_add_widget(cm_edit_id, window_id, res);
3128
3129 return res;
3130 }
3131
text_field_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,text_message * buf,int buf_size,int x_space,int y_space)3132 int text_field_add (int window_id, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, text_message *buf, int buf_size, int x_space, int y_space)
3133 {
3134 return text_field_add_extended (window_id, widget_id++, OnInit, x, y, lx, ly,
3135 TEXT_FIELD_BORDER, 0, 1.0, buf, buf_size, FILTER_ALL, x_space, y_space);
3136 }
3137
text_field_draw(widget_list * w)3138 int text_field_draw (widget_list *w)
3139 {
3140 text_field *tf;
3141 int cursor;
3142 int mx, my;
3143 int i;
3144
3145 if (w == NULL || w->window_id < 0 || w->widget_info == NULL) {
3146 return 0;
3147 }
3148
3149 mx = mouse_x - windows_list.window[w->window_id].pos_x - w->pos_x;
3150 my = mouse_y - windows_list.window[w->window_id].pos_y - w->pos_y;
3151
3152 tf = w->widget_info;
3153 if (tf->scroll_id != -1)
3154 {
3155 /* We have a scrollbar, so check its position and update
3156 * the text offset if necessary.
3157 */
3158 int pos;
3159
3160 if (tf->update_bar)
3161 {
3162 int nr_lines;
3163 int old_cursor = tf->cursor;
3164 tf->buffer[tf->msg].wrap_width = 0;
3165 nr_lines = rewrap_message (&tf->buffer[tf->msg], w->fcat, w->size,
3166 w->len_x - 2*tf->x_space - tf->scrollbar_width, &tf->cursor);
3167 tf->cursor_line += tf->cursor - old_cursor;
3168 _text_field_set_nr_lines (w, nr_lines);
3169 _text_field_scroll_to_cursor(w);
3170 }
3171 pos = vscrollbar_get_pos (w->window_id, tf->scroll_id);
3172 if (pos > tf->line_offset)
3173 {
3174 int delta = pos - tf->line_offset, i;
3175 for (i = tf->offset; i < tf->buffer->len; i++)
3176 {
3177 if (tf->buffer->data[i] == '\n' || tf->buffer->data[i] == '\r')
3178 if (--delta == 0) break;
3179 }
3180 tf->offset = i+1;
3181 tf->line_offset = pos;
3182 }
3183 else if (pos < tf->line_offset)
3184 {
3185 int delta = tf->line_offset - pos + 1, i;
3186 i = tf->offset;
3187 if (i >= tf->buffer[tf->msg].len)
3188 i = tf->buffer[tf->msg].len - 1;
3189 for (; i > 0; i--)
3190 {
3191 if (tf->buffer->data[i-1] == '\n' || tf->buffer->data[i-1] == '\r')
3192 if (--delta == 0) break;
3193 }
3194 tf->offset = i;
3195 tf->line_offset = pos;
3196 }
3197 }
3198
3199
3200 if (w->Flags & TEXT_FIELD_BORDER)
3201 {
3202 // draw the frame
3203 glDisable (GL_TEXTURE_2D);
3204 if(w->r!=-1.0)
3205 glColor3f (w->r, w->g, w->b);
3206
3207 glBegin (GL_LINE_LOOP);
3208 glVertex3i (w->pos_x, w->pos_y, 0);
3209 glVertex3i (w->pos_x + w->len_x, w->pos_y, 0);
3210 glVertex3i (w->pos_x + w->len_x, w->pos_y + w->len_y, 0);
3211 glVertex3i (w->pos_x, w->pos_y + w->len_y, 0);
3212 glEnd ();
3213
3214 glEnable (GL_TEXTURE_2D);
3215 }
3216
3217 if(mx > 0 && mx < w->len_x - tf->scrollbar_width && my > 0 && my < w->len_y && !(w->Flags&WIDGET_CLICK_TRANSPARENT) && w->Flags&TEXT_FIELD_EDITABLE){
3218 elwin_mouse = CURSOR_TEXT;
3219 }
3220
3221 /* Make the cursor blink if the mouse is in the widget */
3222 if((mx > 0 && mx < w->len_x && my > 0 && my < w->len_y)
3223 && cur_time < tf->next_blink - TF_BLINK_DELAY) {
3224 cursor = -1;
3225 } else if(cur_time < tf->next_blink) {
3226 cursor = tf->cursor-tf->offset;
3227 } else {
3228 tf->next_blink = cur_time + 2*TF_BLINK_DELAY;
3229 cursor = tf->cursor-tf->offset;
3230 }
3231
3232 glEnable(GL_TEXTURE_2D);
3233
3234 for (i = 0; i < tf->nr_visible_lines; i++)
3235 tf->select.lines[i].msg = -1;
3236 draw_messages(w->pos_x + tf->x_space, w->pos_y + tf->y_space,
3237 tf->buffer, tf->buf_size, tf->chan_nr, tf->msg, tf->offset, cursor,
3238 w->len_x - 2*tf->x_space - tf->scrollbar_width, w->len_y - 2 * tf->y_space,
3239 w->fcat, w->size, &tf->select);
3240 if (tf->nr_visible_lines && tf->select.lines[0].msg == -1)
3241 {
3242 tf->select.lines[0].msg = tf->msg;
3243 tf->select.lines[0].chr = tf->buffer[tf->msg].len;
3244 }
3245 for (i = 1; i < tf->nr_visible_lines; i++)
3246 {
3247 if (tf->select.lines[i].msg == -1)
3248 {
3249 tf->select.lines[i].msg = tf->select.lines[i - 1].msg;
3250 tf->select.lines[i].chr = tf->buffer[tf->select.lines[i].msg].len;
3251 }
3252 }
3253
3254 #ifdef OPENGL_TRACE
3255 CHECK_GL_ERRORS();
3256 #endif //OPENGL_TRACE
3257
3258 return 1;
3259 }
3260
text_field_set_buf_pos(int window_id,Uint32 widget_id,int msg,int offset)3261 int text_field_set_buf_pos (int window_id, Uint32 widget_id, int msg, int offset)
3262 {
3263 text_field *tf;
3264 widget_list *w = widget_find (window_id, widget_id);
3265 if (w == NULL) {
3266 return 0;
3267 }
3268
3269 tf = (text_field *) w->widget_info;
3270
3271 if (msg < 0)
3272 {
3273 msg = 0;
3274 }
3275 else if (msg >= tf->buf_size)
3276 {
3277 msg = tf->buf_size - 1;
3278 }
3279
3280 if (offset < 0)
3281 {
3282 offset = 0;
3283 }
3284 else if (offset > tf->buffer[msg].len)
3285 {
3286 offset = tf->buffer[msg].len;
3287 }
3288
3289 tf->msg = msg;
3290 tf->offset = offset;
3291
3292 return 1;
3293 }
3294
text_field_clear(int window_id,Uint32 widget_id)3295 int text_field_clear(int window_id, Uint32 widget_id)
3296 {
3297 widget_list *w = widget_find (window_id, widget_id);
3298 text_field *tf;
3299 text_message *msg;
3300
3301 if (w == NULL)
3302 return 0;
3303
3304 tf = w->widget_info;
3305 if (tf == NULL || !(w->Flags & TEXT_FIELD_EDITABLE))
3306 return 0;
3307
3308 msg = &(tf->buffer[tf->msg]);
3309 clear_text_message_data (msg);
3310
3311 tf->cursor = 0;
3312 tf->cursor_line = 0;
3313 tf->offset = 0;
3314 tf->line_offset = 0;
3315 if (tf->scroll_id != -1)
3316 {
3317 vscrollbar_set_bar_len (window_id, tf->scroll_id, 0);
3318 vscrollbar_set_pos (window_id, tf->scroll_id, 0);
3319 tf->update_bar = 1;
3320 }
3321
3322 return 1;
3323 }
3324
text_field_change_font(widget_list * w,font_cat cat)3325 static int text_field_change_font(widget_list *w, font_cat cat)
3326 {
3327 text_field *tf;
3328 int i, nr_lines = 0, nr_vis;
3329
3330 if (!w || !(tf = w->widget_info))
3331 return 0;
3332
3333 for (i = 0; i < tf->buf_size; i++)
3334 {
3335 int *cursor = i == tf->msg ? &(tf->cursor) : NULL;
3336 tf->buffer[i].wrap_width = 0;
3337 nr_lines += rewrap_message(tf->buffer+i, w->fcat, w->size,
3338 w->len_x - 2*tf->x_space - tf->scrollbar_width, cursor);
3339 }
3340
3341 nr_vis = tf->nr_visible_lines;
3342 _text_field_set_nr_visible_lines(w);
3343 _text_field_set_nr_lines(w, nr_lines);
3344 if (tf->nr_visible_lines != nr_vis)
3345 tf->select.lines = realloc(tf->select.lines, tf->nr_visible_lines * sizeof(text_field_line));
3346
3347 return 1;
3348 }
3349
text_field_paste(widget_list * w,const char * text)3350 static int text_field_paste(widget_list *w, const char* text)
3351 {
3352 text_field *tf;
3353 int bytes = strlen(text);
3354 text_message* msg;
3355 int p;
3356
3357 if (!w || !(tf = w->widget_info))
3358 return 0;
3359
3360 // if not editable, don't allow paste
3361 if (!(w->Flags & TEXT_FIELD_EDITABLE))
3362 return 0;
3363
3364 msg = &tf->buffer[tf->msg];
3365
3366 // if can't grow and would over fill, just use what we can
3367 if ((msg->len + bytes >= msg->size) && !(w->Flags & TEXT_FIELD_CAN_GROW))
3368 bytes = msg->size - msg->len - 1;
3369
3370 resize_text_message_data (msg, msg->len + bytes);
3371
3372 p = tf->cursor;
3373 memmove (&msg->data[p + bytes], &msg->data[p], msg->len - p + 1);
3374 memcpy (&msg->data[p], text, bytes);
3375 msg->len += bytes;
3376 tf->cursor += bytes;
3377 _text_field_find_cursor_line (tf);
3378
3379 return 1;
3380 }
3381
3382 //password entry field. We act like a restricted text entry with multiple modes
3383
pword_update_draw_range_right(password_entry * entry,font_cat cat,float size,int max_width)3384 static void pword_update_draw_range_right(password_entry *entry, font_cat cat,
3385 float size, int max_width)
3386 {
3387 const unsigned char* pw = entry->password;
3388 int len = strlen((const char*)pw);
3389 int str_width;
3390
3391 if (entry->status == P_TEXT)
3392 {
3393 str_width = get_buf_width_zoom(pw + entry->draw_begin,
3394 entry->draw_end - entry->draw_begin, cat, size);
3395 if (str_width > max_width)
3396 {
3397 while (entry->draw_end > entry->draw_begin && str_width > max_width)
3398 str_width -= get_char_width_zoom(pw[--entry->draw_end], cat, size);
3399 }
3400 else
3401 {
3402 while (entry->draw_end < len)
3403 {
3404 int char_width = get_char_width_zoom(pw[entry->draw_end], cat, size);
3405 if (str_width + char_width > max_width)
3406 break;
3407 str_width += char_width;
3408 ++entry->draw_end;
3409 }
3410 }
3411 }
3412 else if (entry->status == P_NORMAL)
3413 {
3414 int sw = get_char_width_zoom('*', cat, size);
3415 str_width = (entry->draw_end - entry->draw_begin) * sw;
3416 if (str_width > max_width)
3417 {
3418 while (entry->draw_end > entry->draw_begin && str_width > max_width)
3419 {
3420 --entry->draw_end;
3421 str_width -= sw;
3422 }
3423 }
3424 else
3425 {
3426 while (entry->draw_end < len && str_width + sw <= max_width)
3427 {
3428 ++entry->draw_end;
3429 str_width += sw;
3430 }
3431 }
3432 }
3433 }
3434
pword_check_after_left_move(password_entry * entry,font_cat cat,float size,int max_width)3435 static void pword_check_after_left_move(password_entry *entry, font_cat cat,
3436 float size, int max_width)
3437 {
3438 if (entry->status == P_NONE || entry->cursor_pos >= entry->draw_begin)
3439 return;
3440
3441 entry->draw_begin = entry->cursor_pos;
3442 pword_update_draw_range_right(entry, cat, size, max_width);
3443 }
3444
pword_update_draw_range_left(password_entry * entry,font_cat cat,float size,int max_width)3445 static void pword_update_draw_range_left(password_entry *entry, font_cat cat,
3446 float size, int max_width)
3447 {
3448 const unsigned char* pw = entry->password;
3449 int len = strlen((const char*)pw);
3450 int str_width;
3451
3452 if (entry->status == P_TEXT)
3453 {
3454 str_width = get_buf_width_zoom(pw + entry->draw_begin,
3455 entry->draw_end - entry->draw_begin, cat, size);
3456 if (entry->cursor_pos == len)
3457 str_width += get_char_width_zoom('_', cat, size);
3458 if (str_width > max_width)
3459 {
3460 while (entry->draw_end > entry->draw_begin && str_width > max_width)
3461 str_width -= get_char_width_zoom(pw[entry->draw_begin++], cat, size);
3462 }
3463 else
3464 {
3465 while (entry->draw_begin > 0)
3466 {
3467 int char_width = get_char_width_zoom(pw[entry->draw_begin-1], cat, size);
3468 if (str_width + char_width > max_width)
3469 break;
3470 str_width += char_width;
3471 --entry->draw_begin;
3472 }
3473 }
3474 }
3475 else if (entry->status == P_NORMAL)
3476 {
3477 int sw = get_char_width_zoom('*', cat, size);
3478 str_width = (entry->sel_end - entry->sel_begin) * sw;
3479 if (entry->cursor_pos == len)
3480 str_width += get_char_width_zoom('_', cat, size);
3481 if (str_width > max_width)
3482 {
3483 while (entry->draw_end > entry->draw_begin && str_width > max_width)
3484 {
3485 ++entry->draw_begin;
3486 str_width -= sw;
3487 }
3488 }
3489 else
3490 {
3491 while (entry->draw_begin > 0 && str_width + sw <= max_width)
3492 {
3493 --entry->draw_begin;
3494 str_width += sw;
3495 }
3496 }
3497 }
3498 }
3499
pword_check_after_right_move(password_entry * entry,font_cat cat,float size,int max_width)3500 static void pword_check_after_right_move(password_entry *entry, font_cat cat,
3501 float size, int max_width)
3502 {
3503 int len;
3504
3505 if (entry->status == P_NONE || entry->cursor_pos < entry->draw_end)
3506 return;
3507
3508 len = strlen((const char*)entry->password);
3509 entry->draw_end = min2i(len, entry->cursor_pos+1);
3510 pword_update_draw_range_left(entry, cat, size, max_width);
3511 }
3512
pword_update_after_delete(password_entry * entry,font_cat cat,float size,int max_width)3513 static void pword_update_after_delete(password_entry *entry, font_cat cat,
3514 float size, int max_width)
3515 {
3516 int len = strlen((const char*)entry->password);
3517 entry->draw_begin = min2i(entry->draw_begin, entry->cursor_pos);
3518 entry->draw_end = min2i(len, entry->cursor_pos+1);
3519
3520 pword_update_draw_range_right(entry, cat, size, max_width);
3521 if (entry->draw_end == len)
3522 pword_update_draw_range_left(entry, cat, size, max_width);
3523 }
3524
pword_update_draw_range_after_insert(password_entry * entry,font_cat cat,float size,int max_width)3525 static void pword_update_draw_range_after_insert(password_entry *entry, font_cat cat,
3526 float size, int max_width)
3527 {
3528 int len = strlen((const char*)entry->password);
3529 if (entry->draw_end > len)
3530 entry->draw_end = len;
3531 pword_update_draw_range_right(entry, cat, size, max_width);
3532 if (entry->cursor_pos >= entry->draw_end)
3533 {
3534 entry->draw_end = min2i(len, entry->cursor_pos+1);
3535 pword_update_draw_range_left(entry, cat, size, max_width);
3536 }
3537 }
3538
pword_get_selected_text(const password_entry * entry)3539 static char* pword_get_selected_text(const password_entry *entry)
3540 {
3541 int size, idst, isrc;
3542 char *res;
3543
3544 if (entry->sel_begin < 0 || entry->sel_end <= entry->sel_begin)
3545 return NULL;
3546
3547 size = entry->sel_end - entry->sel_begin + 1;
3548 res = calloc(size, 1);
3549 if (!res)
3550 return NULL;
3551
3552 for (idst = 0, isrc = entry->sel_begin; isrc < entry->sel_end; ++isrc)
3553 {
3554 unsigned char ch = entry->password[isrc];
3555 if (is_printable(ch) || ch == '\n')
3556 res[idst++] = ch;
3557 }
3558
3559 return res;
3560 }
3561
pword_delete(password_entry * entry,int begin,int end,font_cat cat,float size,int max_width)3562 static void pword_delete(password_entry *entry, int begin, int end, font_cat cat,
3563 float size, int max_width)
3564 {
3565 unsigned char* pw = entry->password;
3566 int len = strlen((const char*)pw);
3567 memmove(pw + begin, pw + end, len - end + 1);
3568 entry->cursor_pos = begin;
3569 pword_update_after_delete(entry, cat, size, max_width);
3570 }
3571
pword_insert(password_entry * entry,int pos,const unsigned char * text,int len,font_cat cat,float size,int max_width)3572 static void pword_insert(password_entry *entry, int pos, const unsigned char* text, int len,
3573 font_cat cat, float size, int max_width)
3574 {
3575 unsigned char* pw = entry->password;
3576 if (pos + len >= entry->max_chars)
3577 {
3578 memcpy(pw + pos, text, entry->max_chars - pos);
3579 }
3580 else
3581 {
3582 memmove(pw + pos + len, pw + pos, entry->max_chars - pos - len);
3583 memcpy(pw + pos, text, len);
3584 }
3585 pw[entry->max_chars-1] = '\0';
3586 entry->cursor_pos = min2i(pos + len, entry->max_chars);
3587 pword_update_draw_range_after_insert(entry, cat, size, max_width);
3588 }
3589
pword_field_keypress(widget_list * w,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)3590 static int pword_field_keypress(widget_list *w, int mx, int my, SDL_Keycode key_code,
3591 Uint32 key_unicode, Uint16 key_mod)
3592 {
3593 unsigned char ch = key_to_char(key_unicode);
3594 password_entry *entry;
3595 int shift_on = key_mod & KMOD_SHIFT,
3596 alt_on = key_mod & KMOD_ALT,
3597 ctrl_on = key_mod & KMOD_CTRL;
3598 unsigned char* pw;
3599 int len, cpos;
3600 int max_width;
3601
3602 if (!w || !(entry = w->widget_info))
3603 return 0;
3604
3605 if (entry->status == P_NONE)
3606 return -1;
3607
3608 if (w->Flags & PWORD_FIELD_NO_KEYPRESS)
3609 return 0;
3610
3611 max_width = (int)(0.5 + w->len_x - 4*w->size);
3612 pw = entry->password;
3613 len = strlen((const char*)pw);
3614 cpos = entry->cursor_pos;
3615
3616 if (entry->status == P_TEXT)
3617 {
3618 // Selection support only for regular text input
3619 if (!alt_on && !ctrl_on && shift_on)
3620 {
3621 switch (key_code)
3622 {
3623 case SDLK_LEFT:
3624 if (cpos > 0)
3625 {
3626 if (entry->sel_begin < 0)
3627 {
3628 entry->sel_end = cpos;
3629 entry->sel_begin = cpos - 1;
3630 }
3631 else if (cpos == entry->sel_begin)
3632 {
3633 --entry->sel_begin;
3634 }
3635 else if (cpos == entry->sel_end)
3636 {
3637 if (--entry->sel_end < entry->sel_begin)
3638 SWAP(entry->sel_begin, entry->sel_end);
3639 }
3640 --entry->cursor_pos;
3641 pword_check_after_left_move(entry, w->fcat, w->size, max_width);
3642 }
3643 return 1;
3644 case SDLK_RIGHT:
3645 if (cpos < len)
3646 {
3647 if (entry->sel_begin < 0)
3648 {
3649 entry->sel_end = cpos + 1;
3650 entry->sel_begin = cpos;
3651 }
3652 else if (cpos == entry->sel_begin)
3653 {
3654 if (++entry->sel_begin > entry->sel_end)
3655 SWAP(entry->sel_begin, entry->sel_end);
3656 }
3657 else if (cpos == entry->sel_end)
3658 {
3659 ++entry->sel_end;
3660 }
3661 ++entry->cursor_pos;
3662 pword_check_after_right_move(entry, w->fcat, w->size, max_width);
3663 }
3664 return 1;
3665 case SDLK_HOME:
3666 if (entry->sel_begin < 0)
3667 {
3668 entry->sel_end = cpos;
3669 }
3670 else if (cpos == entry->sel_end)
3671 {
3672 entry->sel_end = entry->sel_begin;
3673 }
3674 entry->sel_begin = 0;
3675 entry->cursor_pos = 0;
3676 pword_check_after_left_move(entry, w->fcat, w->size, max_width);
3677 return 1;
3678 case SDLK_END:
3679 if (entry->sel_begin < 0)
3680 {
3681 entry->sel_begin = cpos;
3682 }
3683 else if (cpos == entry->sel_begin)
3684 {
3685 entry->sel_begin = entry->sel_end;
3686 }
3687 entry->sel_end = len;
3688 entry->cursor_pos = len;
3689 pword_check_after_right_move(entry, w->fcat, w->size, max_width);
3690 return 1;
3691 }
3692 }
3693
3694 // Allow paste into password, but copy or cut only for regular text
3695 if (KEY_DEF_CMP(K_COPY, key_code, key_mod) || KEY_DEF_CMP(K_COPY_ALT, key_code, key_mod))
3696 {
3697 if (entry->sel_begin >= 0 && entry->sel_end > entry->sel_begin)
3698 {
3699 char* sel_text = pword_get_selected_text(entry);
3700 if (sel_text)
3701 {
3702 copy_to_clipboard(sel_text);
3703 free(sel_text);
3704 }
3705 }
3706 return 1;
3707 }
3708
3709 if (KEY_DEF_CMP(K_CUT, key_code, key_mod))
3710 {
3711 char* sel_text = pword_get_selected_text(entry);
3712 if (sel_text)
3713 {
3714 copy_to_clipboard(sel_text);
3715 free(sel_text);
3716
3717 pword_delete(entry, entry->sel_begin, entry->sel_end, w->fcat,
3718 w->size, max_width);
3719 }
3720 entry->sel_begin = entry->sel_end = -1;
3721 return 1;
3722 }
3723 }
3724
3725 // Allow paste into password, but copy or cut only for regular text
3726 if (KEY_DEF_CMP(K_PASTE, key_code, key_mod) || KEY_DEF_CMP(K_PASTE_ALT, key_code, key_mod))
3727 {
3728 if (entry->sel_begin >= 0 && entry->sel_end > entry->sel_begin)
3729 {
3730 pword_delete(entry, entry->sel_begin, entry->sel_end, w->fcat,
3731 w->size, max_width);
3732 entry->sel_begin = entry->sel_end = -1;
3733 }
3734 start_paste(w);
3735 return 1;
3736 }
3737
3738 if (!alt_on && !ctrl_on)
3739 {
3740 switch (key_code)
3741 {
3742 case SDLK_LEFT:
3743 if (cpos > 0)
3744 {
3745 --entry->cursor_pos;
3746 pword_check_after_left_move(entry, w->fcat, w->size, max_width);
3747 }
3748 break;
3749 case SDLK_RIGHT:
3750 if (cpos < len)
3751 {
3752 ++entry->cursor_pos;
3753 pword_check_after_right_move(entry, w->fcat, w->size, max_width);
3754 }
3755 break;
3756 case SDLK_HOME:
3757 entry->cursor_pos = 0;
3758 pword_check_after_left_move(entry, w->fcat, w->size, max_width);
3759 break;
3760 case SDLK_END:
3761 entry->cursor_pos = len;
3762 pword_check_after_right_move(entry, w->fcat, w->size, max_width);
3763 break;
3764 case SDLK_DELETE:
3765 if (entry->sel_begin >= 0 && entry->sel_end > entry->sel_begin)
3766 {
3767 pword_delete(entry, entry->sel_begin, entry->sel_end, w->fcat,
3768 w->size, max_width);
3769 }
3770 else if (cpos < len)
3771 {
3772 pword_delete(entry, cpos, cpos+1, w->fcat, w->size, max_width);
3773 }
3774 break;
3775 case SDLK_BACKSPACE:
3776
3777 if (entry->sel_begin >= 0 && entry->sel_end > entry->sel_begin)
3778 {
3779 pword_delete(entry, entry->sel_begin, entry->sel_end, w->fcat,
3780 w->size, max_width);
3781 }
3782 else if (cpos > 0)
3783 {
3784 pword_delete(entry, cpos-1, cpos, w->fcat, w->size, max_width);
3785 }
3786 break;
3787 default:
3788 if (is_printable(ch))
3789 {
3790 if (entry->sel_begin >= 0 && entry->sel_end > entry->sel_begin)
3791 {
3792 pword_delete(entry, entry->sel_begin, entry->sel_end,
3793 w->fcat, w->size, max_width);
3794 len = strlen((const char*)pw);
3795 }
3796 if (len+1 < entry->max_chars)
3797 pword_insert(entry, entry->cursor_pos, &ch, 1, w->fcat, w->size, max_width);
3798 }
3799 else
3800 {
3801 // Probably key-down event, which does not send unicode. Ignore.
3802 return 0;
3803 }
3804 break;
3805 }
3806
3807 entry->sel_begin = entry->sel_end = -1;
3808 return 1;
3809 }
3810
3811 // No idea how to handle this input
3812 return 0;
3813 }
3814
pword_pos_under_mouse(password_entry * entry,int mx,int space,font_cat cat,float size)3815 static int pword_pos_under_mouse(password_entry* entry, int mx, int space,
3816 font_cat cat, float size)
3817 {
3818 const unsigned char* pw = entry->password;
3819 int i, cw, str_width;
3820
3821 switch (entry->status)
3822 {
3823 case P_NONE:
3824 return -1;
3825 case P_TEXT:
3826 if (mx < space)
3827 // Avoid click before the first character setting the cursor position to -1
3828 return entry->draw_begin;
3829 for (i = entry->draw_begin, str_width = space; pw[i] && str_width <= mx; ++i)
3830 str_width += get_char_width_zoom(pw[i], cat, size);
3831 return (str_width <= mx) ? i : i-1;
3832 case P_NORMAL:
3833 default:
3834 if (mx < space)
3835 // Avoid click before the first character setting the cursor position to -1
3836 return entry->draw_begin;
3837 cw = get_char_width_zoom('*', cat, size);
3838 return min2i(entry->draw_begin + (mx - space + cw - 1) / cw,
3839 strlen((const char*)pw));
3840 }
3841 }
3842
pword_field_click(widget_list * w,int mx,int my,Uint32 flags)3843 static int pword_field_click(widget_list *w, int mx, int my, Uint32 flags)
3844 {
3845 password_entry *entry;
3846 int space;
3847
3848 if (!w || !(entry = w->widget_info))
3849 return 0;
3850
3851 space = (int)(0.5 + 2*w->size);
3852 entry->cursor_pos = pword_pos_under_mouse(entry, mx, space, w->fcat, w->size);
3853 entry->sel_begin = entry->sel_end = -1;
3854
3855 return 1;
3856 }
3857
pword_field_drag(widget_list * w,int mx,int my,Uint32 flags,int dx,int dy)3858 static int pword_field_drag(widget_list *w, int mx, int my, Uint32 flags, int dx, int dy)
3859 {
3860 password_entry *entry;
3861 int space, pos, len;
3862
3863 if (!w || !(entry = w->widget_info) || entry->status != P_TEXT)
3864 return 0;
3865
3866 space = (int)(0.5 + 2*w->size);
3867 len = strlen((const char*)entry->password);
3868 pos = pword_pos_under_mouse(entry, mx, space, w->fcat, w->size);
3869 if (entry->sel_begin < 0 && pos < len)
3870 {
3871 entry->sel_begin = entry->drag_begin = max2i(0, pos);
3872 entry->sel_end = pos + 1;
3873 }
3874 else if (pos <= entry->drag_begin)
3875 {
3876 entry->sel_begin = max2i(0, pos);
3877 entry->sel_end = min2i(len, entry->drag_begin + 1);
3878 }
3879 else
3880 {
3881 entry->sel_begin = entry->drag_begin + 1;
3882 entry->sel_end = min2i(len, pos + 1);
3883 }
3884
3885 return 1;
3886 }
3887
pword_field_mouseover(widget_list * w,int mx,int my)3888 static int pword_field_mouseover(widget_list *w, int mx, int my)
3889 {
3890 password_entry *entry;
3891 if (!w || !(entry = (password_entry*)w->widget_info))
3892 return 0;
3893
3894 entry->mouseover = 1;
3895 return 1;
3896 }
3897
pword_field_draw(widget_list * w)3898 static int pword_field_draw(widget_list *w)
3899 {
3900 password_entry *entry;
3901 unsigned char* start;
3902 size_t len;
3903 int max_width, x_left, x_cursor;
3904 int space = (int)(0.5 + 2*w->size);
3905 int sel_begin, sel_end;
3906 ver_alignment valign;
3907 int draw_cursor, draw_shadow;
3908
3909 if (!w || !(entry = (password_entry*)w->widget_info))
3910 return 0;
3911
3912 switch (entry->status)
3913 {
3914 case P_NONE:
3915 start = (unsigned char*)"N/A";
3916 len = 3;
3917 break;
3918 case P_TEXT:
3919 start = entry->password + entry->draw_begin;
3920 len = entry->draw_end - entry->draw_begin;
3921 break;
3922 case P_NORMAL:
3923 default:
3924 {
3925 len = entry->draw_end - entry->draw_begin;
3926 start = malloc(len);
3927 memset(start, '*', len);
3928 }
3929 }
3930
3931 if (!(w->Flags & PWORD_FIELD_NO_BORDER))
3932 {
3933 // draw the frame
3934 glDisable (GL_TEXTURE_2D);
3935 glColor3f (w->r, w->g, w->b);
3936 glBegin (GL_LINE_LOOP);
3937 glVertex3i (w->pos_x, w->pos_y, 0);
3938 glVertex3i (w->pos_x + w->len_x, w->pos_y, 0);
3939 glVertex3i (w->pos_x + w->len_x, w->pos_y + w->len_y, 0);
3940 glVertex3i (w->pos_x, w->pos_y + w->len_y, 0);
3941 if (entry->draw_begin > 0)
3942 glVertex3i ((int)(w->pos_x + 3*w->size + 0.5), w->pos_y + w->len_y/2, 0);
3943 glEnd ();
3944 glEnable (GL_TEXTURE_2D);
3945 }
3946
3947 x_left = (int)(w->pos_x + 2*w->size + 0.5);
3948 x_cursor = x_left + get_buf_width_zoom(start, entry->cursor_pos - entry->draw_begin,
3949 w->fcat, w->size);
3950 max_width = w->len_x - 2*space;
3951
3952 sel_begin = max2i(entry->sel_begin - entry->draw_begin, 0);
3953 sel_end = max2i(entry->sel_end - entry->draw_begin, 0);
3954 valign = entry->status == P_NORMAL ? CENTER_PASSWORD : CENTER_LINE;
3955 draw_shadow = (entry->shadow_r >= 0.0f);
3956 draw_text(x_left, w->pos_y + w->len_y/2, start, len, w->fcat, TDO_MAX_WIDTH, max_width,
3957 TDO_SHADOW, draw_shadow, TDO_FOREGROUND, w->r, w->g, w->b,
3958 TDO_BACKGROUND, entry->shadow_r, entry->shadow_g, entry->shadow_b,
3959 TDO_ZOOM, w->size, TDO_SEL_BEGIN, sel_begin, TDO_SEL_END, sel_end, TDO_VERTICAL_ALIGNMENT, valign,
3960 TDO_END);
3961 draw_cursor = !(w->Flags & PWORD_FIELD_NO_CURSOR)
3962 && (entry->mouseover || (w->Flags & PWORD_FIELD_DRAW_CURSOR));
3963 if (draw_cursor && cur_time % (2*TF_BLINK_DELAY) < TF_BLINK_DELAY)
3964 {
3965 draw_text(x_cursor, w->pos_y + w->len_y/2, (const unsigned char*)"_", 1, w->fcat,
3966 TDO_SHADOW, draw_shadow, TDO_FOREGROUND, w->r, w->g, w->b,
3967 TDO_BACKGROUND, entry->shadow_r, entry->shadow_g, entry->shadow_b,
3968 TDO_ZOOM, w->size, TDO_VERTICAL_ALIGNMENT, CENTER_LINE, TDO_END);
3969 }
3970
3971 if (entry->status == P_NORMAL)
3972 free(start);
3973
3974 entry->mouseover = 0;
3975 #ifdef OPENGL_TRACE
3976 CHECK_GL_ERRORS();
3977 #endif //OPENGL_TRACE
3978 return 1;
3979 }
3980
pword_field_paste(widget_list * w,const char * text)3981 static int pword_field_paste(widget_list *w, const char* text)
3982 {
3983 int max_width, space;
3984 password_entry *entry;
3985 if (!w || !(entry = w->widget_info))
3986 return 0;
3987 space = (int)(0.5 + 2*w->size);
3988 max_width = w->len_x - 2*space;
3989 pword_insert(entry, entry->cursor_pos, (const unsigned char*)text, strlen(text),
3990 w->fcat, w->size, max_width);
3991 return 1;
3992 }
3993
pword_set_status(widget_list * w,Uint8 status)3994 void pword_set_status(widget_list *w, Uint8 status)
3995 {
3996 password_entry *pword;
3997 if (w && (pword = w->widget_info))
3998 pword->status = status;
3999 }
4000
pword_clear(int window_id,Uint32 widget_id)4001 int pword_clear(int window_id, Uint32 widget_id)
4002 {
4003 widget_list *w = widget_find (window_id, widget_id);
4004 password_entry *entry;
4005 if (!w || !(entry = w->widget_info))
4006 return 0;
4007 if (entry->max_chars)
4008 entry->password[0] = '\0';
4009 entry->cursor_pos = 0;
4010 entry->drag_begin = 0;
4011 entry->draw_end = 0;
4012 entry->sel_begin = 0;
4013 entry->sel_end = 0;
4014 entry->drag_begin = 0;
4015 entry->mouseover = 0;
4016 return 1;
4017 }
4018
pword_field_set_content(int window_id,Uint32 widget_id,const unsigned char * buf,size_t len)4019 int pword_field_set_content(int window_id, Uint32 widget_id, const unsigned char* buf, size_t len)
4020 {
4021 widget_list *w = widget_find(window_id, widget_id);
4022 password_entry *entry;
4023 int str_width = 0, space, max_width;
4024
4025 if (!w || !(entry = w->widget_info))
4026 return 0;
4027
4028 space = (int)(0.5 + 2*w->size);
4029 max_width = w->len_x - 2*space;
4030
4031 safe_strncpy2((char*)entry->password, (const char*)buf, entry->max_chars, len);
4032 entry->sel_begin = entry->sel_end = -1;
4033 entry->cursor_pos = entry->draw_begin = entry->draw_end = 0;
4034 while (entry->password[entry->draw_end])
4035 {
4036 int chr_width = get_char_width_zoom(entry->password[entry->draw_end], w->fcat, w->size);
4037 if (str_width + chr_width > max_width)
4038 break;
4039 str_width += chr_width;
4040 ++entry->draw_end;
4041 }
4042
4043 return 1;
4044 }
4045
pword_field_set_shadow_color(int window_id,Uint32 widget_id,float r,float g,float b)4046 int pword_field_set_shadow_color(int window_id, Uint32 widget_id, float r, float g, float b)
4047 {
4048 widget_list *w = widget_find(window_id, widget_id);
4049 password_entry *entry;
4050
4051 if (!w || !(entry = w->widget_info))
4052 return 0;
4053
4054 entry->shadow_r = r;
4055 entry->shadow_g = g;
4056 entry->shadow_b = b;
4057 return 1;
4058 }
4059
pword_field_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint8 status,float size,unsigned char * buffer,int buffer_size)4060 int pword_field_add_extended (int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint8 status, float size, unsigned char *buffer, int buffer_size)
4061 {
4062 int space = (int)(0.5 + 2*size), max_width = lx - 2*space;
4063 int widget_id;
4064 int str_width = 0;
4065 widget_list *widget;
4066 password_entry *T = calloc(1, sizeof (password_entry));
4067
4068 T->status = status;
4069 T->password = buffer;
4070 T->max_chars = buffer_size;
4071 T->sel_begin = T->sel_end = -1;
4072 T->shadow_r = T->shadow_g = T->shadow_b = -1.0f;
4073
4074 if (ly != 0)
4075 {
4076 T->fixed_height = ly;
4077 }
4078 else
4079 {
4080 window_info *win = &windows_list.window[window_id];
4081 ly = get_line_height(win->font_category, size) + 2*space;
4082 }
4083
4084 widget_id = widget_add (window_id, wid, OnInit, x, y, lx, ly, 0, size, &pword_field_type, T, NULL);
4085
4086 widget = widget_find(window_id, widget_id);
4087 T->draw_end = 0;
4088 while (T->password[T->draw_end])
4089 {
4090 int chr_width = get_char_width_zoom(T->password[T->draw_end], widget->fcat, size);
4091 if (str_width + chr_width > max_width)
4092 break;
4093 str_width += chr_width;
4094 ++T->draw_end;
4095 }
4096
4097 return widget_id;
4098 }
4099
pword_field_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint8 status,unsigned char * buffer,int buffer_size)4100 int pword_field_add (int window_id, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint8 status, unsigned char *buffer, int buffer_size)
4101 {
4102 return pword_field_add_extended (window_id, widget_id++, OnInit, x, y, lx, ly, status, 1.0, buffer, buffer_size);
4103 }
4104
4105 // Multiselect
free_multiselect(widget_list * widget)4106 static int free_multiselect(widget_list *widget)
4107 {
4108 if(widget != NULL) {
4109 multiselect *collection = widget->widget_info;
4110 free(collection->buttons);
4111 free(collection);
4112 }
4113 return 1;
4114 }
4115
multiselect_get_selected(int window_id,Uint32 widget_id)4116 int multiselect_get_selected(int window_id, Uint32 widget_id)
4117 {
4118 widget_list *widget = widget_find(window_id, widget_id);
4119 multiselect *M = widget->widget_info;
4120 if(M == NULL) {
4121 return -1;
4122 } else {
4123 if (M->selected_button < M->nr_buttons)
4124 return M->buttons[M->selected_button].value;
4125 else
4126 return -1;
4127 }
4128 }
4129
_multiselect_selected_is_visible(int window_id,Uint32 widget_id)4130 static int _multiselect_selected_is_visible(int window_id, Uint32 widget_id)
4131 {
4132 widget_list *widget = widget_find(window_id, widget_id);
4133 multiselect *M;
4134 int i, but_start, start_y;
4135
4136 if (!widget || !(M = widget->widget_info))
4137 return 0;
4138
4139 if (M->scrollbar == -1)
4140 // No scrollbar, even if the options is not visible there is nothing we can do about it
4141 return 1;
4142
4143 but_start = vscrollbar_get_pos(M->win_id, M->scrollbar);
4144 if (M->selected_button < but_start)
4145 return 0;
4146 if (M->selected_button == but_start)
4147 return 1;
4148
4149 start_y = M->buttons[but_start].y;
4150 for (i = but_start+1; i < M->nr_buttons; i++)
4151 {
4152 int button_y = M->buttons[i].y - start_y;
4153 if(button_y + M->buttons[i].height > widget->len_y)
4154 // Button won't be shown, and hence neither will the selected button
4155 return 0;
4156 if (M->selected_button == i)
4157 return 1;
4158 }
4159
4160 return 0;
4161 }
4162
multiselect_set_selected(int window_id,Uint32 widget_id,int button_id)4163 int multiselect_set_selected(int window_id, Uint32 widget_id, int button_id)
4164 {
4165 widget_list *widget = widget_find(window_id, widget_id);
4166 multiselect *M = NULL;
4167 if (widget == NULL)
4168 return -1;
4169 M = widget->widget_info;
4170 if(M == NULL) {
4171 return -1;
4172 } else {
4173 int i;
4174 for (i=0; i<M->nr_buttons; i++)
4175 {
4176 if (button_id == M->buttons[i].value)
4177 {
4178 M->selected_button = i;
4179 if (M->scrollbar != -1 && !_multiselect_selected_is_visible(window_id, widget_id))
4180 vscrollbar_set_pos(M->win_id, M->scrollbar, i);
4181
4182 return button_id;
4183 }
4184 }
4185 return -1;
4186 }
4187 }
4188
multiselect_get_scrollbar_pos(int window_id,Uint32 widget_id)4189 int multiselect_get_scrollbar_pos(int window_id, Uint32 widget_id)
4190 {
4191 widget_list *widget = widget_find(window_id, widget_id);
4192 multiselect *M = widget->widget_info;
4193 if ((M == NULL) || (M->scrollbar == -1))
4194 return -1;
4195 return vscrollbar_get_pos(M->win_id, M->scrollbar);
4196 }
4197
multiselect_set_scrollbar_pos(int window_id,Uint32 widget_id,int pos)4198 int multiselect_set_scrollbar_pos(int window_id, Uint32 widget_id, int pos)
4199 {
4200 widget_list *widget = widget_find(window_id, widget_id);
4201 multiselect *M = widget->widget_info;
4202 if ((M == NULL) || (M->scrollbar == -1))
4203 return 0;
4204 return vscrollbar_set_pos(M->win_id, M->scrollbar, pos);
4205 }
4206
multiselect_get_height(int window_id,Uint32 widget_id)4207 int multiselect_get_height(int window_id, Uint32 widget_id)
4208 {
4209 widget_list *widget = widget_find(window_id, widget_id);
4210 return widget->len_y;
4211 }
4212
multiselect_click(widget_list * widget,int mx,int my,Uint32 flags)4213 static int multiselect_click(widget_list *widget, int mx, int my, Uint32 flags)
4214 {
4215 multiselect *M = widget->widget_info;
4216 int i;
4217 Uint16 button_y;
4218 int top_but = 0;
4219 int start_y = 0;
4220
4221 if(M->scrollbar != -1)
4222 {
4223 vscrollbar *scrollbar = widget_find(M->win_id, M->scrollbar)->widget_info;
4224 if(flags&ELW_WHEEL_DOWN)
4225 vscrollbar_set_pos(M->win_id, M->scrollbar, scrollbar->pos+scrollbar->pos_inc);
4226 else if (flags&ELW_WHEEL_UP)
4227 vscrollbar_set_pos(M->win_id, M->scrollbar, scrollbar->pos-scrollbar->pos_inc);
4228 top_but = vscrollbar_get_pos(M->win_id, M->scrollbar);
4229 start_y = M->buttons[top_but].y;
4230 }
4231
4232 for(i = top_but; i < M->nr_buttons; i++)
4233 {
4234 button_y = M->buttons[i].y - start_y;
4235 if (button_y > widget->len_y - M->buttons[i].height)
4236 break;
4237 if((flags&ELW_LEFT_MOUSE || flags&ELW_RIGHT_MOUSE) &&
4238 my > button_y && my < button_y + M->buttons[i].height && mx > M->buttons[i].x && mx < M->buttons[i].x+M->buttons[i].width) {
4239 M->selected_button = i;
4240 do_click_sound();
4241 return 1;
4242 }
4243 }
4244 return 0;
4245 }
4246
multiselect_draw(widget_list * widget)4247 static int multiselect_draw(widget_list *widget)
4248 {
4249 if (widget == NULL) {
4250 return 0;
4251 } else {
4252 int i;
4253 float r, g, b;
4254 float hr, hg, hb;
4255 Uint16 button_y;
4256 multiselect *M = widget->widget_info;
4257 int top_but = (M->scrollbar != -1) ?vscrollbar_get_pos(M->win_id, M->scrollbar) :0;
4258 int start_y = M->buttons[top_but].y;
4259
4260 r = widget->r != -1 ? widget->r : gui_color[0];
4261 g = widget->g != -1 ? widget->g : gui_color[1];
4262 b = widget->b != -1 ? widget->b : gui_color[2];
4263
4264 hr = M->highlighted_red != -1 ? M->highlighted_red : gui_invert_color[0];
4265 hg = M->highlighted_green != -1 ? M->highlighted_green : gui_invert_color[1];
4266 hb = M->highlighted_blue != -1 ? M->highlighted_blue : gui_invert_color[2];
4267
4268 for(i = top_but; i < M->nr_buttons; i++) {
4269 button_y = M->buttons[i].y - start_y;
4270 /* Check if the button can be fully drawn */
4271 if(button_y > widget->len_y - M->buttons[i].height)
4272 break;
4273 draw_smooth_button(M->buttons[i].text, widget->fcat, widget->size, widget->pos_x+M->buttons[i].x, widget->pos_y+button_y, M->buttons[i].width-2*BUTTONRADIUS*widget->size, 1, r, g, b, (i == M->selected_button), hr, hg, hb, 0.5f);
4274 }
4275 }
4276 return 1;
4277 }
4278
multiselect_set_color(widget_list * widget,float r,float g,float b)4279 static int multiselect_set_color(widget_list *widget, float r, float g, float b)
4280 {
4281 multiselect *M = widget->widget_info;
4282 if (!M)
4283 return 0;
4284
4285 if (M->scrollbar != -1)
4286 widget_set_color(M->win_id, M->scrollbar, r, g, b);
4287 return 1;
4288 }
4289
multiselect_button_add(int window_id,Uint32 multiselect_id,Uint16 x,Uint16 y,const char * text,const char selected)4290 int multiselect_button_add(int window_id, Uint32 multiselect_id, Uint16 x, Uint16 y, const char *text, const char selected)
4291 {
4292 return multiselect_button_add_extended(window_id, multiselect_id, x, y, 0, text, DEFAULT_SMALL_RATIO, selected);
4293 }
4294
multiselect_button_add_extended(int window_id,Uint32 multiselect_id,Uint16 x,Uint16 y,int width,const char * text,float size,const char selected)4295 int multiselect_button_add_extended(int window_id, Uint32 multiselect_id, Uint16 x, Uint16 y, int width, const char *text, float size, const char selected)
4296 {
4297 widget_list *widget = widget_find(window_id, multiselect_id);
4298 multiselect *M = widget->widget_info;
4299 int current_button = M->nr_buttons;
4300 int button_height = (int)(0.5 + 2.0 * BUTTONRADIUS * size);
4301
4302 if (text==NULL || strlen(text)==0) {
4303 M->next_value++;
4304 return current_button;
4305 }
4306
4307 if(y+button_height > widget->len_y && (!M->max_height || widget->len_y != M->max_height)) {
4308 widget->len_y = y+button_height;
4309 }
4310
4311 widget->size=size;
4312
4313 if (M->max_height && y+button_height > M->actual_height) {
4314 M->actual_height = y+button_height;
4315 }
4316 if(M->max_buttons == M->nr_buttons) {
4317 /*Allocate space for more buttons*/
4318 M->buttons = realloc(M->buttons, sizeof(*M->buttons) * M->max_buttons * 2);
4319 M->max_buttons *= 2;
4320 }
4321 safe_strncpy((char*)M->buttons[current_button].text, text, sizeof(M->buttons[current_button].text));
4322 if(selected) {
4323 M->selected_button = current_button;
4324 }
4325 M->buttons[current_button].value = M->next_value++;
4326 M->buttons[current_button].x = x;
4327 M->buttons[current_button].y = y;
4328 M->buttons[current_button].width = (width == 0) ? widget->len_x : width;
4329 M->buttons[current_button].height = button_height;
4330
4331 M->nr_buttons++;
4332 if(M->max_height && M->scrollbar == -1 && M->max_height < y) {
4333 int i;
4334
4335 /* Add scrollbar */
4336 M->scrollbar = vscrollbar_add_extended(window_id, widget_id++, NULL, widget->pos_x+widget->len_x-M->scrollbar_width,
4337 widget->pos_y, M->scrollbar_width, M->max_height, 0, 1.0, 0, 1, M->max_height);
4338 widget_set_color(window_id, M->scrollbar, widget->r, widget->g, widget->b);
4339 widget->len_x -= M->scrollbar_width + 2;
4340 widget->len_y = M->max_height;
4341 /* We don't want things to look ugly. */
4342 for(i = 0; i < M->nr_buttons; i++) {
4343 if(M->buttons[i].width > widget->len_x) {
4344 M->buttons[i].width -= M->scrollbar_width + 2;
4345 }
4346 }
4347 }
4348
4349 if (M->scrollbar != -1)
4350 vscrollbar_set_bar_len(window_id, M->scrollbar, M->nr_buttons-widget->len_y/button_height);
4351
4352 return current_button;
4353 }
4354
multiselect_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,int width,Uint16 max_height,float size,float r,float g,float b,float hr,float hg,float hb,int max_buttons)4355 int multiselect_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, int width, Uint16 max_height, float size, float r, float g, float b, float hr, float hg, float hb, int max_buttons)
4356 {
4357 multiselect *T = calloc (1, sizeof (multiselect));
4358 //Save info
4359 T->max_buttons = max_buttons > 0 ? max_buttons : 2;
4360 T->selected_button = 0;
4361 T->next_value = 0;
4362 T->nr_buttons = 0;
4363 T->buttons = malloc(sizeof(*T->buttons) * T->max_buttons);
4364 T->max_height = max_height;
4365 T->scrollbar = -1;
4366 T->scrollbar_width = ELW_BOX_SIZE * size;
4367 T->win_id = window_id;
4368 T->highlighted_red = hr;
4369 T->highlighted_green = hg;
4370 T->highlighted_blue = hb;
4371
4372 return widget_add (window_id, wid, OnInit, x, y, width, 0, 0, size, &multiselect_type, T, NULL);
4373 }
4374
multiselect_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,int width)4375 int multiselect_add(int window_id, int (*OnInit)(), Uint16 x, Uint16 y, int width)
4376 {
4377 return multiselect_add_extended(window_id, widget_id++, OnInit, x, y, width, 0, 1.0f, -1, -1, -1, -1, -1, -1, 0);
4378 }
4379
multiselect_clear(int window_id,Uint32 widget_id)4380 int multiselect_clear(int window_id, Uint32 widget_id)
4381 {
4382 widget_list *widget;
4383 multiselect *M;
4384
4385 widget = widget_find(window_id, widget_id);
4386 if (!widget || !(M = widget->widget_info))
4387 return 0;
4388
4389 M->nr_buttons = 0;
4390 M->next_value = 0;
4391 if (M->scrollbar != -1)
4392 {
4393 widget_destroy(window_id, M->scrollbar);
4394 M->scrollbar = -1;
4395 widget->len_x += M->scrollbar_width + 2;
4396 }
4397
4398 return 1;
4399 }
4400
4401 // Spinbutton
spinbutton_keypress(widget_list * widget,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)4402 static int spinbutton_keypress(widget_list *widget, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
4403 {
4404 spinbutton *button;
4405 if(widget != NULL && (button = widget->widget_info) != NULL && !(key_mod & KMOD_ALT) && !(key_mod & KMOD_CTRL)) {
4406 int len = strlen(button->input_buffer);
4407 char ch = key_to_char(key_unicode);
4408
4409 switch(button->type) {
4410 case SPIN_INT:
4411 {
4412 int value;
4413
4414 if (ch >= '0' && ch <= '9')
4415 {
4416 if (len+1 < sizeof(button->input_buffer))
4417 {
4418 button->input_buffer[len] = ch;
4419 button->input_buffer[len+1] = '\0';
4420 }
4421 }
4422 else if (key_code == SDLK_BACKSPACE)
4423 {
4424 if (len > 0)
4425 button->input_buffer[len-1] = '\0';
4426 }
4427 else
4428 {
4429 return 0;
4430 }
4431
4432 value = atoi(button->input_buffer);
4433 if (value > button->max)
4434 {
4435 safe_snprintf(button->input_buffer, sizeof(button->input_buffer), "%d",
4436 (int)button->max);
4437 *(int *)button->data = button->max;
4438 }
4439 else if (value >= button->min)
4440 {
4441 *(int *)button->data = value;
4442 }
4443
4444 return 1;
4445 }
4446 case SPIN_FLOAT:
4447 {
4448 float value;
4449
4450 if (ch == ',')
4451 ch = '.';
4452 if ((ch >= '0' && ch <= '9') || (ch == '.' && strchr(button->input_buffer, '.') == NULL))
4453 {
4454 /* Make sure we don't insert illegal characters here */
4455 if (button->input_buffer[0] != '0' || ch == '.' || strchr(button->input_buffer, '.') != NULL)
4456 {
4457 /* Append to the end */
4458 if (len+1 < sizeof(button->input_buffer))
4459 {
4460 button->input_buffer[len] = ch;
4461 button->input_buffer[len+1] = '\0';
4462 }
4463 }
4464 }
4465 else if (key_code == SDLK_BACKSPACE)
4466 {
4467 if (len > 0)
4468 button->input_buffer[len-1] = '\0';
4469 }
4470 else
4471 {
4472 return 0;
4473 }
4474
4475 value = atof(button->input_buffer);
4476 if (value > button->max)
4477 {
4478 safe_snprintf(button->input_buffer, sizeof(button->input_buffer), "%.2f",
4479 button->max);
4480 *(float*)button->data = button->max;
4481 }
4482 else if (value >= button->min)
4483 {
4484 *(float*)button->data = value;
4485 }
4486 return 1;
4487 }
4488 }
4489 }
4490 return 0;
4491 }
4492
4493 // Note: Discards dx and dy when used for drag. Must be altered if the drag
4494 // handler changes.
spinbutton_click(widget_list * widget,int mx,int my,Uint32 flags)4495 static int spinbutton_click(widget_list *widget, int mx, int my, Uint32 flags)
4496 {
4497 if(widget != NULL && widget->widget_info != NULL) {
4498 spinbutton *button = widget->widget_info;
4499 int arrow_size = 4 * (int)(0.5 + 5 * widget->size);
4500 Uint8 action = 0;
4501
4502 if(flags&ELW_WHEEL_UP) {
4503 action = 'i'; //i for increase
4504 } else if (flags&ELW_WHEEL_DOWN) {
4505 action = 'd'; //d for decrease
4506 } else if(mx > widget->len_x-arrow_size) {
4507 /* Click on one of the arrows */
4508 if(my < widget->len_y/2) {
4509 action = 'i'; //i for increase
4510 } else {
4511 action = 'd'; //d for decrease
4512 }
4513 do_click_sound();
4514 } else {
4515 action = 0;
4516 }
4517 if(action) {
4518 switch (button->type) {
4519 case SPIN_INT:
4520 switch (action) {
4521 case 'i':
4522 if(*(int *)button->data + button->interval <= button->max) {
4523 *(int *)button->data += button->interval;
4524 }
4525 break;
4526 case 'd':
4527 if(*(int *)button->data - button->interval >= button->min) {
4528 *(int *)button->data -= button->interval;
4529 }
4530 break;
4531 }
4532 safe_snprintf(button->input_buffer, sizeof(button->input_buffer), "%i", *(int *)button->data);
4533 break;
4534 case SPIN_FLOAT:
4535 switch (action) {
4536 case 'i':
4537 //if(*(float *)button->data + button->interval <= button->max+0.000001) { //+0.000001 to avoid issues with floating point values
4538 if (*(float *)button->data + button->interval <= button->max) { // NOTE: Can't do that, values > max may cause crashes. Change the max value intead.
4539 *(float *)button->data += button->interval;
4540 } else {
4541 *(float *)button->data = button->max;
4542 }
4543 break;
4544 case 'd':
4545 if(*(float *)button->data - button->interval >= button->min) {
4546 *(float *)button->data -= button->interval;
4547 } else {
4548 *(float *)button->data = button->min;
4549 }
4550 break;
4551 }
4552 safe_snprintf(button->input_buffer, sizeof(button->input_buffer), "%.2f", *(float *)button->data);
4553 break;
4554 }
4555 return 1;
4556 }
4557 }
4558 return 0;
4559 }
4560
spinbutton_draw(widget_list * widget)4561 static int spinbutton_draw(widget_list *widget)
4562 {
4563 spinbutton *button;
4564 int arrow_size = 0;
4565 char str[255];
4566 int x_space = (int)(0.5 + widget->size*2);
4567 int out_of_range = 0;
4568
4569 if(widget == NULL || (button = widget->widget_info) == NULL) {
4570 return 0;
4571 }
4572
4573 arrow_size = 4 * (int)(0.5 + 5 * widget->size);
4574
4575 switch(button->type) {
4576 case SPIN_INT:
4577 if(atoi(button->input_buffer) < button->min) {
4578 /* The input buffer has a value less than minimum.
4579 * Don't change the data variable and mark the text in red */
4580 out_of_range = 1;
4581 safe_strncpy(str, button->input_buffer, sizeof(str));
4582 } else {
4583 *(int *)button->data = atoi(button->input_buffer);
4584 safe_snprintf(str, sizeof (str), "%i", *(int *)button->data);
4585 }
4586 break;
4587 case SPIN_FLOAT:
4588 if(atof(button->input_buffer) < button->min) {
4589 out_of_range = 1;
4590 safe_strncpy(str, button->input_buffer, sizeof(str));
4591 } else {
4592 char *pointer = strchr(button->input_buffer, '.');
4593 int accuracy;
4594 char format[10];
4595
4596 if(pointer == NULL)
4597 pointer = strchr(button->input_buffer, ',');
4598
4599 accuracy = (pointer == NULL) ? 0 : strlen(pointer+1);
4600 if(accuracy > 3) {
4601 accuracy = 3;
4602 }
4603 safe_snprintf(format, sizeof (format), "%%.%if", accuracy);
4604 safe_snprintf(str, sizeof (str), format, *(float *)button->data);
4605 if(accuracy == 0 && pointer != NULL) {
4606 /* We have a . at the end of the input buffer, but
4607 * safe_snprintf() doesn't write it, so we have to do it manually. */
4608 safe_strcat (str, ".", sizeof (str));
4609 }
4610 }
4611 break;
4612 }
4613
4614 /* Numbers */
4615 if (out_of_range)
4616 glColor3f(1, 0, 0);
4617 else
4618 glColor3f(widget->r, widget->g, widget->b);
4619 draw_text(widget->pos_x + x_space, widget->pos_y + widget->len_y/2, (const unsigned char*)str,
4620 strlen(str), widget->fcat, TDO_ZOOM, widget->size, TDO_VERTICAL_ALIGNMENT, CENTER_DIGITS,
4621 TDO_END);
4622 glDisable(GL_TEXTURE_2D);
4623
4624 /* Border */
4625 glColor3f(widget->r, widget->g, widget->b);
4626 glBegin(GL_LINE_LOOP);
4627 glVertex3i (widget->pos_x, widget->pos_y, 0);
4628 glVertex3i (widget->pos_x + widget->len_x, widget->pos_y, 0);
4629 glVertex3i (widget->pos_x + widget->len_x, widget->pos_y + widget->len_y, 0);
4630 glVertex3i (widget->pos_x, widget->pos_y + widget->len_y, 0);
4631 glEnd ();
4632 /* Line between buttons and input */
4633 glBegin(GL_LINES);
4634 glVertex3i(widget->pos_x+widget->len_x-arrow_size, widget->pos_y,0);
4635 glVertex3i(widget->pos_x+widget->len_x-arrow_size, widget->pos_y+widget->len_y,0);
4636 glEnd();
4637 /* Up arrow */
4638 glBegin(GL_TRIANGLES);
4639 glVertex3i(widget->pos_x+widget->len_x-arrow_size + arrow_size/4, widget->pos_y + widget->len_y/4+2, 0); //Left corner
4640 glVertex3i(widget->pos_x+widget->len_x-arrow_size + arrow_size/2, widget->pos_y + 2, 0); //Top
4641 glVertex3i(widget->pos_x+widget->len_x-arrow_size + 3*arrow_size/4, widget->pos_y + widget->len_y/4+2, 0); //Right corner
4642 glEnd();
4643 /* Button separator */
4644 glBegin(GL_LINES);
4645 glVertex3i(widget->pos_x+widget->len_x-arrow_size, widget->pos_y+widget->len_y/2,0);
4646 glVertex3i(widget->pos_x+widget->len_x, widget->pos_y+widget->len_y/2,0);
4647 glEnd();
4648 /* Down arrow */
4649 glBegin(GL_TRIANGLES);
4650 glVertex3i(widget->pos_x+widget->len_x-arrow_size + arrow_size/4, widget->pos_y + widget->len_y - widget->len_y/4-2, 0); //Left corner
4651 glVertex3i(widget->pos_x+widget->len_x-arrow_size + arrow_size/2, widget->pos_y + widget->len_y - 2, 0); //Bottom
4652 glVertex3i(widget->pos_x+widget->len_x-arrow_size + 3*arrow_size/4, widget->pos_y + widget->len_y - widget->len_y/4-2, 0); //Right corner
4653 glEnd();
4654 glEnable(GL_TEXTURE_2D);
4655 #ifdef OPENGL_TRACE
4656 CHECK_GL_ERRORS();
4657 #endif //OPENGL_TRACE
4658 return 1;
4659 }
4660
spinbutton_add_extended(int window_id,Uint32 wid,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint8 data_type,void * data,float min,float max,float interval,float size)4661 int spinbutton_add_extended(int window_id, Uint32 wid, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint8 data_type, void *data, float min, float max, float interval, float size)
4662 {
4663 spinbutton *T = calloc (1, sizeof (spinbutton));
4664 // Filling the widget info
4665 T->data = data;
4666 T->max = max;
4667 T->min = min;
4668 T->type = data_type;
4669 T->interval = interval;
4670 switch(data_type)
4671 {
4672 case SPIN_FLOAT:
4673 safe_snprintf(T->input_buffer, sizeof(T->input_buffer), "%.2f", *(float *)T->data);
4674 break;
4675 case SPIN_INT:
4676 T->interval = (int)T->interval;
4677 safe_snprintf(T->input_buffer, sizeof(T->input_buffer), "%i", *(int *)T->data);
4678 break;
4679 }
4680
4681 return widget_add (window_id, wid, OnInit, x, y, lx, ly, 0, size, &spinbutton_type, T, NULL);
4682 }
4683
spinbutton_add(int window_id,int (* OnInit)(),Uint16 x,Uint16 y,Uint16 lx,Uint16 ly,Uint8 data_type,void * data,float min,float max,float interval)4684 int spinbutton_add(int window_id, int (*OnInit)(), Uint16 x, Uint16 y, Uint16 lx, Uint16 ly, Uint8 data_type, void *data, float min, float max, float interval)
4685 {
4686 return spinbutton_add_extended(window_id, widget_id++, OnInit, x, y, lx, ly, data_type, data, min, max, interval, 1);
4687 }
4688
4689 // Helper functions for widgets
4690
draw_cross(int centre_x,int centre_y,int half_len,int half_width)4691 void draw_cross(int centre_x, int centre_y, int half_len, int half_width)
4692 {
4693 glBegin(GL_QUADS);
4694 glVertex2i(centre_x - half_len, centre_y - half_len + half_width);
4695 glVertex2i(centre_x - half_len + half_width, centre_y - half_len);
4696 glVertex2i(centre_x + half_len, centre_y + half_len - half_width);
4697 glVertex2i(centre_x + half_len - half_width, centre_y + half_len);
4698 glVertex2i(centre_x + half_len, centre_y - half_len + half_width);
4699 glVertex2i(centre_x + half_len - half_width, centre_y - half_len);
4700 glVertex2i(centre_x - half_len, centre_y + half_len - half_width);
4701 glVertex2i(centre_x - half_len + half_width, centre_y + half_len);
4702 glEnd();
4703 }
4704
4705
4706 // XML Windows
4707 /*
4708 / This section contains the following (unused) functions:
4709 / int AddXMLWindow(char *fn);
4710 / int ReadXMLWindow(xmlNode *a_node);
4711 / int ParseWindow(xmlNode *node);
4712 / int ParseWidget(xmlNode *node, int winid);
4713 / int ParseTab(xmlNode *node, int winid, int colid);
4714 / int GetWidgetType(const char *w);
4715 / As well as definitions for symbolic constants for all of the widget types.
4716 */
4717 /*
4718
4719 #define LABEL 1
4720 #define IMAGE 2
4721 #define CHECKBOX 3
4722 #define BUTTON 4
4723 #define PROGRESSBAR 5
4724 #define VSCROLLBAR 6
4725 #define TABCOLLECTION 7
4726 #define TEXTFIELD 8
4727 #define PWORDFIELD 9
4728 #define MULTISELECT 10
4729 #define SPINBUTTON 11
4730
4731 int AddXMLWindow (char *fn)
4732 {
4733 xmlDocPtr doc = xmlReadFile (fn, NULL, 0);
4734 int w;
4735
4736 if (doc == NULL)
4737 return -1;
4738
4739 w = ReadXMLWindow( xmlDocGetRootElement (doc) );
4740 xmlFreeDoc(doc);
4741
4742 return w;
4743 }
4744
4745 int ReadXMLWindow(xmlNode * a_node)
4746 {
4747 xmlNode *cur_node = NULL;
4748 static int win;
4749
4750 for (cur_node = a_node; cur_node; cur_node = cur_node->next)
4751 {
4752 if (cur_node->type == XML_ELEMENT_NODE)
4753 {
4754 win = ParseWindow (cur_node);
4755 }
4756 }
4757
4758 return win;
4759 }
4760
4761 int ParseWindow (xmlNode *node)
4762 {
4763 xmlAttr *cur_attr = NULL;
4764 xmlNode *child = NULL;
4765
4766 Uint8 name[256] = "Default";
4767 int pos_x = 0, pos_y = 0, size_x = 320, size_y = 240;
4768 Uint32 flags = ELW_WIN_DEFAULT;
4769 int winid;
4770
4771 for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next)
4772 {
4773 if (cur_attr->type == XML_ATTRIBUTE_NODE)
4774 {
4775 //name=""
4776 if ( !xmlStrcasecmp (cur_attr->name,"name") )
4777 {
4778 char *p = name;
4779 my_xmlStrncopy (&p, cur_attr->children->content, sizeof (name) );
4780 continue;
4781 }
4782 //pos_x=""
4783 if ( !xmlStrcasecmp (cur_attr->name, "pos_x") )
4784 {
4785 pos_x = atoi (cur_attr->children->content);
4786 continue;
4787 }
4788 //pos_y=""
4789 if (!xmlStrcasecmp (cur_attr->name, "pos_y"))
4790 {
4791 pos_y = atoi (cur_attr->children->content);
4792 continue;
4793 }
4794 //size_x=""
4795 if ( !xmlStrcasecmp (cur_attr->name, "size_x") )
4796 {
4797 size_x = atoi (cur_attr->children->content);
4798 continue;
4799 }
4800 //size_y=""
4801 if ( !xmlStrcasecmp (cur_attr->name, "size_y") )
4802 {
4803 size_y = atoi (cur_attr->children->content);
4804 continue;
4805 }
4806 //flags=""
4807 if ( !xmlStrcasecmp (cur_attr->name, "flags") )
4808 {
4809 flags = atoi (cur_attr->children->content);
4810 continue;
4811 }
4812 }
4813 }
4814
4815 winid = create_window (name, -1, 0, pos_x, pos_y, size_x, size_y, flags);
4816
4817 for (child = node->children; child; child = child->next)
4818 {
4819 if (child->type == XML_ELEMENT_NODE)
4820 ParseWidget (child, winid);
4821 }
4822
4823 return winid;
4824 }
4825
4826 int ParseWidget (xmlNode *node, int winid)
4827 {
4828 xmlAttr *cur_attr = NULL;
4829
4830 int pos_x = 0, pos_y = 0, len_x = 0, len_y = 0, type = GetWidgetType (node->name), checked = 1, pos = 0, pos_inc = 1;
4831 Uint32 flags = 0, id = widget_id++, tid = 0, max_tabs = 0, tag_height = 0;
4832 float size = 1.0, r = 1.0, g = 1.0, b = 1.0, u1 = 0.0, u2 = 1.0, v1 = 0.0, v2 = 1.0, progress = 0.0;
4833 char text[256] = {'\0'};
4834
4835 for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next)
4836 {
4837 if (cur_attr->type == XML_ATTRIBUTE_NODE)
4838 {
4839 //pos_x=""
4840 if ( !xmlStrcasecmp (cur_attr->name, "pos_x") )
4841 {
4842 pos_x = atoi (cur_attr->children->content);
4843 continue;
4844 }
4845 //pos_y=""
4846 if ( !xmlStrcasecmp (cur_attr->name, "pos_y") )
4847 {
4848 pos_y = atoi (cur_attr->children->content);
4849 continue;
4850 }
4851 //len_x=""
4852 if ( !xmlStrcasecmp (cur_attr->name, "len_x") )
4853 {
4854 len_x = atoi (cur_attr->children->content);
4855 continue;
4856 }
4857 //len_y=""
4858 if ( !xmlStrcasecmp (cur_attr->name, "len_y") )
4859 {
4860 len_y = atoi (cur_attr->children->content);
4861 continue;
4862 }
4863 //flags=""
4864 if ( !xmlStrcasecmp (cur_attr->name, "flags") )
4865 {
4866 flags = atoi (cur_attr->children->content);
4867 continue;
4868 }
4869 //size=""
4870 if ( !xmlStrcasecmp (cur_attr->name, "size") )
4871 {
4872 size = atof (cur_attr->children->content);
4873 continue;
4874 }
4875 //r=""
4876 if ( !xmlStrcasecmp (cur_attr->name, "r") )
4877 {
4878 r = atof (cur_attr->children->content);
4879 continue;
4880 }
4881 //g=""
4882 if ( !xmlStrcasecmp (cur_attr->name, "g") )
4883 {
4884 g = atof (cur_attr->children->content);
4885 continue;
4886 }
4887 //b=""
4888 if ( !xmlStrcasecmp (cur_attr->name, "b") )
4889 {
4890 b = atof (cur_attr->children->content);
4891 continue;
4892 }
4893 //id=""
4894 if ( !xmlStrcasecmp (cur_attr->name,"id") )
4895 {
4896 id = atof (cur_attr->children->content);
4897 widget_id--;
4898 continue;
4899 }
4900
4901 switch (type)
4902 {
4903 case LABEL:
4904 case BUTTON:
4905 //text=""
4906 if ( !xmlStrcasecmp (cur_attr->name, "text") )
4907 {
4908 char *p = text;
4909 my_xmlStrncopy (&p, cur_attr->children->content, 255);
4910 continue;
4911 }
4912 break;
4913 case IMAGE:
4914 //u1=""
4915 if ( !xmlStrcasecmp (cur_attr->name,"u1") )
4916 {
4917 u1 = atof (cur_attr->children->content);
4918 continue;
4919 }
4920 //u2=""
4921 if ( !xmlStrcasecmp (cur_attr->name, "u2") )
4922 {
4923 u2 = atof (cur_attr->children->content);
4924 continue;
4925 }
4926 //v1=""
4927 if ( !xmlStrcasecmp (cur_attr->name, "v1") )
4928 {
4929 v1 = atof (cur_attr->children->content);
4930 continue;
4931 }
4932 //v2=""
4933 if ( !xmlStrcasecmp (cur_attr->name, "v2") )
4934 {
4935 v2 = atof (cur_attr->children->content);
4936 continue;
4937 }
4938 //id=""
4939 if ( !xmlStrcasecmp (cur_attr->name, "tid") )
4940 {
4941 tid = atoi (cur_attr->children->content);
4942 continue;
4943 }
4944 break;
4945
4946 case CHECKBOX:
4947 //checked=""
4948 if ( !xmlStrcasecmp (cur_attr->name, "checked") )
4949 {
4950 checked = atoi (cur_attr->children->content);
4951 continue;
4952 }
4953 break;
4954
4955 case PROGRESSBAR:
4956 //progress=""
4957 if ( !xmlStrcasecmp (cur_attr->name, "progress") )
4958 {
4959 progress = atof (cur_attr->children->content);
4960 continue;
4961 }
4962 break;
4963
4964 case VSCROLLBAR:
4965 //pos=""
4966 if ( !xmlStrcasecmp (cur_attr->name, "pos") )
4967 {
4968 pos = atoi (cur_attr->children->content);
4969 continue;
4970 }
4971 //pos_inc=""
4972 if ( !xmlStrcasecmp (cur_attr->name, "pos_inc") )
4973 {
4974 pos_inc = atoi (cur_attr->children->content);
4975 continue;
4976 }
4977 break;
4978
4979 case TABCOLLECTION:
4980 //max_tabs=""
4981 if ( !xmlStrcasecmp (cur_attr->name, "max_tabs") )
4982 {
4983 max_tabs = atoi (cur_attr->children->content);
4984 continue;
4985 }
4986 //tag_height=""
4987 if ( !xmlStrcasecmp (cur_attr->name, "tag_height") )
4988 {
4989 tag_height = atoi (cur_attr->children->content);
4990 continue;
4991 }
4992 break;
4993 }
4994 }
4995 }
4996
4997 switch(type)
4998 {
4999 case LABEL:
5000 return label_add_extended (winid, id, NULL, pos_x, pos_y, flags, size, text);
5001 case IMAGE:
5002 return image_add_extended (winid, id, NULL, pos_x, pos_y, len_x, len_y, flags, size, tid, u1, v1, u2, v2);
5003 case CHECKBOX:
5004 {
5005 int *checked_ptr = calloc(1,sizeof(int));
5006 *checked_ptr = checked;
5007 return checkbox_add_extended (winid, id, NULL, pos_x, pos_y, len_x, len_y, flags, size, checked_ptr);
5008 }
5009 case BUTTON:
5010 return button_add_extended (winid, id, NULL, pos_x, pos_y, len_x, len_y, flags, size, text);
5011 case PROGRESSBAR:
5012 return progressbar_add_extended (winid, id, NULL, pos_x, pos_y, len_x, len_y, flags, size, progress, NULL);
5013 case VSCROLLBAR:
5014 return vscrollbar_add_extended (winid, id, NULL, pos_x, pos_y, len_x, len_y, flags, size, pos, pos_inc, len_y);
5015 case TABCOLLECTION:
5016 {
5017 xmlNode *tab;
5018 int colid = tab_collection_add_extended (winid, id, NULL, pos_x, pos_y, len_x, len_y, flags, size, max_tabs, tag_height, 0);
5019 for (tab = node->children; tab; tab = tab->next)
5020 {
5021 if (tab->type == XML_ELEMENT_NODE)
5022 ParseTab (tab, winid, colid);
5023 }
5024
5025 return colid;
5026 }
5027 }
5028
5029 return -1;
5030 }
5031
5032 int ParseTab (xmlNode *node, int winid, int colid)
5033 {
5034 int tag_width = 0, closable = 0, tabid;
5035 char label[64] = {'\0'};
5036 xmlAttr *cur_attr = NULL;
5037 xmlNode *child;
5038
5039 for (cur_attr = node->properties; cur_attr; cur_attr = cur_attr->next)
5040 {
5041 if (cur_attr->type == XML_ATTRIBUTE_NODE)
5042 {
5043 if ( !xmlStrcasecmp (cur_attr->name, "label") )
5044 {
5045 char *p = label;
5046 my_xmlStrncopy ( &p, cur_attr->children->content, sizeof (label) );
5047 }
5048 else if ( !xmlStrcasecmp (cur_attr->name, "tag_width") )
5049 {
5050 tag_width = atoi (cur_attr->children->content);
5051 }
5052 else if ( !xmlStrcasecmp (cur_attr->name, "closable") )
5053 {
5054 closable = atoi (cur_attr->children->content);
5055 }
5056 }
5057 }
5058
5059 tabid = tab_add (winid, colid, label, tag_width, closable);
5060
5061 for (child = node->children; child; child = child->next)
5062 {
5063 if (child->type == XML_ELEMENT_NODE)
5064 ParseWidget (node, tabid);
5065 }
5066
5067 return tabid;
5068 }
5069
5070 int GetWidgetType (const char *w)
5071 {
5072 if(!xmlStrcasecmp(w, "LABEL"))
5073 return LABEL;
5074 if(!xmlStrcasecmp(w, "IMAGE"))
5075 return IMAGE;
5076 if(!xmlStrcasecmp(w, "CHECKBOX"))
5077 return CHECKBOX;
5078 if(!xmlStrcasecmp(w, "BUTTON"))
5079 return BUTTON;
5080 if(!xmlStrcasecmp(w, "PROGRESSBAR"))
5081 return PROGRESSBAR;
5082 if(!xmlStrcasecmp(w, "VSCROLLBAR"))
5083 return VSCROLLBAR;
5084 if(!xmlStrcasecmp(w, "TABCOLLECTION"))
5085 return TABCOLLECTION;
5086
5087 return 0;
5088 }
5089 */
5090