16 #define GETTEXT_DOMAIN "wesnoth-lib"
21 #include "addon/manager.hpp"
56 struct filter_transform
58 explicit filter_transform(
const std::vector<std::string>& filtertext) :
filtertext_(filtertext) {}
59 bool operator()(
const config& cfg)
const
73 for(
const auto& [
_, value] : child.attribute_range()) {
89 std::string make_display_dependencies(
90 const std::string& addon_id,
99 for(
const auto& dep_id : deps) {
103 addons_list::const_iterator ali =
addons_list.find(dep_id);
104 addons_tracking_list::const_iterator tli = addon_states.find(dep_id);
112 if(tli == addon_states.end()) {
115 depstate = tli->second;
128 std::string langcode_to_string(
const std::string& lcode)
132 if(ld.localename == lcode || ld.localename.substr(0, 2) == lcode) {
169 {
N_(
"addons_order^Name ($order)"),
"name", 0,
172 {
N_(
"addons_order^Author ($order)"),
"author", 1,
175 {
N_(
"addons_order^Size ($order)"),
"size", 2,
178 {
N_(
"addons_order^Downloads ($order)"),
"downloads", 3,
181 {
N_(
"addons_order^Type ($order)"),
"type", 4,
184 {
N_(
"addons_order^Last updated ($datelike_order)"),
"last_updated", -1,
187 {
N_(
"addons_order^First uploaded ($datelike_order)"),
"first_uploaded", -1,
204 const std::vector<addon_tag> tag_filter_types_{
205 {
"cooperative",
N_(
"addon_tag^Cooperative"),
207 N_(
"addon_tag^All human players are on the same team, versus the AI")},
208 {
"cosmetic",
N_(
"addon_tag^Cosmetic"),
210 N_(
"addon_tag^These make the game look different, without changing gameplay")},
211 {
"difficulty",
N_(
"addon_tag^Difficulty"),
213 N_(
"addon_tag^Can make campaigns easier or harder")},
214 {
"rng",
N_(
"addon_tag^RNG"),
216 N_(
"addon_tag^Modify the randomness in the combat mechanics, or remove it entirely")},
217 {
"survival",
N_(
"addon_tag^Survival"),
219 N_(
"addon_tag^Fight against waves of enemies")},
220 {
"terraforming",
N_(
"addon_tag^Terraforming"),
222 N_(
"addon_tag^Players can change the terrain")},
233 , need_wml_cache_refresh_(false)
243 switch(state.
state) {
246 ?
_(
"addon_state^Not installed")
247 :
_(
"addon_state^Published, not installed");
251 ?
_(
"addon_state^Installed")
252 :
_(
"addon_state^Published");
256 ?
_(
"addon_state^Installed, not tracking local version")
259 :
_(
"addon_state^Published, not tracking local version");
263 ?
_(
"addon_state^Installed ($local_version|), upgradable")
264 :
_(
"addon_state^Published ($local_version| installed), upgradable");
270 ?
_(
"addon_state^Installed ($local_version|), outdated on server")
271 :
_(
"addon_state^Published ($local_version| installed), outdated on server");
277 ?
_(
"addon_state^Installed, not ready to publish")
278 :
_(
"addon_state^Ready to publish");
282 ?
_(
"addon_state^Installed, broken")
283 :
_(
"addon_state^Published, broken");
286 s =
_(
"addon_state^Unknown");
296 stacked_widget& addr_info = find_widget<stacked_widget>(
"server_conn_info");
308 auto addr_box =
dynamic_cast<styled_widget*
>(addr_visible->
find(
"server_addr",
false));
318 addr_box->set_label(full_id.str());
325 addon_list& list = find_widget<addon_list>(
"addons");
327 text_box& filter = find_widget<text_box>(
"filter");
331 this, std::placeholders::_1));
333 this, std::placeholders::_1));
335 this, std::placeholders::_1));
338 this, std::placeholders::_1));
340 this, std::placeholders::_1));
347 menu_button& status_filter = find_widget<menu_button>(
"install_status_filter");
349 std::vector<config> status_filter_entries;
354 status_filter.
set_values(status_filter_entries);
360 auto& tag_filter = find_widget<multimenu_button>(
"tag_filter");
362 std::vector<config> tag_filter_entries;
363 for(
const auto&
f : tag_filter_types_) {
365 if(!
f.tooltip.empty()) {
370 tag_filter.set_values(tag_filter_entries);
375 multimenu_button& type_filter = find_widget<multimenu_button>(
"type_filter");
377 std::vector<config> type_filter_entries;
389 std::set<std::string> languages_available;
391 for (
const auto&
b : a.second.
locales) {
392 languages_available.insert(
b);
395 std::set<std::string> language_strings_available;
396 for (
const auto&
i: languages_available) {
400 if (std::string lang_code_string = langcode_to_string(
i); !lang_code_string.empty()) {
401 language_strings_available.insert(lang_code_string);
404 for (
auto&
i: language_strings_available) {
408 multimenu_button& language_filter = find_widget<multimenu_button>(
"language_filter");
409 std::vector<config> language_filter_entries;
411 language_filter_entries.emplace_back(
"label",
f.second,
"checkbox",
false);
414 language_filter.
set_values(language_filter_entries);
420 menu_button& order_dropdown = find_widget<menu_button>(
"order_dropdown");
422 std::vector<config> order_dropdown_entries;
426 symbols[
"order"] =
_(
"ascending");
428 symbols[
"datelike_order"] =
_(
"oldest to newest");
430 order_dropdown_entries.push_back(entry);
431 symbols[
"order"] =
_(
"descending");
433 symbols[
"datelike_order"] =
_(
"newest to oldest");
434 entry[
"label"] =
VGETTEXT(
f.label.c_str(), symbols);
435 order_dropdown_entries.push_back(entry);
438 order_dropdown.
set_values(order_dropdown_entries);
440 const std::string saved_order_name =
prefs::get().addon_manager_saved_order_name();
443 if(!saved_order_name.empty()) {
445 [&saved_order_name](
const addon_order& order) {return order.as_preference == saved_order_name;});
449 if(saved_order_direction == sort_order::type::ascending) {
450 func = order_it->sort_func_asc;
452 func = order_it->sort_func_desc;
455 find_widget<menu_button>(
"order_dropdown").set_value(
index);
456 auto& addons = find_widget<addon_list>(
"addons");
457 addons.set_addon_order(func);
458 addons.select_first_addon();
466 label& url_label = find_widget<label>(
"url");
472 find_widget<button>(
"install"),
476 find_widget<button>(
"uninstall"),
480 find_widget<button>(
"update"),
484 find_widget<button>(
"publish"),
488 find_widget<button>(
"delete"),
492 find_widget<button>(
"update_all"),
496 find_widget<button>(
"show_help"),
499 if(
stacked_widget* stk = find_widget<stacked_widget>(
"main_stack",
false,
false)) {
500 button& btn = find_widget<button>(
"details_toggle");
504 stk->select_layer(0);
507 stk->get_layer_grid(1)->find_widget<
menu_button>(
"version_filter"),
511 find_widget<menu_button>(
"version_filter"),
558 for(std::string
id : publishable_addons) {
567 pbl_cfg[
"name"] =
id;
568 pbl_cfg[
"local_only"] =
true;
581 show_transient_message(
_(
"No Add-ons Available"),
_(
"There are no add-ons available for download from this server."));
584 addon_list& list = find_widget<addon_list>(
"addons");
587 bool has_upgradable_addons =
false;
592 has_upgradable_addons =
true;
596 find_widget<button>(
"update_all").set_active(has_upgradable_addons);
606 find_widget<addon_list>(
"addons").select_addon(
id);
612 const text_box& name_filter = find_widget<const text_box>(
"filter");
613 const std::string& text = name_filter.
get_value();
616 boost::dynamic_bitset<> res;
622 const config& addon_cfg = *std::find_if(addon_cfgs.begin(), addon_cfgs.end(),
625 return cfg[
"name"] == a.first;
628 res.push_back(filter(addon_cfg));
636 const menu_button& status_filter = find_widget<const menu_button>(
"install_status_filter");
639 boost::dynamic_bitset<> res;
657 const auto& tag_filter = find_widget<const multimenu_button>(
"tag_filter");
658 const auto toggle_states = tag_filter.get_toggle_states();
659 if(toggle_states.none()) {
661 boost::dynamic_bitset<> res_flipped(
addons_.size());
665 std::vector<std::string> selected_tags;
666 for(std::size_t
i = 0;
i < tag_filter_types_.size(); ++
i) {
667 if(toggle_states[
i]) {
668 selected_tags.push_back(tag_filter_types_[
i].
id);
672 boost::dynamic_bitset<> res;
674 bool matched_tag =
false;
675 for(
const auto&
id : selected_tags) {
681 res.push_back(matched_tag);
689 const multimenu_button& type_filter = find_widget<const multimenu_button>(
"type_filter");
692 if(toggle_states.none()) {
694 boost::dynamic_bitset<> res_flipped(
addons_.size());
697 boost::dynamic_bitset<> res;
702 [&a](
const std::pair<ADDON_TYPE, std::string>& entry) {
703 return entry.first == a.second.type;
706 res.push_back(toggle_states[
index]);
714 const multimenu_button& lang_filter = find_widget<const multimenu_button>(
"language_filter");
718 if(toggle_states.none()) {
719 boost::dynamic_bitset<> res_flipped(
addons_.size());
722 boost::dynamic_bitset<> res;
727 std::vector<std::string> lang_string_vector;
728 for (
long unsigned int i = 0;
i < a.second.
locales.size();
i++) {
729 lang_string_vector.push_back(langcode_to_string(a.second.
locales[
i]));
732 for (
long unsigned int i = 0;
i < toggle_states.size();
i++) {
733 if (toggle_states[
i] ==
true) {
738 if ((contains_lang_code || contains_lang_string) ==
true)
757 auto list = find_widget<addon_list>(
"addons",
false,
false);
762 boost::dynamic_bitset<> res =
768 list->set_addon_shown(res);
773 const menu_button& order_menu = find_widget<const menu_button>(
"order_dropdown");
777 if(order == sort_order::type::ascending) {
783 find_widget<addon_list>(
"addons").set_addon_order(func);
790 menu_button& order_menu = find_widget<menu_button>(
"order_dropdown");
792 [sort_column](
const addon_order& order) {return order.column_index == static_cast<int>(sort_column);});
794 if(order == sort_order::type::descending) {
798 prefs::get().set_addon_manager_saved_order_name(order_it->as_preference);
802 template<
void(addon_manager::*fptr)(const addon_info& addon)>
806 if(
stacked_widget* stk = find_widget<stacked_widget>(
"main_stack",
false,
false)) {
807 stk->select_layer(0);
808 find_widget<button>(
"details_toggle").set_label(
_(
"Add-on Details"));
811 addon_list& addons = find_widget<addon_list>(
"addons");
814 if(addon ==
nullptr) {
819 (this->*fptr)(*addon);
828 if(
stacked_widget* stk = find_widget<stacked_widget>(
"main_stack",
false,
false)) {
831 if(addon.
id == find_widget<addon_list>(
"addons").get_selected_addon()->id) {
832 versioned_addon.
current_version = find_widget<menu_button>(
"version_filter").get_value_string();
849 _(
"The following add-on appears to have publishing or version control information stored locally, and will not be removed:")
902 std::string server_msg;
904 const std::string addon_id = addon.
id;
908 const version_info& version_to_publish = cfg[
"version"].str();
910 if(version_to_publish <=
tracking_info_[addon_id].remote_version) {
912 _(
"The remote version of this add-on is greater or equal to the version being uploaded. Do you really wish to continue?"),
921 if(cfg[
"passphrase"].empty()) {
923 if(!gui2::dialogs::addon_auth::execute(cfg)) {
928 }
else if(cfg[
"forum_auth"].to_bool()) {
939 }
else if(gui2::dialogs::addon_license_prompt::execute(server_msg)) {
941 const std::string&
msg =
_(
"The add-on was rejected by the server:") +
944 if (!extra_data.empty()) {
966 const std::string addon_id = addon.
id;
968 "Deleting '$addon|' will permanently erase its download and upload counts on the add-ons server. Do you really wish to continue?",
978 std::string server_msg;
1000 VGETTEXT(
"Do you want to uninstall '$addon|'?", symbols),
1027 std::ostringstream ss;
1032 ?
_(
"%B %d %Y, %I:%M %p")
1035 :
_(
"%B %d %Y, %H:%M");
1049 if(
stacked_widget* stk = find_widget<stacked_widget>(
"main_stack",
false,
false)) {
1050 parent = stk->get_layer_grid(1);
1051 info = stk->get_layer_grid(0)->find_widget<
addon_list>(
"addons").get_selected_addon();
1053 info = find_widget<addon_list>(
"addons").get_selected_addon();
1056 if(
info ==
nullptr) {
1069 status.set_use_markup(
true);
1078 :
_(
"addon_dependencies^None"));
1080 std::string languages;
1082 for(
const auto& lc :
info->locales) {
1083 const std::string& langlabel = langcode_to_string(lc);
1084 if(!langlabel.empty()) {
1085 if(!languages.empty()) {
1088 languages += langlabel;
1094 const std::string& feedback_url =
info->feedback_url;
1103 std::vector<config> version_filter_entries;
1119 for(
const auto&
f :
info->versions) {
1120 version_filter_entries.emplace_back(
"label",
f.str());
1130 version_filter_entries.emplace_back(
"label",
info->current_version.str());
1133 version_filter.set_values(version_filter_entries);
1134 version_filter.set_active(version_filter_entries.size() > 1);
1140 if(
stacked_widget* stk = find_widget<stacked_widget>(
"main_stack",
false,
false)) {
1142 parent_of_addons_list = stk->get_layer_grid(0);
1147 if(
info ==
nullptr) {
1153 != find_widget<menu_button>(
"version_filter").get_value_string();
1157 stacked_widget& install_update_stack = find_widget<stacked_widget>(
"install_update_stack");
1159 find_widget<button>(
"update").set_active(updatable);
bool remove_local_addon(const std::string &addon)
Removes the specified add-on, deleting its full directory structure.
config get_addon_pbl_info(const std::string &addon_name, bool do_validate)
Gets the publish information for an add-on.
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.
bool have_addon_pbl_info(const std::string &addon_name)
Returns whether a .pbl file is present for the specified add-on or not.
std::vector< std::string > available_addons()
Returns a list of local add-ons that can be published.
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on's version information structs.
Add-ons (campaignd) client class.
install_result install_addon_with_checks(const addons_list &addons, const addon_info &addon)
Performs an add-on download and install cycle.
bool delete_remote_addon(const std::string &id, std::string &response_message)
Requests the specified add-on to be removed from the server.
const std::string & get_last_server_error_data() const
Returns the last error message extra data sent by the server, or an empty string.
@ abort
User aborted the operation because of an issue with dependencies or chose not to overwrite the add-on...
const std::string & server_id() const
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
bool using_tls() const
Returns whether the current connection uses TLS.
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
const std::string & get_last_server_error() const
Returns the last error message sent by the server, or an empty string.
const std::string & addr() const
Returns the current hostname:port used for this connection.
const std::string & server_version() const
bool upload_addon(const std::string &id, std::string &response_message, config &cfg, bool local_only)
Uploads an add-on to the server.
A config object defines a single node in a WML file, with access to child nodes.
const_attr_itors attribute_range() const
child_itors child_range(config_key_type key)
boost::iterator_range< const_child_iterator > const_child_itors
config & add_child(config_key_type key)
void set_uninstall_function(addon_op_func_t function)
Sets the function to call when the player clicks the uninstall button.
std::function< bool(const addon_info &, const addon_info &)> addon_sort_func
void set_publish_function(addon_op_func_t function)
Sets the function to upload an addon to the addons server.
void add_list_to_keyboard_chain()
Adds the internal listbox to the keyboard event chain.
void set_install_function(addon_op_func_t function)
Sets the function to call when the player clicks the install button.
void set_delete_function(addon_op_func_t function)
Sets the function to install an addon from the addons server.
const addon_info * get_selected_addon() const
Returns the selected add-on.
void set_update_function(addon_op_func_t function)
Sets the function to call when the player clicks the update button.
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.
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.
static const int DEFAULT_ACTION_RETVAL
Special retval for the toggle panels in the addons list.
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...
void set_addons(const addons_list &addons)
Sets the add-ons to show.
virtual void set_active(const bool active) override
See styled_widget::set_active.
void uninstall_addon(const addon_info &addon)
void on_order_changed(unsigned int sort_column, sort_order::type order)
static const std::vector< std::pair< ADDON_STATUS_FILTER, std::string > > status_filter_types_
virtual void pre_show() override
Actions to be taken before showing the window.
boost::dynamic_bitset get_lang_filter_visibility() const
boost::dynamic_bitset get_status_filter_visibility() const
void update_selected_addon()
void execute_default_action(const addon_info &addon)
Called when the player double-clicks an add-on.
std::vector< std::pair< int, std::string > > language_filter_types_
void execute_default_action_on_selected_addon()
boost::dynamic_bitset get_name_filter_visibility() const
void toggle_details(button &btn, stacked_widget &stk)
void reload_list_and_reselect_item(const std::string id)
void delete_addon(const addon_info &addon)
Performs all backend and UI actions for taking down the specified add-on.
addon_manager(addons_client &client)
config cfg_
Config which contains the list with the campaigns.
boost::dynamic_bitset get_type_filter_visibility() const
void install_selected_addon()
void delete_selected_addon()
void execute_action_on_selected_addon()
addons_tracking_list tracking_info_
static const std::vector< addon_order > all_orders_
void uninstall_selected_addon()
boost::dynamic_bitset get_tag_filter_visibility() const
void publish_selected_addon()
static const std::vector< std::pair< ADDON_TYPE, std::string > > type_filter_types_
void install_addon(const addon_info &addon)
void update_addon(const addon_info &addon)
bool need_wml_cache_refresh_
void publish_addon(const addon_info &addon)
Performs all backend and UI actions for publishing the specified add-on.
void on_selected_version_change()
@ yes_no_buttons
Shows a yes and no button.
@ ok_cancel_buttons
Shows an ok and cancel button.
Abstract base class for all modal dialogs.
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
void set_link_aware(bool l)
std::string get_value() const
void set_text_changed_callback(std::function< void(text_box_base *textbox, const std::string text)> cb)
Set the text_changed callback.
A widget that allows the user to input text in single line.
base class of top level items, the only item which needs to store the final canvases to draw on.
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
void keyboard_capture(widget *widget)
void set_exit_hook(exit_hook mode, std::function< bool(window &)> func)
Sets the window's exit hook.
void close()
Requests to close the window.
status
The status of the window.
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
void set_addon_manager_saved_order_direction(sort_order::type value)
void set_password(const std::string &server, const std::string &login, const std::string &key)
sort_order::type addon_manager_saved_order_direction()
bool use_twelve_hour_clock_format()
std::string password(const std::string &server, const std::string &login)
Represents version numbers.
std::string str() const
Serializes the version number into string form.
Definitions for the interface to Wesnoth Markup Language (WML).
static std::string _(const char *str)
std::string label
What to show in the filter's drop-down list.
const std::vector< std::string > filtertext_
std::string tooltip
Shown when hovering over an entry in the filter's drop-down list.
std::string id
Text to match against addon_info.tags()
std::string size_display_string(double size)
Get a human-readable representation of the specified byte count.
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
void read_addons_list(const config &cfg, addons_list &dest)
Parse the specified add-ons list WML into an actual addons_list object.
std::map< std::string, addon_info > addons_list
language_list get_languages(bool all)
Return a list of available translations.
std::deque< std::unique_ptr< editor_action > > action_stack
Action stack typedef.
const std::string unicode_em_dash
static std::string describe_status_verbose(const addon_tracking_info &state)
REGISTER_DIALOG(editor_edit_unit)
static std::string format_addon_time(std::time_t time)
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
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.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
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.
retval
Default window/dialog return values.
@ OK
Dialog was closed with the OK button.
void show_help(const std::string &show_topic)
Open the help browser, show topic with id show_topic.
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
std::string strftime(const std::string &format, const std::tm *time)
bool ci_search(const std::string &s1, const std::string &s2)
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.
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
std::map< std::string, t_string > string_map
std::vector< std::string > split(const config_attribute_value &val)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
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.
bool is_installed_addon_status(ADDON_STATUS s)
@ ADDON_NOT_TRACKED
No tracking information available.
@ ADDON_INSTALLED_OUTDATED
Version in the server is older than local installation.
@ ADDON_NONE
Add-on is not installed.
@ ADDON_INSTALLED_UPGRADABLE
Version in the server is newer than local installation.
@ ADDON_INSTALLED
Version in the server matches local installation.
@ ADDON_INSTALLED_LOCAL_ONLY
No version in the server.
@ ADDON_INSTALLED_BROKEN
Dependencies not satisfied.
ADDON_STATUS_FILTER
Add-on installation status filters for the user interface.
std::map< std::string, addon_tracking_info > addons_tracking_list
std::vector< std::string > tags
version_info current_version
std::string display_type() const
Get an add-on type identifier for display in the user's language.
std::string display_title_translated_or_original() const
std::string display_title_full() const
std::set< std::string > resolve_dependencies(const addons_list &addons) const
Resolve an add-on's dependency tree in a recursive fashion.
std::vector< std::string > locales
Stores additional status information about add-ons.
version_info installed_version
Contains the outcome of an add-on install operation.
install_outcome outcome
Overall outcome of the operation.
bool wml_changed
Specifies if WML on disk was altered and needs to be reloaded.
addon_list::addon_sort_func sort_func_desc
addon_list::addon_sort_func sort_func_asc
std::string as_preference
The value used in the preferences file.
Exception thrown when the WML parser fails to read a .pbl file.
static map_location::DIRECTION s
@ ADDON_SP_SCENARIO
Single-player scenario.
@ ADDON_MP_SCENARIO
Multiplayer scenario.
@ ADDON_MP_CAMPAIGN
Multiplayer campaign.
@ ADDON_MP_FACTION
Multiplayer faction.
@ ADDON_MEDIA
Miscellaneous content/media (unit packs, terrain packs, music packs, etc.).
@ ADDON_MP_ERA
Multiplayer era.
@ ADDON_CORE
Total Conversion Core.
@ ADDON_SP_CAMPAIGN
Single-player campaign.
@ ADDON_OTHER
an add-on that fits in no other category
@ ADDON_SP_MP_CAMPAIGN
Hybrid campaign.
@ ADDON_MP_MAPS
Multiplayer plain (no WML) map pack.
@ ADDON_MOD
Modification of the game.