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, new TAction() {
174 * Public constructor spawns a shell.
176 * @param application TApplication that manages this window
177 * @param x column relative to parent
178 * @param y row relative to parent
179 * @param flags mask of CENTERED, MODAL, or RESIZABLE
181 public TTerminalWindow(final TApplication application
, final int x
,
182 final int y
, final int flags
) {
184 this(application
, x
, y
, flags
,
185 System
.getProperty("jexer.TTerminal.closeOnExit",
186 "false").equals("true"));
191 * Public constructor spawns a shell.
193 * @param application TApplication that manages this window
194 * @param x column relative to parent
195 * @param y row relative to parent
196 * @param flags mask of CENTERED, MODAL, or RESIZABLE
197 * @param closeOnExit if true, close the window when the shell exits
199 public TTerminalWindow(final TApplication application
, final int x
,
200 final int y
, final int flags
, final boolean closeOnExit
) {
202 super(application
, i18n
.getString("windowTitle"), x
, y
,
203 80 + 2, 24 + 2, flags
);
205 this.closeOnExit
= closeOnExit
;
206 vScroller
= new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
208 // Claim the keystrokes the emulator will need.
212 newStatusBar(i18n
.getString("statusBarRunning"));
215 terminal
= new TTerminalWidget(this, 0, 0, new TAction() {
222 // ------------------------------------------------------------------------
223 // TScrollableWindow ------------------------------------------------------
224 // ------------------------------------------------------------------------
227 * Draw the display buffer.
231 setTitle(terminal
.getTitle());
237 * Handle window/screen resize events.
239 * @param resize resize event
242 public void onResize(final TResizeEvent resize
) {
243 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
244 terminal
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
245 getWidth() - 2, getHeight() - 2));
247 // Resize the scroll bars
255 * Resize scrollbars for a new width/height.
258 public void reflowData() {
259 // Vertical scrollbar
260 terminal
.reflowData();
261 setTopValue(terminal
.getTopValue());
262 setBottomValue(terminal
.getBottomValue());
263 setVerticalBigChange(terminal
.getVerticalBigChange());
264 setVerticalValue(terminal
.getVerticalValue());
270 * @param keypress keystroke event
273 public void onKeypress(final TKeypressEvent keypress
) {
274 if (terminal
.isReading()) {
275 terminal
.onKeypress(keypress
);
277 super.onKeypress(keypress
);
282 * Handle mouse press events.
284 * @param mouse mouse button press event
287 public void onMouseDown(final TMouseEvent mouse
) {
288 if (inWindowMove
|| inWindowResize
) {
289 // TWindow needs to deal with this.
290 super.onMouseDown(mouse
);
294 super.onMouseDown(mouse
);
298 * Handle mouse release events.
300 * @param mouse mouse button release event
303 public void onMouseUp(final TMouseEvent mouse
) {
304 if (inWindowMove
|| inWindowResize
) {
305 // TWindow needs to deal with this.
306 super.onMouseUp(mouse
);
310 super.onMouseUp(mouse
);
314 * Handle mouse motion events.
316 * @param mouse mouse motion event
319 public void onMouseMotion(final TMouseEvent mouse
) {
320 if (inWindowMove
|| inWindowResize
) {
321 // TWindow needs to deal with this.
322 super.onMouseMotion(mouse
);
326 super.onMouseMotion(mouse
);
329 // ------------------------------------------------------------------------
330 // TTerminalWindow --------------------------------------------------------
331 // ------------------------------------------------------------------------
334 * Returns true if this window does not want the application-wide mouse
335 * cursor drawn over it.
337 * @return true if this window does not want the application-wide mouse
338 * cursor drawn over it
341 public boolean hasHiddenMouse() {
342 return terminal
.hasHiddenMouse();
346 * Claim the keystrokes the emulator will need.
348 private void addShortcutKeys() {
349 addShortcutKeypress(kbCtrlA
);
350 addShortcutKeypress(kbCtrlB
);
351 addShortcutKeypress(kbCtrlC
);
352 addShortcutKeypress(kbCtrlD
);
353 addShortcutKeypress(kbCtrlE
);
354 addShortcutKeypress(kbCtrlF
);
355 addShortcutKeypress(kbCtrlG
);
356 addShortcutKeypress(kbCtrlH
);
357 addShortcutKeypress(kbCtrlU
);
358 addShortcutKeypress(kbCtrlJ
);
359 addShortcutKeypress(kbCtrlK
);
360 addShortcutKeypress(kbCtrlL
);
361 addShortcutKeypress(kbCtrlM
);
362 addShortcutKeypress(kbCtrlN
);
363 addShortcutKeypress(kbCtrlO
);
364 addShortcutKeypress(kbCtrlP
);
365 addShortcutKeypress(kbCtrlQ
);
366 addShortcutKeypress(kbCtrlR
);
367 addShortcutKeypress(kbCtrlS
);
368 addShortcutKeypress(kbCtrlT
);
369 addShortcutKeypress(kbCtrlU
);
370 addShortcutKeypress(kbCtrlV
);
371 addShortcutKeypress(kbCtrlW
);
372 addShortcutKeypress(kbCtrlX
);
373 addShortcutKeypress(kbCtrlY
);
374 addShortcutKeypress(kbCtrlZ
);
375 addShortcutKeypress(kbF1
);
376 addShortcutKeypress(kbF2
);
377 addShortcutKeypress(kbF3
);
378 addShortcutKeypress(kbF4
);
379 addShortcutKeypress(kbF5
);
380 addShortcutKeypress(kbF6
);
381 addShortcutKeypress(kbF7
);
382 addShortcutKeypress(kbF8
);
383 addShortcutKeypress(kbF9
);
384 addShortcutKeypress(kbF10
);
385 addShortcutKeypress(kbF11
);
386 addShortcutKeypress(kbF12
);
387 addShortcutKeypress(kbAltA
);
388 addShortcutKeypress(kbAltB
);
389 addShortcutKeypress(kbAltC
);
390 addShortcutKeypress(kbAltD
);
391 addShortcutKeypress(kbAltE
);
392 addShortcutKeypress(kbAltF
);
393 addShortcutKeypress(kbAltG
);
394 addShortcutKeypress(kbAltH
);
395 addShortcutKeypress(kbAltU
);
396 addShortcutKeypress(kbAltJ
);
397 addShortcutKeypress(kbAltK
);
398 addShortcutKeypress(kbAltL
);
399 addShortcutKeypress(kbAltM
);
400 addShortcutKeypress(kbAltN
);
401 addShortcutKeypress(kbAltO
);
402 addShortcutKeypress(kbAltP
);
403 addShortcutKeypress(kbAltQ
);
404 addShortcutKeypress(kbAltR
);
405 addShortcutKeypress(kbAltS
);
406 addShortcutKeypress(kbAltT
);
407 addShortcutKeypress(kbAltU
);
408 addShortcutKeypress(kbAltV
);
409 addShortcutKeypress(kbAltW
);
410 addShortcutKeypress(kbAltX
);
411 addShortcutKeypress(kbAltY
);
412 addShortcutKeypress(kbAltZ
);
416 * Hook for subclasses to be notified of the shell termination.
418 public void onShellExit() {
422 clearShortcutKeypresses();
423 getApplication().postEvent(new TMenuEvent(TMenu
.MID_REPAINT
));