00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "textbuf_type.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 "widgets/console_widget.h"
00026
00027 #include "table/strings.h"
00028
00029 static const uint ICON_HISTORY_SIZE = 20;
00030 static const uint ICON_LINE_SPACING = 2;
00031 static const uint ICON_RIGHT_BORDERWIDTH = 10;
00032 static const uint ICON_BOTTOM_BORDERWIDTH = 12;
00033
00037 struct IConsoleLine {
00038 static IConsoleLine *front;
00039 static int size;
00040
00041 IConsoleLine *previous;
00042 char *buffer;
00043 TextColour colour;
00044 uint16 time;
00045
00051 IConsoleLine(char *buffer, TextColour colour) :
00052 previous(IConsoleLine::front),
00053 buffer(buffer),
00054 colour(colour),
00055 time(0)
00056 {
00057 IConsoleLine::front = this;
00058 IConsoleLine::size++;
00059 }
00060
00064 ~IConsoleLine()
00065 {
00066 IConsoleLine::size--;
00067 free(buffer);
00068
00069 delete previous;
00070 }
00071
00075 static const IConsoleLine *Get(uint index)
00076 {
00077 const IConsoleLine *item = IConsoleLine::front;
00078 while (index != 0 && item != NULL) {
00079 index--;
00080 item = item->previous;
00081 }
00082
00083 return item;
00084 }
00085
00093 static bool Truncate()
00094 {
00095 IConsoleLine *cur = IConsoleLine::front;
00096 if (cur == NULL) return false;
00097
00098 int count = 1;
00099 for (IConsoleLine *item = cur->previous; item != NULL; count++, cur = item, item = item->previous) {
00100 if (item->time > _settings_client.gui.console_backlog_timeout &&
00101 count > _settings_client.gui.console_backlog_length) {
00102 delete item;
00103 cur->previous = NULL;
00104 return true;
00105 }
00106
00107 if (item->time != MAX_UVALUE(uint16)) item->time++;
00108 }
00109
00110 return false;
00111 }
00112
00116 static void Reset()
00117 {
00118 delete IConsoleLine::front;
00119 IConsoleLine::front = NULL;
00120 IConsoleLine::size = 0;
00121 }
00122 };
00123
00124 IConsoleLine *IConsoleLine::front = NULL;
00125 int IConsoleLine::size = 0;
00126
00127
00128
00129 static Textbuf _iconsole_cmdline(ICON_CMDLN_SIZE);
00130 static char *_iconsole_history[ICON_HISTORY_SIZE];
00131 static int _iconsole_historypos;
00132 IConsoleModes _iconsole_mode;
00133
00134
00135
00136
00137
00138 static void IConsoleClearCommand()
00139 {
00140 memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
00141 _iconsole_cmdline.chars = _iconsole_cmdline.bytes = 1;
00142 _iconsole_cmdline.pixels = 0;
00143 _iconsole_cmdline.caretpos = 0;
00144 _iconsole_cmdline.caretxoffs = 0;
00145 SetWindowDirty(WC_CONSOLE, 0);
00146 }
00147
00148 static inline void IConsoleResetHistoryPos()
00149 {
00150 _iconsole_historypos = -1;
00151 }
00152
00153
00154 static const char *IConsoleHistoryAdd(const char *cmd);
00155 static void IConsoleHistoryNavigate(int direction);
00156
00157 static const struct NWidgetPart _nested_console_window_widgets[] = {
00158 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_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
00193 void Scroll(int amount)
00194 {
00195 int max_scroll = max<int>(0, IConsoleLine::size + 1 - this->height / this->line_height);
00196 IConsoleWindow::scroll = Clamp<int>(IConsoleWindow::scroll + amount, 0, max_scroll);
00197 this->SetDirty();
00198 }
00199
00200 virtual void OnPaint()
00201 {
00202 const int right = this->width - 5;
00203
00204 GfxFillRect(0, 0, this->width - 1, this->height - 1, PC_BLACK);
00205 int ypos = this->height - this->line_height;
00206 for (const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll); print != NULL; print = print->previous) {
00207 SetDParamStr(0, print->buffer);
00208 ypos = DrawStringMultiLine(5, right, -this->line_height, ypos, STR_JUST_RAW_STRING, print->colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - ICON_LINE_SPACING;
00209 if (ypos < 0) break;
00210 }
00211
00212 int delta = this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH;
00213 if (delta > 0) {
00214 DrawString(5, right, this->height - this->line_height, "]", (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00215 delta = 0;
00216 }
00217
00218 DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00219
00220 if (_focused_window == this && _iconsole_cmdline.caret) {
00221 DrawString(this->line_offset + delta + _iconsole_cmdline.caretxoffs, right, this->height - this->line_height, "_", TC_WHITE, SA_LEFT | SA_FORCE);
00222 }
00223 }
00224
00225 virtual void OnHundredthTick()
00226 {
00227 if (IConsoleLine::Truncate() &&
00228 (IConsoleWindow::scroll > IConsoleLine::size)) {
00229 IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
00230 this->SetDirty();
00231 }
00232 }
00233
00234 virtual void OnMouseLoop()
00235 {
00236 if (_iconsole_cmdline.HandleCaret()) this->SetDirty();
00237 }
00238
00239 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00240 {
00241 if (_focused_window != this) return ES_NOT_HANDLED;
00242
00243 const int scroll_height = (this->height / this->line_height) - 1;
00244 switch (keycode) {
00245 case WKC_UP:
00246 IConsoleHistoryNavigate(1);
00247 this->SetDirty();
00248 break;
00249
00250 case WKC_DOWN:
00251 IConsoleHistoryNavigate(-1);
00252 this->SetDirty();
00253 break;
00254
00255 case WKC_SHIFT | WKC_PAGEDOWN:
00256 this->Scroll(-scroll_height);
00257 break;
00258
00259 case WKC_SHIFT | WKC_PAGEUP:
00260 this->Scroll(scroll_height);
00261 break;
00262
00263 case WKC_SHIFT | WKC_DOWN:
00264 this->Scroll(-1);
00265 break;
00266
00267 case WKC_SHIFT | WKC_UP:
00268 this->Scroll(1);
00269 break;
00270
00271 case WKC_BACKQUOTE:
00272 IConsoleSwitch();
00273 break;
00274
00275 case WKC_RETURN: case WKC_NUM_ENTER: {
00276
00277
00278
00279 IConsolePrintF(CC_COMMAND, LRM "] %s", _iconsole_cmdline.buf);
00280 const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
00281 IConsoleClearCommand();
00282
00283 if (cmd != NULL) IConsoleCmdExec(cmd);
00284 break;
00285 }
00286
00287 case WKC_CTRL | WKC_RETURN:
00288 _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
00289 IConsoleResize(this);
00290 MarkWholeScreenDirty();
00291 break;
00292
00293 #ifdef WITH_COCOA
00294 case (WKC_META | 'V'):
00295 #endif
00296 case (WKC_CTRL | 'V'):
00297 if (_iconsole_cmdline.InsertClipboard()) {
00298 IConsoleResetHistoryPos();
00299 this->SetDirty();
00300 }
00301 break;
00302
00303 case (WKC_CTRL | 'L'):
00304 IConsoleCmdExec("clear");
00305 break;
00306
00307 #ifdef WITH_COCOA
00308 case (WKC_META | 'U'):
00309 #endif
00310 case (WKC_CTRL | 'U'):
00311 _iconsole_cmdline.DeleteAll();
00312 this->SetDirty();
00313 break;
00314
00315 case WKC_BACKSPACE: case WKC_DELETE:
00316 if (_iconsole_cmdline.DeleteChar(keycode)) {
00317 IConsoleResetHistoryPos();
00318 this->SetDirty();
00319 }
00320 break;
00321
00322 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
00323 if (_iconsole_cmdline.MovePos(keycode)) {
00324 IConsoleResetHistoryPos();
00325 this->SetDirty();
00326 }
00327 break;
00328
00329 default:
00330 if (IsValidChar(key, CS_ALPHANUMERAL)) {
00331 IConsoleWindow::scroll = 0;
00332 _iconsole_cmdline.InsertChar(key);
00333 IConsoleResetHistoryPos();
00334 this->SetDirty();
00335 } else {
00336 return ES_NOT_HANDLED;
00337 }
00338 break;
00339 }
00340 return ES_HANDLED;
00341 }
00342
00343 virtual void OnMouseWheel(int wheel)
00344 {
00345 this->Scroll(-wheel);
00346 }
00347 };
00348
00349 int IConsoleWindow::scroll = 0;
00350
00351 void IConsoleGUIInit()
00352 {
00353 IConsoleResetHistoryPos();
00354 _iconsole_mode = ICONSOLE_CLOSED;
00355
00356 IConsoleLine::Reset();
00357 memset(_iconsole_history, 0, sizeof(_iconsole_history));
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 IConsoleClearBuffer();
00374 }
00375
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
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
00411 void IConsoleClose()
00412 {
00413 if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();
00414 }
00415
00422 static const char *IConsoleHistoryAdd(const char *cmd)
00423 {
00424
00425 while (IsWhitespace(*cmd)) cmd++;
00426
00427
00428 if (StrEmpty(cmd)) return NULL;
00429
00430
00431 if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
00432 free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
00433 memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
00434 _iconsole_history[0] = strdup(cmd);
00435 }
00436
00437
00438 IConsoleResetHistoryPos();
00439 return _iconsole_history[0];
00440 }
00441
00446 static void IConsoleHistoryNavigate(int direction)
00447 {
00448 if (_iconsole_history[0] == NULL) return;
00449 _iconsole_historypos = Clamp(_iconsole_historypos + direction, -1, ICON_HISTORY_SIZE - 1);
00450
00451 if (direction > 0 && _iconsole_history[_iconsole_historypos] == NULL) _iconsole_historypos--;
00452
00453 if (_iconsole_historypos == -1) {
00454 _iconsole_cmdline.DeleteAll();
00455 } else {
00456 _iconsole_cmdline.Assign(_iconsole_history[_iconsole_historypos]);
00457 }
00458 }
00459
00469 void IConsoleGUIPrint(TextColour colour_code, char *str)
00470 {
00471 new IConsoleLine(str, colour_code);
00472 SetWindowDirty(WC_CONSOLE, 0);
00473 }
00474
00475
00481 bool IsValidConsoleColour(TextColour c)
00482 {
00483
00484 if (!(c & TC_IS_PALETTE_COLOUR)) return TC_BEGIN <= c && c < TC_END;
00485
00486
00487
00488 c &= ~TC_IS_PALETTE_COLOUR;
00489 for (uint i = COLOUR_BEGIN; i < COLOUR_END; i++) {
00490 if (_colour_gradient[i][4] == c) return true;
00491 }
00492
00493 return false;
00494 }