The Battle for Wesnoth  1.19.10+dev
editor_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2025
3  by Tomasz Sniatowski <kailoran@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-editor"
17 
19 
20 #include "desktop/open.hpp"
21 
22 #include "editor/action/action.hpp"
26 
29 
31 
33 
39 #include "gui/dialogs/message.hpp"
43 
44 #include "resources.hpp"
45 #include "reports.hpp"
46 #include "wml_exception.hpp"
47 
48 #include "cursor.hpp"
49 #include "desktop/clipboard.hpp"
50 #include "floating_label.hpp"
51 #include "gettext.hpp"
52 #include "picture.hpp"
53 #include "sound.hpp"
54 #include "units/unit.hpp"
56 #include "quit_confirmation.hpp"
57 #include "sdl/input.hpp" // get_mouse_button_mask
58 #include "serialization/chrono.hpp"
59 
60 #include <functional>
61 
62 namespace {
63 static std::vector<std::string> saved_windows_;
64 }
65 
66 namespace editor {
67 
69 
71  : controller_base()
72  , mouse_handler_base()
73  , quit_confirmation(std::bind(&editor_controller::quit_confirm, this))
74  , active_menu_(editor::MAP)
75  , reports_(new reports())
76  , gui_(new editor_display(*this, *reports_))
77  , tods_()
78  , context_manager_(new context_manager(*gui_.get(), game_config_, clear_id ? "" : editor_controller::current_addon_id_))
79  , toolkit_(nullptr)
80  , tooltip_manager_()
81  , floating_label_manager_(nullptr)
82  , help_manager_(nullptr)
83  , do_quit_(false)
84  , quit_mode_(EXIT_ERROR)
85  , music_tracks_()
86 {
87  if(clear_id) {
89  }
90 
91  init_gui();
92  toolkit_.reset(new editor_toolkit(*gui_.get(), key_, game_config_, *context_manager_.get()));
94  context_manager_->locs_ = toolkit_->get_palette_manager()->location_palette_.get();
99 
100  gui().queue_rerender();
101 }
102 
104 {
105  gui_->change_display_context(&get_current_map_context());
106  gui_->add_redraw_observer(std::bind(&editor_controller::display_redraw_callback, this, std::placeholders::_1));
108  gui().set_debug_flag(display::DEBUG_COORDINATES, prefs::get().editor_draw_hex_coordinates());
109  gui().set_debug_flag(display::DEBUG_TERRAIN_CODES, prefs::get().editor_draw_terrain_codes());
110  gui().set_debug_flag(display::DEBUG_NUM_BITMAPS, prefs::get().editor_draw_num_of_bitmaps());
111  gui().set_help_string_enabled(prefs::get().editor_help_text_shown());
112 // halo_manager_.reset(new halo::manager(*gui_));
113 // resources::halo = halo_manager_.get();
114 // ^ These lines no longer necessary, the gui owns its halo manager.
115 // TODO: Should the editor map contexts actually own the halo manager and swap them in and out from the gui?
116 // Note that if that is what happens it might not actually be a good idea for the gui to own the halo manager, so that it can be swapped out
117 // without deleting it.
118 }
119 
121 {
122  for (const config &schedule : game_config.child_range("editor_times")) {
123 
124  const std::string& schedule_id = schedule["id"];
125  /* Use schedule id as the name if schedule name is empty */
126  const std::string& schedule_name = schedule["name"].empty() ? schedule["id"] : schedule["name"];
127  if (schedule_id.empty()) {
128  ERR_ED << "Missing ID attribute in a TOD Schedule.";
129  continue;
130  }
131 
132  tods_map::iterator times = tods_.find(schedule_id);
133  if (times == tods_.end()) {
134  std::pair<tods_map::iterator, bool> new_times =
135  tods_.emplace(schedule_id, std::pair(schedule_name, std::vector<time_of_day>()));
136  times = new_times.first;
137  } else {
138  ERR_ED << "Duplicate TOD Schedule identifiers.";
139  continue;
140  }
141 
142  for (const config &time : schedule.child_range("time")) {
143  times->second.second.emplace_back(time);
144  }
145 
146  }
147 
148  if (tods_.empty()) {
149  ERR_ED << "No editor time-of-day defined";
150  }
151 }
152 
154 {
155  const std::string tag_name = "editor_music";
156  if (game_config.child_range(tag_name).size() == 0) {
157  ERR_ED << "No editor music defined";
158  }
159  else {
160  for (const config& editor_music : game_config.child_range(tag_name)) {
161  for (const config& music : editor_music.child_range("music")) {
162  sound::music_track track(music);
163  if (track.file_path().empty())
164  WRN_ED << "Music track " << track.id() << " not found.";
165  else
166  music_tracks_.emplace_back(music);
167  }
168  }
169  }
170 }
171 
173 {
174  resources::tod_manager = nullptr;
175  resources::filter_con = nullptr;
176 
177  resources::classification = nullptr;
178 }
179 
181 {
182  try {
183  while (!do_quit_) {
184  play_slice();
185  }
186  } catch (const editor_exception& e) {
187  gui2::show_transient_message(_("Fatal error"), e.what());
188  return EXIT_ERROR;
189  } catch (const wml_exception& e) {
190  e.show();
191  }
192  return quit_mode_;
193 }
194 
196 }
197 
198 void editor_controller::do_screenshot(const std::string& screenshot_filename /* = "map_screenshot.png" */)
199 {
200  try {
201  surface screenshot = gui().screenshot(true);
202  if(!screenshot || image::save_image(screenshot, screenshot_filename) != image::save_result::success) {
203  ERR_ED << "Screenshot creation failed!";
204  }
205  } catch (const wml_exception& e) {
206  e.show();
207  }
208 }
209 
211 {
212  std::string modified;
213  std::size_t amount = context_manager_->modified_maps(modified);
214 
215  std::string message;
216  if (amount == 0) {
217  message = _("Do you really want to quit?");
218  } else if (amount == 1 && get_current_map_context().modified()) {
219  message = _("Do you really want to quit? Changes to this map since the last save will be lost.");
220  } else {
221  message = _("Do you really want to quit? The following maps were modified and all changes since the last save will be lost:");
222  message += "\n" + modified;
223  }
224  return quit_confirmation::show_prompt(message);
225 }
226 
228 {
230  if (unit_dlg.show()) {
231  unit_dlg.write();
232  }
233 }
234 
236 {
237  if (tods_.empty()) {
238  gui2::show_error_message(_("No editor time-of-day found."));
239  return;
240  }
241 
243  std::vector<time_of_day> prev_schedule = manager.times();
244 
245  gui2::dialogs::custom_tod tod_dlg(manager.times(), manager.get_current_time(), current_addon_id_);
246 
247  /* Register callback to the dialog so that the map changes can be
248  * previewed in real time.
249  */
250  std::function<void(std::vector<time_of_day>)> update_func(
251  std::bind(
253  this,
254  std::placeholders::_1));
255  tod_dlg.register_callback(update_func);
256 
257  /* Autogenerate schedule id */
258  static constexpr std::string_view ts_format = "%Y-%m-%d_%H-%M-%S";
259  std::string timestamp = chrono::format_local_timestamp(std::chrono::system_clock::now(), ts_format);
260  std::string sch_id = current_addon_id_+"-schedule";
261  /* Set correct textdomain */
262  t_string sch_name("", "wesnoth-"+current_addon_id_);
263 
264  // TODO : Needs better error handling messages
265  /* Show dialog and update current schedule */
266  if(tod_dlg.show()) {
267  /* Save the new schedule */
268  std::vector<time_of_day> schedule = tod_dlg.get_schedule();
269  if(!gui2::dialogs::tod_new_schedule::execute(sch_id, sch_name)) {
270  /* User pressed Cancel. Restore old schedule */
271  update_map_schedule(prev_schedule);
272  return;
273  }
274 
275  /* In case the ID or Name field is blank and user presses OK */
276  if (sch_id.empty()) {
277  sch_id = current_addon_id_ + "-schedule-" + timestamp;
278  } else {
279  /* Check if the id entered is same as any of the existing ids
280  * If so, replace */
281  // TODO : Notify the user if they enter an already existing schedule ID
282  for (auto map_elem : tods_) {
283  if (sch_id == map_elem.first) {
284  sch_id = current_addon_id_ + "-schedule-" + timestamp;
285  }
286  }
287  }
288 
289  tods_.emplace(sch_id, std::pair(sch_name, schedule));
291  get_current_map_context().save_schedule(sch_id, sch_name);
292  gui_->update_tod();
293  context_manager_->refresh_all();
294  } else {
295  /* Restore old schedule */
296  update_map_schedule(prev_schedule);
297  }
298 }
299 
300 void editor_controller::update_map_schedule(const std::vector<time_of_day>& schedule)
301 {
303  gui_->update_tod();
304  context_manager_->refresh_all();
305 }
306 
308 {
309  using namespace hotkey; //reduce hotkey:: clutter
310  int index = cmd.index;
311  switch(cmd.hotkey_command) {
312  case HOTKEY_NULL:
313  if (index >= 0) {
314  unsigned i = static_cast<unsigned>(index);
315 
316  switch (active_menu_) {
317  case editor::MAP:
318  if (i < context_manager_->open_maps()) {
319  return true;
320  }
321  return false;
322  case editor::LOAD_MRU:
323  case editor::PALETTE:
324  case editor::AREA:
325  case editor::ADDON:
326  case editor::SIDE:
327  case editor::TIME:
328  case editor::SCHEDULE:
330  case editor::MUSIC:
331  case editor::LOCAL_TIME:
332  case editor::UNIT_FACING:
333  return true;
334  }
335  }
336  return false;
338  return true;
340  return toolkit_->get_palette_manager()->can_scroll_up();
342  return toolkit_->get_palette_manager()->can_scroll_down();
343  case HOTKEY_ZOOM_IN:
344  return !gui_->zoom_at_max();
345  case HOTKEY_ZOOM_OUT:
346  return !gui_->zoom_at_min();
347  case HOTKEY_ZOOM_DEFAULT:
348  case HOTKEY_FULLSCREEN:
349  case HOTKEY_SCREENSHOT:
351  case HOTKEY_TOGGLE_GRID:
352  case HOTKEY_MOUSE_SCROLL:
353  case HOTKEY_ANIMATE_MAP:
354  case HOTKEY_MUTE:
355  case HOTKEY_PREFERENCES:
356  case HOTKEY_HELP:
357  case HOTKEY_QUIT_GAME:
358  case HOTKEY_SCROLL_UP:
359  case HOTKEY_SCROLL_DOWN:
360  case HOTKEY_SCROLL_LEFT:
361  case HOTKEY_SCROLL_RIGHT:
362  return true; //general hotkeys we can always do
363 
364  case HOTKEY_UNIT_LIST:
365  return !get_current_map_context().units().empty();
366 
367  // TODO Disabling this for now until the functionality can be implemnted.
368  // See the status_table() method
369  case HOTKEY_STATUS_TABLE:
370  //return !get_current_map_context().teams().empty();
371  return false;
372  /////////////////////////////
373 
375  return gui().mouseover_hex().valid();
376 
377  // unit tool related
378  case HOTKEY_DELETE_UNIT:
379  case HOTKEY_RENAME_UNIT:
386  {
387  map_location loc = gui_->mouseover_hex();
388  const unit_map& units = get_current_map_context().units();
389  return (toolkit_->is_mouse_action_set(HOTKEY_EDITOR_TOOL_UNIT) &&
390  units.find(loc) != units.end());
391  }
392 
393  case HOTKEY_UNDO:
396  case HOTKEY_REDO:
398 
405  return true;
406 
407  // Can be enabled as long as a valid addon_id is set
409  return !current_addon_id_.empty();
410 
411  // Only enable when editing a scenario
414 
415  // Only enable when editing a scenario
419 
420  case HOTKEY_EDITOR_PBL:
424  return true;
425 
429 
432  return !get_current_map_context().teams().empty();
433 
434  // brushes
442 
444  return true;
446  return toolkit_->get_palette_manager()->active_palette().supports_swap();
450  {
451  std::string dummy;
452  return context_manager_->modified_maps(dummy) > 1;
453  }
459  return true;
461  return !get_current_map_context().get_filename().empty()
463 
464  // Tools
465  // Pure map editing tools this can be used all the time.
470  return true;
471  // WWL dependent tools which don't rely on defined sides.
478  return !get_current_map_context().teams().empty();
479 
483  return !get_current_map_context().is_pure_map() &&
485 
487  return !get_current_map_context().is_pure_map() &&
489  && !get_current_map_context().map().selection().empty();
490 
495  return !get_current_map_context().map().selection().empty()
496  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
498  return (get_current_map_context().map().selection().size() > 1
499  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE));
503  return !context_manager_->clipboard_empty();
508  return !context_manager_->clipboard_empty()
509  && toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
512  return !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
514  return !get_current_map_context().map().selection().empty()
516  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
534  return true;
538  return true;
539  default:
540  return false;
541  }
542 }
543 
545 {
546  using namespace hotkey;
547  int index = cmd.index;
548  switch (cmd.hotkey_command) {
549 
551  {
553  get_current_map_context().units().find(gui_->mouseover_hex());
554  return un->loyal() ? ACTION_ON : ACTION_OFF;
555 
556  }
558  {
560  get_current_map_context().units().find(gui_->mouseover_hex());
561  return un->can_recruit() ? ACTION_ON : ACTION_OFF;
562  }
564  {
566  get_current_map_context().units().find(gui_->mouseover_hex());
567  return (!un->unrenamable()) ? ACTION_ON : ACTION_OFF;
568  }
569  //TODO remove hardcoded hotkey names
571  return context_manager_->is_active_transitions_hotkey("editor-auto-update-transitions")
574  return context_manager_->is_active_transitions_hotkey("editor-partial-update-transitions")
577  return context_manager_->is_active_transitions_hotkey("editor-no-update-transitions")
580  return toolkit_->is_active_brush("brush-1") ? ACTION_ON : ACTION_OFF;
582  return toolkit_->is_active_brush("brush-2") ? ACTION_ON : ACTION_OFF;
584  return toolkit_->is_active_brush("brush-3") ? ACTION_ON : ACTION_OFF;
586  return toolkit_->is_active_brush("brush-nw-se") ? ACTION_ON : ACTION_OFF;
588  return toolkit_->is_active_brush("brush-sw-ne") ? ACTION_ON : ACTION_OFF;
589 
590  case HOTKEY_TOGGLE_GRID:
591  return prefs::get().grid() ? ACTION_ON : ACTION_OFF;
596  return get_current_map_context().map().selection().empty() ?
607  return toolkit_->is_mouse_action_set(cmd.hotkey_command) ? ACTION_ON : ACTION_OFF;
609  return gui_->debug_flag_set(display::DEBUG_COORDINATES) ? ACTION_ON : ACTION_OFF;
611  return gui_->debug_flag_set(display::DEBUG_TERRAIN_CODES) ? ACTION_ON : ACTION_OFF;
613  return gui_->debug_flag_set(display::DEBUG_NUM_BITMAPS) ? ACTION_ON : ACTION_OFF;
615  return gui_->help_string_enabled() ? ACTION_ON : ACTION_OFF;
617  return (prefs::get().minimap_draw_villages()) ? ACTION_ON : ACTION_OFF;
619  return (prefs::get().minimap_movement_coding()) ? ACTION_ON : ACTION_OFF;
621  return (prefs::get().minimap_terrain_coding()) ? ACTION_ON : ACTION_OFF;
623  return (prefs::get().minimap_draw_units()) ? ACTION_ON : ACTION_OFF;
625  return (prefs::get().minimap_draw_terrain()) ? ACTION_ON : ACTION_OFF;
626  case HOTKEY_ZOOM_DEFAULT:
627  return (gui_->get_zoom_factor() == 1.0) ? ACTION_ON : ACTION_OFF;
628 
629  case HOTKEY_NULL:
630  switch (active_menu_) {
631  case editor::MAP:
632  return index == context_manager_->current_context_index()
634  case editor::LOAD_MRU:
635  return ACTION_STATELESS;
636  case editor::PALETTE:
637  return ACTION_STATELESS;
638  case editor::AREA:
641  case editor::ADDON:
642  return ACTION_STATELESS;
643  case editor::SIDE:
644  return static_cast<std::size_t>(index) == gui_->playing_team_index()
646  case editor::TIME:
649  case editor::LOCAL_TIME:
651  get_current_map_context().get_active_area())
653  case editor::MUSIC:
655  ? ACTION_ON : ACTION_OFF;
656  case editor::SCHEDULE:
657  {
658  tods_map::const_iterator it = tods_.begin();
659  std::advance(it, index);
660  const std::vector<time_of_day>& times1 = it->second.second;
661  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times();
662  return (times1 == times2) ? ACTION_SELECTED : ACTION_DESELECTED;
663  }
665  {
666  tods_map::const_iterator it = tods_.begin();
667  std::advance(it, index);
668  const std::vector<time_of_day>& times1 = it->second.second;
669  int active_area = get_current_map_context().get_active_area();
670  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times(active_area);
671  return (times1 == times2) ? ACTION_SELECTED : ACTION_DESELECTED;
672  }
673  case editor::UNIT_FACING:
674  {
676  assert(un != get_current_map_context().units().end());
678  }
679  }
680  return ACTION_ON;
681  default:
682  return command_executor::get_action_state(cmd);
683  }
684 }
685 
686 bool editor_controller::do_execute_command(const hotkey::ui_command& cmd, bool press, bool release)
687 {
688  using namespace hotkey;
689  HOTKEY_COMMAND command = cmd.hotkey_command;
690  SCOPE_ED;
691  int index = cmd.index;
692 
693  // nothing here handles release; fall through to base implementation
694  if (!press) {
695  return command_executor::do_execute_command(cmd, press, release);
696  }
697 
698  switch (command) {
699  case HOTKEY_NULL:
700  switch (active_menu_) {
701  case MAP:
702  if (index >= 0) {
703  unsigned i = static_cast<unsigned>(index);
704  if (i < context_manager_->size()) {
705  context_manager_->switch_context(index);
706  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
707  return true;
708  }
709  }
710  return false;
711  case LOAD_MRU:
712  if (index >= 0) {
713  context_manager_->load_mru_item(static_cast<unsigned>(index));
714  }
715  return true;
716  case PALETTE:
717  toolkit_->get_palette_manager()->set_group(index);
718  return true;
719  case SIDE:
720  gui_->set_viewing_team_index(index, true);
721  gui_->set_playing_team_index(index);
722  toolkit_->get_palette_manager()->draw_contents();
723  return true;
724  case AREA:
725  {
727  const std::set<map_location>& area =
730  gui_->scroll_to_tiles({ area.begin(), area.end() });
731  return true;
732  }
733  case ADDON:
734  return true;
735  case TIME:
736  {
738  gui_->update_tod();
739  return true;
740  }
741  case LOCAL_TIME:
742  {
744  return true;
745  }
746  case MUSIC:
747  {
748  //TODO mark the map as changed
751  std::vector<config> items;
752  items.emplace_back("id", "editor-playlist");
753  std::shared_ptr<gui::button> b = gui_->find_menu_button("menu-playlist");
754  show_menu(items, b->location().x +1, b->location().y + b->height() +1, false, *gui_);
755  return true;
756  }
757  case SCHEDULE:
758  {
759  tods_map::iterator iter = tods_.begin();
760  std::advance(iter, index);
761  get_current_map_context().replace_schedule(iter->second.second);
762  // TODO: test again after the assign-schedule menu is fixed. Should work, though.
763  gui_->update_tod();
764  return true;
765  }
766  case LOCAL_SCHEDULE:
767  {
768  tods_map::iterator iter = tods_.begin();
769  std::advance(iter, index);
770  get_current_map_context().replace_local_schedule(iter->second.second);
771  return true;
772  }
773  case UNIT_FACING:
774  {
776  assert(un != get_current_map_context().units().end());
777  un->set_facing(map_location::direction(index));
778  un->anim_comp().set_standing();
779  active_menu_ = MAP;
780  return true;
781  }
782  }
783  return true;
784 
785  //Zoom
786  case HOTKEY_ZOOM_IN:
787  gui_->set_zoom(true);
789  toolkit_->set_mouseover_overlay(*gui_);
790  return true;
791  case HOTKEY_ZOOM_OUT:
792  gui_->set_zoom(false);
794  toolkit_->set_mouseover_overlay(*gui_);
795  return true;
796  case HOTKEY_ZOOM_DEFAULT:
797  gui_->toggle_default_zoom();
799  toolkit_->set_mouseover_overlay(*gui_);
800  return true;
801 
802  //Palette
804  //TODO this code waits for the gui2 dialog to get ready
805  // std::vector< std::pair< std::string, std::string >> blah_items;
806  // toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(blah_items);
807  // int selected = 1; //toolkit_->get_palette_manager()->active_palette().get_selected;
808  // gui2::teditor_select_palette_group::execute(selected, blah_items);
809  return true;
811  toolkit_->get_palette_manager()->scroll_up();
812  return true;
814  toolkit_->get_palette_manager()->scroll_down();
815  return true;
816 
817  case HOTKEY_QUIT_GAME:
819  do_quit_ = true;
821  }
822  return true;
825  return true;
827  context_manager_->save_contexts();
828  do_quit_ = true;
830  return true;
833  return true;
836  return true;
838  toolkit_->get_palette_manager()->active_palette().swap();
839  return true;
841  if (dynamic_cast<const editor_action_chain*>(get_current_map_context().last_undo_action()) != nullptr) {
843  context_manager_->refresh_after_action();
844  } else {
845  undo();
846  }
847  return true;
848 
849  //Tool Selection
858  toolkit_->hotkey_set_mouse_action(command);
859  return true;
860 
861  case HOTKEY_EDITOR_PBL:
862  if(initialize_addon()) {
863  context_manager_->edit_pbl();
864  }
865  return true;
866 
868  if(initialize_addon()) {
869  context_manager_->change_addon_id();
870  }
871  return true;
872 
875  return true;
876 
878  {
879  if (!initialize_addon()) {
880  gui2::show_error_message("Could not initialize add-on!");
881  return true;
882  }
883 
885 
886  dlg.set_title(_("Add-on Files"))
888 
889  if (dlg.show()) {
890  std::string filepath = dlg.path();
891  if (filesystem::is_map(filepath) || filesystem::is_cfg(filepath)) {
892  // Open map or scenario
893  context_manager_->load_map(filepath, true);
894  } else {
895  // Open file using OS application for that format
897  desktop::open_object(filepath);
898  } else {
899  gui2::show_message("", _("Opening files is not supported, contact your packager"), gui2::dialogs::message::auto_close);
900  }
901  }
902  }
903 
904  return true;
905  }
906 
908  add_area();
909  return true;
910 
912  change_unit_id();
913  return true;
914 
915  return true;
917  {
918  map_location loc = gui_->mouseover_hex();
920  bool unrenamable = un->unrenamable();
921  un->set_unrenamable(!unrenamable);
922  }
923  return true;
925  {
926  map_location loc = gui_->mouseover_hex();
928  bool canrecruit = un->can_recruit();
929  un->set_can_recruit(!canrecruit);
930  un->anim_comp().set_standing();
931  }
932  return true;
934  {
935  map_location loc = gui_->mouseover_hex();
937  bool loyal = un->loyal();
938  un->set_loyal(!loyal);
939  }
940  return true;
941  case HOTKEY_DELETE_UNIT:
942  {
943  map_location loc = gui_->mouseover_hex();
944  perform_delete(std::make_unique<editor_action_unit_delete>(loc));
945  }
946  return true;
947  case HOTKEY_EDITOR_CLIPBOARD_PASTE: //paste is somewhat different as it might be "one action then revert to previous mode"
948  toolkit_->hotkey_set_mouse_action(command);
949  return true;
950 
951  //Clipboard
953  context_manager_->get_clipboard().rotate_60_cw();
954  toolkit_->update_mouse_action_highlights();
955  return true;
957  context_manager_->get_clipboard().rotate_60_ccw();
958  toolkit_->update_mouse_action_highlights();
959  return true;
961  context_manager_->get_clipboard().flip_horizontal();
962  toolkit_->update_mouse_action_highlights();
963  return true;
965  context_manager_->get_clipboard().flip_vertical();
966  toolkit_->update_mouse_action_highlights();
967  return true;
968 
969  //Brushes
971  toolkit_->cycle_brush();
972  return true;
974  toolkit_->set_brush("brush-1");
975  return true;
977  toolkit_->set_brush("brush-2");
978  return true;
980  toolkit_->set_brush("brush-3");
981  return true;
983  toolkit_->set_brush("brush-nw-se");
984  return true;
986  toolkit_->set_brush("brush-sw-ne");
987  return true;
988 
990  copy_selection();
991  return true;
993  cut_selection();
994  return true;
996  context_manager_->rename_area_dialog();
997  return true;
999  save_area();
1000  return true;
1003  return true;
1005  if(!get_current_map_context().map().everything_selected()) {
1006  context_manager_->perform_refresh(editor_action_select_all());
1007  return true;
1008  }
1009  [[fallthrough]];
1011  context_manager_->perform_refresh(editor_action_select_inverse());
1012  return true;
1014  context_manager_->perform_refresh(editor_action_select_none());
1015  return true;
1017  context_manager_->fill_selection();
1018  return true;
1021  get_current_map_context().map().selection()));
1022  return true;
1023 
1025  context_manager_->edit_scenario_dialog();
1026  return true;
1027 
1030  get_current_map_context().get_active_area());
1031  return true;
1032 
1033  // map specific
1035  context_manager_->close_current_context();
1036  // Copy behaviour from when switching windows to always reset the active tool to the Paint Tool
1037  // This avoids the situation of having a scenario-specific tool active in a map context which can cause a crash if used
1038  // Not elegant but at least avoids a potential crash and is consistent with existing behaviour
1039  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
1040  return true;
1042  context_manager_->load_map_dialog();
1043  return true;
1045  context_manager_->revert_map();
1046  return true;
1047  case HOTKEY_EDITOR_MAP_NEW:
1048  context_manager_->new_map_dialog();
1049  return true;
1051  if(initialize_addon()) {
1052  context_manager_->new_scenario_dialog();
1053  }
1054  return true;
1056  save_map();
1057  return true;
1059  context_manager_->save_all_maps();
1060  return true;
1062  context_manager_->save_map_as_dialog();
1063  return true;
1065  if(initialize_addon()) {
1066  context_manager_->map_to_scenario();
1067  }
1068  return true;
1070  if(initialize_addon()) {
1071  context_manager_->save_scenario_as_dialog();
1072  }
1073  return true;
1075  context_manager_->generate_map_dialog();
1076  return true;
1078  context_manager_->apply_mask_dialog();
1079  return true;
1081  context_manager_->create_mask_to_dialog();
1082  return true;
1084  context_manager_->resize_map_dialog();
1085  return true;
1086 
1087  // Side specific ones
1089  if(get_current_map_context().teams().size() >= 9) {
1090  size_t new_side_num = get_current_map_context().teams().size() + 1;
1091  toolkit_->get_palette_manager()->location_palette_->add_item(std::to_string(new_side_num));
1092  }
1094  gui_->init_flags();
1095  return true;
1097  gui_->set_viewing_team_index(0, true);
1098  gui_->set_playing_team_index(0);
1100  return true;
1102  context_manager_->edit_side_dialog(gui_->viewing_team());
1103  return true;
1104 
1105  // Transitions
1107  context_manager_->set_update_transitions_mode(2);
1108  return true;
1110  context_manager_->set_update_transitions_mode(1);
1111  return true;
1113  context_manager_->set_update_transitions_mode(0);
1114  return true;
1116  if(context_manager_->toggle_update_transitions()) {
1117  return true;
1118  }
1119  [[fallthrough]];
1121  context_manager_->refresh_all();
1122  return true;
1123  // Refresh
1124  case HOTKEY_EDITOR_REFRESH:
1125  context_manager_->reload_map();
1126  return true;
1129  return true;
1130 
1133  prefs::get().set_editor_draw_hex_coordinates(gui().debug_flag_set(display::DEBUG_COORDINATES));
1134  gui().invalidate_all();
1135  return true;
1138  prefs::get().set_editor_draw_terrain_codes(gui().debug_flag_set(display::DEBUG_TERRAIN_CODES));
1139  gui().invalidate_all();
1140  return true;
1143  prefs::get().set_editor_draw_num_of_bitmaps(gui().debug_flag_set(display::DEBUG_NUM_BITMAPS));
1144  gui().invalidate_all();
1145  return true;
1147  gui().set_help_string_enabled(!gui().help_string_enabled());
1148  prefs::get().set_editor_help_text_shown(gui().help_string_enabled());
1149  return true;
1151  location_palette* lp = dynamic_cast<location_palette*>(&toolkit_->get_palette_manager()->active_palette());
1152  if (lp) {
1153  perform_delete(std::make_unique<editor_action_starting_position>(map_location(), lp->selected_item()));
1154  // No idea if this is the right thing to call, but it ensures starting
1155  // position labels get removed on delete.
1156  context_manager_->refresh_after_action();
1157  }
1158  return true;
1159  }
1160  default:
1161  return hotkey::command_executor::do_execute_command(cmd, press, release);
1162  }
1163 }
1164 
1166  if(current_addon_id_.empty()) {
1167  // editor::initialize_addon can return empty id in case of failure
1169  }
1170  context_manager_->set_addon_id(current_addon_id_);
1171  return !current_addon_id_.empty();
1172 }
1173 
1175 {
1176  help::show_help("..editor");
1177 }
1178 
1179 void editor_controller::show_menu(const std::vector<config>& items_arg, int xloc, int yloc, bool context_menu, display& disp)
1180 {
1181  if(context_menu) {
1182  if(!get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(xloc, yloc))) {
1183  return;
1184  }
1185  }
1186 
1187  std::vector<config> items;
1188  for(const auto& c : items_arg) {
1189  const std::string& id = c["id"];
1190 
1192 
1193  if((can_execute_command(cmd) && (!context_menu || in_context_menu(cmd)))
1195  {
1196  items.emplace_back("id", id);
1197  }
1198  }
1199 
1200  // No point in showing an empty menu.
1201  if(items.empty()) {
1202  return;
1203  }
1204 
1205  // Based on the ID of the first entry, we fill the menu contextually.
1206  const std::string& first_id = items.front()["id"];
1207 
1208  if(first_id == "EDITOR-LOAD-MRU-PLACEHOLDER") {
1210  context_manager_->expand_load_mru_menu(items, 0);
1211  }
1212 
1213  if(first_id == "editor-switch-map") {
1215  context_manager_->expand_open_maps_menu(items, 0);
1216  }
1217 
1218  if(first_id == "editor-palette-groups") {
1220  toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(items, 0);
1221  }
1222 
1223  if(first_id == "editor-switch-side") {
1225  context_manager_->expand_sides_menu(items, 0);
1226  }
1227 
1228  if(first_id == "editor-switch-area") {
1230  context_manager_->expand_areas_menu(items, 0);
1231  }
1232 
1233  if(first_id == "editor-pbl") {
1235  }
1236 
1237  if(!items.empty() && items.front()["id"] == "editor-switch-time") {
1239  context_manager_->expand_time_menu(items, 0);
1240  }
1241 
1242  if(first_id == "editor-assign-local-time") {
1244  context_manager_->expand_local_time_menu(items, 0);
1245  }
1246 
1247  if(first_id == "menu-unit-facings") {
1249  auto pos = items.erase(items.begin());
1250  int dir = 0;
1251  std::generate_n(std::inserter<std::vector<config>>(items, pos), static_cast<int>(map_location::direction::indeterminate), [&dir]() -> config {
1253  });
1254  }
1255 
1256  if(first_id == "editor-playlist") {
1258  auto pos = items.erase(items.begin());
1259  std::transform(music_tracks_.begin(), music_tracks_.end(), std::inserter<std::vector<config>>(items, pos), [](const sound::music_track& track) -> config {
1260  return config {"label", track.title().empty() ? track.id() : track.title()};
1261  });
1262  }
1263 
1264  if(first_id == "editor-assign-schedule") {
1266  auto pos = items.erase(items.begin());
1267  std::transform(tods_.begin(), tods_.end(), std::inserter<std::vector<config>>(items, pos), [](const tods_map::value_type& tod) -> config {
1268  return config {"label", tod.second.first};
1269  });
1270  }
1271 
1272  if(first_id == "editor-assign-local-schedule") {
1274  auto pos = items.erase(items.begin());
1275  std::transform(tods_.begin(), tods_.end(), std::inserter<std::vector<config>>(items, pos), [](const tods_map::value_type& tod) -> config {
1276  return config {"label", tod.second.first};
1277  });
1278  }
1279 
1280  command_executor::show_menu(items, xloc, yloc, context_menu, disp);
1281 }
1282 
1284 {
1285  gui_->clear_help_string();
1286  gui2::dialogs::preferences_dialog::display();
1287 
1288  gui_->queue_rerender();
1289 }
1290 
1292 {
1293  prefs::get().set_grid(!prefs::get().grid());
1294  gui_->invalidate_all();
1295 }
1296 
1298 {
1299  map_location loc = gui_->mouseover_hex();
1300  const unit_map & units = get_current_map_context().units();
1301  const unit_map::const_unit_iterator un = units.find(loc);
1302  if(un != units.end()) {
1303  help::show_unit_help(un->type_id(), un->type().show_variations_in_help(), false);
1304  } else {
1305  help::show_help("..units");
1306  }
1307 }
1308 
1309 
1311 {
1312  if (!get_current_map_context().map().selection().empty()) {
1313  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1314  context_manager_->get_clipboard().center_by_mass();
1315  }
1316 }
1317 
1319 {
1320  map_location loc = gui_->mouseover_hex();
1321  unit_map& units = get_current_map_context().units();
1322  const unit_map::unit_iterator& un = units.find(loc);
1323 
1324  const std::string title(N_("Change Unit ID"));
1325  const std::string label(N_("ID:"));
1326 
1327  if(un != units.end()) {
1328  std::string id = un->id();
1329  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1330  un->set_id(id);
1331  }
1332  }
1333 }
1334 
1336 {
1337  map_location loc = gui_->mouseover_hex();
1338  unit_map& units = get_current_map_context().units();
1339  const unit_map::unit_iterator& un = units.find(loc);
1340 
1341  const std::string title(N_("Rename Unit"));
1342  const std::string label(N_("Name:"));
1343 
1344  if(un != units.end()) {
1345  std::string name = un->name();
1346  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1347  //TODO we may not want a translated name here.
1348  un->set_name(name);
1349  }
1350  }
1351 }
1352 
1354 {
1355  std::vector<unit_const_ptr> unit_list;
1356 
1357  const unit_map& units = gui().context().units();
1358  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1359  if(i->side() != gui().viewing_team().side()) {
1360  continue;
1361  }
1362  unit_list.push_back(i.get_shared_ptr());
1363  }
1364 
1366 
1367  if (unit_dlg->show() && unit_dlg->is_selected()) {
1368  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1370  gui().select_hex(loc);
1371  }
1372 }
1373 
1375 {
1376  copy_selection();
1378 }
1379 
1381 {
1382  const std::set<map_location>& area = get_current_map_context().map().selection();
1384 }
1385 
1387 {
1388  const std::set<map_location>& area = get_current_map_context().map().selection();
1390 }
1391 
1393 {
1394  std::stringstream ssx, ssy;
1395  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1396  if (i != get_current_map_context().map().selection().end()) {
1397  ssx << "x = " << i->wml_x();
1398  ssy << "y = " << i->wml_y();
1399  ++i;
1400  while (i != get_current_map_context().map().selection().end()) {
1401  ssx << ", " << i->wml_x();
1402  ssy << ", " << i->wml_y();
1403  ++i;
1404  }
1405  ssx << "\n" << ssy.str() << "\n";
1407  }
1408 }
1409 
1410 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1411 {
1412  if (action) {
1414  }
1415 }
1416 
1417 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1418 {
1419  if (action) {
1420  context_manager_->perform_refresh(*action, drag_part);
1421  }
1422 }
1423 
1425 {
1427  context_manager_->refresh_all();
1428 }
1429 
1431 {
1432  set_button_state();
1433  toolkit_->adjust_size();
1435 }
1436 
1438 {
1440  context_manager_->refresh_after_action();
1441 }
1442 
1444 {
1446  context_manager_->refresh_after_action();
1447 }
1448 
1449 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1450  bool update, map_location /*new_loc*/)
1451 {
1452  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1453  map_location hex_clicked = gui().hex_clicked_on(x, y);
1454  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1455  std::unique_ptr<editor_action> a;
1456  bool partial = false;
1457  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1458  // mainly (only?) used for printing debugging information.
1459  auto last_undo = get_current_map_context().last_undo_action();
1460  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1461  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1462  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1463  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1464  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1465  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1466  }
1467  //Partial means that the mouse action has modified the
1468  //last undo action and the controller shouldn't add
1469  //anything to the undo stack (hence a different perform_ call)
1470  if (a != nullptr) {
1471  if (partial) {
1473  } else {
1475  }
1476  context_manager_->refresh_after_action(true);
1477  }
1478  } else {
1479  get_mouse_action().move(*gui_, hex_clicked);
1480  }
1481  gui().highlight_hex(hex_clicked);
1482 }
1483 
1484 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1485 {
1486  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1487 }
1488 
1490 {
1491  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1492 }
1493 
1494 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1495 {
1497 }
1498 
1499 bool editor_controller::left_click(int x, int y, const bool browse)
1500 {
1501  toolkit_->clear_mouseover_overlay();
1502  if (mouse_handler_base::left_click(x, y, browse))
1503  return true;
1504 
1505  LOG_ED << "Left click, after generic handling";
1506  map_location hex_clicked = gui().hex_clicked_on(x, y);
1507  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1508  return true;
1509 
1510  LOG_ED << "Left click action " << hex_clicked;
1511  auto a = get_mouse_action().click_left(*gui_, x, y);
1512  if(a) {
1513  perform_refresh_delete(std::move(a), true);
1514  set_button_state();
1515  }
1516 
1517  return false;
1518 }
1519 
1520 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1521 {
1522  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1523  perform_delete(std::move(a));
1524 }
1525 
1526 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1527 {
1528  auto a = get_mouse_action().up_left(*gui_, x, y);
1529  if(a) {
1530  perform_delete(std::move(a));
1531  set_button_state();
1532  }
1533  toolkit_->set_mouseover_overlay();
1534  context_manager_->refresh_after_action();
1535 }
1536 
1537 bool editor_controller::right_click(int x, int y, const bool browse)
1538 {
1539  toolkit_->clear_mouseover_overlay();
1540  if (mouse_handler_base::right_click(x, y, browse)) return true;
1541  LOG_ED << "Right click, after generic handling";
1542  map_location hex_clicked = gui().hex_clicked_on(x, y);
1543  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1544  LOG_ED << "Right click action " << hex_clicked;
1545  auto a = get_mouse_action().click_right(*gui_, x, y);
1546  if(a) {
1547  perform_refresh_delete(std::move(a), true);
1548  set_button_state();
1549  }
1550  return false;
1551 }
1552 
1553 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1554 {
1555  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1556  perform_delete(std::move(a));
1557 }
1558 
1559 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1560 {
1561  // Call base method to handle context menus.
1562  mouse_handler_base::right_mouse_up(x, y, browse);
1563 
1564  auto a = get_mouse_action().up_right(*gui_, x, y);
1565  if(a) {
1566  perform_delete(std::move(a));
1567  set_button_state();
1568  }
1569  toolkit_->set_mouseover_overlay();
1570  context_manager_->refresh_after_action();
1571 }
1572 
1574 {
1575  const map_location& loc = gui().mouseover_hex();
1576  if (get_current_map_context().map().on_board(loc) == false)
1577  return;
1578 
1581 }
1582 
1583 void editor_controller::process_keyup_event(const SDL_Event& event)
1584 {
1585  auto a = get_mouse_action().key_event(gui(), event);
1586  perform_refresh_delete(std::move(a));
1587  toolkit_->set_mouseover_overlay();
1588 }
1589 
1591  return this;
1592 }
1593 
1595 {
1597 }
1598 
1600 {
1602 }
1603 
1605 {
1607 }
1608 
1610 {
1612 }
1613 
1615 {
1616  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1617 }
1618 
1619 } //end namespace editor
Editor action classes.
Editor action classes.
map_location loc
Definition: move.cpp:172
static auto & dummy
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
child_itors child_range(config_key_type key)
Definition: config.cpp:268
void set_scroll_up(bool on)
void set_scroll_left(bool on)
virtual bool in_context_menu(const hotkey::ui_command &cmd) const
void set_scroll_right(bool on)
const game_config_view & game_config_
void set_scroll_down(bool on)
virtual void play_slice()
virtual const unit_map & units() const =0
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:96
void toggle_debug_flag(DEBUG_FLAG flag)
Definition: display.hpp:935
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1398
@ DEBUG_COORDINATES
Overlays x,y coords on tiles.
Definition: display.hpp:907
@ DEBUG_NUM_BITMAPS
Overlays number of bitmaps on tiles.
Definition: display.hpp:913
@ DEBUG_TERRAIN_CODES
Overlays terrain codes on tiles.
Definition: display.hpp:910
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:1873
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2972
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition: display.cpp:718
const display_context & context() const
Definition: display.hpp:192
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition: display.cpp:2157
void set_debug_flag(DEBUG_FLAG flag, bool value)
Definition: display.hpp:930
const map_location & mouseover_hex() const
Definition: display.hpp:309
map_location hex_clicked_on(int x, int y) const
given x,y co-ordinates of an onscreen pixel, will return the location of the hex that this pixel corr...
Definition: display.cpp:531
virtual void select_hex(map_location hex)
Definition: display.cpp:1390
Container action wrapping several actions into one.
Definition: action.hpp:88
Paint the same terrain on a number of locations on the map.
Definition: action.hpp:266
Randomize terrain in an area.
Definition: action.hpp:399
The editor_controller class contains the mouse and keyboard event handling routines for the editor.
const std::unique_ptr< context_manager > context_manager_
void preferences() override
Show the preferences dialog.
void refresh_image_cache()
Reload images.
void right_drag_end(int x, int y, const bool browse) override
Called whenever the right mouse drag has "ended".
void unit_editor_dialog()
Show Unit Editor dialog.
bool can_execute_command(const hotkey::ui_command &command) const override
command_executor override
bool quit_confirm()
Show a quit confirmation dialog and returns true if the user pressed 'yes'.
void update_map_schedule(const std::vector< time_of_day > &schedule)
Updates schedule and the map display.
void scroll_up(bool on) override
Handle hotkeys to scroll map.
void cut_selection()
Cut the selection from the current map to the clipboard.
void display_redraw_callback(display &)
Callback function passed to display to be called on queue_rerender.
std::unique_ptr< font::floating_label_context > floating_label_manager_
void show_menu(const std::vector< config > &items_arg, int xloc, int yloc, bool context_menu, display &disp) override
controller_base override
void undo() override
Undos an action in the current map context.
void left_mouse_up(int x, int y, const bool browse) override
Called when the left mouse button is up.
bool left_click(int x, int y, const bool browse) override
Overridden in derived classes, called on a left click (mousedown).
bool allow_mouse_wheel_scroll(int x, int y) override
Derived classes can override this to disable mousewheel scrolling under some circumstances,...
void touch_motion(int x, int y, const bool browse, bool update=false, map_location new_loc=map_location::null_location()) override
virtual std::vector< std::string > additional_actions_pressed() override
void perform_refresh_delete(std::unique_ptr< editor_action > action, bool drag_part=false)
Peform an action on the current map_context, then refresh the display and delete the pointer.
void init_tods(const game_config_view &game_config)
init the available time-of-day settings
bool initialize_addon()
Initialize an addon if the addon id is empty.
const mouse_action & get_mouse_action() const
Get the current mouse action.
map_context & get_current_map_context() const
std::unique_ptr< editor_toolkit > toolkit_
void scroll_right(bool on) override
void init_music(const game_config_view &game_config)
init background music for the editor
const std::unique_ptr< editor_display > gui_
The display object used and owned by the editor.
bool do_execute_command(const hotkey::ui_command &command, bool press=true, bool release=false) override
command_executor override
void export_selection_coords()
Export the WML-compatible list of selected tiles to the system clipboard.
void redo() override
Redos an action in the current map context.
editor_display & gui() override
Reference to the used display objects.
hotkey::ACTION_STATE get_action_state(const hotkey::ui_command &command) const override
command_executor override
void perform_delete(std::unique_ptr< editor_action > action)
Perform an action, then delete the action object.
void right_mouse_up(int x, int y, const bool browse) override
Called when the right mouse button is up.
virtual hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
void mouse_motion(int x, int y, const bool browse, bool update, map_location new_loc=map_location::null_location()) override
Called when a mouse motion event takes place.
EXIT_STATUS main_loop()
Editor main loop.
editor_controller(const editor_controller &)=delete
void custom_tods_dialog()
Display the settings dialog, used to control e.g.
void do_screenshot(const std::string &screenshot_filename="map_screenshot.png")
Takes a screenshot.
void toggle_grid() override
Grid toggle.
void scroll_left(bool on) override
bool right_click(int x, int y, const bool browse) override
Overridden in derived classes, called on a right click (mousedown).
void save_area()
Save the current selection to the active area.
void process_keyup_event(const SDL_Event &event) override
Process keyup (always).
void copy_selection()
Copy the selection on the current map to the clipboard.
void init_gui()
init the display object and general set-up
std::unique_ptr< help::help_manager > help_manager_
std::vector< sound::music_track > music_tracks_
bool right_click_show_menu(int x, int y, const bool browse) override
Called in the default right_click when the context menu is about to be shown, can be used for preproc...
static std::string current_addon_id_
void add_area()
Add a new area to the current context, filled with the selection if any.
void scroll_down(bool on) override
void left_drag_end(int x, int y, const bool browse) override
Called whenever the left mouse drag has "ended".
bool do_quit_
Quit main loop flag.
void save_map() override
Save the map, open dialog if not named yet.
void set_help_string_enabled(bool value)
Sets whether the help text should be shown.
bool everything_selected() const
Definition: editor_map.cpp:207
const std::set< map_location > & selection() const
Return the selection set.
Definition: editor_map.hpp:148
List of starting locations and location ids.
const std::string & selected_item() const
Return the currently selected item.
void save_area(const std::set< map_location > &area)
void new_area(const std::set< map_location > &area)
void perform_partial_action(const editor_action &action)
Performs a partial action, assumes that the top undo action has been modified to maintain coherent st...
void remove_side()
removes the last side from the scenario
void set_active_area(int index)
bool modified() const
virtual const unit_map & units() const override
Const units accessor.
void set_local_starting_time(int time)
int get_active_area() const
void new_side()
Adds a new side to the map.
editor_action * last_undo_action()
void redo()
Re-does a previously undid action, and puts it back in the undo stack.
void set_starting_time(int time)
bool select_area(int index)
Select the nth tod area.
bool can_redo() const
bool can_undo() const
void perform_action(const editor_action &action)
Performs an action (thus modifying the map).
void undo()
Un-does the last action, and puts it in the redo stack for a possible redo.
void replace_schedule(const std::vector< time_of_day > &schedule)
bool is_in_playlist(std::string track_id)
void remove_area(int index)
void replace_local_schedule(const std::vector< time_of_day > &schedule)
Replace the [time]s of the currently active area.
const tod_manager * get_time_manager() const
void partial_undo()
Un-does a single step from a undo action chain.
virtual const editor_map & map() const override
Const map accessor.
void add_to_playlist(const sound::music_track &track)
const std::string & get_filename() const
void set_starting_position_labels(display &disp)
map_labels & get_labels()
virtual const std::vector< team > & teams() const override
Const teams accessor.
bool is_pure_map() const
void save_schedule(const std::string &schedule_id, const std::string &schedule_name)
Save custom time of day schedule in the utils directory.
A map fragment – a collection of locations and information abut them.
virtual std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y)=0
A click, possibly the beginning of a drag.
virtual std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y)=0
A click, possibly the beginning of a drag.
virtual std::unique_ptr< editor_action > drag_end_right(editor_display &disp, int x, int y)
virtual bool has_context_menu() const
virtual std::unique_ptr< editor_action > up_left(editor_display &disp, int x, int y)
virtual void move(editor_display &disp, const map_location &hex)
Mouse move (not a drag).
virtual std::unique_ptr< editor_action > up_right(editor_display &disp, int x, int y)
virtual std::unique_ptr< editor_action > key_event(editor_display &disp, const SDL_Event &e)
Function called by the controller on a key event for the current mouse action.
virtual std::unique_ptr< editor_action > drag_left(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Drag operation.
virtual std::unique_ptr< editor_action > drag_right(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Drag operation.
virtual bool supports_brushes() const
Whether we need the brush bar, is used to grey it out.
virtual std::unique_ptr< editor_action > drag_end_left(editor_display &disp, int x, int y)
The end of dragging.
bool dragging_right_
RMB drag init flag.
bool dragging_left_
LMB drag init flag.
map_location drag_from_hex_
Drag start or mouse-down map location.
A class grating read only view to a vector of config objects, viewed as one config with all children ...
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:389
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:98
const std::vector< time_of_day > get_schedule()
Return current schedule.
Definition: custom_tod.cpp:359
void register_callback(std::function< void(std::vector< time_of_day >)>)
Register callback for update.
Definition: custom_tod.cpp:365
Dialog that allows user to create custom unit types.
Definition: edit_unit.hpp:33
void write()
Write the cfg file.
Definition: edit_unit.cpp:1038
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
Definition: file_dialog.hpp:59
std::string path() const
Gets the current file selection.
@ auto_close
Enables auto close.
Definition: message.hpp:71
bool show(const unsigned auto_close_time=0)
Shows the window.
static std::unique_ptr< units_dialog > build_unit_list_dialog(std::vector< unit_const_ptr > &units_list)
virtual bool do_execute_command(const hotkey::ui_command &command, bool press=true, bool release=false)
void recalculate_labels()
Definition: label.cpp:245
static prefs & get()
Implements a quit confirmation dialog.
static bool show_prompt(const std::string &message)
static bool quit()
Shows the quit confirmation if needed.
static void quit_to_desktop()
Internal representation of music tracks.
const std::string & file_path() const
const std::string & id() const
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
std::vector< std::string > get_area_ids() const
const std::set< map_location > & get_area_by_index(int index) const
int get_current_time(const map_location &loc=map_location::null_location()) const
int get_current_area_time(int index) const
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
bool empty() const
Definition: map.hpp:445
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
Editor action classes.
#define LOG_ED
#define ERR_ED
#define SCOPE_ED
#define WRN_ED
std::size_t i
Definition: function.cpp:1030
#define N_(String)
Definition: gettext.hpp:105
static std::string _(const char *str)
Definition: gettext.hpp:97
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:201
Contains functions for cleanly handling SDL input.
static bool timestamp
Definition: log.cpp:60
auto format_local_timestamp(const std::chrono::system_clock::time_point &time, std::string_view format="%F %T")
Definition: chrono.hpp:62
CURSOR_TYPE get()
Definition: cursor.cpp:216
@ NORMAL
Definition: cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
void copy_to_clipboard(const std::string &text)
Copies text to the clipboard.
Definition: clipboard.cpp:27
bool open_object([[maybe_unused]] const std::string &path_or_url)
Definition: open.cpp:46
constexpr bool open_object_is_supported()
Returns whether open_object() is supported/implemented for the current platform.
Definition: open.hpp:54
static void update()
Manage the empty-palette in the editor.
Definition: action.cpp:31
@ EXIT_ERROR
Definition: editor_main.hpp:27
@ EXIT_NORMAL
Definition: editor_main.hpp:24
@ EXIT_RELOAD_DATA
Definition: editor_main.hpp:26
const t_translation::terrain_code & get_selected_bg_terrain()
std::string initialize_addon()
Definition: editor_main.cpp:32
bool is_cfg(const std::string &filename)
Returns true if the file ends with the wmlfile extension.
bool is_map(const std::string &filename)
Returns true if the file ends with the mapfile extension.
std::string get_current_editor_dir(const std::string &addon_id)
Game configuration data as global variables.
Definition: build_info.cpp:61
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:201
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:148
General purpose widgets.
void show_help(const std::string &show_topic)
Open the help browser, show topic with id show_topic.
Definition: help.cpp:140
void show_terrain_description(const terrain_type &t)
Definition: help.cpp:77
void show_unit_help(const std::string &show_topic, bool has_variations, bool hidden)
Open the help browser, show unit with id unit_id.
Definition: help.cpp:151
Keyboard shortcuts for game actions.
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
@ HOTKEY_EDITOR_TOOL_VILLAGE
@ HOTKEY_MINIMAP_DRAW_VILLAGES
@ HOTKEY_EDITOR_BRUSH_NW_SE
@ HOTKEY_EDITOR_REFRESH
@ HOTKEY_FULLSCREEN
@ HOTKEY_EDITOR_SELECT_NONE
@ HOTKEY_ANIMATE_MAP
@ HOTKEY_SCREENSHOT
@ HOTKEY_EDITOR_CLIPBOARD_ROTATE_CCW
@ HOTKEY_MOUSE_SCROLL
@ HOTKEY_EDITOR_PALETTE_GROUPS
@ HOTKEY_TERRAIN_DESCRIPTION
@ HOTKEY_EDITOR_PALETTE_UPSCROLL
@ HOTKEY_EDITOR_SIDE_REMOVE
@ HOTKEY_EDITOR_BRUSH_NEXT
@ HOTKEY_EDITOR_TOOL_LABEL
@ HOTKEY_EDITOR_CLIPBOARD_ROTATE_CW
@ HOTKEY_EDITOR_BRUSH_3
@ HOTKEY_SCROLL_LEFT
@ HOTKEY_EDITOR_CLIPBOARD_PASTE
@ HOTKEY_EDITOR_PARTIAL_UNDO
@ HOTKEY_EDITOR_UNIT_TOGGLE_CANRECRUIT
@ HOTKEY_EDITOR_PALETTE_ITEM_SWAP
@ HOTKEY_EDITOR_TOOL_PAINT
@ HOTKEY_EDITOR_MAP_CLOSE
@ HOTKEY_EDITOR_MAP_GENERATE
@ HOTKEY_EDITOR_SELECTION_FLIP
@ HOTKEY_EDITOR_SCHEDULE
@ HOTKEY_EDITOR_TOOL_FILL
@ HOTKEY_EDITOR_SCENARIO_SAVE_AS
@ HOTKEY_EDITOR_PLAYLIST
@ HOTKEY_EDITOR_MAP_SAVE_AS
@ HOTKEY_UNIT_DESCRIPTION
@ HOTKEY_SCROLL_RIGHT
@ HOTKEY_EDITOR_SELECT_ALL
@ HOTKEY_EDITOR_DRAW_COORDINATES
@ HOTKEY_EDITOR_TOOL_NEXT
@ HOTKEY_EDITOR_SELECTION_EXPORT
@ HOTKEY_EDITOR_PARTIAL_UPDATE_TRANSITIONS
@ HOTKEY_EDITOR_MAP_CREATE_MASK_TO
@ HOTKEY_TOGGLE_GRID
@ HOTKEY_EDITOR_SELECTION_CUT
@ HOTKEY_EDITOR_UNIT_CHANGE_ID
@ HOTKEY_MINIMAP_DRAW_TERRAIN
@ HOTKEY_EDITOR_DRAW_NUM_OF_BITMAPS
@ HOTKEY_MAP_SCREENSHOT
@ HOTKEY_EDITOR_AREA_ADD
@ HOTKEY_EDITOR_BRUSH_SW_NE
@ HOTKEY_EDITOR_TOOL_UNIT
@ HOTKEY_QUIT_TO_DESKTOP
@ HOTKEY_EDITOR_HELP_TEXT_SHOWN
@ HOTKEY_EDITOR_CHANGE_ADDON_ID
@ HOTKEY_EDITOR_CUSTOM_TODS
@ HOTKEY_EDITOR_REFRESH_IMAGE_CACHE
@ HOTKEY_EDITOR_SELECTION_FILL
@ HOTKEY_EDITOR_CLIPBOARD_FLIP_VERTICAL
@ HOTKEY_EDITOR_MAP_SAVE_ALL
@ HOTKEY_EDITOR_BRUSH_1
@ HOTKEY_EDITOR_SELECTION_ROTATE
@ HOTKEY_EDITOR_MAP_LOAD
@ HOTKEY_RENAME_UNIT
@ HOTKEY_EDITOR_BRUSH_2
@ HOTKEY_EDITOR_TOOL_STARTING_POSITION
@ HOTKEY_MINIMAP_CODING_TERRAIN
@ HOTKEY_EDITOR_NO_UPDATE_TRANSITIONS
@ HOTKEY_EDITOR_SELECTION_COPY
@ HOTKEY_MINIMAP_DRAW_UNITS
@ HOTKEY_EDITOR_PALETTE_DOWNSCROLL
@ HOTKEY_EDITOR_UNIT_TOGGLE_LOYAL
@ HOTKEY_EDITOR_MAP_APPLY_MASK
@ HOTKEY_EDITOR_LOCAL_TIME
@ HOTKEY_EDITOR_UNIT_FACING
@ HOTKEY_PREFERENCES
@ HOTKEY_STATUS_TABLE
@ HOTKEY_DELETE_UNIT
@ HOTKEY_EDITOR_EDIT_UNIT
@ HOTKEY_EDITOR_SIDE_NEW
@ HOTKEY_EDITOR_SELECTION_RANDOMIZE
@ HOTKEY_EDITOR_SCENARIO_NEW
@ HOTKEY_EDITOR_MAP_SWITCH
@ HOTKEY_EDITOR_SELECT_INVERSE
@ HOTKEY_EDITOR_TOGGLE_TRANSITIONS
@ HOTKEY_UNIT_LIST
@ HOTKEY_SCROLL_DOWN
@ HOTKEY_EDITOR_AUTO_UPDATE_TRANSITIONS
@ HOTKEY_ZOOM_DEFAULT
@ HOTKEY_EDITOR_AREA_RENAME
@ HOTKEY_EDITOR_MAP_RESIZE
@ HOTKEY_SCROLL_UP
@ HOTKEY_EDITOR_DRAW_TERRAIN_CODES
@ HOTKEY_EDITOR_MAP_TO_SCENARIO
@ HOTKEY_EDITOR_AREA_REMOVE
@ HOTKEY_QUIT_GAME
@ HOTKEY_EDITOR_UPDATE_TRANSITIONS
@ HOTKEY_EDITOR_TOOL_SELECT
@ HOTKEY_EDITOR_MAP_NEW
@ HOTKEY_EDITOR_MAP_SAVE
@ HOTKEY_EDITOR_AREA_SAVE
@ HOTKEY_EDITOR_REMOVE_LOCATION
@ HOTKEY_EDITOR_SIDE_EDIT
@ HOTKEY_EDITOR_SELECT_ADDON
@ HOTKEY_EDITOR_TOOL_ITEM
@ TITLE_SCREEN__RELOAD_WML
@ HOTKEY_EDITOR_UNIT_TOGGLE_RENAMEABLE
@ HOTKEY_EDITOR_SCENARIO_EDIT
@ HOTKEY_EDITOR_OPEN_ADDON
@ HOTKEY_EDITOR_MAP_REVERT
@ HOTKEY_EDITOR_CLIPBOARD_FLIP_HORIZONTAL
@ HOTKEY_MINIMAP_CODING_UNIT
void flush_cache()
Purges all image caches.
Definition: picture.cpp:200
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:886
int show_menu(lua_State *L)
Displays a popup menu at the current mouse position Best used from a [set_menu_item],...
Definition: lua_gui2.cpp:193
Unit and team statistics.
::tod_manager * tod_manager
Definition: resources.cpp:29
game_classification * classification
Definition: resources.cpp:34
filter_context * filter_con
Definition: resources.cpp:23
uint32_t get_mouse_button_mask()
Returns the current mouse button mask.
Definition: input.cpp:49
void play_music_once(const std::string &file)
Definition: sound.cpp:604
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
constexpr auto transform
Definition: ranges.hpp:41
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Desktop environment interaction functions.
@ partial
There are still moves and/or attacks possible, but the unit doesn't fit in the "unmoved" status.
structure which will hide all current floating labels, and cause floating labels instantiated after i...
The help implementation caches data parsed from the game_config.
Definition: help.hpp:39
Used as the main paramneter for can_execute_command/do_execute_command These functions are used to ex...
hotkey::HOTKEY_COMMAND hotkey_command
The hotkey::HOTKEY_COMMAND associated with this action, HOTKEY_NULL for actions that don't allow hotk...
int index
When this action was the result of a menu click, this is the index of the clicked item in the menu.
Encapsulates the map of the game.
Definition: location.hpp:45
bool valid() const
Definition: location.hpp:110
direction
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:47
static std::string write_translated_direction(direction dir)
Definition: location.cpp:175
Helper class, don't construct this directly.
mock_char c
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define e
#define b