Commit | Line | Data |
---|---|---|
daa4106c | 1 | /* |
34a42e78 KL |
2 | * Jexer - Java Text User Interface |
3 | * | |
e16dda65 | 4 | * The MIT License (MIT) |
34a42e78 | 5 | * |
a69ed767 | 6 | * Copyright (C) 2019 Kevin Lamonte |
34a42e78 | 7 | * |
e16dda65 KL |
8 | * Permission is hereby granted, free of charge, to any person obtaining a |
9 | * copy of this software and associated documentation files (the "Software"), | |
10 | * to deal in the Software without restriction, including without limitation | |
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
12 | * and/or sell copies of the Software, and to permit persons to whom the | |
13 | * Software is furnished to do so, subject to the following conditions: | |
34a42e78 | 14 | * |
e16dda65 KL |
15 | * The above copyright notice and this permission notice shall be included in |
16 | * all copies or substantial portions of the Software. | |
34a42e78 | 17 | * |
e16dda65 KL |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
24 | * DEALINGS IN THE SOFTWARE. | |
34a42e78 KL |
25 | * |
26 | * @author Kevin Lamonte [kevin.lamonte@gmail.com] | |
27 | * @version 1 | |
28 | */ | |
29 | package jexer; | |
30 | ||
339652cc | 31 | import java.util.ResourceBundle; |
34a42e78 | 32 | |
3c5921e6 | 33 | import jexer.menu.TMenu; |
34a42e78 | 34 | import jexer.event.TKeypressEvent; |
b2d49e0f | 35 | import jexer.event.TMenuEvent; |
34a42e78 KL |
36 | import jexer.event.TMouseEvent; |
37 | import jexer.event.TResizeEvent; | |
4941d2d6 | 38 | import static jexer.TCommand.*; |
34a42e78 KL |
39 | import static jexer.TKeypress.*; |
40 | ||
41 | /** | |
42 | * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window. | |
43 | */ | |
4d2c61b4 | 44 | public class TTerminalWindow extends TScrollableWindow { |
34a42e78 | 45 | |
339652cc KL |
46 | /** |
47 | * Translated strings. | |
48 | */ | |
49 | private static final ResourceBundle i18n = ResourceBundle.getBundle(TTerminalWindow.class.getName()); | |
50 | ||
615a0d99 KL |
51 | // ------------------------------------------------------------------------ |
52 | // Variables -------------------------------------------------------------- | |
53 | // ------------------------------------------------------------------------ | |
54 | ||
34a42e78 | 55 | /** |
4d2c61b4 | 56 | * The terminal. |
34a42e78 | 57 | */ |
4d2c61b4 | 58 | private TTerminalWidget terminal; |
1d99a38f | 59 | |
a69ed767 KL |
60 | /** |
61 | * If true, close the window when the shell exits. | |
62 | */ | |
63 | private boolean closeOnExit = false; | |
64 | ||
615a0d99 KL |
65 | // ------------------------------------------------------------------------ |
66 | // Constructors ----------------------------------------------------------- | |
67 | // ------------------------------------------------------------------------ | |
34a42e78 | 68 | |
b2d49e0f KL |
69 | /** |
70 | * Public constructor spawns a custom command line. | |
71 | * | |
72 | * @param application TApplication that manages this window | |
73 | * @param x column relative to parent | |
74 | * @param y row relative to parent | |
75 | * @param commandLine the command line to execute | |
76 | */ | |
77 | public TTerminalWindow(final TApplication application, final int x, | |
78 | final int y, final String commandLine) { | |
79 | ||
00691e80 | 80 | this(application, x, y, RESIZABLE, commandLine.split("\\s+"), |
a69ed767 KL |
81 | System.getProperty("jexer.TTerminal.closeOnExit", |
82 | "false").equals("true")); | |
83 | } | |
84 | ||
85 | /** | |
86 | * Public constructor spawns a custom command line. | |
87 | * | |
88 | * @param application TApplication that manages this window | |
89 | * @param x column relative to parent | |
90 | * @param y row relative to parent | |
91 | * @param commandLine the command line to execute | |
92 | * @param closeOnExit if true, close the window when the command exits | |
93 | */ | |
94 | public TTerminalWindow(final TApplication application, final int x, | |
95 | final int y, final String commandLine, final boolean closeOnExit) { | |
96 | ||
00691e80 | 97 | this(application, x, y, RESIZABLE, commandLine.split("\\s+"), |
a69ed767 | 98 | closeOnExit); |
b2d49e0f KL |
99 | } |
100 | ||
6f8ff91a KL |
101 | /** |
102 | * Public constructor spawns a custom command line. | |
103 | * | |
104 | * @param application TApplication that manages this window | |
105 | * @param x column relative to parent | |
106 | * @param y row relative to parent | |
107 | * @param flags mask of CENTERED, MODAL, or RESIZABLE | |
a0d734e6 | 108 | * @param command the command line to execute |
6f8ff91a KL |
109 | */ |
110 | public TTerminalWindow(final TApplication application, final int x, | |
a0d734e6 | 111 | final int y, final int flags, final String [] command) { |
6f8ff91a | 112 | |
a69ed767 KL |
113 | this(application, x, y, flags, command, |
114 | System.getProperty("jexer.TTerminal.closeOnExit", | |
115 | "false").equals("true")); | |
116 | } | |
117 | ||
118 | /** | |
119 | * Public constructor spawns a custom command line. | |
120 | * | |
121 | * @param application TApplication that manages this window | |
122 | * @param x column relative to parent | |
123 | * @param y row relative to parent | |
124 | * @param flags mask of CENTERED, MODAL, or RESIZABLE | |
125 | * @param command the command line to execute | |
126 | * @param closeOnExit if true, close the window when the command exits | |
127 | */ | |
128 | public TTerminalWindow(final TApplication application, final int x, | |
129 | final int y, final int flags, final String [] command, | |
130 | final boolean closeOnExit) { | |
131 | ||
6f8ff91a KL |
132 | super(application, i18n.getString("windowTitle"), x, y, |
133 | 80 + 2, 24 + 2, flags); | |
134 | ||
955c55b7 KL |
135 | // Require at least one line for the display. |
136 | setMinimumWindowHeight(3); | |
137 | ||
a69ed767 | 138 | this.closeOnExit = closeOnExit; |
4d2c61b4 KL |
139 | vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2); |
140 | ||
141 | // Claim the keystrokes the emulator will need. | |
142 | addShortcutKeys(); | |
a69ed767 | 143 | |
4d2c61b4 | 144 | // Add shortcut text |
4941d2d6 KL |
145 | TStatusBar statusBar = newStatusBar(i18n.getString("statusBarRunning")); |
146 | statusBar.addShortcutKeypress(kbF1, cmHelp, | |
147 | i18n.getString("statusBarHelp")); | |
148 | statusBar.addShortcutKeypress(kbF10, cmMenu, | |
149 | i18n.getString("statusBarMenu")); | |
6f8ff91a | 150 | |
4d2c61b4 | 151 | // Spin it up |
5ca5f8e5 | 152 | terminal = new TTerminalWidget(this, 0, 0, command, new TAction() { |
2bc32111 KL |
153 | public void DO() { |
154 | onShellExit(); | |
155 | } | |
156 | }); | |
6f8ff91a KL |
157 | } |
158 | ||
159 | /** | |
160 | * Public constructor spawns a shell. | |
161 | * | |
162 | * @param application TApplication that manages this window | |
163 | * @param x column relative to parent | |
164 | * @param y row relative to parent | |
165 | * @param flags mask of CENTERED, MODAL, or RESIZABLE | |
166 | */ | |
167 | public TTerminalWindow(final TApplication application, final int x, | |
168 | final int y, final int flags) { | |
169 | ||
a69ed767 KL |
170 | this(application, x, y, flags, |
171 | System.getProperty("jexer.TTerminal.closeOnExit", | |
172 | "false").equals("true")); | |
173 | ||
174 | } | |
175 | ||
176 | /** | |
177 | * Public constructor spawns a shell. | |
178 | * | |
179 | * @param application TApplication that manages this window | |
180 | * @param x column relative to parent | |
181 | * @param y row relative to parent | |
182 | * @param flags mask of CENTERED, MODAL, or RESIZABLE | |
183 | * @param closeOnExit if true, close the window when the shell exits | |
184 | */ | |
185 | public TTerminalWindow(final TApplication application, final int x, | |
186 | final int y, final int flags, final boolean closeOnExit) { | |
187 | ||
6f8ff91a KL |
188 | super(application, i18n.getString("windowTitle"), x, y, |
189 | 80 + 2, 24 + 2, flags); | |
190 | ||
955c55b7 KL |
191 | // Require at least one line for the display. |
192 | setMinimumWindowHeight(3); | |
193 | ||
a69ed767 | 194 | this.closeOnExit = closeOnExit; |
4d2c61b4 | 195 | vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2); |
a69ed767 | 196 | |
4d2c61b4 KL |
197 | // Claim the keystrokes the emulator will need. |
198 | addShortcutKeys(); | |
6f8ff91a | 199 | |
4d2c61b4 | 200 | // Add shortcut text |
4941d2d6 KL |
201 | TStatusBar statusBar = newStatusBar(i18n.getString("statusBarRunning")); |
202 | statusBar.addShortcutKeypress(kbF1, cmHelp, | |
203 | i18n.getString("statusBarHelp")); | |
204 | statusBar.addShortcutKeypress(kbF10, cmMenu, | |
205 | i18n.getString("statusBarMenu")); | |
6f8ff91a | 206 | |
4d2c61b4 | 207 | // Spin it up |
2bc32111 KL |
208 | terminal = new TTerminalWidget(this, 0, 0, new TAction() { |
209 | public void DO() { | |
210 | onShellExit(); | |
211 | } | |
212 | }); | |
6f8ff91a KL |
213 | } |
214 | ||
615a0d99 KL |
215 | // ------------------------------------------------------------------------ |
216 | // TScrollableWindow ------------------------------------------------------ | |
217 | // ------------------------------------------------------------------------ | |
55d2b2c2 | 218 | |
34a42e78 KL |
219 | /** |
220 | * Draw the display buffer. | |
221 | */ | |
222 | @Override | |
223 | public void draw() { | |
1104f8d7 KL |
224 | if (terminal != null) { |
225 | setTitle(terminal.getTitle()); | |
226 | } | |
4d2c61b4 | 227 | reflowData(); |
ab215e38 | 228 | super.draw(); |
107bba16 KL |
229 | } |
230 | ||
be72cb5c | 231 | /** |
615a0d99 | 232 | * Handle window/screen resize events. |
aa77d682 | 233 | * |
615a0d99 | 234 | * @param resize resize event |
aa77d682 | 235 | */ |
615a0d99 KL |
236 | @Override |
237 | public void onResize(final TResizeEvent resize) { | |
4d2c61b4 | 238 | if (resize.getType() == TResizeEvent.Type.WIDGET) { |
1104f8d7 KL |
239 | if (terminal != null) { |
240 | terminal.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, | |
241 | getWidth() - 2, getHeight() - 2)); | |
242 | } | |
615a0d99 | 243 | |
4d2c61b4 KL |
244 | // Resize the scroll bars |
245 | reflowData(); | |
246 | placeScrollbars(); | |
247 | } | |
248 | return; | |
aa77d682 KL |
249 | } |
250 | ||
251 | /** | |
615a0d99 | 252 | * Resize scrollbars for a new width/height. |
aa77d682 | 253 | */ |
615a0d99 KL |
254 | @Override |
255 | public void reflowData() { | |
4d2c61b4 | 256 | // Vertical scrollbar |
1104f8d7 KL |
257 | if (terminal != null) { |
258 | terminal.reflowData(); | |
259 | setTopValue(terminal.getTopValue()); | |
260 | setBottomValue(terminal.getBottomValue()); | |
261 | setVerticalBigChange(terminal.getVerticalBigChange()); | |
262 | setVerticalValue(terminal.getVerticalValue()); | |
263 | } | |
aa77d682 KL |
264 | } |
265 | ||
b2d49e0f | 266 | /** |
615a0d99 KL |
267 | * Handle keystrokes. |
268 | * | |
269 | * @param keypress keystroke event | |
34a42e78 | 270 | */ |
615a0d99 KL |
271 | @Override |
272 | public void onKeypress(final TKeypressEvent keypress) { | |
4941d2d6 KL |
273 | if ((terminal != null) |
274 | && (terminal.isReading()) | |
275 | && (!inKeyboardResize) | |
276 | ) { | |
4d2c61b4 KL |
277 | terminal.onKeypress(keypress); |
278 | } else { | |
279 | super.onKeypress(keypress); | |
34a42e78 | 280 | } |
34a42e78 KL |
281 | } |
282 | ||
283 | /** | |
284 | * Handle mouse press events. | |
285 | * | |
286 | * @param mouse mouse button press event | |
287 | */ | |
288 | @Override | |
289 | public void onMouseDown(final TMouseEvent mouse) { | |
bd8d51fa KL |
290 | if (inWindowMove || inWindowResize) { |
291 | // TWindow needs to deal with this. | |
292 | super.onMouseDown(mouse); | |
293 | return; | |
294 | } | |
34a42e78 | 295 | |
34a42e78 KL |
296 | super.onMouseDown(mouse); |
297 | } | |
298 | ||
bd8d51fa KL |
299 | /** |
300 | * Handle mouse release events. | |
301 | * | |
302 | * @param mouse mouse button release event | |
303 | */ | |
304 | @Override | |
305 | public void onMouseUp(final TMouseEvent mouse) { | |
306 | if (inWindowMove || inWindowResize) { | |
307 | // TWindow needs to deal with this. | |
308 | super.onMouseUp(mouse); | |
309 | return; | |
310 | } | |
311 | ||
bd8d51fa | 312 | super.onMouseUp(mouse); |
dbf8e80a KL |
313 | |
314 | if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) { | |
315 | // Clicked on vertical scrollbar | |
1104f8d7 KL |
316 | if (terminal != null) { |
317 | terminal.setVerticalValue(getVerticalValue()); | |
318 | } | |
dbf8e80a | 319 | } |
bd8d51fa KL |
320 | } |
321 | ||
322 | /** | |
323 | * Handle mouse motion events. | |
324 | * | |
325 | * @param mouse mouse motion event | |
326 | */ | |
327 | @Override | |
328 | public void onMouseMotion(final TMouseEvent mouse) { | |
329 | if (inWindowMove || inWindowResize) { | |
330 | // TWindow needs to deal with this. | |
331 | super.onMouseMotion(mouse); | |
332 | return; | |
333 | } | |
334 | ||
bd8d51fa | 335 | super.onMouseMotion(mouse); |
dbf8e80a KL |
336 | |
337 | if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) { | |
338 | // Clicked/dragged on vertical scrollbar | |
1104f8d7 KL |
339 | if (terminal != null) { |
340 | terminal.setVerticalValue(getVerticalValue()); | |
341 | } | |
dbf8e80a | 342 | } |
bd8d51fa KL |
343 | } |
344 | ||
4941d2d6 KL |
345 | /** |
346 | * Get this window's help topic to load. | |
347 | * | |
348 | * @return the topic name | |
349 | */ | |
350 | @Override | |
351 | public String getHelpTopic() { | |
352 | return "Terminal Window"; | |
353 | } | |
354 | ||
615a0d99 KL |
355 | // ------------------------------------------------------------------------ |
356 | // TTerminalWindow -------------------------------------------------------- | |
357 | // ------------------------------------------------------------------------ | |
358 | ||
80b1b7b5 KL |
359 | /** |
360 | * Returns true if this window does not want the application-wide mouse | |
361 | * cursor drawn over it. | |
362 | * | |
363 | * @return true if this window does not want the application-wide mouse | |
364 | * cursor drawn over it | |
365 | */ | |
366 | @Override | |
367 | public boolean hasHiddenMouse() { | |
1104f8d7 KL |
368 | if (terminal != null) { |
369 | return terminal.hasHiddenMouse(); | |
370 | } | |
371 | return false; | |
80b1b7b5 KL |
372 | } |
373 | ||
615a0d99 KL |
374 | /** |
375 | * Claim the keystrokes the emulator will need. | |
376 | */ | |
377 | private void addShortcutKeys() { | |
378 | addShortcutKeypress(kbCtrlA); | |
379 | addShortcutKeypress(kbCtrlB); | |
380 | addShortcutKeypress(kbCtrlC); | |
381 | addShortcutKeypress(kbCtrlD); | |
382 | addShortcutKeypress(kbCtrlE); | |
383 | addShortcutKeypress(kbCtrlF); | |
384 | addShortcutKeypress(kbCtrlG); | |
385 | addShortcutKeypress(kbCtrlH); | |
386 | addShortcutKeypress(kbCtrlU); | |
387 | addShortcutKeypress(kbCtrlJ); | |
388 | addShortcutKeypress(kbCtrlK); | |
389 | addShortcutKeypress(kbCtrlL); | |
390 | addShortcutKeypress(kbCtrlM); | |
391 | addShortcutKeypress(kbCtrlN); | |
392 | addShortcutKeypress(kbCtrlO); | |
393 | addShortcutKeypress(kbCtrlP); | |
394 | addShortcutKeypress(kbCtrlQ); | |
395 | addShortcutKeypress(kbCtrlR); | |
396 | addShortcutKeypress(kbCtrlS); | |
397 | addShortcutKeypress(kbCtrlT); | |
398 | addShortcutKeypress(kbCtrlU); | |
399 | addShortcutKeypress(kbCtrlV); | |
400 | addShortcutKeypress(kbCtrlW); | |
401 | addShortcutKeypress(kbCtrlX); | |
402 | addShortcutKeypress(kbCtrlY); | |
403 | addShortcutKeypress(kbCtrlZ); | |
404 | addShortcutKeypress(kbF1); | |
405 | addShortcutKeypress(kbF2); | |
406 | addShortcutKeypress(kbF3); | |
407 | addShortcutKeypress(kbF4); | |
408 | addShortcutKeypress(kbF5); | |
409 | addShortcutKeypress(kbF6); | |
410 | addShortcutKeypress(kbF7); | |
411 | addShortcutKeypress(kbF8); | |
412 | addShortcutKeypress(kbF9); | |
413 | addShortcutKeypress(kbF10); | |
414 | addShortcutKeypress(kbF11); | |
415 | addShortcutKeypress(kbF12); | |
416 | addShortcutKeypress(kbAltA); | |
417 | addShortcutKeypress(kbAltB); | |
418 | addShortcutKeypress(kbAltC); | |
419 | addShortcutKeypress(kbAltD); | |
420 | addShortcutKeypress(kbAltE); | |
421 | addShortcutKeypress(kbAltF); | |
422 | addShortcutKeypress(kbAltG); | |
423 | addShortcutKeypress(kbAltH); | |
424 | addShortcutKeypress(kbAltU); | |
425 | addShortcutKeypress(kbAltJ); | |
426 | addShortcutKeypress(kbAltK); | |
427 | addShortcutKeypress(kbAltL); | |
428 | addShortcutKeypress(kbAltM); | |
429 | addShortcutKeypress(kbAltN); | |
430 | addShortcutKeypress(kbAltO); | |
431 | addShortcutKeypress(kbAltP); | |
432 | addShortcutKeypress(kbAltQ); | |
433 | addShortcutKeypress(kbAltR); | |
434 | addShortcutKeypress(kbAltS); | |
435 | addShortcutKeypress(kbAltT); | |
436 | addShortcutKeypress(kbAltU); | |
437 | addShortcutKeypress(kbAltV); | |
438 | addShortcutKeypress(kbAltW); | |
439 | addShortcutKeypress(kbAltX); | |
440 | addShortcutKeypress(kbAltY); | |
441 | addShortcutKeypress(kbAltZ); | |
442 | } | |
443 | ||
615a0d99 KL |
444 | /** |
445 | * Hook for subclasses to be notified of the shell termination. | |
446 | */ | |
447 | public void onShellExit() { | |
a69ed767 KL |
448 | if (closeOnExit) { |
449 | close(); | |
450 | } | |
4d2c61b4 | 451 | clearShortcutKeypresses(); |
615a0d99 KL |
452 | getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT)); |
453 | } | |
454 | ||
5ca5f8e5 KL |
455 | /** |
456 | * Wait for a period of time to get output from the launched process. | |
457 | * | |
458 | * @param millis millis to wait for, or 0 to wait forever | |
459 | * @return true if the launched process has emitted something | |
460 | */ | |
461 | public boolean waitForOutput(final int millis) { | |
462 | if (terminal == null) { | |
463 | return false; | |
464 | } | |
465 | return terminal.waitForOutput(millis); | |
466 | } | |
467 | ||
bff0df27 KL |
468 | /** |
469 | * Get the exit value for the emulator. | |
470 | * | |
471 | * @return exit value | |
472 | */ | |
473 | public int getExitValue() { | |
474 | if (terminal == null) { | |
475 | return -1; | |
476 | } | |
477 | return terminal.getExitValue(); | |
478 | } | |
479 | ||
34a42e78 | 480 | } |