textbuf.cpp

Go to the documentation of this file.
00001 /* $Id: textbuf.cpp 25102 2013-03-17 20:58:40Z rubidium $ */
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 (!IsValidChar(c, this->afilter)) 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 beginning 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->afilter    = CS_ALPHANUMERAL;
00368   this->max_bytes  = max_bytes;
00369   this->max_chars  = max_chars == UINT16_MAX ? max_bytes : max_chars;
00370   this->caret      = true;
00371   this->DeleteAll();
00372 }
00373 
00374 Textbuf::~Textbuf()
00375 {
00376   free(this->buf);
00377 }
00378 
00383 void Textbuf::Assign(StringID string)
00384 {
00385   GetString(this->buf, string, &this->buf[this->max_bytes - 1]);
00386   this->UpdateSize();
00387 }
00388 
00393 void Textbuf::Assign(const char *text)
00394 {
00395   ttd_strlcpy(this->buf, text, this->max_bytes);
00396   this->UpdateSize();
00397 }
00398 
00402 void Textbuf::Print(const char *format, ...)
00403 {
00404   va_list va;
00405   va_start(va, format);
00406   vsnprintf(this->buf, this->max_bytes, format, va);
00407   va_end(va);
00408   this->UpdateSize();
00409 }
00410 
00411 
00417 void Textbuf::UpdateSize()
00418 {
00419   const char *buf = this->buf;
00420 
00421   this->pixels = 0;
00422   this->chars = this->bytes = 1; // terminating zero
00423 
00424   WChar c;
00425   while ((c = Utf8Consume(&buf)) != '\0') {
00426     this->pixels += GetCharacterWidth(FS_NORMAL, c);
00427     this->bytes += Utf8CharLen(c);
00428     this->chars++;
00429   }
00430 
00431   assert(this->bytes <= this->max_bytes);
00432   assert(this->chars <= this->max_chars);
00433 
00434   this->caretpos = this->bytes - 1;
00435   this->caretxoffs = this->pixels;
00436 }
00437 
00442 bool Textbuf::HandleCaret()
00443 {
00444   /* caret changed? */
00445   bool b = !!(_caret_timer & 0x20);
00446 
00447   if (b != this->caret) {
00448     this->caret = b;
00449     return true;
00450   }
00451   return false;
00452 }