The Battle for Wesnoth  1.17.8+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  : modal_dialog(window_id())
246  , orders_()
247  , cfg_()
248  , client_(client)
249  , addons_()
250  , tracking_info_()
251  , need_wml_cache_refresh_(false)
252 {
253 }
254 
255 static std::string describe_status_verbose(const addon_tracking_info& state)
256 {
257  std::string s;
258 
259  utils::string_map i18n_symbols {{"local_version", state.installed_version.str()}};
260 
261  switch(state.state) {
262  case ADDON_NONE:
263  s = !state.can_publish
264  ? _("addon_state^Not installed")
265  : _("addon_state^Published, not installed");
266  break;
267  case ADDON_INSTALLED:
268  s = !state.can_publish
269  ? _("addon_state^Installed")
270  : _("addon_state^Published");
271  break;
272  case ADDON_NOT_TRACKED:
273  s = !state.can_publish
274  ? _("addon_state^Installed, not tracking local version")
275  // Published add-ons often don't have local status information,
276  // hence untracked. This should be considered normal.
277  : _("addon_state^Published, not tracking local version");
278  break;
280  const std::string vstr = !state.can_publish
281  ? _("addon_state^Installed ($local_version|), upgradable")
282  : _("addon_state^Published ($local_version| installed), upgradable");
283 
284  s = utils::interpolate_variables_into_string(vstr, &i18n_symbols);
285  } break;
287  const std::string vstr = !state.can_publish
288  ? _("addon_state^Installed ($local_version|), outdated on server")
289  : _("addon_state^Published ($local_version| installed), outdated on server");
290 
291  s = utils::interpolate_variables_into_string(vstr, &i18n_symbols);
292  } break;
294  s = !state.can_publish
295  ? _("addon_state^Installed, not ready to publish")
296  : _("addon_state^Ready to publish");
297  break;
299  s = !state.can_publish
300  ? _("addon_state^Installed, broken")
301  : _("addon_state^Published, broken");
302  break;
303  default:
304  s = _("addon_state^Unknown");
305  }
306 
308 }
309 
311 {
312  window.set_escape_disabled(true);
313 
314  stacked_widget& addr_info = find_widget<stacked_widget>(&window, "server_conn_info", false);
315  grid* addr_visible;
316 
317  if(client_.using_tls()) {
318  addr_info.select_layer(1);
319  addr_visible = addr_info.get_layer_grid(1);
320  } else {
321  addr_info.select_layer(0);
322  addr_visible = addr_info.get_layer_grid(0);
323  }
324 
325  if(addr_visible) {
326  auto addr_box = dynamic_cast<styled_widget*>(addr_visible->find("server_addr", false));
327  if(addr_box) {
328  addr_box->set_label(client_.addr());
329  }
330  }
331 
332  addon_list& list = find_widget<addon_list>(&window, "addons", false);
333 
334  text_box& filter = find_widget<text_box>(&window, "filter", false);
336 
338  this, std::placeholders::_1));
340  this, std::placeholders::_1));
342  this, std::placeholders::_1));
343 
345  this, std::placeholders::_1));
347  this, std::placeholders::_1));
348 
349  list.set_modified_signal_handler([this]() { on_addon_select(); });
350 
352  load_addon_list();
353 
354  menu_button& status_filter = find_widget<menu_button>(&window, "install_status_filter", false);
355 
356  std::vector<config> status_filter_entries;
357  for(const auto& f : status_filter_types_) {
358  status_filter_entries.emplace_back("label", t_string(f.second, GETTEXT_DOMAIN));
359  }
360 
361  status_filter.set_values(status_filter_entries);
362 
363  connect_signal_notify_modified(status_filter,
364  std::bind(&addon_manager::apply_filters, this));
365 
366  // The tag filter
367  auto& tag_filter = find_widget<multimenu_button>(&window, "tag_filter", false);
368 
369  std::vector<config> tag_filter_entries;
370  for(const auto& f : tag_filter_types_) {
371  tag_filter_entries.emplace_back("label", t_string(f.label, GETTEXT_DOMAIN), "checkbox", false);
372  if(!f.tooltip.empty()) {
373  tag_filter_entries.back()["tooltip"] = t_string(f.tooltip, GETTEXT_DOMAIN);
374  }
375  }
376 
377  tag_filter.set_values(tag_filter_entries);
378 
379  connect_signal_notify_modified(tag_filter, std::bind(&addon_manager::apply_filters, this));
380 
381  // The type filter
382  multimenu_button& type_filter = find_widget<multimenu_button>(&window, "type_filter", false);
383 
384  std::vector<config> type_filter_entries;
385  for(const auto& f : type_filter_types_) {
386  type_filter_entries.emplace_back("label", t_string(f.second, GETTEXT_DOMAIN), "checkbox", false);
387  }
388 
389  type_filter.set_values(type_filter_entries);
390 
391  connect_signal_notify_modified(type_filter,
392  std::bind(&addon_manager::apply_filters, this));
393 
394  // Sorting order
395  menu_button& order_dropdown = find_widget<menu_button>(&window, "order_dropdown", false);
396 
397  std::vector<config> order_dropdown_entries;
398  for(const auto& f : all_orders_) {
399  utils::string_map symbols;
400 
401  symbols["order"] = _("ascending");
402  // TRANSLATORS: Sorting order of dates, oldest first
403  symbols["datelike_order"] = _("oldest to newest");
404  config entry{"label", VGETTEXT(f.label.c_str(), symbols)};
405  order_dropdown_entries.push_back(entry);
406  symbols["order"] = _("descending");
407  // TRANSLATORS: Sorting order of dates, newest first
408  symbols["datelike_order"] = _("newest to oldest");
409  entry["label"] = VGETTEXT(f.label.c_str(), symbols);
410  order_dropdown_entries.push_back(entry);
411  }
412 
413  order_dropdown.set_values(order_dropdown_entries);
414  {
415  const std::string saved_order_name = preferences::addon_manager_saved_order_name();
417 
418  if(!saved_order_name.empty()) {
419  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
420  [&saved_order_name](const addon_order& order) {return order.as_preference == saved_order_name;});
421  if(order_it != all_orders_.end()) {
422  int index = 2 * (std::distance(all_orders_.begin(), order_it));
424  if(saved_order_direction == sort_order::type::ascending) {
425  func = order_it->sort_func_asc;
426  } else {
427  func = order_it->sort_func_desc;
428  ++index;
429  }
430  find_widget<menu_button>(&window, "order_dropdown", false).set_value(index, false);
431  auto& addons = find_widget<addon_list>(&window, "addons", false);
432  addons.set_addon_order(func);
433  addons.select_first_addon();
434  }
435  }
436  }
437 
438  connect_signal_notify_modified(order_dropdown,
439  std::bind(&addon_manager::order_addons, this));
440 
441  label& url_label = find_widget<label>(&window, "url", false);
442 
443  url_label.set_use_markup(true);
444  url_label.set_link_aware(true);
445 
447  find_widget<button>(&window, "install", false),
448  std::bind(&addon_manager::install_selected_addon, this));
449 
451  find_widget<button>(&window, "uninstall", false),
452  std::bind(&addon_manager::uninstall_selected_addon, this));
453 
455  find_widget<button>(&window, "update", false),
456  std::bind(&addon_manager::update_selected_addon, this));
457 
459  find_widget<button>(&window, "publish", false),
460  std::bind(&addon_manager::publish_selected_addon, this));
461 
463  find_widget<button>(&window, "delete", false),
464  std::bind(&addon_manager::delete_selected_addon, this));
465 
467  find_widget<button>(&window, "update_all", false),
468  std::bind(&addon_manager::update_all_addons, this));
469 
471  find_widget<button>(&window, "show_help", false),
472  std::bind(&addon_manager::show_help, this));
473 
474  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
475  button& btn = find_widget<button>(&window, "details_toggle", false);
476  connect_signal_mouse_left_click(btn, std::bind(&addon_manager::toggle_details, this, std::ref(btn), std::ref(*stk)));
477  stk->select_layer(0);
478  }
479 
480  widget* version_filter_parent = &window;
481  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
482  version_filter_parent = stk->get_layer_grid(1);
483  }
484 
485  menu_button& version_filter = find_widget<menu_button>(version_filter_parent, "version_filter", false);
486  connect_signal_notify_modified(version_filter,
488 
489  on_addon_select();
490 
491  window.set_enter_disabled(true);
492 
493  window.keyboard_capture(&filter);
495 
496  list.set_callback_order_change(std::bind(&addon_manager::on_order_changed, this, 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 hide the currently-shown add-on, triggering a different one to be selected in a
691  // way that would seem random unless the user realised that they were typing into a filter box.
692  //
693  // Quick workaround is to not process the new filter if the list isn't visible.
694  auto list = find_widget<addon_list>(get_window(), "addons", false, false);
695  if(!list) {
696  return;
697  }
698 
699  boost::dynamic_bitset<> res =
704  list->set_addon_shown(res);
705 }
706 
708 {
709  const menu_button& order_menu = find_widget<const menu_button>(get_window(), "order_dropdown", false);
710  const addon_order& order_struct = all_orders_.at(order_menu.get_value() / 2);
711  sort_order::type order = order_menu.get_value() % 2 == 0 ? sort_order::type::ascending : sort_order::type::descending;
713  if(order == sort_order::type::ascending) {
714  func = order_struct.sort_func_asc;
715  } else {
716  func = order_struct.sort_func_desc;
717  }
718 
719  find_widget<addon_list>(get_window(), "addons", false).set_addon_order(func);
720  preferences::set_addon_manager_saved_order_name(order_struct.as_preference);
722 }
723 
724 void addon_manager::on_order_changed(unsigned int sort_column, sort_order::type order)
725 {
726  menu_button& order_menu = find_widget<menu_button>(get_window(), "order_dropdown", false);
727  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
728  [sort_column](const addon_order& order) {return order.column_index == static_cast<int>(sort_column);});
729  int index = 2 * (std::distance(all_orders_.begin(), order_it));
730  if(order == sort_order::type::descending) {
731  ++index;
732  }
733  order_menu.set_value(index);
734  preferences::set_addon_manager_saved_order_name(order_it->as_preference);
736 }
737 
738 template<void(addon_manager::*fptr)(const addon_info& addon)>
740 {
741  // Explicitly return to the main page if we're in low-res mode so the list is visible.
742  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
743  stk->select_layer(0);
744  find_widget<button>(get_window(), "details_toggle", false).set_label(_("Add-on Details"));
745  }
746 
747  addon_list& addons = find_widget<addon_list>(get_window(), "addons", false);
748  const addon_info* addon = addons.get_selected_addon();
749 
750  if(addon == nullptr) {
751  return;
752  }
753 
754  try {
755  (this->*fptr)(*addon);
756  } catch(const addons_client::user_exit&) {
757  // User canceled the op.
758  }
759 }
760 
762 {
763  addon_info versioned_addon = addon;
764  widget* parent = get_window();
765  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
766  parent = stk->get_layer_grid(1);
767  }
768  if(addon.id == find_widget<addon_list>(get_window(), "addons", false).get_selected_addon()->id) {
769  versioned_addon.current_version = find_widget<menu_button>(parent, "version_filter", false).get_value_string();
770  }
771 
773 
774  // Take note if any wml_changes occurred
776 
779  }
780 }
781 
783 {
784  if(have_addon_pbl_info(addon.id) || have_addon_in_vcs_tree(addon.id)) {
786  _("The following add-on appears to have publishing or version control information stored locally, and will not be removed:") + " " +
787  addon.display_title_full());
788  return;
789  }
790 
791  bool success = remove_local_addon(addon.id);
792 
793  if(!success) {
794  gui2::show_error_message(_("The following add-on could not be deleted properly:") + " " + addon.display_title_full());
795  } else {
797 
799  }
800 }
801 
803 {
804  /* Currently, the install and update codepaths are the same, so this function simply
805  * calls the other. Since this might change in the future, I'm leaving this function
806  * here for now.
807  *
808  * - vultraz, 2017-03-12
809  */
810  install_addon(addon);
811 }
812 
814 {
815  for(const auto& a : addons_) {
816  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
818 
819  if(result.wml_changed) {
820  // Updating an add-on may have resulted in its dependencies being updated
821  // as well, so we need to reread version info blocks afterwards to make sure
822  // we don't try to re-download newly updated add-ons.
824  }
825 
826  // Take note if any wml_changes occurred
828  }
829  }
830 
832  load_addon_list();
833  }
834 }
835 
836 /** Performs all backend and UI actions for publishing the specified add-on. */
838 {
839  std::string server_msg;
840 
841  const std::string addon_id = addon.id;
842  config cfg = get_addon_pbl_info(addon_id);
843 
844  const version_info& version_to_publish = cfg["version"].str();
845 
846  if(version_to_publish <= tracking_info_[addon_id].remote_version) {
847  const int res = gui2::show_message(_("Warning"),
848  _("The remote version of this add-on is greater or equal to the version being uploaded. Do you really wish to continue?"),
850 
851  if(res != gui2::retval::OK) {
852  return;
853  }
854  }
855 
856  // if the passphrase isn't provided from the _server.pbl, try to pre-populate it from the preferences before prompting for it
857  if(cfg["passphrase"].empty()) {
858  cfg["passphrase"] = preferences::password(preferences::campaign_server(), cfg["author"]);
859  if(!gui2::dialogs::addon_auth::execute(cfg)) {
860  return;
861  } else {
862  preferences::set_password(preferences::campaign_server(), cfg["author"], cfg["passphrase"]);
863  }
864  }
865 
866  if(!::image::exists(cfg["icon"].str())) {
867  gui2::show_error_message(_("Invalid icon path. Make sure the path points to a valid image."));
868  } else if(!client_.request_distribution_terms(server_msg)) {
870  _("The server responded with an error:") + "\n" + client_.get_last_server_error());
871  } else if(gui2::dialogs::addon_license_prompt::execute(server_msg)) {
872  if(!client_.upload_addon(addon_id, server_msg, cfg, tracking_info_[addon_id].state == ADDON_INSTALLED_LOCAL_ONLY)) {
873  const std::string& msg = _("The add-on was rejected by the server:") +
874  "\n\n" + client_.get_last_server_error();
875  const std::string& extra_data = client_.get_last_server_error_data();
876  if (!extra_data.empty()) {
877  // TODO: Allow user to copy the extra data portion to clipboard
878  // or something, maybe display it in a dialog similar to
879  // the WML load errors report in a monospace font and
880  // stuff (having a scroll container is especially
881  // important since a long list can cause the dialog to
882  // overflow).
883  gui2::show_error_message(msg + "\n\n" + extra_data, true);
884  } else {
885  gui2::show_error_message(msg, true);
886  }
887  } else {
888  gui2::show_transient_message(_("Response"), server_msg);
891  }
892  }
893 }
894 
895 /** Performs all backend and UI actions for taking down the specified add-on. */
897 {
898  const std::string addon_id = addon.id;
899  const std::string& text = VGETTEXT(
900  "Deleting '$addon|' will permanently erase its download and upload counts on the add-ons server. Do you really wish to continue?",
901  {{"addon", make_addon_title(addon_id)}} // FIXME: need the real title!
902  );
903 
904  const int res = gui2::show_message(_("Confirm"), text, gui2::dialogs::message::yes_no_buttons);
905 
906  if(res != gui2::retval::OK) {
907  return;
908  }
909 
910  std::string server_msg;
911  if(!client_.delete_remote_addon(addon_id, server_msg)) {
912  gui2::show_error_message(_("The server responded with an error:") + "\n" + client_.get_last_server_error());
913  } else {
914  // FIXME: translation needed!
915  gui2::show_transient_message(_("Response"), server_msg);
918  }
919 }
920 
921 /** Called when the player double-clicks an add-on. */
923 {
924  switch(tracking_info_[addon.id].state) {
925  case ADDON_NONE:
926  install_addon(addon);
927  break;
928  case ADDON_INSTALLED:
929  if(!tracking_info_[addon.id].can_publish) {
930  utils::string_map symbols{ { "addon", addon.display_title_full() } };
931  int res = gui2::show_message(_("Uninstall add-on"),
932  VGETTEXT("Do you want to uninstall '$addon|'?", symbols),
934  if(res == gui2::retval::OK) {
935  uninstall_addon(addon);
936  }
937  }
938  break;
940  update_addon(addon);
941  break;
944  publish_addon(addon);
945  break;
946  default:
947  break;
948  }
949 }
950 
952 {
953  help::show_help("installing_addons");
954 }
955 
956 static std::string format_addon_time(std::time_t time)
957 {
958  if(time) {
959  std::ostringstream ss;
960 
962  // TRANSLATORS: Month + day of month + year + 12-hour time, eg 'November 02 2021, 1:59 PM'. Format for your locale.
963  ? _("%B %d %Y, %I:%M %p")
964  // TRANSLATORS: Month + day of month + year + 24-hour time, eg 'November 02 2021, 13:59'. Format for your locale.
965  : _("%B %d %Y, %H:%M");
966 
967  ss << translation::strftime(format, std::localtime(&time));
968 
969  return ss.str();
970  }
971 
972  return font::unicode_em_dash;
973 }
974 
976 {
977  widget* parent = get_window();
978  widget* parent_of_addons_list = parent;
979  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
980  parent = stk->get_layer_grid(1);
981  parent_of_addons_list = stk->get_layer_grid(0);
982  }
983 
984  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
985 
986  if(info == nullptr) {
987  return;
988  }
989 
990  find_widget<drawing>(parent, "image", false).set_label(info->display_icon());
991 
992  find_widget<styled_widget>(parent, "title", false).set_label(info->display_title_translated_or_original());
993  find_widget<styled_widget>(parent, "description", false).set_label(info->description_translated());
994  menu_button& version_filter = find_widget<menu_button>(parent, "version_filter", false);
995  find_widget<styled_widget>(parent, "author", false).set_label(info->author);
996  find_widget<styled_widget>(parent, "type", false).set_label(info->display_type());
997 
998  styled_widget& status = find_widget<styled_widget>(parent, "status", false);
1000  status.set_use_markup(true);
1001 
1002  find_widget<styled_widget>(parent, "size", false).set_label(size_display_string(info->size));
1003  find_widget<styled_widget>(parent, "downloads", false).set_label(std::to_string(info->downloads));
1004  find_widget<styled_widget>(parent, "created", false).set_label(format_addon_time(info->created));
1005  find_widget<styled_widget>(parent, "updated", false).set_label(format_addon_time(info->updated));
1006 
1007  find_widget<styled_widget>(parent, "dependencies", false).set_label(!info->depends.empty()
1008  ? make_display_dependencies(info->id, addons_, tracking_info_)
1009  : _("addon_dependencies^None"));
1010 
1011  std::string languages;
1012 
1013  for(const auto& lc : info->locales) {
1014  const std::string& langlabel = langcode_to_string(lc);
1015  if(!langlabel.empty()) {
1016  if(!languages.empty()) {
1017  languages += ", ";
1018  }
1019  languages += langlabel;
1020  }
1021  }
1022 
1023  find_widget<styled_widget>(parent, "translations", false).set_label(!languages.empty() ? languages : _("translations^None"));
1024 
1025  const std::string& feedback_url = info->feedback_url;
1026  find_widget<label>(parent, "url", false).set_label(!feedback_url.empty() ? feedback_url : _("url^None"));
1027 
1028  bool installed = is_installed_addon_status(tracking_info_[info->id].state);
1029  bool updatable = tracking_info_[info->id].state == ADDON_INSTALLED_UPGRADABLE;
1030 
1031  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1032  // #TODO: Add tooltips with upload time and pack size
1033  std::vector<config> version_filter_entries;
1034 
1035  if(!tracking_info_[info->id].can_publish) {
1036  action_stack.select_layer(0);
1037 
1038  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1039  install_update_stack.select_layer(updatable ? 1 : 0);
1040 
1041  if(!updatable) {
1042  find_widget<button>(parent, "install", false).set_active(!installed);
1043  } else {
1044  find_widget<button>(parent, "update", false).set_active(true);
1045  }
1046 
1047  find_widget<button>(parent, "uninstall", false).set_active(installed);
1048 
1049  for(const auto& f : info->versions) {
1050  version_filter_entries.emplace_back("label", f.str());
1051  }
1052  } else {
1053  action_stack.select_layer(1);
1054 
1055  // Always enable the publish button, but disable the delete button if not yet published.
1056  find_widget<button>(parent, "publish", false).set_active(true);
1057  find_widget<button>(parent, "delete", false).set_active(!info->local_only);
1058 
1059  // Show only the version to be published
1060  version_filter_entries.emplace_back("label", info->current_version.str());
1061  }
1062 
1063  version_filter.set_values(version_filter_entries);
1064  version_filter.set_active(version_filter_entries.size() > 1);
1065 }
1066 
1068 {
1069  widget* parent = get_window();
1070  widget* parent_of_addons_list = parent;
1071  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
1072  parent = stk->get_layer_grid(1);
1073  parent_of_addons_list = stk->get_layer_grid(0);
1074  }
1075 
1076  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
1077 
1078  if(info == nullptr) {
1079  return;
1080  }
1081 
1082  if(!tracking_info_[info->id].can_publish && is_installed_addon_status(tracking_info_[info->id].state)) {
1083  bool updatable = tracking_info_[info->id].installed_version
1084  != find_widget<menu_button>(parent, "version_filter", false).get_value_string();
1085  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1086  action_stack.select_layer(0);
1087 
1088  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1089  install_update_stack.select_layer(1);
1090  find_widget<button>(parent, "update", false).set_active(updatable);
1091  }
1092 }
1093 
1095 {
1098  return false;
1099  }
1100 
1101  return true;
1102 }
1103 
1104 } // namespace dialogs
window(const builder_window::window_resolution &definition)
< Needs to be initialized in show.
Definition: window.cpp:263
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:151
std::string addon_manager_saved_order_name()
Definition: general.cpp:992
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)
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:606
bool exit_hook(window &window)
Definition: manager.cpp:1094
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:956
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:403
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:179
logger & info()
Definition: log.cpp:171
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:77
const std::string & id() const
Definition: widget.cpp:111
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:573
void execute_default_action(const addon_info &addon)
Called when the player double-clicks an add-on.
Definition: manager.cpp:922
Base class for all widgets.
Definition: widget.hpp:53
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:24
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:299
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:1002
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).
status
The status of the window.
Definition: window.hpp:169
version_info installed_version
Definition: state.hpp:60
std::deque< std::unique_ptr< editor_action > > action_stack
Action stack typedef.
No tracking information available.
Definition: state.hpp:37
const_attr_itors attribute_range() const
Definition: config.cpp:858
Add-on is not installed.
Definition: state.hpp:24
void update_addon(const addon_info &addon)
Definition: manager.cpp:802
Class for a single line text area.
Definition: text_box.hpp:141
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
Definition: manager.cpp:173
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:84
#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:1030
std::string strftime(const std::string &format, const std::tm *time)
Definition: gettext.cpp:547
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: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: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:627
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
std::string display_icon() const
Get an icon path fixed for display (e.g.
Definition: info.cpp:232
widget * parent()
Definition: widget.cpp:161
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: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:249
Shows a yes and no button.
Definition: message.hpp:81
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:374
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
window * get_window()
Returns a pointer to the dialog&#39;s window.
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:255
bool ci_search(const std::string &s1, const std::string &s2)
Definition: gettext.cpp:558
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:896
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:724
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: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:837
virtual void set_active(const bool active) override
See styled_widget::set_active.
config & add_child(config_key_type key)
Definition: config.cpp:514
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:320
void install_addon(const addon_info &addon)
Definition: manager.cpp:761
void uninstall_addon(const addon_info &addon)
Definition: manager.cpp:782
void set_uninstall_function(addon_op_func_t function)
Sets the function to call when the player clicks the uninstall button.
Definition: addon_list.hpp:85
static const std::vector< std::pair< ADDON_TYPE, std::string > > type_filter_types_
Definition: manager.hpp: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
Abstract base class for all modal dialogs.
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:158
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:204
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:273
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
Definition: client.cpp:135
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:1007
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:66
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:572
Single-player campaign.
Definition: validation.hpp:104
void set_addon_manager_saved_order_name(const std::string &value)
Definition: general.cpp:997
bool use_twelve_hour_clock_format()
Definition: general.cpp:967
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:310
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
Definition: client.cpp:118
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:205
Version in the server matches local installation.
Definition: state.hpp:26
std::string description_translated() const
Definition: info.cpp:213