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 "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 IConsoleLine *IConsoleLine::front = NULL;
00123 int IConsoleLine::size = 0;
00124
00125
00126
00127 static Textbuf _iconsole_cmdline;
00128 static char *_iconsole_history[ICON_HISTORY_SIZE];
00129 static int _iconsole_historypos;
00130 IConsoleModes _iconsole_mode;
00131
00132
00133
00134
00135
00136 static void IConsoleClearCommand()
00137 {
00138 memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
00139 _iconsole_cmdline.chars = _iconsole_cmdline.bytes = 1;
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 = -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
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
00280
00281
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 IConsoleResetHistoryPos();
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);
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
00433 while (IsWhitespace(*cmd)) cmd++;
00434
00435
00436 if (StrEmpty(cmd)) return NULL;
00437
00438
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
00446 IConsoleResetHistoryPos();
00447 return _iconsole_history[0];
00448 }
00449
00454 static void IConsoleHistoryNavigate(int direction)
00455 {
00456 if (_iconsole_history[0] == NULL) return;
00457 _iconsole_historypos = Clamp(_iconsole_historypos + direction, -1, ICON_HISTORY_SIZE - 1);
00458
00459 if (direction > 0 && _iconsole_history[_iconsole_historypos] == NULL) _iconsole_historypos--;
00460
00461 if (_iconsole_historypos == -1) {
00462 *_iconsole_cmdline.buf = '\0';
00463 } else {
00464 ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[_iconsole_historypos], _iconsole_cmdline.max_bytes);
00465 }
00466 UpdateTextBufferSize(&_iconsole_cmdline);
00467 }
00468
00478 void IConsoleGUIPrint(TextColour colour_code, char *str)
00479 {
00480 new IConsoleLine(str, colour_code);
00481 SetWindowDirty(WC_CONSOLE, 0);
00482 }
00483
00484
00490 bool IsValidConsoleColour(TextColour c)
00491 {
00492
00493 if (!(c & TC_IS_PALETTE_COLOUR)) return TC_BEGIN <= c && c < TC_END;
00494
00495
00496
00497 c &= ~TC_IS_PALETTE_COLOUR;
00498 for (uint i = COLOUR_BEGIN; i < COLOUR_END; i++) {
00499 if (_colour_gradient[i][4] == c) return true;
00500 }
00501
00502 return false;
00503 }