1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 */
21 
22 /*
23  *  sdl_widgets.cpp - Widgets for SDL dialogs
24  *
25  *  Written in 2000 by Christian Bauer
26  *
27  *  Sept-Nov. 2001 (Woody Zenfell):
28  *      Significant extensions to support more dynamic behavior and more useful layouts
29  *
30  *  Mar 1, 2002 (Woody Zenfell):
31  *      Moved w_levels here from shell_sdl; am using it in Setup Network Game box.
32  *
33  *  May 16, 2002 (Woody Zenfell):
34  *      changes to w_key to support assignment of mouse buttons as well as keys;
35  *      also fixed mouse-movement-while-binding behavior.
36  *
37  *  August 27, 2003 (Woody Zenfell):
38  *	new w_enabling_toggle can enable/disable a bank of other widgets according to its state
39  *	new w_file_chooser displays filename; allows selection of file (via FileSpecifier::ReadDialog())
40  */
41 
42 #include "cseries.h"
43 #include "sdl_dialogs.h"
44 #include "network_dialog_widgets_sdl.h"
45 #include "sdl_fonts.h"
46 #include "sdl_widgets.h"
47 #include "resource_manager.h"
48 
49 #include "shape_descriptors.h"
50 #include "screen_drawing.h"
51 #include "images.h"
52 #include "shell.h"
53 #include "world.h"
54 #include "SoundManager.h"
55 #include "interface.h"
56 #include "player.h"
57 
58 #include "screen.h"
59 
60 // ZZZ: for stringset business for modified w_select
61 #include	"TextStrings.h"
62 
63 #include    "mouse.h"   // (ZZZ) NUM_SDL_MOUSE_BUTTONS, SDLK_BASE_MOUSE_BUTTON
64 #include "joystick.h"
65 
66 #include <sstream>
67 
68 /*
69  *  Widget base class
70  */
71 
72 // ZZZ: initialize my additional storage elements
73 // (thought: I guess "widget" could be simplified, and have a subclass "useful_widget" to handle most things
74 // other than spacers.  Spacers are common and I guess we're starting to eat a fair amount of storage for a
75 // widget that does nothing and draws nothing... oh well, at least RAM is cheap.  ;) )
widget()76 widget::widget() : active(false), dirty(false), enabled(true), font(NULL), identifier(NONE), owning_dialog(NULL), saved_min_width(0), saved_min_height(0), associated_label(0)
77 {
78     rect.x = 0;
79     rect.y = 0;
80     rect.w = 0;
81     rect.h = 0;
82 }
83 
widget(int theme_widget)84 widget::widget(int theme_widget) : active(false), dirty(false), enabled(true), font(get_theme_font(theme_widget, style)), identifier(NONE), owning_dialog(NULL), saved_min_width(0), saved_min_height(0), associated_label(0)
85 {
86     rect.x = 0;
87     rect.y = 0;
88     rect.w = 0;
89     rect.h = 0;
90 }
91 
associate_label(w_label * label)92 void widget::associate_label(w_label *label)
93 {
94 	associated_label = label;
95 }
96 
label(const char * text)97 w_label *widget::label(const char *text)
98 {
99 	if (!associated_label)
100 	{
101 		associated_label = new w_label(text);
102 		associated_label->associate_widget(this);
103 	}
104 
105 	return associated_label;
106 }
107 
108 // ZZZ: enable/disable
set_enabled(bool inEnabled)109 void widget::set_enabled(bool inEnabled)
110 {
111 	if(enabled != inEnabled) {
112 		enabled = inEnabled;
113 
114 		// If we had the focus when we were disabled, we should not have the focus afterward.
115 		if(active && !enabled)
116 			owning_dialog->activate_next_widget();
117 
118 		// Assume we need a redraw to reflect new state
119 		dirty = true;
120 
121 		if (associated_label)
122 			associated_label->set_enabled(inEnabled);
123 	}
124 }
125 
set_active(bool new_active)126 void widget::set_active(bool new_active)
127 {
128 	// Assume we need a redraw to reflect new state
129 	if (enabled && (active != new_active))
130 		dirty = true;
131 	active = new_active;
132 }
133 
place(const SDL_Rect & r,placement_flags flags)134 void widget::place(const SDL_Rect &r, placement_flags flags)
135 {
136 	rect.h = r.h;
137 	rect.y = r.y;
138 
139 	if (flags & placeable::kFill)
140 	{
141 		rect.x = r.x;
142 		rect.w = r.w;
143 	}
144 	else
145 	{
146 		rect.w = saved_min_width;
147 		if (flags & placeable::kAlignLeft)
148 		{
149 			rect.x = r.x;
150 		}
151 		else if (flags & placeable::kAlignRight)
152 		{
153 			rect.x = r.x + r.w - saved_min_width;
154 		}
155 		else
156 		{
157 			rect.x = r.x + (r.w - saved_min_width) / 2;
158 		}
159 	}
160 }
161 
162 
163 /*
164  *  Static text
165  */
166 
167 // ZZZ change: copy the given string instead of just pointing to it.  Much easier for messages that change.
w_static_text(const char * t,int _theme_type)168 w_static_text::w_static_text(const char *t, int _theme_type) : widget(_theme_type), theme_type(_theme_type)
169 {
170         text = strdup(t);
171 	rect.w = text_width(text, font, style);
172 	rect.h = font->get_line_height();
173 	saved_min_height = rect.h;
174 	saved_min_width = rect.w;
175 }
176 
draw(SDL_Surface * s) const177 void w_static_text::draw(SDL_Surface *s) const
178 {
179 	uint32 pixel;
180 	pixel = get_theme_color(theme_type, DEFAULT_STATE, 0);
181 
182 	draw_text(s, text, rect.x, rect.y + font->get_ascent(), pixel, font, style);
183 }
184 
click(int x,int y)185 void w_label::click(int x, int y)
186 {
187 	if (associated_widget)
188 		associated_widget->click(0, 0);
189 
190 }
191 
draw(SDL_Surface * s) const192 void w_label::draw(SDL_Surface *s) const
193 {
194 	int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
195 	uint16 style = 0;
196 	draw_text(s, text, rect.x, rect.y + font->get_ascent() + (rect.h - font->get_line_height()) / 2, get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style);
197 }
198 
199 // ZZZ addition: change text.
200 void
set_text(const char * t)201 w_static_text::set_text(const char* t) {
202     free(text);
203     text = strdup(t);
204     dirty = true;
205 }
206 
207 // ZZZ addition: free text buffer.
~w_static_text()208 w_static_text::~w_static_text() {
209     free(text);
210 }
211 
w_styled_text(const char * t,int _theme_type)212 w_styled_text::w_styled_text(const char *t, int _theme_type) : w_static_text(t, _theme_type), text_string(t) {
213 	rect.w = font->styled_text_width(text_string, text_string.size(), style);
214 	saved_min_width = rect.w;
215 }
216 
set_text(const char * t)217 void w_styled_text::set_text(const char* t)
218 {
219 	w_static_text::set_text(t);
220 	text_string = t;
221 	// maybe reset rect.width here, but parent w_static_text doesn't, so we won't either
222 }
223 
draw(SDL_Surface * s) const224 void w_styled_text::draw(SDL_Surface *s) const
225 {
226 	uint32 pixel;
227 	pixel = get_theme_color(theme_type, DEFAULT_STATE, 0);
228 
229 	font->draw_styled_text(s, text_string, text_string.size(), rect.x, rect.y + font->get_ascent(), pixel, style);
230 }
231 
draw(SDL_Surface * s) const232 void w_slider_text::draw(SDL_Surface *s) const
233 {
234 	int state = associated_slider->enabled ? (associated_slider->active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
235 	uint16 style = 0;
236 	draw_text(s, text, rect.x, rect.y + font->get_ascent() + (rect.h - font->get_line_height()) / 2, get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style);
237 }
238 
239 /*
240  *  Picture (PICT resource)
241  */
242 
w_pict(int id)243 w_pict::w_pict(int id)
244 {
245 	LoadedResource rsrc;
246 	get_resource(FOUR_CHARS_TO_INT('P', 'I', 'C', 'T'), id, rsrc);
247 	picture = picture_to_surface(rsrc);
248 	if (picture) {
249 		rect.w = static_cast<uint16>(picture->w);
250 		rect.h = static_cast<uint16>(picture->h);
251 		SDL_SetColorKey(picture, SDL_TRUE, SDL_MapRGB(picture->format, 0xff, 0xff, 0xff));
252 	} else
253 		rect.w = rect.h = 0;
254 }
255 
~w_pict()256 w_pict::~w_pict()
257 {
258 	if (picture)
259 		SDL_FreeSurface(picture);
260 }
261 
draw(SDL_Surface * s) const262 void w_pict::draw(SDL_Surface *s) const
263 {
264 	if (picture)
265 		SDL_BlitSurface(picture, NULL, s, const_cast<SDL_Rect *>(&rect));
266 }
267 
268 
269 /*
270  *  Button
271  */
272 
w_button_base(const char * t,action_proc p,void * a,int _type)273 w_button_base::w_button_base(const char *t, action_proc p, void *a, int _type) : widget(_type), text(t), proc(p), arg(a), down(false), pressed(false), type(_type)
274 {
275 	rect.w = text_width(text.c_str(), font, style) + get_theme_space(_type, BUTTON_L_SPACE) + get_theme_space(_type, BUTTON_R_SPACE);
276 	button_c_default = get_theme_image(_type, DEFAULT_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, DEFAULT_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, DEFAULT_STATE, BUTTON_R_IMAGE)->w);
277 	button_c_active = get_theme_image(_type, ACTIVE_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, ACTIVE_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, ACTIVE_STATE, BUTTON_R_IMAGE)->w);
278 	button_c_disabled = get_theme_image(_type, DISABLED_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, DISABLED_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, DISABLED_STATE, BUTTON_R_IMAGE)->w);
279 	button_c_pressed = get_theme_image(_type, PRESSED_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, PRESSED_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, PRESSED_STATE, BUTTON_R_IMAGE)->w);
280 
281 	rect.h = static_cast<uint16>(get_theme_space(_type, BUTTON_HEIGHT));
282 
283 	saved_min_width = rect.w;
284 	saved_min_height = rect.h;
285 }
286 
~w_button_base()287 w_button_base::~w_button_base()
288 {
289 	if (button_c_default) SDL_FreeSurface(button_c_default);
290 	if (button_c_active) SDL_FreeSurface(button_c_active);
291 	if (button_c_disabled) SDL_FreeSurface(button_c_disabled);
292 	if (button_c_pressed) SDL_FreeSurface(button_c_pressed);
293 }
294 
set_callback(action_proc p,void * a)295 void w_button_base::set_callback(action_proc p, void *a)
296 {
297 	proc = p;
298 	arg = a;
299 }
300 
draw(SDL_Surface * s) const301 void w_button_base::draw(SDL_Surface *s) const
302 {
303 	// Label (ZZZ: different color for disabled)
304 	int state = DEFAULT_STATE;
305 	if (pressed)
306 		state = PRESSED_STATE;
307 	else if (!enabled)
308 		state = DISABLED_STATE;
309 	else if (active)
310 		state = ACTIVE_STATE;
311 
312 	if (use_theme_images(type))
313 	{
314 		SDL_Surface *button_l = get_theme_image(type, state, BUTTON_L_IMAGE);
315 		SDL_Surface *button_r = get_theme_image(type, state, BUTTON_R_IMAGE);
316 		SDL_Surface *button_c = button_c_default;
317 		if (pressed)
318 			button_c = button_c_pressed;
319 		else if (!enabled)
320 			button_c = button_c_disabled;
321 		else if (active)
322 			button_c = button_c_active;
323 
324 		// Button image
325 		SDL_Rect r = {rect.x, rect.y,
326 			      static_cast<Uint16>(button_l->w),
327 			      static_cast<Uint16>(button_l->h)};
328 		SDL_BlitSurface(button_l, NULL, s, &r);
329 		r.x = r.x + static_cast<Sint16>(button_l->w); // MDA: MSVC throws warnings if we use +=
330 		r.w = static_cast<Uint16>(button_c->w);
331 		r.h = static_cast<Uint16>(button_c->h);
332 		SDL_BlitSurface(button_c, NULL, s, &r);
333 		r.x = r.x + static_cast<Sint16>(button_c->w);
334 		r.w = static_cast<Uint16>(button_r->w);
335 		r.h = static_cast<Uint16>(button_r->h);
336 		SDL_BlitSurface(button_r, NULL, s, &r);
337 	}
338 	else
339 	{
340 		uint32 pixel;
341 		if (use_theme_color(type, BACKGROUND_COLOR))
342 		{
343 			uint32 pixel = get_theme_color(type, state, BACKGROUND_COLOR);
344 			SDL_Rect r = {rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2};
345 			SDL_FillRect(s, &r, pixel);
346 		}
347 
348 		pixel = get_theme_color(type, state, FRAME_COLOR);
349 		draw_rectangle(s, &rect, pixel);
350 	}
351 
352 	draw_text(s, text.c_str(), rect.x + get_theme_space(type, BUTTON_L_SPACE),
353 		  rect.y + get_theme_space(type, BUTTON_T_SPACE) + font->get_ascent(),
354 		  get_theme_color(type, state), font, style);
355 }
356 
mouse_move(int x,int y)357 void w_button_base::mouse_move(int x, int y)
358 {
359 	if (down)
360 	{
361 		if (x >= 0 && x <= rect.w && y >= 0 && y <= rect.h)
362 		{
363 			if (!pressed)
364 				dirty = true;
365 			pressed = true;
366 		}
367 		else
368 		{
369 			if (pressed)
370 				dirty = true;
371 			pressed = false;
372 		}
373 		get_owning_dialog()->draw_dirty_widgets();
374 	}
375 }
376 
mouse_down(int,int)377 void w_button_base::mouse_down(int, int)
378 {
379 	if (!enabled) return;
380 	down = true;
381 	pressed = true;
382 	dirty = true;
383 	get_owning_dialog()->draw_dirty_widgets();
384 }
385 
mouse_up(int x,int y)386 void w_button_base::mouse_up(int x, int y)
387 {
388 	if (!enabled) return;
389 
390 	down = false;
391 	pressed = false;
392 	dirty = true;
393 	get_owning_dialog()->draw_dirty_widgets();
394 
395 	if (proc && x >= 0 && x <= rect.w && y >= 0 && y <= rect.h)
396 		proc(arg);
397 }
398 
click(int,int)399 void w_button_base::click(int /*x*/, int /*y*/)
400 {
401 	// simulate a mouse press
402 	mouse_down(0, 0);
403 	SDL_Delay(1000 / 12);
404 	mouse_up(0, 0);
405 }
406 
407 /*
408  * Clickable link
409  */
410 
prochandler(void * arg)411 void w_hyperlink::prochandler(void *arg)
412 {
413 	toggle_fullscreen(false);
414 	launch_url_in_browser(static_cast<const char *>(arg));
415 	get_owning_dialog()->draw();
416 }
417 
w_hyperlink(const char * url,const char * txt)418 w_hyperlink::w_hyperlink(const char *url, const char *txt) : w_button_base((txt ? txt : url), boost::bind(&w_hyperlink::prochandler, this, _1), const_cast<char *>(url), HYPERLINK_WIDGET)
419 {
420 	rect.w = text_width(text.c_str(), font, style);
421 	rect.h = font->get_line_height();
422 	saved_min_height = rect.h;
423 	saved_min_width = rect.w;
424 }
425 
draw(SDL_Surface * s) const426 void w_hyperlink::draw(SDL_Surface *s) const
427 {
428 	int state = DEFAULT_STATE;
429 	if (pressed)
430 		state = PRESSED_STATE;
431 	else if (!enabled)
432 		state = DISABLED_STATE;
433 	else if (active)
434 		state = ACTIVE_STATE;
435 
436 	uint32 pixel = get_theme_color(HYPERLINK_WIDGET, state, 0);
437 
438 	draw_text(s, text.c_str(), rect.x, rect.y + font->get_ascent(), pixel, font, style);
439 
440 	// draw_text doesn't support underline, so draw one manually
441 	if (style & styleUnderline)
442 	{
443 		SDL_Rect r = {rect.x, rect.y + rect.h - 1, rect.w, 1};
444 		SDL_FillRect(s, &r, pixel);
445 	}
446 }
447 
448 
449 /*
450  * Tabs
451  */
452 
w_tab(const vector<string> & _labels,tab_placer * _placer)453 w_tab::w_tab(const vector<string>& _labels, tab_placer *_placer) : widget(TAB_WIDGET), labels(_labels), placer(_placer), active_tab(1), pressed_tab(0)
454 {
455 	saved_min_height = get_theme_space(TAB_WIDGET, BUTTON_HEIGHT);
456 	for (vector<string>::iterator it = labels.begin(); it != labels.end(); ++it)
457 	{
458 		int l_space = (it == labels.begin()) ? get_theme_space(TAB_WIDGET, BUTTON_L_SPACE) : get_theme_space(TAB_WIDGET, TAB_LC_SPACE);
459 		int r_space = (it == labels.end() - 1) ? get_theme_space(TAB_WIDGET, BUTTON_R_SPACE) : get_theme_space(TAB_WIDGET, TAB_RC_SPACE);
460 		int width = l_space + r_space + font->text_width(it->c_str(), style);
461 		widths.push_back(width);
462 		saved_min_width += width;
463 
464 		// load center images
465 		const int states[] = { DEFAULT_STATE, PRESSED_STATE, ACTIVE_STATE, DISABLED_STATE };
466 		images.resize(PRESSED_STATE + 1);
467 		for (int i = 0; i < 4; ++i)
468 		{
469 			int li_space = (it == labels.begin()) ? get_theme_image(TAB_WIDGET, states[i], TAB_L_IMAGE)->w : get_theme_image(TAB_WIDGET, states[i], TAB_LC_IMAGE)->w;
470 			int ri_space = (it == labels.end() - 1) ? get_theme_image(TAB_WIDGET, states[i], TAB_R_IMAGE)->w : get_theme_image(TAB_WIDGET, states[i], TAB_RC_IMAGE)->w;
471 			int c_space = width - li_space - ri_space;
472 			images[states[i]].push_back(get_theme_image(TAB_WIDGET, states[i], TAB_C_IMAGE, c_space));
473 		}
474 	}
475 
476 }
477 
~w_tab()478 w_tab::~w_tab()
479 {
480 	for (std::vector<std::vector<SDL_Surface *> >::iterator it = images.begin(); it != images.end(); ++it)
481 	{
482 		for (std::vector<SDL_Surface *>::iterator it2 = it->begin(); it2 != it->end(); ++it2)
483 		{
484 			SDL_FreeSurface(*it2);
485 		}
486 	}
487 }
488 
draw(SDL_Surface * s) const489 void w_tab::draw(SDL_Surface *s) const
490 {
491 	int x = rect.x;
492 	for (int i = 0; i < labels.size(); ++i)
493 	{
494 		int state;
495 		if (!enabled)
496 			state = DISABLED_STATE;
497 		else if (i == pressed_tab)
498 			state = PRESSED_STATE;
499 		else if (active && i == active_tab)
500 			state = ACTIVE_STATE;
501 		else
502 			state = DEFAULT_STATE;
503 
504 		int l_space;
505 		int l_offset = 0;
506 
507 		SDL_Surface *l_image;
508 		if (i == 0)
509 		{
510 			l_space = get_theme_space(TAB_WIDGET, BUTTON_L_SPACE);
511 			l_offset = 1;
512 			l_image = get_theme_image(TAB_WIDGET, state, TAB_L_IMAGE);
513 		}
514 		else
515 		{
516 			l_space = get_theme_space(TAB_WIDGET, TAB_LC_SPACE);
517 			l_image = get_theme_image(TAB_WIDGET, state, TAB_LC_IMAGE);
518 		}
519 
520 		int r_space;
521 		int r_offset = 0;
522 		SDL_Surface *r_image;
523 		if (i == labels.size() - 1)
524 		{
525 			r_space = get_theme_space(TAB_WIDGET, BUTTON_R_SPACE);
526 			r_offset = 1;
527 			r_image = get_theme_image(TAB_WIDGET, state, TAB_R_IMAGE);
528 		}
529 		else
530 		{
531 			r_space = get_theme_space(TAB_WIDGET, TAB_RC_SPACE);
532 			r_image = get_theme_image(TAB_WIDGET, state, TAB_RC_IMAGE);
533 		}
534 
535 		int c_space;
536 		SDL_Surface *c_image = images[state][i];
537 		c_space = font->text_width(labels[i].c_str(), style);
538 
539 		if (use_theme_images(TAB_WIDGET))
540 		{
541 			SDL_Rect r = { x, rect.y, static_cast<Uint16>(l_image->w), static_cast<Uint16>(l_image->h) };
542 			SDL_BlitSurface(l_image, NULL, s, &r);
543 			r.x = r.x + static_cast<Sint16>(l_image->w);
544 			r.w = static_cast<Uint16>(c_image->w);
545 			r.h = static_cast<Uint16>(c_image->h);
546 			SDL_BlitSurface(c_image, NULL, s, &r);
547 			r.x = r.x + static_cast<Sint16>(c_image->w);
548 			r.w = static_cast<Uint16>(r_image->w);
549 			r.h = static_cast<Uint16>(r_image->h);
550 			SDL_BlitSurface(r_image, NULL, s, &r);
551 		}
552 		else
553 		{
554 			if (use_theme_color(TAB_WIDGET, BACKGROUND_COLOR))
555 			{
556 				SDL_Rect r = { x + l_offset, rect.y + 1, l_space + c_space + r_space - l_offset - r_offset, get_theme_space(TAB_WIDGET, BUTTON_HEIGHT) - 2 };
557 				uint32 pixel = get_theme_color(TAB_WIDGET, state, BACKGROUND_COLOR);
558 				SDL_FillRect(s, &r, pixel);
559 			}
560 		}
561 
562 		font->draw_text(s, labels[i].c_str(), labels[i].size(), x + l_space, rect.y + get_theme_space(TAB_WIDGET, BUTTON_T_SPACE) + font->get_ascent(), get_theme_color(TAB_WIDGET, state, FOREGROUND_COLOR), style);
563 
564 		x += l_space + c_space + r_space;
565 	}
566 
567 	if (!use_theme_images(TAB_WIDGET))
568 	{
569 		uint32 pixel = get_theme_color(TAB_WIDGET, DEFAULT_STATE, FRAME_COLOR);
570 		// draw the frame
571 		SDL_Rect frame = { rect.x, rect.y, x - rect.x, get_theme_space(TAB_WIDGET, BUTTON_HEIGHT) };
572 		draw_rectangle(s, &frame, pixel);
573 	}
574 }
575 
choose_tab(int i)576 void w_tab::choose_tab(int i)
577 {
578 	pressed_tab = i;
579 	active_tab = (i + 1) % labels.size();
580 	placer->choose_tab(i);
581 	get_owning_dialog()->draw();
582 }
583 
click(int x,int y)584 void w_tab::click(int x, int y)
585 {
586 	if (enabled)
587 	{
588 		if (!x && !y)
589 		{
590 			choose_tab(active_tab);
591 		}
592 		else
593 		{
594 			int width = 0;
595 			for (int i = 0; i < labels.size(); ++i)
596 			{
597 				if (x > width && x < width + widths[i])
598 				{
599 					choose_tab(i);
600 					return;
601 				}
602 
603 				width += widths[i];
604 			}
605 		}
606 	}
607 }
608 
event(SDL_Event & e)609 void w_tab::event(SDL_Event& e)
610 {
611 	if (e.type == SDL_KEYDOWN)
612 	{
613 		switch (e.key.keysym.sym) {
614 		case SDLK_LEFT:
615 			if (active_tab > 0)
616 			{
617 				if (active_tab - 1== pressed_tab)
618 				{
619 					if (pressed_tab > 0)
620 						active_tab -= 2;
621 				}
622 				else
623 					active_tab--;
624 
625 			}
626 			dirty = true;
627 			e.type = SDL_LASTEVENT;
628 			break;
629 
630 		case SDLK_RIGHT:
631 			if (active_tab < labels.size() - 1)
632 			{
633 				if (active_tab + 1 == pressed_tab)
634 				{
635 					if (pressed_tab < labels.size() - 1)
636 						active_tab += 2;
637 				}
638 				else
639 					active_tab++;
640 			}
641 			dirty = true;
642 			e.type = SDL_LASTEVENT;
643 			break;
644 
645 		default:
646 			break;
647 
648 		}
649 	}
650 	else if (e.type == SDL_CONTROLLERBUTTONDOWN)
651 	{
652 		switch (e.cbutton.button) {
653 			case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
654 				if (active_tab > 0)
655 				{
656 					if (active_tab - 1== pressed_tab)
657 					{
658 						if (pressed_tab > 0)
659 							active_tab -= 2;
660 					}
661 					else
662 						active_tab--;
663 
664 				}
665 				dirty = true;
666 				e.type = SDL_LASTEVENT;
667 				break;
668 
669 			case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
670 				if (active_tab < labels.size() - 1)
671 				{
672 					if (active_tab + 1 == pressed_tab)
673 					{
674 						if (pressed_tab < labels.size() - 1)
675 							active_tab += 2;
676 					}
677 					else
678 						active_tab++;
679 				}
680 				dirty = true;
681 				e.type = SDL_LASTEVENT;
682 				break;
683 
684 			default:
685 				break;
686 
687 		}
688 	}
689 }
690 
691 /*
692  *  Selection button
693  */
694 
695 const uint16 MAX_TEXT_WIDTH = 200;
696 
697 // ZZZ: how come we have to do this?  because of that "&" in the typedef for action_proc?
698 // Anyway, this fixes the "crash when clicking in the Environment menu" bug we've seen
699 // in the Windows version all this time.
w_select_button(const char * s,action_proc p,void * a,bool u)700 w_select_button::w_select_button(const char *s, action_proc p, void *a, bool u)
701 	: widget(LABEL_WIDGET), selection(s), proc(p), arg(a), utf8(u), p_flags(placeable::kDefault)
702 {
703 	uint16 max_selection_width = MAX_TEXT_WIDTH;
704 
705 	saved_min_width = max_selection_width;
706 	saved_min_height = font->get_line_height();
707 }
708 
709 
710 
draw(SDL_Surface * s) const711 void w_select_button::draw(SDL_Surface *s) const
712 {
713 	int y = rect.y + font->get_ascent();
714 
715 	// Selection (ZZZ: different color for disabled)
716 	set_drawing_clip_rectangle(0, rect.x + selection_x, static_cast<uint16>(s->h), rect.x + rect.w);
717 
718 	int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
719 
720 	draw_text(s, selection, rect.x + selection_x, y, get_theme_color(ITEM_WIDGET, state), font, style, utf8);
721 	set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
722 
723 	// Cursor
724 	if (active) {
725 		//!!
726 	}
727 }
728 
click(int,int)729 void w_select_button::click(int /*x*/, int /*y*/)
730 {
731     if(enabled)
732 	    proc(arg);
733 }
734 
set_selection(const char * s)735 void w_select_button::set_selection(const char *s)
736 {
737 	selection = s;
738 	if (p_flags & placeable::kAlignRight)
739 	{
740 		selection_x = rect.w - text_width(selection, font, style);
741 	}
742 	dirty = true;
743 }
744 
place(const SDL_Rect & r,placement_flags flags)745 void w_select_button::place(const SDL_Rect &r, placement_flags flags)
746 {
747 	rect.h = r.h;
748 	rect.y = r.y;
749 
750 	rect.x = r.x;
751 	rect.w = r.w;
752 	p_flags = flags;
753 
754 	if (flags & placeable::kAlignRight)
755 	{
756 		selection_x = rect.w - text_width(selection, font, style);
757 	}
758 	else
759 	{
760 		selection_x = 0;
761 	}
762 
763 }
764 
765 /*
766  *  Selection widget (base class)
767  */
768 
769 // ZZZ: change of behavior/semantics:
770 // if passed an invalid selection, reset to a VALID one (i.e. 0)
771 // if no valid labels, returns -1 when asked for selection
772 // draw(), get_selection() check num_labels directly instead of trying to keep selection set at -1
773 
774 static const char* sNoValidOptionsString = "(no valid options)"; // XXX should be moved outside compiled code e.g. to MML
775 
w_select(size_t s,const char ** l)776 w_select::w_select(size_t s, const char **l) : widget(LABEL_WIDGET), labels(l), we_own_labels(false), selection(s), selection_changed_callback(NULL), utf8(false)
777 {
778 	num_labels = 0;
779         if(labels) {
780             while (labels[num_labels])
781                     num_labels++;
782             if (selection >= num_labels || selection < 0)
783                     selection = 0;
784         }
785 
786 	saved_min_height = font->get_line_height();
787 }
788 
789 
~w_select()790 w_select::~w_select() {
791     if(we_own_labels && labels)
792         free(labels);
793 }
794 
place(const SDL_Rect & r,placement_flags flags)795 void w_select::place(const SDL_Rect& r, placement_flags flags)
796 {
797 	rect.h = r.h;
798 	rect.y = r.y;
799 	rect.x = r.x;
800 	rect.w = r.w;
801 
802 }
803 
min_width()804 int w_select::min_width()
805 {
806 	return get_largest_label_width();
807 }
808 
draw(SDL_Surface * s) const809 void w_select::draw(SDL_Surface *s) const
810 {
811 	int y = rect.y + font->get_ascent() + (rect.h - font->get_line_height()) / 2;
812 
813 	// Selection (ZZZ: different color for disabled)
814 	const char *str = (num_labels > 0 ? labels[selection] : sNoValidOptionsString);
815 
816     int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
817 
818     draw_text(s, str, rect.x, y, get_theme_color(ITEM_WIDGET, state), font, style, utf8);
819 
820 	// Cursor
821 	if (active) {
822 		//!!
823 	}
824 }
825 
click(int,int)826 void w_select::click(int /*x*/, int /*y*/)
827 {
828     if(enabled) {
829 	    selection++;
830 	    if (selection >= num_labels)
831 		    selection = 0;
832 
833 	    selection_changed();
834     }
835 }
836 
event(SDL_Event & e)837 void w_select::event(SDL_Event &e)
838 {
839 	if (e.type == SDL_KEYDOWN) {
840 		if (e.key.keysym.sym == SDLK_LEFT) {
841 			if (num_labels == 0)
842 				selection = 0;
843 			else if (selection == 0)
844 				selection = num_labels - 1;
845 			else
846 				selection--;
847 			selection_changed();
848 			e.type = SDL_LASTEVENT;	// Swallow event
849 		} else if (e.key.keysym.sym == SDLK_RIGHT) {
850 			if (selection >= num_labels - 1)
851 				selection = 0;
852 			else
853 				selection++;
854 			selection_changed();
855 			e.type = SDL_LASTEVENT;	// Swallow event
856 		}
857 	} else if (e.type == SDL_CONTROLLERBUTTONDOWN) {
858 		if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) {
859 			if (num_labels == 0)
860 				selection = 0;
861 			else if (selection == 0)
862 				selection = num_labels - 1;
863 			else
864 				selection--;
865 			selection_changed();
866 			e.type = SDL_LASTEVENT;	// Swallow event
867 		} else if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
868 			if (selection >= num_labels - 1)
869 				selection = 0;
870 			else
871 				selection++;
872 			selection_changed();
873 			e.type = SDL_LASTEVENT;	// Swallow event
874 		}
875 	}
876 }
877 
set_selection(size_t s,bool simulate_user_input)878 void w_select::set_selection(size_t s, bool simulate_user_input /* default: false */)
879 {
880 	//if (s >= num_labels || s < 0)
881 	//	s = 0;
882 	assert(s == PIN(s, 0, num_labels - 1));
883 
884     if(selection != s) {
885         selection = s;
886 
887         if(simulate_user_input)
888             selection_changed();
889     }
890 
891 	dirty = true;
892 }
893 
894 // ZZZ: change labels after creation
set_labels(const char ** inLabels)895 void w_select::set_labels(const char** inLabels) {
896     if(we_own_labels && labels != NULL)
897         free(labels);
898 
899     labels = inLabels;
900     num_labels = 0;
901     if(labels) {
902         while(inLabels[num_labels])
903             num_labels++;
904     }
905     we_own_labels = false;
906 	if (selection >= num_labels)
907 		set_selection(0);
908 	else
909 		set_selection(selection);
910 
911     // Hope new labels have same max width as old, or that user called set_full_width() on us.
912 }
913 
914 
915 // ZZZ: set labels according to stringset
set_labels_stringset(short inStringSetID)916 void w_select::set_labels_stringset(short inStringSetID) {
917     // free old label pointers, if appropriate
918     if(we_own_labels && labels != NULL)
919         free(labels);
920 
921     // see if we need space for label pointers.  if so, allocate and fill them in.
922     if(TS_IsPresent(inStringSetID) && TS_CountStrings(inStringSetID) > 0) {
923         num_labels = TS_CountStrings(inStringSetID);
924 
925         // Allocate one extra pointer slot to fill with NULL
926         // Note: this works right if the string count is 0... but we check anyway, can save a malloc/free.
927         labels = (const char**)malloc(sizeof(const char*) * (num_labels + 1));
928         labels[num_labels] = NULL;
929 
930 		for(size_t i = 0; i < num_labels; i++) {
931             // shared references should be OK, stringsets ought to be pretty stable.  No need to copy...
932             labels[i] = TS_GetCString(inStringSetID, i);
933         }
934 
935         // we allocated; we free.
936         we_own_labels = true;
937     }
938     else {	// no stringset or no strings in stringset
939         labels = NULL;
940         num_labels = 0;
941         we_own_labels = false;
942     }
943 
944 	if (selection >= num_labels)
945 		set_selection(0);
946 	else
947 		set_selection(selection);
948 
949     // Hope new labels have same max width as old, or that user called set_full_width() on us.
950 }
951 
selection_changed(void)952 void w_select::selection_changed(void)
953 {
954 	play_dialog_sound(DIALOG_CLICK_SOUND);
955 	dirty = true;
956 
957         // ZZZ: call user-specified callback
958         if(selection_changed_callback != NULL)
959             selection_changed_callback(this);
960 }
961 
962 
963 // ZZZ addition
get_largest_label_width()964 uint16 w_select::get_largest_label_width() {
965     uint16 max_label_width = 0;
966     for (size_t i=0; i<num_labels; i++) {
967             uint16 width = text_width(labels[i], font, style, utf8);
968             if (width > max_label_width)
969                     max_label_width = width;
970     }
971 
972     // ZZZ: account for "no valid options" string
973     if(num_labels <= 0)
974 	    max_label_width = text_width(sNoValidOptionsString, font, style, utf8);
975 
976     return max_label_width;
977 }
978 
979 
980 /*
981  *  On-off toggle
982  */
983 
984 const char *w_toggle::onoff_labels[] = {"\342\230\220", "\342\230\221", NULL };
985 
w_toggle(bool selection,const char ** labels)986 w_toggle::w_toggle(bool selection, const char **labels) : w_select(selection, labels) {
987 	if (labels == onoff_labels && use_theme_images(CHECKBOX))
988 	{
989 		saved_min_height = get_theme_space(CHECKBOX, BUTTON_HEIGHT);
990 	}
991 	else if (labels == onoff_labels)
992 	{
993 		labels_are_utf8(true);
994 		font = get_theme_font(CHECKBOX, style);
995 		saved_min_height = get_theme_space(CHECKBOX, BUTTON_HEIGHT);
996 	}
997 }
998 
draw(SDL_Surface * s) const999 void w_toggle::draw(SDL_Surface *s) const
1000 {
1001 	// Selection (ZZZ: different color for disabled)
1002 	const char *str = (num_labels > 0 ? labels[selection] : sNoValidOptionsString);
1003 
1004 	int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1005 
1006 	if (labels == onoff_labels && use_theme_images(CHECKBOX))
1007 	{
1008 		SDL_Surface *image = get_theme_image(CHECKBOX, state, selection);
1009 		SDL_Rect r = { rect.x, rect.y + (rect.h - saved_min_height) / 2 + get_theme_space(CHECKBOX, BUTTON_T_SPACE), image->w, image->h };
1010 		SDL_BlitSurface(image, 0, s, &r);
1011 	}
1012 	else if (labels == onoff_labels)
1013 	{
1014 		draw_text(s, str, rect.x, rect.y + (rect.h - saved_min_height) / 2 + get_theme_space(CHECKBOX, BUTTON_T_SPACE), get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style, utf8);
1015 	}
1016 	else
1017 	{
1018 		draw_text(s, str, rect.x, rect.y + font->get_ascent(), get_theme_color(ITEM_WIDGET, state), font, style, utf8);
1019 	}
1020 
1021 	// Cursor
1022 	if (active) {
1023 		//!!
1024 	}
1025 }
1026 
min_width()1027 int w_toggle::min_width()
1028 {
1029 	if (labels == onoff_labels && use_theme_images(CHECKBOX))
1030 	{
1031 		return get_theme_image(CHECKBOX, DEFAULT_STATE, 0)->w;
1032 	}
1033 	else
1034 	{
1035 		return w_select::min_width();
1036 	}
1037 }
1038 
1039 
1040 /*
1041  *	Enabling toggle (ZZZ)
1042  *
1043  *	Can enable/disable a bank of other widgets according to its state
1044  */
1045 
1046 
w_enabling_toggle(bool inSelection,bool inEnablesWhenOn,const char ** inLabels)1047 w_enabling_toggle::w_enabling_toggle(bool inSelection, bool inEnablesWhenOn, const char** inLabels)
1048 	: w_toggle(inSelection, inLabels), enables_when_on(inEnablesWhenOn)
1049 {
1050 }
1051 
1052 
1053 void
selection_changed()1054 w_enabling_toggle::selection_changed()
1055 {
1056 	w_toggle::selection_changed();
1057 
1058 	for(DependentCollection::iterator i = dependents.begin(); i != dependents.end(); i++)
1059 		update_widget_enabled(*i);
1060 }
1061 
1062 
1063 /*
1064  *  Player color selection
1065  */
1066 
w_player_color(int selection)1067 w_player_color::w_player_color(int selection) : w_select(selection, NULL)
1068 {
1069 	set_labels_stringset(kTeamColorsStringSetID);
1070 }
1071 
draw(SDL_Surface * s) const1072 void w_player_color::draw(SDL_Surface *s) const
1073 {
1074 	int y = rect.y + font->get_ascent();
1075 
1076 	// Selection
1077 	if (selection >= 0) {
1078 		uint32 pixel = get_dialog_player_color(selection);
1079 		SDL_Rect r = {rect.x, rect.y + 1, 48, rect.h - 2};
1080 		SDL_FillRect(s, &r, pixel);
1081 	} else {
1082 		int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1083 
1084 		draw_text(s, "<unknown>", rect.x, y, get_theme_color(ITEM_WIDGET, state), font, style);
1085 	}
1086 
1087 	// Cursor
1088 	if (active)	{
1089 		//!!
1090 	}
1091 }
1092 
1093 class w_color_block : public widget {
1094 public:
w_color_block(const rgb_color * color)1095 	w_color_block(const rgb_color *color) : m_color(color) {
1096 		saved_min_height = 64;
1097 		saved_min_width = 64;
1098 	}
1099 
draw(SDL_Surface * s) const1100 	void draw(SDL_Surface *s) const {
1101 		uint32 pixel = SDL_MapRGB(s->format, m_color->red >> 8, m_color->green >> 8, m_color->blue >> 8);
1102 		SDL_Rect r = { rect.x, rect.y, 64, 64 };
1103 		SDL_FillRect(s, &r, pixel);
1104 	}
1105 
is_dirty()1106 	bool is_dirty() { return true; }
1107 private:
1108 	const rgb_color *m_color;
1109 };
1110 
1111 
click(int,int)1112 void w_color_picker::click(int, int)
1113 {
1114 	if (!enabled) return;
1115 	dialog d;
1116 
1117 	vertical_placer *placer = new vertical_placer;
1118 	placer->dual_add(new w_title("CHOOSE A COLOR"), d);
1119 	placer->add(new w_spacer(), true);
1120 
1121 	w_color_block *color_block = new w_color_block(&m_color);
1122 	placer->dual_add(color_block, d);
1123 
1124 	table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET));
1125 	table->col_flags(0, placeable::kAlignRight);
1126 
1127 	w_percentage_slider *red_w = new w_percentage_slider(16, m_color.red >> 12);
1128 	table->dual_add(red_w->label("Red"), d);
1129 	table->dual_add(red_w, d);
1130 
1131 	w_percentage_slider *green_w = new w_percentage_slider(16, m_color.green >> 12);
1132 	table->dual_add(green_w->label("Green"), d);
1133 	table->dual_add(green_w, d);
1134 
1135 	w_percentage_slider *blue_w = new w_percentage_slider(16, m_color.blue >> 12);
1136 	table->dual_add(blue_w->label("Blue"), d);
1137 	table->dual_add(blue_w, d);
1138 
1139 	placer->add(table, true);
1140 	placer->add(new w_spacer(), true);
1141 
1142 	horizontal_placer *button_placer = new horizontal_placer;
1143 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
1144 	button_placer->dual_add(new w_button("OK", dialog_ok, &d), d);
1145 
1146 	placer->add(button_placer, true);
1147 
1148 	rgb_color old_color = m_color;
1149 
1150 	d.set_widget_placer(placer);
1151 	d.set_processing_function(w_color_picker::update_color(red_w, green_w, blue_w, &m_color.red, &m_color.green, &m_color.blue));
1152 
1153 	if (d.run() == 0)
1154 	{
1155 		m_color.red = red_w->get_selection() << 12;
1156 		m_color.green = green_w->get_selection() << 12;
1157 		m_color.blue = blue_w->get_selection() << 12;
1158 
1159 		dirty = true;
1160 		get_owning_dialog()->draw_dirty_widgets();
1161 	}
1162 	else
1163 	{
1164 		m_color = old_color;
1165 	}
1166 }
1167 
draw(SDL_Surface * s) const1168 void w_color_picker::draw(SDL_Surface *s) const
1169 {
1170 	uint32 pixel = SDL_MapRGB(s->format, m_color.red >> 8, m_color.green >> 8, m_color.blue >> 8);
1171 	SDL_Rect r = {rect.x, rect.y + 1, 48, rect.h - 2 };
1172 	SDL_FillRect(s, &r, pixel);
1173 }
1174 
1175 /*
1176  *  Text entry widget
1177  */
1178 
w_text_entry(size_t max_c,const char * initial_text)1179 w_text_entry::w_text_entry(size_t max_c, const char *initial_text)
1180 	: widget(TEXT_ENTRY_WIDGET), enter_pressed_callback(NULL), value_changed_callback(NULL), max_chars(max_c), enable_mac_roman(false)
1181 {
1182 	// Initialize buffer
1183 	buf = new char[max_chars + 1];
1184 	set_text(initial_text);
1185 
1186 	saved_min_width = MAX_TEXT_WIDTH;
1187 
1188 	saved_min_height =  (int16) font->get_ascent() + font->get_descent() + font->get_leading();
1189 }
1190 
~w_text_entry()1191 w_text_entry::~w_text_entry()
1192 {
1193 	delete[] buf;
1194 }
1195 
place(const SDL_Rect & r,placement_flags flags)1196 void w_text_entry::place(const SDL_Rect& r, placement_flags flags)
1197 {
1198 	rect.h = (int16) font->get_ascent() + (int16) font->get_descent() + font->get_leading();
1199 
1200 	rect.y = r.y + (r.h - rect.h) / 2;
1201 
1202 	text_x = 0;
1203 	rect.x = r.x;
1204 	rect.w = r.w;
1205 	max_text_width = rect.w;
1206 }
1207 
draw(SDL_Surface * s) const1208 void w_text_entry::draw(SDL_Surface *s) const
1209 {
1210   int y = rect.y + font->get_ascent();
1211 
1212   int16 theRectX = rect.x;
1213   uint16 theRectW = rect.w;
1214   int16 theTextX = text_x;
1215 
1216   // Text
1217   int16 x = theRectX + theTextX;
1218   uint16 width = text_width(buf, font, style);
1219   if (width > max_text_width)
1220     x -= width - max_text_width;
1221   set_drawing_clip_rectangle(0, theRectX + theTextX, static_cast<uint16>(s->h), theRectX + theRectW);
1222 
1223   int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1224 
1225   draw_text(s, buf, x, y, get_theme_color(TEXT_ENTRY_WIDGET, state), font, style);
1226   set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1227 
1228   // Cursor
1229   if (active) {
1230 	  width = text_width(buf, cursor_position, font, style);
1231 	  SDL_Rect r = {x + width - (width ? 1 : 0), rect.y, 1, rect.h};
1232 	  SDL_FillRect(s, &r, get_theme_color(TEXT_ENTRY_WIDGET, CURSOR_STATE));
1233   }
1234 }
1235 
set_active(bool new_active)1236 void w_text_entry::set_active(bool new_active) {
1237 	if (new_active && !active) {
1238 		cursor_position = num_chars;
1239 		SDL_StartTextInput();
1240 	} else if (!new_active && active) {
1241 		SDL_StopTextInput();
1242 	}
1243 	widget::set_active(new_active);
1244 }
1245 
event(SDL_Event & e)1246 void w_text_entry::event(SDL_Event &e)
1247 {
1248 	if (e.type == SDL_KEYDOWN) {
1249 		switch (e.key.keysym.sym) {
1250 			case SDLK_LEFT:
1251 left:			if (cursor_position > 0) {
1252 					cursor_position--;
1253 					dirty = true;
1254 				}
1255 				e.type = SDL_LASTEVENT;
1256 				break;
1257 			case SDLK_RIGHT:
1258 right:			if (cursor_position < num_chars) {
1259 					cursor_position++;
1260 					dirty = true;
1261 				}
1262 				e.type = SDL_LASTEVENT;
1263 				break;
1264 			case SDLK_RETURN:
1265 			case SDLK_KP_ENTER:
1266 				if (enter_pressed_callback)
1267 					enter_pressed_callback(this);
1268 				e.type = SDL_LASTEVENT;
1269 				break;
1270 			case SDLK_BACKSPACE:
1271 backspace:		if (num_chars && cursor_position) {
1272 					memmove(&buf[cursor_position - 1], &buf[cursor_position], num_chars - cursor_position);
1273 					buf[--num_chars] = 0;
1274 					--cursor_position;
1275 					modified_text();
1276 					play_dialog_sound(DIALOG_DELETE_SOUND);
1277 				}
1278 				e.type = SDL_LASTEVENT;
1279 				break;
1280 			case SDLK_DELETE:
1281 del:			if (cursor_position < num_chars) {
1282 					memmove(&buf[cursor_position], &buf[cursor_position + 1], num_chars - cursor_position - 1);
1283 					buf[--num_chars] = 0;
1284 					modified_text();
1285 					play_dialog_sound(DIALOG_DELETE_SOUND);
1286 				}
1287 				e.type = SDL_LASTEVENT;
1288 				break;
1289 			case SDLK_HOME:
1290 home:			if (cursor_position > 0) {
1291 					cursor_position = 0;
1292 					dirty = true;
1293 				}
1294 				e.type = SDL_LASTEVENT;
1295 				break;
1296 			case SDLK_END:
1297 end:			if (cursor_position < num_chars) {
1298 					cursor_position = num_chars;
1299 					dirty = true;
1300 				}
1301 				e.type = SDL_LASTEVENT;
1302 				break;
1303 			case SDLK_UP:
1304 			case SDLK_DOWN:
1305 				break;
1306 			case SDLK_a:
1307 				if (e.key.keysym.mod & KMOD_CTRL)
1308 					goto home;
1309 				break;
1310 			case SDLK_b:
1311 				if (e.key.keysym.mod & KMOD_CTRL)
1312 					goto left;
1313 				break;
1314 			case SDLK_d:
1315 				if (e.key.keysym.mod & KMOD_CTRL)
1316 					goto del;
1317 				break;
1318 			case SDLK_e:
1319 				if (e.key.keysym.mod & KMOD_CTRL)
1320 					goto end;
1321 				break;
1322 			case SDLK_f:
1323 				if (e.key.keysym.mod & KMOD_CTRL)
1324 					goto right;
1325 				break;
1326 			case SDLK_h:
1327 				if (e.key.keysym.mod & KMOD_CTRL)
1328 					goto backspace;
1329 				break;
1330 			case SDLK_k:
1331 				if (e.key.keysym.mod & KMOD_CTRL) {
1332 					if (cursor_position < num_chars) {
1333 						num_chars = cursor_position;
1334 						buf[num_chars] = 0;
1335 						modified_text();
1336 						play_dialog_sound(DIALOG_ERASE_SOUND);
1337 					}
1338 				}
1339 				e.type = SDL_LASTEVENT;
1340 				break;
1341 			case SDLK_t:
1342 				if (e.key.keysym.mod & KMOD_CTRL) {
1343 					if (cursor_position) {
1344 						if (cursor_position == num_chars)
1345 							--cursor_position;
1346 						char tmp = buf[cursor_position - 1];
1347 						buf[cursor_position - 1] = buf[cursor_position];
1348 						buf[cursor_position] = tmp;
1349 						++cursor_position;
1350 						modified_text();
1351 						play_dialog_sound(DIALOG_TYPE_SOUND);
1352 					}
1353 				}
1354 				e.type = SDL_LASTEVENT;
1355 				break;
1356 			case SDLK_u:
1357 				if (e.key.keysym.mod & KMOD_CTRL) {
1358 					if (num_chars && cursor_position) {
1359 						memmove(&buf[0], &buf[cursor_position], num_chars - cursor_position);
1360 						num_chars -= cursor_position;
1361 						buf[num_chars] = 0;
1362 						cursor_position = 0;
1363 						modified_text();
1364 						play_dialog_sound(DIALOG_ERASE_SOUND);
1365 					}
1366 				}
1367 				e.type = SDL_LASTEVENT;
1368 				break;
1369 			case SDLK_w:
1370 				if (e.key.keysym.mod & KMOD_CTRL) {
1371 					size_t erase_position = cursor_position;
1372 					while (erase_position && buf[erase_position - 1] == ' ')
1373 						--erase_position;
1374 					while (erase_position && buf[erase_position - 1] != ' ')
1375 						--erase_position;
1376 					if (erase_position < cursor_position) {
1377 						if (cursor_position < num_chars)
1378 						memmove(&buf[erase_position], &buf[cursor_position], num_chars - cursor_position);
1379 						num_chars -= cursor_position - erase_position;
1380 						cursor_position = erase_position;
1381 						modified_text();
1382 						play_dialog_sound(DIALOG_ERASE_SOUND);
1383 					}
1384 				}
1385 				e.type = SDL_LASTEVENT;
1386 				break;
1387 			default:
1388 				break;
1389 		}
1390 	} else if (e.type == SDL_TEXTINPUT) {
1391 		std::string input_utf8 = e.text.text;
1392 		std::string input_roman = utf8_to_mac_roman(input_utf8);
1393 		for (std::string::iterator it = input_roman.begin(); it != input_roman.end(); ++it)
1394 		{
1395 			uint16 uc = *it;
1396 			if (uc >= ' ' && (uc < 0x80 || enable_mac_roman) && (num_chars + 1) < max_chars) {
1397 				memmove(&buf[cursor_position + 1], &buf[cursor_position], num_chars - cursor_position);
1398 				buf[cursor_position++] = static_cast<char>(uc);
1399 				buf[++num_chars] = 0;
1400 				modified_text();
1401 				play_dialog_sound(DIALOG_TYPE_SOUND);
1402 			}
1403 		}
1404 		e.type = SDL_LASTEVENT;
1405 	}
1406 }
1407 
click(int x,int y)1408 void w_text_entry::click(int x, int y)
1409 {
1410 	bool was_active = active;
1411 	get_owning_dialog()->activate_widget(this);
1412 
1413 	// Don't reposition cursor if:
1414 	// - we were inactive before this click
1415 	// - our text field is empty
1416 	// - the click was simulated (0, 0) or out of bounds
1417 	if (!was_active || !num_chars ||
1418 		(x == 0 && y == 0) ||
1419 	    x < 0 || y < 0 || x >= rect.w || y >= rect.h) {
1420 		return;
1421 	}
1422 
1423 	// Find closest character boundary to click
1424 	int width_remaining = x - text_x;
1425 	size_t pos = 0;
1426 	while (pos < num_chars && width_remaining > 0) {
1427 		int cw = char_width(buf[pos], font, style);
1428 		if (width_remaining > cw/2) {
1429 			pos++; // right side is closer to target than left
1430 		}
1431 		width_remaining -= cw;
1432 	}
1433 	cursor_position = pos;
1434 	dirty = true;
1435 }
1436 
set_text(const char * text)1437 void w_text_entry::set_text(const char *text)
1438 {
1439 	memset(buf, 0, max_chars + 1);
1440 	if (text)
1441 		strncpy(buf, text, max_chars);
1442 	num_chars = strlen(buf);
1443 	cursor_position = num_chars;
1444 	modified_text();
1445 }
1446 
modified_text(void)1447 void w_text_entry::modified_text(void)
1448 {
1449 	dirty = true;
1450 
1451         // ZZZ: callback if desired
1452         if(value_changed_callback)
1453             value_changed_callback(this);
1454 }
1455 
1456 /*
1457  *  Number entry widget
1458  */
1459 
w_number_entry(int initial_number)1460 w_number_entry::w_number_entry(int initial_number) : w_text_entry(/*16*/4, NULL)
1461 {
1462 	set_number(initial_number);
1463 	saved_min_width = MAX_TEXT_WIDTH / 2;
1464 }
1465 
event(SDL_Event & e)1466 void w_number_entry::event(SDL_Event &e)
1467 {
1468 	if (e.type == SDL_TEXTINPUT) {
1469 		std::string input_utf8 = e.text.text;
1470 		std::string input_roman = utf8_to_mac_roman(input_utf8);
1471 		for (std::string::iterator it = input_roman.begin(); it != input_roman.end(); ++it)
1472 		{
1473 			uint16 uc = *it;
1474 			if (uc >= '0' && (uc < 0x80 || enable_mac_roman) && (num_chars + 1) < max_chars) {
1475 				memmove(&buf[cursor_position + 1], &buf[cursor_position], num_chars - cursor_position);
1476 				buf[cursor_position++] = static_cast<char>(uc);
1477 				buf[++num_chars] = 0;
1478 				modified_text();
1479 				play_dialog_sound(DIALOG_TYPE_SOUND);
1480 			}
1481 		}
1482 	}
1483 
1484 	w_text_entry::event(e);
1485 }
1486 
set_number(int number)1487 void w_number_entry::set_number(int number)
1488 {
1489 	char str[16];
1490 	sprintf(str, "%d", number);
1491 	set_text(str);
1492 }
1493 
w_password_entry(size_t max_chars,const char * initial_text)1494 w_password_entry::w_password_entry(size_t max_chars, const char *initial_text) : w_text_entry(max_chars, initial_text)
1495 {
1496 
1497 }
1498 
draw(SDL_Surface * s) const1499 void w_password_entry::draw(SDL_Surface *s) const
1500 {
1501 	string real_text = buf;
1502 	memset(buf, '*', strlen(buf));
1503 	w_text_entry::draw(s);
1504 	strcpy(buf, real_text.c_str());
1505 }
1506 
1507 /*
1508  *  Key name widget
1509  */
1510 
1511 static const std::vector<std::string> WAITING_TEXT = { "waiting for key", "waiting for button", "waiting for button" };
1512 static const std::vector<std::string> UNBOUND_TEXT = { "none", "none", "none" };
1513 
w_key(SDL_Scancode key,w_key::Type event_type)1514 w_key::w_key(SDL_Scancode key, w_key::Type event_type) : widget(LABEL_WIDGET), binding(false), event_type(event_type)
1515 {
1516 	set_key(key);
1517 
1518 	saved_min_width = text_width(WAITING_TEXT[event_type].c_str(), font, style);
1519 	saved_min_height = font->get_line_height();
1520 }
1521 
place(const SDL_Rect & r,placement_flags flags)1522 void w_key::place(const SDL_Rect& r, placement_flags flags)
1523 {
1524 	rect.h = r.h;
1525 	rect.y = r.y;
1526 
1527 	key_x = 0;
1528 	rect.x = r.x;
1529 	rect.w = r.w;
1530 }
1531 
1532 // ZZZ: we provide phony key names for the phony keys used for mouse buttons.
1533 static const char* sMouseButtonKeyName[NUM_SDL_MOUSE_BUTTONS] = {
1534         "Mouse Left",
1535         "Mouse Middle",
1536         "Mouse Right",
1537         "Mouse X1",
1538         "Mouse X2",
1539         "Mouse Scroll Up",
1540         "Mouse Scroll Down"
1541 };
1542 
1543 static const char* sJoystickButtonKeyName[NUM_SDL_JOYSTICK_BUTTONS] = {
1544 	"A", "B", "X", "Y", "Back", "Guide", "Start",
1545 	"LS", "RS", "LB", "RB", "Up", "Down", "Left", "Right",
1546 	"LS Right", "LS Down", "RS Right", "RS Down", "LT", "RT",
1547 	"LS Left", "LS Up", "RS Left", "RS Up", "LT Neg", "RT Neg"
1548 };
1549 
1550 // ZZZ: this injects our phony key names but passes along the rest.
1551 static const char*
GetSDLKeyName(SDL_Scancode inKey)1552 GetSDLKeyName(SDL_Scancode inKey) {
1553 	if (w_key::event_type_for_key(inKey) == w_key::MouseButton)
1554         return sMouseButtonKeyName[inKey - AO_SCANCODE_BASE_MOUSE_BUTTON];
1555 	else if (w_key::event_type_for_key(inKey) == w_key::JoystickButton)
1556 	    return sJoystickButtonKeyName[inKey - AO_SCANCODE_BASE_JOYSTICK_BUTTON];
1557     else
1558         return SDL_GetScancodeName(inKey);
1559 }
1560 
draw(SDL_Surface * s) const1561 void w_key::draw(SDL_Surface *s) const
1562 {
1563 	int y = rect.y + font->get_ascent();
1564 
1565 	// Key
1566 	int16 x = rect.x + key_x;
1567 	if (binding) {
1568 		draw_text(s, WAITING_TEXT[event_type].c_str(), x, y, get_theme_color(ITEM_WIDGET, ACTIVE_STATE), font, style);
1569 	} else if (key == SDL_SCANCODE_UNKNOWN) {
1570 		int state = enabled ? (active ? ACTIVE_STATE : DISABLED_STATE) : DISABLED_STATE;
1571 		draw_text(s, UNBOUND_TEXT[event_type].c_str(), x, y, get_theme_color(ITEM_WIDGET, state), font, style);
1572 	} else {
1573         int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1574 		draw_text(s, GetSDLKeyName(key), x, y, get_theme_color(ITEM_WIDGET, state), font, style);
1575 	}
1576 }
1577 
click(int,int)1578 void w_key::click(int /*x*/, int /*y*/)
1579 {
1580 	get_owning_dialog()->activate_widget(this);
1581     if(enabled) {
1582 	    if (!binding) {
1583 		    binding = true;
1584 		    dirty = true;
1585 	    }
1586     }
1587 }
1588 
set_active(bool new_active)1589 void w_key::set_active(bool new_active) {
1590 	if (!new_active && binding) {
1591 		binding = false;
1592 		dirty = true;
1593 	}
1594 	widget::set_active(new_active);
1595 }
1596 
event_type_for_key(SDL_Scancode key)1597 w_key::Type w_key::event_type_for_key(SDL_Scancode key) {
1598 	if (key >= AO_SCANCODE_BASE_MOUSE_BUTTON && key < (AO_SCANCODE_BASE_MOUSE_BUTTON + NUM_SDL_MOUSE_BUTTONS))
1599 		return MouseButton;
1600 	else if (key >= AO_SCANCODE_BASE_JOYSTICK_BUTTON && key < (AO_SCANCODE_BASE_JOYSTICK_BUTTON + NUM_SDL_JOYSTICK_BUTTONS))
1601 		return JoystickButton;
1602 	return KeyboardKey;
1603 }
1604 
event(SDL_Event & e)1605 void w_key::event(SDL_Event &e)
1606 {
1607     if(binding) {
1608 		bool handled = false;
1609 		bool up = false;
1610 		switch (e.type) {
1611 			case SDL_MOUSEBUTTONDOWN:
1612 				if (event_type == MouseButton) {
1613 					if (e.button.button < NUM_SDL_REAL_MOUSE_BUTTONS) {
1614 						set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_MOUSE_BUTTON + e.button.button - 1));
1615 						handled = true;
1616 					}
1617 				}
1618 				break;
1619 			case SDL_CONTROLLERBUTTONDOWN:
1620 				if ((e.cbutton.button + AO_SCANCODE_BASE_JOYSTICK_BUTTON) == AO_SCANCODE_JOYSTICK_ESCAPE) {
1621 					set_key(SDL_SCANCODE_UNKNOWN);
1622 					handled = true;
1623 				} else if (event_type == JoystickButton) {
1624 					if (e.cbutton.button < SDL_CONTROLLER_BUTTON_MAX) {
1625 						set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + e.cbutton.button));
1626 						handled = true;
1627 					}
1628 				}
1629 				break;
1630 			case SDL_CONTROLLERAXISMOTION:
1631 				if (event_type == JoystickButton) {
1632 					if (e.caxis.value >= 16384) {
1633 						set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + e.caxis.axis));
1634 						handled = true;
1635 					} else if (e.caxis.value <= -16384) {
1636 						set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE + e.caxis.axis));
1637 						handled = true;
1638 					}
1639 				}
1640 				break;
1641 			case SDL_MOUSEWHEEL:
1642 				if (event_type == MouseButton) {
1643 					up = (e.wheel.y > 0);
1644 #if SDL_VERSION_ATLEAST(2,0,4)
1645 					if (e.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
1646 						up = !up;
1647 #endif
1648 					set_key(static_cast<SDL_Scancode>(up ? AO_SCANCODE_MOUSESCROLL_UP : AO_SCANCODE_MOUSESCROLL_DOWN));
1649 					handled = true;
1650 				}
1651 				break;
1652 			case SDL_KEYDOWN:
1653 				if (e.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
1654 					set_key(SDL_SCANCODE_UNKNOWN);
1655 					handled = true;
1656 				} else if (event_type == KeyboardKey) {
1657 					set_key(e.key.keysym.scancode);
1658 					handled = true;
1659 				}
1660 				break;
1661 			case SDL_MOUSEMOTION:
1662 				e.type = SDL_LASTEVENT; // suppress motion while assigning
1663 				break;
1664 			default:
1665 				break;
1666 		}
1667 
1668 		if (handled) {
1669 			dirty = true;
1670 			binding = false;
1671 			// activate next widget by faking a key press
1672 			e.type = SDL_KEYDOWN;
1673 			e.key.keysym.sym = SDLK_DOWN;
1674 			e.key.keysym.scancode = SDL_SCANCODE_DOWN;
1675 		}
1676     }
1677 }
1678 
1679 
set_key(SDL_Scancode k)1680 void w_key::set_key(SDL_Scancode k)
1681 {
1682 	key = k;
1683 }
1684 
1685 /*
1686  * Progress
1687  */
draw(SDL_Surface * s) const1688 void w_progress_bar::draw(SDL_Surface* s) const
1689 {
1690 	int filled_width = (rect.w - 2) * value / max_value;
1691 	SDL_Rect dst_rect = rect;
1692 	dst_rect.h -= 2;
1693 	dst_rect.y += 2;
1694 	SDL_FillRect(s, &dst_rect, get_theme_color(MESSAGE_WIDGET, DEFAULT_STATE, FOREGROUND_COLOR));
1695 	dst_rect.x += filled_width + 1;
1696 	dst_rect.y++;
1697 	dst_rect.h -= 2;
1698 	dst_rect.w = dst_rect.w - filled_width - 2;
1699 	if (use_theme_color(DIALOG_FRAME, BACKGROUND_COLOR))
1700 		SDL_FillRect(s, &dst_rect, get_theme_color(DIALOG_FRAME, DEFAULT_STATE, BACKGROUND_COLOR));
1701 }
1702 
set_progress(int inValue,int inMaxValue)1703 void w_progress_bar::set_progress(int inValue, int inMaxValue)
1704 {
1705 	value = inValue;
1706 	max_value = inMaxValue;
1707 	dirty = true;
1708 }
1709 
1710 
1711 
1712 /*
1713  *  Slider
1714  */
1715 
1716 const int SLIDER_WIDTH = 160;
1717 const int SLIDER_THUMB_HEIGHT = 14;
1718 const int SLIDER_THUMB_WIDTH = 8;
1719 const int SLIDER_TROUGH_HEIGHT = 8;
1720 const int SLIDER_LABEL_SPACE = 5;
1721 
w_slider(int num,int s)1722 w_slider::w_slider(int num, int s) : widget(LABEL_WIDGET), selection(s), num_items(num), thumb_dragging(false), readout(NULL), slider_changed_callback(NULL)
1723 {
1724 	slider_l = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_L_IMAGE);
1725 	slider_r = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_R_IMAGE);
1726 	slider_c = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_C_IMAGE, SLIDER_WIDTH - slider_l->w - slider_r->w);
1727 	thumb = get_theme_image(SLIDER_THUMB, DEFAULT_STATE, 0);
1728 
1729 	trough_width = SLIDER_WIDTH - get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE) - get_theme_space(SLIDER_WIDGET, SLIDER_R_SPACE);
1730 
1731 	saved_min_width = SLIDER_WIDTH;
1732 	if (use_theme_images(SLIDER_WIDGET))
1733 		saved_min_height = std::max(static_cast<uint16>(slider_c->h), static_cast<uint16>(thumb->h));
1734 	else
1735 		saved_min_height = SLIDER_THUMB_HEIGHT + 2;
1736 
1737 	readout_x = saved_min_width + SLIDER_LABEL_SPACE;
1738 	init_formatted_value();
1739 }
1740 
~w_slider()1741 w_slider::~w_slider()
1742 {
1743 	if (slider_c) SDL_FreeSurface(slider_c);
1744 }
1745 
place(const SDL_Rect & r,placement_flags flags)1746 void w_slider::place(const SDL_Rect& r, placement_flags flags)
1747 {
1748 	rect.h = r.h;
1749 	rect.y = r.y + (r.h - saved_min_height) / 2;
1750 	rect.x = r.x;
1751 	slider_x = 0;
1752 	rect.w = r.w;
1753 
1754 	SDL_Rect r2;
1755 	r2.h = r.h;
1756 	r2.y = r.y;
1757 	r2.x = r.x + readout_x;
1758 	r2.w = readout->min_width();
1759 	readout->place(r2, placeable::kDefault);
1760 
1761 	set_selection(selection);
1762 }
1763 
draw(SDL_Surface * s) const1764 void w_slider::draw(SDL_Surface *s) const
1765 {
1766 	if (use_theme_images(SLIDER_WIDGET))
1767 	{
1768 		// Slider trough
1769 		SDL_Rect r = {rect.x + slider_x, rect.y + (saved_min_height - slider_l->h) / 2,
1770 			      static_cast<Uint16>(slider_l->w),
1771 			      static_cast<Uint16>(slider_l->h)};
1772 		SDL_BlitSurface(slider_l, NULL, s, &r);
1773 		r.x = r.x + static_cast<Sint16>(slider_l->w);
1774 		r.w = static_cast<Uint16>(slider_c->w);
1775 		r.h = static_cast<Uint16>(slider_c->h);
1776 		SDL_BlitSurface(slider_c, NULL, s, &r);
1777 		r.x = r.x + static_cast<Sint16>(slider_c->w);
1778 		r.w = static_cast<Uint16>(slider_r->w);
1779 		r.h = static_cast<Uint16>(slider_r->h);
1780 		SDL_BlitSurface(slider_r, NULL, s, &r);
1781 
1782 		// Slider thumb
1783 		r.x = rect.x + static_cast<Sint16>(thumb_x);
1784 		r.y = rect.y + (saved_min_height - thumb->h) / 2 + get_theme_space(SLIDER_WIDGET, SLIDER_T_SPACE);
1785 		r.w = static_cast<Uint16>(thumb->w);
1786 		r.h = static_cast<Uint16>(thumb->h);
1787 		SDL_BlitSurface(thumb, NULL, s, &r);
1788 	}
1789 	else
1790 	{
1791 		SDL_Rect r = {rect.x, rect.y + (saved_min_height - SLIDER_TROUGH_HEIGHT) / 2 + get_theme_space(SLIDER_WIDGET, SLIDER_T_SPACE), SLIDER_WIDTH, SLIDER_TROUGH_HEIGHT};
1792 		uint32 pixel = get_theme_color(SLIDER_WIDGET, DEFAULT_STATE, FRAME_COLOR);
1793 		draw_rectangle(s, &r, pixel);
1794 
1795 		pixel = get_theme_color(SLIDER_WIDGET, DEFAULT_STATE, FOREGROUND_COLOR);
1796 		r.x = r.x + 1;
1797 		r.y = r.y + 1;
1798 		r.w = r.w - 2;
1799 		r.h = r.h - 2;
1800 		SDL_FillRect(s, &r, pixel);
1801 
1802 		pixel = get_theme_color(SLIDER_THUMB, DEFAULT_STATE, FRAME_COLOR);
1803 		r.x = rect.x + static_cast<Sint16>(thumb_x);
1804 		r.y = rect.y + (saved_min_height - SLIDER_THUMB_HEIGHT) / 2;
1805 		r.w = thumb_width();
1806 		r.h = SLIDER_THUMB_HEIGHT;
1807 		draw_rectangle(s, &r, pixel);
1808 
1809 		pixel = get_theme_color(SLIDER_THUMB, DEFAULT_STATE, FOREGROUND_COLOR);
1810 		r.x = r.x + 1;
1811 		r.y = r.y + 1;
1812 		r.w = r.w - 2;
1813 		r.h = r.h - 2;
1814 		SDL_FillRect(s, &r, pixel);
1815 
1816 	}
1817 	readout->draw(s);
1818 }
1819 
mouse_move(int x,int)1820 void w_slider::mouse_move(int x, int /*y*/)
1821 {
1822 	if (thumb_dragging) {
1823 		int delta_x = (x - slider_x - get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE)) - thumb_drag_x;
1824 		set_selection(delta_x * num_items / (trough_width - thumb_width()));
1825 	}
1826 }
1827 
click(int x,int)1828 void w_slider::click(int x, int /*y*/)
1829 {
1830     if(enabled) {
1831 	    if (x >= thumb_x && x < thumb_x + thumb_width()) {
1832 		    thumb_dragging = dirty = true;
1833 		    thumb_drag_x = x - thumb_x;
1834 	    }
1835     }
1836 }
1837 
event(SDL_Event & e)1838 void w_slider::event(SDL_Event &e)
1839 {
1840 	if (e.type == SDL_KEYDOWN) {
1841 		if (e.key.keysym.sym == SDLK_LEFT) {
1842 			set_selection(selection - 1);
1843 			item_selected();
1844 			e.type = SDL_LASTEVENT;	// Swallow event
1845 		} else if (e.key.keysym.sym == SDLK_RIGHT) {
1846 			set_selection(selection + 1);
1847 			item_selected();
1848 			e.type = SDL_LASTEVENT;	// Swallow event
1849 		}
1850 	} else if (e.type == SDL_CONTROLLERBUTTONDOWN) {
1851 		if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) {
1852 			set_selection(selection - 1);
1853 			item_selected();
1854 			e.type = SDL_LASTEVENT;	// Swallow event
1855 		} else if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
1856 			set_selection(selection + 1);
1857 			item_selected();
1858 			e.type = SDL_LASTEVENT;	// Swallow event
1859 		}
1860 	} else if (e.type == SDL_MOUSEBUTTONUP) {
1861 		if (thumb_dragging) {
1862 			thumb_dragging = false;
1863 			dirty = true;
1864 			item_selected();
1865 		}
1866 	}
1867 }
1868 
set_selection(int s)1869 void w_slider::set_selection(int s)
1870 {
1871 	if (s >= num_items)
1872 		s = num_items - 1;
1873 	else if (s < 0)
1874 		s = 0;
1875 	selection = s;
1876 	thumb_x = int(float(selection * (trough_width - thumb_width())) / (num_items - 1) + 0.5);
1877 	thumb_x += get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE) + slider_x;
1878 	dirty = true;
1879 	selection_changed();
1880 }
1881 
thumb_width() const1882 int w_slider::thumb_width() const
1883 {
1884 	if (use_theme_images(SLIDER_WIDGET))
1885 		return thumb->w;
1886 	else
1887 		return SLIDER_THUMB_WIDTH;
1888 }
1889 
formatted_value()1890 std::string w_slider::formatted_value()
1891 {
1892 	std::ostringstream ss;
1893 	ss << selection;
1894 	return ss.str();
1895 }
1896 
selection_changed()1897 void w_slider::selection_changed()
1898 {
1899 	readout->set_text(formatted_value().c_str());
1900 
1901 	if (slider_changed_callback != NULL)
1902 		slider_changed_callback(this);
1903 }
1904 
init_formatted_value()1905 void w_slider::init_formatted_value()
1906 {
1907 	int temp = selection;
1908 	selection = num_items;
1909 	std::string tempstr = formatted_value();
1910 	selection = temp;
1911 	delete readout;
1912 	readout = new w_slider_text(tempstr.c_str());
1913 	readout->associated_slider = this;
1914 	saved_min_height = std::max(saved_min_height, readout->min_height());
1915 	saved_min_width = readout_x + readout->min_width();
1916 }
1917 
formatted_value()1918 std::string w_percentage_slider::formatted_value()
1919 {
1920 	std::ostringstream ss;
1921 	ss << (selection * 100 / (num_items - 1)) << "%";
1922 	return ss.str();
1923 }
1924 
1925 
1926 /*
1927  *  List selection
1928  */
1929 
w_list_base(uint16 width,size_t lines,size_t)1930 w_list_base::w_list_base(uint16 width, size_t lines, size_t /*sel*/) : widget(ITEM_WIDGET), num_items(0), shown_items(lines), thumb_dragging(false), top_item(0)
1931 {
1932 	rect.w = width;
1933 	rect.h = item_height() * static_cast<uint16>(shown_items) + get_theme_space(LIST_WIDGET, T_SPACE) + get_theme_space(LIST_WIDGET, B_SPACE);
1934 
1935 	frame_tl = get_theme_image(LIST_WIDGET, DEFAULT_STATE, TL_IMAGE);
1936 	frame_tr = get_theme_image(LIST_WIDGET, DEFAULT_STATE, TR_IMAGE);
1937 	frame_bl = get_theme_image(LIST_WIDGET, DEFAULT_STATE, BL_IMAGE);
1938 	frame_br = get_theme_image(LIST_WIDGET, DEFAULT_STATE, BR_IMAGE);
1939 	frame_t = get_theme_image(LIST_WIDGET, DEFAULT_STATE, T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
1940 	frame_l = get_theme_image(LIST_WIDGET, DEFAULT_STATE, L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
1941 	frame_r = get_theme_image(LIST_WIDGET, DEFAULT_STATE, R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
1942 	frame_b = get_theme_image(LIST_WIDGET, DEFAULT_STATE, B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
1943 
1944 	thumb_t = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_T_IMAGE);
1945 	thumb_tc = NULL;
1946 	SDL_Surface *thumb_tc_unscaled = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_TC_IMAGE);
1947 	thumb_c = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_C_IMAGE);
1948 	SDL_Surface *thumb_bc_unscaled = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_BC_IMAGE);
1949 	thumb_bc = NULL;
1950 	thumb_b = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_B_IMAGE);
1951 
1952 	min_thumb_height = static_cast<uint16>(thumb_t->h + thumb_tc_unscaled->h + thumb_c->h + thumb_bc_unscaled->h + thumb_b->h);
1953 
1954 	trough_rect.x = rect.w - get_theme_space(LIST_WIDGET, TROUGH_R_SPACE);
1955 	trough_rect.y = get_theme_space(LIST_WIDGET, TROUGH_T_SPACE);
1956 	trough_rect.w = get_theme_space(LIST_WIDGET, TROUGH_WIDTH);
1957 	trough_rect.h = rect.h - get_theme_space(LIST_WIDGET, TROUGH_T_SPACE) - get_theme_space(LIST_WIDGET, TROUGH_B_SPACE);
1958 
1959 	saved_min_width = rect.w;
1960 	saved_min_height = rect.h;
1961 }
1962 
~w_list_base()1963 w_list_base::~w_list_base()
1964 {
1965 	if (frame_t) SDL_FreeSurface(frame_t);
1966 	if (frame_l) SDL_FreeSurface(frame_l);
1967 	if (frame_r) SDL_FreeSurface(frame_r);
1968 	if (frame_b) SDL_FreeSurface(frame_b);
1969 	if (thumb_tc) SDL_FreeSurface(thumb_tc);
1970 	if (thumb_bc) SDL_FreeSurface(thumb_bc);
1971 }
1972 
draw_image(SDL_Surface * dst,SDL_Surface * s,int16 x,int16 y) const1973 void w_list_base::draw_image(SDL_Surface *dst, SDL_Surface *s, int16 x, int16 y) const
1974 {
1975 	SDL_Rect r = {x, y, static_cast<Uint16>(s->w), static_cast<Uint16>(s->h)};
1976 	SDL_BlitSurface(s, NULL, dst, &r);
1977 }
1978 
draw(SDL_Surface * s) const1979 void w_list_base::draw(SDL_Surface *s) const
1980 {
1981 	if (use_theme_images(LIST_WIDGET))
1982 	{
1983 		// Draw frame
1984 		int16 x = rect.x;
1985 		int16 y = rect.y;
1986 		draw_image(s, frame_tl, x, y);
1987 		draw_image(s, frame_t, x + static_cast<int16>(frame_tl->w), y);
1988 		draw_image(s, frame_tr, x + static_cast<int16>(frame_tl->w) + static_cast<int16>(frame_t->w), y);
1989 		draw_image(s, frame_l, x, y + static_cast<int16>(frame_tl->h));
1990 		draw_image(s, frame_r, x + rect.w - static_cast<int16>(frame_r->w), y + static_cast<int16>(frame_tr->h));
1991 		draw_image(s, frame_bl, x, y + static_cast<int16>(frame_tl->h) + static_cast<int16>(frame_l->h));
1992 		draw_image(s, frame_b, x + static_cast<int16>(frame_bl->w), y + rect.h - static_cast<int16>(frame_b->h));
1993 		draw_image(s, frame_br, x + static_cast<int16>(frame_bl->w) + static_cast<int16>(frame_b->w), y + static_cast<int16>(frame_tr->h) + static_cast<int16>(frame_r->h));
1994 
1995 		// Draw thumb
1996 		x = rect.x + trough_rect.x;
1997 		y = rect.y + thumb_y;
1998 		draw_image(s, thumb_t, x, y);
1999 		draw_image(s, thumb_tc, x, y = y + static_cast<int16>(thumb_t->h));
2000 		draw_image(s, thumb_c, x, y = y + static_cast<int16>(thumb_tc->h));
2001 		draw_image(s, thumb_bc, x, y = y + static_cast<int16>(thumb_c->h));
2002 		draw_image(s, thumb_b, x, y = y + static_cast<int16>(thumb_bc->h));
2003 
2004 	}
2005 	else
2006 	{
2007 		uint32 pixel = get_theme_color(LIST_WIDGET, DEFAULT_STATE, FRAME_COLOR);
2008 		draw_rectangle(s, &rect, pixel);
2009 
2010 		SDL_Rect real_trough = { rect.x + trough_rect.x, rect.y + trough_rect.y, trough_rect.w, trough_rect.h };
2011 		draw_rectangle(s, &real_trough, pixel);
2012 		real_trough.x = real_trough.x + 1;
2013 		real_trough.y = real_trough.y + 1;
2014 		real_trough.w = real_trough.w - 2;
2015 		real_trough.h = real_trough.h - 2;
2016 		if (use_theme_color(LIST_THUMB, BACKGROUND_COLOR))
2017 		{
2018 			pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, BACKGROUND_COLOR);
2019 			SDL_FillRect(s, &real_trough, pixel);
2020 		}
2021 
2022 		pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, FRAME_COLOR);
2023 		SDL_Rect thumb_rect = { rect.x + trough_rect.x, rect.y + thumb_y, trough_rect.w, thumb_t->h + thumb_tc->h + thumb_c->h + thumb_bc->h + thumb_b->h};
2024 		draw_rectangle(s, &thumb_rect, pixel);
2025 
2026 		pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, FOREGROUND_COLOR);
2027 		thumb_rect.x = thumb_rect.x + 1;
2028 		thumb_rect.y = thumb_rect.y + 1;
2029 		thumb_rect.w = thumb_rect.w - 2;
2030 		thumb_rect.h = thumb_rect.h - 2;
2031 		SDL_FillRect(s, &thumb_rect, pixel);
2032 
2033 	}
2034 
2035 	// Draw items
2036 	draw_items(s);
2037 }
2038 
mouse_move(int x,int y)2039 void w_list_base::mouse_move(int x, int y)
2040 {
2041 	if (thumb_dragging) {
2042 		int delta_y = y - thumb_drag_y;
2043 		if (delta_y > 0 && num_items > shown_items && trough_rect.h > thumb_height) {
2044 			set_top_item(delta_y * (num_items - shown_items) / (trough_rect.h - thumb_height));
2045 		} else {
2046 		  set_top_item(0);
2047 		}
2048 	} else {
2049 		if (x < get_theme_space(LIST_WIDGET, L_SPACE) || x >= rect.w - get_theme_space(LIST_WIDGET, R_SPACE)
2050 		    || y < get_theme_space(LIST_WIDGET, T_SPACE) || y >= rect.h - get_theme_space(LIST_WIDGET, B_SPACE))
2051 			return;
2052 
2053 		if ((y - get_theme_space(LIST_WIDGET, T_SPACE)) / item_height() + top_item < std::min(num_items, top_item + shown_items))
2054 		{	set_selection((y - get_theme_space(LIST_WIDGET, T_SPACE)) / item_height() + top_item); }
2055 //		else
2056 //		{	set_selection(num_items - 1); }
2057 	}
2058 }
2059 
place(const SDL_Rect & r,placement_flags flags)2060 void w_list_base::place(const SDL_Rect& r, placement_flags flags)
2061 {
2062 	widget::place(r, flags);
2063 
2064 	trough_rect.x = rect.w - get_theme_space(LIST_WIDGET, TROUGH_R_SPACE);
2065 	trough_rect.y = get_theme_space(LIST_WIDGET, TROUGH_T_SPACE);
2066 	trough_rect.w = get_theme_space(LIST_WIDGET, TROUGH_WIDTH);
2067 	trough_rect.h = rect.h - get_theme_space(LIST_WIDGET, TROUGH_T_SPACE) - get_theme_space(LIST_WIDGET, TROUGH_B_SPACE);
2068 
2069 	frame_t = get_theme_image(LIST_WIDGET, DEFAULT_STATE, T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
2070 	frame_l = get_theme_image(LIST_WIDGET, DEFAULT_STATE, L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
2071 	frame_r = get_theme_image(LIST_WIDGET, DEFAULT_STATE, R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
2072 	frame_b = get_theme_image(LIST_WIDGET, DEFAULT_STATE, B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
2073 }
2074 
click(int x,int y)2075 void w_list_base::click(int x, int y)
2076 {
2077 	if (x >= trough_rect.x && x < trough_rect.x + trough_rect.w
2078 //	 && y >= trough_rect.y && y < trough_rect.y + trough_rect.h) {
2079 	    && y >= thumb_y && y <= thumb_y + thumb_height) {
2080 		thumb_dragging = dirty = true;
2081 		thumb_drag_y = y - thumb_y;
2082 	} else {
2083 		if (num_items > 0 && is_item_selectable(selection))
2084 			item_selected();
2085 	}
2086 }
2087 
event(SDL_Event & e)2088 void w_list_base::event(SDL_Event &e)
2089 {
2090 	if (e.type == SDL_KEYDOWN) {
2091 		switch (e.key.keysym.sym) {
2092 			case SDLK_UP:
2093 				if (selection != 0)
2094 				{	set_selection(selection - 1); }
2095 				e.type = SDL_LASTEVENT;	// Prevent selection of previous widget
2096 				break;
2097 			case SDLK_DOWN:
2098 				if (selection < num_items - 1)
2099 				{	set_selection(selection + 1); }
2100 				e.type = SDL_LASTEVENT;	// Prevent selection of next widget
2101 				break;
2102 			case SDLK_PAGEUP:
2103 				if (selection > shown_items)
2104 				{	set_selection(selection - shown_items); }
2105 				else
2106 				{	set_selection(0); }
2107 				break;
2108 			case SDLK_PAGEDOWN:
2109 				if (selection + shown_items < num_items - 1)
2110 				{	set_selection(selection + shown_items); }
2111 				else
2112 				{	set_selection(num_items - 1); }
2113 				break;
2114 			case SDLK_HOME:
2115 				set_selection(0);
2116 				break;
2117 			case SDLK_END:
2118 				set_selection(num_items - 1);
2119 				break;
2120 			default:
2121 				break;
2122 		}
2123 	} else if (e.type == SDL_CONTROLLERBUTTONDOWN) {
2124 		switch (e.cbutton.button) {
2125 			case SDL_CONTROLLER_BUTTON_DPAD_UP:
2126 				if (selection != 0)
2127 				{	set_selection(selection - 1); }
2128 				e.type = SDL_LASTEVENT;	// Prevent selection of previous widget
2129 				break;
2130 			case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
2131 				if (selection < num_items - 1)
2132 				{	set_selection(selection + 1); }
2133 				e.type = SDL_LASTEVENT;	// Prevent selection of next widget
2134 				break;
2135 		}
2136 	} else if (e.type == SDL_MOUSEBUTTONUP) {
2137 		if (thumb_dragging) {
2138 			thumb_dragging = false;
2139 			dirty = true;
2140 		}
2141 	} else if (e.type == SDL_MOUSEWHEEL) {
2142 		int amt = e.wheel.y * -1 * kListScrollSpeed;
2143 		if (amt < 0) {
2144 			amt = amt * -1;
2145 			if (top_item > amt)
2146 				set_top_item(top_item - amt);
2147 			else
2148 				set_top_item(0);
2149 		} else if (amt > 0) {
2150 			if (top_item + amt < num_items - shown_items)
2151 				set_top_item(top_item + amt);
2152 			else
2153 				set_top_item(num_items - shown_items);
2154 		}
2155 	}
2156 }
2157 
set_selection(size_t s)2158 void w_list_base::set_selection(size_t s)
2159 {
2160 	// Set selection, check for bounds
2161 	assert(s == PIN(s, 0, num_items - 1));
2162 	if (s != selection)
2163 		dirty = true;
2164 	selection = s;
2165 
2166 	// Make selection visible
2167 	if (s < top_item)
2168 		set_top_item(s);
2169 	else if (s >= top_item + shown_items)
2170 		set_top_item(s - shown_items + 1);
2171 }
2172 
new_items(void)2173 void w_list_base::new_items(void)
2174 {
2175 	// Reset top item and selection
2176 
2177 	// ghs: actually, remember top item and selection
2178 	size_t saved_top_item = top_item;
2179 	size_t saved_selection = get_selection();
2180 	top_item = selection = 0;
2181 	dirty = true;
2182 
2183 	// Calculate thumb height
2184 	if (num_items <= shown_items)
2185 		thumb_height = trough_rect.h;
2186 	else if (num_items == 0)
2187 		thumb_height = static_cast<uint16>(shown_items) * trough_rect.h;
2188 	else
2189 		thumb_height = uint16(float(shown_items * trough_rect.h) / num_items + 0.5);
2190 	if (thumb_height < min_thumb_height)
2191 		thumb_height = min_thumb_height;
2192 	else if (thumb_height > trough_rect.h)
2193 		thumb_height = trough_rect.h;
2194 
2195 	// Create dynamic thumb images
2196 	if (thumb_tc)
2197 		SDL_FreeSurface(thumb_tc);
2198 	if (thumb_bc)
2199 		SDL_FreeSurface(thumb_bc);
2200 	int rem_height = thumb_height - thumb_t->h - thumb_c->h - thumb_b->h;
2201 	int dyn_height = rem_height / 2;
2202 	thumb_tc = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_TC_IMAGE, 0, dyn_height);
2203 	thumb_bc = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_BC_IMAGE, 0, (rem_height & 1) ? dyn_height + 1 : dyn_height);
2204 
2205 	thumb_y = 0;
2206 	if (thumb_y > trough_rect.h - thumb_height)
2207 		thumb_y = trough_rect.h - thumb_height;
2208 	thumb_y = thumb_y + trough_rect.y;
2209 
2210 	if (saved_selection < num_items)
2211 		set_selection(saved_selection);
2212 	if (saved_top_item)
2213 		set_top_item(saved_top_item);
2214 }
2215 
center_item(size_t i)2216 void w_list_base::center_item(size_t i)
2217 {
2218   set_top_item((i > shown_items / 2) ? i - shown_items / 2 : 0);
2219 }
2220 
set_top_item(size_t i)2221 void w_list_base::set_top_item(size_t i)
2222 {
2223 	// Set top item (check for bounds)
2224 	if (num_items > shown_items)
2225 		i = PIN(i, 0, num_items - shown_items);
2226 	else
2227 		i = 0;
2228 	if (i != top_item)
2229 		dirty = true;
2230 	top_item = i;
2231 
2232 	// Calculate thumb y position
2233 	if (num_items <= shown_items)
2234 		thumb_y = 0;
2235 	else
2236 		thumb_y = int16(float(top_item * (trough_rect.h - thumb_height)) / (num_items - shown_items) + 0.5);
2237 	if (thumb_y > trough_rect.h - thumb_height)
2238 		thumb_y = trough_rect.h - thumb_height;
2239 	thumb_y = thumb_y + trough_rect.y;
2240 }
2241 
2242 
2243 
2244 // ZZZ: maybe this belongs in a new file or something - it's definitely A1-related
2245 // whereas most (but not all) of the other widgets here are sort of 'general-purpose'.
2246 // Anyway, moved here from shell_sdl.h and enhanced ever so slightly, will now be
2247 // using it in the setup network game dialog as well.
2248 
2249 /*
2250  *  Level number dialog
2251  */
2252 
w_levels(const vector<entry_point> & items,dialog * d)2253 w_levels::w_levels(const vector<entry_point> &items, dialog *d)
2254 	  : w_list<entry_point>(items, 400, 8, 0), parent(d), show_level_numbers(true) {}
2255 
2256 // ZZZ: new constructor gives more control over widget's appearance.
w_levels(const vector<entry_point> & items,dialog * d,uint16 inWidth,size_t inNumLines,size_t inSelectedItem,bool in_show_level_numbers)2257 w_levels::w_levels(const vector<entry_point>& items, dialog* d, uint16 inWidth,
2258         size_t inNumLines, size_t inSelectedItem, bool in_show_level_numbers)
2259 	  : w_list<entry_point>(items, inWidth, inNumLines, inSelectedItem), parent(d), show_level_numbers(in_show_level_numbers) {}
2260 
2261 void
item_selected(void)2262 w_levels::item_selected(void)
2263 {
2264 	parent->quit(0);
2265 }
2266 
2267 void
draw_item(vector<entry_point>::const_iterator i,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2268 w_levels::draw_item(vector<entry_point>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
2269 {
2270 	y = y + font->get_ascent();
2271 	char str[256];
2272 
2273     if(show_level_numbers)
2274     	sprintf(str, "%d - %s", i->level_number + 1, i->level_name);
2275     else
2276         sprintf(str, "%s", i->level_name);
2277 
2278 	set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2279 	draw_text(s, str, x, y, get_theme_color(ITEM_WIDGET, selected ? ACTIVE_STATE : DEFAULT_STATE), font, style);
2280 	set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2281 }
2282 
2283 
2284 /*
2285  *  String List
2286  */
2287 
w_string_list(const vector<string> & items,dialog * d,int sel)2288 w_string_list::w_string_list(const vector<string> &items, dialog *d, int sel)
2289 	: w_list<string>(items, 400, 8, sel), parent(d) {}
2290 
item_selected(void)2291 void w_string_list::item_selected(void)
2292 {
2293 	parent->quit(0);
2294 }
2295 
draw_item(vector<string>::const_iterator i,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2296 void w_string_list::draw_item(vector<string>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
2297 {
2298 	y = y + font->get_ascent();
2299 	char str[256];
2300 
2301 	sprintf(str, "%s", i->c_str ());
2302 
2303 	set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2304 	draw_text(s, str, x, y, get_theme_color(ITEM_WIDGET, selected ? ACTIVE_STATE : DEFAULT_STATE), font, style);
2305 	set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2306 }
2307 
2308 
2309 /*
2310  *  Selection Popup
2311  */
2312 
w_select_popup(action_proc p,void * a)2313 w_select_popup::w_select_popup (action_proc p, void *a) : w_select_button ("", gotSelectedCallback, NULL)
2314 {
2315 	set_arg(this);
2316 	selection = -1;
2317 
2318 	action = p;
2319 	arg = a;
2320 }
2321 
set_labels(const vector<string> & inLabels)2322 void w_select_popup::set_labels(const vector<string>& inLabels)
2323 {
2324 	labels = inLabels;
2325 
2326 	// recalculate min width
2327 	saved_min_width = 0;
2328 	for (vector<string>::iterator it = labels.begin(); it != labels.end(); ++it)
2329 	{
2330 		uint16 width = text_width(it->c_str(), font, style);
2331 		if (width > saved_min_width)
2332 			saved_min_width = width;
2333 	}
2334 }
2335 
set_selection(int value)2336 void w_select_popup::set_selection (int value)
2337 {
2338 	if (value < labels.size () && value >= 0)
2339 		selection = value;
2340 	else
2341 		selection = -1;
2342 
2343 	if (selection == -1)
2344 		w_select_button::set_selection ("");
2345 	else
2346 		w_select_button::set_selection (labels[selection].c_str ());
2347 }
2348 
gotSelected()2349 void w_select_popup::gotSelected ()
2350 {
2351 	if (labels.size () > 1) {
2352 		dialog theDialog;
2353 		vertical_placer *placer = new vertical_placer;
2354 
2355 		w_string_list* string_list_w = new w_string_list (labels, &theDialog, selection >= 0 ? selection : 0);
2356 		placer->dual_add (string_list_w, theDialog);
2357 		theDialog.activate_widget(string_list_w);
2358 
2359 		theDialog.set_widget_placer(placer);
2360 		if (theDialog.run () == 0)
2361 			set_selection (string_list_w->get_selection ());
2362 	}
2363 
2364 	if (action)
2365 		action (arg);
2366 }
2367 
2368 
2369 static const char* const sFileChooserInvalidFileString = "(no valid selection)";
2370 
w_file_chooser(const char * inDialogPrompt,Typecode inTypecode)2371 w_file_chooser::w_file_chooser(const char* inDialogPrompt, Typecode inTypecode)
2372 	: w_select_button("", NULL, NULL, true), typecode(inTypecode)
2373 {
2374 	strncpy(dialog_prompt, inDialogPrompt, sizeof(dialog_prompt));
2375 	set_selection(sFileChooserInvalidFileString);
2376 }
2377 
2378 
2379 
2380 void
set_file(const FileSpecifier & inFile)2381 w_file_chooser::set_file(const FileSpecifier& inFile)
2382 {
2383 	file = inFile;
2384 	update_filename();
2385 }
2386 
2387 
2388 
2389 void
click(int,int)2390 w_file_chooser::click(int, int)
2391 {
2392 	if(enabled)
2393 	{
2394 		if(file.ReadDialog(typecode, dialog_prompt))
2395 		{
2396 			update_filename();
2397 			if (m_callback)
2398 				m_callback ();
2399 		}
2400 	}
2401 }
2402 
2403 
2404 
2405 void
update_filename()2406 w_file_chooser::update_filename()
2407 {
2408 	if(file.Exists())
2409 	{
2410 		file.GetName(filename);
2411 		std::string filename_copy = filename;
2412 		strcpy(filename, FileSpecifier::HideExtension(filename_copy).c_str());
2413 		set_selection(filename);
2414 	}
2415 	else
2416 		set_selection(sFileChooserInvalidFileString);
2417 }
2418 
2419 
w_items_in_room_get_name_of_item(GameListMessage::GameListEntry item)2420 const string w_items_in_room_get_name_of_item (GameListMessage::GameListEntry item)
2421 {
2422 	return item.name ();
2423 }
2424 
w_items_in_room_get_name_of_item(prospective_joiner_info item)2425 const string w_items_in_room_get_name_of_item (prospective_joiner_info item)
2426 {
2427 	return std::string(item.name);
2428 }
2429 
w_items_in_room_get_name_of_item(MetaserverPlayerInfo item)2430 const string w_items_in_room_get_name_of_item (MetaserverPlayerInfo item)
2431 {
2432 	return item.name ();
2433 }
2434 
draw_item(const GameListMessage::GameListEntry & item,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2435 void w_games_in_room::draw_item(const GameListMessage::GameListEntry& item, SDL_Surface* s, int16 x, int16 y, uint16 width, bool selected) const
2436 {
2437 	int game_style = style;
2438 
2439 	int state;
2440 	if (!item.compatible())
2441 	{
2442 		state = item.target() ? SELECTED_INCOMPATIBLE_GAME : INCOMPATIBLE_GAME;
2443 	}
2444 	else if (item.running())
2445 	{
2446 		state = item.target() ? SELECTED_RUNNING_GAME : RUNNING_GAME;
2447 	}
2448 	else
2449 	{
2450 		state = item.target() ? SELECTED_GAME : GAME;
2451 	}
2452 
2453 	uint32 fg;
2454 	if (selected)
2455 	{
2456 		fg = get_theme_color(ITEM_WIDGET, ACTIVE_STATE);
2457 	}
2458 	else
2459 	{
2460 		fg = get_theme_color(METASERVER_GAMES, state, FOREGROUND_COLOR);
2461 	}
2462 
2463 	uint32 bg = get_theme_color(METASERVER_GAMES, state, BACKGROUND_COLOR);
2464 	SDL_Rect r = { x, y, width, 3 * font->get_line_height() + 2};
2465 	SDL_FillRect(s, &r, bg);
2466 
2467 	if (use_theme_color(METASERVER_GAMES, FRAME_COLOR))
2468 	{
2469 		uint32 frame = get_theme_color(METASERVER_GAMES, state, FRAME_COLOR);
2470 		draw_rectangle(s, &r, frame);
2471 	}
2472 
2473 
2474 	x += 1;
2475 	width -= 2;
2476 	y += font->get_ascent() + 1;
2477 
2478 	std::ostringstream time_or_ping;
2479 	int right_text_width = 0;
2480 
2481 	// first line, game name, ping or time remaining
2482 	if (item.running())
2483 	{
2484 		if (item.m_description.m_timeLimit && !(item.m_description.m_timeLimit == INT32_MAX || item.m_description.m_timeLimit == -1))
2485 		{
2486 			if (item.minutes_remaining() == 1)
2487 			{
2488 				time_or_ping << "~1 Minute";
2489 			}
2490 			else
2491 			{
2492 				time_or_ping << item.minutes_remaining() << " Minutes";
2493 			}
2494 		}
2495 		else
2496 		{
2497 			time_or_ping << "Untimed";
2498 		}
2499 	}
2500 	else
2501 	{
2502 		// draw ping
2503 	}
2504 
2505 	right_text_width = text_width(time_or_ping.str().c_str(), font, game_style);
2506 
2507 	// draw game name
2508 	set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width - right_text_width);
2509 	font->draw_styled_text(s, item.name(), item.name().size(), x, y, fg, game_style);
2510 
2511 	// draw remaining or ping
2512 	set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2513 	draw_text(s, time_or_ping.str().c_str(), x + width - right_text_width, y, fg, font, game_style);
2514 
2515 	y += font->get_line_height();
2516 
2517 	std::ostringstream game_and_map;
2518 
2519 	if (!item.compatible())
2520 	{
2521 		game_and_map << "|i" << item.m_description.m_scenarioName;
2522 		if (item.m_description.m_scenarioVersion != "")
2523 		{
2524 			game_and_map << ", Version " << item.m_description.m_scenarioVersion;
2525 		}
2526 	}
2527 	else
2528 	{
2529 		game_and_map << item.game_string()
2530 			     << " on |i"
2531 			     << item.m_description.m_mapName;
2532 	}
2533 
2534 	font->draw_styled_text(s, game_and_map.str().c_str(), game_and_map.str().size(), x, y, fg, game_style);
2535 
2536 	y += font->get_line_height();
2537 
2538 	right_text_width = font->styled_text_width(item.m_hostPlayerName, item.m_hostPlayerName.size(), game_style);
2539 	set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width - right_text_width);
2540 
2541 	std::ostringstream game_settings;
2542 	if (item.running())
2543 	{
2544 		if (item.m_description.m_numPlayers == 1)
2545 		{
2546 			game_settings << "1 Player";
2547 		}
2548 		else
2549 		{
2550 			game_settings << static_cast<uint16>(item.m_description.m_numPlayers) << " Players";
2551 		}
2552 	}
2553 	else
2554 	{
2555 		game_settings << static_cast<uint16>(item.m_description.m_numPlayers)
2556 			      << "/"
2557 			      << item.m_description.m_maxPlayers
2558 			      << " Players";
2559 	}
2560 
2561 	if (item.m_description.m_timeLimit && !(item.m_description.m_timeLimit == INT32_MAX || item.m_description.m_timeLimit == -1))
2562 	{
2563 		game_settings << ", "
2564 			      << item.m_description.m_timeLimit / 60 / TICKS_PER_SECOND
2565 			      << " Minutes";
2566 	}
2567 
2568 	if (item.m_description.m_teamsAllowed)
2569 	{
2570 		game_settings << ", Teams";
2571 	}
2572 
2573 	draw_text(s, game_settings.str().c_str(), x, y, fg, font, game_style);
2574 
2575 	set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2576 	font->draw_styled_text(s, item.m_hostPlayerName, item.m_hostPlayerName.size(), x + width - right_text_width, y, fg, game_style);
2577 
2578 	set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2579 }
2580 
2581 
darken(uint8 component,uint8 amount)2582 static inline uint8 darken(uint8 component, uint8 amount)
2583 {
2584 	return PIN((uint16) component * (255 - amount) / 255, 0, 255);
2585 }
2586 
lighten(uint8 component,uint8 amount)2587 static inline uint8 lighten(uint8 component, uint8 amount)
2588 {
2589 	return PIN((uint16) component + (255 - component) * amount / 255, 0, 255);
2590 }
2591 
draw_item(const MetaserverPlayerInfo & item,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2592 void w_players_in_room::draw_item(const MetaserverPlayerInfo& item, SDL_Surface* s,
2593 					int16 x, int16 y, uint16 width, bool selected) const
2594 {
2595 	set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2596 
2597 	SDL_Rect r = {x, y, width, font->get_line_height() + 4};
2598 
2599 	if (item.target())
2600 	{
2601 		SDL_FillRect(s, &r, SDL_MapRGB(s->format, 0xff, 0xff, 0xff));
2602 	}
2603 
2604 	// background is player color
2605 	uint32 pixel;
2606 	if (item.target())
2607 	{
2608 		int amount = 0x7f;
2609 		pixel = SDL_MapRGB(s->format,
2610 				   lighten(item.color()[0] >> 8, amount),
2611 				   lighten(item.color()[1] >> 8, amount),
2612 				   lighten(item.color()[2] >> 8, amount));
2613 	}
2614 	else
2615 	{
2616 		int amount;
2617 		if (item.away())
2618 			amount = 0xbf;
2619 		else
2620 			amount = 0;
2621 
2622 		pixel = SDL_MapRGB(s->format,
2623 				   darken(item.color()[0] >> 8, amount),
2624 				   darken(item.color()[1] >> 8, amount),
2625 				   darken(item.color()[2] >> 8, amount));
2626 	}
2627 
2628 	r.x = x + kPlayerColorSwatchWidth + kSwatchGutter + 1;
2629 	r.y = y + 1;
2630 	r.w = width - kPlayerColorSwatchWidth - kSwatchGutter - 2;
2631 	r.h = font->get_line_height() + 2;
2632 	SDL_FillRect(s, &r, pixel);
2633 
2634 	// team swatch
2635 	r.x = x + 1;
2636 	r.y = y + 1;
2637 	r.w = kPlayerColorSwatchWidth;
2638 	r.h = font->get_line_height() + 2;
2639 
2640 	if (item.target())
2641 	{
2642 		int amount = 0x7f;
2643 		pixel = SDL_MapRGB(s->format,
2644 				   lighten(item.team_color()[0] >> 8, amount),
2645 				   lighten(item.team_color()[1] >> 8, amount),
2646 				   lighten(item.team_color()[2] >> 8, amount));
2647 	}
2648 	else
2649 	{
2650 		int amount;
2651 		if (item.away())
2652 			amount = 0x7f;
2653 		else
2654 			amount = 0;
2655 
2656 		pixel = SDL_MapRGB(s->format,
2657 				   darken(item.team_color()[0] >> 8, amount),
2658 				   darken(item.team_color()[1] >> 8, amount),
2659 				   darken(item.team_color()[2] >> 8, amount));
2660 	}
2661 
2662 	SDL_FillRect(s, &r, pixel);
2663 
2664 	y += font->get_ascent();
2665 	uint32 color;
2666 	if (selected)
2667 	{
2668 		color = get_theme_color(ITEM_WIDGET, ACTIVE_STATE);
2669 	}
2670 	else if (item.away())
2671 	{
2672 		color = SDL_MapRGB(s->format, 0x7f, 0x7f, 0x7f);
2673 	}
2674 	else
2675 	{
2676 		color = SDL_MapRGB(s->format, 0xff, 0xff, 0xff);
2677 	}
2678 
2679 	font->draw_styled_text(s, item.name(), item.name().size(), x + kPlayerColorSwatchWidth + kSwatchGutter + 2, y + 1, color, item.away() ? style : style | styleShadow);
2680 
2681 	set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2682 }
2683 
2684 
append_entry(const ColoredChatEntry & e)2685 void w_colorful_chat::append_entry(const ColoredChatEntry& e)
2686 {
2687 	if (e.message.empty())
2688 	{
2689 		get_owning_dialog()->draw_dirty_widgets();
2690 		return;
2691 	}
2692 
2693 	string name;
2694 	if (font->styled_text_width(e.sender, e.sender.size(), style | styleShadow) > kNameWidth)
2695 		name = string(e.sender, 0, font->trunc_styled_text(e.sender, kNameWidth, style | styleShadow));
2696 	else
2697 		name = e.sender;
2698 
2699 	int message_style = style;
2700 	int available_width = rect.w - get_theme_space(LIST_WIDGET, L_SPACE) - get_theme_space(LIST_WIDGET, R_SPACE);
2701 	if (e.type == ColoredChatEntry::ChatMessage)
2702 	{
2703 		available_width -= kNameWidth + taper_width() + 2;
2704 	}
2705 	else if (e.type == ColoredChatEntry::PrivateMessage)
2706 	{
2707 		message_style |= styleShadow;
2708 		available_width -= kNameWidth + taper_width() + 4;
2709 	}
2710 	else
2711 	{
2712 		message_style |= styleShadow;
2713 		available_width -= 2;
2714 	}
2715 
2716 	size_t usable_characters = font->trunc_styled_text(e.message, available_width, message_style);
2717 	string::const_iterator middle;
2718 	string::const_iterator rest;
2719 	if (usable_characters != e.message.size()) {
2720 		size_t last_space = e.message.find_last_of(' ', usable_characters);
2721 		if (last_space != 0 && last_space <= usable_characters)
2722 		{
2723 			middle = e.message.begin() + last_space;
2724 			rest = middle + 1;
2725 		}
2726 		else
2727 		{
2728 			middle = e.message.begin() + usable_characters;
2729 			rest = middle;
2730 		}
2731 	} else {
2732 		middle = e.message.begin() + usable_characters;
2733 		rest = middle;
2734 	}
2735 
2736 	ColoredChatEntry e_begin = e;
2737 	e_begin.message = string(e.message.begin(), middle);
2738 	e_begin.sender = name;
2739 
2740 	ColoredChatEntry e_rest = e;
2741 	if (rest != e.message.end())
2742 	{
2743 		e_rest.message = font->style_at(e.message, middle, message_style) + string(rest, e.message.end());
2744 	}
2745 	else
2746 		e_rest.message = string(rest, e.message.end());
2747 	e_rest.sender = name;
2748 
2749 	bool save_top_item = top_item < num_items - shown_items;
2750 	size_t saved_top_item = top_item;
2751 	entries.push_back(e_begin);
2752 
2753 	num_items = entries.size();
2754 	new_items();
2755 	if (save_top_item) {
2756 		set_top_item(saved_top_item);
2757 	} else if (num_items > shown_items) {
2758 		set_top_item(num_items - shown_items);
2759 	}
2760 
2761 	append_entry(e_rest);
2762 }
2763 
draw_item(vector<ColoredChatEntry>::const_iterator it,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2764 void w_colorful_chat::draw_item(vector<ColoredChatEntry>::const_iterator it, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
2765 {
2766 	int computed_y = y + font->get_ascent();
2767 
2768 	uint16 message_x = x;
2769 	uint16 message_width = width;
2770 
2771 	if ((*it).type == ColoredChatEntry::ChatMessage || (*it).type == ColoredChatEntry::PrivateMessage)
2772 	{
2773 		// draw the name
2774 		SDL_Rect r = { x, y, kNameWidth, font->get_line_height() + 1};
2775 		uint32 pixel = SDL_MapRGB(s->format,
2776 					  (*it).color.red >> 8,
2777 					  (*it).color.green >> 8,
2778 					  (*it).color.blue >> 8);
2779 		SDL_FillRect(s, &r, pixel);
2780 
2781 		// draw taper
2782 		r.x += kNameWidth ;
2783 		if (it->type == ColoredChatEntry::PrivateMessage)
2784 		{
2785 			// red bar under taper
2786 			r.w = taper_width() + 2;
2787 			SDL_FillRect(s, &r, SDL_MapRGB(s->format, 0x7f, 0x0, 0x0));
2788 		}
2789 
2790 		r.w = 1;
2791 		for (int i = 0; i < taper_width(); ++i)
2792 		{
2793 			r.y++;
2794 			r.h -= 2;
2795 			SDL_FillRect(s, &r, pixel);
2796 			r.x++;
2797 		}
2798 
2799 		set_drawing_clip_rectangle(0, x, static_cast<uint16>(s->h), x + kNameWidth);
2800 		font->draw_styled_text(s, it->sender, it->sender.size(), x + 1, computed_y, SDL_MapRGB(s->format, 0xff, 0xff, 0xff), style | styleShadow);
2801 
2802 		message_x += kNameWidth + taper_width() + 2;
2803 		message_width -= kNameWidth + taper_width() + 2;
2804 	}
2805 
2806 	uint32 message_color = SDL_MapRGB(s->format, 0xff, 0xff, 0xff);
2807 	if (it->type == ColoredChatEntry::ChatMessage)
2808 	{
2809 		message_color = get_theme_color(CHAT_ENTRY, DEFAULT_STATE, FOREGROUND_COLOR);
2810 	}
2811 
2812 	int message_style = style;
2813 	if ((*it).type != ColoredChatEntry::ChatMessage) message_style |= styleShadow;
2814 
2815 	if ((*it).type == ColoredChatEntry::ServerMessage)
2816 	{
2817 		// draw the blue bar
2818 		uint32 pixel = SDL_MapRGB(s->format, 0x0, 0x0, 0x7f);
2819 		SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2820 		SDL_FillRect(s, &r, pixel);
2821 		message_x += 1;
2822 		message_width -= 2;
2823 	}
2824 	else if ((*it).type == ColoredChatEntry::PrivateMessage)
2825 	{
2826 		// draw a red bar
2827 		uint32 pixel = SDL_MapRGB(s->format, 0x7f, 0x0, 0x0);
2828 		SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2829 		SDL_FillRect(s, &r, pixel);
2830 		message_x += 1;
2831 		message_width -= 2;
2832 	}
2833 	else if ((*it).type == ColoredChatEntry::LocalMessage)
2834 	{
2835 		// draw a gray bar
2836 		uint32 pixel = SDL_MapRGB(s->format, 0x3f, 0x3f, 0x3f);
2837 		SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2838 		SDL_FillRect(s, &r, pixel);
2839 		message_x += 1;
2840 		message_width -= 2;
2841 	}
2842 
2843 	set_drawing_clip_rectangle(0, message_x, static_cast<uint16>(s->h), message_x + message_width);
2844 	font->draw_styled_text(s, it->message, it->message.size(), message_x, computed_y, message_color, message_style);
2845 
2846 	set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2847 }
2848 
hide()2849 void SDLWidgetWidget::hide ()
2850 {
2851 	hidden = true;
2852 	m_widget->set_enabled (false);
2853 }
2854 
show()2855 void SDLWidgetWidget::show ()
2856 {
2857 	hidden = false;
2858 	m_widget->set_enabled (!inactive);
2859 }
2860 
deactivate()2861 void SDLWidgetWidget::deactivate ()
2862 {
2863 	inactive = true;
2864 	m_widget->set_enabled (false);
2865 }
2866 
activate()2867 void SDLWidgetWidget::activate ()
2868 {
2869 	inactive = false;
2870 	m_widget->set_enabled (!hidden);
2871 }
2872 
2873 
PlayersInGameWidget(w_players_in_game2 * pig)2874 PlayersInGameWidget::PlayersInGameWidget (w_players_in_game2* pig)
2875 	: SDLWidgetWidget (pig)
2876 	, m_pig (pig)
2877 	{}
2878 
redraw()2879 void PlayersInGameWidget::redraw ()
2880 {
2881 	m_pig->start_displaying_actual_information ();
2882 	m_pig->update_display ();
2883 	m_pig->get_owning_dialog ()->draw_dirty_widgets ();
2884 }
2885 
2886 
2887 
2888