The Battle for Wesnoth  1.17.10+dev
context_manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2022
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 #include "video.hpp"
31 
32 #include "editor/action/action.hpp"
34 #include "preferences/editor.hpp"
35 
41 #include "gui/dialogs/message.hpp"
43 #include "gui/widgets/retval.hpp"
44 
47 #include "game_config_view.hpp"
48 
49 #include "terrain/translation.hpp"
50 
51 #include <memory>
52 
53 namespace editor {
54 
55 static std::vector<std::string> saved_windows_;
56 
57 static const std::string get_menu_marker(const bool changed)
58 {
59  std::ostringstream ss;
60  ss << "[<span ";
61 
62  if(changed) {
63  ss << "color='#f00' ";
64  }
65 
66  ss << ">" << font::unicode_bullet << "</span>]";
67  return ss.str();
68 }
69 
71  : locs_(nullptr)
72  , gui_(gui)
73  , game_config_(game_config)
74  , default_dir_(preferences::editor::default_dir())
75  , map_generators_()
76  , last_map_generator_(nullptr)
77  , current_context_index_(0)
78  , auto_update_transitions_(preferences::editor::auto_update_transitions())
79  , map_contexts_()
80  , clipboard_()
81 {
82  resources::filter_con = this;
83 
84  if(default_dir_.empty()) {
86  }
87 
89  init_map_generators(game_config);
90 }
91 
93 {
94  // Restore default window title
96 
97  resources::filter_con = nullptr;
98 }
99 
101 {
103 
104  // TODO register the tod_manager with the gui?
107 
108  // Reset side when switching to an existing scenario
109  if (gui().get_teams().size() > 0) {
110  gui().set_team(0, true);
111  gui().set_playing_team(0);
112  }
113  gui().init_flags();
114 
115  reload_map();
116 
117  // Enable the labels of the current context;
119 
121 }
122 
124 {
125  gui_.rebuild_all();
131  if(locs_) {
132  for(const auto& loc : get_map_context().map().special_locations().left) {
133  locs_->add_item(loc.first);
134  }
135  if(!get_map_context().is_pure_map()) {
136  // If the scenario has more than 9 teams, add locations for them
137  // (First 9 teams are always in the list)
138  size_t n_teams = get_map_context().teams().size();
139  for(size_t i = 10; i <= n_teams; i++) {
140  locs_->add_item(std::to_string(i));
141  }
142  }
143  }
144 }
145 
147 {
148  gui_.reload_map();
151  refresh_all();
152 }
153 
155 {
156  switch (auto_update_transitions_) {
158  return (item == "editor-auto-update-transitions");
160  return (item == "editor-partial-update-transitions");
162  return (item == "editor-no-update-transitions");
163  }
164 
165  return true; //should not be reached
166 }
167 
169 {
172 
174  return true;
175  }
176 
177  return false;
178 }
179 
180 std::size_t context_manager::modified_maps(std::string& message)
181 {
182  std::vector<std::string> modified;
183  for(auto& mc : map_contexts_) {
184  if(mc->modified()) {
185  if(!mc->get_name().empty()) {
186  modified.push_back(mc->get_name());
187  } else if(!mc->get_filename().empty()) {
188  modified.push_back(mc->get_filename());
189  } else {
190  modified.push_back(mc->get_default_context_name());
191  }
192  }
193  }
194 
195  for(std::string& str : modified) {
196  message += "\n" + font::unicode_bullet + " " + str;
197  }
198 
199  return modified.size();
200 }
201 
202 void context_manager::load_map_dialog(bool force_same_context /* = false */)
203 {
205  if(fn.empty()) {
206  fn = default_dir_;
207  }
208 
210 
211  dlg.set_title(_("Load Map"))
212  .set_path(fn);
213 
214  if(dlg.show()) {
215  load_map(dlg.path(), !force_same_context);
216  }
217 }
218 
219 void context_manager::load_mru_item(unsigned int index, bool force_same_context /* = false */)
220 {
221  const std::vector<std::string>& mru = preferences::editor::recent_files();
222  if(mru.empty() || index >= mru.size()) {
223  return;
224  }
225 
226  load_map(mru[index], !force_same_context);
227 }
228 
230 {
231  team& t = get_map_context().teams()[side_index];
232 
233  editor_team_info team_info(t);
234 
235  if(gui2::dialogs::editor_edit_side::execute(team_info)) {
236  get_map_context().set_side_setup(team_info);
237  }
238 }
239 
241 {
242  map_context& context = get_map_context();
243 
244  // TODO
245  //std::string fn = filesystem::directory_name(context.get_filename());
246 
247  std::string id = context.get_id();
248  std::string name = context.get_name();
249  std::string description = context.get_description();
250 
251  int turns = context.get_time_manager()->number_of_turns();
252  int xp_mod = context.get_xp_mod() ? *context.get_xp_mod() : 70;
253 
254  bool victory = context.victory_defeated();
255  bool random = context.random_start_time();
256 
257  const bool ok = gui2::dialogs::editor_edit_scenario::execute(
258  id, name, description, turns, xp_mod, victory, random
259  );
260 
261  if(!ok) {
262  return;
263  }
264 
265  context.set_scenario_setup(id, name, description, turns, xp_mod, victory, random);
266 
267  if(!name.empty()) {
269  }
270 }
271 
273 {
274  const editor_map& map = get_map_context().map();
275 
276  int w = map.w();
277  int h = map.h();
278 
279  if(gui2::dialogs::editor_new_map::execute(_("New Map"), w, h)) {
281  new_map(w, h, fill, true);
282  }
283 }
284 
286 {
287  const editor_map& map = get_map_context().map();
288 
289  int w = map.w();
290  int h = map.h();
291 
292  if(gui2::dialogs::editor_new_map::execute(_("New Scenario"), w, h)) {
294  new_scenario(w, h, fill, true);
295  }
296 }
297 
298 void context_manager::expand_open_maps_menu(std::vector<config>& items, int i)
299 {
300  auto pos = items.erase(items.begin() + i);
301  std::vector<config> contexts;
302 
303  for(std::size_t mci = 0; mci < map_contexts_.size(); ++mci) {
304  map_context& mc = *map_contexts_[mci];
305 
306  std::string filename;
307  if(mc.is_pure_map()) {
308  filename = filesystem::base_name(mc.get_filename());
309  } else {
310  filename = mc.get_name();
311  }
312 
313  if(filename.empty()) {
314  filename = mc.get_default_context_name();
315  }
316 
317  std::ostringstream ss;
318  ss << "[" << mci + 1 << "] ";
319 
320  const bool changed = mc.modified();
321 
322  if(changed) {
323  ss << "<i>" << filename << "</i>";
324  } else {
325  ss << filename;
326  }
327 
328  if(mc.is_embedded()) {
329  ss << " (E)";
330  }
331 
332  const std::string label = ss.str();
333  const std::string details = get_menu_marker(changed);
334 
335  contexts.emplace_back("label", label, "details", details);
336  }
337 
338  items.insert(pos, contexts.begin(), contexts.end());
339 }
340 
341 void context_manager::expand_load_mru_menu(std::vector<config>& items, int i)
342 {
343  std::vector<std::string> mru = preferences::editor::recent_files();
344 
345  auto pos = items.erase(items.begin() + i);
346 
347  if(mru.empty()) {
348  items.insert(pos, config {"label", _("No Recent Files")});
349  return;
350  }
351 
352  for(std::string& path : mru) {
353  // TODO: add proper leading ellipsization instead, since otherwise
354  // it'll be impossible to tell apart files with identical names and
355  // different parent paths.
357  }
358 
359  std::vector<config> temp;
360  std::transform(mru.begin(), mru.end(), std::back_inserter(temp), [](const std::string& str) {
361  return config {"label", str};
362  });
363 
364  items.insert(pos, temp.begin(), temp.end());
365 }
366 
367 void context_manager::expand_areas_menu(std::vector<config>& items, int i)
368 {
370  if(!tod) {
371  return;
372  }
373 
374  auto pos = items.erase(items.begin() + i);
375  std::vector<config> area_entries;
376 
377  std::vector<std::string> area_ids = tod->get_area_ids();
378 
379  for(std::size_t mci = 0; mci < area_ids.size(); ++mci) {
380  const std::string& area = area_ids[mci];
381 
382  std::stringstream ss;
383  ss << "[" << mci + 1 << "] ";\
384 
385  if(area.empty()) {
386  ss << "<i>" << _("Unnamed Area") << "</i>";
387  } else {
388  ss << area;
389  }
390 
391  const bool changed =
392  mci == static_cast<std::size_t>(get_map_context().get_active_area())
393  && tod->get_area_by_index(mci) != get_map_context().map().selection();
394 
395  const std::string label = ss.str();
396  const std::string details = get_menu_marker(changed);
397 
398  area_entries.emplace_back("label", label, "details", details);
399  }
400 
401  items.insert(pos, area_entries.begin(), area_entries.end());
402 }
403 
404 void context_manager::expand_sides_menu(std::vector<config>& items, int i)
405 {
406  auto pos = items.erase(items.begin() + i);
407  std::vector<config> contexts;
408 
409  for(std::size_t mci = 0; mci < get_map_context().teams().size(); ++mci) {
410 
411  const team& t = get_map_context().teams()[mci];
412  const std::string& teamname = t.user_team_name();
413  std::stringstream label;
414  label << "[" << mci+1 << "] ";
415 
416  if(teamname.empty()) {
417  label << "<i>" << _("New Side") << "</i>";
418  } else {
419  label << teamname;
420  }
421 
422  contexts.emplace_back("label", label.str());
423  }
424 
425  items.insert(pos, contexts.begin(), contexts.end());
426 }
427 
428 void context_manager::expand_time_menu(std::vector<config>& items, int i)
429 {
430  auto pos = items.erase(items.begin() + i);
431  std::vector<config> times;
432 
434 
435  assert(tod_m != nullptr);
436 
437  for(const time_of_day& time : tod_m->times()) {
438  times.emplace_back(
439  "details", time.name, // Use 'details' field here since the image will take the first column
440  "image", time.image
441  );
442  }
443 
444  items.insert(pos, times.begin(), times.end());
445 }
446 
447 void context_manager::expand_local_time_menu(std::vector<config>& items, int i)
448 {
449  auto pos = items.erase(items.begin() + i);
450  std::vector<config> times;
451 
453 
454  for(const time_of_day& time : tod_m->times(get_map_context().get_active_area())) {
455  times.emplace_back(
456  "details", time.name, // Use 'details' field here since the image will take the first column
457  "image", time.image
458  );
459  }
460 
461  items.insert(pos, times.begin(), times.end());
462 }
463 
465 {
466  std::string fn = get_map_context().get_filename();
467  if(fn.empty()) {
468  fn = default_dir_;
469  }
470 
472 
473  dlg.set_title(_("Apply Mask"))
474  .set_path(fn);
475 
476  if(dlg.show()) {
477  try {
478  map_context mask(game_config_, dlg.path());
479  editor_action_apply_mask a(mask.map());
481  } catch (const editor_map_load_exception& e) {
482  gui2::show_transient_message(_("Error loading mask"), e.what());
483  return;
484  } catch (const editor_action_exception& e) {
486  return;
487  }
488  }
489 }
490 
491 void context_manager::perform_refresh(const editor_action& action, bool drag_part /* =false */)
492 {
494  refresh_after_action(drag_part);
495 }
496 
498 {
499  int active_area = get_map_context().get_active_area();
500  std::string name = get_map_context().get_time_manager()->get_area_ids()[active_area];
501 
502  if(gui2::dialogs::edit_text::execute(N_("Rename Area"), N_("Identifier:"), name)) {
503  get_map_context().get_time_manager()->set_area_id(active_area, name);
504  }
505 }
506 
508 {
509  std::string fn = get_map_context().get_filename();
510  if(fn.empty()) {
511  fn = default_dir_;
512  }
513 
515 
516  dlg.set_title(_("Choose Target Map"))
517  .set_path(fn);
518 
519  if(dlg.show()) {
520  try {
521  map_context map(game_config_, dlg.path());
522  editor_action_create_mask a(map.map());
524  } catch (const editor_map_load_exception& e) {
525  gui2::show_transient_message(_("Error loading map"), e.what());
526  return;
527  } catch (const editor_action_exception& e) {
529  return;
530  }
531  }
532 }
533 
535 {
536  if(get_map_context().needs_reload()) {
537  reload_map();
538  return;
539  }
540 
541  const std::set<map_location>& changed_locs = get_map_context().changed_locations();
542 
543  if(get_map_context().needs_terrain_rebuild()) {
546  && (!drag_part || get_map_context().everything_changed())))
547  {
548  gui_.rebuild_all();
551  } else {
552  for(const map_location& loc : changed_locs) {
553  gui_.rebuild_terrain(loc);
554  }
555  gui_.invalidate(changed_locs);
556  }
557  } else {
558  if(get_map_context().everything_changed()) {
560  } else {
561  gui_.invalidate(changed_locs);
562  }
563  }
564 
565  if(get_map_context().needs_labels_reset()) {
567  }
568 
571 }
572 
574 {
575  const editor_map& map = get_map_context().map();
576 
577  int w = map.w();
578  int h = map.h();
579 
581  bool copy = false;
582 
583  if(!gui2::dialogs::editor_resize_map::execute(w, h, dir, copy)) {
584  return;
585  }
586 
587  if(w != map.w() || h != map.h()) {
589  if(copy) {
591  }
592 
593  int x_offset = map.w() - w;
594  int y_offset = map.h() - h;
595 
596  switch (dir) {
600  y_offset = 0;
601  break;
605  y_offset /= 2;
606  break;
610  break;
611  default:
612  y_offset = 0;
613  WRN_ED << "Unknown resize expand direction";
614  break;
615  }
616 
617  switch (dir) {
621  x_offset = 0;
622  break;
626  x_offset /= 2;
627  break;
631  break;
632  default:
633  x_offset = 0;
634  break;
635  }
636 
637  editor_action_resize_map a(w, h, x_offset, y_offset, fill);
638  perform_refresh(a);
639  }
640 }
641 
643 {
644  std::string input_name = get_map_context().get_filename();
645  if(input_name.empty()) {
646  input_name = filesystem::get_dir(default_dir_ + "/maps");
647  }
648 
650 
651  dlg.set_title(_("Save Map As"))
652  .set_save_mode(true)
653  .set_path(input_name)
654  .set_extension(".map");
655 
656  if(!dlg.show()) {
657  return;
658  }
659 
660  save_map_as(dlg.path());
661 }
662 
664 {
665  std::string input_name = get_map_context().get_filename();
666  if(input_name.empty()) {
667  input_name = filesystem::get_dir(default_dir_ + "/scenarios");
668  }
669 
671 
672  dlg.set_title(_("Save Scenario As"))
673  .set_save_mode(true)
674  .set_path(input_name)
675  .set_extension(".cfg");
676 
677  if(!dlg.show()) {
678  return;
679  }
680 
681  save_scenario_as(dlg.path());
682 }
683 
685 {
686  for(const config& i : game_config.child_range("multiplayer")) {
687  if(i["map_generation"].empty() && i["scenario_generation"].empty()) {
688  continue;
689  }
690 
691  // TODO: we should probably use `child` with a try/catch block once that function throws
692  if(const auto generator_cfg = i.optional_child("generator")) {
693  map_generators_.emplace_back(create_map_generator(i["map_generation"].empty() ? i["scenario_generation"] : i["map_generation"], generator_cfg.value()));
694  } else {
695  ERR_ED << "Scenario \"" << i["name"] << "\" with id " << i["id"]
696  << " has map_generation= but no [generator] tag";
697  }
698  }
699 }
700 
702 {
703  if(map_generators_.empty()) {
704  gui2::show_error_message(_("No random map generators found."));
705  return;
706  }
707 
710 
711  if(dialog.show()) {
712  std::string map_string;
714  try {
715  map_string = map_generator->create_map(dialog.get_seed());
716  } catch (const mapgen_exception& e) {
717  gui2::show_transient_message(_("Map creation failed."), e.what());
718  return;
719  }
720 
721  if(map_string.empty()) {
722  gui2::show_transient_message("", _("Map creation failed."));
723  } else {
724  editor_map new_map(map_string);
725  editor_action_whole_map a(new_map);
726  get_map_context().set_needs_labels_reset(); // Ensure Player Start labels are updated together with newly generated map
727  perform_refresh(a);
728  }
729 
730  last_map_generator_ = map_generator;
731  }
732 }
733 
735 {
736  if(get_map_context().modified()) {
737  const int res = gui2::show_message(_("Unsaved Changes"),
738  _("Do you want to discard all changes made to the map since the last save?"), gui2::dialogs::message::yes_no_buttons);
739  return gui2::retval::CANCEL != res;
740  }
741 
742  return true;
743 }
744 
746 {
748 }
749 
750 void context_manager::save_all_maps(bool auto_save_windows)
751 {
752  int current = current_context_index_;
753  saved_windows_.clear();
754  for(std::size_t i = 0; i < map_contexts_.size(); ++i) {
755  switch_context(i);
756  std::string name = get_map_context().get_filename();
757  if(auto_save_windows) {
758  if(name.empty() || filesystem::is_directory(name)) {
759  std::ostringstream s;
760  s << default_dir_ << "/" << "window_" << i + 1;
761  name = s.str();
763  }
764  }
765  saved_windows_.push_back(name);
766  save_map();
767  }
768 
769  switch_context(current);
770 }
771 
773 {
774  const std::string& name = get_map_context().get_filename();
775  if(name.empty() || filesystem::is_directory(name)) {
776  if(get_map_context().is_pure_map()) {
778  } else {
780  }
781  } else {
782  if(get_map_context().is_pure_map()) {
783  write_map();
784  } else {
785  write_scenario();
786  }
787  }
788 }
789 
790 bool context_manager::save_scenario_as(const std::string& filename)
791 {
792  std::size_t is_open = check_open_map(filename);
793  if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
794  gui2::show_transient_message(_("This scenario is already open."), filename);
795  return false;
796  }
797 
798  std::string old_filename = get_map_context().get_filename();
799  bool embedded = get_map_context().is_embedded();
800 
801  get_map_context().set_filename(filename);
802  get_map_context().set_embedded(false);
803 
804  if(!write_scenario(true)) {
805  get_map_context().set_filename(old_filename);
806  get_map_context().set_embedded(embedded);
807  return false;
808  }
809 
810  return true;
811 }
812 
813 bool context_manager::save_map_as(const std::string& filename)
814 {
815  std::size_t is_open = check_open_map(filename);
816  if(is_open < map_contexts_.size() && is_open != static_cast<unsigned>(current_context_index_)) {
817  gui2::show_transient_message(_("This map is already open."), filename);
818  return false;
819  }
820 
821  std::string old_filename = get_map_context().get_filename();
822  bool embedded = get_map_context().is_embedded();
823 
824  get_map_context().set_filename(filename);
825  get_map_context().set_embedded(false);
826 
827  if(!write_map(true)) {
828  get_map_context().set_filename(old_filename);
829  get_map_context().set_embedded(embedded);
830  return false;
831  }
832 
833  return true;
834 }
835 
836 bool context_manager::write_scenario(bool display_confirmation)
837 {
838  try {
840  if(display_confirmation) {
841  gui2::show_transient_message("", _("Scenario saved."));
842  }
843  } catch (const editor_map_save_exception& e) {
845  return false;
846  }
847 
848  return true;
849 }
850 
851 bool context_manager::write_map(bool display_confirmation)
852 {
853  try {
855  if(display_confirmation) {
856  gui2::show_transient_message("", _("Map saved."));
857  }
858  } catch (const editor_map_save_exception& e) {
860  return false;
861  }
862 
863  return true;
864 }
865 
866 std::size_t context_manager::check_open_map(const std::string& fn) const
867 {
868  std::size_t i = 0;
869  while(i < map_contexts_.size() && map_contexts_[i]->get_filename() != fn) {
870  ++i;
871  }
872 
873  return i;
874 }
875 
876 bool context_manager::check_switch_open_map(const std::string& fn)
877 {
878  std::size_t i = check_open_map(fn);
879  if(i < map_contexts_.size()) {
880  gui2::show_transient_message(_("This map is already open."), fn);
881  switch_context(i);
882  return true;
883  }
884 
885  return false;
886 }
887 
888 void context_manager::load_map(const std::string& filename, bool new_context)
889 {
890  if(new_context && check_switch_open_map(filename)) {
891  return;
892  }
893 
894  LOG_ED << "Load map: " << filename << (new_context ? " (new)" : " (same)");
895  try {
896  {
897  context_ptr mc(new map_context(game_config_, filename));
898  if(mc->get_filename() != filename) {
899  if(new_context && check_switch_open_map(mc->get_filename())) {
900  return;
901  }
902  }
903 
904  if(new_context) {
905  int new_id = add_map_context_of(std::move(mc));
906  switch_context(new_id);
907  } else {
908  replace_map_context_with(std::move(mc));
909  }
910  }
911 
912  if(get_map_context().is_embedded()) {
913  const std::string& msg = _("Loaded embedded map data");
914  gui2::show_transient_message(_("Map loaded from scenario"), msg);
915  } else {
916  if(get_map_context().get_filename() != filename) {
917  if(get_map_context().get_map_data_key().empty()) {
918  ERR_ED << "Internal error, map context filename changed: "
919  << filename << " -> " << get_map_context().get_filename()
920  << " with no apparent scenario load";
921  } else {
922  utils::string_map symbols;
923  symbols["old"] = filename;
924  const std::string& msg = _("Loaded referenced map file:\n$new");
925  symbols["new"] = get_map_context().get_filename();
926  symbols["map_data"] = get_map_context().get_map_data_key();
927  gui2::show_transient_message(_("Map loaded from scenario"),
928  //TODO: msg is already translated does vgettext make sense?
929  VGETTEXT(msg.c_str(), symbols));
930  }
931  }
932  }
933  } catch(const editor_map_load_exception& e) {
934  gui2::show_transient_message(_("Error loading map"), e.what());
935  return;
936  }
937 }
938 
940 {
941  if(!confirm_discard()) {
942  return;
943  }
944 
945  std::string filename = get_map_context().get_filename();
946  if(filename.empty()) {
947  ERR_ED << "Empty filename in map revert";
948  return;
949  }
950 
951  load_map(filename, false);
952 }
953 
954 void context_manager::new_map(int width, int height, const t_translation::terrain_code& fill, bool new_context)
955 {
956  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
957  editor_map m(width, height, fill);
958 
959  if(new_context) {
960  int new_id = add_map_context(m, true, default_schedule);
961  switch_context(new_id);
962  } else {
963  replace_map_context(m, true, default_schedule);
964  }
965 }
966 
967 void context_manager::new_scenario(int width, int height, const t_translation::terrain_code& fill, bool new_context)
968 {
969  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
970  editor_map m(width, height, fill);
971 
972  if(new_context) {
973  int new_id = add_map_context(m, false, default_schedule);
974  switch_context(new_id);
975  } else {
976  replace_map_context(m, false, default_schedule);
977  }
978 
979  // Give the new scenario an initial side.
981  gui().set_team(0, true);
982  gui().set_playing_team(0);
983  gui_.init_flags();
984 }
985 
986 //
987 // Context manipulation
988 //
989 
990 template<typename... T>
991 int context_manager::add_map_context(const T&... args)
992 {
993  map_contexts_.emplace_back(new map_context(args...));
994  return map_contexts_.size() - 1;
995 }
996 
998 {
999  map_contexts_.emplace_back(std::move(mc));
1000  return map_contexts_.size() - 1;
1001 }
1002 
1003 template<typename... T>
1005 {
1006  context_ptr new_mc(new map_context(args...));
1007  replace_map_context_with(std::move(new_mc));
1008 }
1009 
1011 {
1014 }
1015 
1017 {
1018  if(saved_windows_.empty()) {
1021 
1022  const config& default_schedule = game_config_.find_child("editor_times", "id", "empty");
1023  add_map_context(editor_map(44, 33, default_terrain), true, default_schedule);
1024  } else {
1025  for(const std::string& filename : saved_windows_) {
1026  add_map_context(game_config_, filename);
1027  }
1028 
1029  saved_windows_.clear();
1030  }
1031 }
1032 
1034 {
1035  if(!confirm_discard()) return;
1036 
1037  if(map_contexts_.size() == 1) {
1039  map_contexts_.erase(map_contexts_.begin());
1040  } else if(current_context_index_ == static_cast<int>(map_contexts_.size()) - 1) {
1041  map_contexts_.pop_back();
1043  } else {
1045  }
1046 
1048 }
1049 
1050 void context_manager::switch_context(const int index, const bool force)
1051 {
1052  if(index < 0 || static_cast<std::size_t>(index) >= map_contexts_.size()) {
1053  WRN_ED << "Invalid index in switch map context: " << index;
1054  return;
1055  }
1056 
1057  if(index == current_context_index_ && !force) {
1058  return;
1059  }
1060 
1061  // Disable the labels of the current context before switching.
1062  // The refresher handles enabling the new ones.
1063  get_map_context().get_labels().enable(false);
1064 
1066 
1068 }
1069 
1071 {
1072  std::string name = get_map_context().get_name();
1073 
1074  if(name.empty()) {
1076  }
1077 
1078  if(name.empty()){
1080  }
1081 
1082  const std::string& wm_title_string = name + " - " + game_config::get_default_title_string();
1083  video::set_window_title(wm_title_string);
1084 }
1085 
1086 } //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:151
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: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:3141
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 set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:617
void change_display_context(const display_context *dc)
Definition: display.cpp:487
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:394
#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:283
const std::string & get_filename() const
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.
const std::vector< std::string > items
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.
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
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:275
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()
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:75
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:807
void reset_starting_position_labels(display &disp)
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.cpp:1640
void select_map_generator(map_generator *mg)
filter_context * filter_con
Definition: resources.cpp:24
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 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:81
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:895
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:3134
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:60
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:61
#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:377
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:546
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:481
virtual const editor_map & map() const override
Const map accessor.
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:204
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:60
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 fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
Definition: draw.cpp:41
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:285
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:414
const t_string get_default_context_name() const
void rebuild_all()
Rebuild all dynamic terrain.
Definition: display.cpp:476
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