|
1 /* $Id$ */ |
|
2 |
|
3 /** @file console.cpp Handling of the in-game console. */ |
|
4 |
|
5 #include "stdafx.h" |
|
6 #include "openttd.h" |
|
7 #include "textbuf_gui.h" |
|
8 #include "window_gui.h" |
|
9 #include "console_gui.h" |
|
10 #include <stdarg.h> |
|
11 #include <string.h> |
|
12 #include "console_internal.h" |
|
13 #include "window_func.h" |
|
14 #include "string_func.h" |
|
15 #include "gfx_func.h" |
|
16 #include "core/math_func.hpp" |
|
17 #include "rev.h" |
|
18 |
|
19 #include "table/strings.h" |
|
20 |
|
21 #define ICON_BUFFER 79 |
|
22 #define ICON_HISTORY_SIZE 20 |
|
23 #define ICON_LINE_HEIGHT 12 |
|
24 #define ICON_RIGHT_BORDERWIDTH 10 |
|
25 #define ICON_BOTTOM_BORDERWIDTH 12 |
|
26 #define ICON_MAX_ALIAS_LINES 40 |
|
27 #define ICON_TOKEN_COUNT 20 |
|
28 |
|
29 /* console modes */ |
|
30 IConsoleModes _iconsole_mode; |
|
31 |
|
32 /* ** main console ** */ |
|
33 static char *_iconsole_buffer[ICON_BUFFER + 1]; |
|
34 static uint16 _iconsole_cbuffer[ICON_BUFFER + 1]; |
|
35 static Textbuf _iconsole_cmdline; |
|
36 |
|
37 /* ** main console cmd buffer ** */ |
|
38 static char *_iconsole_history[ICON_HISTORY_SIZE]; |
|
39 static byte _iconsole_historypos; |
|
40 |
|
41 /* *************** * |
|
42 * end of header * |
|
43 * *************** */ |
|
44 |
|
45 static void IConsoleClearCommand() |
|
46 { |
|
47 memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE); |
|
48 _iconsole_cmdline.length = 0; |
|
49 _iconsole_cmdline.width = 0; |
|
50 _iconsole_cmdline.caretpos = 0; |
|
51 _iconsole_cmdline.caretxoffs = 0; |
|
52 SetWindowDirty(FindWindowById(WC_CONSOLE, 0)); |
|
53 } |
|
54 |
|
55 static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;} |
|
56 |
|
57 |
|
58 static void IConsoleHistoryAdd(const char *cmd); |
|
59 static void IConsoleHistoryNavigate(int direction); |
|
60 |
|
61 struct IConsoleWindow : Window |
|
62 { |
|
63 static byte scroll; |
|
64 |
|
65 IConsoleWindow(const WindowDesc *desc) : Window(desc) |
|
66 { |
|
67 _iconsole_mode = ICONSOLE_OPENED; |
|
68 SetBit(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll |
|
69 |
|
70 this->height = _screen.height / 3; |
|
71 this->width = _screen.width; |
|
72 } |
|
73 |
|
74 ~IConsoleWindow() |
|
75 { |
|
76 _iconsole_mode = ICONSOLE_CLOSED; |
|
77 ClrBit(_no_scroll, SCROLL_CON); |
|
78 } |
|
79 |
|
80 virtual void OnPaint() |
|
81 { |
|
82 int i = IConsoleWindow::scroll; |
|
83 int max = (this->height / ICON_LINE_HEIGHT) - 1; |
|
84 int delta = 0; |
|
85 GfxFillRect(this->left, this->top, this->width, this->height - 1, 0); |
|
86 while ((i > 0) && (i > IConsoleWindow::scroll - max) && (_iconsole_buffer[i] != NULL)) { |
|
87 DoDrawString(_iconsole_buffer[i], 5, |
|
88 this->height - (IConsoleWindow::scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]); |
|
89 i--; |
|
90 } |
|
91 /* If the text is longer than the window, don't show the starting ']' */ |
|
92 delta = this->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH; |
|
93 if (delta > 0) { |
|
94 DoDrawString("]", 5, this->height - ICON_LINE_HEIGHT, CC_COMMAND); |
|
95 delta = 0; |
|
96 } |
|
97 |
|
98 DoDrawString(_iconsole_cmdline.buf, 10 + delta, this->height - ICON_LINE_HEIGHT, CC_COMMAND); |
|
99 |
|
100 if (_iconsole_cmdline.caret) { |
|
101 DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, this->height - ICON_LINE_HEIGHT, TC_WHITE); |
|
102 } |
|
103 } |
|
104 |
|
105 virtual void OnMouseLoop() |
|
106 { |
|
107 if (HandleCaret(&_iconsole_cmdline)) this->SetDirty(); |
|
108 } |
|
109 |
|
110 virtual EventState OnKeyPress(uint16 key, uint16 keycode) |
|
111 { |
|
112 switch (keycode) { |
|
113 case WKC_UP: |
|
114 IConsoleHistoryNavigate(+1); |
|
115 this->SetDirty(); |
|
116 break; |
|
117 |
|
118 case WKC_DOWN: |
|
119 IConsoleHistoryNavigate(-1); |
|
120 this->SetDirty(); |
|
121 break; |
|
122 |
|
123 case WKC_SHIFT | WKC_PAGEUP: |
|
124 if (IConsoleWindow::scroll - (this->height / ICON_LINE_HEIGHT) - 1 < 0) { |
|
125 IConsoleWindow::scroll = 0; |
|
126 } else { |
|
127 IConsoleWindow::scroll -= (this->height / ICON_LINE_HEIGHT) - 1; |
|
128 } |
|
129 this->SetDirty(); |
|
130 break; |
|
131 |
|
132 case WKC_SHIFT | WKC_PAGEDOWN: |
|
133 if (IConsoleWindow::scroll + (this->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) { |
|
134 IConsoleWindow::scroll = ICON_BUFFER; |
|
135 } else { |
|
136 IConsoleWindow::scroll += (this->height / ICON_LINE_HEIGHT) - 1; |
|
137 } |
|
138 this->SetDirty(); |
|
139 break; |
|
140 |
|
141 case WKC_SHIFT | WKC_UP: |
|
142 if (IConsoleWindow::scroll <= 0) { |
|
143 IConsoleWindow::scroll = 0; |
|
144 } else { |
|
145 --IConsoleWindow::scroll; |
|
146 } |
|
147 this->SetDirty(); |
|
148 break; |
|
149 |
|
150 case WKC_SHIFT | WKC_DOWN: |
|
151 if (IConsoleWindow::scroll >= ICON_BUFFER) { |
|
152 IConsoleWindow::scroll = ICON_BUFFER; |
|
153 } else { |
|
154 ++IConsoleWindow::scroll; |
|
155 } |
|
156 this->SetDirty(); |
|
157 break; |
|
158 |
|
159 case WKC_BACKQUOTE: |
|
160 IConsoleSwitch(); |
|
161 break; |
|
162 |
|
163 case WKC_RETURN: case WKC_NUM_ENTER: |
|
164 IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf); |
|
165 IConsoleHistoryAdd(_iconsole_cmdline.buf); |
|
166 |
|
167 IConsoleCmdExec(_iconsole_cmdline.buf); |
|
168 IConsoleClearCommand(); |
|
169 break; |
|
170 |
|
171 case WKC_CTRL | WKC_RETURN: |
|
172 _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL; |
|
173 IConsoleResize(this); |
|
174 MarkWholeScreenDirty(); |
|
175 break; |
|
176 |
|
177 case (WKC_CTRL | 'V'): |
|
178 if (InsertTextBufferClipboard(&_iconsole_cmdline)) { |
|
179 IConsoleResetHistoryPos(); |
|
180 this->SetDirty(); |
|
181 } |
|
182 break; |
|
183 |
|
184 case (WKC_CTRL | 'L'): |
|
185 IConsoleCmdExec("clear"); |
|
186 break; |
|
187 |
|
188 case (WKC_CTRL | 'U'): |
|
189 DeleteTextBufferAll(&_iconsole_cmdline); |
|
190 this->SetDirty(); |
|
191 break; |
|
192 |
|
193 case WKC_BACKSPACE: case WKC_DELETE: |
|
194 if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) { |
|
195 IConsoleResetHistoryPos(); |
|
196 this->SetDirty(); |
|
197 } |
|
198 break; |
|
199 |
|
200 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: |
|
201 if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) { |
|
202 IConsoleResetHistoryPos(); |
|
203 this->SetDirty(); |
|
204 } |
|
205 break; |
|
206 |
|
207 default: |
|
208 if (IsValidChar(key, CS_ALPHANUMERAL)) { |
|
209 IConsoleWindow::scroll = ICON_BUFFER; |
|
210 InsertTextBufferChar(&_iconsole_cmdline, key); |
|
211 IConsoleResetHistoryPos(); |
|
212 this->SetDirty(); |
|
213 } else { |
|
214 return ES_NOT_HANDLED; |
|
215 } |
|
216 } |
|
217 return ES_HANDLED; |
|
218 } |
|
219 }; |
|
220 |
|
221 byte IConsoleWindow::scroll = ICON_BUFFER; |
|
222 |
|
223 static const Widget _iconsole_window_widgets[] = { |
|
224 {WIDGETS_END} |
|
225 }; |
|
226 |
|
227 static const WindowDesc _iconsole_window_desc = { |
|
228 0, 0, 2, 2, 2, 2, |
|
229 WC_CONSOLE, WC_NONE, |
|
230 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, |
|
231 _iconsole_window_widgets, |
|
232 }; |
|
233 |
|
234 void IConsoleGUIInit() |
|
235 { |
|
236 _iconsole_historypos = ICON_HISTORY_SIZE - 1; |
|
237 _iconsole_mode = ICONSOLE_CLOSED; |
|
238 |
|
239 memset(_iconsole_history, 0, sizeof(_iconsole_history)); |
|
240 memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer)); |
|
241 memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer)); |
|
242 _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it |
|
243 _iconsole_cmdline.maxlength = ICON_CMDLN_SIZE; |
|
244 |
|
245 IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision); |
|
246 IConsolePrint(CC_WHITE, "------------------------------------"); |
|
247 IConsolePrint(CC_WHITE, "use \"help\" for more information"); |
|
248 IConsolePrint(CC_WHITE, ""); |
|
249 IConsoleClearCommand(); |
|
250 IConsoleHistoryAdd(""); |
|
251 } |
|
252 |
|
253 void IConsoleClearBuffer() |
|
254 { |
|
255 uint i; |
|
256 for (i = 0; i <= ICON_BUFFER; i++) { |
|
257 free(_iconsole_buffer[i]); |
|
258 _iconsole_buffer[i] = NULL; |
|
259 } |
|
260 } |
|
261 |
|
262 void IConsoleGUIFree() |
|
263 { |
|
264 free(_iconsole_cmdline.buf); |
|
265 IConsoleClearBuffer(); |
|
266 } |
|
267 |
|
268 void IConsoleResize(Window *w) |
|
269 { |
|
270 switch (_iconsole_mode) { |
|
271 case ICONSOLE_OPENED: |
|
272 w->height = _screen.height / 3; |
|
273 w->width = _screen.width; |
|
274 break; |
|
275 case ICONSOLE_FULL: |
|
276 w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH; |
|
277 w->width = _screen.width; |
|
278 break; |
|
279 default: return; |
|
280 } |
|
281 |
|
282 MarkWholeScreenDirty(); |
|
283 } |
|
284 |
|
285 void IConsoleSwitch() |
|
286 { |
|
287 switch (_iconsole_mode) { |
|
288 case ICONSOLE_CLOSED: |
|
289 new IConsoleWindow(&_iconsole_window_desc); |
|
290 break; |
|
291 |
|
292 case ICONSOLE_OPENED: case ICONSOLE_FULL: |
|
293 DeleteWindowById(WC_CONSOLE, 0); |
|
294 break; |
|
295 } |
|
296 |
|
297 MarkWholeScreenDirty(); |
|
298 } |
|
299 |
|
300 void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();} |
|
301 void IConsoleOpen() {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();} |
|
302 |
|
303 /** |
|
304 * Add the entered line into the history so you can look it back |
|
305 * scroll, etc. Put it to the beginning as it is the latest text |
|
306 * @param cmd Text to be entered into the 'history' |
|
307 */ |
|
308 static void IConsoleHistoryAdd(const char *cmd) |
|
309 { |
|
310 free(_iconsole_history[ICON_HISTORY_SIZE - 1]); |
|
311 |
|
312 memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1)); |
|
313 _iconsole_history[0] = strdup(cmd); |
|
314 IConsoleResetHistoryPos(); |
|
315 } |
|
316 |
|
317 /** |
|
318 * Navigate Up/Down in the history of typed commands |
|
319 * @param direction Go further back in history (+1), go to recently typed commands (-1) |
|
320 */ |
|
321 static void IConsoleHistoryNavigate(int direction) |
|
322 { |
|
323 int i = _iconsole_historypos + direction; |
|
324 |
|
325 /* watch out for overflows, just wrap around */ |
|
326 if (i < 0) i = ICON_HISTORY_SIZE - 1; |
|
327 if (i >= ICON_HISTORY_SIZE) i = 0; |
|
328 |
|
329 if (direction > 0) { |
|
330 if (_iconsole_history[i] == NULL) i = 0; |
|
331 } |
|
332 |
|
333 if (direction < 0) { |
|
334 while (i > 0 && _iconsole_history[i] == NULL) i--; |
|
335 } |
|
336 |
|
337 _iconsole_historypos = i; |
|
338 IConsoleClearCommand(); |
|
339 /* copy history to 'command prompt / bash' */ |
|
340 assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE)); |
|
341 ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxlength); |
|
342 UpdateTextBufferSize(&_iconsole_cmdline); |
|
343 } |
|
344 |
|
345 /** |
|
346 * Handle the printing of text entered into the console or redirected there |
|
347 * by any other means. Text can be redirected to other players in a network game |
|
348 * as well as to a logfile. If the network server is a dedicated server, all activities |
|
349 * are also logged. All lines to print are added to a temporary buffer which can be |
|
350 * used as a history to print them onscreen |
|
351 * @param color_code the colour of the command. Red in case of errors, etc. |
|
352 * @param string the message entered or output on the console (notice, error, etc.) |
|
353 */ |
|
354 void IConsoleGUIPrint(ConsoleColour color_code, char *str) |
|
355 { |
|
356 /* move up all the strings in the buffer one place and do the same for colour |
|
357 * to accomodate for the new command/message */ |
|
358 free(_iconsole_buffer[0]); |
|
359 memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER); |
|
360 _iconsole_buffer[ICON_BUFFER] = str; |
|
361 |
|
362 memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER); |
|
363 _iconsole_cbuffer[ICON_BUFFER] = color_code; |
|
364 |
|
365 SetWindowDirty(FindWindowById(WC_CONSOLE, 0)); |
|
366 } |