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 http://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 
137 namespace editor {
138 location_palette::location_palette(editor_display &gui, const config& /*cfg*/,
139  editor_toolkit &toolkit)
140  : common_palette(gui.video())
141  , item_size_(20)
142  //TODO avoid magic number
143  , item_space_(20 + 3)
144  , palette_y_(0)
145  , palette_x_(0)
146  , items_start_(0)
147  , selected_item_()
148  , items_()
149  , toolkit_(toolkit)
150  , buttons_()
151  //, button_add_()
152  //, button_delete_()
153  //, button_goto_()
154  , disp_(gui)
155  {
156  for (int i = 1; i < 10; ++i) {
157  items_.push_back(std::to_string(i));
158  }
159  selected_item_ = items_[0];
160  }
161 
163 {
165  for (gui::widget& b : buttons_) {
166  h.push_back(&b);
167  }
168 #if 0
169  if (button_add_) { h.push_back(button_add_.get()); }
170  if (button_delete_) { h.push_back(button_delete_.get()); }
171  if (button_goto_) { h.push_back(button_goto_.get()); }
172 #endif
173  return h;
174 }
175 
177 {
178  widget::hide(hidden);
179 
180 #if 0
181  std::shared_ptr<gui::button> palette_menu_button = disp_.find_menu_button("menu-editor-terrain");
182  palette_menu_button->set_overlay("");
183  palette_menu_button->enable(false);
184 #endif
185 
186  for(auto& w : handler_members()) {
187  static_cast<gui::widget&>(*w).hide(hidden);
188  }
189 }
190 
192 {
193  int decrement = 1;
194  if(items_start_ >= decrement) {
195  items_start_ -= decrement;
196  draw();
197  return true;
198  }
199  return false;
200 }
202 {
203  return (items_start_ != 0);
204 }
205 
207 {
208  return (items_start_ + num_visible_items() + 1 <= num_items());
209 }
210 
212 {
213  bool end_reached = (!(items_start_ + num_visible_items() + 1 <= num_items()));
214  bool scrolled = false;
215 
216  // move downwards
217  if(!end_reached) {
218  items_start_ += 1;
219  scrolled = true;
220  set_dirty(true);
221  }
222  draw();
223  return scrolled;
224 }
225 
226 void location_palette::adjust_size(const SDL_Rect& target)
227 {
228  palette_x_ = target.x;
229  palette_y_ = target.y;
230  //const int button_height = 22;
231  //const int button_y = 30;
232  int bottom = target.y + target.h;
233 #if 0
234  if (!button_goto_) {
235  button_goto_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height }, _("Go To"), [this]() {
236  //static_cast<mouse_action_starting_position&>(toolkit_.get_mouse_action()). ??
238  if (pos.valid()) {
240  }
241  }));
242  button_add_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height }, _("Add"), [this]() {
243  std::string newid;
244  if (gui2::dialogs::edit_text::execute(_("New Location Identifier"), "", newid)) {
245  static const boost::regex valid_id("[a-zA-Z_]+");
246  if(boost::regex_match(newid, valid_id)) {
247  add_item(newid);
248  }
249  else {
251  _("Error"),
252  _("Invalid location id")
253  );
254  //TODO: a user visible messae would be nice.
255  ERR_ED << "entered invalid location id\n";
256  }
257  }
258  }));
259  button_delete_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height }, _("Delete"), nullptr));
260  }
261  else {
262  button_goto_->set_location(SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height });
263  button_add_->set_location(SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height });
264  button_delete_->set_location(SDL_Rect{ target.x , bottom -= button_y, target.w - 10, button_height });
265  }
266 #endif
267  const int space_for_items = bottom - target.y;
268  const int items_fitting = space_for_items / item_space_;
269  // This might be called while the palette is not visible onscreen.
270  // If that happens, no items will fit and we'll have a negative number here.
271  // Just skip it in that case.
272  if(items_fitting > 0 && num_visible_items() != items_fitting) {
273  location_palette_item lpi(disp_.video(), *this);
274  //Why does this need a pointer to a non-const as second paraeter?
275  //TODO: we should write our own ptr_vector class, boost::ptr_vector has a lot of flaws.
276  buttons_.resize(items_fitting, &lpi);
277  }
278 
279  set_location(target);
280  set_dirty(true);
281 }
282 
283 void location_palette::select_item(const std::string& item_id)
284 {
285  if (selected_item_ != item_id) {
286  selected_item_ = item_id;
287  set_dirty();
288  }
289 }
290 
292 {
293  return items_.size();
294 }
296 {
297  return buttons_.size();
298 }
299 
300 bool location_palette::is_selected_item(const std::string& id)
301 {
302  return selected_item_ == id;
303 }
304 
306 {
308  int y = palette_y_;
309  const int x = palette_x_;
310  const int starting = items_start_;
311  int ending = std::min<int>(starting + num_visible_items(), num_items());
312 #if 0
313  std::shared_ptr<gui::button> upscroll_button = disp_.find_action_button("upscroll-button-editor");
314  if (upscroll_button)
315  upscroll_button->enable(starting != 0);
316  std::shared_ptr<gui::button> downscroll_button = disp_.find_action_button("downscroll-button-editor");
317  if (downscroll_button)
318  downscroll_button->enable(ending != num_items());
319 
320  if (button_goto_) {
321  button_goto_->set_dirty(true);
322  }
323  if (button_add_) {
324  button_add_->set_dirty(true);
325  }
326  if (button_delete_) {
327  button_delete_->set_dirty(true);
328  }
329 #endif
330  for (int i = 0, size = num_visible_items(); i < size; i++) {
331 
332  location_palette_item & tile = buttons_[i];
333 
334  tile.hide(true);
335 
336  if (i >= ending) {
337  //We want to hide all following buttons so we cannot use break here.
338  continue;
339  }
340 
341  const std::string item_id = items_[starting + i];
342 
343  std::stringstream tooltip_text;
344 
345  SDL_Rect dstrect;
346  dstrect.x = x;
347  dstrect.y = y;
348  dstrect.w = location().w - 10;
349  dstrect.h = item_size_ + 2;
350 
351  tile.set_location(dstrect);
352  tile.set_tooltip_string(tooltip_text.str());
353  tile.set_item_id(item_id);
354  tile.set_selected(is_selected_item(item_id));
355  tile.set_dirty(true);
356  tile.hide(false);
357  tile.draw();
358 
359  // Adjust location
360  y += item_space_;
361  }
362 }
363 
364 std::vector<std::string> location_palette::action_pressed() const
365 {
366  std::vector<std::string> res;
367 #if 0
368  if (button_delete_ && button_delete_->pressed()) {
369  res.push_back("editor-remove-location");
370  }
371 #endif
372  return res;
373 }
374 
376 {
377 }
378 
379 // Sort numbers before all other strings.
380 static bool loc_id_comp(const std::string& lhs, const std::string& rhs) {
381  if(is_positive_integer(lhs)) {
382  if(is_positive_integer(rhs)) {
383  return std::stoi(lhs) < std::stoi(rhs);
384  } else {
385  return true;
386  }
387  }
388  if(is_positive_integer(rhs)) {
389  return false;
390  }
391  return lhs < rhs;
392 }
393 
394 void location_palette::add_item(const std::string& id)
395 {
396  int pos;
397  // Insert the new ID at the sorted location, unless it's already in the list
398  const auto itor = std::upper_bound(items_.begin(), items_.end(), id, loc_id_comp);
399  if(itor == items_.begin() || *(itor - 1) != id) {
400  pos = std::distance(items_.begin(), items_.insert(itor, id));
401  }
402  else {
403  pos = std::distance(items_.begin(), itor);
404  }
405  selected_item_ = id;
406  if(num_visible_items() == 0) {
407  items_start_ = 0;
408  } else {
409  items_start_ = std::max(pos - num_visible_items() + 1, items_start_);
410  items_start_ = std::min(pos, items_start_);
411  }
413 }
414 
415 } // end namespace editor
virtual bool scroll_down() override
Scroll the editor-palette down one step if possible.
bool enabled() const
Definition: widget.cpp:205
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:167
virtual bool can_scroll_down() override
bool hidden() const
Definition: widget.cpp:191
bool hit(int x, int y) const
location_palette_item(CVideo &video, editor::location_palette &parent)
Definition: video.hpp:36
boost::ptr_vector< location_palette_item > buttons_
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
editor::location_palette & parent_
#define b
virtual void draw() override
void set_dirty(bool dirty=true)
Definition: widget.cpp:210
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:315
static bool is_positive_integer(const std::string &str)
virtual void hide(bool value=true)
Definition: widget.cpp:155
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.
bool valid() const
Definition: location.hpp:93
const SDL_Rect & location() const
Definition: widget.cpp:137
void draw_rectangle(const SDL_Rect &rect, const color_t &color)
Draw a rectangle outline.
Definition: rect.cpp:57
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:79
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:1192
const std::string & id() const
Definition: widget.cpp:225
Main (common) editor header.
virtual void draw_contents() override
virtual void draw()
Definition: widget.cpp:259
int w
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
bool mouse_locked() const
Definition: widget.cpp:70
void set_tooltip_string(const std::string &str)
Definition: widget.cpp:291
CVideo & video() const
Definition: widget.hpp:84
void adjust_size(const SDL_Rect &target) override
Update the size of this widget.
const gamemap & get_map() const
Definition: display.hpp:109
#define ERR_ED
void fill_rectangle(const SDL_Rect &rect, const color_t &color)
Draws a filled rectangle.
Definition: rect.cpp:65
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:237
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:68
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
map_location special_location(const std::string &id) const
Definition: map.cpp:309
friend bool operator==(state_t r, state_t l)
std::vector< std::string > items_
virtual sdl_handler_vector handler_members() override
widget(const widget &o)
Definition: widget.cpp:33