16 #define GETTEXT_DOMAIN "wesnoth-editor"
55 #include <boost/algorithm/string.hpp>
56 #include <boost/filesystem.hpp>
60 std::vector<std::unique_ptr<editor::map_context>> saved_contexts_;
61 int last_context_ = 0;
63 const std::string get_menu_marker(
const bool changed)
81 , current_addon_(addon_id)
83 , last_map_generator_(nullptr)
84 , current_context_index_(0)
85 , auto_update_transitions_(
prefs::
get().editor_auto_update_transitions())
134 for(
const auto& loc :
get_map_context().map().special_locations().left) {
141 for(
size_t i = 10;
i <= n_teams;
i++) {
160 return (item ==
"editor-auto-update-transitions");
162 return (item ==
"editor-partial-update-transitions");
164 return (item ==
"editor-no-update-transitions");
184 std::vector<std::string> modified;
187 if(!mc->get_name().empty()) {
188 modified.push_back(mc->get_name());
189 }
else if(!mc->get_filename().empty()) {
190 modified.push_back(mc->get_filename());
192 modified.push_back(mc->get_default_context_name());
197 for(std::string& str : modified) {
201 return modified.size();
228 if(mru.empty() ||
index >= mru.size()) {
239 if(gui2::dialogs::editor_edit_side::execute(team_info)) {
255 gui2::dialogs::prompt::execute(new_addon_id);
260 std::string main_cfg = new_dir +
"/_main.cfg";
281 std::string
id = context.
get_id();
282 std::string name = context.
get_name();
291 const bool ok = gui2::dialogs::editor_edit_scenario::execute(
292 id, name, description, turns, xp_mod, victory, random
313 if(gui2::dialogs::editor_new_map::execute(
_(
"New Map"),
w,
h)) {
326 if(gui2::dialogs::editor_new_map::execute(
_(
"New Scenario"),
w,
h)) {
334 auto pos = items.erase(items.begin() +
i);
335 std::vector<config> contexts;
337 for(std::size_t mci = 0; mci <
map_contexts_.size(); ++mci) {
351 std::ostringstream ss;
352 ss <<
"[" << mci + 1 <<
"] ";
366 const std::string
label = ss.str();
367 const std::string details = get_menu_marker(changed);
369 contexts.emplace_back(
"label",
label,
"details", details);
372 items.insert(pos, contexts.begin(), contexts.end());
379 auto pos = items.erase(items.begin() +
i);
382 items.insert(pos,
config {
"label",
_(
"No Recent Files")});
386 for(std::string&
path : mru) {
393 std::vector<config> temp;
394 std::transform(mru.begin(), mru.end(), std::back_inserter(temp), [](
const std::string& str) {
395 return config {
"label", str};
398 items.insert(pos, temp.begin(), temp.end());
401 void context_manager::expand_areas_menu(std::vector<config>& items,
int i)
403 tod_manager* tod = get_map_context().get_time_manager();
408 auto pos = items.erase(items.begin() +
i);
409 std::vector<config> area_entries;
411 std::vector<std::string> area_ids = tod->
get_area_ids();
413 for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
414 const std::string& area = area_ids[mci];
416 std::stringstream ss;
417 ss <<
"[" << mci + 1 <<
"] ";\
426 mci ==
static_cast<std::size_t
>(get_map_context().get_active_area())
429 const std::string
label = ss.str();
430 const std::string details = get_menu_marker(changed);
432 area_entries.emplace_back(
"label",
label,
"details", details);
435 items.insert(pos, area_entries.begin(), area_entries.end());
438 void context_manager::expand_sides_menu(std::vector<config>& items,
int i)
440 auto pos = items.erase(items.begin() +
i);
441 std::vector<config> contexts;
443 for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
445 const team&
t = get_map_context().teams()[mci];
446 const std::string& teamname =
t.user_team_name();
447 std::stringstream
label;
448 label <<
"[" << mci+1 <<
"] ";
450 if(teamname.empty()) {
456 contexts.emplace_back(
"label",
label.str());
459 items.insert(pos, contexts.begin(), contexts.end());
462 void context_manager::expand_time_menu(std::vector<config>& items,
int i)
464 auto pos = items.erase(items.begin() +
i);
465 std::vector<config> times;
467 tod_manager* tod_m = get_map_context().get_time_manager();
469 assert(tod_m !=
nullptr);
473 "details", time.name,
478 items.insert(pos, times.begin(), times.end());
481 void context_manager::expand_local_time_menu(std::vector<config>& items,
int i)
483 auto pos = items.erase(items.begin() +
i);
484 std::vector<config> times;
486 tod_manager* tod_m = get_map_context().get_time_manager();
488 for(
const time_of_day& time : tod_m->
times(get_map_context().get_active_area())) {
490 "details", time.name,
495 items.insert(pos, times.begin(), times.end());
498 void context_manager::apply_mask_dialog()
500 std::string fn = get_map_context().get_filename();
525 void context_manager::perform_refresh(
const editor_action& action,
bool drag_part )
527 get_map_context().perform_action(action);
528 refresh_after_action(drag_part);
531 void context_manager::rename_area_dialog()
533 int active_area = get_map_context().get_active_area();
534 std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
536 if(gui2::dialogs::edit_text::execute(
N_(
"Rename Area"),
N_(
"Identifier:"), name)) {
537 get_map_context().get_time_manager()->set_area_id(active_area, name);
541 void context_manager::create_mask_to_dialog()
543 std::string fn = get_map_context().get_filename();
568 void context_manager::refresh_after_action(
bool drag_part)
570 if(get_map_context().needs_reload()) {
575 const std::set<map_location>& changed_locs = get_map_context().changed_locations();
577 if(get_map_context().needs_terrain_rebuild()) {
580 && (!drag_part || get_map_context().everything_changed())))
583 get_map_context().set_needs_terrain_rebuild(
false);
584 gui_.invalidate_all();
587 gui_.rebuild_terrain(loc);
589 gui_.invalidate(changed_locs);
592 if(get_map_context().everything_changed()) {
593 gui_.invalidate_all();
595 gui_.invalidate(changed_locs);
599 if(get_map_context().needs_labels_reset()) {
600 get_map_context().reset_starting_position_labels(gui_);
603 get_map_context().clear_changed_locations();
604 gui_.recalculate_minimap();
607 void context_manager::resize_map_dialog()
609 const editor_map& map = get_map_context().map();
617 if(!gui2::dialogs::editor_resize_map::execute(
w,
h, dir, copy)) {
621 if(
w != map.
w() ||
h != map.
h()) {
627 int x_offset = map.
w() -
w;
628 int y_offset = map.
h() -
h;
647 WRN_ED <<
"Unknown resize expand direction";
676 void context_manager::save_map_as_dialog()
678 bool first_pick =
false;
679 std::string input_name = get_map_context().get_filename();
680 if(input_name.empty()) {
682 if (editor_controller::current_addon_id_.empty()) {
704 std::string last_folder = save_path.parent_path().filename().string();
705 if ((last_folder ==
"scenarios")
709 VGETTEXT(
"Do you really want to save $type1 in $type2 folder?", {{
"type1",
"map"}, {
"type2",
"scenarios"}}),
715 std::size_t is_open = check_open_map(save_path.string());
716 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
720 std::string old_filename = get_map_context().get_filename();
722 get_map_context().set_filename(save_path.string());
724 if(!write_map(
true)) {
725 get_map_context().set_filename(old_filename);
729 void context_manager::save_scenario_as_dialog()
731 bool first_pick =
false;
732 std::string input_name = get_map_context().get_filename();
733 if(input_name.empty()) {
753 std::string last_folder = save_path.parent_path().filename().string();
754 if ((last_folder ==
"maps")
758 VGETTEXT(
"Do you really want to save $type1 in $type2 folder?", {{
"type1",
"scenario"}, {
"type2",
"maps"}}),
764 std::size_t is_open = check_open_map(save_path.string());
765 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
770 std::string old_filename = get_map_context().get_filename();
772 get_map_context().set_filename(save_path.string());
774 if(!write_scenario(
true)) {
775 get_map_context().set_filename(old_filename);
783 if(
i[
"map_generation"].empty() &&
i[
"scenario_generation"].empty()) {
788 if(
const auto generator_cfg =
i.optional_child(
"generator")) {
789 map_generators_.emplace_back(
create_map_generator(
i[
"map_generation"].empty() ?
i[
"scenario_generation"] :
i[
"map_generation"], generator_cfg.value()));
791 ERR_ED <<
"Scenario \"" <<
i[
"name"] <<
"\" with id " <<
i[
"id"]
792 <<
" has map_generation= but no [generator] tag";
797 void context_manager::generate_map_dialog()
799 if(map_generators_.empty()) {
808 std::string map_string;
817 if(map_string.empty()) {
822 get_map_context().set_needs_labels_reset();
830 bool context_manager::confirm_discard()
832 if(get_map_context().modified()) {
841 void context_manager::fill_selection()
846 void context_manager::save_all_maps()
848 int current = current_context_index_;
849 for(std::size_t
i = 0;
i < map_contexts_.size(); ++
i) {
853 switch_context(current);
856 void context_manager::save_contexts()
858 saved_contexts_.swap(map_contexts_);
859 std::swap(last_context_, current_context_index_);
860 create_blank_context();
861 switch_context(0,
true);
864 void context_manager::save_map(
bool show_confirmation)
866 const std::string& name = get_map_context().get_filename();
868 if(get_map_context().is_pure_map()) {
869 save_map_as_dialog();
871 save_scenario_as_dialog();
874 if(get_map_context().is_pure_map()) {
875 write_map(show_confirmation);
877 write_scenario(show_confirmation);
882 bool context_manager::write_scenario(
bool display_confirmation)
885 get_map_context().save_scenario();
886 if(display_confirmation) {
887 gui_.set_status(
_(
"Scenario saved."),
true);
890 gui_.set_status(
e.what(),
false);
897 bool context_manager::write_map(
bool display_confirmation)
900 get_map_context().save_map();
901 if(display_confirmation) {
902 gui_.set_status(
_(
"Map saved"),
true);
905 gui_.set_status(
e.what(),
false);
912 std::size_t context_manager::check_open_map(
const std::string& fn)
const
915 while(
i < map_contexts_.size() && map_contexts_[
i]->get_filename() != fn) {
922 bool context_manager::check_switch_open_map(
const std::string& fn)
924 std::size_t
i = check_open_map(fn);
925 if(
i < map_contexts_.size()) {
934 void context_manager::load_map(
const std::string&
filename,
bool new_context)
936 if(new_context && check_switch_open_map(
filename)) {
941 if(editor_controller::current_addon_id_.empty()) {
945 editor_controller::current_addon_id_ = addon_at_path.value();
950 set_addon_id(editor_controller::current_addon_id_);
953 if(editor_controller::current_addon_id_.empty()) {
958 LOG_ED <<
"Load map: " <<
filename << (new_context ?
" (new)" :
" (same)");
961 auto mc = std::make_unique<map_context>(game_config_,
filename, current_addon_);
962 if(mc->get_filename() !=
filename) {
963 if(new_context && check_switch_open_map(mc->get_filename())) {
969 int new_id = add_map_context_of(std::move(mc));
970 switch_context(new_id);
972 replace_map_context_with(std::move(mc));
976 if(get_map_context().is_embedded()) {
977 const std::string&
msg =
_(
"Loaded embedded map data");
990 void context_manager::revert_map()
992 if(!confirm_discard()) {
996 std::string
filename = get_map_context().get_filename();
998 ERR_ED <<
"Empty filename in map revert";
1011 int new_id = add_map_context(m,
true, default_schedule, current_addon_);
1012 switch_context(new_id);
1014 replace_map_context(m,
true, default_schedule, current_addon_);
1020 auto default_schedule = game_config_.find_child(
"editor_times",
"id",
"empty");
1024 int new_id = add_map_context(m,
false, *default_schedule, current_addon_);
1025 switch_context(new_id);
1027 replace_map_context(m,
false, *default_schedule, current_addon_);
1031 get_map_context().new_side();
1032 gui().set_viewing_team_index(0,
true);
1033 gui().set_playing_team_index(0);
1041 template<
typename... T>
1042 int context_manager::add_map_context(
const T&... args)
1044 map_contexts_.emplace_back(std::make_unique<map_context>(args...));
1045 return map_contexts_.size() - 1;
1048 int context_manager::add_map_context_of(std::unique_ptr<map_context>&& mc)
1050 map_contexts_.emplace_back(std::move(mc));
1051 return map_contexts_.size() - 1;
1054 template<
typename... T>
1055 void context_manager::replace_map_context(
const T&... args)
1057 replace_map_context_with(std::move(std::make_unique<map_context>(args...)));
1060 void context_manager::replace_map_context_with(std::unique_ptr<map_context>&& mc)
1062 map_contexts_[current_context_index_].swap(mc);
1063 refresh_on_context_change();
1066 void context_manager::create_default_context()
1068 if(saved_contexts_.empty()) {
1069 create_blank_context();
1070 switch_context(0,
true);
1072 saved_contexts_.swap(map_contexts_);
1073 switch_context(last_context_,
true);
1078 void context_manager::create_blank_context()
1087 void context_manager::close_current_context()
1089 if(!confirm_discard())
return;
1091 if(map_contexts_.size() == 1) {
1092 create_default_context();
1093 map_contexts_.erase(map_contexts_.begin());
1094 }
else if(current_context_index_ ==
static_cast<int>(map_contexts_.size()) - 1) {
1095 map_contexts_.pop_back();
1096 current_context_index_--;
1098 map_contexts_.erase(map_contexts_.begin() + current_context_index_);
1101 refresh_on_context_change();
1104 void context_manager::switch_context(
const int index,
const bool force)
1106 if(
index < 0 ||
static_cast<std::size_t
>(
index) >= map_contexts_.size()) {
1107 WRN_ED <<
"Invalid index in switch map context: " <<
index;
1111 if(
index == current_context_index_ && !force) {
1117 get_map_context().get_labels().enable(
false);
1119 current_context_index_ =
index;
1121 refresh_on_context_change();
1126 std::string name = get_map_context().get_name();
1133 name = get_map_context().get_default_context_name();
A config object defines a single node in a WML file, with access to child nodes.
config & find_mandatory_child(config_key_type key, const std::string &name, const std::string &value)
void set_viewing_team_index(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
void recalculate_minimap()
Schedule the minimap for recalculation.
void set_playing_team_index(std::size_t team)
sets the team whose turn it currently is
void rebuild_all()
Rebuild all dynamic terrain.
void change_display_context(const display_context *dc)
void invalidate_all()
Function to invalidate all tiles.
void init_flags()
Init the flag list and the team colors used by ~TC.
void reload_map()
Updates internals that cache map size.
std::vector< std::unique_ptr< map_context > > map_contexts_
The currently opened map context object.
context_manager(editor_display &gui, const game_config_view &game_config, const std::string &addon_id)
bool is_active_transitions_hotkey(const std::string &item)
void new_scenario(int width, int height, const t_translation::terrain_code &fill, bool new_context)
Create a new scenario.
void new_map(int width, int height, const t_translation::terrain_code &fill, bool new_context)
Create a new map.
void new_map_dialog()
Display a new map dialog and process user input.
int auto_update_transitions_
Flag to rebuild terrain on every terrain change.
void load_map_dialog(bool force_same_context=false)
Display a load map dialog and process user input.
void set_window_title()
Displays the specified map name in the window titlebar.
void refresh_all()
Refresh everything, i.e.
void init_map_generators(const game_config_view &game_config)
init available random map generators
bool toggle_update_transitions()
void create_default_context()
Creates a default map context object, used to ensure there is always at least one.
void new_scenario_dialog()
Display a new map dialog and process user input.
void load_map(const std::string &filename, bool new_context)
Load a map given the filename.
void reload_map()
Reload the map after it has significantly changed (when e.g.
void edit_scenario_dialog()
Display a scenario edit dialog and process user input.
std::string current_addon_
The currently selected add-on.
void edit_side_dialog(const team &t)
Display a side edit dialog and process user input.
void load_mru_item(unsigned index, bool force_same_context=false)
Open the specified entry from the recent files list.
map_context & get_map_context()
Get the current map context object.
class location_palette * locs_
void expand_load_mru_menu(std::vector< config > &items, int i)
Menu expanding for most recent loaded list.
void expand_open_maps_menu(std::vector< config > &items, int i)
Menu expanding for open maps list.
void refresh_on_context_change()
Performs the necessary housekeeping necessary when switching contexts.
std::size_t modified_maps(std::string &modified)
Paint the same terrain on a number of locations on the map.
Replace contents of the entire map, Useful as a fallback undo method when something else would be imp...
Base class for all editor actions.
static std::string current_addon_id_
This class adds extra editor-specific functionality to a normal gamemap.
void add_item(const std::string &id)
This class wraps around a map to provide a concise interface for the editor to work with.
void set_needs_reload(bool value=true)
Setter for the reload flag.
const std::string & get_id() const
bool random_start_time() const
void set_side_setup(editor_team_info &info)
const std::string & get_description() const
void clear_changed_locations()
const tod_manager * get_time_manager() const
void reset_starting_position_labels(display &disp)
virtual const editor_map & map() const override
Const map accessor.
void set_needs_terrain_rebuild(bool value=true)
Setter for the terrain rebuild flag.
const t_string get_default_context_name() const
const std::string & get_filename() const
map_labels & get_labels()
virtual const std::vector< team > & teams() const override
Const teams accessor.
game_classification & get_classification()
bool victory_defeated() const
utils::optional< int > get_xp_mod() const
void set_scenario_setup(const std::string &id, const std::string &name, const std::string &description, int turns, int xp_mod, bool victory_defeated, bool random_time)
const std::string & get_name() const
A class grating read only view to a vector of config objects, viewed as one config with all children ...
int w() const
Effective map width.
int h() const
Effective map height.
utils::optional< uint32_t > get_seed()
void select_map_generator(map_generator *mg)
map_generator * get_selected_map_generator()
file_dialog & set_extension(const std::string &value)
Sets allowed file extensions for file names in save mode.
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
file_dialog & set_save_mode(bool value)
Sets the dialog's behavior on non-existent file name inputs.
std::string path() const
Gets the current file selection.
file_dialog & add_extra_path(desktop::GAME_PATH_TYPES path)
@ yes_no_buttons
Shows a yes and no button.
bool show(const unsigned auto_close_time=0)
Shows the window.
virtual std::string create_map(utils::optional< uint32_t > randomseed={})=0
Creates a new map and returns it.
void enable(bool is_enabled)
std::vector< std::string > recent_files()
Retrieves the list of recently opened files.
This class stores all the data for a single 'side' (in game nomenclature).
int number_of_turns() const
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
std::vector< std::string > get_area_ids() const
const std::set< map_location > & get_area_by_index(int index) const
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Declarations for File-IO.
static std::string _(const char *str)
std::string label
What to show in the filter's drop-down list.
map_generator * create_map_generator(const std::string &name, const config &cfg, const config *vars)
@ GAME_EDITOR_MAP_DIR
Editor map dir.
void fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
Manage the empty-palette in the editor.
const t_translation::terrain_code & get_selected_bg_terrain()
std::string initialize_addon()
std::string get_legacy_editor_dir()
const std::string wml_extension
bool is_cfg(const std::string &filename)
Returns true if the file ends with the wmlfile extension.
static bfs::path get_dir(const bfs::path &dirpath)
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
bool rename_dir(const std::string &old_dir, const std::string &new_dir)
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
const std::string map_extension
utils::optional< std::string > get_addon_id_from_path(const std::string &location)
Returns the add-on ID from a path.
const std::string mask_extension
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
std::string get_current_editor_dir(const std::string &addon_id)
const std::string unicode_bullet
Game configuration data as global variables.
std::string get_default_title_string()
std::string default_terrain
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.
@ OK
Dialog was closed with the OK button.
@ CANCEL
Dialog was closed with the CANCEL button.
std::string italic(Args &&... data)
std::string span_color(const color_t &color, Args &&... data)
const int TRANSITION_UPDATE_ON
const int TRANSITION_UPDATE_COUNT
const int TRANSITION_UPDATE_OFF
const int TRANSITION_UPDATE_PARTIAL
::tod_manager * tod_manager
game_classification * classification
filter_context * filter_con
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
const terrain_code NONE_TERRAIN
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.
void set_window_title(const std::string &title)
Sets the title of the main window.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
static std::string get_filename(const std::string &file_code)
std::string filename
Filename.
Encapsulates the map of the game.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Object which defines a time of day with associated bonuses, image, sounds etc.
bool addon_filename_legal(const std::string &name)
Checks whether an add-on file name is legal or not.