00001
00002
00003
00004
00005
00006
00007
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 IConsoleLine *IConsoleLine::front = NULL;
00122 int IConsoleLine::size = 0;
00123
00124
00125
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
00133
00134
00135 static void IConsoleClearCommand()
00136 {
00137 memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
00138 _iconsole_cmdline.size = 1;
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
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);
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
00420 while (IsWhitespace(*cmd)) cmd++;
00421
00422
00423 if (StrEmpty(cmd)) return NULL;
00424
00425
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
00433 IConsoleResetHistoryPos();
00434 return _iconsole_history[0];
00435 }
00436
00441 static void IConsoleHistoryNavigate(int direction)
00442 {
00443 if (_iconsole_history[0] == NULL) return;
00444 int i = _iconsole_historypos + direction;
00445
00446
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
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 }