The Battle for Wesnoth  1.19.14+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::addon:
325  case menu_type::side:
326  case menu_type::time:
327  case menu_type::schedule:
329  case menu_type::music:
332  case menu_type::none:
333  return true;
334  }
335  }
336  return false;
338  return true;
340  return toolkit_->get_palette_manager()->can_scroll_up();
342  return toolkit_->get_palette_manager()->can_scroll_down();
343  case HOTKEY_ZOOM_IN:
344  return !gui_->zoom_at_max();
345  case HOTKEY_ZOOM_OUT:
346  return !gui_->zoom_at_min();
347  case HOTKEY_ZOOM_DEFAULT:
348  case HOTKEY_FULLSCREEN:
349  case HOTKEY_SCREENSHOT:
351  case HOTKEY_TOGGLE_GRID:
352  case HOTKEY_MOUSE_SCROLL:
353  case HOTKEY_ANIMATE_MAP:
354  case HOTKEY_MUTE:
355  case HOTKEY_PREFERENCES:
356  case HOTKEY_HELP:
357  case HOTKEY_QUIT_GAME:
358  case HOTKEY_SCROLL_UP:
359  case HOTKEY_SCROLL_DOWN:
360  case HOTKEY_SCROLL_LEFT:
361  case HOTKEY_SCROLL_RIGHT:
362  return true; //general hotkeys we can always do
363 
364  case HOTKEY_UNIT_LIST:
365  return !get_current_map_context().units().empty();
366 
367  // TODO Disabling this for now until the functionality can be implemnted.
368  // See the status_table() method
369  case HOTKEY_STATUS_TABLE:
370  //return !get_current_map_context().teams().empty();
371  return false;
372  /////////////////////////////
373 
375  return gui().mouseover_hex().valid();
376 
377  // unit tool related
378  case HOTKEY_DELETE_UNIT:
379  case HOTKEY_RENAME_UNIT:
386  {
387  map_location loc = gui_->mouseover_hex();
388  const unit_map& units = get_current_map_context().units();
389  return (toolkit_->is_mouse_action_set(HOTKEY_EDITOR_TOOL_UNIT) &&
390  units.find(loc) != units.end());
391  }
392 
393  case HOTKEY_UNDO:
396  case HOTKEY_REDO:
398 
405  return true;
406 
407  // Can be enabled as long as a valid addon_id is set
409  return !current_addon_id_.empty();
410 
411  // Only enable when editing a scenario
414 
415  // Only enable when editing a scenario
419 
420  case HOTKEY_EDITOR_PBL:
424  return true;
425 
429 
432  return !get_current_map_context().teams().empty();
433 
434  // brushes
442 
444  return true;
446  return toolkit_->get_palette_manager()->active_palette().supports_swap();
450  {
451  std::string dummy;
452  return context_manager_->modified_maps(dummy) > 1;
453  }
459  return true;
461  return !get_current_map_context().get_filename().empty()
463 
464  // Tools
465  // Pure map editing tools this can be used all the time.
470  return true;
471  // WWL dependent tools which don't rely on defined sides.
478  return !get_current_map_context().teams().empty();
479 
483  return !get_current_map_context().is_pure_map() &&
485 
487  return !get_current_map_context().is_pure_map() &&
489  && !get_current_map_context().map().selection().empty();
490 
495  return !get_current_map_context().map().selection().empty()
496  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
498  return (get_current_map_context().map().selection().size() > 1
499  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE));
503  return !context_manager_->clipboard_empty();
508  return !context_manager_->clipboard_empty()
509  && toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
512  return !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
514  return !get_current_map_context().map().selection().empty()
516  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
534  return true;
538  return true;
539  default:
540  return false;
541  }
542 }
543 
545 {
546  using namespace hotkey;
547  int index = cmd.index;
548  switch (cmd.hotkey_command) {
549 
551  {
553  get_current_map_context().units().find(gui_->mouseover_hex());
554  return hotkey::on_if(un->loyal());
555  }
557  {
559  get_current_map_context().units().find(gui_->mouseover_hex());
560  return hotkey::on_if(un->can_recruit());
561  }
563  {
565  get_current_map_context().units().find(gui_->mouseover_hex());
566  return hotkey::on_if(!un->unrenamable());
567  }
568  //TODO remove hardcoded hotkey names
570  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-auto-update-transitions"));
572  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-partial-update-transitions"));
574  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-no-update-transitions"));
576  return hotkey::on_if(toolkit_->is_active_brush("brush-1"));
578  return hotkey::on_if(toolkit_->is_active_brush("brush-2"));
580  return hotkey::on_if(toolkit_->is_active_brush("brush-3"));
582  return hotkey::on_if(toolkit_->is_active_brush("brush-nw-se"));
584  return hotkey::on_if(toolkit_->is_active_brush("brush-sw-ne"));
585 
586  case HOTKEY_TOGGLE_GRID:
587  return hotkey::on_if(prefs::get().grid());
589  return hotkey::selected_if(get_current_map_context().map().everything_selected());
591  return hotkey::selected_if(get_current_map_context().map().selection().empty());
601  return hotkey::on_if(toolkit_->is_mouse_action_set(cmd.hotkey_command));
603  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_COORDINATES));
605  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_TERRAIN_CODES));
607  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_NUM_BITMAPS));
609  return hotkey::on_if(gui_->help_string_enabled());
611  return hotkey::on_if(prefs::get().minimap_draw_villages());
613  return hotkey::on_if(prefs::get().minimap_movement_coding());
615  return hotkey::on_if(prefs::get().minimap_terrain_coding());
617  return hotkey::on_if(prefs::get().minimap_draw_units());
619  return hotkey::on_if(prefs::get().minimap_draw_terrain());
620  case HOTKEY_ZOOM_DEFAULT:
621  return hotkey::on_if(gui_->get_zoom_factor() == 1.0);
622 
623  case HOTKEY_NULL:
624  switch (active_menu_) {
625  case menu_type::map:
626  return hotkey::selected_if(index == context_manager_->current_context_index());
627  case menu_type::load_mru:
629  case menu_type::palette:
631  case menu_type::area:
632  return hotkey::selected_if(index == get_current_map_context().get_active_area());
633  case menu_type::addon:
635  case menu_type::side:
636  return hotkey::selected_if(static_cast<std::size_t>(index) == gui_->playing_team_index());
637  case menu_type::time:
638  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_time());
640  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_area_time(
641  get_current_map_context().get_active_area()));
642  case menu_type::music:
643  return hotkey::on_if(get_current_map_context().playlist_contains(music_tracks_[index]));
644  case menu_type::schedule:
645  {
646  tods_map::const_iterator it = tods_.begin();
647  std::advance(it, index);
648  const std::vector<time_of_day>& times1 = it->second.second;
649  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times();
650  return hotkey::selected_if(times1 == times2);
651  }
653  {
654  tods_map::const_iterator it = tods_.begin();
655  std::advance(it, index);
656  const std::vector<time_of_day>& times1 = it->second.second;
657  int active_area = get_current_map_context().get_active_area();
658  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times(active_area);
659  return hotkey::selected_if(times1 == times2);
660  }
662  {
664  assert(un != get_current_map_context().units().end());
665  return hotkey::selected_if(un->facing() == map_location::direction{index});
666  }
667  case menu_type::none:
669  }
671  default:
672  return command_executor::get_action_state(cmd);
673  }
674 }
675 
676 bool editor_controller::do_execute_command(const hotkey::ui_command& cmd, bool press, bool release)
677 {
678  using namespace hotkey;
679  HOTKEY_COMMAND command = cmd.hotkey_command;
680  SCOPE_ED;
681  int index = cmd.index;
682 
683  // nothing here handles release; fall through to base implementation
684  if (!press) {
685  return command_executor::do_execute_command(cmd, press, release);
686  }
687 
688  switch (command) {
689  case HOTKEY_NULL:
690  switch (active_menu_) {
691  case menu_type::map:
692  if (index >= 0) {
693  unsigned i = static_cast<unsigned>(index);
694  if (i < context_manager_->size()) {
695  context_manager_->switch_context(index);
696  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
697  return true;
698  }
699  }
700  return false;
701  case menu_type::load_mru:
702  if (index >= 0) {
703  context_manager_->load_mru_item(static_cast<unsigned>(index));
704  }
705  return true;
706  case menu_type::palette:
707  toolkit_->get_palette_manager()->set_group(index);
708  return true;
709  case menu_type::side:
710  gui_->set_viewing_team_index(index, true);
711  gui_->set_playing_team_index(index);
712  toolkit_->get_palette_manager()->draw_contents();
713  return true;
714  case menu_type::area:
715  {
717  const std::set<map_location>& area =
720  gui_->scroll_to_tiles({ area.begin(), area.end() });
721  return true;
722  }
723  case menu_type::addon:
724  return true;
725  case menu_type::time:
726  {
728  gui_->update_tod();
729  return true;
730  }
732  {
734  return true;
735  }
736  case menu_type::music:
737  {
738  //TODO mark the map as changed
741  return true;
742  }
743  case menu_type::schedule:
744  {
745  tods_map::iterator iter = tods_.begin();
746  std::advance(iter, index);
747  get_current_map_context().replace_schedule(iter->second.second);
748  // TODO: test again after the assign-schedule menu is fixed. Should work, though.
749  gui_->update_tod();
750  return true;
751  }
753  {
754  tods_map::iterator iter = tods_.begin();
755  std::advance(iter, index);
756  get_current_map_context().replace_local_schedule(iter->second.second);
757  return true;
758  }
760  {
762  assert(un != get_current_map_context().units().end());
763  un->set_facing(map_location::direction(index));
764  un->anim_comp().set_standing();
765  return true;
766  }
767  case menu_type::none:
768  return true;
769  }
770  return true;
771 
772  //Zoom
773  case HOTKEY_ZOOM_IN:
774  gui_->set_zoom(true);
776  toolkit_->set_mouseover_overlay(*gui_);
777  return true;
778  case HOTKEY_ZOOM_OUT:
779  gui_->set_zoom(false);
781  toolkit_->set_mouseover_overlay(*gui_);
782  return true;
783  case HOTKEY_ZOOM_DEFAULT:
784  gui_->toggle_default_zoom();
786  toolkit_->set_mouseover_overlay(*gui_);
787  return true;
788 
789  //Palette
791  //TODO this code waits for the gui2 dialog to get ready
792 // std::vector< std::pair< std::string, std::string >> blah_items;
793 // toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(blah_items);
794 // int selected = 1; //toolkit_->get_palette_manager()->active_palette().get_selected;
795 // gui2::teditor_select_palette_group::execute(selected, blah_items);
796  return true;
798  toolkit_->get_palette_manager()->scroll_up();
799  return true;
801  toolkit_->get_palette_manager()->scroll_down();
802  return true;
803 
804  case HOTKEY_QUIT_GAME:
806  do_quit_ = true;
808  }
809  return true;
812  return true;
814  context_manager_->save_contexts();
815  do_quit_ = true;
817  return true;
820  return true;
823  return true;
825  toolkit_->get_palette_manager()->active_palette().swap();
826  return true;
828  if (dynamic_cast<const editor_action_chain*>(get_current_map_context().last_undo_action()) != nullptr) {
830  context_manager_->refresh_after_action();
831  } else {
832  undo();
833  }
834  return true;
835 
836  //Tool Selection
845  toolkit_->hotkey_set_mouse_action(command);
846  return true;
847 
848  case HOTKEY_EDITOR_PBL:
849  if(initialize_addon()) {
850  context_manager_->edit_pbl();
851  }
852  return true;
853 
855  if(initialize_addon()) {
856  context_manager_->change_addon_id();
857  }
858  return true;
859 
862  return true;
863 
865  {
866  if (!initialize_addon()) {
867  gui2::show_error_message("Could not initialize add-on!");
868  return true;
869  }
870 
872 
873  dlg.set_title(_("Add-on Files"))
875 
876  if (dlg.show()) {
877  std::string filepath = dlg.path();
878  if (filesystem::is_map(filepath) || filesystem::is_cfg(filepath)) {
879  // Open map or scenario
880  context_manager_->load_map(filepath, true);
881  } else {
882  // Open file using OS application for that format
884  desktop::open_object(filepath);
885  } else {
886  gui2::show_message("", _("Opening files is not supported, contact your packager"), gui2::dialogs::message::auto_close);
887  }
888  }
889  }
890 
891  return true;
892  }
893 
895  add_area();
896  return true;
897 
899  change_unit_id();
900  return true;
901 
903  {
904  map_location loc = gui_->mouseover_hex();
906  bool unrenamable = un->unrenamable();
907  un->set_unrenamable(!unrenamable);
908  return true;
909  }
911  {
912  map_location loc = gui_->mouseover_hex();
914  bool canrecruit = un->can_recruit();
915  un->set_can_recruit(!canrecruit);
916  un->anim_comp().set_standing();
917  return true;
918  }
920  {
921  map_location loc = gui_->mouseover_hex();
923  bool loyal = un->loyal();
924  un->set_loyal(!loyal);
925  return true;
926  }
927  case HOTKEY_DELETE_UNIT:
928  {
929  map_location loc = gui_->mouseover_hex();
930  perform_delete(std::make_unique<editor_action_unit_delete>(loc));
931  return true;
932  }
933  case HOTKEY_EDITOR_CLIPBOARD_PASTE: //paste is somewhat different as it might be "one action then revert to previous mode"
934  toolkit_->hotkey_set_mouse_action(command);
935  return true;
936 
937  //Clipboard
939  context_manager_->get_clipboard().rotate_60_cw();
940  toolkit_->update_mouse_action_highlights();
941  return true;
943  context_manager_->get_clipboard().rotate_60_ccw();
944  toolkit_->update_mouse_action_highlights();
945  return true;
947  context_manager_->get_clipboard().flip_horizontal();
948  toolkit_->update_mouse_action_highlights();
949  return true;
951  context_manager_->get_clipboard().flip_vertical();
952  toolkit_->update_mouse_action_highlights();
953  return true;
954 
955  //Brushes
957  toolkit_->cycle_brush();
958  return true;
960  toolkit_->set_brush("brush-1");
961  return true;
963  toolkit_->set_brush("brush-2");
964  return true;
966  toolkit_->set_brush("brush-3");
967  return true;
969  toolkit_->set_brush("brush-nw-se");
970  return true;
972  toolkit_->set_brush("brush-sw-ne");
973  return true;
974 
976  copy_selection();
977  return true;
979  cut_selection();
980  return true;
982  context_manager_->rename_area_dialog();
983  return true;
985  save_area();
986  return true;
989  return true;
991  if(!get_current_map_context().map().everything_selected()) {
992  context_manager_->perform_refresh(editor_action_select_all());
993  return true;
994  }
995  [[fallthrough]];
998  return true;
1000  context_manager_->perform_refresh(editor_action_select_none());
1001  return true;
1003  context_manager_->fill_selection();
1004  return true;
1007  get_current_map_context().map().selection()));
1008  return true;
1009 
1011  context_manager_->edit_scenario_dialog();
1012  return true;
1013 
1016  get_current_map_context().get_active_area());
1017  return true;
1018 
1019  // map specific
1021  context_manager_->close_current_context();
1022  // Copy behaviour from when switching windows to always reset the active tool to the Paint Tool
1023  // This avoids the situation of having a scenario-specific tool active in a map context which can cause a crash if used
1024  // Not elegant but at least avoids a potential crash and is consistent with existing behaviour
1025  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
1026  return true;
1028  context_manager_->load_map_dialog();
1029  return true;
1031  context_manager_->revert_map();
1032  return true;
1033  case HOTKEY_EDITOR_MAP_NEW:
1034  context_manager_->new_map_dialog();
1035  return true;
1037  if(initialize_addon()) {
1038  context_manager_->new_scenario_dialog();
1039  }
1040  return true;
1042  save_map();
1043  return true;
1045  context_manager_->save_all_maps();
1046  return true;
1048  context_manager_->save_map_as_dialog();
1049  return true;
1051  if(initialize_addon()) {
1052  context_manager_->map_to_scenario();
1053  }
1054  return true;
1056  if(initialize_addon()) {
1057  context_manager_->save_scenario_as_dialog();
1058  }
1059  return true;
1061  context_manager_->generate_map_dialog();
1062  return true;
1064  context_manager_->apply_mask_dialog();
1065  return true;
1067  context_manager_->create_mask_to_dialog();
1068  return true;
1070  context_manager_->resize_map_dialog();
1071  return true;
1072 
1073  // Side specific ones
1075  if(get_current_map_context().teams().size() >= 9) {
1076  std::size_t new_side_num = get_current_map_context().teams().size() + 1;
1077  toolkit_->get_palette_manager()->location_palette_->add_item(std::to_string(new_side_num));
1078  }
1080  gui_->init_flags();
1081  return true;
1083  gui_->set_viewing_team_index(0, true);
1084  gui_->set_playing_team_index(0);
1086  return true;
1088  context_manager_->edit_side_dialog(gui_->viewing_team());
1089  return true;
1090 
1091  // Transitions
1093  context_manager_->set_update_transitions_mode(2);
1094  return true;
1096  context_manager_->set_update_transitions_mode(1);
1097  return true;
1099  context_manager_->set_update_transitions_mode(0);
1100  return true;
1102  if(context_manager_->toggle_update_transitions()) {
1103  return true;
1104  }
1105  [[fallthrough]];
1107  context_manager_->refresh_all();
1108  return true;
1109  // Refresh
1110  case HOTKEY_EDITOR_REFRESH:
1111  context_manager_->reload_map();
1112  return true;
1115  return true;
1116 
1119  prefs::get().set_editor_draw_hex_coordinates(gui().debug_flag_set(display::DEBUG_COORDINATES));
1120  gui().invalidate_all();
1121  return true;
1124  prefs::get().set_editor_draw_terrain_codes(gui().debug_flag_set(display::DEBUG_TERRAIN_CODES));
1125  gui().invalidate_all();
1126  return true;
1129  prefs::get().set_editor_draw_num_of_bitmaps(gui().debug_flag_set(display::DEBUG_NUM_BITMAPS));
1130  gui().invalidate_all();
1131  return true;
1133  gui().set_help_string_enabled(!gui().help_string_enabled());
1134  prefs::get().set_editor_help_text_shown(gui().help_string_enabled());
1135  return true;
1137  location_palette* lp = dynamic_cast<location_palette*>(&toolkit_->get_palette_manager()->active_palette());
1138  if (lp) {
1139  perform_delete(std::make_unique<editor_action_starting_position>(map_location(), lp->selected_item()));
1140  // No idea if this is the right thing to call, but it ensures starting
1141  // position labels get removed on delete.
1142  context_manager_->refresh_after_action();
1143  }
1144  return true;
1145  }
1146  default:
1147  return hotkey::command_executor::do_execute_command(cmd, press, release);
1148  }
1149 }
1150 
1152  if(current_addon_id_.empty()) {
1153  // editor::initialize_addon can return empty id in case of failure
1155  }
1156  context_manager_->set_addon_id(current_addon_id_);
1157  return !current_addon_id_.empty();
1158 }
1159 
1161 {
1162  help::show_help("..editor");
1163 }
1164 
1166 {
1167  // Keep the music menu open to allow multiple selections easily
1168  return active_menu_ == menu_type::music;
1169 }
1170 
1171 void editor_controller::show_menu(const std::vector<config>& items_arg, const point& menu_loc, bool context_menu)
1172 {
1173  // Ensure active_menu_ is only valid within the scope of this function.
1175 
1176  if(context_menu
1177  && !get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(menu_loc.x, menu_loc.y)))
1178  {
1179  return;
1180  }
1181 
1182  std::vector<config> items;
1183  for(const auto& c : items_arg) {
1184  const std::string& id = c["id"];
1185  const auto cmd = hotkey::ui_command(id);
1186 
1187  if((can_execute_command(cmd) && (!context_menu || in_context_menu(cmd)))
1188  || cmd.hotkey_command == hotkey::HOTKEY_NULL)
1189  {
1190  items.emplace_back("id", id);
1191  }
1192  }
1193 
1194  // No point in showing an empty menu.
1195  if(items.empty()) {
1196  return;
1197  }
1198 
1199  // Based on the ID of the first entry, we fill the menu contextually.
1200  const std::string& first_id = items.front()["id"];
1201 
1202  // All generated items (might be empty).
1203  std::vector<config> generated;
1204 
1205  if(first_id == "EDITOR-LOAD-MRU-PLACEHOLDER") {
1207  context_manager_->expand_load_mru_menu(generated);
1208  }
1209 
1210  else if(first_id == "editor-switch-map") {
1212  context_manager_->expand_open_maps_menu(generated);
1213  }
1214 
1215  else if(first_id == "editor-palette-groups") {
1217  toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(generated);
1218  }
1219 
1220  else if(first_id == "editor-switch-side") {
1222  context_manager_->expand_sides_menu(generated);
1223  }
1224 
1225  else if(first_id == "editor-switch-area") {
1227  context_manager_->expand_areas_menu(generated);
1228  }
1229 
1230  else if(first_id == "editor-pbl") {
1232  }
1233 
1234  else if(first_id == "editor-switch-time") {
1236  context_manager_->expand_time_menu(generated);
1237  }
1238 
1239  else if(first_id == "editor-assign-local-time") {
1241  context_manager_->expand_local_time_menu(generated);
1242  }
1243 
1244  else if(first_id == "menu-unit-facings") {
1246  auto count = static_cast<int>(map_location::direction::indeterminate);
1247  std::generate_n(std::back_inserter(generated), count, [dir = 0]() mutable {
1249  });
1250  }
1251 
1252  else if(first_id == "editor-playlist") {
1254  std::transform(music_tracks_.begin(), music_tracks_.end(), std::back_inserter(generated),
1255  [](const std::shared_ptr<sound::music_track>& track) {
1256  return config{"label", track->title().empty() ? track->id() : track->title()};
1257  });
1258  }
1259 
1260  else if(first_id == "editor-assign-schedule") {
1262  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1263  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1264  }
1265 
1266  else if(first_id == "editor-assign-local-schedule") {
1268  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1269  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1270  }
1271 
1272  else {
1273  // No placeholders, show everything
1274  command_executor::show_menu(items, menu_loc, context_menu);
1275  return;
1276  }
1277 
1278  // Splice the lists, excluding placeholder entry
1279  if(items.size() > 1) {
1280  std::move(items.begin() + 1, items.end(), std::back_inserter(generated));
1281  }
1282 
1283  command_executor::show_menu(generated, menu_loc, context_menu);
1284 }
1285 
1287 {
1288  gui_->clear_help_string();
1289  gui2::dialogs::preferences_dialog::display();
1290 
1291  gui_->queue_rerender();
1292 }
1293 
1295 {
1296  prefs::get().set_grid(!prefs::get().grid());
1297  gui_->invalidate_all();
1298 }
1299 
1301 {
1302  map_location loc = gui_->mouseover_hex();
1303  const unit_map& units = get_current_map_context().units();
1304  const unit_map::const_unit_iterator un = units.find(loc);
1305  if(un != units.end()) {
1306  help::show_unit_description(un->type());
1307  } else {
1308  help::show_help("..units");
1309  }
1310 }
1311 
1312 
1314 {
1315  if (!get_current_map_context().map().selection().empty()) {
1316  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1317  context_manager_->get_clipboard().center_by_mass();
1318  }
1319 }
1320 
1322 {
1323  map_location loc = gui_->mouseover_hex();
1324  unit_map& units = get_current_map_context().units();
1325  const unit_map::unit_iterator& un = units.find(loc);
1326 
1327  const std::string title(N_("Change Unit ID"));
1328  const std::string label(N_("ID:"));
1329 
1330  if(un != units.end()) {
1331  std::string id = un->id();
1332  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1333  un->set_id(id);
1334  }
1335  }
1336 }
1337 
1339 {
1340  map_location loc = gui_->mouseover_hex();
1341  unit_map& units = get_current_map_context().units();
1342  const unit_map::unit_iterator& un = units.find(loc);
1343 
1344  const std::string title(N_("Rename Unit"));
1345  const std::string label(N_("Name:"));
1346 
1347  if(un != units.end()) {
1348  std::string name = un->name();
1349  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1350  //TODO we may not want a translated name here.
1351  un->set_name(name);
1352  }
1353  }
1354 }
1355 
1357 {
1358  std::vector<unit_const_ptr> unit_list;
1359 
1360  const unit_map& units = gui().context().units();
1361  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1362  if(i->side() != gui().viewing_team().side()) {
1363  continue;
1364  }
1365  unit_list.push_back(i.get_shared_ptr());
1366  }
1367 
1369 
1370  if (unit_dlg->show() && unit_dlg->is_selected()) {
1371  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1373  gui().select_hex(loc);
1374  }
1375 }
1376 
1378 {
1379  copy_selection();
1381 }
1382 
1384 {
1385  const std::set<map_location>& area = get_current_map_context().map().selection();
1387 }
1388 
1390 {
1391  const std::set<map_location>& area = get_current_map_context().map().selection();
1393 }
1394 
1396 {
1397  std::stringstream ssx, ssy;
1398  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1399  if (i != get_current_map_context().map().selection().end()) {
1400  ssx << "x = " << i->wml_x();
1401  ssy << "y = " << i->wml_y();
1402  ++i;
1403  while (i != get_current_map_context().map().selection().end()) {
1404  ssx << ", " << i->wml_x();
1405  ssy << ", " << i->wml_y();
1406  ++i;
1407  }
1408  ssx << "\n" << ssy.str() << "\n";
1410  }
1411 }
1412 
1413 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1414 {
1415  if (action) {
1417  }
1418 }
1419 
1420 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1421 {
1422  if (action) {
1423  context_manager_->perform_refresh(*action, drag_part);
1424  }
1425 }
1426 
1428 {
1430  context_manager_->refresh_all();
1431 }
1432 
1434 {
1435  set_button_state();
1436  toolkit_->adjust_size();
1438 }
1439 
1441 {
1443  context_manager_->refresh_after_action();
1444 }
1445 
1447 {
1449  context_manager_->refresh_after_action();
1450 }
1451 
1452 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1453  bool update, map_location /*new_loc*/)
1454 {
1455  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1456  map_location hex_clicked = gui().hex_clicked_on(x, y);
1457  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1458  std::unique_ptr<editor_action> a;
1459  bool partial = false;
1460  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1461  // mainly (only?) used for printing debugging information.
1462  auto last_undo = get_current_map_context().last_undo_action();
1463  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1464  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1465  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1466  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1467  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1468  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1469  }
1470  //Partial means that the mouse action has modified the
1471  //last undo action and the controller shouldn't add
1472  //anything to the undo stack (hence a different perform_ call)
1473  if (a != nullptr) {
1474  if (partial) {
1476  } else {
1478  }
1479  context_manager_->refresh_after_action(true);
1480  }
1481  } else {
1482  get_mouse_action().move(*gui_, hex_clicked);
1483  }
1484  gui().highlight_hex(hex_clicked);
1485 }
1486 
1487 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1488 {
1489  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1490 }
1491 
1493 {
1494  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1495 }
1496 
1497 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1498 {
1500 }
1501 
1502 bool editor_controller::left_click(int x, int y, const bool browse)
1503 {
1504  toolkit_->clear_mouseover_overlay();
1505  if (mouse_handler_base::left_click(x, y, browse))
1506  return true;
1507 
1508  LOG_ED << "Left click, after generic handling";
1509  map_location hex_clicked = gui().hex_clicked_on(x, y);
1510  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1511  return true;
1512 
1513  LOG_ED << "Left click action " << hex_clicked;
1514  auto a = get_mouse_action().click_left(*gui_, x, y);
1515  if(a) {
1516  perform_refresh_delete(std::move(a), true);
1517  set_button_state();
1518  }
1519 
1520  return false;
1521 }
1522 
1523 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1524 {
1525  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1526  perform_delete(std::move(a));
1527 }
1528 
1529 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1530 {
1531  auto a = get_mouse_action().up_left(*gui_, x, y);
1532  if(a) {
1533  perform_delete(std::move(a));
1534  set_button_state();
1535  }
1536  toolkit_->set_mouseover_overlay();
1537  context_manager_->refresh_after_action();
1538 }
1539 
1540 bool editor_controller::right_click(int x, int y, const bool browse)
1541 {
1542  toolkit_->clear_mouseover_overlay();
1543  if (mouse_handler_base::right_click(x, y, browse)) return true;
1544  LOG_ED << "Right click, after generic handling";
1545  map_location hex_clicked = gui().hex_clicked_on(x, y);
1546  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1547  LOG_ED << "Right click action " << hex_clicked;
1548  auto a = get_mouse_action().click_right(*gui_, x, y);
1549  if(a) {
1550  perform_refresh_delete(std::move(a), true);
1551  set_button_state();
1552  }
1553  return false;
1554 }
1555 
1556 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1557 {
1558  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1559  perform_delete(std::move(a));
1560 }
1561 
1562 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1563 {
1564  // Call base method to handle context menus.
1565  mouse_handler_base::right_mouse_up(x, y, browse);
1566 
1567  auto a = get_mouse_action().up_right(*gui_, x, y);
1568  if(a) {
1569  perform_delete(std::move(a));
1570  set_button_state();
1571  }
1572  toolkit_->set_mouseover_overlay();
1573  context_manager_->refresh_after_action();
1574 }
1575 
1577 {
1578  const map_location& loc = gui().mouseover_hex();
1579  if (get_current_map_context().map().on_board(loc) == false)
1580  return;
1581 
1584 }
1585 
1586 void editor_controller::process_keyup_event(const SDL_Event& event)
1587 {
1588  auto a = get_mouse_action().key_event(gui(), event);
1589  perform_refresh_delete(std::move(a));
1590  toolkit_->set_mouseover_overlay();
1591 }
1592 
1594  return this;
1595 }
1596 
1598 {
1600 }
1601 
1603 {
1605 }
1606 
1608 {
1610 }
1611 
1613 {
1615 }
1616 
1618 {
1619  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1620 }
1621 
1622 } // 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
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_
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:906
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.
#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