The Battle for Wesnoth  1.19.18+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"
51 #include "gettext.hpp"
52 #include "picture.hpp"
53 #include "quit_confirmation.hpp"
54 #include "sdl/input.hpp" // get_mouse_button_mask
55 #include "serialization/chrono.hpp"
56 #include "sound.hpp"
58 #include "units/unit.hpp"
59 #include "utils/scope_exit.hpp"
60 
61 #include <functional>
62 
63 namespace editor
64 {
65 namespace
66 {
67 auto parse_editor_music(const config_array_view& editor_music_range)
68 {
69  std::vector<std::shared_ptr<sound::music_track>> tracks;
70 
71  for(const config& editor_music : editor_music_range) {
72  for(const config& music : editor_music.child_range("music")) {
73  if(auto track = sound::music_track::create(music)) {
74  tracks.push_back(std::move(track));
75  }
76  }
77  }
78 
79  if(tracks.empty()) {
80  ERR_ED << "No editor music defined";
81  }
82 
83  return tracks;
84 }
85 
86 auto parse_map_generators(const config_array_view& multiplayer_tag_range)
87 {
88  std::vector<std::unique_ptr<map_generator>> generators;
89 
90  for(const config& i : multiplayer_tag_range) {
91  if(i["map_generation"].empty() && i["scenario_generation"].empty()) {
92  continue;
93  }
94 
95  if(const auto generator_cfg = i.optional_child("generator")) {
96  generators.emplace_back(create_map_generator(i["map_generation"].str(i["scenario_generation"]), generator_cfg.value()));
97  }
98  }
99 
100  return generators;
101 }
102 
103 } // namespace
104 
105 std::string editor_controller::current_addon_id_ = "";
106 
108  : controller_base()
109  , mouse_handler_base()
110  , quit_confirmation(std::bind(&editor_controller::quit_confirm, this))
111  , active_menu_(menu_type::none)
112  , reports_(new reports())
113  , gui_(new editor_display(*this, *reports_))
114  , tods_()
115  , context_manager_(new context_manager(*gui_.get(), clear_id ? "" : editor_controller::current_addon_id_))
116  , toolkit_(nullptr)
117  , tooltip_manager_()
118  , floating_label_manager_(nullptr)
119  , help_manager_(help::help_manager::get_instance())
120  , do_quit_(false)
121  , quit_mode_(EXIT_ERROR)
122  , music_tracks_(parse_editor_music(game_config_.child_range("editor_music")))
123  , map_generators_(parse_map_generators(game_config_.child_range("multiplayer")))
124 {
125  if(clear_id) {
127  }
128 
129  init_gui();
130  toolkit_.reset(new editor_toolkit(*gui_.get(), game_config_, *context_manager_.get()));
131  context_manager_->locs_ = toolkit_->get_palette_manager()->location_palette_.get();
135 
136  gui().queue_rerender();
137 }
138 
140 {
142  gui().add_redraw_observer(std::bind(&editor_controller::display_redraw_callback, this, std::placeholders::_1));
144  gui().set_debug_flag(display::DEBUG_COORDINATES, prefs::get().editor_draw_hex_coordinates());
145  gui().set_debug_flag(display::DEBUG_TERRAIN_CODES, prefs::get().editor_draw_terrain_codes());
146  gui().set_debug_flag(display::DEBUG_NUM_BITMAPS, prefs::get().editor_draw_num_of_bitmaps());
147  gui().set_help_string_enabled(prefs::get().editor_help_text_shown());
148 // halo_manager_.reset(new halo::manager(*gui_));
149 // resources::halo = halo_manager_.get();
150 // ^ These lines no longer necessary, the gui owns its halo manager.
151 // TODO: Should the editor map contexts actually own the halo manager and swap them in and out from the gui?
152 // 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
153 // without deleting it.
154 }
155 
157 {
158  for (const config &schedule : game_config.child_range("editor_times")) {
159 
160  const std::string& schedule_id = schedule["id"];
161  /* Use schedule id as the name if schedule name is empty */
162  const std::string& schedule_name = schedule["name"].empty() ? schedule["id"] : schedule["name"];
163  if (schedule_id.empty()) {
164  ERR_ED << "Missing ID attribute in a TOD Schedule.";
165  continue;
166  }
167 
168  tods_map::iterator times = tods_.find(schedule_id);
169  if (times == tods_.end()) {
170  std::pair<tods_map::iterator, bool> new_times =
171  tods_.emplace(schedule_id, std::pair(schedule_name, std::vector<time_of_day>()));
172  times = new_times.first;
173  } else {
174  ERR_ED << "Duplicate TOD Schedule identifiers.";
175  continue;
176  }
177 
178  for (const config &time : schedule.child_range("time")) {
179  times->second.second.emplace_back(time);
180  }
181 
182  }
183 
184  if (tods_.empty()) {
185  ERR_ED << "No editor time-of-day defined";
186  }
187 }
188 
190 {
191  resources::tod_manager = nullptr;
192  resources::filter_con = nullptr;
193 
194  resources::classification = nullptr;
195 }
196 
198 {
199  try {
200  while (!do_quit_) {
201  play_slice();
202  }
203  } catch (const editor_exception& e) {
204  gui2::show_transient_message(_("Fatal error"), e.what());
205  return EXIT_ERROR;
206  } catch (const wml_exception& e) {
207  e.show();
208  }
209  return quit_mode_;
210 }
211 
213 }
214 
215 void editor_controller::do_screenshot(const std::string& screenshot_filename /* = "map_screenshot.png" */)
216 {
217  try {
218  surface screenshot = gui().screenshot(true);
219  if(!screenshot || image::save_image(screenshot, screenshot_filename) != image::save_result::success) {
220  ERR_ED << "Screenshot creation failed!";
221  }
222  } catch (const wml_exception& e) {
223  e.show();
224  }
225 }
226 
228 {
229  std::string modified;
230  std::size_t amount = context_manager_->modified_maps(modified);
231 
232  std::string message;
233  if (amount == 0) {
234  message = _("Do you really want to quit?");
235  } else if (amount == 1 && get_current_map_context().modified()) {
236  message = _("Do you really want to quit? Changes to this map since the last save will be lost.");
237  } else {
238  message = _("Do you really want to quit? The following maps were modified and all changes since the last save will be lost:");
239  message += "\n" + modified;
240  }
241  return quit_confirmation::show_prompt(message);
242 }
243 
245 {
247  if (unit_dlg.show()) {
248  unit_dlg.write();
249  }
250 }
251 
253 {
254  if (tods_.empty()) {
255  gui2::show_error_message(_("No editor time-of-day found."));
256  return;
257  }
258 
260  std::vector<time_of_day> prev_schedule = manager.times();
261 
262  gui2::dialogs::custom_tod tod_dlg(manager.times(), manager.get_current_time(), current_addon_id_);
263 
264  /* Register callback to the dialog so that the map changes can be
265  * previewed in real time.
266  */
267  std::function<void(std::vector<time_of_day>)> update_func(
268  std::bind(
270  this,
271  std::placeholders::_1));
272  tod_dlg.register_callback(update_func);
273 
274  /* Autogenerate schedule id */
275  static constexpr std::string_view ts_format = "%Y-%m-%d_%H-%M-%S";
276  std::string timestamp = chrono::format_local_timestamp(std::chrono::system_clock::now(), ts_format);
277  std::string sch_id = current_addon_id_+"-schedule";
278  /* Set correct textdomain */
279  t_string sch_name("", "wesnoth-"+current_addon_id_);
280 
281  // TODO : Needs better error handling messages
282  /* Show dialog and update current schedule */
283  if(tod_dlg.show()) {
284  /* Save the new schedule */
285  std::vector<time_of_day> schedule = tod_dlg.get_schedule();
286  if(!gui2::dialogs::tod_new_schedule::execute(sch_id, sch_name)) {
287  /* User pressed Cancel. Restore old schedule */
288  update_map_schedule(prev_schedule);
289  return;
290  }
291 
292  /* In case the ID or Name field is blank and user presses OK */
293  if (sch_id.empty()) {
294  sch_id = current_addon_id_ + "-schedule-" + timestamp;
295  } else {
296  /* Check if the id entered is same as any of the existing ids
297  * If so, replace */
298  // TODO : Notify the user if they enter an already existing schedule ID
299  for (auto map_elem : tods_) {
300  if (sch_id == map_elem.first) {
301  sch_id = current_addon_id_ + "-schedule-" + timestamp;
302  }
303  }
304  }
305 
306  tods_.emplace(sch_id, std::pair(sch_name, schedule));
308  get_current_map_context().save_schedule(sch_id, sch_name);
309  gui_->update_tod();
310  context_manager_->refresh_all();
311  } else {
312  /* Restore old schedule */
313  update_map_schedule(prev_schedule);
314  }
315 }
316 
317 void editor_controller::update_map_schedule(const std::vector<time_of_day>& schedule)
318 {
320  gui_->update_tod();
321  context_manager_->refresh_all();
322 }
323 
325 {
326  using namespace hotkey; //reduce hotkey:: clutter
327  int index = cmd.index;
328  switch(cmd.hotkey_command) {
329  case HOTKEY_NULL:
330  if (index >= 0) {
331  unsigned i = static_cast<unsigned>(index);
332 
333  switch (active_menu_) {
334  case menu_type::map:
335  if (i < context_manager_->open_maps()) {
336  return true;
337  }
338  return false;
339  case menu_type::load_mru:
340  case menu_type::palette:
341  case menu_type::area:
342  case menu_type::side:
343  case menu_type::time:
344  case menu_type::schedule:
346  case menu_type::music:
349  case menu_type::none:
350  return true;
351  }
352  }
353  return false;
355  return true;
357  return toolkit_->get_palette_manager()->can_scroll_up();
359  return toolkit_->get_palette_manager()->can_scroll_down();
360  case HOTKEY_ZOOM_IN:
361  return !gui_->zoom_at_max();
362  case HOTKEY_ZOOM_OUT:
363  return !gui_->zoom_at_min();
364  case HOTKEY_ZOOM_DEFAULT:
365  case HOTKEY_FULLSCREEN:
366  case HOTKEY_SCREENSHOT:
368  case HOTKEY_TOGGLE_GRID:
369  case HOTKEY_MOUSE_SCROLL:
370  case HOTKEY_ANIMATE_MAP:
371  case HOTKEY_MUTE:
372  case HOTKEY_PREFERENCES:
373  case HOTKEY_HELP:
374  case HOTKEY_QUIT_GAME:
375  case HOTKEY_SCROLL_UP:
376  case HOTKEY_SCROLL_DOWN:
377  case HOTKEY_SCROLL_LEFT:
378  case HOTKEY_SCROLL_RIGHT:
379  return true; //general hotkeys we can always do
380 
381  case HOTKEY_UNIT_LIST:
382  return !get_current_map_context().units().empty();
383 
384  // TODO Disabling this for now until the functionality can be implemnted.
385  // See the status_table() method
386  case HOTKEY_STATUS_TABLE:
387  //return !get_current_map_context().teams().empty();
388  return false;
389  /////////////////////////////
390 
392  return gui().mouseover_hex().valid();
393 
394  // unit tool related
395  case HOTKEY_DELETE_UNIT:
396  case HOTKEY_RENAME_UNIT:
403  {
404  map_location loc = gui_->mouseover_hex();
405  const unit_map& units = get_current_map_context().units();
406  return (toolkit_->is_mouse_action_set(HOTKEY_EDITOR_TOOL_UNIT) &&
407  units.find(loc) != units.end());
408  }
409 
410  case HOTKEY_UNDO:
413  case HOTKEY_REDO:
415 
422  return true;
423 
424  // Can be enabled as long as a valid addon_id is set
426  return !current_addon_id_.empty();
427 
428  // Only enable when editing a scenario
431 
432  // Only enable when editing a scenario
436 
437  case HOTKEY_EDITOR_PBL:
441  return true;
442 
446 
449  return !get_current_map_context().teams().empty();
450 
451  // brushes
459 
461  return true;
463  return toolkit_->get_palette_manager()->active_palette().supports_swap();
467  {
468  std::string dummy;
469  return context_manager_->modified_maps(dummy) > 1;
470  }
476  return true;
478  return !get_current_map_context().get_filename().empty()
480 
481  // Tools
482  // Pure map editing tools this can be used all the time.
487  return true;
488  // WWL dependent tools which don't rely on defined sides.
495  return !get_current_map_context().teams().empty();
496 
500  return !get_current_map_context().is_pure_map() &&
502 
504  return !get_current_map_context().is_pure_map() &&
506  && !get_current_map_context().map().selection().empty();
507 
512  return !get_current_map_context().map().selection().empty()
513  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
515  return (get_current_map_context().map().selection().size() > 1
516  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE));
520  return !context_manager_->clipboard_empty();
525  return !context_manager_->clipboard_empty()
526  && toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
529  return !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
531  return !get_current_map_context().map().selection().empty()
533  && !toolkit_->is_mouse_action_set(HOTKEY_EDITOR_CLIPBOARD_PASTE);
551  return true;
555  return true;
556  default:
557  return false;
558  }
559 }
560 
562 {
563  using namespace hotkey;
564  int index = cmd.index;
565  switch (cmd.hotkey_command) {
566 
568  {
570  get_current_map_context().units().find(gui_->mouseover_hex());
571  return hotkey::on_if(un->loyal());
572  }
574  {
576  get_current_map_context().units().find(gui_->mouseover_hex());
577  return hotkey::on_if(un->can_recruit());
578  }
580  {
582  get_current_map_context().units().find(gui_->mouseover_hex());
583  return hotkey::on_if(!un->unrenamable());
584  }
585  //TODO remove hardcoded hotkey names
587  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-auto-update-transitions"));
589  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-partial-update-transitions"));
591  return hotkey::selected_if(context_manager_->is_active_transitions_hotkey("editor-no-update-transitions"));
593  return hotkey::on_if(toolkit_->is_active_brush("brush-1"));
595  return hotkey::on_if(toolkit_->is_active_brush("brush-2"));
597  return hotkey::on_if(toolkit_->is_active_brush("brush-3"));
599  return hotkey::on_if(toolkit_->is_active_brush("brush-nw-se"));
601  return hotkey::on_if(toolkit_->is_active_brush("brush-sw-ne"));
602 
603  case HOTKEY_TOGGLE_GRID:
604  return hotkey::on_if(prefs::get().grid());
606  return hotkey::selected_if(get_current_map_context().map().everything_selected());
608  return hotkey::selected_if(get_current_map_context().map().selection().empty());
618  return hotkey::on_if(toolkit_->is_mouse_action_set(cmd.hotkey_command));
620  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_COORDINATES));
622  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_TERRAIN_CODES));
624  return hotkey::on_if(gui_->debug_flag_set(display::DEBUG_NUM_BITMAPS));
626  return hotkey::on_if(gui_->help_string_enabled());
628  return hotkey::on_if(prefs::get().minimap_draw_villages());
630  return hotkey::on_if(prefs::get().minimap_movement_coding());
632  return hotkey::on_if(prefs::get().minimap_terrain_coding());
634  return hotkey::on_if(prefs::get().minimap_draw_units());
636  return hotkey::on_if(prefs::get().minimap_draw_terrain());
637  case HOTKEY_ZOOM_DEFAULT:
638  return hotkey::on_if(gui_->get_zoom_factor() == 1.0);
639 
640  case HOTKEY_NULL:
641  switch (active_menu_) {
642  case menu_type::map:
643  return hotkey::selected_if(index == context_manager_->current_context_index());
644  case menu_type::load_mru:
646  case menu_type::palette:
648  case menu_type::area:
649  return hotkey::selected_if(index == get_current_map_context().get_active_area());
650  case menu_type::side:
651  return hotkey::selected_if(static_cast<std::size_t>(index) == gui_->playing_team_index());
652  case menu_type::time:
653  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_time());
655  return hotkey::selected_if(index == get_current_map_context().get_time_manager()->get_current_area_time(
656  get_current_map_context().get_active_area()));
657  case menu_type::music:
658  return hotkey::on_if(get_current_map_context().playlist_contains(music_tracks_[index]));
659  case menu_type::schedule:
660  {
661  tods_map::const_iterator it = tods_.begin();
662  std::advance(it, index);
663  const std::vector<time_of_day>& times1 = it->second.second;
664  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times();
665  return hotkey::selected_if(times1 == times2);
666  }
668  {
669  tods_map::const_iterator it = tods_.begin();
670  std::advance(it, index);
671  const std::vector<time_of_day>& times1 = it->second.second;
672  int active_area = get_current_map_context().get_active_area();
673  const std::vector<time_of_day>& times2 = get_current_map_context().get_time_manager()->times(active_area);
674  return hotkey::selected_if(times1 == times2);
675  }
677  {
679  assert(un != get_current_map_context().units().end());
680  return hotkey::selected_if(un->facing() == map_location::direction{index});
681  }
682  case menu_type::none:
684  }
686  default:
687  return command_executor::get_action_state(cmd);
688  }
689 }
690 
691 bool editor_controller::do_execute_command(const hotkey::ui_command& cmd, bool press, bool release)
692 {
693  using namespace hotkey;
694  HOTKEY_COMMAND command = cmd.hotkey_command;
695  SCOPE_ED;
696  int index = cmd.index;
697 
698  // nothing here handles release; fall through to base implementation
699  if (!press) {
700  return command_executor::do_execute_command(cmd, press, release);
701  }
702 
703  switch (command) {
704  case HOTKEY_NULL:
705  switch (active_menu_) {
706  case menu_type::map:
707  if (index >= 0) {
708  unsigned i = static_cast<unsigned>(index);
709  if (i < context_manager_->size()) {
710  context_manager_->switch_context(index);
711  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
712  return true;
713  }
714  }
715  return false;
716  case menu_type::load_mru:
717  if (index >= 0) {
718  context_manager_->load_mru_item(static_cast<unsigned>(index));
719  }
720  return true;
721  case menu_type::palette:
722  toolkit_->get_palette_manager()->set_group(index);
723  return true;
724  case menu_type::side:
725  gui_->set_viewing_team_index(index, true);
726  gui_->set_playing_team_index(index);
727  toolkit_->get_palette_manager()->draw_contents();
728  return true;
729  case menu_type::area:
730  {
732  const std::set<map_location>& area =
735  gui_->scroll_to_tiles({ area.begin(), area.end() });
736  return true;
737  }
738  case menu_type::time:
739  {
741  gui_->update_tod();
742  return true;
743  }
745  {
747  return true;
748  }
749  case menu_type::music:
750  {
751  //TODO mark the map as changed
754  return true;
755  }
756  case menu_type::schedule:
757  {
758  tods_map::iterator iter = tods_.begin();
759  std::advance(iter, index);
760  get_current_map_context().replace_schedule(iter->second.second);
761  // TODO: test again after the assign-schedule menu is fixed. Should work, though.
762  gui_->update_tod();
763  return true;
764  }
766  {
767  tods_map::iterator iter = tods_.begin();
768  std::advance(iter, index);
769  get_current_map_context().replace_local_schedule(iter->second.second);
770  return true;
771  }
773  {
775  assert(un != get_current_map_context().units().end());
776  un->set_facing(map_location::direction(index));
777  un->anim_comp().set_standing();
778  return true;
779  }
780  case menu_type::none:
781  return true;
782  }
783  return true;
784 
785  //Zoom
786  case HOTKEY_ZOOM_IN:
787  gui_->set_zoom(true);
789  toolkit_->set_mouseover_overlay(*gui_);
790  return true;
791  case HOTKEY_ZOOM_OUT:
792  gui_->set_zoom(false);
794  toolkit_->set_mouseover_overlay(*gui_);
795  return true;
796  case HOTKEY_ZOOM_DEFAULT:
797  gui_->toggle_default_zoom();
799  toolkit_->set_mouseover_overlay(*gui_);
800  return true;
801 
802  //Palette
804  //TODO this code waits for the gui2 dialog to get ready
805 // std::vector< std::pair< std::string, std::string >> blah_items;
806 // toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(blah_items);
807 // int selected = 1; //toolkit_->get_palette_manager()->active_palette().get_selected;
808 // gui2::teditor_select_palette_group::execute(selected, blah_items);
809  return true;
811  toolkit_->get_palette_manager()->scroll_up();
812  return true;
814  toolkit_->get_palette_manager()->scroll_down();
815  return true;
816 
817  case HOTKEY_QUIT_GAME:
819  do_quit_ = true;
821  }
822  return true;
825  return true;
827  context_manager_->save_contexts();
828  do_quit_ = true;
830  return true;
833  return true;
836  return true;
838  toolkit_->get_palette_manager()->active_palette().swap();
839  return true;
841  if (dynamic_cast<const editor_action_chain*>(get_current_map_context().last_undo_action()) != nullptr) {
843  context_manager_->refresh_after_action();
844  } else {
845  undo();
846  }
847  return true;
848 
849  //Tool Selection
858  toolkit_->hotkey_set_mouse_action(command);
859  return true;
860 
861  case HOTKEY_EDITOR_PBL:
862  if(initialize_addon()) {
863  context_manager_->edit_pbl();
864  }
865  return true;
866 
868  if(initialize_addon()) {
869  context_manager_->change_addon_id();
870  }
871  return true;
872 
874  select_addon();
875  return true;
876 
878  {
879  if (!initialize_addon()) {
880  gui2::show_error_message("Could not initialize add-on!");
881  return true;
882  }
883 
885 
886  dlg.set_title(_("Add-on Files"))
888 
889  if (dlg.show()) {
890  std::string filepath = dlg.path();
891  if (filesystem::is_map(filepath) || filesystem::is_cfg(filepath)) {
892  // Open map or scenario
893  context_manager_->load_map(filepath, true);
894  } else {
895  // Open file using OS application for that format
897  desktop::open_object(filepath);
898  } else {
899  gui2::show_message("", _("Opening files is not supported, contact your packager"), gui2::dialogs::message::auto_close);
900  }
901  }
902  }
903 
904  return true;
905  }
906 
908  add_area();
909  return true;
910 
912  change_unit_id();
913  return true;
914 
916  {
917  map_location loc = gui_->mouseover_hex();
919  bool unrenamable = un->unrenamable();
920  un->set_unrenamable(!unrenamable);
921  return true;
922  }
924  {
925  map_location loc = gui_->mouseover_hex();
927  bool canrecruit = un->can_recruit();
928  un->set_can_recruit(!canrecruit);
929  un->anim_comp().set_standing();
930  return true;
931  }
933  {
934  map_location loc = gui_->mouseover_hex();
936  bool loyal = un->loyal();
937  un->set_loyal(!loyal);
938  return true;
939  }
940  case HOTKEY_DELETE_UNIT:
941  {
942  map_location loc = gui_->mouseover_hex();
943  perform_delete(std::make_unique<editor_action_unit_delete>(loc));
944  return true;
945  }
946  case HOTKEY_EDITOR_CLIPBOARD_PASTE: //paste is somewhat different as it might be "one action then revert to previous mode"
947  toolkit_->hotkey_set_mouse_action(command);
948  return true;
949 
950  //Clipboard
952  context_manager_->get_clipboard().rotate_60_cw();
953  toolkit_->update_mouse_action_highlights();
954  return true;
956  context_manager_->get_clipboard().rotate_60_ccw();
957  toolkit_->update_mouse_action_highlights();
958  return true;
960  context_manager_->get_clipboard().flip_horizontal();
961  toolkit_->update_mouse_action_highlights();
962  return true;
964  context_manager_->get_clipboard().flip_vertical();
965  toolkit_->update_mouse_action_highlights();
966  return true;
967 
968  //Brushes
970  toolkit_->cycle_brush();
971  return true;
973  toolkit_->set_brush("brush-1");
974  return true;
976  toolkit_->set_brush("brush-2");
977  return true;
979  toolkit_->set_brush("brush-3");
980  return true;
982  toolkit_->set_brush("brush-nw-se");
983  return true;
985  toolkit_->set_brush("brush-sw-ne");
986  return true;
987 
989  copy_selection();
990  return true;
992  cut_selection();
993  return true;
995  context_manager_->rename_area_dialog();
996  return true;
998  save_area();
999  return true;
1002  return true;
1004  if(!get_current_map_context().map().everything_selected()) {
1005  context_manager_->perform_refresh(editor_action_select_all());
1006  return true;
1007  }
1008  [[fallthrough]];
1010  context_manager_->perform_refresh(editor_action_select_inverse());
1011  return true;
1013  context_manager_->perform_refresh(editor_action_select_none());
1014  return true;
1016  context_manager_->fill_selection();
1017  return true;
1020  get_current_map_context().map().selection()));
1021  return true;
1022 
1024  context_manager_->edit_scenario_dialog();
1025  return true;
1026 
1029  get_current_map_context().get_active_area());
1030  return true;
1031 
1032  // map specific
1034  context_manager_->close_current_context();
1035  // Copy behaviour from when switching windows to always reset the active tool to the Paint Tool
1036  // This avoids the situation of having a scenario-specific tool active in a map context which can cause a crash if used
1037  // Not elegant but at least avoids a potential crash and is consistent with existing behaviour
1038  toolkit_->hotkey_set_mouse_action(HOTKEY_EDITOR_TOOL_PAINT);
1039  return true;
1041  context_manager_->load_map_dialog();
1042  return true;
1044  context_manager_->revert_map();
1045  return true;
1046  case HOTKEY_EDITOR_MAP_NEW:
1047  context_manager_->new_map_dialog();
1048  return true;
1050  if(initialize_addon()) {
1051  context_manager_->new_scenario_dialog();
1052  }
1053  return true;
1055  save_map();
1056  return true;
1058  context_manager_->save_all_maps();
1059  return true;
1061  context_manager_->save_map_as_dialog();
1062  return true;
1064  if(initialize_addon()) {
1065  context_manager_->map_to_scenario();
1066  }
1067  return true;
1069  if(initialize_addon()) {
1070  context_manager_->save_scenario_as_dialog();
1071  }
1072  return true;
1074  context_manager_->generate_map_dialog(map_generators_);
1075  return true;
1077  context_manager_->apply_mask_dialog();
1078  return true;
1080  context_manager_->create_mask_to_dialog();
1081  return true;
1083  context_manager_->resize_map_dialog();
1084  return true;
1085 
1086  // Side specific ones
1088  if(get_current_map_context().teams().size() >= 9) {
1089  std::size_t new_side_num = get_current_map_context().teams().size() + 1;
1090  toolkit_->get_palette_manager()->location_palette_->add_item(std::to_string(new_side_num));
1091  }
1093  gui_->init_flags();
1094  return true;
1096  gui_->set_viewing_team_index(0, true);
1097  gui_->set_playing_team_index(0);
1099  return true;
1101  context_manager_->edit_side_dialog(gui_->viewing_team());
1102  return true;
1103 
1104  // Transitions
1106  context_manager_->set_update_transitions_mode(2);
1107  return true;
1109  context_manager_->set_update_transitions_mode(1);
1110  return true;
1112  context_manager_->set_update_transitions_mode(0);
1113  return true;
1115  if(context_manager_->toggle_update_transitions()) {
1116  return true;
1117  }
1118  [[fallthrough]];
1120  context_manager_->refresh_all();
1121  return true;
1122  // Refresh
1123  case HOTKEY_EDITOR_REFRESH:
1124  context_manager_->reload_map();
1125  return true;
1128  return true;
1129 
1132  prefs::get().set_editor_draw_hex_coordinates(gui().debug_flag_set(display::DEBUG_COORDINATES));
1133  gui().invalidate_all();
1134  return true;
1137  prefs::get().set_editor_draw_terrain_codes(gui().debug_flag_set(display::DEBUG_TERRAIN_CODES));
1138  gui().invalidate_all();
1139  return true;
1142  prefs::get().set_editor_draw_num_of_bitmaps(gui().debug_flag_set(display::DEBUG_NUM_BITMAPS));
1143  gui().invalidate_all();
1144  return true;
1146  gui().set_help_string_enabled(!gui().help_string_enabled());
1147  prefs::get().set_editor_help_text_shown(gui().help_string_enabled());
1148  return true;
1150  location_palette* lp = dynamic_cast<location_palette*>(&toolkit_->get_palette_manager()->active_palette());
1151  if (lp) {
1152  perform_delete(std::make_unique<editor_action_starting_position>(map_location(), lp->selected_item()));
1153  // No idea if this is the right thing to call, but it ensures starting
1154  // position labels get removed on delete.
1155  context_manager_->refresh_after_action();
1156  }
1157  return true;
1158  }
1159  default:
1160  return hotkey::command_executor::do_execute_command(cmd, press, release);
1161  }
1162 }
1163 
1165 {
1167  context_manager_->set_addon_id(current_addon_id_);
1168 }
1169 
1171 {
1172  if(current_addon_id_.empty()) {
1173  select_addon();
1174  }
1175 
1176  // current_addon_id_ could be empty if editor::initialize_addon failed
1177  return !current_addon_id_.empty();
1178 }
1179 
1181 {
1182  help::show_help("..editor");
1183 }
1184 
1186 {
1187  // Keep the music menu open to allow multiple selections easily
1188  return active_menu_ == menu_type::music;
1189 }
1190 
1191 void editor_controller::show_menu(const std::vector<config>& items_arg, const point& menu_loc, bool context_menu)
1192 {
1193  // Ensure active_menu_ is only valid within the scope of this function.
1195 
1196  if(context_menu
1197  && !get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(menu_loc.x, menu_loc.y)))
1198  {
1199  return;
1200  }
1201 
1202  std::vector<config> items;
1203  for(const auto& c : items_arg) {
1204  const std::string& id = c["id"];
1205  const auto cmd = hotkey::ui_command(id);
1206 
1207  if((can_execute_command(cmd) && (!context_menu || in_context_menu(cmd)))
1208  || cmd.hotkey_command == hotkey::HOTKEY_NULL)
1209  {
1210  items.emplace_back("id", id);
1211  }
1212  }
1213 
1214  // No point in showing an empty menu.
1215  if(items.empty()) {
1216  return;
1217  }
1218 
1219  // Based on the ID of the first entry, we fill the menu contextually.
1220  const std::string& first_id = items.front()["id"];
1221 
1222  // All generated items (might be empty).
1223  std::vector<config> generated;
1224 
1225  if(first_id == "EDITOR-LOAD-MRU-PLACEHOLDER") {
1227  context_manager_->expand_load_mru_menu(generated);
1228  }
1229 
1230  else if(first_id == "editor-switch-map") {
1232  context_manager_->expand_open_maps_menu(generated);
1233  }
1234 
1235  else if(first_id == "editor-palette-groups") {
1237  toolkit_->get_palette_manager()->active_palette().expand_palette_groups_menu(generated);
1238  }
1239 
1240  else if(first_id == "editor-switch-side") {
1242  context_manager_->expand_sides_menu(generated);
1243  }
1244 
1245  else if(first_id == "editor-switch-area") {
1247  context_manager_->expand_areas_menu(generated);
1248  }
1249 
1250  else if(first_id == "editor-switch-time") {
1252  context_manager_->expand_time_menu(generated);
1253  }
1254 
1255  else if(first_id == "editor-assign-local-time") {
1257  context_manager_->expand_local_time_menu(generated);
1258  }
1259 
1260  else if(first_id == "menu-unit-facings") {
1262  auto count = static_cast<int>(map_location::direction::indeterminate);
1263  std::generate_n(std::back_inserter(generated), count, [dir = 0]() mutable {
1265  });
1266  }
1267 
1268  else if(first_id == "editor-playlist") {
1270  std::transform(music_tracks_.begin(), music_tracks_.end(), std::back_inserter(generated),
1271  [](const std::shared_ptr<sound::music_track>& track) {
1272  return config{"label", track->title().empty() ? track->id() : track->title()};
1273  });
1274  }
1275 
1276  else if(first_id == "editor-assign-schedule") {
1278  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1279  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1280  }
1281 
1282  else if(first_id == "editor-assign-local-schedule") {
1284  std::transform(tods_.begin(), tods_.end(), std::back_inserter(generated),
1285  [](const tods_map::value_type& tod) { return config{"label", tod.second.first}; });
1286  }
1287 
1288  else {
1289  // No placeholders, show everything
1290  command_executor::show_menu(items, menu_loc, context_menu);
1291  return;
1292  }
1293 
1294  // Splice the lists, excluding placeholder entry
1295  if(items.size() > 1) {
1296  std::move(items.begin() + 1, items.end(), std::back_inserter(generated));
1297  }
1298 
1299  command_executor::show_menu(generated, menu_loc, context_menu);
1300 }
1301 
1303 {
1304  gui_->clear_help_string();
1305  gui2::dialogs::preferences_dialog::display();
1306 
1307  gui_->queue_rerender();
1308 }
1309 
1311 {
1312  prefs::get().set_grid(!prefs::get().grid());
1313  gui_->invalidate_all();
1314 }
1315 
1317 {
1318  map_location loc = gui_->mouseover_hex();
1319  const unit_map& units = get_current_map_context().units();
1320  const unit_map::const_unit_iterator un = units.find(loc);
1321  if(un != units.end()) {
1322  help::show_unit_description(un->type());
1323  } else {
1324  help::show_help("..units");
1325  }
1326 }
1327 
1328 
1330 {
1331  if (!get_current_map_context().map().selection().empty()) {
1332  context_manager_->get_clipboard() = map_fragment(get_current_map_context().map(), get_current_map_context().map().selection());
1333  context_manager_->get_clipboard().center_by_mass();
1334  }
1335 }
1336 
1338 {
1339  map_location loc = gui_->mouseover_hex();
1340  unit_map& units = get_current_map_context().units();
1341  const unit_map::unit_iterator& un = units.find(loc);
1342 
1343  const std::string title(N_("Change Unit ID"));
1344  const std::string label(N_("ID:"));
1345 
1346  if(un != units.end()) {
1347  std::string id = un->id();
1348  if (gui2::dialogs::edit_text::execute(title, label, id)) {
1349  un->set_id(id);
1350  }
1351  }
1352 }
1353 
1355 {
1356  map_location loc = gui_->mouseover_hex();
1357  unit_map& units = get_current_map_context().units();
1358  const unit_map::unit_iterator& un = units.find(loc);
1359 
1360  const std::string title(N_("Rename Unit"));
1361  const std::string label(N_("Name:"));
1362 
1363  if(un != units.end()) {
1364  std::string name = un->name();
1365  if(gui2::dialogs::edit_text::execute(title, label, name)) {
1366  //TODO we may not want a translated name here.
1367  un->set_name(name);
1368  }
1369  }
1370 }
1371 
1373 {
1374  std::vector<unit_const_ptr> unit_list;
1375 
1376  const unit_map& units = gui().context().units();
1377  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
1378  if(i->side() != gui().viewing_team().side()) {
1379  continue;
1380  }
1381  unit_list.push_back(i.get_shared_ptr());
1382  }
1383 
1385 
1386  if (unit_dlg->show() && unit_dlg->is_selected()) {
1387  const map_location& loc = unit_list[unit_dlg->get_selected_index()]->get_location();
1389  gui().select_hex(loc);
1390  }
1391 }
1392 
1394 {
1395  copy_selection();
1397 }
1398 
1400 {
1401  const std::set<map_location>& area = get_current_map_context().map().selection();
1403 }
1404 
1406 {
1407  const std::set<map_location>& area = get_current_map_context().map().selection();
1409 }
1410 
1412 {
1413  std::stringstream ssx, ssy;
1414  std::set<map_location>::const_iterator i = get_current_map_context().map().selection().begin();
1415  if (i != get_current_map_context().map().selection().end()) {
1416  ssx << "x = " << i->wml_x();
1417  ssy << "y = " << i->wml_y();
1418  ++i;
1419  while (i != get_current_map_context().map().selection().end()) {
1420  ssx << ", " << i->wml_x();
1421  ssy << ", " << i->wml_y();
1422  ++i;
1423  }
1424  ssx << "\n" << ssy.str() << "\n";
1426  }
1427 }
1428 
1429 void editor_controller::perform_delete(std::unique_ptr<editor_action> action)
1430 {
1431  if (action) {
1433  }
1434 }
1435 
1436 void editor_controller::perform_refresh_delete(std::unique_ptr<editor_action> action, bool drag_part /* =false */)
1437 {
1438  if (action) {
1439  context_manager_->perform_refresh(*action, drag_part);
1440  }
1441 }
1442 
1444 {
1446  context_manager_->refresh_all();
1447 }
1448 
1450 {
1451  set_button_state();
1452  toolkit_->adjust_size();
1454 }
1455 
1457 {
1459  context_manager_->refresh_after_action();
1460 }
1461 
1463 {
1465  context_manager_->refresh_after_action();
1466 }
1467 
1468 void editor_controller::mouse_motion(int x, int y, const bool /*browse*/,
1469  bool update, map_location /*new_loc*/)
1470 {
1471  if (mouse_handler_base::mouse_motion_default(x, y, update)) return;
1472  map_location hex_clicked = gui().hex_clicked_on(x, y);
1473  if (get_current_map_context().map().on_board_with_border(drag_from_hex_) && is_dragging()) {
1474  std::unique_ptr<editor_action> a;
1475  bool partial = false;
1476  // last_undo is a non-owning pointer. Although it could have other uses, it seems to be
1477  // mainly (only?) used for printing debugging information.
1478  auto last_undo = get_current_map_context().last_undo_action();
1479  if (dragging_left_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(1)) != 0) {
1480  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1481  a = get_mouse_action().drag_left(*gui_, x, y, partial, last_undo);
1482  } else if (dragging_right_ && (sdl::get_mouse_button_mask() & SDL_BUTTON(3)) != 0) {
1483  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return;
1484  a = get_mouse_action().drag_right(*gui_, x, y, partial, last_undo);
1485  }
1486  //Partial means that the mouse action has modified the
1487  //last undo action and the controller shouldn't add
1488  //anything to the undo stack (hence a different perform_ call)
1489  if (a != nullptr) {
1490  if (partial) {
1492  } else {
1494  }
1495  context_manager_->refresh_after_action(true);
1496  }
1497  } else {
1498  get_mouse_action().move(*gui_, hex_clicked);
1499  }
1500  gui().highlight_hex(hex_clicked);
1501 }
1502 
1503 void editor_controller::touch_motion(int /* x */, int /* y */, const bool /* browse */, bool /* update */, map_location /* new_loc */)
1504 {
1505  // Not implemented at all. Sorry, it's a very low priority for iOS port.
1506 }
1507 
1509 {
1510  return get_current_map_context().map().on_board_with_border(gui().hex_clicked_on(x,y));
1511 }
1512 
1513 bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool /*browse*/)
1514 {
1516 }
1517 
1518 bool editor_controller::left_click(int x, int y, const bool browse)
1519 {
1520  toolkit_->clear_mouseover_overlay();
1521  if (mouse_handler_base::left_click(x, y, browse))
1522  return true;
1523 
1524  LOG_ED << "Left click, after generic handling";
1525  map_location hex_clicked = gui().hex_clicked_on(x, y);
1526  if (!get_current_map_context().map().on_board_with_border(hex_clicked))
1527  return true;
1528 
1529  LOG_ED << "Left click action " << hex_clicked;
1530  auto a = get_mouse_action().click_left(*gui_, x, y);
1531  if(a) {
1532  perform_refresh_delete(std::move(a), true);
1533  set_button_state();
1534  }
1535 
1536  return false;
1537 }
1538 
1539 void editor_controller::left_drag_end(int x, int y, const bool /*browse*/)
1540 {
1541  auto a = get_mouse_action().drag_end_left(*gui_, x, y);
1542  perform_delete(std::move(a));
1543 }
1544 
1545 void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/)
1546 {
1547  auto a = get_mouse_action().up_left(*gui_, x, y);
1548  if(a) {
1549  perform_delete(std::move(a));
1550  set_button_state();
1551  }
1552  toolkit_->set_mouseover_overlay();
1553  context_manager_->refresh_after_action();
1554 }
1555 
1556 bool editor_controller::right_click(int x, int y, const bool browse)
1557 {
1558  toolkit_->clear_mouseover_overlay();
1559  if (mouse_handler_base::right_click(x, y, browse)) return true;
1560  LOG_ED << "Right click, after generic handling";
1561  map_location hex_clicked = gui().hex_clicked_on(x, y);
1562  if (!get_current_map_context().map().on_board_with_border(hex_clicked)) return true;
1563  LOG_ED << "Right click action " << hex_clicked;
1564  auto a = get_mouse_action().click_right(*gui_, x, y);
1565  if(a) {
1566  perform_refresh_delete(std::move(a), true);
1567  set_button_state();
1568  }
1569  return false;
1570 }
1571 
1572 void editor_controller::right_drag_end(int x, int y, const bool /*browse*/)
1573 {
1574  auto a = get_mouse_action().drag_end_right(*gui_, x, y);
1575  perform_delete(std::move(a));
1576 }
1577 
1578 void editor_controller::right_mouse_up(int x, int y, const bool browse)
1579 {
1580  // Call base method to handle context menus.
1581  mouse_handler_base::right_mouse_up(x, y, browse);
1582 
1583  auto a = get_mouse_action().up_right(*gui_, x, y);
1584  if(a) {
1585  perform_delete(std::move(a));
1586  set_button_state();
1587  }
1588  toolkit_->set_mouseover_overlay();
1589  context_manager_->refresh_after_action();
1590 }
1591 
1593 {
1594  const map_location& loc = gui().mouseover_hex();
1595  if (get_current_map_context().map().on_board(loc) == false)
1596  return;
1597 
1600 }
1601 
1602 void editor_controller::process_keyup_event(const SDL_Event& event)
1603 {
1604  auto a = get_mouse_action().key_event(gui(), event);
1605  perform_refresh_delete(std::move(a));
1606  toolkit_->set_mouseover_overlay();
1607 }
1608 
1610  return this;
1611 }
1612 
1614 {
1616 }
1617 
1619 {
1621 }
1622 
1624 {
1626 }
1627 
1629 {
1631 }
1632 
1634 {
1635  return toolkit_->get_palette_manager()->active_palette().action_pressed();
1636 }
1637 
1638 } // 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: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:1380
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:2198
@ 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:1855
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2954
surface screenshot(bool map_screenshot=false)
Capture a (map-)screenshot into a surface.
Definition: display.cpp:715
const display_context & context() const
Definition: display.hpp:184
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition: display.cpp:2139
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:1372
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.
std::vector< std::unique_ptr< map_generator > > map_generators_
Available random map generators.
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:358
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:84
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:975
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
map_generator * create_map_generator(const std::string &name, const config &cfg, const config *vars)
Definition: map_create.cpp:28
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:73
void show_unit_description(const unit &u)
Definition: help.cpp:63
void show_help(const std::string &show_topic)
Open the help browser.
Definition: help.cpp:83
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