PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/1.0.0/src/console_gui.cpp

https://github.com/bodhi/OpenTTD
C++ | 480 lines | 339 code | 78 blank | 63 comment | 61 complexity | f28a104beee27a57efdba23def37f0e6 MD5 | raw file
  1. /* $Id: console_gui.cpp 18809 2010-01-15 16:41:15Z rubidium $ */
  2. /*
  3. * This file is part of OpenTTD.
  4. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
  5. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  6. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
  7. */
  8. /** @file console_gui.cpp Handling the GUI of the in-game console. */
  9. #include "stdafx.h"
  10. #include "textbuf_gui.h"
  11. #include "window_gui.h"
  12. #include "console_gui.h"
  13. #include "console_internal.h"
  14. #include "window_func.h"
  15. #include "string_func.h"
  16. #include "gfx_func.h"
  17. #include "settings_type.h"
  18. #include "console_func.h"
  19. #include "rev.h"
  20. enum {
  21. ICON_HISTORY_SIZE = 20,
  22. ICON_LINE_SPACING = 2,
  23. ICON_RIGHT_BORDERWIDTH = 10,
  24. ICON_BOTTOM_BORDERWIDTH = 12,
  25. };
  26. /**
  27. * Container for a single line of console output
  28. */
  29. struct IConsoleLine {
  30. static IConsoleLine *front; ///< The front of the console backlog buffer
  31. static int size; ///< The amount of items in the backlog
  32. IConsoleLine *previous; ///< The previous console message.
  33. char *buffer; ///< The data to store.
  34. TextColour colour; ///< The colour of the line.
  35. uint16 time; ///< The amount of time the line is in the backlog.
  36. /**
  37. * Initialize the console line.
  38. * @param buffer the data to print.
  39. * @param colour the colour of the line.
  40. */
  41. IConsoleLine(char *buffer, TextColour colour) :
  42. previous(IConsoleLine::front),
  43. buffer(buffer),
  44. colour(colour),
  45. time(0)
  46. {
  47. IConsoleLine::front = this;
  48. IConsoleLine::size++;
  49. }
  50. /**
  51. * Clear this console line and any further ones.
  52. */
  53. ~IConsoleLine()
  54. {
  55. IConsoleLine::size--;
  56. free(buffer);
  57. delete previous;
  58. }
  59. /**
  60. * Get the index-ed item in the list.
  61. */
  62. static const IConsoleLine *Get(uint index)
  63. {
  64. const IConsoleLine *item = IConsoleLine::front;
  65. while (index != 0 && item != NULL) {
  66. index--;
  67. item = item->previous;
  68. }
  69. return item;
  70. }
  71. /**
  72. * Truncate the list removing everything older than/more than the amount
  73. * as specified in the config file.
  74. * As a side effect also increase the time the other lines have been in
  75. * the list.
  76. * @return true if and only if items got removed.
  77. */
  78. static bool Truncate()
  79. {
  80. IConsoleLine *cur = IConsoleLine::front;
  81. if (cur == NULL) return false;
  82. int count = 1;
  83. for (IConsoleLine *item = cur->previous; item != NULL; count++, cur = item, item = item->previous) {
  84. if (item->time > _settings_client.gui.console_backlog_timeout &&
  85. count > _settings_client.gui.console_backlog_length) {
  86. delete item;
  87. cur->previous = NULL;
  88. return true;
  89. }
  90. if (item->time != MAX_UVALUE(uint16)) item->time++;
  91. }
  92. return false;
  93. }
  94. /**
  95. * Reset the complete console line backlog.
  96. */
  97. static void Reset()
  98. {
  99. delete IConsoleLine::front;
  100. IConsoleLine::front = NULL;
  101. IConsoleLine::size = 0;
  102. }
  103. };
  104. /* static */ IConsoleLine *IConsoleLine::front = NULL;
  105. /* static */ int IConsoleLine::size = 0;
  106. /* ** main console cmd buffer ** */
  107. static Textbuf _iconsole_cmdline;
  108. static char *_iconsole_history[ICON_HISTORY_SIZE];
  109. static byte _iconsole_historypos;
  110. IConsoleModes _iconsole_mode;
  111. /* *************** *
  112. * end of header *
  113. * *************** */
  114. static void IConsoleClearCommand()
  115. {
  116. memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
  117. _iconsole_cmdline.size = 1; // only terminating zero
  118. _iconsole_cmdline.width = 0;
  119. _iconsole_cmdline.caretpos = 0;
  120. _iconsole_cmdline.caretxoffs = 0;
  121. SetWindowDirty(WC_CONSOLE, 0);
  122. }
  123. static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
  124. static const char *IConsoleHistoryAdd(const char *cmd);
  125. static void IConsoleHistoryNavigate(int direction);
  126. /** Widgets of the console window. */
  127. enum ConsoleWidgets {
  128. CW_BACKGROUND, ///< Background of the console
  129. };
  130. static const struct NWidgetPart _nested_console_window_widgets[] = {
  131. NWidget(WWT_EMPTY, INVALID_COLOUR, CW_BACKGROUND), SetResize(1, 1),
  132. };
  133. static const WindowDesc _console_window_desc(
  134. WDP_MANUAL, 0, 0,
  135. WC_CONSOLE, WC_NONE,
  136. 0,
  137. _nested_console_window_widgets, lengthof(_nested_console_window_widgets)
  138. );
  139. struct IConsoleWindow : Window
  140. {
  141. static int scroll;
  142. int line_height;
  143. int line_offset;
  144. IConsoleWindow() : Window()
  145. {
  146. _iconsole_mode = ICONSOLE_OPENED;
  147. this->line_height = FONT_HEIGHT_NORMAL + ICON_LINE_SPACING;
  148. this->line_offset = GetStringBoundingBox("] ").width + 5;
  149. this->InitNested(&_console_window_desc, 0);
  150. ResizeWindow(this, _screen.width, _screen.height / 3);
  151. }
  152. ~IConsoleWindow()
  153. {
  154. _iconsole_mode = ICONSOLE_CLOSED;
  155. }
  156. virtual void OnPaint()
  157. {
  158. const int max = (this->height / this->line_height) - 1;
  159. const int right = this->width - 5;
  160. const IConsoleLine *print = IConsoleLine::Get(IConsoleWindow::scroll);
  161. GfxFillRect(this->left, this->top, this->width, this->height - 1, 0);
  162. for (int i = 0; i < max && print != NULL; i++, print = print->previous) {
  163. DrawString(5, right, this->height - (2 + i) * this->line_height, print->buffer, print->colour, SA_LEFT | SA_FORCE);
  164. }
  165. /* If the text is longer than the window, don't show the starting ']' */
  166. int delta = this->width - this->line_offset - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
  167. if (delta > 0) {
  168. DrawString(5, right, this->height - this->line_height, "]", (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
  169. delta = 0;
  170. }
  171. DrawString(this->line_offset + delta, right, this->height - this->line_height, _iconsole_cmdline.buf, (TextColour)CC_COMMAND, SA_LEFT | SA_FORCE);
  172. if (_focused_window == this && _iconsole_cmdline.caret) {
  173. DrawString(this->line_offset + delta + _iconsole_cmdline.caretxoffs, right, this->height - this->line_height, "_", TC_WHITE, SA_LEFT | SA_FORCE);
  174. }
  175. }
  176. virtual void OnHundredthTick()
  177. {
  178. if (IConsoleLine::Truncate() &&
  179. (IConsoleWindow::scroll > IConsoleLine::size)) {
  180. IConsoleWindow::scroll = max(0, IConsoleLine::size - (this->height / this->line_height) + 1);
  181. this->SetDirty();
  182. }
  183. }
  184. virtual void OnMouseLoop()
  185. {
  186. if (HandleCaret(&_iconsole_cmdline)) this->SetDirty();
  187. }
  188. virtual EventState OnKeyPress(uint16 key, uint16 keycode)
  189. {
  190. if (_focused_window != this) return ES_NOT_HANDLED;
  191. const int scroll_height = (this->height / this->line_height) - 1;
  192. switch (keycode) {
  193. case WKC_UP:
  194. IConsoleHistoryNavigate(1);
  195. this->SetDirty();
  196. break;
  197. case WKC_DOWN:
  198. IConsoleHistoryNavigate(-1);
  199. this->SetDirty();
  200. break;
  201. case WKC_SHIFT | WKC_PAGEDOWN:
  202. if (IConsoleWindow::scroll - scroll_height < 0) {
  203. IConsoleWindow::scroll = 0;
  204. } else {
  205. IConsoleWindow::scroll -= scroll_height;
  206. }
  207. this->SetDirty();
  208. break;
  209. case WKC_SHIFT | WKC_PAGEUP:
  210. if (IConsoleWindow::scroll + scroll_height > IConsoleLine::size - scroll_height) {
  211. IConsoleWindow::scroll = IConsoleLine::size - scroll_height;
  212. } else {
  213. IConsoleWindow::scroll += scroll_height;
  214. }
  215. this->SetDirty();
  216. break;
  217. case WKC_SHIFT | WKC_DOWN:
  218. if (IConsoleWindow::scroll <= 0) {
  219. IConsoleWindow::scroll = 0;
  220. } else {
  221. --IConsoleWindow::scroll;
  222. }
  223. this->SetDirty();
  224. break;
  225. case WKC_SHIFT | WKC_UP:
  226. if (IConsoleWindow::scroll >= IConsoleLine::size) {
  227. IConsoleWindow::scroll = IConsoleLine::size;
  228. } else {
  229. ++IConsoleWindow::scroll;
  230. }
  231. this->SetDirty();
  232. break;
  233. case WKC_BACKQUOTE:
  234. IConsoleSwitch();
  235. break;
  236. case WKC_RETURN: case WKC_NUM_ENTER: {
  237. IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf);
  238. const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
  239. IConsoleClearCommand();
  240. if (cmd != NULL) IConsoleCmdExec(cmd);
  241. } break;
  242. case WKC_CTRL | WKC_RETURN:
  243. _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
  244. IConsoleResize(this);
  245. MarkWholeScreenDirty();
  246. break;
  247. #ifdef WITH_COCOA
  248. case (WKC_META | 'V'):
  249. #endif
  250. case (WKC_CTRL | 'V'):
  251. if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
  252. IConsoleResetHistoryPos();
  253. this->SetDirty();
  254. }
  255. break;
  256. case (WKC_CTRL | 'L'):
  257. IConsoleCmdExec("clear");
  258. break;
  259. #ifdef WITH_COCOA
  260. case (WKC_META | 'U'):
  261. #endif
  262. case (WKC_CTRL | 'U'):
  263. DeleteTextBufferAll(&_iconsole_cmdline);
  264. this->SetDirty();
  265. break;
  266. case WKC_BACKSPACE: case WKC_DELETE:
  267. if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) {
  268. IConsoleResetHistoryPos();
  269. this->SetDirty();
  270. }
  271. break;
  272. case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
  273. if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) {
  274. IConsoleResetHistoryPos();
  275. this->SetDirty();
  276. }
  277. break;
  278. default:
  279. if (IsValidChar(key, CS_ALPHANUMERAL)) {
  280. IConsoleWindow::scroll = 0;
  281. InsertTextBufferChar(&_iconsole_cmdline, key);
  282. IConsoleResetHistoryPos();
  283. this->SetDirty();
  284. } else {
  285. return ES_NOT_HANDLED;
  286. }
  287. }
  288. return ES_HANDLED;
  289. }
  290. };
  291. int IConsoleWindow::scroll = 0;
  292. void IConsoleGUIInit()
  293. {
  294. _iconsole_historypos = ICON_HISTORY_SIZE - 1;
  295. _iconsole_mode = ICONSOLE_CLOSED;
  296. IConsoleLine::Reset();
  297. memset(_iconsole_history, 0, sizeof(_iconsole_history));
  298. _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it
  299. _iconsole_cmdline.maxsize = ICON_CMDLN_SIZE;
  300. IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
  301. IConsolePrint(CC_WHITE, "------------------------------------");
  302. IConsolePrint(CC_WHITE, "use \"help\" for more information");
  303. IConsolePrint(CC_WHITE, "");
  304. IConsoleClearCommand();
  305. }
  306. void IConsoleClearBuffer()
  307. {
  308. IConsoleLine::Reset();
  309. }
  310. void IConsoleGUIFree()
  311. {
  312. free(_iconsole_cmdline.buf);
  313. IConsoleClearBuffer();
  314. }
  315. void IConsoleResize(Window *w)
  316. {
  317. switch (_iconsole_mode) {
  318. case ICONSOLE_OPENED:
  319. w->height = _screen.height / 3;
  320. w->width = _screen.width;
  321. break;
  322. case ICONSOLE_FULL:
  323. w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
  324. w->width = _screen.width;
  325. break;
  326. default: return;
  327. }
  328. MarkWholeScreenDirty();
  329. }
  330. void IConsoleSwitch()
  331. {
  332. switch (_iconsole_mode) {
  333. case ICONSOLE_CLOSED:
  334. new IConsoleWindow();
  335. break;
  336. case ICONSOLE_OPENED: case ICONSOLE_FULL:
  337. DeleteWindowById(WC_CONSOLE, 0);
  338. break;
  339. }
  340. MarkWholeScreenDirty();
  341. }
  342. void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
  343. /**
  344. * Add the entered line into the history so you can look it back
  345. * scroll, etc. Put it to the beginning as it is the latest text
  346. * @param cmd Text to be entered into the 'history'
  347. * @return the command to execute
  348. */
  349. static const char *IConsoleHistoryAdd(const char *cmd)
  350. {
  351. /* Strip all spaces at the begin */
  352. while (IsWhitespace(*cmd)) cmd++;
  353. /* Do not put empty command in history */
  354. if (StrEmpty(cmd)) return NULL;
  355. /* Do not put in history if command is same as previous */
  356. if (_iconsole_history[0] == NULL || strcmp(_iconsole_history[0], cmd) != 0) {
  357. free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
  358. memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
  359. _iconsole_history[0] = strdup(cmd);
  360. }
  361. /* Reset the history position */
  362. IConsoleResetHistoryPos();
  363. return _iconsole_history[0];
  364. }
  365. /**
  366. * Navigate Up/Down in the history of typed commands
  367. * @param direction Go further back in history (+1), go to recently typed commands (-1)
  368. */
  369. static void IConsoleHistoryNavigate(int direction)
  370. {
  371. if (_iconsole_history[0] == NULL) return; // Empty history
  372. int i = _iconsole_historypos + direction;
  373. /* watch out for overflows, just wrap around */
  374. if (i < 0) i = ICON_HISTORY_SIZE - 1;
  375. if (i >= ICON_HISTORY_SIZE) i = 0;
  376. if (direction > 0) {
  377. if (_iconsole_history[i] == NULL) i = 0;
  378. }
  379. if (direction < 0) {
  380. while (i > 0 && _iconsole_history[i] == NULL) i--;
  381. }
  382. _iconsole_historypos = i;
  383. IConsoleClearCommand();
  384. /* copy history to 'command prompt / bash' */
  385. assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
  386. ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxsize);
  387. UpdateTextBufferSize(&_iconsole_cmdline);
  388. }
  389. /**
  390. * Handle the printing of text entered into the console or redirected there
  391. * by any other means. Text can be redirected to other clients in a network game
  392. * as well as to a logfile. If the network server is a dedicated server, all activities
  393. * are also logged. All lines to print are added to a temporary buffer which can be
  394. * used as a history to print them onscreen
  395. * @param colour_code the colour of the command. Red in case of errors, etc.
  396. * @param str the message entered or output on the console (notice, error, etc.)
  397. */
  398. void IConsoleGUIPrint(ConsoleColour colour_code, char *str)
  399. {
  400. new IConsoleLine(str, (TextColour)colour_code);
  401. SetWindowDirty(WC_CONSOLE, 0);
  402. }