The Battle for Wesnoth  1.17.0-dev
manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  by Mark de Wever <koraq@xs4all.nl>
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 "addon/info.hpp"
21 #include "addon/manager.hpp"
22 #include "addon/state.hpp"
23 
24 #include "desktop/clipboard.hpp"
25 #include "desktop/open.hpp"
26 
27 #include "help/help.hpp"
28 #include "gettext.hpp"
29 #include "gui/auxiliary/filter.hpp"
33 #include "gui/dialogs/message.hpp"
35 #include "gui/widgets/button.hpp"
36 #include "gui/widgets/label.hpp"
40 #include "gui/widgets/drawing.hpp"
41 #include "gui/widgets/image.hpp"
42 #include "gui/widgets/listbox.hpp"
43 #include "gui/widgets/pane.hpp"
44 #include "gui/widgets/settings.hpp"
46 #include "gui/widgets/text_box.hpp"
47 #include "gui/widgets/window.hpp"
49 #include "formula/string_utils.hpp"
50 #include "picture.hpp"
51 #include "language.hpp"
52 #include "preferences/general.hpp"
53 #include "utils/general.hpp"
54 
55 #include "config.hpp"
56 
57 #include <functional>
58 
59 #include <iomanip>
60 #include <sstream>
61 #include <stdexcept>
62 
63 namespace gui2::dialogs
64 {
65 
66 namespace {
67  struct filter_transform
68  {
69  explicit filter_transform(const std::vector<std::string>& filtertext) : filtertext_(filtertext) {}
70  bool operator()(const config& cfg) const
71  {
72  for(const auto& filter : filtertext_)
73  {
74  bool found = false;
75  for(const auto& attribute : cfg.attribute_range())
76  {
77  std::string val = attribute.second.str();
78  if(std::search(val.begin(),
79  val.end(),
80  filter.begin(),
81  filter.end(),
83  != val.end())
84  {
85  found = true;
86  break;
87  }
88  }
89  for(const config& child : cfg.child_range("translation")) {
90  for(const auto& attribute : child.attribute_range()) {
91  std::string val = attribute.second.str();
92  if(translation::ci_search(val, filter)) {
93  found = true;
94  break;
95  }
96  }
97  }
98  if(!found) {
99  return false;
100  }
101  }
102  return true;
103  }
104  const std::vector<std::string> filtertext_;
105  };
106 
107  std::string make_display_dependencies(
108  const std::string& addon_id,
109  const addons_list& addons_list,
110  const addons_tracking_list& addon_states)
111  {
112  const addon_info& addon = addons_list.at(addon_id);
113  std::string str;
114 
115  const std::set<std::string>& deps = addon.resolve_dependencies(addons_list);
116 
117  for(const auto& dep_id : deps) {
118  addon_info dep;
119  addon_tracking_info depstate;
120 
121  addons_list::const_iterator ali = addons_list.find(dep_id);
122  addons_tracking_list::const_iterator tli = addon_states.find(dep_id);
123 
124  if(ali == addons_list.end()) {
125  dep.id = dep_id; // Build dummy addon_info.
126  } else {
127  dep = ali->second;
128  }
129 
130  if(tli == addon_states.end()) {
131  depstate = get_addon_tracking_info(dep);
132  } else {
133  depstate = tli->second;
134  }
135 
136  if(!str.empty()) {
137  str += ", ";
138  }
139 
141  }
142 
143  return str;
144  }
145 
146  std::string langcode_to_string(const std::string& lcode)
147  {
148  for(const auto & ld : get_languages())
149  {
150  if(ld.localename == lcode || ld.localename.substr(0, 2) == lcode) {
151  return ld.language;
152  }
153  }
154 
155  return "";
156  }
157 }
158 
159 REGISTER_DIALOG(addon_manager)
160 
161 const std::vector<std::pair<ADDON_STATUS_FILTER, std::string>> addon_manager::status_filter_types_{
162  {FILTER_ALL, N_("addons_view^All Add-ons")},
163  {FILTER_INSTALLED, N_("addons_view^Installed")},
164  {FILTER_UPGRADABLE, N_("addons_view^Upgradable")},
165  {FILTER_PUBLISHABLE, N_("addons_view^Publishable")},
166  {FILTER_NOT_INSTALLED, N_("addons_view^Not Installed")},
167 };
168 
169 const std::vector<std::pair<ADDON_TYPE, std::string>> addon_manager::type_filter_types_{
170  {ADDON_SP_CAMPAIGN, N_("addons_of_type^Campaigns")},
171  {ADDON_SP_SCENARIO, N_("addons_of_type^Scenarios")},
172  {ADDON_SP_MP_CAMPAIGN, N_("addons_of_type^SP/MP campaigns")},
173  {ADDON_MP_CAMPAIGN, N_("addons_of_type^MP campaigns")},
174  {ADDON_MP_SCENARIO, N_("addons_of_type^MP scenarios")},
175  {ADDON_MP_MAPS, N_("addons_of_type^MP map-packs")},
176  {ADDON_MP_ERA, N_("addons_of_type^MP eras")},
177  {ADDON_MP_FACTION, N_("addons_of_type^MP factions")},
178  {ADDON_MOD, N_("addons_of_type^Modifications")},
179  {ADDON_CORE, N_("addons_of_type^Cores")},
180  {ADDON_MEDIA, N_("addons_of_type^Resources")},
181  // FIXME: (also in WML) should this and Unknown be a single option in the UI?
182  {ADDON_OTHER, N_("addons_of_type^Other")},
183  {ADDON_UNKNOWN, N_("addons_of_type^Unknown")},
184 };
185 
186 const std::vector<addon_manager::addon_order> addon_manager::all_orders_{
187  {N_("addons_order^Name ($order)"), "name", 0,
188  [](const addon_info& a, const addon_info& b) { return a.title < b.title; },
189  [](const addon_info& a, const addon_info& b) { return a.title > b.title; }},
190  {N_("addons_order^Author ($order)"), "author", 1,
191  [](const addon_info& a, const addon_info& b) { return a.author < b.author; },
192  [](const addon_info& a, const addon_info& b) { return a.author > b.author; }},
193  {N_("addons_order^Size ($order)"), "size", 2,
194  [](const addon_info& a, const addon_info& b) { return a.size < b.size; },
195  [](const addon_info& a, const addon_info& b) { return a.size > b.size; }},
196  {N_("addons_order^Downloads ($order)"), "downloads", 3,
197  [](const addon_info& a, const addon_info& b) { return a.downloads < b.downloads; },
198  [](const addon_info& a, const addon_info& b) { return a.downloads > b.downloads; }},
199  {N_("addons_order^Type ($order)"), "type", 4,
200  [](const addon_info& a, const addon_info& b) { return a.display_type() < b.display_type(); },
201  [](const addon_info& a, const addon_info& b) { return a.display_type() > b.display_type(); }},
202  {N_("addons_order^Last updated ($datelike_order)"), "last_updated", -1,
203  [](const addon_info& a, const addon_info& b) { return a.updated < b.updated; },
204  [](const addon_info& a, const addon_info& b) { return a.updated > b.updated; }},
205  {N_("addons_order^First uploaded ($datelike_order)"), "first_uploaded", -1,
206  [](const addon_info& a, const addon_info& b) { return a.created < b.created; },
207  [](const addon_info& a, const addon_info& b) { return a.created > b.created; }}
208 };
209 
210 namespace
211 {
212 struct addon_tag
213 {
214  /** Text to match against addon_info.tags() */
215  std::string id;
216  /** What to show in the filter's drop-down list */
217  std::string label;
218  /** Shown when hovering over an entry in the filter's drop-down list */
219  std::string tooltip;
220 };
221 
222 const std::vector<addon_tag> tag_filter_types_{
223  {"cooperative", N_("addon_tag^Cooperative"),
224  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
225  N_("addon_tag^All human players are on the same team, versus the AI")},
226  {"cosmetic", N_("addon_tag^Cosmetic"),
227  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
228  N_("addon_tag^These make the game look different, without changing gameplay")},
229  {"difficulty", N_("addon_tag^Difficulty"),
230  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
231  N_("addon_tag^Can make campaigns easier or harder")},
232  {"rng", N_("addon_tag^RNG"),
233  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
234  N_("addon_tag^Modify the randomness in the combat mechanics, or remove it entirely")},
235  {"survival", N_("addon_tag^Survival"),
236  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
237  N_("addon_tag^Fight against waves of enemies")},
238  {"terraforming", N_("addon_tag^Terraforming"),
239  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
240  N_("addon_tag^Players can change the terrain")},
241 };
242 };
243 
245  : orders_()
246  , cfg_()
247  , client_(client)
248  , addons_()
249  , tracking_info_()
250  , need_wml_cache_refresh_(false)
251 {
252 }
253 
254 static std::string describe_status_verbose(const addon_tracking_info& state)
255 {
256  std::string s;
257 
258  utils::string_map i18n_symbols {{"local_version", state.installed_version.str()}};
259 
260  switch(state.state) {
261  case ADDON_NONE:
262  s = !state.can_publish
263  ? _("addon_state^Not installed")
264  : _("addon_state^Published, not installed");
265  break;
266  case ADDON_INSTALLED:
267  s = !state.can_publish
268  ? _("addon_state^Installed")
269  : _("addon_state^Published");
270  break;
271  case ADDON_NOT_TRACKED:
272  s = !state.can_publish
273  ? _("addon_state^Installed, not tracking local version")
274  // Published add-ons often don't have local status information,
275  // hence untracked. This should be considered normal.
276  : _("addon_state^Published, not tracking local version");
277  break;
279  const std::string vstr = !state.can_publish
280  ? _("addon_state^Installed ($local_version|), upgradable")
281  : _("addon_state^Published ($local_version| installed), upgradable");
282 
283  s = utils::interpolate_variables_into_string(vstr, &i18n_symbols);
284  } break;
286  const std::string vstr = !state.can_publish
287  ? _("addon_state^Installed ($local_version|), outdated on server")
288  : _("addon_state^Published ($local_version| installed), outdated on server");
289 
290  s = utils::interpolate_variables_into_string(vstr, &i18n_symbols);
291  } break;
293  s = !state.can_publish
294  ? _("addon_state^Installed, not ready to publish")
295  : _("addon_state^Ready to publish");
296  break;
298  s = !state.can_publish
299  ? _("addon_state^Installed, broken")
300  : _("addon_state^Published, broken");
301  break;
302  default:
303  s = _("addon_state^Unknown");
304  }
305 
307 }
308 
310 {
311  window.set_escape_disabled(true);
312 
313  stacked_widget& addr_info = find_widget<stacked_widget>(&window, "server_conn_info", false);
314  grid* addr_visible;
315 
316  if(client_.using_tls()) {
317  addr_info.select_layer(1);
318  addr_visible = addr_info.get_layer_grid(1);
319  } else {
320  addr_info.select_layer(0);
321  addr_visible = addr_info.get_layer_grid(0);
322  }
323 
324  if(addr_visible) {
325  auto addr_box = dynamic_cast<styled_widget*>(addr_visible->find("server_addr", false));
326  if(addr_box) {
327  addr_box->set_label(client_.addr());
328  }
329  }
330 
331  addon_list& list = find_widget<addon_list>(&window, "addons", false);
332 
333  text_box& filter = find_widget<text_box>(&window, "filter", false);
335 
337  this, std::placeholders::_1));
339  this, std::placeholders::_1));
341  this, std::placeholders::_1));
342 
344  this, std::placeholders::_1));
346  this, std::placeholders::_1));
347 
348  list.set_modified_signal_handler([this]() { on_addon_select(); });
349 
351  load_addon_list();
352 
353  menu_button& status_filter = find_widget<menu_button>(&window, "install_status_filter", false);
354 
355  std::vector<config> status_filter_entries;
356  for(const auto& f : status_filter_types_) {
357  status_filter_entries.emplace_back("label", t_string(f.second, GETTEXT_DOMAIN));
358  }
359 
360  status_filter.set_values(status_filter_entries);
361 
362  connect_signal_notify_modified(status_filter,
363  std::bind(&addon_manager::apply_filters, this));
364 
365  // The tag filter
366  auto& tag_filter = find_widget<multimenu_button>(&window, "tag_filter", false);
367 
368  std::vector<config> tag_filter_entries;
369  for(const auto& f : tag_filter_types_) {
370  tag_filter_entries.emplace_back("label", t_string(f.label, GETTEXT_DOMAIN), "checkbox", false);
371  if(!f.tooltip.empty()) {
372  tag_filter_entries.back()["tooltip"] = t_string(f.tooltip, GETTEXT_DOMAIN);
373  }
374  }
375 
376  tag_filter.set_values(tag_filter_entries);
377 
378  connect_signal_notify_modified(tag_filter, std::bind(&addon_manager::apply_filters, this));
379 
380  // The type filter
381  multimenu_button& type_filter = find_widget<multimenu_button>(&window, "type_filter", false);
382 
383  std::vector<config> type_filter_entries;
384  for(const auto& f : type_filter_types_) {
385  type_filter_entries.emplace_back("label", t_string(f.second, GETTEXT_DOMAIN), "checkbox", false);
386  }
387 
388  type_filter.set_values(type_filter_entries);
389 
390  connect_signal_notify_modified(type_filter,
391  std::bind(&addon_manager::apply_filters, this));
392 
393  // Sorting order
394  menu_button& order_dropdown = find_widget<menu_button>(&window, "order_dropdown", false);
395 
396  std::vector<config> order_dropdown_entries;
397  for(const auto& f : all_orders_) {
398  utils::string_map symbols;
399 
400  symbols["order"] = _("ascending");
401  // TRANSLATORS: Sorting order of dates, oldest first
402  symbols["datelike_order"] = _("oldest to newest");
403  config entry{"label", VGETTEXT(f.label.c_str(), symbols)};
404  order_dropdown_entries.push_back(entry);
405  symbols["order"] = _("descending");
406  // TRANSLATORS: Sorting order of dates, newest first
407  symbols["datelike_order"] = _("newest to oldest");
408  entry["label"] = VGETTEXT(f.label.c_str(), symbols);
409  order_dropdown_entries.push_back(entry);
410  }
411 
412  order_dropdown.set_values(order_dropdown_entries);
413  {
414  const std::string saved_order_name = preferences::addon_manager_saved_order_name();
415  const preferences::SORT_ORDER saved_order_direction =
417 
418  if(!saved_order_name.empty()) {
419  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
420  [&saved_order_name](const addon_order& order) {return order.as_preference == saved_order_name;});
421  if(order_it != all_orders_.end()) {
422  int index = 2 * (std::distance(all_orders_.begin(), order_it));
424  if(saved_order_direction == preferences::SORT_ORDER::ASCENDING) {
425  func = order_it->sort_func_asc;
426  } else {
427  func = order_it->sort_func_desc;
428  ++index;
429  }
430  find_widget<menu_button>(&window, "order_dropdown", false).set_value(index, false);
431  auto& addons = find_widget<addon_list>(&window, "addons", false);
432  addons.set_addon_order(func);
433  addons.select_first_addon();
434  }
435  }
436  }
437 
438  connect_signal_notify_modified(order_dropdown,
439  std::bind(&addon_manager::order_addons, this));
440 
441  label& url_label = find_widget<label>(&window, "url", false);
442 
443  url_label.set_use_markup(true);
444  url_label.set_link_aware(true);
445 
447  find_widget<button>(&window, "install", false),
448  std::bind(&addon_manager::install_selected_addon, this));
449 
451  find_widget<button>(&window, "uninstall", false),
452  std::bind(&addon_manager::uninstall_selected_addon, this));
453 
455  find_widget<button>(&window, "update", false),
456  std::bind(&addon_manager::update_selected_addon, this));
457 
459  find_widget<button>(&window, "publish", false),
460  std::bind(&addon_manager::publish_selected_addon, this));
461 
463  find_widget<button>(&window, "delete", false),
464  std::bind(&addon_manager::delete_selected_addon, this));
465 
467  find_widget<button>(&window, "update_all", false),
468  std::bind(&addon_manager::update_all_addons, this));
469 
471  find_widget<button>(&window, "show_help", false),
472  std::bind(&addon_manager::show_help, this));
473 
474  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
475  button& btn = find_widget<button>(&window, "details_toggle", false);
476  connect_signal_mouse_left_click(btn, std::bind(&addon_manager::toggle_details, this, std::ref(btn), std::ref(*stk)));
477  stk->select_layer(0);
478  }
479 
480  widget* version_filter_parent = &window;
481  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
482  version_filter_parent = stk->get_layer_grid(1);
483  }
484 
485  menu_button& version_filter = find_widget<menu_button>(version_filter_parent, "version_filter", false);
486  connect_signal_notify_modified(version_filter,
488 
489  on_addon_select();
490 
491  window.set_enter_disabled(true);
492 
493  window.keyboard_capture(&filter);
495 
497  std::placeholders::_1, std::placeholders::_2));
498 
499  // Use handle the special addon_list retval to allow installing addons on double click
500  window.set_exit_hook(std::bind(&addon_manager::exit_hook, this, std::placeholders::_1));
501 }
502 
504 {
505  if(stk.current_layer() == 0) {
506  btn.set_label(_("addons^Back to List"));
507  stk.select_layer(1);
508  } else {
509  btn.set_label(_("Add-on Details"));
510  stk.select_layer(0);
511  }
512 }
513 
515 {
517  if(!cfg_) {
518  gui2::show_error_message(_("An error occurred while downloading the add-ons list from the server."));
519  get_window()->close();
520  }
521 }
522 
524 {
527  }
528 
530 
531  std::vector<std::string> publishable_addons = available_addons();
532 
533  for(std::string id : publishable_addons) {
534  if(addons_.find(id) == addons_.end()) {
535  // Get a config from the addon's pbl file
536  // Note that the name= key is necessary or stuff breaks, since the filter code uses this key
537  // to match add-ons in the config list. It also fills in addon_info's id field. It's also
538  // neccessay to set local_only here so that flag can be properly set after addons_ is cleared
539  // and recreated by read_addons_list.
540  config pbl_cfg = get_addon_pbl_info(id);
541  pbl_cfg["name"] = id;
542  pbl_cfg["local_only"] = true;
543 
544  // Add the add-on to the list.
545  addon_info addon(pbl_cfg);
546  addons_[id] = addon;
547 
548  // Add the addon to the config entry
549  cfg_.add_child("campaign", std::move(pbl_cfg));
550  }
551  }
552 
553  if(addons_.empty()) {
554  show_transient_message(_("No Add-ons Available"), _("There are no add-ons available for download from this server."));
555  }
556 
557  addon_list& list = find_widget<addon_list>(get_window(), "addons", false);
558  list.set_addons(addons_);
559 
560  bool has_upgradable_addons = false;
561  for(const auto& a : addons_) {
562  tracking_info_[a.first] = get_addon_tracking_info(a.second);
563 
564  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
565  has_upgradable_addons = true;
566  }
567  }
568 
569  find_widget<button>(get_window(), "update_all", false).set_active(has_upgradable_addons);
570 
571  apply_filters();
572 }
573 
575 {
576  load_addon_list();
577 
578  // Reselect the add-on.
579  find_widget<addon_list>(get_window(), "addons", false).select_addon(id);
580  on_addon_select();
581 }
582 
583 boost::dynamic_bitset<> addon_manager::get_name_filter_visibility() const
584 {
585  const text_box& name_filter = find_widget<const text_box>(get_window(), "filter", false);
586  const std::string& text = name_filter.get_value();
587 
588  filter_transform filter(utils::split(text, ' '));
589  boost::dynamic_bitset<> res;
590 
591  const config::const_child_itors& addon_cfgs = cfg_.child_range("campaign");
592 
593  for(const auto& a : addons_)
594  {
595  const config& addon_cfg = *std::find_if(addon_cfgs.begin(), addon_cfgs.end(),
596  [&a](const config& cfg)
597  {
598  return cfg["name"] == a.first;
599  });
600 
601  res.push_back(filter(addon_cfg));
602  }
603 
604  return res;
605 }
606 
607 boost::dynamic_bitset<> addon_manager::get_status_filter_visibility() const
608 {
609  const menu_button& status_filter = find_widget<const menu_button>(get_window(), "install_status_filter", false);
610  const ADDON_STATUS_FILTER selection = status_filter_types_[status_filter.get_value()].first;
611 
612  boost::dynamic_bitset<> res;
613  for(const auto& a : addons_) {
614  const addon_tracking_info& info = tracking_info_.at(a.second.id);
615 
616  res.push_back(
617  (selection == FILTER_ALL) ||
618  (selection == FILTER_INSTALLED && is_installed_addon_status(info.state)) ||
619  (selection == FILTER_UPGRADABLE && info.state == ADDON_INSTALLED_UPGRADABLE) ||
620  (selection == FILTER_PUBLISHABLE && info.can_publish == true) ||
621  (selection == FILTER_NOT_INSTALLED && info.state == ADDON_NONE)
622  );
623  }
624 
625  return res;
626 }
627 
628 boost::dynamic_bitset<> addon_manager::get_tag_filter_visibility() const
629 {
630  const auto& tag_filter = find_widget<const multimenu_button>(get_window(), "tag_filter", false);
631  const auto toggle_states = tag_filter.get_toggle_states();
632  if(toggle_states.none()) {
633  // Nothing selected. It means that all add-ons are shown.
634  boost::dynamic_bitset<> res_flipped(addons_.size());
635  return ~res_flipped;
636  }
637 
638  std::vector<std::string> selected_tags;
639  for(std::size_t i = 0; i < tag_filter_types_.size(); ++i) {
640  if(toggle_states[i]) {
641  selected_tags.push_back(tag_filter_types_[i].id);
642  }
643  }
644 
645  boost::dynamic_bitset<> res;
646  for(const auto& a : addons_) {
647  bool matched_tag = false;
648  for(const auto& id : selected_tags) {
649  if(utils::contains(a.second.tags, id)) {
650  matched_tag = true;
651  break;
652  }
653  }
654  res.push_back(matched_tag);
655  }
656 
657  return res;
658 }
659 
660 boost::dynamic_bitset<> addon_manager::get_type_filter_visibility() const
661 {
662  const multimenu_button& type_filter = find_widget<const multimenu_button>(get_window(), "type_filter", false);
663 
664  boost::dynamic_bitset<> toggle_states = type_filter.get_toggle_states();
665  if(toggle_states.none()) {
666  // Nothing selected. It means that *all* add-ons are shown.
667  boost::dynamic_bitset<> res_flipped(addons_.size());
668  return ~res_flipped;
669  } else {
670  boost::dynamic_bitset<> res;
671 
672  for(const auto& a : addons_) {
673  int index = std::distance(type_filter_types_.begin(),
674  std::find_if(type_filter_types_.begin(), type_filter_types_.end(),
675  [&a](const std::pair<ADDON_TYPE, std::string>& entry) {
676  return entry.first == a.second.type;
677  })
678  );
679  res.push_back(toggle_states[index]);
680  }
681 
682  return res;
683  }
684 }
685 
687 {
688  // In the small-screen layout, the text_box for the filter keeps keyboard focus even when the
689  // details panel is visible, which means this can be called when the list isn't visible. That
690  // causes problems both because find_widget can throw exceptions, but also because changing the
691  // filters can trigger on_addon_select and thus affect which add-on's details are shown.
692  //
693  // Quick workaround is to not process the new filter if the list isn't visible.
694  auto list = find_widget<addon_list>(get_window(), "addons", false, false);
695  if(!list) {
696  return;
697  }
698 
699  boost::dynamic_bitset<> res =
704  list->set_addon_shown(res);
705 }
706 
708 {
709  const menu_button& order_menu = find_widget<const menu_button>(get_window(), "order_dropdown", false);
710  const addon_order& order_struct = all_orders_.at(order_menu.get_value() / 2);
711  preferences::SORT_ORDER order = order_menu.get_value() % 2 == 0 ? preferences::SORT_ORDER::ASCENDING : preferences::SORT_ORDER::DESCENDING;
713  if(order == preferences::SORT_ORDER::ASCENDING) {
714  func = order_struct.sort_func_asc;
715  } else {
716  func = order_struct.sort_func_desc;
717  }
718 
719  find_widget<addon_list>(get_window(), "addons", false).set_addon_order(func);
720  preferences::set_addon_manager_saved_order_name(order_struct.as_preference);
722 }
723 
724 void addon_manager::on_order_changed(unsigned int sort_column, preferences::SORT_ORDER order)
725 {
726  menu_button& order_menu = find_widget<menu_button>(get_window(), "order_dropdown", false);
727  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
728  [sort_column](const addon_order& order) {return order.column_index == static_cast<int>(sort_column);});
729  int index = 2 * (std::distance(all_orders_.begin(), order_it));
730  if(order == preferences::SORT_ORDER::DESCENDING) {
731  ++index;
732  }
733  order_menu.set_value(index);
734  preferences::set_addon_manager_saved_order_name(order_it->as_preference);
736 }
737 
738 template<void(addon_manager::*fptr)(const addon_info& addon)>
740 {
741  // Explicitly return to the main page if we're in low-res mode so the list is visible.
742  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
743  stk->select_layer(0);
744  find_widget<button>(get_window(), "details_toggle", false).set_label(_("Add-on Details"));
745  }
746 
747  addon_list& addons = find_widget<addon_list>(get_window(), "addons", false);
748  const addon_info* addon = addons.get_selected_addon();
749 
750  if(addon == nullptr) {
751  return;
752  }
753 
754  try {
755  (this->*fptr)(*addon);
756  } catch(const addons_client::user_exit&) {
757  // User canceled the op.
758  }
759 }
760 
762 {
763  addon_info versioned_addon = addon;
764  widget* parent = get_window();
765  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
766  parent = stk->get_layer_grid(1);
767  }
768  if(addon.id == find_widget<addon_list>(get_window(), "addons", false).get_selected_addon()->id) {
769  versioned_addon.current_version = find_widget<menu_button>(parent, "version_filter", false).get_value_string();
770  }
771 
773 
774  // Take note if any wml_changes occurred
776 
779  }
780 }
781 
783 {
784  if(have_addon_pbl_info(addon.id) || have_addon_in_vcs_tree(addon.id)) {
786  _("The following add-on appears to have publishing or version control information stored locally, and will not be removed:") + " " +
787  addon.display_title_full());
788  return;
789  }
790 
791  bool success = remove_local_addon(addon.id);
792 
793  if(!success) {
794  gui2::show_error_message(_("The following add-on could not be deleted properly:") + " " + addon.display_title_full());
795  } else {
797 
799  }
800 }
801 
803 {
804  /* Currently, the install and update codepaths are the same, so this function simply
805  * calls the other. Since this might change in the future, I'm leaving this function
806  * here for now.
807  *
808  * - vultraz, 2017-03-12
809  */
810  install_addon(addon);
811 }
812 
814 {
815  for(const auto& a : addons_) {
816  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
818 
819  if(result.wml_changed) {
820  // Updating an add-on may have resulted in its dependencies being updated
821  // as well, so we need to reread version info blocks afterwards to make sure
822  // we don't try to re-download newly updated add-ons.
824  }
825 
826  // Take note if any wml_changes occurred
828  }
829  }
830 
832  load_addon_list();
833  }
834 }
835 
836 /** Performs all backend and UI actions for publishing the specified add-on. */
838 {
839  std::string server_msg;
840 
841  const std::string addon_id = addon.id;
842  config cfg = get_addon_pbl_info(addon_id);
843 
844  const version_info& version_to_publish = cfg["version"].str();
845 
846  if(version_to_publish <= tracking_info_[addon_id].remote_version) {
847  const int res = gui2::show_message(_("Warning"),
848  _("The remote version of this add-on is greater or equal to the version being uploaded. Do you really wish to continue?"),
850 
851  if(res != gui2::retval::OK) {
852  return;
853  }
854  }
855 
856  // the passphrase isn't provided, prompt for it
857  if(cfg["passphrase"].empty()) {
858  if(!gui2::dialogs::addon_auth::execute(cfg)) {
859  return;
860  }
861  }
862 
863  if(!::image::exists(cfg["icon"].str())) {
864  gui2::show_error_message(_("Invalid icon path. Make sure the path points to a valid image."));
865  } else if(!client_.request_distribution_terms(server_msg)) {
867  _("The server responded with an error:") + "\n" + client_.get_last_server_error());
868  } else if(gui2::dialogs::addon_license_prompt::execute(server_msg)) {
869  if(!client_.upload_addon(addon_id, server_msg, cfg, tracking_info_[addon_id].state == ADDON_INSTALLED_LOCAL_ONLY)) {
870  const std::string& msg = _("The add-on was rejected by the server:") +
871  "\n\n" + client_.get_last_server_error();
872  const std::string& extra_data = client_.get_last_server_error_data();
873  if (!extra_data.empty()) {
874  // TODO: Allow user to copy the extra data portion to clipboard
875  // or something, maybe display it in a dialog similar to
876  // the WML load errors report in a monospace font and
877  // stuff (having a scroll container is especially
878  // important since a long list can cause the dialog to
879  // overflow).
880  gui2::show_error_message(msg + "\n\n" + extra_data, true);
881  } else {
882  gui2::show_error_message(msg, true);
883  }
884  } else {
885  gui2::show_transient_message(_("Response"), server_msg);
888  }
889  }
890 }
891 
892 /** Performs all backend and UI actions for taking down the specified add-on. */
894 {
895  const std::string addon_id = addon.id;
896  const std::string& text = VGETTEXT(
897  "Deleting '$addon|' will permanently erase its download and upload counts on the add-ons server. Do you really wish to continue?",
898  {{"addon", make_addon_title(addon_id)}} // FIXME: need the real title!
899  );
900 
901  const int res = gui2::show_message(_("Confirm"), text, gui2::dialogs::message::yes_no_buttons);
902 
903  if(res != gui2::retval::OK) {
904  return;
905  }
906 
907  std::string server_msg;
908  if(!client_.delete_remote_addon(addon_id, server_msg)) {
909  gui2::show_error_message(_("The server responded with an error:") + "\n" + client_.get_last_server_error());
910  } else {
911  // FIXME: translation needed!
912  gui2::show_transient_message(_("Response"), server_msg);
915  }
916 }
917 
918 /** Called when the player double-clicks an add-on. */
920 {
921  switch(tracking_info_[addon.id].state) {
922  case ADDON_NONE:
923  install_addon(addon);
924  break;
925  case ADDON_INSTALLED:
926  if(!tracking_info_[addon.id].can_publish) {
927  utils::string_map symbols{ { "addon", addon.display_title_full() } };
928  int res = gui2::show_message(_("Uninstall add-on"),
929  VGETTEXT("Do you want to uninstall '$addon|'?", symbols),
931  if(res == gui2::retval::OK) {
932  uninstall_addon(addon);
933  }
934  }
935  break;
937  update_addon(addon);
938  break;
941  publish_addon(addon);
942  break;
943  default:
944  break;
945  }
946 }
947 
949 {
950  help::show_help("installing_addons");
951 }
952 
953 static std::string format_addon_time(std::time_t time)
954 {
955  if(time) {
956  std::ostringstream ss;
957 
959  ? "%B %d %Y, %I:%M %p"
960  : "%B %d %Y, %H:%M";
961 
962  ss << std::put_time(std::localtime(&time), format);
963 
964  return ss.str();
965  }
966 
967  return font::unicode_em_dash;
968 }
969 
971 {
972  const addon_info* info = find_widget<addon_list>(get_window(), "addons", false).get_selected_addon();
973 
974  if(info == nullptr) {
975  return;
976  }
977 
978  widget* parent = get_window();
979  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
980  parent = stk->get_layer_grid(1);
981  }
982 
983  find_widget<drawing>(parent, "image", false).set_label(info->display_icon());
984 
985  find_widget<styled_widget>(parent, "title", false).set_label(info->display_title_translated_or_original());
986  find_widget<styled_widget>(parent, "description", false).set_label(info->description_translated());
987  menu_button& version_filter = find_widget<menu_button>(parent, "version_filter", false);
988  find_widget<styled_widget>(parent, "author", false).set_label(info->author);
989  find_widget<styled_widget>(parent, "type", false).set_label(info->display_type());
990 
991  styled_widget& status = find_widget<styled_widget>(parent, "status", false);
993  status.set_use_markup(true);
994 
995  find_widget<styled_widget>(parent, "size", false).set_label(size_display_string(info->size));
996  find_widget<styled_widget>(parent, "downloads", false).set_label(std::to_string(info->downloads));
997  find_widget<styled_widget>(parent, "created", false).set_label(format_addon_time(info->created));
998  find_widget<styled_widget>(parent, "updated", false).set_label(format_addon_time(info->updated));
999 
1000  find_widget<styled_widget>(parent, "dependencies", false).set_label(!info->depends.empty()
1001  ? make_display_dependencies(info->id, addons_, tracking_info_)
1002  : _("addon_dependencies^None"));
1003 
1004  std::string languages;
1005 
1006  for(const auto& lc : info->locales) {
1007  const std::string& langlabel = langcode_to_string(lc);
1008  if(!langlabel.empty()) {
1009  if(!languages.empty()) {
1010  languages += ", ";
1011  }
1012  languages += langlabel;
1013  }
1014  }
1015 
1016  find_widget<styled_widget>(parent, "translations", false).set_label(!languages.empty() ? languages : _("translations^None"));
1017 
1018  const std::string& feedback_url = info->feedback_url;
1019  find_widget<label>(parent, "url", false).set_label(!feedback_url.empty() ? feedback_url : _("url^None"));
1020 
1021  bool installed = is_installed_addon_status(tracking_info_[info->id].state);
1022  bool updatable = tracking_info_[info->id].state == ADDON_INSTALLED_UPGRADABLE;
1023 
1024  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1025  // #TODO: Add tooltips with upload time and pack size
1026  std::vector<config> version_filter_entries;
1027 
1028  if(!tracking_info_[info->id].can_publish) {
1029  action_stack.select_layer(0);
1030 
1031  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1032  install_update_stack.select_layer(updatable ? 1 : 0);
1033 
1034  if(!updatable) {
1035  find_widget<button>(parent, "install", false).set_active(!installed);
1036  } else {
1037  find_widget<button>(parent, "update", false).set_active(true);
1038  }
1039 
1040  find_widget<button>(parent, "uninstall", false).set_active(installed);
1041 
1042  for(const auto& f : info->versions) {
1043  version_filter_entries.emplace_back("label", f.str());
1044  }
1045  } else {
1046  action_stack.select_layer(1);
1047 
1048  // Always enable the publish button, but disable the delete button if not yet published.
1049  find_widget<button>(parent, "publish", false).set_active(true);
1050  find_widget<button>(parent, "delete", false).set_active(!info->local_only);
1051 
1052  // Show only the version to be published
1053  version_filter_entries.emplace_back("label", info->current_version.str());
1054  }
1055 
1056  version_filter.set_values(version_filter_entries);
1057  version_filter.set_active(version_filter_entries.size() > 1);
1058 }
1059 
1061 {
1062  widget* parent = get_window();
1063  widget* parent_of_addons_list = parent;
1064  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
1065  parent = stk->get_layer_grid(1);
1066  parent_of_addons_list = stk->get_layer_grid(0);
1067  }
1068 
1069  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
1070 
1071  if(info == nullptr) {
1072  return;
1073  }
1074 
1075  if(!tracking_info_[info->id].can_publish && is_installed_addon_status(tracking_info_[info->id].state)) {
1076  bool updatable = tracking_info_[info->id].installed_version
1077  != find_widget<menu_button>(parent, "version_filter", false).get_value_string();
1078  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1079  action_stack.select_layer(0);
1080 
1081  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1082  install_update_stack.select_layer(1);
1083  find_widget<button>(parent, "update", false).set_active(updatable);
1084  }
1085 }
1086 
1088 {
1091  return false;
1092  }
1093 
1094  return true;
1095 }
1096 
1097 } // namespace dialogs
int size
Definition: info.hpp:87
const std::string & get_last_server_error() const
Returns the last error message sent by the server, or an empty string.
Definition: client.hpp:77
void read_addons_list(const config &cfg, addons_list &dest)
Parse the specified add-ons list WML into an actual addons_list object.
Definition: info.cpp:294
void close()
Requests to close the window.
Definition: window.hpp:182
void set_text_changed_callback(std::function< void(text_box_base *textbox, const std::string text)> cb)
Set the text_changed callback.
ADDON_STATUS state
Definition: state.hpp:57
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:153
std::string addon_manager_saved_order_name()
Definition: general.cpp:969
Modification of the game.
Definition: validation.hpp:110
Single-player scenario.
Definition: validation.hpp:102
void show_help(const std::string &show_topic, int xloc, int yloc)
Open the help browser, show topic with id show_topic.
Definition: help.cpp:144
config cfg_
Config which contains the list with the campaigns.
Definition: manager.hpp:98
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with &#39;$&#39; in the string &#39;str&#39; with the equivalent ...
const std::string & addr() const
Returns the current hostname:port used for this connection.
Definition: client.hpp:74
std::map< std::string, t_string > string_map
boost::dynamic_bitset get_status_filter_visibility() const
Definition: manager.cpp:607
bool exit_hook(window &window)
Definition: manager.cpp:1087
std::function< bool(const addon_info &, const addon_info &)> addon_sort_func
Definition: addon_list.hpp:42
an add-on that fits in no other category
Definition: validation.hpp:113
void set_addon_manager_saved_order_direction(SORT_ORDER value)
Definition: general.cpp:984
static std::string format_addon_time(std::time_t time)
Definition: manager.cpp:953
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:61
SORT_ORDER addon_manager_saved_order_direction()
Definition: general.cpp:979
Total Conversion Core.
Definition: validation.hpp:100
addons_tracking_list tracking_info_
Definition: manager.hpp:104
bool using_tls() const
Returns whether the current connection uses TLS.
Definition: client.hpp:216
preferences::SORT_ORDER SORT_ORDER
Definition: listbox.cpp:42
std::string display_title_translated_or_original() const
Definition: info.cpp:207
grid * get_layer_grid(unsigned int i)
Gets the grid for a specified layer.
logger & info()
Definition: log.cpp:89
void add_list_to_keyboard_chain()
Adds the internal listbox to the keyboard event chain.
Definition: addon_list.cpp:372
#define a
Shows an ok and cancel button.
Definition: message.hpp:76
std::string get_value() const
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 reload_list_and_reselect_item(const std::string id)
Definition: manager.cpp:574
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 execute_default_action(const addon_info &addon)
Called when the player double-clicks an add-on.
Definition: manager.cpp:919
Base class for all widgets.
Definition: widget.hpp:49
int current_layer() const
Gets the current visible layer number.
Multiplayer faction.
Definition: validation.hpp:108
virtual void set_value(unsigned value, bool fire_event=false) override
Inherited from selectable_item.
Definition: menu_button.hpp:87
bool chars_equal_insensitive(char a, char b)
Definition: general.hpp:23
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:299
window * get_window() const
Returns a pointer to the dialog&#39;s window.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
static const std::vector< std::pair< ADDON_STATUS_FILTER, std::string > > status_filter_types_
Definition: manager.hpp:106
A label displays a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:57
A multimenu_button is a styled_widget to choose an element from a list of elements.
std::string feedback_url
Definition: info.hpp:101
bool have_addon_in_vcs_tree(const std::string &addon_name)
Returns whether the specified add-on appears to be managed by a VCS or not.
Definition: manager.cpp:54
static std::string _(const char *str)
Definition: gettext.hpp:93
Definitions for the interface to Wesnoth Markup Language (WML).
Define the common filters for the gui2::pane class.
void set_callback_order_change(std::function< void(unsigned, preferences::SORT_ORDER)> callback)
Sets up a callback that will be called when the player changes the sorting order. ...
Definition: addon_list.hpp:138
version_info installed_version
Definition: state.hpp:60
std::deque< std::unique_ptr< editor_action > > action_stack
Action stack typedef.
No tracking information available.
Definition: state.hpp:37
const_attr_itors attribute_range() const
Definition: config.cpp:858
Add-on is not installed.
Definition: state.hpp:24
void update_addon(const addon_info &addon)
Definition: manager.cpp:802
Class for a single line text area.
Definition: text_box.hpp:141
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
Definition: manager.cpp:173
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:76
#define b
const std::vector< std::string > filtertext_
Definition: manager.cpp:104
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:1011
Base container class.
Definition: grid.hpp:31
Desktop environment interaction functions.
virtual unsigned get_value() const override
Inherited from selectable_item.
Definition: menu_button.hpp:84
boost::dynamic_bitset get_name_filter_visibility() const
Definition: manager.cpp:583
void set_delete_function(addon_op_func_t function)
Sets the function to install an addon from the addons server.
Definition: addon_list.hpp:103
Version in the server is older than local installation.
Definition: state.hpp:30
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:187
bool is_installed_addon_status(ADDON_STATUS s)
Definition: state.hpp:40
std::string label
What to show in the filter&#39;s drop-down list.
Definition: manager.cpp:217
This file contains the settings handling of the widget library.
ADDON_STATUS_FILTER
Add-on installation status filters for the user interface.
Definition: state.hpp:84
boost::dynamic_bitset get_tag_filter_visibility() const
Definition: manager.cpp:628
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:172
std::string display_icon() const
Get an icon path fixed for display (e.g.
Definition: info.cpp:232
const std::string & get_last_server_error_data() const
Returns the last error message extra data sent by the server, or an empty string. ...
Definition: client.hpp:80
std::string id
Definition: info.hpp:76
Version in the server is newer than local installation.
Definition: state.hpp:28
language_list get_languages(bool all)
Return a list of available translations.
Definition: language.cpp:127
void toggle_details(button &btn, stacked_widget &stk)
Definition: manager.cpp:503
void set_values(const std::vector<::config > &values, unsigned selected=0)
std::string display_type() const
Get an add-on type identifier for display in the user&#39;s language.
Definition: info.cpp:249
Shows a yes and no button.
Definition: message.hpp:80
virtual void set_use_markup(bool use_markup)
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on&#39;s version information structs.
Definition: manager.cpp:356
void set_addons(const addons_list &addons)
Sets the add-ons to show.
Definition: addon_list.cpp:158
bool remove_local_addon(const std::string &addon)
Removes the specified add-on, deleting its full directory structure.
Definition: manager.cpp:130
Dependencies not satisfied.
Definition: state.hpp:35
Add-ons (campaignd) client class.
Definition: client.hpp:40
int get_retval()
Definition: window.hpp:365
config get_addon_pbl_info(const std::string &addon_name)
Gets the publish information for an add-on.
Definition: manager.cpp:68
User aborted the operation because of an issue with dependencies or chose not to overwrite the add-on...
Miscellaneous content/media (unit packs, terrain packs, music packs, etc.).
Definition: validation.hpp:112
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:206
static std::string describe_status_verbose(const addon_tracking_info &state)
Definition: manager.cpp:254
bool ci_search(const std::string &s1, const std::string &s2)
Definition: gettext.cpp:534
void on_order_changed(unsigned int sort_column, preferences::SORT_ORDER order)
Definition: manager.cpp:724
bool local_only
Definition: info.hpp:108
std::time_t created
Definition: info.hpp:104
std::vector< std::string > locales
Definition: info.hpp:94
Multiplayer scenario.
Definition: validation.hpp:105
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
boost::dynamic_bitset get_toggle_states() const
Get the current state of the menu options.
Multiplayer era.
Definition: validation.hpp:107
void delete_addon(const addon_info &addon)
Performs all backend and UI actions for taking down the specified add-on.
Definition: manager.cpp:893
std::size_t i
Definition: function.cpp:967
bool have_addon_pbl_info(const std::string &addon_name)
Returns whether a .pbl file is present for the specified add-on or not.
Definition: manager.cpp:63
addons_client & client_
Definition: manager.hpp:100
static map_location::DIRECTION s
std::string size_display_string(double size)
Get a human-readable representation of the specified byte count.
Definition: info.cpp:311
addon_manager(addons_client &client)
Definition: manager.cpp:244
void set_publish_function(addon_op_func_t function)
Sets the function to upload an addon to the addons server.
Definition: addon_list.hpp:97
std::time_t updated
Definition: info.hpp:103
void execute_default_action_on_selected_addon()
Definition: manager.hpp:146
void set_update_function(addon_op_func_t function)
Sets the function to call when the player clicks the update button.
Definition: addon_list.hpp:91
std::string display_title_full() const
Definition: info.cpp:224
Multiplayer plain (no WML) map pack.
Definition: validation.hpp:106
#define N_(String)
Definition: gettext.hpp:101
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
No version in the server.
Definition: state.hpp:32
Base class for all visible items.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
install_outcome outcome
Overall outcome of the operation.
Definition: client.hpp:129
std::set< version_info, std::greater< version_info > > versions
Definition: info.hpp:83
void select_layer(const int layer)
Selects and displays a particular layer.
boost::dynamic_bitset get_type_filter_visibility() const
Definition: manager.cpp:660
static std::string colorize_addon_state_string(const std::string &str, ADDON_STATUS state, bool verbose=false)
Changes the color of an add-on state string (installed, outdated, etc.) according to the state itself...
Definition: addon_list.cpp:65
Represents version numbers.
void publish_addon(const addon_info &addon)
Performs all backend and UI actions for publishing the specified add-on.
Definition: manager.cpp:837
config & add_child(config_key_type key)
Definition: config.cpp:514
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:320
void install_addon(const addon_info &addon)
Definition: manager.cpp:761
void uninstall_addon(const addon_info &addon)
Definition: manager.cpp:782
void set_uninstall_function(addon_op_func_t function)
Sets the function to call when the player clicks the uninstall button.
Definition: addon_list.hpp:85
static const std::vector< std::pair< ADDON_TYPE, std::string > > type_filter_types_
Definition: manager.hpp:107
std::vector< std::string > depends
Definition: info.hpp:98
#define f
addon_tracking_info get_addon_tracking_info(const addon_info &addon)
Get information about an add-on comparing its local state with the add-ons server entry...
Definition: state.cpp:25
Hybrid campaign.
Definition: validation.hpp:103
std::vector< std::string > split(const config_attribute_value &val)
bool wml_changed
Specifies if WML on disk was altered and needs to be reloaded.
Definition: client.hpp:138
std::map< std::string, addon_tracking_info > addons_tracking_list
Definition: state.hpp:64
const std::string unicode_em_dash
Definition: constants.cpp:44
std::string title
Definition: info.hpp:77
std::set< std::string > resolve_dependencies(const addons_list &addons) const
Resolve an add-on&#39;s dependency tree in a recursive fashion.
Definition: info.cpp:281
Simple push button.
Definition: button.hpp:36
std::string str() const
Serializes the version number into string form.
bool upload_addon(const std::string &id, std::string &response_message, config &cfg, bool local_only)
Uploads an add-on to the server.
Definition: client.cpp:154
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:206
void set_install_function(addon_op_func_t function)
Sets the function to call when the player clicks the install button.
Definition: addon_list.hpp:79
void set_values(const std::vector<::config > &values)
Set the available menu options.
Stores additional status information about add-ons.
Definition: state.hpp:46
Dialog was closed with the OK button.
Definition: retval.hpp:35
A stacked widget holds several widgets on top of each other.
int downloads
Definition: info.hpp:88
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
bool delete_remote_addon(const std::string &id, std::string &response_message)
Requests the specified add-on to be removed from the server.
Definition: client.cpp:264
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
Definition: client.cpp:131
Contains the outcome of an add-on install operation.
Definition: client.hpp:124
version_info current_version
Definition: info.hpp:82
std::map< std::string, addon_info > addons_list
Definition: info.hpp:28
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
install_result install_addon_with_checks(const addons_list &addons, const addon_info &addon)
Performs an add-on download and install cycle.
Definition: client.cpp:555
Single-player campaign.
Definition: validation.hpp:101
void set_addon_manager_saved_order_name(const std::string &value)
Definition: general.cpp:974
bool use_twelve_hour_clock_format()
Definition: general.cpp:944
static const int DEFAULT_ACTION_RETVAL
Special retval for the toggle panels in the addons list.
Definition: addon_list.hpp:47
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: manager.cpp:309
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
Definition: client.cpp:114
void set_modified_signal_handler(const std::function< void()> &callback)
Sets up a callback that will be called when the player selects an add-on.
Definition: addon_list.hpp:55
Multiplayer campaign.
Definition: validation.hpp:104
#define GETTEXT_DOMAIN
Definition: manager.cpp:16
std::string author
Definition: info.hpp:85
void set_link_aware(bool l)
Definition: label.cpp:91
static const std::vector< addon_order > all_orders_
Definition: manager.hpp:108
std::string tooltip
Shown when hovering over an entry in the filter&#39;s drop-down list.
Definition: manager.cpp:219
Version in the server matches local installation.
Definition: state.hpp:26
std::string description_translated() const
Definition: info.cpp:213