OpenTTD Source: src/textbuf.cpp Source File

OpenTTD Source 1.11.0-RC1

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
116 void Textbuf::DeleteAll()
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
132 bool Textbuf::InsertChar(WChar key)
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
225 bool Textbuf::InsertClipboard()
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
283 void Textbuf::UpdateStringIter()
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
291 void Textbuf::UpdateWidth()
292 {
293 this->pixels = GetStringBoundingBox(this->buf, FS_NORMAL).width;
294 }
295
297 void Textbuf::UpdateCaretPosition()
298 {
299 this->caretxoffs = this->chars > 1 ? GetCharPosInString(this->buf, this->buf + this->caretpos, FS_NORMAL).x : 0;
300 }
301
303 void Textbuf::UpdateMarkedText()
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
396 void Textbuf::Assign(StringID string)
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
430 void Textbuf::UpdateSize()
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
456 bool Textbuf::HandleCaret()
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