# 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 "textbuf_type.h"
    00014 #include "string_func.h"
    00015 #include "gfx_type.h"
    00016 #include "gfx_func.h"
    00017 #include "window_func.h"
    00018
    00025 bool GetClipboardContents(char *buffer, size_t buff_len);
    00026
    00027 int _caret_timer;
    00028
    00029
    00036 bool Textbuf::CanDelChar(bool backspace)
    00037 {
    00038   return backspace ? this->caretpos != 0 : this->caretpos < this->bytes - 1;
    00039 }
    00040
    00048 WChar Textbuf::GetNextDelChar(bool backspace)
    00049 {
    00050   assert(this->CanDelChar(backspace));
    00051
    00052   const char *s;
    00053   if (backspace) {
    00054     s = Utf8PrevChar(this->buf + this->caretpos);
    00055   } else {
    00056     s = this->buf + this->caretpos;
    00057   }
    00058
    00059   WChar c;
    00060   Utf8Decode(&c, s);
    00061   return c;
    00062 }
    00063
    00070 void Textbuf::DelChar(bool backspace)
    00071 {
    00072   assert(this->CanDelChar(backspace));
    00073
    00074   WChar c;
    00075   char *s = this->buf + this->caretpos;
    00076
    00077   if (backspace) s = Utf8PrevChar(s);
    00078
    00079   uint16 len = (uint16)Utf8Decode(&c, s);
    00080   uint width = GetCharacterWidth(FS_NORMAL, c);
    00081
    00082   this->pixels -= width;
    00083   if (backspace) {
    00084     this->caretpos   -= len;
    00085     this->caretxoffs -= width;
    00086   }
    00087
    00088   /* Move the remaining characters over the marker */
    00089   memmove(s, s + len, this->bytes - (s - this->buf) - len);
    00090   this->bytes -= len;
    00091   this->chars--;
    00092 }
    00093
    00100 bool Textbuf::DeleteChar(int delmode)
    00101 {
    00102   if (delmode == WKC_BACKSPACE || delmode == WKC_DELETE) {
    00103     bool backspace = delmode == WKC_BACKSPACE;
    00104     if (CanDelChar(backspace)) {
    00105       this->DelChar(backspace);
    00106       return true;
    00107     }
    00108     return false;
    00109   }
    00110
    00111   if (delmode == (WKC_CTRL | WKC_BACKSPACE) || delmode == (WKC_CTRL | WKC_DELETE)) {
    00112     bool backspace = delmode == (WKC_CTRL | WKC_BACKSPACE);
    00113
    00114     if (!CanDelChar(backspace)) return false;
    00115     WChar c = this->GetNextDelChar(backspace);
    00116
    00117     /* Backspace: Delete left whitespaces.
    00118      * Delete:    Delete right word.
    00119      */
    00120     while (backspace ? IsWhitespace(c) : !IsWhitespace(c)) {
    00121       this->DelChar(backspace);
    00122       if (!this->CanDelChar(backspace)) return true;
    00123       c = this->GetNextDelChar(backspace);
    00124     }
    00125     /* Backspace: Delete left word.
    00126      * Delete:    Delete right whitespaces.
    00127      */
    00128     while (backspace ? !IsWhitespace(c) : IsWhitespace(c)) {
    00129       this->DelChar(backspace);
    00130       if (!this->CanDelChar(backspace)) return true;
    00131       c = this->GetNextDelChar(backspace);
    00132     }
    00133     return true;
    00134   }
    00135
    00136   return false;
    00137 }
    00138
    00142 void Textbuf::DeleteAll()
    00143 {
    00144   memset(this->buf, 0, this->max_bytes);
    00145   this->bytes = this->chars = 1;
    00146   this->pixels = this->caretpos = this->caretxoffs = 0;
    00147 }
    00148
    00156 bool Textbuf::InsertChar(WChar key)
    00157 {
    00158   const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
    00159   uint16 len = (uint16)Utf8CharLen(key);
    00160   if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) {
    00161     memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
    00162     Utf8Encode(this->buf + this->caretpos, key);
    00163     this->chars++;
    00164     this->bytes  += len;
    00165     this->pixels += charwidth;
    00166
    00167     this->caretpos   += len;
    00168     this->caretxoffs += charwidth;
    00169     return true;
    00170   }
    00171   return false;
    00172 }
    00173
    00180 bool Textbuf::InsertClipboard()
    00181 {
    00182   char utf8_buf[512];
    00183
    00184   if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
    00185
    00186   uint16 pixels = 0, bytes = 0, chars = 0;
    00187   WChar c;
    00188   for (const char *ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
    00189     if (!IsPrintable(c)) break;
    00190
    00191     byte len = Utf8CharLen(c);
    00192     if (this->bytes + bytes + len > this->max_bytes) break;
    00193     if (this->chars + chars + 1   > this->max_chars) break;
    00194
    00195     byte char_pixels = GetCharacterWidth(FS_NORMAL, c);
    00196
    00197     pixels += char_pixels;
    00198     bytes += len;
    00199     chars++;
    00200   }
    00201
    00202   if (bytes == 0) return false;
    00203
    00204   memmove(this->buf + this->caretpos + bytes, this->buf + this->caretpos, this->bytes - this->caretpos);
    00205   memcpy(this->buf + this->caretpos, utf8_buf, bytes);
    00206   this->pixels += pixels;
    00207   this->caretxoffs += pixels;
    00208
    00209   this->bytes += bytes;
    00210   this->chars += chars;
    00211   this->caretpos += bytes;
    00212   assert(this->bytes <= this->max_bytes);
    00213   assert(this->chars <= this->max_chars);
    00214   this->buf[this->bytes - 1] = '\0'; // terminating zero
    00215
    00216   return true;
    00217 }
    00218
    00223 bool Textbuf::CanMoveCaretLeft()
    00224 {
    00225   return this->caretpos != 0;
    00226 }
    00227
    00233 WChar Textbuf::MoveCaretLeft()
    00234 {
    00235   assert(this->CanMoveCaretLeft());
    00236
    00237   WChar c;
    00238   const char *s = Utf8PrevChar(this->buf + this->caretpos);
    00239   Utf8Decode(&c, s);
    00240   this->caretpos    = s - this->buf;
    00241   this->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
    00242
    00243   return c;
    00244 }
    00245
    00250 bool Textbuf::CanMoveCaretRight()
    00251 {
    00252   return this->caretpos < this->bytes - 1;
    00253 }
    00254
    00260 WChar Textbuf::MoveCaretRight()
    00261 {
    00262   assert(this->CanMoveCaretRight());
    00263
    00264   WChar c;
    00265   this->caretpos   += (uint16)Utf8Decode(&c, this->buf + this->caretpos);
    00266   this->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
    00267
    00268   Utf8Decode(&c, this->buf + this->caretpos);
    00269   return c;
    00270 }
    00271
    00278 bool Textbuf::MovePos(int navmode)
    00279 {
    00280   switch (navmode) {
    00281     case WKC_LEFT:
    00282       if (this->CanMoveCaretLeft()) {
    00283         this->MoveCaretLeft();
    00284         return true;
    00285       }
    00286       break;
    00287
    00288     case WKC_CTRL | WKC_LEFT: {
    00289       if (!this->CanMoveCaretLeft()) break;
    00290
    00291       /* Unconditionally move one char to the left. */
    00292       WChar c = this->MoveCaretLeft();
    00293       /* Consume left whitespaces. */
    00294       while (IsWhitespace(c)) {
    00295         if (!this->CanMoveCaretLeft()) return true;
    00296         c = this->MoveCaretLeft();
    00297       }
    00298       /* Consume left word. */
    00299       while (!IsWhitespace(c)) {
    00300         if (!this->CanMoveCaretLeft()) return true;
    00301         c = this->MoveCaretLeft();
    00302       }
    00303       /* Place caret at the begining of the left word. */
    00304       this->MoveCaretRight();
    00305       return true;
    00306     }
    00307
    00308     case WKC_RIGHT:
    00309       if (this->CanMoveCaretRight()) {
    00310         this->MoveCaretRight();
    00311         return true;
    00312       }
    00313       break;
    00314
    00315     case WKC_CTRL | WKC_RIGHT: {
    00316       if (!this->CanMoveCaretRight()) break;
    00317
    00318       /* Unconditionally move one char to the right. */
    00319       WChar c = this->MoveCaretRight();
    00320       /* Continue to consume current word. */
    00321       while (!IsWhitespace(c)) {
    00322         if (!this->CanMoveCaretRight()) return true;
    00323         c = this->MoveCaretRight();
    00324       }
    00325       /* Consume right whitespaces. */
    00326       while (IsWhitespace(c)) {
    00327         if (!this->CanMoveCaretRight()) return true;
    00328         c = this->MoveCaretRight();
    00329       }
    00330       return true;
    00331     }
    00332
    00333     case WKC_HOME:
    00334       this->caretpos = 0;
    00335       this->caretxoffs = 0;
    00336       return true;
    00337
    00338     case WKC_END:
    00339       this->caretpos = this->bytes - 1;
    00340       this->caretxoffs = this->pixels;
    00341       return true;
    00342
    00343     default:
    00344       break;
    00345   }
    00346
    00347   return false;
    00348 }
    00349
    00356 void Textbuf::Initialize(char *buf, uint16 max_bytes)
    00357 {
    00358   this->Initialize(buf, max_bytes, max_bytes);
    00359 }
    00360
    00368 void Textbuf::Initialize(char *buf, uint16 max_bytes, uint16 max_chars)
    00369 {
    00370   assert(max_bytes != 0);
    00371   assert(max_chars != 0);
    00372
    00373   this->buf        = buf;
    00374   this->max_bytes  = max_bytes;
    00375   this->max_chars  = max_chars;
    00376   this->caret      = true;
    00377   this->UpdateSize();
    00378 }
    00379
    00385 void Textbuf::UpdateSize()
    00386 {
    00387   const char *buf = this->buf;
    00388
    00389   this->pixels = 0;
    00390   this->chars = this->bytes = 1; // terminating zero
    00391
    00392   WChar c;
    00393   while ((c = Utf8Consume(&buf)) != '\0') {
    00394     this->pixels += GetCharacterWidth(FS_NORMAL, c);
    00395     this->bytes += Utf8CharLen(c);
    00396     this->chars++;
    00397   }
    00398
    00399   assert(this->bytes <= this->max_bytes);
    00400   assert(this->chars <= this->max_chars);
    00401
    00402   this->caretpos = this->bytes - 1;
    00403   this->caretxoffs = this->pixels;
    00404 }
    00405
    00410 bool Textbuf::HandleCaret()
    00411 {
    00412   /* caret changed? */
    00413   bool b = !!(_caret_timer & 0x20);
    00414
    00415   if (b != this->caret) {
    00416     this->caret = b;
    00417     return true;
    00418   }
    00419   return false;
    00420 }