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 
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  if(current_addon_id_.empty()) {
1148  // editor::initialize_addon can return empty id in case of failure
1150  }
1151  context_manager_->set_addon_id(current_addon_id_);
1152  return !current_addon_id_.empty();
1153 }
1154 
1156 {
1157  help::show_help("..editor");
1158 }
1159 
1161 {
1162  // Keep the music menu open to allow multiple selections easily
1163  return active_menu_ == menu_type::music;
1164 }
1165 
1166 void editor_controller::show_menu(const std::vector<config>& items_arg, const point& menu_loc, bool context_menu)
1167 {
1168  // Ensure active_menu_ is only valid within the scope of this function.
1170 
1171  if(context_menu
1172  && !get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(menu_loc.x, menu_loc.y)))
1173  {
1174  return;
1175  }
1176 
1177  std::vector<config> items;
1178  for(const auto& c : items_arg) {
1179  const std::string& id = c["id"];
1180  const auto cmd = hotkey::ui_command(id);
1181 
1182  if((can_execute_command(cmd) && (!context_menu || in_context_menu(cmd)))
1183  || cmd.hotkey_command == hotkey::HOTKEY_NULL)
1184  {
1185  items.emplace_back("id", id);
1186  }
1187  }
1188 
1189  // No point in showing an empty menu.
1190  if(items.empty()) {
1191  return;
1192  }
1193 
1194  // Based on the ID of the first entry, we fill the menu contextually.
1195  const std::string& first_id = items.front()["id"];
1196 
1197  // All generated items (might be empty).
1198  std::vector<config> generated;
1199 
1200  if(first_id == "EDITOR-LOAD-MRU-PLACEHOLDER") {
1202  context_manager_->expand_load_mru_menu(generated);
1203  }
1204 
1205  else if(first_id == "editor-switch-map") {
1207  context_manager_->expand_open_maps_menu(generated);
1208  }
1209 
1210  else if(first_id == "editor-palette-groups") {
1212  toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(generated);
1213  }
1214 
1215  else if(first_id == "editor-switch-side") {
1217  context_manager_->expand_sides_menu(generated);
1218  }
1219 
1220  else if(first_id == "editor-switch-area") {
1222  context_manager_->expand_areas_menu(generated);
1223  }
1224 
1225  else if(first_id == "editor-switch-time") {
1227  context_manager_->expand_time_menu(generated);
1228  }
1229 
1230  else if(first_id == "editor-assign-local-time") {
1232  context_manager_->expand_local_time_menu(generated);
1233  }
1234 
1235  else if(first_id == "menu-unit-facings") {
1237  auto count = static_cast<int>(map_location::direction::indeterminate);
1238  std::generate_n(std::back_inserter(generated), count, [dir = 0]() mutable {
1240  });
1241  }
1242 
1243  else if(first_id == "editor-playlist") {
1245  std::transform(music_tracks_.begin(), music_tracks_.end(), std::back_inserter(generated),
1246  [](const std::shared_ptr<sound::music_track>& track) {
1247  return config{"label", track->title().empty() ? track->id() : track->title()};
1248  });
1249  }
1250 
1251  else if(first_id == "editor-assign-schedule") {
1253  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1254  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1255  }
1256 
1257  else if(first_id == "editor-assign-local-schedule") {
1259  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1260  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1261  }
1262 
1263  else {
1264  // No placeholders, show everything
1265  command_executor::show_menu(items, menu_loc, context_menu);
1266  return;
1267  }
1268 
1269  // Splice the lists, excluding placeholder entry
1270  if(items.size() > 1) {
1271  std::move(items.begin() + 1, items.end(), std::back_inserter(generated));
1272  }
1273 
1274  command_executor::show_menu(generated, menu_loc, context_menu);
1275 }
1276 
1278 {
1279  gui_->clear_help_string();
1280  gui2::dialogs::preferences_dialog::display();
1281 
1282  gui_->queue_rerender();
1283 }
1284 
1286 {
1287  prefs::get().set_grid(!prefs::get().grid());
1288  gui_->invalidate_all();
1289 }
1290 
1292 {
1293  map_location loc = gui_->mouseover_hex();
1294  const unit_map& units = get_current_map_context().units();
1295  const unit_map::const_unit_iterator un = units.find(loc);
1296  if(un != units.end()) {
1297  help::show_unit_description(un->type());
1298  } else {
1299  help::show_help("..units");
1300  }
1301 }
1302 
1303 
1305 {
1306  if (!get_current_map_context().map().selection().empty()) {
1307  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1308  context_manager_->get_clipboard().center_by_mass();
1309  }
1310 }
1311 
1313 {
1314  map_location loc = gui_->mouseover_hex();
1315  unit_map& units = get_current_map_context().units();
1316  const unit_map::unit_iterator& un = units.find(loc);
1317 
1318  const std::string title(N_("Change Unit ID"));
1319  const std::string label(N_("ID:"));
1320 
1321  if(un != units.end()) {
1322  std::string id = un->id();
1323  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1324  un->set_id(id);
1325  }
1326  }
1327 }
1328 
1330 {
1331  map_location loc = gui_->mouseover_hex();
1332  unit_map& units = get_current_map_context().units();
1333  const unit_map::unit_iterator& un = units.find(loc);
1334 
1335  const std::string title(N_("Rename Unit"));
1336  const std::string label(N_("Name:"));
1337 
1338  if(un != units.end()) {
1339  std::string name = un->name();
1340  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1341  //TODO we may not want a translated name here.
1342  un->set_name(name);
1343  }
1344  }
1345 }
1346 
1348 {
1349  std::vector<unit_const_ptr> unit_list;
1350 
1351  const unit_map& units = gui().context().units();
1352  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1353  if(i->side() != gui().viewing_team().side()) {
1354  continue;
1355  }
1356  unit_list.push_back(i.get_shared_ptr());
1357  }
1358 
1360 
1361  if (unit_dlg->show() && unit_dlg->is_selected()) {
1362  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1364  gui().select_hex(loc);
1365  }
1366 }
1367 
1369 {
1370  copy_selection();
1372 }
1373 
1375 {
1376  const std::set<map_location>& area = get_current_map_context().map().selection();
1378 }
1379 
1381 {
1382  const std::set<map_location>& area = get_current_map_context().map().selection();
1384 }
1385 
1387 {
1388  std::stringstream ssx, ssy;
1389  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1390  if (i != get_current_map_context().map().selection().end()) {
1391  ssx << "x = " << i->wml_x();
1392  ssy << "y = " << i->wml_y();
1393  ++i;
1394  while (i != get_current_map_context().map().selection().end()) {
1395  ssx << ", " << i->wml_x();
1396  ssy << ", " << i->wml_y();
1397  ++i;
1398  }
1399  ssx << "\n" << ssy.str() << "\n";
1401  }
1402 }
1403 
1404 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1405 {
1406  if (action) {
1408  }
1409 }
1410 
1411 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1412 {
1413  if (action) {
1414  context_manager_->perform_refresh(*action, drag_part);
1415  }
1416 }
1417 
1419 {
1421  context_manager_->refresh_all();
1422 }
1423 
1425 {
1426  set_button_state();
1427  toolkit_->adjust_size();
1429 }
1430 
1432 {
1434  context_manager_->refresh_after_action();
1435 }
1436 
1438 {
1440  context_manager_->refresh_after_action();
1441 }
1442 
1443 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1444  bool update, map_location /*new_loc*/)
1445 {
1446  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1447  map_location hex_clicked = gui().hex_clicked_on(x, y);
1448  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1449  std::unique_ptr<editor_action> a;
1450  bool partial = false;
1451  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1452  // mainly (only?) used for printing debugging information.
1453  auto last_undo = get_current_map_context().last_undo_action();
1454  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1455  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1456  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1457  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1458  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1459  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1460  }
1461  //Partial means that the mouse action has modified the
1462  //last undo action and the controller shouldn't add
1463  //anything to the undo stack (hence a different perform_ call)
1464  if (a != nullptr) {
1465  if (partial) {
1467  } else {
1469  }
1470  context_manager_->refresh_after_action(true);
1471  }
1472  } else {
1473  get_mouse_action().move(*gui_, hex_clicked);
1474  }
1475  gui().highlight_hex(hex_clicked);
1476 }
1477 
1478 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1479 {
1480  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1481 }
1482 
1484 {
1485  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1486 }
1487 
1488 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1489 {
1491 }
1492 
1493 bool editor_controller::left_click(int x, int y, const bool browse)
1494 {
1495  toolkit_->clear_mouseover_overlay();
1496  if (mouse_handler_base::left_click(x, y, browse))
1497  return true;
1498 
1499  LOG_ED << "Left click, after generic handling";
1500  map_location hex_clicked = gui().hex_clicked_on(x, y);
1501  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1502  return true;
1503 
1504  LOG_ED << "Left click action " << hex_clicked;
1505  auto a = get_mouse_action().click_left(*gui_, x, y);
1506  if(a) {
1507  perform_refresh_delete(std::move(a), true);
1508  set_button_state();
1509  }
1510 
1511  return false;
1512 }
1513 
1514 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1515 {
1516  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1517  perform_delete(std::move(a));
1518 }
1519 
1520 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1521 {
1522  auto a = get_mouse_action().up_left(*gui_, x, y);
1523  if(a) {
1524  perform_delete(std::move(a));
1525  set_button_state();
1526  }
1527  toolkit_->set_mouseover_overlay();
1528  context_manager_->refresh_after_action();
1529 }
1530 
1531 bool editor_controller::right_click(int x, int y, const bool browse)
1532 {
1533  toolkit_->clear_mouseover_overlay();
1534  if (mouse_handler_base::right_click(x, y, browse)) return true;
1535  LOG_ED << "Right click, after generic handling";
1536  map_location hex_clicked = gui().hex_clicked_on(x, y);
1537  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1538  LOG_ED << "Right click action " << hex_clicked;
1539  auto a = get_mouse_action().click_right(*gui_, x, y);
1540  if(a) {
1541  perform_refresh_delete(std::move(a), true);
1542  set_button_state();
1543  }
1544  return false;
1545 }
1546 
1547 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1548 {
1549  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1550  perform_delete(std::move(a));
1551 }
1552 
1553 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1554 {
1555  // Call base method to handle context menus.
1556  mouse_handler_base::right_mouse_up(x, y, browse);
1557 
1558  auto a = get_mouse_action().up_right(*gui_, x, y);
1559  if(a) {
1560  perform_delete(std::move(a));
1561  set_button_state();
1562  }
1563  toolkit_->set_mouseover_overlay();
1564  context_manager_->refresh_after_action();
1565 }
1566 
1568 {
1569  const map_location& loc = gui().mouseover_hex();
1570  if (get_current_map_context().map().on_board(loc) == false)
1571  return;
1572 
1575 }
1576 
1577 void editor_controller::process_keyup_event(const SDL_Event& event)
1578 {
1579  auto a = get_mouse_action().key_event(gui(), event);
1580  perform_refresh_delete(std::move(a));
1581  toolkit_->set_mouseover_overlay();
1582 }
1583 
1585  return this;
1586 }
1587 
1589 {
1591 }
1592 
1594 {
1596 }
1597 
1599 {
1601 }
1602 
1604 {
1606 }
1607 
1609 {
1610  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1611 }
1612 
1613 } // 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