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