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 }