console_gui.cpp

Go to the documentation of this file.
00001 /* $Id: console_gui.cpp 21047 2010-10-27 20:15:18Z 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 "textbuf_gui.h"
00014 #include "window_gui.h"
00015 #include "console_gui.h"
00016 #include "console_internal.h"
00017 #include "window_func.h"
00018 #include "string_func.h"
00019 #include "gfx_func.h"
00020 #include "settings_type.h"
00021 #include "console_func.h"
00022 #include "rev.h"
00023 
00024 
00025 enum {
00026   ICON_HISTORY_SIZE       = 20,
00027   ICON_LINE_SPACING       =  2,
00028   ICON_RIGHT_BORDERWIDTH  = 10,
00029   ICON_BOTTOM_BORDERWIDTH = 12,
00030 };
00031 
00035 struct IConsoleLine {
00036   static IConsoleLine *front; 
00037   static int size;            
00038 
00039   IConsoleLine *previous; 
00040   char *buffer;           
00041   TextColour colour;      
00042   uint16 time;            
00043 
00049   IConsoleLine(char *buffer, TextColour colour) :
00050       previous(IConsoleLine::front),
00051       buffer(buffer),
00052       colour(colour),
00053       time(0)
00054   {
00055     IConsoleLine::front = this;
00056     IConsoleLine::size++;
00057   }
00058 
00062   ~IConsoleLine()
00063   {
00064     IConsoleLine::size--;
00065     free(buffer);
00066 
00067     delete previous;
00068   }
00069 
00073   static const IConsoleLine *Get(uint index)
00074   {
00075     const IConsoleLine *item = IConsoleLine::front;
00076     while (index != 0 && item != NULL) {
00077       index--;
00078       item = item->previous;
00079     }
00080 
00081     return item;
00082   }
00083 
00091   static bool Truncate()
00092   {
00093     IConsoleLine *cur = IConsoleLine::front;
00094     if (cur == NULL) return false;
00095 
00096     int count = 1;
00097     for (IConsoleLine *item = cur->previous; item != NULL; count++, cur = item, item = item->previous) {
00098       if (item->time > _settings_client.gui.console_backlog_timeout &&
00099           count > _settings_client.gui.console_backlog_length) {
00100         delete item;
00101         cur->previous = NULL;
00102         return true;
00103       }
00104 
00105       if (item->time != MAX_UVALUE(uint16)) item->time++;
00106     }
00107 
00108     return false;
00109   }
00110 
00114   static void Reset()
00115   {
00116     delete IConsoleLine::front;
00117     IConsoleLine::front = NULL;
00118     IConsoleLine::size = 0;
00119   }
00120 };
00121 
00122 /* static */ IConsoleLine *IConsoleLine::front = NULL;
00123 /* static */ int IConsoleLine::size  = 0;
00124 
00125 
00126 /* ** main console cmd buffer ** */
00127 static Textbuf _iconsole_cmdline;
00128 static char *_iconsole_history[ICON_HISTORY_SIZE];
00129 static byte _iconsole_historypos;
00130 IConsoleModes _iconsole_mode;
00131 
00132 /* *************** *
00133  *  end of header  *
00134  * *************** */
00135 
00136 static void IConsoleClearCommand()
00137 {
00138   memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
00139   _iconsole_cmdline.size = 1; // only terminating zero
00140   _iconsole_cmdline.width = 0;
00141   _iconsole_cmdline.caretpos = 0;
00142   _iconsole_cmdline.caretxoffs = 0;
00143   SetWindowDirty(WC_CONSOLE, 0);
00144 }
00145 
00146 static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
00147 
00148 
00149 static const char *IConsoleHistoryAdd(const char *cmd);
00150 static void IConsoleHistoryNavigate(int direction);
00151 
00153 enum ConsoleWidgets {
00154   CW_BACKGROUND, 
00155 };
00156 
00157 static const struct NWidgetPart _nested_console_window_widgets[] = {
00158   NWidget(WWT_EMPTY, INVALID_COLOUR, CW_BACKGROUND), SetResize(1, 1),
00159 };
00160 
00161 static const WindowDesc _console_window_desc(
00162   WDP_MANUAL, 0, 0,
00163   WC_CONSOLE, WC_NONE,
00164   0,
00165   _nested_console_window_widgets, lengthof(_nested_console_window_widgets)
00166 );
00167 
00168 struct IConsoleWindow : Window
00169 {
00170   static int scroll;
00171   int line_height;
00172   int line_offset;
00173 
00174   IConsoleWindow() : Window()
00175   {
00176     _iconsole_mode = ICONSOLE_OPENED;
00177     this->line_height = FONT_HEIGHT_NORMAL + ICON_LINE_SPACING;
00178     this->line_offset = GetStringBoundingBox("] ").width + 5;
00179 
00180     this->InitNested(&_console_window_desc, 0);
00181     ResizeWindow(this, _screen.width, _screen.height / 3);
00182   }
00183 
00184   ~IConsoleWindow()
00185   {
00186     _iconsole_mode = ICONSOLE_CLOSED;
00187   }
00188 
00189   virtual void OnPaint()
00190   {
00191     const int max = (this->height / this->line_height) - 1;
00192     const int right = this->width - 5;
00193 
00194     const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll);
00195     GfxFillRect(this->left, this->top, this->width, this->height - 1, 0);
00196     for (int i = 0; i < max && print != NULL; i++, print = print->previous) {
00197       DrawString(5, right, this->height - (2 + i) * this->line_height, print->buffer, print->colour, SA_LEFT | SA_FORCE);
00198     }
00199     /* If the text is longer than the window, don't show the starting ']' */
00200     int delta = this->width - this->line_offset - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
00201     if (delta > 0) {
00202       DrawString(5, right, this->height - this->line_height, "]", (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00203       delta = 0;
00204     }
00205 
00206     DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00207 
00208     if (_focused_window == this && _iconsole_cmdline.caret) {
00209       DrawString(this->line_offset + delta + _iconsole_cmdline.caretxoffs, right, this->height - this->line_height, "_", TC_WHITE, SA_LEFT | SA_FORCE);
00210     }
00211   }
00212 
00213   virtual void OnHundredthTick()
00214   {
00215     if (IConsoleLine::Truncate() &&
00216         (IConsoleWindow::scroll > IConsoleLine::size)) {
00217       IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
00218       this->SetDirty();
00219     }
00220   }
00221 
00222   virtual void OnMouseLoop()
00223   {
00224     if (HandleCaret(&_iconsole_cmdline)) this->SetDirty();
00225   }
00226 
00227   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00228   {
00229     if (_focused_window != this) return ES_NOT_HANDLED;
00230 
00231     const int scroll_height = (this->height / this->line_height) - 1;
00232     switch (keycode) {
00233       case WKC_UP:
00234         IConsoleHistoryNavigate(1);
00235         this->SetDirty();
00236         break;
00237 
00238       case WKC_DOWN:
00239         IConsoleHistoryNavigate(-1);
00240         this->SetDirty();
00241         break;
00242 
00243       case WKC_SHIFT | WKC_PAGEDOWN:
00244         if (IConsoleWindow::scroll - scroll_height < 0) {
00245           IConsoleWindow::scroll = 0;
00246         } else {
00247           IConsoleWindow::scroll -= scroll_height;
00248         }
00249         this->SetDirty();
00250         break;
00251 
00252       case WKC_SHIFT | WKC_PAGEUP:
00253         if (IConsoleWindow::scroll + scroll_height > IConsoleLine::size - scroll_height) {
00254           IConsoleWindow::scroll = IConsoleLine::size - scroll_height;
00255         } else {
00256           IConsoleWindow::scroll += scroll_height;
00257         }
00258         this->SetDirty();
00259         break;
00260 
00261       case WKC_SHIFT | WKC_DOWN:
00262         if (IConsoleWindow::scroll <= 0) {
00263           IConsoleWindow::scroll = 0;
00264         } else {
00265           --IConsoleWindow::scroll;
00266         }
00267         this->SetDirty();
00268         break;
00269 
00270       case WKC_SHIFT | WKC_UP:
00271         if (IConsoleWindow::scroll >= IConsoleLine::size) {
00272           IConsoleWindow::scroll = IConsoleLine::size;
00273         } else {
00274           ++IConsoleWindow::scroll;
00275         }
00276         this->SetDirty();
00277         break;
00278 
00279       case WKC_BACKQUOTE:
00280         IConsoleSwitch();
00281         break;
00282 
00283       case WKC_RETURN: case WKC_NUM_ENTER: {
00284         /* We always want the ] at the left side; we always force these strings to be left
00285          * aligned anyway. So enforce this in all cases by addding a left-to-right marker,
00286          * otherwise it will be drawn at the wrong side with right-to-left texts. */
00287         IConsolePrintF(CC_COMMAND, LRM "] %s", _iconsole_cmdline.buf);
00288         const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
00289         IConsoleClearCommand();
00290 
00291         if (cmd != NULL) IConsoleCmdExec(cmd);
00292       } break;
00293 
00294       case WKC_CTRL | WKC_RETURN:
00295         _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
00296         IConsoleResize(this);
00297         MarkWholeScreenDirty();
00298         break;
00299 
00300 #ifdef WITH_COCOA
00301       case (WKC_META | 'V'):
00302 #endif
00303       case (WKC_CTRL | 'V'):
00304         if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
00305           IConsoleResetHistoryPos();
00306           this->SetDirty();
00307         }
00308         break;
00309 
00310       case (WKC_CTRL | 'L'):
00311         IConsoleCmdExec("clear");
00312         break;
00313 
00314 #ifdef WITH_COCOA
00315       case (WKC_META | 'U'):
00316 #endif
00317       case (WKC_CTRL | 'U'):
00318         DeleteTextBufferAll(&_iconsole_cmdline);
00319         this->SetDirty();
00320         break;
00321 
00322       case WKC_BACKSPACE: case WKC_DELETE:
00323         if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) {
00324           IConsoleResetHistoryPos();
00325           this->SetDirty();
00326         }
00327         break;
00328 
00329       case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
00330         if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) {
00331           IConsoleResetHistoryPos();
00332           this->SetDirty();
00333         }
00334         break;
00335 
00336       default:
00337         if (IsValidChar(key, CS_ALPHANUMERAL)) {
00338           IConsoleWindow::scroll = 0;
00339           InsertTextBufferChar(&_iconsole_cmdline, key);
00340           IConsoleResetHistoryPos();
00341           this->SetDirty();
00342         } else {
00343           return ES_NOT_HANDLED;
00344         }
00345     }
00346     return ES_HANDLED;
00347   }
00348 };
00349 
00350 int IConsoleWindow::scroll = 0;
00351 
00352 void IConsoleGUIInit()
00353 {
00354   _iconsole_historypos = ICON_HISTORY_SIZE - 1;
00355   _iconsole_mode = ICONSOLE_CLOSED;
00356 
00357   IConsoleLine::Reset();
00358   memset(_iconsole_history, 0, sizeof(_iconsole_history));
00359 
00360   _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it
00361   _iconsole_cmdline.maxsize = ICON_CMDLN_SIZE;
00362 
00363   IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
00364   IConsolePrint(CC_WHITE,  "------------------------------------");
00365   IConsolePrint(CC_WHITE,  "use \"help\" for more information");
00366   IConsolePrint(CC_WHITE,  "");
00367   IConsoleClearCommand();
00368 }
00369 
00370 void IConsoleClearBuffer()
00371 {
00372   IConsoleLine::Reset();
00373 }
00374 
00375 void IConsoleGUIFree()
00376 {
00377   free(_iconsole_cmdline.buf);
00378   IConsoleClearBuffer();
00379 }
00380 
00381 void IConsoleResize(Window *w)
00382 {
00383   switch (_iconsole_mode) {
00384     case ICONSOLE_OPENED:
00385       w->height = _screen.height / 3;
00386       w->width = _screen.width;
00387       break;
00388     case ICONSOLE_FULL:
00389       w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
00390       w->width = _screen.width;
00391       break;
00392     default: return;
00393   }
00394 
00395   MarkWholeScreenDirty();
00396 }
00397 
00398 void IConsoleSwitch()
00399 {
00400   switch (_iconsole_mode) {
00401     case ICONSOLE_CLOSED:
00402       new IConsoleWindow();
00403       break;
00404 
00405     case ICONSOLE_OPENED: case ICONSOLE_FULL:
00406       DeleteWindowById(WC_CONSOLE, 0);
00407       break;
00408   }
00409 
00410   MarkWholeScreenDirty();
00411 }
00412 
00413 void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
00414 
00421 static const char *IConsoleHistoryAdd(const char *cmd)
00422 {
00423   /* Strip all spaces at the begin */
00424   while (IsWhitespace(*cmd)) cmd++;
00425 
00426   /* Do not put empty command in history */
00427   if (StrEmpty(cmd)) return NULL;
00428 
00429   /* Do not put in history if command is same as previous */
00430   if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
00431     free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
00432     memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
00433     _iconsole_history[0] = strdup(cmd);
00434   }
00435 
00436   /* Reset the history position */
00437   IConsoleResetHistoryPos();
00438   return _iconsole_history[0];
00439 }
00440 
00445 static void IConsoleHistoryNavigate(int direction)
00446 {
00447   if (_iconsole_history[0] == NULL) return; // Empty history
00448   int i = _iconsole_historypos + direction;
00449 
00450   /* watch out for overflows, just wrap around */
00451   if (i < 0) i = ICON_HISTORY_SIZE - 1;
00452   if (i >= ICON_HISTORY_SIZE) i = 0;
00453 
00454   if (direction > 0) {
00455     if (_iconsole_history[i] == NULL) i = 0;
00456   }
00457 
00458   if (direction < 0) {
00459     while (i > 0 && _iconsole_history[i] == NULL) i--;
00460   }
00461 
00462   _iconsole_historypos = i;
00463   IConsoleClearCommand();
00464   /* copy history to 'command prompt / bash' */
00465   assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
00466   ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxsize);
00467   UpdateTextBufferSize(&_iconsole_cmdline);
00468 }
00469 
00479 void IConsoleGUIPrint(ConsoleColour colour_code, char *str)
00480 {
00481   new IConsoleLine(str, (TextColour)colour_code);
00482   SetWindowDirty(WC_CONSOLE, 0);
00483 }

Generated on Sun Nov 14 14:41:50 2010 for OpenTTD by  doxygen 1.6.1