The Battle for Wesnoth  1.17.0-dev
manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "addon/info.hpp"
20 #include "addon/manager.hpp"
21 #include "addon/state.hpp"
22 
23 #include "desktop/clipboard.hpp"
24 #include "desktop/open.hpp"
25 
26 #include "help/help.hpp"
27 #include "gettext.hpp"
28 #include "gui/auxiliary/filter.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/pane.hpp"
43 #include "gui/widgets/settings.hpp"
45 #include "gui/widgets/text_box.hpp"
46 #include "gui/widgets/window.hpp"
48 #include "formula/string_utils.hpp"
49 #include "picture.hpp"
50 #include "language.hpp"
51 #include "preferences/general.hpp"
52 #include "utils/general.hpp"
53 
54 #include "config.hpp"
55 
56 #include <functional>
57 
58 #include <iomanip>
59 #include <sstream>
60 #include <stdexcept>
61 
62 namespace gui2::dialogs
63 {
64 
65 namespace {
66  struct filter_transform
67  {
68  explicit filter_transform(const std::vector<std::string>& filtertext) : filtertext_(filtertext) {}
69  bool operator()(const config& cfg) const
70  {
71  for(const auto& filter : filtertext_)
72  {
73  bool found = false;
74  for(const auto& attribute : cfg.attribute_range())
75  {
76  std::string val = attribute.second.str();
77  if(std::search(val.begin(),
78  val.end(),
79  filter.begin(),
80  filter.end(),
82  != val.end())
83  {
84  found = true;
85  break;
86  }
87  }
88  for(const config& child : cfg.child_range("translation")) {
89  for(const auto& attribute : child.attribute_range()) {
90  std::string val = attribute.second.str();
91  if(translation::ci_search(val, filter)) {
92  found = true;
93  break;
94  }
95  }
96  }
97  if(!found) {
98  return false;
99  }
100  }
101  return true;
102  }
103  const std::vector<std::string> filtertext_;
104  };
105 
106  std::string make_display_dependencies(
107  const std::string& addon_id,
108  const addons_list& addons_list,
109  const addons_tracking_list& addon_states)
110  {
111  const addon_info& addon = addons_list.at(addon_id);
112  std::string str;
113 
114  const std::set<std::string>& deps = addon.resolve_dependencies(addons_list);
115 
116  for(const auto& dep_id : deps) {
117  addon_info dep;
118  addon_tracking_info depstate;
119 
120  addons_list::const_iterator ali = addons_list.find(dep_id);
121  addons_tracking_list::const_iterator tli = addon_states.find(dep_id);
122 
123  if(ali == addons_list.end()) {
124  dep.id = dep_id; // Build dummy addon_info.
125  } else {
126  dep = ali->second;
127  }
128 
129  if(tli == addon_states.end()) {
130  depstate = get_addon_tracking_info(dep);
131  } else {
132  depstate = tli->second;
133  }
134 
135  if(!str.empty()) {
136  str += ", ";
137  }
138 
140  }
141 
142  return str;
143  }
144 
145  std::string langcode_to_string(const std::string& lcode)
146  {
147  for(const auto & ld : get_languages())
148  {
149  if(ld.localename == lcode || ld.localename.substr(0, 2) == lcode) {
150  return ld.language;
151  }
152  }
153 
154  return "";
155  }
156 }
157 
158 REGISTER_DIALOG(addon_manager)
159 
160 const std::vector<std::pair<ADDON_STATUS_FILTER, std::string>> addon_manager::status_filter_types_{
161  {FILTER_ALL, N_("addons_view^All Add-ons")},
162  {FILTER_INSTALLED, N_("addons_view^Installed")},
163  {FILTER_UPGRADABLE, N_("addons_view^Upgradable")},
164  {FILTER_PUBLISHABLE, N_("addons_view^Publishable")},
165  {FILTER_NOT_INSTALLED, N_("addons_view^Not Installed")},
166 };
167 
168 const std::vector<std::pair<ADDON_TYPE, std::string>> addon_manager::type_filter_types_{
169  {ADDON_SP_CAMPAIGN, N_("addons_of_type^Campaigns")},
170  {ADDON_SP_SCENARIO, N_("addons_of_type^Scenarios")},
171  {ADDON_SP_MP_CAMPAIGN, N_("addons_of_type^SP/MP campaigns")},
172  {ADDON_MP_CAMPAIGN, N_("addons_of_type^MP campaigns")},
173  {ADDON_MP_SCENARIO, N_("addons_of_type^MP scenarios")},
174  {ADDON_MP_MAPS, N_("addons_of_type^MP map-packs")},
175  {ADDON_MP_ERA, N_("addons_of_type^MP eras")},
176  {ADDON_MP_FACTION, N_("addons_of_type^MP factions")},
177  {ADDON_MOD, N_("addons_of_type^Modifications")},
178  {ADDON_CORE, N_("addons_of_type^Cores")},
179  {ADDON_MEDIA, N_("addons_of_type^Resources")},
180  // FIXME: (also in WML) should this and Unknown be a single option in the UI?
181  {ADDON_OTHER, N_("addons_of_type^Other")},
182  {ADDON_UNKNOWN, N_("addons_of_type^Unknown")},
183 };
184 
185 const std::vector<addon_manager::addon_order> addon_manager::all_orders_{
186  {N_("addons_order^Name ($order)"), "name", 0,
187  [](const addon_info& a, const addon_info& b) { return a.title < b.title; },
188  [](const addon_info& a, const addon_info& b) { return a.title > b.title; }},
189  {N_("addons_order^Author ($order)"), "author", 1,
190  [](const addon_info& a, const addon_info& b) { return a.author < b.author; },
191  [](const addon_info& a, const addon_info& b) { return a.author > b.author; }},
192  {N_("addons_order^Size ($order)"), "size", 2,
193  [](const addon_info& a, const addon_info& b) { return a.size < b.size; },
194  [](const addon_info& a, const addon_info& b) { return a.size > b.size; }},
195  {N_("addons_order^Downloads ($order)"), "downloads", 3,
196  [](const addon_info& a, const addon_info& b) { return a.downloads < b.downloads; },
197  [](const addon_info& a, const addon_info& b) { return a.downloads > b.downloads; }},
198  {N_("addons_order^Type ($order)"), "type", 4,
199  [](const addon_info& a, const addon_info& b) { return a.display_type() < b.display_type(); },
200  [](const addon_info& a, const addon_info& b) { return a.display_type() > b.display_type(); }},
201  {N_("addons_order^Last updated ($datelike_order)"), "last_updated", -1,
202  [](const addon_info& a, const addon_info& b) { return a.updated < b.updated; },
203  [](const addon_info& a, const addon_info& b) { return a.updated > b.updated; }},
204  {N_("addons_order^First uploaded ($datelike_order)"), "first_uploaded", -1,
205  [](const addon_info& a, const addon_info& b) { return a.created < b.created; },
206  [](const addon_info& a, const addon_info& b) { return a.created > b.created; }}
207 };
208 
209 namespace
210 {
211 struct addon_tag
212 {
213  /** Text to match against addon_info.tags() */
214  std::string id;
215  /** What to show in the filter's drop-down list */
216  std::string label;
217  /** Shown when hovering over an entry in the filter's drop-down list */
218  std::string tooltip;
219 };
220 
221 const std::vector<addon_tag> tag_filter_types_{
222  {"cooperative", N_("addon_tag^Cooperative"),
223  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
224  N_("addon_tag^All human players are on the same team, versus the AI")},
225  {"cosmetic", N_("addon_tag^Cosmetic"),
226  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
227  N_("addon_tag^These make the game look different, without changing gameplay")},
228  {"difficulty", N_("addon_tag^Difficulty"),
229  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
230  N_("addon_tag^Can make campaigns easier or harder")},
231  {"rng", N_("addon_tag^RNG"),
232  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
233  N_("addon_tag^Modify the randomness in the combat mechanics, or remove it entirely")},
234  {"survival", N_("addon_tag^Survival"),
235  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
236  N_("addon_tag^Fight against waves of enemies")},
237  {"terraforming", N_("addon_tag^Terraforming"),
238  // TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
239  N_("addon_tag^Players can change the terrain")},
240 };
241 };
242 
244  : orders_()
245  , cfg_()
246  , client_(client)
247  , addons_()
248  , tracking_info_()
249  , need_wml_cache_refresh_(false)
250 {
251 }
252 
253 static std::string describe_status_verbose(const addon_tracking_info& state)
254 {
255  std::string s;
256 
257  utils::string_map i18n_symbols {{"local_version", state.installed_version.str()}};
258 
259  switch(state.state) {
260  case ADDON_NONE:
261  s = !state.can_publish
262  ? _("addon_state^Not installed")
263  : _("addon_state^Published, not installed");
264  break;
265  case ADDON_INSTALLED:
266  s = !state.can_publish
267  ? _("addon_state^Installed")
268  : _("addon_state^Published");
269  break;
270  case ADDON_NOT_TRACKED:
271  s = !state.can_publish
272  ? _("addon_state^Installed, not tracking local version")
273  // Published add-ons often don't have local status information,
274  // hence untracked. This should be considered normal.
275  : _("addon_state^Published, not tracking local version");
276  break;
278  const std::string vstr = !state.can_publish
279  ? _("addon_state^Installed ($local_version|), upgradable")
280  : _("addon_state^Published ($local_version| installed), upgradable");
281 
282  s = utils::interpolate_variables_into_string(vstr, &i18n_symbols);
283  } break;
285  const std::string vstr = !state.can_publish
286  ? _("addon_state^Installed ($local_version|), outdated on server")
287  : _("addon_state^Published ($local_version| installed), outdated on server");
288 
289  s = utils::interpolate_variables_into_string(vstr, &i18n_symbols);
290  } break;
292  s = !state.can_publish
293  ? _("addon_state^Installed, not ready to publish")
294  : _("addon_state^Ready to publish");
295  break;
297  s = !state.can_publish
298  ? _("addon_state^Installed, broken")
299  : _("addon_state^Published, broken");
300  break;
301  default:
302  s = _("addon_state^Unknown");
303  }
304 
306 }
307 
309 {
310  window.set_escape_disabled(true);
311 
312  stacked_widget& addr_info = find_widget<stacked_widget>(&window, "server_conn_info", false);
313  grid* addr_visible;
314 
315  if(client_.using_tls()) {
316  addr_info.select_layer(1);
317  addr_visible = addr_info.get_layer_grid(1);
318  } else {
319  addr_info.select_layer(0);
320  addr_visible = addr_info.get_layer_grid(0);
321  }
322 
323  if(addr_visible) {
324  auto addr_box = dynamic_cast<styled_widget*>(addr_visible->find("server_addr", false));
325  if(addr_box) {
326  addr_box->set_label(client_.addr());
327  }
328  }
329 
330  addon_list& list = find_widget<addon_list>(&window, "addons", false);
331 
332  text_box& filter = find_widget<text_box>(&window, "filter", false);
334 
336  this, std::placeholders::_1));
338  this, std::placeholders::_1));
340  this, std::placeholders::_1));
341 
343  this, std::placeholders::_1));
345  this, std::placeholders::_1));
346 
347  list.set_modified_signal_handler([this]() { on_addon_select(); });
348 
350  load_addon_list();
351 
352  menu_button& status_filter = find_widget<menu_button>(&window, "install_status_filter", false);
353 
354  std::vector<config> status_filter_entries;
355  for(const auto& f : status_filter_types_) {
356  status_filter_entries.emplace_back("label", t_string(f.second, GETTEXT_DOMAIN));
357  }
358 
359  status_filter.set_values(status_filter_entries);
360 
361  connect_signal_notify_modified(status_filter,
362  std::bind(&addon_manager::apply_filters, this));
363 
364  // The tag filter
365  auto& tag_filter = find_widget<multimenu_button>(&window, "tag_filter", false);
366 
367  std::vector<config> tag_filter_entries;
368  for(const auto& f : tag_filter_types_) {
369  tag_filter_entries.emplace_back("label", t_string(f.label, GETTEXT_DOMAIN), "checkbox", false);
370  if(!f.tooltip.empty()) {
371  tag_filter_entries.back()["tooltip"] = t_string(f.tooltip, GETTEXT_DOMAIN);
372  }
373  }
374 
375  tag_filter.set_values(tag_filter_entries);
376 
377  connect_signal_notify_modified(tag_filter, std::bind(&addon_manager::apply_filters, this));
378 
379  // The type filter
380  multimenu_button& type_filter = find_widget<multimenu_button>(&window, "type_filter", false);
381 
382  std::vector<config> type_filter_entries;
383  for(const auto& f : type_filter_types_) {
384  type_filter_entries.emplace_back("label", t_string(f.second, GETTEXT_DOMAIN), "checkbox", false);
385  }
386 
387  type_filter.set_values(type_filter_entries);
388 
389  connect_signal_notify_modified(type_filter,
390  std::bind(&addon_manager::apply_filters, this));
391 
392  // Sorting order
393  menu_button& order_dropdown = find_widget<menu_button>(&window, "order_dropdown", false);
394 
395  std::vector<config> order_dropdown_entries;
396  for(const auto& f : all_orders_) {
397  utils::string_map symbols;
398 
399  symbols["order"] = _("ascending");
400  // TRANSLATORS: Sorting order of dates, oldest first
401  symbols["datelike_order"] = _("oldest to newest");
402  config entry{"label", VGETTEXT(f.label.c_str(), symbols)};
403  order_dropdown_entries.push_back(entry);
404  symbols["order"] = _("descending");
405  // TRANSLATORS: Sorting order of dates, newest first
406  symbols["datelike_order"] = _("newest to oldest");
407  entry["label"] = VGETTEXT(f.label.c_str(), symbols);
408  order_dropdown_entries.push_back(entry);
409  }
410 
411  order_dropdown.set_values(order_dropdown_entries);
412  {
413  const std::string saved_order_name = preferences::addon_manager_saved_order_name();
414  const preferences::SORT_ORDER saved_order_direction =
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 == preferences::SORT_ORDER::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 
496  std::placeholders::_1, std::placeholders::_2));
497 
498  // Use handle the special addon_list retval to allow installing addons on double click
499  window.set_exit_hook(std::bind(&addon_manager::exit_hook, this, std::placeholders::_1));
500 }
501 
503 {
504  if(stk.current_layer() == 0) {
505  btn.set_label(_("addons^Back to List"));
506  stk.select_layer(1);
507  } else {
508  btn.set_label(_("Add-on Details"));
509  stk.select_layer(0);
510  }
511 }
512 
514 {
516  if(!cfg_) {
517  gui2::show_error_message(_("An error occurred while downloading the add-ons list from the server."));
518  get_window()->close();
519  }
520 }
521 
523 {
526  }
527 
529 
530  std::vector<std::string> publishable_addons = available_addons();
531 
532  for(std::string id : publishable_addons) {
533  if(addons_.find(id) == addons_.end()) {
534  // Get a config from the addon's pbl file
535  // Note that the name= key is necessary or stuff breaks, since the filter code uses this key
536  // to match add-ons in the config list. It also fills in addon_info's id field. It's also
537  // neccessay to set local_only here so that flag can be properly set after addons_ is cleared
538  // and recreated by read_addons_list.
539  config pbl_cfg = get_addon_pbl_info(id);
540  pbl_cfg["name"] = id;
541  pbl_cfg["local_only"] = true;
542 
543  // Add the add-on to the list.
544  addon_info addon(pbl_cfg);
545  addons_[id] = addon;
546 
547  // Add the addon to the config entry
548  cfg_.add_child("campaign", std::move(pbl_cfg));
549  }
550  }
551 
552  if(addons_.empty()) {
553  show_transient_message(_("No Add-ons Available"), _("There are no add-ons available for download from this server."));
554  }
555 
556  addon_list& list = find_widget<addon_list>(get_window(), "addons", false);
557  list.set_addons(addons_);
558 
559  bool has_upgradable_addons = false;
560  for(const auto& a : addons_) {
561  tracking_info_[a.first] = get_addon_tracking_info(a.second);
562 
563  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
564  has_upgradable_addons = true;
565  }
566  }
567 
568  find_widget<button>(get_window(), "update_all", false).set_active(has_upgradable_addons);
569 
570  apply_filters();
571 }
572 
574 {
575  load_addon_list();
576 
577  // Reselect the add-on.
578  find_widget<addon_list>(get_window(), "addons", false).select_addon(id);
579  on_addon_select();
580 }
581 
582 boost::dynamic_bitset<> addon_manager::get_name_filter_visibility() const
583 {
584  const text_box& name_filter = find_widget<const text_box>(get_window(), "filter", false);
585  const std::string& text = name_filter.get_value();
586 
587  filter_transform filter(utils::split(text, ' '));
588  boost::dynamic_bitset<> res;
589 
590  const config::const_child_itors& addon_cfgs = cfg_.child_range("campaign");
591 
592  for(const auto& a : addons_)
593  {
594  const config& addon_cfg = *std::find_if(addon_cfgs.begin(), addon_cfgs.end(),
595  [&a](const config& cfg)
596  {
597  return cfg["name"] == a.first;
598  });
599 
600  res.push_back(filter(addon_cfg));
601  }
602 
603  return res;
604 }
605 
606 boost::dynamic_bitset<> addon_manager::get_status_filter_visibility() const
607 {
608  const menu_button& status_filter = find_widget<const menu_button>(get_window(), "install_status_filter", false);
609  const ADDON_STATUS_FILTER selection = status_filter_types_[status_filter.get_value()].first;
610 
611  boost::dynamic_bitset<> res;
612  for(const auto& a : addons_) {
613  const addon_tracking_info& info = tracking_info_.at(a.second.id);
614 
615  res.push_back(
616  (selection == FILTER_ALL) ||
617  (selection == FILTER_INSTALLED && is_installed_addon_status(info.state)) ||
618  (selection == FILTER_UPGRADABLE && info.state == ADDON_INSTALLED_UPGRADABLE) ||
619  (selection == FILTER_PUBLISHABLE && info.can_publish == true) ||
620  (selection == FILTER_NOT_INSTALLED && info.state == ADDON_NONE)
621  );
622  }
623 
624  return res;
625 }
626 
627 boost::dynamic_bitset<> addon_manager::get_tag_filter_visibility() const
628 {
629  const auto& tag_filter = find_widget<const multimenu_button>(get_window(), "tag_filter", false);
630  const auto toggle_states = tag_filter.get_toggle_states();
631  if(toggle_states.none()) {
632  // Nothing selected. It means that all add-ons are shown.
633  boost::dynamic_bitset<> res_flipped(addons_.size());
634  return ~res_flipped;
635  }
636 
637  std::vector<std::string> selected_tags;
638  for(std::size_t i = 0; i < tag_filter_types_.size(); ++i) {
639  if(toggle_states[i]) {
640  selected_tags.push_back(tag_filter_types_[i].id);
641  }
642  }
643 
644  boost::dynamic_bitset<> res;
645  for(const auto& a : addons_) {
646  bool matched_tag = false;
647  for(const auto& id : selected_tags) {
648  if(utils::contains(a.second.tags, id)) {
649  matched_tag = true;
650  break;
651  }
652  }
653  res.push_back(matched_tag);
654  }
655 
656  return res;
657 }
658 
659 boost::dynamic_bitset<> addon_manager::get_type_filter_visibility() const
660 {
661  const multimenu_button& type_filter = find_widget<const multimenu_button>(get_window(), "type_filter", false);
662 
663  boost::dynamic_bitset<> toggle_states = type_filter.get_toggle_states();
664  if(toggle_states.none()) {
665  // Nothing selected. It means that *all* add-ons are shown.
666  boost::dynamic_bitset<> res_flipped(addons_.size());
667  return ~res_flipped;
668  } else {
669  boost::dynamic_bitset<> res;
670 
671  for(const auto& a : addons_) {
672  int index = std::distance(type_filter_types_.begin(),
673  std::find_if(type_filter_types_.begin(), type_filter_types_.end(),
674  [&a](const std::pair<ADDON_TYPE, std::string>& entry) {
675  return entry.first == a.second.type;
676  })
677  );
678  res.push_back(toggle_states[index]);
679  }
680 
681  return res;
682  }
683 }
684 
686 {
687  // In the small-screen layout, the text_box for the filter keeps keyboard focus even when the
688  // details panel is visible, which means this can be called when the list isn't visible. That
689  // causes problems both because find_widget can throw exceptions, but also because changing the
690  // filters can trigger on_addon_select and thus affect which add-on's details are shown.
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  preferences::SORT_ORDER order = order_menu.get_value() % 2 == 0 ? preferences::SORT_ORDER::ASCENDING : preferences::SORT_ORDER::DESCENDING;
712  if(order == preferences::SORT_ORDER::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, preferences::SORT_ORDER 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 == preferences::SORT_ORDER::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  // the passphrase isn't provided, prompt for it
856  if(cfg["passphrase"].empty()) {
857  if(!gui2::dialogs::addon_auth::execute(cfg)) {
858  return;
859  }
860  }
861 
862  if(!::image::exists(cfg["icon"].str())) {
863  gui2::show_error_message(_("Invalid icon path. Make sure the path points to a valid image."));
864  } else if(!client_.request_distribution_terms(server_msg)) {
866  _("The server responded with an error:") + "\n" + client_.get_last_server_error());
867  } else if(gui2::dialogs::addon_license_prompt::execute(server_msg)) {
868  if(!client_.upload_addon(addon_id, server_msg, cfg, tracking_info_[addon_id].state == ADDON_INSTALLED_LOCAL_ONLY)) {
869  const std::string& msg = _("The add-on was rejected by the server:") +
870  "\n\n" + client_.get_last_server_error();
871  const std::string& extra_data = client_.get_last_server_error_data();
872  if (!extra_data.empty()) {
873  // TODO: Allow user to copy the extra data portion to clipboard
874  // or something, maybe display it in a dialog similar to
875  // the WML load errors report in a monospace font and
876  // stuff (having a scroll container is especially
877  // important since a long list can cause the dialog to
878  // overflow).
879  gui2::show_error_message(msg + "\n\n" + extra_data, true);
880  } else {
881  gui2::show_error_message(msg, true);
882  }
883  } else {
884  gui2::show_transient_message(_("Response"), server_msg);
887  }
888  }
889 }
890 
891 /** Performs all backend and UI actions for taking down the specified add-on. */
893 {
894  const std::string addon_id = addon.id;
895  const std::string& text = VGETTEXT(
896  "Deleting '$addon|' will permanently erase its download and upload counts on the add-ons server. Do you really wish to continue?",
897  {{"addon", make_addon_title(addon_id)}} // FIXME: need the real title!
898  );
899 
900  const int res = gui2::show_message(_("Confirm"), text, gui2::dialogs::message::yes_no_buttons);
901 
902  if(res != gui2::retval::OK) {
903  return;
904  }
905 
906  std::string server_msg;
907  if(!client_.delete_remote_addon(addon_id, server_msg)) {
908  gui2::show_error_message(_("The server responded with an error:") + "\n" + client_.get_last_server_error());
909  } else {
910  // FIXME: translation needed!
911  gui2::show_transient_message(_("Response"), server_msg);
914  }
915 }
916 
917 /** Called when the player double-clicks an add-on. */
919 {
920  switch(tracking_info_[addon.id].state) {
921  case ADDON_NONE:
922  install_addon(addon);
923  break;
924  case ADDON_INSTALLED:
925  if(!tracking_info_[addon.id].can_publish) {
926  utils::string_map symbols{ { "addon", addon.display_title_full() } };
927  int res = gui2::show_message(_("Uninstall add-on"),
928  VGETTEXT("Do you want to uninstall '$addon|'?", symbols),
930  if(res == gui2::retval::OK) {
931  uninstall_addon(addon);
932  }
933  }
934  break;
936  update_addon(addon);
937  break;
940  publish_addon(addon);
941  break;
942  default:
943  break;
944  }
945 }
946 
948 {
949  help::show_help("installing_addons");
950 }
951 
952 static std::string format_addon_time(std::time_t time)
953 {
954  if(time) {
955  std::ostringstream ss;
956 
958  ? "%Y-%m-%d %I:%M %p"
959  : "%Y-%m-%d %H:%M";
960 
961  ss << std::put_time(std::localtime(&time), format);
962 
963  return ss.str();
964  }
965 
966  return font::unicode_em_dash;
967 }
968 
970 {
971  const addon_info* info = find_widget<addon_list>(get_window(), "addons", false).get_selected_addon();
972 
973  if(info == nullptr) {
974  return;
975  }
976 
977  widget* parent = get_window();
978  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
979  parent = stk->get_layer_grid(1);
980  }
981 
982  find_widget<drawing>(parent, "image", false).set_label(info->display_icon());
983 
984  find_widget<styled_widget>(parent, "title", false).set_label(info->display_title_translated_or_original());
985  find_widget<styled_widget>(parent, "description", false).set_label(info->description_translated());
986  menu_button& version_filter = find_widget<menu_button>(parent, "version_filter", false);
987  find_widget<styled_widget>(parent, "author", false).set_label(info->author);
988  find_widget<styled_widget>(parent, "type", false).set_label(info->display_type());
989 
990  styled_widget& status = find_widget<styled_widget>(parent, "status", false);
992  status.set_use_markup(true);
993 
994  find_widget<styled_widget>(parent, "size", false).set_label(size_display_string(info->size));
995  find_widget<styled_widget>(parent, "downloads", false).set_label(std::to_string(info->downloads));
996  find_widget<styled_widget>(parent, "created", false).set_label(format_addon_time(info->created));
997  find_widget<styled_widget>(parent, "updated", false).set_label(format_addon_time(info->updated));
998 
999  // Although this is a user-visible string, use utils::join instead of format_conjunct_list
1000  // because "x, y, z" is clearer than "x, y and z" in this context.
1001  find_widget<styled_widget>(parent, "tags", false).set_label(utils::join(info->tags, ", "));
1002 
1003  find_widget<styled_widget>(parent, "dependencies", false).set_label(!info->depends.empty()
1004  ? make_display_dependencies(info->id, addons_, tracking_info_)
1005  : _("addon_dependencies^None"));
1006 
1007  std::string languages;
1008 
1009  for(const auto& lc : info->locales) {
1010  const std::string& langlabel = langcode_to_string(lc);
1011  if(!langlabel.empty()) {
1012  if(!languages.empty()) {
1013  languages += ", ";
1014  }
1015  languages += langlabel;
1016  }
1017  }
1018 
1019  find_widget<styled_widget>(parent, "translations", false).set_label(!languages.empty() ? languages : _("translations^None"));
1020 
1021  const std::string& feedback_url = info->feedback_url;
1022  find_widget<label>(parent, "url", false).set_label(!feedback_url.empty() ? feedback_url : _("url^None"));
1023 
1024  bool installed = is_installed_addon_status(tracking_info_[info->id].state);
1025  bool updatable = tracking_info_[info->id].state == ADDON_INSTALLED_UPGRADABLE;
1026 
1027  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1028  // #TODO: Add tooltips with upload time and pack size
1029  std::vector<config> version_filter_entries;
1030 
1031  if(!tracking_info_[info->id].can_publish) {
1032  action_stack.select_layer(0);
1033 
1034  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1035  install_update_stack.select_layer(updatable ? 1 : 0);
1036 
1037  if(!updatable) {
1038  find_widget<button>(parent, "install", false).set_active(!installed);
1039  } else {
1040  find_widget<button>(parent, "update", false).set_active(true);
1041  }
1042 
1043  find_widget<button>(parent, "uninstall", false).set_active(installed);
1044 
1045  for(const auto& f : info->versions) {
1046  version_filter_entries.emplace_back("label", f.str());
1047  }
1048  version_filter.set_active(true);
1049  } else {
1050  action_stack.select_layer(1);
1051 
1052  // Always enable the publish button, but disable the delete button if not yet published.
1053  find_widget<button>(parent, "publish", false).set_active(true);
1054  find_widget<button>(parent, "delete", false).set_active(!info->local_only);
1055 
1056  // Show only the version to be published
1057  version_filter_entries.emplace_back("label", info->current_version.str());
1058  version_filter.set_active(false);
1059  }
1060  version_filter.set_values(version_filter_entries);
1061 }
1062 
1064 {
1065  widget* parent = get_window();
1066  widget* parent_of_addons_list = parent;
1067  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
1068  parent = stk->get_layer_grid(1);
1069  parent_of_addons_list = stk->get_layer_grid(0);
1070  }
1071 
1072  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
1073 
1074  if(info == nullptr) {
1075  return;
1076  }
1077 
1078  if(!tracking_info_[info->id].can_publish && is_installed_addon_status(tracking_info_[info->id].state)) {
1079  bool updatable = tracking_info_[info->id].installed_version
1080  != find_widget<menu_button>(parent, "version_filter", false).get_value_string();
1081  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1082  action_stack.select_layer(0);
1083 
1084  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1085  install_update_stack.select_layer(1);
1086  find_widget<button>(parent, "update", false).set_active(updatable);
1087  }
1088 }
1089 
1091 {
1094  return false;
1095  }
1096 
1097  return true;
1098 }
1099 
1100 } // namespace dialogs
int size
Definition: info.hpp:86
const std::string & get_last_server_error() const
Returns the last error message sent by the server, or an empty string.
Definition: client.hpp:75
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:293
void close()
Requests to close the window.
Definition: window.hpp:181
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:56
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:152
std::string addon_manager_saved_order_name()
Definition: general.cpp:958
Modification of the game.
Definition: validation.hpp:108
Single-player scenario.
Definition: validation.hpp:100
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:143
config cfg_
Config which contains the list with the campaigns.
Definition: manager.hpp:97
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:72
std::map< std::string, t_string > string_map
boost::dynamic_bitset get_status_filter_visibility() const
Definition: manager.cpp:606
bool exit_hook(window &window)
Definition: manager.cpp:1090
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:111
void set_addon_manager_saved_order_direction(SORT_ORDER value)
Definition: general.cpp:973
static std::string format_addon_time(std::time_t time)
Definition: manager.cpp:952
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:60
SORT_ORDER addon_manager_saved_order_direction()
Definition: general.cpp:968
Total Conversion Core.
Definition: validation.hpp:98
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
addons_tracking_list tracking_info_
Definition: manager.hpp:103
bool using_tls() const
Returns whether the current connection uses TLS.
Definition: client.hpp:214
preferences::SORT_ORDER SORT_ORDER
Definition: listbox.cpp:41
std::string display_title_translated_or_original() const
Definition: info.cpp:206
grid * get_layer_grid(unsigned int i)
Gets the grid for a specified layer.
logger & info()
Definition: log.cpp:87
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:75
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:359
void reload_list_and_reselect_item(const std::string id)
Definition: manager.cpp:573
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:918
Base class for all widgets.
Definition: widget.hpp:48
int current_layer() const
Gets the current visible layer number.
Multiplayer faction.
Definition: validation.hpp:106
virtual void set_value(unsigned value, bool fire_event=false) override
Inherited from selectable_item.
Definition: menu_button.hpp:86
std::vector< std::string > tags
Definition: info.hpp:92
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:298
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:109
static const std::vector< std::pair< ADDON_STATUS_FILTER, std::string > > status_filter_types_
Definition: manager.hpp:105
A label displays a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:56
A multimenu_button is a styled_widget to choose an element from a list of elements.
std::string feedback_url
Definition: info.hpp:100
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:52
static std::string _(const char *str)
Definition: gettext.hpp:92
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:59
std::deque< std::unique_ptr< editor_action > > action_stack
Action stack typedef.
No tracking information available.
Definition: state.hpp:36
const_attr_itors attribute_range() const
Definition: config.cpp:837
Add-on is not installed.
Definition: state.hpp:23
void update_addon(const addon_info &addon)
Definition: manager.cpp:801
Class for a single line text area.
Definition: text_box.hpp:140
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
Definition: manager.cpp:171
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:103
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:1010
Base container class.
Definition: grid.hpp:30
Desktop environment interaction functions.
virtual unsigned get_value() const override
Inherited from selectable_item.
Definition: menu_button.hpp:83
boost::dynamic_bitset get_name_filter_visibility() const
Definition: manager.cpp:582
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:29
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:186
bool is_installed_addon_status(ADDON_STATUS s)
Definition: state.hpp:39
std::string label
What to show in the filter&#39;s drop-down list.
Definition: manager.cpp:216
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:83
boost::dynamic_bitset get_tag_filter_visibility() const
Definition: manager.cpp:627
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:171
std::string display_icon() const
Get an icon path fixed for display (e.g.
Definition: info.cpp:231
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:78
std::string id
Definition: info.hpp:75
Version in the server is newer than local installation.
Definition: state.hpp:27
language_list get_languages(bool all)
Return a list of available translations.
Definition: language.cpp:126
void toggle_details(button &btn, stacked_widget &stk)
Definition: manager.cpp:502
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:248
Shows a yes and no button.
Definition: message.hpp:79
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:354
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:128
Dependencies not satisfied.
Definition: state.hpp:34
Add-ons (campaignd) client class.
Definition: client.hpp:38
int get_retval()
Definition: window.hpp:364
config get_addon_pbl_info(const std::string &addon_name)
Gets the publish information for an add-on.
Definition: manager.cpp:66
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:110
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:204
static std::string describe_status_verbose(const addon_tracking_info &state)
Definition: manager.cpp:253
bool ci_search(const std::string &s1, const std::string &s2)
Definition: gettext.cpp:515
void on_order_changed(unsigned int sort_column, preferences::SORT_ORDER order)
Definition: manager.cpp:723
bool local_only
Definition: info.hpp:107
std::time_t created
Definition: info.hpp:103
std::vector< std::string > locales
Definition: info.hpp:93
Multiplayer scenario.
Definition: validation.hpp:103
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:214
boost::dynamic_bitset get_toggle_states() const
Get the current state of the menu options.
Multiplayer era.
Definition: validation.hpp:105
void delete_addon(const addon_info &addon)
Performs all backend and UI actions for taking down the specified add-on.
Definition: manager.cpp:892
std::size_t i
Definition: function.cpp:940
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:61
addons_client & client_
Definition: manager.hpp:99
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:310
addon_manager(addons_client &client)
Definition: manager.cpp:243
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:102
void execute_default_action_on_selected_addon()
Definition: manager.hpp:145
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:223
Multiplayer plain (no WML) map pack.
Definition: validation.hpp:104
#define N_(String)
Definition: gettext.hpp:100
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:69
No version in the server.
Definition: state.hpp:31
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:127
std::set< version_info, std::greater< version_info > > versions
Definition: info.hpp:82
void select_layer(const int layer)
Selects and displays a particular layer.
boost::dynamic_bitset get_type_filter_visibility() const
Definition: manager.cpp:659
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:503
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:319
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:106
std::vector< std::string > depends
Definition: info.hpp:97
#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:24
Hybrid campaign.
Definition: validation.hpp:101
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:136
std::map< std::string, addon_tracking_info > addons_tracking_list
Definition: state.hpp:63
const std::string unicode_em_dash
Definition: constants.cpp:43
std::string title
Definition: info.hpp:76
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:280
Simple push button.
Definition: button.hpp:35
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:152
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:205
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:45
Dialog was closed with the OK button.
Definition: retval.hpp:34
A stacked widget holds several widgets on top of each other.
int downloads
Definition: info.hpp:87
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
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:262
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
Definition: client.cpp:129
Contains the outcome of an add-on install operation.
Definition: client.hpp:122
version_info current_version
Definition: info.hpp:81
std::map< std::string, addon_info > addons_list
Definition: info.hpp:27
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:64
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:553
Single-player campaign.
Definition: validation.hpp:99
void set_addon_manager_saved_order_name(const std::string &value)
Definition: general.cpp:963
bool use_twelve_hour_clock_format()
Definition: general.cpp:933
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:308
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
Definition: client.cpp:112
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:102
#define GETTEXT_DOMAIN
Definition: manager.cpp:15
std::string author
Definition: info.hpp:84
void set_link_aware(bool l)
Definition: label.cpp:90
static const std::vector< addon_order > all_orders_
Definition: manager.hpp:107
std::string tooltip
Shown when hovering over an entry in the filter&#39;s drop-down list.
Definition: manager.cpp:218
Version in the server matches local installation.
Definition: state.hpp:25
std::string description_translated() const
Definition: info.cpp:212