The Battle for Wesnoth  1.17.10+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  : modal_dialog(window_id())
85  , filename_(data.filename)
86  , save_index_manager_(data.manager)
87  , change_difficulty_(register_bool("change_difficulty", true, data.select_difficulty))
88  , show_replay_(register_bool("show_replay", true, data.show_replay))
89  , cancel_orders_(register_bool("cancel_orders", true, data.cancel_orders))
90  , summary_(data.summary)
91  , games_()
92  , cache_config_(cache_config)
93  , last_words_()
94 {
95 }
96 
98 {
99  // Allow deleting saves with the Delete key.
100  connect_signal_pre_key_press(window, std::bind(&game_load::key_press_callback, this, std::placeholders::_5));
101 
102  text_box* filter = find_widget<text_box>(&window, "txtFilter", false, true);
103 
104  filter->set_text_changed_callback(std::bind(&game_load::filter_text_changed, this, std::placeholders::_2));
105 
106  listbox& list = find_widget<listbox>(&window, "savegame_list", false);
107 
109 
110  window.keyboard_capture(filter);
111  window.add_to_keyboard_chain(&list);
112 
113  list.register_sorting_option(0, [this](const int i) { return games_[i].name(); });
114  list.register_sorting_option(1, [this](const int i) { return games_[i].modified(); });
115 
117 
118  connect_signal_mouse_left_click(find_widget<button>(&window, "delete", false),
119  std::bind(&game_load::delete_button_callback, this));
120 
121  connect_signal_mouse_left_click(find_widget<button>(&window, "browse_saves_folder", false),
122  std::bind(&game_load::browse_button_callback, this));
123 
124  menu_button& dir_list = find_widget<menu_button>(&window, "dirList", false);
125 
126  dir_list.set_use_markup(true);
127  set_save_dir_list(dir_list);
128 
130 
132 }
133 
135 {
136  const auto other_dirs = filesystem::find_other_version_saves_dirs();
137  if(other_dirs.empty()) {
139  return;
140  }
141 
142  std::vector<config> options;
143 
144  // The first option in the list is the current version's save dir
145  options.emplace_back("label", _("game_version^Current Version"), "path", "");
146 
147  for(const auto& known_dir : other_dirs) {
148  options.emplace_back(
149  "label", VGETTEXT("game_version^Wesnoth $version", utils::string_map{{"version", known_dir.version}}),
150  "path", known_dir.path
151  );
152  }
153 
154  dir_list.set_values(options);
155 }
156 
158 {
159  listbox& list = find_widget<listbox>(get_window(), "savegame_list", false);
160 
161  list.clear();
162 
163  games_ = save_index_manager_->get_saves_list();
164 
165  for(const auto& game : games_) {
168 
169  std::string name = game.name();
170  utils::ellipsis_truncate(name, 40);
171  item["label"] = name;
172  data.emplace("filename", item);
173 
174  item["label"] = game.format_time_summary();
175  data.emplace("date", item);
176 
177  list.add_row(data);
178  }
179 
180  find_widget<button>(get_window(), "delete", false).set_active(!save_index_manager_->read_only());
181 }
182 
184 {
185  filename_ = game.name();
186  summary_ = game.summary();
187 
188  find_widget<minimap>(get_window(), "minimap", false)
189  .set_map_data(summary_["map_data"]);
190 
191  find_widget<label>(get_window(), "lblScenario", false)
192  .set_label(summary_["label"]);
193 
194  listbox& leader_list = find_widget<listbox>(get_window(), "leader_list", false);
195 
196  leader_list.clear();
197 
198  const std::string sprite_scale_mod = (formatter() << "~SCALE_INTO(" << game_config::tile_size << ',' << game_config::tile_size << ')').str();
199 
200  for(const auto& leader : summary_.child_range("leader")) {
203 
204  // First, we evaluate whether the leader image as provided exists.
205  // If not, we try getting a binary path-independent path. If that still doesn't
206  // work, we fallback on unknown-unit.png.
207  std::string leader_image = leader["leader_image"].str();
208  if(!::image::exists(leader_image)) {
209  leader_image = filesystem::get_independent_binary_file_path("images", leader_image);
210 
211  // The leader TC modifier isn't appending if the independent image path can't
212  // be resolved during save_index entry creation, so we need to add it here.
213  if(!leader_image.empty()) {
214  leader_image += leader["leader_image_tc_modifier"].str();
215  }
216  }
217 
218  if(leader_image.empty()) {
219  leader_image = "units/unknown-unit.png" + leader["leader_image_tc_modifier"].str();
220  } else {
221  // Scale down any sprites larger than 72x72
222  leader_image += sprite_scale_mod;
223  }
224 
225  item["label"] = leader_image;
226  data.emplace("imgLeader", item);
227 
228  item["label"] = leader["leader_name"];
229  data.emplace("leader_name", item);
230 
231  item["label"] = leader["gold"];
232  data.emplace("leader_gold", item);
233 
234  // TRANSLATORS: "reserve" refers to units on the recall list
235  item["label"] = VGETTEXT("$active active, $reserve reserve", {{"active", leader["units"]}, {"reserve", leader["recall_units"]}});
236  data.emplace("leader_troops", item);
237 
238  leader_list.add_row(data);
239  }
240 
241  std::stringstream str;
242  str << game.format_time_local() << "\n";
244 
245  // The new label value may have more or less lines than the previous value, so invalidate the layout.
246  find_widget<scroll_label>(get_window(), "slblSummary", false).set_label(str.str());
248 
249  toggle_button& replay_toggle = dynamic_cast<toggle_button&>(*show_replay_->get_widget());
250  toggle_button& cancel_orders_toggle = dynamic_cast<toggle_button&>(*cancel_orders_->get_widget());
251  toggle_button& change_difficulty_toggle = dynamic_cast<toggle_button&>(*change_difficulty_->get_widget());
252 
253  const bool is_replay = savegame::loadgame::is_replay_save(summary_);
254  const bool is_scenario_start = summary_["turn"].empty();
255 
256  // Always toggle show_replay on if the save is a replay
257  replay_toggle.set_value(is_replay);
258  replay_toggle.set_active(!is_replay && !is_scenario_start);
259 
260  // Cancel orders doesn't make sense on replay saves or start-of-scenario saves
261  cancel_orders_toggle.set_active(!is_replay && !is_scenario_start);
262 
263  // Changing difficulty doesn't make sense on non-start-of-scenario saves
264  change_difficulty_toggle.set_active(!is_replay && is_scenario_start);
265 }
266 
267 // This is a wrapper that prevents a corrupted save file (if it happens to be
268 // the first in the list) from making the dialog fail to open.
270 {
271  bool successfully_displayed_a_game = false;
272 
273  try {
274  const int selected_row = find_widget<listbox>(get_window(), "savegame_list", false).get_selected_row();
275  if(selected_row < 0) {
276  find_widget<button>(get_window(), "delete", false).set_active(false);
277  } else {
278  find_widget<button>(get_window(), "delete", false).set_active(!save_index_manager_->read_only());
280  successfully_displayed_a_game = true;
281  }
282  } catch(const config::error& e) {
283  // Clear the UI widgets, show an error message.
284  const std::string preamble = _("The selected file is corrupt: ");
285  const std::string message = e.message.empty() ? "(no details)" : e.message;
286  ERR_GAMELOADDLG << preamble << message;
287  }
288 
289  if(!successfully_displayed_a_game) {
290  find_widget<minimap>(get_window(), "minimap", false).set_map_data("");
291  find_widget<label>(get_window(), "lblScenario", false)
292  .set_label("");
293  find_widget<scroll_label>(get_window(), "slblSummary", false)
294  .set_label("");
295 
296  listbox& leader_list = find_widget<listbox>(get_window(), "leader_list", false);
297  leader_list.clear();
298 
299  toggle_button& replay_toggle = dynamic_cast<toggle_button&>(*show_replay_->get_widget());
300  toggle_button& cancel_orders_toggle = dynamic_cast<toggle_button&>(*cancel_orders_->get_widget());
301  toggle_button& change_difficulty_toggle = dynamic_cast<toggle_button&>(*change_difficulty_->get_widget());
302 
303  replay_toggle.set_active(false);
304  cancel_orders_toggle.set_active(false);
305  change_difficulty_toggle.set_active(false);
306  }
307 
308  // Disable Load button if nothing is selected or if the currently selected file can't be loaded
309  find_widget<button>(get_window(), "ok", false).set_active(successfully_displayed_a_game);
310 
311  // Disable 'Enter' loading in the same circumstance
312  get_window()->set_enter_disabled(!successfully_displayed_a_game);
313 }
314 
315 void game_load::filter_text_changed(const std::string& text)
316 {
317  listbox& list = find_widget<listbox>(get_window(), "savegame_list", false);
318 
319  const std::vector<std::string> words = utils::split(text, ' ');
320 
321  if(words == last_words_)
322  return;
323  last_words_ = words;
324 
325  boost::dynamic_bitset<> show_items;
326  show_items.resize(list.get_item_count(), true);
327 
328  if(!text.empty()) {
329  for(unsigned int i = 0; i < list.get_item_count() && i < games_.size(); i++) {
330  bool found = false;
331  for(const auto & word : words)
332  {
333  found = std::search(games_[i].name().begin(),
334  games_[i].name().end(),
335  word.begin(),
336  word.end(),
338  != games_[i].name().end();
339 
340  if(!found) {
341  // one word doesn't match, we don't reach words.end()
342  break;
343  }
344  }
345 
346  show_items[i] = found;
347  }
348  }
349 
350  list.set_row_shown(show_items);
351 }
352 
353 void game_load::evaluate_summary_string(std::stringstream& str, const config& cfg_summary)
354 {
355  if(cfg_summary["corrupt"].to_bool()) {
356  str << "\n<span color='#f00'>" << _("(Invalid)") << "</span>";
357  // \todo: this skips the catch() statement in display_savegame. Low priority, as the
358  // dialog's state is reasonable; the "load" button is inactive, the "delete" button is
359  // active, and (cosmetic bug) it leaves the "change difficulty" toggle active. Can be
360  // triggered by creating an empty file in the save directory.
361  return;
362  }
363 
364  const std::string& campaign_type = cfg_summary["campaign_type"];
365  const std::string campaign_id = cfg_summary["campaign"];
366  auto campaign_type_enum = campaign_type::get_enum(campaign_type);
367 
368  if(campaign_type_enum) {
369  switch(*campaign_type_enum) {
370  case campaign_type::type::scenario: {
371  const config* campaign = nullptr;
372  if(!campaign_id.empty()) {
373  if(const config& c = cache_config_.find_child("campaign", "id", campaign_id)) {
374  campaign = &c;
375  }
376  }
377 
378  utils::string_map symbols;
379  if(campaign != nullptr) {
380  symbols["campaign_name"] = (*campaign)["name"];
381  } else {
382  // Fallback to nontranslatable campaign id.
383  symbols["campaign_name"] = "(" + campaign_id + ")";
384  }
385 
386  str << VGETTEXT("Campaign: $campaign_name", symbols);
387 
388  // Display internal id for debug purposes if we didn't above
389  if(game_config::debug && (campaign != nullptr)) {
390  str << '\n' << "(" << campaign_id << ")";
391  }
392  break;
393  }
394  case campaign_type::type::multiplayer:
395  str << _("Multiplayer");
396  break;
397  case campaign_type::type::tutorial:
398  str << _("Tutorial");
399  break;
400  case campaign_type::type::test:
401  str << _("Test scenario");
402  break;
403  }
404  } else {
405  str << campaign_type;
406  }
407 
408  str << "\n";
409 
410  if(savegame::loadgame::is_replay_save(cfg_summary)) {
411  str << _("Replay");
412  } else if(!cfg_summary["turn"].empty()) {
413  str << _("Turn") << " " << cfg_summary["turn"];
414  } else {
415  str << _("Scenario start");
416  }
417 
418  if(campaign_type_enum) {
419  switch (*campaign_type_enum) {
420  case campaign_type::type::scenario:
421  case campaign_type::type::multiplayer: {
422  const config* campaign = nullptr;
423  if (!campaign_id.empty()) {
424  if (const config& c = cache_config_.find_child("campaign", "id", campaign_id)) {
425  campaign = &c;
426  }
427  }
428 
429  // 'SCENARIO' or SP should only ever be campaigns
430  // 'MULTIPLAYER' may be a campaign with difficulty or single scenario without difficulty
431  // For the latter do not show the difficulty - even though it will be listed as
432  // NORMAL -> Medium in the save file it should not be considered valid (GitHub Issue #5321)
433  if (campaign != nullptr) {
434  str << "\n" << _("Difficulty: ");
435  try {
436  const config& difficulty = campaign->find_child("difficulty", "define", cfg_summary["difficulty"]);
437  std::ostringstream ss;
438  ss << difficulty["label"] << " (" << difficulty["description"] << ")";
439  str << ss.str();
440  }
441  catch (const config::error&) {
442  // fall back to standard difficulty string in case of exception
443  str << string_table[cfg_summary["difficulty"]];
444  }
445  }
446 
447  break;
448  }
449  case campaign_type::type::tutorial:
450  case campaign_type::type::test:
451  break;
452  }
453  } else {
454  }
455 
456  if(!cfg_summary["version"].empty()) {
457  str << "\n" << _("Version: ") << cfg_summary["version"];
458  }
459 
460  const std::vector<std::string>& active_mods = utils::split(cfg_summary["active_mods"]);
461  if(!active_mods.empty()) {
462  str << "\n" << _("Modifications: ");
463  for(const auto& mod_id : active_mods) {
464  std::string mod_name;
465  try {
466  mod_name = cache_config_.find_child("modification", "id", mod_id)["name"].str();
467  } catch(const config::error&) {
468  // Fallback to nontranslatable mod id.
469  mod_name = "(" + mod_id + ")";
470  }
471 
472  str << "\n" << font::unicode_bullet << " " << mod_name;
473  }
474  }
475 }
477 {
479 }
480 
482 {
483  listbox& list = find_widget<listbox>(get_window(), "savegame_list", false);
484 
485  const std::size_t index = std::size_t(list.get_selected_row());
486  if(index < games_.size()) {
487 
488  // See if we should ask the user for deletion confirmation
490  if(!gui2::dialogs::game_delete::execute()) {
491  return;
492  }
493  }
494 
495  // Delete the file
496  save_index_manager_->delete_game(games_[index].name());
497 
498  // Remove it from the list of saves
499  games_.erase(games_.begin() + index);
500 
501  list.remove_row(index);
502 
504  }
505 }
506 
507 void game_load::key_press_callback(const SDL_Keycode key)
508 {
509  //
510  // Don't delete games when we're typing in the textbox!
511  //
512  // I'm not sure if this check was necessary when I first added this feature
513  // (I didn't check at the time), but regardless, it's needed now. If it turns
514  // out I screwed something up in my refactoring, I'll remove this.
515  //
516  // - vultraz, 2017-08-28
517  //
518  if(find_widget<text_box>(get_window(), "txtFilter", false).get_state() == text_box_base::FOCUSED) {
519  return;
520  }
521 
522  if(key == SDLK_DELETE) {
524  }
525 }
526 
528 {
529  menu_button& dir_list = find_widget<menu_button>(get_window(), "dirList", false);
530 
531  const auto& path = dir_list.get_value_config()["path"].str();
532  if(path.empty()) {
534  } else {
535  save_index_manager_ = std::make_shared<savegame::save_index_class>(path);
536  }
537 
540 }
541 
542 } // namespace dialogs
Define the common log macros for the gui toolkit.
window(const builder_window::window_resolution &definition)
< Needs to be initialized in show.
Definition: window.cpp:263
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
grid::iterator end()
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:179
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:157
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
std::string_view data
Definition: picture.cpp:206
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. ...
grid & add_row(const widget_item &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:61
void key_press_callback(const SDL_Keycode key)
Definition: game_load.cpp:507
bool chars_equal_insensitive(char a, char b)
Definition: general.hpp:24
void set_save_dir_list(menu_button &dir_list)
Definition: game_load.cpp:134
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:270
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
virtual void set_label(const t_string &label)
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1022
The listbox class.
Definition: listbox.hpp:45
std::string format_time_local() const
Definition: save_index.cpp:252
const config & options()
Definition: game.cpp:556
Desktop environment interaction functions.
std::map< std::string, t_string > widget_item
Definition: widget.hpp:32
The base template for associating string values with enum values.
Definition: enum_base.hpp:32
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:120
void set_visible(const visibility visible)
Definition: widget.cpp:456
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)
Shows a transient message to the user.
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:126
virtual void set_use_markup(bool use_markup)
std::vector< savegame::save_info > games_
Definition: game_load.hpp:87
virtual unsigned get_state() const override
See styled_widget::get_state.
Definition: panel.cpp:63
const std::string & name() const
Definition: save_index.hpp:39
window * get_window()
Returns a pointer to the dialog&#39;s window.
styled_widget * get_widget()
Definition: field.hpp:193
bool open_object([[maybe_unused]] const std::string &path_or_url)
Definition: open.cpp:47
grid::iterator begin()
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: game_load.cpp:97
std::size_t i
Definition: function.cpp:967
static constexpr std::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
The user set the widget invisible, that means:
void filter_text_changed(const std::string &text)
Definition: game_load.cpp:315
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:777
virtual void set_active(const bool active) override
See styled_widget::set_active.
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:183
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
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
Abstract base class for all modal dialogs.
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:174
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:81
std::string message
Definition: exceptions.hpp:30
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:683
static lg::log_domain log_gameloaddlg
Definition: game_load.cpp:51
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
#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:846
void register_sorting_option(const int col, const Func &f)
Definition: listbox.hpp:263
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:66
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:138
void evaluate_summary_string(std::stringstream &str, const config &cfg_summary)
Definition: game_load.cpp:353
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:205
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:414
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