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