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]
32 import java
.awt
.FontMetrics
;
33 import java
.awt
.Graphics2D
;
34 import java
.awt
.image
.BufferedImage
;
36 import java
.io
.InputStream
;
37 import java
.io
.IOException
;
38 import java
.lang
.reflect
.Field
;
39 import java
.text
.MessageFormat
;
40 import java
.util
.ArrayList
;
41 import java
.util
.HashMap
;
42 import java
.util
.List
;
44 import java
.util
.ResourceBundle
;
46 import jexer
.backend
.ECMA48Terminal
;
47 import jexer
.backend
.GlyphMaker
;
48 import jexer
.backend
.MultiScreen
;
49 import jexer
.backend
.SwingTerminal
;
50 import jexer
.bits
.Cell
;
51 import jexer
.bits
.CellAttributes
;
52 import jexer
.event
.TKeypressEvent
;
53 import jexer
.event
.TMenuEvent
;
54 import jexer
.event
.TMouseEvent
;
55 import jexer
.event
.TResizeEvent
;
56 import jexer
.menu
.TMenu
;
57 import jexer
.tterminal
.DisplayLine
;
58 import jexer
.tterminal
.DisplayListener
;
59 import jexer
.tterminal
.ECMA48
;
60 import static jexer
.TKeypress
.*;
63 * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window.
65 public class TTerminalWindow
extends TScrollableWindow
{
70 private static final ResourceBundle i18n
= ResourceBundle
.getBundle(TTerminalWindow
.class.getName());
72 // ------------------------------------------------------------------------
73 // Variables --------------------------------------------------------------
74 // ------------------------------------------------------------------------
79 private TTerminalWidget terminal
;
82 * If true, close the window when the shell exits.
84 private boolean closeOnExit
= false;
86 // ------------------------------------------------------------------------
87 // Constructors -----------------------------------------------------------
88 // ------------------------------------------------------------------------
91 * Public constructor spawns a custom command line.
93 * @param application TApplication that manages this window
94 * @param x column relative to parent
95 * @param y row relative to parent
96 * @param commandLine the command line to execute
98 public TTerminalWindow(final TApplication application
, final int x
,
99 final int y
, final String commandLine
) {
101 this(application
, x
, y
, RESIZABLE
, commandLine
.split("\\s+"),
102 System
.getProperty("jexer.TTerminal.closeOnExit",
103 "false").equals("true"));
107 * Public constructor spawns a custom command line.
109 * @param application TApplication that manages this window
110 * @param x column relative to parent
111 * @param y row relative to parent
112 * @param commandLine the command line to execute
113 * @param closeOnExit if true, close the window when the command exits
115 public TTerminalWindow(final TApplication application
, final int x
,
116 final int y
, final String commandLine
, final boolean closeOnExit
) {
118 this(application
, x
, y
, RESIZABLE
, commandLine
.split("\\s+"),
123 * Public constructor spawns a custom command line.
125 * @param application TApplication that manages this window
126 * @param x column relative to parent
127 * @param y row relative to parent
128 * @param flags mask of CENTERED, MODAL, or RESIZABLE
129 * @param command the command line to execute
131 public TTerminalWindow(final TApplication application
, final int x
,
132 final int y
, final int flags
, final String
[] command
) {
134 this(application
, x
, y
, flags
, command
,
135 System
.getProperty("jexer.TTerminal.closeOnExit",
136 "false").equals("true"));
140 * Public constructor spawns a custom command line.
142 * @param application TApplication that manages this window
143 * @param x column relative to parent
144 * @param y row relative to parent
145 * @param flags mask of CENTERED, MODAL, or RESIZABLE
146 * @param command the command line to execute
147 * @param closeOnExit if true, close the window when the command exits
149 public TTerminalWindow(final TApplication application
, final int x
,
150 final int y
, final int flags
, final String
[] command
,
151 final boolean closeOnExit
) {
153 super(application
, i18n
.getString("windowTitle"), x
, y
,
154 80 + 2, 24 + 2, flags
);
156 // Require at least one line for the display.
157 setMinimumWindowHeight(3);
159 this.closeOnExit
= closeOnExit
;
160 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
162 // Claim the keystrokes the emulator will need.
166 newStatusBar(i18n
.getString("statusBarRunning"));
169 terminal
= new TTerminalWidget(this, 0, 0, new TAction() {
177 * Public constructor spawns a shell.
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
184 public TTerminalWindow(final TApplication application
, final int x
,
185 final int y
, final int flags
) {
187 this(application
, x
, y
, flags
,
188 System
.getProperty("jexer.TTerminal.closeOnExit",
189 "false").equals("true"));
194 * Public constructor spawns a shell.
196 * @param application TApplication that manages this window
197 * @param x column relative to parent
198 * @param y row relative to parent
199 * @param flags mask of CENTERED, MODAL, or RESIZABLE
200 * @param closeOnExit if true, close the window when the shell exits
202 public TTerminalWindow(final TApplication application
, final int x
,
203 final int y
, final int flags
, final boolean closeOnExit
) {
205 super(application
, i18n
.getString("windowTitle"), x
, y
,
206 80 + 2, 24 + 2, flags
);
208 // Require at least one line for the display.
209 setMinimumWindowHeight(3);
211 this.closeOnExit
= closeOnExit
;
212 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
214 // Claim the keystrokes the emulator will need.
218 newStatusBar(i18n
.getString("statusBarRunning"));
221 terminal
= new TTerminalWidget(this, 0, 0, new TAction() {
228 // ------------------------------------------------------------------------
229 // TScrollableWindow ------------------------------------------------------
230 // ------------------------------------------------------------------------
233 * Draw the display buffer.
237 setTitle(terminal
.getTitle());
243 * Handle window/screen resize events.
245 * @param resize resize event
248 public void onResize(final TResizeEvent resize
) {
249 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
250 terminal
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
251 getWidth() - 2, getHeight() - 2));
253 // Resize the scroll bars
261 * Resize scrollbars for a new width/height.
264 public void reflowData() {
265 // Vertical scrollbar
266 terminal
.reflowData();
267 setTopValue(terminal
.getTopValue());
268 setBottomValue(terminal
.getBottomValue());
269 setVerticalBigChange(terminal
.getVerticalBigChange());
270 setVerticalValue(terminal
.getVerticalValue());
276 * @param keypress keystroke event
279 public void onKeypress(final TKeypressEvent keypress
) {
280 if (terminal
.isReading()) {
281 terminal
.onKeypress(keypress
);
283 super.onKeypress(keypress
);
288 * Handle mouse press events.
290 * @param mouse mouse button press event
293 public void onMouseDown(final TMouseEvent mouse
) {
294 if (inWindowMove
|| inWindowResize
) {
295 // TWindow needs to deal with this.
296 super.onMouseDown(mouse
);
300 super.onMouseDown(mouse
);
304 * Handle mouse release events.
306 * @param mouse mouse button release event
309 public void onMouseUp(final TMouseEvent mouse
) {
310 if (inWindowMove
|| inWindowResize
) {
311 // TWindow needs to deal with this.
312 super.onMouseUp(mouse
);
316 super.onMouseUp(mouse
);
318 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
319 // Clicked on vertical scrollbar
320 terminal
.setVerticalValue(getVerticalValue());
325 * Handle mouse motion events.
327 * @param mouse mouse motion event
330 public void onMouseMotion(final TMouseEvent mouse
) {
331 if (inWindowMove
|| inWindowResize
) {
332 // TWindow needs to deal with this.
333 super.onMouseMotion(mouse
);
337 super.onMouseMotion(mouse
);
339 if (mouse
.isMouse1() && mouseOnVerticalScroller(mouse
)) {
340 // Clicked/dragged on vertical scrollbar
341 terminal
.setVerticalValue(getVerticalValue());
345 // ------------------------------------------------------------------------
346 // TTerminalWindow --------------------------------------------------------
347 // ------------------------------------------------------------------------
350 * Returns true if this window does not want the application-wide mouse
351 * cursor drawn over it.
353 * @return true if this window does not want the application-wide mouse
354 * cursor drawn over it
357 public boolean hasHiddenMouse() {
358 return terminal
.hasHiddenMouse();
362 * Claim the keystrokes the emulator will need.
364 private void addShortcutKeys() {
365 addShortcutKeypress(kbCtrlA
);
366 addShortcutKeypress(kbCtrlB
);
367 addShortcutKeypress(kbCtrlC
);
368 addShortcutKeypress(kbCtrlD
);
369 addShortcutKeypress(kbCtrlE
);
370 addShortcutKeypress(kbCtrlF
);
371 addShortcutKeypress(kbCtrlG
);
372 addShortcutKeypress(kbCtrlH
);
373 addShortcutKeypress(kbCtrlU
);
374 addShortcutKeypress(kbCtrlJ
);
375 addShortcutKeypress(kbCtrlK
);
376 addShortcutKeypress(kbCtrlL
);
377 addShortcutKeypress(kbCtrlM
);
378 addShortcutKeypress(kbCtrlN
);
379 addShortcutKeypress(kbCtrlO
);
380 addShortcutKeypress(kbCtrlP
);
381 addShortcutKeypress(kbCtrlQ
);
382 addShortcutKeypress(kbCtrlR
);
383 addShortcutKeypress(kbCtrlS
);
384 addShortcutKeypress(kbCtrlT
);
385 addShortcutKeypress(kbCtrlU
);
386 addShortcutKeypress(kbCtrlV
);
387 addShortcutKeypress(kbCtrlW
);
388 addShortcutKeypress(kbCtrlX
);
389 addShortcutKeypress(kbCtrlY
);
390 addShortcutKeypress(kbCtrlZ
);
391 addShortcutKeypress(kbF1
);
392 addShortcutKeypress(kbF2
);
393 addShortcutKeypress(kbF3
);
394 addShortcutKeypress(kbF4
);
395 addShortcutKeypress(kbF5
);
396 addShortcutKeypress(kbF6
);
397 addShortcutKeypress(kbF7
);
398 addShortcutKeypress(kbF8
);
399 addShortcutKeypress(kbF9
);
400 addShortcutKeypress(kbF10
);
401 addShortcutKeypress(kbF11
);
402 addShortcutKeypress(kbF12
);
403 addShortcutKeypress(kbAltA
);
404 addShortcutKeypress(kbAltB
);
405 addShortcutKeypress(kbAltC
);
406 addShortcutKeypress(kbAltD
);
407 addShortcutKeypress(kbAltE
);
408 addShortcutKeypress(kbAltF
);
409 addShortcutKeypress(kbAltG
);
410 addShortcutKeypress(kbAltH
);
411 addShortcutKeypress(kbAltU
);
412 addShortcutKeypress(kbAltJ
);
413 addShortcutKeypress(kbAltK
);
414 addShortcutKeypress(kbAltL
);
415 addShortcutKeypress(kbAltM
);
416 addShortcutKeypress(kbAltN
);
417 addShortcutKeypress(kbAltO
);
418 addShortcutKeypress(kbAltP
);
419 addShortcutKeypress(kbAltQ
);
420 addShortcutKeypress(kbAltR
);
421 addShortcutKeypress(kbAltS
);
422 addShortcutKeypress(kbAltT
);
423 addShortcutKeypress(kbAltU
);
424 addShortcutKeypress(kbAltV
);
425 addShortcutKeypress(kbAltW
);
426 addShortcutKeypress(kbAltX
);
427 addShortcutKeypress(kbAltY
);
428 addShortcutKeypress(kbAltZ
);
432 * Hook for subclasses to be notified of the shell termination.
434 public void onShellExit() {
438 clearShortcutKeypresses();
439 getApplication().postEvent(new TMenuEvent(TMenu
.MID_REPAINT
));