16 #define GETTEXT_DOMAIN "wesnoth-editor"
54 #include <boost/algorithm/string.hpp>
55 #include <boost/filesystem.hpp>
59 std::vector<std::unique_ptr<editor::map_context>> saved_contexts_;
60 int last_context_ = 0;
62 const std::string get_menu_marker(
const bool changed)
64 std::ostringstream ss;
68 ss <<
"color='#f00' ";
84 , current_addon_(addon_id)
86 , last_map_generator_(nullptr)
87 , current_context_index_(0)
88 , auto_update_transitions_(
prefs::
get().editor_auto_update_transitions())
114 if (
gui().get_teams().
size() > 0) {
137 for(
const auto& loc :
get_map_context().map().special_locations().left) {
144 for(
size_t i = 10;
i <= n_teams;
i++) {
163 return (
item ==
"editor-auto-update-transitions");
165 return (
item ==
"editor-partial-update-transitions");
167 return (
item ==
"editor-no-update-transitions");
187 std::vector<std::string> modified;
190 if(!mc->get_name().empty()) {
191 modified.push_back(mc->get_name());
192 }
else if(!mc->get_filename().empty()) {
193 modified.push_back(mc->get_filename());
195 modified.push_back(mc->get_default_context_name());
200 for(std::string& str : modified) {
204 return modified.size();
231 if(mru.empty() ||
index >= mru.size()) {
242 if(gui2::dialogs::editor_edit_side::execute(team_info)) {
258 gui2::dialogs::prompt::execute(new_addon_id);
263 std::string main_cfg = new_dir +
"/_main.cfg";
284 std::string
id = context.
get_id();
285 std::string name = context.
get_name();
294 const bool ok = gui2::dialogs::editor_edit_scenario::execute(
295 id, name, description, turns, xp_mod, victory, random
316 if(gui2::dialogs::editor_new_map::execute(
_(
"New Map"),
w,
h)) {
329 if(gui2::dialogs::editor_new_map::execute(
_(
"New Scenario"),
w,
h)) {
337 auto pos = items.erase(items.begin() +
i);
338 std::vector<config> contexts;
340 for(std::size_t mci = 0; mci <
map_contexts_.size(); ++mci) {
354 std::ostringstream ss;
355 ss <<
"[" << mci + 1 <<
"] ";
369 const std::string
label = ss.str();
370 const std::string details = get_menu_marker(changed);
372 contexts.emplace_back(
"label",
label,
"details", details);
375 items.insert(pos, contexts.begin(), contexts.end());
382 auto pos = items.erase(items.begin() +
i);
385 items.insert(pos,
config {
"label",
_(
"No Recent Files")});
389 for(std::string&
path : mru) {
396 std::vector<config> temp;
397 std::transform(mru.begin(), mru.end(), std::back_inserter(temp), [](
const std::string& str) {
398 return config {
"label", str};
401 items.insert(pos, temp.begin(), temp.end());
404 void context_manager::expand_areas_menu(std::vector<config>& items,
int i)
406 tod_manager* tod = get_map_context().get_time_manager();
411 auto pos = items.erase(items.begin() +
i);
412 std::vector<config> area_entries;
414 std::vector<std::string> area_ids = tod->
get_area_ids();
416 for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
417 const std::string& area = area_ids[mci];
419 std::stringstream ss;
420 ss <<
"[" << mci + 1 <<
"] ";\
423 ss <<
"<i>" <<
_(
"Unnamed Area") <<
"</i>";
429 mci ==
static_cast<std::size_t
>(get_map_context().get_active_area())
432 const std::string
label = ss.str();
433 const std::string details = get_menu_marker(changed);
435 area_entries.emplace_back(
"label",
label,
"details", details);
438 items.insert(pos, area_entries.begin(), area_entries.end());
441 void context_manager::expand_sides_menu(std::vector<config>& items,
int i)
443 auto pos = items.erase(items.begin() +
i);
444 std::vector<config> contexts;
446 for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
448 const team&
t = get_map_context().teams()[mci];
449 const std::string& teamname =
t.user_team_name();
450 std::stringstream
label;
451 label <<
"[" << mci+1 <<
"] ";
453 if(teamname.empty()) {
454 label <<
"<i>" <<
_(
"New Side") <<
"</i>";
459 contexts.emplace_back(
"label",
label.str());
462 items.insert(pos, contexts.begin(), contexts.end());
465 void context_manager::expand_time_menu(std::vector<config>& items,
int i)
467 auto pos = items.erase(items.begin() +
i);
468 std::vector<config> times;
470 tod_manager* tod_m = get_map_context().get_time_manager();
472 assert(tod_m !=
nullptr);
476 "details", time.name,
481 items.insert(pos, times.begin(), times.end());
484 void context_manager::expand_local_time_menu(std::vector<config>& items,
int i)
486 auto pos = items.erase(items.begin() +
i);
487 std::vector<config> times;
489 tod_manager* tod_m = get_map_context().get_time_manager();
491 for(
const time_of_day& time : tod_m->
times(get_map_context().get_active_area())) {
493 "details", time.name,
498 items.insert(pos, times.begin(), times.end());
501 void context_manager::apply_mask_dialog()
503 std::string fn = get_map_context().get_filename();
528 void context_manager::perform_refresh(
const editor_action& action,
bool drag_part )
530 get_map_context().perform_action(action);
531 refresh_after_action(drag_part);
534 void context_manager::rename_area_dialog()
536 int active_area = get_map_context().get_active_area();
537 std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
539 if(gui2::dialogs::edit_text::execute(
N_(
"Rename Area"),
N_(
"Identifier:"), name)) {
540 get_map_context().get_time_manager()->set_area_id(active_area, name);
544 void context_manager::create_mask_to_dialog()
546 std::string fn = get_map_context().get_filename();
571 void context_manager::refresh_after_action(
bool drag_part)
573 if(get_map_context().needs_reload()) {
578 const std::set<map_location>& changed_locs = get_map_context().changed_locations();
580 if(get_map_context().needs_terrain_rebuild()) {
583 && (!drag_part || get_map_context().everything_changed())))
586 get_map_context().set_needs_terrain_rebuild(
false);
587 gui_.invalidate_all();
590 gui_.rebuild_terrain(loc);
592 gui_.invalidate(changed_locs);
595 if(get_map_context().everything_changed()) {
596 gui_.invalidate_all();
598 gui_.invalidate(changed_locs);
602 if(get_map_context().needs_labels_reset()) {
603 get_map_context().reset_starting_position_labels(gui_);
606 get_map_context().clear_changed_locations();
607 gui_.recalculate_minimap();
610 void context_manager::resize_map_dialog()
612 const editor_map& map = get_map_context().map();
620 if(!gui2::dialogs::editor_resize_map::execute(
w,
h, dir, copy)) {
624 if(
w != map.
w() ||
h != map.
h()) {
630 int x_offset = map.
w() -
w;
631 int y_offset = map.
h() -
h;
650 WRN_ED <<
"Unknown resize expand direction";
679 void context_manager::save_map_as_dialog()
681 bool first_pick =
false;
682 std::string input_name = get_map_context().get_filename();
683 if(input_name.empty()) {
685 if (editor_controller::current_addon_id_.empty()) {
707 std::string last_folder = save_path.parent_path().filename().string();
708 if ((last_folder ==
"scenarios")
712 VGETTEXT(
"Do you really want to save $type1 in $type2 folder?", {{
"type1",
"map"}, {
"type2",
"scenarios"}}),
718 std::size_t is_open = check_open_map(save_path.string());
719 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
723 std::string old_filename = get_map_context().get_filename();
725 get_map_context().set_filename(save_path.string());
727 if(!write_map(
true)) {
728 get_map_context().set_filename(old_filename);
732 void context_manager::save_scenario_as_dialog()
734 bool first_pick =
false;
735 std::string input_name = get_map_context().get_filename();
736 if(input_name.empty()) {
756 std::string last_folder = save_path.parent_path().filename().string();
757 if ((last_folder ==
"maps")
761 VGETTEXT(
"Do you really want to save $type1 in $type2 folder?", {{
"type1",
"scenario"}, {
"type2",
"maps"}}),
767 std::size_t is_open = check_open_map(save_path.string());
768 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
773 std::string old_filename = get_map_context().get_filename();
775 get_map_context().set_filename(save_path.string());
777 if(!write_scenario(
true)) {
778 get_map_context().set_filename(old_filename);
786 if(
i[
"map_generation"].empty() &&
i[
"scenario_generation"].empty()) {
791 if(
const auto generator_cfg =
i.optional_child(
"generator")) {
792 map_generators_.emplace_back(
create_map_generator(
i[
"map_generation"].empty() ?
i[
"scenario_generation"] :
i[
"map_generation"], generator_cfg.value()));
794 ERR_ED <<
"Scenario \"" <<
i[
"name"] <<
"\" with id " <<
i[
"id"]
795 <<
" has map_generation= but no [generator] tag";
800 void context_manager::generate_map_dialog()
802 if(map_generators_.empty()) {
811 std::string map_string;
820 if(map_string.empty()) {
825 get_map_context().set_needs_labels_reset();
833 bool context_manager::confirm_discard()
835 if(get_map_context().modified()) {
844 void context_manager::fill_selection()
849 void context_manager::save_all_maps()
851 int current = current_context_index_;
852 for(std::size_t
i = 0;
i < map_contexts_.size(); ++
i) {
856 switch_context(current);
859 void context_manager::save_contexts()
861 saved_contexts_.swap(map_contexts_);
862 std::swap(last_context_, current_context_index_);
863 create_blank_context();
864 switch_context(0,
true);
867 void context_manager::save_map(
bool show_confirmation)
869 const std::string& name = get_map_context().get_filename();
871 if(get_map_context().is_pure_map()) {
872 save_map_as_dialog();
874 save_scenario_as_dialog();
877 if(get_map_context().is_pure_map()) {
878 write_map(show_confirmation);
880 write_scenario(show_confirmation);
885 bool context_manager::write_scenario(
bool display_confirmation)
888 get_map_context().save_scenario();
889 if(display_confirmation) {
890 gui_.set_status(
_(
"Scenario saved."),
true);
893 gui_.set_status(
e.what(),
false);
900 bool context_manager::write_map(
bool display_confirmation)
903 get_map_context().save_map();
904 if(display_confirmation) {
905 gui_.set_status(
_(
"Map saved"),
true);
908 gui_.set_status(
e.what(),
false);
915 std::size_t context_manager::check_open_map(
const std::string& fn)
const
918 while(
i < map_contexts_.size() && map_contexts_[
i]->get_filename() != fn) {
925 bool context_manager::check_switch_open_map(
const std::string& fn)
927 std::size_t
i = check_open_map(fn);
928 if(
i < map_contexts_.size()) {
937 void context_manager::load_map(
const std::string&
filename,
bool new_context)
939 if(new_context && check_switch_open_map(
filename)) {
944 if(editor_controller::current_addon_id_.empty()) {
948 editor_controller::current_addon_id_ = addon_at_path.value();
953 set_addon_id(editor_controller::current_addon_id_);
956 if(editor_controller::current_addon_id_.empty()) {
961 LOG_ED <<
"Load map: " <<
filename << (new_context ?
" (new)" :
" (same)");
964 auto mc = std::make_unique<map_context>(game_config_,
filename, current_addon_);
965 if(mc->get_filename() !=
filename) {
966 if(new_context && check_switch_open_map(mc->get_filename())) {
972 int new_id = add_map_context_of(std::move(mc));
973 switch_context(new_id);
975 replace_map_context_with(std::move(mc));
979 if(get_map_context().is_embedded()) {
980 const std::string&
msg =
_(
"Loaded embedded map data");
993 void context_manager::revert_map()
995 if(!confirm_discard()) {
999 std::string
filename = get_map_context().get_filename();
1001 ERR_ED <<
"Empty filename in map revert";
1014 int new_id = add_map_context(m,
true, default_schedule, current_addon_);
1015 switch_context(new_id);
1017 replace_map_context(m,
true, default_schedule, current_addon_);
1023 auto default_schedule = game_config_.find_child(
"editor_times",
"id",
"empty");
1027 int new_id = add_map_context(m,
false, *default_schedule, current_addon_);
1028 switch_context(new_id);
1030 replace_map_context(m,
false, *default_schedule, current_addon_);
1034 get_map_context().new_side();
1035 gui().set_viewing_team_index(0,
true);
1036 gui().set_playing_team_index(0);
1044 template<
typename... T>
1045 int context_manager::add_map_context(
const T&... args)
1047 map_contexts_.emplace_back(std::make_unique<map_context>(args...));
1048 return map_contexts_.size() - 1;
1051 int context_manager::add_map_context_of(std::unique_ptr<map_context>&& mc)
1053 map_contexts_.emplace_back(std::move(mc));
1054 return map_contexts_.size() - 1;
1057 template<
typename... T>
1058 void context_manager::replace_map_context(
const T&... args)
1060 replace_map_context_with(std::move(std::make_unique<map_context>(args...)));
1063 void context_manager::replace_map_context_with(std::unique_ptr<map_context>&& mc)
1065 map_contexts_[current_context_index_].swap(mc);
1066 refresh_on_context_change();
1069 void context_manager::create_default_context()
1071 if(saved_contexts_.empty()) {
1072 create_blank_context();
1073 switch_context(0,
true);
1075 saved_contexts_.swap(map_contexts_);
1076 switch_context(last_context_,
true);
1081 void context_manager::create_blank_context()
1090 void context_manager::close_current_context()
1092 if(!confirm_discard())
return;
1094 if(map_contexts_.size() == 1) {
1095 create_default_context();
1096 map_contexts_.erase(map_contexts_.begin());
1097 }
else if(current_context_index_ ==
static_cast<int>(map_contexts_.size()) - 1) {
1098 map_contexts_.pop_back();
1099 current_context_index_--;
1101 map_contexts_.erase(map_contexts_.begin() + current_context_index_);
1104 refresh_on_context_change();
1107 void context_manager::switch_context(
const int index,
const bool force)
1109 if(
index < 0 ||
static_cast<std::size_t
>(
index) >= map_contexts_.size()) {
1110 WRN_ED <<
"Invalid index in switch map context: " <<
index;
1114 if(
index == current_context_index_ && !force) {
1120 get_map_context().get_labels().enable(
false);
1122 current_context_index_ =
index;
1124 refresh_on_context_change();
1129 std::string name = get_map_context().get_name();
1136 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::pair< std::string, unsigned > item
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.