The Battle for Wesnoth  1.17.0-dev
context_manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
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 
18 #include "resources.hpp"
19 #include "team.hpp"
20 
21 #include "display.hpp"
24 #include "filesystem.hpp"
25 #include "formula/string_utils.hpp"
26 #include "game_board.hpp"
29 #include "gettext.hpp"
30 
31 #include "editor/action/action.hpp"
33 #include "preferences/editor.hpp"
34 
40 #include "gui/dialogs/message.hpp"
42 #include "gui/widgets/retval.hpp"
43 
46 #include "game_config_view.hpp"
47 
48 #include "terrain/translation.hpp"
49 
50 #include <memory>
51 
52 namespace editor {
53 
54 static std::vector<std::string> saved_windows_;
55 
56 static const std::string get_menu_marker(const bool changed)
57 {
58  std::ostringstream ss;
59  ss << "[<span ";
60 
61  if(changed) {
62  ss << "color='#f00' ";
63  }
64 
65  ss << ">" << font::unicode_bullet << "</span>]";
66  return ss.str();
67 }
68 
70  : locs_(nullptr)
71  , gui_(gui)
72  , game_config_(game_config)
73  , default_dir_(preferences::editor::default_dir())
74  , map_generators_()
75  , last_map_generator_(nullptr)
76  , current_context_index_(0)
77  , auto_update_transitions_(preferences::editor::auto_update_transitions())
78  , map_contexts_()
79  , clipboard_()
80 {
81  resources::filter_con = this;
82 
83  if(default_dir_.empty()) {
85  }
86 
88  init_map_generators(game_config);
89 }
90 
92 {
93  // Restore default window title
95 
96  resources::filter_con = nullptr;
97 }
98 
100 {
102 
103  // TODO register the tod_manager with the gui?
106 
107  // Reset side when switching to an existing scenario
108  if (gui().get_teams().size() > 0) {
109  gui().set_team(0, true);
110  gui().set_playing_team(0);
111  }
112  gui().init_flags();
113 
114  reload_map();
115 
116  // Enable the labels of the current context;
118 
120 }
121 
123 {
124  gui_.rebuild_all();
130  if(locs_) {
131  for(const auto& loc : get_map_context().map().special_locations().left) {
132  locs_->add_item(loc.first);
133  }
134  if(!get_map_context().is_pure_map()) {
135  // If the scenario has more than 9 teams, add locations for them
136  // (First 9 teams are always in the list)
137  size_t n_teams = get_map_context().teams().size();
138  for(size_t i = 10; i <= n_teams; i++) {
139  locs_->add_item(std::to_string(i));
140  }
141  }
142  }
143 }
144 
146 {
147  gui_.reload_map();
150  refresh_all();
151 }
152 
154 {
155  switch (auto_update_transitions_) {
157  return (item == "editor-auto-update-transitions");
159  return (item == "editor-partial-update-transitions");
161  return (item == "editor-no-update-transitions");
162  }
163 
164  return true; //should not be reached
165 }
166 
168 {
171 
173  return true;
174  }
175 
176  return false;
177 }
178 
179 std::size_t context_manager::modified_maps(std::string& message)
180 {
181  std::vector<std::string> modified;
182  for(auto& mc : map_contexts_) {
183  if(mc->modified()) {
184  if(!mc->get_name().empty()) {
185  modified.push_back(mc->get_name());
186  } else if(!mc->get_filename().empty()) {
187  modified.push_back(mc->get_filename());
188  } else {
189  modified.push_back(mc->get_default_context_name());
190  }
191  }
192  }
193 
194  for(std::string& str : modified) {
195  message += "\n" + font::unicode_bullet + " " + str;
196  }
197 
198  return modified.size();
199 }
200 
201 void context_manager::load_map_dialog(bool force_same_context /* = false */)
202 {
204  if(fn.empty()) {
205  fn = default_dir_;
206  }
207 
209 
210  dlg.set_title(_("Load Map"))
211  .set_path(fn);
212 
213  if(dlg.show()) {
214  load_map(dlg.path(), !force_same_context);
215  }
216 }
217 
218 void context_manager::load_mru_item(unsigned int index, bool force_same_context /* = false */)
219 {
220  const std::vector<std::string>& mru = preferences::editor::recent_files();
221  if(mru.empty() || index >= mru.size()) {
222  return;
223  }
224 
225  load_map(mru[index], !force_same_context);
226 }
227 
229 {
230  team& t = get_map_context().teams()[side_index];
231 
232  editor_team_info team_info(t);
233 
234  if(gui2::dialogs::editor_edit_side::execute(team_info)) {
235  get_map_context().set_side_setup(team_info);
236  }
237 }
238 
240 {
241  map_context& context = get_map_context();
242 
243  // TODO
244  //std::string fn = filesystem::directory_name(context.get_filename());
245 
246  std::string id = context.get_id();
247  std::string name = context.get_name();
248  std::string description = context.get_description();
249 
250  int turns = context.get_time_manager()->number_of_turns();
251  int xp_mod = context.get_xp_mod() ? *context.get_xp_mod() : 70;
252 
253  bool victory = context.victory_defeated();
254  bool random = context.random_start_time();
255 
256  const bool ok = gui2::dialogs::editor_edit_scenario::execute(
257  id, name, description, turns, xp_mod, victory, random
258  );
259 
260  if(!ok) {
261  return;
262  }
263 
264  context.set_scenario_setup(id, name, description, turns, xp_mod, victory, random);
265 
266  if(!name.empty()) {
268  }
269 }
270 
272 {
273  const editor_map& map = get_map_context().map();
274 
275  int w = map.w();
276  int h = map.h();
277 
278  if(gui2::dialogs::editor_new_map::execute(_("New Map"), w, h)) {
280  new_map(w, h, fill, true);
281  }
282 }
283 
285 {
286  const editor_map& map = get_map_context().map();
287 
288  int w = map.w();
289  int h = map.h();
290 
291  if(gui2::dialogs::editor_new_map::execute(_("New Scenario"), w, h)) {
293  new_scenario(w, h, fill, true);
294  }
295 }
296 
297 void context_manager::expand_open_maps_menu(std::vector<config>& items, int i)
298 {
299  auto pos = items.erase(items.begin() + i);
300  std::vector<config> contexts;
301 
302  for(std::size_t mci = 0; mci < map_contexts_.size(); ++mci) {
303  map_context& mc = *map_contexts_[mci];
304 
305  std::string filename;
306  if(mc.is_pure_map()) {
307  filename = filesystem::base_name(mc.get_filename());
308  } else {
309  filename = mc.get_name();
310  }
311 
312  if(filename.empty()) {
313  filename = mc.get_default_context_name();
314  }
315 
316  std::ostringstream ss;
317  ss << "[" << mci + 1 << "] ";
318 
319  const bool changed = mc.modified();
320 
321  if(changed) {
322  ss << "<i>" << filename << "</i>";
323  } else {
324  ss << filename;
325  }
326 
327  if(mc.is_embedded()) {
328  ss << " (E)";
329  }
330 
331  const std::string label = ss.str();
332  const std::string details = get_menu_marker(changed);
333 
334  contexts.emplace_back("label", label, "details", details);
335  }
336 
337  items.insert(pos, contexts.begin(), contexts.end());
338 }
339 
340 void context_manager::expand_load_mru_menu(std::vector<config>& items, int i)
341 {
342  std::vector<std::string> mru = preferences::editor::recent_files();
343 
344  auto pos = items.erase(items.begin() + i);
345 
346  if(mru.empty()) {
347  items.insert(pos, config {"label", _("No Recent Files")});
348  return;
349  }
350 
351  for(std::string& path : mru) {
352  // TODO: add proper leading ellipsization instead, since otherwise
353  // it'll be impossible to tell apart files with identical names and
354  // different parent paths.
356  }
357 
358  std::vector<config> temp;
359  std::transform(mru.begin(), mru.end(), std::back_inserter(temp), [](const std::string& str) {
360  return config {"label", str};
361  });
362 
363  items.insert(pos, temp.begin(), temp.end());
364 }
365 
366 void context_manager::expand_areas_menu(std::vector<config>& items, int i)
367 {
369  if(!tod) {
370  return;
371  }
372 
373  auto pos = items.erase(items.begin() + i);
374  std::vector<config> area_entries;
375 
376  std::vector<std::string> area_ids = tod->get_area_ids();
377 
378  for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
379  const std::string& area = area_ids[mci];
380 
381  std::stringstream ss;
382  ss << "[" << mci + 1 << "] ";\
383 
384  if(area.empty()) {
385  ss << "<i>" << _("Unnamed Area") << "</i>";
386  } else {
387  ss << area;
388  }
389 
390  const bool changed =
391  mci == static_cast<std::size_t>(get_map_context().get_active_area())
392  && tod->get_area_by_index(mci) != get_map_context().map().selection();
393 
394  const std::string label = ss.str();
395  const std::string details = get_menu_marker(changed);
396 
397  area_entries.emplace_back("label", label, "details", details);
398  }
399 
400  items.insert(pos, area_entries.begin(), area_entries.end());
401 }
402 
403 void context_manager::expand_sides_menu(std::vector<config>& items, int i)
404 {
405  auto pos = items.erase(items.begin() + i);
406  std::vector<config> contexts;
407 
408  for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
409 
410  const team& t = get_map_context().teams()[mci];
411  const std::string& teamname = t.user_team_name();
412  std::stringstream label;
413  label << "[" << mci+1 << "] ";
414 
415  if(teamname.empty()) {
416  label << "<i>" << _("New Side") << "</i>";
417  } else {
418  label << teamname;
419  }
420 
421  contexts.emplace_back("label", label.str());
422  }
423 
424  items.insert(pos, contexts.begin(), contexts.end());
425 }
426 
427 void context_manager::expand_time_menu(std::vector<config>& items, int i)
428 {
429  auto pos = items.erase(items.begin() + i);
430  std::vector<config> times;
431 
433 
434  assert(tod_m != nullptr);
435 
436  for(const time_of_day& time : tod_m->times()) {
437  times.emplace_back(
438  "details", time.name, // Use 'details' field here since the image will take the first column
439  "image", time.image
440  );
441  }
442 
443  items.insert(pos, times.begin(), times.end());
444 }
445 
446 void context_manager::expand_local_time_menu(std::vector<config>& items, int i)
447 {
448  auto pos = items.erase(items.begin() + i);
449  std::vector<config> times;
450 
452 
453  for(const time_of_day& time : tod_m->times(get_map_context().get_active_area())) {
454  times.emplace_back(
455  "details", time.name, // Use 'details' field here since the image will take the first column
456  "image", time.image
457  );
458  }
459 
460  items.insert(pos, times.begin(), times.end());
461 }
462 
464 {
465  std::string fn = get_map_context().get_filename();
466  if(fn.empty()) {
467  fn = default_dir_;
468  }
469 
471 
472  dlg.set_title(_("Apply Mask"))
473  .set_path(fn);
474 
475  if(dlg.show()) {
476  try {
477  map_context mask(game_config_, dlg.path());
478  editor_action_apply_mask a(mask.map());
480  } catch (const editor_map_load_exception& e) {
481  gui2::show_transient_message(_("Error loading mask"), e.what());
482  return;
483  } catch (const editor_action_exception& e) {
485  return;
486  }
487  }
488 }
489 
490 void context_manager::perform_refresh(const editor_action& action, bool drag_part /* =false */)
491 {
493  refresh_after_action(drag_part);
494 }
495 
497 {
498  int active_area = get_map_context().get_active_area();
499  std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
500 
501  if(gui2::dialogs::edit_text::execute(N_("Rename Area"), N_("Identifier:"), name)) {
502  get_map_context().get_time_manager()->set_area_id(active_area, name);
503  }
504 }
505 
507 {
508  std::string fn = get_map_context().get_filename();
509  if(fn.empty()) {
510  fn = default_dir_;
511  }
512 
514 
515  dlg.set_title(_("Choose Target Map"))
516  .set_path(fn);
517 
518  if(dlg.show()) {
519  try {
520  map_context map(game_config_, dlg.path());
521  editor_action_create_mask a(map.map());
523  } catch (const editor_map_load_exception& e) {
524  gui2::show_transient_message(_("Error loading map"), e.what());
525  return;
526  } catch (const editor_action_exception& e) {
528  return;
529  }
530  }
531 }
532 
534 {
535  if(get_map_context().needs_reload()) {
536  reload_map();
537  return;
538  }
539 
540  const std::set<map_location>& changed_locs = get_map_context().changed_locations();
541 
542  if(get_map_context().needs_terrain_rebuild()) {
545  && (!drag_part || get_map_context().everything_changed())))
546  {
547  gui_.rebuild_all();
550  } else {
551  for(const map_location& loc : changed_locs) {
552  gui_.rebuild_terrain(loc);
553  }
554  gui_.invalidate(changed_locs);
555  }
556  } else {
557  if(get_map_context().everything_changed()) {
559  } else {
560  gui_.invalidate(changed_locs);
561  }
562  }
563 
564  if(get_map_context().needs_labels_reset()) {
566  }
567 
570 }
571 
573 {
574  const editor_map& map = get_map_context().map();
575 
576  int w = map.w();
577  int h = map.h();
578 
580  bool copy = false;
581 
582  if(!gui2::dialogs::editor_resize_map::execute(w, h, dir, copy)) {
583  return;
584  }
585 
586  if(w != map.w() || h != map.h()) {
588  if(copy) {
590  }
591 
592  int x_offset = map.w() - w;
593  int y_offset = map.h() - h;
594 
595  switch (dir) {
599  y_offset = 0;
600  break;
604  y_offset /= 2;
605  break;
609  break;
610  default:
611  y_offset = 0;
612  WRN_ED << "Unknown resize expand direction" << std::endl;
613  break;
614  }
615 
616  switch (dir) {
620  x_offset = 0;
621  break;
625  x_offset /= 2;
626  break;
630  break;
631  default:
632  x_offset = 0;
633  break;
634  }
635 
636  editor_action_resize_map a(w, h, x_offset, y_offset, fill);
637  perform_refresh(a);
638  }
639 }
640 
642 {
643  std::string input_name = get_map_context().get_filename();
644  if(input_name.empty()) {
645  input_name = filesystem::get_dir(default_dir_ + "/maps");
646  }
647 
649 
650  dlg.set_title(_("Save Map As"))
651  .set_save_mode(true)
652  .set_path(input_name)
653  .set_extension(".map");
654 
655  if(!dlg.show()) {
656  return;
657  }
658 
659  save_map_as(dlg.path());
660 }
661 
663 {
664  std::string input_name = get_map_context().get_filename();
665  if(input_name.empty()) {
666  input_name = filesystem::get_dir(default_dir_ + "/scenarios");
667  }
668 
670 
671  dlg.set_title(_("Save Scenario As"))
672  .set_save_mode(true)
673  .set_path(input_name)
674  .set_extension(".cfg");
675 
676  if(!dlg.show()) {
677  return;
678  }
679 
680  save_scenario_as(dlg.path());
681 }
682 
684 {
685  for(const config& i : game_config.child_range("multiplayer")) {
686  if(i["map_generation"].empty() && i["scenario_generation"].empty()) {
687  continue;
688  }
689 
690  // TODO: we should probably use `child` with a try/catch block once that function throws
691  if(const auto generator_cfg = i.optional_child("generator")) {
692  map_generators_.emplace_back(create_map_generator(i["map_generation"].empty() ? i["scenario_generation"] : i["map_generation"], generator_cfg.value()));
693  } else {
694  ERR_ED << "Scenario \"" << i["name"] << "\" with id " << i["id"]
695  << " has map_generation= but no [generator] tag" << std::endl;
696  }
697  }
698 }
699 
701 {
702  if(map_generators_.empty()) {
703  gui2::show_error_message(_("No random map generators found."));
704  return;
705  }
706 
709 
710  if(dialog.show()) {
711  std::string map_string;
713  try {
714  map_string = map_generator->create_map(dialog.get_seed());
715  } catch (const mapgen_exception& e) {
716  gui2::show_transient_message(_("Map creation failed."), e.what());
717  return;
718  }
719 
720  if(map_string.empty()) {
721  gui2::show_transient_message("", _("Map creation failed."));
722  } else {
723  editor_map new_map(map_string);
724  editor_action_whole_map a(new_map);
725  get_map_context().set_needs_labels_reset(); // Ensure Player Start labels are updated together with newly generated map
726  perform_refresh(a);
727  }
728 
729  last_map_generator_ = map_generator;
730  }
731 }
732 
734 {
735  if(get_map_context().modified()) {
736  const int res = gui2::show_message(_("Unsaved Changes"),
737  _("Do you want to discard all changes made to the map since the last save?"), gui2::dialogs::message::yes_no_buttons);
738  return gui2::retval::CANCEL != res;
739  }
740 
741  return true;
742 }
743 
745 {
747 }
748 
749 void context_manager::save_all_maps(bool auto_save_windows)
750 {
751  int current = current_context_index_;
752  saved_windows_.clear();
753  for(std::size_t i = 0; i < map_contexts_.size(); ++i) {
754  switch_context(i);
755  std::string name = get_map_context().get_filename();
756  if(auto_save_windows) {
757  if(name.empty() || filesystem::is_directory(name)) {
758  std::ostringstream s;
759  s << default_dir_ << "/" << "window_" << i + 1;
760  name = s.str();
762  }
763  }
764  saved_windows_.push_back(name);
765  save_map();
766  }
767 
768  switch_context(current);
769 }
770 
772 {
773  const std::string& name = get_map_context().get_filename();
774  if(name.empty() || filesystem::is_directory(name)) {
775  if(get_map_context().is_pure_map()) {
777  } else {
779  }
780  } else {
781  if(get_map_context().is_pure_map()) {
782  write_map();
783  } else {
784  write_scenario();
785  }
786  }
787 }
788 
789 bool context_manager::save_scenario_as(const std::string& filename)
790 {
791  std::size_t is_open = check_open_map(filename);
792  if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
793  gui2::show_transient_message(_("This scenario is already open."), filename);
794  return false;
795  }
796 
797  std::string old_filename = get_map_context().get_filename();
798  bool embedded = get_map_context().is_embedded();
799 
800  get_map_context().set_filename(filename);
801  get_map_context().set_embedded(false);
802 
803  if(!write_scenario(true)) {
804  get_map_context().set_filename(old_filename);
805  get_map_context().set_embedded(embedded);
806  return false;
807  }
808 
809  return true;
810 }
811 
812 bool context_manager::save_map_as(const std::string& filename)
813 {
814  std::size_t is_open = check_open_map(filename);
815  if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
816  gui2::show_transient_message(_("This map is already open."), filename);
817  return false;
818  }
819 
820  std::string old_filename = get_map_context().get_filename();
821  bool embedded = get_map_context().is_embedded();
822 
823  get_map_context().set_filename(filename);
824  get_map_context().set_embedded(false);
825 
826  if(!write_map(true)) {
827  get_map_context().set_filename(old_filename);
828  get_map_context().set_embedded(embedded);
829  return false;
830  }
831 
832  return true;
833 }
834 
835 bool context_manager::write_scenario(bool display_confirmation)
836 {
837  try {
839  if(display_confirmation) {
840  gui2::show_transient_message("", _("Scenario saved."));
841  }
842  } catch (const editor_map_save_exception& e) {
844  return false;
845  }
846 
847  return true;
848 }
849 
850 bool context_manager::write_map(bool display_confirmation)
851 {
852  try {
854  if(display_confirmation) {
855  gui2::show_transient_message("", _("Map saved."));
856  }
857  } catch (const editor_map_save_exception& e) {
859  return false;
860  }
861 
862  return true;
863 }
864 
865 std::size_t context_manager::check_open_map(const std::string& fn) const
866 {
867  std::size_t i = 0;
868  while(i < map_contexts_.size() && map_contexts_[i]->get_filename() != fn) {
869  ++i;
870  }
871 
872  return i;
873 }
874 
875 bool context_manager::check_switch_open_map(const std::string& fn)
876 {
877  std::size_t i = check_open_map(fn);
878  if(i < map_contexts_.size()) {
879  gui2::show_transient_message(_("This map is already open."), fn);
880  switch_context(i);
881  return true;
882  }
883 
884  return false;
885 }
886 
887 void context_manager::load_map(const std::string& filename, bool new_context)
888 {
889  if(new_context && check_switch_open_map(filename)) {
890  return;
891  }
892 
893  LOG_ED << "Load map: " << filename << (new_context ? " (new)" : " (same)") << "\n";
894  try {
895  {
896  context_ptr mc(new map_context(game_config_, filename));
897  if(mc->get_filename() != filename) {
898  if(new_context && check_switch_open_map(mc->get_filename())) {
899  return;
900  }
901  }
902 
903  if(new_context) {
904  int new_id = add_map_context_of(std::move(mc));
905  switch_context(new_id);
906  } else {
907  replace_map_context_with(std::move(mc));
908  }
909  }
910 
911  if(get_map_context().is_embedded()) {
912  const std::string& msg = _("Loaded embedded map data");
913  gui2::show_transient_message(_("Map loaded from scenario"), msg);
914  } else {
915  if(get_map_context().get_filename() != filename) {
916  if(get_map_context().get_map_data_key().empty()) {
917  ERR_ED << "Internal error, map context filename changed: "
918  << filename << " -> " << get_map_context().get_filename()
919  << " with no apparent scenario load\n";
920  } else {
921  utils::string_map symbols;
922  symbols["old"] = filename;
923  const std::string& msg = _("Loaded referenced map file:\n$new");
924  symbols["new"] = get_map_context().get_filename();
925  symbols["map_data"] = get_map_context().get_map_data_key();
926  gui2::show_transient_message(_("Map loaded from scenario"),
927  //TODO: msg is already translated does vgettext make sense?
928  VGETTEXT(msg.c_str(), symbols));
929  }
930  }
931  }
932  } catch(const editor_map_load_exception& e) {
933  gui2::show_transient_message(_("Error loading map"), e.what());
934  return;
935  }
936 }
937 
939 {
940  if(!confirm_discard()) {
941  return;
942  }
943 
944  std::string filename = get_map_context().get_filename();
945  if(filename.empty()) {
946  ERR_ED << "Empty filename in map revert" << std::endl;
947  return;
948  }
949 
950  load_map(filename, false);
951 }
952 
953 void context_manager::new_map(int width, int height, const t_translation::terrain_code& fill, bool new_context)
954 {
955  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
956  editor_map m(width, height, fill);
957 
958  if(new_context) {
959  int new_id = add_map_context(m, true, default_schedule);
960  switch_context(new_id);
961  } else {
962  replace_map_context(m, true, default_schedule);
963  }
964 }
965 
966 void context_manager::new_scenario(int width, int height, const t_translation::terrain_code& fill, bool new_context)
967 {
968  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
969  editor_map m(width, height, fill);
970 
971  if(new_context) {
972  int new_id = add_map_context(m, false, default_schedule);
973  switch_context(new_id);
974  } else {
975  replace_map_context(m, false, default_schedule);
976  }
977 
978  // Give the new scenario an initial side.
980  gui().set_team(0, true);
981  gui().set_playing_team(0);
982  gui_.init_flags();
983 }
984 
985 //
986 // Context manipulation
987 //
988 
989 template<typename... T>
990 int context_manager::add_map_context(const T&... args)
991 {
992  map_contexts_.emplace_back(new map_context(args...));
993  return map_contexts_.size() - 1;
994 }
995 
997 {
998  map_contexts_.emplace_back(std::move(mc));
999  return map_contexts_.size() - 1;
1000 }
1001 
1002 template<typename... T>
1004 {
1005  context_ptr new_mc(new map_context(args...));
1006  replace_map_context_with(std::move(new_mc));
1007 }
1008 
1010 {
1013 }
1014 
1016 {
1017  if(saved_windows_.empty()) {
1020 
1021  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
1022  add_map_context(editor_map(44, 33, default_terrain), true, default_schedule);
1023  } else {
1024  for(const std::string& filename : saved_windows_) {
1025  add_map_context(game_config_, filename);
1026  }
1027 
1028  saved_windows_.clear();
1029  }
1030 }
1031 
1033 {
1034  if(!confirm_discard()) return;
1035 
1036  if(map_contexts_.size() == 1) {
1038  map_contexts_.erase(map_contexts_.begin());
1039  } else if(current_context_index_ == static_cast<int>(map_contexts_.size()) - 1) {
1040  map_contexts_.pop_back();
1042  } else {
1044  }
1045 
1047 }
1048 
1049 void context_manager::switch_context(const int index, const bool force)
1050 {
1051  if(index < 0 || static_cast<std::size_t>(index) >= map_contexts_.size()) {
1052  WRN_ED << "Invalid index in switch map context: " << index << std::endl;
1053  return;
1054  }
1055 
1056  if(index == current_context_index_ && !force) {
1057  return;
1058  }
1059 
1060  // Disable the labels of the current context before switching.
1061  // The refresher handles enabling the new ones.
1062  get_map_context().get_labels().enable(false);
1063 
1065 
1067 }
1068 
1070 {
1071  std::string name = get_map_context().get_name();
1072 
1073  if(name.empty()) {
1075  }
1076 
1077  if(name.empty()){
1079  }
1080 
1081  const std::string& wm_title_string = name + " - " + game_config::get_default_title_string();
1082  CVideo::get_singleton().set_window_title(wm_title_string);
1083 }
1084 
1085 } //Namespace editor
int auto_update_transitions()
Definition: editor.cpp:25
bool write_scenario(bool display_confirmation=false)
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
void set_side_setup(editor_team_info &info)
TODO.
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:153
const std::string & get_name() const
::tod_manager * tod_manager
Definition: resources.cpp:30
void apply_mask_dialog()
Display an apply mask dialog and process user input.
std::size_t check_open_map(const std::string &fn) const
Check if a map is already open.
bool check_switch_open_map(const std::string &fn)
Check if a map is already open.
std::unique_ptr< map_context > context_ptr
std::size_t modified_maps(std::string &modified)
int auto_update_transitions_
Flag to rebuild terrain on every terrain change.
const t_translation::terrain_code & get_selected_bg_terrain()
std::vector< std::string > recent_files()
Retrieves the list of recently opened files.
Definition: editor.cpp:119
const terrain_code NONE_TERRAIN
Definition: translation.hpp:59
void edit_scenario_dialog()
Display a scenario edit dialog and process user input.
std::map< std::string, t_string > string_map
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3026
file_dialog & set_extension(const std::string &value)
Sets the default file extension for file names in save mode.
game_classification * classification
Definition: resources.cpp:35
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
void change_display_context(const display_context *dc)
Definition: display.cpp:515
void replace_map_context_with(context_ptr &&mc)
std::vector< context_ptr > map_contexts_
The currently opened map context object.
void new_side()
Adds a new side to the map.
void reload_map()
Reload the map after it has significantly changed (when e.g.
bool save_scenario()
Saves the scenario under the current filename.
void resize_map_dialog()
Display a load map dialog and process user input.
void set_playing_team(std::size_t team)
set_playing_team sets the team whose turn it currently is
Definition: display.cpp:421
#define a
config_array_view child_range(config_key_type key) const
std::optional< uint32_t > get_seed()
void save_scenario_as_dialog()
Display a save map as dialog and process user input.
void rename_area_dialog()
Display an dialog to querry a new id for an [time_area].
void refresh_after_action(bool drag_part=false)
Refresh the display after an action has been performed.
void init_flags()
Init the flag list and the team colors used by ~TC.
Definition: display.cpp:308
const std::string & get_filename() const
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, const bool restore_background)
Shows a transient message to the user.
void set_area_id(int area_index, const std::string &id)
map_labels & get_labels()
void save_map()
Save the map, open dialog if not named yet.
#define LOG_ED
General purpose widgets.
std::string default_dir_
Default directory for map load/save as dialogs.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
std::string get_default_title_string()
void perform_refresh(const editor_action &action, bool drag_part=false)
Perform an action on the current map_context, then refresh the display.
file_dialog & set_save_mode(bool value)
Sets the dialog&#39;s behavior on non-existent file name inputs.
static CVideo & get_singleton()
Definition: video.hpp:49
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
const std::vector< std::string > items
int add_map_context(const T &... args)
Add a map context.
Replace contents of the entire map, Useful as a fallback undo method when something else would be imp...
Definition: action.hpp:38
static std::string _(const char *str)
Definition: gettext.hpp:93
bool show(const unsigned auto_close_time=0)
Shows the window.
bool confirm_discard()
Shows an are-you-sure dialog if the map was modified.
void set_needs_reload(bool value=true)
Setter for the reload flag.
map_context & get_map_context()
Get the current map context object.
void refresh_all()
Refresh everything, i.e.
int add_map_context_of(context_ptr &&mc)
map_generator * get_selected_map_generator()
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)
TODO.
static bfs::path get_dir(const bfs::path &dirpath)
Definition: filesystem.cpp:274
void revert_map()
Revert the map by reloading it from disk.
const std::string & get_id() const
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
bool modified() const
void expand_areas_menu(std::vector< config > &items, int i)
Menu expanding for the map&#39;s defined areas.
std::string path() const
Gets the current file selection.
std::string default_dir()
Definition: editor.cpp:33
#define WRN_ED
game_classification & get_classification()
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:339
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:56
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
void close_current_context()
Closes the active map context.
std::string label
What to show in the filter&#39;s drop-down list.
Definition: manager.cpp:217
int w() const
Effective map width.
Definition: map.hpp:50
const std::set< map_location > & selection() const
Return the selection set.
Definition: editor_map.hpp:149
void load_map_dialog(bool force_same_context=false)
Display a load map dialog and process user input.
std::string get_user_data_dir()
Definition: filesystem.cpp:792
void reset_starting_position_labels(display &disp)
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.hpp:619
void select_map_generator(map_generator *mg)
filter_context * filter_con
Definition: resources.cpp:24
void set_embedded(bool v)
const game_config_view & game_config_
const std::set< map_location > changed_locations() const
void set_auto_update_transitions(int value)
Definition: editor.cpp:29
void new_scenario_dialog()
Display a new map dialog and process user input.
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
Editor action classes.
bool random_start_time() const
bool save_map()
Saves the map under the current filename.
void enable(bool is_enabled)
Definition: label.cpp:254
std::string path
Definition: game_config.cpp:39
Modify, read and display user preferences.
Shows a yes and no button.
Definition: message.hpp:80
map_display and display: classes which take care of displaying the map and game-data on the screen...
void refresh_on_context_change()
Performs the necessary housekeeping necessary when switching contexts.
bool is_pure_map() const
void create_buttons()
Definition: display.cpp:879
Manage the empty-palette in the editor.
Definition: action.cpp:30
std::string default_terrain
Definition: game_config.cpp:71
void perform_action(const editor_action &action)
Performs an action (thus modifying the map).
const char * what() const noexcept
Definition: exceptions.hpp:36
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3019
bool is_active_transitions_hotkey(const std::string &item)
void switch_context(const int index, const bool force=false)
Switches the context to the one under the specified index.
void new_scenario(int width, int height, const t_translation::terrain_code &fill, bool new_context)
Create a new scenario.
Paint the same terrain on a number of locations on the map.
Definition: action.hpp:265
void new_map(int width, int height, const t_translation::terrain_code &fill, bool new_context)
Create a new map.
bool is_embedded() const
int number_of_turns() const
void load_map(const std::string &filename, bool new_context)
Load a map given the filename.
std::vector< std::string > get_area_ids() const
void set_needs_terrain_rebuild(bool value=true)
Setter for the terrain rebuild flag.
Encapsulates the map of the game.
Definition: location.hpp:38
class location_palette * locs_
const std::string & get_description() const
void clear_changed_locations()
This class adds extra editor-specific functionality to a normal gamemap.
Definition: editor_map.hpp:70
map_generator * create_map_generator(const std::string &name, const config &cfg, const config *vars)
Definition: map_create.cpp:29
std::size_t i
Definition: function.cpp:967
void add_item(const std::string &id)
void fill_selection()
Fill the selection with the foreground terrain.
void save_map_as_dialog()
Display a save map as dialog and process user input.
bool write_map(bool display_confirmation=false)
Save the map under a given filename.
Game configuration data as global variables.
Definition: build_info.cpp:59
void expand_local_time_menu(std::vector< config > &items, int i)
Menu expanding for the map&#39;s defined areas.
void rebuild_terrain(const map_location &loc)
bool save_scenario_as(const std::string &filename)
const tod_manager * get_time_manager() const
static map_location::DIRECTION s
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
editor_display & gui()
Base class for all editor actions.
Definition: action_base.hpp:41
void save_all_maps(bool auto_save_windows=false)
Save all maps, open dialog if not named yet, except when using auto_save_windows which will name unna...
const std::string & get_map_data_key() const
void create_mask_to_dialog()
Display an apply mask dialog and process user input.
Declarations for File-IO.
int w
This class wraps around a map to provide a concise interface for the editor to work with...
Definition: map_context.hpp:60
#define N_(String)
Definition: gettext.hpp:101
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.
Definition: unicode.cpp:72
const std::string unicode_bullet
Definition: constants.cpp:47
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
void replace_map_context(const T &... args)
Replace the current map context and refresh accordingly.
const config & find_child(config_key_type key, const std::string &name, const std::string &value) const
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
context_manager(editor_display &gui, const game_config_view &game_config)
void set_team(std::size_t team, bool observe=false)
Sets the team controlled by the player using the computer.
Definition: display.cpp:402
void expand_sides_menu(std::vector< config > &items, int i)
Menu expanding for the map&#39;s player sides.
bool victory_defeated() const
static std::string get_filename(const std::string &file_code)
map_generator * last_map_generator_
int turns()
Definition: game.cpp:559
void edit_side_dialog(int side_index)
Display a side edit dialog and process user input.
double t
Definition: astarsearch.cpp:65
void reload_map()
Updates internals that cache map size.
Definition: display.cpp:509
virtual const editor_map & map() const override
Const map accessor.
lu_byte left
Definition: lparser.cpp:1226
int get_active_area() const
#define ERR_ED
void set_needs_labels_reset(bool value=true)
Setter for the labels reset flag.
std::optional< int > get_xp_mod() const
std::vector< std::unique_ptr< map_generator > > map_generators_
Available random map generators.
void set_filename(const std::string &fn)
void expand_load_mru_menu(std::vector< config > &items, int i)
Menu expanding for most recent loaded list.
const std::set< map_location > & get_area_by_index(int index) const
void init_map_generators(const game_config_view &game_config)
init available random map generators
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:206
void generate_map_dialog()
Display a generate random map dialog and process user input.
void set_window_title()
Displays the specified map name in the window titlebar.
void create_default_context()
Creates a default map context object, used to ensure there is always at least one.
The dialog for selecting which random generator to use in the editor.
#define e
bool save_map_as(const std::string &filename)
Save the map under a given filename.
void expand_time_menu(std::vector< config > &items, int i)
Menu expanding for the map&#39;s defined areas.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
virtual std::string create_map(std::optional< uint32_t > randomseed={})=0
Creates a new map and returns it.
void load_mru_item(unsigned index, bool force_same_context=false)
Open the specified entry from the recent files list.
virtual const std::vector< team > & teams() const override
Const teams accessor.
int h() const
Effective map height.
Definition: map.hpp:53
static std::vector< std::string > saved_windows_
static const std::string get_menu_marker(const bool changed)
void expand_open_maps_menu(std::vector< config > &items, int i)
Menu expanding for open maps list.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
const t_string & user_team_name() const
Definition: team.hpp:310
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410
const t_string get_default_context_name() const
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:504
void new_map_dialog()
Display a new map dialog and process user input.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
Definition: file_dialog.hpp:57