The Battle for Wesnoth  1.19.7+dev
mouse_action.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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 
18 #include "editor/action/action.hpp"
19 #include "editor/toolkit/brush.hpp"
22 
23 #include "gettext.hpp"
25 
26 namespace editor {
27 
29 {
30  return false;
31 }
32 
34 {
35  if (hex == previous_move_hex_) {
36  return;
37  }
38 
39  update_brush_highlights(disp, hex);
40  previous_move_hex_ = hex;
41 }
42 
44 {
45  disp.set_brush_locs(affected_hexes(disp, hex));
46 }
47 
48 std::set<map_location> mouse_action::affected_hexes(
49  editor_display& /*disp*/, const map_location& hex)
50 {
51  std::set<map_location> res;
52  res.insert(hex);
53  return res;
54 }
55 
56 std::unique_ptr<editor_action> mouse_action::drag_left(editor_display& /*disp*/,
57  int /*x*/, int /*y*/, bool& /*partial*/, editor_action* /*last_undo*/)
58 {
59  return nullptr;
60 }
61 
62 std::unique_ptr<editor_action> mouse_action::drag_right(editor_display& /*disp*/,
63  int /*x*/, int /*y*/, bool& /*partial*/, editor_action* /*last_undo*/)
64 {
65  return nullptr;
66 }
67 
68 std::unique_ptr<editor_action> mouse_action::drag_end_left(
69  editor_display& /*disp*/, int /*x*/, int /*y*/)
70 {
71  return nullptr;
72 }
73 
74 std::unique_ptr<editor_action> mouse_action::drag_end_right(
75  editor_display& /*disp*/, int /*x*/, int /*y*/)
76 {
77  return nullptr;
78 }
79 
80 std::unique_ptr<editor_action> mouse_action::up_right(
81  editor_display& /*disp*/, int /*x*/, int /*y*/)
82 {
83  return nullptr;
84 }
85 
86 std::unique_ptr<editor_action> mouse_action::up_left(
87  editor_display& /*disp*/, int /*x*/, int /*y*/)
88 {
89  return nullptr;
90 }
91 
92 std::unique_ptr<editor_action> mouse_action::key_event(
93  editor_display& disp, const SDL_Event& event)
94 {
95  if (!has_alt_modifier() && (event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9')) {
96  int side = event.key.keysym.sym - '0';
97  if (side >= 1 && side <= gamemap::MAX_PLAYERS) {
98  map_location pos = disp.get_map().starting_position(side);
99  if (pos.valid()) {
100  disp.scroll_to_tile(pos, display::WARP);
101  }
102  }
103  return nullptr;
104  }
105  if (!disp.get_map().on_board(previous_move_hex_) || event.type != SDL_KEYUP) {
106  return nullptr;
107  }
108  std::unique_ptr<editor_action> a;
109  if ((has_alt_modifier() && (event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9'))
110  || event.key.keysym.sym == SDLK_DELETE) {
111  int res = event.key.keysym.sym - '0';
112  if (res > gamemap::MAX_PLAYERS || event.key.keysym.sym == SDLK_DELETE) res = 0;
113  const std::string* old_id = disp.get_map().is_special_location(previous_move_hex_);
114  if (res == 0 && old_id != nullptr) {
115  a = std::make_unique<editor_action_starting_position>(map_location(), *old_id);
116  } else if (res > 0 && (old_id == nullptr || *old_id == std::to_string(res))) {
117  a = std::make_unique<editor_action_starting_position>(previous_move_hex_, std::to_string(res));
118  }
119  }
120  return a;
121 }
122 
124 {
126 }
127 
129 {
130  return key_[SDLK_RALT] || key_[SDLK_LALT];
131 }
132 
134 {
135  return key_[SDLK_RSHIFT] || key_[SDLK_LSHIFT];
136 }
137 
139 {
140 #ifdef __APPLE__
141  return key_[SDLK_RGUI] || key_[SDLK_LGUI];
142 #else
143  return key_[SDLK_RCTRL] || key_[SDLK_LCTRL];
144 #endif
145 }
146 
148  editor_display& disp,
149  const t_translation::terrain_code & fg,
150  const t_translation::terrain_code & bg)
151 {
152  const std::string fg_path = disp.get_map().get_terrain_info(fg).editor_image();
153  const std::string bg_path = disp.get_map().get_terrain_info(bg).editor_image();
154  const std::string blank_hex = "misc/blank-hex.png";
155 
156  if (!image::exists(fg_path) || !image::exists(bg_path)) {
157  ERR_ED << "Missing terrain icon";
159  return;
160  }
161 
162  // For efficiency the size of the tile is cached.
163  // We assume all tiles are of the same size.
164  // The zoom factor can change, so it's not cached.
165  // NOTE: when zooming and not moving the mouse, there are glitches.
166  // Since the optimal alpha factor is unknown, it has to be calculated
167  // on the fly, and caching the surfaces makes no sense yet.
168  static const int size = image::get_size(blank_hex).x;
169  static const int half_size = size / 2;
170  static const int quarter_size = size / 4;
171  static const int offset = 2;
172  static const int new_size = half_size - 2;
173 
174  // Inserting scaled FG and BG hexes via IPF.
175  std::stringstream path;
176  // "hex~BLIT(fg~SCALE(ns,ns),2,qs)~BLIT(bg~SCALE(ns,ns),hs,qs)"
177  path << blank_hex
178  << "~BLIT(" << fg_path << "~SCALE(" << new_size << "," << new_size
179  << ")," << offset << "," << quarter_size << ")"
180  << "~BLIT(" << bg_path << "~SCALE(" << new_size << "," << new_size
181  << ")," << half_size << "," << quarter_size << ")";
182 
183  // Set as mouseover overlay.
185 }
186 
188  editor_display& /*disp*/, const map_location& hex)
189 {
190  return get_brush().project(hex);
191 }
192 
193 std::unique_ptr<editor_action> brush_drag_mouse_action::click_left(editor_display& disp, int x, int y)
194 {
195  map_location hex = disp.hex_clicked_on(x, y);
196  previous_drag_hex_ = hex;
197  return click_perform_left(disp, affected_hexes(disp, hex));
198 }
199 
200 std::unique_ptr<editor_action> brush_drag_mouse_action::click_right(editor_display& disp, int x, int y)
201 {
202  map_location hex = disp.hex_clicked_on(x, y);
203  previous_drag_hex_ = hex;
204  return click_perform_right(disp, affected_hexes(disp, hex));
205 }
206 
207 std::unique_ptr<editor_action> brush_drag_mouse_action::drag_left(editor_display& disp,
208  int x, int y, bool& partial, editor_action* last_undo)
209 {
210  return drag_generic<&brush_drag_mouse_action::click_perform_left>(disp, x, y, partial, last_undo);
211 }
212 
213 std::unique_ptr<editor_action> brush_drag_mouse_action::drag_right(editor_display& disp,
214  int x, int y, bool& partial, editor_action* last_undo)
215 {
216  return drag_generic<&brush_drag_mouse_action::click_perform_right>(disp, x, y, partial, last_undo);
217 }
218 
219 std::unique_ptr<editor_action> brush_drag_mouse_action::drag_end(
220  editor_display& /*disp*/, int /*x*/, int /*y*/)
221 {
222  return nullptr;
223 }
224 
225 template <std::unique_ptr<editor_action> (brush_drag_mouse_action::*perform_func)(editor_display&, const std::set<map_location>&)>
226 std::unique_ptr<editor_action> brush_drag_mouse_action::drag_generic(editor_display& disp, int x, int y, bool& partial, editor_action* last_undo)
227 {
228  map_location hex = disp.hex_clicked_on(x, y);
229  move(disp, hex);
230  if (hex != previous_drag_hex_) {
231  editor_action_extendable* last_undo_x = dynamic_cast<editor_action_extendable*>(last_undo);
232  LOG_ED << "Last undo is " << last_undo << " and as x " << last_undo_x;
233  partial = true;
234  auto a = (this->*perform_func)(disp, affected_hexes(disp, hex));
235  previous_drag_hex_ = hex;
236  return a;
237  } else {
238  return nullptr;
239  }
240 }
241 
243 {
244  assert(brush_);
245  assert(*brush_);
246  return **brush_;
247 }
248 
249 
250 std::unique_ptr<editor_action> mouse_action_paint::click_left(editor_display& disp, int x, int y)
251 {
252  if (has_ctrl_modifier()) {
253  map_location hex = disp.hex_clicked_on(x, y);
255  return nullptr;
256  } else {
257  return brush_drag_mouse_action::click_left(disp, x, y);
258  }
259 }
260 
261 std::unique_ptr<editor_action> mouse_action_paint::click_right(editor_display& disp, int x, int y)
262 {
263  if (has_ctrl_modifier()) {
264  map_location hex = disp.hex_clicked_on(x, y);
266  return nullptr;
267  } else {
268  return brush_drag_mouse_action::click_right(disp, x, y);
269  }
270 }
271 
272 std::unique_ptr<editor_action> mouse_action_paint::click_perform_left(
273  editor_display& /*disp*/, const std::set<map_location>& hexes)
274 {
275  if (has_ctrl_modifier()) return nullptr;
276  // \todo why is this a chain and not an individual action? I guess that mouse drags were meant
277  // to be optimised with one undo for multiple hexes, but it seems that was never added.
278  auto chain = std::make_unique<editor_action_chain>();
279  chain->append_action(std::make_unique<editor_action_paint_area>(
281  return chain;
282 }
283 
284 std::unique_ptr<editor_action> mouse_action_paint::click_perform_right(
285  editor_display& /*disp*/, const std::set<map_location>& hexes)
286 {
287  if (has_ctrl_modifier()) return nullptr;
288  // \todo why is this a chain and not an individual action?
289  auto chain = std::make_unique<editor_action_chain>();
290  chain->append_action(std::make_unique<editor_action_paint_area>(
292  return chain;
293 }
294 
296 {
299 }
300 
301 
302 
303 
304 
306 {
307  return true;
308 }
309 
310 std::set<map_location> mouse_action_paste::affected_hexes(
311  editor_display& /*disp*/, const map_location& hex)
312 {
313  return paste_.get_offset_area(hex);
314 }
315 
316 std::unique_ptr<editor_action> mouse_action_paste::click_left(editor_display& disp, int x, int y)
317 {
318  map_location hex = disp.hex_clicked_on(x, y);
319  return std::make_unique<editor_action_paste>(paste_, hex);
320 }
321 
322 std::unique_ptr<editor_action> mouse_action_paste::click_right(editor_display& /*disp*/, int /*x*/, int /*y*/)
323 {
324  return nullptr;
325 }
326 
328 {
331  // center 60px icon on blank hex template
333  "misc/blank-hex.png",
334  "~BLIT(icons/action/editor-paste_60.png,6,6)"
335  )
336  )
337  );
338 }
339 
340 std::set<map_location> mouse_action_fill::affected_hexes(
341  editor_display& disp, const map_location& hex)
342 {
343  return disp.get_map().get_contiguous_terrain_tiles(hex);
344 }
345 
346 std::unique_ptr<editor_action> mouse_action_fill::click_left(editor_display& disp, int x, int y)
347 {
348  map_location hex = disp.hex_clicked_on(x, y);
349  if (has_ctrl_modifier()) {
351  return nullptr;
352  } else {
353  /** @todo only take the base terrain into account when searching for contiguous terrain when painting base only */
354  //or use a different key modifier for that
355  return std::make_unique<editor_action_fill>(hex, terrain_palette_.selected_fg_item(),
357  }
358 }
359 
360 std::unique_ptr<editor_action> mouse_action_fill::click_right(editor_display& disp, int x, int y)
361 {
362  map_location hex = disp.hex_clicked_on(x, y);
363  if (has_ctrl_modifier()) {
365  return nullptr;
366  } else {
367  /** @todo only take the base terrain into account when searching for contiguous terrain when painting base only */
368  //or use a different key modifier for that
369  return std::make_unique<editor_action_fill>(hex, terrain_palette_.selected_bg_item(),
371  }
372 }
373 
375 {
378 }
379 
380 std::unique_ptr<editor_action> mouse_action_starting_position::up_left(editor_display& disp, int x, int y)
381 {
382  if (!click_) return nullptr;
383  click_ = false;
384  map_location hex = disp.hex_clicked_on(x, y);
385  if (!disp.get_map().on_board(hex)) {
386  return nullptr;
387  }
388  auto player_starting_at_hex = disp.get_map().is_special_location(hex);
389 
390  if (has_ctrl_modifier()) {
391  if (player_starting_at_hex) {
392  location_palette_.add_item(*player_starting_at_hex);
393  }
394  return nullptr;
395  }
396 
397  std::string new_player_at_hex = location_palette_.selected_item();
398  std::unique_ptr<editor_action> a;
399 
400  if(!player_starting_at_hex || new_player_at_hex != *player_starting_at_hex) {
401  // Set a starting position
402  a = std::make_unique<editor_action_starting_position>(hex, new_player_at_hex);
403  }
404  else {
405  // Erase current starting position
406  a = std::make_unique<editor_action_starting_position>(map_location(), *player_starting_at_hex);
407  }
408 
409  update_brush_highlights(disp, hex);
410 
411  return a;
412 }
413 
414 std::unique_ptr<editor_action> mouse_action_starting_position::click_left(editor_display& /*disp*/, int /*x*/, int /*y*/)
415 {
416  click_ = true;
417  return nullptr;
418 }
419 
420 std::unique_ptr<editor_action> mouse_action_starting_position::up_right(editor_display& disp, int x, int y)
421 {
422  map_location hex = disp.hex_clicked_on(x, y);
423  auto player_starting_at_hex = disp.get_map().is_special_location(hex);
424  if (player_starting_at_hex != nullptr) {
425  return std::make_unique<editor_action_starting_position>(map_location(), *player_starting_at_hex);
426  } else {
427  return nullptr;
428  }
429 }
430 
431 std::unique_ptr<editor_action> mouse_action_starting_position::click_right(editor_display& /*disp*/, int /*x*/, int /*y*/)
432 {
433  return nullptr;
434 }
435 
437 {
440  // center 60px icon on blank hex template
442  "misc/blank-hex.png",
443  "~BLIT(icons/action/editor-tool-starting-position_60.png,6,6)"
444  )
445  )
446  );
447 }
448 
449 
450 } //end namespace editor
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:1975
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:538
A brush-drag mouse action base class which adds brush and drag processing to a basic mouse action.
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y)
Calls click_perform_left()
std::unique_ptr< editor_action > drag_right(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Calls click_perform for every new hex the mouse is dragged into.
const brush *const *const brush_
Current brush handle.
virtual std::unique_ptr< editor_action > click_perform_right(editor_display &disp, const std::set< map_location > &hexes)=0
The actual action function which is called by click() and drag().
const brush & get_brush()
Brush accessor.
std::unique_ptr< editor_action > drag_left(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Calls click_perform() for every new hex the mouse is dragged into.
std::unique_ptr< editor_action > drag_end(editor_display &disp, int x, int y)
End of dragging.
std::unique_ptr< editor_action > drag_generic(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Template helper gathering actions common for both drag_right and drag_left.
map_location previous_drag_hex_
The previous hex dragged into.
std::set< map_location > affected_hexes(editor_display &disp, const map_location &hex)
The affected hexes of a brush action are the result of projecting the current brush on the mouseover ...
virtual std::unique_ptr< editor_action > click_perform_left(editor_display &disp, const std::set< map_location > &hexes)=0
The actual action function which is called by click() and drag().
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y)
Calls click_perform_right()
The brush class represents a single brush – a set of relative locations around a "hotspot",...
Definition: brush.hpp:27
std::set< map_location > project(const map_location &hotspot) const
Get a set of locations affected (i.e.
Definition: brush.cpp:61
Base class for actions that: 1) operate on an area 2) can be used as undo for a click-drag operation ...
Definition: action.hpp:61
Base class for all editor actions.
Definition: action_base.hpp:42
void set_mouseover_hex_overlay(const texture &image)
Sets texture to be drawn in hex under the mouse's location.
void set_brush_locs(const std::set< map_location > &hexes)
const editor_map & get_map() const
std::set< map_location > get_contiguous_terrain_tiles(const map_location &start) const
Get a contiguous set of tiles having the same terrain as the starting location.
Definition: editor_map.cpp:115
const std::string & selected_item() const
Return the currently selected item.
void add_item(const std::string &id)
std::set< map_location > get_offset_area(const map_location &offset) const
Get the area covered by this map fragment, shifted by an offset.
terrain_palette & terrain_palette_
std::set< map_location > affected_hexes(editor_display &disp, const map_location &hex) override
Tiles that will be painted to, possibly use modifier keys here.
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y) override
Left / right click fills with the respective terrain.
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
Left / right click fills with the respective terrain.
virtual void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
std::unique_ptr< editor_action > click_perform_right(editor_display &disp, const std::set< map_location > &hexes) override
Create an appropriate editor_action and return it.
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
Handle terrain sampling before calling generic handler.
terrain_palette & terrain_palette_
void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y) override
Handle terrain sampling before calling generic handler.
std::unique_ptr< editor_action > click_perform_left(editor_display &disp, const std::set< map_location > &hexes) override
Create an appropriate editor_action and return it.
virtual bool has_context_menu() const override
std::set< map_location > affected_hexes(editor_display &disp, const map_location &hex) override
Show an outline of where the paste will go.
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y) override
Return a paste with offset action.
virtual void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
Right click does nothing for now.
const map_fragment & paste_
Reference to the buffer used for pasting (e.g.
std::unique_ptr< editor_action > up_right(editor_display &disp, int x, int y) override
Right click only erases the starting position if there is one.
std::unique_ptr< editor_action > up_left(editor_display &disp, int x, int y) override
Left click displays a player-number-selector dialog and then creates an action or returns nullptr if ...
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
A click, possibly the beginning of a drag.
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y) override
A click, possibly the beginning of a drag.
virtual void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
bool has_shift_modifier() const
void update_brush_highlights(editor_display &disp, const map_location &hex)
Unconditionally update the brush highlights for the current tool when hex is the center location.
virtual std::unique_ptr< editor_action > drag_end_right(editor_display &disp, int x, int y)
virtual bool has_context_menu() const
void set_terrain_mouse_overlay(editor_display &disp, const t_translation::terrain_code &fg, const t_translation::terrain_code &bg)
Helper function for derived classes that need a active-terrain mouse overlay.
virtual std::set< map_location > affected_hexes(editor_display &disp, const map_location &hex)
Locations that would be affected by a click, used by move to update highlights.
virtual std::unique_ptr< editor_action > up_left(editor_display &disp, int x, int y)
bool has_ctrl_modifier() const
const CKey & key_
Key presses, used for modifiers (alt, shift) in some operations.
map_location previous_move_hex_
The hex previously used in move operations.
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 void set_mouse_overlay(editor_display &disp)
Set the mouse overlay for this action.
bool has_alt_modifier() const
virtual std::unique_ptr< editor_action > drag_end_left(editor_display &disp, int x, int y)
The end of dragging.
void select_fg_item(const t_translation::terrain_code &terrain)
const t_translation::terrain_code & selected_bg_item() const
const t_translation::terrain_code & selected_fg_item() const
void select_bg_item(const t_translation::terrain_code &terrain)
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
static const int MAX_PLAYERS
Maximum number of players supported.
Definition: map.hpp:45
map_location starting_position(int side) const
Definition: map.cpp:324
const std::string * is_special_location(const map_location &loc) const
returns the name of the special location at position loc, null if no such location exists.
Definition: map.cpp:357
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:98
Generic locator abstracting the location of an image.
Definition: picture.hpp:59
const std::string & editor_image() const
Definition: terrain.hpp:47
Editor action classes.
#define LOG_ED
#define ERR_ED
Manage the empty-palette in the editor.
Definition: action.cpp:31
std::string path
Definition: filesystem.cpp:91
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:818
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
Definition: picture.cpp:920
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition: picture.cpp:777
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
@ partial
There are still moves and/or attacks possible, but the unit doesn't fit in the "unmoved" status.
Encapsulates the map of the game.
Definition: location.hpp:45
bool valid() const
Definition: location.hpp:110
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49