16 #define GETTEXT_DOMAIN "wesnoth-editor"
54 #include <boost/algorithm/string.hpp>
62 std::ostringstream ss;
66 ss <<
"color='#f00' ";
78 , current_addon_(addon_id)
80 , last_map_generator_(nullptr)
81 , current_context_index_(0)
113 if (
gui().get_teams().
size() > 0) {
136 for(
const auto& loc :
get_map_context().map().special_locations().left) {
143 for(
size_t i = 10;
i <= n_teams;
i++) {
162 return (
item ==
"editor-auto-update-transitions");
164 return (
item ==
"editor-partial-update-transitions");
166 return (
item ==
"editor-no-update-transitions");
186 std::vector<std::string> modified;
189 if(!mc->get_name().empty()) {
190 modified.push_back(mc->get_name());
191 }
else if(!mc->get_filename().empty()) {
192 modified.push_back(mc->get_filename());
194 modified.push_back(mc->get_default_context_name());
199 for(std::string& str : modified) {
203 return modified.size();
226 if(mru.empty() ||
index >= mru.size()) {
239 if(gui2::dialogs::editor_edit_side::execute(team_info)) {
255 gui2::dialogs::prompt::execute(new_addon_id);
279 std::string
id = context.
get_id();
280 std::string name = context.
get_name();
289 const bool ok = gui2::dialogs::editor_edit_scenario::execute(
290 id, name, description,
turns, xp_mod, victory, random
311 if(gui2::dialogs::editor_new_map::execute(
_(
"New Map"),
w,
h)) {
324 if(gui2::dialogs::editor_new_map::execute(
_(
"New Scenario"),
w,
h)) {
333 std::vector<config> contexts;
335 for(std::size_t mci = 0; mci <
map_contexts_.size(); ++mci) {
338 std::string filename;
345 if(filename.empty()) {
349 std::ostringstream ss;
350 ss <<
"[" << mci + 1 <<
"] ";
355 ss <<
"<i>" << filename <<
"</i>";
364 const std::string
label = ss.str();
367 contexts.emplace_back(
"label",
label,
"details", details);
370 items.insert(pos, contexts.begin(), contexts.end());
380 items.insert(pos,
config {
"label",
_(
"No Recent Files")});
384 for(std::string&
path : mru) {
391 std::vector<config> temp;
392 std::transform(mru.begin(), mru.end(), std::back_inserter(temp), [](
const std::string& str) {
393 return config {
"label", str};
396 items.insert(pos, temp.begin(), temp.end());
399 void context_manager::expand_areas_menu(std::vector<config>&
items,
int i)
401 tod_manager* tod = get_map_context().get_time_manager();
407 std::vector<config> area_entries;
409 std::vector<std::string> area_ids = tod->
get_area_ids();
411 for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
412 const std::string& area = area_ids[mci];
414 std::stringstream ss;
415 ss <<
"[" << mci + 1 <<
"] ";\
418 ss <<
"<i>" <<
_(
"Unnamed Area") <<
"</i>";
424 mci ==
static_cast<std::size_t
>(get_map_context().get_active_area())
427 const std::string
label = ss.str();
430 area_entries.emplace_back(
"label",
label,
"details", details);
433 items.insert(pos, area_entries.begin(), area_entries.end());
436 void context_manager::expand_sides_menu(std::vector<config>&
items,
int i)
439 std::vector<config> contexts;
441 for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
443 const team&
t = get_map_context().teams()[mci];
444 const std::string& teamname =
t.user_team_name();
445 std::stringstream
label;
446 label <<
"[" << mci+1 <<
"] ";
448 if(teamname.empty()) {
449 label <<
"<i>" <<
_(
"New Side") <<
"</i>";
454 contexts.emplace_back(
"label",
label.str());
457 items.insert(pos, contexts.begin(), contexts.end());
460 void context_manager::expand_time_menu(std::vector<config>&
items,
int i)
463 std::vector<config> times;
465 tod_manager* tod_m = get_map_context().get_time_manager();
467 assert(tod_m !=
nullptr);
471 "details", time.name,
476 items.insert(pos, times.begin(), times.end());
479 void context_manager::expand_local_time_menu(std::vector<config>&
items,
int i)
482 std::vector<config> times;
484 tod_manager* tod_m = get_map_context().get_time_manager();
486 for(
const time_of_day& time : tod_m->
times(get_map_context().get_active_area())) {
488 "details", time.name,
493 items.insert(pos, times.begin(), times.end());
496 void context_manager::apply_mask_dialog()
498 std::string fn = get_map_context().get_filename();
523 void context_manager::perform_refresh(
const editor_action& action,
bool drag_part )
525 get_map_context().perform_action(action);
526 refresh_after_action(drag_part);
529 void context_manager::rename_area_dialog()
531 int active_area = get_map_context().get_active_area();
532 std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
534 if(gui2::dialogs::edit_text::execute(
N_(
"Rename Area"),
N_(
"Identifier:"), name)) {
535 get_map_context().get_time_manager()->set_area_id(active_area, name);
539 void context_manager::create_mask_to_dialog()
541 std::string fn = get_map_context().get_filename();
566 void context_manager::refresh_after_action(
bool drag_part)
568 if(get_map_context().needs_reload()) {
573 const std::set<map_location>& changed_locs = get_map_context().changed_locations();
575 if(get_map_context().needs_terrain_rebuild()) {
578 && (!drag_part || get_map_context().everything_changed())))
581 get_map_context().set_needs_terrain_rebuild(
false);
582 gui_.invalidate_all();
585 gui_.rebuild_terrain(loc);
587 gui_.invalidate(changed_locs);
590 if(get_map_context().everything_changed()) {
591 gui_.invalidate_all();
593 gui_.invalidate(changed_locs);
597 if(get_map_context().needs_labels_reset()) {
598 get_map_context().reset_starting_position_labels(gui_);
601 get_map_context().clear_changed_locations();
602 gui_.recalculate_minimap();
605 void context_manager::resize_map_dialog()
607 const editor_map& map = get_map_context().map();
615 if(!gui2::dialogs::editor_resize_map::execute(
w,
h, dir, copy)) {
619 if(
w != map.
w() ||
h != map.
h()) {
625 int x_offset = map.
w() -
w;
626 int y_offset = map.
h() -
h;
645 WRN_ED <<
"Unknown resize expand direction";
674 void context_manager::save_map_as_dialog()
676 std::string input_name = get_map_context().get_filename();
677 if(input_name.empty()) {
692 std::size_t is_open = check_open_map(dlg.
path());
693 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
697 std::string old_filename = get_map_context().get_filename();
699 get_map_context().set_filename(dlg.
path());
701 if(!write_map(
true)) {
702 get_map_context().set_filename(old_filename);
706 void context_manager::save_scenario_as_dialog()
708 std::string input_name = get_map_context().get_filename();
709 if(input_name.empty()) {
725 std::size_t is_open = check_open_map(dlg.
path());
726 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
731 std::string old_filename = get_map_context().get_filename();
733 get_map_context().set_filename(dlg.
path());
735 if(!write_scenario(
true)) {
736 get_map_context().set_filename(old_filename);
744 if(
i[
"map_generation"].empty() &&
i[
"scenario_generation"].empty()) {
749 if(
const auto generator_cfg =
i.optional_child(
"generator")) {
750 map_generators_.emplace_back(
create_map_generator(
i[
"map_generation"].empty() ?
i[
"scenario_generation"] :
i[
"map_generation"], generator_cfg.value()));
752 ERR_ED <<
"Scenario \"" <<
i[
"name"] <<
"\" with id " <<
i[
"id"]
753 <<
" has map_generation= but no [generator] tag";
758 void context_manager::generate_map_dialog()
760 if(map_generators_.empty()) {
769 std::string map_string;
778 if(map_string.empty()) {
783 get_map_context().set_needs_labels_reset();
791 bool context_manager::confirm_discard()
793 if(get_map_context().modified()) {
802 void context_manager::fill_selection()
807 void context_manager::save_all_maps(
bool auto_save_windows)
809 int current = current_context_index_;
811 for(std::size_t
i = 0;
i < map_contexts_.size(); ++
i) {
813 std::string name = get_map_context().get_filename();
814 if(auto_save_windows) {
816 std::ostringstream
s;
817 s << default_dir_ <<
"/" <<
"window_" <<
i + 1;
818 if(!get_map_context().is_embedded() && !get_map_context().is_pure_map()) {
824 get_map_context().set_filename(name);
831 switch_context(current);
834 void context_manager::save_map()
836 const std::string& name = get_map_context().get_filename();
838 if(get_map_context().is_pure_map()) {
839 save_map_as_dialog();
841 save_scenario_as_dialog();
844 if(get_map_context().is_pure_map()) {
852 bool context_manager::write_scenario(
bool display_confirmation)
855 get_map_context().save_scenario();
856 if(display_confirmation) {
867 bool context_manager::write_map(
bool display_confirmation)
870 get_map_context().save_map();
871 if(display_confirmation) {
882 std::size_t context_manager::check_open_map(
const std::string& fn)
const
885 while(
i < map_contexts_.size() && map_contexts_[
i]->get_filename() != fn) {
892 bool context_manager::check_switch_open_map(
const std::string& fn)
894 std::size_t
i = check_open_map(fn);
895 if(
i < map_contexts_.size()) {
904 void context_manager::load_map(
const std::string& filename,
bool new_context)
906 if(new_context && check_switch_open_map(filename)) {
911 if(editor_controller::current_addon_id_ ==
"") {
915 if(editor_controller::current_addon_id_ ==
"") {
918 set_addon_id(editor_controller::current_addon_id_);
921 if(editor_controller::current_addon_id_ ==
"") {
926 LOG_ED <<
"Load map: " << filename << (new_context ?
" (new)" :
" (same)");
930 if(mc->get_filename() != filename) {
931 if(new_context && check_switch_open_map(mc->get_filename())) {
937 int new_id = add_map_context_of(std::move(mc));
938 switch_context(new_id);
940 replace_map_context_with(std::move(mc));
944 if(get_map_context().is_embedded()) {
945 const std::string&
msg =
_(
"Loaded embedded map data");
958 void context_manager::revert_map()
960 if(!confirm_discard()) {
964 std::string filename = get_map_context().get_filename();
965 if(filename.empty()) {
966 ERR_ED <<
"Empty filename in map revert";
970 load_map(filename,
false);
979 int new_id = add_map_context(m,
true, default_schedule, current_addon_);
980 switch_context(new_id);
982 replace_map_context(m,
true, default_schedule, current_addon_);
988 auto default_schedule = game_config_.find_child(
"editor_times",
"id",
"empty");
992 int new_id = add_map_context(m,
false, *default_schedule, current_addon_);
993 switch_context(new_id);
995 replace_map_context(m,
false, *default_schedule, current_addon_);
999 get_map_context().new_side();
1000 gui().set_team(0,
true);
1001 gui().set_playing_team(0);
1009 template<
typename... T>
1010 int context_manager::add_map_context(
const T&... args)
1012 map_contexts_.emplace_back(
new map_context(args...));
1013 return map_contexts_.size() - 1;
1018 map_contexts_.emplace_back(std::move(mc));
1019 return map_contexts_.size() - 1;
1022 template<
typename... T>
1023 void context_manager::replace_map_context(
const T&... args)
1026 replace_map_context_with(std::move(new_mc));
1031 map_contexts_[current_context_index_].swap(mc);
1032 refresh_on_context_change();
1035 void context_manager::create_default_context()
1045 add_map_context(game_config_, filename, current_addon_);
1052 void context_manager::close_current_context()
1054 if(!confirm_discard())
return;
1056 if(map_contexts_.size() == 1) {
1057 create_default_context();
1058 map_contexts_.erase(map_contexts_.begin());
1059 }
else if(current_context_index_ ==
static_cast<int>(map_contexts_.size()) - 1) {
1060 map_contexts_.pop_back();
1061 current_context_index_--;
1063 map_contexts_.erase(map_contexts_.begin() + current_context_index_);
1066 refresh_on_context_change();
1069 void context_manager::switch_context(
const int index,
const bool force)
1071 if(
index < 0 ||
static_cast<std::size_t
>(
index) >= map_contexts_.size()) {
1072 WRN_ED <<
"Invalid index in switch map context: " <<
index;
1076 if(
index == current_context_index_ && !force) {
1082 get_map_context().get_labels().enable(
false);
1084 current_context_index_ =
index;
1086 refresh_on_context_change();
1091 std::string name = get_map_context().get_name();
1098 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 recalculate_minimap()
Schedule the minimap for recalculation.
void rebuild_all()
Rebuild all dynamic terrain.
void change_display_context(const display_context *dc)
void set_team(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
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.
void set_playing_team(std::size_t team)
set_playing_team sets the team whose turn it currently is
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.
std::string default_dir_
Default directory for map load/save as dialogs.
std::unique_ptr< map_context > context_ptr
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.
std::vector< context_ptr > map_contexts_
The currently opened map context object.
void edit_side_dialog(int side_index)
Display a side edit dialog and process user input.
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.
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
std::optional< int > get_xp_mod() 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
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.
The dialog for selecting which random generator to use in the editor.
std::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 the default file extension 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(std::optional< uint32_t > randomseed={})=0
Creates a new map and returns it.
void enable(bool is_enabled)
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
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.
static const std::string get_menu_marker(const bool changed)
const t_translation::terrain_code & get_selected_bg_terrain()
static std::vector< std::string > saved_windows_
std::string initialize_addon()
std::string get_legacy_editor_dir()
static bfs::path get_dir(const bfs::path &dirpath)
std::string get_user_data_dir()
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.
bool ends_with(const std::string &str, const std::string &suffix)
std::string get_addon_id_from_path(const std::string &location)
Returns the add-on ID from a path.
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.
@ CANCEL
Dialog was closed with the CANCEL button.
std::pair< std::string, unsigned > item
const std::vector< std::string > items
std::string default_dir()
void set_auto_update_transitions(int value)
int auto_update_transitions()
std::vector< std::string > recent_files()
Retrieves the list of recently opened files.
@ TRANSITION_UPDATE_PARTIAL
@ TRANSITION_UPDATE_COUNT
Modify, read and display user preferences.
::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)
int main(int, char **argv)
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.
static map_location::DIRECTION s
bool addon_filename_legal(const std::string &name)
Checks whether an add-on file name is legal or not.