PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/aleph-inio-branch/Source_Files/Misc/sdl_dialogs.cpp

#
C++ | 1277 lines | 868 code | 227 blank | 182 comment | 167 complexity | 0c492bc4fe0364dbb7e614356c4aff69 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, BSD-3-Clause, GPL-3.0, LGPL-3.0, MPL-2.0-no-copyleft-exception, Zlib, GPL-2.0
  1. /*
  2. Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
  3. and the "Aleph One" developers.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. This license is contained in the file "COPYING",
  13. which is included with this source code; it is available online at
  14. http://www.gnu.org/licenses/gpl.html
  15. */
  16. /*
  17. * sdl_dialogs.cpp - SDL implementation of user dialogs
  18. *
  19. * Written in 2000 by Christian Bauer
  20. */
  21. #include "cseries.h"
  22. #include "sdl_dialogs.h"
  23. #include "sdl_fonts.h"
  24. #include "sdl_widgets.h"
  25. #include "shape_descriptors.h"
  26. #include "screen_drawing.h"
  27. #include "shell.h"
  28. #include "screen.h"
  29. #include "images.h"
  30. #include "world.h"
  31. #include "mysound.h"
  32. #include "game_errors.h"
  33. #include "XML_Loader_SDL.h"
  34. #include "XML_ParseTreeRoot.h"
  35. #ifdef __MVCPP__
  36. #include <string>
  37. #endif
  38. // Global variables
  39. dialog *top_dialog = NULL;
  40. static SDL_Surface *dialog_surface = NULL;
  41. static sdl_font_info *default_font = NULL;
  42. static SDL_Surface *default_image = NULL;
  43. static OpenedResourceFile theme_resources;
  44. static TextSpec dialog_font_spec[NUM_DIALOG_FONTS];
  45. static sdl_font_info *dialog_font[NUM_DIALOG_FONTS];
  46. static SDL_Color dialog_color[NUM_DIALOG_COLORS];
  47. static int dialog_space[NUM_DIALOG_SPACES];
  48. #ifdef __MVCPP__ // VC++ doesn't like the other way -- throws an error about a default constructor.
  49. struct dialog_image_spec_stru
  50. {
  51. string name;
  52. bool scale;
  53. };
  54. static dialog_image_spec_stru dialog_image_spec[NUM_DIALOG_IMAGES];
  55. #else
  56. static struct {
  57. string name;
  58. bool scale;
  59. } dialog_image_spec[NUM_DIALOG_IMAGES];
  60. #endif
  61. static SDL_Surface *dialog_image[NUM_DIALOG_IMAGES];
  62. // Prototypes
  63. static void unload_theme(void);
  64. static void set_theme_defaults(void);
  65. /*
  66. * Initialize dialog manager
  67. */
  68. void initialize_dialogs(FileSpecifier &theme)
  69. {
  70. // Allocate surface for dialogs (this surface is needed because when
  71. // OpenGL is active, we can't write directly to the screen)
  72. dialog_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 16, 0x7c00, 0x03e0, 0x001f, 0);
  73. assert(dialog_surface);
  74. // Default font and image
  75. static const TextSpec default_font_spec = {kFontIDMonaco, styleNormal, 12};
  76. default_font = load_font(default_font_spec);
  77. assert(default_font);
  78. default_image = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, 1, 24, 0xff0000, 0x00ff00, 0x0000ff, 0);
  79. assert(default_image);
  80. uint32 transp = SDL_MapRGB(default_image->format, 0x00, 0xff, 0xff);
  81. SDL_FillRect(default_image, NULL, transp);
  82. SDL_SetColorKey(default_image, SDL_SRCCOLORKEY, transp);
  83. // Load theme from preferences
  84. load_theme(theme);
  85. }
  86. /*
  87. * Theme MML parser
  88. */
  89. class XML_ImageParser : public XML_ElementParser {
  90. public:
  91. XML_ImageParser(int base, int num = 1) : XML_ElementParser("image"), base_index(base), max_index(num - 1) {}
  92. bool Start()
  93. {
  94. have_index = have_name = false;
  95. scale = false;
  96. return true;
  97. }
  98. bool HandleAttribute(const char *tag, const char *value)
  99. {
  100. if (StringsEqual(tag, "index")) {
  101. if (ReadBoundedNumericalValue(value, "%d", index, 0, max_index))
  102. have_index = true;
  103. else
  104. return false;
  105. } else if (StringsEqual(tag, "file")) {
  106. name = value;
  107. have_name = true;
  108. } else if (StringsEqual(tag, "scale")) {
  109. return ReadBooleanValue(value, scale);
  110. } else {
  111. UnrecognizedTag();
  112. return false;
  113. }
  114. return true;
  115. }
  116. bool AttributesDone()
  117. {
  118. if (!have_index || !have_name) {
  119. AttribsMissing();
  120. return false;
  121. }
  122. dialog_image_spec[base_index + index].name = name;
  123. dialog_image_spec[base_index + index].scale = scale;
  124. return true;
  125. }
  126. private:
  127. int base_index, max_index;
  128. bool have_index, have_name;
  129. int index;
  130. string name;
  131. bool scale;
  132. };
  133. static XML_ImageParser FrameImageParser(FRAME_TL_IMAGE, 8);
  134. static XML_ImageParser ListImageParser(LIST_TL_IMAGE, 8);
  135. static XML_ImageParser ThumbImageParser(THUMB_T_IMAGE, 5);
  136. static XML_ImageParser SliderImageParser(SLIDER_L_IMAGE, 4);
  137. static XML_ImageParser ButtonImageParser(BUTTON_L_IMAGE, 3);
  138. class XML_DColorParser : public XML_ElementParser {
  139. public:
  140. XML_DColorParser(int base, int num = 1) : XML_ElementParser("color"), base_index(base), max_index(num - 1) {}
  141. bool Start()
  142. {
  143. have_red = have_green = have_blue = false;
  144. idx = 0;
  145. return true;
  146. }
  147. bool HandleAttribute(const char *tag, const char *value)
  148. {
  149. float v;
  150. if (StringsEqual(tag, "index")) {
  151. return ReadBoundedNumericalValue(value, "%d", idx, 0, max_index);
  152. } else if (StringsEqual(tag, "red")) {
  153. if (ReadNumericalValue(value, "%f", v)) {
  154. have_red = true;
  155. color.r = uint8(PIN(255 * v + 0.5, 0, 255));
  156. } else
  157. return false;
  158. } else if (StringsEqual(tag, "green")) {
  159. if (ReadNumericalValue(value, "%f", v)) {
  160. have_green = true;
  161. color.g = uint8(PIN(255 * v + 0.5, 0, 255));
  162. } else
  163. return false;
  164. } else if (StringsEqual(tag, "blue")) {
  165. if (ReadNumericalValue(value, "%f", v)) {
  166. have_blue = true;
  167. color.b = uint8(PIN(255 * v + 0.5, 0, 255));
  168. } else
  169. return false;
  170. } else {
  171. UnrecognizedTag();
  172. return false;
  173. }
  174. return true;
  175. }
  176. bool AttributesDone()
  177. {
  178. if (!have_red || !have_green || !have_blue) {
  179. AttribsMissing();
  180. return false;
  181. }
  182. dialog_color[base_index + idx] = color;
  183. return true;
  184. }
  185. private:
  186. int base_index, max_index;
  187. bool have_red, have_green, have_blue;
  188. int idx;
  189. SDL_Color color;
  190. };
  191. static XML_DColorParser BackgroundColorParser(BACKGROUND_COLOR);
  192. static XML_DColorParser TitleColorParser(TITLE_COLOR);
  193. static XML_DColorParser ButtonColorParser(BUTTON_COLOR, 2);
  194. static XML_DColorParser LabelColorParser(LABEL_COLOR, 2);
  195. static XML_DColorParser ItemColorParser(ITEM_COLOR, 2);
  196. static XML_DColorParser MessageColorParser(MESSAGE_COLOR);
  197. static XML_DColorParser TextEntryColorParser(TEXT_ENTRY_COLOR, 3);
  198. class XML_DFontParser : public XML_ElementParser {
  199. public:
  200. XML_DFontParser(int i) : XML_ElementParser("font"), idx(i) {}
  201. bool Start()
  202. {
  203. have_id = have_size = false;
  204. style = 0;
  205. return true;
  206. }
  207. bool HandleAttribute(const char *tag, const char *value)
  208. {
  209. if (StringsEqual(tag, "id")) {
  210. if (ReadNumericalValue(value, "%d", id))
  211. have_id = true;
  212. else
  213. return false;
  214. } else if (StringsEqual(tag, "size")) {
  215. if (ReadNumericalValue(value, "%d", size))
  216. have_size = true;
  217. else
  218. return false;
  219. } else if (StringsEqual(tag, "style")) {
  220. return ReadNumericalValue(value, "%d", style);
  221. } else {
  222. UnrecognizedTag();
  223. return false;
  224. }
  225. return true;
  226. }
  227. bool AttributesDone()
  228. {
  229. if (!have_id || !have_size) {
  230. AttribsMissing();
  231. return false;
  232. }
  233. dialog_font_spec[idx].font = id;
  234. dialog_font_spec[idx].style = style;
  235. dialog_font_spec[idx].size = size;
  236. return true;
  237. }
  238. private:
  239. bool have_id, have_size;
  240. int idx;
  241. int id, size, style;
  242. };
  243. static XML_DFontParser TitleFontParser(TITLE_FONT);
  244. static XML_DFontParser ButtonFontParser(BUTTON_FONT);
  245. static XML_DFontParser LabelFontParser(LABEL_FONT);
  246. static XML_DFontParser ItemFontParser(ITEM_FONT);
  247. static XML_DFontParser MessageFontParser(MESSAGE_FONT);
  248. static XML_DFontParser TextEntryFontParser(TEXT_ENTRY_FONT);
  249. class XML_FrameParser : public XML_ElementParser {
  250. public:
  251. XML_FrameParser() : XML_ElementParser("frame") {}
  252. bool HandleAttribute(const char *tag, const char *value)
  253. {
  254. if (StringsEqual(tag, "top")) {
  255. return ReadNumericalValue(value, "%d", dialog_space[FRAME_T_SPACE]);
  256. } else if (StringsEqual(tag, "bottom")) {
  257. return ReadNumericalValue(value, "%d", dialog_space[FRAME_B_SPACE]);
  258. } else if (StringsEqual(tag, "left")) {
  259. return ReadNumericalValue(value, "%d", dialog_space[FRAME_L_SPACE]);
  260. } else if (StringsEqual(tag, "right")) {
  261. return ReadNumericalValue(value, "%d", dialog_space[FRAME_R_SPACE]);
  262. } else {
  263. UnrecognizedTag();
  264. return false;
  265. }
  266. return true;
  267. }
  268. };
  269. static XML_FrameParser FrameParser;
  270. struct XML_BackgroundParser : public XML_ElementParser {XML_BackgroundParser() : XML_ElementParser("background") {}};
  271. static XML_BackgroundParser BackgroundParser;
  272. struct XML_TitleParser : public XML_ElementParser {XML_TitleParser() : XML_ElementParser("title") {}};
  273. static XML_TitleParser TitleParser;
  274. class XML_SpacerParser : public XML_ElementParser {
  275. public:
  276. XML_SpacerParser() : XML_ElementParser("spacer") {}
  277. bool HandleAttribute(const char *tag, const char *value)
  278. {
  279. if (StringsEqual(tag, "height")) {
  280. return ReadNumericalValue(value, "%d", dialog_space[SPACER_HEIGHT]);
  281. } else {
  282. UnrecognizedTag();
  283. return false;
  284. }
  285. return true;
  286. }
  287. };
  288. static XML_SpacerParser SpacerParser;
  289. class XML_ButtonParser : public XML_ElementParser {
  290. public:
  291. XML_ButtonParser() : XML_ElementParser("button") {}
  292. bool HandleAttribute(const char *tag, const char *value)
  293. {
  294. if (StringsEqual(tag, "top")) {
  295. return ReadNumericalValue(value, "%d", dialog_space[BUTTON_T_SPACE]);
  296. } else if (StringsEqual(tag, "left")) {
  297. return ReadNumericalValue(value, "%d", dialog_space[BUTTON_L_SPACE]);
  298. } else if (StringsEqual(tag, "right")) {
  299. return ReadNumericalValue(value, "%d", dialog_space[BUTTON_R_SPACE]);
  300. } else if (StringsEqual(tag, "height")) {
  301. return ReadNumericalValue(value, "%d", dialog_space[BUTTON_HEIGHT]);
  302. } else {
  303. UnrecognizedTag();
  304. return false;
  305. }
  306. return true;
  307. }
  308. };
  309. static XML_ButtonParser ButtonParser;
  310. struct XML_LabelParser : public XML_ElementParser {XML_LabelParser() : XML_ElementParser("label") {}};
  311. static XML_LabelParser LabelParser;
  312. class XML_DItemParser : public XML_ElementParser {
  313. public:
  314. XML_DItemParser() : XML_ElementParser("item") {}
  315. bool HandleAttribute(const char *tag, const char *value)
  316. {
  317. if (StringsEqual(tag, "space")) {
  318. return ReadNumericalValue(value, "%d", dialog_space[LABEL_ITEM_SPACE]);
  319. } else {
  320. UnrecognizedTag();
  321. return false;
  322. }
  323. return true;
  324. }
  325. };
  326. static XML_DItemParser ItemParser;
  327. struct XML_MessageParser : public XML_ElementParser {XML_MessageParser() : XML_ElementParser("message") {}};
  328. static XML_MessageParser MessageParser;
  329. struct XML_TextEntryParser : public XML_ElementParser {XML_TextEntryParser() : XML_ElementParser("text_entry") {}};
  330. static XML_TextEntryParser TextEntryParser;
  331. class XML_TroughParser : public XML_ElementParser {
  332. public:
  333. XML_TroughParser() : XML_ElementParser("trough") {}
  334. bool HandleAttribute(const char *tag, const char *value)
  335. {
  336. if (StringsEqual(tag, "top")) {
  337. return ReadNumericalValue(value, "%d", dialog_space[TROUGH_T_SPACE]);
  338. } else if (StringsEqual(tag, "bottom")) {
  339. return ReadNumericalValue(value, "%d", dialog_space[TROUGH_B_SPACE]);
  340. } else if (StringsEqual(tag, "right")) {
  341. return ReadNumericalValue(value, "%d", dialog_space[TROUGH_R_SPACE]);
  342. } else if (StringsEqual(tag, "width")) {
  343. return ReadNumericalValue(value, "%d", dialog_space[TROUGH_WIDTH]);
  344. } else {
  345. UnrecognizedTag();
  346. return false;
  347. }
  348. return true;
  349. }
  350. };
  351. static XML_TroughParser TroughParser;
  352. struct XML_ThumbParser : public XML_ElementParser {XML_ThumbParser() : XML_ElementParser("thumb") {}};
  353. static XML_ThumbParser ThumbParser;
  354. class XML_ListParser : public XML_ElementParser {
  355. public:
  356. XML_ListParser() : XML_ElementParser("list") {}
  357. bool HandleAttribute(const char *tag, const char *value)
  358. {
  359. if (StringsEqual(tag, "top")) {
  360. return ReadNumericalValue(value, "%d", dialog_space[LIST_T_SPACE]);
  361. } else if (StringsEqual(tag, "bottom")) {
  362. return ReadNumericalValue(value, "%d", dialog_space[LIST_B_SPACE]);
  363. } else if (StringsEqual(tag, "left")) {
  364. return ReadNumericalValue(value, "%d", dialog_space[LIST_L_SPACE]);
  365. } else if (StringsEqual(tag, "right")) {
  366. return ReadNumericalValue(value, "%d", dialog_space[LIST_R_SPACE]);
  367. } else {
  368. UnrecognizedTag();
  369. return false;
  370. }
  371. return true;
  372. }
  373. };
  374. static XML_ListParser ListParser;
  375. class XML_SliderParser : public XML_ElementParser {
  376. public:
  377. XML_SliderParser() : XML_ElementParser("slider") {}
  378. bool HandleAttribute(const char *tag, const char *value)
  379. {
  380. if (StringsEqual(tag, "top")) {
  381. return ReadNumericalValue(value, "%d", dialog_space[SLIDER_T_SPACE]);
  382. } else if (StringsEqual(tag, "left")) {
  383. return ReadNumericalValue(value, "%d", dialog_space[SLIDER_L_SPACE]);
  384. } else if (StringsEqual(tag, "right")) {
  385. return ReadNumericalValue(value, "%d", dialog_space[SLIDER_R_SPACE]);
  386. } else {
  387. UnrecognizedTag();
  388. return false;
  389. }
  390. return true;
  391. }
  392. };
  393. static XML_SliderParser SliderParser;
  394. class XML_ThemeParser : public XML_ElementParser {
  395. public:
  396. XML_ThemeParser() : XML_ElementParser("theme") {}
  397. };
  398. static XML_ThemeParser ThemeParser;
  399. XML_ElementParser *Theme_GetParser()
  400. {
  401. FrameParser.AddChild(&FrameImageParser);
  402. ThemeParser.AddChild(&FrameParser);
  403. BackgroundParser.AddChild(&BackgroundColorParser);
  404. ThemeParser.AddChild(&BackgroundParser);
  405. TitleParser.AddChild(&TitleFontParser);
  406. TitleParser.AddChild(&TitleColorParser);
  407. ThemeParser.AddChild(&TitleParser);
  408. ThemeParser.AddChild(&SpacerParser);
  409. ButtonParser.AddChild(&ButtonFontParser);
  410. ButtonParser.AddChild(&ButtonColorParser);
  411. ButtonParser.AddChild(&ButtonImageParser);
  412. ThemeParser.AddChild(&ButtonParser);
  413. LabelParser.AddChild(&LabelFontParser);
  414. LabelParser.AddChild(&LabelColorParser);
  415. ThemeParser.AddChild(&LabelParser);
  416. ItemParser.AddChild(&ItemFontParser);
  417. ItemParser.AddChild(&ItemColorParser);
  418. ThemeParser.AddChild(&ItemParser);
  419. MessageParser.AddChild(&MessageFontParser);
  420. MessageParser.AddChild(&MessageColorParser);
  421. ThemeParser.AddChild(&MessageParser);
  422. TextEntryParser.AddChild(&TextEntryFontParser);
  423. TextEntryParser.AddChild(&TextEntryColorParser);
  424. ThemeParser.AddChild(&TextEntryParser);
  425. ListParser.AddChild(&ListImageParser);
  426. ListParser.AddChild(&TroughParser);
  427. ThumbParser.AddChild(&ThumbImageParser);
  428. ListParser.AddChild(&ThumbParser);
  429. ThemeParser.AddChild(&ListParser);
  430. SliderParser.AddChild(&SliderImageParser);
  431. ThemeParser.AddChild(&SliderParser);
  432. return &ThemeParser;
  433. }
  434. /*
  435. * Load theme
  436. */
  437. void load_theme(FileSpecifier &theme)
  438. {
  439. // Unload previous theme
  440. unload_theme();
  441. // Set defaults, the theme overrides these
  442. set_theme_defaults();
  443. // Parse theme MML script
  444. FileSpecifier theme_mml = theme + "theme.mml";
  445. XML_Loader_SDL loader;
  446. loader.CurrentElement = &RootParser;
  447. loader.ParseFile(theme_mml);
  448. // Open resource file
  449. FileSpecifier theme_rsrc = theme + "resources";
  450. theme_rsrc.Open(theme_resources);
  451. clear_game_error();
  452. // Load fonts
  453. for (int i=0; i<NUM_DIALOG_FONTS; i++)
  454. dialog_font[i] = load_font(dialog_font_spec[i]);
  455. // Load images
  456. for (int i=0; i<NUM_DIALOG_IMAGES; i++) {
  457. FileSpecifier file = theme + dialog_image_spec[i].name;
  458. SDL_Surface *s =SDL_LoadBMP(file.GetPath());
  459. if (s)
  460. SDL_SetColorKey(s, SDL_SRCCOLORKEY, SDL_MapRGB(s->format, 0x00, 0xff, 0xff));
  461. dialog_image[i] = s;
  462. }
  463. }
  464. /*
  465. * Set theme default values
  466. */
  467. static const SDL_Color default_dialog_color[NUM_DIALOG_COLORS] = {
  468. {0x00, 0x00, 0x00}, // BACKGROUND COLOR
  469. {0xc0, 0xc0, 0xc0}, // TITLE_COLOR
  470. {0xc0, 0x00, 0x00}, // BUTTON_COLOR
  471. {0xff, 0xff, 0xff}, // BUTTON_ACTIVE_COLOR
  472. {0x20, 0x20, 0xff}, // LABEL_COLOR
  473. {0x80, 0x80, 0xff}, // LABEL_ACTIVE_COLOR
  474. {0x40, 0xff, 0x40}, // ITEM_COLOR
  475. {0xff, 0xff, 0xff}, // ITEM_ACTIVE_COLOR
  476. {0xff, 0xff, 0xff}, // MESSAGE_COLOR
  477. {0x40, 0xff, 0x40}, // TEXT_ENTRY_COLOR
  478. {0xff, 0xff, 0xff}, // TEXT_ENTRY_ACTIVE_COLOR
  479. {0xff, 0xff, 0xff}, // TEXT_ENTRY_CURSOR_COLOR
  480. {0x60, 0x60, 0x60} // KEY_BINDING_COLOR
  481. };
  482. static const int default_dialog_space[NUM_DIALOG_SPACES] = {
  483. 6, // FRAME_T_SPACE
  484. 6, // FRAME_L_SPACE
  485. 6, // FRAME_R_SPACE
  486. 6, // FRAME_B_SPACE
  487. 8, // SPACER_HEIGHT
  488. 16, // LABEL_ITEM_SPACE
  489. 2, // LIST_T_SPACE
  490. 2, // LIST_L_SPACE
  491. 18, // LIST_R_SPACE
  492. 2, // LIST_B_SPACE
  493. 0, // TROUGH_T_SPACE
  494. 16, // TROUGH_R_SPACE
  495. 0, // TROUGH_B_SPACE
  496. 16, // TROUGH_WIDTH
  497. 0, // SLIDER_T_SPACE
  498. 0, // SLIDER_L_SPACE
  499. 0, // SLIDER_R_SPACE
  500. 0, // BUTTON_T_SPACE
  501. 0, // BUTTON_L_SPACE
  502. 0, // BUTTON_R_SPACE
  503. 14 // BUTTON_HEIGHT
  504. };
  505. static void set_theme_defaults(void)
  506. {
  507. for (int i=0; i<NUM_DIALOG_FONTS; i++)
  508. dialog_font[i] = NULL;
  509. for (int i=0; i<NUM_DIALOG_COLORS; i++)
  510. dialog_color[i] = default_dialog_color[i];
  511. for (int i=0; i<NUM_DIALOG_IMAGES; i++) {
  512. dialog_image_spec[i].name = "";
  513. dialog_image_spec[i].scale = false;
  514. dialog_image[i] = NULL;
  515. }
  516. for (int i=0; i<NUM_DIALOG_SPACES; i++)
  517. dialog_space[i] = default_dialog_space[i];
  518. }
  519. /*
  520. * Unload theme
  521. */
  522. static void unload_theme(void)
  523. {
  524. // Unload fonts
  525. for (int i=0; i<NUM_DIALOG_FONTS; i++)
  526. if (dialog_font[i]) {
  527. unload_font(dialog_font[i]);
  528. dialog_font[i] = NULL;
  529. }
  530. // Free surfaces
  531. for (int i=0; i<NUM_DIALOG_IMAGES; i++)
  532. if (dialog_image[i]) {
  533. SDL_FreeSurface(dialog_image[i]);
  534. dialog_image[i] = NULL;
  535. }
  536. // Close resource file
  537. theme_resources.Close();
  538. }
  539. /*
  540. * Get dialog font/color/image/space from theme
  541. */
  542. const sdl_font_info *get_dialog_font(int which, uint16 &style)
  543. {
  544. assert(which >= 0 && which < NUM_DIALOG_FONTS);
  545. const sdl_font_info *font = dialog_font[which];
  546. if (font) {
  547. style = dialog_font_spec[which].style;
  548. return font;
  549. } else {
  550. style = styleNormal;
  551. return default_font;
  552. }
  553. }
  554. uint32 get_dialog_color(int which)
  555. {
  556. assert(which >= 0 && which < NUM_DIALOG_COLORS);
  557. return SDL_MapRGB(dialog_surface->format, dialog_color[which].r, dialog_color[which].g, dialog_color[which].b);
  558. }
  559. // ZZZ: added this for convenience; taken from w_player_color::draw().
  560. // Obviously, this color does not come from the theme.
  561. uint32 get_dialog_player_color(int colorIndex) {
  562. SDL_Color c;
  563. _get_interface_color(PLAYER_COLOR_BASE_INDEX + colorIndex, &c);
  564. return SDL_MapRGB(dialog_surface->format, c.r, c.g, c.b);
  565. }
  566. SDL_Surface *get_dialog_image(int which, int width, int height)
  567. {
  568. assert(which >= 0 && which < NUM_DIALOG_IMAGES);
  569. SDL_Surface *s = dialog_image[which];
  570. if (s == NULL)
  571. s = default_image;
  572. // If no width and height is given, the surface is returned as-is and must
  573. // not be freed by the caller
  574. if (width == 0 && height == 0)
  575. return s;
  576. // Otherwise, a new tiled/rescaled surface is created which must be freed
  577. // by the caller
  578. int req_width = width ? width : s->w;
  579. int req_height = height ? height : s->h;
  580. SDL_Surface *s2 = dialog_image_spec[which].scale ? rescale_surface(s, req_width, req_height) : tile_surface(s, req_width, req_height);
  581. SDL_SetColorKey(s2, SDL_SRCCOLORKEY, SDL_MapRGB(s2->format, 0x00, 0xff, 0xff));
  582. return s2;
  583. }
  584. int get_dialog_space(int which)
  585. {
  586. assert(which >= 0 && which < NUM_DIALOG_SPACES);
  587. return dialog_space[which];
  588. }
  589. /*
  590. * Play dialog sound
  591. */
  592. void play_dialog_sound(int which)
  593. {
  594. play_sound(which, NULL, NONE);
  595. }
  596. /*
  597. * Dialog destructor
  598. */
  599. dialog::~dialog()
  600. {
  601. // Free all widgets
  602. vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
  603. while (i != end) {
  604. delete *i;
  605. i++;
  606. }
  607. }
  608. /*
  609. * Add widget
  610. */
  611. void dialog::add(widget *w)
  612. {
  613. widgets.push_back(w);
  614. w->set_owning_dialog(this);
  615. }
  616. /*
  617. * Layout dialog
  618. */
  619. void dialog::layout()
  620. {
  621. // Layout all widgets, calculate total width and height
  622. int y = get_dialog_space(FRAME_T_SPACE);
  623. int left = 0;
  624. int right = 0;
  625. vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
  626. while (i != end) {
  627. widget *w = *i;
  628. w->rect.y = y;
  629. y += w->layout();
  630. if (w->rect.x < left)
  631. left = w->rect.x;
  632. if (w->rect.x + w->rect.w > right)
  633. right = w->rect.x + w->rect.w;
  634. i++;
  635. }
  636. left = abs(left);
  637. rect.w = (left > right ? left : right) * 2 + get_dialog_space(FRAME_L_SPACE) + get_dialog_space(FRAME_R_SPACE);
  638. rect.h = y + get_dialog_space(FRAME_B_SPACE);
  639. // Center dialog on video surface
  640. SDL_Surface *video = SDL_GetVideoSurface();
  641. rect.x = (video->w - rect.w) / 2;
  642. rect.y = (video->h - rect.h) / 2;
  643. // Transform positions of all widgets to be relative to the dialog surface
  644. i = widgets.begin();
  645. int offset_x = (rect.w - get_dialog_space(FRAME_L_SPACE) - get_dialog_space(FRAME_R_SPACE)) / 2 + get_dialog_space(FRAME_L_SPACE);
  646. // ZZZ: to provide more detailed layout info to widgets
  647. int leftmost_x = get_dialog_space(FRAME_L_SPACE);
  648. int usable_width = rect.w - leftmost_x - get_dialog_space(FRAME_R_SPACE);
  649. while (i != end) {
  650. (*i)->rect.x += offset_x;
  651. (*i)->capture_layout_information(leftmost_x, usable_width);
  652. i++;
  653. }
  654. }
  655. /*
  656. * Update part of dialog on screen
  657. */
  658. void dialog::update(SDL_Rect r) const
  659. {
  660. SDL_Surface *video = SDL_GetVideoSurface();
  661. SDL_Rect dst_rect = {r.x + rect.x, r.y + rect.y, r.w, r.h};
  662. SDL_BlitSurface(dialog_surface, &r, video, &dst_rect);
  663. SDL_UpdateRects(video, 1, &dst_rect);
  664. #ifdef HAVE_OPENGL
  665. if (video->flags & SDL_OPENGL)
  666. SDL_GL_SwapBuffers();
  667. #endif
  668. }
  669. /*
  670. * Draw dialog
  671. */
  672. void dialog::draw_widget(widget *w, bool do_update) const
  673. {
  674. // Clear and redraw widget
  675. SDL_FillRect(dialog_surface, &w->rect, get_dialog_color(BACKGROUND_COLOR));
  676. w->draw(dialog_surface);
  677. w->dirty = false;
  678. // Blit to screen
  679. if (do_update)
  680. update(w->rect);
  681. }
  682. static void draw_frame_image(SDL_Surface *s, int x, int y)
  683. {
  684. SDL_Rect r = {x, y, s->w, s->h};
  685. SDL_BlitSurface(s, NULL, dialog_surface, &r);
  686. }
  687. void dialog::draw(void) const
  688. {
  689. // Draw frame
  690. draw_frame_image(frame_tl, 0, 0);
  691. draw_frame_image(frame_t, frame_tl->w, 0);
  692. draw_frame_image(frame_tr, frame_tl->w + frame_t->w, 0);
  693. draw_frame_image(frame_l, 0, frame_tl->h);
  694. draw_frame_image(frame_r, rect.w - frame_r->w, frame_tr->h);
  695. draw_frame_image(frame_bl, 0, frame_tl->h + frame_l->h);
  696. draw_frame_image(frame_b, frame_bl->w, rect.h - frame_b->h);
  697. draw_frame_image(frame_br, frame_bl->w + frame_b->w, frame_tr->h + frame_r->h);
  698. // Draw all widgets
  699. vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
  700. while (i != end) {
  701. draw_widget(*i, false);
  702. i++;
  703. }
  704. // Blit to screen
  705. SDL_Rect r = {0, 0, rect.w, rect.h};
  706. update(r);
  707. }
  708. /*
  709. * Deactivate currently active widget
  710. */
  711. void dialog::deactivate_currently_active_widget(bool draw)
  712. {
  713. if (active_widget) {
  714. active_widget->active = false;
  715. if (draw)
  716. draw_widget(active_widget);
  717. active_widget = NULL;
  718. active_widget_num = NONE;
  719. }
  720. }
  721. /*
  722. * Activate widget
  723. */
  724. void dialog::activate_widget(int num, bool draw)
  725. {
  726. if (num == active_widget_num)
  727. return;
  728. if (!widgets[num]->is_selectable())
  729. return;
  730. // Deactivate previously active widget
  731. deactivate_currently_active_widget(draw);
  732. // Activate new widget
  733. active_widget = widgets[num];
  734. active_widget_num = num;
  735. active_widget->active = true;
  736. if (draw) {
  737. draw_widget(active_widget);
  738. // play_dialog_sound(DIALOG_SELECT_SOUND);
  739. }
  740. }
  741. /*
  742. * Activate first selectable widget (don't draw)
  743. */
  744. void dialog::activate_first_widget(void)
  745. {
  746. for (unsigned i=0; i<widgets.size(); i++) {
  747. if (widgets[i]->is_selectable()) {
  748. activate_widget(i, false);
  749. break;
  750. }
  751. }
  752. }
  753. /*
  754. * Activate next/previous selectable widget
  755. */
  756. void dialog::activate_next_widget(void)
  757. {
  758. int i = active_widget_num;
  759. do {
  760. i++;
  761. if (i >= int(widgets.size()))
  762. i = 0;
  763. } while (!widgets[i]->is_selectable() && i != active_widget_num);
  764. // Either widgets[i] is selectable, or i == active_widget_num (in which case we wrapped all the way around)
  765. if (widgets[i]->is_selectable())
  766. activate_widget(i);
  767. else
  768. deactivate_currently_active_widget();
  769. }
  770. void dialog::activate_prev_widget(void)
  771. {
  772. int i = active_widget_num;
  773. do {
  774. if (i == 0)
  775. i = widgets.size() - 1;
  776. else
  777. i--;
  778. } while (!widgets[i]->is_selectable() && i != active_widget_num);
  779. // Either widgets[i] is selectable, or i == active_widget_num (in which case we wrapped all the way around)
  780. if (widgets[i]->is_selectable())
  781. activate_widget(i);
  782. else
  783. deactivate_currently_active_widget();
  784. }
  785. /*
  786. * Find widget given video surface coordinates (<0 = none found)
  787. */
  788. int dialog::find_widget(int x, int y)
  789. {
  790. // Transform to dialog coordinates
  791. x -= rect.x;
  792. y -= rect.y;
  793. // Find widget
  794. vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
  795. int num = 0;
  796. while (i != end) {
  797. widget *w = *i;
  798. if (x >= w->rect.x && y >= w->rect.y && x < w->rect.x + w->rect.w && y < w->rect.y + w->rect.h)
  799. return num;
  800. i++; num++;
  801. }
  802. return -1;
  803. }
  804. /*
  805. * Find widget by its numeric ID
  806. */
  807. widget *dialog::get_widget_by_id(short inID) const
  808. {
  809. // Find first matching widget
  810. vector<widget *>::const_iterator i = widgets.begin(), end = widgets.end();
  811. while (i != end) {
  812. widget *w = *i;
  813. if (w->get_identifier() == inID)
  814. return w;
  815. i++;
  816. }
  817. return NULL;
  818. }
  819. /*
  820. * Handle event
  821. */
  822. void dialog::event(SDL_Event &e)
  823. {
  824. // First pass event to active widget (which may modify it)
  825. if (active_widget)
  826. active_widget->event(e);
  827. // Remaining events handled by dialog
  828. switch (e.type) {
  829. // Key pressed
  830. case SDL_KEYDOWN:
  831. switch (e.key.keysym.sym) {
  832. case SDLK_ESCAPE: // ESC = Exit dialog
  833. quit(-1);
  834. break;
  835. case SDLK_UP: // Up = Activate previous widget
  836. case SDLK_LEFT:
  837. activate_prev_widget();
  838. break;
  839. case SDLK_DOWN: // Down = Activate next widget
  840. case SDLK_RIGHT:
  841. activate_next_widget();
  842. break;
  843. case SDLK_TAB:
  844. if (e.key.keysym.mod & KMOD_SHIFT)
  845. activate_prev_widget();
  846. else
  847. activate_next_widget();
  848. break;
  849. case SDLK_RETURN: // Return = Action on widget
  850. active_widget->click(0, 0);
  851. break;
  852. case SDLK_F9: // F9 = Screen dump
  853. dump_screen();
  854. break;
  855. default:
  856. break;
  857. }
  858. break;
  859. // Mouse moved
  860. case SDL_MOUSEMOTION: {
  861. int x = e.motion.x, y = e.motion.y;
  862. int num = find_widget(x, y);
  863. if (num >= 0) {
  864. activate_widget(num);
  865. widget *w = widgets[num];
  866. w->mouse_move(x - rect.x - w->rect.x, y - rect.y - w->rect.y);
  867. }
  868. break;
  869. }
  870. // Mouse button pressed
  871. case SDL_MOUSEBUTTONDOWN: {
  872. int x = e.button.x, y = e.button.y;
  873. int num = find_widget(x, y);
  874. if (num >= 0) {
  875. widget *w = widgets[num];
  876. w->click(x - rect.x - w->rect.x, y - rect.y - w->rect.y);
  877. }
  878. break;
  879. }
  880. // Quit requested
  881. case SDL_QUIT:
  882. exit(0);
  883. break;
  884. }
  885. // Redraw dirty widgets
  886. for (unsigned i=0; i<widgets.size(); i++)
  887. if (widgets[i]->dirty)
  888. draw_widget(widgets[i]);
  889. }
  890. /*
  891. * Run dialog modally, returns result code (0 = ok, -1 = cancel)
  892. */
  893. int dialog::run(bool intro_exit_sounds)
  894. {
  895. // Put dialog on screen
  896. start(intro_exit_sounds);
  897. // Run dialog loop
  898. while (!done) {
  899. // Process events
  900. process_events();
  901. if (done)
  902. break;
  903. // Run custom processing function
  904. if (processing_function != NULL)
  905. processing_function(this);
  906. // Give time to system
  907. global_idle_proc();
  908. SDL_Delay(10);
  909. }
  910. // Remove dialog from screen
  911. return finish(intro_exit_sounds);
  912. }
  913. /*
  914. * Put dialog on screen
  915. */
  916. void dialog::start(bool play_sound)
  917. {
  918. // Make sure nobody tries re-entrancy with us
  919. assert(!done);
  920. // Set new active dialog
  921. parent_dialog = top_dialog;
  922. top_dialog = this;
  923. // Clear dialog surface
  924. SDL_FillRect(dialog_surface, NULL, get_dialog_color(BACKGROUND_COLOR));
  925. // Activate first widget
  926. activate_first_widget();
  927. // Layout dialog
  928. layout();
  929. // Get frame images
  930. frame_tl = get_dialog_image(FRAME_TL_IMAGE);
  931. frame_tr = get_dialog_image(FRAME_TR_IMAGE);
  932. frame_bl = get_dialog_image(FRAME_BL_IMAGE);
  933. frame_br = get_dialog_image(FRAME_BR_IMAGE);
  934. frame_t = get_dialog_image(FRAME_T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
  935. frame_l = get_dialog_image(FRAME_L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
  936. frame_r = get_dialog_image(FRAME_R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
  937. frame_b = get_dialog_image(FRAME_B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
  938. // Draw dialog
  939. draw();
  940. // Show cursor
  941. cursor_was_visible = SDL_ShowCursor(true);
  942. // Welcome sound
  943. if (play_sound)
  944. play_dialog_sound(DIALOG_INTRO_SOUND);
  945. // Enable unicode key translation
  946. // (ZZZ: hmm maybe this should be done on entering/leaving run_a_little?
  947. // I guess really we need to "push" the current UNICODE state and set it true on entering run_a_little,
  948. // then "pop" the previous state on leaving run_a_little. In the meantime, we hope nobody tries their own
  949. // event processing between dialog::start and dialog::finish (or at least is prepared to live with UNICODE) :) )
  950. SDL_EnableUNICODE(true);
  951. // Prepare for dialog event loop
  952. result = 0;
  953. done = false;
  954. }
  955. /*
  956. * Process pending dialog events
  957. */
  958. bool dialog::process_events()
  959. {
  960. while (!done) {
  961. // Get next event
  962. SDL_Event e;
  963. e.type = SDL_NOEVENT;
  964. SDL_PollEvent(&e);
  965. if (e.type == SDL_NOEVENT)
  966. break;
  967. // Handle event
  968. event(e);
  969. }
  970. return done;
  971. }
  972. /*
  973. * Remove dialog from screen
  974. */
  975. int dialog::finish(bool play_sound)
  976. {
  977. // Disable unicode key translation
  978. SDL_EnableUNICODE(false);
  979. // Farewell sound
  980. if (play_sound)
  981. play_dialog_sound(result == 0 ? DIALOG_OK_SOUND : DIALOG_CANCEL_SOUND);
  982. // Hide cursor
  983. if (!cursor_was_visible)
  984. SDL_ShowCursor(false);
  985. // Clear dialog surface
  986. SDL_FillRect(dialog_surface, NULL, get_dialog_color(BACKGROUND_COLOR));
  987. // Erase dialog from screen
  988. SDL_Surface *video = SDL_GetVideoSurface();
  989. SDL_FillRect(video, &rect, SDL_MapRGB(video->format, 0, 0, 0));
  990. SDL_UpdateRects(video, 1, &rect);
  991. #ifdef HAVE_OPENGL
  992. if (video->flags & SDL_OPENGL)
  993. SDL_GL_SwapBuffers();
  994. #endif
  995. // Free frame images
  996. if (frame_t) SDL_FreeSurface(frame_t);
  997. if (frame_l) SDL_FreeSurface(frame_l);
  998. if (frame_r) SDL_FreeSurface(frame_r);
  999. if (frame_b) SDL_FreeSurface(frame_b);
  1000. // Restore active dialog
  1001. top_dialog = parent_dialog;
  1002. parent_dialog = NULL;
  1003. if (top_dialog) {
  1004. clear_screen();
  1005. top_dialog->draw();
  1006. }
  1007. // Allow dialog to be run again later
  1008. done = false;
  1009. return result;
  1010. }
  1011. /*
  1012. * Quit dialog, return result
  1013. */
  1014. void dialog::quit(int r)
  1015. {
  1016. result = r;
  1017. done = true;
  1018. }
  1019. /*
  1020. * Standard callback functions
  1021. */
  1022. void dialog_ok(void *arg)
  1023. {
  1024. dialog *d = (dialog *)arg;
  1025. d->quit(0);
  1026. }
  1027. void dialog_cancel(void *arg)
  1028. {
  1029. dialog *d = (dialog *)arg;
  1030. d->quit(-1);
  1031. }
  1032. // ZZZ: commonly-used callback for text entry enter_pressed_callback
  1033. void dialog_try_ok(w_text_entry* text_entry) {
  1034. w_button* ok_button = dynamic_cast<w_button*>(text_entry->get_owning_dialog()->get_widget_by_id(iOK));
  1035. // This is arguably a violation of the sdl_dialog/sdl_widget standard behavior since
  1036. // ok_button is clicked without first becoming the active widget. With the current
  1037. // implementation of w_button::click, should not be a problem...
  1038. if(ok_button != NULL)
  1039. ok_button->click(0,0);
  1040. }
  1041. // ZZZ: commonly-used callback to enable/disable "OK" based on whether a text_entry has data
  1042. void dialog_disable_ok_if_empty(w_text_entry* inTextEntry) {
  1043. modify_control_enabled(inTextEntry->get_owning_dialog(), iOK,
  1044. (inTextEntry->get_text()[0] == 0) ? CONTROL_INACTIVE : CONTROL_ACTIVE);
  1045. }