# textbuf.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include <stdarg.h>
    00014
    00015 #include "textbuf_type.h"
    00016 #include "string_func.h"
    00017 #include "strings_func.h"
    00018 #include "gfx_type.h"
    00019 #include "gfx_func.h"
    00020 #include "window_func.h"
    00021 #include "core/alloc_func.hpp"
    00022
    00029 bool GetClipboardContents(char *buffer, size_t buff_len);
    00030
    00031 int _caret_timer;
    00032
    00033
    00040 bool Textbuf::CanDelChar(bool backspace)
    00041 {
    00042   return backspace ? this->caretpos != 0 : this->caretpos < this->bytes - 1;
    00043 }
    00044
    00052 WChar Textbuf::GetNextDelChar(bool backspace)
    00053 {
    00054   assert(this->CanDelChar(backspace));
    00055
    00056   const char *s;
    00057   if (backspace) {
    00058     s = Utf8PrevChar(this->buf + this->caretpos);
    00059   } else {
    00060     s = this->buf + this->caretpos;
    00061   }
    00062
    00063   WChar c;
    00064   Utf8Decode(&c, s);
    00065   return c;
    00066 }
    00067
    00074 void Textbuf::DelChar(bool backspace)
    00075 {
    00076   assert(this->CanDelChar(backspace));
    00077
    00078   WChar c;
    00079   char *s = this->buf + this->caretpos;
    00080
    00081   if (backspace) s = Utf8PrevChar(s);
    00082
    00083   uint16 len = (uint16)Utf8Decode(&c, s);
    00084   uint width = GetCharacterWidth(FS_NORMAL, c);
    00085
    00086   this->pixels -= width;
    00087   if (backspace) {
    00088     this->caretpos   -= len;
    00089     this->caretxoffs -= width;
    00090   }
    00091
    00092   /* Move the remaining characters over the marker */
    00093   memmove(s, s + len, this->bytes - (s - this->buf) - len);
    00094   this->bytes -= len;
    00095   this->chars--;
    00096 }
    00097
    00104 bool Textbuf::DeleteChar(int delmode)
    00105 {
    00106   if (delmode == WKC_BACKSPACE || delmode == WKC_DELETE) {
    00107     bool backspace = delmode == WKC_BACKSPACE;
    00108     if (CanDelChar(backspace)) {
    00109       this->DelChar(backspace);
    00110       return true;
    00111     }
    00112     return false;
    00113   }
    00114
    00115   if (delmode == (WKC_CTRL | WKC_BACKSPACE) || delmode == (WKC_CTRL | WKC_DELETE)) {
    00116     bool backspace = delmode == (WKC_CTRL | WKC_BACKSPACE);
    00117
    00118     if (!CanDelChar(backspace)) return false;
    00119     WChar c = this->GetNextDelChar(backspace);
    00120
    00121     /* Backspace: Delete left whitespaces.
    00122      * Delete:    Delete right word.
    00123      */
    00124     while (backspace ? IsWhitespace(c) : !IsWhitespace(c)) {
    00125       this->DelChar(backspace);
    00126       if (!this->CanDelChar(backspace)) return true;
    00127       c = this->GetNextDelChar(backspace);
    00128     }
    00129     /* Backspace: Delete left word.
    00130      * Delete:    Delete right whitespaces.
    00131      */
    00132     while (backspace ? !IsWhitespace(c) : IsWhitespace(c)) {
    00133       this->DelChar(backspace);
    00134       if (!this->CanDelChar(backspace)) return true;
    00135       c = this->GetNextDelChar(backspace);
    00136     }
    00137     return true;
    00138   }
    00139
    00140   return false;
    00141 }
    00142
    00146 void Textbuf::DeleteAll()
    00147 {
    00148   memset(this->buf, 0, this->max_bytes);
    00149   this->bytes = this->chars = 1;
    00150   this->pixels = this->caretpos = this->caretxoffs = 0;
    00151 }
    00152
    00160 bool Textbuf::InsertChar(WChar key)
    00161 {
    00162   const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
    00163   uint16 len = (uint16)Utf8CharLen(key);
    00164   if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) {
    00165     memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
    00166     Utf8Encode(this->buf + this->caretpos, key);
    00167     this->chars++;
    00168     this->bytes  += len;
    00169     this->pixels += charwidth;
    00170
    00171     this->caretpos   += len;
    00172     this->caretxoffs += charwidth;
    00173     return true;
    00174   }
    00175   return false;
    00176 }
    00177
    00184 bool Textbuf::InsertClipboard()
    00185 {
    00186   char utf8_buf[512];
    00187
    00188   if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
    00189
    00190   uint16 pixels = 0, bytes = 0, chars = 0;
    00191   WChar c;
    00192   for (const char *ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
    00193     if (!IsPrintable(c)) break;
    00194
    00195     byte len = Utf8CharLen(c);
    00196     if (this->bytes + bytes + len > this->max_bytes) break;
    00197     if (this->chars + chars + 1   > this->max_chars) break;
    00198
    00199     byte char_pixels = GetCharacterWidth(FS_NORMAL, c);
    00200
    00201     pixels += char_pixels;
    00202     bytes += len;
    00203     chars++;
    00204   }
    00205
    00206   if (bytes == 0) return false;
    00207
    00208   memmove(this->buf + this->caretpos + bytes, this->buf + this->caretpos, this->bytes - this->caretpos);
    00209   memcpy(this->buf + this->caretpos, utf8_buf, bytes);
    00210   this->pixels += pixels;
    00211   this->caretxoffs += pixels;
    00212
    00213   this->bytes += bytes;
    00214   this->chars += chars;
    00215   this->caretpos += bytes;
    00216   assert(this->bytes <= this->max_bytes);
    00217   assert(this->chars <= this->max_chars);
    00218   this->buf[this->bytes - 1] = '\0'; // terminating zero
    00219
    00220   return true;
    00221 }
    00222
    00227 bool Textbuf::CanMoveCaretLeft()
    00228 {
    00229   return this->caretpos != 0;
    00230 }
    00231
    00237 WChar Textbuf::MoveCaretLeft()
    00238 {
    00239   assert(this->CanMoveCaretLeft());
    00240
    00241   WChar c;
    00242   const char *s = Utf8PrevChar(this->buf + this->caretpos);
    00243   Utf8Decode(&c, s);
    00244   this->caretpos    = s - this->buf;
    00245   this->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
    00246
    00247   return c;
    00248 }
    00249
    00254 bool Textbuf::CanMoveCaretRight()
    00255 {
    00256   return this->caretpos < this->bytes - 1;
    00257 }
    00258
    00264 WChar Textbuf::MoveCaretRight()
    00265 {
    00266   assert(this->CanMoveCaretRight());
    00267
    00268   WChar c;
    00269   this->caretpos   += (uint16)Utf8Decode(&c, this->buf + this->caretpos);
    00270   this->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
    00271
    00272   Utf8Decode(&c, this->buf + this->caretpos);
    00273   return c;
    00274 }
    00275
    00282 bool Textbuf::MovePos(int navmode)
    00283 {
    00284   switch (navmode) {
    00285     case WKC_LEFT:
    00286       if (this->CanMoveCaretLeft()) {
    00287         this->MoveCaretLeft();
    00288         return true;
    00289       }
    00290       break;
    00291
    00292     case WKC_CTRL | WKC_LEFT: {
    00293       if (!this->CanMoveCaretLeft()) break;
    00294
    00295       /* Unconditionally move one char to the left. */
    00296       WChar c = this->MoveCaretLeft();
    00297       /* Consume left whitespaces. */
    00298       while (IsWhitespace(c)) {
    00299         if (!this->CanMoveCaretLeft()) return true;
    00300         c = this->MoveCaretLeft();
    00301       }
    00302       /* Consume left word. */
    00303       while (!IsWhitespace(c)) {
    00304         if (!this->CanMoveCaretLeft()) return true;
    00305         c = this->MoveCaretLeft();
    00306       }
    00307       /* Place caret at the begining of the left word. */
    00308       this->MoveCaretRight();
    00309       return true;
    00310     }
    00311
    00312     case WKC_RIGHT:
    00313       if (this->CanMoveCaretRight()) {
    00314         this->MoveCaretRight();
    00315         return true;
    00316       }
    00317       break;
    00318
    00319     case WKC_CTRL | WKC_RIGHT: {
    00320       if (!this->CanMoveCaretRight()) break;
    00321
    00322       /* Unconditionally move one char to the right. */
    00323       WChar c = this->MoveCaretRight();
    00324       /* Continue to consume current word. */
    00325       while (!IsWhitespace(c)) {
    00326         if (!this->CanMoveCaretRight()) return true;
    00327         c = this->MoveCaretRight();
    00328       }
    00329       /* Consume right whitespaces. */
    00330       while (IsWhitespace(c)) {
    00331         if (!this->CanMoveCaretRight()) return true;
    00332         c = this->MoveCaretRight();
    00333       }
    00334       return true;
    00335     }
    00336
    00337     case WKC_HOME:
    00338       this->caretpos = 0;
    00339       this->caretxoffs = 0;
    00340       return true;
    00341
    00342     case WKC_END:
    00343       this->caretpos = this->bytes - 1;
    00344       this->caretxoffs = this->pixels;
    00345       return true;
    00346
    00347     default:
    00348       break;
    00349   }
    00350
    00351   return false;
    00352 }
    00353
    00361 Textbuf::Textbuf(uint16 max_bytes, uint16 max_chars)
    00362   : buf(MallocT<char>(max_bytes))
    00363 {
    00364   assert(max_bytes != 0);
    00365   assert(max_chars != 0);
    00366
    00367   this->max_bytes  = max_bytes;
    00368   this->max_chars  = max_chars == UINT16_MAX ? max_bytes : max_chars;
    00369   this->caret      = true;
    00370   this->DeleteAll();
    00371 }
    00372
    00373 Textbuf::~Textbuf()
    00374 {
    00375   free(this->buf);
    00376 }
    00377
    00382 void Textbuf::Assign(StringID string)
    00383 {
    00384   GetString(this->buf, string, &this->buf[this->max_bytes - 1]);
    00385   this->UpdateSize();
    00386 }
    00387
    00392 void Textbuf::Assign(const char *text)
    00393 {
    00394   ttd_strlcpy(this->buf, text, this->max_bytes);
    00395   this->UpdateSize();
    00396 }
    00397
    00401 void Textbuf::Print(const char *format, ...)
    00402 {
    00403   va_list va;
    00404   va_start(va, format);
    00405   vsnprintf(this->buf, this->max_bytes, format, va);
    00406   va_end(va);
    00407   this->UpdateSize();
    00408 }
    00409
    00410
    00416 void Textbuf::UpdateSize()
    00417 {
    00418   const char *buf = this->buf;
    00419
    00420   this->pixels = 0;
    00421   this->chars = this->bytes = 1; // terminating zero
    00422
    00423   WChar c;
    00424   while ((c = Utf8Consume(&buf)) != '\0') {
    00425     this->pixels += GetCharacterWidth(FS_NORMAL, c);
    00426     this->bytes += Utf8CharLen(c);
    00427     this->chars++;
    00428   }
    00429
    00430   assert(this->bytes <= this->max_bytes);
    00431   assert(this->chars <= this->max_chars);
    00432
    00433   this->caretpos = this->bytes - 1;
    00434   this->caretxoffs = this->pixels;
    00435 }
    00436
    00441 bool Textbuf::HandleCaret()
    00442 {
    00443   /* caret changed? */
    00444   bool b = !!(_caret_timer & 0x20);
    00445
    00446   if (b != this->caret) {
    00447     this->caret = b;
    00448     return true;
    00449   }
    00450   return false;
    00451 }