The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
game_load.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2016 by Jörg Hinrichs <[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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "formula/string_utils.hpp"
20 #include "gettext.hpp"
21 #include "game_config.hpp"
22 #include "game_preferences.hpp"
23 #include "game_classification.hpp"
24 #include "gui/auxiliary/field.hpp"
25 #include "gui/core/log.hpp"
27 #include "gui/dialogs/helper.hpp"
28 #include "gui/widgets/button.hpp"
29 #include "gui/widgets/image.hpp"
30 #include "gui/widgets/label.hpp"
31 #ifdef GUI2_EXPERIMENTAL_LISTBOX
32 #include "gui/widgets/list.hpp"
33 #else
34 #include "gui/widgets/listbox.hpp"
35 #endif
36 #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 "preferences_display.hpp"
43 #include "savegame.hpp"
45 
46 #include <cctype>
47 #include "utils/functional.hpp"
48 
49 namespace gui2
50 {
51 
52 /*WIKI
53  * @page = GUIWindowDefinitionWML
54  * @order = 2_game_load
55  *
56  * == Load a game ==
57  *
58  * This shows the dialog to select and load a savegame file.
59  *
60  * @begin{table}{dialog_widgets}
61  *
62  * txtFilter & & text & m &
63  * The filter for the listbox items. $
64  *
65  * savegame_list & & listbox & m &
66  * List of savegames. $
67  *
68  * -filename & & control & m &
69  * Name of the savegame. $
70  *
71  * -date & & control & o &
72  * Date the savegame was created. $
73  *
74  * -minimap & & minimap & m &
75  * Minimap of the selected savegame. $
76  *
77  * -imgLeader & & image & m &
78  * The image of the leader in the selected savegame. $
79  *
80  * -lblScenario & & label & m &
81  * The name of the scenario of the selected savegame. $
82  *
83  * -lblSummary & & label & m &
84  * Summary of the selected savegame. $
85  *
86  * @end{table}
87  */
88 
89 REGISTER_DIALOG(game_load)
90 
91 tgame_load::tgame_load(const config& cache_config)
92  : txtFilter_(register_text("txtFilter", true))
93  , chk_change_difficulty_(register_bool("change_difficulty", true))
94  , chk_show_replay_(register_bool("show_replay", true))
95  , chk_cancel_orders_(register_bool("cancel_orders", true))
96  , filename_()
97  , change_difficulty_(false)
98  , show_replay_(false)
99  , cancel_orders_(false)
100  , games_()
101  , cache_config_(cache_config)
102  , last_words_()
103  , summary_()
104 {
105 }
106 
108 {
109  assert(txtFilter_);
110 
111  find_widget<tminimap>(&window, "minimap", false).set_config(&cache_config_);
112 
114  = find_widget<ttext_box>(&window, "txtFilter", false, true);
115 
117  std::bind(&tgame_load::filter_text_changed, this, _1, _2));
118 
119  tlistbox* list
120  = find_widget<tlistbox>(&window, "savegame_list", false, true);
121 
122 #ifdef GUI2_EXPERIMENTAL_LISTBOX
125  *this,
126  std::ref(window)));
127 #else
129  dialog_callback<tgame_load, &tgame_load::list_item_clicked>);
130 #endif
131  window.keyboard_capture(list);
132 
133  {
136  }
137  fill_game_list(window, games_);
138 
140  find_widget<tbutton>(&window, "delete", false),
142  this,
143  std::ref(window)));
144 
145  display_savegame(window);
146 }
147 
148 bool tgame_load::compare_name(unsigned i1, unsigned i2) const
149 {
150  return games_[i1].name() < games_[i2].name();
151 }
152 
153 bool tgame_load::compare_date(unsigned i1, unsigned i2) const
154 {
155  return games_[i1].modified() < games_[i2].modified();
156 }
157 
158 bool tgame_load::compare_name_rev(unsigned i1, unsigned i2) const
159 {
160  return games_[i1].name() > games_[i2].name();
161 }
162 
163 bool tgame_load::compare_date_rev(unsigned i1, unsigned i2) const
164 {
165  return games_[i1].modified() > games_[i2].modified();
166 }
167 
169  std::vector<savegame::save_info>& games)
170 {
171  tlistbox& list = find_widget<tlistbox>(&window, "savegame_list", false);
172  list.clear();
173 
174  for(const auto & game : games)
175  {
176  std::map<std::string, string_map> data;
177  string_map item;
178 
179  std::string name = game.name();
180  utils::ellipsis_truncate(name, 50);
181  item["label"] = name;
182  data.insert(std::make_pair("filename", item));
183 
184  item["label"] = game.format_time_summary();
185  data.insert(std::make_pair("date", item));
186 
187  list.add_row(data);
188  }
189  std::vector<tgenerator_::torder_func> order_funcs(2);
190  order_funcs[0] = std::bind(&tgame_load::compare_name, this, _1, _2);
191  order_funcs[1] = std::bind(&tgame_load::compare_name_rev, this, _1, _2);
192  list.set_column_order(0, order_funcs);
193  order_funcs[0] = std::bind(&tgame_load::compare_date, this, _1, _2);
194  order_funcs[1] = std::bind(&tgame_load::compare_date_rev, this, _1, _2);
195  list.set_column_order(1, order_funcs);
196 }
197 
199 {
200  display_savegame(window);
201 }
202 
204 {
205  twindow& window = *textbox->get_window();
206 
207  tlistbox& list = find_widget<tlistbox>(&window, "savegame_list", false);
208 
209  const std::vector<std::string> words = utils::split(text, ' ');
210 
211  if(words == last_words_)
212  return;
213  last_words_ = words;
214 
215  std::vector<bool> show_items(list.get_item_count(), true);
216 
217  if(!text.empty()) {
218  for(unsigned int i = 0; i < list.get_item_count(); i++) {
219  tgrid* row = list.get_row_grid(i);
220 
221  tgrid::iterator it = row->begin();
222  tlabel& filename_label
223  = find_widget<tlabel>(*it, "filename", false);
224 
225  bool found = false;
226  for(const auto & word : words)
227  {
228  found = std::search(filename_label.label().str().begin(),
229  filename_label.label().str().end(),
230  word.begin(),
231  word.end(),
233  != filename_label.label().str().end();
234 
235  if(!found) {
236  // one word doesn't match, we don't reach words.end()
237  break;
238  }
239  }
240 
241  show_items[i] = found;
242  }
243  }
244 
245  list.set_row_shown(show_items);
246 }
247 
249 {
253 
254  if(!games_.empty()) {
255  const int index =
256  find_widget<tlistbox>(&window, "savegame_list", false).get_selected_row();
257  summary_ = games_[index].summary();
258  }
259 }
260 
262 {
263  const int selected_row
264  = find_widget<tlistbox>(&window, "savegame_list", false)
265  .get_selected_row();
266 
267  if(selected_row == -1) {
268  return;
269  }
270 
271  savegame::save_info& game = games_[selected_row];
272  filename_ = game.name();
273 
274  const config& summary = game.summary();
275 
276  find_widget<timage>(&window, "imgLeader", false)
277  .set_label(summary["leader_image"]);
278 
279  find_widget<tminimap>(&window, "minimap", false)
280  .set_map_data(summary["map_data"]);
281 
282  find_widget<tlabel>(&window, "lblScenario", false)
283  .set_label(summary["label"]);
284 
285  std::stringstream str;
286  str << game.format_time_local();
287  evaluate_summary_string(str, summary);
288 
289  ttoggle_button& replay_toggle =
290  find_widget<ttoggle_button>(&window, "show_replay", false);
291 
292  ttoggle_button& cancel_orders_toggle =
293  find_widget<ttoggle_button>(&window, "cancel_orders", false);
294 
295  const bool is_replay = savegame::loadgame::is_replay_save(summary);
296  const bool is_scenario_start = summary["turn"].empty();
297 
298  // Always toggle show_replay on if the save is a replay
299  replay_toggle.set_value(is_replay);
300  replay_toggle.set_active(!is_replay && !is_scenario_start);
301 
302  // Cancel orders doesnt make sense on replay saves or start-of-scenario saves.
303  cancel_orders_toggle.set_active(!is_replay && !is_scenario_start);
304 
305  find_widget<tlabel>(&window, "lblSummary", false).set_label(str.str());
306 
307  // TODO: Find a better way to change the label width
308  // window.invalidate_layout();
309 }
310 
311 void tgame_load::evaluate_summary_string(std::stringstream& str,
312  const config& cfg_summary)
313 {
314 
315  const std::string& campaign_type = cfg_summary["campaign_type"];
316  if(cfg_summary["corrupt"].to_bool()) {
317  str << "\n" << _("#(Invalid)");
318  } else {
319  str << "\n";
320 
321  try
322  {
323  game_classification::CAMPAIGN_TYPE ct
324  = lexical_cast<game_classification::CAMPAIGN_TYPE>(
325  campaign_type);
326 
327  switch(ct.v) {
329  const std::string campaign_id = cfg_summary["campaign"];
330  const config* campaign = nullptr;
331  if(!campaign_id.empty()) {
332  if(const config& c = cache_config_.find_child(
333  "campaign", "id", campaign_id)) {
334 
335  campaign = &c;
336  }
337  }
338  utils::string_map symbols;
339  if(campaign != nullptr) {
340  symbols["campaign_name"] = (*campaign)["name"];
341  } else {
342  // Fallback to nontranslatable campaign id.
343  symbols["campaign_name"] = "(" + campaign_id + ")";
344  }
345  str << vgettext("Campaign: $campaign_name", symbols);
346 
347  // Display internal id for debug purposes if we didn't above
348  if(game_config::debug && (campaign != nullptr)) {
349  str << '\n' << "(" << campaign_id << ")";
350  }
351  break;
352  }
353  case game_classification::CAMPAIGN_TYPE::MULTIPLAYER:
354  str << _("Multiplayer");
355  break;
356  case game_classification::CAMPAIGN_TYPE::TUTORIAL:
357  str << _("Tutorial");
358  break;
359  case game_classification::CAMPAIGN_TYPE::TEST:
360  str << _("Test scenario");
361  break;
362  }
363  }
364  catch(bad_lexical_cast&)
365  {
366  str << campaign_type;
367  }
368 
369  str << "\n";
370 
371  if(savegame::loadgame::is_replay_save(cfg_summary)) {
372  str << _("Replay");
373  } else if(!cfg_summary["turn"].empty()) {
374  str << _("Turn") << " " << cfg_summary["turn"];
375  } else {
376  str << _("Scenario start");
377  }
378 
379  str << "\n" << _("Difficulty: ")
380  << string_table[cfg_summary["difficulty"]];
381 
382  if(!cfg_summary["version"].empty()) {
383  str << "\n" << _("Version: ") << cfg_summary["version"];
384  }
385  }
386 }
387 
389 {
390  tlistbox& list = find_widget<tlistbox>(&window, "savegame_list", false);
391 
392  const size_t index = size_t(list.get_selected_row());
393  if(index < games_.size()) {
394 
395  // See if we should ask the user for deletion confirmation
397  if(!gui2::tgame_delete::execute(window.video())) {
398  return;
399  }
400  }
401 
402  // Delete the file
404 
405  // Remove it from the list of saves
406  games_.erase(games_.begin() + index);
407  list.remove_row(index);
408 
409  display_savegame(window);
410  }
411 }
412 
413 } // namespace gui2
Define the common log macros for the gui toolkit.
virtual void set_active(const bool active) override
See tcontrol::set_active.
const config & cache_config_
Definition: game_load.hpp:92
tfield_bool * chk_cancel_orders_
Definition: game_load.hpp:84
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:150
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:94
const GLfloat * c
Definition: glew.h:12741
void connect_signal_notify_modified(tdispatcher &dispatcher, const tsignal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.hpp:725
Base container class.
Definition: grid.hpp:29
This file contains the window object, this object is a top level container which has the event manage...
std::vector< save_info > get_saves_list(const std::string *dir, const std::string *filter)
Get a list of available saves.
Definition: save_index.cpp:161
REGISTER_DIALOG(label_settings)
void connect_signal_mouse_left_click(tdispatcher &dispatcher, const tsignal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.hpp:710
std::vector< savegame::save_info > games_
Definition: game_load.hpp:91
Class for a single line text area.
Definition: text_box.hpp:118
T get_widget_value(twindow &window)
Gets the value of the field.
Definition: field.hpp:398
Label showing a text.
Definition: label.hpp:29
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed...
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
bool empty() const
Definition: config.cpp:1105
To lexical_cast(From value)
Lexical cast converts one type to another.
void pre_show(twindow &window)
Inherited from tdialog.
Definition: game_load.cpp:107
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
Class for a toggle button.
std::string filename_
Definition: action_wml.cpp:506
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
tfield_bool * chk_show_replay_
Definition: game_load.hpp:83
void ellipsis_truncate(std::string &str, const size_t size)
Truncates a string to a given utf-8 character count and then appends an ellipsis. ...
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
std::vector< std::string > last_words_
Definition: game_load.hpp:94
const config & summary() const
Definition: save_index.cpp:187
bool chars_equal_insensitive(char a, char b)
Definition: util.hpp:206
bool compare_date_rev(unsigned i1, unsigned i2) const
Definition: game_load.cpp:163
std::map< std::string, t_string > string_map
iterator begin()
Definition: grid.hpp:440
This file contains the settings handling of the widget library.
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:138
void 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:74
void delete_button_callback(twindow &window)
Definition: game_load.cpp:388
bool compare_date(unsigned i1, unsigned i2) const
Definition: game_load.cpp:153
tfield_text * txtFilter_
Definition: game_load.hpp:81
const config & summary()
Definition: game_load.hpp:49
void delete_game(const std::string &name)
Delete a savegame.
Definition: save_index.cpp:307
bool compare_name_rev(unsigned i1, unsigned i2) const
Definition: game_load.cpp:158
static bool is_replay_save(const config &cfg)
Definition: savegame.hpp:68
std::string format_time_local() const
Definition: save_index.cpp:191
void set_column_order(unsigned col, const std::vector< tgenerator_::torder_func > &func)
Definition: listbox.cpp:596
Iterator for the tchild items.
Definition: grid.hpp:401
const t_string & label() const
Definition: control.hpp:248
void fill_game_list(twindow &window, std::vector< savegame::save_info > &games)
Definition: game_load.cpp:168
std::map< std::string, t_string > string_map
Definition: generator.hpp:23
void display_savegame(twindow &window)
Definition: game_load.cpp:261
void list_item_clicked(twindow &window)
Definition: game_load.cpp:198
static bool execute(CVideo &video)
The execute function see tdialog for more information.
Definition: game_delete.hpp:29
GLuint index
Definition: glew.h:1782
The listbox class.
Definition: listbox.hpp:39
void post_show(twindow &window)
Inherited from tdialog.
Definition: game_load.cpp:248
size_t i
Definition: function.cpp:1057
void filter_text_changed(ttext_ *textbox, const std::string &text)
Definition: game_load.cpp:203
bool compare_name(unsigned i1, unsigned i2) const
Definition: game_load.cpp:148
std::string vgettext(const char *msgid, const utils::string_map &symbols)
GLuint const GLchar * name
Definition: glew.h:1782
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:3448
GLenum GLenum GLvoid * row
Definition: glew.h:3805
Filename and modification date for a file list.
Definition: save_index.hpp:29
GLenum GLint ref
Definition: glew.h:1813
const std::string & name() const
Definition: save_index.hpp:38
symbol_table string_table
Definition: language.cpp:65
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
void set_callback_value_change(const std::function< void(twidget &)> &callback)
Definition: listbox.hpp:225
#define c
Definition: glew.h:12743
void evaluate_summary_string(std::stringstream &str, const config &cfg_summary)
Definition: game_load.cpp:311
static void set_label(twindow &window, const std::string &id, const std::string &label)
Definition: unit_attack.cpp:89
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.
std::string filename_
Definition: game_load.hpp:86
const std::string & str() const
Definition: tstring.hpp:170
tfield_bool * chk_change_difficulty_
Definition: game_load.hpp:82
twindow * get_window()
Get the parent window.
Definition: widget.cpp:116
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
void set_value(const unsigned selected)
Inherited from tselectable_.
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:237
Thrown when a lexical_cast fails.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
Abstract base class for text items.
Definition: text.hpp:43
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:131
const tgrid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:215
bool change_difficulty_
Definition: game_load.hpp:87
void set_text_changed_callback(std::function< void(ttext_ *textbox, const std::string text)> cb)
Set the text_changed callback.
Definition: text.hpp:87