The Battle for Wesnoth  1.15.0-dev
location_palette.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-editor"
16 
18 
19 #include "gettext.hpp"
20 #include "font/marked-up_text.hpp"
21 #include "font/standard_colors.hpp"
22 #include "tooltips.hpp"
23 
24 #include "editor/editor_common.hpp"
28 
29 #include "formula/string_utils.hpp"
30 
31 #include <boost/regex.hpp>
32 
33 static bool is_positive_integer(const std::string& str) {
34  return str != "0" && std::find_if(str.begin(), str.end(), [](char c) { return !std::isdigit(c); }) == str.end();
35 }
36 
38 {
39 public:
40  struct state_t {
42  : selected(false)
43  , mouseover(false)
44  {}
45  bool selected;
46  bool mouseover;
47  friend bool operator==(state_t r, state_t l)
48  {
49  return r.selected == l.selected && r.mouseover == l.mouseover;
50  }
51 
52  };
54  : gui::widget(video, true)
55  , parent_(parent)
56  {
57  }
58 
59  void draw_contents() override
60  {
61  if (state_.mouseover) {
62  sdl::fill_rectangle(location(), {200, 200, 200, 26});
63  }
64  if (state_.selected) {
65  sdl::draw_rectangle(location(), {255, 255, 255, 255});
66  }
67  font::draw_text(&video(), location(), 16, font::NORMAL_COLOR, desc_.empty() ? id_ : desc_, location().x + 2, location().y, 0);
68  }
69 
70  //TODO move to widget
71  bool hit(int x, int y) const
72  {
73  return sdl::point_in_rect(x, y, location());
74  }
75 
76  void mouse_up(const SDL_MouseButtonEvent& e)
77  {
78  if (!(hit(e.x, e.y)))
79  return;
80  if (e.button == SDL_BUTTON_LEFT) {
82  }
83  if (e.button == SDL_BUTTON_RIGHT) {
84  //TODO: add a context menu with the following options:
85  // 1) 'copy it to clipboard'
86  // 2) 'jump to item'
87  // 3) 'delete item'.
88  }
89  }
90 
91  void handle_event(const SDL_Event& e) override
92  {
94 
95  if (hidden() || !enabled() || mouse_locked())
96  return;
97 
98  state_t start_state = state_;
99 
100  switch (e.type) {
101  case SDL_MOUSEBUTTONUP:
102  mouse_up(e.button);
103  break;
104  case SDL_MOUSEMOTION:
105  state_.mouseover = hit(e.motion.x, e.motion.y);
106  break;
107  default:
108  return;
109  }
110 
111  if (!(start_state == state_))
112  set_dirty(true);
113  }
114 
115  void set_item_id(const std::string& id)
116  {
117  id_ = id;
118  if (is_positive_integer(id)) {
119  desc_ = VGETTEXT("Player $side_num", utils::string_map{ {"side_num", id} });
120  }
121  else {
122  desc_ = "";
123  }
124  }
126  {
128  }
129  void draw() override { gui::widget::draw(); }
130 private:
131  std::string id_;
132  std::string desc_;
135 };
136 
138 {
139 public:
140  location_palette_button(CVideo& video, const SDL_Rect& location, const std::string& text, const std::function<void (void)>& callback)
141  : gui::button(video, text)
142  , callback_(callback)
143  {
144  this->set_location(location.x, location.y);
145  this->hide(false);
146  }
147 protected:
148  virtual void mouse_up(const SDL_MouseButtonEvent& e) override
149  {
151  if (callback_) {
152  if (this->pressed()) {
153  callback_();
154  }
155  }
156  }
157  std::function<void (void)> callback_;
158 
159 };
160 namespace editor {
161 location_palette::location_palette(editor_display &gui, const config& /*cfg*/,
162  editor_toolkit &toolkit)
163  : common_palette(gui.video())
164  , item_size_(20)
165  //TODO avoid magic number
166  , item_space_(20 + 3)
167  , palette_y_(0)
168  , palette_x_(0)
169  , items_start_(0)
170  , selected_item_()
171  , items_()
172  , toolkit_(toolkit)
173  , buttons_()
174  , button_add_()
175  , button_delete_()
176  , button_goto_()
177  , help_handle_(-1)
178  , disp_(gui)
179  {
180  for (int i = 1; i < 10; ++i) {
181  items_.push_back(std::to_string(i));
182  }
183  selected_item_ = items_[0];
184  }
185 
187 {
189  for (gui::widget& b : buttons_) {
190  h.push_back(&b);
191  }
192  if (button_add_) { h.push_back(button_add_.get()); }
193  if (button_delete_) { h.push_back(button_delete_.get()); }
194  if (button_goto_) { h.push_back(button_goto_.get()); }
195  return h;
196 }
197 
199 {
200  widget::hide(hidden);
201 
203 
204  std::shared_ptr<gui::button> palette_menu_button = disp_.find_menu_button("menu-editor-terrain");
205  palette_menu_button->set_overlay("");
206  palette_menu_button->enable(false);
207 
208  for(auto& w : handler_members()) {
209  static_cast<gui::widget&>(*w).hide(hidden);
210  }
211 }
212 
214 {
215  int decrement = 1;
216  if(items_start_ >= decrement) {
217  items_start_ -= decrement;
218  draw();
219  return true;
220  }
221  return false;
222 }
224 {
225  return (items_start_ != 0);
226 }
227 
229 {
230  return (items_start_ + num_visible_items() + 1 <= num_items());
231 }
232 
234 {
235  bool end_reached = (!(items_start_ + num_visible_items() + 1 <= num_items()));
236  bool scrolled = false;
237 
238  // move downwards
239  if(!end_reached) {
240  items_start_ += 1;
241  scrolled = true;
242  set_dirty(true);
243  }
244  draw();
245  return scrolled;
246 }
247 
248 void location_palette::adjust_size(const SDL_Rect& target)
249 {
250  palette_x_ = target.x;
251  palette_y_ = target.y;
252  const int button_height = 22;
253  const int button_y = 30;
254  int bottom = target.y + target.h;
255  if (!button_goto_) {
256  button_goto_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height }, _("Go To"), [this]() {
257  //static_cast<mouse_action_starting_position&>(toolkit_.get_mouse_action()). ??
259  if (pos.valid()) {
261  }
262  }));
263  button_add_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height }, _("Add"), [this]() {
264  std::string newid;
265  if (gui2::dialogs::edit_text::execute(_("New Location Identifier"), "", newid)) {
266  static const boost::regex valid_id("[a-zA-Z0-9_]+");
267  if(boost::regex_match(newid, valid_id)) {
268  add_item(newid);
269  }
270  else {
272  _("Error"),
273  _("Invalid location id")
274  );
275  //TODO: a user visible messae would be nice.
276  ERR_ED << "entered invalid location id\n";
277  }
278  }
279  }));
280  button_delete_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height }, _("Delete"), nullptr));
281  }
282  else {
283  button_goto_->set_location(SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height });
284  button_add_->set_location(SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height });
285  button_delete_->set_location(SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height });
286  }
287 
288  const int space_for_items = bottom - target.y;
289  const int items_fitting = space_for_items / item_space_;
290  // This might be called while the palette is not visible onscreen.
291  // If that happens, no items will fit and we'll have a negative number here.
292  // Just skip it in that case.
293  if(items_fitting > 0 && num_visible_items() != items_fitting) {
294  location_palette_item lpi(disp_.video(), *this);
295  //Why does this need a pointer to a non-const as second paraeter?
296  //TODO: we should write our own ptr_vector class, boost::ptr_vector has a lot of flaws.
297  buttons_.resize(items_fitting, &lpi);
298  }
299 
300  set_location(target);
301  set_dirty(true);
304 }
305 
306 void location_palette::select_item(const std::string& item_id)
307 {
308  if (selected_item_ != item_id) {
309  selected_item_ = item_id;
310  set_dirty();
311  }
314 }
315 
317 {
318  return items_.size();
319 }
321 {
322  return buttons_.size();
323 }
324 
325 bool location_palette::is_selected_item(const std::string& id)
326 {
327  return selected_item_ == id;
328 }
329 
331 {
333  int y = palette_y_;
334  const int x = palette_x_;
335  const int starting = items_start_;
336  int ending = std::min<int>(starting + num_visible_items(), num_items());
337  std::shared_ptr<gui::button> upscroll_button = disp_.find_action_button("upscroll-button-editor");
338  if (upscroll_button)
339  upscroll_button->enable(starting != 0);
340  std::shared_ptr<gui::button> downscroll_button = disp_.find_action_button("downscroll-button-editor");
341  if (downscroll_button)
342  downscroll_button->enable(ending != num_items());
343 
344  if (button_goto_) {
345  button_goto_->set_dirty(true);
346  }
347  if (button_add_) {
348  button_add_->set_dirty(true);
349  }
350  if (button_delete_) {
351  button_delete_->set_dirty(true);
352  }
353  for (int i = 0, size = num_visible_items(); i < size; i++) {
354 
355  location_palette_item & tile = buttons_[i];
356 
357  tile.hide(true);
358 
359  if (i >= ending) {
360  //We want to hide all following buttons so we cannot use break here.
361  continue;
362  }
363 
364  const std::string item_id = items_[starting + i];
365 
366  std::stringstream tooltip_text;
367 
368  SDL_Rect dstrect;
369  dstrect.x = x;
370  dstrect.y = y;
371  dstrect.w = location().w - 10;
372  dstrect.h = item_size_ + 2;
373 
374  tile.set_location(dstrect);
375  tile.set_tooltip_string(tooltip_text.str());
376  tile.set_item_id(item_id);
377  tile.set_selected(is_selected_item(item_id));
378  tile.set_dirty(true);
379  tile.hide(false);
380  tile.draw();
381 
382  // Adjust location
383  y += item_space_;
384  }
385 }
386 
387 std::vector<std::string> location_palette::action_pressed() const
388 {
389  std::vector<std::string> res;
390  if (button_delete_ && button_delete_->pressed()) {
391  res.push_back("editor-remove-location");
392  }
393  return res;
394 }
395 
397 {
398 }
399 
400 // Sort numbers before all other strings.
401 static bool loc_id_comp(const std::string& lhs, const std::string& rhs) {
402  if(is_positive_integer(lhs)) {
403  if(is_positive_integer(rhs)) {
404  return std::stoi(lhs) < std::stoi(rhs);
405  } else {
406  return true;
407  }
408  }
409  if(is_positive_integer(rhs)) {
410  return false;
411  }
412  return lhs < rhs;
413 }
414 
415 void location_palette::add_item(const std::string& id)
416 {
417  int pos;
418  // Insert the new ID at the sorted location, unless it's already in the list
419  const auto itor = std::upper_bound(items_.begin(), items_.end(), id, loc_id_comp);
420  if(itor == items_.begin() || *(itor - 1) != id) {
421  pos = std::distance(items_.begin(), items_.insert(itor, id));
422  }
423  else {
424  pos = std::distance(items_.begin(), itor);
425  }
426  selected_item_ = id;
427  if(num_visible_items() == 0) {
428  items_start_ = 0;
429  } else {
430  items_start_ = std::max(pos - num_visible_items() + 1, items_start_);
431  items_start_ = std::min(pos, items_start_);
432  }
434 }
435 
436 } // end namespace editor
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:804
virtual bool scroll_down() override
Scroll the editor-palette down one step if possible.
bool enabled() const
Definition: widget.cpp:210
virtual void mouse_up(const SDL_MouseButtonEvent &event)
Definition: button.cpp:501
virtual bool can_scroll_up() override
std::map< std::string, t_string > string_map
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:176
virtual bool can_scroll_down() override
bool hidden() const
Definition: widget.cpp:196
bool hit(int x, int y) const
location_palette_item(CVideo &video, editor::location_palette &parent)
Definition: video.hpp:31
boost::ptr_vector< location_palette_item > buttons_
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, const bool restore_background)
Shows a transient message to the user.
General purpose widgets.
virtual bool is_selected_item(const std::string &id)
void set_mouseover_overlay(editor_display &gui)
#define h
virtual std::vector< std::string > action_pressed() const override
virtual void mouse_up(const SDL_MouseButtonEvent &e) override
editor::location_palette & parent_
std::unique_ptr< location_palette_button > button_goto_
#define b
virtual void draw() override
void set_dirty(bool dirty=true)
Definition: widget.cpp:215
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
virtual void handle_event(const SDL_Event &)
Definition: widget.cpp:338
static bool is_positive_integer(const std::string &str)
virtual void hide(bool value=true)
Definition: widget.cpp:160
bool valid() const
Definition: location.hpp:93
const SDL_Rect & location() const
Definition: widget.cpp:142
void draw_rectangle(const SDL_Rect &rect, const color_t &color)
Draw a rectangle outline.
Definition: rect.cpp:57
location_palette_button(CVideo &video, const SDL_Rect &location, const std::string &text, const std::function< void(void)> &callback)
virtual std::string get_help_string()
Manage the empty-palette in the editor.
Definition: action.cpp:29
int num_visible_items()
Return the maximum number of items shown at the same time.
void set_item_id(const std::string &id)
virtual void set_location(const SDL_Rect &rect)
Definition: widget.cpp:83
const color_t NORMAL_COLOR
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:22
void set_selected(bool selected)
Encapsulates the map of the game.
Definition: location.hpp:42
virtual bool scroll_up() override
Scroll the editor-palette up one step if possible.
void mouse_up(const SDL_MouseButtonEvent &e)
std::size_t i
Definition: function.cpp:933
void add_item(const std::string &id)
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:2160
const std::string & id() const
Definition: widget.cpp:230
Main (common) editor header.
virtual void draw_contents() override
virtual void draw()
Definition: widget.cpp:277
int w
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
bool mouse_locked() const
Definition: widget.cpp:70
std::unique_ptr< location_palette_button > button_add_
void set_tooltip_string(const std::string &str)
Definition: widget.cpp:312
CVideo & video() const
Definition: widget.hpp:84
int set_help_string(const std::string &str)
Displays a help string with the given text.
Definition: video.cpp:487
void adjust_size(const SDL_Rect &target) override
Update the size of this widget.
const gamemap & get_map() const
Definition: display.hpp:92
std::unique_ptr< location_palette_button > button_delete_
#define ERR_ED
SDL_Rect draw_text(surface &dst, const SDL_Rect &area, int size, const color_t &color, const std::string &txt, int x, int y, bool use_tooltips, int style)
Function to draw text on a surface.
void fill_rectangle(const SDL_Rect &rect, const color_t &color)
Draws a filled rectangle.
Definition: rect.cpp:65
std::function< void(void)> callback_
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:196
void draw_contents() override
#define e
virtual void select_item(const std::string &item_id)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
mock_char c
int num_items() override
Return the number of items in the palette.
static bool loc_id_comp(const std::string &lhs, const std::string &rhs)
void hide(bool hidden) override
void handle_event(const SDL_Event &e) override
std::shared_ptr< gui::button > find_menu_button(const std::string &id)
Definition: display.cpp:814
map_location special_location(const std::string &id) const
Definition: map.cpp:319
friend bool operator==(state_t r, state_t l)
std::vector< std::string > items_
void clear_help_string(int handle)
Removes the help string with the given handle.
Definition: video.cpp:519
virtual sdl_handler_vector handler_members() override
widget(const widget &o)
Definition: widget.cpp:33