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 "gfx_func.h"
00020 #include "settings_type.h"
00021 #include "rev.h"
00022 
00023 
00024 enum {
00025   ICON_HISTORY_SIZE       = 20,
00026   ICON_LINE_SPACING       =  2,
00027   ICON_RIGHT_BORDERWIDTH  = 10,
00028   ICON_BOTTOM_BORDERWIDTH = 12,
00029 };
00030 
00034 struct IConsoleLine {
00035   static IConsoleLine *front; 
00036   static int size;            
00037 
00038   IConsoleLine *previous; 
00039   char *buffer;           
00040   TextColour colour;      
00041   uint16 time;            
00042 
00048   IConsoleLine(char *buffer, TextColour colour) :
00049       previous(IConsoleLine::front),
00050       buffer(buffer),
00051       colour(colour),
00052       time(0)
00053   {
00054     IConsoleLine::front = this;
00055     IConsoleLine::size++;
00056   }
00057 
00061   ~IConsoleLine()
00062   {
00063     IConsoleLine::size--;
00064     free(buffer);
00065 
00066     delete previous;
00067   }
00068 
00072   static const IConsoleLine *Get(uint index)
00073   {
00074     const IConsoleLine *item = IConsoleLine::front;
00075     while (index != 0 && item != NULL) {
00076       index--;
00077       item = item->previous;
00078     }
00079 
00080     return item;
00081   }
00082 
00090   static bool Truncate()
00091   {
00092     IConsoleLine *cur = IConsoleLine::front;
00093     if (cur == NULL) return false;
00094 
00095     int count = 1;
00096     for (IConsoleLine *item = cur->previous; item != NULL; count++, cur = item, item = item->previous) {
00097       if (item->time > _settings_client.gui.console_backlog_timeout &&
00098           count > _settings_client.gui.console_backlog_length) {
00099         delete item;
00100         cur->previous = NULL;
00101         return true;
00102       }
00103 
00104       if (item->time != MAX_UVALUE(uint16)) item->time++;
00105     }
00106 
00107     return false;
00108   }
00109 
00113   static void Reset()
00114   {
00115     delete IConsoleLine::front;
00116     IConsoleLine::front = NULL;
00117     IConsoleLine::size = 0;
00118   }
00119 };
00120 
00121 /* static */ IConsoleLine *IConsoleLine::front = NULL;
00122 /* static */ int IConsoleLine::size  = 0;
00123 
00124 
00125 /* ** main console cmd buffer ** */
00126 static Textbuf _iconsole_cmdline;
00127 static char *_iconsole_history[ICON_HISTORY_SIZE];
00128 static byte _iconsole_historypos;
00129 IConsoleModes _iconsole_mode;
00130 
00131 /* *************** *
00132  *  end of header  *
00133  * *************** */
00134 
00135 static void IConsoleClearCommand()
00136 {
00137   memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
00138   _iconsole_cmdline.size = 1; // only terminating zero
00139   _iconsole_cmdline.width = 0;
00140   _iconsole_cmdline.caretpos = 0;
00141   _iconsole_cmdline.caretxoffs = 0;
00142   SetWindowDirty(WC_CONSOLE, 0);
00143 }
00144 
00145 static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
00146 
00147 
00148 static const char *IConsoleHistoryAdd(const char *cmd);
00149 static void IConsoleHistoryNavigate(int direction);
00150 
00152 enum ConsoleWidgets {
00153   CW_BACKGROUND, 
00154 };
00155 
00156 static const struct NWidgetPart _nested_console_window_widgets[] = {
00157   NWidget(WWT_EMPTY, INVALID_COLOUR, CW_BACKGROUND), SetResize(1, 1),
00158 };
00159 
00160 static const WindowDesc _console_window_desc(
00161   WDP_MANUAL, 0, 0,
00162   WC_CONSOLE, WC_NONE,
00163   0,
00164   _nested_console_window_widgets, lengthof(_nested_console_window_widgets)
00165 );
00166 
00167 struct IConsoleWindow : Window
00168 {
00169   static int scroll;
00170   int line_height;
00171   int line_offset;
00172 
00173   IConsoleWindow() : Window()
00174   {
00175     _iconsole_mode = ICONSOLE_OPENED;
00176     this->line_height = FONT_HEIGHT_NORMAL + ICON_LINE_SPACING;
00177     this->line_offset = GetStringBoundingBox("] ").width + 5;
00178 
00179     this->InitNested(&_console_window_desc, 0);
00180     ResizeWindow(this, _screen.width, _screen.height / 3);
00181   }
00182 
00183   ~IConsoleWindow()
00184   {
00185     _iconsole_mode = ICONSOLE_CLOSED;
00186   }
00187 
00188   virtual void OnPaint()
00189   {
00190     const int max = (this->height / this->line_height) - 1;
00191     const int right = this->width - 5;
00192 
00193     const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll);
00194     GfxFillRect(this->left, this->top, this->width, this->height - 1, 0);
00195     for (int i = 0; i < max && print != NULL; i++, print = print->previous) {
00196       DrawString(5, right, this->height - (2 + i) * this->line_height, print->buffer, print->colour, SA_LEFT | SA_FORCE);
00197     }
00198     /* If the text is longer than the window, don't show the starting ']' */
00199     int delta = this->width - this->line_offset - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
00200     if (delta > 0) {
00201       DrawString(5, right, this->height - this->line_height, "]", (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00202       delta = 0;
00203     }
00204 
00205     DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00206 
00207     if (_focused_window == this && _iconsole_cmdline.caret) {
00208       DrawString(this->line_offset + delta + _iconsole_cmdline.caretxoffs, right, this->height - this->line_height, "_", TC_WHITE, SA_LEFT | SA_FORCE);
00209     }
00210   }
00211 
00212   virtual void OnHundredthTick()
00213   {
00214     if (IConsoleLine::Truncate() &&
00215         (IConsoleWindow::scroll > IConsoleLine::size)) {
00216       IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
00217       this->SetDirty();
00218     }
00219   }
00220 
00221   virtual void OnMouseLoop()
00222   {
00223     if (HandleCaret(&_iconsole_cmdline)) this->SetDirty();
00224   }
00225 
00226   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00227   {
00228     if (_focused_window != this) return ES_NOT_HANDLED;
00229 
00230     const int scroll_height = (this->height / this->line_height) - 1;
00231     switch (keycode) {
00232       case WKC_UP:
00233         IConsoleHistoryNavigate(1);
00234         this->SetDirty();
00235         break;
00236 
00237       case WKC_DOWN:
00238         IConsoleHistoryNavigate(-1);
00239         this->SetDirty();
00240         break;
00241 
00242       case WKC_SHIFT | WKC_PAGEDOWN:
00243         if (IConsoleWindow::scroll - scroll_height < 0) {
00244           IConsoleWindow::scroll = 0;
00245         } else {
00246           IConsoleWindow::scroll -= scroll_height;
00247         }
00248         this->SetDirty();
00249         break;
00250 
00251       case WKC_SHIFT | WKC_PAGEUP:
00252         if (IConsoleWindow::scroll + scroll_height > IConsoleLine::size - scroll_height) {
00253           IConsoleWindow::scroll = IConsoleLine::size - scroll_height;
00254         } else {
00255           IConsoleWindow::scroll += scroll_height;
00256         }
00257         this->SetDirty();
00258         break;
00259 
00260       case WKC_SHIFT | WKC_DOWN:
00261         if (IConsoleWindow::scroll <= 0) {
00262           IConsoleWindow::scroll = 0;
00263         } else {
00264           --IConsoleWindow::scroll;
00265         }
00266         this->SetDirty();
00267         break;
00268 
00269       case WKC_SHIFT | WKC_UP:
00270         if (IConsoleWindow::scroll >= IConsoleLine::size) {
00271           IConsoleWindow::scroll = IConsoleLine::size;
00272         } else {
00273           ++IConsoleWindow::scroll;
00274         }
00275         this->SetDirty();
00276         break;
00277 
00278       case WKC_BACKQUOTE:
00279         IConsoleSwitch();
00280         break;
00281 
00282       case WKC_RETURN: case WKC_NUM_ENTER: {
00283         IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf);
00284         const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
00285         IConsoleClearCommand();
00286 
00287         if (cmd != NULL) IConsoleCmdExec(cmd);
00288       } break;
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     }
00342     return ES_HANDLED;
00343   }
00344 };
00345 
00346 int IConsoleWindow::scroll = 0;
00347 
00348 void IConsoleGUIInit()
00349 {
00350   _iconsole_historypos = ICON_HISTORY_SIZE - 1;
00351   _iconsole_mode = ICONSOLE_CLOSED;
00352 
00353   IConsoleLine::Reset();
00354   memset(_iconsole_history, 0, sizeof(_iconsole_history));
00355 
00356   _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it
00357   _iconsole_cmdline.maxsize = ICON_CMDLN_SIZE;
00358 
00359   IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
00360   IConsolePrint(CC_WHITE,  "------------------------------------");
00361   IConsolePrint(CC_WHITE,  "use \"help\" for more information");
00362   IConsolePrint(CC_WHITE,  "");
00363   IConsoleClearCommand();
00364 }
00365 
00366 void IConsoleClearBuffer()
00367 {
00368   IConsoleLine::Reset();
00369 }
00370 
00371 void IConsoleGUIFree()
00372 {
00373   free(_iconsole_cmdline.buf);
00374   IConsoleClearBuffer();
00375 }
00376 
00377 void IConsoleResize(Window *w)
00378 {
00379   switch (_iconsole_mode) {
00380     case ICONSOLE_OPENED:
00381       w->height = _screen.height / 3;
00382       w->width = _screen.width;
00383       break;
00384     case ICONSOLE_FULL:
00385       w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
00386       w->width = _screen.width;
00387       break;
00388     default: return;
00389   }
00390 
00391   MarkWholeScreenDirty();
00392 }
00393 
00394 void IConsoleSwitch()
00395 {
00396   switch (_iconsole_mode) {
00397     case ICONSOLE_CLOSED:
00398       new IConsoleWindow();
00399       break;
00400 
00401     case ICONSOLE_OPENED: case ICONSOLE_FULL:
00402       DeleteWindowById(WC_CONSOLE, 0);
00403       break;
00404   }
00405 
00406   MarkWholeScreenDirty();
00407 }
00408 
00409 void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
00410 
00417 static const char *IConsoleHistoryAdd(const char *cmd)
00418 {
00419   /* Strip all spaces at the begin */
00420   while (IsWhitespace(*cmd)) cmd++;
00421 
00422   /* Do not put empty command in history */
00423   if (StrEmpty(cmd)) return NULL;
00424 
00425   /* Do not put in history if command is same as previous */
00426   if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
00427     free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
00428     memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
00429     _iconsole_history[0] = strdup(cmd);
00430   }
00431 
00432   /* Reset the history position */
00433   IConsoleResetHistoryPos();
00434   return _iconsole_history[0];
00435 }
00436 
00441 static void IConsoleHistoryNavigate(int direction)
00442 {
00443   if (_iconsole_history[0] == NULL) return; // Empty history
00444   int i = _iconsole_historypos + direction;
00445 
00446   /* watch out for overflows, just wrap around */
00447   if (i < 0) i = ICON_HISTORY_SIZE - 1;
00448   if (i >= ICON_HISTORY_SIZE) i = 0;
00449 
00450   if (direction > 0) {
00451     if (_iconsole_history[i] == NULL) i = 0;
00452   }
00453 
00454   if (direction < 0) {
00455     while (i > 0 && _iconsole_history[i] == NULL) i--;
00456   }
00457 
00458   _iconsole_historypos = i;
00459   IConsoleClearCommand();
00460   /* copy history to 'command prompt / bash' */
00461   assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
00462   ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxsize);
00463   UpdateTextBufferSize(&_iconsole_cmdline);
00464 }
00465 
00475 void IConsoleGUIPrint(ConsoleColour colour_code, char *str)
00476 {
00477   new IConsoleLine(str, (TextColour)colour_code);
00478   SetWindowDirty(WC_CONSOLE, 0);
00479 }

Generated on Sat Dec 26 20:06:00 2009 for OpenTTD by  doxygen 1.5.6