The Battle for Wesnoth  1.17.4+dev
game_load.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2022
3  by Jörg Hinrichs <joerg.hinrichs@alice-dsl.de>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "desktop/open.hpp"
21 #include "filesystem.hpp"
22 #include "font/pango/escape.hpp"
23 #include "formatter.hpp"
24 #include "formula/string_utils.hpp"
25 #include "game_classification.hpp"
26 #include "game_config.hpp"
27 #include "gettext.hpp"
28 #include "gui/auxiliary/field.hpp"
29 #include "gui/core/log.hpp"
31 #include "gui/widgets/button.hpp"
32 #include "gui/widgets/image.hpp"
33 #include "gui/widgets/label.hpp"
34 #include "gui/widgets/listbox.hpp"
35 #include "gui/widgets/minimap.hpp"
37 #include "gui/widgets/settings.hpp"
38 #include "gui/widgets/text_box.hpp"
40 #include "gui/widgets/window.hpp"
41 #include "language.hpp"
42 #include "picture.hpp"
43 #include "preferences/game.hpp"
45 #include "utils/general.hpp"
46 #include <functional>
47 #include "game_config_view.hpp"
48 
49 #include <cctype>
50 
51 static lg::log_domain log_gameloaddlg{"gui/dialogs/game_load_dialog"};
52 #define ERR_GAMELOADDLG LOG_STREAM(err, log_gameloaddlg)
53 #define WRN_GAMELOADDLG LOG_STREAM(warn, log_gameloaddlg)
54 #define LOG_GAMELOADDLG LOG_STREAM(info, log_gameloaddlg)
55 #define DBG_GAMELOADDLG LOG_STREAM(debug, log_gameloaddlg)
56 
57 namespace gui2::dialogs
58 {
59 
60 REGISTER_DIALOG(game_load)
61 
62 bool game_load::execute(const game_config_view& cache_config, savegame::load_game_metadata& data)
63 {
64  if(savegame::save_index_class::default_saves_dir()->get_saves_list().empty()) {
65  bool found_files = false;
66  for(const auto& dir : filesystem::find_other_version_saves_dirs()) {
67  if(!found_files) {
68  // this needs to be a shared_ptr because get_saves_list() uses shared_from_this
69  auto index = std::make_shared<savegame::save_index_class>(dir.path);
70  found_files = !index->get_saves_list().empty();
71  }
72  }
73 
74  if(!found_files) {
75  gui2::show_transient_message(_("No Saved Games"), _("There are no saved games to load."));
76  return false;
77  }
78  }
79 
80  return game_load(cache_config, data).show();
81 }
82 
84  : filename_(data.filename)
85  , save_index_manager_(data.manager)
86  , change_difficulty_(register_bool("change_difficulty", true, data.select_difficulty))
87  , show_replay_(register_bool("show_replay", true, data.show_replay))
88  , cancel_orders_(register_bool("cancel_orders", true, data.cancel_orders))
89  , summary_(data.summary)
90  , games_()
91  , cache_config_(cache_config)
92  , last_words_()
93 {
94 }
95 
97 {
98  // Allow deleting saves with the Delete key.
99  connect_signal_pre_key_press(window, std::bind(&game_load::key_press_callback, this, std::placeholders::_5));
100 
101  text_box* filter = find_widget<text_box>(&window, "txtFilter", false, true);
102 
103  filter->set_text_changed_callback(std::bind(&game_load::filter_text_changed, this, std::placeholders::_2));
104 
105  listbox& list = find_widget<listbox>(&window, "savegame_list", false);
106 
108 
109  window.keyboard_capture(filter);
110  window.add_to_keyboard_chain(&list);
111 
112  list.register_sorting_option(0, [this](const int i) { return games_[i].name(); });
113  list.register_sorting_option(1, [this](const int i) { return games_[i].modified(); });
114 
116 
117  connect_signal_mouse_left_click(find_widget<button>(&window, "delete", false),
118  std::bind(&game_load::delete_button_callback, this));
119 
120  connect_signal_mouse_left_click(find_widget<button>(&window, "browse_saves_folder", false),
121  std::bind(&game_load::browse_button_callback, this));
122 
123  menu_button& dir_list = find_widget<menu_button>(&window, "dirList", false);
124 
125  dir_list.set_use_markup(true);
126  set_save_dir_list(dir_list);
127 
129 
131 }
132 
134 {
135  const auto other_dirs = filesystem::find_other_version_saves_dirs();
136  if(other_dirs.empty()) {
138  return;
139  }
140 
141  std::vector<config> options;
142 
143  // The first option in the list is the current version's save dir
144  options.emplace_back("label", _("game_version^Current Version"), "path", "");
145 
146  for(const auto& known_dir : other_dirs) {
147  options.emplace_back(
148  "label", VGETTEXT("game_version^Wesnoth $version", utils::string_map{{"version", known_dir.version}}),
149  "path", known_dir.path
150  );
151  }
152 
153  dir_list.set_values(options);
154 }
155 
157 {
158  listbox& list = find_widget<listbox>(get_window(), "savegame_list", false);
159 
160  list.clear();
161 
162  games_ = save_index_manager_->get_saves_list();
163 
164  for(const auto& game : games_) {
165  std::map<std::string, string_map> data;
167 
168  std::string name = game.name();
169  utils::ellipsis_truncate(name, 40);
170  item["label"] = name;
171  data.emplace("filename", item);
172 
173  item["label"] = game.format_time_summary();
174  data.emplace("date", item);
175 
176  list.add_row(data);
177  }
178 
179  find_widget<button>(get_window(), "delete", false).set_active(!save_index_manager_->read_only());
180 }
181 
183 {
184  filename_ = game.name();
185  summary_ = game.summary();
186 
187  find_widget<minimap>(get_window(), "minimap", false)
188  .set_map_data(summary_["map_data"]);
189 
190  find_widget<label>(get_window(), "lblScenario", false)
191  .set_label(summary_["label"]);
192 
193  listbox& leader_list = find_widget<listbox>(get_window(), "leader_list", false);
194 
195  leader_list.clear();
196 
197  const std::string sprite_scale_mod = (formatter() << "~SCALE_INTO(" << game_config::tile_size << ',' << game_config::tile_size << ')').str();
198 
199  for(const auto& leader : summary_.child_range("leader")) {
200  std::map<std::string, string_map> data;
202 
203  // First, we evaluate whether the leader image as provided exists.
204  // If not, we try getting a binary path-independent path. If that still doesn't
205  // work, we fallback on unknown-unit.png.
206  std::string leader_image = leader["leader_image"].str();
207  if(!::image::exists(leader_image)) {
208  leader_image = filesystem::get_independent_binary_file_path("images", leader_image);
209 
210  // The leader TC modifier isn't appending if the independent image path can't
211  // be resolved during save_index entry creation, so we need to add it here.
212  if(!leader_image.empty()) {
213  leader_image += leader["leader_image_tc_modifier"].str();
214  }
215  }
216 
217  if(leader_image.empty()) {
218  leader_image = "units/unknown-unit.png" + leader["leader_image_tc_modifier"].str();
219  } else {
220  // Scale down any sprites larger than 72x72
221  leader_image += sprite_scale_mod;
222  }
223 
224  item["label"] = leader_image;
225  data.emplace("imgLeader", item);
226 
227  item["label"] = leader["leader_name"];
228  data.emplace("leader_name", item);
229 
230  item["label"] = leader["gold"];
231  data.emplace("leader_gold", item);
232 
233  // TRANSLATORS: "reserve" refers to units on the recall list
234  item["label"] = VGETTEXT("$active active, $reserve reserve", {{"active", leader["units"]}, {"reserve", leader["recall_units"]}});
235  data.emplace("leader_troops", item);
236 
237  leader_list.add_row(data);
238  }
239 
240  std::stringstream str;
241  str << game.format_time_local() << "\n";
243 
244  // The new label value may have more or less lines than the previous value, so invalidate the layout.
245  find_widget<scroll_label>(get_window(), "slblSummary", false).set_label(str.str());
247 
248  toggle_button& replay_toggle = dynamic_cast<toggle_button&>(*show_replay_->get_widget());
249  toggle_button& cancel_orders_toggle = dynamic_cast<toggle_button&>(*cancel_orders_->get_widget());
250  toggle_button& change_difficulty_toggle = dynamic_cast<toggle_button&>(*change_difficulty_->get_widget());
251 
252  const bool is_replay = savegame::loadgame::is_replay_save(summary_);
253  const bool is_scenario_start = summary_["turn"].empty();
254 
255  // Always toggle show_replay on if the save is a replay
256  replay_toggle.set_value(is_replay);
257  replay_toggle.set_active(!is_replay && !is_scenario_start);
258 
259  // Cancel orders doesn't make sense on replay saves or start-of-scenario saves
260  cancel_orders_toggle.set_active(!is_replay && !is_scenario_start);
261 
262  // Changing difficulty doesn't make sense on non-start-of-scenario saves
263  change_difficulty_toggle.set_active(!is_replay && is_scenario_start);
264 }
265 
266 // This is a wrapper that prevents a corrupted save file (if it happens to be
267 // the first in the list) from making the dialog fail to open.
269 {
270  bool successfully_displayed_a_game = false;
271 
272  try {
273  const int selected_row = find_widget<listbox>(get_window(), "savegame_list", false).get_selected_row();
274  if(selected_row < 0) {
275  find_widget<button>(get_window(), "delete", false).set_active(false);
276  } else {
277  find_widget<button>(get_window(), "delete", false).set_active(!save_index_manager_->read_only());
279  successfully_displayed_a_game = true;
280  }
281  } catch(const config::error& e) {
282  // Clear the UI widgets, show an error message.
283  const std::string preamble = _("The selected file is corrupt: ");
284  const std::string message = e.message.empty() ? "(no details)" : e.message;
285  ERR_GAMELOADDLG << preamble << message << "\n";
286  }
287 
288  if(!successfully_displayed_a_game) {
289  find_widget<minimap>(get_window(), "minimap", false).set_map_data("");
290  find_widget<label>(get_window(), "lblScenario", false)
291  .set_label("");
292  find_widget<scroll_label>(get_window(), "slblSummary", false)
293  .set_label("");
294 
295  listbox& leader_list = find_widget<listbox>(get_window(), "leader_list", false);
296  leader_list.clear();
297 
298  toggle_button& replay_toggle = dynamic_cast<toggle_button&>(*show_replay_->get_widget());
299  toggle_button& cancel_orders_toggle = dynamic_cast<toggle_button&>(*cancel_orders_->get_widget());
300  toggle_button& change_difficulty_toggle = dynamic_cast<toggle_button&>(*change_difficulty_->get_widget());
301 
302  replay_toggle.set_active(false);
303  cancel_orders_toggle.set_active(false);
304  change_difficulty_toggle.set_active(false);
305  }
306 
307  // Disable Load button if nothing is selected or if the currently selected file can't be loaded
308  find_widget<button>(get_window(), "ok", false).set_active(successfully_displayed_a_game);
309 
310  // Disable 'Enter' loading in the same circumstance
311  get_window()->set_enter_disabled(!successfully_displayed_a_game);
312 }
313 
314 void game_load::filter_text_changed(const std::string& text)
315 {
316  listbox& list = find_widget<listbox>(get_window(), "savegame_list", false);
317 
318  const std::vector<std::string> words = utils::split(text, ' ');
319 
320  if(words == last_words_)
321  return;
322  last_words_ = words;
323 
324  boost::dynamic_bitset<> show_items;
325  show_items.resize(list.get_item_count(), true);
326 
327  if(!text.empty()) {
328  for(unsigned int i = 0; i < list.get_item_count() && i < games_.size(); i++) {
329  bool found = false;
330  for(const auto & word : words)
331  {
332  found = std::search(games_[i].name().begin(),
333  games_[i].name().end(),
334  word.begin(),
335  word.end(),
337  != games_[i].name().end();
338 
339  if(!found) {
340  // one word doesn't match, we don't reach words.end()
341  break;
342  }
343  }
344 
345  show_items[i] = found;
346  }
347  }
348 
349  list.set_row_shown(show_items);
350 }
351 
352 void game_load::evaluate_summary_string(std::stringstream& str, const config& cfg_summary)
353 {
354  if(cfg_summary["corrupt"].to_bool()) {
355  str << "\n<span color='#f00'>" << _("(Invalid)") << "</span>";
356  // \todo: this skips the catch() statement in display_savegame. Low priority, as the
357  // dialog's state is reasonable; the "load" button is inactive, the "delete" button is
358  // active, and (cosmetic bug) it leaves the "change difficulty" toggle active. Can be
359  // triggered by creating an empty file in the save directory.
360  return;
361  }
362 
363  const std::string& campaign_type = cfg_summary["campaign_type"];
364  const std::string campaign_id = cfg_summary["campaign"];
365  auto campaign_type_enum = campaign_type::get_enum(campaign_type);
366 
367  if(campaign_type_enum) {
368  switch(*campaign_type_enum) {
369  case campaign_type::type::scenario: {
370  const config* campaign = nullptr;
371  if(!campaign_id.empty()) {
372  if(const config& c = cache_config_.find_child("campaign", "id", campaign_id)) {
373  campaign = &c;
374  }
375  }
376 
377  utils::string_map symbols;
378  if(campaign != nullptr) {
379  symbols["campaign_name"] = (*campaign)["name"];
380  } else {
381  // Fallback to nontranslatable campaign id.
382  symbols["campaign_name"] = "(" + campaign_id + ")";
383  }
384 
385  str << VGETTEXT("Campaign: $campaign_name", symbols);
386 
387  // Display internal id for debug purposes if we didn't above
388  if(game_config::debug && (campaign != nullptr)) {
389  str << '\n' << "(" << campaign_id << ")";
390  }
391  break;
392  }
393  case campaign_type::type::multiplayer:
394  str << _("Multiplayer");
395  break;
396  case campaign_type::type::tutorial:
397  str << _("Tutorial");
398  break;
399  case campaign_type::type::test:
400  str << _("Test scenario");
401  break;
402  }
403  } else {
404  str << campaign_type;
405  }
406 
407  str << "\n";
408 
409  if(savegame::loadgame::is_replay_save(cfg_summary)) {
410  str << _("Replay");
411  } else if(!cfg_summary["turn"].empty()) {
412  str << _("Turn") << " " << cfg_summary["turn"];
413  } else {
414  str << _("Scenario start");
415  }
416 
417  if(campaign_type_enum) {
418  switch (*campaign_type_enum) {
419  case campaign_type::type::scenario:
420  case campaign_type::type::multiplayer: {
421  const config* campaign = nullptr;
422  if (!campaign_id.empty()) {
423  if (const config& c = cache_config_.find_child("campaign", "id", campaign_id)) {
424  campaign = &c;
425  }
426  }
427 
428  // 'SCENARIO' or SP should only ever be campaigns
429  // 'MULTIPLAYER' may be a campaign with difficulty or single scenario without difficulty
430  // For the latter do not show the difficulty - even though it will be listed as
431  // NORMAL -> Medium in the save file it should not be considered valid (GitHub Issue #5321)
432  if (campaign != nullptr) {
433  str << "\n" << _("Difficulty: ");
434  try {
435  const config& difficulty = campaign->find_child("difficulty", "define", cfg_summary["difficulty"]);
436  std::ostringstream ss;
437  ss << difficulty["label"] << " (" << difficulty["description"] << ")";
438  str << ss.str();
439  }
440  catch (const config::error&) {
441  // fall back to standard difficulty string in case of exception
442  str << string_table[cfg_summary["difficulty"]];
443  }
444  }
445 
446  break;
447  }
448  case campaign_type::type::tutorial:
449  case campaign_type::type::test:
450  break;
451  }
452  } else {
453  }
454 
455  if(!cfg_summary["version"].empty()) {
456  str << "\n" << _("Version: ") << cfg_summary["version"];
457  }
458 
459  const std::vector<std::string>& active_mods = utils::split(cfg_summary["active_mods"]);
460  if(!active_mods.empty()) {
461  str << "\n" << _("Modifications: ");
462  for(const auto& mod_id : active_mods) {
463  std::string mod_name;
464  try {
465  mod_name = cache_config_.find_child("modification", "id", mod_id)["name"].str();
466  } catch(const config::error&) {
467  // Fallback to nontranslatable mod id.
468  mod_name = "(" + mod_id + ")";
469  }
470 
471  str << "\n" << font::unicode_bullet << " " << mod_name;
472  }
473  }
474 }
476 {
478 }
479 
481 {
482  listbox& list = find_widget<listbox>(get_window(), "savegame_list", false);
483 
484  const std::size_t index = std::size_t(list.get_selected_row());
485  if(index < games_.size()) {
486 
487  // See if we should ask the user for deletion confirmation
489  if(!gui2::dialogs::game_delete::execute()) {
490  return;
491  }
492  }
493 
494  // Delete the file
495  save_index_manager_->delete_game(games_[index].name());
496 
497  // Remove it from the list of saves
498  games_.erase(games_.begin() + index);
499 
500  list.remove_row(index);
501 
503  }
504 }
505 
506 void game_load::key_press_callback(const SDL_Keycode key)
507 {
508  //
509  // Don't delete games when we're typing in the textbox!
510  //
511  // I'm not sure if this check was necessary when I first added this feature
512  // (I didn't check at the time), but regardless, it's needed now. If it turns
513  // out I screwed something up in my refactoring, I'll remove this.
514  //
515  // - vultraz, 2017-08-28
516  //
517  if(find_widget<text_box>(get_window(), "txtFilter", false).get_state() == text_box_base::FOCUSED) {
518  return;
519  }
520 
521  if(key == SDLK_DELETE) {
523  }
524 }
525 
527 {
528  menu_button& dir_list = find_widget<menu_button>(get_window(), "dirList", false);
529 
530  const auto& path = dir_list.get_value_config()["path"].str();
531  if(path.empty()) {
533  } else {
534  save_index_manager_ = std::make_shared<savegame::save_index_class>(path);
535  }
536 
539 }
540 
541 } // namespace dialogs
Define the common log macros for the gui toolkit.
void set_text_changed_callback(std::function< void(text_box_base *textbox, const std::string text)> cb)
Set the text_changed callback.
const ::config & get_value_config() const
Returns the entire config object for the selected row.
Definition: menu_button.hpp:99
virtual void set_value(unsigned selected, bool fire_event=false) override
Inherited from selectable_item.
std::map< std::string, t_string > string_map
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:61
field_bool * change_difficulty_
Definition: game_load.hpp:81
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:172
config & find_child(config_key_type 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:885
Main class to show messages to the user.
Definition: message.hpp:35
This shows the dialog to select and load a savegame file.
Definition: game_load.hpp:49
#define ERR_GAMELOADDLG
Definition: game_load.cpp:52
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:210
std::shared_ptr< savegame::save_index_class > & save_index_manager_
Definition: game_load.hpp:79
void populate_game_list()
Update (both internally and visually) the list of games.
Definition: game_load.cpp:156
const game_config_view & cache_config_
Definition: game_load.hpp:88
This file contains the window object, this object is a top level container which has the event manage...
child_itors child_range(config_key_type key)
Definition: config.cpp:344
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
void ellipsis_truncate(std::string &str, const std::size_t size)
Truncates a string to a given utf-8 character count and then appends an ellipsis. ...
void key_press_callback(const SDL_Keycode key)
Definition: game_load.cpp:506
bool chars_equal_insensitive(char a, char b)
Definition: general.hpp:23
void set_save_dir_list(menu_button &dir_list)
Definition: game_load.cpp:133
window * get_window() const
Returns a pointer to the dialog&#39;s window.
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:271
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed...
static std::string _(const char *str)
Definition: gettext.hpp:93
bool show(const unsigned auto_close_time=0)
Shows the window.
std::vector< std::string > last_words_
Definition: game_load.hpp:90
game_load(const game_config_view &cache_config, savegame::load_game_metadata &data)
Definition: game_load.cpp:83
const config & summary() const
Definition: save_index.cpp:247
Class for a single line text area.
Definition: text_box.hpp:141
std::string get_independent_binary_file_path(const std::string &type, const std::string &filename)
Returns an asset path to filename for binary path-independent use in saved games. ...
std::string filename_
Definition: action_wml.cpp:564
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1025
The listbox class.
Definition: listbox.hpp:45
std::string format_time_local() const
Definition: save_index.cpp:252
const config & options()
Definition: game.cpp:562
Desktop environment interaction functions.
The base template for associating string values with enum values.
Definition: enum_base.hpp:30
This file contains the settings handling of the widget library.
std::ostringstream wrapper.
Definition: formatter.hpp:39
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:121
void set_visible(const visibility visible)
Definition: widget.cpp:477
string_enums::enum_base< campaign_type_defines > campaign_type
static bool is_replay_save(const config &cfg)
Definition: savegame.hpp:126
void set_values(const std::vector<::config > &values, unsigned selected=0)
std::string path
Definition: game_config.cpp:39
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:127
virtual void set_use_markup(bool use_markup)
std::vector< savegame::save_info > games_
Definition: game_load.hpp:87
const std::string & name() const
Definition: save_index.hpp:39
styled_widget * get_widget()
Definition: field.hpp:193
bool open_object([[maybe_unused]] const std::string &path_or_url)
Definition: open.cpp:47
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: game_load.cpp:96
std::size_t i
Definition: function.cpp:967
The user set the widget invisible, that means:
void filter_text_changed(const std::string &text)
Definition: game_load.cpp:314
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
grid & add_row(const string_map &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:62
field_bool * show_replay_
Definition: game_load.hpp:82
Declarations for File-IO.
const bool & debug
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
const std::string unicode_bullet
Definition: constants.cpp:47
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
bool ask_delete_saves()
Definition: game.cpp:783
const config & find_child(config_key_type key, const std::string &name, const std::string &value) const
void display_savegame_internal(const savegame::save_info &game)
Part of display_savegame that might throw a config::error if the savegame data is corrupt...
Definition: game_load.cpp:182
field_bool * cancel_orders_
Definition: game_load.hpp:83
std::string & filename_
Definition: game_load.hpp:78
Filename and modification date for a file list.
Definition: save_index.hpp:26
static std::optional< typename T::type > get_enum(const std::string value)
Convert a string into its enum equivalent.
Definition: enum_base.hpp:52
std::vector< std::string > split(const config_attribute_value &val)
unsigned int tile_size
Definition: game_config.cpp:69
symbol_table string_table
Definition: language.cpp:65
void connect_signal_pre_key_press(dispatcher &dispatcher, const signal_keyboard &signal)
Connects the signal for &#39;snooping&#39; on the keypress.
Definition: dispatcher.cpp:167
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:82
std::string message
Definition: exceptions.hpp:30
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:799
static lg::log_domain log_gameloaddlg
Definition: game_load.cpp:51
#define e
std::vector< other_version_dir > find_other_version_saves_dirs()
Searches for directories containing saves created by other versions of Wesnoth.
Definition: filesystem.cpp:826
void register_sorting_option(const int col, const Func &f)
Definition: listbox.hpp:269
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
mock_char c
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:139
void evaluate_summary_string(std::stringstream &str, const config &cfg_summary)
Definition: game_load.cpp:352
bool empty() const
Definition: config.cpp:941
Class for a toggle button.
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:198
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410
virtual void set_active(const bool active) override
See styled_widget::set_active.
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:286