The Battle for Wesnoth  1.17.12+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 #include <iomanip>
59 #include <set>
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(true))
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  // Language filter
395  // Prepare shown languages, source all available languages from the addons themselves
396  std::set<std::string> languages_available;
397  for(const auto& a : addons_) {
398  for (const auto& b : a.second.locales) {
399  languages_available.insert(b);
400  }
401  }
402  std::set<std::string> language_strings_available;
403  for (const auto& i: languages_available) {
404  // Only show languages, which have a translation as per langcode_to_string() method
405  // Do not show tranlations with their langcode e.g. "sv_SV"
406  // Also put them into a set, so same lang strings are not producing doublettes
407  if (std::string lang_code_string = langcode_to_string(i); !lang_code_string.empty()) {
408  language_strings_available.insert(lang_code_string);
409  }
410  }
411  for (auto& i: language_strings_available) {
412  language_filter_types_.emplace_back(language_filter_types_.size(), std::move(i));
413  }
414  // The language filter
415  multimenu_button& language_filter = find_widget<multimenu_button>(&window, "language_filter", false);
416  std::vector<config> language_filter_entries;
417  for(const auto& f : language_filter_types_) {
418  language_filter_entries.emplace_back("label", f.second, "checkbox", false);
419  }
420 
421  language_filter.set_values(language_filter_entries);
422 
423  connect_signal_notify_modified(language_filter,
424  std::bind(&addon_manager::apply_filters, this));
425 
426  // Sorting order
427  menu_button& order_dropdown = find_widget<menu_button>(&window, "order_dropdown", false);
428 
429  std::vector<config> order_dropdown_entries;
430  for(const auto& f : all_orders_) {
431  utils::string_map symbols;
432 
433  symbols["order"] = _("ascending");
434  // TRANSLATORS: Sorting order of dates, oldest first
435  symbols["datelike_order"] = _("oldest to newest");
436  config entry{"label", VGETTEXT(f.label.c_str(), symbols)};
437  order_dropdown_entries.push_back(entry);
438  symbols["order"] = _("descending");
439  // TRANSLATORS: Sorting order of dates, newest first
440  symbols["datelike_order"] = _("newest to oldest");
441  entry["label"] = VGETTEXT(f.label.c_str(), symbols);
442  order_dropdown_entries.push_back(entry);
443  }
444 
445  order_dropdown.set_values(order_dropdown_entries);
446  {
447  const std::string saved_order_name = preferences::addon_manager_saved_order_name();
449 
450  if(!saved_order_name.empty()) {
451  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
452  [&saved_order_name](const addon_order& order) {return order.as_preference == saved_order_name;});
453  if(order_it != all_orders_.end()) {
454  int index = 2 * (std::distance(all_orders_.begin(), order_it));
456  if(saved_order_direction == sort_order::type::ascending) {
457  func = order_it->sort_func_asc;
458  } else {
459  func = order_it->sort_func_desc;
460  ++index;
461  }
462  find_widget<menu_button>(&window, "order_dropdown", false).set_value(index, false);
463  auto& addons = find_widget<addon_list>(&window, "addons", false);
464  addons.set_addon_order(func);
465  addons.select_first_addon();
466  }
467  }
468  }
469 
470  connect_signal_notify_modified(order_dropdown,
471  std::bind(&addon_manager::order_addons, this));
472 
473  label& url_label = find_widget<label>(&window, "url", false);
474 
475  url_label.set_use_markup(true);
476  url_label.set_link_aware(true);
477 
479  find_widget<button>(&window, "install", false),
480  std::bind(&addon_manager::install_selected_addon, this));
481 
483  find_widget<button>(&window, "uninstall", false),
484  std::bind(&addon_manager::uninstall_selected_addon, this));
485 
487  find_widget<button>(&window, "update", false),
488  std::bind(&addon_manager::update_selected_addon, this));
489 
491  find_widget<button>(&window, "publish", false),
492  std::bind(&addon_manager::publish_selected_addon, this));
493 
495  find_widget<button>(&window, "delete", false),
496  std::bind(&addon_manager::delete_selected_addon, this));
497 
499  find_widget<button>(&window, "update_all", false),
500  std::bind(&addon_manager::update_all_addons, this));
501 
503  find_widget<button>(&window, "show_help", false),
504  std::bind(&addon_manager::show_help, this));
505 
506  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
507  button& btn = find_widget<button>(&window, "details_toggle", false);
508  connect_signal_mouse_left_click(btn, std::bind(&addon_manager::toggle_details, this, std::ref(btn), std::ref(*stk)));
509  stk->select_layer(0);
510  }
511 
512  widget* version_filter_parent = &window;
513  if(stacked_widget* stk = find_widget<stacked_widget>(&window, "main_stack", false, false)) {
514  version_filter_parent = stk->get_layer_grid(1);
515  }
516 
517  menu_button& version_filter = find_widget<menu_button>(version_filter_parent, "version_filter", false);
518  connect_signal_notify_modified(version_filter,
520 
521  on_addon_select();
522 
523  window.set_enter_disabled(true);
524 
525  window.keyboard_capture(&filter);
527 
528  list.set_callback_order_change(std::bind(&addon_manager::on_order_changed, this, std::placeholders::_1, std::placeholders::_2));
529 
530  // Use handle the special addon_list retval to allow installing addons on double click
531  window.set_exit_hook(window::exit_hook::on_all, std::bind(&addon_manager::exit_hook, this, std::placeholders::_1));
532 }
533 
535 {
536  if(stk.current_layer() == 0) {
537  btn.set_label(_("addons^Back to List"));
538  stk.select_layer(1);
539  } else {
540  btn.set_label(_("Add-on Details"));
541  stk.select_layer(0);
542  }
543 }
544 
546 {
548  if(!cfg_) {
549  gui2::show_error_message(_("An error occurred while downloading the add-ons list from the server."));
550  get_window()->close();
551  }
552 }
553 
555 {
558  }
559 
561 
562  std::vector<std::string> publishable_addons = available_addons();
563 
564  for(std::string id : publishable_addons) {
565  if(addons_.find(id) == addons_.end()) {
566  // Get a config from the addon's pbl file
567  // Note that the name= key is necessary or stuff breaks, since the filter code uses this key
568  // to match add-ons in the config list. It also fills in addon_info's id field. It's also
569  // neccessay to set local_only here so that flag can be properly set after addons_ is cleared
570  // and recreated by read_addons_list.
571  try {
572  config pbl_cfg = get_addon_pbl_info(id, false);
573  pbl_cfg["name"] = id;
574  pbl_cfg["local_only"] = true;
575 
576  // Add the add-on to the list.
577  addon_info addon(pbl_cfg);
578  addons_[id] = addon;
579 
580  // Add the addon to the config entry
581  cfg_.add_child("campaign", std::move(pbl_cfg));
582  } catch(invalid_pbl_exception&) {}
583  }
584  }
585 
586  if(addons_.empty()) {
587  show_transient_message(_("No Add-ons Available"), _("There are no add-ons available for download from this server."));
588  }
589 
590  addon_list& list = find_widget<addon_list>(get_window(), "addons", false);
591  list.set_addons(addons_);
592 
593  bool has_upgradable_addons = false;
594  for(const auto& a : addons_) {
595  tracking_info_[a.first] = get_addon_tracking_info(a.second);
596 
597  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
598  has_upgradable_addons = true;
599  }
600  }
601 
602  find_widget<button>(get_window(), "update_all", false).set_active(has_upgradable_addons);
603 
604  apply_filters();
605 }
606 
608 {
609  load_addon_list();
610 
611  // Reselect the add-on.
612  find_widget<addon_list>(get_window(), "addons", false).select_addon(id);
613  on_addon_select();
614 }
615 
616 boost::dynamic_bitset<> addon_manager::get_name_filter_visibility() const
617 {
618  const text_box& name_filter = find_widget<const text_box>(get_window(), "filter", false);
619  const std::string& text = name_filter.get_value();
620 
621  filter_transform filter(utils::split(text, ' '));
622  boost::dynamic_bitset<> res;
623 
624  const config::const_child_itors& addon_cfgs = cfg_.child_range("campaign");
625 
626  for(const auto& a : addons_)
627  {
628  const config& addon_cfg = *std::find_if(addon_cfgs.begin(), addon_cfgs.end(),
629  [&a](const config& cfg)
630  {
631  return cfg["name"] == a.first;
632  });
633 
634  res.push_back(filter(addon_cfg));
635  }
636 
637  return res;
638 }
639 
640 boost::dynamic_bitset<> addon_manager::get_status_filter_visibility() const
641 {
642  const menu_button& status_filter = find_widget<const menu_button>(get_window(), "install_status_filter", false);
643  const ADDON_STATUS_FILTER selection = status_filter_types_[status_filter.get_value()].first;
644 
645  boost::dynamic_bitset<> res;
646  for(const auto& a : addons_) {
647  const addon_tracking_info& info = tracking_info_.at(a.second.id);
648 
649  res.push_back(
650  (selection == FILTER_ALL) ||
651  (selection == FILTER_INSTALLED && is_installed_addon_status(info.state)) ||
652  (selection == FILTER_UPGRADABLE && info.state == ADDON_INSTALLED_UPGRADABLE) ||
653  (selection == FILTER_PUBLISHABLE && info.can_publish == true) ||
654  (selection == FILTER_NOT_INSTALLED && info.state == ADDON_NONE)
655  );
656  }
657 
658  return res;
659 }
660 
661 boost::dynamic_bitset<> addon_manager::get_tag_filter_visibility() const
662 {
663  const auto& tag_filter = find_widget<const multimenu_button>(get_window(), "tag_filter", false);
664  const auto toggle_states = tag_filter.get_toggle_states();
665  if(toggle_states.none()) {
666  // Nothing selected. It means that all add-ons are shown.
667  boost::dynamic_bitset<> res_flipped(addons_.size());
668  return ~res_flipped;
669  }
670 
671  std::vector<std::string> selected_tags;
672  for(std::size_t i = 0; i < tag_filter_types_.size(); ++i) {
673  if(toggle_states[i]) {
674  selected_tags.push_back(tag_filter_types_[i].id);
675  }
676  }
677 
678  boost::dynamic_bitset<> res;
679  for(const auto& a : addons_) {
680  bool matched_tag = false;
681  for(const auto& id : selected_tags) {
682  if(utils::contains(a.second.tags, id)) {
683  matched_tag = true;
684  break;
685  }
686  }
687  res.push_back(matched_tag);
688  }
689 
690  return res;
691 }
692 
693 boost::dynamic_bitset<> addon_manager::get_type_filter_visibility() const
694 {
695  const multimenu_button& type_filter = find_widget<const multimenu_button>(get_window(), "type_filter", false);
696 
697  boost::dynamic_bitset<> toggle_states = type_filter.get_toggle_states();
698  if(toggle_states.none()) {
699  // Nothing selected. It means that *all* add-ons are shown.
700  boost::dynamic_bitset<> res_flipped(addons_.size());
701  return ~res_flipped;
702  } else {
703  boost::dynamic_bitset<> res;
704 
705  for(const auto& a : addons_) {
706  int index = std::distance(type_filter_types_.begin(),
707  std::find_if(type_filter_types_.begin(), type_filter_types_.end(),
708  [&a](const std::pair<ADDON_TYPE, std::string>& entry) {
709  return entry.first == a.second.type;
710  })
711  );
712  res.push_back(toggle_states[index]);
713  }
714  return res;
715  }
716 }
717 
718 boost::dynamic_bitset<> addon_manager::get_lang_filter_visibility() const
719 {
720  const multimenu_button& lang_filter = find_widget<const multimenu_button>(get_window(), "language_filter", false);
721 
722  boost::dynamic_bitset<> toggle_states = lang_filter.get_toggle_states();
723 
724  if(toggle_states.none()) {
725  boost::dynamic_bitset<> res_flipped(addons_.size());
726  return ~res_flipped;
727  } else {
728  boost::dynamic_bitset<> res;
729  for(const auto& a : addons_) {
730  bool retval = false;
731  // langcode -> string conversion vector, to be able to detect either
732  // langcodes or langstring entries
733  std::vector<std::string> lang_string_vector;
734  for (long unsigned int i = 0; i < a.second.locales.size(); i++) {
735  lang_string_vector.push_back(langcode_to_string(a.second.locales[i]));
736  }
737  // Find all toggle states, where toggle = true and lang = lang
738  for (long unsigned int i = 0; i < toggle_states.size(); i++) {
739  if (toggle_states[i] == true) {
740  // does lang_code match?
741  bool contains_lang_code = utils::contains(a.second.locales, language_filter_types_[i].second);
742  // does land_string match?
743  bool contains_lang_string = utils::contains(lang_string_vector, language_filter_types_[i].second);
744  if ((contains_lang_code || contains_lang_string) == true)
745  retval = true;
746  }
747  }
748  res.push_back(retval);
749  }
750  return res;
751  }
752 }
753 
755 {
756  // In the small-screen layout, the text_box for the filter keeps keyboard focus even when the
757  // details panel is visible, which means this can be called when the list isn't visible. That
758  // causes problems both because find_widget can throw exceptions, but also because changing the
759  // filters can hide the currently-shown add-on, triggering a different one to be selected in a
760  // way that would seem random unless the user realised that they were typing into a filter box.
761  //
762  // Quick workaround is to not process the new filter if the list isn't visible.
763  auto list = find_widget<addon_list>(get_window(), "addons", false, false);
764  if(!list) {
765  return;
766  }
767 
768  boost::dynamic_bitset<> res =
774  list->set_addon_shown(res);
775 }
776 
778 {
779  const menu_button& order_menu = find_widget<const menu_button>(get_window(), "order_dropdown", false);
780  const addon_order& order_struct = all_orders_.at(order_menu.get_value() / 2);
781  sort_order::type order = order_menu.get_value() % 2 == 0 ? sort_order::type::ascending : sort_order::type::descending;
783  if(order == sort_order::type::ascending) {
784  func = order_struct.sort_func_asc;
785  } else {
786  func = order_struct.sort_func_desc;
787  }
788 
789  find_widget<addon_list>(get_window(), "addons", false).set_addon_order(func);
790  preferences::set_addon_manager_saved_order_name(order_struct.as_preference);
792 }
793 
794 void addon_manager::on_order_changed(unsigned int sort_column, sort_order::type order)
795 {
796  menu_button& order_menu = find_widget<menu_button>(get_window(), "order_dropdown", false);
797  auto order_it = std::find_if(all_orders_.begin(), all_orders_.end(),
798  [sort_column](const addon_order& order) {return order.column_index == static_cast<int>(sort_column);});
799  int index = 2 * (std::distance(all_orders_.begin(), order_it));
800  if(order == sort_order::type::descending) {
801  ++index;
802  }
803  order_menu.set_value(index);
804  preferences::set_addon_manager_saved_order_name(order_it->as_preference);
806 }
807 
808 template<void(addon_manager::*fptr)(const addon_info& addon)>
810 {
811  // Explicitly return to the main page if we're in low-res mode so the list is visible.
812  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
813  stk->select_layer(0);
814  find_widget<button>(get_window(), "details_toggle", false).set_label(_("Add-on Details"));
815  }
816 
817  addon_list& addons = find_widget<addon_list>(get_window(), "addons", false);
818  const addon_info* addon = addons.get_selected_addon();
819 
820  if(addon == nullptr) {
821  return;
822  }
823 
824  try {
825  (this->*fptr)(*addon);
826  } catch(const addons_client::user_exit&) {
827  // User canceled the op.
828  }
829 }
830 
832 {
833  addon_info versioned_addon = addon;
834  widget* parent = get_window();
835  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
836  parent = stk->get_layer_grid(1);
837  }
838  if(addon.id == find_widget<addon_list>(get_window(), "addons", false).get_selected_addon()->id) {
839  versioned_addon.current_version = find_widget<menu_button>(parent, "version_filter", false).get_value_string();
840  }
841 
843 
844  // Take note if any wml_changes occurred
846 
849  }
850 }
851 
853 {
854  if(have_addon_pbl_info(addon.id) || have_addon_in_vcs_tree(addon.id)) {
856  _("The following add-on appears to have publishing or version control information stored locally, and will not be removed:") + " " +
857  addon.display_title_full());
858  return;
859  }
860 
861  bool success = remove_local_addon(addon.id);
862 
863  if(!success) {
864  gui2::show_error_message(_("The following add-on could not be deleted properly:") + " " + addon.display_title_full());
865  } else {
867 
869  }
870 }
871 
873 {
874  /* Currently, the install and update codepaths are the same, so this function simply
875  * calls the other. Since this might change in the future, I'm leaving this function
876  * here for now.
877  *
878  * - vultraz, 2017-03-12
879  */
880  install_addon(addon);
881 }
882 
884 {
885  for(const auto& a : addons_) {
886  if(tracking_info_[a.first].state == ADDON_INSTALLED_UPGRADABLE) {
888 
889  if(result.wml_changed) {
890  // Updating an add-on may have resulted in its dependencies being updated
891  // as well, so we need to reread version info blocks afterwards to make sure
892  // we don't try to re-download newly updated add-ons.
894  }
895 
896  // Take note if any wml_changes occurred
898  }
899  }
900 
902  load_addon_list();
903  }
904 }
905 
906 /** Performs all backend and UI actions for publishing the specified add-on. */
908 {
909  std::string server_msg;
910 
911  const std::string addon_id = addon.id;
912  // Since the user is planning to upload an addon, this is the right time to validate the .pbl.
913  config cfg = get_addon_pbl_info(addon_id, true);
914 
915  const version_info& version_to_publish = cfg["version"].str();
916 
917  if(version_to_publish <= tracking_info_[addon_id].remote_version) {
918  const int res = gui2::show_message(_("Warning"),
919  _("The remote version of this add-on is greater or equal to the version being uploaded. Do you really wish to continue?"),
921 
922  if(res != gui2::retval::OK) {
923  return;
924  }
925  }
926 
927  // if the passphrase isn't provided from the _server.pbl, try to pre-populate it from the preferences before prompting for it
928  if(cfg["passphrase"].empty()) {
929  cfg["passphrase"] = preferences::password(preferences::campaign_server(), cfg["author"]);
930  if(!gui2::dialogs::addon_auth::execute(cfg)) {
931  return;
932  } else {
933  preferences::set_password(preferences::campaign_server(), cfg["author"], cfg["passphrase"]);
934  }
935  }
936 
937  if(!::image::exists(cfg["icon"].str())) {
938  gui2::show_error_message(_("Invalid icon path. Make sure the path points to a valid image."));
939  } else if(!client_.request_distribution_terms(server_msg)) {
941  _("The server responded with an error:") + "\n" + client_.get_last_server_error());
942  } else if(gui2::dialogs::addon_license_prompt::execute(server_msg)) {
943  if(!client_.upload_addon(addon_id, server_msg, cfg, tracking_info_[addon_id].state == ADDON_INSTALLED_LOCAL_ONLY)) {
944  const std::string& msg = _("The add-on was rejected by the server:") +
945  "\n\n" + client_.get_last_server_error();
946  const std::string& extra_data = client_.get_last_server_error_data();
947  if (!extra_data.empty()) {
948  // TODO: Allow user to copy the extra data portion to clipboard
949  // or something, maybe display it in a dialog similar to
950  // the WML load errors report in a monospace font and
951  // stuff (having a scroll container is especially
952  // important since a long list can cause the dialog to
953  // overflow).
954  gui2::show_error_message(msg + "\n\n" + extra_data, true);
955  } else {
956  gui2::show_error_message(msg, true);
957  }
958  } else {
959  gui2::show_transient_message(_("Response"), server_msg);
962  }
963  }
964 }
965 
966 /** Performs all backend and UI actions for taking down the specified add-on. */
968 {
969  const std::string addon_id = addon.id;
970  const std::string& text = VGETTEXT(
971  "Deleting '$addon|' will permanently erase its download and upload counts on the add-ons server. Do you really wish to continue?",
972  {{"addon", make_addon_title(addon_id)}} // FIXME: need the real title!
973  );
974 
975  const int res = gui2::show_message(_("Confirm"), text, gui2::dialogs::message::yes_no_buttons);
976 
977  if(res != gui2::retval::OK) {
978  return;
979  }
980 
981  std::string server_msg;
982  if(!client_.delete_remote_addon(addon_id, server_msg)) {
983  gui2::show_error_message(_("The server responded with an error:") + "\n" + client_.get_last_server_error());
984  } else {
985  // FIXME: translation needed!
986  gui2::show_transient_message(_("Response"), server_msg);
989  }
990 }
991 
992 /** Called when the player double-clicks an add-on. */
994 {
995  switch(tracking_info_[addon.id].state) {
996  case ADDON_NONE:
997  install_addon(addon);
998  break;
999  case ADDON_INSTALLED:
1000  if(!tracking_info_[addon.id].can_publish) {
1001  utils::string_map symbols{ { "addon", addon.display_title_full() } };
1002  int res = gui2::show_message(_("Uninstall add-on"),
1003  VGETTEXT("Do you want to uninstall '$addon|'?", symbols),
1005  if(res == gui2::retval::OK) {
1006  uninstall_addon(addon);
1007  }
1008  }
1009  break;
1011  update_addon(addon);
1012  break;
1015  publish_addon(addon);
1016  break;
1017  default:
1018  break;
1019  }
1020 }
1021 
1023 {
1024  help::show_help("installing_addons");
1025 }
1026 
1027 static std::string format_addon_time(std::time_t time)
1028 {
1029  if(time) {
1030  std::ostringstream ss;
1031 
1033  // TRANSLATORS: Month + day of month + year + 12-hour time, eg 'November 02 2021, 1:59 PM'. Format for your locale.
1034  ? _("%B %d %Y, %I:%M %p")
1035  // TRANSLATORS: Month + day of month + year + 24-hour time, eg 'November 02 2021, 13:59'. Format for your locale.
1036  : _("%B %d %Y, %H:%M");
1037 
1038  ss << translation::strftime(format, std::localtime(&time));
1039 
1040  return ss.str();
1041  }
1042 
1043  return font::unicode_em_dash;
1044 }
1045 
1047 {
1048  widget* parent = get_window();
1049  widget* parent_of_addons_list = parent;
1050  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
1051  parent = stk->get_layer_grid(1);
1052  parent_of_addons_list = stk->get_layer_grid(0);
1053  }
1054 
1055  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
1056 
1057  if(info == nullptr) {
1058  return;
1059  }
1060 
1061  find_widget<drawing>(parent, "image", false).set_label(info->display_icon());
1062 
1063  find_widget<styled_widget>(parent, "title", false).set_label(info->display_title_translated_or_original());
1064  find_widget<styled_widget>(parent, "description", false).set_label(info->description_translated());
1065  menu_button& version_filter = find_widget<menu_button>(parent, "version_filter", false);
1066  find_widget<styled_widget>(parent, "author", false).set_label(info->author);
1067  find_widget<styled_widget>(parent, "type", false).set_label(info->display_type());
1068 
1069  styled_widget& status = find_widget<styled_widget>(parent, "status", false);
1071  status.set_use_markup(true);
1072 
1073  find_widget<styled_widget>(parent, "size", false).set_label(size_display_string(info->size));
1074  find_widget<styled_widget>(parent, "downloads", false).set_label(std::to_string(info->downloads));
1075  find_widget<styled_widget>(parent, "created", false).set_label(format_addon_time(info->created));
1076  find_widget<styled_widget>(parent, "updated", false).set_label(format_addon_time(info->updated));
1077 
1078  find_widget<styled_widget>(parent, "dependencies", false).set_label(!info->depends.empty()
1079  ? make_display_dependencies(info->id, addons_, tracking_info_)
1080  : _("addon_dependencies^None"));
1081 
1082  std::string languages;
1083 
1084  for(const auto& lc : info->locales) {
1085  const std::string& langlabel = langcode_to_string(lc);
1086  if(!langlabel.empty()) {
1087  if(!languages.empty()) {
1088  languages += ", ";
1089  }
1090  languages += langlabel;
1091  }
1092  }
1093 
1094  find_widget<styled_widget>(parent, "translations", false).set_label(!languages.empty() ? languages : _("translations^None"));
1095 
1096  const std::string& feedback_url = info->feedback_url;
1097  find_widget<label>(parent, "url", false).set_label(!feedback_url.empty() ? feedback_url : _("url^None"));
1098 
1099  bool installed = is_installed_addon_status(tracking_info_[info->id].state);
1100  bool updatable = tracking_info_[info->id].state == ADDON_INSTALLED_UPGRADABLE;
1101 
1102  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1103  // #TODO: Add tooltips with upload time and pack size
1104  std::vector<config> version_filter_entries;
1105 
1106  if(!tracking_info_[info->id].can_publish) {
1107  action_stack.select_layer(0);
1108 
1109  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1110  install_update_stack.select_layer(updatable ? 1 : 0);
1111 
1112  if(!updatable) {
1113  find_widget<button>(parent, "install", false).set_active(!installed);
1114  } else {
1115  find_widget<button>(parent, "update", false).set_active(true);
1116  }
1117 
1118  find_widget<button>(parent, "uninstall", false).set_active(installed);
1119 
1120  for(const auto& f : info->versions) {
1121  version_filter_entries.emplace_back("label", f.str());
1122  }
1123  } else {
1124  action_stack.select_layer(1);
1125 
1126  // Always enable the publish button, but disable the delete button if not yet published.
1127  find_widget<button>(parent, "publish", false).set_active(true);
1128  find_widget<button>(parent, "delete", false).set_active(!info->local_only);
1129 
1130  // Show only the version to be published
1131  version_filter_entries.emplace_back("label", info->current_version.str());
1132  }
1133 
1134  version_filter.set_values(version_filter_entries);
1135  version_filter.set_active(version_filter_entries.size() > 1);
1136 }
1137 
1139 {
1140  widget* parent = get_window();
1141  widget* parent_of_addons_list = parent;
1142  if(stacked_widget* stk = find_widget<stacked_widget>(get_window(), "main_stack", false, false)) {
1143  parent = stk->get_layer_grid(1);
1144  parent_of_addons_list = stk->get_layer_grid(0);
1145  }
1146 
1147  const addon_info* info = find_widget<addon_list>(parent_of_addons_list, "addons", false).get_selected_addon();
1148 
1149  if(info == nullptr) {
1150  return;
1151  }
1152 
1153  if(!tracking_info_[info->id].can_publish && is_installed_addon_status(tracking_info_[info->id].state)) {
1154  bool updatable = tracking_info_[info->id].installed_version
1155  != find_widget<menu_button>(parent, "version_filter", false).get_value_string();
1156  stacked_widget& action_stack = find_widget<stacked_widget>(parent, "action_stack", false);
1157  action_stack.select_layer(0);
1158 
1159  stacked_widget& install_update_stack = find_widget<stacked_widget>(parent, "install_update_stack", false);
1160  install_update_stack.select_layer(1);
1161  find_widget<button>(parent, "update", false).set_active(updatable);
1162  }
1163 }
1164 
1166 {
1169  return false;
1170  }
1171 
1172  return true;
1173 }
1174 
1175 } // 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:999
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:640
bool exit_hook(window &window)
Definition: manager.cpp:1165
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:1027
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:182
void add_list_to_keyboard_chain()
Adds the internal listbox to the keyboard event chain.
Definition: addon_list.cpp:372
#define a
Exception thrown when the WML parser fails to read a .pbl file.
Definition: manager.hpp:45
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
std::vector< std::pair< int, std::string > > language_filter_types_
Definition: manager.hpp:106
void reload_list_and_reselect_item(const std::string id)
Definition: manager.cpp:607
void execute_default_action(const addon_info &addon)
Called when the player double-clicks an add-on.
Definition: manager.cpp:993
Base class for all widgets.
Definition: widget.hpp:53
int current_layer() const
Gets the current visible layer number.
config get_addon_pbl_info(const std::string &addon_name, bool do_validate)
Gets the publish information for an add-on.
Definition: manager.cpp:70
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:1009
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:56
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:872
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:186
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:1022
std::string strftime(const std::string &format, const std::tm *time)
Definition: gettext.cpp:567
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:616
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:661
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:534
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:388
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:143
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
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:578
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:967
std::size_t i
Definition: function.cpp:968
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:65
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: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
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:794
std::string display_title_full() const
Definition: info.cpp:224
Multiplayer plain (no WML) map pack.
Definition: validation.hpp:109
boost::dynamic_bitset get_lang_filter_visibility() const
Definition: manager.cpp:718
#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:693
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:907
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:831
void uninstall_addon(const addon_info &addon)
Definition: manager.cpp:852
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
retval
Default window/dialog return values.
Definition: retval.hpp:29
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:1014
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:574
Single-player campaign.
Definition: validation.hpp:104
void set_addon_manager_saved_order_name(const std::string &value)
Definition: general.cpp:1004
bool use_twelve_hour_clock_format()
Definition: general.cpp:974
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:107
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