The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
context_manager.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16#define GETTEXT_DOMAIN "wesnoth-editor"
17
19
20#include "addon/validation.hpp"
28
29#include "filesystem.hpp"
31#include "gettext.hpp"
32#include "resources.hpp"
33#include "team.hpp"
34
36
46
50#include "game_config_view.hpp"
51
54
55#include "units/unit.hpp"
56#include "video.hpp"
57
58#include <memory>
59#include <boost/algorithm/string.hpp>
60#include <boost/filesystem.hpp>
61
62namespace {
63
64std::vector<std::unique_ptr<editor::map_context>> saved_contexts_;
65int last_context_ = 0;
66
67std::string get_menu_marker(const bool changed)
68{
69 if (changed) {
70 return "[" + markup::span_color("#f00", font::unicode_bullet) + "]";
71 } else {
73 }
74}
75
76} // namespace
77
78namespace editor {
79
81 : locs_(nullptr)
82 , gui_(gui)
83 , default_dir_(filesystem::get_dir(filesystem::get_legacy_editor_dir()))
84 , current_addon_(addon_id)
85 , current_context_index_(0)
86 , auto_update_transitions_(prefs::get().editor_auto_update_transitions())
87 , map_contexts_()
88 , clipboard_()
89{
92}
93
101
103{
105
106 // TODO register the tod_manager with the gui?
109
110 // Reset side when switching to an existing scenario
111 if(!get_map_context().teams().empty()) {
112 gui().set_viewing_team_index(0, true);
114 }
115 gui().init_flags();
116
117 reload_map();
118
119 // Enable the labels of the current context;
121
123}
124
126{
133 if(locs_) {
134 for(const auto& loc : get_map_context().map().special_locations().left) {
135 locs_->add_item(loc.first);
136 }
138 // If the scenario has more than 9 teams, add locations for them
139 // (First 9 teams are always in the list)
140 std::size_t n_teams = get_map_context().teams().size();
141 for(std::size_t i = 10; i <= n_teams; i++) {
142 locs_->add_item(std::to_string(i));
143 }
144 }
145 }
146}
147
155
157{
158 switch (auto_update_transitions_) {
160 return (item == "editor-auto-update-transitions");
162 return (item == "editor-partial-update-transitions");
164 return (item == "editor-no-update-transitions");
165 }
166
167 return true; //should not be reached
168}
169
181
182std::size_t context_manager::modified_maps(std::string& message)
183{
184 std::vector<std::string> modified;
185 for(auto& mc : map_contexts_) {
186 if(mc->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());
191 } else {
192 modified.push_back(mc->get_default_context_name());
193 }
194 }
195 }
196
197 for(std::string& str : modified) {
198 message += "\n" + font::unicode_bullet + " " + str;
199 }
200
201 return modified.size();
202}
203
204void context_manager::load_map_dialog(bool force_same_context /* = false */)
205{
206 std::string fn = get_map_context().get_filename();
207 if(fn.empty()) {
209 fn = filesystem::get_legacy_editor_dir() + "/maps";
210 } else {
212 }
213 }
214
216
217 dlg.set_title(_("Load Map or Scenario"))
218 .set_path(fn);
219
220 if(dlg.show()) {
221 load_map(dlg.path(), !force_same_context);
222 }
223}
224
225void context_manager::load_mru_item(unsigned index, bool force_same_context /* = false */)
226{
227 const std::vector<std::string>& mru = prefs::get().recent_files();
228 if(mru.empty() || index >= mru.size()) {
229 return;
230 }
231
232 load_map(mru[index], !force_same_context);
233}
234
236{
237 editor_team_info team_info(t);
238
239 if(gui2::dialogs::editor_edit_side::execute(team_info)) {
240 get_map_context().set_side_setup(team_info);
241 }
242}
243
245{
246 if(!current_addon_.empty()) {
247 std::string pbl = filesystem::get_current_editor_dir(current_addon_) + "/_server.pbl";
248 gui2::dialogs::editor_edit_pbl::execute(pbl, current_addon_);
249 }
250}
251
253{
254 std::string new_addon_id = current_addon_;
255 gui2::dialogs::prompt::execute(new_addon_id);
256
258 std::string new_dir = filesystem::get_current_editor_dir(new_addon_id);
259 if(addon_filename_legal(new_addon_id) && filesystem::rename_dir(old_dir, new_dir)) {
260 std::string main_cfg = new_dir + "/_main.cfg";
261 std::string main = filesystem::read_file(main_cfg);
262
263 // update paths
264 boost::replace_all(main, "/"+current_addon_, "/"+new_addon_id);
265 // update textdomain
266 boost::replace_all(main, "wesnoth-"+current_addon_, "wesnoth-"+new_addon_id);
267 filesystem::write_file(main_cfg, main);
268
269 current_addon_ = new_addon_id;
270
271 for(std::unique_ptr<map_context>& context : map_contexts_) {
272 context->set_addon_id(current_addon_);
273 }
274 }
275}
276
278{
279 map_context& context = get_map_context();
280
281 std::string id = context.get_id();
282 std::string name = context.get_name();
283 std::string description = context.get_description();
284
285 int turns = context.get_time_manager()->number_of_turns();
286 int xp_mod = context.get_xp_mod() ? *context.get_xp_mod() : 70;
287
288 bool victory = context.victory_defeated();
289 bool random = context.random_start_time();
290
291 const bool ok = gui2::dialogs::editor_edit_scenario::execute(
292 id, name, description, turns, xp_mod, victory, random
293 );
294
295 if(!ok) {
296 return;
297 }
298
299 context.set_scenario_setup(id, name, description, turns, xp_mod, victory, random);
300
301 if(!name.empty()) {
303 }
304}
305
307{
308 const editor_map& map = get_map_context().map();
309
310 int w = map.w();
311 int h = map.h();
312
313 if(gui2::dialogs::editor_new_map::execute(_("New Map"), w, h)) {
315 new_map(w, h, fill, true);
316 }
317}
318
320{
321 const editor_map& map = get_map_context().map();
322
323 int w = map.w();
324 int h = map.h();
325
326 if(gui2::dialogs::editor_new_map::execute(_("New Scenario"), w, h)) {
328 new_scenario(w, h, fill, true);
329 }
330}
331
332void context_manager::expand_open_maps_menu(std::vector<config>& items) const
333{
334 for(std::size_t mci = 0; mci < map_contexts_.size(); ++mci) {
335 map_context& mc = *map_contexts_[mci];
336
337 std::string filename;
338 if(mc.is_pure_map()) {
340 } else {
341 filename = mc.get_name();
342 }
343
344 if(filename.empty()) {
346 }
347
348 std::ostringstream ss;
349 ss << "[" << mci + 1 << "] ";
350
351 const bool changed = mc.modified();
352
353 if(changed) {
355 } else {
356 ss << filename;
357 }
358
359 if(mc.is_embedded()) {
360 ss << " (E)";
361 }
362
363 items.emplace_back("label", ss.str(), "details", get_menu_marker(changed));
364 }
365}
366
367void context_manager::expand_load_mru_menu(std::vector<config>& items) const
368{
369 std::vector<std::string> mru = prefs::get().recent_files();
370 if(mru.empty()) {
371 items.emplace_back("label", _("No Recent Files"));
372 return;
373 }
374
375 for(const std::string& path : mru) {
376 // TODO: add proper leading ellipsization instead, since otherwise
377 // it'll be impossible to tell apart files with identical names and
378 // different parent paths.
379 items.emplace_back("label", filesystem::base_name(path));
380 }
381}
382
383void context_manager::expand_areas_menu(std::vector<config>& items) const
384{
386 if(!tod) {
387 return;
388 }
389
390 std::vector<std::string> area_ids = tod->get_area_ids();
391
392 for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
393 const std::string& area = area_ids[mci];
394
395 std::stringstream ss;
396 ss << "[" << mci + 1 << "] ";
397
398 if(area.empty()) {
399 ss << markup::italic(_("Unnamed Area"));
400 } else {
401 ss << area;
402 }
403
404 const bool changed =
405 mci == static_cast<std::size_t>(get_map_context().get_active_area())
406 && tod->get_area_by_index(mci) != get_map_context().map().selection();
407
408 items.emplace_back("label", ss.str(), "details", get_menu_marker(changed));
409 }
410}
411
412void context_manager::expand_sides_menu(std::vector<config>& items) const
413{
414 for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
415
416 const team& t = get_map_context().teams()[mci];
417 const std::string& teamname = t.user_team_name();
418 std::stringstream label;
419 label << "[" << mci+1 << "] ";
420
421 if(teamname.empty()) {
422 label << markup::italic(_("New Side"));
423 } else {
424 label << teamname;
425 }
426
427 items.emplace_back("label", label.str());
428 }
429}
430
431void context_manager::expand_time_menu(std::vector<config>& items) const
432{
434 assert(tod_m != nullptr);
435
436 for(const time_of_day& time : tod_m->times()) {
437 // Use 'details' field here since the image will take the first column
438 items.emplace_back("details", time.name, "image", time.image);
439 }
440}
441
442void context_manager::expand_local_time_menu(std::vector<config>& items) const
443{
445
446 for(const time_of_day& time : tod_m->times(get_map_context().get_active_area())) {
447 // Use 'details' field here since the image will take the first column
448 items.emplace_back("details", time.name, "image", time.image);
449 }
450}
451
453{
454 std::string fn = get_map_context().get_filename();
455 if(fn.empty()) {
457 }
458
460
461 dlg.set_title(_("Apply Mask"))
462 .set_path(fn);
463
464 if(dlg.show()) {
465 try {
466 map_context mask(dlg.path(), current_addon_);
469 } catch (const editor_map_load_exception& e) {
470 gui2::show_transient_message(_("Error loading mask"), e.what());
471 return;
472 } catch (const editor_action_exception& e) {
474 return;
475 }
476 }
477}
478
479void context_manager::perform_refresh(const editor_action& action, bool drag_part /* =false */)
480{
482 refresh_after_action(drag_part);
483}
484
486{
487 int active_area = get_map_context().get_active_area();
488 std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
489
490 if(gui2::dialogs::edit_text::execute(N_("Rename Area"), N_("Identifier:"), name)) {
491 get_map_context().get_time_manager()->set_area_id(active_area, name);
492 }
493}
494
496{
497 std::string fn = get_map_context().get_filename();
498 if(fn.empty()) {
500 }
501
503
504 dlg.set_title(_("Choose Target Map"))
505 .set_path(fn);
506
507 if(dlg.show()) {
508 try {
509 map_context map(dlg.path(), current_addon_);
512 } catch (const editor_map_load_exception& e) {
513 gui2::show_transient_message(_("Error loading map"), e.what());
514 return;
515 } catch (const editor_action_exception& e) {
517 return;
518 }
519 }
520}
521
523{
524 if(get_map_context().needs_reload()) {
525 reload_map();
526 return;
527 }
528
529 const std::set<map_location>& changed_locs = get_map_context().changed_locations();
530
531 if(get_map_context().needs_terrain_rebuild()) {
534 && (!drag_part || get_map_context().everything_changed())))
535 {
539 } else {
540 for(const map_location& loc : changed_locs) {
542 }
543 gui_.invalidate(changed_locs);
544 }
545 } else {
548 } else {
549 gui_.invalidate(changed_locs);
550 }
551 }
552
555 }
556
559}
560
562{
563 const editor_map& map = get_map_context().map();
564
565 int w = map.w();
566 int h = map.h();
567
569 bool copy = false;
570
571 if(!gui2::dialogs::editor_resize_map::execute(w, h, dir, copy)) {
572 return;
573 }
574
575 if(w != map.w() || h != map.h()) {
577 if(copy) {
579 }
580
581 int x_offset = map.w() - w;
582 int y_offset = map.h() - h;
583
584 switch (dir) {
588 y_offset = 0;
589 break;
593 y_offset /= 2;
594 break;
598 break;
599 default:
600 y_offset = 0;
601 WRN_ED << "Unknown resize expand direction";
602 break;
603 }
604
605 switch (dir) {
609 x_offset = 0;
610 break;
614 x_offset /= 2;
615 break;
619 break;
620 default:
621 x_offset = 0;
622 break;
623 }
624
625 auto chain = std::make_unique<editor_action_chain>();
626 auto units = get_map_context().units();
627 for (const auto& u : units) {
628 map_location l{u.get_location().x - x_offset, u.get_location().y - y_offset};
629 chain->append_action(std::make_unique<editor_action_unit_replace>(u.get_location(), l));
630 }
631
632 chain->append_action(std::make_unique<editor_action_item_move_all>(x_offset, y_offset));
633 chain->append_action(std::make_unique<editor_action_time_area_move_all>(x_offset, y_offset));
634
635 // TODO Reduces the number of editor actions in the following loop.
636 // It can be a lot if the map has a lot of labels (say hundreds).
639 map_location new_loc{loc.x - x_offset, loc.y - y_offset};
640 const terrain_label* old_label = labels.get_label(loc);
641
642 if (old_label == nullptr) {
643 return;
644 }
645
646 // Create action chain to:
647 // 1. Delete label in initial hex
648 // 2. Delete label in target hex if it exists, otherwise will no-op
649 // 3. Create label in target hex
650 chain->append_action(std::make_unique<editor_action_label_delete>(loc));
651 chain->append_action(std::make_unique<editor_action_label_delete>(new_loc));
652 chain->append_action(
653 std::make_unique<editor_action_label>(
654 new_loc,
655 old_label->text(),
656 old_label->team_name(),
657 old_label->color(),
658 old_label->visible_in_fog(),
659 old_label->visible_in_shroud(),
660 old_label->immutable(),
661 old_label->category()
662 )
663 );
664 });
665
666 chain->append_action(
667 std::make_unique<editor_action_resize_map>(w, h, x_offset, y_offset, fill));
668 perform_refresh(*chain);
669 }
670}
671
673{
674 bool first_pick = false;
675 std::string input_name = get_map_context().get_filename();
676 if(input_name.empty()) {
677 first_pick = true;
679 input_name = filesystem::get_legacy_editor_dir() + "/maps";
680 } else {
682 }
683 }
684
686
687 dlg.set_title(_("Save Map As"))
688 .set_save_mode(true)
689 .set_path(input_name)
692
693 if(!dlg.show()) {
694 return;
695 }
696
697 boost::filesystem::path save_path(dlg.path());
698
699 // Show warning the first time user tries to save in a wrong folder
700 std::string last_folder = save_path.parent_path().filename().string();
701 if ((last_folder == "scenarios")
702 && first_pick
704 _("Error"),
705 _("Do you really want to save a map in the scenarios folder?"),
707 {
708 return;
709 }
710
711 std::size_t is_open = check_open_map(save_path.string());
712 if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
713 gui2::show_transient_message(_("This map is already open."), save_path.string());
714 }
715
716 std::string old_filename = get_map_context().get_filename();
717
718 get_map_context().set_filename(save_path.string());
719
720 if(!write_map(true)) {
721 get_map_context().set_filename(old_filename);
722 }
723}
724
726{
727 bool first_pick = false;
728 std::string input_name = get_map_context().get_filename();
729 if(input_name.empty()) {
730 first_pick = true;
732 }
733
735
736 dlg.set_title(_("Save Scenario As"))
737 .set_save_mode(true)
738 .set_path(input_name)
741
742 if(!dlg.show()) {
743 return;
744 }
745
746 boost::filesystem::path save_path(dlg.path());
747
748 // Show warning the first time user tries to save in a wrong folder
749 std::string last_folder = save_path.parent_path().filename().string();
750 if ((last_folder == "maps")
751 && first_pick
753 _("Error"),
754 _("Do you really want to save a scenario in the maps folder?"),
756 {
757 return;
758 }
759
760 std::size_t is_open = check_open_map(save_path.string());
761 if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
762 gui2::show_transient_message(_("This scenario is already open."), save_path.string());
763 return;
764 }
765
766 std::string old_filename = get_map_context().get_filename();
767
768 get_map_context().set_filename(save_path.string());
769
770 if(!write_scenario(true)) {
771 get_map_context().set_filename(old_filename);
772 return;
773 }
774}
775
776void context_manager::generate_map_dialog(const std::vector<std::unique_ptr<map_generator>>& map_generators)
777{
778 if(map_generators.empty()) {
779 gui2::show_error_message(_("No random map generators found."));
780 return;
781 }
782
783 gui2::dialogs::editor_generate_map dialog(map_generators);
784 dialog.select_map_generator(get_map_context().last_used_generator());
785
786 if(dialog.show()) {
787 std::string map_string;
789 try {
790 map_string = map_generator->create_map(dialog.get_seed());
791 } catch (const mapgen_exception& e) {
792 gui2::show_transient_message(_("Map creation failed."), e.what());
793 return;
794 }
795
796 if(map_string.empty()) {
797 gui2::show_transient_message("", _("Map creation failed."));
798 } else {
799 editor_map new_map(map_string);
801 get_map_context().set_needs_labels_reset(); // Ensure Player Start labels are updated together with newly generated map
803 }
804
806 }
807}
808
810{
811 if(get_map_context().modified()) {
812 const int res = gui2::show_message(_("Unsaved Changes"),
813 _("Do you want to discard all changes made to the map since the last save?"), gui2::dialogs::message::yes_no_buttons);
814 return gui2::retval::CANCEL != res;
815 }
816
817 return true;
818}
819
824
826{
827 int current = current_context_index_;
828 for(std::size_t i = 0; i < map_contexts_.size(); ++i) {
830 save_map();
831 }
832 switch_context(current);
833}
834
836{
837 saved_contexts_.swap(map_contexts_);
838 std::swap(last_context_, current_context_index_);
840 switch_context(0, true);
841}
842
843void context_manager::save_map(bool show_confirmation)
844{
845 const std::string& name = get_map_context().get_filename();
846 if(name.empty() || filesystem::is_directory(name)) {
847 if(get_map_context().is_pure_map()) {
849 } else {
851 }
852 } else {
853 if(get_map_context().is_pure_map()) {
854 write_map(show_confirmation);
855 } else {
856 write_scenario(show_confirmation);
857 }
858 }
859}
860
861bool context_manager::write_scenario(bool display_confirmation)
862{
863 try {
865 if(display_confirmation) {
866 gui_.set_status(_("Scenario saved."), true);
867 }
868 } catch (const editor_map_save_exception& e) {
869 gui_.set_status(e.what(), false);
870 return false;
871 }
872
873 return true;
874}
875
876bool context_manager::write_map(bool display_confirmation)
877{
878 try {
880 if(display_confirmation) {
881 gui_.set_status(_("Map saved"), true);
882 }
883 } catch (const editor_map_save_exception& e) {
884 gui_.set_status(e.what(), false);
885 return false;
886 }
887
888 return true;
889}
890
891std::size_t context_manager::check_open_map(const std::string& fn) const
892{
893 std::size_t i = 0;
894 while(i < map_contexts_.size() && map_contexts_[i]->get_filename() != fn) {
895 ++i;
896 }
897
898 return i;
899}
900
901bool context_manager::check_switch_open_map(const std::string& fn)
902{
903 std::size_t i = check_open_map(fn);
904 if(i < map_contexts_.size()) {
905 gui2::show_transient_message(_("This map is already open."), fn);
907 return true;
908 }
909
910 return false;
911}
912
913void context_manager::load_map(const std::string& filename, bool new_context)
914{
915 if(new_context && check_switch_open_map(filename)) {
916 return;
917 }
918
921 // if no addon id has been set and the file being loaded is from an addon
922 // then use the file path to determine the addon rather than showing a dialog
923 if(auto addon_at_path = filesystem::get_addon_id_from_path(filename)) {
924 editor_controller::current_addon_id_ = addon_at_path.value();
925 } else {
927 }
928
930 }
931
933 return;
934 }
935 }
936
937 LOG_ED << "Load map: " << filename << (new_context ? " (new)" : " (same)");
938 try {
939 {
940 auto mc = std::make_unique<map_context>(filename, current_addon_);
941 if(mc->get_filename() != filename) {
942 if(new_context && check_switch_open_map(mc->get_filename())) {
943 return;
944 }
945 }
946
947 if(new_context) {
948 int new_id = add_map_context_of(std::move(mc));
949 switch_context(new_id);
950 } else {
951 replace_map_context_with(std::move(mc));
952 }
953 }
954
955 if(get_map_context().is_embedded()) {
956 const std::string& msg = _("Loaded embedded map data");
957 gui2::show_transient_message(_("Map loaded from scenario"), msg);
958 } else {
960 gui2::show_transient_message(_("Map loaded from scenario"), _("Loaded referenced map file:")+"\n"+get_map_context().get_filename());
961 }
962 }
963 } catch(const editor_map_load_exception& e) {
964 gui2::show_transient_message(_("Error loading map"), e.what());
965 return;
966 }
967}
968
970{
971 if(!confirm_discard()) {
972 return;
973 }
974
975 std::string filename = get_map_context().get_filename();
976 if(filename.empty()) {
977 ERR_ED << "Empty filename in map revert";
978 return;
979 }
980
981 load_map(filename, false);
982}
983
984void context_manager::init_context(int width, int height, const t_translation::terrain_code& fill, bool new_context, bool is_pure_map) {
985 editor_map m(width, height, fill);
986
987 if(new_context) {
988 int new_id = add_map_context(m, is_pure_map, current_addon_);
989 switch_context(new_id);
990 } else {
991 replace_map_context(m, is_pure_map, current_addon_);
992 }
993}
994
995void context_manager::new_map(int width, int height, const t_translation::terrain_code& fill, bool new_context)
996{
997 init_context(width, height, fill, new_context, true);
998}
999
1000void context_manager::new_scenario(int width, int height, const t_translation::terrain_code& fill, bool new_context)
1001{
1002 init_context(width, height, fill, new_context, false);
1003
1004 // Give the new scenario an initial side.
1006 gui().set_viewing_team_index(0, true);
1008 gui_.init_flags();
1009}
1010
1012{
1014
1015 // Give the converted scenario a number of sides
1016 // equal to the number of valid starting positions.
1017 int start_pos_count = get_map_context().map().num_valid_starting_positions();
1018 if(start_pos_count == 0) {
1019 start_pos_count = 1;
1020 }
1021 for(int i = 0; i < start_pos_count; i++) {
1023 }
1024 gui().set_viewing_team_index(0, true);
1026 gui_.init_flags();
1027}
1028
1029//
1030// Context manipulation
1031//
1032
1033template<typename... T>
1035{
1036 map_contexts_.emplace_back(new map_context(std::forward<T>(args)...));
1037 return map_contexts_.size() - 1;
1038}
1039
1040int context_manager::add_map_context_of(std::unique_ptr<map_context>&& mc)
1041{
1042 map_contexts_.emplace_back(std::move(mc));
1043 return map_contexts_.size() - 1;
1044}
1045
1046template<typename... T>
1048{
1049 map_contexts_[current_context_index_].reset(new map_context(std::forward<T>(args)...));
1051}
1052
1053void context_manager::replace_map_context_with(std::unique_ptr<map_context>&& mc)
1054{
1055 map_contexts_[current_context_index_] = std::move(mc);
1057}
1058
1060{
1061 if(saved_contexts_.empty()) {
1063 switch_context(0, true);
1064 } else {
1065 saved_contexts_.swap(map_contexts_);
1066 switch_context(last_context_, true);
1067 last_context_ = 0;
1068 }
1069}
1070
1078
1080{
1081 if(!confirm_discard()) return;
1082
1083 if(map_contexts_.size() == 1) {
1085 map_contexts_.erase(map_contexts_.begin());
1086 } else if(current_context_index_ == static_cast<int>(map_contexts_.size()) - 1) {
1087 map_contexts_.pop_back();
1089 } else {
1091 }
1092
1094}
1095
1096void context_manager::switch_context(const int index, const bool force)
1097{
1098 if(index < 0 || static_cast<std::size_t>(index) >= map_contexts_.size()) {
1099 WRN_ED << "Invalid index in switch map context: " << index;
1100 return;
1101 }
1102
1103 if(index == current_context_index_ && !force) {
1104 return;
1105 }
1106
1107 // Disable the labels of the current context before switching.
1108 // The refresher handles enabling the new ones.
1110
1111 current_context_index_ = index;
1112
1114}
1115
1117{
1118 std::string name = get_map_context().get_name();
1119
1120 if(name.empty()) {
1122 }
1123
1124 if(name.empty()){
1126 }
1127
1128 const std::string& wm_title_string = name + " - " + game_config::get_default_title_string();
1129 video::set_window_title(wm_title_string);
1130}
1131
1132} //Namespace editor
Editor action classes.
Editor action classes.
Editor action classes.
map_location loc
Definition move.cpp:172
double t
void set_viewing_team_index(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
Definition display.cpp:342
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition display.cpp:1465
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition display.cpp:2927
void set_playing_team_index(std::size_t team)
sets the team whose turn it currently is
Definition display.cpp:359
void rebuild_all()
Rebuild all dynamic terrain.
Definition display.cpp:428
void change_display_context(const display_context *dc)
Definition display.cpp:439
void invalidate_all()
Function to invalidate all tiles.
Definition display.cpp:2920
void init_flags()
Init the flag list and the team colors used by ~TC.
Definition display.cpp:257
void reload_map()
Updates internals that cache map size.
Definition display.cpp:433
void create_buttons()
Definition display.cpp:831
std::vector< std::unique_ptr< map_context > > map_contexts_
The currently opened map context object.
bool write_scenario(bool display_confirmation=true)
void replace_map_context(T &&... args)
Replace the current map context and refresh accordingly.
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.
int add_map_context(T &&... args)
Add a map context.
void expand_local_time_menu(std::vector< config > &items) const
Menu expanding for the map's defined areas.
void new_map(int width, int height, const t_translation::terrain_code &fill, bool new_context)
Create a new map.
void fill_selection()
Fill the selection with the foreground terrain.
void new_map_dialog()
Display a new map dialog and process user input.
bool confirm_discard()
Shows an are-you-sure dialog if the map was modified.
int auto_update_transitions_
Flag to rebuild terrain on every terrain change.
void rename_area_dialog()
Display an dialog to querry a new id for an [time_area].
void revert_map()
Revert the map by reloading it from disk.
void load_map_dialog(bool force_same_context=false)
Display a load map dialog and process user input.
bool check_switch_open_map(const std::string &fn)
Check if a map is already open.
void set_window_title()
Displays the specified map name in the window titlebar.
void switch_context(const int index, const bool force=false)
Switches the context to the one under the specified index.
void expand_sides_menu(std::vector< config > &items) const
Menu expanding for the map's player sides.
std::size_t check_open_map(const std::string &fn) const
Check if a map is already open.
void save_scenario_as_dialog()
Display a save map as dialog and process user input.
context_manager(editor_display &gui, const std::string &addon_id)
void refresh_all()
Refresh everything, i.e.
void save_all_maps()
Save all maps, show save dialogs for unsaved ones.
int add_map_context_of(std::unique_ptr< map_context > &&mc)
void expand_time_menu(std::vector< config > &items) const
Menu expanding for the map's defined areas.
void map_to_scenario()
Convert existing map to scenario.
void save_map_as_dialog()
Display a save map as dialog and process user input.
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 apply_mask_dialog()
Display an apply mask 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 save_contexts()
Save all open map_contexts to memory.
void set_addon_id(const std::string &id)
void create_mask_to_dialog()
Display an apply mask dialog and process user input.
void close_current_context()
Closes the active map context.
void resize_map_dialog()
Display a load map dialog and process user input.
void edit_scenario_dialog()
Display a scenario edit dialog and process user input.
std::string current_addon_
The currently selected add-on.
void replace_map_context_with(std::unique_ptr< map_context > &&mc)
void perform_refresh(const editor_action &action, bool drag_part=false)
Perform an action on the current map_context, then refresh the display.
void generate_map_dialog(const std::vector< std::unique_ptr< map_generator > > &map_generators)
Display a generate random map dialog and process user input.
void init_context(int width, int height, const t_translation::terrain_code &fill, bool new_context, bool is_pure_map=true)
void expand_load_mru_menu(std::vector< config > &items) const
Menu expanding for most recent loaded list.
void save_map(bool show_confirmation=true)
Save the map, open dialog if not named yet.
void edit_side_dialog(const team &t)
Display a side edit dialog and process user input.
bool write_map(bool display_confirmation=true)
Save the map under a given filename.
void load_mru_item(unsigned index, bool force_same_context=false)
Open the specified entry from the recent files list.
void expand_areas_menu(std::vector< config > &items) const
Menu expanding for the map's defined areas.
map_context & get_map_context()
Get the current map context object.
void expand_open_maps_menu(std::vector< config > &items) const
Menu expanding for open maps list.
class location_palette * locs_
void refresh_after_action(bool drag_part=false)
Refresh the display after an action has been performed.
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.
Definition action.hpp:266
Replace contents of the entire map, Useful as a fallback undo method when something else would be imp...
Definition action.hpp:39
Base class for all editor actions.
static std::string current_addon_id_
void rebuild_terrain(const map_location &loc)
void set_status(const std::string &str, const bool is_success)
Set a status text at the bottom left of the map area.
This class adds extra editor-specific functionality to a normal gamemap.
std::set< map_location > selection() const
Return the selection set.
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_last_used_generator(map_generator *generator)
const std::string & get_id() const
int get_active_area() const
void set_needs_labels_reset(bool value=true)
Setter for the labels reset flag.
map_labels & get_labels()
void set_needs_reload(bool value=true)
Setter for the reload flag.
const std::set< map_location > changed_locations() const
void new_side()
Adds a new side to the map.
const std::string & get_filename() const
virtual const editor_map & map() const override
Const map accessor.
bool random_start_time() const
void set_side_setup(editor_team_info &info)
virtual const unit_map & units() const override
Const units accessor.
void save_scenario()
Saves the scenario under the current filename.
game_classification & get_classification()
void perform_action(const editor_action &action)
Performs an action (thus modifying the map).
const tod_manager * get_time_manager() const
bool is_embedded() const
bool needs_labels_reset() const
Getter for the labels reset flag.
void save_map()
Saves the map under the current filename.
void reset_starting_position_labels(display &disp)
const std::string & get_name() const
void set_needs_terrain_rebuild(bool value=true)
Setter for the terrain rebuild flag.
virtual const std::vector< team > & teams() const override
Const teams accessor.
bool everything_changed() const
const t_string get_default_context_name() const
const std::string & get_description() const
utils::optional< int > get_xp_mod() const
bool is_pure_map() const
bool victory_defeated() const
void set_filename(const std::string &fn)
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)
int w() const
Effective map width.
Definition map.hpp:50
location_map & special_locations()
Definition map.hpp:105
int h() const
Effective map height.
Definition map.hpp:53
void for_each_loc(const F &f) const
Definition map.hpp:140
int num_valid_starting_positions() const
Counts the number of sides that have valid starting positions on this map.
Definition map.cpp:297
utils::optional< uint32_t > get_seed()
void select_map_generator(map_generator *mg)
map_generator * get_selected_map_generator()
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
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)
file_dialog & set_extension(const std::string &value)
Sets allowed file extensions for file names in save mode.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
@ yes_no_buttons
Shows a yes and no button.
Definition message.hpp:81
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)
Definition label.cpp:255
const terrain_label * get_label(const map_location &loc, const std::string &team_name) const
Definition label.hpp:48
static prefs & get()
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).
Definition team.hpp:74
To store label data Class implements logic for rendering.
Definition label.hpp:111
bool visible_in_shroud() const
Definition label.hpp:169
bool immutable() const
Definition label.hpp:174
const std::string & team_name() const
Definition label.hpp:154
const color_t & color() const
Definition label.hpp:184
bool visible_in_fog() const
Definition label.hpp:164
const t_string & text() const
Definition label.hpp:139
const std::string & category() const
Definition label.hpp:159
const std::vector< time_of_day > & times() const
int number_of_turns() const
std::vector< std::string > get_area_ids() const
const std::set< map_location > & get_area_by_index(int index) const
void set_area_id(int area_index, const std::string &id)
Editor action classes.
#define LOG_ED
#define ERR_ED
#define WRN_ED
Declarations for File-IO.
#define N_(String)
Definition gettext.hpp:108
static std::string _(const char *str)
Definition gettext.hpp:100
std::string label
What to show in the filter's drop-down list.
Definition manager.cpp:201
@ GAME_EDITOR_MAP_DIR
Editor map dir.
Definition paths.hpp:61
Manage the empty-palette in the editor.
Definition action.cpp:31
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.
std::string base_name(const std::string &file, const bool remove_extension=false)
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.
std::string get_dir(const std::string &dir)
const std::string mask_extension
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode=std::ios_base::binary)
Throws io_exception if an error occurs.
std::string get_current_editor_dir(const std::string &addon_id)
const std::string unicode_bullet
Definition constants.cpp:47
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.
Definition message.cpp:201
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.
Definition message.cpp:148
@ OK
Dialog was closed with the OK button.
Definition retval.hpp:35
@ CANCEL
Dialog was closed with the CANCEL button.
Definition retval.hpp:38
General purpose widgets.
std::string italic(Args &&... data)
Applies italic Pango markup to the input.
Definition markup.hpp:176
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
Definition markup.hpp:110
const int TRANSITION_UPDATE_ON
const int TRANSITION_UPDATE_COUNT
const int TRANSITION_UPDATE_OFF
const int TRANSITION_UPDATE_PARTIAL
::tod_manager * tod_manager
Definition resources.cpp:29
game_classification * classification
Definition resources.cpp:34
filter_context * filter_con
Definition resources.cpp:23
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
constexpr terrain_code NONE_TERRAIN
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition video.cpp:691
int w
Definition pathfind.cpp:188
static std::string get_filename(const std::string &file_code)
int main(int, char **)
Definition sdl3.cpp:25
std::string filename
Filename.
Encapsulates the map of the game.
Definition location.hpp:46
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.
#define i
#define e
#define h