The Battle for Wesnoth  1.19.15+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 "quit_confirmation.hpp"
53 #include "sdl/input.hpp" // get_mouse_button_mask
54 #include "serialization/chrono.hpp"
55 #include "sound.hpp"
57 #include "units/unit.hpp"
58 #include "utils/scope_exit.hpp"
59 
60 #include <functional>
61 
62 namespace editor
63 {
64 namespace
65 {
66 auto parse_editor_music(const config_array_view& editor_music_range)
67 {
68  std::vector<std::shared_ptr<sound::music_track>> tracks;
69 
70  for(const config& editor_music : editor_music_range) {
71  for(const config& music : editor_music.child_range("music")) {
72  if(auto track = sound::music_track::create(music)) {
73  tracks.push_back(std::move(track));
74  }
75  }
76  }
77 
78  if(tracks.empty()) {
79  ERR_ED << "No editor music defined";
80  }
81 
82  return tracks;
83 }
84 
85 } // namespace
86 
88 
90  : controller_base()
91  , mouse_handler_base()
92  , quit_confirmation(std::bind(&editor_controller::quit_confirm, this))
93  , active_menu_(menu_type::none)
94  , reports_(new reports())
95  , gui_(new editor_display(*this, *reports_))
96  , tods_()
97  , context_manager_(new context_manager(*gui_.get(), game_config_, clear_id ? "" : editor_controller::current_addon_id_))
98  , toolkit_(nullptr)
99  , tooltip_manager_()
100  , floating_label_manager_(nullptr)
101  , help_manager_(nullptr)
102  , do_quit_(false)
103  , quit_mode_(EXIT_ERROR)
104  , music_tracks_(parse_editor_music(game_config_.child_range("editor_music")))
105 {
106  if(clear_id) {
108  }
109 
110  init_gui();
111  toolkit_.reset(new editor_toolkit(*gui_.get(), game_config_, *context_manager_.get()));
113  context_manager_->locs_ = toolkit_->get_palette_manager()->location_palette_.get();
117 
118  gui().queue_rerender();
119 }
120 
122 {
124  gui().add_redraw_observer(std::bind(&editor_controller::display_redraw_callback, this, std::placeholders::_1));
126  gui().set_debug_flag(display::DEBUG_COORDINATES, prefs::get().editor_draw_hex_coordinates());
127  gui().set_debug_flag(display::DEBUG_TERRAIN_CODES, prefs::get().editor_draw_terrain_codes());
128  gui().set_debug_flag(display::DEBUG_NUM_BITMAPS, prefs::get().editor_draw_num_of_bitmaps());
129  gui().set_help_string_enabled(prefs::get().editor_help_text_shown());
130 // halo_manager_.reset(new halo::manager(*gui_));
131 // resources::halo = halo_manager_.get();
132 // ^ These lines no longer necessary, the gui owns its halo manager.
133 // TODO: Should the editor map contexts actually own the halo manager and swap them in and out from the gui?
134 // 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
135 // without deleting it.
136 }
137 
139 {
140  for (const config &schedule : game_config.child_range("editor_times")) {
141 
142  const std::string& schedule_id = schedule["id"];
143  /* Use schedule id as the name if schedule name is empty */
144  const std::string& schedule_name = schedule["name"].empty() ? schedule["id"] : schedule["name"];
145  if (schedule_id.empty()) {
146  ERR_ED << "Missing ID attribute in a TOD Schedule.";
147  continue;
148  }
149 
150  tods_map::iterator times = tods_.find(schedule_id);
151  if (times == tods_.end()) {
152  std::pair<tods_map::iterator, bool> new_times =
153  tods_.emplace(schedule_id, std::pair(schedule_name, std::vector<time_of_day>()));
154  times = new_times.first;
155  } else {
156  ERR_ED << "Duplicate TOD Schedule identifiers.";
157  continue;
158  }
159 
160  for (const config &time : schedule.child_range("time")) {
161  times->second.second.emplace_back(time);
162  }
163 
164  }
165 
166  if (tods_.empty()) {
167  ERR_ED << "No editor time-of-day defined";
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 menu_type::map:
317  if (i < context_manager_->open_maps()) {
318  return true;
319  }
320  return false;
321  case menu_type::load_mru:
322  case menu_type::palette:
323  case menu_type::area:
324  case menu_type::side:
325  case menu_type::time:
326  case menu_type::schedule:
328  case menu_type::music:
331  case menu_type::none:
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 hotkey::on_if(un->loyal());
554  }
556  {
558  get_current_map_context().units().find(gui_->mouseover_hex());
559  return hotkey::on_if(un->can_recruit());
560  }
562  {
564  get_current_map_context().units().find(gui_->mouseover_hex());
565  return hotkey::on_if(!un->unrenamable());
566  }
567  //TODO remove hardcoded hotkey names
569  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-auto-update-transitions"));
571  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-partial-update-transitions"));
573  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-no-update-transitions"));
575  return hotkey::on_if(toolkit_->is_active_brush("brush-1"));
577  return hotkey::on_if(toolkit_->is_active_brush("brush-2"));
579  return hotkey::on_if(toolkit_->is_active_brush("brush-3"));
581  return hotkey::on_if(toolkit_->is_active_brush("brush-nw-se"));
583  return hotkey::on_if(toolkit_->is_active_brush("brush-sw-ne"));
584 
585  case HOTKEY_TOGGLE_GRID:
586  return hotkey::on_if(prefs::get().grid());
588  return hotkey::selected_if(get_current_map_context().map().everything_selected());
590  return hotkey::selected_if(get_current_map_context().map().selection().empty());
600  return hotkey::on_if(toolkit_->is_mouse_action_set(cmd.hotkey_command));
602  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_COORDINATES));
604  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_TERRAIN_CODES));
606  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_NUM_BITMAPS));
608  return hotkey::on_if(gui_->help_string_enabled());
610  return hotkey::on_if(prefs::get().minimap_draw_villages());
612  return hotkey::on_if(prefs::get().minimap_movement_coding());
614  return hotkey::on_if(prefs::get().minimap_terrain_coding());
616  return hotkey::on_if(prefs::get().minimap_draw_units());
618  return hotkey::on_if(prefs::get().minimap_draw_terrain());
619  case HOTKEY_ZOOM_DEFAULT:
620  return hotkey::on_if(gui_->get_zoom_factor() == 1.0);
621 
622  case HOTKEY_NULL:
623  switch (active_menu_) {
624  case menu_type::map:
625  return hotkey::selected_if(index == context_manager_->current_context_index());
626  case menu_type::load_mru:
628  case menu_type::palette:
630  case menu_type::area:
631  return hotkey::selected_if(index == get_current_map_context().get_active_area());
632  case menu_type::side:
633  return hotkey::selected_if(static_cast<std::size_t>(index) == gui_->playing_team_index());
634  case menu_type::time:
635  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_time());
637  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_area_time(
638  get_current_map_context().get_active_area()));
639  case menu_type::music:
640  return hotkey::on_if(get_current_map_context().playlist_contains(music_tracks_[index]));
641  case menu_type::schedule:
642  {
643  tods_map::const_iterator it = tods_.begin();
644  std::advance(it, index);
645  const std::vector<time_of_day>& times1 = it->second.second;
646  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times();
647  return hotkey::selected_if(times1 == times2);
648  }
650  {
651  tods_map::const_iterator it = tods_.begin();
652  std::advance(it, index);
653  const std::vector<time_of_day>& times1 = it->second.second;
654  int active_area = get_current_map_context().get_active_area();
655  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times(active_area);
656  return hotkey::selected_if(times1 == times2);
657  }
659  {
661  assert(un != get_current_map_context().units().end());
662  return hotkey::selected_if(un->facing() == map_location::direction{index});
663  }
664  case menu_type::none:
666  }
668  default:
669  return command_executor::get_action_state(cmd);
670  }
671 }
672 
673 bool editor_controller::do_execute_command(const hotkey::ui_command& cmd, bool press, bool release)
674 {
675  using namespace hotkey;
676  HOTKEY_COMMAND command = cmd.hotkey_command;
677  SCOPE_ED;
678  int index = cmd.index;
679 
680  // nothing here handles release; fall through to base implementation
681  if (!press) {
682  return command_executor::do_execute_command(cmd, press, release);
683  }
684 
685  switch (command) {
686  case HOTKEY_NULL:
687  switch (active_menu_) {
688  case menu_type::map:
689  if (index >= 0) {
690  unsigned i = static_cast<unsigned>(index);
691  if (i < context_manager_->size()) {
692  context_manager_->switch_context(index);
693  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
694  return true;
695  }
696  }
697  return false;
698  case menu_type::load_mru:
699  if (index >= 0) {
700  context_manager_->load_mru_item(static_cast<unsigned>(index));
701  }
702  return true;
703  case menu_type::palette:
704  toolkit_->get_palette_manager()->set_group(index);
705  return true;
706  case menu_type::side:
707  gui_->set_viewing_team_index(index, true);
708  gui_->set_playing_team_index(index);
709  toolkit_->get_palette_manager()->draw_contents();
710  return true;
711  case menu_type::area:
712  {
714  const std::set<map_location>& area =
717  gui_->scroll_to_tiles({ area.begin(), area.end() });
718  return true;
719  }
720  case menu_type::time:
721  {
723  gui_->update_tod();
724  return true;
725  }
727  {
729  return true;
730  }
731  case menu_type::music:
732  {
733  //TODO mark the map as changed
736  return true;
737  }
738  case menu_type::schedule:
739  {
740  tods_map::iterator iter = tods_.begin();
741  std::advance(iter, index);
742  get_current_map_context().replace_schedule(iter->second.second);
743  // TODO: test again after the assign-schedule menu is fixed. Should work, though.
744  gui_->update_tod();
745  return true;
746  }
748  {
749  tods_map::iterator iter = tods_.begin();
750  std::advance(iter, index);
751  get_current_map_context().replace_local_schedule(iter->second.second);
752  return true;
753  }
755  {
757  assert(un != get_current_map_context().units().end());
758  un->set_facing(map_location::direction(index));
759  un->anim_comp().set_standing();
760  return true;
761  }
762  case menu_type::none:
763  return true;
764  }
765  return true;
766 
767  //Zoom
768  case HOTKEY_ZOOM_IN:
769  gui_->set_zoom(true);
771  toolkit_->set_mouseover_overlay(*gui_);
772  return true;
773  case HOTKEY_ZOOM_OUT:
774  gui_->set_zoom(false);
776  toolkit_->set_mouseover_overlay(*gui_);
777  return true;
778  case HOTKEY_ZOOM_DEFAULT:
779  gui_->toggle_default_zoom();
781  toolkit_->set_mouseover_overlay(*gui_);
782  return true;
783 
784  //Palette
786  //TODO this code waits for the gui2 dialog to get ready
787 // std::vector< std::pair< std::string, std::string >> blah_items;
788 // toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(blah_items);
789 // int selected = 1; //toolkit_->get_palette_manager()->active_palette().get_selected;
790 // gui2::teditor_select_palette_group::execute(selected, blah_items);
791  return true;
793  toolkit_->get_palette_manager()->scroll_up();
794  return true;
796  toolkit_->get_palette_manager()->scroll_down();
797  return true;
798 
799  case HOTKEY_QUIT_GAME:
801  do_quit_ = true;
803  }
804  return true;
807  return true;
809  context_manager_->save_contexts();
810  do_quit_ = true;
812  return true;
815  return true;
818  return true;
820  toolkit_->get_palette_manager()->active_palette().swap();
821  return true;
823  if (dynamic_cast<const editor_action_chain*>(get_current_map_context().last_undo_action()) != nullptr) {
825  context_manager_->refresh_after_action();
826  } else {
827  undo();
828  }
829  return true;
830 
831  //Tool Selection
840  toolkit_->hotkey_set_mouse_action(command);
841  return true;
842 
843  case HOTKEY_EDITOR_PBL:
844  if(initialize_addon()) {
845  context_manager_->edit_pbl();
846  }
847  return true;
848 
850  if(initialize_addon()) {
851  context_manager_->change_addon_id();
852  }
853  return true;
854 
856  select_addon();
857  return true;
858 
860  {
861  if (!initialize_addon()) {
862  gui2::show_error_message("Could not initialize add-on!");
863  return true;
864  }
865 
867 
868  dlg.set_title(_("Add-on Files"))
870 
871  if (dlg.show()) {
872  std::string filepath = dlg.path();
873  if (filesystem::is_map(filepath) || filesystem::is_cfg(filepath)) {
874  // Open map or scenario
875  context_manager_->load_map(filepath, true);
876  } else {
877  // Open file using OS application for that format
879  desktop::open_object(filepath);
880  } else {
881  gui2::show_message("", _("Opening files is not supported, contact your packager"), gui2::dialogs::message::auto_close);
882  }
883  }
884  }
885 
886  return true;
887  }
888 
890  add_area();
891  return true;
892 
894  change_unit_id();
895  return true;
896 
898  {
899  map_location loc = gui_->mouseover_hex();
901  bool unrenamable = un->unrenamable();
902  un->set_unrenamable(!unrenamable);
903  return true;
904  }
906  {
907  map_location loc = gui_->mouseover_hex();
909  bool canrecruit = un->can_recruit();
910  un->set_can_recruit(!canrecruit);
911  un->anim_comp().set_standing();
912  return true;
913  }
915  {
916  map_location loc = gui_->mouseover_hex();
918  bool loyal = un->loyal();
919  un->set_loyal(!loyal);
920  return true;
921  }
922  case HOTKEY_DELETE_UNIT:
923  {
924  map_location loc = gui_->mouseover_hex();
925  perform_delete(std::make_unique<editor_action_unit_delete>(loc));
926  return true;
927  }
928  case HOTKEY_EDITOR_CLIPBOARD_PASTE: //paste is somewhat different as it might be "one action then revert to previous mode"
929  toolkit_->hotkey_set_mouse_action(command);
930  return true;
931 
932  //Clipboard
934  context_manager_->get_clipboard().rotate_60_cw();
935  toolkit_->update_mouse_action_highlights();
936  return true;
938  context_manager_->get_clipboard().rotate_60_ccw();
939  toolkit_->update_mouse_action_highlights();
940  return true;
942  context_manager_->get_clipboard().flip_horizontal();
943  toolkit_->update_mouse_action_highlights();
944  return true;
946  context_manager_->get_clipboard().flip_vertical();
947  toolkit_->update_mouse_action_highlights();
948  return true;
949 
950  //Brushes
952  toolkit_->cycle_brush();
953  return true;
955  toolkit_->set_brush("brush-1");
956  return true;
958  toolkit_->set_brush("brush-2");
959  return true;
961  toolkit_->set_brush("brush-3");
962  return true;
964  toolkit_->set_brush("brush-nw-se");
965  return true;
967  toolkit_->set_brush("brush-sw-ne");
968  return true;
969 
971  copy_selection();
972  return true;
974  cut_selection();
975  return true;
977  context_manager_->rename_area_dialog();
978  return true;
980  save_area();
981  return true;
984  return true;
986  if(!get_current_map_context().map().everything_selected()) {
987  context_manager_->perform_refresh(editor_action_select_all());
988  return true;
989  }
990  [[fallthrough]];
993  return true;
995  context_manager_->perform_refresh(editor_action_select_none());
996  return true;
998  context_manager_->fill_selection();
999  return true;
1002  get_current_map_context().map().selection()));
1003  return true;
1004 
1006  context_manager_->edit_scenario_dialog();
1007  return true;
1008 
1011  get_current_map_context().get_active_area());
1012  return true;
1013 
1014  // map specific
1016  context_manager_->close_current_context();
1017  // Copy behaviour from when switching windows to always reset the active tool to the Paint Tool
1018  // This avoids the situation of having a scenario-specific tool active in a map context which can cause a crash if used
1019  // Not elegant but at least avoids a potential crash and is consistent with existing behaviour
1020  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
1021  return true;
1023  context_manager_->load_map_dialog();
1024  return true;
1026  context_manager_->revert_map();
1027  return true;
1028  case HOTKEY_EDITOR_MAP_NEW:
1029  context_manager_->new_map_dialog();
1030  return true;
1032  if(initialize_addon()) {
1033  context_manager_->new_scenario_dialog();
1034  }
1035  return true;
1037  save_map();
1038  return true;
1040  context_manager_->save_all_maps();
1041  return true;
1043  context_manager_->save_map_as_dialog();
1044  return true;
1046  if(initialize_addon()) {
1047  context_manager_->map_to_scenario();
1048  }
1049  return true;
1051  if(initialize_addon()) {
1052  context_manager_->save_scenario_as_dialog();
1053  }
1054  return true;
1056  context_manager_->generate_map_dialog();
1057  return true;
1059  context_manager_->apply_mask_dialog();
1060  return true;
1062  context_manager_->create_mask_to_dialog();
1063  return true;
1065  context_manager_->resize_map_dialog();
1066  return true;
1067 
1068  // Side specific ones
1070  if(get_current_map_context().teams().size() >= 9) {
1071  std::size_t new_side_num = get_current_map_context().teams().size() + 1;
1072  toolkit_->get_palette_manager()->location_palette_->add_item(std::to_string(new_side_num));
1073  }
1075  gui_->init_flags();
1076  return true;
1078  gui_->set_viewing_team_index(0, true);
1079  gui_->set_playing_team_index(0);
1081  return true;
1083  context_manager_->edit_side_dialog(gui_->viewing_team());
1084  return true;
1085 
1086  // Transitions
1088  context_manager_->set_update_transitions_mode(2);
1089  return true;
1091  context_manager_->set_update_transitions_mode(1);
1092  return true;
1094  context_manager_->set_update_transitions_mode(0);
1095  return true;
1097  if(context_manager_->toggle_update_transitions()) {
1098  return true;
1099  }
1100  [[fallthrough]];
1102  context_manager_->refresh_all();
1103  return true;
1104  // Refresh
1105  case HOTKEY_EDITOR_REFRESH:
1106  context_manager_->reload_map();
1107  return true;
1110  return true;
1111 
1114  prefs::get().set_editor_draw_hex_coordinates(gui().debug_flag_set(display::DEBUG_COORDINATES));
1115  gui().invalidate_all();
1116  return true;
1119  prefs::get().set_editor_draw_terrain_codes(gui().debug_flag_set(display::DEBUG_TERRAIN_CODES));
1120  gui().invalidate_all();
1121  return true;
1124  prefs::get().set_editor_draw_num_of_bitmaps(gui().debug_flag_set(display::DEBUG_NUM_BITMAPS));
1125  gui().invalidate_all();
1126  return true;
1128  gui().set_help_string_enabled(!gui().help_string_enabled());
1129  prefs::get().set_editor_help_text_shown(gui().help_string_enabled());
1130  return true;
1132  location_palette* lp = dynamic_cast<location_palette*>(&toolkit_->get_palette_manager()->active_palette());
1133  if (lp) {
1134  perform_delete(std::make_unique<editor_action_starting_position>(map_location(), lp->selected_item()));
1135  // No idea if this is the right thing to call, but it ensures starting
1136  // position labels get removed on delete.
1137  context_manager_->refresh_after_action();
1138  }
1139  return true;
1140  }
1141  default:
1142  return hotkey::command_executor::do_execute_command(cmd, press, release);
1143  }
1144 }
1145 
1147 {
1149  context_manager_->set_addon_id(current_addon_id_);
1150 }
1151 
1153 {
1154  if(current_addon_id_.empty()) {
1155  select_addon();
1156  }
1157 
1158  // current_addon_id_ could be empty if editor::initialize_addon failed
1159  return !current_addon_id_.empty();
1160 }
1161 
1163 {
1164  help::show_help("..editor");
1165 }
1166 
1168 {
1169  // Keep the music menu open to allow multiple selections easily
1170  return active_menu_ == menu_type::music;
1171 }
1172 
1173 void editor_controller::show_menu(const std::vector<config>& items_arg, const point& menu_loc, bool context_menu)
1174 {
1175  // Ensure active_menu_ is only valid within the scope of this function.
1177 
1178  if(context_menu
1179  && !get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(menu_loc.x, menu_loc.y)))
1180  {
1181  return;
1182  }
1183 
1184  std::vector<config> items;
1185  for(const auto& c : items_arg) {
1186  const std::string& id = c["id"];
1187  const auto cmd = hotkey::ui_command(id);
1188 
1189  if((can_execute_command(cmd) && (!context_menu || in_context_menu(cmd)))
1190  || cmd.hotkey_command == hotkey::HOTKEY_NULL)
1191  {
1192  items.emplace_back("id", id);
1193  }
1194  }
1195 
1196  // No point in showing an empty menu.
1197  if(items.empty()) {
1198  return;
1199  }
1200 
1201  // Based on the ID of the first entry, we fill the menu contextually.
1202  const std::string& first_id = items.front()["id"];
1203 
1204  // All generated items (might be empty).
1205  std::vector<config> generated;
1206 
1207  if(first_id == "EDITOR-LOAD-MRU-PLACEHOLDER") {
1209  context_manager_->expand_load_mru_menu(generated);
1210  }
1211 
1212  else if(first_id == "editor-switch-map") {
1214  context_manager_->expand_open_maps_menu(generated);
1215  }
1216 
1217  else if(first_id == "editor-palette-groups") {
1219  toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(generated);
1220  }
1221 
1222  else if(first_id == "editor-switch-side") {
1224  context_manager_->expand_sides_menu(generated);
1225  }
1226 
1227  else if(first_id == "editor-switch-area") {
1229  context_manager_->expand_areas_menu(generated);
1230  }
1231 
1232  else if(first_id == "editor-switch-time") {
1234  context_manager_->expand_time_menu(generated);
1235  }
1236 
1237  else if(first_id == "editor-assign-local-time") {
1239  context_manager_->expand_local_time_menu(generated);
1240  }
1241 
1242  else if(first_id == "menu-unit-facings") {
1244  auto count = static_cast<int>(map_location::direction::indeterminate);
1245  std::generate_n(std::back_inserter(generated), count, [dir = 0]() mutable {
1247  });
1248  }
1249 
1250  else if(first_id == "editor-playlist") {
1252  std::transform(music_tracks_.begin(), music_tracks_.end(), std::back_inserter(generated),
1253  [](const std::shared_ptr<sound::music_track>& track) {
1254  return config{"label", track->title().empty() ? track->id() : track->title()};
1255  });
1256  }
1257 
1258  else if(first_id == "editor-assign-schedule") {
1260  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1261  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1262  }
1263 
1264  else if(first_id == "editor-assign-local-schedule") {
1266  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1267  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1268  }
1269 
1270  else {
1271  // No placeholders, show everything
1272  command_executor::show_menu(items, menu_loc, context_menu);
1273  return;
1274  }
1275 
1276  // Splice the lists, excluding placeholder entry
1277  if(items.size() > 1) {
1278  std::move(items.begin() + 1, items.end(), std::back_inserter(generated));
1279  }
1280 
1281  command_executor::show_menu(generated, menu_loc, context_menu);
1282 }
1283 
1285 {
1286  gui_->clear_help_string();
1287  gui2::dialogs::preferences_dialog::display();
1288 
1289  gui_->queue_rerender();
1290 }
1291 
1293 {
1294  prefs::get().set_grid(!prefs::get().grid());
1295  gui_->invalidate_all();
1296 }
1297 
1299 {
1300  map_location loc = gui_->mouseover_hex();
1301  const unit_map& units = get_current_map_context().units();
1302  const unit_map::const_unit_iterator un = units.find(loc);
1303  if(un != units.end()) {
1304  help::show_unit_description(un->type());
1305  } else {
1306  help::show_help("..units");
1307  }
1308 }
1309 
1310 
1312 {
1313  if (!get_current_map_context().map().selection().empty()) {
1314  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1315  context_manager_->get_clipboard().center_by_mass();
1316  }
1317 }
1318 
1320 {
1321  map_location loc = gui_->mouseover_hex();
1322  unit_map& units = get_current_map_context().units();
1323  const unit_map::unit_iterator& un = units.find(loc);
1324 
1325  const std::string title(N_("Change Unit ID"));
1326  const std::string label(N_("ID:"));
1327 
1328  if(un != units.end()) {
1329  std::string id = un->id();
1330  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1331  un->set_id(id);
1332  }
1333  }
1334 }
1335 
1337 {
1338  map_location loc = gui_->mouseover_hex();
1339  unit_map& units = get_current_map_context().units();
1340  const unit_map::unit_iterator& un = units.find(loc);
1341 
1342  const std::string title(N_("Rename Unit"));
1343  const std::string label(N_("Name:"));
1344 
1345  if(un != units.end()) {
1346  std::string name = un->name();
1347  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1348  //TODO we may not want a translated name here.
1349  un->set_name(name);
1350  }
1351  }
1352 }
1353 
1355 {
1356  std::vector<unit_const_ptr> unit_list;
1357 
1358  const unit_map& units = gui().context().units();
1359  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1360  if(i->side() != gui().viewing_team().side()) {
1361  continue;
1362  }
1363  unit_list.push_back(i.get_shared_ptr());
1364  }
1365 
1367 
1368  if (unit_dlg->show() && unit_dlg->is_selected()) {
1369  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1371  gui().select_hex(loc);
1372  }
1373 }
1374 
1376 {
1377  copy_selection();
1379 }
1380 
1382 {
1383  const std::set<map_location>& area = get_current_map_context().map().selection();
1385 }
1386 
1388 {
1389  const std::set<map_location>& area = get_current_map_context().map().selection();
1391 }
1392 
1394 {
1395  std::stringstream ssx, ssy;
1396  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1397  if (i != get_current_map_context().map().selection().end()) {
1398  ssx << "x = " << i->wml_x();
1399  ssy << "y = " << i->wml_y();
1400  ++i;
1401  while (i != get_current_map_context().map().selection().end()) {
1402  ssx << ", " << i->wml_x();
1403  ssy << ", " << i->wml_y();
1404  ++i;
1405  }
1406  ssx << "\n" << ssy.str() << "\n";
1408  }
1409 }
1410 
1411 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1412 {
1413  if (action) {
1415  }
1416 }
1417 
1418 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1419 {
1420  if (action) {
1421  context_manager_->perform_refresh(*action, drag_part);
1422  }
1423 }
1424 
1426 {
1428  context_manager_->refresh_all();
1429 }
1430 
1432 {
1433  set_button_state();
1434  toolkit_->adjust_size();
1436 }
1437 
1439 {
1441  context_manager_->refresh_after_action();
1442 }
1443 
1445 {
1447  context_manager_->refresh_after_action();
1448 }
1449 
1450 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1451  bool update, map_location /*new_loc*/)
1452 {
1453  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1454  map_location hex_clicked = gui().hex_clicked_on(x, y);
1455  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1456  std::unique_ptr<editor_action> a;
1457  bool partial = false;
1458  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1459  // mainly (only?) used for printing debugging information.
1460  auto last_undo = get_current_map_context().last_undo_action();
1461  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1462  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1463  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1464  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1465  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1466  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1467  }
1468  //Partial means that the mouse action has modified the
1469  //last undo action and the controller shouldn't add
1470  //anything to the undo stack (hence a different perform_ call)
1471  if (a != nullptr) {
1472  if (partial) {
1474  } else {
1476  }
1477  context_manager_->refresh_after_action(true);
1478  }
1479  } else {
1480  get_mouse_action().move(*gui_, hex_clicked);
1481  }
1482  gui().highlight_hex(hex_clicked);
1483 }
1484 
1485 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1486 {
1487  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1488 }
1489 
1491 {
1492  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1493 }
1494 
1495 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1496 {
1498 }
1499 
1500 bool editor_controller::left_click(int x, int y, const bool browse)
1501 {
1502  toolkit_->clear_mouseover_overlay();
1503  if (mouse_handler_base::left_click(x, y, browse))
1504  return true;
1505 
1506  LOG_ED << "Left click, after generic handling";
1507  map_location hex_clicked = gui().hex_clicked_on(x, y);
1508  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1509  return true;
1510 
1511  LOG_ED << "Left click action " << hex_clicked;
1512  auto a = get_mouse_action().click_left(*gui_, x, y);
1513  if(a) {
1514  perform_refresh_delete(std::move(a), true);
1515  set_button_state();
1516  }
1517 
1518  return false;
1519 }
1520 
1521 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1522 {
1523  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1524  perform_delete(std::move(a));
1525 }
1526 
1527 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1528 {
1529  auto a = get_mouse_action().up_left(*gui_, x, y);
1530  if(a) {
1531  perform_delete(std::move(a));
1532  set_button_state();
1533  }
1534  toolkit_->set_mouseover_overlay();
1535  context_manager_->refresh_after_action();
1536 }
1537 
1538 bool editor_controller::right_click(int x, int y, const bool browse)
1539 {
1540  toolkit_->clear_mouseover_overlay();
1541  if (mouse_handler_base::right_click(x, y, browse)) return true;
1542  LOG_ED << "Right click, after generic handling";
1543  map_location hex_clicked = gui().hex_clicked_on(x, y);
1544  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1545  LOG_ED << "Right click action " << hex_clicked;
1546  auto a = get_mouse_action().click_right(*gui_, x, y);
1547  if(a) {
1548  perform_refresh_delete(std::move(a), true);
1549  set_button_state();
1550  }
1551  return false;
1552 }
1553 
1554 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1555 {
1556  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1557  perform_delete(std::move(a));
1558 }
1559 
1560 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1561 {
1562  // Call base method to handle context menus.
1563  mouse_handler_base::right_mouse_up(x, y, browse);
1564 
1565  auto a = get_mouse_action().up_right(*gui_, x, y);
1566  if(a) {
1567  perform_delete(std::move(a));
1568  set_button_state();
1569  }
1570  toolkit_->set_mouseover_overlay();
1571  context_manager_->refresh_after_action();
1572 }
1573 
1575 {
1576  const map_location& loc = gui().mouseover_hex();
1577  if (get_current_map_context().map().on_board(loc) == false)
1578  return;
1579 
1582 }
1583 
1584 void editor_controller::process_keyup_event(const SDL_Event& event)
1585 {
1586  auto a = get_mouse_action().key_event(gui(), event);
1587  perform_refresh_delete(std::move(a));
1588  toolkit_->set_mouseover_overlay();
1589 }
1590 
1592  return this;
1593 }
1594 
1596 {
1598 }
1599 
1601 {
1603 }
1604 
1606 {
1608 }
1609 
1611 {
1613 }
1614 
1616 {
1617  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1618 }
1619 
1620 } // 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)
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:88
void toggle_debug_flag(DEBUG_FLAG flag)
Definition: display.hpp:927
void change_display_context(const display_context *dc)
Definition: display.cpp:433
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1379
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:2197
@ DEBUG_COORDINATES
Overlays x,y coords on tiles.
Definition: display.hpp:899
@ DEBUG_NUM_BITMAPS
Overlays number of bitmaps on tiles.
Definition: display.hpp:905
@ DEBUG_TERRAIN_CODES
Overlays terrain codes on tiles.
Definition: display.hpp:902
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:1854
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2953
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition: display.cpp:714
const display_context & context() const
Definition: display.hpp:184
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition: display.cpp:2138
void set_debug_flag(DEBUG_FLAG flag, bool value)
Definition: display.hpp:922
const map_location & mouseover_hex() const
Definition: display.hpp:301
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:527
virtual void select_hex(map_location hex)
Definition: display.cpp:1371
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.
bool keep_menu_open() const override
command_executor override
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_
menu_type active_menu_
The currently invoked dropdown menu.
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
void select_addon()
Show dialog to select active addon or create a new one.
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()
Show dialog to select active addon or create a new one if one is not yet initialized.
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_
std::vector< std::shared_ptr< sound::music_track > > music_tracks_
void scroll_right(bool on) override
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.
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
hotkey::action_state get_action_state(const hotkey::ui_command &command) const override
command_executor override
std::unique_ptr< help::help_manager > help_manager_
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...
void show_menu(const std::vector< config > &items_arg, const point &menu_loc, bool context_menu) override
command_executor override
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 toggle_track(const std::shared_ptr< sound::music_track > &track)
Remove the given track from the current playlist if present, else appends it.
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)
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.
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:362
void register_callback(std::function< void(std::vector< time_of_day >)>)
Register callback for update.
Definition: custom_tod.cpp:368
Dialog that allows user to create custom unit types.
Definition: edit_unit.hpp:33
void write()
Write the cfg file.
Definition: edit_unit.cpp:1035
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)
virtual bool in_context_menu(const hotkey::ui_command &) const
Determines whether the command should be in the context menu or not.
void recalculate_labels()
Definition: label.cpp:246
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()
static std::shared_ptr< music_track > create(const config &cfg)
std::vector< std::string > get_area_ids() const
const std::set< map_location > & get_area_by_index(int index) const
const std::vector< time_of_day > & times() const
int get_current_time() const
Definition: tod_manager.hpp:44
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
std::size_t i
Definition: function.cpp:1032
std::vector< std::reference_wrapper< const config > > config_array_view
#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:89
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:50
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.
action_state on_if(bool condition)
Returns action_state::on if condition is true, else action_state::off.
action_state selected_if(bool condition)
Returns action_state::selected if condition is true, else action_state::deselected.
@ 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:210
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:910
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:81
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.
#define ON_SCOPE_EXIT(...)
Run some arbitrary code (a lambda) when the current scope exits The lambda body follows this header,...
Definition: scope_exit.hpp:43
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 parameter for can_execute_command/do_execute_command These functions are used to exe...
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:46
bool valid() const
Definition: location.hpp:111
direction
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:48
static std::string write_translated_direction(direction dir)
Definition: location.cpp:174
Holds a 2D point.
Definition: point.hpp:25
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