# console_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: console_gui.cpp 15723 2009-03-15 15:12:06Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "textbuf_gui.h"
    00007 #include "window_gui.h"
    00008 #include "console_gui.h"
    00009 #include "console_internal.h"
    00010 #include "window_func.h"
    00011 #include "string_func.h"
    00012 #include "gfx_func.h"
    00013 #include "core/math_func.hpp"
    00014 #include "settings_type.h"
    00015 #include "rev.h"
    00016
    00017 #include "table/strings.h"
    00018
    00019 enum {
    00020   ICON_HISTORY_SIZE       = 20,
    00021   ICON_LINE_HEIGHT        = 12,
    00022   ICON_RIGHT_BORDERWIDTH  = 10,
    00023   ICON_BOTTOM_BORDERWIDTH = 12,
    00024 };
    00025
    00029 struct IConsoleLine {
    00030   static IConsoleLine *front;
    00031   static int size;
    00032
    00033   IConsoleLine *previous;
    00034   char *buffer;
    00035   TextColour colour;
    00036   uint16 time;
    00037
    00043   IConsoleLine(char *buffer, TextColour colour) :
    00044       previous(IConsoleLine::front),
    00045       buffer(buffer),
    00046       colour(colour),
    00047       time(0)
    00048   {
    00049     IConsoleLine::front = this;
    00050     IConsoleLine::size++;
    00051   }
    00052
    00056   ~IConsoleLine()
    00057   {
    00058     IConsoleLine::size--;
    00059     free(buffer);
    00060
    00061     delete previous;
    00062   }
    00063
    00067   static const IConsoleLine *Get(uint index)
    00068   {
    00069     const IConsoleLine *item = IConsoleLine::front;
    00070     while (index != 0 && item != NULL) {
    00071       index--;
    00072       item = item->previous;
    00073     }
    00074
    00075     return item;
    00076   }
    00077
    00085   static bool Truncate()
    00086   {
    00087     IConsoleLine *cur = IConsoleLine::front;
    00088     if (cur == NULL) return false;
    00089
    00090     int count = 1;
    00091     for (IConsoleLine *item = cur->previous; item != NULL; count++, cur = item, item = item->previous) {
    00092       if (item->time > _settings_client.gui.console_backlog_timeout &&
    00093           count > _settings_client.gui.console_backlog_length) {
    00094         delete item;
    00095         cur->previous = NULL;
    00096         return true;
    00097       }
    00098
    00099       if (item->time != MAX_UVALUE(uint16)) item->time++;
    00100     }
    00101
    00102     return false;
    00103   }
    00104
    00108   static void Reset()
    00109   {
    00110     delete IConsoleLine::front;
    00111     IConsoleLine::front = NULL;
    00112     IConsoleLine::size = 0;
    00113   }
    00114 };
    00115
    00116 /* static */ IConsoleLine *IConsoleLine::front = NULL;
    00117 /* static */ int IConsoleLine::size  = 0;
    00118
    00119
    00120 /* ** main console cmd buffer ** */
    00121 static Textbuf _iconsole_cmdline;
    00122 static char *_iconsole_history[ICON_HISTORY_SIZE];
    00123 static byte _iconsole_historypos;
    00124 IConsoleModes _iconsole_mode;
    00125
    00126 /* *************** *
    00127  *  end of header  *
    00128  * *************** */
    00129
    00130 static void IConsoleClearCommand()
    00131 {
    00132   memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
    00133   _iconsole_cmdline.size = 1; // only terminating zero
    00134   _iconsole_cmdline.width = 0;
    00135   _iconsole_cmdline.caretpos = 0;
    00136   _iconsole_cmdline.caretxoffs = 0;
    00137   SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
    00138 }
    00139
    00140 static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
    00141
    00142
    00143 static void IConsoleHistoryAdd(const char *cmd);
    00144 static void IConsoleHistoryNavigate(int direction);
    00145
    00146 struct IConsoleWindow : Window
    00147 {
    00148   static int scroll;
    00149
    00150   IConsoleWindow(const WindowDesc *desc) : Window(desc)
    00151   {
    00152     _iconsole_mode = ICONSOLE_OPENED;
    00153
    00154     this->height = _screen.height / 3;
    00155     this->width  = _screen.width;
    00156   }
    00157
    00158   ~IConsoleWindow()
    00159   {
    00160     _iconsole_mode = ICONSOLE_CLOSED;
    00161   }
    00162
    00163   virtual void OnPaint()
    00164   {
    00165     int max = (this->height / ICON_LINE_HEIGHT) - 1;
    00166     const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll);
    00167     GfxFillRect(this->left, this->top, this->width, this->height - 1, 0);
    00168     for (int i = 0; i < max && print != NULL; i++, print = print->previous) {
    00169       DoDrawString(print->buffer, 5,
    00170         this->height - (2 + i) * ICON_LINE_HEIGHT, print->colour);
    00171     }
    00172     /* If the text is longer than the window, don't show the starting ']' */
    00173     int delta = this->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
    00174     if (delta > 0) {
    00175       DoDrawString("]", 5, this->height - ICON_LINE_HEIGHT, (TextColour)CC_COMMAND);
    00176       delta = 0;
    00177     }
    00178
    00179     DoDrawString(_iconsole_cmdline.buf, 10 + delta, this->height - ICON_LINE_HEIGHT, (TextColour)CC_COMMAND);
    00180
    00181     if (_focused_window == this && _iconsole_cmdline.caret) {
    00182       DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, this->height - ICON_LINE_HEIGHT, TC_WHITE);
    00183     }
    00184   }
    00185
    00186   virtual void OnHundredthTick()
    00187   {
    00188     if (IConsoleLine::Truncate() &&
    00189         (IConsoleWindow::scroll > IConsoleLine::size)) {
    00190       IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / ICON_LINE_HEIGHT) + 1);
    00191       this->SetDirty();
    00192     }
    00193   }
    00194
    00195   virtual void OnMouseLoop()
    00196   {
    00197     if (HandleCaret(&_iconsole_cmdline)) this->SetDirty();
    00198   }
    00199
    00200   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    00201   {
    00202     if (_focused_window != this) return ES_NOT_HANDLED;
    00203
    00204     const int scroll_height = (this->height / ICON_LINE_HEIGHT) - 1;
    00205     switch (keycode) {
    00206       case WKC_UP:
    00207         IConsoleHistoryNavigate(1);
    00208         this->SetDirty();
    00209         break;
    00210
    00211       case WKC_DOWN:
    00212         IConsoleHistoryNavigate(-1);
    00213         this->SetDirty();
    00214         break;
    00215
    00216       case WKC_SHIFT | WKC_PAGEDOWN:
    00217         if (IConsoleWindow::scroll - scroll_height < 0) {
    00218           IConsoleWindow::scroll = 0;
    00219         } else {
    00220           IConsoleWindow::scroll -= scroll_height;
    00221         }
    00222         this->SetDirty();
    00223         break;
    00224
    00225       case WKC_SHIFT | WKC_PAGEUP:
    00226         if (IConsoleWindow::scroll + scroll_height > IConsoleLine::size - scroll_height) {
    00227           IConsoleWindow::scroll = IConsoleLine::size - scroll_height;
    00228         } else {
    00229           IConsoleWindow::scroll += scroll_height;
    00230         }
    00231         this->SetDirty();
    00232         break;
    00233
    00234       case WKC_SHIFT | WKC_DOWN:
    00235         if (IConsoleWindow::scroll <= 0) {
    00236           IConsoleWindow::scroll = 0;
    00237         } else {
    00238           --IConsoleWindow::scroll;
    00239         }
    00240         this->SetDirty();
    00241         break;
    00242
    00243       case WKC_SHIFT | WKC_UP:
    00244         if (IConsoleWindow::scroll >= IConsoleLine::size) {
    00245           IConsoleWindow::scroll = IConsoleLine::size;
    00246         } else {
    00247           ++IConsoleWindow::scroll;
    00248         }
    00249         this->SetDirty();
    00250         break;
    00251
    00252       case WKC_BACKQUOTE:
    00253         IConsoleSwitch();
    00254         break;
    00255
    00256       case WKC_RETURN: case WKC_NUM_ENTER:
    00257         IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf);
    00258         IConsoleHistoryAdd(_iconsole_cmdline.buf);
    00259
    00260         IConsoleCmdExec(_iconsole_cmdline.buf);
    00261         IConsoleClearCommand();
    00262         break;
    00263
    00264       case WKC_CTRL | WKC_RETURN:
    00265         _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
    00266         IConsoleResize(this);
    00267         MarkWholeScreenDirty();
    00268         break;
    00269
    00270       case (WKC_CTRL | 'V'):
    00271         if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
    00272           IConsoleResetHistoryPos();
    00273           this->SetDirty();
    00274         }
    00275         break;
    00276
    00277       case (WKC_CTRL | 'L'):
    00278         IConsoleCmdExec("clear");
    00279         break;
    00280
    00281       case (WKC_CTRL | 'U'):
    00282         DeleteTextBufferAll(&_iconsole_cmdline);
    00283         this->SetDirty();
    00284         break;
    00285
    00286       case WKC_BACKSPACE: case WKC_DELETE:
    00287         if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) {
    00288           IConsoleResetHistoryPos();
    00289           this->SetDirty();
    00290         }
    00291         break;
    00292
    00293       case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
    00294         if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) {
    00295           IConsoleResetHistoryPos();
    00296           this->SetDirty();
    00297         }
    00298         break;
    00299
    00300       default:
    00301         if (IsValidChar(key, CS_ALPHANUMERAL)) {
    00302           IConsoleWindow::scroll = 0;
    00303           InsertTextBufferChar(&_iconsole_cmdline, key);
    00304           IConsoleResetHistoryPos();
    00305           this->SetDirty();
    00306         } else {
    00307           return ES_NOT_HANDLED;
    00308         }
    00309     }
    00310     return ES_HANDLED;
    00311   }
    00312 };
    00313
    00314 int IConsoleWindow::scroll = 0;
    00315
    00316 static const Widget _iconsole_window_widgets[] = {
    00317   {WIDGETS_END}
    00318 };
    00319
    00320 static const WindowDesc _iconsole_window_desc(
    00321   0, 0, 2, 2, 2, 2,
    00322   WC_CONSOLE, WC_NONE,
    00323   WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00324   _iconsole_window_widgets
    00325 );
    00326
    00327 void IConsoleGUIInit()
    00328 {
    00329   _iconsole_historypos = ICON_HISTORY_SIZE - 1;
    00330   _iconsole_mode = ICONSOLE_CLOSED;
    00331
    00332   IConsoleLine::Reset();
    00333   memset(_iconsole_history, 0, sizeof(_iconsole_history));
    00334
    00335   _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it
    00336   _iconsole_cmdline.maxsize = ICON_CMDLN_SIZE;
    00337
    00338   IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
    00339   IConsolePrint(CC_WHITE,  "------------------------------------");
    00340   IConsolePrint(CC_WHITE,  "use \"help\" for more information");
    00341   IConsolePrint(CC_WHITE,  "");
    00342   IConsoleClearCommand();
    00343   IConsoleHistoryAdd("");
    00344 }
    00345
    00346 void IConsoleClearBuffer()
    00347 {
    00348   IConsoleLine::Reset();
    00349 }
    00350
    00351 void IConsoleGUIFree()
    00352 {
    00353   free(_iconsole_cmdline.buf);
    00354   IConsoleClearBuffer();
    00355 }
    00356
    00357 void IConsoleResize(Window *w)
    00358 {
    00359   switch (_iconsole_mode) {
    00360     case ICONSOLE_OPENED:
    00361       w->height = _screen.height / 3;
    00362       w->width = _screen.width;
    00363       break;
    00364     case ICONSOLE_FULL:
    00365       w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
    00366       w->width = _screen.width;
    00367       break;
    00368     default: return;
    00369   }
    00370
    00371   MarkWholeScreenDirty();
    00372 }
    00373
    00374 void IConsoleSwitch()
    00375 {
    00376   switch (_iconsole_mode) {
    00377     case ICONSOLE_CLOSED:
    00378       new IConsoleWindow(&_iconsole_window_desc);
    00379       break;
    00380
    00381     case ICONSOLE_OPENED: case ICONSOLE_FULL:
    00382       DeleteWindowById(WC_CONSOLE, 0);
    00383       break;
    00384   }
    00385
    00386   MarkWholeScreenDirty();
    00387 }
    00388
    00389 void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
    00390 void IConsoleOpen()  {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();}
    00391
    00397 static void IConsoleHistoryAdd(const char *cmd)
    00398 {
    00399   free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
    00400
    00401   memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
    00402   _iconsole_history[0] = strdup(cmd);
    00403   IConsoleResetHistoryPos();
    00404 }
    00405
    00410 static void IConsoleHistoryNavigate(int direction)
    00411 {
    00412   int i = _iconsole_historypos + direction;
    00413
    00414   /* watch out for overflows, just wrap around */
    00415   if (i < 0) i = ICON_HISTORY_SIZE - 1;
    00416   if (i >= ICON_HISTORY_SIZE) i = 0;
    00417
    00418   if (direction > 0) {
    00419     if (_iconsole_history[i] == NULL) i = 0;
    00420   }
    00421
    00422   if (direction < 0) {
    00423     while (i > 0 && _iconsole_history[i] == NULL) i--;
    00424   }
    00425
    00426   _iconsole_historypos = i;
    00427   IConsoleClearCommand();
    00428   /* copy history to 'command prompt / bash' */
    00429   assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
    00430   ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxsize);
    00431   UpdateTextBufferSize(&_iconsole_cmdline);
    00432 }
    00433
    00443 void IConsoleGUIPrint(ConsoleColour colour_code, char *str)
    00444 {
    00445   new IConsoleLine(str, (TextColour)colour_code);
    00446   SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
    00447 }