00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "global.hpp"
00019
00020 #include "about.hpp"
00021 #include "cursor.hpp"
00022 #include "display.hpp"
00023 #include "events.hpp"
00024 #include "game_config.hpp"
00025 #include "game_preferences.hpp"
00026 #include "gettext.hpp"
00027 #include "help.hpp"
00028 #include "image.hpp"
00029 #include "language.hpp"
00030 #include "marked-up_text.hpp"
00031 #include "log.hpp"
00032 #include "sdl_utils.hpp"
00033 #include "sound.hpp"
00034 #include "construct_dialog.hpp"
00035 #include "unit.hpp"
00036 #include "util.hpp"
00037 #include "video.hpp"
00038 #include "wml_separators.hpp"
00039 #include "serialization/parser.hpp"
00040 #include "serialization/string_utils.hpp"
00041 #include "widgets/button.hpp"
00042 #include "widgets/menu.hpp"
00043 #include "widgets/scrollbar.hpp"
00044 #include "widgets/widget.hpp"
00045
00046 #include <algorithm>
00047 #include <iostream>
00048 #include <list>
00049 #include <locale>
00050 #include <map>
00051 #include <queue>
00052 #include <set>
00053 #include <sstream>
00054
00055 #define DBG_HELP LOG_STREAM(debug, help)
00056 #define LOG_HELP LOG_STREAM(info, help)
00057 #define ERR_HELP LOG_STREAM(err, help)
00058
00059 namespace help {
00060
00061 help_button::help_button(display& disp, const std::string &help_topic)
00062 : dialog_button(disp.video(), _("Help")), disp_(disp), topic_(help_topic), help_hand_(NULL)
00063 {}
00064
00065 int help_button::action(gui::dialog_process_info &info) {
00066 if(!topic_.empty()) {
00067 show_help();
00068 info.clear_buttons();
00069 }
00070 return gui::CONTINUE_DIALOG;
00071 }
00072
00073 void help_button::show_help()
00074 {
00075 help::show_help(disp_, topic_);
00076 }
00077
00078 bool help_button::can_execute_command(hotkey::HOTKEY_COMMAND cmd, int) const
00079 {
00080 return (topic_.empty() == false && cmd == hotkey::HOTKEY_HELP) || cmd == hotkey::HOTKEY_SCREENSHOT;
00081 }
00082
00083 void help_button::join() {
00084 dialog_button::join();
00085
00086
00087 delete help_hand_;
00088 help_hand_ = new hotkey::basic_handler(&disp_, this);
00089 }
00090
00091 void help_button::leave() {
00092 dialog_button::leave();
00093
00094
00095 delete help_hand_;
00096 help_hand_ = NULL;
00097 }
00098
00099
00100
00101 static void generate_contents();
00102
00103 struct section;
00104
00105 typedef std::vector<section *> section_list;
00106
00107
00108 class topic_generator
00109 {
00110 unsigned count;
00111 friend class topic_text;
00112 public:
00113 topic_generator(): count(1) {}
00114 virtual std::string operator()() const = 0;
00115 virtual ~topic_generator() {}
00116 };
00117
00118 class text_topic_generator: public topic_generator {
00119 std::string text_;
00120 public:
00121 text_topic_generator(std::string const &t): text_(t) {}
00122 virtual std::string operator()() const { return text_; }
00123 };
00124
00125
00126
00127 class topic_text
00128 {
00129 mutable std::vector< std::string > parsed_text_;
00130 mutable topic_generator *generator_;
00131 public:
00132 ~topic_text();
00133 topic_text(): generator_(NULL) {}
00134 topic_text(std::string const &t): generator_(new text_topic_generator(t)) {}
00135 explicit topic_text(topic_generator *g): generator_(g) {}
00136 topic_text &operator=(topic_generator *g);
00137 topic_text(topic_text const &t);
00138
00139 const std::vector<std::string>& parsed_text() const;
00140 };
00141
00142
00143 struct topic
00144 {
00145 topic() {}
00146 topic(const std::string &_title, const std::string &_id)
00147 : title(_title), id(_id) {}
00148 topic(const std::string &_title, const std::string &_id, const std::string &_text)
00149 : title(_title), id(_id), text(_text) {}
00150 topic(const std::string &_title, const std::string &_id, topic_generator *g)
00151 : title(_title), id(_id), text(g) {}
00152
00153 bool operator==(const topic &) const;
00154 bool operator!=(const topic &t) const { return !operator==(t); }
00155
00156 bool operator<(const topic &) const;
00157 std::string title, id;
00158 mutable topic_text text;
00159 };
00160
00161 typedef std::list<topic> topic_list;
00162
00163
00164 struct section {
00165 section() : title(""), id("") {}
00166 section(const section&);
00167 section& operator=(const section&);
00168 ~section();
00169
00170 bool operator==(const section &) const;
00171
00172 bool operator<(const section &) const;
00173
00174
00175 void add_section(const section &s);
00176
00177 void clear();
00178 std::string title, id;
00179 topic_list topics;
00180 section_list sections;
00181 int level;
00182 };
00183
00184
00185
00186
00187 class has_id
00188 {
00189 public:
00190 has_id(const std::string &id) : id_(id) {}
00191 bool operator()(const topic &t) { return t.id == id_; }
00192 bool operator()(const section &s) { return s.id == id_; }
00193 bool operator()(const section *s) { return s != NULL && s->id == id_; }
00194 private:
00195 const std::string id_;
00196 };
00197
00198
00199 class title_less
00200 {
00201 public:
00202 bool operator()(const topic &t1, const topic &t2) {
00203 return strcoll(t1.title.c_str(), t2.title.c_str()) < 0; }
00204 };
00205
00206
00207 class section_less
00208 {
00209 public:
00210 bool operator()(const section* s1, const section* s2) {
00211 return strcoll(s1->title.c_str(), s2->title.c_str()) < 0; }
00212 };
00213
00214
00215 struct delete_section
00216 {
00217 void operator()(section *s) { delete s; }
00218 };
00219
00220 struct create_section
00221 {
00222 section *operator()(const section *s) { return new section(*s); }
00223 section *operator()(const section &s) { return new section(s); }
00224 };
00225
00226
00227
00228 class help_menu : public gui::menu
00229 {
00230 public:
00231 help_menu(CVideo &video, const section &toplevel, int max_height=-1);
00232 int process();
00233
00234
00235
00236 void select_topic(const topic &t);
00237
00238
00239
00240
00241 const topic *chosen_topic();
00242
00243 private:
00244
00245 struct visible_item {
00246 visible_item(const section *_sec, const std::string &visible_string);
00247 visible_item(const topic *_t, const std::string &visible_string);
00248
00249
00250 const topic *t;
00251 const section *sec;
00252 std::string visible_string;
00253 bool operator==(const visible_item &vis_item) const;
00254 bool operator==(const section &sec) const;
00255 bool operator==(const topic &t) const;
00256 };
00257
00258
00259
00260 void update_visible_items(const section &top_level, unsigned starting_level=0);
00261
00262
00263 bool expanded(const section &sec);
00264
00265
00266
00267 void expand(const section &sec);
00268
00269
00270
00271 void contract(const section &sec);
00272
00273
00274
00275 std::string indented_icon(const std::string &icon, const unsigned level);
00276
00277
00278 std::string get_string_to_show(const section &sec, const unsigned level);
00279
00280
00281 std::string get_string_to_show(const topic &topic, const unsigned level);
00282
00283
00284 void display_visible_items();
00285
00286
00287
00288 bool select_topic_internal(const topic &t, const section &sec);
00289
00290 std::vector<visible_item> visible_items_;
00291 const section &toplevel_;
00292 std::set<const section*> expanded_;
00293 surface_restorer restorer_;
00294 SDL_Rect rect_;
00295 topic const *chosen_topic_;
00296 visible_item selected_item_;
00297 };
00298
00299
00300 struct parse_error
00301 {
00302 parse_error(const std::string& msg) : message(msg) {}
00303 std::string message;
00304 };
00305
00306
00307 class help_text_area : public gui::scrollarea
00308 {
00309 public:
00310 help_text_area(CVideo &video, const section &toplevel);
00311
00312 void show_topic(const topic &t);
00313
00314
00315
00316
00317 std::string ref_at(const int x, const int y);
00318
00319 protected:
00320 virtual void scroll(unsigned int pos);
00321 virtual void set_inner_location(const SDL_Rect& rect);
00322
00323 private:
00324 enum ALIGNMENT {LEFT, MIDDLE, RIGHT, HERE};
00325
00326
00327 ALIGNMENT str_to_align(const std::string &s);
00328
00329
00330
00331 struct item {
00332
00333 item(surface surface, int x, int y, const std::string text="",
00334 const std::string reference_to="", bool floating=false,
00335 bool box=false, ALIGNMENT alignment=HERE);
00336
00337 item(surface surface, int x, int y,
00338 bool floating, bool box=false, ALIGNMENT=HERE);
00339
00340
00341 SDL_Rect rect;
00342
00343 surface surf;
00344
00345
00346 std::string text;
00347
00348
00349
00350 std::string ref_to;
00351
00352
00353
00354 bool floating;
00355 bool box;
00356 ALIGNMENT align;
00357 };
00358
00359
00360 class item_at {
00361 public:
00362 item_at(const int x, const int y) : x_(x), y_(y) {}
00363 bool operator()(const item&) const;
00364 private:
00365 const int x_, y_;
00366 };
00367
00368
00369
00370 void set_items();
00371
00372
00373
00374
00375 void handle_ref_cfg(const config &cfg);
00376 void handle_img_cfg(const config &cfg);
00377 void handle_bold_cfg(const config &cfg);
00378 void handle_italic_cfg(const config &cfg);
00379 void handle_header_cfg(const config &cfg);
00380 void handle_jump_cfg(const config &cfg);
00381 void handle_format_cfg(const config &cfg);
00382
00383 void draw_contents();
00384
00385
00386
00387
00388
00389
00390 void add_text_item(const std::string text, const std::string ref_dst="",
00391 int font_size=-1, bool bold=false, bool italic=false,
00392 SDL_Color color=font::NORMAL_COLOUR);
00393
00394
00395 void add_img_item(const std::string path, const std::string alignment, const bool floating,
00396 const bool box);
00397
00398
00399 void down_one_line();
00400
00401
00402
00403 void adjust_last_row();
00404
00405
00406 int get_remaining_width();
00407
00408
00409
00410
00411 int get_min_x(const int y, const int height=0);
00412
00413
00414 int get_max_x(const int y, const int height=0);
00415
00416
00417
00418
00419
00420
00421 int get_y_for_floating_img(const int width, const int x, const int desired_y);
00422
00423
00424
00425 void add_item(const item& itm);
00426
00427 std::list<item> items_;
00428 std::list<item *> last_row_;
00429 const section &toplevel_;
00430 topic const *shown_topic_;
00431 const int title_spacing_;
00432
00433 std::pair<int, int> curr_loc_;
00434 const unsigned min_row_height_;
00435 unsigned curr_row_height_;
00436
00437 int contents_height_;
00438 };
00439
00440
00441 class help_browser : public gui::widget
00442 {
00443 public:
00444 help_browser(display &disp, const section &toplevel);
00445
00446 void adjust_layout();
00447
00448
00449
00450 void show_topic(const std::string &topic_id);
00451
00452 protected:
00453 virtual void update_location(SDL_Rect const &rect);
00454 virtual void process_event();
00455 virtual void handle_event(const SDL_Event &event);
00456
00457 private:
00458
00459
00460
00461 void update_cursor();
00462 void show_topic(const topic &t, bool save_in_history=true);
00463
00464
00465
00466 void move_in_history(std::deque<const topic *> &from, std::deque<const topic *> &to);
00467 display &disp_;
00468 help_menu menu_;
00469 help_text_area text_area_;
00470 const section &toplevel_;
00471 bool ref_cursor_;
00472 std::deque<const topic *> back_topics_, forward_topics_;
00473 gui::button back_button_, forward_button_;
00474 topic const *shown_topic_;
00475 };
00476
00477
00478
00479
00480
00481
00482
00483 static void generate_sections(const config *help_cfg, const std::string &generator, section &sec, int level);
00484 static std::vector<topic> generate_topics(const bool sort_topics,const std::string &generator);
00485 static std::string generate_topic_text(const std::string &generator, const config *help_cfg,
00486 const section &sec, const std::vector<topic>& generated_topics);
00487 static std::string generate_about_text();
00488 static std::string generate_contents_links(const std::string& section_name, config const *help_cfg);
00489 static std::string generate_contents_links(const section &sec, const std::vector<topic>& topics);
00490 static void generate_races_sections(const config *help_cfg, section &sec, int level);
00491 static std::vector<topic> generate_unit_topics(const bool, const std::string& race);
00492 enum UNIT_DESCRIPTION_TYPE {FULL_DESCRIPTION, NO_DESCRIPTION, NON_REVEALING_DESCRIPTION};
00493
00494
00495
00496
00497 static UNIT_DESCRIPTION_TYPE description_type(const unit_type &type);
00498 static std::vector<topic> generate_ability_topics(const bool);
00499 static std::vector<topic> generate_weapon_special_topics(const bool);
00500
00501
00502
00503 static section parse_config(const config *cfg);
00504
00505 static void parse_config_internal(const config *help_cfg, const config *section_cfg,
00506 section &sec, int level=0);
00507
00508
00509
00510 static bool section_is_referenced(const std::string §ion_id, const config &cfg);
00511
00512
00513 static bool topic_is_referenced(const std::string &topic_id, const config &cfg);
00514
00515
00516
00517
00518 static const topic *find_topic(const section &sec, const std::string &id);
00519
00520
00521
00522
00523 static const section *find_section(const section &sec, const std::string &id);
00524
00525
00526
00527
00528 static std::vector<std::string> parse_text(const std::string &text);
00529
00530
00531
00532 static std::string convert_to_wml(const std::string &element_name, const std::string &contents);
00533
00534
00535
00536 static bool get_bool(const std::string &s);
00537
00538
00539
00540 static SDL_Color string_to_color(const std::string &s);
00541
00542
00543 static std::vector<std::string> split_in_width(const std::string &s, const int font_size, const unsigned width);
00544
00545 static std::string remove_first_space(const std::string& text);
00546
00547
00548 static std::string to_lower(const std::string &s);
00549
00550
00551 static std::string escape(const std::string &s);
00552
00553
00554
00555 static std::string get_first_word(const std::string &s);
00556
00557 }
00558
00559 namespace {
00560 const config *game_cfg = NULL;
00561 gamemap *map = NULL;
00562
00563 help::section toplevel;
00564
00565 help::section hidden_sections;
00566
00567 int last_num_encountered_units = -1;
00568 int last_num_encountered_terrains = -1;
00569 bool last_debug_state = game_config::debug;
00570
00571 config dummy_cfg;
00572 std::vector<std::string> empty_string_vector;
00573 const int max_section_level = 15;
00574 const int menu_font_size = font::SIZE_NORMAL;
00575 const int title_size = font::SIZE_LARGE;
00576 const int title2_size = font::SIZE_15;
00577 const int box_width = 2;
00578 const int normal_font_size = font::SIZE_SMALL;
00579 const unsigned max_history = 100;
00580 const std::string topic_img = "help/topic.png";
00581 const std::string closed_section_img = "help/closed_section.png";
00582 const std::string open_section_img = "help/open_section.png";
00583 const std::string indentation_img = "help/indentation.png";
00584
00585 const std::string default_show_topic = "introduction_topic";
00586 const std::string unknown_unit_topic = ".unknown_unit";
00587 const std::string unit_prefix = "unit_";
00588 const std::string race_prefix = "race_";
00589
00590
00591 static std::string hidden_symbol(bool hidden = true) {
00592 return (hidden ? "." : "");
00593 }
00594
00595 static bool is_visible_id(const std::string &id) {
00596 return (id.empty() || id[0] != '.');
00597 }
00598
00599
00600
00601
00602 static bool is_valid_id(const std::string &id) {
00603 if (id == "toplevel") {
00604 return false;
00605 }
00606 if (id.find(unit_prefix) == 0 || id.find(hidden_symbol() + unit_prefix) == 0) {
00607 return false;
00608 }
00609 if (id.find("ability_") == 0) {
00610 return false;
00611 }
00612 if (id.find("weaponspecial_") == 0) {
00613 return false;
00614 }
00615 if (id == "hidden") {
00616 return false;
00617 }
00618 return true;
00619 }
00620
00621
00622
00623
00624 class about_text_formatter {
00625 public:
00626 std::string operator()(const std::string &s) {
00627 if (s.empty()) return s;
00628
00629 if (s[0] == '+')
00630 return " \n<header>text='" + help::escape(s.substr(1)) + "'</header>";
00631 if (s[0] == '-')
00632 return s.substr(1);
00633 return s;
00634 }
00635 };
00636
00637 }
00638
00639
00640 static std::string jump_to(const unsigned pos)
00641 {
00642 std::stringstream ss;
00643 ss << "<jump>to=" << pos << "</jump>";
00644 return ss.str();
00645 }
00646
00647 static std::string jump(const unsigned amount)
00648 {
00649 std::stringstream ss;
00650 ss << "<jump>amount=" << amount << "</jump>";
00651 return ss.str();
00652 }
00653
00654 static std::string bold(const std::string &s)
00655 {
00656 std::stringstream ss;
00657 ss << "<bold>text='" << help::escape(s) << "'</bold>";
00658 return ss.str();
00659 }
00660
00661 typedef std::vector<std::vector<std::pair<std::string, unsigned int > > > table_spec;
00662
00663
00664
00665
00666 static std::string generate_table(const table_spec &tab, const unsigned int spacing=font::relative_size(20))
00667 {
00668 table_spec::const_iterator row_it;
00669 std::vector<std::pair<std::string, unsigned> >::const_iterator col_it;
00670 unsigned int num_cols = 0;
00671 for (row_it = tab.begin(); row_it != tab.end(); row_it++) {
00672 if (row_it->size() > num_cols) {
00673 num_cols = row_it->size();
00674 }
00675 }
00676 std::vector<unsigned int> col_widths(num_cols, 0);
00677
00678 for (row_it = tab.begin(); row_it != tab.end(); row_it++) {
00679 unsigned int col = 0;
00680 for (col_it = row_it->begin(); col_it != row_it->end(); col_it++) {
00681 if (col_widths[col] < col_it->second + spacing) {
00682 col_widths[col] = col_it->second + spacing;
00683 }
00684 col++;
00685 }
00686 }
00687 std::vector<unsigned int> col_starts(num_cols);
00688
00689 for (unsigned int i = 0; i < num_cols; i++) {
00690 unsigned int this_col_start = 0;
00691 for (unsigned int j = 0; j < i; j++) {
00692 this_col_start += col_widths[j];
00693 }
00694 col_starts[i] = this_col_start;
00695 }
00696 std::stringstream ss;
00697 for (row_it = tab.begin(); row_it != tab.end(); row_it++) {
00698 unsigned int col = 0;
00699 for (col_it = row_it->begin(); col_it != row_it->end(); col_it++) {
00700 ss << jump_to(col_starts[col]) << col_it->first;
00701 col++;
00702 }
00703 ss << "\n";
00704 }
00705 return ss.str();
00706 }
00707
00708
00709 static unsigned image_width(const std::string &filename)
00710 {
00711 image::locator loc(filename);
00712 surface surf(image::get_image(loc));
00713 if (surf != NULL) {
00714 return surf->w;
00715 }
00716 return 0;
00717 }
00718
00719 static void push_tab_pair(std::vector<std::pair<std::string, unsigned int> > &v, const std::string &s)
00720 {
00721 v.push_back(std::make_pair(s, font::line_width(s, normal_font_size)));
00722 }
00723
00724 namespace help {
00725
00726 help_manager::help_manager(const config *cfg, gamemap *_map)
00727 {
00728 game_cfg = cfg == NULL ? &dummy_cfg : cfg;
00729 map = _map;
00730 }
00731
00732 void generate_contents()
00733 {
00734 toplevel.clear();
00735 hidden_sections.clear();
00736 if (game_cfg != NULL) {
00737 const config *help_config = game_cfg->child("help");
00738 if (help_config == NULL) {
00739 help_config = &dummy_cfg;
00740 }
00741 try {
00742 toplevel = parse_config(help_config);
00743
00744
00745
00746
00747
00748
00749 config hidden_toplevel;
00750 std::stringstream ss;
00751 config::const_child_itors itors;
00752 for (itors = help_config->child_range("section"); itors.first != itors.second;
00753 itors.first++) {
00754 const std::string id = (*(*itors.first))["id"];
00755 if (find_section(toplevel, id) == NULL) {
00756
00757
00758
00759 if (!section_is_referenced(id, *help_config)) {
00760 if (ss.str() != "") {
00761 ss << ",";
00762 }
00763 ss << id;
00764 }
00765 }
00766 }
00767 hidden_toplevel["sections"] = ss.str();
00768 ss.str("");
00769 for (itors = help_config->child_range("topic"); itors.first != itors.second;
00770 itors.first++) {
00771 const std::string id = (*(*itors.first))["id"];
00772 if (find_topic(toplevel, id) == NULL) {
00773 if (!topic_is_referenced(id, *help_config)) {
00774 if (ss.str() != "") {
00775 ss << ",";
00776 }
00777 ss << id;
00778 }
00779 }
00780 }
00781 hidden_toplevel["topics"] = ss.str();
00782 config hidden_cfg = *help_config;
00783
00784 hidden_cfg.clear_children("toplevel");
00785 hidden_cfg.add_child("toplevel", hidden_toplevel);
00786 hidden_sections = parse_config(&hidden_cfg);
00787 }
00788 catch (parse_error e) {
00789 std::stringstream msg;
00790 msg << "Parse error when parsing help text: '" << e.message << "'";
00791 std::cerr << msg.str() << std::endl;
00792 }
00793 }
00794 }
00795
00796 help_manager::~help_manager()
00797 {
00798 game_cfg = NULL;
00799 map = NULL;
00800 toplevel.clear();
00801 hidden_sections.clear();
00802
00803
00804 last_num_encountered_units = -1;
00805 last_num_encountered_terrains = -1;
00806 }
00807
00808 bool section_is_referenced(const std::string §ion_id, const config &cfg)
00809 {
00810 const config *toplevel = cfg.child("toplevel");
00811 if (toplevel != NULL) {
00812 const std::vector<std::string> toplevel_refs
00813 = utils::quoted_split((*toplevel)["sections"]);
00814 if (std::find(toplevel_refs.begin(), toplevel_refs.end(), section_id)
00815 != toplevel_refs.end()) {
00816 return true;
00817 }
00818 }
00819 for (config::const_child_itors itors = cfg.child_range("section");
00820 itors.first != itors.second; itors.first++) {
00821 const std::vector<std::string> sections_refd
00822 = utils::quoted_split((*(*itors.first))["sections"]);
00823 if (std::find(sections_refd.begin(), sections_refd.end(), section_id)
00824 != sections_refd.end()) {
00825 return true;
00826 }
00827 }
00828 return false;
00829 }
00830
00831 bool topic_is_referenced(const std::string &topic_id, const config &cfg)
00832 {
00833 const config *toplevel = cfg.child("toplevel");
00834 if (toplevel != NULL) {
00835 const std::vector<std::string> toplevel_refs
00836 = utils::quoted_split((*toplevel)["topics"]);
00837 if (std::find(toplevel_refs.begin(), toplevel_refs.end(), topic_id)
00838 != toplevel_refs.end()) {
00839 return true;
00840 }
00841 }
00842 for (config::const_child_itors itors = cfg.child_range("section");
00843 itors.first != itors.second; itors.first++) {
00844 const std::vector<std::string> topics_refd
00845 = utils::quoted_split((*(*itors.first))["topics"]);
00846 if (std::find(topics_refd.begin(), topics_refd.end(), topic_id)
00847 != topics_refd.end()) {
00848 return true;
00849 }
00850 }
00851 return false;
00852 }
00853
00854 void parse_config_internal(const config *help_cfg, const config *section_cfg,
00855 section &sec, int level)
00856 {
00857 if (level > max_section_level) {
00858 std::cerr << "Maximum section depth has been reached. Maybe circular dependency?"
00859 << std::endl;
00860 }
00861 else if (section_cfg != NULL) {
00862 const std::vector<std::string> sections = utils::quoted_split((*section_cfg)["sections"]);
00863 sec.level = level;
00864 const std::string id = level == 0 ? "toplevel" : (*section_cfg)["id"];
00865 if (level != 0) {
00866 if (!is_valid_id(id)) {
00867 std::stringstream ss;
00868 ss << "Invalid ID, used for internal purpose: '" << id << "'";
00869 throw parse_error(ss.str());
00870 }
00871 }
00872 const std::string title = level == 0 ? "" : (*section_cfg)["title"];
00873 sec.id = id;
00874 sec.title = title;
00875 std::vector<std::string>::const_iterator it;
00876
00877 for (it = sections.begin(); it != sections.end(); it++) {
00878 config const *child_cfg = help_cfg->find_child("section", "id", *it);
00879 if (child_cfg != NULL) {
00880 section child_section;
00881 parse_config_internal(help_cfg, child_cfg, child_section, level + 1);
00882 sec.add_section(child_section);
00883 }
00884 else {
00885 std::stringstream ss;
00886 ss << "Help-section '" << *it << "' referenced from '"
00887 << id << "' but could not be found.";
00888 throw parse_error(ss.str());
00889 }
00890 }
00891
00892 generate_sections(help_cfg, (*section_cfg)["sections_generator"], sec, level);
00893
00894 if ((*section_cfg)["sort_sections"] == "yes") {
00895 std::sort(sec.sections.begin(),sec.sections.end(), section_less());
00896 }
00897
00898 bool sort_topics = false;
00899 bool sort_generated = true;
00900
00901 if ((*section_cfg)["sort_topics"] == "yes") {
00902 sort_topics = true;
00903 sort_generated = false;
00904 } else if ((*section_cfg)["sort_topics"] == "no") {
00905 sort_topics = false;
00906 sort_generated = false;
00907 } else if ((*section_cfg)["sort_topics"] == "generated") {
00908 sort_topics = false;
00909 sort_generated = true;
00910 } else if ((*section_cfg)["sort_topics"] != "") {
00911 std::stringstream ss;
00912 ss << "Invalid sort option: '" << (*section_cfg)["sort_topics"] << "'";
00913 throw parse_error(ss.str());
00914 }
00915
00916 std::vector<topic> generated_topics =
00917 generate_topics(sort_generated,(*section_cfg)["generator"]);
00918
00919 const std::vector<std::string> topics_id = utils::quoted_split((*section_cfg)["topics"]);
00920 std::vector<topic> topics;
00921
00922
00923 for (it = topics_id.begin(); it != topics_id.end(); it++) {
00924 config const *topic_cfg = help_cfg->find_child("topic", "id", *it);
00925 if (topic_cfg != NULL) {
00926 std::string text = (*topic_cfg)["text"];
00927 text += generate_topic_text((*topic_cfg)["generator"], help_cfg, sec, generated_topics);
00928 topic child_topic((*topic_cfg)["title"], (*topic_cfg)["id"], text);
00929 if (!is_valid_id(child_topic.id)) {
00930 std::stringstream ss;
00931 ss << "Invalid ID, used for internal purpose: '" << id << "'";
00932 throw parse_error(ss.str());
00933 }
00934 topics.push_back(child_topic);
00935 }
00936 else {
00937 std::stringstream ss;
00938 ss << "Help-topic '" << *it << "' referenced from '" << id
00939 << "' but could not be found." << std::endl;
00940 throw parse_error(ss.str());
00941 }
00942 }
00943
00944 if (sort_topics) {
00945 std::sort(topics.begin(),topics.end(), title_less());
00946 std::sort(generated_topics.begin(),
00947 generated_topics.end(), title_less());
00948 std::merge(generated_topics.begin(),
00949 generated_topics.end(),topics.begin(),topics.end()
00950 ,std::back_inserter(sec.topics),title_less());
00951 }
00952 else {
00953 std::copy(topics.begin(), topics.end(),
00954 std::back_inserter(sec.topics));
00955 std::copy(generated_topics.begin(),
00956 generated_topics.end(),
00957 std::back_inserter(sec.topics));
00958 }
00959 }
00960 }
00961
00962 section parse_config(const config *cfg)
00963 {
00964 section sec;
00965 if (cfg != NULL) {
00966 config const *toplevel_cfg = cfg->child("toplevel");
00967 parse_config_internal(cfg, toplevel_cfg, sec);
00968 }
00969 return sec;
00970 }
00971
00972 std::vector<topic> generate_topics(const bool sort_generated,const std::string &generator)
00973 {
00974 std::vector<topic> res;
00975 if (generator == "") {
00976 return res;
00977 }
00978
00979 if (generator == "abilities") {
00980 res = generate_ability_topics(sort_generated);
00981 } else if (generator == "weapon_specials") {
00982 res = generate_weapon_special_topics(sort_generated);
00983 } else {
00984 std::vector<std::string> parts = utils::split(generator, ':', utils::STRIP_SPACES);
00985 if (parts[0] == "units" && parts.size()>1) {
00986 res = generate_unit_topics(sort_generated, parts[1]);
00987 }
00988 }
00989
00990 return res;
00991 }
00992
00993 void generate_sections(const config *help_cfg, const std::string &generator, section &sec, int level)
00994 {
00995 if (generator == "races") {
00996 generate_races_sections(help_cfg, sec, level);
00997 }
00998 }
00999
01000 std::string generate_topic_text(const std::string &generator, const config *help_cfg, const section &sec, const std::vector<topic>& generated_topics)
01001 {
01002 std::string empty_string = "";
01003 if (generator == "") {
01004 return empty_string;
01005 } else if (generator == "about") {
01006 return generate_about_text();
01007 } else {
01008 std::vector<std::string> parts = utils::split(generator, ':');
01009 if (parts.size()>1 && parts[0] == "contents") {
01010 if (parts[1] == "generated") {
01011 return generate_contents_links(sec, generated_topics);
01012 } else {
01013 return generate_contents_links(parts[1], help_cfg);
01014 }
01015 }
01016 }
01017 return empty_string;
01018 }
01019
01020 topic_text::~topic_text()
01021 {
01022 if (generator_ && --generator_->count == 0)
01023 delete generator_;
01024 }
01025
01026 topic_text::topic_text(topic_text const &t): parsed_text_(t.parsed_text_), generator_(t.generator_)
01027 {
01028 if (generator_)
01029 ++generator_->count;
01030 }
01031
01032 topic_text &topic_text::operator=(topic_generator *g)
01033 {
01034 if (generator_ && --generator_->count == 0)
01035 delete generator_;
01036 generator_ = g;
01037 return *this;
01038 }
01039
01040 const std::vector<std::string>& topic_text::parsed_text() const
01041 {
01042 if (generator_) {
01043 parsed_text_ = parse_text((*generator_)());
01044 if (--generator_->count == 0)
01045 delete generator_;
01046 generator_ = NULL;
01047 }
01048 return parsed_text_;
01049 }
01050
01051 std::vector<topic> generate_weapon_special_topics(const bool sort_generated)
01052 {
01053
01054 std::vector<topic> topics;
01055 std::map<std::string, std::string> special_description;
01056 std::map<std::string, std::set<std::string> > special_units;
01057 for(unit_type_data::unit_type_map::const_iterator i = unit_type_data::types().begin();
01058 i != unit_type_data::types().end(); i++) {
01059 const unit_type &type = (*i).second;
01060
01061
01062 if (description_type(type) == FULL_DESCRIPTION) {
01063 std::vector<attack_type> attacks = type.attacks();
01064 for (std::vector<attack_type>::const_iterator it = attacks.begin();
01065 it != attacks.end(); it++) {
01066
01067 std::vector<std::string> specials = (*it).special_tooltips(true);
01068 std::vector<std::string>::iterator sp_it;
01069 for (sp_it = specials.begin(); sp_it != specials.end(); ++sp_it)
01070 {
01071 std::string special = *sp_it;
01072 ++sp_it;
01073
01074
01075
01076 special.erase(std::find(special.begin(),special.end(),'('),special.end());
01077 if (special != "") {
01078 if (special_description.find(special) == special_description.end()) {
01079 std::string description = *sp_it;
01080 const size_t colon_pos = description.find(':');
01081 if (colon_pos != std::string::npos) {
01082
01083 description.erase(0, colon_pos + 2);
01084 }
01085 special_description[special] = description;
01086 }
01087
01088 if (!type.hide_help()) {
01089
01090 std::string type_name = type.type_name();
01091 std::string ref_id = unit_prefix + type.id();
01092
01093
01094 std::string link = "<ref>text='" + escape(type_name) + "' dst='" + escape(ref_id) + "'</ref>";
01095 special_units[special].insert(link);
01096 }
01097 }
01098 }
01099 }
01100 }
01101 }
01102
01103 for (std::map<std::string, std::string>::iterator s = special_description.begin(); s != special_description.end(); s++) {
01104 std::string name = utils::capitalize(gettext(s->first.c_str()));
01105 std::string id = "weaponspecial_" + s->first;
01106 std::stringstream text;
01107 text << s->second;
01108 text << "\n\n" << _("<header>text='Units having this special attack'</header>") << "\n";
01109 std::set<std::string>& units = special_units[s->first];
01110 for (std::set<std::string>::iterator u = units.begin(); u != units.end();u++) {
01111 text << (*u) << "\n";
01112 }
01113
01114 topics.push_back( topic(name, id, text.str()) );
01115 }
01116
01117 if (sort_generated)
01118 std::sort(topics.begin(), topics.end(), title_less());
01119 return topics;
01120 }
01121
01122 std::vector<topic> generate_ability_topics(const bool sort_generated)
01123 {
01124 std::vector<topic> topics;
01125 std::map<std::string, std::string> ability_description;
01126 std::map<std::string, std::set<std::string> > ability_units;
01127
01128
01129
01130
01131 for(unit_type_data::unit_type_map::const_iterator i = unit_type_data::types().begin();
01132 i != unit_type_data::types().end(); ++i) {
01133 const unit_type &type = (*i).second;
01134 if (description_type(type) == FULL_DESCRIPTION) {
01135 std::vector<std::string> descriptions = type.ability_tooltips();
01136 std::vector<std::string>::const_iterator desc_it = descriptions.begin();
01137 for (std::vector<std::string>::const_iterator it = type.abilities().begin();
01138 it != type.abilities().end(); ++it, ++desc_it) {
01139 if (ability_description.find(*it) == ability_description.end()) {
01140
01141 std::string description;
01142 if(desc_it != descriptions.end()) {
01143 description = *desc_it;
01144 } else {
01145 description = string_table[*it + "_description"];
01146 }
01147 const size_t colon_pos = description.find(':');
01148 if (colon_pos != std::string::npos) {
01149
01150 description.erase(0, colon_pos + 2);
01151 }
01152 ability_description[*it] = description;
01153 }
01154
01155 if (!type.hide_help()) {
01156
01157 std::string type_name = type.type_name();
01158 std::string ref_id = unit_prefix + type.id();
01159
01160
01161 std::string link = "<ref>text='" + escape(type_name) + "' dst='" + escape(ref_id) + "'</ref>";
01162 ability_units[*it].insert(link);
01163 }
01164 }
01165 }
01166 }
01167
01168 for (std::map<std::string, std::string>::iterator a = ability_description.begin(); a != ability_description.end(); a++) {
01169 std::string name = utils::capitalize(gettext(a->first.c_str()));
01170 std::string id = "ability_" + a->first;
01171 std::stringstream text;
01172 text << a->second;
01173 text << "\n\n" << _("<header>text='Units having this ability'</header>") << "\n";
01174 std::set<std::string>& units = ability_units[a->first];
01175 for (std::set<std::string>::iterator u = units.begin(); u != units.end();u++) {
01176 text << (*u) << "\n";
01177 }
01178
01179 topics.push_back( topic(name, id, text.str()) );
01180 }
01181
01182 if (sort_generated)
01183 std::sort(topics.begin(), topics.end(), title_less());
01184 return topics;
01185 }
01186
01187 class unit_topic_generator: public topic_generator
01188 {
01189 const unit_type& type_;
01190 typedef std::pair< std::string, unsigned > item;
01191 void push_header(std::vector< item > &row, char const *name) const {
01192 row.push_back(item(bold(name), font::line_width(name, normal_font_size, TTF_STYLE_BOLD)));
01193 }
01194 public:
01195 unit_topic_generator(const unit_type &t): type_(t) {}
01196 virtual std::string operator()() const {
01197 std::stringstream ss;
01198 std::string clear_stringstream;
01199 const std::string detailed_description = type_.unit_description();
01200 const unit_type& female_type = type_.get_gender_unit_type(unit_race::FEMALE);
01201 const unit_type& male_type = type_.get_gender_unit_type(unit_race::MALE);
01202
01203
01204 #ifdef LOW_MEM
01205 ss << "<img>src='" << male_type.image() << "'</img> ";
01206 #else
01207 ss << "<img>src='" << male_type.image() << "~RC(" << male_type.flag_rgb() << ">1)" << "'</img> ";
01208 #endif
01209
01210 if (&female_type != &male_type)
01211 #ifdef LOW_MEM
01212 ss << "<img>src='" << female_type.image() << "'</img> ";
01213 #else
01214 ss << "<img>src='" << female_type.image() << "~RC(" << female_type.flag_rgb() << ">1)" << "'</img> ";
01215 #endif
01216
01217
01218 ss << "<format>font_size=" << font::relative_size(11) << " text=' " << escape(_("level"))
01219 << " " << type_.level() << "'</format>";
01220
01221 const std::string& male_portrait = male_type.image_profile();
01222 const std::string& female_portrait = female_type.image_profile();
01223
01224 if (male_portrait.empty() == false && male_portrait != male_type.image()) {
01225 ss << "<img>src='" << male_portrait << "' align='right'</img> ";
01226 }
01227
01228 if (female_portrait.empty() == false && female_portrait != male_portrait && female_portrait != female_type.image()) {
01229 ss << "<img>src='" << female_portrait << "' align='right'</img> ";
01230 }
01231
01232 ss << "\n";
01233
01234
01235 std::vector<std::string> from_units = type_.advances_from();
01236 if (!from_units.empty())
01237 {
01238 ss << _("Advances from: ");
01239 for (std::vector<std::string>::const_iterator from_iter = from_units.begin();
01240 from_iter != from_units.end();
01241 ++from_iter)
01242 {
01243 std::string unit_id = *from_iter;
01244 std::map<std::string,unit_type>::const_iterator type = unit_type_data::types().find(unit_id);
01245 if (type != unit_type_data::types().end())
01246 {
01247 std::string lang_unit = type->second.type_name();
01248 std::string ref_id;
01249 if (description_type(type->second) == FULL_DESCRIPTION && !type->second.hide_help()) {
01250 ref_id = unit_prefix + type->second.id();
01251 } else {
01252 ref_id = unknown_unit_topic;
01253 lang_unit += " (?)";
01254 }
01255 ss << "<ref>dst='" << escape(ref_id) << "' text='" << escape(lang_unit) << "'</ref>";
01256 if (from_iter + 1 != from_units.end())
01257 ss << ", ";
01258 }
01259 }
01260 ss << "\n";
01261 }
01262
01263
01264
01265 std::vector<std::string> next_units = type_.advances_to();
01266 if (!next_units.empty()) {
01267 ss << _("Advances to: ");
01268 for (std::vector<std::string>::const_iterator advance_it = next_units.begin(),
01269 advance_end = next_units.end();
01270 advance_it != advance_end; ++advance_it) {
01271 std::string unit_id = *advance_it;
01272 std::map<std::string,unit_type>::const_iterator type = unit_type_data::types().find(unit_id);
01273 if(type != unit_type_data::types().end()) {
01274 std::string lang_unit = type->second.type_name();
01275 std::string ref_id;
01276 if (description_type(type->second) == FULL_DESCRIPTION && !type->second.hide_help()) {
01277 ref_id = unit_prefix + type->second.id();
01278 } else {
01279 ref_id = unknown_unit_topic;
01280 lang_unit += " (?)";
01281 }
01282 ss << "<ref>dst='" << escape(ref_id) << "' text='" << escape(lang_unit) << "'</ref>";
01283 if (advance_it + 1 != advance_end)
01284 ss << ", ";
01285 }
01286 }
01287 ss << "\n";
01288 }
01289
01290
01291
01292 const std::string race_id = type_.race();
01293 std::string race_name;
01294 const race_map::const_iterator race_it = unit_type_data::types().races().find(race_id);
01295 if (race_it != unit_type_data::types().races().end()) {
01296 race_name = race_it->second.plural_name();
01297 } else {
01298 race_name = _ ("race^Miscellaneous");
01299 }
01300 ss << _("Race: ");
01301 ss << "<ref>dst='" << escape("..race_"+race_id) << "' text='" << escape(race_name) << "'</ref>";
01302 ss << "\n";
01303
01304
01305
01306 if (!type_.abilities().empty()) {
01307 ss << _("Abilities: ");
01308 for(std::vector<std::string>::const_iterator ability_it = type_.abilities().begin(),
01309 ability_end = type_.abilities().end();
01310 ability_it != ability_end; ++ability_it) {
01311 const std::string ref_id = std::string("ability_") + *ability_it;
01312 std::string lang_ability = gettext(ability_it->c_str());
01313 ss << "<ref>dst='" << escape(ref_id) << "' text='" << escape(lang_ability)
01314 << "'</ref>";
01315 if (ability_it + 1 != ability_end)
01316 ss << ", ";
01317 }
01318 ss << "\n";
01319 }
01320
01321 if (!next_units.empty() || !type_.ability_tooltips().empty())
01322 ss << "\n";
01323
01324 ss << _("HP: ") << type_.hitpoints() << jump(30)
01325 << _("Moves: ") << type_.movement() << jump(30)
01326 << _("Cost: ") << type_.cost() << jump(30)
01327 << _("Alignment: ")
01328 << "<ref>dst='time_of_day' text='"
01329 << type_.alignment_description(type_.alignment())
01330 << "'</ref>"
01331 << jump(30);
01332 if (type_.can_advance())
01333 ss << _("Required XP: ") << type_.experience_needed();
01334
01335
01336 ss << "\n\n" << detailed_description;
01337
01338
01339 std::vector<attack_type> attacks = type_.attacks();
01340 if (!attacks.empty()) {
01341
01342 ss << "\n\n<header>text='" << escape(_("unit help^Attacks"))
01343 << "'</header>\n\n";
01344 table_spec table;
01345
01346 std::vector<item> first_row;
01347
01348 first_row.push_back(item("", 0));
01349 push_header(first_row, _("unit help^Name"));
01350 push_header(first_row, _("Type"));
01351 push_header(first_row, _("Strikes"));
01352 push_header(first_row, _("Range"));
01353 push_header(first_row, _("Special"));
01354 table.push_back(first_row);
01355
01356 for(std::vector<attack_type>::const_iterator attack_it = attacks.begin(),
01357 attack_end = attacks.end();
01358 attack_it != attack_end; ++attack_it) {
01359 std::string lang_weapon = attack_it->name();
01360 std::string lang_type = gettext(attack_it->type().c_str());
01361 std::vector<item> row;
01362 std::stringstream attack_ss;
01363 attack_ss << "<img>src='" << (*attack_it).icon() << "'</img>";
01364 row.push_back(std::make_pair(attack_ss.str(),
01365 image_width(attack_it->icon())));
01366 push_tab_pair(row, lang_weapon);
01367 push_tab_pair(row, lang_type);
01368 attack_ss.str(clear_stringstream);
01369 attack_ss << attack_it->damage() << '-' << attack_it->num_attacks() << " " << attack_it->accuracy_parry_description();
01370 push_tab_pair(row, attack_ss.str());
01371 attack_ss.str(clear_stringstream);
01372 push_tab_pair(row, _((*attack_it).range().c_str()));
01373
01374
01375
01376 std::vector<std::string> specials = attack_it->special_tooltips(true);
01377 if(!specials.empty())
01378 {
01379 std::string lang_special = "";
01380 std::vector<std::string>::iterator sp_it;
01381 for (sp_it = specials.begin(); sp_it != specials.end(); sp_it++) {
01382 const std::string ref_id = std::string("weaponspecial_")
01383 + (*sp_it);
01384 lang_special = gettext(sp_it->c_str());
01385 attack_ss << "<ref>dst='" << escape(ref_id)
01386 << "' text='" << escape(lang_special) << "'</ref>";
01387 if((sp_it + 1) != specials.end() && (sp_it + 2) != specials.end())
01388 {
01389 attack_ss << ", ";
01390 }
01391 sp_it++;
01392 }
01393 row.push_back(std::make_pair(attack_ss.str(),
01394 font::line_width(lang_special, normal_font_size)));
01395
01396 }
01397 table.push_back(row);
01398 }
01399 ss << generate_table(table);
01400 }
01401
01402
01403 ss << "\n\n<header>text='" << escape(_("Resistances"))
01404 << "'</header>\n\n";
01405 table_spec resistance_table;
01406 std::vector<item> first_res_row;
01407 push_header(first_res_row, _("Attack Type"));
01408 push_header(first_res_row, _("Resistance"));
01409 resistance_table.push_back(first_res_row);
01410 const unit_movement_type &movement_type = type_.movement_type();
01411 string_map dam_tab = movement_type.damage_table();
01412 for(string_map::const_iterator dam_it = dam_tab.begin(), dam_end = dam_tab.end();
01413 dam_it != dam_end; ++dam_it) {
01414 std::vector<item> row;
01415 int resistance = 100 - atoi((*dam_it).second.c_str());
01416 char resi[16];
01417 snprintf(resi,sizeof(resi),"% 4d%%",resistance);
01418
01419
01420 std::string color;
01421 if (resistance < 0)
01422 color = "red";
01423 else if (resistance <= 20)
01424 color = "yellow";
01425 else if (resistance <= 40)
01426 color = "white";
01427 else
01428 color = "green";
01429
01430 std::string lang_weapon = gettext(dam_it->first.c_str());
01431 push_tab_pair(row, lang_weapon);
01432 std::stringstream str;
01433 str << "<format>color=" << color << " text='"<< resi << "'</format>";
01434 const std::string markup = str.str();
01435 str.str(clear_stringstream);
01436 str << resi;
01437 row.push_back(std::make_pair(markup,
01438 font::line_width(str.str(), normal_font_size)));
01439 resistance_table.push_back(row);
01440 }
01441 ss << generate_table(resistance_table);
01442
01443 if (map != NULL) {
01444
01445 ss << "\n\n<header>text='" << escape(_("Terrain Modifiers"))
01446 << "'</header>\n\n";
01447 std::vector<item> first_row;
01448 table_spec table;
01449 push_header(first_row, _("Terrain"));
01450 push_header(first_row, _("Defense"));
01451 push_header(first_row, _("Movement Cost"));
01452
01453 table.push_back(first_row);
01454 std::set<t_translation::t_terrain>::const_iterator terrain_it =
01455 preferences::encountered_terrains().begin();
01456
01457 for (; terrain_it != preferences::encountered_terrains().end();
01458 terrain_it++) {
01459 const t_translation::t_terrain terrain = *terrain_it;
01460 if (terrain == t_translation::FOGGED || terrain == t_translation::VOID_TERRAIN || terrain == t_translation::OFF_MAP_USER)
01461 continue;
01462 const terrain_type& info = map->get_terrain_info(terrain);
01463
01464 if (info.union_type().size() == 1 && info.union_type()[0] == info.number() && info.is_nonnull()) {
01465 std::vector<item> row;
01466 const std::string& name = info.name();
01467 const std::string id = info.id();
01468 const int moves = movement_type.movement_cost(*map,terrain);
01469 std::stringstream str;
01470 str << "<ref>text='" << escape(name) << "' dst='"
01471 << escape(std::string("terrain_") + id) << "'</ref>";
01472 row.push_back(std::make_pair(str.str(),
01473 font::line_width(name, normal_font_size)));
01474
01475
01476 str.str(clear_stringstream);
01477 const int defense =
01478 100 - movement_type.defense_modifier(*map,terrain);
01479 std::string color;
01480 if (defense <= 10)
01481 color = "red";
01482 else if (defense <= 30)
01483 color = "yellow";
01484 else if (defense <= 50)
01485 color = "white";
01486 else
01487 color = "green";
01488
01489 str << "<format>color=" << color << " text='"<< defense << "%'</format>";
01490 const std::string markup = str.str();
01491 str.str(clear_stringstream);
01492 str << defense << "%";
01493 row.push_back(std::make_pair(markup,
01494 font::line_width(str.str(), normal_font_size)));
01495
01496
01497 str.str(clear_stringstream);
01498 if (moves > type_.movement() )
01499 color = "red";
01500 else if (moves > 1)
01501 color = "yellow";
01502 else
01503 color = "white";
01504
01505
01506 str << "<format>color=" << color << " text='"<< moves << "'</format>";
01507 push_tab_pair(row, str.str());
01508
01509 table.push_back(row);
01510 }
01511 }
01512 ss << generate_table(table);
01513 }
01514 return ss.str();
01515 }
01516 };
01517
01518 void generate_races_sections(const config *help_cfg, section &sec, int level)
01519 {
01520 std::set<std::string> races;
01521 std::set<std::string> visible_races;
01522
01523 for(unit_type_data::unit_type_map::const_iterator i = unit_type_data::types().begin();
01524 i != unit_type_data::types().end(); i++) {
01525 const unit_type &type = (*i).second;
01526 UNIT_DESCRIPTION_TYPE desc_type = description_type(type);
01527 if (desc_type == FULL_DESCRIPTION) {
01528 races.insert(type.race());
01529 if (!type.hide_help())
01530 visible_races.insert(type.race());
01531 }
01532 }
01533
01534 std::stringstream text;
01535
01536 for(std::set<std::string>::iterator it = races.begin(); it != races.end(); it++) {
01537 section race_section;
01538 config section_cfg;
01539
01540 bool hidden = (visible_races.count(*it) == 0);
01541
01542 section_cfg["id"] = hidden_symbol(hidden) + race_prefix + *it;
01543
01544 std::string title;
01545 const race_map::const_iterator race_it = unit_type_data::types().races().find(*it);
01546 if (race_it != unit_type_data::types().races().end()) {
01547 title = race_it->second.plural_name();
01548 } else {
01549 title = _ ("race^Miscellaneous");
01550 }
01551 section_cfg["title"] = title;
01552
01553 section_cfg["generator"] = "units:" + *it;
01554
01555 parse_config_internal(help_cfg, §ion_cfg, race_section, level+1);
01556 sec.add_section(race_section);
01557 }
01558 }
01559
01560
01561 std::vector<topic> generate_unit_topics(const bool sort_generated, const std::string& race)
01562 {
01563 std::vector<topic> topics;
01564 std::set<std::string> race_units;
01565
01566 for(unit_type_data::unit_type_map::const_iterator i = unit_type_data::types().begin();
01567 i != unit_type_data::types().end(); i++) {
01568 const unit_type &type = (*i).second;
01569
01570 if (type.race() != race)
01571 continue;
01572 UNIT_DESCRIPTION_TYPE desc_type = description_type(type);
01573 if (desc_type != FULL_DESCRIPTION)
01574 continue;
01575
01576 const std::string type_name = type.type_name();
01577 const std::string ref_id = hidden_symbol(type.hide_help()) + unit_prefix + type.id();
01578 topic unit_topic(type_name, ref_id, "");
01579
01580 topics.push_back(unit_topic);
01581
01582 if (!type.hide_help()) {
01583
01584
01585 std::string link = "<ref>text='" + escape(type_name) + "' dst='" + escape(ref_id) + "'</ref>";
01586 race_units.insert(link);
01587 }
01588 }
01589
01590
01591 std::string race_id = "..race_"+race;
01592 std::string race_name;
01593 std::string race_description;
01594 const race_map::const_iterator race_it = unit_type_data::types().races().find(race);
01595 if (race_it != unit_type_data::types().races().end()) {
01596 race_name = race_it->second.plural_name();
01597 race_description = race_it->second.description();
01598
01599 } else {
01600 race_name = _ ("race^Miscellaneous");
01601
01602 }
01603
01604 std::stringstream text;
01605 text << race_description;
01606 text << "\n\n" << _("<header>text='Units of this race'</header>") << "\n";
01607 for (std::set<std::string>::iterator u = race_units.begin(); u != race_units.end();u++) {
01608 text << (*u) << "\n";
01609 }
01610 topics.push_back(topic(race_name, race_id, text.str()) );
01611
01612 if (sort_generated)
01613 std::sort(topics.begin(), topics.end(), title_less());
01614 return topics;
01615 }
01616
01617 UNIT_DESCRIPTION_TYPE description_type(const unit_type &type)
01618 {
01619 if (game_config::debug) {
01620 return FULL_DESCRIPTION;
01621 }
01622
01623 const std::set<std::string> &encountered_units = preferences::encountered_units();
01624 if (encountered_units.find(type.id()) != encountered_units.end()) {
01625 return FULL_DESCRIPTION;
01626 }
01627 return NO_DESCRIPTION;
01628 }
01629
01630 std::string generate_about_text()
01631 {
01632 std::vector<std::string> about_lines = about::get_text();
01633 std::vector<std::string> res_lines;
01634 std::transform(about_lines.begin(), about_lines.end(), std::back_inserter(res_lines),
01635 about_text_formatter());
01636 res_lines.erase(std::remove(res_lines.begin(), res_lines.end(), ""), res_lines.end());
01637 std::string text = utils::join(res_lines, '\n');
01638 return text;
01639 }
01640
01641 std::string generate_contents_links(const std::string& section_name, config const *help_cfg)
01642 {
01643 std::stringstream res;
01644
01645 config const *section_cfg = help_cfg->find_child("section", "id", section_name);
01646 if (section_cfg == NULL) {
01647 return res.str();
01648 }
01649
01650 std::vector<std::string> topics = utils::quoted_split((*section_cfg)["topics"]);
01651
01652
01653 typedef std::pair<std::string,std::string> link;
01654 std::vector<link> topics_links;
01655
01656 std::vector<std::string>::iterator t;
01657
01658 for (t = topics.begin(); t != topics.end(); t++) {
01659 config const *topic_cfg = help_cfg->find_child("topic", "id", *t);
01660 if (topic_cfg != NULL) {
01661 std::string id = (*topic_cfg)["id"];
01662 if (is_visible_id(id))
01663 topics_links.push_back(link((*topic_cfg)["title"], id));
01664 }
01665 }
01666
01667 if ((*section_cfg)["sort_topics"] == "yes") {
01668 std::sort(topics_links.begin(),topics_links.end());
01669 }
01670
01671 std::vector<link>::iterator l;
01672 for (l = topics_links.begin(); l != topics_links.end(); l++) {
01673 std::string link = "<ref>text='" + escape(l->first) + "' dst='" + escape(l->second) + "'</ref>";
01674 res << link <<"\n";
01675 }
01676
01677 return res.str();
01678 }
01679
01680 std::string generate_contents_links(const section &sec, const std::vector<topic>& topics)
01681 {
01682 std::stringstream res;
01683
01684 section_list::const_iterator s;
01685 for (s = sec.sections.begin(); s != sec.sections.end(); s++) {
01686 if (is_visible_id((*s)->id)) {
01687 std::string link = "<ref>text='" + escape((*s)->title) + "' dst='.." + escape((*s)->id) + "'</ref>";
01688 res << link <<"\n";
01689 }
01690 }
01691
01692 std::vector<topic>::const_iterator t;
01693 for (t = topics.begin(); t != topics.end(); t++) {
01694 if (is_visible_id(t->id)) {
01695 std::string link = "<ref>text='" + escape(t->title) + "' dst='" + escape(t->id) + "'</ref>";
01696 res << link <<"\n";
01697 }
01698 }
01699
01700 return res.str();
01701 }
01702
01703 bool topic::operator==(const topic &t) const
01704 {
01705 return t.id == id;
01706 }
01707
01708 bool topic::operator<(const topic &t) const
01709 {
01710 return id < t.id;
01711 }
01712
01713 section::~section()
01714 {
01715 std::for_each(sections.begin(), sections.end(), delete_section());
01716 }
01717
01718 section::section(const section &sec)
01719 : title(sec.title), id(sec.id), topics(sec.topics), level(sec.level)
01720 {
01721 std::transform(sec.sections.begin(), sec.sections.end(),
01722 std::back_inserter(sections), create_section());
01723 }
01724
01725 section& section::operator=(const section &sec)
01726 {
01727 title = sec.title;
01728 id = sec.id;
01729 level = sec.level;
01730 std::copy(sec.topics.begin(), sec.topics.end(), std::back_inserter(topics));
01731 std::transform(sec.sections.begin(), sec.sections.end(),
01732 std::back_inserter(sections), create_section());
01733 return *this;
01734 }
01735
01736
01737 bool section::operator==(const section &sec) const
01738 {
01739 return sec.id == id;
01740 }
01741
01742 bool section::operator<(const section &sec) const
01743 {
01744 return id < sec.id;
01745 }
01746
01747 void section::add_section(const section &s)
01748 {
01749 sections.push_back(new section(s));
01750 }
01751
01752 void section::clear()
01753 {
01754 topics.clear();
01755 std::for_each(sections.begin(), sections.end(), delete_section());
01756 sections.clear();
01757 }
01758
01759 help_menu::help_menu(CVideo &video, section const &toplevel, int max_height)
01760 : gui::menu(video, empty_string_vector, true, max_height, -1, NULL, &gui::menu::bluebg_style),
01761 toplevel_(toplevel), chosen_topic_(NULL), selected_item_(&toplevel, "")
01762 {
01763 silent_ = true;
01764 update_visible_items(toplevel_);
01765 display_visible_items();
01766 if (!visible_items_.empty())
01767 selected_item_ = visible_items_.front();
01768 }
01769
01770 bool help_menu::expanded(const section &sec)
01771 {
01772 return expanded_.find(&sec) != expanded_.end();
01773 }
01774
01775 void help_menu::expand(const section &sec)
01776 {
01777 if (sec.id != "toplevel" && expanded_.insert(&sec).second) {
01778 sound::play_UI_sound(game_config::sounds::menu_expand);
01779 }
01780 }
01781
01782 void help_menu::contract(const section &sec)
01783 {
01784 if (expanded_.erase(&sec)) {
01785 sound::play_UI_sound(game_config::sounds::menu_contract);
01786 }
01787 }
01788
01789 void help_menu::update_visible_items(const section &sec, unsigned level)
01790 {
01791 if (level == 0) {
01792
01793 visible_items_.clear();
01794 }
01795 section_list::const_iterator sec_it;
01796 for (sec_it = sec.sections.begin(); sec_it != sec.sections.end(); sec_it++) {
01797 if (is_visible_id((*sec_it)->id)) {
01798 const std::string vis_string = get_string_to_show(*(*sec_it), level + 1);
01799 visible_items_.push_back(visible_item(*sec_it, vis_string));
01800 if (expanded(*(*sec_it))) {
01801 update_visible_items(*(*sec_it), level + 1);
01802 }
01803 }
01804 }
01805 topic_list::const_iterator topic_it;
01806 for (topic_it = sec.topics.begin(); topic_it != sec.topics.end(); topic_it++) {
01807 if (is_visible_id(topic_it->id)) {
01808 const std::string vis_string = get_string_to_show(*topic_it, level + 1);
01809 visible_items_.push_back(visible_item(&(*topic_it), vis_string));
01810 }
01811 }
01812 }
01813
01814 std::string help_menu::indented_icon(const std::string& icon, const unsigned level) {
01815 std::stringstream to_show;
01816 for (unsigned i = 1; i < level; i++) {
01817 to_show << IMAGE_PREFIX << indentation_img << IMG_TEXT_SEPARATOR;
01818 }
01819
01820 to_show << IMAGE_PREFIX << icon;
01821 return to_show.str();
01822 }
01823
01824 std::string help_menu::get_string_to_show(const section &sec, const unsigned level)
01825 {
01826 std::stringstream to_show;
01827 to_show << indented_icon(expanded(sec) ? open_section_img : closed_section_img, level)
01828 << IMG_TEXT_SEPARATOR << sec.title;
01829 return to_show.str();
01830 }
01831
01832 std::string help_menu::get_string_to_show(const topic &topic, const unsigned level)
01833 {
01834 std::stringstream to_show;
01835 to_show << indented_icon(topic_img, level)
01836 << IMG_TEXT_SEPARATOR << topic.title;
01837 return to_show.str();
01838 }
01839
01840 bool help_menu::select_topic_internal(const topic &t, const section &sec)
01841 {
01842 topic_list::const_iterator tit =
01843 std::find(sec.topics.begin(), sec.topics.end(), t);
01844 if (tit != sec.topics.end()) {
01845
01846
01847 if (t.id.size()<2 || t.id[0] != '.' || t.id[1] != '.')
01848 expand(sec);
01849 return true;
01850 }
01851 section_list::const_iterator sit;
01852 for (sit = sec.sections.begin(); sit != sec.sections.end(); sit++) {
01853 if (select_topic_internal(t, *(*sit))) {
01854 expand(sec);
01855 return true;
01856 }
01857 }
01858 return false;
01859 }
01860
01861 void help_menu::select_topic(const topic &t)
01862 {
01863 if (selected_item_ == t) {
01864
01865 return;
01866 }
01867 if (select_topic_internal(t, toplevel_)) {
01868 update_visible_items(toplevel_);
01869 for (std::vector<visible_item>::const_iterator it = visible_items_.begin();
01870 it != visible_items_.end(); it++) {
01871 if (*it == t) {
01872 selected_item_ = *it;
01873 break;
01874 }
01875 }
01876 display_visible_items();
01877 }
01878 }
01879
01880 int help_menu::process()
01881 {
01882 int res = menu::process();
01883 int mousex, mousey;
01884 SDL_GetMouseState(&mousex,&mousey);
01885
01886 if (!visible_items_.empty() &&
01887 static_cast<size_t>(res) < visible_items_.size()) {
01888
01889 selected_item_ = visible_items_[res];
01890 const section* sec = selected_item_.sec;
01891 if (sec != NULL) {
01892
01893 int x = mousex - menu::location().x;
01894
01895 const std::string icon_img = expanded(*sec) ? open_section_img : closed_section_img;
01896
01897 int text_start = style_->item_size(indented_icon(icon_img, sec->level)).w - style_->get_thickness();
01898
01899
01900
01901 if (menu::double_clicked() || x < text_start) {
01902
01903
01904 expanded(*sec) ? contract(*sec) : expand(*sec);
01905 update_visible_items(toplevel_);
01906 display_visible_items();
01907 } else if (x >= text_start){
01908
01909 chosen_topic_ = find_topic(toplevel, ".."+sec->id );
01910 }
01911 } else if (selected_item_.t != NULL) {
01912
01913 chosen_topic_ = selected_item_.t;
01914 }
01915 }
01916 return res;
01917 }
01918
01919 const topic *help_menu::chosen_topic()
01920 {
01921 const topic *ret = chosen_topic_;
01922 chosen_topic_ = NULL;
01923 return ret;
01924 }
01925
01926 void help_menu::display_visible_items()
01927 {
01928 std::vector<std::string> menu_items;
01929 for(std::vector<visible_item>::const_iterator items_it = visible_items_.begin(),
01930 end = visible_items_.end(); items_it != end; ++items_it) {
01931 std::string to_show = items_it->visible_string;
01932 if (selected_item_ == *items_it)
01933 to_show = std::string("*") + to_show;
01934 menu_items.push_back(to_show);
01935 }
01936 set_items(menu_items, false, true);
01937 }
01938
01939 help_menu::visible_item::visible_item(const section *_sec, const std::string &vis_string) :
01940 t(NULL), sec(_sec), visible_string(vis_string) {}
01941
01942 help_menu::visible_item::visible_item(const topic *_t, const std::string &vis_string) :
01943 t(_t), sec(NULL), visible_string(vis_string) {}
01944
01945 bool help_menu::visible_item::operator==(const section &_sec) const
01946 {
01947 return sec != NULL && *sec == _sec;
01948 }
01949
01950 bool help_menu::visible_item::operator==(const topic &_t) const
01951 {
01952 return t != NULL && *t == _t;
01953 }
01954
01955 bool help_menu::visible_item::operator==(const visible_item &vis_item) const
01956 {
01957 return t == vis_item.t && sec == vis_item.sec;
01958 }
01959
01960 help_text_area::help_text_area(CVideo &video, const section &toplevel)
01961 : gui::scrollarea(video), toplevel_(toplevel), shown_topic_(NULL),
01962 title_spacing_(16), curr_loc_(0, 0),
01963 min_row_height_(font::get_max_height(normal_font_size)), curr_row_height_(min_row_height_),
01964 contents_height_(0)
01965 {
01966 set_scroll_rate(40);
01967 }
01968
01969 void help_text_area::set_inner_location(SDL_Rect const &rect)
01970 {
01971 bg_register(rect);
01972 if (shown_topic_)
01973 set_items();
01974 }
01975
01976 void help_text_area::show_topic(const topic &t)
01977 {
01978 shown_topic_ = &t;
01979 set_items();
01980 set_dirty(true);
01981 }
01982
01983
01984 help_text_area::item::item(surface surface, int x, int y, const std::string _text,
01985 const std::string reference_to, bool _floating,
01986 bool _box, ALIGNMENT alignment)
01987 : surf(surface), text(_text), ref_to(reference_to), floating(_floating), box(_box),
01988 align(alignment)
01989 {
01990 rect.x = x;
01991 rect.y = y;
01992 rect.w = box ? surface->w + box_width * 2 : surface->w;
01993 rect.h = box ? surface->h + box_width * 2 : surface->h;
01994 }
01995
01996 help_text_area::item::item(surface surface, int x, int y, bool _floating,
01997 bool _box, ALIGNMENT alignment)
01998 : surf(surface), text(""), ref_to(""), floating(_floating), box(_box), align(alignment)
01999 {
02000 rect.x = x;
02001 rect.y = y;
02002 rect.w = box ? surface->w + box_width * 2 : surface->w;
02003 rect.h = box ? surface->h + box_width * 2 : surface->h;
02004 }
02005
02006 void help_text_area::set_items()
02007 {
02008 last_row_.clear();
02009 items_.clear();
02010 curr_loc_.first = 0;
02011 curr_loc_.second = 0;
02012 curr_row_height_ = min_row_height_;
02013
02014 const std::string show_title =
02015 font::make_text_ellipsis(shown_topic_->title, title_size, inner_location().w);
02016 surface surf(font::get_rendered_text(show_title, title_size,
02017 font::NORMAL_COLOUR, TTF_STYLE_BOLD));
02018 if (surf != NULL) {
02019 add_item(item(surf, 0, 0, show_title));
02020 curr_loc_.second = title_spacing_;
02021 contents_height_ = title_spacing_;
02022 down_one_line();
02023 }
02024
02025 std::vector<std::string> const &parsed_items = shown_topic_->text.parsed_text();
02026 std::vector<std::string>::const_iterator it;
02027 for (it = parsed_items.begin(); it != parsed_items.end(); it++) {
02028 if (*it != "" && (*it)[0] == '[') {
02029
02030 try {
02031 config cfg;
02032 std::istringstream stream(*it);
02033 read(cfg, stream);
02034 config *child = cfg.child("ref");
02035 if (child != NULL) {
02036 handle_ref_cfg(*child);
02037 }
02038 child = cfg.child("img");
02039 if (child != NULL) {
02040 handle_img_cfg(*child);
02041 }
02042 child = cfg.child("bold");
02043 if (child != NULL) {
02044 handle_bold_cfg(*child);
02045 }
02046 child = cfg.child("italic");
02047 if (child != NULL) {
02048 handle_italic_cfg(*child);
02049 }
02050 child = cfg.child("header");
02051 if (child != NULL) {
02052 handle_header_cfg(*child);
02053 }
02054 child = cfg.child("jump");
02055 if (child != NULL) {
02056 handle_jump_cfg(*child);
02057 }
02058 child = cfg.child("format");
02059 if (child != NULL) {
02060 handle_format_cfg(*child);
02061 }
02062 }
02063 catch (config::error e) {
02064 std::stringstream msg;
02065 msg << "Error when parsing help markup as WML: '" << e.message << "'";
02066 throw parse_error(msg.str());
02067 }
02068 }
02069 else {
02070 add_text_item(*it);
02071 }
02072 }
02073 down_one_line();
02074 int h = height();
02075 set_position(0);
02076 set_full_size(contents_height_);
02077 set_shown_size(h);
02078 }
02079
02080 void help_text_area::handle_ref_cfg(const config &cfg)
02081 {
02082 const std::string dst = cfg["dst"];
02083 const std::string text = cfg["text"];
02084 const bool force = get_bool(cfg["force"]);
02085 bool show_ref = true;
02086 if (find_topic(toplevel_, dst) == NULL && !force) {
02087 show_ref = false;
02088
02089
02090
02091
02092
02093
02094
02095
02096
02097
02098
02099 #if 0
02100 if (game_config::debug) {
02101 std::stringstream msg;
02102 msg << "Reference to non-existent topic '" << dst
02103 << "'. Please submit a bug report if you have not"
02104 "modified the game files yourself. Erroneous config: ";
02105 write(msg, cfg);
02106 throw parse_error(msg.str());
02107 }
02108 #endif
02109 }
02110 if (dst == "") {
02111 std::stringstream msg;
02112 msg << "Ref markup must have dst attribute. Please submit a bug"
02113 " report if you have not modified the game files yourself. Erroneous config: ";
02114 write(msg, cfg);
02115 throw parse_error(msg.str());
02116 }
02117 if (show_ref) {
02118 add_text_item(text, dst);
02119 }
02120 else {
02121 add_text_item(text);
02122 }
02123 }
02124
02125 void help_text_area::handle_img_cfg(const config &cfg)
02126 {
02127 const std::string src = cfg["src"];
02128 const std::string align = cfg["align"];
02129 const bool floating = get_bool(cfg["float"]);
02130 bool box = true;
02131 if (cfg["box"] != "" && !get_bool(cfg["box"])) {
02132 box = false;
02133 }
02134 if (src == "") {
02135 throw parse_error("Img markup must have src attribute.");
02136 }
02137 add_img_item(src, align, floating, box);
02138 }
02139
02140 void help_text_area::handle_bold_cfg(const config &cfg)
02141 {
02142 const std::string text = cfg["text"];
02143 if (text == "") {
02144 throw parse_error("Bold markup must have text attribute.");
02145 }
02146 add_text_item(text, "", -1, true);
02147 }
02148
02149 void help_text_area::handle_italic_cfg(const config &cfg)
02150 {
02151 const std::string text = cfg["text"];
02152 if (text == "") {
02153 throw parse_error("Italic markup must have text attribute.");
02154 }
02155 add_text_item(text, "", -1, false, true);
02156 }
02157
02158 void help_text_area::handle_header_cfg(const config &cfg)
02159 {
02160 const std::string text = cfg["text"];
02161 if (text == "") {
02162 throw parse_error("Header markup must have text attribute.");
02163 }
02164 add_text_item(text, "", title2_size, true);
02165 }
02166
02167 void help_text_area::handle_jump_cfg(const config &cfg)
02168 {
02169 const std::string amount_str = cfg["amount"];
02170 const std::string to_str = cfg["to"];
02171 if (amount_str == "" && to_str == "") {
02172 throw parse_error("Jump markup must have either a to or an amount attribute.");
02173 }
02174 unsigned jump_to = curr_loc_.first;
02175 if (amount_str != "") {
02176 unsigned amount;
02177 try {
02178 amount = lexical_cast<unsigned, std::string>(amount_str);
02179 }
02180 catch (bad_lexical_cast) {
02181 throw parse_error("Invalid amount the amount attribute in jump markup.");
02182 }
02183 jump_to += amount;
02184 }
02185 if (to_str != "") {
02186 unsigned to;
02187 try {
02188 to = lexical_cast<unsigned, std::string>(to_str);
02189 }
02190 catch (bad_lexical_cast) {
02191 throw parse_error("Invalid amount in the to attribute in jump markup.");
02192 }
02193 if (to < jump_to) {
02194 down_one_line();
02195 }
02196 jump_to = to;
02197 }
02198 if (jump_to != 0 && static_cast<int>(jump_to) <
02199 get_max_x(curr_loc_.first, curr_row_height_)) {
02200
02201 curr_loc_.first = jump_to;
02202 }
02203 }
02204
02205 void help_text_area::handle_format_cfg(const config &cfg)
02206 {
02207 const std::string text = cfg["text"];
02208 if (text == "") {
02209 throw parse_error("Format markup must have text attribute.");
02210 }
02211 const bool bold = get_bool(cfg["bold"]);
02212 const bool italic = get_bool(cfg["italic"]);
02213 int font_size = normal_font_size;
02214 if (cfg["font_size"] != "") {
02215 try {
02216 font_size = lexical_cast<int, std::string>(cfg["font_size"]);
02217 } catch (bad_lexical_cast) {
02218 throw parse_error("Invalid font_size in format markup.");
02219 }
02220 }
02221 SDL_Color color = string_to_color(cfg["color"]);
02222 add_text_item(text, "", font_size, bold, italic, color);
02223 }
02224
02225 void help_text_area::add_text_item(const std::string text, const std::string ref_dst,
02226 int _font_size, bool bold, bool italic,
02227 SDL_Color text_color)
02228 {
02229 const int font_size = _font_size < 0 ? normal_font_size : _font_size;
02230 if (text.empty())
02231 return;
02232 const int remaining_width = get_remaining_width();
02233 size_t first_word_start = text.find_first_not_of(" ");
02234 if (first_word_start == std::string::npos) {
02235 first_word_start = 0;
02236 }
02237 if (text[first_word_start] == '\n') {
02238 down_one_line();
02239 std::string rest_text = text;
02240 rest_text.erase(0, first_word_start + 1);
02241 add_text_item(rest_text, ref_dst, _font_size, bold, italic, text_color);
02242 return;
02243 }
02244 const std::string first_word = get_first_word(text);
02245 int state = ref_dst == "" ? 0 : TTF_STYLE_UNDERLINE;
02246 state |= bold ? TTF_STYLE_BOLD : 0;
02247 state |= italic ? TTF_STYLE_ITALIC : 0;
02248 if (curr_loc_.first != get_min_x(curr_loc_.second, curr_row_height_)
02249 && remaining_width < font::line_width(first_word, font_size, state)) {
02250
02251
02252 down_one_line();
02253 add_text_item(text, ref_dst, _font_size, bold, italic, text_color);
02254 }
02255 else {
02256 std::vector<std::string> parts = split_in_width(text, font_size, remaining_width);
02257 std::string first_part = parts.front();
02258
02259 const SDL_Color color = ref_dst == "" ? text_color : font::YELLOW_COLOUR;
02260 surface surf(font::get_rendered_text(first_part, font_size, color, state));
02261 if (!surf.null())
02262 add_item(item(surf, curr_loc_.first, curr_loc_.second, first_part, ref_dst));
02263 if (parts.size() > 1) {
02264
02265 std::string& s = parts.back();
02266
02267 const std::string first_word_before = get_first_word(s);
02268 const std::string first_word_after = get_first_word(remove_first_space(s));
02269 if (get_remaining_width() >= font::line_width(first_word_after, font_size, state)
02270 && get_remaining_width()
02271 < font::line_width(first_word_before, font_size, state)) {
02272
02273
02274
02275 s = remove_first_space(s);
02276 down_one_line();
02277 }
02278 else if (!(font::line_width(first_word_before, font_size, state)
02279 < get_remaining_width())) {
02280 s = remove_first_space(s);
02281 }
02282 add_text_item(s, ref_dst, _font_size, bold, italic, text_color);
02283
02284 }
02285 }
02286 }
02287
02288 void help_text_area::add_img_item(const std::string path, const std::string alignment,
02289 const bool floating, const bool box)
02290 {
02291 surface surf(image::get_image(path));
02292 if (surf.null())
02293 return;
02294 ALIGNMENT align = str_to_align(alignment);
02295 if (align == HERE && floating) {
02296 LOG_STREAM(warn, display) << "Floating image with align HERE, aligning left.\n";
02297 align = LEFT;
02298 }
02299 const int width = surf->w + (box ? box_width * 2 : 0);
02300 int xpos;
02301 int ypos = curr_loc_.second;
02302 int text_width = inner_location().w;
02303 switch (align) {
02304 case HERE:
02305 xpos = curr_loc_.first;
02306 break;
02307 case LEFT:
02308 default:
02309 xpos = 0;
02310 break;
02311 case MIDDLE:
02312 xpos = text_width / 2 - width / 2 - (box ? box_width : 0);
02313 break;
02314 case RIGHT:
02315 xpos = text_width - width - (box ? box_width * 2 : 0);
02316 break;
02317 }
02318 if (curr_loc_.first != get_min_x(curr_loc_.second, curr_row_height_)
02319 && (xpos < curr_loc_.first || xpos + width > text_width)) {
02320 down_one_line();
02321 add_img_item(path, alignment, floating, box);
02322 }
02323 else {
02324 if (!floating) {
02325 curr_loc_.first = xpos;
02326 }
02327 else {
02328 ypos = get_y_for_floating_img(width, xpos, ypos);
02329 }
02330 add_item(item(surf, xpos, ypos, floating, box, align));
02331 }
02332 }
02333
02334 int help_text_area::get_y_for_floating_img(const int width, const int x, const int desired_y)
02335 {
02336 int min_y = desired_y;
02337 for (std::list<item>::const_iterator it = items_.begin(); it != items_.end(); it++) {
02338 const item& itm = *it;
02339 if (itm.floating) {
02340 if ((itm.rect.x + itm.rect.w > x && itm.rect.x < x + width)
02341 || (itm.rect.x > x && itm.rect.x < x + width)) {
02342 min_y = maximum<int>(min_y, itm.rect.y + itm.rect.h);
02343 }
02344 }
02345 }
02346 return min_y;
02347 }
02348
02349 int help_text_area::get_min_x(const int y, const int height)
02350 {
02351 int min_x = 0;
02352 for (std::list<item>::const_iterator it = items_.begin(); it != items_.end(); it++) {
02353 const item& itm = *it;
02354 if (itm.floating) {
02355 if (itm.rect.y < y + height && itm.rect.y + itm.rect.h > y && itm.align == LEFT) {
02356 min_x = maximum<int>(min_x, itm.rect.w + 5);
02357 }
02358 }
02359 }
02360 return min_x;
02361 }
02362
02363 int help_text_area::get_max_x(const int y, const int height)
02364 {
02365 int text_width = inner_location().w;
02366 int max_x = text_width;
02367 for (std::list<item>::const_iterator it = items_.begin(); it != items_.end(); it++) {
02368 const item& itm = *it;
02369 if (itm.floating) {
02370 if (itm.rect.y < y + height && itm.rect.y + itm.rect.h > y) {
02371 if (itm.align == RIGHT) {
02372 max_x = minimum<int>(max_x, text_width - itm.rect.w - 5);
02373 } else if (itm.align == MIDDLE) {
02374 max_x = minimum<int>(max_x, text_width / 2 - itm.rect.w / 2 - 5);
02375 }
02376 }
02377 }
02378 }
02379 return max_x;
02380 }
02381
02382 void help_text_area::add_item(const item &itm)
02383 {
02384 items_.push_back(itm);
02385 if (!itm.floating) {
02386 curr_loc_.first += itm.rect.w;
02387 curr_row_height_ = maximum<int>(itm.rect.h, curr_row_height_);
02388 contents_height_ = maximum<int>(contents_height_, curr_loc_.second + curr_row_height_);
02389 last_row_.push_back(&items_.back());
02390 }
02391 else {
02392 if (itm.align == LEFT) {
02393 curr_loc_.first = itm.rect.w + 5;
02394 }
02395 contents_height_ = maximum<int>(contents_height_, itm.rect.y + itm.rect.h);
02396 }
02397 }
02398
02399
02400 help_text_area::ALIGNMENT help_text_area::str_to_align(const std::string &s)
02401 {
02402 const std::string cmp_str = to_lower(s);
02403 if (cmp_str == "left") {
02404 return LEFT;
02405 } else if (cmp_str == "middle") {
02406 return MIDDLE;
02407 } else if (cmp_str == "right") {
02408 return RIGHT;
02409 } else if (cmp_str == "here" || cmp_str == "") {
02410 return HERE;
02411 }
02412 std::stringstream msg;
02413 msg << "Invalid alignment string: '" << s << "'";
02414 throw parse_error(msg.str());
02415 }
02416
02417 void help_text_area::down_one_line()
02418 {
02419 adjust_last_row();
02420 last_row_.clear();
02421 curr_loc_.second += curr_row_height_ + (curr_row_height_ == min_row_height_ ? 0 : 2);
02422 curr_row_height_ = min_row_height_;
02423 contents_height_ = maximum<int>(curr_loc_.second + curr_row_height_, contents_height_);
02424 curr_loc_.first = get_min_x(curr_loc_.second, curr_row_height_);
02425 }
02426
02427 void help_text_area::adjust_last_row()
02428 {
02429 for (std::list<item *>::iterator it = last_row_.begin(); it != last_row_.end(); it++) {
02430 item &itm = *(*it);
02431 const int gap = curr_row_height_ - itm.rect.h;
02432 itm.rect.y += gap / 2;
02433 }
02434 }
02435
02436 int help_text_area::get_remaining_width()
02437 {
02438 const int total_w = get_max_x(curr_loc_.second, curr_row_height_);
02439 return total_w - curr_loc_.first;
02440 }
02441
02442 void help_text_area::draw_contents()
02443 {
02444 SDL_Rect const &loc = inner_location();
02445 bg_restore();
02446 surface const screen = video().getSurface();
02447 clip_rect_setter clip_rect_set(screen, loc);
02448 for(std::list<item>::const_iterator it = items_.begin(), end = items_.end(); it != end; ++it) {
02449 SDL_Rect dst = it->rect;
02450 dst.y -= get_position();
02451 if (dst.y < static_cast<int>(loc.h) && dst.y + it->rect.h > 0) {
02452 dst.x += loc.x;
02453 dst.y += loc.y;
02454 if (it->box) {
02455 for (int i = 0; i < box_width; i++) {
02456 draw_rectangle(dst.x, dst.y, it->rect.w - i * 2, it->rect.h - i * 2,
02457 0, screen);
02458 dst.x++;
02459 dst.y++;
02460 }
02461 }
02462 SDL_BlitSurface(it->surf, NULL, screen, &dst);
02463 }
02464 }
02465 update_rect(loc);
02466 }
02467
02468 void help_text_area::scroll(unsigned int)
02469 {
02470
02471
02472
02473 set_dirty(true);
02474 }
02475
02476 bool help_text_area::item_at::operator()(const item& item) const {
02477 return point_in_rect(x_, y_, item.rect);
02478 }
02479
02480 std::string help_text_area::ref_at(const int x, const int y)
02481 {
02482 const int local_x = x - location().x;
02483 const int local_y = y - location().y;
02484 if (local_y < static_cast<int>(height()) && local_y > 0) {
02485 const int cmp_y = local_y + get_position();
02486 const std::list<item>::const_iterator it =
02487 std::find_if(items_.begin(), items_.end(), item_at(local_x, cmp_y));
02488 if (it != items_.end()) {
02489 if ((*it).ref_to != "") {
02490 return ((*it).ref_to);
02491 }
02492 }
02493 }
02494 return "";
02495 }
02496
02497
02498
02499 help_browser::help_browser(display &disp, const section &toplevel)
02500 : gui::widget(disp.video()), disp_(disp), menu_(disp.video(), toplevel),
02501 text_area_(disp.video(), toplevel), toplevel_(toplevel), ref_cursor_(false),
02502 back_button_(disp.video(), _(" < Back"), gui::button::TYPE_PRESS),
02503 forward_button_(disp.video(), _("Forward >"), gui::button::TYPE_PRESS),
02504 shown_topic_(NULL)
02505 {
02506
02507
02508
02509 back_button_.hide(true);
02510 forward_button_.hide(true);
02511
02512 set_measurements(font::relative_size(400), font::relative_size(500));
02513 }
02514
02515 void help_browser::adjust_layout()
02516 {
02517 const int menu_buttons_padding = font::relative_size(10);
02518 const int menu_y = location().y;
02519 const int menu_x = location().x;
02520 #ifdef USE_TINY_GUI
02521 const int menu_w = 120;
02522 #else
02523 const int menu_w = 250;
02524 #endif
02525 const int menu_h = height() - back_button_.height() - menu_buttons_padding;
02526
02527 const int menu_text_area_padding = font::relative_size(10);
02528 const int text_area_y = location().y;
02529 const int text_area_x = menu_x + menu_w + menu_text_area_padding;
02530 const int text_area_w = width() - menu_w - menu_text_area_padding;
02531 const int text_area_h = height();
02532
02533 const int button_border_padding = 0;
02534 const int button_button_padding = font::relative_size(10);
02535 const int back_button_x = location().x + button_border_padding;
02536 const int back_button_y = menu_y + menu_h + menu_buttons_padding;
02537 const int forward_button_x = back_button_x + back_button_.width() + button_button_padding;
02538 const int forward_button_y = back_button_y;
02539
02540 menu_.set_width(menu_w);
02541 menu_.set_location(menu_x, menu_y);
02542 menu_.set_max_height(menu_h);
02543 menu_.set_max_width(menu_w);
02544
02545 text_area_.set_location(text_area_x, text_area_y);
02546 text_area_.set_width(text_area_w);
02547 text_area_.set_height(text_area_h);
02548
02549 back_button_.set_location(back_button_x, back_button_y);
02550 forward_button_.set_location(forward_button_x, forward_button_y);
02551
02552 set_dirty(true);
02553 }
02554
02555 void help_browser::update_location(SDL_Rect const &)
02556 {
02557 adjust_layout();
02558 }
02559
02560 void help_browser::process_event()
02561 {
02562 CKey key;
02563 int mousex, mousey;
02564 SDL_GetMouseState(&mousex,&mousey);
02565
02566
02567 if (point_in_rect(mousex, mousey, menu_.location())) {
02568 menu_.process();
02569 const topic *chosen_topic = menu_.chosen_topic();
02570 if (chosen_topic != NULL && chosen_topic != shown_topic_) {
02571
02572 show_topic(*chosen_topic);
02573 }
02574 }
02575 if (back_button_.pressed()) {
02576 move_in_history(back_topics_, forward_topics_);
02577 }
02578 if (forward_button_.pressed()) {
02579 move_in_history(forward_topics_, back_topics_);
02580 }
02581 back_button_.hide(back_topics_.empty());
02582 forward_button_.hide(forward_topics_.empty());
02583 }
02584
02585 void help_browser::move_in_history(std::deque<const topic *> &from,
02586 std::deque<const topic *> &to)
02587 {
02588 if (!from.empty()) {
02589 const topic *to_show = from.back();
02590 from.pop_back();
02591 if (shown_topic_ != NULL) {
02592 if (to.size() > max_history) {
02593 to.pop_front();
02594 }
02595 to.push_back(shown_topic_);
02596 }
02597 show_topic(*to_show, false);
02598 }
02599 }
02600
02601
02602 void help_browser::handle_event(const SDL_Event &event)
02603 {
02604 SDL_MouseButtonEvent mouse_event = event.button;
02605 if (event.type == SDL_MOUSEBUTTONDOWN) {
02606 if (mouse_event.button == SDL_BUTTON_LEFT) {
02607
02608 const int mousex = mouse_event.x;
02609 const int mousey = mouse_event.y;
02610 const std::string ref = text_area_.ref_at(mousex, mousey);
02611 if (ref != "") {
02612 const topic *t = find_topic(toplevel_, ref);
02613 if (t == NULL) {
02614 std::stringstream msg;
02615 msg << _("Reference to unknown topic: ") << "'" << ref << "'.";
02616 gui::message_dialog(disp_, "", msg.str()).show();
02617 update_cursor();
02618 }
02619 else {
02620 show_topic(*t);
02621 update_cursor();
02622 }
02623 }
02624 }
02625 }
02626 else if (event.type == SDL_MOUSEMOTION) {
02627 update_cursor();
02628 }
02629 }
02630
02631 void help_browser::update_cursor()
02632 {
02633 int mousex, mousey;
02634 SDL_GetMouseState(&mousex,&mousey);
02635 const std::string ref = text_area_.ref_at(mousex, mousey);
02636 if (ref != "" && !ref_cursor_) {
02637 cursor::set(cursor::HYPERLINK);
02638 ref_cursor_ = true;
02639 }
02640 else if (ref == "" && ref_cursor_) {
02641 cursor::set(cursor::NORMAL);
02642 ref_cursor_ = false;
02643 }
02644 }
02645
02646
02647 const topic *find_topic(const section &sec, const std::string &id)
02648 {
02649 topic_list::const_iterator tit =
02650 std::find_if(sec.topics.begin(), sec.topics.end(), has_id(id));
02651 if (tit != sec.topics.end()) {
02652 return &(*tit);
02653 }
02654 section_list::const_iterator sit;
02655 for (sit = sec.sections.begin(); sit != sec.sections.end(); sit++) {
02656 const topic *t = find_topic(*(*sit), id);
02657 if (t != NULL) {
02658 return t;
02659 }
02660 }
02661 return NULL;
02662 }
02663
02664 const section *find_section(const section &sec, const std::string &id)
02665 {
02666 section_list::const_iterator sit =
02667 std::find_if(sec.sections.begin(), sec.sections.end(), has_id(id));
02668 if (sit != sec.sections.end()) {
02669 return *sit;
02670 }
02671 for (sit = sec.sections.begin(); sit != sec.sections.end(); sit++) {
02672 const section *s = find_section(*(*sit), id);
02673 if (s != NULL) {
02674 return s;
02675 }
02676 }
02677 return NULL;
02678 }
02679
02680 void help_browser::show_topic(const std::string &topic_id)
02681 {
02682 const topic *t = find_topic(toplevel_, topic_id);
02683
02684 if (t != NULL) {
02685 show_topic(*t);
02686 } else if (topic_id.find(unit_prefix)==0 || topic_id.find(hidden_symbol() + unit_prefix)==0) {
02687 show_topic(unknown_unit_topic);
02688 } else {
02689 std::cerr << "Help browser tried to show topic with id '" << topic_id
02690 << "' but that topic could not be found." << std::endl;
02691 }
02692 }
02693
02694 void help_browser::show_topic(const topic &t, bool save_in_history)
02695 {
02696 log_scope("show_topic");
02697
02698 if (save_in_history) {
02699 forward_topics_.clear();
02700 if (shown_topic_ != NULL) {
02701 if (back_topics_.size() > max_history) {
02702 back_topics_.pop_front();
02703 }
02704 back_topics_.push_back(shown_topic_);
02705 }
02706 }
02707
02708
02709 if (t.text.parsed_text().size() == 0){
02710 if (t.id.find(unit_prefix) == 0){
02711 std::string unit_id = t.id.substr(unit_prefix.length(), t.id.length() - unit_prefix.length());
02712 const unit_type& type = unit_type_data::types().find(unit_id, unit_type::WITHOUT_ANIMATIONS)->second;
02713 t.text = new unit_topic_generator(type);
02714 }
02715 }
02716
02717 shown_topic_ = &t;
02718 text_area_.show_topic(t);
02719 menu_.select_topic(t);
02720 update_cursor();
02721 }
02722
02723 std::vector<std::string> parse_text(const std::string &text)
02724 {
02725 std::vector<std::string> res;
02726 bool last_char_escape = false;
02727 const char escape_char = '\\';
02728 std::stringstream ss;
02729 size_t pos;
02730 enum { ELEMENT_NAME, OTHER } state = OTHER;
02731 for (pos = 0; pos < text.size(); pos++) {
02732 const char c = text[pos];
02733 if (c == escape_char && !last_char_escape) {
02734 last_char_escape = true;
02735 }
02736 else {
02737 if (state == OTHER) {
02738 if (c == '<') {
02739 if (last_char_escape) {
02740 ss << c;
02741 }
02742 else {
02743 res.push_back(ss.str());
02744 ss.str("");
02745 state = ELEMENT_NAME;
02746 }
02747 }
02748 else {
02749 ss << c;
02750 }
02751 }
02752 else if (state == ELEMENT_NAME) {
02753 if (c == '/') {
02754 std::string msg = "Erroneous / in element name.";
02755 throw parse_error(msg);
02756 }
02757 else if (c == '>') {
02758
02759 std::stringstream s;
02760 const std::string element_name = ss.str();
02761 ss.str("");
02762 s << "</" << element_name << ">";
02763 const std::string end_element_name = s.str();
02764 size_t end_pos = text.find(end_element_name, pos);
02765 if (end_pos == std::string::npos) {
02766 std::stringstream msg;
02767 msg << "Unterminated element: " << element_name;
02768 throw parse_error(msg.str());
02769 }
02770 s.str("");
02771 const std::string contents = text.substr(pos + 1, end_pos - pos - 1);
02772 const std::string element = convert_to_wml(element_name, contents);
02773 res.push_back(element);
02774 pos = end_pos + end_element_name.size() - 1;
02775 state = OTHER;
02776 }
02777 else {
02778 ss << c;
02779 }
02780 }
02781 last_char_escape = false;
02782 }
02783 }
02784 if (state == ELEMENT_NAME) {
02785 std::stringstream msg;
02786 msg << "Element '" << ss.str() << "' continues through end of string.";
02787 throw parse_error(msg.str());
02788 }
02789 if (ss.str() != "") {
02790
02791 res.push_back(ss.str());
02792 }
02793 return res;
02794 }
02795
02796 std::string convert_to_wml(const std::string &element_name, const std::string &contents)
02797 {
02798 std::stringstream ss;
02799 bool in_quotes = false;
02800 bool last_char_escape = false;
02801 const char escape_char = '\\';
02802 std::vector<std::string> attributes;
02803
02804
02805
02806
02807 for (size_t pos = 0; pos < contents.size(); pos++) {
02808 const char c = contents[pos];
02809 if (c == escape_char && !last_char_escape) {
02810 last_char_escape = true;
02811 }
02812 else {
02813 if (c == '\'' && !last_char_escape) {
02814 ss << '"';
02815 in_quotes = !in_quotes;
02816 }
02817 else if ((c == ' ' || c == '\n') && !last_char_escape && !in_quotes) {
02818
02819 attributes.push_back(ss.str());
02820 ss.str("");
02821 }
02822 else {
02823 ss << c;
02824 }
02825 last_char_escape = false;
02826 }
02827 }
02828 if (in_quotes) {
02829 std::stringstream msg;
02830 msg << "Unterminated single quote after: '" << ss.str() << "'";
02831 throw parse_error(msg.str());
02832 }
02833 if (ss.str() != "") {
02834 attributes.push_back(ss.str());
02835 }
02836 ss.str("");
02837
02838 ss << "[" << element_name << "]\n";
02839 for (std::vector<std::string>::const_iterator it = attributes.begin();
02840 it != attributes.end(); it++) {
02841 ss << *it << "\n";
02842 }
02843 ss << "[/" << element_name << "]\n";
02844 return ss.str();
02845 }
02846
02847 bool get_bool(const std::string &s)
02848 {
02849 const std::string cmp_str = to_lower(s);
02850 if (cmp_str == "yes" || cmp_str == "true" || cmp_str == "1" || cmp_str == "on") {
02851 return true;
02852 }
02853 return false;
02854 }
02855
02856 SDL_Color string_to_color(const std::string &s)
02857 {
02858 const std::string cmp_str = to_lower(s);
02859 if (cmp_str == "green") {
02860 return font::GOOD_COLOUR;
02861 }
02862 if (cmp_str == "red") {
02863 return font::BAD_COLOUR;
02864 }
02865 if (cmp_str == "black") {
02866 return font::BLACK_COLOUR;
02867 }
02868 if (cmp_str == "yellow") {
02869 return font::YELLOW_COLOUR;
02870 }
02871 return font::NORMAL_COLOUR;
02872 }
02873
02874 std::vector<std::string> split_in_width(const std::string &s, const int font_size,
02875 const unsigned width)
02876 {
02877 std::vector<std::string> res;
02878 try {
02879 const std::string& first_line = font::word_wrap_text(s, font_size, width, -1, 1);
02880 res.push_back(first_line);
02881 if(s.size() > first_line.size()) {
02882 res.push_back(s.substr(first_line.size()));
02883 }
02884 }
02885 catch (utils::invalid_utf8_exception e)
02886 {
02887 throw parse_error (_("corrupted original file"));
02888 }
02889
02890 return res;
02891 }
02892
02893 std::string remove_first_space(const std::string& text)
02894 {
02895 if (text.length() > 0 && text[0] == ' ') {
02896 return text.substr(1);
02897 }
02898 return text;
02899 }
02900
02901 std::string to_lower(const std::string &s)
02902 {
02903 std::string lower_string;
02904 lower_string.resize(s.size());
02905 std::transform(s.begin(), s.end(), lower_string.begin(), tolower);
02906 return lower_string;
02907 }
02908
02909 std::string escape(const std::string &s)
02910 {
02911 std::string res = s;
02912 if(!res.empty()) {
02913 std::string::size_type pos = 0;
02914 do {
02915 pos = res.find_first_of("'\\", pos);
02916 if(pos != std::string::npos) {
02917 res.insert(pos, 1, '\\');
02918 pos += 2;
02919 }
02920 } while(pos < res.size() && pos != std::string::npos);
02921 }
02922 return res;
02923 }
02924
02925 std::string get_first_word(const std::string &s)
02926 {
02927 size_t first_word_start = s.find_first_not_of(' ');
02928 if (first_word_start == std::string::npos) {
02929 return s;
02930 }
02931 size_t first_word_end = s.find_first_of(" \n", first_word_start);
02932 if( first_word_end == first_word_start ) {
02933
02934 first_word_end = first_word_start+1;
02935 }
02936 return s.substr(0, first_word_end);
02937 }
02938
02939
02940
02941
02942 void show_help(display &disp, const std::string& show_topic, int xloc, int yloc)
02943 {
02944 show_help(disp, toplevel, show_topic, xloc, yloc);
02945 }
02946
02947
02948
02949
02950 void show_unit_help(display &disp, const std::string& show_topic, bool hidden, int xloc, int yloc)
02951 {
02952 show_help(disp, toplevel, hidden_symbol(hidden) + unit_prefix + show_topic, xloc, yloc);
02953 }
02954
02955
02956
02957
02958
02959 void show_help(display &disp, const section &toplevel_sec,
02960 const std::string& show_topic,
02961 int xloc, int yloc)
02962 {
02963 const events::event_context dialog_events_context;
02964 const gui::dialog_manager manager;
02965 const resize_lock prevent_resizing;
02966
02967 CVideo& screen = disp.video();
02968 surface const scr = screen.getSurface();
02969
02970 const int width = minimum<int>(font::relative_size(900), scr->w - font::relative_size(20));
02971 const int height = minimum<int>(font::relative_size(800), scr->h - font::relative_size(150));
02972 const int left_padding = font::relative_size(10);
02973 const int right_padding = font::relative_size(10);
02974 const int top_padding = font::relative_size(10);
02975 const int bot_padding = font::relative_size(10);
02976
02977
02978
02979 if (yloc <= -1 || xloc <= -1) {
02980 xloc = scr->w / 2 - width / 2;
02981 yloc = scr->h / 2 - height / 2;
02982 }
02983 std::vector<gui::button*> buttons_ptr;
02984 gui::button close_button_(disp.video(), _("Close"));
02985 buttons_ptr.push_back(&close_button_);
02986
02987 gui::dialog_frame f(disp.video(), _("The Battle for Wesnoth Help"), gui::dialog_frame::default_style,
02988 true, &buttons_ptr);
02989 f.layout(xloc, yloc, width, height);
02990 f.draw();
02991
02992
02993
02994 unit_type_data::types().build_all(unit_type::HELP_INDEX);
02995
02996 if (preferences::encountered_units().size() != size_t(last_num_encountered_units) ||
02997 preferences::encountered_terrains().size() != size_t(last_num_encountered_terrains) ||
02998 last_debug_state != game_config::debug ||
02999 last_num_encountered_units < 0) {
03000
03001 last_num_encountered_units = preferences::encountered_units().size();
03002 last_num_encountered_terrains = preferences::encountered_terrains().size();
03003 last_debug_state = game_config::debug;
03004 generate_contents();
03005 }
03006 try {
03007 help_browser hb(disp, toplevel_sec);
03008 hb.set_location(xloc + left_padding, yloc + top_padding);
03009 hb.set_width(width - left_padding - right_padding);
03010 hb.set_height(height - top_padding - bot_padding);
03011 if (show_topic != "") {
03012 hb.show_topic(show_topic);
03013 }
03014 else {
03015 hb.show_topic(default_show_topic);
03016 }
03017 hb.set_dirty(true);
03018 events::raise_draw_event();
03019 disp.flip();
03020 disp.invalidate_all();
03021 CKey key;
03022 for (;;) {
03023 events::pump();
03024 events::raise_process_event();
03025 events::raise_draw_event();
03026 if (key[SDLK_ESCAPE]) {
03027
03028 return;
03029 }
03030 for (std::vector<gui::button*>::iterator button_it = buttons_ptr.begin();
03031 button_it != buttons_ptr.end(); button_it++) {
03032 if ((*button_it)->pressed()) {
03033
03034 return;
03035 }
03036 }
03037 disp.flip();
03038 disp.delay(10);
03039 }
03040 }
03041 catch (parse_error e) {
03042 std::stringstream msg;
03043 msg << _("Parse error when parsing help text: ") << "'" << e.message << "'";
03044 gui::message_dialog(disp, "", msg.str()).show();
03045 }
03046 }
03047
03048 }