use more template, use replace input stream
[nikiroo-utils.git] / src / jexer / TTerminalWindow.java
CommitLineData
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 */
29package jexer;
30
339652cc 31import java.util.ResourceBundle;
34a42e78 32
3c5921e6 33import jexer.menu.TMenu;
34a42e78 34import jexer.event.TKeypressEvent;
b2d49e0f 35import jexer.event.TMenuEvent;
34a42e78
KL
36import jexer.event.TMouseEvent;
37import jexer.event.TResizeEvent;
4941d2d6 38import static jexer.TCommand.*;
34a42e78
KL
39import static jexer.TKeypress.*;
40
41/**
42 * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window.
43 */
4d2c61b4 44public 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}