The Battle for Wesnoth  1.17.12+dev
mouse_action.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2022
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.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.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);
254  terrain_palette_.select_fg_item(disp.map().get_terrain(hex));
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);
265  terrain_palette_.select_bg_item(disp.map().get_terrain(hex));
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>(
280  hexes, terrain_palette_.selected_fg_item(), has_shift_modifier()));
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>(
291  hexes, terrain_palette_.selected_bg_item(), has_shift_modifier()));
292  return chain;
293 }
294 
296 {
297  set_terrain_mouse_overlay(disp, terrain_palette_.selected_fg_item(),
298  terrain_palette_.selected_bg_item());
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.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()) {
350  terrain_palette_.select_fg_item(disp.map().get_terrain(hex));
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()) {
364  terrain_palette_.select_bg_item(disp.map().get_terrain(hex));
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 {
376  set_terrain_mouse_overlay(disp, terrain_palette_.selected_fg_item(),
377  terrain_palette_.selected_bg_item());
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.map().on_board(hex)) {
386  return nullptr;
387  }
388  auto player_starting_at_hex = disp.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.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
const editor_map & map() const
virtual std::unique_ptr< editor_action > drag_end_left(editor_display &disp, int x, int y)
The end of dragging.
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
Right click does nothing for now.
const CKey & key_
Key presses, used for modifiers (alt, shift) in some operations.
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y) override
Return a paste with offset action.
const brush & get_brush()
Brush accessor.
map_location previous_move_hex_
The hex previously used in move operations.
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
map_location starting_position(int side) const
Definition: map.cpp:324
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_left(editor_display &disp, int x, int y) override
Left / right click fills with the respective terrain.
const 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:581
#define a
void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
void set_brush_locs(const std::set< map_location > &hexes)
#define LOG_ED
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
A click, possibly the beginning of a drag.
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
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:98
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.
const std::string & editor_image() const
Definition: terrain.hpp:47
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Definition: picture.cpp:970
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.
virtual std::unique_ptr< editor_action > up_right(editor_display &disp, int x, int y)
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
Handle terrain sampling before calling generic handler.
virtual std::unique_ptr< editor_action > drag_end_right(editor_display &disp, int x, int y)
static const int MAX_PLAYERS
Maximum number of players supported.
Definition: map.hpp:45
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.
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1022
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
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 bool has_context_menu() const
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
virtual void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
bool valid() const
Definition: location.hpp:89
bool has_shift_modifier() const
virtual void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
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
Editor action classes.
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...
The brush class represents a single brush – a set of relative locations around a "hotspot"...
Definition: brush.hpp:26
std::string path
Definition: game_config.cpp:39
Manage the empty-palette in the editor.
Definition: action.cpp:30
Generic locator abstracting the location of an image.
Definition: picture.hpp:62
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y) override
A click, possibly the beginning of a drag.
Encapsulates the map of the game.
Definition: location.hpp:38
virtual bool has_context_menu() const override
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.
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:2056
virtual void set_mouse_overlay(editor_display &disp) override
Set the mouse overlay for this action.
Base class for all editor actions.
Definition: action_base.hpp:41
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.
std::unique_ptr< editor_action > click_right(editor_display &disp, int x, int y) override
Left / right click fills with the respective terrain.
Base class for actions that: 1) operate on an area 2) can be used as undo for a click-drag operation ...
Definition: action.hpp:60
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
std::unique_ptr< editor_action > click_left(editor_display &disp, int x, int y)
Calls click_perform_left()
virtual std::unique_ptr< editor_action > up_left(editor_display &disp, int x, int y)
virtual void set_mouse_overlay(editor_display &disp)
Set the mouse overlay for this action.
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 ...
const gamemap & get_map() const
Definition: display.hpp:102
virtual void move(editor_display &disp, const map_location &hex)
Mouse move (not a drag).
#define ERR_ED
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.
bool has_ctrl_modifier() const
virtual std::unique_ptr< editor_action > drag_left(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Drag operation.
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_perform_left(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)
Calls click_perform_right()
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.
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.
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:1128
virtual std::unique_ptr< editor_action > drag_right(editor_display &disp, int x, int y, bool &partial, editor_action *last_undo)
Drag operation.
void set_mouseover_hex_overlay(const texture &image)
Sets texture to be drawn in hex under the mouse&#39;s location.
std::unique_ptr< editor_action > drag_end(editor_display &disp, int x, int y)
End of dragging.
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 ...
bool has_alt_modifier() const