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_dialogs.cpp - SDL implementation of user dialogs
24  *
25  *  Written in 2000 by Christian Bauer
26  *
27  *  11 Mar 2002 (Woody Zenfell): renamed XML_FrameParser to XML_DFrameParser
28  *      to resolve conflict with new animated-model frame parsing code.
29  *
30  *  5 Feb 2003 (Woody Zenfell): exposed draw_dirty_widgets() functionality
31  *	also trying to fix fullscreen drawing problems related to flipping
32  */
33 
34 #include "cseries.h"
35 #include "sdl_dialogs.h"
36 #include "sdl_fonts.h"
37 #include "sdl_widgets.h"
38 
39 #include "shape_descriptors.h"
40 #include "screen_drawing.h"
41 #include "shell.h"
42 #include "screen.h"
43 #include "images.h"
44 #include "world.h"
45 #include "SoundManager.h"
46 #include "game_errors.h"
47 #include "Plugins.h"
48 
49 // for fixing broken theme paths
50 #include "interface.h"
51 #include "preferences.h"
52 
53 #ifdef HAVE_OPENGL
54 #include "OGL_Headers.h"
55 #include "OGL_Setup.h"
56 #include "OGL_Blitter.h"
57 #include "OGL_Render.h"
58 #endif
59 
60 #include "InfoTree.h"
61 #include "Logging.h"
62 
63 #include <map>
64 #include <string>
65 
66 // Global variables
67 dialog *top_dialog = NULL;
68 
69 static SDL_Surface *dialog_surface = NULL;
70 
71 static SDL_Surface *default_image = NULL;
72 
73 static OpenedResourceFile theme_resources;
74 
75 struct dialog_image_spec_type {
76 	string name;
77 	bool scale;
78 };
79 
80 struct theme_state
81 {
82 	std::map<int, SDL_Color> colors;
83 	std::map<int, dialog_image_spec_type> image_specs;
84 	std::map<int, SDL_Surface *> images;
85 };
86 
87 struct theme_widget
88 {
89 	std::map<int, theme_state> states;
90 	font_info *font;
91 	TextSpec font_spec;
92 	bool font_set;
93 	std::map<int, int> spaces;
94 
theme_widgettheme_widget95 	theme_widget() : font(0), font_set(false) { }
96 };
97 
98 static FileSpecifier theme_path;
99 static std::map<int, theme_widget> dialog_theme;
100 
101 // Prototypes
102 static void shutdown_dialogs(void);
103 static bool load_theme(FileSpecifier &theme);
104 static void unload_theme(void);
105 static void set_theme_defaults(void);
106 
107 /*
108  *  Initialize dialog manager
109  */
110 
initialize_dialogs()111 void initialize_dialogs()
112 {
113 	// Allocate surface for dialogs (this surface is needed because when
114 	// OpenGL is active, we can't write directly to the screen)
115 	dialog_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 16, 0x7c00, 0x03e0, 0x001f, 0);
116 	assert(dialog_surface);
117 
118 	// Default image
119 	default_image = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, 1, 24, 0xff0000, 0x00ff00, 0x0000ff, 0);
120 	assert(default_image);
121 	uint32 transp = SDL_MapRGB(default_image->format, 0x00, 0xff, 0xff);
122 	SDL_FillRect(default_image, NULL, transp);
123 	SDL_SetColorKey(default_image, SDL_TRUE, transp);
124 
125 	// Load theme from preferences, if it exists
126 	load_dialog_theme(true);
127 
128 	atexit(shutdown_dialogs);
129 }
130 
131 
132 /*
133  *  Shutdown dialog manager
134  */
135 
shutdown_dialogs(void)136 static void shutdown_dialogs(void)
137 {
138 	unload_theme();
139 }
140 
141 
142 /*
143  *  Theme MML parser
144  */
145 
parse_theme_image(InfoTree root,int type,int state,int max_index)146 static void parse_theme_image(InfoTree root, int type, int state, int max_index)
147 {
148 	int index = -1;
149 	std::string name;
150 	if (!root.read_attr("file", name) ||
151 		!root.read_attr_bounded("index", index, 0, max_index))
152 		return;
153 
154 	bool scale = false;
155 	root.read_attr("scale", scale);
156 	dialog_theme[type].states[state].image_specs[index].name = name;
157 	dialog_theme[type].states[state].image_specs[index].scale = scale;
158 }
159 
parse_theme_images(InfoTree root,int type,int state,int num_items=1)160 static void parse_theme_images(InfoTree root, int type, int state, int num_items = 1)
161 {
162 	BOOST_FOREACH(InfoTree img, root.children_named("image"))
163 	{
164 		parse_theme_image(img, type, state, num_items - 1);
165 	}
166 }
167 
parse_theme_color(InfoTree root,int type,int state,int max_index)168 static void parse_theme_color(InfoTree root, int type, int state, int max_index)
169 {
170 	int index = 0;
171 	root.read_attr_bounded("index", index, 0, max_index);
172 
173 	float red, green, blue;
174 	if (!root.read_attr("red", red) ||
175 		!root.read_attr("green", green) ||
176 		!root.read_attr("blue", blue))
177 		return;
178 
179 	SDL_Color color;
180 	color.r = uint8(PIN(255 * red + 0.5, 0, 255));
181 	color.g = uint8(PIN(255 * green + 0.5, 0, 255));
182 	color.b = uint8(PIN(255 * blue + 0.5, 0, 255));
183 	color.a = 0xff;
184 	dialog_theme[type].states[state].colors[index] = color;
185 }
186 
parse_theme_colors(InfoTree root,int type,int state,int num_items=1)187 static void parse_theme_colors(InfoTree root, int type, int state, int num_items = 1)
188 {
189 	BOOST_FOREACH(InfoTree color, root.children_named("color"))
190 	{
191 		parse_theme_color(color, type, state, num_items - 1);
192 	}
193 }
194 
parse_theme_font(InfoTree root,int type)195 static void parse_theme_font(InfoTree root, int type)
196 {
197 	int size = -1;
198 	if (!root.read_attr("size", size))
199 		return;
200 
201 	int id = kFontIDMonaco;
202 	bool have_id = root.read_attr("id", id);
203 	std::string path;
204 	bool have_path = root.read_attr("file", path);
205 	if (!have_id && !have_path)
206 		return;
207 
208 	dialog_theme[type].font_spec.font = id;
209 	dialog_theme[type].font_spec.size = size;
210 	dialog_theme[type].font_spec.normal = path;
211 
212 	dialog_theme[type].font_spec.style = 0;
213 	root.read_attr("style", dialog_theme[type].font_spec.style);
214 	dialog_theme[type].font_spec.adjust_height = 0;
215 	root.read_attr("adjust_height", dialog_theme[type].font_spec.adjust_height);
216 	root.read_attr("bold_file", dialog_theme[type].font_spec.bold);
217 	root.read_attr("italic_file", dialog_theme[type].font_spec.oblique);
218 	root.read_attr("bold_italic_file", dialog_theme[type].font_spec.bold_oblique);
219 
220 	dialog_theme[type].font_set = true;
221 }
222 
parse_theme_fonts(InfoTree root,int type)223 static void parse_theme_fonts(InfoTree root, int type)
224 {
225 	BOOST_FOREACH(InfoTree child, root.children_named("font"))
226 		parse_theme_font(child, type);
227 }
228 
start_parse_widget(int theme_widget)229 void start_parse_widget(int theme_widget)
230 {
231 	if (dialog_theme.find(theme_widget) != dialog_theme.end())
232 		dialog_theme[theme_widget].states.clear();
233 }
234 
parse_default(InfoTree root)235 static void parse_default(InfoTree root)
236 {
237 	start_parse_widget(DEFAULT_WIDGET);
238 	parse_theme_colors(root, DEFAULT_WIDGET, DEFAULT_STATE, 3);
239 	parse_theme_fonts(root, DEFAULT_WIDGET);
240 }
241 
parse_frame(InfoTree root)242 static void parse_frame(InfoTree root)
243 {
244 	root.read_attr("top", dialog_theme[DIALOG_FRAME].spaces[T_SPACE]);
245 	root.read_attr("bottom", dialog_theme[DIALOG_FRAME].spaces[B_SPACE]);
246 	root.read_attr("left", dialog_theme[DIALOG_FRAME].spaces[L_SPACE]);
247 	root.read_attr("right", dialog_theme[DIALOG_FRAME].spaces[R_SPACE]);
248 
249 	parse_theme_colors(root, DIALOG_FRAME, DEFAULT_STATE, 3);
250 	parse_theme_images(root, DIALOG_FRAME, DEFAULT_STATE, 8);
251 }
252 
parse_title(InfoTree root)253 static void parse_title(InfoTree root)
254 {
255 	parse_theme_colors(root, TITLE_WIDGET, DEFAULT_STATE);
256 	parse_theme_fonts(root, TITLE_WIDGET);
257 }
258 
parse_spacer(InfoTree root)259 static void parse_spacer(InfoTree root)
260 {
261 	root.read_attr("height", dialog_theme[SPACER_WIDGET].spaces[0]);
262 }
263 
parse_button(InfoTree root)264 static void parse_button(InfoTree root)
265 {
266 	start_parse_widget(BUTTON_WIDGET);
267 	root.read_attr("top", dialog_theme[BUTTON_WIDGET].spaces[BUTTON_T_SPACE]);
268 	root.read_attr("left", dialog_theme[BUTTON_WIDGET].spaces[BUTTON_L_SPACE]);
269 	root.read_attr("right", dialog_theme[BUTTON_WIDGET].spaces[BUTTON_R_SPACE]);
270 	root.read_attr("height", dialog_theme[BUTTON_WIDGET].spaces[BUTTON_HEIGHT]);
271 
272 	parse_theme_colors(root, BUTTON_WIDGET, DEFAULT_STATE, 3);
273 	parse_theme_fonts(root, BUTTON_WIDGET);
274 	parse_theme_images(root, BUTTON_WIDGET, DEFAULT_STATE, 3);
275 
276 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
277 	{
278 		parse_theme_colors(child, BUTTON_WIDGET, ACTIVE_STATE, 3);
279 		parse_theme_images(child, BUTTON_WIDGET, ACTIVE_STATE, 3);
280 	}
281 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
282 	{
283 		parse_theme_colors(child, BUTTON_WIDGET, DISABLED_STATE, 3);
284 		parse_theme_images(child, BUTTON_WIDGET, DISABLED_STATE, 3);
285 	}
286 	BOOST_FOREACH(InfoTree child, root.children_named("pressed"))
287 	{
288 		parse_theme_colors(child, BUTTON_WIDGET, PRESSED_STATE, 3);
289 		parse_theme_images(child, BUTTON_WIDGET, PRESSED_STATE, 3);
290 	}
291 }
292 
parse_tiny_button(InfoTree root)293 static void parse_tiny_button(InfoTree root)
294 {
295 	start_parse_widget(TINY_BUTTON);
296 	root.read_attr("top", dialog_theme[TINY_BUTTON].spaces[BUTTON_T_SPACE]);
297 	root.read_attr("left", dialog_theme[TINY_BUTTON].spaces[BUTTON_L_SPACE]);
298 	root.read_attr("right", dialog_theme[TINY_BUTTON].spaces[BUTTON_R_SPACE]);
299 	root.read_attr("height", dialog_theme[TINY_BUTTON].spaces[BUTTON_HEIGHT]);
300 
301 	parse_theme_colors(root, TINY_BUTTON, DEFAULT_STATE, 3);
302 	parse_theme_fonts(root, TINY_BUTTON);
303 	parse_theme_images(root, TINY_BUTTON, DEFAULT_STATE, 3);
304 
305 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
306 	{
307 		parse_theme_colors(child, TINY_BUTTON, ACTIVE_STATE, 3);
308 		parse_theme_images(child, TINY_BUTTON, ACTIVE_STATE, 3);
309 	}
310 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
311 	{
312 		parse_theme_colors(child, TINY_BUTTON, DISABLED_STATE, 3);
313 		parse_theme_images(child, TINY_BUTTON, DISABLED_STATE, 3);
314 	}
315 	BOOST_FOREACH(InfoTree child, root.children_named("pressed"))
316 	{
317 		parse_theme_colors(child, TINY_BUTTON, PRESSED_STATE, 3);
318 		parse_theme_images(child, TINY_BUTTON, PRESSED_STATE, 3);
319 	}
320 }
321 
parse_hyperlink(InfoTree root)322 static void parse_hyperlink(InfoTree root)
323 {
324 	start_parse_widget(HYPERLINK_WIDGET);
325 
326 	parse_theme_colors(root, HYPERLINK_WIDGET, DEFAULT_STATE, 3);
327 	parse_theme_fonts(root, HYPERLINK_WIDGET);
328 
329 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
330 	{
331 		parse_theme_colors(child, HYPERLINK_WIDGET, ACTIVE_STATE, 3);
332 	}
333 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
334 	{
335 		parse_theme_colors(child, HYPERLINK_WIDGET, DISABLED_STATE, 3);
336 	}
337 	BOOST_FOREACH(InfoTree child, root.children_named("pressed"))
338 	{
339 		parse_theme_colors(child, HYPERLINK_WIDGET, PRESSED_STATE, 3);
340 	}
341 }
342 
parse_item(InfoTree root)343 static void parse_item(InfoTree root)
344 {
345 	start_parse_widget(ITEM_WIDGET);
346 	root.read_attr("space", dialog_theme[ITEM_WIDGET].spaces[0]);
347 
348 	parse_theme_colors(root, ITEM_WIDGET, DEFAULT_STATE);
349 	parse_theme_fonts(root, ITEM_WIDGET);
350 
351 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
352 	{
353 		parse_theme_colors(child, ITEM_WIDGET, ACTIVE_STATE);
354 	}
355 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
356 	{
357 		parse_theme_colors(child, ITEM_WIDGET, DISABLED_STATE);
358 	}
359 }
360 
parse_label(InfoTree root)361 static void parse_label(InfoTree root)
362 {
363 	start_parse_widget(LABEL_WIDGET);
364 
365 	parse_theme_colors(root, LABEL_WIDGET, DEFAULT_STATE);
366 	parse_theme_fonts(root, LABEL_WIDGET);
367 
368 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
369 	{
370 		parse_theme_colors(child, LABEL_WIDGET, ACTIVE_STATE);
371 	}
372 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
373 	{
374 		parse_theme_colors(child, LABEL_WIDGET, DISABLED_STATE);
375 	}
376 }
377 
parse_message(InfoTree root)378 static void parse_message(InfoTree root)
379 {
380 	start_parse_widget(MESSAGE_WIDGET);
381 	parse_theme_colors(root, MESSAGE_WIDGET, DEFAULT_STATE);
382 	parse_theme_fonts(root, MESSAGE_WIDGET);
383 }
384 
parse_text_entry(InfoTree root)385 static void parse_text_entry(InfoTree root)
386 {
387 	start_parse_widget(TEXT_ENTRY_WIDGET);
388 
389 	parse_theme_colors(root, TEXT_ENTRY_WIDGET, DEFAULT_STATE);
390 	parse_theme_fonts(root, TEXT_ENTRY_WIDGET);
391 
392 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
393 	{
394 		parse_theme_colors(child, TEXT_ENTRY_WIDGET, ACTIVE_STATE);
395 	}
396 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
397 	{
398 		parse_theme_colors(child, TEXT_ENTRY_WIDGET, DISABLED_STATE);
399 	}
400 	BOOST_FOREACH(InfoTree child, root.children_named("cursor"))
401 	{
402 		parse_theme_colors(child, TEXT_ENTRY_WIDGET, CURSOR_STATE);
403 	}
404 }
405 
parse_chat_entry(InfoTree root)406 static void parse_chat_entry(InfoTree root)
407 {
408 	start_parse_widget(CHAT_ENTRY);
409 	root.read_attr("name_width", dialog_theme[CHAT_ENTRY].spaces[0]);
410 
411 	parse_theme_colors(root, CHAT_ENTRY, DEFAULT_STATE);
412 	parse_theme_fonts(root, CHAT_ENTRY);
413 }
414 
parse_list(InfoTree root)415 static void parse_list(InfoTree root)
416 {
417 	start_parse_widget(LIST_WIDGET);
418 	root.read_attr("top", dialog_theme[LIST_WIDGET].spaces[T_SPACE]);
419 	root.read_attr("bottom", dialog_theme[LIST_WIDGET].spaces[B_SPACE]);
420 	root.read_attr("left", dialog_theme[LIST_WIDGET].spaces[L_SPACE]);
421 	root.read_attr("right", dialog_theme[LIST_WIDGET].spaces[R_SPACE]);
422 
423 	parse_theme_colors(root, LIST_WIDGET, DEFAULT_STATE, 3);
424 	parse_theme_images(root, LIST_WIDGET, DEFAULT_STATE, 8);
425 
426 	BOOST_FOREACH(InfoTree child, root.children_named("trough"))
427 	{
428 		child.read_attr("top", dialog_theme[LIST_WIDGET].spaces[TROUGH_T_SPACE]);
429 		child.read_attr("bottom", dialog_theme[LIST_WIDGET].spaces[TROUGH_B_SPACE]);
430 		child.read_attr("right", dialog_theme[LIST_WIDGET].spaces[TROUGH_R_SPACE]);
431 		child.read_attr("width", dialog_theme[LIST_WIDGET].spaces[TROUGH_WIDTH]);
432 	}
433 	BOOST_FOREACH(InfoTree child, root.children_named("thumb"))
434 	{
435 		start_parse_widget(LIST_THUMB);
436 		parse_theme_colors(child, LIST_THUMB, DEFAULT_STATE, 3);
437 		parse_theme_images(child, LIST_THUMB, DEFAULT_STATE, 5);
438 	}
439 }
440 
parse_slider(InfoTree root)441 static void parse_slider(InfoTree root)
442 {
443 	start_parse_widget(SLIDER_WIDGET);
444 	root.read_attr("top", dialog_theme[SLIDER_WIDGET].spaces[SLIDER_T_SPACE]);
445 	root.read_attr("left", dialog_theme[SLIDER_WIDGET].spaces[SLIDER_L_SPACE]);
446 	root.read_attr("right", dialog_theme[SLIDER_WIDGET].spaces[SLIDER_R_SPACE]);
447 
448 	parse_theme_colors(root, SLIDER_WIDGET, DEFAULT_STATE, 3);
449 	parse_theme_images(root, SLIDER_WIDGET, DEFAULT_STATE, 3);
450 
451 	BOOST_FOREACH(InfoTree child, root.children_named("thumb"))
452 	{
453 		start_parse_widget(SLIDER_THUMB);
454 		parse_theme_colors(child, SLIDER_THUMB, DEFAULT_STATE, 3);
455 		parse_theme_images(child, SLIDER_THUMB, DEFAULT_STATE);
456 	}
457 }
458 
parse_checkbox(InfoTree root)459 static void parse_checkbox(InfoTree root)
460 {
461 	start_parse_widget(CHECKBOX);
462 	root.read_attr("top", dialog_theme[CHECKBOX].spaces[BUTTON_T_SPACE]);
463 	root.read_attr("height", dialog_theme[CHECKBOX].spaces[BUTTON_HEIGHT]);
464 
465 	parse_theme_fonts(root, CHECKBOX);
466 	parse_theme_images(root, CHECKBOX, DEFAULT_STATE, 2);
467 
468 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
469 	{
470 		parse_theme_images(child, CHECKBOX, ACTIVE_STATE, 2);
471 	}
472 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
473 	{
474 		parse_theme_images(child, CHECKBOX, DISABLED_STATE, 2);
475 	}
476 }
477 
parse_tab(InfoTree root)478 static void parse_tab(InfoTree root)
479 {
480 	start_parse_widget(TAB_WIDGET);
481 	root.read_attr("top", dialog_theme[TAB_WIDGET].spaces[BUTTON_T_SPACE]);
482 	root.read_attr("left", dialog_theme[TAB_WIDGET].spaces[BUTTON_L_SPACE]);
483 	root.read_attr("right", dialog_theme[TAB_WIDGET].spaces[BUTTON_R_SPACE]);
484 	root.read_attr("height", dialog_theme[TAB_WIDGET].spaces[BUTTON_HEIGHT]);
485 	root.read_attr("inner_left", dialog_theme[TAB_WIDGET].spaces[TAB_LC_SPACE]);
486 	root.read_attr("inner_right", dialog_theme[TAB_WIDGET].spaces[TAB_RC_SPACE]);
487 
488 	parse_theme_colors(root, TAB_WIDGET, DEFAULT_STATE, 3);
489 	parse_theme_fonts(root, TAB_WIDGET);
490 	parse_theme_images(root, TAB_WIDGET, DEFAULT_STATE, 5);
491 
492 	BOOST_FOREACH(InfoTree child, root.children_named("active"))
493 	{
494 		parse_theme_colors(child, TAB_WIDGET, ACTIVE_STATE, 3);
495 		parse_theme_images(child, TAB_WIDGET, ACTIVE_STATE, 5);
496 	}
497 	BOOST_FOREACH(InfoTree child, root.children_named("disabled"))
498 	{
499 		parse_theme_colors(child, TAB_WIDGET, DISABLED_STATE, 3);
500 		parse_theme_images(child, TAB_WIDGET, DISABLED_STATE, 5);
501 	}
502 	BOOST_FOREACH(InfoTree child, root.children_named("pressed"))
503 	{
504 		parse_theme_colors(child, TAB_WIDGET, PRESSED_STATE, 3);
505 		parse_theme_images(child, TAB_WIDGET, PRESSED_STATE, 5);
506 	}
507 }
508 
parse_metaserver(InfoTree root)509 static void parse_metaserver(InfoTree root)
510 {
511 	start_parse_widget(METASERVER_WIDGETS);
512 	BOOST_FOREACH(InfoTree child, root.children_named("games"))
513 	{
514 		start_parse_widget(METASERVER_GAMES);
515 		child.read_attr("entries", dialog_theme[METASERVER_GAMES].spaces[w_games_in_room::GAME_ENTRIES]);
516 		child.read_attr("spacing", dialog_theme[METASERVER_GAMES].spaces[w_games_in_room::GAME_SPACING]);
517 
518 		parse_theme_colors(child, METASERVER_GAMES, w_games_in_room::GAME, 3);
519 		parse_theme_fonts(child, METASERVER_GAMES);
520 
521 		BOOST_FOREACH(InfoTree gtype, child.children_named("selected"))
522 		{
523 			parse_theme_colors(gtype, METASERVER_GAMES, w_games_in_room::SELECTED_GAME, 3);
524 		}
525 		BOOST_FOREACH(InfoTree gtype, child.children_named("running"))
526 		{
527 			parse_theme_colors(gtype, METASERVER_GAMES, w_games_in_room::RUNNING_GAME, 3);
528 			BOOST_FOREACH(InfoTree stype, gtype.children_named("selected"))
529 			{
530 				parse_theme_colors(stype, METASERVER_GAMES, w_games_in_room::SELECTED_RUNNING_GAME, 3);
531 			}
532 		}
533 		BOOST_FOREACH(InfoTree gtype, child.children_named("incompatible"))
534 		{
535 			parse_theme_colors(gtype, METASERVER_GAMES, w_games_in_room::INCOMPATIBLE_GAME, 3);
536 			BOOST_FOREACH(InfoTree stype, gtype.children_named("selected"))
537 			{
538 				parse_theme_colors(stype, METASERVER_GAMES, w_games_in_room::SELECTED_INCOMPATIBLE_GAME, 3);
539 			}
540 		}
541 	}
542 	BOOST_FOREACH(InfoTree child, root.children_named("players"))
543 	{
544 		start_parse_widget(METASERVER_PLAYERS);
545 		child.read_attr("lines", dialog_theme[METASERVER_PLAYERS].spaces[0]);
546 
547 		parse_theme_fonts(child, METASERVER_PLAYERS);
548 	}
549 }
550 
parse_theme_file(FileSpecifier & theme_mml)551 static bool parse_theme_file(FileSpecifier& theme_mml)
552 {
553 	if (!theme_mml.Exists())
554 		return false;
555 	bool success = false;
556 	try {
557 		InfoTree root = InfoTree::load_xml(theme_mml).get_child("marathon.theme");
558 
559 		BOOST_FOREACH(InfoTree child, root.children_named("default"))
560 			parse_default(child);
561 		BOOST_FOREACH(InfoTree child, root.children_named("frame"))
562 			parse_frame(child);
563 		BOOST_FOREACH(InfoTree child, root.children_named("title"))
564 			parse_title(child);
565 		BOOST_FOREACH(InfoTree child, root.children_named("spacer"))
566 			parse_spacer(child);
567 		BOOST_FOREACH(InfoTree child, root.children_named("button"))
568 			parse_button(child);
569 		BOOST_FOREACH(InfoTree child, root.children_named("tiny_button"))
570 			parse_tiny_button(child);
571 		BOOST_FOREACH(InfoTree child, root.children_named("hyperlink"))
572 			parse_hyperlink(child);
573 		BOOST_FOREACH(InfoTree child, root.children_named("item"))
574 			parse_item(child);
575 		BOOST_FOREACH(InfoTree child, root.children_named("label"))
576 			parse_label(child);
577 		BOOST_FOREACH(InfoTree child, root.children_named("message"))
578 			parse_message(child);
579 		BOOST_FOREACH(InfoTree child, root.children_named("text_entry"))
580 			parse_text_entry(child);
581 		BOOST_FOREACH(InfoTree child, root.children_named("chat_entry"))
582 			parse_chat_entry(child);
583 		BOOST_FOREACH(InfoTree child, root.children_named("list"))
584 			parse_list(child);
585 		BOOST_FOREACH(InfoTree child, root.children_named("slider"))
586 			parse_slider(child);
587 		BOOST_FOREACH(InfoTree child, root.children_named("checkbox"))
588 			parse_checkbox(child);
589 		BOOST_FOREACH(InfoTree child, root.children_named("tab"))
590 			parse_tab(child);
591 		BOOST_FOREACH(InfoTree child, root.children_named("metaserver"))
592 			parse_metaserver(child);
593 
594 		success = true;
595 	} catch (InfoTree::parse_error e) {
596 		logError("error parsing %s: %s", theme_mml.GetPath(), e.what());
597 	} catch (InfoTree::path_error e) {
598 		logError("error parsing %s: %s", theme_mml.GetPath(), e.what());
599 	} catch (InfoTree::data_error e) {
600 		logError("error parsing %s: %s", theme_mml.GetPath(), e.what());
601 	} catch (InfoTree::unexpected_error e) {
602 		logError("error parsing %s: %s", theme_mml.GetPath(), e.what());
603 	}
604 	return success;
605 }
606 
607 /*
608  *  Load theme
609  */
610 
611 extern vector<DirectorySpecifier> data_search_path;
612 
load_dialog_theme(bool force_reload)613 bool load_dialog_theme(bool force_reload)
614 {
615 	FileSpecifier new_theme;
616 	const Plugin* theme_plugin = Plugins::instance()->find_theme();
617 	if (theme_plugin)
618 	{
619 		new_theme = theme_plugin->directory + theme_plugin->theme;
620 	}
621 	else
622 	{
623 		get_default_theme_spec(new_theme);
624 	}
625 	if (force_reload || new_theme != theme_path)
626 	{
627 		return load_theme(new_theme);
628 	}
629 	return false;
630 }
631 
load_theme(FileSpecifier & theme)632 bool load_theme(FileSpecifier &theme)
633 {
634 	// Unload previous theme
635 	unload_theme();
636 
637 	// Set defaults, the theme overrides these
638 	set_theme_defaults();
639 
640 	// Parse theme MML script
641 	FileSpecifier theme_mml = theme + "theme2.mml";
642 	bool success = parse_theme_file(theme_mml);
643 	if (success)
644 	{
645 		theme_path = theme;
646 
647 		// Open resource file
648 		FileSpecifier theme_rsrc = theme + "resources";
649 		theme_rsrc.Open(theme_resources);
650 	}
651 	clear_game_error();
652 
653 	// Load fonts
654 	if (success)
655 		data_search_path.insert(data_search_path.begin(), theme);
656 	for (std::map<int, theme_widget>::iterator i = dialog_theme.begin(); i != dialog_theme.end(); ++i)
657 	{
658 		if (i->second.font_set) {
659 			i->second.font = load_font(i->second.font_spec);
660 			if (!i->second.font) {
661 				TextSpec fallback_spec = { -1, i->second.font_spec.style, i->second.font_spec.size, 0, "mono" };
662 				i->second.font = load_font(fallback_spec);
663 			}
664 		} else
665 			i->second.font = 0;
666 	}
667 	if (success)
668 		data_search_path.erase(data_search_path.begin());
669 
670 	// Load images
671 	for (std::map<int, theme_widget>::iterator i = dialog_theme.begin(); i != dialog_theme.end(); ++i)
672 	{
673 		for (std::map<int, theme_state>::iterator j = i->second.states.begin(); j != i->second.states.end(); ++j)
674 		{
675 			for (std::map<int, dialog_image_spec_type>::iterator k = j->second.image_specs.begin(); k != j->second.image_specs.end(); ++k)
676 			{
677 				FileSpecifier file = theme + k->second.name;
678 				OpenedFile of;
679 				if (file.Open(of))
680 				{
681 					SDL_Surface *s = SDL_LoadBMP_RW(of.GetRWops(), 0);
682 					if (s)
683 						SDL_SetColorKey(s, SDL_TRUE, SDL_MapRGB(s->format, 0x00, 0xff, 0xff));
684 					j->second.images[k->first] = s;
685 				}
686 			}
687 		}
688 	}
689 
690 	return success;
691 }
692 
693 
694 /*
695  *  Set theme default values
696  */
697 
make_color(uint8 r,uint8 g,uint8 b)698 static inline SDL_Color make_color(uint8 r, uint8 g, uint8 b)
699 {
700 	SDL_Color c;
701 	c.r = r;
702 	c.g = g;
703 	c.b = b;
704 	c.a = 0xff;
705 	return c;
706 }
707 
set_theme_defaults(void)708 static void set_theme_defaults(void)
709 {
710 	// new theme defaults
711 	static const TextSpec default_font_spec = {kFontIDMonaco, styleNormal, 12, 0, "mono"};
712 	dialog_theme[DEFAULT_WIDGET].font_spec = default_font_spec;
713 	dialog_theme[DEFAULT_WIDGET].font_set = true;
714 
715 	dialog_theme[DEFAULT_WIDGET].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
716 	dialog_theme[DEFAULT_WIDGET].states[DEFAULT_STATE].colors[BACKGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
717 	dialog_theme[DEFAULT_WIDGET].states[DEFAULT_STATE].colors[FRAME_COLOR] = make_color(0x3f, 0x3f, 0x3f);
718 
719 	dialog_theme[TITLE_WIDGET].font_spec = dialog_theme[DEFAULT_WIDGET].font_spec;
720 	dialog_theme[TITLE_WIDGET].font_spec.size = 24;
721 	dialog_theme[TITLE_WIDGET].font_set = true;
722 
723 	dialog_theme[DIALOG_FRAME].spaces[T_SPACE] = 8;
724 	dialog_theme[DIALOG_FRAME].spaces[L_SPACE] = 8;
725 	dialog_theme[DIALOG_FRAME].spaces[R_SPACE] = 8;
726 	dialog_theme[DIALOG_FRAME].spaces[B_SPACE] = 8;
727 
728 	dialog_theme[SPACER_WIDGET].spaces[0] = 8;
729 
730 	dialog_theme[LABEL_WIDGET].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0xff, 0x0);
731 	dialog_theme[LABEL_WIDGET].states[DISABLED_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x9b, 0x0);
732 	dialog_theme[LABEL_WIDGET].states[ACTIVE_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
733 
734 	dialog_theme[ITEM_WIDGET].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0xff, 0x0);
735 	dialog_theme[ITEM_WIDGET].states[ACTIVE_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
736 	dialog_theme[ITEM_WIDGET].states[DISABLED_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x9b, 0x0);
737 	dialog_theme[ITEM_WIDGET].spaces[0] = 16;
738 
739 	dialog_theme[TEXT_ENTRY_WIDGET].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0xff, 0x0);
740 	dialog_theme[TEXT_ENTRY_WIDGET].states[ACTIVE_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
741 	dialog_theme[TEXT_ENTRY_WIDGET].states[CURSOR_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
742 	dialog_theme[TEXT_ENTRY_WIDGET].states[DISABLED_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x9b, 0x0);
743 
744 	dialog_theme[BUTTON_WIDGET].spaces[BUTTON_T_SPACE] = 4;
745 	dialog_theme[BUTTON_WIDGET].spaces[BUTTON_L_SPACE] = 4;
746 	dialog_theme[BUTTON_WIDGET].spaces[BUTTON_R_SPACE] = 4;
747 	dialog_theme[BUTTON_WIDGET].spaces[BUTTON_HEIGHT] = 24;
748 	dialog_theme[BUTTON_WIDGET].states[DEFAULT_STATE].colors[BACKGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
749 	dialog_theme[BUTTON_WIDGET].states[ACTIVE_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
750 	dialog_theme[BUTTON_WIDGET].states[DISABLED_STATE].colors[FOREGROUND_COLOR] = make_color(0x7f, 0x7f, 0x7f);
751 
752 	dialog_theme[BUTTON_WIDGET].states[PRESSED_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
753 	dialog_theme[BUTTON_WIDGET].states[PRESSED_STATE].colors[BACKGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
754 
755 	dialog_theme[BUTTON_WIDGET].font_spec = dialog_theme[DEFAULT_WIDGET].font_spec;
756 	dialog_theme[BUTTON_WIDGET].font_spec.size = 14;
757 	dialog_theme[BUTTON_WIDGET].font_set = true;
758 
759 	dialog_theme[SLIDER_WIDGET].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
760 	dialog_theme[SLIDER_THUMB].states[DEFAULT_STATE].colors[FRAME_COLOR] = make_color(0x0, 0xff, 0x0);
761 	dialog_theme[SLIDER_THUMB].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
762 
763 	dialog_theme[LIST_THUMB].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
764 	dialog_theme[LIST_THUMB].states[DEFAULT_STATE].colors[FRAME_COLOR] = make_color(0x0, 0xff, 0x0);
765 	dialog_theme[LIST_WIDGET].spaces[T_SPACE] = 2;
766 	dialog_theme[LIST_WIDGET].spaces[L_SPACE] = 2;
767 	dialog_theme[LIST_WIDGET].spaces[R_SPACE] = 14;
768 	dialog_theme[LIST_WIDGET].spaces[B_SPACE] = 2;
769 	dialog_theme[LIST_WIDGET].spaces[TROUGH_R_SPACE] = 12;
770 	dialog_theme[LIST_WIDGET].spaces[TROUGH_WIDTH] = 12;
771 
772 	dialog_theme[TINY_BUTTON].states[DEFAULT_STATE].colors[BACKGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
773 	dialog_theme[TINY_BUTTON].states[ACTIVE_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
774 	dialog_theme[TINY_BUTTON].states[DISABLED_STATE].colors[FOREGROUND_COLOR] = make_color(0x7f, 0x7f, 0x7f);
775 	dialog_theme[TINY_BUTTON].spaces[BUTTON_T_SPACE] = 2;
776 	dialog_theme[TINY_BUTTON].spaces[BUTTON_L_SPACE] = 2;
777 	dialog_theme[TINY_BUTTON].spaces[BUTTON_R_SPACE] = 2;
778 	dialog_theme[TINY_BUTTON].spaces[BUTTON_HEIGHT] = 18;
779 	dialog_theme[TINY_BUTTON].states[PRESSED_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
780 	dialog_theme[TINY_BUTTON].states[PRESSED_STATE].colors[BACKGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
781 
782 	dialog_theme[HYPERLINK_WIDGET].font_spec = dialog_theme[DEFAULT_WIDGET].font_spec;
783 	dialog_theme[HYPERLINK_WIDGET].font_spec.style = 4;
784 	dialog_theme[HYPERLINK_WIDGET].font_set = true;
785 	dialog_theme[HYPERLINK_WIDGET].states[DEFAULT_STATE].colors[FOREGROUND_COLOR] = make_color(0x7f, 0x7f, 0xff);
786 	dialog_theme[HYPERLINK_WIDGET].states[ACTIVE_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
787 	dialog_theme[HYPERLINK_WIDGET].states[DISABLED_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x9b, 0x0);
788 	dialog_theme[HYPERLINK_WIDGET].states[PRESSED_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
789 
790 	dialog_theme[CHECKBOX].font_spec = dialog_theme[DEFAULT_WIDGET].font_spec;
791 	dialog_theme[CHECKBOX].font_spec.size = 22;
792 	dialog_theme[CHECKBOX].font_set = true;
793 	dialog_theme[CHECKBOX].spaces[BUTTON_T_SPACE] = 13;
794 	dialog_theme[CHECKBOX].spaces[BUTTON_HEIGHT] = 15;
795 
796 	dialog_theme[TAB_WIDGET].spaces[BUTTON_T_SPACE] = 4;
797 	dialog_theme[TAB_WIDGET].spaces[BUTTON_L_SPACE] = 4;
798 	dialog_theme[TAB_WIDGET].spaces[BUTTON_R_SPACE] = 4;
799 	dialog_theme[TAB_WIDGET].spaces[BUTTON_HEIGHT] = 24;
800 	dialog_theme[TAB_WIDGET].spaces[TAB_LC_SPACE] = 4;
801 	dialog_theme[TAB_WIDGET].spaces[TAB_RC_SPACE] = 4;
802 	dialog_theme[TAB_WIDGET].font_spec = dialog_theme[DEFAULT_WIDGET].font_spec;
803 	dialog_theme[TAB_WIDGET].font_spec.size = 14;
804 	dialog_theme[TAB_WIDGET].font_set = true;
805 	dialog_theme[TAB_WIDGET].states[DEFAULT_STATE].colors[BACKGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
806 	dialog_theme[TAB_WIDGET].states[ACTIVE_STATE].colors[FOREGROUND_COLOR] = make_color(0xff, 0xe7, 0x0);
807 	dialog_theme[TAB_WIDGET].states[PRESSED_STATE].colors[FOREGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
808 	dialog_theme[TAB_WIDGET].states[PRESSED_STATE].colors[BACKGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
809 
810 	dialog_theme[CHAT_ENTRY].spaces[0] = 100;
811 
812 	dialog_theme[METASERVER_PLAYERS].spaces[0] = 8;
813 
814 	dialog_theme[METASERVER_GAMES].spaces[w_games_in_room::GAME_SPACING] = 4;
815 	dialog_theme[METASERVER_GAMES].spaces[w_games_in_room::GAME_ENTRIES] = 3;
816 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::GAME].colors[FOREGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
817 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::INCOMPATIBLE_GAME].colors[FOREGROUND_COLOR] = make_color(0x7f, 0, 0);
818 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::RUNNING_GAME].colors[FOREGROUND_COLOR] = make_color(0x7f, 0x7f, 0x7f);
819 
820 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::SELECTED_GAME].colors[FOREGROUND_COLOR] = make_color(0x0, 0x0, 0x0);
821 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::SELECTED_GAME].colors[BACKGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
822 
823 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::SELECTED_INCOMPATIBLE_GAME].colors[FOREGROUND_COLOR] = make_color(0x7f, 0, 0);
824 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::SELECTED_INCOMPATIBLE_GAME].colors[BACKGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
825 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::SELECTED_RUNNING_GAME].colors[FOREGROUND_COLOR] = make_color(0x7f, 0x7f, 0x7f);
826 	dialog_theme[METASERVER_GAMES].states[w_games_in_room::SELECTED_RUNNING_GAME].colors[BACKGROUND_COLOR] = make_color(0xff, 0xff, 0xff);
827 
828 }
829 
830 /*
831  *  Unload theme
832  */
833 
unload_theme(void)834 static void unload_theme(void)
835 {
836 	// Unload fonts
837 	for (std::map<int, theme_widget>::iterator i = dialog_theme.begin(); i != dialog_theme.end(); ++i)
838 	{
839 		if (i->second.font)
840 		{
841 			unload_font(i->second.font);
842 			i->second.font = 0;
843 		}
844 	}
845 	// Free surfaces
846 
847 	for (std::map<int, theme_widget>::iterator i = dialog_theme.begin(); i != dialog_theme.end(); ++i)
848 	{
849 		for (std::map<int, theme_state>::iterator j = i->second.states.begin(); j != i->second.states.end(); ++j)
850 		{
851 			for (std::map<int, SDL_Surface*>::iterator k = j->second.images.begin(); k != j->second.images.end(); ++k)
852 			{
853 				if (k->second)
854 				{
855 					SDL_FreeSurface(k->second);
856 					k->second = 0;
857 				}
858 			}
859 		}
860 	}
861 
862 	dialog_theme.clear();
863 	theme_path = FileSpecifier();
864 
865 	// Close resource file
866 	theme_resources.Close();
867 }
868 
869 
870 /*
871  *  Get dialog font/color/image/space from theme
872  */
873 
874 
875 // ZZZ: added this for convenience; taken from w_player_color::draw().
876 // Obviously, this color does not come from the theme.
get_dialog_player_color(size_t colorIndex)877 uint32 get_dialog_player_color(size_t colorIndex) {
878         SDL_Color c;
879         _get_interface_color(PLAYER_COLOR_BASE_INDEX + colorIndex, &c);
880         return SDL_MapRGB(dialog_surface->format, c.r, c.g, c.b);
881 }
882 
get_theme_font(int widget_type,uint16 & style)883 font_info *get_theme_font(int widget_type, uint16 &style)
884 {
885 	std::map<int, theme_widget>::iterator i = dialog_theme.find(widget_type);
886 	if (i != dialog_theme.end() && i->second.font)
887 	{
888 		style = i->second.font_spec.style;
889 		return i->second.font;
890 	}
891 	else
892 	{
893 		i = dialog_theme.find(DEFAULT_WIDGET);
894 		style = i->second.font_spec.style;
895 		return i->second.font;
896 	}
897 }
898 
get_theme_color(int widget_type,int state,int which)899 uint32 get_theme_color(int widget_type, int state, int which)
900 {
901 	SDL_Color c = dialog_theme[DEFAULT_WIDGET].states[DEFAULT_STATE].colors[which];
902 
903 	bool found = false;
904 	std::map<int, theme_widget>::iterator i = dialog_theme.find(widget_type);
905 	if (i != dialog_theme.end())
906 	{
907 		std::map<int, theme_state>::iterator j = i->second.states.find(state);
908 		if (j != i->second.states.end())
909 		{
910 			std::map<int, SDL_Color>::iterator k = j->second.colors.find(which);
911 			if (k != j->second.colors.end())
912 			{
913 				c = k->second;
914 				found = true;
915 			}
916 		}
917 
918 		if (!found)
919 		{
920 			j = i->second.states.find(DEFAULT_STATE);
921 			if (j != i->second.states.end())
922 			{
923 				std::map<int, SDL_Color>::iterator k = j->second.colors.find(which);
924 				if (k != j->second.colors.end())
925 				{
926 					c = k->second;
927 					found = true;
928 				}
929 			}
930 		}
931 	}
932 
933 	return SDL_MapRGB(dialog_surface->format, c.r, c.g, c.b);
934 }
935 
get_theme_image(int widget_type,int state,int which,int width,int height)936 SDL_Surface *get_theme_image(int widget_type, int state, int which, int width, int height)
937 {
938 	SDL_Surface *s = default_image;
939 	bool scale = false;
940 	bool found = false;
941 
942 	std::map<int, theme_widget>::iterator i = dialog_theme.find(widget_type);
943 	if (i != dialog_theme.end())
944 	{
945 		std::map<int, theme_state>::iterator j = i->second.states.find(state);
946 		if (j != i->second.states.end())
947 		{
948 			std::map<int, SDL_Surface*>::iterator k = j->second.images.find(which);
949 			if (k != j->second.images.end())
950 			{
951 				s = k->second;
952 				scale = j->second.image_specs[k->first].scale;
953 				found = true;
954 			}
955 		}
956 
957 		if (!found)
958 		{
959 			j = i->second.states.find(DEFAULT_STATE);
960 			if (j != i->second.states.end())
961 			{
962 				std::map<int, SDL_Surface*>::iterator k = j->second.images.find(which);
963 				if (k != j->second.images.end())
964 				{
965 					s = k->second;
966 					scale = j->second.image_specs[k->first].scale;
967 					found = true;
968 				}
969 			}
970 		}
971 	}
972 
973 	// If no width and height is given, the surface is returned
974 	// as-is and must not be freed by the caller
975 	if (width == 0 && height == 0)
976 	{
977 		return s;
978 	}
979 
980 	// Otherwise, a new tiled/rescaled surface is created which
981 	// must be freed by the caller
982 	int req_width = width ? width : s->w;
983 	if (req_width < 1)
984 		req_width = 1;
985 	int req_height = height ? height : s->h;
986 	if (req_height < 1)
987 		req_height = 1;
988 	SDL_Surface *s2 = scale ? rescale_surface(s, req_width, req_height) : tile_surface(s, req_width, req_height);
989 	SDL_SetColorKey(s2, SDL_TRUE, SDL_MapRGB(s2->format, 0x00, 0xff, 0xff));
990 	return s2;
991 
992 }
993 
use_theme_images(int widget_type)994 bool use_theme_images(int widget_type)
995 {
996 	std::map<int, theme_widget>::iterator i = dialog_theme.find(widget_type);
997 	if (i != dialog_theme.end())
998 	{
999 		std::map<int, theme_state>::iterator j = i->second.states.find(DEFAULT_STATE);
1000 		if (j != i->second.states.end())
1001 		{
1002 			return j->second.image_specs.size();
1003 		}
1004 	}
1005 
1006 	return false;
1007 }
1008 
use_theme_color(int widget_type,int which)1009 bool use_theme_color(int widget_type, int which)
1010 {
1011 	std::map<int, theme_widget>::iterator i = dialog_theme.find(widget_type);
1012 	if (i != dialog_theme.end())
1013 	{
1014 		std::map<int, theme_state>::iterator j = i->second.states.find(DEFAULT_STATE);
1015 		if (j != i->second.states.end())
1016 		{
1017 			std::map<int, SDL_Color>::iterator k = j->second.colors.find(which);
1018 			if (k != j->second.colors.end())
1019 			{
1020 				return true;
1021 			}
1022 		}
1023 	}
1024 	return false;
1025 }
1026 
get_theme_space(int widget_type,int which)1027 int get_theme_space(int widget_type, int which)
1028 {
1029 	std::map<int, theme_widget>::iterator i = dialog_theme.find(widget_type);
1030 	if (i != dialog_theme.end())
1031 	{
1032 		std::map<int, int>::iterator j = i->second.spaces.find(which);
1033 		if (j != i->second.spaces.end())
1034 		{
1035 			return j->second;
1036 		}
1037 	}
1038 
1039 	return 0;
1040 }
1041 
1042 
1043 /*
1044  *  Play dialog sound
1045  */
1046 
1047 int16 dialog_sound_definitions[] = {
1048 	_snd_pattern_buffer,
1049 	_snd_pattern_buffer,
1050 	_snd_defender_hit,
1051 	_snd_spht_door_obstructed,
1052 	_snd_major_fusion_charged,
1053 	_snd_computer_interface_page,
1054 	_snd_computer_interface_page,
1055 	_snd_hummer_attack,
1056 	_snd_compiler_death
1057 };
1058 
1059 int16* original_dialog_sound_definitions = NULL;
1060 
number_of_dialog_sounds()1061 int number_of_dialog_sounds() { return NUMBER_OF_DIALOG_SOUNDS; }
1062 
play_dialog_sound(int which)1063 void play_dialog_sound(int which)
1064 {
1065 	if (dialog_sound_definitions[which] != NONE)
1066 	{
1067 		SoundManager::instance()->PlaySound(dialog_sound_definitions[which], 0, NONE);
1068 	}
1069 }
1070 
~widget_placer()1071 widget_placer::~widget_placer()
1072 {
1073 	for (std::vector<placeable *>::iterator it = m_owned.begin(); it != m_owned.end(); it++)
1074 	{
1075 		delete (*it);
1076 	}
1077 }
1078 
add(placeable * p,bool assume_ownership)1079 void table_placer::add(placeable *p, bool assume_ownership)
1080 {
1081 	if (m_add == 0)
1082 	{
1083 		m_table.resize(m_table.size() + 1);
1084 		m_table[m_table.size() - 1].resize(m_columns);
1085 	}
1086 	m_table[m_table.size() - 1][m_add++] = p;
1087 	if (m_add == m_columns) m_add = 0;
1088 
1089 	if (assume_ownership) this->assume_ownership(p);
1090 }
1091 
add_row(placeable * p,bool assume_ownership)1092 void table_placer::add_row(placeable *p, bool assume_ownership)
1093 {
1094 	assert(m_add == 0);
1095 	m_table.resize(m_table.size() + 1);
1096 	m_table[m_table.size() - 1].resize(1);
1097 
1098 	m_table[m_table.size() - 1][0] = p;
1099 
1100 	if (assume_ownership) this->assume_ownership(p);
1101 }
1102 
dual_add(widget * w,dialog & d)1103 void table_placer::dual_add(widget *w, dialog &d)
1104 {
1105 	add(static_cast<placeable *>(w));
1106 	d.add(w);
1107 }
1108 
dual_add_row(widget * w,dialog & d)1109 void table_placer::dual_add_row(widget *w, dialog &d)
1110 {
1111 	add_row(static_cast<placeable *>(w));
1112 	d.add(w);
1113 }
1114 
min_height()1115 int table_placer::min_height()
1116 {
1117 	int height = 0;
1118 	for (int row = 0; row < m_table.size(); ++row)
1119 	{
1120 		height += row_height(row);
1121 	}
1122 
1123 	return height;
1124 }
1125 
col_width(int col)1126 int table_placer::col_width(int col)
1127 {
1128 	int width = 0;
1129 	for (int row = 0; row < m_table.size(); ++row)
1130 	{
1131 		if (m_table[row].size() == 1) continue;
1132 		int min_width = m_table[row][col]->min_width();
1133 		if (min_width > width)
1134 			width = min_width;
1135 	}
1136 
1137 	if (m_col_min_widths[col] > width)
1138 		width = m_col_min_widths[col];
1139 
1140 	return width;
1141 }
1142 
row_height(int row)1143 int table_placer::row_height(int row)
1144 {
1145 	int height = 0;
1146 	for (int col = 0; col < m_table[row].size(); ++col)
1147 	{
1148 		int min_height = m_table[row][col]->min_height();
1149 		if (min_height > height)
1150 			height = min_height;
1151 	}
1152 
1153 	return height;
1154 }
1155 
min_width()1156 int table_placer::min_width()
1157 {
1158 	int width = 0;
1159 	if (m_balance_widths)
1160 	{
1161 		for (int col = 0; col < m_columns; ++col)
1162 		{
1163 			int c_width = col_width(col);
1164 			if (c_width > width)
1165 				width = c_width;
1166 		}
1167 
1168 		width = width * m_columns + (m_columns - 1) * m_space;
1169 	}
1170 	else
1171 	{
1172 		for (int col = 0; col < m_columns; ++col)
1173 		{
1174 			width += col_width(col);
1175 		}
1176 
1177 		width += (m_columns - 1) * m_space;
1178 	}
1179 
1180 	for (int row = 0; row < m_table.size(); ++row)
1181 	{
1182 		if (m_table[row].size() == 1)
1183 		{
1184 			int min_width = m_table[row][0]->min_width();
1185 			if (min_width > width) width = min_width;
1186 		}
1187 	}
1188 
1189 	return width;
1190 }
1191 
place(const SDL_Rect & r,placement_flags flags)1192 void table_placer::place(const SDL_Rect &r, placement_flags flags)
1193 {
1194 	int w = min_width();
1195 
1196 	std::vector<int> column_widths(m_columns);
1197 
1198 	if (m_balance_widths)
1199 	{
1200 		if (flags & kFill)
1201 		{
1202 			for (int i = 0; i < column_widths.size(); i++)
1203 			{
1204 				column_widths[i] = (r.w - (m_columns - 1) * m_space) / m_columns;
1205 			}
1206 		}
1207 		else
1208 		{
1209 			int column_width = col_width(0);
1210 			for (int i = 1; i < column_widths.size(); i++)
1211 			{
1212 				if (col_width(i) > column_width)
1213 					column_width = col_width(i);
1214 			}
1215 
1216 			for (int i = 0; i < column_widths.size(); i++)
1217 			{
1218 				column_widths[i] = column_width;
1219 			}
1220 		}
1221 	}
1222 	else if (flags & kFill)
1223 	{
1224 		int pool = r.w - (m_columns - 1) * m_space;
1225 		int columns_remaining = m_columns;
1226 		for (int i = 0; i < m_columns; i++)
1227 		{
1228 			if (!(m_col_flags[i] & kFill))
1229 			{
1230 				columns_remaining--;
1231 				column_widths[i] = col_width(i);
1232 				pool -= column_widths[i];
1233 			}
1234 		}
1235 
1236 		if (columns_remaining == m_columns || columns_remaining == 0)
1237 		{
1238 			for (int i = 0; i < column_widths.size(); i++)
1239 			{
1240 				column_widths[i] = col_width(i) + (pool / m_columns);
1241 			}
1242 		}
1243 		else
1244 		{
1245 			int remaining_w = r.w / columns_remaining;
1246 			for (int i = 0; i < m_columns; i++)
1247 			{
1248 				if (m_col_flags[i] & kFill)
1249 				{
1250 					int column_width = col_width(i);
1251 					if (column_width > remaining_w)
1252 					{
1253 						pool -= column_width - remaining_w;
1254 						column_widths[i] = column_width;
1255 					}
1256 				}
1257 
1258 				if (pool < 0)
1259 				{
1260 					// bail!
1261 					for (int i = 0; i < column_widths.size(); i++)
1262 					{
1263 						column_widths[i] = col_width(i) + (pool / m_columns);
1264 					}
1265 				}
1266 				else
1267 				{
1268 					remaining_w = pool / columns_remaining;
1269 					for (int i = 0; i < column_widths.size(); i++)
1270 					{
1271 						if (m_col_flags[i] & kFill)
1272 						{
1273 							int column_width = col_width(i);
1274 							if (remaining_w > column_width)
1275 							{
1276 								column_widths[i] = remaining_w;
1277 							}
1278 							else
1279 							{
1280 								column_widths[i] = column_width;
1281 							}
1282 						}
1283 					}
1284 				}
1285 			}
1286 		}
1287 	}
1288 	else
1289 	{
1290 		for (int i = 0; i < column_widths.size(); i++)
1291 		{
1292 			column_widths[i] = col_width(i);
1293 		}
1294 	}
1295 
1296 
1297 	int y_offset = 0;
1298 	for (int row = 0; row < m_table.size(); ++row)
1299 	{
1300 		bool full_row = m_table[row].size() == 1;
1301 		int x_offset;
1302 		if ((flags & kFill) || (flags & kAlignLeft))
1303 		{
1304 			x_offset = 0;
1305 		}
1306 		else if (flags & kAlignRight)
1307 		{
1308 			x_offset = r.w - w;
1309 		}
1310 		else
1311 		{
1312 			x_offset = (r.w - w) / 2;
1313 		}
1314 
1315 		for (int col = 0; col < m_table[row].size(); ++col)
1316 		{
1317 			SDL_Rect wr;
1318 			wr.w = full_row ? w : column_widths[col];
1319 
1320 			wr.h = row_height(row);
1321 			wr.x = r.x + x_offset;
1322 			wr.y = r.y + y_offset;
1323 
1324 			m_table[row][col]->place(wr, full_row ? placeable::kDefault : m_col_flags[col]);
1325 
1326 			x_offset += wr.w;
1327 			x_offset += m_space;
1328 		}
1329 
1330 		y_offset += row_height(row);
1331 	}
1332 }
1333 
visible(bool visible)1334 void table_placer::visible(bool visible)
1335 {
1336 	for (int row = 0; row < m_table.size(); ++row)
1337 	{
1338 		for (int col = 0; col < m_table[row].size(); ++col)
1339 		{
1340 			m_table[row][col]->visible(visible);
1341 		}
1342 	}
1343 }
1344 
add(placeable * p,bool assume_ownership)1345 void vertical_placer::add(placeable *p, bool assume_ownership)
1346 {
1347 	m_widgets.push_back(p);
1348 	m_widget_heights.push_back(p->min_height());
1349 	m_placement_flags.push_back(m_add_flags);
1350 
1351 	if (assume_ownership) this->assume_ownership(p);
1352 }
1353 
dual_add(widget * w,dialog & d)1354 void vertical_placer::dual_add(widget *w, dialog &d)
1355 {
1356 	add(static_cast<placeable *>(w));
1357 	d.add(w);
1358 }
1359 
min_height()1360 int vertical_placer::min_height()
1361 {
1362 	int height = 0;
1363 	for (std::vector<placeable *>::iterator it = m_widgets.begin(); it != m_widgets.end(); it++)
1364 	{
1365 		height += (*it)->min_height();
1366 	}
1367 
1368 	if (m_widgets.size())
1369 		height += (m_widgets.size() - 1) * m_space;
1370 
1371 	return height;
1372 
1373 }
1374 
min_width()1375 int vertical_placer::min_width()
1376 {
1377 	if (m_widgets.size())
1378 	{
1379 		int min_width = m_widgets[0]->min_width();
1380 		for (std::vector<placeable *>::iterator it = m_widgets.begin(); it != m_widgets.end(); it++)
1381 		{
1382 			if ((*it)->min_width() > min_width)
1383 				min_width = (*it)->min_width();
1384 		}
1385 		return std::max(min_width, m_min_width);
1386 	}
1387 	else
1388 	{
1389 		return m_min_width;
1390 	}
1391 }
1392 
place(const SDL_Rect & r,placement_flags flags)1393 void vertical_placer::place(const SDL_Rect &r, placement_flags flags)
1394 {
1395 	int y_offset = 0;
1396 	int w = (flags & kFill) ? r.w : min_width();
1397 	for (int i = 0; i < m_widgets.size(); i++)
1398 	{
1399 		SDL_Rect wr;
1400 		if ((flags & kFill) || (flags & kAlignLeft))
1401 		{
1402 			wr.x = r.x;
1403 		}
1404 		else if (flags & kAlignRight)
1405 		{
1406 			wr.x = r.x + r.w - w;
1407 		}
1408 		else
1409 		{
1410 			wr.x = r.x + (r.w - w) / 2;
1411 		}
1412 		wr.w = w;
1413 		wr.h = m_widgets[i]->min_height();
1414 		wr.y = r.y + y_offset;
1415 
1416 		m_widgets[i]->place(wr, m_placement_flags[i]);
1417 
1418 		y_offset += wr.h;
1419 		y_offset += m_space;
1420 
1421 	}
1422 }
1423 
visible(bool visible)1424 void vertical_placer::visible(bool visible)
1425 {
1426 	for (std::vector<placeable *>::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it)
1427 	{
1428 		(*it)->visible(visible);
1429 	}
1430 	widget_placer::visible(visible);
1431 }
1432 
add(placeable * p,bool assume_ownership)1433 void horizontal_placer::add(placeable *p, bool assume_ownership)
1434 {
1435 	m_widgets.push_back(p);
1436 	m_widget_widths.push_back(p->min_width());
1437 	m_placement_flags.push_back(m_add_flags);
1438 
1439 	if (assume_ownership) this->assume_ownership(p);
1440 }
1441 
dual_add(widget * w,dialog & d)1442 void horizontal_placer::dual_add(widget *w, dialog &d)
1443 {
1444 	add(static_cast<placeable *>(w));
1445 	d.add(w);
1446 }
1447 
min_height()1448 int horizontal_placer::min_height()
1449 {
1450 	if (m_widgets.size())
1451 	{
1452 		int min_height = m_widgets[0]->min_height();
1453 		for (std::vector<placeable *>::iterator it = m_widgets.begin(); it != m_widgets.end(); it++)
1454 		{
1455 			if ((*it)->min_height() > min_height)
1456  				min_height = (*it)->min_height();
1457 		}
1458 		return min_height;
1459 	}
1460 	else
1461 	{
1462 		return 0;
1463 	}
1464 
1465 }
1466 
min_width()1467 int horizontal_placer::min_width()
1468 {
1469 	int width = 0;
1470 	if (m_balance_widths)
1471 	{
1472 		// find the largest width
1473 		for (std::vector<placeable *>::iterator it = m_widgets.begin(); it != m_widgets.end(); it++)
1474 		{
1475 			if ((*it)->min_width() > width)
1476 				width = (*it)->min_width();
1477 		}
1478 
1479 		width = width * m_widgets.size();
1480 	}
1481 	else
1482 	{
1483 		for (std::vector<placeable *>::iterator it = m_widgets.begin(); it != m_widgets.end(); it++)
1484 		{
1485 			width += (*it)->min_width();
1486 		}
1487 	}
1488 
1489 	if (m_widgets.size())
1490 		width += (m_widgets.size() - 1) * m_space;
1491 
1492 	return width;
1493 }
1494 
place(const SDL_Rect & r,placement_flags flags)1495 void horizontal_placer::place(const SDL_Rect &r, placement_flags flags)
1496 {
1497 	int x_offset;
1498 	if ((flags & kAlignLeft) || (flags & kFill))
1499 	{
1500 		x_offset = 0;
1501 	}
1502 	else if (flags & kAlignRight)
1503 	{
1504 		x_offset = r.w - min_width();
1505 	}
1506 	else
1507 	{
1508 		x_offset = (r.w - min_width()) / 2;
1509 	}
1510 
1511 	int h = (flags & kFill) ? r.h : min_height();
1512 	int w = 0;
1513 
1514 	bool fill_all_widgets = false;
1515 	if (m_balance_widths)
1516 	{
1517 		if (flags & kFill)
1518 		{
1519 			w = r.w / m_widgets.size();
1520 		}
1521 		else
1522 		{
1523 			for (int i = 0; i < m_widgets.size(); i++)
1524 			{
1525 				if (m_widgets[i]->min_width() > w)
1526 					w = m_widgets[i]->min_width();
1527 			}
1528 		}
1529 	}
1530 	else if (flags & kFill)
1531 	{
1532 		int pool = r.w - (m_widgets.size() - 1) * m_space;
1533 		int widgets_remaining = m_widgets.size();
1534 		for (int i = 0; i < m_widgets.size(); i++)
1535 		{
1536 			if (!(m_placement_flags[i] & kFill))
1537 			{
1538 				widgets_remaining--;
1539 				pool -= m_widgets[i]->min_width();
1540 			}
1541 		}
1542 
1543 		if (widgets_remaining == m_widgets.size() || widgets_remaining == 0)
1544 		{
1545 			fill_all_widgets = true;
1546 			w = r.w / m_widgets.size();
1547 		}
1548 		else
1549 		{
1550 			w = r.w / widgets_remaining;
1551 			for (int i = 0; i < m_widgets.size(); i++)
1552 			{
1553 				if (m_placement_flags[i] & kFill)
1554 				{
1555 					int min_width = m_widgets[i]->min_width();
1556 					if (min_width > w)
1557 					{
1558 						pool -= min_width - w;
1559 					}
1560 				}
1561 			}
1562 
1563 			if (pool < 0)
1564 			{
1565 				// bail!
1566 				fill_all_widgets = true;
1567 				w = r.w / m_widgets.size();
1568 			}
1569 			else
1570 			{
1571 				w = pool / widgets_remaining;
1572 			}
1573 		}
1574 	}
1575 
1576 	for (int i = 0; i < m_widgets.size(); i++)
1577 	{
1578 		int min_width = m_widgets[i]->min_width();
1579 
1580 		SDL_Rect wr;
1581 		if (m_balance_widths || fill_all_widgets || (m_placement_flags[i] & kFill && w > min_width))
1582 		{
1583 			wr.w = w;
1584 		}
1585 		else
1586 		{
1587 			wr.w = min_width;
1588 		}
1589 
1590 		wr.h = h;
1591 		wr.y = r.y;
1592 		wr.x = r.x + x_offset;
1593 
1594 		m_widgets[i]->place(wr, m_placement_flags[i]);
1595 
1596 		x_offset += wr.w;
1597 		x_offset += m_space;
1598 	}
1599 }
visible(bool visible)1600 void horizontal_placer::visible(bool visible)
1601 {
1602 	for (std::vector<placeable *>::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it)
1603 	{
1604 		(*it)->visible(visible);
1605 	}
1606 	widget_placer::visible(visible);
1607 }
1608 
add(placeable * p,bool assume_ownership)1609 void tab_placer::add(placeable *p, bool assume_ownership)
1610 {
1611 	if (m_tabs.size())
1612 		p->visible(false);
1613 	else
1614 		p->visible(true);
1615 
1616 	m_tabs.push_back(p);
1617 
1618 	if (assume_ownership) this->assume_ownership(p);
1619 }
1620 
dual_add(widget * w,dialog & d)1621 void tab_placer::dual_add(widget *w, dialog& d)
1622 {
1623 	add(static_cast<placeable *>(w));
1624 	d.add(w);
1625 }
1626 
min_height()1627 int tab_placer::min_height()
1628 {
1629 	int height = 0;
1630 	for (std::vector<placeable *>::iterator it = m_tabs.begin(); it != m_tabs.end(); it++)
1631 	{
1632 		if ((*it)->min_height() > height)
1633 			height = (*it)->min_height();
1634 	}
1635 
1636 	return height;
1637 }
1638 
min_width()1639 int tab_placer::min_width()
1640 {
1641 	int width = 0;
1642 	for (std::vector<placeable *>::iterator it = m_tabs.begin(); it != m_tabs.end(); it++)
1643 	{
1644 		if ((*it)->min_width() > width)
1645 			width = (*it)->min_width();
1646 	}
1647 
1648 	return width;
1649 }
1650 
choose_tab(int new_tab)1651 void tab_placer::choose_tab(int new_tab)
1652 {
1653 	assert(new_tab < m_tabs.size());
1654 
1655 	m_tabs[m_tab]->visible(false);
1656 	if (visible())
1657 		m_tabs[new_tab]->visible(true);
1658 	m_tab = new_tab;
1659 }
1660 
place(const SDL_Rect & r,placement_flags flags)1661 void tab_placer::place(const SDL_Rect& r, placement_flags flags)
1662 {
1663 	int h = (flags & kFill) ? r.h : min_height();
1664 
1665 	for (std::vector<placeable *>::iterator it = m_tabs.begin(); it != m_tabs.end(); ++it)
1666 	{
1667 		SDL_Rect wr;
1668 		wr.w = (*it)->min_width();
1669 		wr.h = h;
1670 		wr.y = r.y;
1671 		int x_offset;
1672 		if (flags & kAlignLeft)
1673 			x_offset = 0;
1674 		else if (flags & kAlignRight)
1675 			x_offset = r.w - (*it)->min_width();
1676 		else
1677 			x_offset = (r.w - (*it)->min_width()) / 2;
1678 
1679 		wr.x = r.x + x_offset;
1680 
1681 		(*it)->place(wr);
1682 	}
1683 }
1684 
visible(bool visible)1685 void tab_placer::visible(bool visible)
1686 {
1687 	widget_placer::visible(visible);
1688 	if (m_tabs.size())
1689 	{
1690 		m_tabs[m_tab]->visible(visible);
1691 	}
1692 }
1693 
1694 /*
1695  *  Dialog constructor
1696  */
1697 
dialog()1698 dialog::dialog() : active_widget(NULL), mouse_widget(0), active_widget_num(UNONE), done(false),
1699             cursor_was_visible(false), parent_dialog(NULL),
1700 		   processing_function(NULL), placer(0), last_redraw(0)
1701 {
1702 }
1703 
1704 
1705 /*
1706  *  Dialog destructor
1707  */
1708 
~dialog()1709 dialog::~dialog()
1710 {
1711 	// Free all widgets
1712 	vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
1713 	while (i != end) {
1714 		delete *i;
1715 		i++;
1716 	}
1717 
1718 	if (placer) {
1719 		delete placer;
1720 		placer = 0;
1721 	}
1722 }
1723 
1724 
1725 /*
1726  *  Add widget
1727  */
1728 
add(widget * w)1729 void dialog::add(widget *w)
1730 {
1731 	widgets.push_back(w);
1732         w->set_owning_dialog(this);
1733 }
1734 
1735 /*
1736  *  Layout dialog
1737  */
1738 
layout()1739 void dialog::layout()
1740 {
1741 	assert(placer);
1742 
1743 	layout_for_fullscreen = get_screen_mode()->fullscreen;
1744 
1745 	// Layout all widgets, calculate total width and height
1746 	SDL_Rect placer_rect;
1747 	placer_rect.w = placer->min_width();
1748 	placer_rect.h = placer->min_height();
1749 
1750 	rect.w = get_theme_space(DIALOG_FRAME, L_SPACE) + placer_rect.w + get_theme_space(DIALOG_FRAME, R_SPACE);
1751 	rect.h = get_theme_space(DIALOG_FRAME, T_SPACE) + placer_rect.h + get_theme_space(DIALOG_FRAME, B_SPACE);
1752 
1753 	// Center dialog on menu surface
1754 	int surface_w = MainScreenLogicalWidth();
1755 	int surface_h = MainScreenLogicalHeight();
1756 	if (MainScreenIsOpenGL())
1757 	{
1758 		surface_w = 640;
1759 		surface_h = 480;
1760 	}
1761 	rect.x = (surface_w - rect.w) / 2;
1762 	rect.y = (surface_h - rect.h) / 2;
1763 
1764 	placer_rect.x = get_theme_space(DIALOG_FRAME, L_SPACE);
1765 	placer_rect.y = get_theme_space(DIALOG_FRAME, T_SPACE);
1766 
1767 	placer->place(placer_rect);
1768 }
1769 
1770 
1771 /*
1772  *  Update part of dialog on screen
1773  */
1774 
update(SDL_Rect r) const1775 void dialog::update(SDL_Rect r) const
1776 {
1777 #ifdef HAVE_OPENGL
1778 	if (OGL_IsActive()) {
1779 		OGL_Blitter::BoundScreen(false);
1780 		clear_screen(false);
1781 		OGL_Blitter blitter;
1782 		SDL_Rect src = { 0, 0, rect.w, rect.h };
1783 		blitter.Load(*dialog_surface, src);
1784 		blitter.Draw(rect);
1785 
1786 		MainScreenSwap();
1787 	} else
1788 #endif
1789 	{
1790 		SDL_Surface *video = MainScreenSurface();
1791 		SDL_Rect dst_rect = rect;
1792 		SDL_Rect src_rect = { 0, 0, rect.w, rect.h };
1793 		SDL_BlitSurface(dialog_surface, &src_rect, video, &dst_rect);
1794 		MainScreenUpdateRects(1, &dst_rect);
1795 
1796 	}
1797 }
1798 
1799 
1800 /*
1801  *  Draw dialog
1802  */
1803 
draw_widget(widget * w,bool do_update) const1804 void dialog::draw_widget(widget *w, bool do_update) const
1805 {
1806 	// Clear and redraw widget
1807 	SDL_FillRect(dialog_surface, &w->rect, get_theme_color(DIALOG_FRAME, DEFAULT_STATE, BACKGROUND_COLOR));
1808 	w->draw(dialog_surface);
1809 	w->dirty = false;
1810 
1811 	// Blit to screen
1812 	if (do_update)
1813 		update(w->rect);
1814 }
1815 
draw_frame_image(SDL_Surface * s,int x,int y)1816 static void draw_frame_image(SDL_Surface *s, int x, int y)
1817 {
1818 	SDL_Rect r = {x, y, s->w, s->h};
1819 	SDL_BlitSurface(s, NULL, dialog_surface, &r);
1820 }
1821 
draw(void)1822 void dialog::draw(void)
1823 {
1824 	if (get_screen_mode()->fullscreen != layout_for_fullscreen)
1825 		layout();
1826 
1827 	// Clear dialog surface
1828 	SDL_FillRect(dialog_surface, NULL, get_theme_color(DIALOG_FRAME, DEFAULT_STATE, BACKGROUND_COLOR));
1829 
1830 	if (use_theme_images(DIALOG_FRAME))
1831 	{
1832 		// Draw frame
1833 		draw_frame_image(frame_tl, 0, 0);
1834 		draw_frame_image(frame_t, frame_tl->w, 0);
1835 		draw_frame_image(frame_tr, frame_tl->w + frame_t->w, 0);
1836 		draw_frame_image(frame_l, 0, frame_tl->h);
1837 		draw_frame_image(frame_r, rect.w - frame_r->w, frame_tr->h);
1838 		draw_frame_image(frame_bl, 0, frame_tl->h + frame_l->h);
1839 		draw_frame_image(frame_b, frame_bl->w, rect.h - frame_b->h);
1840 		draw_frame_image(frame_br, frame_bl->w + frame_b->w, frame_tr->h + frame_r->h);
1841 	}
1842 	else
1843 	{
1844 		uint32 pixel = get_theme_color(DIALOG_FRAME, DEFAULT_STATE, FRAME_COLOR);
1845 		SDL_Rect r = {0, 0, rect.w, rect.h};
1846 		draw_rectangle(dialog_surface, &r, pixel);
1847 	}
1848 
1849 	// Draw all visible widgets
1850 	vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
1851 	while (i != end) {
1852 		if ((*i)->visible())
1853 			draw_widget(*i, false);
1854 		i++;
1855 	}
1856 
1857 	// Blit to screen
1858 	SDL_Rect r = {0, 0, rect.w, rect.h};
1859 	update(r);
1860 }
1861 
1862 void
draw_dirty_widgets() const1863 dialog::draw_dirty_widgets() const
1864 {
1865 	if (top_dialog != this) return;
1866         for (unsigned i=0; i<widgets.size(); i++)
1867 		if (widgets[i]->is_dirty())
1868 			if (widgets[i]->visible())
1869 				draw_widget(widgets[i]);
1870 
1871 }
1872 
1873 /*
1874  *  Deactivate currently active widget
1875  */
1876 
deactivate_currently_active_widget()1877 void dialog::deactivate_currently_active_widget()
1878 {
1879 	if (active_widget) {
1880 		active_widget->set_active(false);
1881 		if (active_widget->associated_label)
1882 			active_widget->associated_label->set_active(false);
1883 
1884         active_widget = NULL;
1885         active_widget_num = UNONE;
1886 	}
1887 }
1888 
1889 
1890 /*
1891  *  Activate widget
1892  */
1893 
activate_widget(widget * w)1894 void dialog::activate_widget(widget *w)
1895 {
1896 	for (size_t i = 0; i < widgets.size(); i++)
1897 	{
1898 		if (widgets[i] == w)
1899 		{
1900 			activate_widget(i);
1901 			return;
1902 		}
1903 	}
1904 }
1905 
activate_widget(size_t num)1906 void dialog::activate_widget(size_t num)
1907 {
1908 	if (num == active_widget_num)
1909 		return;
1910 	// BUG: may crash if num==UNONE or NONE
1911 	if (!widgets[num]->is_selectable())
1912 		return;
1913 
1914 	// Deactivate previously active widget
1915 	deactivate_currently_active_widget();
1916 
1917 	// Activate new widget
1918 	w_label *label = dynamic_cast<w_label *>(widgets[num]);
1919 	if (label && label->associated_widget)
1920 	{
1921 		if (widgets[num + 1 % widgets.size()] == label->associated_widget)
1922 		{
1923 			active_widget = label->associated_widget;
1924 			active_widget_num = num + 1 % widgets.size();
1925 		}
1926 		else if (widgets[num - 1 % widgets.size()] == label->associated_widget)
1927 		{
1928 			active_widget = label->associated_widget;
1929 			active_widget_num = num - 1 % widgets.size();
1930 		}
1931 		else
1932 			// labels must be placed immediately before or
1933 			// after their associated widgets!
1934 			assert(false);
1935 	}
1936 	else
1937 	{
1938 		active_widget = widgets[num];
1939 		active_widget_num = num;
1940 	}
1941 
1942 
1943 	active_widget->set_active(true);
1944 	if (active_widget->associated_label)
1945 		active_widget->associated_label->set_active(true);
1946 }
1947 
1948 
1949 /*
1950  *  Activate first selectable widget (don't draw)
1951  */
1952 
activate_first_widget(void)1953 void dialog::activate_first_widget(void)
1954 {
1955 	for (size_t i=0; i<widgets.size(); i++) {
1956 		if (widgets[i]->is_selectable() && widgets[i]->visible()) {
1957 			activate_widget(i);
1958 			break;
1959 		}
1960 	}
1961 }
1962 
1963 
1964 /*
1965  *  Activate next/previous selectable widget
1966  */
1967 
activate_next_widget(void)1968 void dialog::activate_next_widget(void)
1969 {
1970 	if (!active_widget)
1971 	{
1972 		activate_first_widget();
1973 		return;
1974 	}
1975 	size_t i = active_widget_num;
1976 	// BUG: infinate loop if active_widget_num == UNONE or NONE
1977 	do {
1978 		i++;
1979 		if (i >= int(widgets.size()))
1980 			i = 0;
1981 	} while ((!(widgets[i]->is_selectable() && widgets[i]->visible()) || (widgets[i]->associated_label == widgets[active_widget_num] || widgets[active_widget_num]->associated_label == widgets[i])) && i != active_widget_num);
1982 
1983     // Either widgets[i] is selectable, or i == active_widget_num (in which case we wrapped all the way around)
1984 	if (widgets[i]->is_selectable() && widgets[i]->visible())
1985 		activate_widget(i);
1986 	else
1987 		deactivate_currently_active_widget();
1988 }
1989 
activate_prev_widget(void)1990 void dialog::activate_prev_widget(void)
1991 {
1992 	if (!active_widget)
1993 	{
1994 		activate_first_widget();
1995 	}
1996 
1997 	size_t i = active_widget_num;
1998 	// BUG: infinate loop if active_widget_num == UNONE or NONE
1999 	do {
2000 		if (i == 0)
2001 			i = widgets.size() - 1;
2002 		else
2003 			i--;
2004 	} while ((!(widgets[i]->is_selectable() && widgets[i]->visible()) || (widgets[i]->associated_label == widgets[active_widget_num] || widgets[active_widget_num]->associated_label == widgets[i])) && i != active_widget_num);
2005 
2006     // Either widgets[i] is selectable, or i == active_widget_num (in which case we wrapped all the way around)
2007 	if (widgets[i]->is_selectable() && widgets[i]->visible())
2008 		activate_widget(i);
2009 	else
2010 		deactivate_currently_active_widget();
2011 }
2012 
2013 
2014 /*
2015  *  Find widget given video surface coordinates (<0 = none found)
2016  */
2017 
find_widget(int x,int y)2018 int dialog::find_widget(int x, int y)
2019 {
2020 	// Transform to dialog coordinates
2021 	x -= rect.x;
2022 	y -= rect.y;
2023 
2024 	// Find widget
2025 	vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
2026 	int num = 0;
2027 	while (i != end) {
2028 		widget *w = *i;
2029 		if ((*i)->visible())
2030 			if (x >= w->rect.x && y >= w->rect.y && x < w->rect.x + w->rect.w && y < w->rect.y + w->rect.h)
2031 				return num;
2032 		i++; num++;
2033 	}
2034 	return -1;
2035 }
2036 
2037 
2038 /*
2039  *  Find widget by its numeric ID
2040  */
2041 
get_widget_by_id(short inID) const2042 widget *dialog::get_widget_by_id(short inID) const
2043 {
2044 	// Find first matching widget
2045 	vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
2046 	while (i != end) {
2047 		widget *w = *i;
2048 		if (w->get_identifier() == inID)
2049 			return w;
2050 		i++;
2051 	}
2052 	return NULL;
2053 }
2054 
2055 
2056 /*
2057  *  Handle event
2058  */
2059 
event(SDL_Event & e)2060 void dialog::event(SDL_Event &e)
2061 {
2062 
2063   bool handled = false;
2064   // handle events we do not want widgets to see or modify
2065   switch (e.type) {
2066   case SDL_KEYDOWN:
2067 
2068     if (e.key.keysym.sym == SDLK_RETURN
2069 	&& ((e.key.keysym.mod & KMOD_ALT) || (e.key.keysym.mod & KMOD_GUI))) {
2070       toggle_fullscreen(!(get_screen_mode()->fullscreen));
2071       draw();
2072       handled = true;
2073     }
2074     break;
2075   case SDL_WINDOWEVENT:
2076     if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
2077 		draw();
2078 		handled = true;
2079 	}
2080     break;
2081   }
2082 
2083   if (!handled) {
2084 	  // First pass event to active widget (which may modify it)
2085 	  if (active_widget)
2086 		  active_widget->event(e);
2087 
2088 	  // handle mouse events
2089 	  if (e.type == SDL_MOUSEMOTION)
2090 	  {
2091 		  int x = e.motion.x, y = e.motion.y;
2092 #ifdef HAVE_OPENGL
2093 		  if (OGL_IsActive())
2094 			  OGL_Blitter::WindowToScreen(x, y);
2095 #endif
2096 		  widget *target = 0;
2097 		  if (mouse_widget)
2098 			  target = mouse_widget;
2099 		  else
2100 		  {
2101 			  int num = find_widget(x, y);
2102 			  if (num >= 0)
2103 			  {
2104 				  assert(num == (size_t) num);
2105 				  target = widgets[num];
2106 			  }
2107 		  }
2108 
2109 		  if (target)
2110 		  {
2111 			  target->event(e);
2112 			  target->mouse_move(x - rect.x - target->rect.x, y - rect.y - target->rect.y);
2113 		  }
2114 	  }
2115 	  else if (e.type == SDL_MOUSEBUTTONDOWN)
2116 	  {
2117 		  int x = e.button.x, y = e.button.y;
2118 #ifdef HAVE_OPENGL
2119 		  if (OGL_IsActive())
2120 			  OGL_Blitter::WindowToScreen(x, y);
2121 #endif
2122 		  int num = find_widget(x, y);
2123 		  if (num >= 0)
2124 		  {
2125 			  assert(num == (size_t) num);
2126 			  mouse_widget = widgets[num];
2127 			  mouse_widget->event(e);
2128 			  if (e.button.button == SDL_BUTTON_LEFT || e.button.button == SDL_BUTTON_RIGHT)
2129 				  mouse_widget->mouse_down(x - rect.x - mouse_widget->rect.x, y - rect.y - mouse_widget->rect.y);
2130 		  }
2131 	  }
2132 	  else if (e.type == SDL_MOUSEBUTTONUP)
2133 	  {
2134 		  if (mouse_widget)
2135 		  {
2136 			  mouse_widget->event(e);
2137 			  if (e.button.button == SDL_BUTTON_LEFT || e.button.button == SDL_BUTTON_RIGHT)
2138 			  {
2139 				  int x = e.button.x, y = e.button.y;
2140 #ifdef HAVE_OPENGL
2141 				  if (OGL_IsActive())
2142 					  OGL_Blitter::WindowToScreen(x, y);
2143 #endif
2144 				  mouse_widget->mouse_up(x - rect.x - mouse_widget->rect.x, y - rect.y - mouse_widget->rect.y);
2145 			  }
2146 
2147 			  mouse_widget = 0;
2148 		  }
2149 	  }
2150 	  else if (e.type == SDL_KEYDOWN)
2151 	  {
2152 		  switch (e.key.keysym.sym) {
2153 		  case SDLK_ESCAPE:		// ESC = Exit dialog
2154 			  quit(-1);
2155 			  break;
2156 		  case SDLK_UP:			// Up = Activate previous widget
2157 		  case SDLK_LEFT:
2158 			  activate_prev_widget();
2159 			  break;
2160 		  case SDLK_DOWN:			// Down = Activate next widget
2161 		  case SDLK_RIGHT:
2162 			  activate_next_widget();
2163 			  break;
2164 		  case SDLK_TAB:
2165 			  if (e.key.keysym.mod & KMOD_SHIFT)
2166 				  activate_prev_widget();
2167 			  else
2168 				  activate_next_widget();
2169 			  break;
2170 		  case SDLK_RETURN: 		// Return = Action on widget
2171 			  if (active_widget) active_widget->click(0, 0);
2172 			  break;
2173 		  case SDLK_F9:			// F9 = Screen dump
2174 			  dump_screen();
2175 			  break;
2176 		  default:
2177 			  break;
2178 		  }
2179 	  }
2180 	  else if (e.type == SDL_CONTROLLERBUTTONDOWN)
2181 	  {
2182 		  switch (e.cbutton.button) {
2183 			  case SDL_CONTROLLER_BUTTON_B:
2184 				  quit(-1);
2185 				  break;
2186 			  case SDL_CONTROLLER_BUTTON_DPAD_UP:
2187 			  case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
2188 				  activate_prev_widget();
2189 				  break;
2190 			  case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
2191 			  case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
2192 				  activate_next_widget();
2193 				  break;
2194 			  case SDL_CONTROLLER_BUTTON_A:
2195 				  if (active_widget) active_widget->click(0, 0);
2196 				  break;
2197 			  default:
2198 				  break;
2199 		  }
2200 	  }
2201 	  else if (e.type == SDL_QUIT)
2202 	  {
2203 		// Quit requested
2204 			exit(0);
2205 	  }
2206   }
2207 }
2208 
2209 
2210 /*
2211  *  Run dialog modally, returns result code (0 = ok, -1 = cancel)
2212  */
2213 
run(bool intro_exit_sounds)2214 int dialog::run(bool intro_exit_sounds)
2215 {
2216 	// Put dialog on screen
2217 	start(intro_exit_sounds);
2218 
2219 	// Run dialog loop
2220 	while (!done) {
2221 		// Process events
2222 		process_events();
2223 		if (done)
2224 			break;
2225 
2226 		if (SDL_GetTicks() > last_redraw + TICKS_PER_SECOND / 30)
2227 		{
2228 			draw_dirty_widgets();
2229 			last_redraw = SDL_GetTicks();
2230 		}
2231 
2232 		// Run custom processing function
2233 		if (processing_function)
2234 			processing_function(this);
2235 
2236 		// Give time to system
2237 		global_idle_proc();
2238 		SDL_Delay(10);
2239 	}
2240 
2241 	// Remove dialog from screen
2242 	return finish(intro_exit_sounds);
2243 }
2244 
2245 
2246 /*
2247  *  Put dialog on screen
2248  */
2249 
start(bool play_sound)2250 void dialog::start(bool play_sound)
2251 {
2252 	// Make sure nobody tries re-entrancy with us
2253 	assert(!done);
2254 
2255 	// Set new active dialog
2256 	parent_dialog = top_dialog;
2257 	top_dialog = this;
2258 
2259 	// Clear dialog surface
2260 	SDL_FillRect(dialog_surface, NULL, get_theme_color(DIALOG_FRAME, DEFAULT_STATE, BACKGROUND_COLOR));
2261 
2262 	// Activate first widget
2263 //	activate_first_widget();
2264 
2265 	// Layout dialog
2266 	layout();
2267 
2268 	// Get frame images
2269 	frame_tl = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, TL_IMAGE);
2270 	frame_tr = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, TR_IMAGE);
2271 	frame_bl = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, BL_IMAGE);
2272 	frame_br = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, BR_IMAGE);
2273 	frame_t = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
2274 	frame_l = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
2275 	frame_r = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
2276 	frame_b = get_theme_image(DIALOG_FRAME, DEFAULT_STATE, B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
2277 
2278 #if (defined(HAVE_OPENGL) && defined(OPENGL_DOESNT_COPY_ON_SWAP))
2279 	if (OGL_IsActive()) {
2280         // blank both buffers to avoid flickering
2281         clear_screen();
2282 	}
2283 #endif
2284 
2285 	// Draw dialog
2286 	draw();
2287 
2288 	// Show cursor
2289 	cursor_was_visible = (SDL_ShowCursor(SDL_ENABLE) == SDL_ENABLE);
2290 
2291 	// Welcome sound
2292 	if (play_sound)
2293 		play_dialog_sound(DIALOG_INTRO_SOUND);
2294 
2295 	// Prepare for dialog event loop
2296 	result = 0;
2297 	done = false;
2298 }
2299 
2300 
2301 /*
2302  *  Process pending dialog events
2303  */
2304 
process_events()2305 bool dialog::process_events()
2306 {
2307 	while (!done) {
2308 		SDL_Event e;
2309 		if (SDL_PollEvent(&e))
2310 			event(e);
2311 		else
2312 			break;
2313 	}
2314 
2315 	return done;
2316 }
2317 
2318 
2319 /*
2320  *  Remove dialog from screen
2321  */
2322 
finish(bool play_sound)2323 int dialog::finish(bool play_sound)
2324 {
2325 	SDL_StopTextInput();
2326 
2327 	// Farewell sound
2328 	if (play_sound)
2329 		play_dialog_sound(result == 0 ? DIALOG_OK_SOUND : DIALOG_CANCEL_SOUND);
2330 
2331 	// Hide cursor
2332 	if (!cursor_was_visible)
2333 		SDL_ShowCursor(false);
2334 
2335 	// Clear dialog surface
2336 	SDL_FillRect(dialog_surface, NULL, get_theme_color(DIALOG_FRAME, DEFAULT_STATE, BACKGROUND_COLOR));
2337 
2338 #ifdef HAVE_OPENGL
2339 	if (OGL_IsActive()) {
2340         glColor4f(0, 0, 0, 1);
2341 #ifdef OPENGL_DOESNT_COPY_ON_SWAP
2342         for (int i = 0; i < 2; i++)  // execute for both buffers
2343 #endif
2344         {
2345 			OGL_RenderRect(rect);
2346             MainScreenSwap();
2347         }
2348 	} else
2349 #endif
2350 	{
2351 
2352 		// Erase dialog from screen
2353 		SDL_Surface *video = MainScreenSurface();
2354 		SDL_FillRect(video, &rect, SDL_MapRGB(video->format, 0, 0, 0));
2355 		MainScreenUpdateRects(1, &rect);
2356 	}
2357 
2358 	// Free frame images
2359 	if (frame_t) SDL_FreeSurface(frame_t);
2360 	if (frame_l) SDL_FreeSurface(frame_l);
2361 	if (frame_r) SDL_FreeSurface(frame_r);
2362 	if (frame_b) SDL_FreeSurface(frame_b);
2363 
2364 	// Restore active dialog
2365 	top_dialog = parent_dialog;
2366 	parent_dialog = NULL;
2367 	if (top_dialog) {
2368 		clear_screen();
2369 		top_dialog->draw();
2370 	}
2371 
2372 	// Allow dialog to be run again later
2373 	done = false;
2374 
2375 	return result;
2376 }
2377 
2378 
2379 /*
2380  *  Quit dialog, return result
2381  */
2382 
quit(int r)2383 void dialog::quit(int r)
2384 {
2385 	result = r;
2386 	done = true;
2387 }
2388 
2389 
2390 /*
2391  *  Standard callback functions
2392  */
2393 
dialog_ok(void * arg)2394 void dialog_ok(void *arg)
2395 {
2396 	dialog *d = (dialog *)arg;
2397 	d->quit(0);
2398 }
2399 
dialog_cancel(void * arg)2400 void dialog_cancel(void *arg)
2401 {
2402 	dialog *d = (dialog *)arg;
2403 	d->quit(-1);
2404 }
2405 
2406 // ZZZ: commonly-used callback for text entry enter_pressed_callback
dialog_try_ok(w_text_entry * text_entry)2407 void dialog_try_ok(w_text_entry* text_entry) {
2408     w_button* ok_button = dynamic_cast<w_button*>(text_entry->get_owning_dialog()->get_widget_by_id(iOK));
2409 
2410     // This is arguably a violation of the sdl_dialog/sdl_widget standard behavior since
2411     // ok_button is clicked without first becoming the active widget.  With the current
2412     // implementation of w_button::click, should not be a problem...
2413     if(ok_button != NULL)
2414         ok_button->click(0,0);
2415 }
2416 
2417 // ZZZ: commonly-used callback to enable/disable "OK" based on whether a text_entry has data
dialog_disable_ok_if_empty(w_text_entry * inTextEntry)2418 void dialog_disable_ok_if_empty(w_text_entry* inTextEntry) {
2419     modify_control_enabled(inTextEntry->get_owning_dialog(), iOK,
2420         (inTextEntry->get_text()[0] == 0) ? CONTROL_INACTIVE : CONTROL_ACTIVE);
2421 }
2422