The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
text_box.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2016 by Mark de Wever <[email protected]>
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-lib"
16 
17 #include "gui/widgets/text_box.hpp"
18 
19 #include "font.hpp"
20 #include "gui/core/log.hpp"
22 #include "gui/widgets/settings.hpp"
23 #include "gui/widgets/window.hpp"
24 #include "game_preferences.hpp"
26 #include "utils/functional.hpp"
27 
28 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
29 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
30 
31 namespace gui2
32 {
33 
34 // ------------ WIDGET -----------{
35 
36 REGISTER_WIDGET(text_box)
37 
39  const bool enabled)
40 {
41  std::vector<std::string>* vec = preferences::get_history(id);
42  return ttext_history(vec, enabled);
43 }
44 
46 {
47  if(!enabled_) {
48  return;
49  } else {
50  if(!text.empty() && (history_->empty() || text != history_->back())) {
51  history_->push_back(text);
52  }
53 
54  pos_ = history_->size();
55  }
56 }
57 
59 {
60 
61  if(!enabled_) {
62  return "";
63  } else if(pos_ == history_->size()) {
64  unsigned curr = pos_;
65  push(text);
66  pos_ = curr;
67  }
68 
69  if(pos_ != 0) {
70  --pos_;
71  }
72 
73  return get_value();
74 }
75 
77 {
78  if(!enabled_) {
79  return "";
80  } else if(pos_ == history_->size()) {
81  push(text);
82  } else {
83  pos_++;
84  }
85 
86  return get_value();
87 }
88 
90 {
91  if(!enabled_ || pos_ == history_->size()) {
92  return "";
93  } else {
94  return history_->at(pos_);
95  }
96 }
97 
99  : ttext_()
100  , history_()
101  , max_input_length_(0)
102  , text_x_offset_(0)
103  , text_y_offset_(0)
104  , text_height_(0)
105  , dragging_(false)
106 {
108 
109  connect_signal<event::MOUSE_MOTION>(std::bind(
110  &ttext_box::signal_handler_mouse_motion, this, _2, _3, _5));
111  connect_signal<event::LEFT_BUTTON_DOWN>(std::bind(
113  connect_signal<event::LEFT_BUTTON_UP>(std::bind(
115  connect_signal<event::LEFT_BUTTON_DOUBLE_CLICK>(std::bind(
117 }
118 
119 void ttext_box::place(const tpoint& origin, const tpoint& size)
120 {
121  // Inherited.
122  tcontrol::place(origin, size);
123 
126 
128 
129  update_offsets();
130 }
131 
133 {
134  /***** Gather the info *****/
135 
136  // Set the cursor info.
137  const unsigned start = get_selection_start();
138  const int length = get_selection_length();
139 
141 
142  PangoEllipsizeMode ellipse_mode = PANGO_ELLIPSIZE_NONE;
143  if(!can_wrap()) {
144  if((start + length) > (get_length() / 2)) {
145  ellipse_mode = PANGO_ELLIPSIZE_START;
146  } else {
147  ellipse_mode = PANGO_ELLIPSIZE_END;
148  }
149  }
150  set_ellipse_mode(ellipse_mode);
151 
152  // Set the selection info
153  unsigned start_offset = 0;
154  unsigned end_offset = 0;
155  if(length == 0) {
156  // No nothing.
157  } else if(length > 0) {
158  start_offset = get_cursor_position(start).x;
159  end_offset = get_cursor_position(start + length).x;
160  } else {
161  start_offset = get_cursor_position(start + length).x;
162  end_offset = get_cursor_position(start).x;
163  }
164 
165  /***** Set in all canvases *****/
166 
167  const int max_width = get_text_maximum_width();
168  const int max_height = get_text_maximum_height();
169 
170  for(auto & tmp : canvas())
171  {
172 
173  tmp.set_variable("text", variant(get_value()));
174  tmp.set_variable("text_x_offset", variant(text_x_offset_));
175  tmp.set_variable("text_y_offset", variant(text_y_offset_));
176  tmp.set_variable("text_maximum_width", variant(max_width));
177  tmp.set_variable("text_maximum_height", variant(max_height));
178 
179  tmp.set_variable("cursor_offset",
180  variant(get_cursor_position(start + length).x));
181 
182  tmp.set_variable("selection_offset", variant(start_offset));
183  tmp.set_variable("selection_width", variant(end_offset - start_offset));
184  tmp.set_variable("text_wrap_mode", variant(ellipse_mode));
185  }
186 }
187 
188 void ttext_box::delete_char(const bool before_cursor)
189 {
190  if(before_cursor) {
191  set_cursor(get_selection_start() - 1, false);
192  }
193 
195 
197 }
198 
200 {
201  if(get_selection_length() == 0) {
202  return;
203  }
204 
205  // If we have a negative range change it to a positive range.
206  // This makes the rest of the algoritms easier.
207  int len = get_selection_length();
208  unsigned start = get_selection_start();
209  if(len < 0) {
210  len = -len;
211  start -= len;
212  }
213 
214  utf8::string tmp = get_value();
215  set_value(utf8::erase(tmp, start, len));
216  set_cursor(start, false);
217 }
218 
219 void ttext_box::handle_mouse_selection(tpoint mouse, const bool start_selection)
220 {
221  mouse.x -= get_x();
222  mouse.y -= get_y();
223  // FIXME we don't test for overflow in width
224  if(mouse.x < static_cast<int>(text_x_offset_)
225  || mouse.y < static_cast<int>(text_y_offset_)
226  || mouse.y >= static_cast<int>(text_y_offset_ + text_height_)) {
227  return;
228  }
229 
231  mouse.y - text_y_offset_)).x;
232 
233  if(offset < 0) {
234  return;
235  }
236 
237 
238  set_cursor(offset, !start_selection);
239  update_canvas();
240  set_is_dirty(true);
241  dragging_ |= start_selection;
242 }
243 
245 {
246  assert(config());
247 
249  conf = boost::dynamic_pointer_cast<const ttext_box_definition::tresolution>(
250  config());
251 
252  assert(conf);
253 
254  text_height_ = font::get_max_height(conf->text_font_size);
255 
257  variables.add("height", variant(get_height()));
258  variables.add("width", variant(get_width()));
259  variables.add("text_font_height", variant(text_height_));
260 
261  text_x_offset_ = conf->text_x_offset(variables);
262  text_y_offset_ = conf->text_y_offset(variables);
263 
264  // Since this variable doesn't change set it here instead of in
265  // update_canvas().
266  for(auto & tmp : canvas())
267  {
268  tmp.set_variable("text_font_height", variant(text_height_));
269  }
270 
271  // Force an update of the canvas since now text_font_height is known.
272  update_canvas();
273 }
274 
276 {
277  if(!history_.get_enabled()) {
278  return false;
279  }
280 
281  const std::string str = history_.up(get_value());
282  if(!str.empty()) {
283  set_value(str);
284  }
285  return true;
286 }
287 
289 {
290  if(!history_.get_enabled()) {
291  return false;
292  }
293 
294  const std::string str = history_.down(get_value());
295  if(!str.empty()) {
296  set_value(str);
297  }
298  return true;
299 }
300 
301 void ttext_box::handle_key_default(bool& handled,
302  SDLKey key,
303  SDLMod modifier,
304  const utf8::string& unicode)
305 {
306  if(key == SDLK_TAB && (modifier & KMOD_CTRL)) {
307  if(!(modifier & KMOD_SHIFT)) {
308  handled = history_up();
309  } else {
310  handled = history_down();
311  }
312  }
313 
314  if(!handled) {
315  // Inherited.
316  ttext_::handle_key_default(handled, key, modifier, unicode);
317  }
318 }
319 
320 void ttext_box::handle_key_clear_line(SDLMod /*modifier*/, bool& handled)
321 {
322  handled = true;
323 
324  set_value("");
325 }
326 
328 {
329  assert(config());
330 
332  conf = boost::dynamic_pointer_cast<const ttext_box_definition::tresolution>(
333  config());
334 
335  assert(conf);
336 
337  set_font_size(conf->text_font_size);
338  set_font_style(conf->text_font_style);
339 
340  update_offsets();
341 }
342 
344 {
345  static const std::string type = "text_box";
346  return type;
347 }
348 
350  bool& handled,
351  const tpoint& coordinate)
352 {
353  DBG_GUI_E << get_control_type() << "[" << id() << "]: " << event << ".\n";
354 
355  if(dragging_) {
356  handle_mouse_selection(coordinate, false);
357  }
358 
359  handled = true;
360 }
361 
363  bool& handled)
364 {
365  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
366 
367  /*
368  * Copied from the base class see how we can do inheritance with the new
369  * system...
370  */
371  get_window()->keyboard_capture(this);
373 
375 
376  handled = true;
377 }
378 
380  bool& handled)
381 {
382  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
383 
384  dragging_ = false;
385  handled = true;
386 }
387 
388 void
390  bool& handled)
391 {
392  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
393 
394  select_all();
395  handled = true;
396 }
397 
398 // }---------- DEFINITION ---------{
399 
401  : tcontrol_definition(cfg)
402 {
403  DBG_GUI_P << "Parsing text_box " << id << '\n';
404 
405  load_resolutions<tresolution>(cfg);
406 }
407 
408 /*WIKI
409  * @page = GUIWidgetDefinitionWML
410  * @order = 1_text_box
411  *
412  * == Text box ==
413  *
414  * The definition of a text box.
415  *
416  * @begin{parent}{name="gui/"}
417  * @begin{tag}{name="text_box_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
418  * The resolution for a text box also contains the following keys:
419  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super=generic/widget_definition/resolution}
420  * @begin{table}{config}
421  * text_x_offset & f_unsigned & "" & The x offset of the text in the text
422  * box. This is needed for the code to
423  * determine where in the text the mouse
424  * clicks, so it can set the cursor
425  * properly. $
426  * text_y_offset & f_unsigned & "" & The y offset of the text in the text
427  * box. $
428  * @end{table}
429  *
430  * The following states exist:
431  * * state_enabled, the text box is enabled.
432  * * state_disabled, the text box is disabled.
433  * * state_focused, the text box has the focus of the keyboard.
434  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
435  * @end{tag}{name="state_enabled"}
436  * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
437  * @end{tag}{name="state_disabled"}
438  * @begin{tag}{name="state_focused"}{min=0}{max=1}{super="generic/state"}
439  * @end{tag}{name="state_focused"}
440  * @end{tag}{name="resolution"}
441  * @end{tag}{name="text_box_definition"}
442  * @end{parent}{name="gui/"}
443  */
446  , text_x_offset(cfg["text_x_offset"])
447  , text_y_offset(cfg["text_y_offset"])
448 {
449  // Note the order should be the same as the enum tstate in text_box.hpp.
450  state.push_back(tstate_definition(cfg.child("state_enabled")));
451  state.push_back(tstate_definition(cfg.child("state_disabled")));
452  state.push_back(tstate_definition(cfg.child("state_focused")));
453 }
454 
455 // }---------- BUILDER -----------{
456 
457 /*WIKI
458  * @page = GUIWidgetInstanceWML
459  * @order = 2_text_box
460  *
461  * == Text box ==
462  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
463  * @begin{tag}{name="text_box"}{min="0"}{max="-1"}{super="generic/widget_instance"}
464  * @begin{table}{config}
465  * label & t_string & "" & The initial text of the text box. $
466  * history & string & "" & The name of the history for the text
467  * box.
468  * A history saves the data entered in a
469  * text box between the games. With the up
470  * and down arrow it can be accessed. To
471  * create a new history item just add a
472  * new unique name for this field and the
473  * engine will handle the rest. $
474  * @end{table}
475  * @end{tag}{name="text_box"}
476  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
477  */
478 
479 namespace implementation
480 {
481 
482 tbuilder_text_box::tbuilder_text_box(const config& cfg)
483  : tbuilder_control(cfg)
484  , history(cfg["history"])
485  , max_input_length(cfg["max_input_length"])
486 {
487 }
488 
490 {
491  ttext_box* widget = new ttext_box();
492 
493  init_control(widget);
494 
495  // A textbox doesn't have a label but a text
496  widget->set_value(label);
497 
498  if(!history.empty()) {
499  widget->set_history(history);
500  }
501 
503 
504  DBG_GUI_G << "Window builder: placed text box '" << id
505  << "' with definition '" << definition << "'.\n";
506 
507  return widget;
508 }
509 
510 } // namespace implementation
511 
512 // }------------ END --------------
513 
514 } // namespace gui2
Define the common log macros for the gui toolkit.
unsigned get_width() const
Definition: widget.cpp:294
#define DBG_GUI_P
Definition: log.hpp:69
void set_selection_length(const int selection_length)
Definition: text.cpp:192
void handle_key_clear_line(SDLMod modifier, bool &handled)
Inherited from ttext_.
Definition: text_box.cpp:320
unsigned pos_
The current position in the history.
Definition: text_box.hpp:111
Class for text input history.
Definition: text_box.hpp:32
#define SDLMod
Definition: compat.hpp:30
bool dragging_
Is the mouse in dragging mode, this affects selection in mouse move.
Definition: text_box.hpp:210
GLenum GLsizei const GLuint GLboolean enabled
Definition: glew.h:2497
void set_font_size(const unsigned font_size)
Definition: text.hpp:191
void set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.hpp:211
virtual bool can_wrap() const
Can the widget wrap.
Definition: widget.cpp:211
ttext_history history_
The history text for this widget.
Definition: text_box.hpp:179
unsigned text_y_offset_
The y offset in the widget where the text starts.
Definition: text_box.hpp:197
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
unsigned text_x_offset_
The x offset in the widget where the text starts.
Definition: text_box.hpp:190
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: text_box.cpp:119
bool get_enabled() const
Definition: text_box.hpp:96
void set_font_style(const unsigned font_style)
Definition: text.hpp:196
tresolution_definition_ptr config()
Definition: control.hpp:299
This file contains the window object, this object is a top level container which has the event manage...
void set_maximum_width(const int width)
Definition: text.hpp:201
std::string up(const std::string &text="")
One step up in the history.
Definition: text_box.cpp:58
bool history_up()
Goes one item up in the history.
Definition: text_box.cpp:275
void load_config_extra()
Inherited from tcontrol.
Definition: text_box.cpp:327
STL namespace.
int get_y() const
Definition: widget.cpp:289
void signal_handler_left_button_down(const event::tevent event, bool &handled)
Definition: text_box.cpp:362
Class for a single line text area.
Definition: text_box.hpp:118
#define LOG_HEADER
Definition: text_box.cpp:29
unsigned text_height_
The height of the text itself.
Definition: text_box.hpp:204
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:435
std::string get_value() const
Definition: text.hpp:76
size_t get_selection_start() const
Definition: text.hpp:218
void select_all()
Selects all text.
Definition: text.hpp:135
void update_offsets()
Updates text_x_offset_ and text_y_offset_.
Definition: text_box.cpp:244
virtual void set_value(const std::string &text)
The set_value is virtual for the tpassword_box class.
Definition: text.cpp:95
#define SDLKey
Definition: compat.hpp:29
virtual void update_canvas() override
See tcontrol::update_canvas.
Definition: text_box.cpp:132
Base class of a resolution, contains the common keys for a resolution.
tpoint get_column_line(const tpoint &position) const
Definition: text.hpp:186
GLuint GLsizei GLsizei * length
Definition: glew.h:1793
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
void init_control(tcontrol *control) const
Definition: control.cpp:675
std::string definition
Parameters for the control.
Definition: control.hpp:530
std::string get_value() const
Gets the current history value.
Definition: text_box.cpp:89
GLintptr offset
Definition: glew.h:1650
This file contains the settings handling of the widget library.
int y
y coordinate.
Definition: point.hpp:34
GLenum GLsizei len
Definition: glew.h:5662
int get_text_maximum_width() const
Returns the maximum width available for the text.
Definition: control.cpp:388
GLuint start
Definition: glew.h:1221
size_t get_length() const
Definition: text.hpp:62
tpoint get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:149
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: control.cpp:250
void push(const std::string &text)
Push string into the history.
Definition: text_box.cpp:45
int get_x() const
Definition: widget.cpp:284
void signal_handler_left_button_double_click(const event::tevent event, bool &handled)
Definition: text_box.cpp:389
tevent
The event send to the dispatcher.
Definition: handler.hpp:54
void set_wants_mouse_left_double_click(const bool click=true)
int get_max_height(int size)
Definition: font.cpp:960
void handle_key_default(bool &handled, SDLKey key, SDLMod modifier, const utf8::string &unicode)
Inherited from ttext_.
Definition: text_box.cpp:301
Contains the state info for a resolution.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
int x
x coordinate.
Definition: point.hpp:31
ttext_box_definition(const config &cfg)
Definition: text_box.cpp:400
bool enabled_
Is the history enabled.
Definition: text_box.hpp:114
std::vector< std::string > * get_history(const std::string &id)
Returns a pointer to the history vector associated with given id making a new one if it doesn't exist...
void handle_mouse_selection(tpoint mouse, const bool start_selection)
Definition: text_box.cpp:219
#define DBG_GUI_E
Definition: log.hpp:35
virtual void handle_key_default(bool &handled, SDLKey key, SDLMod modifier, const utf8::string &unicode)
Default key handler if none of the above functions is called.
Definition: text.cpp:282
size_t get_selection_length() const
Definition: text.hpp:224
void delete_selection()
Inherited from ttext_.
Definition: text_box.cpp:199
map_location curr
Definition: astarsearch.cpp:67
unsigned get_height() const
Definition: widget.cpp:299
const std::string & id() const
Definition: widget.cpp:109
std::string down(const std::string &text="")
One step down in the history.
Definition: text_box.cpp:76
Holds a 2D point.
Definition: point.hpp:24
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
std::vector< tcanvas > & canvas()
Definition: control.hpp:282
void set_max_input_length(const size_t length)
Definition: text_box.hpp:136
map_formula_callable & add(const std::string &key, const variant &value)
Definition: formula.cpp:41
void set_history(const std::string &id)
Definition: text_box.hpp:131
GLsizeiptr size
Definition: glew.h:1649
gui2::tpoint get_cursor_position(const unsigned column, const unsigned line=0) const
Definition: text.hpp:180
std::vector< tstate_definition > state
cl_event event
Definition: glew.h:3070
int get_text_maximum_height() const
Returns the maximum height available for the text.
Definition: control.cpp:396
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
Base class for all widgets.
Definition: widget.hpp:49
void mouse_capture(const bool capture=true)
Definition: window.cpp:1388
void signal_handler_mouse_motion(const event::tevent event, bool &handled, const tpoint &coordinate)
Definition: text_box.cpp:349
std::vector< std::string > * history_
The items in the history.
Definition: text_box.hpp:108
void signal_handler_left_button_up(const event::tevent event, bool &handled)
Definition: text_box.cpp:379
void delete_char(const bool before_cursor)
Inherited from ttext_.
Definition: text_box.cpp:188
void set_maximum_length(const size_t maximum_length)
Definition: text.cpp:73
void keyboard_capture(twidget *widget)
Definition: window.cpp:1394
twindow * get_window()
Get the parent window.
Definition: widget.cpp:116
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
size_t max_input_length_
The maximum length of the text input.
Definition: text_box.hpp:182
#define DBG_GUI_G
Definition: log.hpp:41
GLsizei const GLcharARB ** string
Definition: glew.h:4503
Abstract base class for text items.
Definition: text.hpp:43
Contains the implementation details for lexical_cast and shouldn't be used directly.
bool history_down()
Goes one item down in the history.
Definition: text_box.cpp:288
std::string string
void set_cursor(const size_t offset, const bool select)
Moves the cursor at the wanted position.
Definition: text.cpp:108
utf8::string & erase(utf8::string &str, const size_t start, const size_t len)
Erases a portion of a UTF-8 string.
Definition: unicode.cpp:106
virtual const std::string & get_control_type() const override
See tcontrol::get_control_type.
Definition: text_box.cpp:343
void set_maximum_height(const int height, const bool multiline)
Definition: text.hpp:206