retrofit
[fanfix.git] / src / jexer / TTerminalWindow.java
... / ...
CommitLineData
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 Kevin Lamonte
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
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.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer;
30
31import java.util.ResourceBundle;
32
33import jexer.menu.TMenu;
34import jexer.event.TKeypressEvent;
35import jexer.event.TMenuEvent;
36import jexer.event.TMouseEvent;
37import jexer.event.TResizeEvent;
38import static jexer.TKeypress.*;
39
40/**
41 * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window.
42 */
43public class TTerminalWindow extends TScrollableWindow {
44
45 /**
46 * Translated strings.
47 */
48 private static final ResourceBundle i18n = ResourceBundle.getBundle(TTerminalWindow.class.getName());
49
50 // ------------------------------------------------------------------------
51 // Variables --------------------------------------------------------------
52 // ------------------------------------------------------------------------
53
54 /**
55 * The terminal.
56 */
57 private TTerminalWidget terminal;
58
59 /**
60 * If true, close the window when the shell exits.
61 */
62 private boolean closeOnExit = false;
63
64 // ------------------------------------------------------------------------
65 // Constructors -----------------------------------------------------------
66 // ------------------------------------------------------------------------
67
68 /**
69 * Public constructor spawns a custom command line.
70 *
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
75 */
76 public TTerminalWindow(final TApplication application, final int x,
77 final int y, final String commandLine) {
78
79 this(application, x, y, RESIZABLE, commandLine.split("\\s+"),
80 System.getProperty("jexer.TTerminal.closeOnExit",
81 "false").equals("true"));
82 }
83
84 /**
85 * Public constructor spawns a custom command line.
86 *
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
92 */
93 public TTerminalWindow(final TApplication application, final int x,
94 final int y, final String commandLine, final boolean closeOnExit) {
95
96 this(application, x, y, RESIZABLE, commandLine.split("\\s+"),
97 closeOnExit);
98 }
99
100 /**
101 * Public constructor spawns a custom command line.
102 *
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
108 */
109 public TTerminalWindow(final TApplication application, final int x,
110 final int y, final int flags, final String [] command) {
111
112 this(application, x, y, flags, command,
113 System.getProperty("jexer.TTerminal.closeOnExit",
114 "false").equals("true"));
115 }
116
117 /**
118 * Public constructor spawns a custom command line.
119 *
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
126 */
127 public TTerminalWindow(final TApplication application, final int x,
128 final int y, final int flags, final String [] command,
129 final boolean closeOnExit) {
130
131 super(application, i18n.getString("windowTitle"), x, y,
132 80 + 2, 24 + 2, flags);
133
134 // Require at least one line for the display.
135 setMinimumWindowHeight(3);
136
137 this.closeOnExit = closeOnExit;
138 vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
139
140 // Claim the keystrokes the emulator will need.
141 addShortcutKeys();
142
143 // Add shortcut text
144 newStatusBar(i18n.getString("statusBarRunning"));
145
146 // Spin it up
147 terminal = new TTerminalWidget(this, 0, 0, command, new TAction() {
148 public void DO() {
149 onShellExit();
150 }
151 });
152 }
153
154 /**
155 * Public constructor spawns a shell.
156 *
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
161 */
162 public TTerminalWindow(final TApplication application, final int x,
163 final int y, final int flags) {
164
165 this(application, x, y, flags,
166 System.getProperty("jexer.TTerminal.closeOnExit",
167 "false").equals("true"));
168
169 }
170
171 /**
172 * Public constructor spawns a shell.
173 *
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
179 */
180 public TTerminalWindow(final TApplication application, final int x,
181 final int y, final int flags, final boolean closeOnExit) {
182
183 super(application, i18n.getString("windowTitle"), x, y,
184 80 + 2, 24 + 2, flags);
185
186 // Require at least one line for the display.
187 setMinimumWindowHeight(3);
188
189 this.closeOnExit = closeOnExit;
190 vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
191
192 // Claim the keystrokes the emulator will need.
193 addShortcutKeys();
194
195 // Add shortcut text
196 newStatusBar(i18n.getString("statusBarRunning"));
197
198 // Spin it up
199 terminal = new TTerminalWidget(this, 0, 0, new TAction() {
200 public void DO() {
201 onShellExit();
202 }
203 });
204 }
205
206 // ------------------------------------------------------------------------
207 // TScrollableWindow ------------------------------------------------------
208 // ------------------------------------------------------------------------
209
210 /**
211 * Draw the display buffer.
212 */
213 @Override
214 public void draw() {
215 if (terminal != null) {
216 setTitle(terminal.getTitle());
217 }
218 reflowData();
219 super.draw();
220 }
221
222 /**
223 * Handle window/screen resize events.
224 *
225 * @param resize resize event
226 */
227 @Override
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));
233 }
234
235 // Resize the scroll bars
236 reflowData();
237 placeScrollbars();
238 }
239 return;
240 }
241
242 /**
243 * Resize scrollbars for a new width/height.
244 */
245 @Override
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());
254 }
255 }
256
257 /**
258 * Handle keystrokes.
259 *
260 * @param keypress keystroke event
261 */
262 @Override
263 public void onKeypress(final TKeypressEvent keypress) {
264 if ((terminal != null) && (terminal.isReading())) {
265 terminal.onKeypress(keypress);
266 } else {
267 super.onKeypress(keypress);
268 }
269 }
270
271 /**
272 * Handle mouse press events.
273 *
274 * @param mouse mouse button press event
275 */
276 @Override
277 public void onMouseDown(final TMouseEvent mouse) {
278 if (inWindowMove || inWindowResize) {
279 // TWindow needs to deal with this.
280 super.onMouseDown(mouse);
281 return;
282 }
283
284 super.onMouseDown(mouse);
285 }
286
287 /**
288 * Handle mouse release events.
289 *
290 * @param mouse mouse button release event
291 */
292 @Override
293 public void onMouseUp(final TMouseEvent mouse) {
294 if (inWindowMove || inWindowResize) {
295 // TWindow needs to deal with this.
296 super.onMouseUp(mouse);
297 return;
298 }
299
300 super.onMouseUp(mouse);
301
302 if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
303 // Clicked on vertical scrollbar
304 if (terminal != null) {
305 terminal.setVerticalValue(getVerticalValue());
306 }
307 }
308 }
309
310 /**
311 * Handle mouse motion events.
312 *
313 * @param mouse mouse motion event
314 */
315 @Override
316 public void onMouseMotion(final TMouseEvent mouse) {
317 if (inWindowMove || inWindowResize) {
318 // TWindow needs to deal with this.
319 super.onMouseMotion(mouse);
320 return;
321 }
322
323 super.onMouseMotion(mouse);
324
325 if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
326 // Clicked/dragged on vertical scrollbar
327 if (terminal != null) {
328 terminal.setVerticalValue(getVerticalValue());
329 }
330 }
331 }
332
333 // ------------------------------------------------------------------------
334 // TTerminalWindow --------------------------------------------------------
335 // ------------------------------------------------------------------------
336
337 /**
338 * Returns true if this window does not want the application-wide mouse
339 * cursor drawn over it.
340 *
341 * @return true if this window does not want the application-wide mouse
342 * cursor drawn over it
343 */
344 @Override
345 public boolean hasHiddenMouse() {
346 if (terminal != null) {
347 return terminal.hasHiddenMouse();
348 }
349 return false;
350 }
351
352 /**
353 * Claim the keystrokes the emulator will need.
354 */
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);
420 }
421
422 /**
423 * Hook for subclasses to be notified of the shell termination.
424 */
425 public void onShellExit() {
426 if (closeOnExit) {
427 close();
428 }
429 clearShortcutKeypresses();
430 getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
431 }
432
433 /**
434 * Wait for a period of time to get output from the launched process.
435 *
436 * @param millis millis to wait for, or 0 to wait forever
437 * @return true if the launched process has emitted something
438 */
439 public boolean waitForOutput(final int millis) {
440 if (terminal == null) {
441 return false;
442 }
443 return terminal.waitForOutput(millis);
444 }
445
446}