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 #include "video/video_driver.hpp"
00025
00026 #include "widgets/console_widget.h"
00027
00028 #include "table/strings.h"
00029
00030 static const uint ICON_HISTORY_SIZE = 20;
00031 static const uint ICON_LINE_SPACING = 2;
00032 static const uint ICON_RIGHT_BORDERWIDTH = 10;
00033 static const uint ICON_BOTTOM_BORDERWIDTH = 12;
00034
00038 struct IConsoleLine {
00039 static IConsoleLine *front;
00040 static int size;
00041
00042 IConsoleLine *previous;
00043 char *buffer;
00044 TextColour colour;
00045 uint16 time;
00046
00052 IConsoleLine(char *buffer, TextColour colour) :
00053 previous(IConsoleLine::front),
00054 buffer(buffer),
00055 colour(colour),
00056 time(0)
00057 {
00058 IConsoleLine::front = this;
00059 IConsoleLine::size++;
00060 }
00061
00065 ~IConsoleLine()
00066 {
00067 IConsoleLine::size--;
00068 free(buffer);
00069
00070 delete previous;
00071 }
00072
00076 static const IConsoleLine *Get(uint index)
00077 {
00078 const IConsoleLine *item = IConsoleLine::front;
00079 while (index != 0 && item != NULL) {
00080 index--;
00081 item = item->previous;
00082 }
00083
00084 return item;
00085 }
00086
00094 static bool Truncate()
00095 {
00096 IConsoleLine *cur = IConsoleLine::front;
00097 if (cur == NULL) return false;
00098
00099 int count = 1;
00100 for (IConsoleLine *item = cur->previous; item != NULL; count++, cur = item, item = item->previous) {
00101 if (item->time > _settings_client.gui.console_backlog_timeout &&
00102 count > _settings_client.gui.console_backlog_length) {
00103 delete item;
00104 cur->previous = NULL;
00105 return true;
00106 }
00107
00108 if (item->time != MAX_UVALUE(uint16)) item->time++;
00109 }
00110
00111 return false;
00112 }
00113
00117 static void Reset()
00118 {
00119 delete IConsoleLine::front;
00120 IConsoleLine::front = NULL;
00121 IConsoleLine::size = 0;
00122 }
00123 };
00124
00125 IConsoleLine *IConsoleLine::front = NULL;
00126 int IConsoleLine::size = 0;
00127
00128
00129
00130 static Textbuf _iconsole_cmdline(ICON_CMDLN_SIZE);
00131 static char *_iconsole_history[ICON_HISTORY_SIZE];
00132 static int _iconsole_historypos;
00133 IConsoleModes _iconsole_mode;
00134
00135
00136
00137
00138
00139 static void IConsoleClearCommand()
00140 {
00141 memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
00142 _iconsole_cmdline.chars = _iconsole_cmdline.bytes = 1;
00143 _iconsole_cmdline.pixels = 0;
00144 _iconsole_cmdline.caretpos = 0;
00145 _iconsole_cmdline.caretxoffs = 0;
00146 SetWindowDirty(WC_CONSOLE, 0);
00147 }
00148
00149 static inline void IConsoleResetHistoryPos()
00150 {
00151 _iconsole_historypos = -1;
00152 }
00153
00154
00155 static const char *IConsoleHistoryAdd(const char *cmd);
00156 static void IConsoleHistoryNavigate(int direction);
00157
00158 static const struct NWidgetPart _nested_console_window_widgets[] = {
00159 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_BACKGROUND), SetResize(1, 1),
00160 };
00161
00162 static WindowDesc _console_window_desc(
00163 WDP_MANUAL, NULL, 0, 0,
00164 WC_CONSOLE, WC_NONE,
00165 0,
00166 _nested_console_window_widgets, lengthof(_nested_console_window_widgets)
00167 );
00168
00169 struct IConsoleWindow : Window
00170 {
00171 static int scroll;
00172 int line_height;
00173 int line_offset;
00174
00175 IConsoleWindow() : Window(&_console_window_desc)
00176 {
00177 _iconsole_mode = ICONSOLE_OPENED;
00178 this->line_height = FONT_HEIGHT_NORMAL + ICON_LINE_SPACING;
00179 this->line_offset = GetStringBoundingBox("] ").width + 5;
00180
00181 this->InitNested(0);
00182 ResizeWindow(this, _screen.width, _screen.height / 3);
00183 }
00184
00185 ~IConsoleWindow()
00186 {
00187 _iconsole_mode = ICONSOLE_CLOSED;
00188 _video_driver->EditBoxLostFocus();
00189 }
00190
00195 void Scroll(int amount)
00196 {
00197 int max_scroll = max<int>(0, IConsoleLine::size + 1 - this->height / this->line_height);
00198 IConsoleWindow::scroll = Clamp<int>(IConsoleWindow::scroll + amount, 0, max_scroll);
00199 this->SetDirty();
00200 }
00201
00202 virtual void OnPaint()
00203 {
00204 const int right = this->width - 5;
00205
00206 GfxFillRect(0, 0, this->width - 1, this->height - 1, PC_BLACK);
00207 int ypos = this->height - this->line_height;
00208 for (const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll); print != NULL; print = print->previous) {
00209 SetDParamStr(0, print->buffer);
00210 ypos = DrawStringMultiLine(5, right, -this->line_height, ypos, STR_JUST_RAW_STRING, print->colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - ICON_LINE_SPACING;
00211 if (ypos < 0) break;
00212 }
00213
00214 int delta = this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH;
00215 if (delta > 0) {
00216 DrawString(5, right, this->height - this->line_height, "]", (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00217 delta = 0;
00218 }
00219
00220
00221 if (_iconsole_cmdline.marklength != 0) GfxFillRect(this->line_offset + delta + _iconsole_cmdline.markxoffs, this->height - this->line_height, this->line_offset + delta + _iconsole_cmdline.markxoffs + _iconsole_cmdline.marklength, this->height - 1, PC_DARK_RED);
00222
00223 DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
00224
00225 if (_focused_window == this && _iconsole_cmdline.caret) {
00226 DrawString(this->line_offset + delta + _iconsole_cmdline.caretxoffs, right, this->height - this->line_height, "_", TC_WHITE, SA_LEFT | SA_FORCE);
00227 }
00228 }
00229
00230 virtual void OnHundredthTick()
00231 {
00232 if (IConsoleLine::Truncate() &&
00233 (IConsoleWindow::scroll > IConsoleLine::size)) {
00234 IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
00235 this->SetDirty();
00236 }
00237 }
00238
00239 virtual void OnMouseLoop()
00240 {
00241 if (_iconsole_cmdline.HandleCaret()) this->SetDirty();
00242 }
00243
00244 virtual EventState OnKeyPress(WChar key, uint16 keycode)
00245 {
00246 if (_focused_window != this) return ES_NOT_HANDLED;
00247
00248 const int scroll_height = (this->height / this->line_height) - 1;
00249 switch (keycode) {
00250 case WKC_UP:
00251 IConsoleHistoryNavigate(1);
00252 this->SetDirty();
00253 break;
00254
00255 case WKC_DOWN:
00256 IConsoleHistoryNavigate(-1);
00257 this->SetDirty();
00258 break;
00259
00260 case WKC_SHIFT | WKC_PAGEDOWN:
00261 this->Scroll(-scroll_height);
00262 break;
00263
00264 case WKC_SHIFT | WKC_PAGEUP:
00265 this->Scroll(scroll_height);
00266 break;
00267
00268 case WKC_SHIFT | WKC_DOWN:
00269 this->Scroll(-1);
00270 break;
00271
00272 case WKC_SHIFT | WKC_UP:
00273 this->Scroll(1);
00274 break;
00275
00276 case WKC_BACKQUOTE:
00277 IConsoleSwitch();
00278 break;
00279
00280 case WKC_RETURN: case WKC_NUM_ENTER: {
00281
00282
00283
00284 IConsolePrintF(CC_COMMAND, LRM "] %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
00292 case WKC_CTRL | WKC_RETURN:
00293 _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
00294 IConsoleResize(this);
00295 MarkWholeScreenDirty();
00296 break;
00297
00298 case (WKC_CTRL | 'L'):
00299 IConsoleCmdExec("clear");
00300 break;
00301
00302 default:
00303 if (_iconsole_cmdline.HandleKeyPress(key, keycode) != HKPR_NOT_HANDLED) {
00304 IConsoleWindow::scroll = 0;
00305 IConsoleResetHistoryPos();
00306 this->SetDirty();
00307 } else {
00308 return ES_NOT_HANDLED;
00309 }
00310 break;
00311 }
00312 return ES_HANDLED;
00313 }
00314
00315 virtual void InsertTextString(int wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
00316 {
00317 if (_iconsole_cmdline.InsertString(str, marked, caret, insert_location, replacement_end)) {
00318 IConsoleWindow::scroll = 0;
00319 IConsoleResetHistoryPos();
00320 this->SetDirty();
00321 }
00322 }
00323
00324 virtual const char *GetFocusedText() const
00325 {
00326 return _iconsole_cmdline.buf;
00327 }
00328
00329 virtual const char *GetCaret() const
00330 {
00331 return _iconsole_cmdline.buf + _iconsole_cmdline.caretpos;
00332 }
00333
00334 virtual const char *GetMarkedText(size_t *length) const
00335 {
00336 if (_iconsole_cmdline.markend == 0) return NULL;
00337
00338 *length = _iconsole_cmdline.markend - _iconsole_cmdline.markpos;
00339 return _iconsole_cmdline.buf + _iconsole_cmdline.markpos;
00340 }
00341
00342 virtual Point GetCaretPosition() const
00343 {
00344 int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
00345 Point pt = {this->line_offset + delta + _iconsole_cmdline.caretxoffs, this->height - this->line_height};
00346
00347 return pt;
00348 }
00349
00350 virtual Rect GetTextBoundingRect(const char *from, const char *to) const
00351 {
00352 int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
00353
00354 Point p1 = GetCharPosInString(_iconsole_cmdline.buf, from, FS_NORMAL);
00355 Point p2 = from != to ? GetCharPosInString(_iconsole_cmdline.buf, from) : p1;
00356
00357 Rect r = {this->line_offset + delta + p1.x, this->height - this->line_height, this->line_offset + delta + p2.x, this->height};
00358 return r;
00359 }
00360
00361 virtual const char *GetTextCharacterAtPosition(const Point &pt) const
00362 {
00363 int delta = min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
00364
00365 if (!IsInsideMM(pt.y, this->height - this->line_height, this->height)) return NULL;
00366
00367 return GetCharAtPosition(_iconsole_cmdline.buf, pt.x - delta);
00368 }
00369
00370 virtual void OnMouseWheel(int wheel)
00371 {
00372 this->Scroll(-wheel);
00373 }
00374
00375 virtual void OnFocusLost()
00376 {
00377 _video_driver->EditBoxLostFocus();
00378 }
00379 };
00380
00381 int IConsoleWindow::scroll = 0;
00382
00383 void IConsoleGUIInit()
00384 {
00385 IConsoleResetHistoryPos();
00386 _iconsole_mode = ICONSOLE_CLOSED;
00387
00388 IConsoleLine::Reset();
00389 memset(_iconsole_history, 0, sizeof(_iconsole_history));
00390
00391 IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
00392 IConsolePrint(CC_WHITE, "------------------------------------");
00393 IConsolePrint(CC_WHITE, "use \"help\" for more information");
00394 IConsolePrint(CC_WHITE, "");
00395 IConsoleClearCommand();
00396 }
00397
00398 void IConsoleClearBuffer()
00399 {
00400 IConsoleLine::Reset();
00401 }
00402
00403 void IConsoleGUIFree()
00404 {
00405 IConsoleClearBuffer();
00406 }
00407
00409 void IConsoleResize(Window *w)
00410 {
00411 switch (_iconsole_mode) {
00412 case ICONSOLE_OPENED:
00413 w->height = _screen.height / 3;
00414 w->width = _screen.width;
00415 break;
00416 case ICONSOLE_FULL:
00417 w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
00418 w->width = _screen.width;
00419 break;
00420 default: return;
00421 }
00422
00423 MarkWholeScreenDirty();
00424 }
00425
00427 void IConsoleSwitch()
00428 {
00429 switch (_iconsole_mode) {
00430 case ICONSOLE_CLOSED:
00431 new IConsoleWindow();
00432 break;
00433
00434 case ICONSOLE_OPENED: case ICONSOLE_FULL:
00435 DeleteWindowById(WC_CONSOLE, 0);
00436 break;
00437 }
00438
00439 MarkWholeScreenDirty();
00440 }
00441
00443 void IConsoleClose()
00444 {
00445 if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();
00446 }
00447
00454 static const char *IConsoleHistoryAdd(const char *cmd)
00455 {
00456
00457 while (IsWhitespace(*cmd)) cmd++;
00458
00459
00460 if (StrEmpty(cmd)) return NULL;
00461
00462
00463 if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
00464 free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
00465 memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
00466 _iconsole_history[0] = strdup(cmd);
00467 }
00468
00469
00470 IConsoleResetHistoryPos();
00471 return _iconsole_history[0];
00472 }
00473
00478 static void IConsoleHistoryNavigate(int direction)
00479 {
00480 if (_iconsole_history[0] == NULL) return;
00481 _iconsole_historypos = Clamp(_iconsole_historypos + direction, -1, ICON_HISTORY_SIZE - 1);
00482
00483 if (direction > 0 && _iconsole_history[_iconsole_historypos] == NULL) _iconsole_historypos--;
00484
00485 if (_iconsole_historypos == -1) {
00486 _iconsole_cmdline.DeleteAll();
00487 } else {
00488 _iconsole_cmdline.Assign(_iconsole_history[_iconsole_historypos]);
00489 }
00490 }
00491
00501 void IConsoleGUIPrint(TextColour colour_code, char *str)
00502 {
00503 new IConsoleLine(str, colour_code);
00504 SetWindowDirty(WC_CONSOLE, 0);
00505 }
00506
00507
00513 bool IsValidConsoleColour(TextColour c)
00514 {
00515
00516 if (!(c & TC_IS_PALETTE_COLOUR)) return TC_BEGIN <= c && c < TC_END;
00517
00518
00519
00520 c &= ~TC_IS_PALETTE_COLOUR;
00521 for (uint i = COLOUR_BEGIN; i < COLOUR_END; i++) {
00522 if (_colour_gradient[i][4] == c) return true;
00523 }
00524
00525 return false;
00526 }