The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
text.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.hpp"
18 
19 #include "desktop/clipboard.hpp"
20 #include "gui/core/log.hpp"
23 
24 #include "utils/functional.hpp"
25 
26 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
27 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
28 
29 namespace gui2
30 {
31 
33  : tcontrol(COUNT)
34  , state_(ENABLED)
35  , text_()
36  , selection_start_(0)
37  , selection_length_(0)
38  , text_changed_callback_()
39 {
40 #ifdef __unix__
41  // pastes on UNIX systems.
42  connect_signal<event::MIDDLE_BUTTON_CLICK>(std::bind(
44 
45 #endif
46 
47  connect_signal<event::SDL_KEY_DOWN>(std::bind(
48  &ttext_::signal_handler_sdl_key_down, this, _2, _3, _5, _6, _7));
49 
50  connect_signal<event::RECEIVE_KEYBOARD_FOCUS>(std::bind(
52  connect_signal<event::LOSE_KEYBOARD_FOCUS>(
53  std::bind(&ttext_::signal_handler_lose_keyboard_focus, this, _2));
54 }
55 
56 void ttext_::set_active(const bool active)
57 {
58  if(get_active() != active) {
59  set_state(active ? ENABLED : DISABLED);
60  }
61 }
62 
63 bool ttext_::get_active() const
64 {
65  return state_ != DISABLED;
66 }
67 
68 unsigned ttext_::get_state() const
69 {
70  return state_;
71 }
72 
73 void ttext_::set_maximum_length(const size_t maximum_length)
74 {
75  if(maximum_length == 0) {
76  return;
77  }
78 
79  const bool need_update = text_.get_length() > maximum_length;
80 
81  text_.set_maximum_length(maximum_length);
82 
83  if(need_update) {
84  if(selection_start_ > maximum_length) {
85  selection_start_ = maximum_length;
87  } else if(selection_start_ + selection_length_ > maximum_length) {
88  selection_length_ = maximum_length - selection_start_;
89  }
90  update_canvas();
91  set_is_dirty(true);
92  }
93 }
94 
95 void ttext_::set_value(const std::string& text)
96 {
97  if(text != text_.text()) {
98  text_.set_text(text, false);
99 
100  // default to put the cursor at the end of the buffer.
102  selection_length_ = 0;
103  update_canvas();
104  set_is_dirty(true);
105  }
106 }
107 
108 void ttext_::set_cursor(const size_t offset, const bool select)
109 {
110  if(select) {
111 
112  if(selection_start_ == offset) {
113  selection_length_ = 0;
114  } else {
115  selection_length_ = -static_cast<int>(selection_start_ - offset);
116  }
117 
118 #ifdef __unix__
119  // selecting copies on UNIX systems.
120  copy_selection(true);
121 #endif
122  update_canvas();
123  set_is_dirty(true);
124 
125  } else {
126  assert(offset <= text_.get_length());
128  selection_length_ = 0;
129 
130  update_canvas();
131  set_is_dirty(true);
132  }
133 }
134 
135 void ttext_::insert_char(const utf8::string& unicode)
136 {
138 
139  if(text_.insert_text(selection_start_, unicode)) {
140 
141  // Update status
142  set_cursor(selection_start_ + 1, false);
143  update_canvas();
144  set_is_dirty(true);
145  }
146 }
147 
148 void ttext_::copy_selection(const bool mouse)
149 {
150  if(selection_length_ == 0) {
151  return;
152  }
153 
154  unsigned end, start = selection_start_;
155  const utf8::string txt = text_.text();
156 
157  if(selection_length_ > 0) {
158  end = utf8::index(txt, start + selection_length_);
159  start = utf8::index(txt, start);
160  } else {
161  // inverse selection: selection_start_ is in fact the end
162  end = utf8::index(txt, start);
163  start = utf8::index(txt, start + selection_length_);
164  }
165  desktop::clipboard::copy_to_clipboard(txt.substr(start, end - start), mouse);
166 }
167 
168 void ttext_::paste_selection(const bool mouse)
169 {
171  if(text.empty()) {
172  return;
173  }
174 
176 
178 
179  update_canvas();
180  set_is_dirty(true);
181  fire(event::NOTIFY_MODIFIED, *this, nullptr);
182 }
183 
184 void ttext_::set_selection_start(const size_t selection_start)
185 {
186  if(selection_start != selection_start_) {
187  selection_start_ = selection_start;
188  set_is_dirty(true);
189  }
190 }
191 
192 void ttext_::set_selection_length(const int selection_length)
193 {
194  if(selection_length != selection_length_) {
195  selection_length_ = selection_length;
196  set_is_dirty(true);
197  }
198 }
199 
200 void ttext_::set_state(const tstate state)
201 {
202  if(state != state_) {
203  state_ = state;
204  set_is_dirty(true);
205  }
206 }
207 
208 void ttext_::handle_key_left_arrow(SDLMod modifier, bool& handled)
209 {
210  /** @todo implement the ctrl key. */
211  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
212 
213  handled = true;
214  const int offset = selection_start_ - 1 + selection_length_;
215  if(offset >= 0) {
216  set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
217  }
218 }
219 
220 void ttext_::handle_key_right_arrow(SDLMod modifier, bool& handled)
221 {
222  /** @todo implement the ctrl key. */
223  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
224 
225  handled = true;
226  const size_t offset = selection_start_ + 1 + selection_length_;
227  if(offset <= text_.get_length()) {
228  set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
229  }
230 }
231 
232 void ttext_::handle_key_home(SDLMod modifier, bool& handled)
233 {
234  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
235 
236  handled = true;
237  if(modifier & KMOD_CTRL) {
238  goto_start_of_data((modifier & KMOD_SHIFT) != 0);
239  } else {
240  goto_start_of_line((modifier & KMOD_SHIFT) != 0);
241  }
242 }
243 
244 void ttext_::handle_key_end(SDLMod modifier, bool& handled)
245 {
246  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
247 
248  handled = true;
249  if(modifier & KMOD_CTRL) {
250  goto_end_of_data((modifier & KMOD_SHIFT) != 0);
251  } else {
252  goto_end_of_line((modifier & KMOD_SHIFT) != 0);
253  }
254 }
255 
256 void ttext_::handle_key_backspace(SDLMod /*modifier*/, bool& handled)
257 {
258  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
259 
260  handled = true;
261  if(selection_length_ != 0) {
263  } else if(selection_start_) {
264  delete_char(true);
265  }
266  fire(event::NOTIFY_MODIFIED, *this, nullptr);
267 }
268 
269 void ttext_::handle_key_delete(SDLMod /*modifier*/, bool& handled)
270 {
271  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
272 
273  handled = true;
274  if(selection_length_ != 0) {
276  } else if(selection_start_ < text_.get_length()) {
277  delete_char(false);
278  }
279  fire(event::NOTIFY_MODIFIED, *this, nullptr);
280 }
281 
282 void ttext_::handle_key_default(bool& handled,
283  SDLKey /*key*/,
284  SDLMod /*modifier*/,
285  const utf8::string& unicode)
286 {
287  DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
288 
289  if(unicode.size() > 1 || unicode[0] != 0) {
290  handled = true;
291  insert_char(unicode);
292  fire(event::NOTIFY_MODIFIED, *this, nullptr);
293  }
294 }
295 
297  bool& handled)
298 {
299  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
300 
301  paste_selection(true);
302 
303  handled = true;
304 }
305 
307  bool& handled,
308  const SDLKey key,
309  SDLMod modifier,
310  const utf8::string& unicode)
311 {
312 
313  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
314 
315 // For copy/paste we use a different key on the MAC. Other ctrl modifiers won't
316 // be modifed seems not to be required when I read the comment in
317 // widgets/textbox.cpp:516. Would be nice if somebody on a MAC would test it.
318 #ifdef __APPLE__
319  const unsigned copypaste_modifier = KMOD_LMETA | KMOD_RMETA;
320 #else
321  const unsigned copypaste_modifier = KMOD_CTRL;
322 #endif
323 
324  switch(key) {
325 
326  case SDLK_LEFT:
327  handle_key_left_arrow(modifier, handled);
328  break;
329 
330  case SDLK_RIGHT:
331  handle_key_right_arrow(modifier, handled);
332  break;
333 
334  case SDLK_UP:
335  handle_key_up_arrow(modifier, handled);
336  break;
337 
338  case SDLK_DOWN:
339  handle_key_down_arrow(modifier, handled);
340  break;
341 
342  case SDLK_PAGEUP:
343  handle_key_page_up(modifier, handled);
344  break;
345 
346  case SDLK_PAGEDOWN:
347  handle_key_page_down(modifier, handled);
348  break;
349 
350  case SDLK_a:
351  if(!(modifier & KMOD_CTRL)) {
352  handle_key_default(handled, key, modifier, unicode);
353  break;
354  }
355 
356  // If ctrl-a is used for home drop the control modifier
357  modifier = static_cast<SDLMod>(modifier & ~KMOD_CTRL);
358  /* FALL DOWN */
359 
360  case SDLK_HOME:
361  handle_key_home(modifier, handled);
362  break;
363 
364  case SDLK_e:
365  if(!(modifier & KMOD_CTRL)) {
366  handle_key_default(handled, key, modifier, unicode);
367  break;
368  }
369 
370  // If ctrl-e is used for end drop the control modifier
371  modifier = static_cast<SDLMod>(modifier & ~KMOD_CTRL);
372  /* FALL DOWN */
373 
374  case SDLK_END:
375  handle_key_end(modifier, handled);
376  break;
377 
378  case SDLK_BACKSPACE:
379  handle_key_backspace(modifier, handled);
380  break;
381 
382  case SDLK_u:
383  if(modifier & KMOD_CTRL) {
384  handle_key_clear_line(modifier, handled);
385  } else {
386  handle_key_default(handled, key, modifier, unicode);
387  }
388  break;
389 
390  case SDLK_DELETE:
391  handle_key_delete(modifier, handled);
392  break;
393 
394  case SDLK_c:
395  if(!(modifier & copypaste_modifier)) {
396  handle_key_default(handled, key, modifier, unicode);
397  break;
398  }
399 
400  // atm we don't care whether there is something to copy or paste
401  // if nothing is there we still don't want to be chained.
402  copy_selection(false);
403  handled = true;
404  break;
405 
406  case SDLK_x:
407  if(!(modifier & copypaste_modifier)) {
408  handle_key_default(handled, key, modifier, unicode);
409  break;
410  }
411 
412  copy_selection(false);
414  handled = true;
415  break;
416 
417  case SDLK_v:
418  if(!(modifier & copypaste_modifier)) {
419  handle_key_default(handled, key, modifier, unicode);
420  break;
421  }
422 
423  paste_selection(false);
424  handled = true;
425  break;
426 
427  default:
428  handle_key_default(handled, key, modifier, unicode);
429  }
430 
432  text_changed_callback_(this, this->text());
433  }
434 }
435 
437 {
438  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
439 
441 }
442 
444 {
445  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
446 
448 }
449 
450 } // namespace gui2
Define the common log macros for the gui toolkit.
void set_selection_length(const int selection_length)
Definition: text.cpp:192
virtual void delete_char(const bool before_cursor)=0
Deletes the character.
virtual void delete_selection()=0
Deletes the current selection.
font::ttext text_
The text entered in the widget.
Definition: text.hpp:252
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:73
#define SDLMod
Definition: compat.hpp:30
virtual void handle_key_left_arrow(SDLMod modifier, bool &handled)
Left arrow key pressed.
Definition: text.cpp:208
int selection_length_
Length of the selected text.
Definition: text.hpp:264
virtual void handle_key_end(SDLMod modifier, bool &handled)
End key pressed.
Definition: text.cpp:244
std::string copy_from_clipboard(const bool)
Copies text from the clipboard.
Definition: clipboard.cpp:45
const std::string & text() const
Definition: text.hpp:220
virtual void set_active(const bool active) override
See tcontrol::set_active.
Definition: text.cpp:56
ttext & set_maximum_length(const size_t maximum_length)
Definition: text.cpp:533
unsigned insert_text(const unsigned offset, const std::string &text)
Inserts UTF-8 text.
Definition: text.cpp:204
virtual void paste_selection(const bool mouse)
Pastes the current selection.
Definition: text.cpp:168
tstate
Note the order of the states must be the same as defined in settings.hpp.
Definition: text.hpp:234
void signal_handler_lose_keyboard_focus(const event::tevent event)
Definition: text.cpp:443
void signal_handler_sdl_key_down(const event::tevent event, bool &handled, const SDLKey key, SDLMod modifier, const utf8::string &unicode)
Definition: text.cpp:306
tformula< t_string > text_
The text to draw.
Definition: canvas.cpp:1268
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:435
virtual void handle_key_right_arrow(SDLMod modifier, bool &handled)
Right arrow key pressed.
Definition: text.cpp:220
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
std::function< void(ttext_ *textbox, const std::string text)> text_changed_callback_
Text changed callback.
Definition: text.hpp:438
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
virtual void handle_key_backspace(SDLMod modifier, bool &handled)
Backspace key pressed.
Definition: text.cpp:256
#define LOG_HEADER
Definition: text.cpp:27
GLintptr offset
Definition: glew.h:1650
void goto_end_of_data(const bool select=false)
Moves the cursor to the end of all text.
Definition: text.hpp:110
virtual void insert_char(const utf8::string &unicode)
Inserts a character at the cursor.
Definition: text.cpp:135
GLuint GLuint end
Definition: glew.h:1221
tstate state_
Current state of the widget.
Definition: text.hpp:249
size_t selection_start_
Start of the selected text.
Definition: text.hpp:255
void goto_start_of_data(const bool select=false)
Moves the cursor to the beginning of the data.
Definition: text.hpp:129
bool fire(const tevent event, twidget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:144
virtual void handle_key_clear_line(SDLMod modifier, bool &handled)=0
Clears the current line.
size_t get_length() const
Gets the length of the text in characters.
Definition: text.hpp:202
GLuint start
Definition: glew.h:1221
Send by a widget to notify others its contents or state are modified.
Definition: handler.hpp:133
virtual void handle_key_up_arrow(SDLMod modifier, bool &handled)=0
Every key can have several behaviors.
tevent
The event send to the dispatcher.
Definition: handler.hpp:54
virtual void handle_key_delete(SDLMod modifier, bool &handled)
Delete key pressed.
Definition: text.cpp:269
virtual unsigned get_state() const override
See tcontrol::get_state.
Definition: text.cpp:68
#define KMOD_RMETA
Definition: compat.hpp:36
virtual void handle_key_down_arrow(SDLMod modifier, bool &handled)=0
Down arrow key pressed.
virtual void update_canvas()
Updates the canvas(ses).
Definition: control.cpp:364
#define DBG_GUI_E
Definition: log.hpp:35
virtual void goto_start_of_line(const bool select=false)=0
Moves the cursor to the beginning of the line.
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
void signal_handler_receive_keyboard_focus(const event::tevent event)
Definition: text.cpp:436
virtual void handle_key_page_down(SDLMod, bool &)
Page down key.
Definition: text.hpp:406
virtual void handle_key_home(SDLMod modifier, bool &handled)
Home key pressed.
Definition: text.cpp:232
virtual void handle_key_page_up(SDLMod, bool &)
Page up key.
Definition: text.hpp:394
Base class for all visible items.
Definition: control.hpp:34
void set_selection_start(const size_t selection_start)
Definition: text.cpp:184
void signal_handler_middle_button_click(const event::tevent event, bool &handled)
Definition: text.cpp:296
void copy_to_clipboard(const std::string &text, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:40
cl_event event
Definition: glew.h:3070
#define KMOD_LMETA
Definition: compat.hpp:35
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:360
void set_maximum_length(const size_t maximum_length)
Definition: text.cpp:73
virtual bool get_active() const override
See tcontrol::get_active.
Definition: text.cpp:63
const std::string & text() const
Definition: text.hpp:81
virtual void goto_end_of_line(const bool select=false)=0
Moves the cursor to the end of the line.
void set_state(const tstate state)
Definition: text.cpp:200
virtual void copy_selection(const bool mouse)
Copies the current selection.
Definition: text.cpp:148
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::string string
void set_cursor(const size_t offset, const bool select)
Moves the cursor at the wanted position.
Definition: text.cpp:108
#define LOG_SCOPE_HEADER
Definition: text.cpp:26