The Battle for Wesnoth  1.17.4+dev
manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2022
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"
32 #include "gui/dialogs/message.hpp"
34 #include "gui/widgets/button.hpp"
35 #include "gui/widgets/label.hpp"
39 #include "gui/widgets/drawing.hpp"
40 #include "gui/widgets/image.hpp"
41 #include "gui/widgets/listbox.hpp"
42 #include "gui/widgets/settings.hpp"
44 #include "gui/widgets/text_box.hpp"
45 #include "gui/widgets/window.hpp"
47 #include "preferences/game.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();
416 
417  if(!saved_order_name.empty()) {
418  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
419  [&saved_order_name](const addon_order& order) {return order.as_preference == saved_order_name;});
420  if(order_it != all_orders_.end()) {
421  int index = 2 * (std::distance(all_orders_.begin(), order_it));
423  if(saved_order_direction == sort_order::type::ascending) {
424  func = order_it->sort_func_asc;
425  } else {
426  func = order_it->sort_func_desc;
427  ++index;
428  }
429  find_widget<menu_button>(&window, "order_dropdown", false).set_value(index, false);
430  auto& addons = find_widget<addon_list>(&window, "addons", false);
431  addons.set_addon_order(func);
432  addons.select_first_addon();
433  }
434  }
435  }
436 
437  connect_signal_notify_modified(order_dropdown,
438  std::bind(&addon_manager::order_addons, this));
439 
440  label& url_label = find_widget<label>(&window, "url", false);
441 
442  url_label.set_use_markup(true);
443  url_label.set_link_aware(true);
444 
446  find_widget<button>(&window, "install", false),
447  std::bind(&addon_manager::install_selected_addon, this));
448 
450  find_widget<button>(&window, "uninstall", false),
451  std::bind(&addon_manager::uninstall_selected_addon, this));
452 
454  find_widget<button>(&window, "update", false),
455  std::bind(&addon_manager::update_selected_addon, this));
456 
458  find_widget<button>(&window, "publish", false),
459  std::bind(&addon_manager::publish_selected_addon, this));
460 
462  find_widget<button>(&window, "delete", false),
463  std::bind(&addon_manager::delete_selected_addon, this));
464 
466  find_widget<button>(&window, "update_all", false),
467  std::bind(&addon_manager::update_all_addons, this));
468 
470  find_widget<button>(&window, "show_help", false),
471  std::bind(&addon_manager::show_help, this));
472 
473  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
474  button& btn = find_widget<button>(&window, "details_toggle", false);
475  connect_signal_mouse_left_click(btn, std::bind(&addon_manager::toggle_details, this, std::ref(btn), std::ref(*stk)));
476  stk->select_layer(0);
477  }
478 
479  widget* version_filter_parent = &window;
480  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
481  version_filter_parent = stk->get_layer_grid(1);
482  }
483 
484  menu_button& version_filter = find_widget<menu_button>(version_filter_parent, "version_filter", false);
485  connect_signal_notify_modified(version_filter,
487 
488  on_addon_select();
489 
490  window.set_enter_disabled(true);
491 
492  window.keyboard_capture(&filter);
494 
495  list.set_callback_order_change(std::bind(&addon_manager::on_order_changed, this, std::placeholders::_1, std::placeholders::_2));
496 
497  // Use handle the special addon_list retval to allow installing addons on double click
498  window.set_exit_hook(std::bind(&addon_manager::exit_hook, this, std::placeholders::_1));
499 }
500 
502 {
503  if(stk.current_layer() == 0) {
504  btn.set_label(_("addons^Back to List"));
505  stk.select_layer(1);
506  } else {
507  btn.set_label(_("Add-on Details"));
508  stk.select_layer(0);
509  }
510 }
511 
513 {
515  if(!cfg_) {
516  gui2::show_error_message(_("An error occurred while downloading the add-ons list from the server."));
517  get_window()->close();
518  }
519 }
520 
522 {
525  }
526 
528 
529  std::vector<std::string> publishable_addons = available_addons();
530 
531  for(std::string id : publishable_addons) {
532  if(addons_.find(id) == addons_.end()) {
533  // Get a config from the addon's pbl file
534  // Note that the name= key is necessary or stuff breaks, since the filter code uses this key
535  // to match add-ons in the config list. It also fills in addon_info's id field. It's also
536  // neccessay to set local_only here so that flag can be properly set after addons_ is cleared
537  // and recreated by read_addons_list.
538  config pbl_cfg = get_addon_pbl_info(id);
539  pbl_cfg["name"] = id;
540  pbl_cfg["local_only"] = true;
541 
542  // Add the add-on to the list.
543  addon_info addon(pbl_cfg);
544  addons_[id] = addon;
545 
546  // Add the addon to the config entry
547  cfg_.add_child("campaign", std::move(pbl_cfg));
548  }
549  }
550 
551  if(addons_.empty()) {
552  show_transient_message(_("No Add-ons Available"), _("There are no add-ons available for download from this server."));
553  }
554 
555  addon_list& list = find_widget<addon_list>(get_window(), "addons", false);
556  list.set_addons(addons_);
557 
558  bool has_upgradable_addons = false;
559  for(const auto& a : addons_) {
560  tracking_info_[a.first] = get_addon_tracking_info(a.second);
561 
562  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
563  has_upgradable_addons = true;
564  }
565  }
566 
567  find_widget<button>(get_window(), "update_all", false).set_active(has_upgradable_addons);
568 
569  apply_filters();
570 }
571 
573 {
574  load_addon_list();
575 
576  // Reselect the add-on.
577  find_widget<addon_list>(get_window(), "addons", false).select_addon(id);
578  on_addon_select();
579 }
580 
581 boost::dynamic_bitset<> addon_manager::get_name_filter_visibility() const
582 {
583  const text_box& name_filter = find_widget<const text_box>(get_window(), "filter", false);
584  const std::string& text = name_filter.get_value();
585 
586  filter_transform filter(utils::split(text, ' '));
587  boost::dynamic_bitset<> res;
588 
589  const config::const_child_itors& addon_cfgs = cfg_.child_range("campaign");
590 
591  for(const auto& a : addons_)
592  {
593  const config& addon_cfg = *std::find_if(addon_cfgs.begin(), addon_cfgs.end(),
594  [&a](const config& cfg)
595  {
596  return cfg["name"] == a.first;
597  });
598 
599  res.push_back(filter(addon_cfg));
600  }
601 
602  return res;
603 }
604 
605 boost::dynamic_bitset<> addon_manager::get_status_filter_visibility() const
606 {
607  const menu_button& status_filter = find_widget<const menu_button>(get_window(), "install_status_filter", false);
608  const ADDON_STATUS_FILTER selection = status_filter_types_[status_filter.get_value()].first;
609 
610  boost::dynamic_bitset<> res;
611  for(const auto& a : addons_) {
612  const addon_tracking_info& info = tracking_info_.at(a.second.id);
613 
614  res.push_back(
615  (selection == FILTER_ALL) ||
616  (selection == FILTER_INSTALLED && is_installed_addon_status(info.state)) ||
617  (selection == FILTER_UPGRADABLE && info.state == ADDON_INSTALLED_UPGRADABLE) ||
618  (selection == FILTER_PUBLISHABLE && info.can_publish == true) ||
619  (selection == FILTER_NOT_INSTALLED && info.state == ADDON_NONE)
620  );
621  }
622 
623  return res;
624 }
625 
626 boost::dynamic_bitset<> addon_manager::get_tag_filter_visibility() const
627 {
628  const auto& tag_filter = find_widget<const multimenu_button>(get_window(), "tag_filter", false);
629  const auto toggle_states = tag_filter.get_toggle_states();
630  if(toggle_states.none()) {
631  // Nothing selected. It means that all add-ons are shown.
632  boost::dynamic_bitset<> res_flipped(addons_.size());
633  return ~res_flipped;
634  }
635 
636  std::vector<std::string> selected_tags;
637  for(std::size_t i = 0; i < tag_filter_types_.size(); ++i) {
638  if(toggle_states[i]) {
639  selected_tags.push_back(tag_filter_types_[i].id);
640  }
641  }
642 
643  boost::dynamic_bitset<> res;
644  for(const auto& a : addons_) {
645  bool matched_tag = false;
646  for(const auto& id : selected_tags) {
647  if(utils::contains(a.second.tags, id)) {
648  matched_tag = true;
649  break;
650  }
651  }
652  res.push_back(matched_tag);
653  }
654 
655  return res;
656 }
657 
658 boost::dynamic_bitset<> addon_manager::get_type_filter_visibility() const
659 {
660  const multimenu_button& type_filter = find_widget<const multimenu_button>(get_window(), "type_filter", false);
661 
662  boost::dynamic_bitset<> toggle_states = type_filter.get_toggle_states();
663  if(toggle_states.none()) {
664  // Nothing selected. It means that *all* add-ons are shown.
665  boost::dynamic_bitset<> res_flipped(addons_.size());
666  return ~res_flipped;
667  } else {
668  boost::dynamic_bitset<> res;
669 
670  for(const auto& a : addons_) {
671  int index = std::distance(type_filter_types_.begin(),
672  std::find_if(type_filter_types_.begin(), type_filter_types_.end(),
673  [&a](const std::pair<ADDON_TYPE, std::string>& entry) {
674  return entry.first == a.second.type;
675  })
676  );
677  res.push_back(toggle_states[index]);
678  }
679 
680  return res;
681  }
682 }
683 
685 {
686  // In the small-screen layout, the text_box for the filter keeps keyboard focus even when the
687  // details panel is visible, which means this can be called when the list isn't visible. That
688  // causes problems both because find_widget can throw exceptions, but also because changing the
689  // filters can hide the currently-shown add-on, triggering a different one to be selected in a
690  // way that would seem random unless the user realised that they were typing into a filter box.
691  //
692  // Quick workaround is to not process the new filter if the list isn't visible.
693  auto list = find_widget<addon_list>(get_window(), "addons", false, false);
694  if(!list) {
695  return;
696  }
697 
698  boost::dynamic_bitset<> res =
703  list->set_addon_shown(res);
704 }
705 
707 {
708  const menu_button& order_menu = find_widget<const menu_button>(get_window(), "order_dropdown", false);
709  const addon_order& order_struct = all_orders_.at(order_menu.get_value() / 2);
710  sort_order::type order = order_menu.get_value() % 2 == 0 ? sort_order::type::ascending : sort_order::type::descending;
712  if(order == sort_order::type::ascending) {
713  func = order_struct.sort_func_asc;
714  } else {
715  func = order_struct.sort_func_desc;
716  }
717 
718  find_widget<addon_list>(get_window(), "addons", false).set_addon_order(func);
719  preferences::set_addon_manager_saved_order_name(order_struct.as_preference);
721 }
722 
723 void addon_manager::on_order_changed(unsigned int sort_column, sort_order::type order)
724 {
725  menu_button& order_menu = find_widget<menu_button>(get_window(), "order_dropdown", false);
726  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
727  [sort_column](const addon_order& order) {return order.column_index == static_cast<int>(sort_column);});
728  int index = 2 * (std::distance(all_orders_.begin(), order_it));
729  if(order == sort_order::type::descending) {
730  ++index;
731  }
732  order_menu.set_value(index);
733  preferences::set_addon_manager_saved_order_name(order_it->as_preference);
735 }
736 
737 template<void(addon_manager::*fptr)(const addon_info& addon)>
739 {
740  // Explicitly return to the main page if we're in low-res mode so the list is visible.
741  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
742  stk->select_layer(0);
743  find_widget<button>(get_window(), "details_toggle", false).set_label(_("Add-on Details"));
744  }
745 
746  addon_list& addons = find_widget<addon_list>(get_window(), "addons", false);
747  const addon_info* addon = addons.get_selected_addon();
748 
749  if(addon == nullptr) {
750  return;
751  }
752 
753  try {
754  (this->*fptr)(*addon);
755  } catch(const addons_client::user_exit&) {
756  // User canceled the op.
757  }
758 }
759 
761 {
762  addon_info versioned_addon = addon;
763  widget* parent = get_window();
764  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
765  parent = stk->get_layer_grid(1);
766  }
767  if(addon.id == find_widget<addon_list>(get_window(), "addons", false).get_selected_addon()->id) {
768  versioned_addon.current_version = find_widget<menu_button>(parent, "version_filter", false).get_value_string();
769  }
770 
772 
773  // Take note if any wml_changes occurred
775 
778  }
779 }
780 
782 {
783  if(have_addon_pbl_info(addon.id) || have_addon_in_vcs_tree(addon.id)) {
785  _("The following add-on appears to have publishing or version control information stored locally, and will not be removed:") + " " +
786  addon.display_title_full());
787  return;
788  }
789 
790  bool success = remove_local_addon(addon.id);
791 
792  if(!success) {
793  gui2::show_error_message(_("The following add-on could not be deleted properly:") + " " + addon.display_title_full());
794  } else {
796 
798  }
799 }
800 
802 {
803  /* Currently, the install and update codepaths are the same, so this function simply
804  * calls the other. Since this might change in the future, I'm leaving this function
805  * here for now.
806  *
807  * - vultraz, 2017-03-12
808  */
809  install_addon(addon);
810 }
811 
813 {
814  for(const auto& a : addons_) {
815  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
817 
818  if(result.wml_changed) {
819  // Updating an add-on may have resulted in its dependencies being updated
820  // as well, so we need to reread version info blocks afterwards to make sure
821  // we don't try to re-download newly updated add-ons.
823  }
824 
825  // Take note if any wml_changes occurred
827  }
828  }
829 
831  load_addon_list();
832  }
833 }
834 
835 /** Performs all backend and UI actions for publishing the specified add-on. */
837 {
838  std::string server_msg;
839 
840  const std::string addon_id = addon.id;
841  config cfg = get_addon_pbl_info(addon_id);
842 
843  const version_info& version_to_publish = cfg["version"].str();
844 
845  if(version_to_publish <= tracking_info_[addon_id].remote_version) {
846  const int res = gui2::show_message(_("Warning"),
847  _("The remote version of this add-on is greater or equal to the version being uploaded. Do you really wish to continue?"),
849 
850  if(res != gui2::retval::OK) {
851  return;
852  }
853  }
854 
855  // if the passphrase isn't provided from the _server.pbl, try to pre-populate it from the preferences before prompting for it
856  if(cfg["passphrase"].empty()) {
857  cfg["passphrase"] = preferences::password(preferences::campaign_server(), cfg["author"]);
858  if(!gui2::dialogs::addon_auth::execute(cfg)) {
859  return;
860  } else {
861  preferences::set_password(preferences::campaign_server(), cfg["author"], cfg["passphrase"]);
862  }
863  }
864 
865  if(!::image::exists(cfg["icon"].str())) {
866  gui2::show_error_message(_("Invalid icon path. Make sure the path points to a valid image."));
867  } else if(!client_.request_distribution_terms(server_msg)) {
869  _("The server responded with an error:") + "\n" + client_.get_last_server_error());
870  } else if(gui2::dialogs::addon_license_prompt::execute(server_msg)) {
871  if(!client_.upload_addon(addon_id, server_msg, cfg, tracking_info_[addon_id].state == ADDON_INSTALLED_LOCAL_ONLY)) {
872  const std::string& msg = _("The add-on was rejected by the server:") +
873  "\n\n" + client_.get_last_server_error();
874  const std::string& extra_data = client_.get_last_server_error_data();
875  if (!extra_data.empty()) {
876  // TODO: Allow user to copy the extra data portion to clipboard
877  // or something, maybe display it in a dialog similar to
878  // the WML load errors report in a monospace font and
879  // stuff (having a scroll container is especially
880  // important since a long list can cause the dialog to
881  // overflow).
882  gui2::show_error_message(msg + "\n\n" + extra_data, true);
883  } else {
884  gui2::show_error_message(msg, true);
885  }
886  } else {
887  gui2::show_transient_message(_("Response"), server_msg);
890  }
891  }
892 }
893 
894 /** Performs all backend and UI actions for taking down the specified add-on. */
896 {
897  const std::string addon_id = addon.id;
898  const std::string& text = VGETTEXT(
899  "Deleting '$addon|' will permanently erase its download and upload counts on the add-ons server. Do you really wish to continue?",
900  {{"addon", make_addon_title(addon_id)}} // FIXME: need the real title!
901  );
902 
903  const int res = gui2::show_message(_("Confirm"), text, gui2::dialogs::message::yes_no_buttons);
904 
905  if(res != gui2::retval::OK) {
906  return;
907  }
908 
909  std::string server_msg;
910  if(!client_.delete_remote_addon(addon_id, server_msg)) {
911  gui2::show_error_message(_("The server responded with an error:") + "\n" + client_.get_last_server_error());
912  } else {
913  // FIXME: translation needed!
914  gui2::show_transient_message(_("Response"), server_msg);
917  }
918 }
919 
920 /** Called when the player double-clicks an add-on. */
922 {
923  switch(tracking_info_[addon.id].state) {
924  case ADDON_NONE:
925  install_addon(addon);
926  break;
927  case ADDON_INSTALLED:
928  if(!tracking_info_[addon.id].can_publish) {
929  utils::string_map symbols{ { "addon", addon.display_title_full() } };
930  int res = gui2::show_message(_("Uninstall add-on"),
931  VGETTEXT("Do you want to uninstall '$addon|'?", symbols),
933  if(res == gui2::retval::OK) {
934  uninstall_addon(addon);
935  }
936  }
937  break;
939  update_addon(addon);
940  break;
943  publish_addon(addon);
944  break;
945  default:
946  break;
947  }
948 }
949 
951 {
952  help::show_help("installing_addons");
953 }
954 
955 static std::string format_addon_time(std::time_t time)
956 {
957  if(time) {
958  std::ostringstream ss;
959 
961  // TRANSLATORS: Month + day of month + year + 12-hour time, eg 'November 02 2021, 1:59 PM'. Format for your locale.
962  ? _("%B %d %Y, %I:%M %p")
963  // TRANSLATORS: Month + day of month + year + 24-hour time, eg 'November 02 2021, 13:59'. Format for your locale.
964  : _("%B %d %Y, %H:%M");
965 
966  ss << translation::strftime(format, std::localtime(&time));
967 
968  return ss.str();
969  }
970 
971  return font::unicode_em_dash;
972 }
973 
975 {
976  widget* parent = get_window();
977  widget* parent_of_addons_list = parent;
978  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
979  parent = stk->get_layer_grid(1);
980  parent_of_addons_list = stk->get_layer_grid(0);
981  }
982 
983  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
984 
985  if(info == nullptr) {
986  return;
987  }
988 
989  find_widget<drawing>(parent, "image", false).set_label(info->display_icon());
990 
991  find_widget<styled_widget>(parent, "title", false).set_label(info->display_title_translated_or_original());
992  find_widget<styled_widget>(parent, "description", false).set_label(info->description_translated());
993  menu_button& version_filter = find_widget<menu_button>(parent, "version_filter", false);
994  find_widget<styled_widget>(parent, "author", false).set_label(info->author);
995  find_widget<styled_widget>(parent, "type", false).set_label(info->display_type());
996 
997  styled_widget& status = find_widget<styled_widget>(parent, "status", false);
999  status.set_use_markup(true);
1000 
1001  find_widget<styled_widget>(parent, "size", false).set_label(size_display_string(info->size));
1002  find_widget<styled_widget>(parent, "downloads", false).set_label(std::to_string(info->downloads));
1003  find_widget<styled_widget>(parent, "created", false).set_label(format_addon_time(info->created));
1004  find_widget<styled_widget>(parent, "updated", false).set_label(format_addon_time(info->updated));
1005 
1006  find_widget<styled_widget>(parent, "dependencies", false).set_label(!info->depends.empty()
1007  ? make_display_dependencies(info->id, addons_, tracking_info_)
1008  : _("addon_dependencies^None"));
1009 
1010  std::string languages;
1011 
1012  for(const auto& lc : info->locales) {
1013  const std::string& langlabel = langcode_to_string(lc);
1014  if(!langlabel.empty()) {
1015  if(!languages.empty()) {
1016  languages += ", ";
1017  }
1018  languages += langlabel;
1019  }
1020  }
1021 
1022  find_widget<styled_widget>(parent, "translations", false).set_label(!languages.empty() ? languages : _("translations^None"));
1023 
1024  const std::string& feedback_url = info->feedback_url;
1025  find_widget<label>(parent, "url", false).set_label(!feedback_url.empty() ? feedback_url : _("url^None"));
1026 
1027  bool installed = is_installed_addon_status(tracking_info_[info->id].state);
1028  bool updatable = tracking_info_[info->id].state == ADDON_INSTALLED_UPGRADABLE;
1029 
1030  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1031  // #TODO: Add tooltips with upload time and pack size
1032  std::vector<config> version_filter_entries;
1033 
1034  if(!tracking_info_[info->id].can_publish) {
1035  action_stack.select_layer(0);
1036 
1037  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1038  install_update_stack.select_layer(updatable ? 1 : 0);
1039 
1040  if(!updatable) {
1041  find_widget<button>(parent, "install", false).set_active(!installed);
1042  } else {
1043  find_widget<button>(parent, "update", false).set_active(true);
1044  }
1045 
1046  find_widget<button>(parent, "uninstall", false).set_active(installed);
1047 
1048  for(const auto& f : info->versions) {
1049  version_filter_entries.emplace_back("label", f.str());
1050  }
1051  } else {
1052  action_stack.select_layer(1);
1053 
1054  // Always enable the publish button, but disable the delete button if not yet published.
1055  find_widget<button>(parent, "publish", false).set_active(true);
1056  find_widget<button>(parent, "delete", false).set_active(!info->local_only);
1057 
1058  // Show only the version to be published
1059  version_filter_entries.emplace_back("label", info->current_version.str());
1060  }
1061 
1062  version_filter.set_values(version_filter_entries);
1063  version_filter.set_active(version_filter_entries.size() > 1);
1064 }
1065 
1067 {
1068  widget* parent = get_window();
1069  widget* parent_of_addons_list = parent;
1070  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
1071  parent = stk->get_layer_grid(1);
1072  parent_of_addons_list = stk->get_layer_grid(0);
1073  }
1074 
1075  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
1076 
1077  if(info == nullptr) {
1078  return;
1079  }
1080 
1081  if(!tracking_info_[info->id].can_publish && is_installed_addon_status(tracking_info_[info->id].state)) {
1082  bool updatable = tracking_info_[info->id].installed_version
1083  != find_widget<menu_button>(parent, "version_filter", false).get_value_string();
1084  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1085  action_stack.select_layer(0);
1086 
1087  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1088  install_update_stack.select_layer(1);
1089  find_widget<button>(parent, "update", false).set_active(updatable);
1090  }
1091 }
1092 
1094 {
1097  return false;
1098  }
1099 
1100  return true;
1101 }
1102 
1103 } // 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:996
Modification of the game.
Definition: validation.hpp:113
Single-player scenario.
Definition: validation.hpp:105
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:96
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:605
bool exit_hook(window &window)
Definition: manager.cpp:1093
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:116
static std::string format_addon_time(std::time_t time)
Definition: manager.cpp:955
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:61
Total Conversion Core.
Definition: validation.hpp:103
addons_tracking_list tracking_info_
Definition: manager.hpp:102
bool using_tls() const
Returns whether the current connection uses TLS.
Definition: client.hpp:216
std::string display_title_translated_or_original() const
Definition: info.cpp:207
std::string campaign_server()
Definition: game.cpp:409
grid * get_layer_grid(unsigned int i)
Gets the grid for a specified layer.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:172
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:572
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:921
Base class for all widgets.
Definition: widget.hpp:49
int current_layer() const
Gets the current visible layer number.
Multiplayer faction.
Definition: validation.hpp:111
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:104
sort_order::type addon_manager_saved_order_direction()
Definition: general.cpp:1006
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).
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:801
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:83
#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:1025
std::string strftime(const std::string &format, const std::tm *time)
Definition: gettext.cpp:548
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:581
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
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:626
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 set_password(const std::string &server, const std::string &login, const std::string &key)
void toggle_details(button &btn, stacked_widget &stk)
Definition: manager.cpp:501
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:115
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:205
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:559
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:108
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:110
void delete_addon(const addon_info &addon)
Performs all backend and UI actions for taking down the specified add-on.
Definition: manager.cpp:895
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:98
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
std::string password(const std::string &server, const std::string &login)
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:144
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
void set_callback_order_change(std::function< void(unsigned, sort_order::type)> callback)
Sets up a callback that will be called when the player changes the sorting order. ...
Definition: addon_list.hpp:138
void on_order_changed(unsigned int sort_column, sort_order::type order)
Definition: manager.cpp:723
std::string display_title_full() const
Definition: info.cpp:224
Multiplayer plain (no WML) map pack.
Definition: validation.hpp:109
#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:658
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:836
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:760
void uninstall_addon(const addon_info &addon)
Definition: manager.cpp:781
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:105
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:106
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:157
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:60
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:272
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
Definition: client.cpp:134
Contains the outcome of an add-on install operation.
Definition: client.hpp:124
version_info current_version
Definition: info.hpp:82
void set_addon_manager_saved_order_direction(sort_order::type value)
Definition: general.cpp:1011
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:566
Single-player campaign.
Definition: validation.hpp:104
void set_addon_manager_saved_order_name(const std::string &value)
Definition: general.cpp:1001
bool use_twelve_hour_clock_format()
Definition: general.cpp:971
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:117
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:107
#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:106
std::string tooltip
Shown when hovering over an entry in the filter&#39;s drop-down list.
Definition: manager.cpp:219
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:198
Version in the server matches local installation.
Definition: state.hpp:26
std::string description_translated() const
Definition: info.cpp:213