The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
help_impl.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "help_impl.hpp"
16 
17 #include "about.hpp" // for get_text
18 #include "display.hpp" // for display
19 #include "display_context.hpp" // for display_context
20 #include "game_config.hpp" // for debug, menu_contract, etc
21 #include "game_config_manager.hpp" // for game_config_manager
22 #include "game_preferences.hpp" // for encountered_terrains, etc
23 #include "gettext.hpp" // for _, gettext, N_
25 #include "hotkey/hotkey_command.hpp" // for is_scope_active, etc
26 #include "image.hpp" // for get_image, locator
27 #include "log.hpp" // for LOG_STREAM, logger, etc
28 #include "utils/make_enum.hpp" // for operator<<
29 #include "map/map.hpp" // for gamemap
30 #include "marked-up_text.hpp" // for is_cjk_char, word_wrap_text
31 #include "units/race.hpp" // for unit_race, etc
32 #include "resources.hpp" // for tod_manager, config_manager
33 #include "sdl/utils.hpp" // for surface
34 #include "serialization/string_utils.hpp" // for split, quoted_split, etc
35 #include "serialization/unicode_cast.hpp" // for unicode_cast
36 #include "serialization/unicode_types.hpp" // for char_t, etc
37 #include "terrain/terrain.hpp" // for terrain_type
38 #include "terrain/translation.hpp" // for operator==, t_list, etc
39 #include "terrain/type_data.hpp" // for terrain_type_data, etc
40 #include "time_of_day.hpp" // for time_of_day
41 #include "tod_manager.hpp" // for tod_manager
42 #include "tstring.hpp" // for t_string, operator<<
43 #include "units/types.hpp" // for unit_type, unit_type_data, etc
44 #include "serialization/unicode.hpp" // for iterator
45 
46 #include <assert.h> // for assert
47 #include <algorithm> // for sort, find, transform, etc
48 #include <boost/shared_ptr.hpp> // for shared_ptr
49 #include <iostream> // for operator<<, basic_ostream, etc
50 #include <iterator> // for back_insert_iterator, etc
51 #include <map> // for map, etc
52 #include <set>
53 #include <SDL.h>
54 
55 static lg::log_domain log_display("display");
56 #define WRN_DP LOG_STREAM(warn, log_display)
57 
58 static lg::log_domain log_help("help");
59 #define WRN_HP LOG_STREAM(warn, log_help)
60 #define DBG_HP LOG_STREAM(debug, log_help)
61 
62 namespace help {
63 
64 const config *game_cfg = nullptr;
65 // The default toplevel.
67 // All sections and topics not referenced from the default toplevel.
69 
73 
75 std::vector<std::string> empty_string_vector;
76 const int max_section_level = 15;
79 const int box_width = 2;
81 const unsigned max_history = 100;
82 const std::string topic_img = "help/topic.png";
83 const std::string closed_section_img = "help/closed_section.png";
84 const std::string open_section_img = "help/open_section.png";
85 // The topic to open by default when opening the help dialog.
86 const std::string default_show_topic = "..introduction";
87 const std::string unknown_unit_topic = ".unknown_unit";
88 const std::string unit_prefix = "unit_";
89 const std::string terrain_prefix = "terrain_";
90 const std::string race_prefix = "race_";
91 const std::string faction_prefix = "faction_";
92 const std::string era_prefix = "era_";
93 const std::string variation_prefix = "variation_";
94 
95 bool section_is_referenced(const std::string &section_id, const config &cfg)
96 {
97  if (const config &toplevel = cfg.child("toplevel"))
98  {
99  const std::vector<std::string> toplevel_refs
100  = utils::quoted_split(toplevel["sections"]);
101  if (std::find(toplevel_refs.begin(), toplevel_refs.end(), section_id)
102  != toplevel_refs.end()) {
103  return true;
104  }
105  }
106 
107  for (const config &section : cfg.child_range("section"))
108  {
109  const std::vector<std::string> sections_refd
110  = utils::quoted_split(section["sections"]);
111  if (std::find(sections_refd.begin(), sections_refd.end(), section_id)
112  != sections_refd.end()) {
113  return true;
114  }
115  }
116  return false;
117 }
118 
119 bool topic_is_referenced(const std::string &topic_id, const config &cfg)
120 {
121  if (const config &toplevel = cfg.child("toplevel"))
122  {
123  const std::vector<std::string> toplevel_refs
124  = utils::quoted_split(toplevel["topics"]);
125  if (std::find(toplevel_refs.begin(), toplevel_refs.end(), topic_id)
126  != toplevel_refs.end()) {
127  return true;
128  }
129  }
130 
131  for (const config &section : cfg.child_range("section"))
132  {
133  const std::vector<std::string> topics_refd
134  = utils::quoted_split(section["topics"]);
135  if (std::find(topics_refd.begin(), topics_refd.end(), topic_id)
136  != topics_refd.end()) {
137  return true;
138  }
139  }
140  return false;
141 }
142 
143 void parse_config_internal(const config *help_cfg, const config *section_cfg,
144  section &sec, int level)
145 {
146  if (level > max_section_level) {
147  std::cerr << "Maximum section depth has been reached. Maybe circular dependency?"
148  << std::endl;
149  }
150  else if (section_cfg != nullptr) {
151  const std::vector<std::string> sections = utils::quoted_split((*section_cfg)["sections"]);
152  sec.level = level;
153  std::string id = level == 0 ? "toplevel" : (*section_cfg)["id"].str();
154  if (level != 0) {
155  if (!is_valid_id(id)) {
156  std::stringstream ss;
157  ss << "Invalid ID, used for internal purpose: '" << id << "'";
158  throw parse_error(ss.str());
159  }
160  }
161  std::string title = level == 0 ? "" : (*section_cfg)["title"].str();
162  sec.id = id;
163  sec.title = title;
164  std::vector<std::string>::const_iterator it;
165  // Find all child sections.
166  for (it = sections.begin(); it != sections.end(); ++it) {
167  if (const config &child_cfg = help_cfg->find_child("section", "id", *it))
168  {
169  section child_section;
170  parse_config_internal(help_cfg, &child_cfg, child_section, level + 1);
171  sec.add_section(child_section);
172  }
173  else {
174  std::stringstream ss;
175  ss << "Help-section '" << *it << "' referenced from '"
176  << id << "' but could not be found.";
177  throw parse_error(ss.str());
178  }
179  }
180 
181  generate_sections(help_cfg, (*section_cfg)["sections_generator"], sec, level);
182  //TODO: harmonize topics/sections sorting
183  if ((*section_cfg)["sort_sections"] == "yes") {
184  std::sort(sec.sections.begin(),sec.sections.end(), section_less());
185  }
186 
187  bool sort_topics = false;
188  bool sort_generated = true;
189 
190  if ((*section_cfg)["sort_topics"] == "yes") {
191  sort_topics = true;
192  sort_generated = false;
193  } else if ((*section_cfg)["sort_topics"] == "no") {
194  sort_topics = false;
195  sort_generated = false;
196  } else if ((*section_cfg)["sort_topics"] == "generated") {
197  sort_topics = false;
198  sort_generated = true;
199  } else if (!(*section_cfg)["sort_topics"].empty()) {
200  std::stringstream ss;
201  ss << "Invalid sort option: '" << (*section_cfg)["sort_topics"] << "'";
202  throw parse_error(ss.str());
203  }
204 
205  std::vector<topic> generated_topics =
206  generate_topics(sort_generated,(*section_cfg)["generator"]);
207 
208  const std::vector<std::string> topics_id = utils::quoted_split((*section_cfg)["topics"]);
209  std::vector<topic> topics;
210 
211  // Find all topics in this section.
212  for (it = topics_id.begin(); it != topics_id.end(); ++it) {
213  if (const config &topic_cfg = help_cfg->find_child("topic", "id", *it))
214  {
215  std::string text = topic_cfg["text"];
216  text += generate_topic_text(topic_cfg["generator"], help_cfg, sec, generated_topics);
217  topic child_topic(topic_cfg["title"], topic_cfg["id"], text);
218  if (!is_valid_id(child_topic.id)) {
219  std::stringstream ss;
220  ss << "Invalid ID, used for internal purpose: '" << id << "'";
221  throw parse_error(ss.str());
222  }
223  topics.push_back(child_topic);
224  }
225  else {
226  std::stringstream ss;
227  ss << "Help-topic '" << *it << "' referenced from '" << id
228  << "' but could not be found." << std::endl;
229  throw parse_error(ss.str());
230  }
231  }
232 
233  if (sort_topics) {
234  std::sort(topics.begin(),topics.end(), title_less());
235  std::sort(generated_topics.begin(),
236  generated_topics.end(), title_less());
237  std::merge(generated_topics.begin(),
238  generated_topics.end(),topics.begin(),topics.end()
239  ,std::back_inserter(sec.topics),title_less());
240  }
241  else {
242  sec.topics.insert(sec.topics.end(),
243  topics.begin(), topics.end());
244  sec.topics.insert(sec.topics.end(),
245  generated_topics.begin(), generated_topics.end());
246  }
247  }
248 }
249 
251 {
252  section sec;
253  if (cfg != nullptr) {
254  config const &toplevel_cfg = cfg->child("toplevel");
255  parse_config_internal(cfg, toplevel_cfg ? &toplevel_cfg : nullptr, sec);
256  }
257  return sec;
258 }
259 
260 std::vector<topic> generate_topics(const bool sort_generated,const std::string &generator)
261 {
262  std::vector<topic> res;
263  if (generator == "") {
264  return res;
265  }
266 
267  if (generator == "abilities") {
268  res = generate_ability_topics(sort_generated);
269  } else if (generator == "weapon_specials") {
270  res = generate_weapon_special_topics(sort_generated);
271  } else if (generator == "time_of_days") {
272  res = generate_time_of_day_topics(sort_generated);
273  } else {
274  std::vector<std::string> parts = utils::split(generator, ':', utils::STRIP_SPACES);
275  if (parts[0] == "units" && parts.size()>1) {
276  res = generate_unit_topics(sort_generated, parts[1]);
277  } else if (parts[0] == "era" && parts.size()>1) {
278  res = generate_era_topics(sort_generated, parts[1]);
279  } else {
280  WRN_HP << "Found a topic generator that I didn't recognize: " << generator << "\n";
281  }
282  }
283 
284  return res;
285 }
286 
287 void generate_sections(const config *help_cfg, const std::string &generator, section &sec, int level)
288 {
289  if (generator == "races") {
290  generate_races_sections(help_cfg, sec, level);
291  } else if (generator == "terrains") {
292  generate_terrain_sections(help_cfg, sec, level);
293  } else if (generator == "eras") {
294  DBG_HP << "Generating eras...\n";
295  generate_era_sections(help_cfg, sec, level);
296  } else {
297  std::vector<std::string> parts = utils::split(generator, ':', utils::STRIP_SPACES);
298  if (parts[0] == "units" && parts.size()>1) {
299  generate_unit_sections(help_cfg, sec, level, true, parts[1]);
300  } else if (generator.size() > 0) {
301  WRN_HP << "Found a section generator that I didn't recognize: " << generator << "\n";
302  }
303  }
304 }
305 
306 std::string generate_topic_text(const std::string &generator, const config *help_cfg, const section &sec, const std::vector<topic>& generated_topics)
307 {
309  if (generator == "") {
310  return empty_string;
311  } else if (generator == "about") {
312  return generate_about_text();
313  } else {
314  std::vector<std::string> parts = utils::split(generator, ':');
315  if (parts.size()>1 && parts[0] == "contents") {
316  if (parts[1] == "generated") {
317  return generate_contents_links(sec, generated_topics);
318  } else {
319  return generate_contents_links(parts[1], help_cfg);
320  }
321  }
322  }
323  return empty_string;
324 }
325 
327 {
328  if (generator_ && --generator_->count == 0)
329  delete generator_;
330 }
331 
332 topic_text::topic_text(topic_text const &t): parsed_text_(t.parsed_text_), generator_(t.generator_)
333 {
334  if (generator_)
335  ++generator_->count;
336 }
337 
339 {
340  if (generator_ && --generator_->count == 0)
341  delete generator_;
342  generator_ = g;
343  return *this;
344 }
345 
346 const std::vector<std::string>& topic_text::parsed_text() const
347 {
348  if (generator_) {
350  if (--generator_->count == 0)
351  delete generator_;
352  generator_ = nullptr;
353  }
354  return parsed_text_;
355 }
356 
357 std::vector<topic> generate_time_of_day_topics(const bool /*sort_generated*/)
358 {
359  std::vector<topic> topics;
360  std::stringstream toplevel;
361 
362  if (! resources::tod_manager) {
363  toplevel << N_("Only available during a scenario.");
364  topics.push_back( topic("Time of Day Schedule", "..schedule", toplevel.str()) );
365  return topics;
366  }
367  const std::vector<time_of_day>& times = resources::tod_manager->times();
368  for (const time_of_day& time : times)
369  {
370  const std::string id = "time_of_day_" + time.id;
371  const std::string image = "<img>src='" + time.image + "'</img>";
372  std::stringstream text;
373 
374  toplevel << make_link(time.name.str(), id) << jump_to(160) <<
375  image << jump(30) << time.lawful_bonus << '\n';
376 
377  text << image << '\n' <<
378  time.description.str() << '\n' <<
379  "Lawful Bonus: " << time.lawful_bonus << '\n' <<
380  '\n' << make_link(N_("Schedule"), "..schedule");
381 
382  topics.push_back( topic(time.name.str(), id, text.str()) );
383  }
384 
385  topics.push_back( topic("Time of Day Schedule", "..schedule", toplevel.str()) );
386  return topics;
387 }
388 
389 std::vector<topic> generate_weapon_special_topics(const bool sort_generated)
390 {
391  std::vector<topic> topics;
392 
393  std::map<t_string, std::string> special_description;
394  std::map<t_string, std::set<std::string, string_less> > special_units;
395 
396  for (const unit_type_data::unit_type_map::value_type &i : unit_types.types())
397  {
398  const unit_type &type = i.second;
399  // Only show the weapon special if we find it on a unit that
400  // detailed description should be shown about.
401  if (description_type(type) != FULL_DESCRIPTION)
402  continue;
403 
404  std::vector<attack_type> attacks = type.attacks();
405  for (std::vector<attack_type>::const_iterator it = attacks.begin();
406  it != attacks.end(); ++it) {
407 
408  std::vector<std::pair<t_string, t_string> > specials = it->special_tooltips();
409  for ( size_t i = 0; i != specials.size(); ++i )
410  {
411  special_description.insert(std::make_pair(specials[i].first, specials[i].second));
412 
413  if (!type.hide_help()) {
414  //add a link in the list of units having this special
415  std::string type_name = type.type_name();
416  //check for variations (walking corpse/soulless etc)
417  const std::string section_prefix = type.show_variations_in_help() ? ".." : "";
418  std::string ref_id = section_prefix + unit_prefix + type.id();
419  //we put the translated name at the beginning of the hyperlink,
420  //so the automatic alphabetic sorting of std::set can use it
421  std::string link = make_link(type_name, ref_id);
422  special_units[specials[i].first].insert(link);
423  }
424  }
425  }
426  }
427 
428  for (std::map<t_string, std::string>::iterator s = special_description.begin();
429  s != special_description.end(); ++s) {
430  // use untranslated name to have universal topic id
431  std::string id = "weaponspecial_" + s->first.base_str();
432  std::stringstream text;
433  text << s->second;
434  text << "\n\n" << _("<header>text='Units with this special attack'</header>") << "\n";
435  std::set<std::string, string_less> &units = special_units[s->first];
436  for (std::set<std::string, string_less>::iterator u = units.begin(); u != units.end(); ++u) {
437  text << "• " << (*u) << "\n";
438  }
439 
440  topics.push_back( topic(s->first, id, text.str()) );
441  }
442 
443  if (sort_generated)
444  std::sort(topics.begin(), topics.end(), title_less());
445  return topics;
446 }
447 
448 
449 std::vector<topic> generate_ability_topics(const bool sort_generated)
450 {
451  std::vector<topic> topics;
452  std::map<t_string, std::string> ability_description;
453  std::map<t_string, std::set<std::string, string_less> > ability_units;
454  // Look through all the unit types, check if a unit of this type
455  // should have a full description, if so, add this units abilities
456  // for display. We do not want to show abilities that the user has
457  // not encountered yet.
458  for (const unit_type_data::unit_type_map::value_type &i : unit_types.types())
459  {
460  const unit_type &type = i.second;
461  if (description_type(type) == FULL_DESCRIPTION) {
462 
463  std::vector<t_string> const* abil_vecs[2];
464  abil_vecs[0] = &type.abilities();
465  abil_vecs[1] = &type.adv_abilities();
466 
467  std::vector<t_string> const* desc_vecs[2];
468  desc_vecs[0] = &type.ability_tooltips();
469  desc_vecs[1] = &type.adv_ability_tooltips();
470 
471  for(int i=0; i<2; ++i) {
472  std::vector<t_string> const& abil_vec = *abil_vecs[i];
473  std::vector<t_string> const& desc_vec = *desc_vecs[i];
474  for(size_t j=0; j < abil_vec.size(); ++j) {
475  t_string const& abil_name = abil_vec[j];
476  std::string const abil_desc =
477  j >= desc_vec.size() ? "" : desc_vec[j].str();
478 
479  ability_description.insert(std::make_pair(abil_name, abil_desc));
480 
481  if (!type.hide_help()) {
482  //add a link in the list of units with this ability
483  std::string type_name = type.type_name();
484  std::string ref_id = unit_prefix + type.id();
485  //we put the translated name at the beginning of the hyperlink,
486  //so the automatic alphabetic sorting of std::set can use it
487  std::string link = make_link(type_name, ref_id);
488  ability_units[abil_name].insert(link);
489  }
490  }
491  }
492  }
493  }
494 
495  for (std::map<t_string, std::string>::iterator a = ability_description.begin(); a != ability_description.end(); ++a) {
496  // we generate topic's id using the untranslated version of the ability's name
497  std::string id = "ability_" + a->first.base_str();
498  std::stringstream text;
499  text << a->second; //description
500  text << "\n\n" << _("<header>text='Units with this ability'</header>") << "\n";
501  std::set<std::string, string_less> &units = ability_units[a->first];
502  for (std::set<std::string, string_less>::iterator u = units.begin(); u != units.end(); ++u) {
503  text << "• " << (*u) << "\n";
504  }
505 
506  topics.push_back( topic(a->first, id, text.str()) );
507  }
508 
509  if (sort_generated)
510  std::sort(topics.begin(), topics.end(), title_less());
511  return topics;
512 }
513 
514 std::vector<topic> generate_era_topics(const bool sort_generated, const std::string & era_id)
515 {
516  std::vector<topic> topics;
517 
518  const config & era = game_cfg->find_child("era","id", era_id);
519  if(era && !era["hide_help"].to_bool()) {
520  topics = generate_faction_topics(era, sort_generated);
521 
522  std::vector<std::string> faction_links;
523  for (const topic & t : topics) {
524  faction_links.push_back(make_link(t.title, t.id));
525  }
526 
527  std::stringstream text;
528  text << "<header>text='" << _("Era:") << " " << era["name"] << "'</header>" << "\n";
529  text << "\n";
530  const config::attribute_value& description = era["description"];
531  if (!description.empty()) {
532  text << description.t_str() << "\n";
533  text << "\n";
534  }
535 
536  text << "<header>text='" << _("Factions") << "'</header>" << "\n";
537 
538  std::sort(faction_links.begin(), faction_links.end());
539  for (const std::string &link : faction_links) {
540  text << "• " << link << "\n";
541  }
542 
543  topic era_topic(era["name"], ".." + era_prefix + era["id"].str(), text.str());
544 
545  topics.push_back( era_topic );
546  }
547  return topics;
548 }
549 
550 std::vector<topic> generate_faction_topics(const config & era, const bool sort_generated)
551 {
552  std::vector<topic> topics;
553  for (const config &f : era.child_range("multiplayer_side")) {
554  const std::string& id = f["id"];
555  if (id == "Random")
556  continue;
557 
558  std::stringstream text;
559 
560  const config::attribute_value& description = f["description"];
561  if (!description.empty()) {
562  text << description.t_str() << "\n";
563  text << "\n";
564  }
565 
566  const std::vector<std::string> recruit_ids = utils::split(f["recruit"]);
567  std::set<std::string> races;
568  std::set<std::string> alignments;
569 
570  for (const std::string & u_id : recruit_ids) {
571  if (const unit_type * t = unit_types.find(u_id, unit_type::HELP_INDEXED)) {
572  assert(t);
573  const unit_type & type = *t;
574 
575  if (const unit_race *r = unit_types.find_race(type.race_id())) {
576  races.insert(make_link(r->plural_name(), std::string("..") + race_prefix + r->id()));
577  }
578  DBG_HP << type.alignment() << " -> " << type.alignment_description(type.alignment(), type.genders().front()) << "\n";
579  alignments.insert(make_link(type.alignment_description(type.alignment(), type.genders().front()), "time_of_day"));
580  }
581  }
582 
583  if (!races.empty()) {
584  std::set<std::string>::iterator it = races.begin();
585  text << _("Races: ") << *(it++);
586  while(it != races.end()) {
587  text << ", " << *(it++);
588  }
589  text << "\n\n";
590  }
591 
592  if (!alignments.empty()) {
593  std::set<std::string>::iterator it = alignments.begin();
594  text << _("Alignments: ") << *(it++);
595  while(it != alignments.end()) {
596  text << ", " << *(it++);
597  }
598  text << "\n\n";
599  }
600 
601  text << "<header>text='" << _("Leaders") << "'</header>" << "\n";
602  const std::vector<std::string> leaders =
603  make_unit_links_list( utils::split(f["leader"]), true );
604  for (const std::string &link : leaders) {
605  text << "• " << link << "\n";
606  }
607 
608  text << "\n";
609 
610  text << "<header>text='" << _("Recruits") << "'</header>" << "\n";
611  const std::vector<std::string> recruit_links =
612  make_unit_links_list( recruit_ids, true );
613  for (const std::string &link : recruit_links) {
614  text << "• " << link << "\n";
615  }
616 
617  const std::string name = f["name"];
618  const std::string ref_id = faction_prefix + id;
619  topics.push_back( topic(name, ref_id, text.str()) );
620  }
621  if (sort_generated)
622  std::sort(topics.begin(), topics.end(), title_less());
623  return topics;
624 }
625 
627 {
628  std::string link;
629 
631  if (!type) {
632  std::cerr << "Unknown unit type : " << type_id << "\n";
633  // don't return an hyperlink (no page)
634  // instead show the id (as hint)
635  link = type_id;
636  } else if (!type->hide_help()) {
637  std::string name = type->type_name();
638  std::string ref_id;
639  if (description_type(*type) == FULL_DESCRIPTION) {
640  const std::string section_prefix = type->show_variations_in_help() ? ".." : "";
641  ref_id = section_prefix + unit_prefix + type->id();
642  } else {
643  ref_id = unknown_unit_topic;
644  name += " (?)";
645  }
646  link = make_link(name, ref_id);
647  } // if hide_help then link is an empty string
648 
649  return link;
650 }
651 
652 std::vector<std::string> make_unit_links_list(const std::vector<std::string>& type_id_list, bool ordered)
653 {
654  std::vector<std::string> links_list;
655  for (const std::string &type_id : type_id_list) {
656  std::string unit_link = make_unit_link(type_id);
657  if (!unit_link.empty())
658  links_list.push_back(unit_link);
659  }
660 
661  if (ordered)
662  std::sort(links_list.begin(), links_list.end());
663 
664  return links_list;
665 }
666 
667 void generate_races_sections(const config *help_cfg, section &sec, int level)
668 {
669  std::set<std::string, string_less> races;
670  std::set<std::string, string_less> visible_races;
671 
672  for (const unit_type_data::unit_type_map::value_type &i : unit_types.types())
673  {
674  const unit_type &type = i.second;
675  UNIT_DESCRIPTION_TYPE desc_type = description_type(type);
676  if (desc_type == FULL_DESCRIPTION) {
677  races.insert(type.race_id());
678  if (!type.hide_help())
679  visible_races.insert(type.race_id());
680  }
681  }
682 
683  for(std::set<std::string, string_less>::iterator it = races.begin(); it != races.end(); ++it) {
684  section race_section;
685  config section_cfg;
686 
687  bool hidden = (visible_races.count(*it) == 0);
688 
689  section_cfg["id"] = hidden_symbol(hidden) + race_prefix + *it;
690 
691  std::string title;
692  if (const unit_race *r = unit_types.find_race(*it)) {
693  title = r->plural_name();
694  } else {
695  title = _ ("race^Miscellaneous");
696  }
697  section_cfg["title"] = title;
698 
699  section_cfg["sections_generator"] = "units:" + *it;
700  section_cfg["generator"] = "units:" + *it;
701 
702  parse_config_internal(help_cfg, &section_cfg, race_section, level+1);
703  sec.add_section(race_section);
704  }
705 }
706 
707 void generate_era_sections(const config* help_cfg, section & sec, int level)
708 {
709  for (const config & era : game_cfg->child_range("era")) {
710  if (era["hide_help"].to_bool()) {
711  continue;
712  }
713 
714  DBG_HP << "Adding help section: " << era["id"].str() << "\n";
715 
716  section era_section;
717  config section_cfg;
718  section_cfg["id"] = era_prefix + era["id"].str();
719  section_cfg["title"] = era["name"];
720 
721  section_cfg["generator"] = "era:" + era["id"].str();
722 
723  DBG_HP << section_cfg.debug() << "\n";
724 
725  parse_config_internal(help_cfg, &section_cfg, era_section, level+1);
726  sec.add_section(era_section);
727  }
728 }
729 
730 void generate_terrain_sections(const config* /*help_cfg*/, section& sec, int /*level*/)
731 {
733 
734  if (!tdata) {
735  WRN_HP << "When building terrain help sections, couldn't acquire terrain types data, aborting.\n";
736  return;
737  }
738 
739  std::map<std::string, section> base_map;
740 
741  const t_translation::t_list& t_listi = tdata->list();
742 
743  for (const t_translation::t_terrain& t : t_listi) {
744 
745  const terrain_type& info = tdata->get_terrain_info(t);
746 
747  bool hidden = info.is_combined() || info.hide_help();
748 
751  hidden = true;
752 
753  topic terrain_topic;
754  terrain_topic.title = info.editor_name();
755  terrain_topic.id = hidden_symbol(hidden) + terrain_prefix + info.id();
756  terrain_topic.text = new terrain_topic_generator(info);
757 
758  t_translation::t_list base_terrains = tdata->underlying_union_terrain(t);
759  for (const t_translation::t_terrain& base : base_terrains) {
760 
761  const terrain_type& base_info = tdata->get_terrain_info(base);
762 
763  if (!base_info.is_nonnull() || base_info.hide_help())
764  continue;
765 
766  section& base_section = base_map[base_info.id()];
767 
768  base_section.id = terrain_prefix + base_info.id();
769  base_section.title = base_info.editor_name();
770 
771  if (base_info.id() == info.id())
772  terrain_topic.id = ".." + terrain_prefix + info.id();
773  base_section.topics.push_back(terrain_topic);
774  }
775  }
776 
777  for (std::map<std::string, section>::const_iterator it = base_map.begin(); it != base_map.end(); ++it) {
778  sec.add_section(it->second);
779  }
780 }
781 
782 void generate_unit_sections(const config* /*help_cfg*/, section& sec, int level, const bool /*sort_generated*/, const std::string& race)
783 {
784  for (const unit_type_data::unit_type_map::value_type &i : unit_types.types()) {
785  const unit_type &type = i.second;
786 
787  if (type.race_id() != race)
788  continue;
789 
790  if (!type.show_variations_in_help())
791  continue;
792 
793  section base_unit;
794  for (const std::string &variation_id : type.variations()) {
795  // TODO: Do we apply encountered stuff to variations?
796  const unit_type &var_type = type.get_variation(variation_id);
797  const std::string topic_name = var_type.type_name() + "\n" + var_type.variation_name();
798  const std::string var_ref = hidden_symbol(var_type.hide_help()) + variation_prefix + var_type.id() + "_" + variation_id;
799 
800  topic var_topic(topic_name, var_ref, "");
801  var_topic.text = new unit_topic_generator(var_type, variation_id);
802  base_unit.topics.push_back(var_topic);
803  }
804 
805  const std::string type_name = type.type_name();
806  const std::string ref_id = hidden_symbol(type.hide_help()) + unit_prefix + type.id();
807 
808  base_unit.id = ref_id;
809  base_unit.title = type_name;
810  base_unit.level = level +1;
811 
812  sec.add_section(base_unit);
813  }
814 }
815 
816 std::vector<topic> generate_unit_topics(const bool sort_generated, const std::string& race)
817 {
818  std::vector<topic> topics;
819  std::set<std::string, string_less> race_units;
820  std::set<std::string, string_less> race_topics;
821  std::set<std::string> alignments;
822 
823  for (const unit_type_data::unit_type_map::value_type &i : unit_types.types())
824  {
825  const unit_type &type = i.second;
826 
827  if (type.race_id() != race)
828  continue;
829 
830  UNIT_DESCRIPTION_TYPE desc_type = description_type(type);
831  if (desc_type != FULL_DESCRIPTION)
832  continue;
833 
834  const std::string type_name = type.type_name();
835  const std::string real_prefix = type.show_variations_in_help() ? ".." : "";
836  const std::string ref_id = hidden_symbol(type.hide_help()) + real_prefix + unit_prefix + type.id();
837  topic unit_topic(type_name, ref_id, "");
838  unit_topic.text = new unit_topic_generator(type);
839  topics.push_back(unit_topic);
840 
841  if (!type.hide_help()) {
842  // we also record an hyperlink of this unit
843  // in the list used for the race topic
844  std::string link = make_link(type_name, ref_id);
845  race_units.insert(link);
846 
847  alignments.insert(make_link(type.alignment_description(type.alignment(), type.genders().front()), "time_of_day"));
848  }
849  }
850 
851  //generate the hidden race description topic
852  std::string race_id = "..race_"+race;
853  std::string race_name;
854  std::string race_description;
855  if (const unit_race *r = unit_types.find_race(race)) {
856  race_name = r->plural_name();
857  race_description = r->description();
858  // if (description.empty()) description = _("No description Available");
859  for (const config &additional_topic : r->additional_topics())
860  {
861  std::string id = additional_topic["id"];
862  std::string title = additional_topic["title"];
863  std::string text = additional_topic["text"];
864  //topic additional_topic(title, id, text);
865  topics.push_back(topic(title,id,text));
866  std::string link = make_link(title, id);
867  race_topics.insert(link);
868  }
869  } else {
870  race_name = _ ("race^Miscellaneous");
871  // description = _("Here put the description of the Miscellaneous race");
872  }
873 
874  std::stringstream text;
875 
876  if (!race_description.empty()) {
877  text << race_description << "\n\n";
878  }
879 
880  if (!alignments.empty()) {
881  std::set<std::string>::iterator it = alignments.begin();
882  text << (alignments.size() > 1 ? _("Alignments: ") : _("Alignment: ")) << *(it++);
883  while(it != alignments.end()) {
884  text << ", " << *(it++);
885  }
886  text << "\n\n";
887  }
888 
889  text << _("<header>text='Units of this race'</header>") << "\n";
890  for (std::set<std::string, string_less>::iterator u = race_units.begin(); u != race_units.end(); ++u) {
891  text << "• " << (*u) << "\n";
892  }
893 
894  topics.push_back(topic(race_name, race_id, text.str()) );
895 
896  if (sort_generated)
897  std::sort(topics.begin(), topics.end(), title_less());
898 
899  return topics;
900 }
901 
903 {
906  return FULL_DESCRIPTION;
907  }
908 
909  const std::set<std::string> &encountered_units = preferences::encountered_units();
910  if (encountered_units.find(type.id()) != encountered_units.end()) {
911  return FULL_DESCRIPTION;
912  }
913  return NO_DESCRIPTION;
914 }
915 
917 {
918  std::vector<std::string> about_lines = about::get_text();
919  std::vector<std::string> res_lines;
920  std::transform(about_lines.begin(), about_lines.end(), std::back_inserter(res_lines),
922  res_lines.erase(std::remove(res_lines.begin(), res_lines.end(), ""), res_lines.end());
923  std::string text = utils::join(res_lines, "\n");
924  return text;
925 }
926 
927 std::string generate_contents_links(const std::string& section_name, config const *help_cfg)
928 {
929  config const &section_cfg = help_cfg->find_child("section", "id", section_name);
930  if (!section_cfg) {
931  return std::string();
932  }
933 
934  std::ostringstream res;
935 
936  std::vector<std::string> topics = utils::quoted_split(section_cfg["topics"]);
937 
938  // we use an intermediate structure to allow a conditional sorting
939  typedef std::pair<std::string,std::string> link;
940  std::vector<link> topics_links;
941 
943  // Find all topics in this section.
944  for (t = topics.begin(); t != topics.end(); ++t) {
945  if (config const &topic_cfg = help_cfg->find_child("topic", "id", *t)) {
946  std::string id = topic_cfg["id"];
947  if (is_visible_id(id))
948  topics_links.push_back(link(topic_cfg["title"], id));
949  }
950  }
951 
952  if (section_cfg["sort_topics"] == "yes") {
953  std::sort(topics_links.begin(),topics_links.end());
954  }
955 
957  for (l = topics_links.begin(); l != topics_links.end(); ++l) {
958  std::string link = make_link(l->first, l->second);
959  res << "• " << link << "\n";
960  }
961 
962  return res.str();
963 }
964 
965 std::string generate_contents_links(const section &sec, const std::vector<topic>& topics)
966 {
967  std::stringstream res;
968 
969  section_list::const_iterator s;
970  for (s = sec.sections.begin(); s != sec.sections.end(); ++s) {
971  if (is_visible_id((*s)->id)) {
972  std::string link = make_link((*s)->title, ".."+(*s)->id);
973  res << "• " << link << "\n";
974  }
975  }
976 
977  std::vector<topic>::const_iterator t;
978  for (t = topics.begin(); t != topics.end(); ++t) {
979  if (is_visible_id(t->id)) {
980  std::string link = make_link(t->title, t->id);
981  res << "• " << link << "\n";
982  }
983  }
984  return res.str();
985 }
986 
987 bool topic::operator==(const topic &t) const
988 {
989  return t.id == id;
990 }
991 
992 bool topic::operator<(const topic &t) const
993 {
994  return id < t.id;
995 }
996 
998 {
999  std::for_each(sections.begin(), sections.end(), delete_section());
1000 }
1001 
1003  title(sec.title),
1004  id(sec.id),
1005  topics(sec.topics),
1006  sections(),
1007  level(sec.level)
1008 {
1009  std::transform(sec.sections.begin(), sec.sections.end(),
1010  std::back_inserter(sections), create_section());
1011 }
1012 
1014 {
1015  title = sec.title;
1016  id = sec.id;
1017  level = sec.level;
1018  topics.insert(topics.end(), sec.topics.begin(), sec.topics.end());
1019  std::transform(sec.sections.begin(), sec.sections.end(),
1020  std::back_inserter(sections), create_section());
1021  return *this;
1022 }
1023 
1024 
1025 bool section::operator==(const section &sec) const
1026 {
1027  return sec.id == id;
1028 }
1029 
1030 bool section::operator<(const section &sec) const
1031 {
1032  return id < sec.id;
1033 }
1034 
1036 {
1037  sections.push_back(new section(s));
1038 }
1039 
1041 {
1042  topics.clear();
1043  std::for_each(sections.begin(), sections.end(), delete_section());
1044  sections.clear();
1045 }
1046 
1047 
1048 
1049 const topic *find_topic(const section &sec, const std::string &id)
1050 {
1051  topic_list::const_iterator tit =
1052  std::find_if(sec.topics.begin(), sec.topics.end(), has_id(id));
1053  if (tit != sec.topics.end()) {
1054  return &(*tit);
1055  }
1056  section_list::const_iterator sit;
1057  for (sit = sec.sections.begin(); sit != sec.sections.end(); ++sit) {
1058  const topic *t = find_topic(*(*sit), id);
1059  if (t != nullptr) {
1060  return t;
1061  }
1062  }
1063  return nullptr;
1064 }
1065 
1066 const section *find_section(const section &sec, const std::string &id)
1067 {
1068  section_list::const_iterator sit =
1069  std::find_if(sec.sections.begin(), sec.sections.end(), has_id(id));
1070  if (sit != sec.sections.end()) {
1071  return *sit;
1072  }
1073  for (sit = sec.sections.begin(); sit != sec.sections.end(); ++sit) {
1074  const section *s = find_section(*(*sit), id);
1075  if (s != nullptr) {
1076  return s;
1077  }
1078  }
1079  return nullptr;
1080 }
1081 
1082 std::vector<std::string> parse_text(const std::string &text)
1083 {
1084  std::vector<std::string> res;
1085  bool last_char_escape = false;
1086  const char escape_char = '\\';
1087  std::stringstream ss;
1088  size_t pos;
1089  enum { ELEMENT_NAME, OTHER } state = OTHER;
1090  for (pos = 0; pos < text.size(); ++pos) {
1091  const char c = text[pos];
1092  if (c == escape_char && !last_char_escape) {
1093  last_char_escape = true;
1094  }
1095  else {
1096  if (state == OTHER) {
1097  if (c == '<') {
1098  if (last_char_escape) {
1099  ss << c;
1100  }
1101  else {
1102  res.push_back(ss.str());
1103  ss.str("");
1104  state = ELEMENT_NAME;
1105  }
1106  }
1107  else {
1108  ss << c;
1109  }
1110  }
1111  else if (state == ELEMENT_NAME) {
1112  if (c == '/') {
1113  std::string msg = "Erroneous / in element name.";
1114  throw parse_error(msg);
1115  }
1116  else if (c == '>') {
1117  // End of this name.
1118  std::stringstream s;
1119  const std::string element_name = ss.str();
1120  ss.str("");
1121  s << "</" << element_name << ">";
1122  const std::string end_element_name = s.str();
1123  size_t end_pos = text.find(end_element_name, pos);
1124  if (end_pos == std::string::npos) {
1125  std::stringstream msg;
1126  msg << "Unterminated element: " << element_name;
1127  throw parse_error(msg.str());
1128  }
1129  s.str("");
1130  const std::string contents = text.substr(pos + 1, end_pos - pos - 1);
1131  const std::string element = convert_to_wml(element_name, contents);
1132  res.push_back(element);
1133  pos = end_pos + end_element_name.size() - 1;
1134  state = OTHER;
1135  }
1136  else {
1137  ss << c;
1138  }
1139  }
1140  last_char_escape = false;
1141  }
1142  }
1143  if (state == ELEMENT_NAME) {
1144  std::stringstream msg;
1145  msg << "Element '" << ss.str() << "' continues through end of string.";
1146  throw parse_error(msg.str());
1147  }
1148  if (ss.str() != "") {
1149  // Add the last string.
1150  res.push_back(ss.str());
1151  }
1152  return res;
1153 }
1154 
1155 std::string convert_to_wml(const std::string &element_name, const std::string &contents)
1156 {
1157  std::stringstream ss;
1158  bool in_quotes = false;
1159  bool last_char_escape = false;
1160  const char escape_char = '\\';
1161  std::vector<std::string> attributes;
1162  // Find the different attributes.
1163  // No checks are made for the equal sign or something like that.
1164  // Attributes are just separated by spaces or newlines.
1165  // Attributes that contain spaces must be in single quotes.
1166  for (size_t pos = 0; pos < contents.size(); ++pos) {
1167  const char c = contents[pos];
1168  if (c == escape_char && !last_char_escape) {
1169  last_char_escape = true;
1170  }
1171  else {
1172  if (c == '\'' && !last_char_escape) {
1173  ss << '"';
1174  in_quotes = !in_quotes;
1175  }
1176  else if ((c == ' ' || c == '\n') && !last_char_escape && !in_quotes) {
1177  // Space or newline, end of attribute.
1178  attributes.push_back(ss.str());
1179  ss.str("");
1180  }
1181  else {
1182  ss << c;
1183  }
1184  last_char_escape = false;
1185  }
1186  }
1187  if (in_quotes) {
1188  std::stringstream msg;
1189  msg << "Unterminated single quote after: '" << ss.str() << "'";
1190  throw parse_error(msg.str());
1191  }
1192  if (ss.str() != "") {
1193  attributes.push_back(ss.str());
1194  }
1195  ss.str("");
1196  // Create the WML.
1197  ss << "[" << element_name << "]\n";
1198  for (std::vector<std::string>::const_iterator it = attributes.begin();
1199  it != attributes.end(); ++it) {
1200  ss << *it << "\n";
1201  }
1202  ss << "[/" << element_name << "]\n";
1203  return ss.str();
1204 }
1205 
1206 SDL_Color string_to_color(const std::string &cmp_str)
1207 {
1208  if (cmp_str == "green") {
1209  return font::GOOD_COLOR;
1210  }
1211  if (cmp_str == "red") {
1212  return font::BAD_COLOR;
1213  }
1214  if (cmp_str == "black") {
1215  return font::BLACK_COLOR;
1216  }
1217  if (cmp_str == "yellow") {
1218  return font::YELLOW_COLOR;
1219  }
1220  if (cmp_str == "white") {
1221  return font::BIGMAP_COLOR;
1222  }
1223  // a #rrggbb color in pango format.
1224  if (*cmp_str.c_str() == '#' && cmp_str.size() == 7) {
1225  return int_to_color(strtoul(cmp_str.c_str() + 1, nullptr, 16));
1226  }
1227  return font::NORMAL_COLOR;
1228 }
1229 
1230 std::vector<std::string> split_in_width(const std::string &s, const int font_size,
1231  const unsigned width)
1232 {
1233  std::vector<std::string> res;
1234  try {
1235  const std::string& first_line = font::word_wrap_text(s, font_size, width, -1, 1, true);
1236  res.push_back(first_line);
1237  if(s.size() > first_line.size()) {
1238  res.push_back(s.substr(first_line.size()));
1239  }
1240  }
1242  {
1243  throw parse_error (_("corrupted original file"));
1244  }
1245 
1246  return res;
1247 }
1248 
1250 {
1251  if (text.length() > 0 && text[0] == ' ') {
1252  return text.substr(1);
1253  }
1254  return text;
1255 }
1256 
1258 {
1259  size_t first_word_start = s.find_first_not_of(' ');
1260  if (first_word_start == std::string::npos) {
1261  return s;
1262  }
1263  size_t first_word_end = s.find_first_of(" \n", first_word_start);
1264  if( first_word_end == first_word_start ) {
1265  // This word is '\n'.
1266  first_word_end = first_word_start+1;
1267  }
1268 
1269  //if no gap(' ' or '\n') found, test if it is CJK character
1270  std::string re = s.substr(0, first_word_end);
1271 
1272  utf8::iterator ch(re);
1273  if (ch == utf8::iterator::end(re))
1274  return re;
1275 
1276  ucs4::char_t firstchar = *ch;
1277  if (font::is_cjk_char(firstchar)) {
1278  re = unicode_cast<utf8::string>(firstchar);
1279  }
1280  return re;
1281 }
1282 
1284 {
1285  toplevel.clear();
1286  hidden_sections.clear();
1287  if (game_cfg != nullptr) {
1288  const config *help_config = &game_cfg->child("help");
1289  if (!*help_config) {
1290  help_config = &dummy_cfg;
1291  }
1292  try {
1293  toplevel = parse_config(help_config);
1294  // Create a config object that contains everything that is
1295  // not referenced from the toplevel element. Read this
1296  // config and save these sections and topics so that they
1297  // can be referenced later on when showing help about
1298  // specified things, but that should not be shown when
1299  // opening the help browser in the default manner.
1300  config hidden_toplevel;
1301  std::stringstream ss;
1302  for (const config &section : help_config->child_range("section"))
1303  {
1304  const std::string id = section["id"];
1305  if (find_section(toplevel, id) == nullptr) {
1306  // This section does not exist referenced from the
1307  // toplevel. Hence, add it to the hidden ones if it
1308  // is not referenced from another section.
1309  if (!section_is_referenced(id, *help_config)) {
1310  if (ss.str() != "") {
1311  ss << ",";
1312  }
1313  ss << id;
1314  }
1315  }
1316  }
1317  hidden_toplevel["sections"] = ss.str();
1318  ss.str("");
1319  for (const config &topic : help_config->child_range("topic"))
1320  {
1321  const std::string id = topic["id"];
1322  if (find_topic(toplevel, id) == nullptr) {
1323  if (!topic_is_referenced(id, *help_config)) {
1324  if (ss.str() != "") {
1325  ss << ",";
1326  }
1327  ss << id;
1328  }
1329  }
1330  }
1331  hidden_toplevel["topics"] = ss.str();
1332  config hidden_cfg = *help_config;
1333  // Change the toplevel to our new, custom built one.
1334  hidden_cfg.clear_children("toplevel");
1335  hidden_cfg.add_child("toplevel", hidden_toplevel);
1336  hidden_sections = parse_config(&hidden_cfg);
1337  }
1338  catch (parse_error& e) {
1339  std::stringstream msg;
1340  msg << "Parse error when parsing help text: '" << e.message << "'";
1341  std::cerr << msg.str() << std::endl;
1342  }
1343  }
1344 }
1345 
1346 // id starting with '.' are hidden
1348  return (hidden ? "." : "");
1349 }
1350 
1351 bool is_visible_id(const std::string &id) {
1352  return (id.empty() || id[0] != '.');
1353 }
1354 
1355 /// Return true if the id is valid for user defined topics and
1356 /// sections. Some IDs are special, such as toplevel and may not be
1357 /// be defined in the config.
1358 bool is_valid_id(const std::string &id) {
1359  if (id == "toplevel") {
1360  return false;
1361  }
1362  if (id.find(unit_prefix) == 0 || id.find(hidden_symbol() + unit_prefix) == 0) {
1363  return false;
1364  }
1365  if (id.find("ability_") == 0) {
1366  return false;
1367  }
1368  if (id.find("weaponspecial_") == 0) {
1369  return false;
1370  }
1371  if (id == "hidden") {
1372  return false;
1373  }
1374  return true;
1375 }
1376 
1377 
1378 // Return the width for the image with filename.
1379 unsigned image_width(const std::string &filename)
1380 {
1381  image::locator loc(filename);
1383  if (surf != nullptr) {
1384  return surf->w;
1385  }
1386  return 0;
1387 }
1388 
1389 void push_tab_pair(std::vector<std::pair<std::string, unsigned int> > &v, const std::string &s)
1390 {
1391  v.push_back(std::make_pair(s, font::line_width(s, normal_font_size)));
1392 }
1393 
1394 std::string generate_table(const table_spec &tab, const unsigned int spacing)
1395 {
1396  table_spec::const_iterator row_it;
1397  std::vector<std::pair<std::string, unsigned> >::const_iterator col_it;
1398  unsigned int num_cols = 0;
1399  for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1400  if (row_it->size() > num_cols) {
1401  num_cols = row_it->size();
1402  }
1403  }
1404  std::vector<unsigned int> col_widths(num_cols, 0);
1405  // Calculate the width of all columns, including spacing.
1406  for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1407  unsigned int col = 0;
1408  for (col_it = row_it->begin(); col_it != row_it->end(); ++col_it) {
1409  if (col_widths[col] < col_it->second + spacing) {
1410  col_widths[col] = col_it->second + spacing;
1411  }
1412  ++col;
1413  }
1414  }
1415  std::vector<unsigned int> col_starts(num_cols);
1416  // Calculate the starting positions of all columns
1417  for (unsigned int i = 0; i < num_cols; ++i) {
1418  unsigned int this_col_start = 0;
1419  for (unsigned int j = 0; j < i; ++j) {
1420  this_col_start += col_widths[j];
1421  }
1422  col_starts[i] = this_col_start;
1423  }
1424  std::stringstream ss;
1425  for (row_it = tab.begin(); row_it != tab.end(); ++row_it) {
1426  unsigned int col = 0;
1427  for (col_it = row_it->begin(); col_it != row_it->end(); ++col_it) {
1428  ss << jump_to(col_starts[col]) << col_it->first;
1429  ++col;
1430  }
1431  ss << "\n";
1432  }
1433  return ss.str();
1434 }
1435 
1436 /// Prepend all chars with meaning inside attributes with a backslash.
1438 {
1439  return utils::escape(s, "'\\");
1440 }
1441 
1442 /// Load the appropriate terrain types data to use
1444  if (display::get_singleton()) {
1446  } else if (game_config_manager::get()){
1448  } else {
1449  return tdata_cache();
1450  }
1451 }
1452 
1453 
1454 } // end namespace help
std::string jump_to(const unsigned pos)
Definition: help_impl.hpp:381
const SDL_Color BIGMAP_COLOR
Definition: font.cpp:575
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
child_itors child_range(const std::string &key)
Definition: config.cpp:613
section parse_config(const config *cfg)
Parse a help config, return the top level section.
Definition: help_impl.cpp:250
std::string id
Definition: help_impl.hpp:165
::tod_manager * tod_manager
Definition: resources.cpp:31
std::string make_unit_link(const std::string &type_id)
return a hyperlink with the unit's name and pointing to the unit page return empty string if this uni...
Definition: help_impl.cpp:626
const std::string open_section_img
Definition: help_impl.cpp:84
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
const std::string unit_prefix
Definition: help_impl.cpp:88
config dummy_cfg
Definition: help_impl.cpp:74
std::vector< topic > generate_unit_topics(const bool sort_generated, const std::string &race)
Definition: help_impl.cpp:816
const std::string era_prefix
Definition: help_impl.cpp:92
void generate_unit_sections(const config *, section &sec, int level, const bool, const std::string &race)
Definition: help_impl.cpp:782
const std::string topic_img
Definition: help_impl.cpp:82
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:112
boost::shared_ptr< terrain_type_data > tdata_cache
const std::vector< t_string > & abilities() const
Definition: types.hpp:166
void push_tab_pair(std::vector< std::pair< std::string, unsigned int > > &v, const std::string &s)
Definition: help_impl.cpp:1389
std::string era()
const std::string race
const int title_size
Definition: help_impl.cpp:77
GLint level
Definition: glew.h:1220
bool operator==(const section &) const
Two sections are equal if their IDs are equal.
Definition: help_impl.cpp:1025
const std::vector< std::string > & parsed_text() const
Definition: help_impl.cpp:346
const SDL_Color BLACK_COLOR
Definition: font.cpp:569
const std::string closed_section_img
Definition: help_impl.cpp:83
const GLfloat * c
Definition: glew.h:12741
std::string remove_first_space(const std::string &text)
Definition: help_impl.cpp:1249
int pos
Definition: formula.cpp:800
A section contains topics and sections along with title and ID.
Definition: help_impl.hpp:143
const std::string unknown_unit_topic
Definition: help_impl.cpp:87
const std::string race_prefix
Definition: help_impl.cpp:90
rng * generator
This generator is automatically synced during synced context.
Definition: random_new.cpp:52
std::string generate_topic_text(const std::string &generator, const config *help_cfg, const section &sec, const std::vector< topic > &generated_topics)
Definition: help_impl.cpp:306
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
t_string t_str() const
Definition: config.cpp:358
SDL_Color int_to_color(const Uint32 rgb)
Definition: utils.cpp:63
logger & info()
Definition: log.cpp:91
#define WRN_HP
Definition: help_impl.cpp:59
const std::vector< t_string > & ability_tooltips() const
Definition: types.hpp:167
std::vector< std::string > get_text(const std::string &campaign, bool split_multiline_headers)
Definition: about.cpp:110
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
help::section toplevel
Definition: help_impl.cpp:66
const std::string & variation_name() const
Definition: types.hpp:136
Thrown when the help system fails to parse something.
Definition: help_impl.hpp:221
std::vector< std::string > empty_string_vector
Definition: help_impl.cpp:75
bool is_scope_active(hk_scopes s)
std::string word_wrap_text(const std::string &unwrapped_text, int font_size, int max_width, int max_height, int max_lines, bool partial_line)
Wrap text.
bool is_overlay() const
Definition: terrain.hpp:76
unit_type_data unit_types
Definition: types.cpp:1314
const std::string & id() const
Definition: terrain.hpp:37
bool operator<(const topic &) const
Comparison on the ID.
Definition: help_impl.cpp:992
GLboolean GLboolean g
Definition: glew.h:7319
bool is_combined() const
Definition: terrain.hpp:77
const int normal_font_size
Definition: help_impl.cpp:80
std::string debug() const
Definition: config.cpp:1438
topic_generator * generator_
Definition: help_impl.hpp:84
unsigned image_width(const std::string &filename)
Definition: help_impl.cpp:1379
void clear_children(const std::string &key)
Definition: config.cpp:820
const std::string terrain_prefix
Definition: help_impl.cpp:89
int last_num_encountered_terrains
Definition: help_impl.cpp:71
bool empty() const
Tests for an attribute that either was never set or was set to "".
Definition: config.cpp:375
const config * game_cfg
Definition: help_impl.cpp:64
const SDL_Color NORMAL_COLOR
Definition: font.cpp:564
GLdouble GLdouble t
Definition: glew.h:1366
bool is_visible_id(const std::string &id)
Definition: help_impl.cpp:1351
const t_string & editor_name() const
Definition: terrain.hpp:34
virtual const gamemap & map() const =0
std::string generate_about_text()
Definition: help_impl.cpp:916
std::vector< topic > generate_weapon_special_topics(const bool sort_generated)
Definition: help_impl.cpp:389
std::vector< std::string > make_unit_links_list(const std::vector< std::string > &type_id_list, bool ordered)
return a list of hyperlinks to unit's pages (ordered or not)
Definition: help_impl.cpp:652
Variant for storing WML attributes.
Definition: config.hpp:223
const tdata_cache & tdata() const
Definition: map.hpp:70
void generate_sections(const config *help_cfg, const std::string &generator, section &sec, int level)
Dispatch generators to their appropriate functions.
Definition: help_impl.cpp:287
GLdouble l
Definition: glew.h:6966
const int SIZE_NORMAL
Definition: font.hpp:58
const SDL_Color GOOD_COLOR
Definition: font.cpp:567
help::section hidden_sections
Definition: help_impl.cpp:68
section_list sections
Definition: help_impl.hpp:167
static const std::string empty_string
Definition: frame.cpp:119
const section * find_section(const section &sec, const std::string &id)
Search for the section with the specified identifier in the section and its subsections.
Definition: help_impl.cpp:1066
GLuint GLenum GLenum transform
Definition: glew.h:11418
STRIP_SPACES : strips leading and trailing blank spaces.
std::string id
Definition: help_impl.hpp:136
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:48
To be used as a function object to locate sections and topics with a specified ID.
Definition: help_impl.hpp:174
static game_config_manager * get()
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
const unit_race * find_race(const std::string &) const
Definition: types.cpp:1286
The text displayed in a topic.
Definition: help_impl.hpp:81
std::string generate_table(const table_spec &tab, const unsigned int spacing)
Definition: help_impl.cpp:1394
GLuint GLuint end
Definition: glew.h:1221
GLuint id
Definition: glew.h:1647
std::string hidden_symbol(bool hidden)
Definition: help_impl.cpp:1347
const SDL_Color BAD_COLOR
Definition: font.cpp:568
const unit_type_map & types() const
Definition: types.hpp:313
std::string title
Definition: help_impl.hpp:165
const int SIZE_15
Definition: font.hpp:64
const GLdouble * v
Definition: glew.h:1359
std::vector< topic > generate_era_topics(const bool sort_generated, const std::string &era_id)
Definition: help_impl.cpp:514
std::vector< std::vector< std::pair< std::string, unsigned int > > > table_spec
Definition: help_impl.hpp:402
UNIT_DESCRIPTION_TYPE
Definition: help_impl.hpp:250
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
void generate_terrain_sections(const config *, section &sec, int)
Definition: help_impl.cpp:730
std::set< t_translation::t_terrain > & encountered_terrains()
std::vector< topic > generate_faction_topics(const config &era, const bool sort_generated)
Definition: help_impl.cpp:550
const std::vector< unit_race::GENDER > & genders() const
The returned vector will not be empty, provided this has been built to the HELP_INDEXED status...
Definition: types.hpp:198
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:741
config & add_child(const std::string &key)
Definition: config.cpp:743
tdata_cache load_terrain_types_data()
Load the appropriate terrain types data to use.
Definition: help_impl.cpp:1443
map_display and display: classes which take care of displaying the map and game-data on the screen...
std::vector< std::string > parsed_text_
Definition: help_impl.hpp:83
static lg::log_domain log_help("help")
bool show_all_units_in_help()
bool is_nonnull() const
Definition: terrain.hpp:51
surf
Definition: filter.cpp:143
bool hide_help() const
Definition: terrain.hpp:39
std::string race_id() const
Returns the ID of this type's race without the need to build the type.
Definition: types.hpp:215
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:47
void parse_config_internal(const config *help_cfg, const config *section_cfg, section &sec, int level)
Recursive function used by parse_config.
Definition: help_impl.cpp:143
std::string join(T const &v, const std::string &s=",")
Generates a new string joining container items in a list.
const int box_width
Definition: help_impl.cpp:79
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
bool section_is_referenced(const std::string &section_id, const config &cfg)
Return true if the section with id section_id is referenced from another section in the config...
Definition: help_impl.cpp:95
bool topic_is_referenced(const std::string &topic_id, const config &cfg)
Return true if the topic with id topic_id is referenced from another section in the config...
Definition: help_impl.cpp:119
std::vector< std::string > quoted_split(std::string const &val, char c, int flags, char quote)
This function is identical to split(), except it does not split when it otherwise would if the previo...
To be used as a function object when sorting section lists on the title.
Definition: help_impl.hpp:194
GLuint res
Definition: glew.h:9258
const std::string variation_prefix
Definition: help_impl.cpp:93
Thrown by operations encountering invalid UTF-8 data.
topic_text & operator=(topic_generator *g)
Definition: help_impl.cpp:338
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:457
int last_num_encountered_units
Definition: help_impl.cpp:70
std::string make_link(const std::string &text, const std::string &dst)
Definition: help_impl.hpp:375
std::string convert_to_wml(const std::string &element_name, const std::string &contents)
Convert the contents to wml attributes, surrounded within [element_name]...[/element_name].
Definition: help_impl.cpp:1155
boost::uint32_t char_t
bool is_cjk_char(const ucs4::char_t ch)
Determine if a ucs4::char_t is a CJK character.
size_t i
Definition: function.cpp:1057
std::vector< std::string > variations() const
Definition: types.cpp:726
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
std::set< std::string > & encountered_units()
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
void generate_races_sections(const config *help_cfg, section &sec, int level)
Definition: help_impl.cpp:667
#define N_(String)
Definition: gettext.hpp:90
static int sort(lua_State *L)
Definition: ltablib.cpp:246
bool hide_help() const
Definition: types.cpp:561
static iterator_base end(const string_type &str)
const tdata_cache & terrain_types() const
std::string escape(const std::string &s)
Prepend all chars with meaning inside attributes with a backslash.
Definition: help_impl.cpp:1437
std::vector< std::string > parse_text(const std::string &text)
Parse a text string.
Definition: help_impl.cpp:1082
const unsigned max_history
Definition: help_impl.cpp:81
GLuint const GLchar * name
Definition: glew.h:1782
const int SIZE_LARGE
Definition: font.hpp:66
std::vector< topic > generate_time_of_day_topics(const bool)
Definition: help_impl.cpp:357
static lg::log_domain log_display("display")
#define DBG_HP
Definition: help_impl.cpp:60
std::string generate_contents_links(const std::string &section_name, config const *help_cfg)
Definition: help_impl.cpp:927
const std::string faction_prefix
Definition: help_impl.cpp:91
std::string jump(const unsigned amount)
Definition: help_impl.hpp:388
const std::vector< t_string > & adv_abilities() const
Definition: types.hpp:170
const std::string default_show_topic
Definition: help_impl.cpp:86
void generate_contents()
Generate the help contents from the configurations given to the manager.
Definition: help_impl.cpp:1283
#define g
Definition: glew.h:12730
bool find(E event, F functor)
Tests whether an event handler is available.
std::vector< topic > generate_topics(const bool sort_generated, const std::string &generator)
Definition: help_impl.cpp:260
const topic * find_topic(const section &sec, const std::string &id)
Search for the topic with the specified identifier in the section and its subsections.
Definition: help_impl.cpp:1049
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
bool operator<(const section &) const
Comparison on the ID.
Definition: help_impl.cpp:1030
std::vector< attack_type > attacks() const
Definition: types.cpp:484
config & find_child(const std::string &key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:1010
this module manages the cache of images.
Definition: image.cpp:75
Standard logging facilities (interface).
Generate a topic text on the fly.
Definition: help_impl.hpp:62
std::string message
Definition: exceptions.hpp:29
GLint * first
Definition: glew.h:1496
const int title2_size
Definition: help_impl.cpp:78
A topic contains a title, an id and some text.
Definition: help_impl.hpp:111
#define c
Definition: glew.h:12743
bool last_debug_state
Definition: help_impl.cpp:72
const display_context & get_disp_context() const
Definition: display.hpp:167
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1155
const std::string remove
remove directive
GLint GLint GLint GLint GLint GLint GLsizei width
Definition: glew.h:1220
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define e
Definition: help.cpp:57
Class to be used as a function object when generating the about text.
Definition: help_impl.hpp:359
SDL_Color string_to_color(const std::string &cmp_str)
Return the color the string represents.
Definition: help_impl.cpp:1206
const SDL_Color YELLOW_COLOR
Definition: font.cpp:570
std::string title
Definition: help_impl.hpp:136
bool is_valid_id(const std::string &id)
Return true if the id is valid for user defined topics and sections.
Definition: help_impl.cpp:1358
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
static std::string alignment_description(ALIGNMENT align, unit_race::GENDER gender=unit_race::MALE)
GLdouble s
Definition: glew.h:1358
std::string get_first_word(const std::string &s)
Return the first word in s, not removing any spaces in the start of it.
Definition: help_impl.cpp:1257
const int font_size
bool operator==(const topic &) const
Two topics are equal if their IDs are equal.
Definition: help_impl.cpp:987
void add_section(const section &s)
Allocate memory for and add the section.
Definition: help_impl.cpp:1035
UNIT_DESCRIPTION_TYPE description_type(const unit_type &type)
Return the type of description that should be shown for a unit of the given kind. ...
Definition: help_impl.cpp:902
std::vector< topic > generate_ability_topics(const bool sort_generated)
Definition: help_impl.cpp:449
void generate_era_sections(const config *help_cfg, section &sec, int level)
Definition: help_impl.cpp:707
Defines the MAKE_ENUM macro.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
int line_width(const std::string &line, int font_size, int style)
Determine the width of a line of text given a certain font size.
Definition: font.cpp:969
unit_map * units
Definition: resources.cpp:35
const int max_section_level
Definition: help_impl.cpp:76
To be used as a function object when sorting topic lists on the title.
Definition: help_impl.hpp:186
std::vector< std::string > split_in_width(const std::string &s, const int font_size, const unsigned width)
Make a best effort to word wrap s. All parts are less than width.
Definition: help_impl.cpp:1230
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:115
topic_list topics
Definition: help_impl.hpp:166
topic_text text
Definition: help_impl.hpp:137
std::string string
std::vector< t_terrain > t_list
Definition: translation.hpp:75
section & operator=(const section &)
Definition: help_impl.cpp:1013
GLclampf f
Definition: glew.h:3024
const std::vector< t_string > & adv_ability_tooltips() const
Definition: types.hpp:171