16 #define GETTEXT_DOMAIN "wesnoth-editor"
56 #include <boost/algorithm/string.hpp>
64 std::ostringstream ss;
68 ss <<
"color='#f00' ";
80 , current_addon_(addon_id)
82 , last_map_generator_(nullptr)
83 , current_context_index_(0)
115 if (
gui().get_teams().
size() > 0) {
138 for(
const auto& loc :
get_map_context().map().special_locations().left) {
145 for(
size_t i = 10;
i <= n_teams;
i++) {
164 return (
item ==
"editor-auto-update-transitions");
166 return (
item ==
"editor-partial-update-transitions");
168 return (
item ==
"editor-no-update-transitions");
188 std::vector<std::string> modified;
191 if(!mc->get_name().empty()) {
192 modified.push_back(mc->get_name());
193 }
else if(!mc->get_filename().empty()) {
194 modified.push_back(mc->get_filename());
196 modified.push_back(mc->get_default_context_name());
201 for(std::string& str : modified) {
205 return modified.size();
213 }
else if(fn.empty()) {
230 if(mru.empty() ||
index >= mru.size()) {
243 if(gui2::dialogs::editor_edit_side::execute(team_info)) {
259 gui2::dialogs::prompt::execute(new_addon_id);
283 std::string
id = context.
get_id();
284 std::string name = context.
get_name();
293 const bool ok = gui2::dialogs::editor_edit_scenario::execute(
294 id, name, description,
turns, xp_mod, victory, random
315 if(gui2::dialogs::editor_new_map::execute(
_(
"New Map"),
w,
h)) {
328 if(gui2::dialogs::editor_new_map::execute(
_(
"New Scenario"),
w,
h)) {
337 std::vector<config> contexts;
339 for(std::size_t mci = 0; mci <
map_contexts_.size(); ++mci) {
342 std::string filename;
349 if(filename.empty()) {
353 std::ostringstream ss;
354 ss <<
"[" << mci + 1 <<
"] ";
359 ss <<
"<i>" << filename <<
"</i>";
368 const std::string
label = ss.str();
371 contexts.emplace_back(
"label",
label,
"details", details);
374 items.insert(pos, contexts.begin(), contexts.end());
384 items.insert(pos,
config {
"label",
_(
"No Recent Files")});
388 for(std::string&
path : mru) {
395 std::vector<config> temp;
396 std::transform(mru.begin(), mru.end(), std::back_inserter(temp), [](
const std::string& str) {
397 return config {
"label", str};
400 items.insert(pos, temp.begin(), temp.end());
403 void context_manager::expand_areas_menu(std::vector<config>&
items,
int i)
405 tod_manager* tod = get_map_context().get_time_manager();
411 std::vector<config> area_entries;
413 std::vector<std::string> area_ids = tod->
get_area_ids();
415 for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
416 const std::string& area = area_ids[mci];
418 std::stringstream ss;
419 ss <<
"[" << mci + 1 <<
"] ";\
422 ss <<
"<i>" <<
_(
"Unnamed Area") <<
"</i>";
428 mci ==
static_cast<std::size_t
>(get_map_context().get_active_area())
431 const std::string
label = ss.str();
434 area_entries.emplace_back(
"label",
label,
"details", details);
437 items.insert(pos, area_entries.begin(), area_entries.end());
440 void context_manager::expand_sides_menu(std::vector<config>&
items,
int i)
443 std::vector<config> contexts;
445 for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
447 const team&
t = get_map_context().teams()[mci];
448 const std::string& teamname =
t.user_team_name();
449 std::stringstream
label;
450 label <<
"[" << mci+1 <<
"] ";
452 if(teamname.empty()) {
453 label <<
"<i>" <<
_(
"New Side") <<
"</i>";
458 contexts.emplace_back(
"label",
label.str());
461 items.insert(pos, contexts.begin(), contexts.end());
464 void context_manager::expand_time_menu(std::vector<config>&
items,
int i)
467 std::vector<config> times;
469 tod_manager* tod_m = get_map_context().get_time_manager();
471 assert(tod_m !=
nullptr);
475 "details", time.name,
480 items.insert(pos, times.begin(), times.end());
483 void context_manager::expand_local_time_menu(std::vector<config>&
items,
int i)
486 std::vector<config> times;
488 tod_manager* tod_m = get_map_context().get_time_manager();
490 for(
const time_of_day& time : tod_m->
times(get_map_context().get_active_area())) {
492 "details", time.name,
497 items.insert(pos, times.begin(), times.end());
500 void context_manager::apply_mask_dialog()
502 std::string fn = get_map_context().get_filename();
527 void context_manager::perform_refresh(
const editor_action& action,
bool drag_part )
529 get_map_context().perform_action(action);
530 refresh_after_action(drag_part);
533 void context_manager::rename_area_dialog()
535 int active_area = get_map_context().get_active_area();
536 std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
538 if(gui2::dialogs::edit_text::execute(
N_(
"Rename Area"),
N_(
"Identifier:"), name)) {
539 get_map_context().get_time_manager()->set_area_id(active_area, name);
543 void context_manager::create_mask_to_dialog()
545 std::string fn = get_map_context().get_filename();
570 void context_manager::refresh_after_action(
bool drag_part)
572 if(get_map_context().needs_reload()) {
577 const std::set<map_location>& changed_locs = get_map_context().changed_locations();
579 if(get_map_context().needs_terrain_rebuild()) {
582 && (!drag_part || get_map_context().everything_changed())))
585 get_map_context().set_needs_terrain_rebuild(
false);
586 gui_.invalidate_all();
589 gui_.rebuild_terrain(loc);
591 gui_.invalidate(changed_locs);
594 if(get_map_context().everything_changed()) {
595 gui_.invalidate_all();
597 gui_.invalidate(changed_locs);
601 if(get_map_context().needs_labels_reset()) {
602 get_map_context().reset_starting_position_labels(gui_);
605 get_map_context().clear_changed_locations();
606 gui_.recalculate_minimap();
609 void context_manager::resize_map_dialog()
611 const editor_map& map = get_map_context().map();
619 if(!gui2::dialogs::editor_resize_map::execute(
w,
h, dir, copy)) {
623 if(
w != map.
w() ||
h != map.
h()) {
629 int x_offset = map.
w() -
w;
630 int y_offset = map.
h() -
h;
649 WRN_ED <<
"Unknown resize expand direction";
678 void context_manager::save_map_as_dialog()
680 std::string input_name = get_map_context().get_filename();
681 if(current_addon_ ==
"") {
683 }
else if(input_name.empty() || input_name.find(
"/maps") == std::string::npos) {
698 std::size_t is_open = check_open_map(dlg.
path());
699 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
703 std::string old_filename = get_map_context().get_filename();
705 get_map_context().set_filename(dlg.
path());
707 if(!write_map(
true)) {
708 get_map_context().set_filename(old_filename);
712 void context_manager::save_scenario_as_dialog()
714 std::string input_name = get_map_context().get_filename();
715 if(input_name.empty() || input_name.find(
"/scenarios") == std::string::npos) {
731 std::size_t is_open = check_open_map(dlg.
path());
732 if(is_open < map_contexts_.size() && is_open !=
static_cast<unsigned>(current_context_index_)) {
737 std::string old_filename = get_map_context().get_filename();
739 get_map_context().set_filename(dlg.
path());
741 if(!write_scenario(
true)) {
742 get_map_context().set_filename(old_filename);
750 if(
i[
"map_generation"].empty() &&
i[
"scenario_generation"].empty()) {
755 if(
const auto generator_cfg =
i.optional_child(
"generator")) {
756 map_generators_.emplace_back(
create_map_generator(
i[
"map_generation"].empty() ?
i[
"scenario_generation"] :
i[
"map_generation"], generator_cfg.value()));
758 ERR_ED <<
"Scenario \"" <<
i[
"name"] <<
"\" with id " <<
i[
"id"]
759 <<
" has map_generation= but no [generator] tag";
764 void context_manager::generate_map_dialog()
766 if(map_generators_.empty()) {
775 std::string map_string;
784 if(map_string.empty()) {
789 get_map_context().set_needs_labels_reset();
797 bool context_manager::confirm_discard()
799 if(get_map_context().modified()) {
808 void context_manager::fill_selection()
813 void context_manager::save_all_maps(
bool auto_save_windows)
815 int current = current_context_index_;
817 for(std::size_t
i = 0;
i < map_contexts_.size(); ++
i) {
819 std::string name = get_map_context().get_filename();
820 if(auto_save_windows) {
822 std::ostringstream
s;
823 s << default_dir_ <<
"/" <<
"window_" <<
i + 1;
824 if(!get_map_context().is_embedded() && !get_map_context().is_pure_map()) {
830 get_map_context().set_filename(name);
837 switch_context(current);
840 void context_manager::save_map()
842 const std::string& name = get_map_context().get_filename();
844 if(get_map_context().is_pure_map()) {
845 save_map_as_dialog();
847 save_scenario_as_dialog();
850 if(get_map_context().is_pure_map()) {
858 bool context_manager::write_scenario(
bool display_confirmation)
861 get_map_context().save_scenario();
862 if(display_confirmation) {
873 bool context_manager::write_map(
bool display_confirmation)
876 get_map_context().save_map();
877 if(display_confirmation) {
888 std::size_t context_manager::check_open_map(
const std::string& fn)
const
891 while(
i < map_contexts_.size() && map_contexts_[
i]->get_filename() != fn) {
898 bool context_manager::check_switch_open_map(
const std::string& fn)
900 std::size_t
i = check_open_map(fn);
901 if(
i < map_contexts_.size()) {
910 void context_manager::load_map(
const std::string& filename,
bool new_context)
912 if(new_context && check_switch_open_map(filename)) {
917 if(editor_controller::current_addon_id_ ==
"") {
921 if(editor_controller::current_addon_id_ ==
"") {
924 set_addon_id(editor_controller::current_addon_id_);
927 if(editor_controller::current_addon_id_ ==
"") {
932 LOG_ED <<
"Load map: " << filename << (new_context ?
" (new)" :
" (same)");
936 if(mc->get_filename() != filename) {
937 if(new_context && check_switch_open_map(mc->get_filename())) {
943 int new_id = add_map_context_of(std::move(mc));
944 switch_context(new_id);
946 replace_map_context_with(std::move(mc));
950 if(get_map_context().is_embedded()) {
951 const std::string&
msg =
_(
"Loaded embedded map data");
964 void context_manager::revert_map()
966 if(!confirm_discard()) {
970 std::string filename = get_map_context().get_filename();
971 if(filename.empty()) {
972 ERR_ED <<
"Empty filename in map revert";
976 load_map(filename,
false);
985 int new_id = add_map_context(m,
true, default_schedule, current_addon_);
986 switch_context(new_id);
988 replace_map_context(m,
true, default_schedule, current_addon_);
994 auto default_schedule = game_config_.find_child(
"editor_times",
"id",
"empty");
998 int new_id = add_map_context(m,
false, *default_schedule, current_addon_);
999 switch_context(new_id);
1001 replace_map_context(m,
false, *default_schedule, current_addon_);
1005 get_map_context().new_side();
1006 gui().set_team(0,
true);
1007 gui().set_playing_team(0);
1015 template<
typename... T>
1016 int context_manager::add_map_context(
const T&... args)
1018 map_contexts_.emplace_back(
new map_context(args...));
1019 return map_contexts_.size() - 1;
1024 map_contexts_.emplace_back(std::move(mc));
1025 return map_contexts_.size() - 1;
1028 template<
typename... T>
1029 void context_manager::replace_map_context(
const T&... args)
1032 replace_map_context_with(std::move(new_mc));
1037 map_contexts_[current_context_index_].swap(mc);
1038 refresh_on_context_change();
1041 void context_manager::create_default_context()
1051 add_map_context(game_config_, filename, current_addon_);
1058 void context_manager::close_current_context()
1060 if(!confirm_discard())
return;
1062 if(map_contexts_.size() == 1) {
1063 create_default_context();
1064 map_contexts_.erase(map_contexts_.begin());
1065 }
else if(current_context_index_ ==
static_cast<int>(map_contexts_.size()) - 1) {
1066 map_contexts_.pop_back();
1067 current_context_index_--;
1069 map_contexts_.erase(map_contexts_.begin() + current_context_index_);
1072 refresh_on_context_change();
1075 void context_manager::switch_context(
const int index,
const bool force)
1077 if(
index < 0 ||
static_cast<std::size_t
>(
index) >= map_contexts_.size()) {
1078 WRN_ED <<
"Invalid index in switch map context: " <<
index;
1082 if(
index == current_context_index_ && !force) {
1088 get_map_context().get_labels().enable(
false);
1090 current_context_index_ =
index;
1092 refresh_on_context_change();
1097 std::string name = get_map_context().get_name();
1104 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 directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
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.