The Battle for Wesnoth  1.19.11+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"
29 
30 #include "help/help.hpp"
31 
37 #include "gui/dialogs/message.hpp"
41 
43 #include "resources.hpp"
44 #include "reports.hpp"
45 #include "wml_exception.hpp"
46 
47 #include "cursor.hpp"
48 #include "desktop/clipboard.hpp"
49 #include "floating_label.hpp"
50 #include "gettext.hpp"
51 #include "picture.hpp"
52 #include "sound.hpp"
53 #include "units/unit.hpp"
55 #include "quit_confirmation.hpp"
56 #include "sdl/input.hpp" // get_mouse_button_mask
57 #include "serialization/chrono.hpp"
58 
59 #include <functional>
60 
61 namespace {
62 static std::vector<std::string> saved_windows_;
63 }
64 
65 namespace editor {
66 
68 
70  : controller_base()
71  , mouse_handler_base()
72  , quit_confirmation(std::bind(&editor_controller::quit_confirm, this))
73  , active_menu_(editor::MAP)
74  , reports_(new reports())
75  , gui_(new editor_display(*this, *reports_))
76  , tods_()
77  , context_manager_(new context_manager(*gui_.get(), game_config_, clear_id ? "" : editor_controller::current_addon_id_))
78  , toolkit_(nullptr)
79  , tooltip_manager_()
80  , floating_label_manager_(nullptr)
81  , help_manager_(nullptr)
82  , do_quit_(false)
83  , quit_mode_(EXIT_ERROR)
84  , music_tracks_()
85 {
86  if(clear_id) {
88  }
89 
90  init_gui();
91  toolkit_.reset(new editor_toolkit(*gui_.get(), key_, game_config_, *context_manager_.get()));
93  context_manager_->locs_ = toolkit_->get_palette_manager()->location_palette_.get();
98 
99  gui().queue_rerender();
100 }
101 
103 {
105  gui().add_redraw_observer(std::bind(&editor_controller::display_redraw_callback, this, std::placeholders::_1));
107  gui().set_debug_flag(display::DEBUG_COORDINATES, prefs::get().editor_draw_hex_coordinates());
108  gui().set_debug_flag(display::DEBUG_TERRAIN_CODES, prefs::get().editor_draw_terrain_codes());
109  gui().set_debug_flag(display::DEBUG_NUM_BITMAPS, prefs::get().editor_draw_num_of_bitmaps());
110  gui().set_help_string_enabled(prefs::get().editor_help_text_shown());
111 // halo_manager_.reset(new halo::manager(*gui_));
112 // resources::halo = halo_manager_.get();
113 // ^ These lines no longer necessary, the gui owns its halo manager.
114 // TODO: Should the editor map contexts actually own the halo manager and swap them in and out from the gui?
115 // 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
116 // without deleting it.
117 }
118 
120 {
121  for (const config &schedule : game_config.child_range("editor_times")) {
122 
123  const std::string& schedule_id = schedule["id"];
124  /* Use schedule id as the name if schedule name is empty */
125  const std::string& schedule_name = schedule["name"].empty() ? schedule["id"] : schedule["name"];
126  if (schedule_id.empty()) {
127  ERR_ED << "Missing ID attribute in a TOD Schedule.";
128  continue;
129  }
130 
131  tods_map::iterator times = tods_.find(schedule_id);
132  if (times == tods_.end()) {
133  std::pair<tods_map::iterator, bool> new_times =
134  tods_.emplace(schedule_id, std::pair(schedule_name, std::vector<time_of_day>()));
135  times = new_times.first;
136  } else {
137  ERR_ED << "Duplicate TOD Schedule identifiers.";
138  continue;
139  }
140 
141  for (const config &time : schedule.child_range("time")) {
142  times->second.second.emplace_back(time);
143  }
144 
145  }
146 
147  if (tods_.empty()) {
148  ERR_ED << "No editor time-of-day defined";
149  }
150 }
151 
153 {
154  const std::string tag_name = "editor_music";
155  if (game_config.child_range(tag_name).size() == 0) {
156  ERR_ED << "No editor music defined";
157  }
158  else {
159  for (const config& editor_music : game_config.child_range(tag_name)) {
160  for (const config& music : editor_music.child_range("music")) {
161  sound::music_track track(music);
162  if (track.file_path().empty())
163  WRN_ED << "Music track " << track.id() << " not found.";
164  else
165  music_tracks_.emplace_back(music);
166  }
167  }
168  }
169 }
170 
172 {
173  resources::tod_manager = nullptr;
174  resources::filter_con = nullptr;
175 
176  resources::classification = nullptr;
177 }
178 
180 {
181  try {
182  while (!do_quit_) {
183  play_slice();
184  }
185  } catch (const editor_exception& e) {
186  gui2::show_transient_message(_("Fatal error"), e.what());
187  return EXIT_ERROR;
188  } catch (const wml_exception& e) {
189  e.show();
190  }
191  return quit_mode_;
192 }
193 
195 }
196 
197 void editor_controller::do_screenshot(const std::string& screenshot_filename /* = "map_screenshot.png" */)
198 {
199  try {
200  surface screenshot = gui().screenshot(true);
201  if(!screenshot || image::save_image(screenshot, screenshot_filename) != image::save_result::success) {
202  ERR_ED << "Screenshot creation failed!";
203  }
204  } catch (const wml_exception& e) {
205  e.show();
206  }
207 }
208 
210 {
211  std::string modified;
212  std::size_t amount = context_manager_->modified_maps(modified);
213 
214  std::string message;
215  if (amount == 0) {
216  message = _("Do you really want to quit?");
217  } else if (amount == 1 && get_current_map_context().modified()) {
218  message = _("Do you really want to quit? Changes to this map since the last save will be lost.");
219  } else {
220  message = _("Do you really want to quit? The following maps were modified and all changes since the last save will be lost:");
221  message += "\n" + modified;
222  }
223  return quit_confirmation::show_prompt(message);
224 }
225 
227 {
229  if (unit_dlg.show()) {
230  unit_dlg.write();
231  }
232 }
233 
235 {
236  if (tods_.empty()) {
237  gui2::show_error_message(_("No editor time-of-day found."));
238  return;
239  }
240 
242  std::vector<time_of_day> prev_schedule = manager.times();
243 
244  gui2::dialogs::custom_tod tod_dlg(manager.times(), manager.get_current_time(), current_addon_id_);
245 
246  /* Register callback to the dialog so that the map changes can be
247  * previewed in real time.
248  */
249  std::function<void(std::vector<time_of_day>)> update_func(
250  std::bind(
252  this,
253  std::placeholders::_1));
254  tod_dlg.register_callback(update_func);
255 
256  /* Autogenerate schedule id */
257  static constexpr std::string_view ts_format = "%Y-%m-%d_%H-%M-%S";
258  std::string timestamp = chrono::format_local_timestamp(std::chrono::system_clock::now(), ts_format);
259  std::string sch_id = current_addon_id_+"-schedule";
260  /* Set correct textdomain */
261  t_string sch_name("", "wesnoth-"+current_addon_id_);
262 
263  // TODO : Needs better error handling messages
264  /* Show dialog and update current schedule */
265  if(tod_dlg.show()) {
266  /* Save the new schedule */
267  std::vector<time_of_day> schedule = tod_dlg.get_schedule();
268  if(!gui2::dialogs::tod_new_schedule::execute(sch_id, sch_name)) {
269  /* User pressed Cancel. Restore old schedule */
270  update_map_schedule(prev_schedule);
271  return;
272  }
273 
274  /* In case the ID or Name field is blank and user presses OK */
275  if (sch_id.empty()) {
276  sch_id = current_addon_id_ + "-schedule-" + timestamp;
277  } else {
278  /* Check if the id entered is same as any of the existing ids
279  * If so, replace */
280  // TODO : Notify the user if they enter an already existing schedule ID
281  for (auto map_elem : tods_) {
282  if (sch_id == map_elem.first) {
283  sch_id = current_addon_id_ + "-schedule-" + timestamp;
284  }
285  }
286  }
287 
288  tods_.emplace(sch_id, std::pair(sch_name, schedule));
290  get_current_map_context().save_schedule(sch_id, sch_name);
291  gui_->update_tod();
292  context_manager_->refresh_all();
293  } else {
294  /* Restore old schedule */
295  update_map_schedule(prev_schedule);
296  }
297 }
298 
299 void editor_controller::update_map_schedule(const std::vector<time_of_day>& schedule)
300 {
302  gui_->update_tod();
303  context_manager_->refresh_all();
304 }
305 
307 {
308  using namespace hotkey; //reduce hotkey:: clutter
309  int index = cmd.index;
310  switch(cmd.hotkey_command) {
311  case HOTKEY_NULL:
312  if (index >= 0) {
313  unsigned i = static_cast<unsigned>(index);
314 
315  switch (active_menu_) {
316  case editor::MAP:
317  if (i < context_manager_->open_maps()) {
318  return true;
319  }
320  return false;
321  case editor::LOAD_MRU:
322  case editor::PALETTE:
323  case editor::AREA:
324  case editor::ADDON:
325  case editor::SIDE:
326  case editor::TIME:
327  case editor::SCHEDULE:
329  case editor::MUSIC:
330  case editor::LOCAL_TIME:
331  case editor::UNIT_FACING:
332  return true;
333  }
334  }
335  return false;
337  return true;
339  return toolkit_->get_palette_manager()->can_scroll_up();
341  return toolkit_->get_palette_manager()->can_scroll_down();
342  case HOTKEY_ZOOM_IN:
343  return !gui_->zoom_at_max();
344  case HOTKEY_ZOOM_OUT:
345  return !gui_->zoom_at_min();
346  case HOTKEY_ZOOM_DEFAULT:
347  case HOTKEY_FULLSCREEN:
348  case HOTKEY_SCREENSHOT:
350  case HOTKEY_TOGGLE_GRID:
351  case HOTKEY_MOUSE_SCROLL:
352  case HOTKEY_ANIMATE_MAP:
353  case HOTKEY_MUTE:
354  case HOTKEY_PREFERENCES:
355  case HOTKEY_HELP:
356  case HOTKEY_QUIT_GAME:
357  case HOTKEY_SCROLL_UP:
358  case HOTKEY_SCROLL_DOWN:
359  case HOTKEY_SCROLL_LEFT:
360  case HOTKEY_SCROLL_RIGHT:
361  return true; //general hotkeys we can always do
362 
363  case HOTKEY_UNIT_LIST:
364  return !get_current_map_context().units().empty();
365 
366  // TODO Disabling this for now until the functionality can be implemnted.
367  // See the status_table() method
368  case HOTKEY_STATUS_TABLE:
369  //return !get_current_map_context().teams().empty();
370  return false;
371  /////////////////////////////
372 
374  return gui().mouseover_hex().valid();
375 
376  // unit tool related
377  case HOTKEY_DELETE_UNIT:
378  case HOTKEY_RENAME_UNIT:
385  {
386  map_location loc = gui_->mouseover_hex();
387  const unit_map& units = get_current_map_context().units();
388  return (toolkit_->is_mouse_action_set(HOTKEY_EDITOR_TOOL_UNIT) &&
389  units.find(loc) != units.end());
390  }
391 
392  case HOTKEY_UNDO:
395  case HOTKEY_REDO:
397 
404  return true;
405 
406  // Can be enabled as long as a valid addon_id is set
408  return !current_addon_id_.empty();
409 
410  // Only enable when editing a scenario
413 
414  // Only enable when editing a scenario
418 
419  case HOTKEY_EDITOR_PBL:
423  return true;
424 
428 
431  return !get_current_map_context().teams().empty();
432 
433  // brushes
441 
443  return true;
445  return toolkit_->get_palette_manager()->active_palette().supports_swap();
449  {
450  std::string dummy;
451  return context_manager_->modified_maps(dummy) > 1;
452  }
458  return true;
460  return !get_current_map_context().get_filename().empty()
462 
463  // Tools
464  // Pure map editing tools this can be used all the time.
469  return true;
470  // WWL dependent tools which don't rely on defined sides.
477  return !get_current_map_context().teams().empty();
478 
482  return !get_current_map_context().is_pure_map() &&
484 
486  return !get_current_map_context().is_pure_map() &&
488  && !get_current_map_context().map().selection().empty();
489 
494  return !get_current_map_context().map().selection().empty()
495  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
497  return (get_current_map_context().map().selection().size() > 1
498  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE));
502  return !context_manager_->clipboard_empty();
507  return !context_manager_->clipboard_empty()
508  && toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
511  return !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
513  return !get_current_map_context().map().selection().empty()
515  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
533  return true;
537  return true;
538  default:
539  return false;
540  }
541 }
542 
544 {
545  using namespace hotkey;
546  int index = cmd.index;
547  switch (cmd.hotkey_command) {
548 
550  {
552  get_current_map_context().units().find(gui_->mouseover_hex());
553  return un->loyal() ? ACTION_ON : ACTION_OFF;
554 
555  }
557  {
559  get_current_map_context().units().find(gui_->mouseover_hex());
560  return un->can_recruit() ? ACTION_ON : ACTION_OFF;
561  }
563  {
565  get_current_map_context().units().find(gui_->mouseover_hex());
566  return (!un->unrenamable()) ? ACTION_ON : ACTION_OFF;
567  }
568  //TODO remove hardcoded hotkey names
570  return context_manager_->is_active_transitions_hotkey("editor-auto-update-transitions")
573  return context_manager_->is_active_transitions_hotkey("editor-partial-update-transitions")
576  return context_manager_->is_active_transitions_hotkey("editor-no-update-transitions")
579  return toolkit_->is_active_brush("brush-1") ? ACTION_ON : ACTION_OFF;
581  return toolkit_->is_active_brush("brush-2") ? ACTION_ON : ACTION_OFF;
583  return toolkit_->is_active_brush("brush-3") ? ACTION_ON : ACTION_OFF;
585  return toolkit_->is_active_brush("brush-nw-se") ? ACTION_ON : ACTION_OFF;
587  return toolkit_->is_active_brush("brush-sw-ne") ? ACTION_ON : ACTION_OFF;
588 
589  case HOTKEY_TOGGLE_GRID:
590  return prefs::get().grid() ? ACTION_ON : ACTION_OFF;
595  return get_current_map_context().map().selection().empty() ?
606  return toolkit_->is_mouse_action_set(cmd.hotkey_command) ? ACTION_ON : ACTION_OFF;
608  return gui_->debug_flag_set(display::DEBUG_COORDINATES) ? ACTION_ON : ACTION_OFF;
610  return gui_->debug_flag_set(display::DEBUG_TERRAIN_CODES) ? ACTION_ON : ACTION_OFF;
612  return gui_->debug_flag_set(display::DEBUG_NUM_BITMAPS) ? ACTION_ON : ACTION_OFF;
614  return gui_->help_string_enabled() ? ACTION_ON : ACTION_OFF;
616  return (prefs::get().minimap_draw_villages()) ? ACTION_ON : ACTION_OFF;
618  return (prefs::get().minimap_movement_coding()) ? ACTION_ON : ACTION_OFF;
620  return (prefs::get().minimap_terrain_coding()) ? ACTION_ON : ACTION_OFF;
622  return (prefs::get().minimap_draw_units()) ? ACTION_ON : ACTION_OFF;
624  return (prefs::get().minimap_draw_terrain()) ? ACTION_ON : ACTION_OFF;
625  case HOTKEY_ZOOM_DEFAULT:
626  return (gui_->get_zoom_factor() == 1.0) ? ACTION_ON : ACTION_OFF;
627 
628  case HOTKEY_NULL:
629  switch (active_menu_) {
630  case editor::MAP:
631  return index == context_manager_->current_context_index()
633  case editor::LOAD_MRU:
634  return ACTION_STATELESS;
635  case editor::PALETTE:
636  return ACTION_STATELESS;
637  case editor::AREA:
640  case editor::ADDON:
641  return ACTION_STATELESS;
642  case editor::SIDE:
643  return static_cast<std::size_t>(index) == gui_->playing_team_index()
645  case editor::TIME:
648  case editor::LOCAL_TIME:
650  get_current_map_context().get_active_area())
652  case editor::MUSIC:
654  ? ACTION_ON : ACTION_OFF;
655  case editor::SCHEDULE:
656  {
657  tods_map::const_iterator it = tods_.begin();
658  std::advance(it, index);
659  const std::vector<time_of_day>& times1 = it->second.second;
660  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times();
661  return (times1 == times2) ? ACTION_SELECTED : ACTION_DESELECTED;
662  }
664  {
665  tods_map::const_iterator it = tods_.begin();
666  std::advance(it, index);
667  const std::vector<time_of_day>& times1 = it->second.second;
668  int active_area = get_current_map_context().get_active_area();
669  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times(active_area);
670  return (times1 == times2) ? ACTION_SELECTED : ACTION_DESELECTED;
671  }
672  case editor::UNIT_FACING:
673  {
675  assert(un != get_current_map_context().units().end());
677  }
678  }
679  return ACTION_ON;
680  default:
681  return command_executor::get_action_state(cmd);
682  }
683 }
684 
685 bool editor_controller::do_execute_command(const hotkey::ui_command& cmd, bool press, bool release)
686 {
687  using namespace hotkey;
688  HOTKEY_COMMAND command = cmd.hotkey_command;
689  SCOPE_ED;
690  int index = cmd.index;
691 
692  // nothing here handles release; fall through to base implementation
693  if (!press) {
694  return command_executor::do_execute_command(cmd, press, release);
695  }
696 
697  switch (command) {
698  case HOTKEY_NULL:
699  switch (active_menu_) {
700  case MAP:
701  if (index >= 0) {
702  unsigned i = static_cast<unsigned>(index);
703  if (i < context_manager_->size()) {
704  context_manager_->switch_context(index);
705  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
706  return true;
707  }
708  }
709  return false;
710  case LOAD_MRU:
711  if (index >= 0) {
712  context_manager_->load_mru_item(static_cast<unsigned>(index));
713  }
714  return true;
715  case PALETTE:
716  toolkit_->get_palette_manager()->set_group(index);
717  return true;
718  case SIDE:
719  gui_->set_viewing_team_index(index, true);
720  gui_->set_playing_team_index(index);
721  toolkit_->get_palette_manager()->draw_contents();
722  return true;
723  case AREA:
724  {
726  const std::set<map_location>& area =
729  gui_->scroll_to_tiles({ area.begin(), area.end() });
730  return true;
731  }
732  case ADDON:
733  return true;
734  case TIME:
735  {
737  gui_->update_tod();
738  return true;
739  }
740  case LOCAL_TIME:
741  {
743  return true;
744  }
745  case MUSIC:
746  {
747  //TODO mark the map as changed
750  std::vector<config> items;
751  items.emplace_back("id", "editor-playlist");
752  std::shared_ptr<gui::button> b = gui_->find_menu_button("menu-playlist");
753  show_menu(items, b->location().x +1, b->location().y + b->height() +1, false, *gui_);
754  return true;
755  }
756  case SCHEDULE:
757  {
758  tods_map::iterator iter = tods_.begin();
759  std::advance(iter, index);
760  get_current_map_context().replace_schedule(iter->second.second);
761  // TODO: test again after the assign-schedule menu is fixed. Should work, though.
762  gui_->update_tod();
763  return true;
764  }
765  case LOCAL_SCHEDULE:
766  {
767  tods_map::iterator iter = tods_.begin();
768  std::advance(iter, index);
769  get_current_map_context().replace_local_schedule(iter->second.second);
770  return true;
771  }
772  case UNIT_FACING:
773  {
775  assert(un != get_current_map_context().units().end());
776  un->set_facing(map_location::direction(index));
777  un->anim_comp().set_standing();
778  active_menu_ = MAP;
779  return true;
780  }
781  }
782  return true;
783 
784  //Zoom
785  case HOTKEY_ZOOM_IN:
786  gui_->set_zoom(true);
788  toolkit_->set_mouseover_overlay(*gui_);
789  return true;
790  case HOTKEY_ZOOM_OUT:
791  gui_->set_zoom(false);
793  toolkit_->set_mouseover_overlay(*gui_);
794  return true;
795  case HOTKEY_ZOOM_DEFAULT:
796  gui_->toggle_default_zoom();
798  toolkit_->set_mouseover_overlay(*gui_);
799  return true;
800 
801  //Palette
803  //TODO this code waits for the gui2 dialog to get ready
804  // std::vector< std::pair< std::string, std::string >> blah_items;
805  // toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(blah_items);
806  // int selected = 1; //toolkit_->get_palette_manager()->active_palette().get_selected;
807  // gui2::teditor_select_palette_group::execute(selected, blah_items);
808  return true;
810  toolkit_->get_palette_manager()->scroll_up();
811  return true;
813  toolkit_->get_palette_manager()->scroll_down();
814  return true;
815 
816  case HOTKEY_QUIT_GAME:
818  do_quit_ = true;
820  }
821  return true;
824  return true;
826  context_manager_->save_contexts();
827  do_quit_ = true;
829  return true;
832  return true;
835  return true;
837  toolkit_->get_palette_manager()->active_palette().swap();
838  return true;
840  if (dynamic_cast<const editor_action_chain*>(get_current_map_context().last_undo_action()) != nullptr) {
842  context_manager_->refresh_after_action();
843  } else {
844  undo();
845  }
846  return true;
847 
848  //Tool Selection
857  toolkit_->hotkey_set_mouse_action(command);
858  return true;
859 
860  case HOTKEY_EDITOR_PBL:
861  if(initialize_addon()) {
862  context_manager_->edit_pbl();
863  }
864  return true;
865 
867  if(initialize_addon()) {
868  context_manager_->change_addon_id();
869  }
870  return true;
871 
874  return true;
875 
877  {
878  if (!initialize_addon()) {
879  gui2::show_error_message("Could not initialize add-on!");
880  return true;
881  }
882 
884 
885  dlg.set_title(_("Add-on Files"))
887 
888  if (dlg.show()) {
889  std::string filepath = dlg.path();
890  if (filesystem::is_map(filepath) || filesystem::is_cfg(filepath)) {
891  // Open map or scenario
892  context_manager_->load_map(filepath, true);
893  } else {
894  // Open file using OS application for that format
896  desktop::open_object(filepath);
897  } else {
898  gui2::show_message("", _("Opening files is not supported, contact your packager"), gui2::dialogs::message::auto_close);
899  }
900  }
901  }
902 
903  return true;
904  }
905 
907  add_area();
908  return true;
909 
911  change_unit_id();
912  return true;
913 
914  return true;
916  {
917  map_location loc = gui_->mouseover_hex();
919  bool unrenamable = un->unrenamable();
920  un->set_unrenamable(!unrenamable);
921  }
922  return true;
924  {
925  map_location loc = gui_->mouseover_hex();
927  bool canrecruit = un->can_recruit();
928  un->set_can_recruit(!canrecruit);
929  un->anim_comp().set_standing();
930  }
931  return true;
933  {
934  map_location loc = gui_->mouseover_hex();
936  bool loyal = un->loyal();
937  un->set_loyal(!loyal);
938  }
939  return true;
940  case HOTKEY_DELETE_UNIT:
941  {
942  map_location loc = gui_->mouseover_hex();
943  perform_delete(std::make_unique<editor_action_unit_delete>(loc));
944  }
945  return true;
946  case HOTKEY_EDITOR_CLIPBOARD_PASTE: //paste is somewhat different as it might be "one action then revert to previous mode"
947  toolkit_->hotkey_set_mouse_action(command);
948  return true;
949 
950  //Clipboard
952  context_manager_->get_clipboard().rotate_60_cw();
953  toolkit_->update_mouse_action_highlights();
954  return true;
956  context_manager_->get_clipboard().rotate_60_ccw();
957  toolkit_->update_mouse_action_highlights();
958  return true;
960  context_manager_->get_clipboard().flip_horizontal();
961  toolkit_->update_mouse_action_highlights();
962  return true;
964  context_manager_->get_clipboard().flip_vertical();
965  toolkit_->update_mouse_action_highlights();
966  return true;
967 
968  //Brushes
970  toolkit_->cycle_brush();
971  return true;
973  toolkit_->set_brush("brush-1");
974  return true;
976  toolkit_->set_brush("brush-2");
977  return true;
979  toolkit_->set_brush("brush-3");
980  return true;
982  toolkit_->set_brush("brush-nw-se");
983  return true;
985  toolkit_->set_brush("brush-sw-ne");
986  return true;
987 
989  copy_selection();
990  return true;
992  cut_selection();
993  return true;
995  context_manager_->rename_area_dialog();
996  return true;
998  save_area();
999  return true;
1002  return true;
1004  if(!get_current_map_context().map().everything_selected()) {
1005  context_manager_->perform_refresh(editor_action_select_all());
1006  return true;
1007  }
1008  [[fallthrough]];
1010  context_manager_->perform_refresh(editor_action_select_inverse());
1011  return true;
1013  context_manager_->perform_refresh(editor_action_select_none());
1014  return true;
1016  context_manager_->fill_selection();
1017  return true;
1020  get_current_map_context().map().selection()));
1021  return true;
1022 
1024  context_manager_->edit_scenario_dialog();
1025  return true;
1026 
1029  get_current_map_context().get_active_area());
1030  return true;
1031 
1032  // map specific
1034  context_manager_->close_current_context();
1035  // Copy behaviour from when switching windows to always reset the active tool to the Paint Tool
1036  // This avoids the situation of having a scenario-specific tool active in a map context which can cause a crash if used
1037  // Not elegant but at least avoids a potential crash and is consistent with existing behaviour
1038  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
1039  return true;
1041  context_manager_->load_map_dialog();
1042  return true;
1044  context_manager_->revert_map();
1045  return true;
1046  case HOTKEY_EDITOR_MAP_NEW:
1047  context_manager_->new_map_dialog();
1048  return true;
1050  if(initialize_addon()) {
1051  context_manager_->new_scenario_dialog();
1052  }
1053  return true;
1055  save_map();
1056  return true;
1058  context_manager_->save_all_maps();
1059  return true;
1061  context_manager_->save_map_as_dialog();
1062  return true;
1064  if(initialize_addon()) {
1065  context_manager_->map_to_scenario();
1066  }
1067  return true;
1069  if(initialize_addon()) {
1070  context_manager_->save_scenario_as_dialog();
1071  }
1072  return true;
1074  context_manager_->generate_map_dialog();
1075  return true;
1077  context_manager_->apply_mask_dialog();
1078  return true;
1080  context_manager_->create_mask_to_dialog();
1081  return true;
1083  context_manager_->resize_map_dialog();
1084  return true;
1085 
1086  // Side specific ones
1088  if(get_current_map_context().teams().size() >= 9) {
1089  size_t new_side_num = get_current_map_context().teams().size() + 1;
1090  toolkit_->get_palette_manager()->location_palette_->add_item(std::to_string(new_side_num));
1091  }
1093  gui_->init_flags();
1094  return true;
1096  gui_->set_viewing_team_index(0, true);
1097  gui_->set_playing_team_index(0);
1099  return true;
1101  context_manager_->edit_side_dialog(gui_->viewing_team());
1102  return true;
1103 
1104  // Transitions
1106  context_manager_->set_update_transitions_mode(2);
1107  return true;
1109  context_manager_->set_update_transitions_mode(1);
1110  return true;
1112  context_manager_->set_update_transitions_mode(0);
1113  return true;
1115  if(context_manager_->toggle_update_transitions()) {
1116  return true;
1117  }
1118  [[fallthrough]];
1120  context_manager_->refresh_all();
1121  return true;
1122  // Refresh
1123  case HOTKEY_EDITOR_REFRESH:
1124  context_manager_->reload_map();
1125  return true;
1128  return true;
1129 
1132  prefs::get().set_editor_draw_hex_coordinates(gui().debug_flag_set(display::DEBUG_COORDINATES));
1133  gui().invalidate_all();
1134  return true;
1137  prefs::get().set_editor_draw_terrain_codes(gui().debug_flag_set(display::DEBUG_TERRAIN_CODES));
1138  gui().invalidate_all();
1139  return true;
1142  prefs::get().set_editor_draw_num_of_bitmaps(gui().debug_flag_set(display::DEBUG_NUM_BITMAPS));
1143  gui().invalidate_all();
1144  return true;
1146  gui().set_help_string_enabled(!gui().help_string_enabled());
1147  prefs::get().set_editor_help_text_shown(gui().help_string_enabled());
1148  return true;
1150  location_palette* lp = dynamic_cast<location_palette*>(&toolkit_->get_palette_manager()->active_palette());
1151  if (lp) {
1152  perform_delete(std::make_unique<editor_action_starting_position>(map_location(), lp->selected_item()));
1153  // No idea if this is the right thing to call, but it ensures starting
1154  // position labels get removed on delete.
1155  context_manager_->refresh_after_action();
1156  }
1157  return true;
1158  }
1159  default:
1160  return hotkey::command_executor::do_execute_command(cmd, press, release);
1161  }
1162 }
1163 
1165  if(current_addon_id_.empty()) {
1166  // editor::initialize_addon can return empty id in case of failure
1168  }
1169  context_manager_->set_addon_id(current_addon_id_);
1170  return !current_addon_id_.empty();
1171 }
1172 
1174 {
1175  help::show_help("..editor");
1176 }
1177 
1178 void editor_controller::show_menu(const std::vector<config>& items_arg, int xloc, int yloc, bool context_menu, display& disp)
1179 {
1180  if(context_menu) {
1181  if(!get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(xloc, yloc))) {
1182  return;
1183  }
1184  }
1185 
1186  std::vector<config> items;
1187  for(const auto& c : items_arg) {
1188  const std::string& id = c["id"];
1189 
1191 
1192  if((can_execute_command(cmd) && (!context_menu || in_context_menu(cmd)))
1194  {
1195  items.emplace_back("id", id);
1196  }
1197  }
1198 
1199  // No point in showing an empty menu.
1200  if(items.empty()) {
1201  return;
1202  }
1203 
1204  // Based on the ID of the first entry, we fill the menu contextually.
1205  const std::string& first_id = items.front()["id"];
1206 
1207  if(first_id == "EDITOR-LOAD-MRU-PLACEHOLDER") {
1209  context_manager_->expand_load_mru_menu(items, 0);
1210  }
1211 
1212  if(first_id == "editor-switch-map") {
1214  context_manager_->expand_open_maps_menu(items, 0);
1215  }
1216 
1217  if(first_id == "editor-palette-groups") {
1219  toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(items, 0);
1220  }
1221 
1222  if(first_id == "editor-switch-side") {
1224  context_manager_->expand_sides_menu(items, 0);
1225  }
1226 
1227  if(first_id == "editor-switch-area") {
1229  context_manager_->expand_areas_menu(items, 0);
1230  }
1231 
1232  if(first_id == "editor-pbl") {
1234  }
1235 
1236  if(!items.empty() && items.front()["id"] == "editor-switch-time") {
1238  context_manager_->expand_time_menu(items, 0);
1239  }
1240 
1241  if(first_id == "editor-assign-local-time") {
1243  context_manager_->expand_local_time_menu(items, 0);
1244  }
1245 
1246  if(first_id == "menu-unit-facings") {
1248  auto pos = items.erase(items.begin());
1249  int dir = 0;
1250  std::generate_n(std::inserter<std::vector<config>>(items, pos), static_cast<int>(map_location::direction::indeterminate), [&dir]() -> config {
1252  });
1253  }
1254 
1255  if(first_id == "editor-playlist") {
1257  auto pos = items.erase(items.begin());
1258  std::transform(music_tracks_.begin(), music_tracks_.end(), std::inserter<std::vector<config>>(items, pos), [](const sound::music_track& track) -> config {
1259  return config {"label", track.title().empty() ? track.id() : track.title()};
1260  });
1261  }
1262 
1263  if(first_id == "editor-assign-schedule") {
1265  auto pos = items.erase(items.begin());
1266  std::transform(tods_.begin(), tods_.end(), std::inserter<std::vector<config>>(items, pos), [](const tods_map::value_type& tod) -> config {
1267  return config {"label", tod.second.first};
1268  });
1269  }
1270 
1271  if(first_id == "editor-assign-local-schedule") {
1273  auto pos = items.erase(items.begin());
1274  std::transform(tods_.begin(), tods_.end(), std::inserter<std::vector<config>>(items, pos), [](const tods_map::value_type& tod) -> config {
1275  return config {"label", tod.second.first};
1276  });
1277  }
1278 
1279  command_executor::show_menu(items, xloc, yloc, context_menu, disp);
1280 }
1281 
1283 {
1284  gui_->clear_help_string();
1285  gui2::dialogs::preferences_dialog::display();
1286 
1287  gui_->queue_rerender();
1288 }
1289 
1291 {
1292  prefs::get().set_grid(!prefs::get().grid());
1293  gui_->invalidate_all();
1294 }
1295 
1297 {
1298  map_location loc = gui_->mouseover_hex();
1299  const unit_map& units = get_current_map_context().units();
1300  const unit_map::const_unit_iterator un = units.find(loc);
1301  if(un != units.end()) {
1302  help::show_unit_description(un->type());
1303  } else {
1304  help::show_help("..units");
1305  }
1306 }
1307 
1308 
1310 {
1311  if (!get_current_map_context().map().selection().empty()) {
1312  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1313  context_manager_->get_clipboard().center_by_mass();
1314  }
1315 }
1316 
1318 {
1319  map_location loc = gui_->mouseover_hex();
1320  unit_map& units = get_current_map_context().units();
1321  const unit_map::unit_iterator& un = units.find(loc);
1322 
1323  const std::string title(N_("Change Unit ID"));
1324  const std::string label(N_("ID:"));
1325 
1326  if(un != units.end()) {
1327  std::string id = un->id();
1328  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1329  un->set_id(id);
1330  }
1331  }
1332 }
1333 
1335 {
1336  map_location loc = gui_->mouseover_hex();
1337  unit_map& units = get_current_map_context().units();
1338  const unit_map::unit_iterator& un = units.find(loc);
1339 
1340  const std::string title(N_("Rename Unit"));
1341  const std::string label(N_("Name:"));
1342 
1343  if(un != units.end()) {
1344  std::string name = un->name();
1345  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1346  //TODO we may not want a translated name here.
1347  un->set_name(name);
1348  }
1349  }
1350 }
1351 
1353 {
1354  std::vector<unit_const_ptr> unit_list;
1355 
1356  const unit_map& units = gui().context().units();
1357  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1358  if(i->side() != gui().viewing_team().side()) {
1359  continue;
1360  }
1361  unit_list.push_back(i.get_shared_ptr());
1362  }
1363 
1365 
1366  if (unit_dlg->show() && unit_dlg->is_selected()) {
1367  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1369  gui().select_hex(loc);
1370  }
1371 }
1372 
1374 {
1375  copy_selection();
1377 }
1378 
1380 {
1381  const std::set<map_location>& area = get_current_map_context().map().selection();
1383 }
1384 
1386 {
1387  const std::set<map_location>& area = get_current_map_context().map().selection();
1389 }
1390 
1392 {
1393  std::stringstream ssx, ssy;
1394  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1395  if (i != get_current_map_context().map().selection().end()) {
1396  ssx << "x = " << i->wml_x();
1397  ssy << "y = " << i->wml_y();
1398  ++i;
1399  while (i != get_current_map_context().map().selection().end()) {
1400  ssx << ", " << i->wml_x();
1401  ssy << ", " << i->wml_y();
1402  ++i;
1403  }
1404  ssx << "\n" << ssy.str() << "\n";
1406  }
1407 }
1408 
1409 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1410 {
1411  if (action) {
1413  }
1414 }
1415 
1416 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1417 {
1418  if (action) {
1419  context_manager_->perform_refresh(*action, drag_part);
1420  }
1421 }
1422 
1424 {
1426  context_manager_->refresh_all();
1427 }
1428 
1430 {
1431  set_button_state();
1432  toolkit_->adjust_size();
1434 }
1435 
1437 {
1439  context_manager_->refresh_after_action();
1440 }
1441 
1443 {
1445  context_manager_->refresh_after_action();
1446 }
1447 
1448 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1449  bool update, map_location /*new_loc*/)
1450 {
1451  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1452  map_location hex_clicked = gui().hex_clicked_on(x, y);
1453  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1454  std::unique_ptr<editor_action> a;
1455  bool partial = false;
1456  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1457  // mainly (only?) used for printing debugging information.
1458  auto last_undo = get_current_map_context().last_undo_action();
1459  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1460  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1461  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1462  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1463  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1464  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1465  }
1466  //Partial means that the mouse action has modified the
1467  //last undo action and the controller shouldn't add
1468  //anything to the undo stack (hence a different perform_ call)
1469  if (a != nullptr) {
1470  if (partial) {
1472  } else {
1474  }
1475  context_manager_->refresh_after_action(true);
1476  }
1477  } else {
1478  get_mouse_action().move(*gui_, hex_clicked);
1479  }
1480  gui().highlight_hex(hex_clicked);
1481 }
1482 
1483 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1484 {
1485  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1486 }
1487 
1489 {
1490  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1491 }
1492 
1493 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1494 {
1496 }
1497 
1498 bool editor_controller::left_click(int x, int y, const bool browse)
1499 {
1500  toolkit_->clear_mouseover_overlay();
1501  if (mouse_handler_base::left_click(x, y, browse))
1502  return true;
1503 
1504  LOG_ED << "Left click, after generic handling";
1505  map_location hex_clicked = gui().hex_clicked_on(x, y);
1506  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1507  return true;
1508 
1509  LOG_ED << "Left click action " << hex_clicked;
1510  auto a = get_mouse_action().click_left(*gui_, x, y);
1511  if(a) {
1512  perform_refresh_delete(std::move(a), true);
1513  set_button_state();
1514  }
1515 
1516  return false;
1517 }
1518 
1519 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1520 {
1521  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1522  perform_delete(std::move(a));
1523 }
1524 
1525 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1526 {
1527  auto a = get_mouse_action().up_left(*gui_, x, y);
1528  if(a) {
1529  perform_delete(std::move(a));
1530  set_button_state();
1531  }
1532  toolkit_->set_mouseover_overlay();
1533  context_manager_->refresh_after_action();
1534 }
1535 
1536 bool editor_controller::right_click(int x, int y, const bool browse)
1537 {
1538  toolkit_->clear_mouseover_overlay();
1539  if (mouse_handler_base::right_click(x, y, browse)) return true;
1540  LOG_ED << "Right click, after generic handling";
1541  map_location hex_clicked = gui().hex_clicked_on(x, y);
1542  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1543  LOG_ED << "Right click action " << hex_clicked;
1544  auto a = get_mouse_action().click_right(*gui_, x, y);
1545  if(a) {
1546  perform_refresh_delete(std::move(a), true);
1547  set_button_state();
1548  }
1549  return false;
1550 }
1551 
1552 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1553 {
1554  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1555  perform_delete(std::move(a));
1556 }
1557 
1558 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1559 {
1560  // Call base method to handle context menus.
1561  mouse_handler_base::right_mouse_up(x, y, browse);
1562 
1563  auto a = get_mouse_action().up_right(*gui_, x, y);
1564  if(a) {
1565  perform_delete(std::move(a));
1566  set_button_state();
1567  }
1568  toolkit_->set_mouseover_overlay();
1569  context_manager_->refresh_after_action();
1570 }
1571 
1573 {
1574  const map_location& loc = gui().mouseover_hex();
1575  if (get_current_map_context().map().on_board(loc) == false)
1576  return;
1577 
1580 }
1581 
1582 void editor_controller::process_keyup_event(const SDL_Event& event)
1583 {
1584  auto a = get_mouse_action().key_event(gui(), event);
1585  perform_refresh_delete(std::move(a));
1586  toolkit_->set_mouseover_overlay();
1587 }
1588 
1590  return this;
1591 }
1592 
1594 {
1596 }
1597 
1599 {
1601 }
1602 
1604 {
1606 }
1607 
1609 {
1611 }
1612 
1614 {
1615  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1616 }
1617 
1618 } //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
void change_display_context(const display_context *dc)
Definition: display.cpp:437
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1398
void add_redraw_observer(const std::function< void(display &)> &f)
Adds a redraw observer, a function object to be called when a full rerender is queued.
Definition: display.cpp:2216
@ 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:358
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:97
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:218
@ NORMAL
Definition: cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:178
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:139
void show_terrain_description(const terrain_type &t)
Definition: help.cpp:65
void show_unit_description(const unit &u)
Definition: help.cpp:96
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:898
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:600
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