OpenTTD Source  1.11.0-beta2
textbuf.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include <stdarg.h>
12 
13 #include "textbuf_type.h"
14 #include "string_func.h"
15 #include "strings_func.h"
16 #include "gfx_type.h"
17 #include "gfx_func.h"
18 #include "window_func.h"
19 #include "core/alloc_func.hpp"
20 
21 #include "safeguards.h"
22 
31 bool GetClipboardContents(char *buffer, const char *last);
32 
33 int _caret_timer;
34 
35 
42 bool Textbuf::CanDelChar(bool backspace)
43 {
44  return backspace ? this->caretpos != 0 : this->caretpos < this->bytes - 1;
45 }
46 
53 bool Textbuf::DeleteChar(uint16 keycode)
54 {
55  bool word = (keycode & WKC_CTRL) != 0;
56 
57  keycode &= ~WKC_SPECIAL_KEYS;
58  if (keycode != WKC_BACKSPACE && keycode != WKC_DELETE) return false;
59 
60  bool backspace = keycode == WKC_BACKSPACE;
61 
62  if (!CanDelChar(backspace)) return false;
63 
64  char *s = this->buf + this->caretpos;
65  uint16 len = 0;
66 
67  if (word) {
68  /* Delete a complete word. */
69  if (backspace) {
70  /* Delete whitespace and word in front of the caret. */
71  len = this->caretpos - (uint16)this->char_iter->Prev(StringIterator::ITER_WORD);
72  s -= len;
73  } else {
74  /* Delete word and following whitespace following the caret. */
75  len = (uint16)this->char_iter->Next(StringIterator::ITER_WORD) - this->caretpos;
76  }
77  /* Update character count. */
78  for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) {
79  this->chars--;
80  }
81  } else {
82  /* Delete a single character. */
83  if (backspace) {
84  /* Delete the last code point in front of the caret. */
85  s = Utf8PrevChar(s);
86  WChar c;
87  len = (uint16)Utf8Decode(&c, s);
88  this->chars--;
89  } else {
90  /* Delete the complete character following the caret. */
91  len = (uint16)this->char_iter->Next(StringIterator::ITER_CHARACTER) - this->caretpos;
92  /* Update character count. */
93  for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) {
94  this->chars--;
95  }
96  }
97  }
98 
99  /* Move the remaining characters over the marker */
100  memmove(s, s + len, this->bytes - (s - this->buf) - len);
101  this->bytes -= len;
102 
103  if (backspace) this->caretpos -= len;
104 
105  this->UpdateStringIter();
106  this->UpdateWidth();
107  this->UpdateCaretPosition();
108  this->UpdateMarkedText();
109 
110  return true;
111 }
112 
117 {
118  memset(this->buf, 0, this->max_bytes);
119  this->bytes = this->chars = 1;
120  this->pixels = this->caretpos = this->caretxoffs = 0;
121  this->markpos = this->markend = this->markxoffs = this->marklength = 0;
122  this->UpdateStringIter();
123 }
124 
133 {
134  uint16 len = (uint16)Utf8CharLen(key);
135  if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) {
136  memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
137  Utf8Encode(this->buf + this->caretpos, key);
138  this->chars++;
139  this->bytes += len;
140  this->caretpos += len;
141 
142  this->UpdateStringIter();
143  this->UpdateWidth();
144  this->UpdateCaretPosition();
145  this->UpdateMarkedText();
146  return true;
147  }
148  return false;
149 }
150 
162 bool Textbuf::InsertString(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
163 {
164  uint16 insertpos = (marked && this->marklength != 0) ? this->markpos : this->caretpos;
165  if (insert_location != nullptr) {
166  insertpos = insert_location - this->buf;
167  if (insertpos > this->bytes) return false;
168 
169  if (replacement_end != nullptr) {
170  this->DeleteText(insertpos, replacement_end - this->buf, str == nullptr);
171  }
172  } else {
173  if (marked) this->DiscardMarkedText(str == nullptr);
174  }
175 
176  if (str == nullptr) return false;
177 
178  uint16 bytes = 0, chars = 0;
179  WChar c;
180  for (const char *ptr = str; (c = Utf8Consume(&ptr)) != '\0';) {
181  if (!IsValidChar(c, this->afilter)) break;
182 
183  byte len = Utf8CharLen(c);
184  if (this->bytes + bytes + len > this->max_bytes) break;
185  if (this->chars + chars + 1 > this->max_chars) break;
186 
187  bytes += len;
188  chars++;
189 
190  /* Move caret if needed. */
191  if (ptr == caret) this->caretpos = insertpos + bytes;
192  }
193 
194  if (bytes == 0) return false;
195 
196  if (marked) {
197  this->markpos = insertpos;
198  this->markend = insertpos + bytes;
199  }
200 
201  memmove(this->buf + insertpos + bytes, this->buf + insertpos, this->bytes - insertpos);
202  memcpy(this->buf + insertpos, str, bytes);
203 
204  this->bytes += bytes;
205  this->chars += chars;
206  if (!marked && caret == nullptr) this->caretpos += bytes;
207  assert(this->bytes <= this->max_bytes);
208  assert(this->chars <= this->max_chars);
209  this->buf[this->bytes - 1] = '\0'; // terminating zero
210 
211  this->UpdateStringIter();
212  this->UpdateWidth();
213  this->UpdateCaretPosition();
214  this->UpdateMarkedText();
215 
216  return true;
217 }
218 
226 {
227  char utf8_buf[512];
228 
229  if (!GetClipboardContents(utf8_buf, lastof(utf8_buf))) return false;
230 
231  return this->InsertString(utf8_buf, false);
232 }
233 
240 void Textbuf::DeleteText(uint16 from, uint16 to, bool update)
241 {
242  uint c = 0;
243  const char *s = this->buf + from;
244  while (s < this->buf + to) {
245  Utf8Consume(&s);
246  c++;
247  }
248 
249  /* Strip marked characters from buffer. */
250  memmove(this->buf + from, this->buf + to, this->bytes - to);
251  this->bytes -= to - from;
252  this->chars -= c;
253 
254  /* Fixup caret if needed. */
255  if (this->caretpos > from) {
256  if (this->caretpos <= to) {
257  this->caretpos = from;
258  } else {
259  this->caretpos -= to - from;
260  }
261  }
262 
263  if (update) {
264  this->UpdateStringIter();
265  this->UpdateCaretPosition();
266  this->UpdateMarkedText();
267  }
268 }
269 
274 void Textbuf::DiscardMarkedText(bool update)
275 {
276  if (this->markend == 0) return;
277 
278  this->DeleteText(this->markpos, this->markend, update);
279  this->markpos = this->markend = this->markxoffs = this->marklength = 0;
280 }
281 
284 {
285  this->char_iter->SetString(this->buf);
286  size_t pos = this->char_iter->SetCurPosition(this->caretpos);
287  this->caretpos = pos == StringIterator::END ? 0 : (uint16)pos;
288 }
289 
292 {
293  this->pixels = GetStringBoundingBox(this->buf, FS_NORMAL).width;
294 }
295 
298 {
299  this->caretxoffs = this->chars > 1 ? GetCharPosInString(this->buf, this->buf + this->caretpos, FS_NORMAL).x : 0;
300 }
301 
304 {
305  if (this->markend != 0) {
306  this->markxoffs = GetCharPosInString(this->buf, this->buf + this->markpos, FS_NORMAL).x;
307  this->marklength = GetCharPosInString(this->buf, this->buf + this->markend, FS_NORMAL).x - this->markxoffs;
308  } else {
309  this->markxoffs = this->marklength = 0;
310  }
311 }
312 
319 bool Textbuf::MovePos(uint16 keycode)
320 {
321  switch (keycode) {
322  case WKC_LEFT:
323  case WKC_CTRL | WKC_LEFT: {
324  if (this->caretpos == 0) break;
325 
326  size_t pos = this->char_iter->Prev(keycode & WKC_CTRL ? StringIterator::ITER_WORD : StringIterator::ITER_CHARACTER);
327  if (pos == StringIterator::END) return true;
328 
329  this->caretpos = (uint16)pos;
330  this->UpdateCaretPosition();
331  return true;
332  }
333 
334  case WKC_RIGHT:
335  case WKC_CTRL | WKC_RIGHT: {
336  if (this->caretpos >= this->bytes - 1) break;
337 
338  size_t pos = this->char_iter->Next(keycode & WKC_CTRL ? StringIterator::ITER_WORD : StringIterator::ITER_CHARACTER);
339  if (pos == StringIterator::END) return true;
340 
341  this->caretpos = (uint16)pos;
342  this->UpdateCaretPosition();
343  return true;
344  }
345 
346  case WKC_HOME:
347  this->caretpos = 0;
348  this->char_iter->SetCurPosition(this->caretpos);
349  this->UpdateCaretPosition();
350  return true;
351 
352  case WKC_END:
353  this->caretpos = this->bytes - 1;
354  this->char_iter->SetCurPosition(this->caretpos);
355  this->UpdateCaretPosition();
356  return true;
357 
358  default:
359  break;
360  }
361 
362  return false;
363 }
364 
371 Textbuf::Textbuf(uint16 max_bytes, uint16 max_chars)
372  : buf(MallocT<char>(max_bytes))
373 {
374  assert(max_bytes != 0);
375  assert(max_chars != 0);
376 
377  this->char_iter = StringIterator::Create();
378 
379  this->afilter = CS_ALPHANUMERAL;
380  this->max_bytes = max_bytes;
381  this->max_chars = max_chars == UINT16_MAX ? max_bytes : max_chars;
382  this->caret = true;
383  this->DeleteAll();
384 }
385 
386 Textbuf::~Textbuf()
387 {
388  delete this->char_iter;
389  free(this->buf);
390 }
391 
397 {
398  GetString(this->buf, string, &this->buf[this->max_bytes - 1]);
399  this->UpdateSize();
400 }
401 
406 void Textbuf::Assign(const char *text)
407 {
408  strecpy(this->buf, text, &this->buf[this->max_bytes - 1]);
409  this->UpdateSize();
410 }
411 
415 void Textbuf::Print(const char *format, ...)
416 {
417  va_list va;
418  va_start(va, format);
419  vseprintf(this->buf, &this->buf[this->max_bytes - 1], format, va);
420  va_end(va);
421  this->UpdateSize();
422 }
423 
424 
431 {
432  const char *buf = this->buf;
433 
434  this->chars = this->bytes = 1; // terminating zero
435 
436  WChar c;
437  while ((c = Utf8Consume(&buf)) != '\0') {
438  this->bytes += Utf8CharLen(c);
439  this->chars++;
440  }
441  assert(this->bytes <= this->max_bytes);
442  assert(this->chars <= this->max_chars);
443 
444  this->caretpos = this->bytes - 1;
445  this->UpdateStringIter();
446  this->UpdateWidth();
447  this->UpdateMarkedText();
448 
449  this->UpdateCaretPosition();
450 }
451 
457 {
458  /* caret changed? */
459  bool b = !!(_caret_timer & 0x20);
460 
461  if (b != this->caret) {
462  this->caret = b;
463  return true;
464  }
465  return false;
466 }
467 
468 HandleKeyPressResult Textbuf::HandleKeyPress(WChar key, uint16 keycode)
469 {
470  bool edited = false;
471 
472  switch (keycode) {
473  case WKC_ESC: return HKPR_CANCEL;
474 
475  case WKC_RETURN: case WKC_NUM_ENTER: return HKPR_CONFIRM;
476 
477  case (WKC_CTRL | 'V'):
478  edited = this->InsertClipboard();
479  break;
480 
481  case (WKC_CTRL | 'U'):
482  this->DeleteAll();
483  edited = true;
484  break;
485 
486  case WKC_BACKSPACE: case WKC_DELETE:
487  case WKC_CTRL | WKC_BACKSPACE: case WKC_CTRL | WKC_DELETE:
488  edited = this->DeleteChar(keycode);
489  break;
490 
491  case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
492  case WKC_CTRL | WKC_LEFT: case WKC_CTRL | WKC_RIGHT:
493  this->MovePos(keycode);
494  break;
495 
496  default:
497  if (IsValidChar(key, this->afilter)) {
498  edited = this->InsertChar(key);
499  } else {
500  return HKPR_NOT_HANDLED;
501  }
502  break;
503  }
504 
505  return edited ? HKPR_EDITING : HKPR_CURSOR;
506 }
Textbuf::DeleteText
void DeleteText(uint16 from, uint16 to, bool update)
Delete a part of the text.
Definition: textbuf.cpp:240
StringIterator::Prev
virtual size_t Prev(IterType what=ITER_CHARACTER)=0
Move the cursor back by one iteration unit.
HKPR_EDITING
@ HKPR_EDITING
Textbuf content changed.
Definition: textbuf_type.h:22
WChar
char32_t WChar
Type for wide characters, i.e.
Definition: string_type.h:35
Textbuf::max_bytes
uint16 max_bytes
the maximum size of the buffer in bytes (including terminating '\0')
Definition: textbuf_type.h:33
Textbuf::Print
void CDECL Print(const char *format,...) WARN_FORMAT(2
Print a formatted string into the textbuffer.
Definition: textbuf.cpp:415
HKPR_CONFIRM
@ HKPR_CONFIRM
Return or enter key pressed.
Definition: textbuf_type.h:24
textbuf_type.h
StringIterator::END
static const size_t END
Sentinel to indicate end-of-iteration.
Definition: string_base.h:23
Textbuf::Assign
void Assign(StringID string)
Render a string into the textbuffer.
Definition: textbuf.cpp:396
Textbuf::MovePos
bool MovePos(uint16 keycode)
Handle text navigation with arrow keys left/right.
Definition: textbuf.cpp:319
Utf8CharLen
static int8 Utf8CharLen(WChar c)
Return the length of a UTF-8 encoded character.
Definition: string_func.h:109
Textbuf::caretpos
uint16 caretpos
the current position of the caret in the buffer, in bytes
Definition: textbuf_type.h:39
Utf8Encode
size_t Utf8Encode(T buf, WChar c)
Encode a unicode character and place it in the buffer.
Definition: string.cpp:523
Textbuf::caretxoffs
uint16 caretxoffs
the current position of the caret in pixels
Definition: textbuf_type.h:40
StringIterator::Next
virtual size_t Next(IterType what=ITER_CHARACTER)=0
Advance the cursor by one iteration unit.
Textbuf::markpos
uint16 markpos
the start position of the marked area in the buffer, in bytes
Definition: textbuf_type.h:41
StringIterator::SetString
virtual void SetString(const char *s)=0
Set a new iteration string.
MallocT
static T * MallocT(size_t num_elements)
Simplified allocation function that allocates the specified number of elements of the given type.
Definition: alloc_func.hpp:57
Textbuf::InsertString
bool InsertString(const char *str, bool marked, const char *caret=nullptr, const char *insert_location=nullptr, const char *replacement_end=nullptr)
Insert a string into the text buffer.
Definition: textbuf.cpp:162
StringIterator::ITER_CHARACTER
@ ITER_CHARACTER
Iterate over characters (or more exactly grapheme clusters).
Definition: string_base.h:18
Textbuf::pixels
uint16 pixels
the current size of the string in pixels
Definition: textbuf_type.h:37
GetStringBoundingBox
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:842
Textbuf::buf
char *const buf
buffer in which text is saved
Definition: textbuf_type.h:32
gfx_func.h
StringIterator::Create
static StringIterator * Create()
Create a new iterator instance.
Definition: string.cpp:812
Textbuf::HandleCaret
bool HandleCaret()
Handle the flashing of the caret.
Definition: textbuf.cpp:456
GetClipboardContents
bool GetClipboardContents(char *buffer, const char *last)
Try to retrieve the current clipboard contents.
Definition: os2.cpp:182
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:207
StringIterator::SetCurPosition
virtual size_t SetCurPosition(size_t pos)=0
Change the current string cursor.
Textbuf::marklength
uint16 marklength
the length of the marked area in pixels
Definition: textbuf_type.h:44
HandleKeyPressResult
HandleKeyPressResult
Return values for Textbuf::HandleKeypress.
Definition: textbuf_type.h:20
Textbuf::UpdateMarkedText
void UpdateMarkedText()
Update pixel positions of the marked text area.
Definition: textbuf.cpp:303
safeguards.h
Textbuf::caret
bool caret
is the caret ("_") visible or not
Definition: textbuf_type.h:38
IsValidChar
bool IsValidChar(WChar key, CharSetFilter afilter)
Only allow certain keys.
Definition: string.cpp:383
Textbuf::DeleteChar
bool DeleteChar(uint16 keycode)
Delete a character from a textbuffer, either with 'Delete' or 'Backspace' The character is delete fro...
Definition: textbuf.cpp:53
Textbuf::UpdateCaretPosition
void UpdateCaretPosition()
Update pixel position of the caret.
Definition: textbuf.cpp:297
vseprintf
int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap)
Safer implementation of vsnprintf; same as vsnprintf except:
Definition: string.cpp:61
Textbuf::afilter
CharSetFilter afilter
Allowed characters.
Definition: textbuf_type.h:31
StringIterator::ITER_WORD
@ ITER_WORD
Iterate over words.
Definition: string_base.h:19
stdafx.h
CS_ALPHANUMERAL
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition: string_type.h:27
Utf8Decode
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition: string.cpp:481
Textbuf::markend
uint16 markend
the end position of the marked area in the buffer, in bytes
Definition: textbuf_type.h:42
string_func.h
StringID
uint32 StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
Textbuf::bytes
uint16 bytes
the current size of the string in bytes (including terminating '\0')
Definition: textbuf_type.h:35
strings_func.h
Textbuf::InsertClipboard
bool InsertClipboard()
Insert a chunk of text from the clipboard onto the textbuffer.
Definition: textbuf.cpp:225
alloc_func.hpp
Textbuf::Textbuf
Textbuf(uint16 max_bytes, uint16 max_chars=UINT16_MAX)
Initialize the textbuffer by supplying it the buffer to write into and the maximum length of this buf...
Definition: textbuf.cpp:371
Textbuf::UpdateSize
void UpdateSize()
Update Textbuf type with its actual physical character and screenlength Get the count of characters i...
Definition: textbuf.cpp:430
HKPR_CANCEL
@ HKPR_CANCEL
Escape key pressed.
Definition: textbuf_type.h:25
Textbuf::UpdateStringIter
void UpdateStringIter()
Update the character iter after the text has changed.
Definition: textbuf.cpp:283
window_func.h
Textbuf::chars
uint16 chars
the current size of the string in characters (including terminating '\0')
Definition: textbuf_type.h:36
Textbuf::max_chars
uint16 max_chars
the maximum size of the buffer in characters (including terminating '\0')
Definition: textbuf_type.h:34
HKPR_NOT_HANDLED
@ HKPR_NOT_HANDLED
Key does not affect editboxes.
Definition: textbuf_type.h:26
Textbuf::UpdateWidth
void UpdateWidth()
Update pixel width of the text.
Definition: textbuf.cpp:291
strecpy
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: string.cpp:112
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:454
Textbuf::InsertChar
bool InsertChar(WChar key)
Insert a character to a textbuffer.
Definition: textbuf.cpp:132
gfx_type.h
GetCharPosInString
Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsize)
Get the leading corner of a character in a single-line string relative to the start of the string.
Definition: gfx.cpp:870
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:383
Textbuf::CanDelChar
bool CanDelChar(bool backspace)
Checks if it is possible to delete a character.
Definition: textbuf.cpp:42
Textbuf::DeleteAll
void CDECL void DeleteAll()
Delete every character in the textbuffer.
Definition: textbuf.cpp:116
Utf8PrevChar
static char * Utf8PrevChar(char *s)
Retrieve the previous UNICODE character in an UTF-8 encoded string.
Definition: string_func.h:153
HKPR_CURSOR
@ HKPR_CURSOR
Non-text change, e.g. cursor position.
Definition: textbuf_type.h:23
Textbuf::DiscardMarkedText
void DiscardMarkedText(bool update=true)
Discard any marked text.
Definition: textbuf.cpp:274
Textbuf::markxoffs
uint16 markxoffs
the start position of the marked area in pixels
Definition: textbuf_type.h:43