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 this.closeOnExit
= closeOnExit
;
157 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
159 // Claim the keystrokes the emulator will need.
163 newStatusBar(i18n
.getString("statusBarRunning"));
166 terminal
= new TTerminalWidget(this, 0, 0);
170 * Public constructor spawns a shell.
172 * @param application TApplication that manages this window
173 * @param x column relative to parent
174 * @param y row relative to parent
175 * @param flags mask of CENTERED, MODAL, or RESIZABLE
177 public TTerminalWindow(final TApplication application
, final int x
,
178 final int y
, final int flags
) {
180 this(application
, x
, y
, flags
,
181 System
.getProperty("jexer.TTerminal.closeOnExit",
182 "false").equals("true"));
187 * Public constructor spawns a shell.
189 * @param application TApplication that manages this window
190 * @param x column relative to parent
191 * @param y row relative to parent
192 * @param flags mask of CENTERED, MODAL, or RESIZABLE
193 * @param closeOnExit if true, close the window when the shell exits
195 public TTerminalWindow(final TApplication application
, final int x
,
196 final int y
, final int flags
, final boolean closeOnExit
) {
198 super(application
, i18n
.getString("windowTitle"), x
, y
,
199 80 + 2, 24 + 2, flags
);
201 this.closeOnExit
= closeOnExit
;
202 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
204 // Claim the keystrokes the emulator will need.
208 newStatusBar(i18n
.getString("statusBarRunning"));
211 terminal
= new TTerminalWidget(this, 0, 0);
214 // ------------------------------------------------------------------------
215 // TScrollableWindow ------------------------------------------------------
216 // ------------------------------------------------------------------------
219 * Draw the display buffer.
223 setTitle(terminal
.getTitle());
229 * Handle window/screen resize events.
231 * @param resize resize event
234 public void onResize(final TResizeEvent resize
) {
235 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
236 terminal
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
237 getWidth() - 2, getHeight() - 2));
239 // Resize the scroll bars
247 * Resize scrollbars for a new width/height.
250 public void reflowData() {
251 // Vertical scrollbar
252 terminal
.reflowData();
253 setTopValue(terminal
.getTopValue());
254 setBottomValue(terminal
.getBottomValue());
255 setVerticalBigChange(terminal
.getVerticalBigChange());
256 setVerticalValue(terminal
.getVerticalValue());
262 * @param keypress keystroke event
265 public void onKeypress(final TKeypressEvent keypress
) {
266 if (terminal
.isReading()) {
267 terminal
.onKeypress(keypress
);
269 super.onKeypress(keypress
);
274 * Handle mouse press events.
276 * @param mouse mouse button press event
279 public void onMouseDown(final TMouseEvent mouse
) {
280 if (inWindowMove
|| inWindowResize
) {
281 // TWindow needs to deal with this.
282 super.onMouseDown(mouse
);
286 super.onMouseDown(mouse
);
290 * Handle mouse release events.
292 * @param mouse mouse button release event
295 public void onMouseUp(final TMouseEvent mouse
) {
296 if (inWindowMove
|| inWindowResize
) {
297 // TWindow needs to deal with this.
298 super.onMouseUp(mouse
);
302 super.onMouseUp(mouse
);
306 * Handle mouse motion events.
308 * @param mouse mouse motion event
311 public void onMouseMotion(final TMouseEvent mouse
) {
312 if (inWindowMove
|| inWindowResize
) {
313 // TWindow needs to deal with this.
314 super.onMouseMotion(mouse
);
318 super.onMouseMotion(mouse
);
321 // ------------------------------------------------------------------------
322 // TTerminalWindow --------------------------------------------------------
323 // ------------------------------------------------------------------------
326 * Returns true if this window does not want the application-wide mouse
327 * cursor drawn over it.
329 * @return true if this window does not want the application-wide mouse
330 * cursor drawn over it
333 public boolean hasHiddenMouse() {
334 return terminal
.hasHiddenMouse();
338 * Claim the keystrokes the emulator will need.
340 private void addShortcutKeys() {
341 addShortcutKeypress(kbCtrlA
);
342 addShortcutKeypress(kbCtrlB
);
343 addShortcutKeypress(kbCtrlC
);
344 addShortcutKeypress(kbCtrlD
);
345 addShortcutKeypress(kbCtrlE
);
346 addShortcutKeypress(kbCtrlF
);
347 addShortcutKeypress(kbCtrlG
);
348 addShortcutKeypress(kbCtrlH
);
349 addShortcutKeypress(kbCtrlU
);
350 addShortcutKeypress(kbCtrlJ
);
351 addShortcutKeypress(kbCtrlK
);
352 addShortcutKeypress(kbCtrlL
);
353 addShortcutKeypress(kbCtrlM
);
354 addShortcutKeypress(kbCtrlN
);
355 addShortcutKeypress(kbCtrlO
);
356 addShortcutKeypress(kbCtrlP
);
357 addShortcutKeypress(kbCtrlQ
);
358 addShortcutKeypress(kbCtrlR
);
359 addShortcutKeypress(kbCtrlS
);
360 addShortcutKeypress(kbCtrlT
);
361 addShortcutKeypress(kbCtrlU
);
362 addShortcutKeypress(kbCtrlV
);
363 addShortcutKeypress(kbCtrlW
);
364 addShortcutKeypress(kbCtrlX
);
365 addShortcutKeypress(kbCtrlY
);
366 addShortcutKeypress(kbCtrlZ
);
367 addShortcutKeypress(kbF1
);
368 addShortcutKeypress(kbF2
);
369 addShortcutKeypress(kbF3
);
370 addShortcutKeypress(kbF4
);
371 addShortcutKeypress(kbF5
);
372 addShortcutKeypress(kbF6
);
373 addShortcutKeypress(kbF7
);
374 addShortcutKeypress(kbF8
);
375 addShortcutKeypress(kbF9
);
376 addShortcutKeypress(kbF10
);
377 addShortcutKeypress(kbF11
);
378 addShortcutKeypress(kbF12
);
379 addShortcutKeypress(kbAltA
);
380 addShortcutKeypress(kbAltB
);
381 addShortcutKeypress(kbAltC
);
382 addShortcutKeypress(kbAltD
);
383 addShortcutKeypress(kbAltE
);
384 addShortcutKeypress(kbAltF
);
385 addShortcutKeypress(kbAltG
);
386 addShortcutKeypress(kbAltH
);
387 addShortcutKeypress(kbAltU
);
388 addShortcutKeypress(kbAltJ
);
389 addShortcutKeypress(kbAltK
);
390 addShortcutKeypress(kbAltL
);
391 addShortcutKeypress(kbAltM
);
392 addShortcutKeypress(kbAltN
);
393 addShortcutKeypress(kbAltO
);
394 addShortcutKeypress(kbAltP
);
395 addShortcutKeypress(kbAltQ
);
396 addShortcutKeypress(kbAltR
);
397 addShortcutKeypress(kbAltS
);
398 addShortcutKeypress(kbAltT
);
399 addShortcutKeypress(kbAltU
);
400 addShortcutKeypress(kbAltV
);
401 addShortcutKeypress(kbAltW
);
402 addShortcutKeypress(kbAltX
);
403 addShortcutKeypress(kbAltY
);
404 addShortcutKeypress(kbAltZ
);
408 * Hook for subclasses to be notified of the shell termination.
410 public void onShellExit() {
414 clearShortcutKeypresses();
415 getApplication().postEvent(new TMenuEvent(TMenu
.MID_REPAINT
));