The Battle for Wesnoth  1.19.17+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_(help::help_manager::get_instance())
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()));
112  context_manager_->locs_ = toolkit_->get_palette_manager()->location_palette_.get();
116 
117  gui().queue_rerender();
118 }
119 
121 {
123  gui().add_redraw_observer(std::bind(&editor_controller::display_redraw_callback, this, std::placeholders::_1));
125  gui().set_debug_flag(display::DEBUG_COORDINATES, prefs::get().editor_draw_hex_coordinates());
126  gui().set_debug_flag(display::DEBUG_TERRAIN_CODES, prefs::get().editor_draw_terrain_codes());
127  gui().set_debug_flag(display::DEBUG_NUM_BITMAPS, prefs::get().editor_draw_num_of_bitmaps());
128  gui().set_help_string_enabled(prefs::get().editor_help_text_shown());
129 // halo_manager_.reset(new halo::manager(*gui_));
130 // resources::halo = halo_manager_.get();
131 // ^ These lines no longer necessary, the gui owns its halo manager.
132 // TODO: Should the editor map contexts actually own the halo manager and swap them in and out from the gui?
133 // 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
134 // without deleting it.
135 }
136 
138 {
139  for (const config &schedule : game_config.child_range("editor_times")) {
140 
141  const std::string& schedule_id = schedule["id"];
142  /* Use schedule id as the name if schedule name is empty */
143  const std::string& schedule_name = schedule["name"].empty() ? schedule["id"] : schedule["name"];
144  if (schedule_id.empty()) {
145  ERR_ED << "Missing ID attribute in a TOD Schedule.";
146  continue;
147  }
148 
149  tods_map::iterator times = tods_.find(schedule_id);
150  if (times == tods_.end()) {
151  std::pair<tods_map::iterator, bool> new_times =
152  tods_.emplace(schedule_id, std::pair(schedule_name, std::vector<time_of_day>()));
153  times = new_times.first;
154  } else {
155  ERR_ED << "Duplicate TOD Schedule identifiers.";
156  continue;
157  }
158 
159  for (const config &time : schedule.child_range("time")) {
160  times->second.second.emplace_back(time);
161  }
162 
163  }
164 
165  if (tods_.empty()) {
166  ERR_ED << "No editor time-of-day defined";
167  }
168 }
169 
171 {
172  resources::tod_manager = nullptr;
173  resources::filter_con = nullptr;
174 
175  resources::classification = nullptr;
176 }
177 
179 {
180  try {
181  while (!do_quit_) {
182  play_slice();
183  }
184  } catch (const editor_exception& e) {
185  gui2::show_transient_message(_("Fatal error"), e.what());
186  return EXIT_ERROR;
187  } catch (const wml_exception& e) {
188  e.show();
189  }
190  return quit_mode_;
191 }
192 
194 }
195 
196 void editor_controller::do_screenshot(const std::string& screenshot_filename /* = "map_screenshot.png" */)
197 {
198  try {
199  surface screenshot = gui().screenshot(true);
200  if(!screenshot || image::save_image(screenshot, screenshot_filename) != image::save_result::success) {
201  ERR_ED << "Screenshot creation failed!";
202  }
203  } catch (const wml_exception& e) {
204  e.show();
205  }
206 }
207 
209 {
210  std::string modified;
211  std::size_t amount = context_manager_->modified_maps(modified);
212 
213  std::string message;
214  if (amount == 0) {
215  message = _("Do you really want to quit?");
216  } else if (amount == 1 && get_current_map_context().modified()) {
217  message = _("Do you really want to quit? Changes to this map since the last save will be lost.");
218  } else {
219  message = _("Do you really want to quit? The following maps were modified and all changes since the last save will be lost:");
220  message += "\n" + modified;
221  }
222  return quit_confirmation::show_prompt(message);
223 }
224 
226 {
228  if (unit_dlg.show()) {
229  unit_dlg.write();
230  }
231 }
232 
234 {
235  if (tods_.empty()) {
236  gui2::show_error_message(_("No editor time-of-day found."));
237  return;
238  }
239 
241  std::vector<time_of_day> prev_schedule = manager.times();
242 
243  gui2::dialogs::custom_tod tod_dlg(manager.times(), manager.get_current_time(), current_addon_id_);
244 
245  /* Register callback to the dialog so that the map changes can be
246  * previewed in real time.
247  */
248  std::function<void(std::vector<time_of_day>)> update_func(
249  std::bind(
251  this,
252  std::placeholders::_1));
253  tod_dlg.register_callback(update_func);
254 
255  /* Autogenerate schedule id */
256  static constexpr std::string_view ts_format = "%Y-%m-%d_%H-%M-%S";
257  std::string timestamp = chrono::format_local_timestamp(std::chrono::system_clock::now(), ts_format);
258  std::string sch_id = current_addon_id_+"-schedule";
259  /* Set correct textdomain */
260  t_string sch_name("", "wesnoth-"+current_addon_id_);
261 
262  // TODO : Needs better error handling messages
263  /* Show dialog and update current schedule */
264  if(tod_dlg.show()) {
265  /* Save the new schedule */
266  std::vector<time_of_day> schedule = tod_dlg.get_schedule();
267  if(!gui2::dialogs::tod_new_schedule::execute(sch_id, sch_name)) {
268  /* User pressed Cancel. Restore old schedule */
269  update_map_schedule(prev_schedule);
270  return;
271  }
272 
273  /* In case the ID or Name field is blank and user presses OK */
274  if (sch_id.empty()) {
275  sch_id = current_addon_id_ + "-schedule-" + timestamp;
276  } else {
277  /* Check if the id entered is same as any of the existing ids
278  * If so, replace */
279  // TODO : Notify the user if they enter an already existing schedule ID
280  for (auto map_elem : tods_) {
281  if (sch_id == map_elem.first) {
282  sch_id = current_addon_id_ + "-schedule-" + timestamp;
283  }
284  }
285  }
286 
287  tods_.emplace(sch_id, std::pair(sch_name, schedule));
289  get_current_map_context().save_schedule(sch_id, sch_name);
290  gui_->update_tod();
291  context_manager_->refresh_all();
292  } else {
293  /* Restore old schedule */
294  update_map_schedule(prev_schedule);
295  }
296 }
297 
298 void editor_controller::update_map_schedule(const std::vector<time_of_day>& schedule)
299 {
301  gui_->update_tod();
302  context_manager_->refresh_all();
303 }
304 
306 {
307  using namespace hotkey; //reduce hotkey:: clutter
308  int index = cmd.index;
309  switch(cmd.hotkey_command) {
310  case HOTKEY_NULL:
311  if (index >= 0) {
312  unsigned i = static_cast<unsigned>(index);
313 
314  switch (active_menu_) {
315  case menu_type::map:
316  if (i < context_manager_->open_maps()) {
317  return true;
318  }
319  return false;
320  case menu_type::load_mru:
321  case menu_type::palette:
322  case menu_type::area:
323  case menu_type::side:
324  case menu_type::time:
325  case menu_type::schedule:
327  case menu_type::music:
330  case menu_type::none:
331  return true;
332  }
333  }
334  return false;
336  return true;
338  return toolkit_->get_palette_manager()->can_scroll_up();
340  return toolkit_->get_palette_manager()->can_scroll_down();
341  case HOTKEY_ZOOM_IN:
342  return !gui_->zoom_at_max();
343  case HOTKEY_ZOOM_OUT:
344  return !gui_->zoom_at_min();
345  case HOTKEY_ZOOM_DEFAULT:
346  case HOTKEY_FULLSCREEN:
347  case HOTKEY_SCREENSHOT:
349  case HOTKEY_TOGGLE_GRID:
350  case HOTKEY_MOUSE_SCROLL:
351  case HOTKEY_ANIMATE_MAP:
352  case HOTKEY_MUTE:
353  case HOTKEY_PREFERENCES:
354  case HOTKEY_HELP:
355  case HOTKEY_QUIT_GAME:
356  case HOTKEY_SCROLL_UP:
357  case HOTKEY_SCROLL_DOWN:
358  case HOTKEY_SCROLL_LEFT:
359  case HOTKEY_SCROLL_RIGHT:
360  return true; //general hotkeys we can always do
361 
362  case HOTKEY_UNIT_LIST:
363  return !get_current_map_context().units().empty();
364 
365  // TODO Disabling this for now until the functionality can be implemnted.
366  // See the status_table() method
367  case HOTKEY_STATUS_TABLE:
368  //return !get_current_map_context().teams().empty();
369  return false;
370  /////////////////////////////
371 
373  return gui().mouseover_hex().valid();
374 
375  // unit tool related
376  case HOTKEY_DELETE_UNIT:
377  case HOTKEY_RENAME_UNIT:
384  {
385  map_location loc = gui_->mouseover_hex();
386  const unit_map& units = get_current_map_context().units();
387  return (toolkit_->is_mouse_action_set(HOTKEY_EDITOR_TOOL_UNIT) &&
388  units.find(loc) != units.end());
389  }
390 
391  case HOTKEY_UNDO:
394  case HOTKEY_REDO:
396 
403  return true;
404 
405  // Can be enabled as long as a valid addon_id is set
407  return !current_addon_id_.empty();
408 
409  // Only enable when editing a scenario
412 
413  // Only enable when editing a scenario
417 
418  case HOTKEY_EDITOR_PBL:
422  return true;
423 
427 
430  return !get_current_map_context().teams().empty();
431 
432  // brushes
440 
442  return true;
444  return toolkit_->get_palette_manager()->active_palette().supports_swap();
448  {
449  std::string dummy;
450  return context_manager_->modified_maps(dummy) > 1;
451  }
457  return true;
459  return !get_current_map_context().get_filename().empty()
461 
462  // Tools
463  // Pure map editing tools this can be used all the time.
468  return true;
469  // WWL dependent tools which don't rely on defined sides.
476  return !get_current_map_context().teams().empty();
477 
481  return !get_current_map_context().is_pure_map() &&
483 
485  return !get_current_map_context().is_pure_map() &&
487  && !get_current_map_context().map().selection().empty();
488 
493  return !get_current_map_context().map().selection().empty()
494  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
496  return (get_current_map_context().map().selection().size() > 1
497  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE));
501  return !context_manager_->clipboard_empty();
506  return !context_manager_->clipboard_empty()
507  && toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
510  return !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
512  return !get_current_map_context().map().selection().empty()
514  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
532  return true;
536  return true;
537  default:
538  return false;
539  }
540 }
541 
543 {
544  using namespace hotkey;
545  int index = cmd.index;
546  switch (cmd.hotkey_command) {
547 
549  {
551  get_current_map_context().units().find(gui_->mouseover_hex());
552  return hotkey::on_if(un->loyal());
553  }
555  {
557  get_current_map_context().units().find(gui_->mouseover_hex());
558  return hotkey::on_if(un->can_recruit());
559  }
561  {
563  get_current_map_context().units().find(gui_->mouseover_hex());
564  return hotkey::on_if(!un->unrenamable());
565  }
566  //TODO remove hardcoded hotkey names
568  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-auto-update-transitions"));
570  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-partial-update-transitions"));
572  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-no-update-transitions"));
574  return hotkey::on_if(toolkit_->is_active_brush("brush-1"));
576  return hotkey::on_if(toolkit_->is_active_brush("brush-2"));
578  return hotkey::on_if(toolkit_->is_active_brush("brush-3"));
580  return hotkey::on_if(toolkit_->is_active_brush("brush-nw-se"));
582  return hotkey::on_if(toolkit_->is_active_brush("brush-sw-ne"));
583 
584  case HOTKEY_TOGGLE_GRID:
585  return hotkey::on_if(prefs::get().grid());
587  return hotkey::selected_if(get_current_map_context().map().everything_selected());
589  return hotkey::selected_if(get_current_map_context().map().selection().empty());
599  return hotkey::on_if(toolkit_->is_mouse_action_set(cmd.hotkey_command));
601  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_COORDINATES));
603  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_TERRAIN_CODES));
605  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_NUM_BITMAPS));
607  return hotkey::on_if(gui_->help_string_enabled());
609  return hotkey::on_if(prefs::get().minimap_draw_villages());
611  return hotkey::on_if(prefs::get().minimap_movement_coding());
613  return hotkey::on_if(prefs::get().minimap_terrain_coding());
615  return hotkey::on_if(prefs::get().minimap_draw_units());
617  return hotkey::on_if(prefs::get().minimap_draw_terrain());
618  case HOTKEY_ZOOM_DEFAULT:
619  return hotkey::on_if(gui_->get_zoom_factor() == 1.0);
620 
621  case HOTKEY_NULL:
622  switch (active_menu_) {
623  case menu_type::map:
624  return hotkey::selected_if(index == context_manager_->current_context_index());
625  case menu_type::load_mru:
627  case menu_type::palette:
629  case menu_type::area:
630  return hotkey::selected_if(index == get_current_map_context().get_active_area());
631  case menu_type::side:
632  return hotkey::selected_if(static_cast<std::size_t>(index) == gui_->playing_team_index());
633  case menu_type::time:
634  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_time());
636  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_area_time(
637  get_current_map_context().get_active_area()));
638  case menu_type::music:
639  return hotkey::on_if(get_current_map_context().playlist_contains(music_tracks_[index]));
640  case menu_type::schedule:
641  {
642  tods_map::const_iterator it = tods_.begin();
643  std::advance(it, index);
644  const std::vector<time_of_day>& times1 = it->second.second;
645  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times();
646  return hotkey::selected_if(times1 == times2);
647  }
649  {
650  tods_map::const_iterator it = tods_.begin();
651  std::advance(it, index);
652  const std::vector<time_of_day>& times1 = it->second.second;
653  int active_area = get_current_map_context().get_active_area();
654  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times(active_area);
655  return hotkey::selected_if(times1 == times2);
656  }
658  {
660  assert(un != get_current_map_context().units().end());
661  return hotkey::selected_if(un->facing() == map_location::direction{index});
662  }
663  case menu_type::none:
665  }
667  default:
668  return command_executor::get_action_state(cmd);
669  }
670 }
671 
672 bool editor_controller::do_execute_command(const hotkey::ui_command& cmd, bool press, bool release)
673 {
674  using namespace hotkey;
675  HOTKEY_COMMAND command = cmd.hotkey_command;
676  SCOPE_ED;
677  int index = cmd.index;
678 
679  // nothing here handles release; fall through to base implementation
680  if (!press) {
681  return command_executor::do_execute_command(cmd, press, release);
682  }
683 
684  switch (command) {
685  case HOTKEY_NULL:
686  switch (active_menu_) {
687  case menu_type::map:
688  if (index >= 0) {
689  unsigned i = static_cast<unsigned>(index);
690  if (i < context_manager_->size()) {
691  context_manager_->switch_context(index);
692  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
693  return true;
694  }
695  }
696  return false;
697  case menu_type::load_mru:
698  if (index >= 0) {
699  context_manager_->load_mru_item(static_cast<unsigned>(index));
700  }
701  return true;
702  case menu_type::palette:
703  toolkit_->get_palette_manager()->set_group(index);
704  return true;
705  case menu_type::side:
706  gui_->set_viewing_team_index(index, true);
707  gui_->set_playing_team_index(index);
708  toolkit_->get_palette_manager()->draw_contents();
709  return true;
710  case menu_type::area:
711  {
713  const std::set<map_location>& area =
716  gui_->scroll_to_tiles({ area.begin(), area.end() });
717  return true;
718  }
719  case menu_type::time:
720  {
722  gui_->update_tod();
723  return true;
724  }
726  {
728  return true;
729  }
730  case menu_type::music:
731  {
732  //TODO mark the map as changed
735  return true;
736  }
737  case menu_type::schedule:
738  {
739  tods_map::iterator iter = tods_.begin();
740  std::advance(iter, index);
741  get_current_map_context().replace_schedule(iter->second.second);
742  // TODO: test again after the assign-schedule menu is fixed. Should work, though.
743  gui_->update_tod();
744  return true;
745  }
747  {
748  tods_map::iterator iter = tods_.begin();
749  std::advance(iter, index);
750  get_current_map_context().replace_local_schedule(iter->second.second);
751  return true;
752  }
754  {
756  assert(un != get_current_map_context().units().end());
757  un->set_facing(map_location::direction(index));
758  un->anim_comp().set_standing();
759  return true;
760  }
761  case menu_type::none:
762  return true;
763  }
764  return true;
765 
766  //Zoom
767  case HOTKEY_ZOOM_IN:
768  gui_->set_zoom(true);
770  toolkit_->set_mouseover_overlay(*gui_);
771  return true;
772  case HOTKEY_ZOOM_OUT:
773  gui_->set_zoom(false);
775  toolkit_->set_mouseover_overlay(*gui_);
776  return true;
777  case HOTKEY_ZOOM_DEFAULT:
778  gui_->toggle_default_zoom();
780  toolkit_->set_mouseover_overlay(*gui_);
781  return true;
782 
783  //Palette
785  //TODO this code waits for the gui2 dialog to get ready
786 // std::vector< std::pair< std::string, std::string >> blah_items;
787 // toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(blah_items);
788 // int selected = 1; //toolkit_->get_palette_manager()->active_palette().get_selected;
789 // gui2::teditor_select_palette_group::execute(selected, blah_items);
790  return true;
792  toolkit_->get_palette_manager()->scroll_up();
793  return true;
795  toolkit_->get_palette_manager()->scroll_down();
796  return true;
797 
798  case HOTKEY_QUIT_GAME:
800  do_quit_ = true;
802  }
803  return true;
806  return true;
808  context_manager_->save_contexts();
809  do_quit_ = true;
811  return true;
814  return true;
817  return true;
819  toolkit_->get_palette_manager()->active_palette().swap();
820  return true;
822  if (dynamic_cast<const editor_action_chain*>(get_current_map_context().last_undo_action()) != nullptr) {
824  context_manager_->refresh_after_action();
825  } else {
826  undo();
827  }
828  return true;
829 
830  //Tool Selection
839  toolkit_->hotkey_set_mouse_action(command);
840  return true;
841 
842  case HOTKEY_EDITOR_PBL:
843  if(initialize_addon()) {
844  context_manager_->edit_pbl();
845  }
846  return true;
847 
849  if(initialize_addon()) {
850  context_manager_->change_addon_id();
851  }
852  return true;
853 
855  select_addon();
856  return true;
857 
859  {
860  if (!initialize_addon()) {
861  gui2::show_error_message("Could not initialize add-on!");
862  return true;
863  }
864 
866 
867  dlg.set_title(_("Add-on Files"))
869 
870  if (dlg.show()) {
871  std::string filepath = dlg.path();
872  if (filesystem::is_map(filepath) || filesystem::is_cfg(filepath)) {
873  // Open map or scenario
874  context_manager_->load_map(filepath, true);
875  } else {
876  // Open file using OS application for that format
878  desktop::open_object(filepath);
879  } else {
880  gui2::show_message("", _("Opening files is not supported, contact your packager"), gui2::dialogs::message::auto_close);
881  }
882  }
883  }
884 
885  return true;
886  }
887 
889  add_area();
890  return true;
891 
893  change_unit_id();
894  return true;
895 
897  {
898  map_location loc = gui_->mouseover_hex();
900  bool unrenamable = un->unrenamable();
901  un->set_unrenamable(!unrenamable);
902  return true;
903  }
905  {
906  map_location loc = gui_->mouseover_hex();
908  bool canrecruit = un->can_recruit();
909  un->set_can_recruit(!canrecruit);
910  un->anim_comp().set_standing();
911  return true;
912  }
914  {
915  map_location loc = gui_->mouseover_hex();
917  bool loyal = un->loyal();
918  un->set_loyal(!loyal);
919  return true;
920  }
921  case HOTKEY_DELETE_UNIT:
922  {
923  map_location loc = gui_->mouseover_hex();
924  perform_delete(std::make_unique<editor_action_unit_delete>(loc));
925  return true;
926  }
927  case HOTKEY_EDITOR_CLIPBOARD_PASTE: //paste is somewhat different as it might be "one action then revert to previous mode"
928  toolkit_->hotkey_set_mouse_action(command);
929  return true;
930 
931  //Clipboard
933  context_manager_->get_clipboard().rotate_60_cw();
934  toolkit_->update_mouse_action_highlights();
935  return true;
937  context_manager_->get_clipboard().rotate_60_ccw();
938  toolkit_->update_mouse_action_highlights();
939  return true;
941  context_manager_->get_clipboard().flip_horizontal();
942  toolkit_->update_mouse_action_highlights();
943  return true;
945  context_manager_->get_clipboard().flip_vertical();
946  toolkit_->update_mouse_action_highlights();
947  return true;
948 
949  //Brushes
951  toolkit_->cycle_brush();
952  return true;
954  toolkit_->set_brush("brush-1");
955  return true;
957  toolkit_->set_brush("brush-2");
958  return true;
960  toolkit_->set_brush("brush-3");
961  return true;
963  toolkit_->set_brush("brush-nw-se");
964  return true;
966  toolkit_->set_brush("brush-sw-ne");
967  return true;
968 
970  copy_selection();
971  return true;
973  cut_selection();
974  return true;
976  context_manager_->rename_area_dialog();
977  return true;
979  save_area();
980  return true;
983  return true;
985  if(!get_current_map_context().map().everything_selected()) {
986  context_manager_->perform_refresh(editor_action_select_all());
987  return true;
988  }
989  [[fallthrough]];
992  return true;
994  context_manager_->perform_refresh(editor_action_select_none());
995  return true;
997  context_manager_->fill_selection();
998  return true;
1001  get_current_map_context().map().selection()));
1002  return true;
1003 
1005  context_manager_->edit_scenario_dialog();
1006  return true;
1007 
1010  get_current_map_context().get_active_area());
1011  return true;
1012 
1013  // map specific
1015  context_manager_->close_current_context();
1016  // Copy behaviour from when switching windows to always reset the active tool to the Paint Tool
1017  // This avoids the situation of having a scenario-specific tool active in a map context which can cause a crash if used
1018  // Not elegant but at least avoids a potential crash and is consistent with existing behaviour
1019  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
1020  return true;
1022  context_manager_->load_map_dialog();
1023  return true;
1025  context_manager_->revert_map();
1026  return true;
1027  case HOTKEY_EDITOR_MAP_NEW:
1028  context_manager_->new_map_dialog();
1029  return true;
1031  if(initialize_addon()) {
1032  context_manager_->new_scenario_dialog();
1033  }
1034  return true;
1036  save_map();
1037  return true;
1039  context_manager_->save_all_maps();
1040  return true;
1042  context_manager_->save_map_as_dialog();
1043  return true;
1045  if(initialize_addon()) {
1046  context_manager_->map_to_scenario();
1047  }
1048  return true;
1050  if(initialize_addon()) {
1051  context_manager_->save_scenario_as_dialog();
1052  }
1053  return true;
1055  context_manager_->generate_map_dialog();
1056  return true;
1058  context_manager_->apply_mask_dialog();
1059  return true;
1061  context_manager_->create_mask_to_dialog();
1062  return true;
1064  context_manager_->resize_map_dialog();
1065  return true;
1066 
1067  // Side specific ones
1069  if(get_current_map_context().teams().size() >= 9) {
1070  std::size_t new_side_num = get_current_map_context().teams().size() + 1;
1071  toolkit_->get_palette_manager()->location_palette_->add_item(std::to_string(new_side_num));
1072  }
1074  gui_->init_flags();
1075  return true;
1077  gui_->set_viewing_team_index(0, true);
1078  gui_->set_playing_team_index(0);
1080  return true;
1082  context_manager_->edit_side_dialog(gui_->viewing_team());
1083  return true;
1084 
1085  // Transitions
1087  context_manager_->set_update_transitions_mode(2);
1088  return true;
1090  context_manager_->set_update_transitions_mode(1);
1091  return true;
1093  context_manager_->set_update_transitions_mode(0);
1094  return true;
1096  if(context_manager_->toggle_update_transitions()) {
1097  return true;
1098  }
1099  [[fallthrough]];
1101  context_manager_->refresh_all();
1102  return true;
1103  // Refresh
1104  case HOTKEY_EDITOR_REFRESH:
1105  context_manager_->reload_map();
1106  return true;
1109  return true;
1110 
1113  prefs::get().set_editor_draw_hex_coordinates(gui().debug_flag_set(display::DEBUG_COORDINATES));
1114  gui().invalidate_all();
1115  return true;
1118  prefs::get().set_editor_draw_terrain_codes(gui().debug_flag_set(display::DEBUG_TERRAIN_CODES));
1119  gui().invalidate_all();
1120  return true;
1123  prefs::get().set_editor_draw_num_of_bitmaps(gui().debug_flag_set(display::DEBUG_NUM_BITMAPS));
1124  gui().invalidate_all();
1125  return true;
1127  gui().set_help_string_enabled(!gui().help_string_enabled());
1128  prefs::get().set_editor_help_text_shown(gui().help_string_enabled());
1129  return true;
1131  location_palette* lp = dynamic_cast<location_palette*>(&toolkit_->get_palette_manager()->active_palette());
1132  if (lp) {
1133  perform_delete(std::make_unique<editor_action_starting_position>(map_location(), lp->selected_item()));
1134  // No idea if this is the right thing to call, but it ensures starting
1135  // position labels get removed on delete.
1136  context_manager_->refresh_after_action();
1137  }
1138  return true;
1139  }
1140  default:
1141  return hotkey::command_executor::do_execute_command(cmd, press, release);
1142  }
1143 }
1144 
1146 {
1148  context_manager_->set_addon_id(current_addon_id_);
1149 }
1150 
1152 {
1153  if(current_addon_id_.empty()) {
1154  select_addon();
1155  }
1156 
1157  // current_addon_id_ could be empty if editor::initialize_addon failed
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, const point& menu_loc, bool context_menu)
1173 {
1174  // Ensure active_menu_ is only valid within the scope of this function.
1176 
1177  if(context_menu
1178  && !get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(menu_loc.x, menu_loc.y)))
1179  {
1180  return;
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-switch-time") {
1233  context_manager_->expand_time_menu(generated);
1234  }
1235 
1236  else if(first_id == "editor-assign-local-time") {
1238  context_manager_->expand_local_time_menu(generated);
1239  }
1240 
1241  else if(first_id == "menu-unit-facings") {
1243  auto count = static_cast<int>(map_location::direction::indeterminate);
1244  std::generate_n(std::back_inserter(generated), count, [dir = 0]() mutable {
1246  });
1247  }
1248 
1249  else if(first_id == "editor-playlist") {
1251  std::transform(music_tracks_.begin(), music_tracks_.end(), std::back_inserter(generated),
1252  [](const std::shared_ptr<sound::music_track>& track) {
1253  return config{"label", track->title().empty() ? track->id() : track->title()};
1254  });
1255  }
1256 
1257  else if(first_id == "editor-assign-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 if(first_id == "editor-assign-local-schedule") {
1265  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1266  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1267  }
1268 
1269  else {
1270  // No placeholders, show everything
1271  command_executor::show_menu(items, menu_loc, context_menu);
1272  return;
1273  }
1274 
1275  // Splice the lists, excluding placeholder entry
1276  if(items.size() > 1) {
1277  std::move(items.begin() + 1, items.end(), std::back_inserter(generated));
1278  }
1279 
1280  command_executor::show_menu(generated, menu_loc, context_menu);
1281 }
1282 
1284 {
1285  gui_->clear_help_string();
1286  gui2::dialogs::preferences_dialog::display();
1287 
1288  gui_->queue_rerender();
1289 }
1290 
1292 {
1293  prefs::get().set_grid(!prefs::get().grid());
1294  gui_->invalidate_all();
1295 }
1296 
1298 {
1299  map_location loc = gui_->mouseover_hex();
1300  const unit_map& units = get_current_map_context().units();
1301  const unit_map::const_unit_iterator un = units.find(loc);
1302  if(un != units.end()) {
1303  help::show_unit_description(un->type());
1304  } else {
1305  help::show_help("..units");
1306  }
1307 }
1308 
1309 
1311 {
1312  if (!get_current_map_context().map().selection().empty()) {
1313  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1314  context_manager_->get_clipboard().center_by_mass();
1315  }
1316 }
1317 
1319 {
1320  map_location loc = gui_->mouseover_hex();
1321  unit_map& units = get_current_map_context().units();
1322  const unit_map::unit_iterator& un = units.find(loc);
1323 
1324  const std::string title(N_("Change Unit ID"));
1325  const std::string label(N_("ID:"));
1326 
1327  if(un != units.end()) {
1328  std::string id = un->id();
1329  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1330  un->set_id(id);
1331  }
1332  }
1333 }
1334 
1336 {
1337  map_location loc = gui_->mouseover_hex();
1338  unit_map& units = get_current_map_context().units();
1339  const unit_map::unit_iterator& un = units.find(loc);
1340 
1341  const std::string title(N_("Rename Unit"));
1342  const std::string label(N_("Name:"));
1343 
1344  if(un != units.end()) {
1345  std::string name = un->name();
1346  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1347  //TODO we may not want a translated name here.
1348  un->set_name(name);
1349  }
1350  }
1351 }
1352 
1354 {
1355  std::vector<unit_const_ptr> unit_list;
1356 
1357  const unit_map& units = gui().context().units();
1358  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1359  if(i->side() != gui().viewing_team().side()) {
1360  continue;
1361  }
1362  unit_list.push_back(i.get_shared_ptr());
1363  }
1364 
1366 
1367  if (unit_dlg->show() && unit_dlg->is_selected()) {
1368  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1370  gui().select_hex(loc);
1371  }
1372 }
1373 
1375 {
1376  copy_selection();
1378 }
1379 
1381 {
1382  const std::set<map_location>& area = get_current_map_context().map().selection();
1384 }
1385 
1387 {
1388  const std::set<map_location>& area = get_current_map_context().map().selection();
1390 }
1391 
1393 {
1394  std::stringstream ssx, ssy;
1395  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1396  if (i != get_current_map_context().map().selection().end()) {
1397  ssx << "x = " << i->wml_x();
1398  ssy << "y = " << i->wml_y();
1399  ++i;
1400  while (i != get_current_map_context().map().selection().end()) {
1401  ssx << ", " << i->wml_x();
1402  ssy << ", " << i->wml_y();
1403  ++i;
1404  }
1405  ssx << "\n" << ssy.str() << "\n";
1407  }
1408 }
1409 
1410 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1411 {
1412  if (action) {
1414  }
1415 }
1416 
1417 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1418 {
1419  if (action) {
1420  context_manager_->perform_refresh(*action, drag_part);
1421  }
1422 }
1423 
1425 {
1427  context_manager_->refresh_all();
1428 }
1429 
1431 {
1432  set_button_state();
1433  toolkit_->adjust_size();
1435 }
1436 
1438 {
1440  context_manager_->refresh_after_action();
1441 }
1442 
1444 {
1446  context_manager_->refresh_after_action();
1447 }
1448 
1449 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1450  bool update, map_location /*new_loc*/)
1451 {
1452  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1453  map_location hex_clicked = gui().hex_clicked_on(x, y);
1454  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1455  std::unique_ptr<editor_action> a;
1456  bool partial = false;
1457  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1458  // mainly (only?) used for printing debugging information.
1459  auto last_undo = get_current_map_context().last_undo_action();
1460  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1461  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1462  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1463  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1464  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1465  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1466  }
1467  //Partial means that the mouse action has modified the
1468  //last undo action and the controller shouldn't add
1469  //anything to the undo stack (hence a different perform_ call)
1470  if (a != nullptr) {
1471  if (partial) {
1473  } else {
1475  }
1476  context_manager_->refresh_after_action(true);
1477  }
1478  } else {
1479  get_mouse_action().move(*gui_, hex_clicked);
1480  }
1481  gui().highlight_hex(hex_clicked);
1482 }
1483 
1484 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1485 {
1486  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1487 }
1488 
1490 {
1491  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1492 }
1493 
1494 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1495 {
1497 }
1498 
1499 bool editor_controller::left_click(int x, int y, const bool browse)
1500 {
1501  toolkit_->clear_mouseover_overlay();
1502  if (mouse_handler_base::left_click(x, y, browse))
1503  return true;
1504 
1505  LOG_ED << "Left click, after generic handling";
1506  map_location hex_clicked = gui().hex_clicked_on(x, y);
1507  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1508  return true;
1509 
1510  LOG_ED << "Left click action " << hex_clicked;
1511  auto a = get_mouse_action().click_left(*gui_, x, y);
1512  if(a) {
1513  perform_refresh_delete(std::move(a), true);
1514  set_button_state();
1515  }
1516 
1517  return false;
1518 }
1519 
1520 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1521 {
1522  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1523  perform_delete(std::move(a));
1524 }
1525 
1526 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1527 {
1528  auto a = get_mouse_action().up_left(*gui_, x, y);
1529  if(a) {
1530  perform_delete(std::move(a));
1531  set_button_state();
1532  }
1533  toolkit_->set_mouseover_overlay();
1534  context_manager_->refresh_after_action();
1535 }
1536 
1537 bool editor_controller::right_click(int x, int y, const bool browse)
1538 {
1539  toolkit_->clear_mouseover_overlay();
1540  if (mouse_handler_base::right_click(x, y, browse)) return true;
1541  LOG_ED << "Right click, after generic handling";
1542  map_location hex_clicked = gui().hex_clicked_on(x, y);
1543  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1544  LOG_ED << "Right click action " << hex_clicked;
1545  auto a = get_mouse_action().click_right(*gui_, x, y);
1546  if(a) {
1547  perform_refresh_delete(std::move(a), true);
1548  set_button_state();
1549  }
1550  return false;
1551 }
1552 
1553 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1554 {
1555  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1556  perform_delete(std::move(a));
1557 }
1558 
1559 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1560 {
1561  // Call base method to handle context menus.
1562  mouse_handler_base::right_mouse_up(x, y, browse);
1563 
1564  auto a = get_mouse_action().up_right(*gui_, x, y);
1565  if(a) {
1566  perform_delete(std::move(a));
1567  set_button_state();
1568  }
1569  toolkit_->set_mouseover_overlay();
1570  context_manager_->refresh_after_action();
1571 }
1572 
1574 {
1575  const map_location& loc = gui().mouseover_hex();
1576  if (get_current_map_context().map().on_board(loc) == false)
1577  return;
1578 
1581 }
1582 
1583 void editor_controller::process_keyup_event(const SDL_Event& event)
1584 {
1585  auto a = get_mouse_action().key_event(gui(), event);
1586  perform_refresh_delete(std::move(a));
1587  toolkit_->set_mouseover_overlay();
1588 }
1589 
1591  return this;
1592 }
1593 
1595 {
1597 }
1598 
1600 {
1602 }
1603 
1605 {
1607 }
1608 
1610 {
1612 }
1613 
1615 {
1616  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1617 }
1618 
1619 } // 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:157
child_itors child_range(std::string_view key)
Definition: config.cpp:267
void set_scroll_up(bool on)
void set_scroll_left(bool on)
void set_scroll_right(bool on)
const game_config_view & game_config_
void set_scroll_down(bool on)
virtual void play_slice()
virtual const unit_map & units() const =0
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:88
void toggle_debug_flag(DEBUG_FLAG flag)
Definition: display.hpp:927
void change_display_context(const display_context *dc)
Definition: display.cpp:433
virtual void highlight_hex(map_location hex)
Definition: display.cpp:1379
void add_redraw_observer(const std::function< void(display &)> &f)
Adds a redraw observer, a function object to be called when a full rerender is queued.
Definition: display.cpp:2197
@ DEBUG_COORDINATES
Overlays x,y coords on tiles.
Definition: display.hpp:899
@ DEBUG_NUM_BITMAPS
Overlays number of bitmaps on tiles.
Definition: display.hpp:905
@ DEBUG_TERRAIN_CODES
Overlays terrain codes on tiles.
Definition: display.hpp:902
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:1854
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2953
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition: display.cpp:714
const display_context & context() const
Definition: display.hpp:184
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition: display.cpp:2138
void set_debug_flag(DEBUG_FLAG flag, bool value)
Definition: display.hpp:922
const map_location & mouseover_hex() const
Definition: display.hpp:301
map_location hex_clicked_on(int x, int y) const
given x,y co-ordinates of an onscreen pixel, will return the location of the hex that this pixel corr...
Definition: display.cpp:527
virtual void select_hex(map_location hex)
Definition: display.cpp:1371
Container action wrapping several actions into one.
Definition: action.hpp:88
Paint the same terrain on a number of locations on the map.
Definition: action.hpp:266
Randomize terrain in an area.
Definition: action.hpp:399
The editor_controller class contains the mouse and keyboard event handling routines for the editor.
const std::unique_ptr< context_manager > context_manager_
void preferences() override
Show the preferences dialog.
void refresh_image_cache()
Reload images.
void right_drag_end(int x, int y, const bool browse) override
Called whenever the right mouse drag has "ended".
void unit_editor_dialog()
Show Unit Editor dialog.
bool can_execute_command(const hotkey::ui_command &command) const override
command_executor override
bool quit_confirm()
Show a quit confirmation dialog and returns true if the user pressed 'yes'.
void update_map_schedule(const std::vector< time_of_day > &schedule)
Updates schedule and the map display.
void scroll_up(bool on) override
Handle hotkeys to scroll map.
void cut_selection()
Cut the selection from the current map to the clipboard.
bool keep_menu_open() const override
command_executor override
void display_redraw_callback(display &)
Callback function passed to display to be called on queue_rerender.
std::unique_ptr< font::floating_label_context > floating_label_manager_
menu_type active_menu_
The currently invoked dropdown menu.
void undo() override
Undos an action in the current map context.
void left_mouse_up(int x, int y, const bool browse) override
Called when the left mouse button is up.
bool left_click(int x, int y, const bool browse) override
Overridden in derived classes, called on a left click (mousedown).
bool allow_mouse_wheel_scroll(int x, int y) override
Derived classes can override this to disable mousewheel scrolling under some circumstances,...
void touch_motion(int x, int y, const bool browse, bool update=false, map_location new_loc=map_location::null_location()) override
void select_addon()
Show dialog to select active addon or create a new one.
virtual std::vector< std::string > additional_actions_pressed() override
void perform_refresh_delete(std::unique_ptr< editor_action > action, bool drag_part=false)
Peform an action on the current map_context, then refresh the display and delete the pointer.
void init_tods(const game_config_view &game_config)
init the available time-of-day settings
bool initialize_addon()
Show dialog to select active addon or create a new one if one is not yet initialized.
const mouse_action & get_mouse_action() const
Get the current mouse action.
map_context & get_current_map_context() const
std::unique_ptr< editor_toolkit > toolkit_
std::vector< std::shared_ptr< sound::music_track > > music_tracks_
void scroll_right(bool on) override
const std::unique_ptr< editor_display > gui_
The display object used and owned by the editor.
bool do_execute_command(const hotkey::ui_command &command, bool press=true, bool release=false) override
command_executor override
void export_selection_coords()
Export the WML-compatible list of selected tiles to the system clipboard.
void redo() override
Redos an action in the current map context.
editor_display & gui() override
Reference to the used display objects.
void perform_delete(std::unique_ptr< editor_action > action)
Perform an action, then delete the action object.
void right_mouse_up(int x, int y, const bool browse) override
Called when the right mouse button is up.
virtual hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
void mouse_motion(int x, int y, const bool browse, bool update, map_location new_loc=map_location::null_location()) override
Called when a mouse motion event takes place.
EXIT_STATUS main_loop()
Editor main loop.
editor_controller(const editor_controller &)=delete
void custom_tods_dialog()
Display the settings dialog, used to control e.g.
void do_screenshot(const std::string &screenshot_filename="map_screenshot.png")
Takes a screenshot.
void toggle_grid() override
Grid toggle.
void scroll_left(bool on) override
bool right_click(int x, int y, const bool browse) override
Overridden in derived classes, called on a right click (mousedown).
void save_area()
Save the current selection to the active area.
void process_keyup_event(const SDL_Event &event) override
Process keyup (always).
void copy_selection()
Copy the selection on the current map to the clipboard.
void init_gui()
init the display object and general set-up
hotkey::action_state get_action_state(const hotkey::ui_command &command) const override
command_executor override
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_terrain_description(const terrain_type &t)
Definition: help.cpp:48
void show_unit_description(const unit &u)
Definition: help.cpp:79
void show_help(const std::string &show_topic)
Open the help browser.
Definition: help.cpp:128
Keyboard shortcuts for game actions.
action_state on_if(bool condition)
Returns action_state::on if condition is true, else action_state::off.
action_state selected_if(bool condition)
Returns action_state::selected if condition is true, else action_state::deselected.
@ HOTKEY_EDITOR_TOOL_VILLAGE
@ HOTKEY_MINIMAP_DRAW_VILLAGES
@ HOTKEY_EDITOR_BRUSH_NW_SE
@ HOTKEY_EDITOR_REFRESH
@ HOTKEY_FULLSCREEN
@ HOTKEY_EDITOR_SELECT_NONE
@ HOTKEY_ANIMATE_MAP
@ HOTKEY_SCREENSHOT
@ HOTKEY_EDITOR_CLIPBOARD_ROTATE_CCW
@ HOTKEY_MOUSE_SCROLL
@ HOTKEY_EDITOR_PALETTE_GROUPS
@ HOTKEY_TERRAIN_DESCRIPTION
@ HOTKEY_EDITOR_PALETTE_UPSCROLL
@ HOTKEY_EDITOR_SIDE_REMOVE
@ HOTKEY_EDITOR_BRUSH_NEXT
@ HOTKEY_EDITOR_TOOL_LABEL
@ HOTKEY_EDITOR_CLIPBOARD_ROTATE_CW
@ HOTKEY_EDITOR_BRUSH_3
@ HOTKEY_SCROLL_LEFT
@ HOTKEY_EDITOR_CLIPBOARD_PASTE
@ HOTKEY_EDITOR_PARTIAL_UNDO
@ HOTKEY_EDITOR_UNIT_TOGGLE_CANRECRUIT
@ HOTKEY_EDITOR_PALETTE_ITEM_SWAP
@ HOTKEY_EDITOR_TOOL_PAINT
@ HOTKEY_EDITOR_MAP_CLOSE
@ HOTKEY_EDITOR_MAP_GENERATE
@ HOTKEY_EDITOR_SELECTION_FLIP
@ HOTKEY_EDITOR_SCHEDULE
@ HOTKEY_EDITOR_TOOL_FILL
@ HOTKEY_EDITOR_SCENARIO_SAVE_AS
@ HOTKEY_EDITOR_PLAYLIST
@ HOTKEY_EDITOR_MAP_SAVE_AS
@ HOTKEY_UNIT_DESCRIPTION
@ HOTKEY_SCROLL_RIGHT
@ HOTKEY_EDITOR_SELECT_ALL
@ HOTKEY_EDITOR_DRAW_COORDINATES
@ HOTKEY_EDITOR_TOOL_NEXT
@ HOTKEY_EDITOR_SELECTION_EXPORT
@ HOTKEY_EDITOR_PARTIAL_UPDATE_TRANSITIONS
@ HOTKEY_EDITOR_MAP_CREATE_MASK_TO
@ HOTKEY_TOGGLE_GRID
@ HOTKEY_EDITOR_SELECTION_CUT
@ HOTKEY_EDITOR_UNIT_CHANGE_ID
@ HOTKEY_MINIMAP_DRAW_TERRAIN
@ HOTKEY_EDITOR_DRAW_NUM_OF_BITMAPS
@ HOTKEY_MAP_SCREENSHOT
@ HOTKEY_EDITOR_AREA_ADD
@ HOTKEY_EDITOR_BRUSH_SW_NE
@ HOTKEY_EDITOR_TOOL_UNIT
@ HOTKEY_QUIT_TO_DESKTOP
@ HOTKEY_EDITOR_HELP_TEXT_SHOWN
@ HOTKEY_EDITOR_CHANGE_ADDON_ID
@ HOTKEY_EDITOR_CUSTOM_TODS
@ HOTKEY_EDITOR_REFRESH_IMAGE_CACHE
@ HOTKEY_EDITOR_SELECTION_FILL
@ HOTKEY_EDITOR_CLIPBOARD_FLIP_VERTICAL
@ HOTKEY_EDITOR_MAP_SAVE_ALL
@ HOTKEY_EDITOR_BRUSH_1
@ HOTKEY_EDITOR_SELECTION_ROTATE
@ HOTKEY_EDITOR_MAP_LOAD
@ HOTKEY_RENAME_UNIT
@ HOTKEY_EDITOR_BRUSH_2
@ HOTKEY_EDITOR_TOOL_STARTING_POSITION
@ HOTKEY_MINIMAP_CODING_TERRAIN
@ HOTKEY_EDITOR_NO_UPDATE_TRANSITIONS
@ HOTKEY_EDITOR_SELECTION_COPY
@ HOTKEY_MINIMAP_DRAW_UNITS
@ HOTKEY_EDITOR_PALETTE_DOWNSCROLL
@ HOTKEY_EDITOR_UNIT_TOGGLE_LOYAL
@ HOTKEY_EDITOR_MAP_APPLY_MASK
@ HOTKEY_EDITOR_LOCAL_TIME
@ HOTKEY_EDITOR_UNIT_FACING
@ HOTKEY_PREFERENCES
@ HOTKEY_STATUS_TABLE
@ HOTKEY_DELETE_UNIT
@ HOTKEY_EDITOR_EDIT_UNIT
@ HOTKEY_EDITOR_SIDE_NEW
@ HOTKEY_EDITOR_SELECTION_RANDOMIZE
@ HOTKEY_EDITOR_SCENARIO_NEW
@ HOTKEY_EDITOR_MAP_SWITCH
@ HOTKEY_EDITOR_SELECT_INVERSE
@ HOTKEY_EDITOR_TOGGLE_TRANSITIONS
@ HOTKEY_UNIT_LIST
@ HOTKEY_SCROLL_DOWN
@ HOTKEY_EDITOR_AUTO_UPDATE_TRANSITIONS
@ HOTKEY_ZOOM_DEFAULT
@ HOTKEY_EDITOR_AREA_RENAME
@ HOTKEY_EDITOR_MAP_RESIZE
@ HOTKEY_SCROLL_UP
@ HOTKEY_EDITOR_DRAW_TERRAIN_CODES
@ HOTKEY_EDITOR_MAP_TO_SCENARIO
@ HOTKEY_EDITOR_AREA_REMOVE
@ HOTKEY_QUIT_GAME
@ HOTKEY_EDITOR_UPDATE_TRANSITIONS
@ HOTKEY_EDITOR_TOOL_SELECT
@ HOTKEY_EDITOR_MAP_NEW
@ HOTKEY_EDITOR_MAP_SAVE
@ HOTKEY_EDITOR_AREA_SAVE
@ HOTKEY_EDITOR_REMOVE_LOCATION
@ HOTKEY_EDITOR_SIDE_EDIT
@ HOTKEY_EDITOR_SELECT_ADDON
@ HOTKEY_EDITOR_TOOL_ITEM
@ TITLE_SCREEN__RELOAD_WML
@ HOTKEY_EDITOR_UNIT_TOGGLE_RENAMEABLE
@ HOTKEY_EDITOR_SCENARIO_EDIT
@ HOTKEY_EDITOR_OPEN_ADDON
@ HOTKEY_EDITOR_MAP_REVERT
@ HOTKEY_EDITOR_CLIPBOARD_FLIP_HORIZONTAL
@ HOTKEY_MINIMAP_CODING_UNIT
void flush_cache()
Purges all image caches.
Definition: picture.cpp:210
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:910
int show_menu(lua_State *L)
Displays a popup menu at the current mouse position Best used from a [set_menu_item],...
Definition: lua_gui2.cpp:193
Unit and team statistics.
::tod_manager * tod_manager
Definition: resources.cpp:29
game_classification * classification
Definition: resources.cpp:34
filter_context * filter_con
Definition: resources.cpp:23
uint32_t get_mouse_button_mask()
Returns the current mouse button mask.
Definition: input.cpp:49
void play_music_once(const std::string &file)
Definition: sound.cpp:600
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:81
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
constexpr auto transform
Definition: ranges.hpp:41
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Desktop environment interaction functions.
@ partial
There are still moves and/or attacks possible, but the unit doesn't fit in the "unmoved" status.
#define ON_SCOPE_EXIT(...)
Run some arbitrary code (a lambda) when the current scope exits The lambda body follows this header,...
Definition: scope_exit.hpp:43
structure which will hide all current floating labels, and cause floating labels instantiated after i...
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