The Battle for Wesnoth  1.19.13+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 {
63 static std::vector<std::string> saved_windows_;
64 }
65 
66 namespace editor {
67 
69 
71  : controller_base()
72  , mouse_handler_base()
73  , quit_confirmation(std::bind(&editor_controller::quit_confirm, this))
74  , active_menu_(menu_type::none)
75  , reports_(new reports())
76  , gui_(new editor_display(*this, *reports_))
77  , tods_()
78  , context_manager_(new context_manager(*gui_.get(), game_config_, clear_id ? "" : editor_controller::current_addon_id_))
79  , toolkit_(nullptr)
80  , tooltip_manager_()
81  , floating_label_manager_(nullptr)
82  , help_manager_(nullptr)
83  , do_quit_(false)
84  , quit_mode_(EXIT_ERROR)
85  , music_tracks_()
86 {
87  if(clear_id) {
89  }
90 
91  init_gui();
92  toolkit_.reset(new editor_toolkit(*gui_.get(), key_, game_config_, *context_manager_.get()));
94  context_manager_->locs_ = toolkit_->get_palette_manager()->location_palette_.get();
99 
100  gui().queue_rerender();
101 }
102 
104 {
106  gui().add_redraw_observer(std::bind(&editor_controller::display_redraw_callback, this, std::placeholders::_1));
108  gui().set_debug_flag(display::DEBUG_COORDINATES, prefs::get().editor_draw_hex_coordinates());
109  gui().set_debug_flag(display::DEBUG_TERRAIN_CODES, prefs::get().editor_draw_terrain_codes());
110  gui().set_debug_flag(display::DEBUG_NUM_BITMAPS, prefs::get().editor_draw_num_of_bitmaps());
111  gui().set_help_string_enabled(prefs::get().editor_help_text_shown());
112 // halo_manager_.reset(new halo::manager(*gui_));
113 // resources::halo = halo_manager_.get();
114 // ^ These lines no longer necessary, the gui owns its halo manager.
115 // TODO: Should the editor map contexts actually own the halo manager and swap them in and out from the gui?
116 // Note that if that is what happens it might not actually be a good idea for the gui to own the halo manager, so that it can be swapped out
117 // without deleting it.
118 }
119 
121 {
122  for (const config &schedule : game_config.child_range("editor_times")) {
123 
124  const std::string& schedule_id = schedule["id"];
125  /* Use schedule id as the name if schedule name is empty */
126  const std::string& schedule_name = schedule["name"].empty() ? schedule["id"] : schedule["name"];
127  if (schedule_id.empty()) {
128  ERR_ED << "Missing ID attribute in a TOD Schedule.";
129  continue;
130  }
131 
132  tods_map::iterator times = tods_.find(schedule_id);
133  if (times == tods_.end()) {
134  std::pair<tods_map::iterator, bool> new_times =
135  tods_.emplace(schedule_id, std::pair(schedule_name, std::vector<time_of_day>()));
136  times = new_times.first;
137  } else {
138  ERR_ED << "Duplicate TOD Schedule identifiers.";
139  continue;
140  }
141 
142  for (const config &time : schedule.child_range("time")) {
143  times->second.second.emplace_back(time);
144  }
145 
146  }
147 
148  if (tods_.empty()) {
149  ERR_ED << "No editor time-of-day defined";
150  }
151 }
152 
154 {
155  const std::string tag_name = "editor_music";
156  if (game_config.child_range(tag_name).size() == 0) {
157  ERR_ED << "No editor music defined";
158  }
159  else {
160  for (const config& editor_music : game_config.child_range(tag_name)) {
161  for (const config& music : editor_music.child_range("music")) {
162  sound::music_track track(music);
163  if (track.file_path().empty())
164  WRN_ED << "Music track " << track.id() << " not found.";
165  else
166  music_tracks_.emplace_back(music);
167  }
168  }
169  }
170 }
171 
173 {
174  resources::tod_manager = nullptr;
175  resources::filter_con = nullptr;
176 
177  resources::classification = nullptr;
178 }
179 
181 {
182  try {
183  while (!do_quit_) {
184  play_slice();
185  }
186  } catch (const editor_exception& e) {
187  gui2::show_transient_message(_("Fatal error"), e.what());
188  return EXIT_ERROR;
189  } catch (const wml_exception& e) {
190  e.show();
191  }
192  return quit_mode_;
193 }
194 
196 }
197 
198 void editor_controller::do_screenshot(const std::string& screenshot_filename /* = "map_screenshot.png" */)
199 {
200  try {
201  surface screenshot = gui().screenshot(true);
202  if(!screenshot || image::save_image(screenshot, screenshot_filename) != image::save_result::success) {
203  ERR_ED << "Screenshot creation failed!";
204  }
205  } catch (const wml_exception& e) {
206  e.show();
207  }
208 }
209 
211 {
212  std::string modified;
213  std::size_t amount = context_manager_->modified_maps(modified);
214 
215  std::string message;
216  if (amount == 0) {
217  message = _("Do you really want to quit?");
218  } else if (amount == 1 && get_current_map_context().modified()) {
219  message = _("Do you really want to quit? Changes to this map since the last save will be lost.");
220  } else {
221  message = _("Do you really want to quit? The following maps were modified and all changes since the last save will be lost:");
222  message += "\n" + modified;
223  }
224  return quit_confirmation::show_prompt(message);
225 }
226 
228 {
230  if (unit_dlg.show()) {
231  unit_dlg.write();
232  }
233 }
234 
236 {
237  if (tods_.empty()) {
238  gui2::show_error_message(_("No editor time-of-day found."));
239  return;
240  }
241 
243  std::vector<time_of_day> prev_schedule = manager.times();
244 
245  gui2::dialogs::custom_tod tod_dlg(manager.times(), manager.get_current_time(), current_addon_id_);
246 
247  /* Register callback to the dialog so that the map changes can be
248  * previewed in real time.
249  */
250  std::function<void(std::vector<time_of_day>)> update_func(
251  std::bind(
253  this,
254  std::placeholders::_1));
255  tod_dlg.register_callback(update_func);
256 
257  /* Autogenerate schedule id */
258  static constexpr std::string_view ts_format = "%Y-%m-%d_%H-%M-%S";
259  std::string timestamp = chrono::format_local_timestamp(std::chrono::system_clock::now(), ts_format);
260  std::string sch_id = current_addon_id_+"-schedule";
261  /* Set correct textdomain */
262  t_string sch_name("", "wesnoth-"+current_addon_id_);
263 
264  // TODO : Needs better error handling messages
265  /* Show dialog and update current schedule */
266  if(tod_dlg.show()) {
267  /* Save the new schedule */
268  std::vector<time_of_day> schedule = tod_dlg.get_schedule();
269  if(!gui2::dialogs::tod_new_schedule::execute(sch_id, sch_name)) {
270  /* User pressed Cancel. Restore old schedule */
271  update_map_schedule(prev_schedule);
272  return;
273  }
274 
275  /* In case the ID or Name field is blank and user presses OK */
276  if (sch_id.empty()) {
277  sch_id = current_addon_id_ + "-schedule-" + timestamp;
278  } else {
279  /* Check if the id entered is same as any of the existing ids
280  * If so, replace */
281  // TODO : Notify the user if they enter an already existing schedule ID
282  for (auto map_elem : tods_) {
283  if (sch_id == map_elem.first) {
284  sch_id = current_addon_id_ + "-schedule-" + timestamp;
285  }
286  }
287  }
288 
289  tods_.emplace(sch_id, std::pair(sch_name, schedule));
291  get_current_map_context().save_schedule(sch_id, sch_name);
292  gui_->update_tod();
293  context_manager_->refresh_all();
294  } else {
295  /* Restore old schedule */
296  update_map_schedule(prev_schedule);
297  }
298 }
299 
300 void editor_controller::update_map_schedule(const std::vector<time_of_day>& schedule)
301 {
303  gui_->update_tod();
304  context_manager_->refresh_all();
305 }
306 
308 {
309  using namespace hotkey; //reduce hotkey:: clutter
310  int index = cmd.index;
311  switch(cmd.hotkey_command) {
312  case HOTKEY_NULL:
313  if (index >= 0) {
314  unsigned i = static_cast<unsigned>(index);
315 
316  switch (active_menu_) {
317  case menu_type::map:
318  if (i < context_manager_->open_maps()) {
319  return true;
320  }
321  return false;
322  case menu_type::load_mru:
323  case menu_type::palette:
324  case menu_type::area:
325  case menu_type::addon:
326  case menu_type::side:
327  case menu_type::time:
328  case menu_type::schedule:
330  case menu_type::music:
333  case menu_type::none:
334  return true;
335  }
336  }
337  return false;
339  return true;
341  return toolkit_->get_palette_manager()->can_scroll_up();
343  return toolkit_->get_palette_manager()->can_scroll_down();
344  case HOTKEY_ZOOM_IN:
345  return !gui_->zoom_at_max();
346  case HOTKEY_ZOOM_OUT:
347  return !gui_->zoom_at_min();
348  case HOTKEY_ZOOM_DEFAULT:
349  case HOTKEY_FULLSCREEN:
350  case HOTKEY_SCREENSHOT:
352  case HOTKEY_TOGGLE_GRID:
353  case HOTKEY_MOUSE_SCROLL:
354  case HOTKEY_ANIMATE_MAP:
355  case HOTKEY_MUTE:
356  case HOTKEY_PREFERENCES:
357  case HOTKEY_HELP:
358  case HOTKEY_QUIT_GAME:
359  case HOTKEY_SCROLL_UP:
360  case HOTKEY_SCROLL_DOWN:
361  case HOTKEY_SCROLL_LEFT:
362  case HOTKEY_SCROLL_RIGHT:
363  return true; //general hotkeys we can always do
364 
365  case HOTKEY_UNIT_LIST:
366  return !get_current_map_context().units().empty();
367 
368  // TODO Disabling this for now until the functionality can be implemnted.
369  // See the status_table() method
370  case HOTKEY_STATUS_TABLE:
371  //return !get_current_map_context().teams().empty();
372  return false;
373  /////////////////////////////
374 
376  return gui().mouseover_hex().valid();
377 
378  // unit tool related
379  case HOTKEY_DELETE_UNIT:
380  case HOTKEY_RENAME_UNIT:
387  {
388  map_location loc = gui_->mouseover_hex();
389  const unit_map& units = get_current_map_context().units();
390  return (toolkit_->is_mouse_action_set(HOTKEY_EDITOR_TOOL_UNIT) &&
391  units.find(loc) != units.end());
392  }
393 
394  case HOTKEY_UNDO:
397  case HOTKEY_REDO:
399 
406  return true;
407 
408  // Can be enabled as long as a valid addon_id is set
410  return !current_addon_id_.empty();
411 
412  // Only enable when editing a scenario
415 
416  // Only enable when editing a scenario
420 
421  case HOTKEY_EDITOR_PBL:
425  return true;
426 
430 
433  return !get_current_map_context().teams().empty();
434 
435  // brushes
443 
445  return true;
447  return toolkit_->get_palette_manager()->active_palette().supports_swap();
451  {
452  std::string dummy;
453  return context_manager_->modified_maps(dummy) > 1;
454  }
460  return true;
462  return !get_current_map_context().get_filename().empty()
464 
465  // Tools
466  // Pure map editing tools this can be used all the time.
471  return true;
472  // WWL dependent tools which don't rely on defined sides.
479  return !get_current_map_context().teams().empty();
480 
484  return !get_current_map_context().is_pure_map() &&
486 
488  return !get_current_map_context().is_pure_map() &&
490  && !get_current_map_context().map().selection().empty();
491 
496  return !get_current_map_context().map().selection().empty()
497  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
499  return (get_current_map_context().map().selection().size() > 1
500  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE));
504  return !context_manager_->clipboard_empty();
509  return !context_manager_->clipboard_empty()
510  && toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
513  return !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
515  return !get_current_map_context().map().selection().empty()
517  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
535  return true;
539  return true;
540  default:
541  return false;
542  }
543 }
544 
546 {
547  using namespace hotkey;
548  int index = cmd.index;
549  switch (cmd.hotkey_command) {
550 
552  {
554  get_current_map_context().units().find(gui_->mouseover_hex());
555  return hotkey::on_if(un->loyal());
556  }
558  {
560  get_current_map_context().units().find(gui_->mouseover_hex());
561  return hotkey::on_if(un->can_recruit());
562  }
564  {
566  get_current_map_context().units().find(gui_->mouseover_hex());
567  return hotkey::on_if(!un->unrenamable());
568  }
569  //TODO remove hardcoded hotkey names
571  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-auto-update-transitions"));
573  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-partial-update-transitions"));
575  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-no-update-transitions"));
577  return hotkey::on_if(toolkit_->is_active_brush("brush-1"));
579  return hotkey::on_if(toolkit_->is_active_brush("brush-2"));
581  return hotkey::on_if(toolkit_->is_active_brush("brush-3"));
583  return hotkey::on_if(toolkit_->is_active_brush("brush-nw-se"));
585  return hotkey::on_if(toolkit_->is_active_brush("brush-sw-ne"));
586 
587  case HOTKEY_TOGGLE_GRID:
588  return hotkey::on_if(prefs::get().grid());
590  return hotkey::selected_if(get_current_map_context().map().everything_selected());
592  return hotkey::selected_if(get_current_map_context().map().selection().empty());
602  return hotkey::on_if(toolkit_->is_mouse_action_set(cmd.hotkey_command));
604  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_COORDINATES));
606  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_TERRAIN_CODES));
608  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_NUM_BITMAPS));
610  return hotkey::on_if(gui_->help_string_enabled());
612  return hotkey::on_if(prefs::get().minimap_draw_villages());
614  return hotkey::on_if(prefs::get().minimap_movement_coding());
616  return hotkey::on_if(prefs::get().minimap_terrain_coding());
618  return hotkey::on_if(prefs::get().minimap_draw_units());
620  return hotkey::on_if(prefs::get().minimap_draw_terrain());
621  case HOTKEY_ZOOM_DEFAULT:
622  return hotkey::on_if(gui_->get_zoom_factor() == 1.0);
623 
624  case HOTKEY_NULL:
625  switch (active_menu_) {
626  case menu_type::map:
627  return hotkey::selected_if(index == context_manager_->current_context_index());
628  case menu_type::load_mru:
630  case menu_type::palette:
632  case menu_type::area:
633  return hotkey::selected_if(index == get_current_map_context().get_active_area());
634  case menu_type::addon:
636  case menu_type::side:
637  return hotkey::selected_if(static_cast<std::size_t>(index) == gui_->playing_team_index());
638  case menu_type::time:
639  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_time());
641  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_area_time(
642  get_current_map_context().get_active_area()));
643  case menu_type::music:
644  return hotkey::on_if(get_current_map_context().is_in_playlist(music_tracks_[index].id()));
645  case menu_type::schedule:
646  {
647  tods_map::const_iterator it = tods_.begin();
648  std::advance(it, index);
649  const std::vector<time_of_day>& times1 = it->second.second;
650  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times();
651  return hotkey::selected_if(times1 == times2);
652  }
654  {
655  tods_map::const_iterator it = tods_.begin();
656  std::advance(it, index);
657  const std::vector<time_of_day>& times1 = it->second.second;
658  int active_area = get_current_map_context().get_active_area();
659  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times(active_area);
660  return hotkey::selected_if(times1 == times2);
661  }
663  {
665  assert(un != get_current_map_context().units().end());
666  return hotkey::selected_if(un->facing() == map_location::direction{index});
667  }
668  case menu_type::none:
670  }
672  default:
673  return command_executor::get_action_state(cmd);
674  }
675 }
676 
677 bool editor_controller::do_execute_command(const hotkey::ui_command& cmd, bool press, bool release)
678 {
679  using namespace hotkey;
680  HOTKEY_COMMAND command = cmd.hotkey_command;
681  SCOPE_ED;
682  int index = cmd.index;
683 
684  // nothing here handles release; fall through to base implementation
685  if (!press) {
686  return command_executor::do_execute_command(cmd, press, release);
687  }
688 
689  switch (command) {
690  case HOTKEY_NULL:
691  switch (active_menu_) {
692  case menu_type::map:
693  if (index >= 0) {
694  unsigned i = static_cast<unsigned>(index);
695  if (i < context_manager_->size()) {
696  context_manager_->switch_context(index);
697  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
698  return true;
699  }
700  }
701  return false;
702  case menu_type::load_mru:
703  if (index >= 0) {
704  context_manager_->load_mru_item(static_cast<unsigned>(index));
705  }
706  return true;
707  case menu_type::palette:
708  toolkit_->get_palette_manager()->set_group(index);
709  return true;
710  case menu_type::side:
711  gui_->set_viewing_team_index(index, true);
712  gui_->set_playing_team_index(index);
713  toolkit_->get_palette_manager()->draw_contents();
714  return true;
715  case menu_type::area:
716  {
718  const std::set<map_location>& area =
721  gui_->scroll_to_tiles({ area.begin(), area.end() });
722  return true;
723  }
724  case menu_type::addon:
725  return true;
726  case menu_type::time:
727  {
729  gui_->update_tod();
730  return true;
731  }
733  {
735  return true;
736  }
737  case menu_type::music:
738  {
739  //TODO mark the map as changed
742  return true;
743  }
744  case menu_type::schedule:
745  {
746  tods_map::iterator iter = tods_.begin();
747  std::advance(iter, index);
748  get_current_map_context().replace_schedule(iter->second.second);
749  // TODO: test again after the assign-schedule menu is fixed. Should work, though.
750  gui_->update_tod();
751  return true;
752  }
754  {
755  tods_map::iterator iter = tods_.begin();
756  std::advance(iter, index);
757  get_current_map_context().replace_local_schedule(iter->second.second);
758  return true;
759  }
761  {
763  assert(un != get_current_map_context().units().end());
764  un->set_facing(map_location::direction(index));
765  un->anim_comp().set_standing();
766  return true;
767  }
768  case menu_type::none:
769  return true;
770  }
771  return true;
772 
773  //Zoom
774  case HOTKEY_ZOOM_IN:
775  gui_->set_zoom(true);
777  toolkit_->set_mouseover_overlay(*gui_);
778  return true;
779  case HOTKEY_ZOOM_OUT:
780  gui_->set_zoom(false);
782  toolkit_->set_mouseover_overlay(*gui_);
783  return true;
784  case HOTKEY_ZOOM_DEFAULT:
785  gui_->toggle_default_zoom();
787  toolkit_->set_mouseover_overlay(*gui_);
788  return true;
789 
790  //Palette
792  //TODO this code waits for the gui2 dialog to get ready
793 // std::vector< std::pair< std::string, std::string >> blah_items;
794 // toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(blah_items);
795 // int selected = 1; //toolkit_->get_palette_manager()->active_palette().get_selected;
796 // gui2::teditor_select_palette_group::execute(selected, blah_items);
797  return true;
799  toolkit_->get_palette_manager()->scroll_up();
800  return true;
802  toolkit_->get_palette_manager()->scroll_down();
803  return true;
804 
805  case HOTKEY_QUIT_GAME:
807  do_quit_ = true;
809  }
810  return true;
813  return true;
815  context_manager_->save_contexts();
816  do_quit_ = true;
818  return true;
821  return true;
824  return true;
826  toolkit_->get_palette_manager()->active_palette().swap();
827  return true;
829  if (dynamic_cast<const editor_action_chain*>(get_current_map_context().last_undo_action()) != nullptr) {
831  context_manager_->refresh_after_action();
832  } else {
833  undo();
834  }
835  return true;
836 
837  //Tool Selection
846  toolkit_->hotkey_set_mouse_action(command);
847  return true;
848 
849  case HOTKEY_EDITOR_PBL:
850  if(initialize_addon()) {
851  context_manager_->edit_pbl();
852  }
853  return true;
854 
856  if(initialize_addon()) {
857  context_manager_->change_addon_id();
858  }
859  return true;
860 
863  return true;
864 
866  {
867  if (!initialize_addon()) {
868  gui2::show_error_message("Could not initialize add-on!");
869  return true;
870  }
871 
873 
874  dlg.set_title(_("Add-on Files"))
876 
877  if (dlg.show()) {
878  std::string filepath = dlg.path();
879  if (filesystem::is_map(filepath) || filesystem::is_cfg(filepath)) {
880  // Open map or scenario
881  context_manager_->load_map(filepath, true);
882  } else {
883  // Open file using OS application for that format
885  desktop::open_object(filepath);
886  } else {
887  gui2::show_message("", _("Opening files is not supported, contact your packager"), gui2::dialogs::message::auto_close);
888  }
889  }
890  }
891 
892  return true;
893  }
894 
896  add_area();
897  return true;
898 
900  change_unit_id();
901  return true;
902 
904  {
905  map_location loc = gui_->mouseover_hex();
907  bool unrenamable = un->unrenamable();
908  un->set_unrenamable(!unrenamable);
909  return true;
910  }
912  {
913  map_location loc = gui_->mouseover_hex();
915  bool canrecruit = un->can_recruit();
916  un->set_can_recruit(!canrecruit);
917  un->anim_comp().set_standing();
918  return true;
919  }
921  {
922  map_location loc = gui_->mouseover_hex();
924  bool loyal = un->loyal();
925  un->set_loyal(!loyal);
926  return true;
927  }
928  case HOTKEY_DELETE_UNIT:
929  {
930  map_location loc = gui_->mouseover_hex();
931  perform_delete(std::make_unique<editor_action_unit_delete>(loc));
932  return true;
933  }
934  case HOTKEY_EDITOR_CLIPBOARD_PASTE: //paste is somewhat different as it might be "one action then revert to previous mode"
935  toolkit_->hotkey_set_mouse_action(command);
936  return true;
937 
938  //Clipboard
940  context_manager_->get_clipboard().rotate_60_cw();
941  toolkit_->update_mouse_action_highlights();
942  return true;
944  context_manager_->get_clipboard().rotate_60_ccw();
945  toolkit_->update_mouse_action_highlights();
946  return true;
948  context_manager_->get_clipboard().flip_horizontal();
949  toolkit_->update_mouse_action_highlights();
950  return true;
952  context_manager_->get_clipboard().flip_vertical();
953  toolkit_->update_mouse_action_highlights();
954  return true;
955 
956  //Brushes
958  toolkit_->cycle_brush();
959  return true;
961  toolkit_->set_brush("brush-1");
962  return true;
964  toolkit_->set_brush("brush-2");
965  return true;
967  toolkit_->set_brush("brush-3");
968  return true;
970  toolkit_->set_brush("brush-nw-se");
971  return true;
973  toolkit_->set_brush("brush-sw-ne");
974  return true;
975 
977  copy_selection();
978  return true;
980  cut_selection();
981  return true;
983  context_manager_->rename_area_dialog();
984  return true;
986  save_area();
987  return true;
990  return true;
992  if(!get_current_map_context().map().everything_selected()) {
993  context_manager_->perform_refresh(editor_action_select_all());
994  return true;
995  }
996  [[fallthrough]];
999  return true;
1001  context_manager_->perform_refresh(editor_action_select_none());
1002  return true;
1004  context_manager_->fill_selection();
1005  return true;
1008  get_current_map_context().map().selection()));
1009  return true;
1010 
1012  context_manager_->edit_scenario_dialog();
1013  return true;
1014 
1017  get_current_map_context().get_active_area());
1018  return true;
1019 
1020  // map specific
1022  context_manager_->close_current_context();
1023  // Copy behaviour from when switching windows to always reset the active tool to the Paint Tool
1024  // This avoids the situation of having a scenario-specific tool active in a map context which can cause a crash if used
1025  // Not elegant but at least avoids a potential crash and is consistent with existing behaviour
1026  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
1027  return true;
1029  context_manager_->load_map_dialog();
1030  return true;
1032  context_manager_->revert_map();
1033  return true;
1034  case HOTKEY_EDITOR_MAP_NEW:
1035  context_manager_->new_map_dialog();
1036  return true;
1038  if(initialize_addon()) {
1039  context_manager_->new_scenario_dialog();
1040  }
1041  return true;
1043  save_map();
1044  return true;
1046  context_manager_->save_all_maps();
1047  return true;
1049  context_manager_->save_map_as_dialog();
1050  return true;
1052  if(initialize_addon()) {
1053  context_manager_->map_to_scenario();
1054  }
1055  return true;
1057  if(initialize_addon()) {
1058  context_manager_->save_scenario_as_dialog();
1059  }
1060  return true;
1062  context_manager_->generate_map_dialog();
1063  return true;
1065  context_manager_->apply_mask_dialog();
1066  return true;
1068  context_manager_->create_mask_to_dialog();
1069  return true;
1071  context_manager_->resize_map_dialog();
1072  return true;
1073 
1074  // Side specific ones
1076  if(get_current_map_context().teams().size() >= 9) {
1077  size_t new_side_num = get_current_map_context().teams().size() + 1;
1078  toolkit_->get_palette_manager()->location_palette_->add_item(std::to_string(new_side_num));
1079  }
1081  gui_->init_flags();
1082  return true;
1084  gui_->set_viewing_team_index(0, true);
1085  gui_->set_playing_team_index(0);
1087  return true;
1089  context_manager_->edit_side_dialog(gui_->viewing_team());
1090  return true;
1091 
1092  // Transitions
1094  context_manager_->set_update_transitions_mode(2);
1095  return true;
1097  context_manager_->set_update_transitions_mode(1);
1098  return true;
1100  context_manager_->set_update_transitions_mode(0);
1101  return true;
1103  if(context_manager_->toggle_update_transitions()) {
1104  return true;
1105  }
1106  [[fallthrough]];
1108  context_manager_->refresh_all();
1109  return true;
1110  // Refresh
1111  case HOTKEY_EDITOR_REFRESH:
1112  context_manager_->reload_map();
1113  return true;
1116  return true;
1117 
1120  prefs::get().set_editor_draw_hex_coordinates(gui().debug_flag_set(display::DEBUG_COORDINATES));
1121  gui().invalidate_all();
1122  return true;
1125  prefs::get().set_editor_draw_terrain_codes(gui().debug_flag_set(display::DEBUG_TERRAIN_CODES));
1126  gui().invalidate_all();
1127  return true;
1130  prefs::get().set_editor_draw_num_of_bitmaps(gui().debug_flag_set(display::DEBUG_NUM_BITMAPS));
1131  gui().invalidate_all();
1132  return true;
1134  gui().set_help_string_enabled(!gui().help_string_enabled());
1135  prefs::get().set_editor_help_text_shown(gui().help_string_enabled());
1136  return true;
1138  location_palette* lp = dynamic_cast<location_palette*>(&toolkit_->get_palette_manager()->active_palette());
1139  if (lp) {
1140  perform_delete(std::make_unique<editor_action_starting_position>(map_location(), lp->selected_item()));
1141  // No idea if this is the right thing to call, but it ensures starting
1142  // position labels get removed on delete.
1143  context_manager_->refresh_after_action();
1144  }
1145  return true;
1146  }
1147  default:
1148  return hotkey::command_executor::do_execute_command(cmd, press, release);
1149  }
1150 }
1151 
1153  if(current_addon_id_.empty()) {
1154  // editor::initialize_addon can return empty id in case of failure
1156  }
1157  context_manager_->set_addon_id(current_addon_id_);
1158  return !current_addon_id_.empty();
1159 }
1160 
1162 {
1163  help::show_help("..editor");
1164 }
1165 
1167 {
1168  // Keep the music menu open to allow multiple selections easily
1169  return active_menu_ == menu_type::music;
1170 }
1171 
1172 void editor_controller::show_menu(const std::vector<config>& items_arg, int xloc, int yloc, bool context_menu)
1173 {
1174  // Ensure active_menu_ is only valid within the scope of this function.
1176 
1177  if(context_menu) {
1178  if(!get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(xloc, yloc))) {
1179  return;
1180  }
1181  }
1182 
1183  std::vector<config> items;
1184  for(const auto& c : items_arg) {
1185  const std::string& id = c["id"];
1186  const auto cmd = hotkey::ui_command(id);
1187 
1188  if((can_execute_command(cmd) && (!context_menu || in_context_menu(cmd)))
1189  || cmd.hotkey_command == hotkey::HOTKEY_NULL)
1190  {
1191  items.emplace_back("id", id);
1192  }
1193  }
1194 
1195  // No point in showing an empty menu.
1196  if(items.empty()) {
1197  return;
1198  }
1199 
1200  // Based on the ID of the first entry, we fill the menu contextually.
1201  const std::string& first_id = items.front()["id"];
1202 
1203  // All generated items (might be empty).
1204  std::vector<config> generated;
1205 
1206  if(first_id == "EDITOR-LOAD-MRU-PLACEHOLDER") {
1208  context_manager_->expand_load_mru_menu(generated);
1209  }
1210 
1211  else if(first_id == "editor-switch-map") {
1213  context_manager_->expand_open_maps_menu(generated);
1214  }
1215 
1216  else if(first_id == "editor-palette-groups") {
1218  toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(generated);
1219  }
1220 
1221  else if(first_id == "editor-switch-side") {
1223  context_manager_->expand_sides_menu(generated);
1224  }
1225 
1226  else if(first_id == "editor-switch-area") {
1228  context_manager_->expand_areas_menu(generated);
1229  }
1230 
1231  else if(first_id == "editor-pbl") {
1233  }
1234 
1235  else if(first_id == "editor-switch-time") {
1237  context_manager_->expand_time_menu(generated);
1238  }
1239 
1240  else if(first_id == "editor-assign-local-time") {
1242  context_manager_->expand_local_time_menu(generated);
1243  }
1244 
1245  else if(first_id == "menu-unit-facings") {
1247  auto count = static_cast<int>(map_location::direction::indeterminate);
1248  std::generate_n(std::back_inserter(generated), count, [dir = 0]() mutable {
1250  });
1251  }
1252 
1253  else if(first_id == "editor-playlist") {
1255  std::transform(music_tracks_.begin(), music_tracks_.end(), std::back_inserter(generated),
1256  [](const sound::music_track& track) {
1257  return config{"label", track.title().empty() ? track.id() : track.title()};
1258  });
1259  }
1260 
1261  else if(first_id == "editor-assign-schedule") {
1263  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1264  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1265  }
1266 
1267  else if(first_id == "editor-assign-local-schedule") {
1269  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1270  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1271  }
1272 
1273  else {
1274  // No placeholders, show everything
1275  command_executor::show_menu(items, xloc, yloc, context_menu);
1276  return;
1277  }
1278 
1279  // Splice the lists, excluding placeholder entry
1280  if(items.size() > 1) {
1281  std::move(items.begin() + 1, items.end(), std::back_inserter(generated));
1282  }
1283 
1284  command_executor::show_menu(generated, xloc, yloc, context_menu);
1285 }
1286 
1288 {
1289  gui_->clear_help_string();
1290  gui2::dialogs::preferences_dialog::display();
1291 
1292  gui_->queue_rerender();
1293 }
1294 
1296 {
1297  prefs::get().set_grid(!prefs::get().grid());
1298  gui_->invalidate_all();
1299 }
1300 
1302 {
1303  map_location loc = gui_->mouseover_hex();
1304  const unit_map& units = get_current_map_context().units();
1305  const unit_map::const_unit_iterator un = units.find(loc);
1306  if(un != units.end()) {
1307  help::show_unit_description(un->type());
1308  } else {
1309  help::show_help("..units");
1310  }
1311 }
1312 
1313 
1315 {
1316  if (!get_current_map_context().map().selection().empty()) {
1317  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1318  context_manager_->get_clipboard().center_by_mass();
1319  }
1320 }
1321 
1323 {
1324  map_location loc = gui_->mouseover_hex();
1325  unit_map& units = get_current_map_context().units();
1326  const unit_map::unit_iterator& un = units.find(loc);
1327 
1328  const std::string title(N_("Change Unit ID"));
1329  const std::string label(N_("ID:"));
1330 
1331  if(un != units.end()) {
1332  std::string id = un->id();
1333  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1334  un->set_id(id);
1335  }
1336  }
1337 }
1338 
1340 {
1341  map_location loc = gui_->mouseover_hex();
1342  unit_map& units = get_current_map_context().units();
1343  const unit_map::unit_iterator& un = units.find(loc);
1344 
1345  const std::string title(N_("Rename Unit"));
1346  const std::string label(N_("Name:"));
1347 
1348  if(un != units.end()) {
1349  std::string name = un->name();
1350  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1351  //TODO we may not want a translated name here.
1352  un->set_name(name);
1353  }
1354  }
1355 }
1356 
1358 {
1359  std::vector<unit_const_ptr> unit_list;
1360 
1361  const unit_map& units = gui().context().units();
1362  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1363  if(i->side() != gui().viewing_team().side()) {
1364  continue;
1365  }
1366  unit_list.push_back(i.get_shared_ptr());
1367  }
1368 
1370 
1371  if (unit_dlg->show() && unit_dlg->is_selected()) {
1372  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1374  gui().select_hex(loc);
1375  }
1376 }
1377 
1379 {
1380  copy_selection();
1382 }
1383 
1385 {
1386  const std::set<map_location>& area = get_current_map_context().map().selection();
1388 }
1389 
1391 {
1392  const std::set<map_location>& area = get_current_map_context().map().selection();
1394 }
1395 
1397 {
1398  std::stringstream ssx, ssy;
1399  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1400  if (i != get_current_map_context().map().selection().end()) {
1401  ssx << "x = " << i->wml_x();
1402  ssy << "y = " << i->wml_y();
1403  ++i;
1404  while (i != get_current_map_context().map().selection().end()) {
1405  ssx << ", " << i->wml_x();
1406  ssy << ", " << i->wml_y();
1407  ++i;
1408  }
1409  ssx << "\n" << ssy.str() << "\n";
1411  }
1412 }
1413 
1414 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1415 {
1416  if (action) {
1418  }
1419 }
1420 
1421 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1422 {
1423  if (action) {
1424  context_manager_->perform_refresh(*action, drag_part);
1425  }
1426 }
1427 
1429 {
1431  context_manager_->refresh_all();
1432 }
1433 
1435 {
1436  set_button_state();
1437  toolkit_->adjust_size();
1439 }
1440 
1442 {
1444  context_manager_->refresh_after_action();
1445 }
1446 
1448 {
1450  context_manager_->refresh_after_action();
1451 }
1452 
1453 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1454  bool update, map_location /*new_loc*/)
1455 {
1456  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1457  map_location hex_clicked = gui().hex_clicked_on(x, y);
1458  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1459  std::unique_ptr<editor_action> a;
1460  bool partial = false;
1461  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1462  // mainly (only?) used for printing debugging information.
1463  auto last_undo = get_current_map_context().last_undo_action();
1464  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1465  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1466  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1467  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1468  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1469  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1470  }
1471  //Partial means that the mouse action has modified the
1472  //last undo action and the controller shouldn't add
1473  //anything to the undo stack (hence a different perform_ call)
1474  if (a != nullptr) {
1475  if (partial) {
1477  } else {
1479  }
1480  context_manager_->refresh_after_action(true);
1481  }
1482  } else {
1483  get_mouse_action().move(*gui_, hex_clicked);
1484  }
1485  gui().highlight_hex(hex_clicked);
1486 }
1487 
1488 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1489 {
1490  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1491 }
1492 
1494 {
1495  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1496 }
1497 
1498 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1499 {
1501 }
1502 
1503 bool editor_controller::left_click(int x, int y, const bool browse)
1504 {
1505  toolkit_->clear_mouseover_overlay();
1506  if (mouse_handler_base::left_click(x, y, browse))
1507  return true;
1508 
1509  LOG_ED << "Left click, after generic handling";
1510  map_location hex_clicked = gui().hex_clicked_on(x, y);
1511  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1512  return true;
1513 
1514  LOG_ED << "Left click action " << hex_clicked;
1515  auto a = get_mouse_action().click_left(*gui_, x, y);
1516  if(a) {
1517  perform_refresh_delete(std::move(a), true);
1518  set_button_state();
1519  }
1520 
1521  return false;
1522 }
1523 
1524 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1525 {
1526  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1527  perform_delete(std::move(a));
1528 }
1529 
1530 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1531 {
1532  auto a = get_mouse_action().up_left(*gui_, x, y);
1533  if(a) {
1534  perform_delete(std::move(a));
1535  set_button_state();
1536  }
1537  toolkit_->set_mouseover_overlay();
1538  context_manager_->refresh_after_action();
1539 }
1540 
1541 bool editor_controller::right_click(int x, int y, const bool browse)
1542 {
1543  toolkit_->clear_mouseover_overlay();
1544  if (mouse_handler_base::right_click(x, y, browse)) return true;
1545  LOG_ED << "Right click, after generic handling";
1546  map_location hex_clicked = gui().hex_clicked_on(x, y);
1547  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1548  LOG_ED << "Right click action " << hex_clicked;
1549  auto a = get_mouse_action().click_right(*gui_, x, y);
1550  if(a) {
1551  perform_refresh_delete(std::move(a), true);
1552  set_button_state();
1553  }
1554  return false;
1555 }
1556 
1557 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1558 {
1559  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1560  perform_delete(std::move(a));
1561 }
1562 
1563 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1564 {
1565  // Call base method to handle context menus.
1566  mouse_handler_base::right_mouse_up(x, y, browse);
1567 
1568  auto a = get_mouse_action().up_right(*gui_, x, y);
1569  if(a) {
1570  perform_delete(std::move(a));
1571  set_button_state();
1572  }
1573  toolkit_->set_mouseover_overlay();
1574  context_manager_->refresh_after_action();
1575 }
1576 
1578 {
1579  const map_location& loc = gui().mouseover_hex();
1580  if (get_current_map_context().map().on_board(loc) == false)
1581  return;
1582 
1585 }
1586 
1587 void editor_controller::process_keyup_event(const SDL_Event& event)
1588 {
1589  auto a = get_mouse_action().key_event(gui(), event);
1590  perform_refresh_delete(std::move(a));
1591  toolkit_->set_mouseover_overlay();
1592 }
1593 
1595  return this;
1596 }
1597 
1599 {
1601 }
1602 
1604 {
1606 }
1607 
1609 {
1611 }
1612 
1614 {
1616 }
1617 
1619 {
1620  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1621 }
1622 
1623 } //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_
void scroll_right(bool on) override
void init_music(const game_config_view &game_config)
init background music for the editor
const std::unique_ptr< editor_display > gui_
The display object used and owned by the editor.
bool do_execute_command(const hotkey::ui_command &command, bool press=true, bool release=false) override
command_executor override
void export_selection_coords()
Export the WML-compatible list of selected tiles to the system clipboard.
void redo() override
Redos an action in the current map context.
editor_display & gui() override
Reference to the used display objects.
void show_menu(const std::vector< config > &items_arg, int xloc, int yloc, bool context_menu) override
controller_base override
void perform_delete(std::unique_ptr< editor_action > action)
Perform an action, then delete the action object.
void right_mouse_up(int x, int y, const bool browse) override
Called when the right mouse button is up.
virtual hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
void mouse_motion(int x, int y, const bool browse, bool update, map_location new_loc=map_location::null_location()) override
Called when a mouse motion event takes place.
EXIT_STATUS main_loop()
Editor main loop.
editor_controller(const editor_controller &)=delete
void custom_tods_dialog()
Display the settings dialog, used to control e.g.
void do_screenshot(const std::string &screenshot_filename="map_screenshot.png")
Takes a screenshot.
void toggle_grid() override
Grid toggle.
void scroll_left(bool on) override
bool right_click(int x, int y, const bool browse) override
Overridden in derived classes, called on a right click (mousedown).
void save_area()
Save the current selection to the active area.
void process_keyup_event(const SDL_Event &event) override
Process keyup (always).
void copy_selection()
Copy the selection on the current map to the clipboard.
void init_gui()
init the display object and general set-up
hotkey::action_state get_action_state(const hotkey::ui_command &command) const override
command_executor override
std::unique_ptr< help::help_manager > help_manager_
std::vector< sound::music_track > music_tracks_
bool right_click_show_menu(int x, int y, const bool browse) override
Called in the default right_click when the context menu is about to be shown, can be used for preproc...
static std::string current_addon_id_
void add_area()
Add a new area to the current context, filled with the selection if any.
void scroll_down(bool on) override
void left_drag_end(int x, int y, const bool browse) override
Called whenever the left mouse drag has "ended".
bool do_quit_
Quit main loop flag.
void save_map() override
Save the map, open dialog if not named yet.
void set_help_string_enabled(bool value)
Sets whether the help text should be shown.
bool everything_selected() const
Definition: editor_map.cpp:207
const std::set< map_location > & selection() const
Return the selection set.
Definition: editor_map.hpp:148
List of starting locations and location ids.
const std::string & selected_item() const
Return the currently selected item.
void save_area(const std::set< map_location > &area)
void new_area(const std::set< map_location > &area)
void perform_partial_action(const editor_action &action)
Performs a partial action, assumes that the top undo action has been modified to maintain coherent st...
void remove_side()
removes the last side from the scenario
void set_active_area(int index)
bool modified() const
virtual const unit_map & units() const override
Const units accessor.
void set_local_starting_time(int time)
int get_active_area() const
void new_side()
Adds a new side to the map.
editor_action * last_undo_action()
void redo()
Re-does a previously undid action, and puts it back in the undo stack.
void set_starting_time(int time)
bool select_area(int index)
Select the nth tod area.
bool can_redo() const
bool can_undo() const
void perform_action(const editor_action &action)
Performs an action (thus modifying the map).
void undo()
Un-does the last action, and puts it in the redo stack for a possible redo.
void replace_schedule(const std::vector< time_of_day > &schedule)
void remove_area(int index)
void replace_local_schedule(const std::vector< time_of_day > &schedule)
Replace the [time]s of the currently active area.
const tod_manager * get_time_manager() const
void partial_undo()
Un-does a single step from a undo action chain.
virtual const editor_map & map() const override
Const map accessor.
void add_to_playlist(const sound::music_track &track)
const std::string & get_filename() const
void set_starting_position_labels(display &disp)
map_labels & get_labels()
virtual const std::vector< team > & teams() const override
Const teams accessor.
bool is_pure_map() const
void save_schedule(const std::string &schedule_id, const std::string &schedule_name)
Save custom time of day schedule in the utils directory.
A map fragment – a collection of locations and information abut them.
virtual std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y)=0
A click, possibly the beginning of a drag.
virtual std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y)=0
A click, possibly the beginning of a drag.
virtual std::unique_ptr< editor_action > drag_end_right(editor_display &disp, int x, int y)
virtual bool has_context_menu() const
virtual std::unique_ptr< editor_action > up_left(editor_display &disp, int x, int y)
virtual void move(editor_display &disp, const map_location &hex)
Mouse move (not a drag).
virtual std::unique_ptr< editor_action > up_right(editor_display &disp, int x, int y)
virtual std::unique_ptr< editor_action > key_event(editor_display &disp, const SDL_Event &e)
Function called by the controller on a key event for the current mouse action.
virtual std::unique_ptr< editor_action > drag_left(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Drag operation.
virtual std::unique_ptr< editor_action > drag_right(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Drag operation.
virtual bool supports_brushes() const
Whether we need the brush bar, is used to grey it out.
virtual std::unique_ptr< editor_action > drag_end_left(editor_display &disp, int x, int y)
The end of dragging.
bool dragging_right_
RMB drag init flag.
bool dragging_left_
LMB drag init flag.
map_location drag_from_hex_
Drag start or mouse-down map location.
A class grating read only view to a vector of config objects, viewed as one config with all children ...
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:389
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:98
const std::vector< time_of_day > get_schedule()
Return current schedule.
Definition: custom_tod.cpp: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:245
static prefs & get()
Implements a quit confirmation dialog.
static bool show_prompt(const std::string &message)
static bool quit()
Shows the quit confirmation if needed.
static void quit_to_desktop()
Internal representation of music tracks.
const std::string & file_path() const
const std::string & id() const
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
std::vector< std::string > get_area_ids() const
const std::set< map_location > & get_area_by_index(int index) const
int get_current_time(const map_location &loc=map_location::null_location()) const
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
bool empty() const
Definition: map.hpp:445
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
Editor action classes.
#define LOG_ED
#define ERR_ED
#define SCOPE_ED
#define WRN_ED
std::size_t i
Definition: function.cpp:1032
#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:45
bool valid() const
Definition: location.hpp:110
direction
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:47
static std::string write_translated_direction(direction dir)
Definition: location.cpp:174
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