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  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-editor"
16 
17 #include "resources.hpp"
18 #include "team.hpp"
19 
20 #include "display.hpp"
23 #include "filesystem.hpp"
24 #include "formula/string_utils.hpp"
25 #include "game_board.hpp"
28 #include "gettext.hpp"
29 
30 #include "editor/action/action.hpp"
32 #include "preferences/editor.hpp"
33 
39 #include "gui/dialogs/message.hpp"
41 #include "gui/widgets/retval.hpp"
42 
45 #include "game_config_view.hpp"
46 
47 #include "terrain/translation.hpp"
48 
49 #include <memory>
50 
51 namespace editor {
52 
53 static std::vector<std::string> saved_windows_;
54 
55 static const std::string get_menu_marker(const bool changed)
56 {
57  std::ostringstream ss;
58  ss << "[<span ";
59 
60  if(changed) {
61  ss << "color='#f00' ";
62  }
63 
64  ss << ">" << font::unicode_bullet << "</span>]";
65  return ss.str();
66 }
67 
69  : locs_(nullptr)
70  , gui_(gui)
71  , game_config_(game_config)
72  , default_dir_(preferences::editor::default_dir())
73  , map_generators_()
74  , last_map_generator_(nullptr)
75  , current_context_index_(0)
76  , auto_update_transitions_(preferences::editor::auto_update_transitions())
77  , map_contexts_()
78  , clipboard_()
79 {
80  resources::filter_con = this;
81 
82  if(default_dir_.empty()) {
84  }
85 
87  init_map_generators(game_config);
88 }
89 
91 {
92  // Restore default window title
94 
95  resources::filter_con = nullptr;
96 }
97 
99 {
101 
102  // TODO register the tod_manager with the gui?
105 
106  // Reset side when switching to an existing scenario
107  if (gui().get_teams().size() > 0) {
108  gui().set_team(0, true);
109  gui().set_playing_team(0);
110  }
111  gui().init_flags();
112 
113  reload_map();
114 
115  // Enable the labels of the current context;
117 
119 }
120 
122 {
123  gui_.rebuild_all();
129  if(locs_) {
130  for(const auto& loc : get_map_context().map().special_locations().left) {
131  locs_->add_item(loc.first);
132  }
133  if(!get_map_context().is_pure_map()) {
134  // If the scenario has more than 9 teams, add locations for them
135  // (First 9 teams are always in the list)
136  size_t n_teams = get_map_context().teams().size();
137  for(size_t i = 10; i <= n_teams; i++) {
138  locs_->add_item(std::to_string(i));
139  }
140  }
141  }
142 }
143 
145 {
146  gui_.reload_map();
149  refresh_all();
150 }
151 
153 {
154  switch (auto_update_transitions_) {
156  return (item == "editor-auto-update-transitions");
158  return (item == "editor-partial-update-transitions");
160  return (item == "editor-no-update-transitions");
161  }
162 
163  return true; //should not be reached
164 }
165 
167 {
170 
172  return true;
173  }
174 
175  return false;
176 }
177 
178 std::size_t context_manager::modified_maps(std::string& message)
179 {
180  std::vector<std::string> modified;
181  for(auto& mc : map_contexts_) {
182  if(mc->modified()) {
183  if(!mc->get_name().empty()) {
184  modified.push_back(mc->get_name());
185  } else if(!mc->get_filename().empty()) {
186  modified.push_back(mc->get_filename());
187  } else {
188  modified.push_back(mc->get_default_context_name());
189  }
190  }
191  }
192 
193  for(std::string& str : modified) {
194  message += "\n" + font::unicode_bullet + " " + str;
195  }
196 
197  return modified.size();
198 }
199 
200 void context_manager::load_map_dialog(bool force_same_context /* = false */)
201 {
203  if(fn.empty()) {
204  fn = default_dir_;
205  }
206 
208 
209  dlg.set_title(_("Load Map"))
210  .set_path(fn);
211 
212  if(dlg.show()) {
213  load_map(dlg.path(), !force_same_context);
214  }
215 }
216 
217 void context_manager::load_mru_item(unsigned int index, bool force_same_context /* = false */)
218 {
219  const std::vector<std::string>& mru = preferences::editor::recent_files();
220  if(mru.empty() || index >= mru.size()) {
221  return;
222  }
223 
224  load_map(mru[index], !force_same_context);
225 }
226 
228 {
229  team& t = get_map_context().teams()[side_index];
230 
231  editor_team_info team_info(t);
232 
233  if(gui2::dialogs::editor_edit_side::execute(team_info)) {
234  get_map_context().set_side_setup(team_info);
235  }
236 }
237 
239 {
240  map_context& context = get_map_context();
241 
242  // TODO
243  //std::string fn = filesystem::directory_name(context.get_filename());
244 
245  std::string id = context.get_id();
246  std::string name = context.get_name();
247  std::string description = context.get_description();
248 
249  int turns = context.get_time_manager()->number_of_turns();
250  int xp_mod = context.get_xp_mod() ? *context.get_xp_mod() : 70;
251 
252  bool victory = context.victory_defeated();
253  bool random = context.random_start_time();
254 
255  const bool ok = gui2::dialogs::editor_edit_scenario::execute(
256  id, name, description, turns, xp_mod, victory, random
257  );
258 
259  if(!ok) {
260  return;
261  }
262 
263  context.set_scenario_setup(id, name, description, turns, xp_mod, victory, random);
264 
265  if(!name.empty()) {
267  }
268 }
269 
271 {
272  const editor_map& map = get_map_context().map();
273 
274  int w = map.w();
275  int h = map.h();
276 
277  if(gui2::dialogs::editor_new_map::execute(_("New Map"), w, h)) {
279  new_map(w, h, fill, true);
280  }
281 }
282 
284 {
285  const editor_map& map = get_map_context().map();
286 
287  int w = map.w();
288  int h = map.h();
289 
290  if(gui2::dialogs::editor_new_map::execute(_("New Scenario"), w, h)) {
292  new_scenario(w, h, fill, true);
293  }
294 }
295 
296 void context_manager::expand_open_maps_menu(std::vector<config>& items, int i)
297 {
298  auto pos = items.erase(items.begin() + i);
299  std::vector<config> contexts;
300 
301  for(std::size_t mci = 0; mci < map_contexts_.size(); ++mci) {
302  map_context& mc = *map_contexts_[mci];
303 
304  std::string filename;
305  if(mc.is_pure_map()) {
306  filename = filesystem::base_name(mc.get_filename());
307  } else {
308  filename = mc.get_name();
309  }
310 
311  if(filename.empty()) {
312  filename = mc.get_default_context_name();
313  }
314 
315  std::ostringstream ss;
316  ss << "[" << mci + 1 << "] ";
317 
318  const bool changed = mc.modified();
319 
320  if(changed) {
321  ss << "<i>" << filename << "</i>";
322  } else {
323  ss << filename;
324  }
325 
326  if(mc.is_embedded()) {
327  ss << " (E)";
328  }
329 
330  const std::string label = ss.str();
331  const std::string details = get_menu_marker(changed);
332 
333  contexts.emplace_back("label", label, "details", details);
334  }
335 
336  items.insert(pos, contexts.begin(), contexts.end());
337 }
338 
339 void context_manager::expand_load_mru_menu(std::vector<config>& items, int i)
340 {
341  std::vector<std::string> mru = preferences::editor::recent_files();
342 
343  auto pos = items.erase(items.begin() + i);
344 
345  if(mru.empty()) {
346  items.insert(pos, config {"label", _("No Recent Files")});
347  return;
348  }
349 
350  for(std::string& path : mru) {
351  // TODO: add proper leading ellipsization instead, since otherwise
352  // it'll be impossible to tell apart files with identical names and
353  // different parent paths.
355  }
356 
357  std::vector<config> temp;
358  std::transform(mru.begin(), mru.end(), std::back_inserter(temp), [](const std::string& str) {
359  return config {"label", str};
360  });
361 
362  items.insert(pos, temp.begin(), temp.end());
363 }
364 
365 void context_manager::expand_areas_menu(std::vector<config>& items, int i)
366 {
368  if(!tod) {
369  return;
370  }
371 
372  auto pos = items.erase(items.begin() + i);
373  std::vector<config> area_entries;
374 
375  std::vector<std::string> area_ids = tod->get_area_ids();
376 
377  for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
378  const std::string& area = area_ids[mci];
379 
380  std::stringstream ss;
381  ss << "[" << mci + 1 << "] ";\
382 
383  if(area.empty()) {
384  ss << "<i>" << _("Unnamed Area") << "</i>";
385  } else {
386  ss << area;
387  }
388 
389  const bool changed =
390  mci == static_cast<std::size_t>(get_map_context().get_active_area())
391  && tod->get_area_by_index(mci) != get_map_context().map().selection();
392 
393  const std::string label = ss.str();
394  const std::string details = get_menu_marker(changed);
395 
396  area_entries.emplace_back("label", label, "details", details);
397  }
398 
399  items.insert(pos, area_entries.begin(), area_entries.end());
400 }
401 
402 void context_manager::expand_sides_menu(std::vector<config>& items, int i)
403 {
404  auto pos = items.erase(items.begin() + i);
405  std::vector<config> contexts;
406 
407  for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
408 
409  const team& t = get_map_context().teams()[mci];
410  const std::string& teamname = t.user_team_name();
411  std::stringstream label;
412  label << "[" << mci+1 << "] ";
413 
414  if(teamname.empty()) {
415  label << "<i>" << _("New Side") << "</i>";
416  } else {
417  label << teamname;
418  }
419 
420  contexts.emplace_back("label", label.str());
421  }
422 
423  items.insert(pos, contexts.begin(), contexts.end());
424 }
425 
426 void context_manager::expand_time_menu(std::vector<config>& items, int i)
427 {
428  auto pos = items.erase(items.begin() + i);
429  std::vector<config> times;
430 
432 
433  assert(tod_m != nullptr);
434 
435  for(const time_of_day& time : tod_m->times()) {
436  times.emplace_back(
437  "details", time.name, // Use 'details' field here since the image will take the first column
438  "image", time.image
439  );
440  }
441 
442  items.insert(pos, times.begin(), times.end());
443 }
444 
445 void context_manager::expand_local_time_menu(std::vector<config>& items, int i)
446 {
447  auto pos = items.erase(items.begin() + i);
448  std::vector<config> times;
449 
451 
452  for(const time_of_day& time : tod_m->times(get_map_context().get_active_area())) {
453  times.emplace_back(
454  "details", time.name, // Use 'details' field here since the image will take the first column
455  "image", time.image
456  );
457  }
458 
459  items.insert(pos, times.begin(), times.end());
460 }
461 
463 {
464  std::string fn = get_map_context().get_filename();
465  if(fn.empty()) {
466  fn = default_dir_;
467  }
468 
470 
471  dlg.set_title(_("Apply Mask"))
472  .set_path(fn);
473 
474  if(dlg.show()) {
475  try {
476  map_context mask(game_config_, dlg.path());
477  editor_action_apply_mask a(mask.map());
479  } catch (const editor_map_load_exception& e) {
480  gui2::show_transient_message(_("Error loading mask"), e.what());
481  return;
482  } catch (const editor_action_exception& e) {
484  return;
485  }
486  }
487 }
488 
489 void context_manager::perform_refresh(const editor_action& action, bool drag_part /* =false */)
490 {
492  refresh_after_action(drag_part);
493 }
494 
496 {
497  int active_area = get_map_context().get_active_area();
498  std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
499 
500  if(gui2::dialogs::edit_text::execute(N_("Rename Area"), N_("Identifier:"), name)) {
501  get_map_context().get_time_manager()->set_area_id(active_area, name);
502  }
503 }
504 
506 {
507  std::string fn = get_map_context().get_filename();
508  if(fn.empty()) {
509  fn = default_dir_;
510  }
511 
513 
514  dlg.set_title(_("Choose Target Map"))
515  .set_path(fn);
516 
517  if(dlg.show()) {
518  try {
519  map_context map(game_config_, dlg.path());
520  editor_action_create_mask a(map.map());
522  } catch (const editor_map_load_exception& e) {
523  gui2::show_transient_message(_("Error loading map"), e.what());
524  return;
525  } catch (const editor_action_exception& e) {
527  return;
528  }
529  }
530 }
531 
533 {
534  if(get_map_context().needs_reload()) {
535  reload_map();
536  return;
537  }
538 
539  const std::set<map_location>& changed_locs = get_map_context().changed_locations();
540 
541  if(get_map_context().needs_terrain_rebuild()) {
544  && (!drag_part || get_map_context().everything_changed())))
545  {
546  gui_.rebuild_all();
549  } else {
550  for(const map_location& loc : changed_locs) {
551  gui_.rebuild_terrain(loc);
552  }
553  gui_.invalidate(changed_locs);
554  }
555  } else {
556  if(get_map_context().everything_changed()) {
558  } else {
559  gui_.invalidate(changed_locs);
560  }
561  }
562 
563  if(get_map_context().needs_labels_reset()) {
565  }
566 
569 }
570 
572 {
573  const editor_map& map = get_map_context().map();
574 
575  int w = map.w();
576  int h = map.h();
577 
579  bool copy = false;
580 
581  if(!gui2::dialogs::editor_resize_map::execute(w, h, dir, copy)) {
582  return;
583  }
584 
585  if(w != map.w() || h != map.h()) {
587  if(copy) {
589  }
590 
591  int x_offset = map.w() - w;
592  int y_offset = map.h() - h;
593 
594  switch (dir) {
598  y_offset = 0;
599  break;
603  y_offset /= 2;
604  break;
608  break;
609  default:
610  y_offset = 0;
611  WRN_ED << "Unknown resize expand direction" << std::endl;
612  break;
613  }
614 
615  switch (dir) {
619  x_offset = 0;
620  break;
624  x_offset /= 2;
625  break;
629  break;
630  default:
631  x_offset = 0;
632  break;
633  }
634 
635  editor_action_resize_map a(w, h, x_offset, y_offset, fill);
636  perform_refresh(a);
637  }
638 }
639 
641 {
642  std::string input_name = get_map_context().get_filename();
643  if(input_name.empty()) {
644  input_name = filesystem::get_dir(default_dir_ + "/maps");
645  }
646 
648 
649  dlg.set_title(_("Save Map As"))
650  .set_save_mode(true)
651  .set_path(input_name)
652  .set_extension(".map");
653 
654  if(!dlg.show()) {
655  return;
656  }
657 
658  save_map_as(dlg.path());
659 }
660 
662 {
663  std::string input_name = get_map_context().get_filename();
664  if(input_name.empty()) {
665  input_name = filesystem::get_dir(default_dir_ + "/scenarios");
666  }
667 
669 
670  dlg.set_title(_("Save Scenario As"))
671  .set_save_mode(true)
672  .set_path(input_name)
673  .set_extension(".cfg");
674 
675  if(!dlg.show()) {
676  return;
677  }
678 
679  save_scenario_as(dlg.path());
680 }
681 
683 {
684  for(const config& i : game_config.child_range("multiplayer")) {
685  if(i["map_generation"].empty() && i["scenario_generation"].empty()) {
686  continue;
687  }
688 
689  // TODO: we should probably use `child` with a try/catch block once that function throws
690  if(const auto generator_cfg = i.optional_child("generator")) {
691  map_generators_.emplace_back(create_map_generator(i["map_generation"].empty() ? i["scenario_generation"] : i["map_generation"], generator_cfg.value()));
692  } else {
693  ERR_ED << "Scenario \"" << i["name"] << "\" with id " << i["id"]
694  << " has map_generation= but no [generator] tag" << std::endl;
695  }
696  }
697 }
698 
700 {
701  if(map_generators_.empty()) {
702  gui2::show_error_message(_("No random map generators found."));
703  return;
704  }
705 
708 
709  if(dialog.show()) {
710  std::string map_string;
712  try {
713  map_string = map_generator->create_map(dialog.get_seed());
714  } catch (const mapgen_exception& e) {
715  gui2::show_transient_message(_("Map creation failed."), e.what());
716  return;
717  }
718 
719  if(map_string.empty()) {
720  gui2::show_transient_message("", _("Map creation failed."));
721  } else {
722  editor_map new_map(map_string);
723  editor_action_whole_map a(new_map);
724  get_map_context().set_needs_labels_reset(); // Ensure Player Start labels are updated together with newly generated map
725  perform_refresh(a);
726  }
727 
728  last_map_generator_ = map_generator;
729  }
730 }
731 
733 {
734  if(get_map_context().modified()) {
735  const int res = gui2::show_message(_("Unsaved Changes"),
736  _("Do you want to discard all changes made to the map since the last save?"), gui2::dialogs::message::yes_no_buttons);
737  return gui2::retval::CANCEL != res;
738  }
739 
740  return true;
741 }
742 
744 {
746 }
747 
748 void context_manager::save_all_maps(bool auto_save_windows)
749 {
750  int current = current_context_index_;
751  saved_windows_.clear();
752  for(std::size_t i = 0; i < map_contexts_.size(); ++i) {
753  switch_context(i);
754  std::string name = get_map_context().get_filename();
755  if(auto_save_windows) {
756  if(name.empty() || filesystem::is_directory(name)) {
757  std::ostringstream s;
758  s << default_dir_ << "/" << "window_" << i + 1;
759  name = s.str();
761  }
762  }
763  saved_windows_.push_back(name);
764  save_map();
765  }
766 
767  switch_context(current);
768 }
769 
771 {
772  const std::string& name = get_map_context().get_filename();
773  if(name.empty() || filesystem::is_directory(name)) {
774  if(get_map_context().is_pure_map()) {
776  } else {
778  }
779  } else {
780  if(get_map_context().is_pure_map()) {
781  write_map();
782  } else {
783  write_scenario();
784  }
785  }
786 }
787 
788 bool context_manager::save_scenario_as(const std::string& filename)
789 {
790  std::size_t is_open = check_open_map(filename);
791  if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
792  gui2::show_transient_message(_("This scenario is already open."), filename);
793  return false;
794  }
795 
796  std::string old_filename = get_map_context().get_filename();
797  bool embedded = get_map_context().is_embedded();
798 
799  get_map_context().set_filename(filename);
800  get_map_context().set_embedded(false);
801 
802  if(!write_scenario(true)) {
803  get_map_context().set_filename(old_filename);
804  get_map_context().set_embedded(embedded);
805  return false;
806  }
807 
808  return true;
809 }
810 
811 bool context_manager::save_map_as(const std::string& filename)
812 {
813  std::size_t is_open = check_open_map(filename);
814  if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
815  gui2::show_transient_message(_("This map is already open."), filename);
816  return false;
817  }
818 
819  std::string old_filename = get_map_context().get_filename();
820  bool embedded = get_map_context().is_embedded();
821 
822  get_map_context().set_filename(filename);
823  get_map_context().set_embedded(false);
824 
825  if(!write_map(true)) {
826  get_map_context().set_filename(old_filename);
827  get_map_context().set_embedded(embedded);
828  return false;
829  }
830 
831  return true;
832 }
833 
834 bool context_manager::write_scenario(bool display_confirmation)
835 {
836  try {
838  if(display_confirmation) {
839  gui2::show_transient_message("", _("Scenario saved."));
840  }
841  } catch (const editor_map_save_exception& e) {
843  return false;
844  }
845 
846  return true;
847 }
848 
849 bool context_manager::write_map(bool display_confirmation)
850 {
851  try {
853  if(display_confirmation) {
854  gui2::show_transient_message("", _("Map saved."));
855  }
856  } catch (const editor_map_save_exception& e) {
858  return false;
859  }
860 
861  return true;
862 }
863 
864 std::size_t context_manager::check_open_map(const std::string& fn) const
865 {
866  std::size_t i = 0;
867  while(i < map_contexts_.size() && map_contexts_[i]->get_filename() != fn) {
868  ++i;
869  }
870 
871  return i;
872 }
873 
874 bool context_manager::check_switch_open_map(const std::string& fn)
875 {
876  std::size_t i = check_open_map(fn);
877  if(i < map_contexts_.size()) {
878  gui2::show_transient_message(_("This map is already open."), fn);
879  switch_context(i);
880  return true;
881  }
882 
883  return false;
884 }
885 
886 void context_manager::load_map(const std::string& filename, bool new_context)
887 {
888  if(new_context && check_switch_open_map(filename)) {
889  return;
890  }
891 
892  LOG_ED << "Load map: " << filename << (new_context ? " (new)" : " (same)") << "\n";
893  try {
894  {
895  context_ptr mc(new map_context(game_config_, filename));
896  if(mc->get_filename() != filename) {
897  if(new_context && check_switch_open_map(mc->get_filename())) {
898  return;
899  }
900  }
901 
902  if(new_context) {
903  int new_id = add_map_context_of(std::move(mc));
904  switch_context(new_id);
905  } else {
906  replace_map_context_with(std::move(mc));
907  }
908  }
909 
910  if(get_map_context().is_embedded()) {
911  const std::string& msg = _("Loaded embedded map data");
912  gui2::show_transient_message(_("Map loaded from scenario"), msg);
913  } else {
914  if(get_map_context().get_filename() != filename) {
915  if(get_map_context().get_map_data_key().empty()) {
916  ERR_ED << "Internal error, map context filename changed: "
917  << filename << " -> " << get_map_context().get_filename()
918  << " with no apparent scenario load\n";
919  } else {
920  utils::string_map symbols;
921  symbols["old"] = filename;
922  const std::string& msg = _("Loaded referenced map file:\n$new");
923  symbols["new"] = get_map_context().get_filename();
924  symbols["map_data"] = get_map_context().get_map_data_key();
925  gui2::show_transient_message(_("Map loaded from scenario"),
926  //TODO: msg is already translated does vgettext make sense?
927  VGETTEXT(msg.c_str(), symbols));
928  }
929  }
930  }
931  } catch(const editor_map_load_exception& e) {
932  gui2::show_transient_message(_("Error loading map"), e.what());
933  return;
934  }
935 }
936 
938 {
939  if(!confirm_discard()) {
940  return;
941  }
942 
943  std::string filename = get_map_context().get_filename();
944  if(filename.empty()) {
945  ERR_ED << "Empty filename in map revert" << std::endl;
946  return;
947  }
948 
949  load_map(filename, false);
950 }
951 
952 void context_manager::new_map(int width, int height, const t_translation::terrain_code& fill, bool new_context)
953 {
954  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
955  editor_map m(width, height, fill);
956 
957  if(new_context) {
958  int new_id = add_map_context(m, true, default_schedule);
959  switch_context(new_id);
960  } else {
961  replace_map_context(m, true, default_schedule);
962  }
963 }
964 
965 void context_manager::new_scenario(int width, int height, const t_translation::terrain_code& fill, bool new_context)
966 {
967  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
968  editor_map m(width, height, fill);
969 
970  if(new_context) {
971  int new_id = add_map_context(m, false, default_schedule);
972  switch_context(new_id);
973  } else {
974  replace_map_context(m, false, default_schedule);
975  }
976 
977  // Give the new scenario an initial side.
979  gui().set_team(0, true);
980  gui().set_playing_team(0);
981  gui_.init_flags();
982 }
983 
984 //
985 // Context manipulation
986 //
987 
988 template<typename... T>
989 int context_manager::add_map_context(const T&... args)
990 {
991  map_contexts_.emplace_back(new map_context(args...));
992  return map_contexts_.size() - 1;
993 }
994 
996 {
997  map_contexts_.emplace_back(std::move(mc));
998  return map_contexts_.size() - 1;
999 }
1000 
1001 template<typename... T>
1003 {
1004  context_ptr new_mc(new map_context(args...));
1005  replace_map_context_with(std::move(new_mc));
1006 }
1007 
1009 {
1012 }
1013 
1015 {
1016  if(saved_windows_.empty()) {
1019 
1020  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
1021  add_map_context(editor_map(44, 33, default_terrain), true, default_schedule);
1022  } else {
1023  for(const std::string& filename : saved_windows_) {
1024  add_map_context(game_config_, filename);
1025  }
1026 
1027  saved_windows_.clear();
1028  }
1029 }
1030 
1032 {
1033  if(!confirm_discard()) return;
1034 
1035  if(map_contexts_.size() == 1) {
1037  map_contexts_.erase(map_contexts_.begin());
1038  } else if(current_context_index_ == static_cast<int>(map_contexts_.size()) - 1) {
1039  map_contexts_.pop_back();
1041  } else {
1043  }
1044 
1046 }
1047 
1048 void context_manager::switch_context(const int index, const bool force)
1049 {
1050  if(index < 0 || static_cast<std::size_t>(index) >= map_contexts_.size()) {
1051  WRN_ED << "Invalid index in switch map context: " << index << std::endl;
1052  return;
1053  }
1054 
1055  if(index == current_context_index_ && !force) {
1056  return;
1057  }
1058 
1059  // Disable the labels of the current context before switching.
1060  // The refresher handles enabling the new ones.
1061  get_map_context().get_labels().enable(false);
1062 
1064 
1066 }
1067 
1069 {
1070  std::string name = get_map_context().get_name();
1071 
1072  if(name.empty()) {
1074  }
1075 
1076  if(name.empty()){
1078  }
1079 
1080  const std::string& wm_title_string = name + " - " + game_config::get_default_title_string();
1081  CVideo::get_singleton().set_window_title(wm_title_string);
1082 }
1083 
1084 } //Namespace editor
int auto_update_transitions()
Definition: editor.cpp:24
bool write_scenario(bool display_confirmation=false)
Dialog was closed with the CANCEL button.
Definition: retval.hpp:37
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:152
const std::string & get_name() const
::tod_manager * tod_manager
Definition: resources.cpp:29
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:118
const terrain_code NONE_TERRAIN
Definition: translation.hpp:58
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:3025
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:34
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
void change_display_context(const display_context *dc)
Definition: display.cpp:514
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:420
#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:307
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:49
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:48
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
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:37
static std::string _(const char *str)
Definition: gettext.hpp:92
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:273
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:32
#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:332
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:55
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:71
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:216
int w() const
Effective map width.
Definition: map.hpp:49
const std::set< map_location > & selection() const
Return the selection set.
Definition: editor_map.hpp:148
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:791
void reset_starting_position_labels(display &disp)
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.hpp:618
void select_map_generator(map_generator *mg)
filter_context * filter_con
Definition: resources.cpp:23
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:28
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:253
std::string path
Definition: game_config.cpp:38
Modify, read and display user preferences.
Shows a yes and no button.
Definition: message.hpp:79
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:878
Manage the empty-palette in the editor.
Definition: action.cpp:29
std::string default_terrain
Definition: game_config.cpp:70
void perform_action(const editor_action &action)
Performs an action (thus modifying the map).
const char * what() const noexcept
Definition: exceptions.hpp:35
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3018
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:264
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:37
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:69
map_generator * create_map_generator(const std::string &name, const config &cfg, const config *vars)
Definition: map_create.cpp:28
std::size_t i
Definition: function.cpp:940
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:58
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:40
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:59
#define N_(String)
Definition: gettext.hpp:100
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:69
const std::string unicode_bullet
Definition: constants.cpp:46
#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:401
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:558
void edit_side_dialog(int side_index)
Display a side edit dialog and process user input.
double t
Definition: astarsearch.cpp:63
void reload_map()
Updates internals that cache map size.
Definition: display.cpp:508
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:205
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:59
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:52
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:309
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:409
const t_string get_default_context_name() const
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:503
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:56