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 "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 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 byte _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.size = 1;
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
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 IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf);
00285 const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
00286 IConsoleClearCommand();
00287
00288 if (cmd != NULL) IConsoleCmdExec(cmd);
00289 } break;
00290
00291 case WKC_CTRL | WKC_RETURN:
00292 _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
00293 IConsoleResize(this);
00294 MarkWholeScreenDirty();
00295 break;
00296
00297 #ifdef WITH_COCOA
00298 case (WKC_META | 'V'):
00299 #endif
00300 case (WKC_CTRL | 'V'):
00301 if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
00302 IConsoleResetHistoryPos();
00303 this->SetDirty();
00304 }
00305 break;
00306
00307 case (WKC_CTRL | 'L'):
00308 IConsoleCmdExec("clear");
00309 break;
00310
00311 #ifdef WITH_COCOA
00312 case (WKC_META | 'U'):
00313 #endif
00314 case (WKC_CTRL | 'U'):
00315 DeleteTextBufferAll(&_iconsole_cmdline);
00316 this->SetDirty();
00317 break;
00318
00319 case WKC_BACKSPACE: case WKC_DELETE:
00320 if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) {
00321 IConsoleResetHistoryPos();
00322 this->SetDirty();
00323 }
00324 break;
00325
00326 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
00327 if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) {
00328 IConsoleResetHistoryPos();
00329 this->SetDirty();
00330 }
00331 break;
00332
00333 default:
00334 if (IsValidChar(key, CS_ALPHANUMERAL)) {
00335 IConsoleWindow::scroll = 0;
00336 InsertTextBufferChar(&_iconsole_cmdline, key);
00337 IConsoleResetHistoryPos();
00338 this->SetDirty();
00339 } else {
00340 return ES_NOT_HANDLED;
00341 }
00342 }
00343 return ES_HANDLED;
00344 }
00345 };
00346
00347 int IConsoleWindow::scroll = 0;
00348
00349 void IConsoleGUIInit()
00350 {
00351 _iconsole_historypos = ICON_HISTORY_SIZE - 1;
00352 _iconsole_mode = ICONSOLE_CLOSED;
00353
00354 IConsoleLine::Reset();
00355 memset(_iconsole_history, 0, sizeof(_iconsole_history));
00356
00357 _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE);
00358 _iconsole_cmdline.maxsize = ICON_CMDLN_SIZE;
00359
00360 IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
00361 IConsolePrint(CC_WHITE, "------------------------------------");
00362 IConsolePrint(CC_WHITE, "use \"help\" for more information");
00363 IConsolePrint(CC_WHITE, "");
00364 IConsoleClearCommand();
00365 }
00366
00367 void IConsoleClearBuffer()
00368 {
00369 IConsoleLine::Reset();
00370 }
00371
00372 void IConsoleGUIFree()
00373 {
00374 free(_iconsole_cmdline.buf);
00375 IConsoleClearBuffer();
00376 }
00377
00378 void IConsoleResize(Window *w)
00379 {
00380 switch (_iconsole_mode) {
00381 case ICONSOLE_OPENED:
00382 w->height = _screen.height / 3;
00383 w->width = _screen.width;
00384 break;
00385 case ICONSOLE_FULL:
00386 w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
00387 w->width = _screen.width;
00388 break;
00389 default: return;
00390 }
00391
00392 MarkWholeScreenDirty();
00393 }
00394
00395 void IConsoleSwitch()
00396 {
00397 switch (_iconsole_mode) {
00398 case ICONSOLE_CLOSED:
00399 new IConsoleWindow();
00400 break;
00401
00402 case ICONSOLE_OPENED: case ICONSOLE_FULL:
00403 DeleteWindowById(WC_CONSOLE, 0);
00404 break;
00405 }
00406
00407 MarkWholeScreenDirty();
00408 }
00409
00410 void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
00411
00418 static const char *IConsoleHistoryAdd(const char *cmd)
00419 {
00420
00421 while (IsWhitespace(*cmd)) cmd++;
00422
00423
00424 if (StrEmpty(cmd)) return NULL;
00425
00426
00427 if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
00428 free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
00429 memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
00430 _iconsole_history[0] = strdup(cmd);
00431 }
00432
00433
00434 IConsoleResetHistoryPos();
00435 return _iconsole_history[0];
00436 }
00437
00442 static void IConsoleHistoryNavigate(int direction)
00443 {
00444 if (_iconsole_history[0] == NULL) return;
00445 int i = _iconsole_historypos + direction;
00446
00447
00448 if (i < 0) i = ICON_HISTORY_SIZE - 1;
00449 if (i >= ICON_HISTORY_SIZE) i = 0;
00450
00451 if (direction > 0) {
00452 if (_iconsole_history[i] == NULL) i = 0;
00453 }
00454
00455 if (direction < 0) {
00456 while (i > 0 && _iconsole_history[i] == NULL) i--;
00457 }
00458
00459 _iconsole_historypos = i;
00460 IConsoleClearCommand();
00461
00462 assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
00463 ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxsize);
00464 UpdateTextBufferSize(&_iconsole_cmdline);
00465 }
00466
00476 void IConsoleGUIPrint(ConsoleColour colour_code, char *str)
00477 {
00478 new IConsoleLine(str, (TextColour)colour_code);
00479 SetWindowDirty(WC_CONSOLE, 0);
00480 }