2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.ResourceBundle
;
33 import jexer
.menu
.TMenu
;
34 import jexer
.event
.TKeypressEvent
;
35 import jexer
.event
.TMenuEvent
;
36 import jexer
.event
.TMouseEvent
;
37 import jexer
.event
.TResizeEvent
;
38 import static jexer
.TKeypress
.*;
41 * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window.
43 public class TTerminalWindow
extends TScrollableWindow
{
48 private static final ResourceBundle i18n
= ResourceBundle
.getBundle(TTerminalWindow
.class.getName());
50 // ------------------------------------------------------------------------
51 // Variables --------------------------------------------------------------
52 // ------------------------------------------------------------------------
57 private TTerminalWidget terminal
;
60 * If true, close the window when the shell exits.
62 private boolean closeOnExit
= false;
64 // ------------------------------------------------------------------------
65 // Constructors -----------------------------------------------------------
66 // ------------------------------------------------------------------------
69 * Public constructor spawns a custom command line.
71 * @param application TApplication that manages this window
72 * @param x column relative to parent
73 * @param y row relative to parent
74 * @param commandLine the command line to execute
76 public TTerminalWindow(final TApplication application
, final int x
,
77 final int y
, final String commandLine
) {
79 this(application
, x
, y
, RESIZABLE
, commandLine
.split("\\s+"),
80 System
.getProperty("jexer.TTerminal.closeOnExit",
81 "false").equals("true"));
85 * Public constructor spawns a custom command line.
87 * @param application TApplication that manages this window
88 * @param x column relative to parent
89 * @param y row relative to parent
90 * @param commandLine the command line to execute
91 * @param closeOnExit if true, close the window when the command exits
93 public TTerminalWindow(final TApplication application
, final int x
,
94 final int y
, final String commandLine
, final boolean closeOnExit
) {
96 this(application
, x
, y
, RESIZABLE
, commandLine
.split("\\s+"),
101 * Public constructor spawns a custom command line.
103 * @param application TApplication that manages this window
104 * @param x column relative to parent
105 * @param y row relative to parent
106 * @param flags mask of CENTERED, MODAL, or RESIZABLE
107 * @param command the command line to execute
109 public TTerminalWindow(final TApplication application
, final int x
,
110 final int y
, final int flags
, final String
[] command
) {
112 this(application
, x
, y
, flags
, command
,
113 System
.getProperty("jexer.TTerminal.closeOnExit",
114 "false").equals("true"));
118 * Public constructor spawns a custom command line.
120 * @param application TApplication that manages this window
121 * @param x column relative to parent
122 * @param y row relative to parent
123 * @param flags mask of CENTERED, MODAL, or RESIZABLE
124 * @param command the command line to execute
125 * @param closeOnExit if true, close the window when the command exits
127 public TTerminalWindow(final TApplication application
, final int x
,
128 final int y
, final int flags
, final String
[] command
,
129 final boolean closeOnExit
) {
131 super(application
, i18n
.getString("windowTitle"), x
, y
,
132 80 + 2, 24 + 2, flags
);
134 // Require at least one line for the display.
135 setMinimumWindowHeight(3);
137 this.closeOnExit
= closeOnExit
;
138 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
140 // Claim the keystrokes the emulator will need.
144 newStatusBar(i18n
.getString("statusBarRunning"));
147 terminal
= new TTerminalWidget(this, 0, 0, command
, new TAction() {
155 * Public constructor spawns a shell.
157 * @param application TApplication that manages this window
158 * @param x column relative to parent
159 * @param y row relative to parent
160 * @param flags mask of CENTERED, MODAL, or RESIZABLE
162 public TTerminalWindow(final TApplication application
, final int x
,
163 final int y
, final int flags
) {
165 this(application
, x
, y
, flags
,
166 System
.getProperty("jexer.TTerminal.closeOnExit",
167 "false").equals("true"));
172 * Public constructor spawns a shell.
174 * @param application TApplication that manages this window
175 * @param x column relative to parent
176 * @param y row relative to parent
177 * @param flags mask of CENTERED, MODAL, or RESIZABLE
178 * @param closeOnExit if true, close the window when the shell exits
180 public TTerminalWindow(final TApplication application
, final int x
,
181 final int y
, final int flags
, final boolean closeOnExit
) {
183 super(application
, i18n
.getString("windowTitle"), x
, y
,
184 80 + 2, 24 + 2, flags
);
186 // Require at least one line for the display.
187 setMinimumWindowHeight(3);
189 this.closeOnExit
= closeOnExit
;
190 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
192 // Claim the keystrokes the emulator will need.
196 newStatusBar(i18n
.getString("statusBarRunning"));
199 terminal
= new TTerminalWidget(this, 0, 0, new TAction() {
206 // ------------------------------------------------------------------------
207 // TScrollableWindow ------------------------------------------------------
208 // ------------------------------------------------------------------------
211 * Draw the display buffer.
215 if (terminal
!= null) {
216 setTitle(terminal
.getTitle());
223 * Handle window/screen resize events.
225 * @param resize resize event
228 public void onResize(final TResizeEvent resize
) {
229 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
230 if (terminal
!= null) {
231 terminal
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
232 getWidth() - 2, getHeight() - 2));
235 // Resize the scroll bars
243 * Resize scrollbars for a new width/height.
246 public void reflowData() {
247 // Vertical scrollbar
248 if (terminal
!= null) {
249 terminal
.reflowData();
250 setTopValue(terminal
.getTopValue());
251 setBottomValue(terminal
.getBottomValue());
252 setVerticalBigChange(terminal
.getVerticalBigChange());
253 setVerticalValue(terminal
.getVerticalValue());
260 * @param keypress keystroke event
263 public void onKeypress(final TKeypressEvent keypress
) {
264 if ((terminal
!= null) && (terminal
.isReading())) {
265 terminal
.onKeypress(keypress
);
267 super.onKeypress(keypress
);
272 * Handle mouse press events.
274 * @param mouse mouse button press event
277 public void onMouseDown(final TMouseEvent mouse
) {
278 if (inWindowMove
|| inWindowResize
) {
279 // TWindow needs to deal with this.
280 super.onMouseDown(mouse
);
284 super.onMouseDown(mouse
);
288 * Handle mouse release events.
290 * @param mouse mouse button release event
293 public void onMouseUp(final TMouseEvent mouse
) {
294 if (inWindowMove
|| inWindowResize
) {
295 // TWindow needs to deal with this.
296 super.onMouseUp(mouse
);
300 super.onMouseUp(mouse
);
302 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
303 // Clicked on vertical scrollbar
304 if (terminal
!= null) {
305 terminal
.setVerticalValue(getVerticalValue());
311 * Handle mouse motion events.
313 * @param mouse mouse motion event
316 public void onMouseMotion(final TMouseEvent mouse
) {
317 if (inWindowMove
|| inWindowResize
) {
318 // TWindow needs to deal with this.
319 super.onMouseMotion(mouse
);
323 super.onMouseMotion(mouse
);
325 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
326 // Clicked/dragged on vertical scrollbar
327 if (terminal
!= null) {
328 terminal
.setVerticalValue(getVerticalValue());
333 // ------------------------------------------------------------------------
334 // TTerminalWindow --------------------------------------------------------
335 // ------------------------------------------------------------------------
338 * Returns true if this window does not want the application-wide mouse
339 * cursor drawn over it.
341 * @return true if this window does not want the application-wide mouse
342 * cursor drawn over it
345 public boolean hasHiddenMouse() {
346 if (terminal
!= null) {
347 return terminal
.hasHiddenMouse();
353 * Claim the keystrokes the emulator will need.
355 private void addShortcutKeys() {
356 addShortcutKeypress(kbCtrlA
);
357 addShortcutKeypress(kbCtrlB
);
358 addShortcutKeypress(kbCtrlC
);
359 addShortcutKeypress(kbCtrlD
);
360 addShortcutKeypress(kbCtrlE
);
361 addShortcutKeypress(kbCtrlF
);
362 addShortcutKeypress(kbCtrlG
);
363 addShortcutKeypress(kbCtrlH
);
364 addShortcutKeypress(kbCtrlU
);
365 addShortcutKeypress(kbCtrlJ
);
366 addShortcutKeypress(kbCtrlK
);
367 addShortcutKeypress(kbCtrlL
);
368 addShortcutKeypress(kbCtrlM
);
369 addShortcutKeypress(kbCtrlN
);
370 addShortcutKeypress(kbCtrlO
);
371 addShortcutKeypress(kbCtrlP
);
372 addShortcutKeypress(kbCtrlQ
);
373 addShortcutKeypress(kbCtrlR
);
374 addShortcutKeypress(kbCtrlS
);
375 addShortcutKeypress(kbCtrlT
);
376 addShortcutKeypress(kbCtrlU
);
377 addShortcutKeypress(kbCtrlV
);
378 addShortcutKeypress(kbCtrlW
);
379 addShortcutKeypress(kbCtrlX
);
380 addShortcutKeypress(kbCtrlY
);
381 addShortcutKeypress(kbCtrlZ
);
382 addShortcutKeypress(kbF1
);
383 addShortcutKeypress(kbF2
);
384 addShortcutKeypress(kbF3
);
385 addShortcutKeypress(kbF4
);
386 addShortcutKeypress(kbF5
);
387 addShortcutKeypress(kbF6
);
388 addShortcutKeypress(kbF7
);
389 addShortcutKeypress(kbF8
);
390 addShortcutKeypress(kbF9
);
391 addShortcutKeypress(kbF10
);
392 addShortcutKeypress(kbF11
);
393 addShortcutKeypress(kbF12
);
394 addShortcutKeypress(kbAltA
);
395 addShortcutKeypress(kbAltB
);
396 addShortcutKeypress(kbAltC
);
397 addShortcutKeypress(kbAltD
);
398 addShortcutKeypress(kbAltE
);
399 addShortcutKeypress(kbAltF
);
400 addShortcutKeypress(kbAltG
);
401 addShortcutKeypress(kbAltH
);
402 addShortcutKeypress(kbAltU
);
403 addShortcutKeypress(kbAltJ
);
404 addShortcutKeypress(kbAltK
);
405 addShortcutKeypress(kbAltL
);
406 addShortcutKeypress(kbAltM
);
407 addShortcutKeypress(kbAltN
);
408 addShortcutKeypress(kbAltO
);
409 addShortcutKeypress(kbAltP
);
410 addShortcutKeypress(kbAltQ
);
411 addShortcutKeypress(kbAltR
);
412 addShortcutKeypress(kbAltS
);
413 addShortcutKeypress(kbAltT
);
414 addShortcutKeypress(kbAltU
);
415 addShortcutKeypress(kbAltV
);
416 addShortcutKeypress(kbAltW
);
417 addShortcutKeypress(kbAltX
);
418 addShortcutKeypress(kbAltY
);
419 addShortcutKeypress(kbAltZ
);
423 * Hook for subclasses to be notified of the shell termination.
425 public void onShellExit() {
429 clearShortcutKeypresses();
430 getApplication().postEvent(new TMenuEvent(TMenu
.MID_REPAINT
));
434 * Wait for a period of time to get output from the launched process.
436 * @param millis millis to wait for, or 0 to wait forever
437 * @return true if the launched process has emitted something
439 public boolean waitForOutput(final int millis
) {
440 if (terminal
== null) {
443 return terminal
.waitForOutput(millis
);