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