console_gui.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_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 "strings_func.h"
00020 #include "gfx_func.h"
00021 #include "settings_type.h"
00022 #include "console_func.h"
00023 #include "rev.h"
00024 
00025 #include "table/strings.h"
00026 
00027 static const uint ICON_HISTORY_SIZE       = 20;
00028 static const uint ICON_LINE_SPACING       =  2;
00029 static const uint ICON_RIGHT_BORDERWIDTH  = 10;
00030 static const uint ICON_BOTTOM_BORDERWIDTH = 12;
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.chars = _iconsole_cmdline.bytes = 1; // only terminating zero
00140   _iconsole_cmdline.pixels = 0;
00141   _iconsole_cmdline.caretpos = 0;
00142   _iconsole_cmdline.caretxoffs = 0;
00143   SetWindowDirty(WC_CONSOLE, 0);
00144 }
00145 
00146 static inline void IConsoleResetHistoryPos()
00147 {
00148   _iconsole_historypos = ICON_HISTORY_SIZE - 1;
00149 }
00150 
00151 
00152 static const char *IConsoleHistoryAdd(const char *cmd);
00153 static void IConsoleHistoryNavigate(int direction);
00154 
00156 enum ConsoleWidgets {
00157   CW_BACKGROUND, 
00158 };
00159 
00160 static const struct NWidgetPart _nested_console_window_widgets[] = {
00161   NWidget(WWT_EMPTY, INVALID_COLOUR, CW_BACKGROUND), SetResize(1, 1),
00162 };
00163 
00164 static const WindowDesc _console_window_desc(
00165   WDP_MANUAL, 0, 0,
00166   WC_CONSOLE, WC_NONE,
00167   0,
00168   _nested_console_window_widgets, lengthof(_nested_console_window_widgets)
00169 );
00170 
00171 struct IConsoleWindow : Window
00172 {
00173   static int scroll;
00174   int line_height;   
00175   int line_offset;
00176 
00177   IConsoleWindow() : Window()
00178   {
00179     _iconsole_mode = ICONSOLE_OPENED;
00180     this->line_height = FONT_HEIGHT_NORMAL + ICON_LINE_SPACING;
00181     this->line_offset = GetStringBoundingBox("] ").width + 5;
00182 
00183     this->InitNested(&_console_window_desc, 0);
00184     ResizeWindow(this, _screen.width, _screen.height / 3);
00185   }
00186 
00187   ~IConsoleWindow()
00188   {
00189     _iconsole_mode = ICONSOLE_CLOSED;
00190   }
00191 
00196   void Scroll(int amount)
00197   {
00198     int max_scroll = max<int>(0, IConsoleLine::size + 1 - this->height / this->line_height);
00199     IConsoleWindow::scroll = Clamp<int>(IConsoleWindow::scroll + amount, 0, max_scroll);
00200     this->SetDirty();
00201   }
00202 
00203   virtual void OnPaint()
00204   {
00205     const int right = this->width - 5;
00206 
00207     GfxFillRect(0, 0, this->width - 1, this->height - 1, PC_BLACK);
00208     int ypos = this->height - this->line_height;
00209     for (const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll); print != NULL; print = print->previous) {
00210       SetDParamStr(0, print->buffer);
00211       ypos = DrawStringMultiLine(5, right, -this->line_height, ypos, STR_JUST_RAW_STRING, print->colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - ICON_LINE_SPACING;
00212       if (ypos < 0) break;
00213     }
00214     /* If the text is longer than the window, don't show the starting ']' */
00215     int delta = this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH;
00216     if (delta > 0) {
00217       DrawString(5, right, this->height - this->line_height, "]", (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00218       delta = 0;
00219     }
00220 
00221     DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00222 
00223     if (_focused_window == this && _iconsole_cmdline.caret) {
00224       DrawString(this->line_offset + delta + _iconsole_cmdline.caretxoffs, right, this->height - this->line_height, "_", TC_WHITE, SA_LEFT | SA_FORCE);
00225     }
00226   }
00227 
00228   virtual void OnHundredthTick()
00229   {
00230     if (IConsoleLine::Truncate() &&
00231         (IConsoleWindow::scroll > IConsoleLine::size)) {
00232       IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
00233       this->SetDirty();
00234     }
00235   }
00236 
00237   virtual void OnMouseLoop()
00238   {
00239     if (HandleCaret(&_iconsole_cmdline)) this->SetDirty();
00240   }
00241 
00242   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00243   {
00244     if (_focused_window != this) return ES_NOT_HANDLED;
00245 
00246     const int scroll_height = (this->height / this->line_height) - 1;
00247     switch (keycode) {
00248       case WKC_UP:
00249         IConsoleHistoryNavigate(1);
00250         this->SetDirty();
00251         break;
00252 
00253       case WKC_DOWN:
00254         IConsoleHistoryNavigate(-1);
00255         this->SetDirty();
00256         break;
00257 
00258       case WKC_SHIFT | WKC_PAGEDOWN:
00259         this->Scroll(-scroll_height);
00260         break;
00261 
00262       case WKC_SHIFT | WKC_PAGEUP:
00263         this->Scroll(scroll_height);
00264         break;
00265 
00266       case WKC_SHIFT | WKC_DOWN:
00267         this->Scroll(-1);
00268         break;
00269 
00270       case WKC_SHIFT | WKC_UP:
00271         this->Scroll(1);
00272         break;
00273 
00274       case WKC_BACKQUOTE:
00275         IConsoleSwitch();
00276         break;
00277 
00278       case WKC_RETURN: case WKC_NUM_ENTER: {
00279         /* We always want the ] at the left side; we always force these strings to be left
00280          * aligned anyway. So enforce this in all cases by addding a left-to-right marker,
00281          * otherwise it will be drawn at the wrong side with right-to-left texts. */
00282         IConsolePrintF(CC_COMMAND, LRM "] %s", _iconsole_cmdline.buf);
00283         const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
00284         IConsoleClearCommand();
00285 
00286         if (cmd != NULL) IConsoleCmdExec(cmd);
00287         break;
00288       }
00289 
00290       case WKC_CTRL | WKC_RETURN:
00291         _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
00292         IConsoleResize(this);
00293         MarkWholeScreenDirty();
00294         break;
00295 
00296 #ifdef WITH_COCOA
00297       case (WKC_META | 'V'):
00298 #endif
00299       case (WKC_CTRL | 'V'):
00300         if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
00301           IConsoleResetHistoryPos();
00302           this->SetDirty();
00303         }
00304         break;
00305 
00306       case (WKC_CTRL | 'L'):
00307         IConsoleCmdExec("clear");
00308         break;
00309 
00310 #ifdef WITH_COCOA
00311       case (WKC_META | 'U'):
00312 #endif
00313       case (WKC_CTRL | 'U'):
00314         DeleteTextBufferAll(&_iconsole_cmdline);
00315         this->SetDirty();
00316         break;
00317 
00318       case WKC_BACKSPACE: case WKC_DELETE:
00319         if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) {
00320           IConsoleResetHistoryPos();
00321           this->SetDirty();
00322         }
00323         break;
00324 
00325       case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
00326         if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) {
00327           IConsoleResetHistoryPos();
00328           this->SetDirty();
00329         }
00330         break;
00331 
00332       default:
00333         if (IsValidChar(key, CS_ALPHANUMERAL)) {
00334           IConsoleWindow::scroll = 0;
00335           InsertTextBufferChar(&_iconsole_cmdline, key);
00336           IConsoleResetHistoryPos();
00337           this->SetDirty();
00338         } else {
00339           return ES_NOT_HANDLED;
00340         }
00341         break;
00342     }
00343     return ES_HANDLED;
00344   }
00345 
00346   virtual void OnMouseWheel(int wheel)
00347   {
00348     this->Scroll(-wheel);
00349   }
00350 };
00351 
00352 int IConsoleWindow::scroll = 0;
00353 
00354 void IConsoleGUIInit()
00355 {
00356   _iconsole_historypos = ICON_HISTORY_SIZE - 1;
00357   _iconsole_mode = ICONSOLE_CLOSED;
00358 
00359   IConsoleLine::Reset();
00360   memset(_iconsole_history, 0, sizeof(_iconsole_history));
00361 
00362   _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it
00363   _iconsole_cmdline.max_bytes = ICON_CMDLN_SIZE;
00364   _iconsole_cmdline.max_chars = ICON_CMDLN_SIZE;
00365 
00366   IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
00367   IConsolePrint(CC_WHITE,  "------------------------------------");
00368   IConsolePrint(CC_WHITE,  "use \"help\" for more information");
00369   IConsolePrint(CC_WHITE,  "");
00370   IConsoleClearCommand();
00371 }
00372 
00373 void IConsoleClearBuffer()
00374 {
00375   IConsoleLine::Reset();
00376 }
00377 
00378 void IConsoleGUIFree()
00379 {
00380   free(_iconsole_cmdline.buf);
00381   IConsoleClearBuffer();
00382 }
00383 
00385 void IConsoleResize(Window *w)
00386 {
00387   switch (_iconsole_mode) {
00388     case ICONSOLE_OPENED:
00389       w->height = _screen.height / 3;
00390       w->width = _screen.width;
00391       break;
00392     case ICONSOLE_FULL:
00393       w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
00394       w->width = _screen.width;
00395       break;
00396     default: return;
00397   }
00398 
00399   MarkWholeScreenDirty();
00400 }
00401 
00403 void IConsoleSwitch()
00404 {
00405   switch (_iconsole_mode) {
00406     case ICONSOLE_CLOSED:
00407       new IConsoleWindow();
00408       break;
00409 
00410     case ICONSOLE_OPENED: case ICONSOLE_FULL:
00411       DeleteWindowById(WC_CONSOLE, 0);
00412       break;
00413   }
00414 
00415   MarkWholeScreenDirty();
00416 }
00417 
00419 void IConsoleClose()
00420 {
00421   if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();
00422 }
00423 
00430 static const char *IConsoleHistoryAdd(const char *cmd)
00431 {
00432   /* Strip all spaces at the begin */
00433   while (IsWhitespace(*cmd)) cmd++;
00434 
00435   /* Do not put empty command in history */
00436   if (StrEmpty(cmd)) return NULL;
00437 
00438   /* Do not put in history if command is same as previous */
00439   if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
00440     free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
00441     memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
00442     _iconsole_history[0] = strdup(cmd);
00443   }
00444 
00445   /* Reset the history position */
00446   IConsoleResetHistoryPos();
00447   return _iconsole_history[0];
00448 }
00449 
00454 static void IConsoleHistoryNavigate(int direction)
00455 {
00456   if (_iconsole_history[0] == NULL) return; // Empty history
00457   int i = _iconsole_historypos + direction;
00458 
00459   /* watch out for overflows, just wrap around */
00460   if (i < 0) i = ICON_HISTORY_SIZE - 1;
00461   if ((uint)i >= ICON_HISTORY_SIZE) i = 0;
00462 
00463   if (direction > 0) {
00464     if (_iconsole_history[i] == NULL) i = 0;
00465   }
00466 
00467   if (direction < 0) {
00468     while (i > 0 && _iconsole_history[i] == NULL) i--;
00469   }
00470 
00471   _iconsole_historypos = i;
00472   IConsoleClearCommand();
00473   /* copy history to 'command prompt / bash' */
00474   assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
00475   ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.max_bytes);
00476   UpdateTextBufferSize(&_iconsole_cmdline);
00477 }
00478 
00488 void IConsoleGUIPrint(TextColour colour_code, char *str)
00489 {
00490   new IConsoleLine(str, colour_code);
00491   SetWindowDirty(WC_CONSOLE, 0);
00492 }
00493 
00494 
00500 bool IsValidConsoleColour(TextColour c)
00501 {
00502   /* A normal text colour is used. */
00503   if (!(c & TC_IS_PALETTE_COLOUR)) return TC_BEGIN <= c && c < TC_END;
00504 
00505   /* A text colour from the palette is used; must be the company
00506    * colour gradient, so it must be one of those. */
00507   c &= ~TC_IS_PALETTE_COLOUR;
00508   for (uint i = COLOUR_BEGIN; i < COLOUR_END; i++) {
00509     if (_colour_gradient[i][4] == c) return true;
00510   }
00511 
00512   return false;
00513 }

Generated on Sun May 8 07:30:11 2011 for OpenTTD by  doxygen 1.6.1