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]
29 package jexer
.backend
;
31 import java
.awt
.BorderLayout
;
32 import java
.awt
.Color
;
34 import java
.awt
.FontMetrics
;
35 import java
.awt
.Graphics2D
;
36 import java
.awt
.Graphics
;
37 import java
.awt
.Insets
;
38 import java
.awt
.Rectangle
;
39 import java
.awt
.RenderingHints
;
40 import java
.awt
.Toolkit
;
41 import java
.awt
.event
.ComponentEvent
;
42 import java
.awt
.event
.ComponentListener
;
43 import java
.awt
.event
.KeyEvent
;
44 import java
.awt
.event
.KeyListener
;
45 import java
.awt
.event
.MouseEvent
;
46 import java
.awt
.event
.MouseListener
;
47 import java
.awt
.event
.MouseMotionListener
;
48 import java
.awt
.event
.MouseWheelEvent
;
49 import java
.awt
.event
.MouseWheelListener
;
50 import java
.awt
.event
.WindowEvent
;
51 import java
.awt
.event
.WindowListener
;
52 import java
.awt
.geom
.Rectangle2D
;
53 import java
.awt
.image
.BufferedImage
;
54 import java
.io
.InputStream
;
55 import java
.util
.ArrayList
;
56 import java
.util
.HashMap
;
57 import java
.util
.List
;
59 import javax
.swing
.JComponent
;
60 import javax
.swing
.JFrame
;
61 import javax
.swing
.ImageIcon
;
62 import javax
.swing
.SwingUtilities
;
64 import jexer
.TKeypress
;
65 import jexer
.bits
.Cell
;
66 import jexer
.bits
.CellAttributes
;
67 import jexer
.event
.TCommandEvent
;
68 import jexer
.event
.TInputEvent
;
69 import jexer
.event
.TKeypressEvent
;
70 import jexer
.event
.TMouseEvent
;
71 import jexer
.event
.TResizeEvent
;
72 import static jexer
.TCommand
.*;
73 import static jexer
.TKeypress
.*;
76 * This Screen backend reads keystrokes and mouse events and draws to either
77 * a Java Swing JFrame (potentially triple-buffered) or a JComponent.
79 * This class is a bit of an inversion of typical GUI classes. It performs
80 * all of the drawing logic from SwingTerminal (which is not a Swing class),
81 * and uses a SwingComponent wrapper class to call the JFrame or JComponent
84 public class SwingTerminal
extends LogicalScreen
85 implements TerminalReader
,
86 ComponentListener
, KeyListener
,
87 MouseListener
, MouseMotionListener
,
88 MouseWheelListener
, WindowListener
{
90 // ------------------------------------------------------------------------
91 // Constants --------------------------------------------------------------
92 // ------------------------------------------------------------------------
95 * The icon image location.
97 private static final String ICONFILE
= "jexer_logo_128.png";
100 * The terminus font resource filename.
102 public static final String FONTFILE
= "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
105 * Cursor style to draw.
107 public enum CursorStyle
{
109 * Use an underscore for the cursor.
114 * Use a solid block for the cursor.
119 * Use an outlined block for the cursor.
124 * Use a vertical bar for the cursor.
129 // ------------------------------------------------------------------------
130 // Variables --------------------------------------------------------------
131 // ------------------------------------------------------------------------
133 // Colors to map DOS colors to AWT colors.
134 private static Color MYBLACK
;
135 private static Color MYRED
;
136 private static Color MYGREEN
;
137 private static Color MYYELLOW
;
138 private static Color MYBLUE
;
139 private static Color MYMAGENTA
;
140 private static Color MYCYAN
;
141 private static Color MYWHITE
;
142 private static Color MYBOLD_BLACK
;
143 private static Color MYBOLD_RED
;
144 private static Color MYBOLD_GREEN
;
145 private static Color MYBOLD_YELLOW
;
146 private static Color MYBOLD_BLUE
;
147 private static Color MYBOLD_MAGENTA
;
148 private static Color MYBOLD_CYAN
;
149 private static Color MYBOLD_WHITE
;
152 * When true, all the MYBLACK, MYRED, etc. colors are set.
154 private static boolean dosColors
= false;
157 * The Swing component or frame to draw to.
159 private SwingComponent swing
;
162 * A cache of previously-rendered glyphs for blinking text, when it is
165 private Map
<Cell
, BufferedImage
> glyphCacheBlink
;
168 * A cache of previously-rendered glyphs for non-blinking, or
169 * blinking-and-visible, text.
171 private Map
<Cell
, BufferedImage
> glyphCache
;
174 * If true, we were successful at getting the font dimensions.
176 private boolean gotFontDimensions
= false;
179 * The currently selected font.
181 private Font font
= null;
184 * The currently selected font size in points.
186 private int fontSize
= 16;
189 * Width of a character cell in pixels.
191 private int textWidth
= 16;
194 * Height of a character cell in pixels.
196 private int textHeight
= 20;
199 * Width of a character cell in pixels, as reported by font.
201 private int fontTextWidth
= 1;
204 * Height of a character cell in pixels, as reported by font.
206 private int fontTextHeight
= 1;
209 * Descent of a character cell in pixels.
211 private int maxDescent
= 0;
214 * System-dependent Y adjustment for text in the character cell.
216 private int textAdjustY
= 0;
219 * System-dependent X adjustment for text in the character cell.
221 private int textAdjustX
= 0;
224 * System-dependent height adjustment for text in the character cell.
226 private int textAdjustHeight
= 0;
229 * System-dependent width adjustment for text in the character cell.
231 private int textAdjustWidth
= 0;
234 * Top pixel absolute location.
236 private int top
= 30;
239 * Left pixel absolute location.
241 private int left
= 30;
244 * The cursor style to draw.
246 private CursorStyle cursorStyle
= CursorStyle
.UNDERLINE
;
249 * The number of millis to wait before switching the blink from visible
250 * to invisible. Set to 0 or negative to disable blinking.
252 private long blinkMillis
= 500;
255 * If true, the cursor should be visible right now based on the blink
258 private boolean cursorBlinkVisible
= true;
261 * The time that the blink last flipped from visible to invisible or
262 * from invisible to visible.
264 private long lastBlinkTime
= 0;
267 * The session information.
269 private SwingSessionInfo sessionInfo
;
272 * The listening object that run() wakes up on new input.
274 private Object listener
;
277 * The event queue, filled up by a thread reading on input.
279 private List
<TInputEvent
> eventQueue
;
282 * The last reported mouse X position.
284 private int oldMouseX
= -1;
287 * The last reported mouse Y position.
289 private int oldMouseY
= -1;
292 * true if mouse1 was down. Used to report mouse1 on the release event.
294 private boolean mouse1
= false;
297 * true if mouse2 was down. Used to report mouse2 on the release event.
299 private boolean mouse2
= false;
302 * true if mouse3 was down. Used to report mouse3 on the release event.
304 private boolean mouse3
= false;
306 // ------------------------------------------------------------------------
307 // Constructors -----------------------------------------------------------
308 // ------------------------------------------------------------------------
311 * Static constructor.
318 * Public constructor creates a new JFrame to render to.
320 * @param windowWidth the number of text columns to start with
321 * @param windowHeight the number of text rows to start with
322 * @param fontSize the size in points. Good values to pick are: 16, 20,
324 * @param listener the object this backend needs to wake up when new
327 public SwingTerminal(final int windowWidth
, final int windowHeight
,
328 final int fontSize
, final Object listener
) {
330 this.fontSize
= fontSize
;
335 SwingUtilities
.invokeAndWait(new Runnable() {
338 JFrame frame
= new JFrame() {
341 * Serializable version.
343 private static final long serialVersionUID
= 1;
346 * The code that performs the actual drawing.
348 public SwingTerminal screen
= null;
351 * Anonymous class initializer saves the screen
352 * reference, so that paint() and the like call out
356 this.screen
= SwingTerminal
.this;
360 * Update redraws the whole screen.
362 * @param gr the Swing Graphics context
365 public void update(final Graphics gr
) {
366 // The default update clears the area. Don't do
367 // that, instead just paint it directly.
372 * Paint redraws the whole screen.
374 * @param gr the Swing Graphics context
377 public void paint(final Graphics gr
) {
378 if (screen
!= null) {
385 ClassLoader loader
= Thread
.currentThread().
386 getContextClassLoader();
387 frame
.setIconImage((new ImageIcon(loader
.
388 getResource(ICONFILE
))).getImage());
390 // Get the Swing component
391 SwingTerminal
.this.swing
= new SwingComponent(frame
);
393 // Hang onto top and left for drawing.
394 Insets insets
= SwingTerminal
.this.swing
.getInsets();
395 SwingTerminal
.this.left
= insets
.left
;
396 SwingTerminal
.this.top
= insets
.top
;
398 // Load the font so that we can set sessionInfo.
401 // Get the default cols x rows and set component size
403 SwingTerminal
.this.sessionInfo
=
404 new SwingSessionInfo(SwingTerminal
.this.swing
,
405 SwingTerminal
.this.textWidth
,
406 SwingTerminal
.this.textHeight
,
407 windowWidth
, windowHeight
);
409 SwingTerminal
.this.setDimensions(sessionInfo
.
410 getWindowWidth(), sessionInfo
.getWindowHeight());
412 SwingTerminal
.this.resizeToScreen(true);
413 SwingTerminal
.this.swing
.setVisible(true);
416 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
418 } catch (InterruptedException e
) {
422 this.listener
= listener
;
426 eventQueue
= new ArrayList
<TInputEvent
>();
428 // Add listeners to Swing.
429 swing
.addKeyListener(this);
430 swing
.addWindowListener(this);
431 swing
.addComponentListener(this);
432 swing
.addMouseListener(this);
433 swing
.addMouseMotionListener(this);
434 swing
.addMouseWheelListener(this);
438 * Public constructor renders to an existing JComponent.
440 * @param component the Swing component to render to
441 * @param windowWidth the number of text columns to start with
442 * @param windowHeight the number of text rows to start with
443 * @param fontSize the size in points. Good values to pick are: 16, 20,
445 * @param listener the object this backend needs to wake up when new
448 public SwingTerminal(final JComponent component
, final int windowWidth
,
449 final int windowHeight
, final int fontSize
, final Object listener
) {
451 this.fontSize
= fontSize
;
456 SwingUtilities
.invokeAndWait(new Runnable() {
459 JComponent newComponent
= new JComponent() {
462 * Serializable version.
464 private static final long serialVersionUID
= 1;
467 * The code that performs the actual drawing.
469 public SwingTerminal screen
= null;
472 * Anonymous class initializer saves the screen
473 * reference, so that paint() and the like call out
477 this.screen
= SwingTerminal
.this;
481 * Update redraws the whole screen.
483 * @param gr the Swing Graphics context
486 public void update(final Graphics gr
) {
487 // The default update clears the area. Don't do
488 // that, instead just paint it directly.
493 * Paint redraws the whole screen.
495 * @param gr the Swing Graphics context
498 public void paint(final Graphics gr
) {
499 if (screen
!= null) {
504 component
.setLayout(new BorderLayout());
505 component
.add(newComponent
);
507 // Allow key events to be received
508 component
.setFocusable(true);
510 // Get the Swing component
511 SwingTerminal
.this.swing
= new SwingComponent(component
);
513 // Hang onto top and left for drawing.
514 Insets insets
= SwingTerminal
.this.swing
.getInsets();
515 SwingTerminal
.this.left
= insets
.left
;
516 SwingTerminal
.this.top
= insets
.top
;
518 // Load the font so that we can set sessionInfo.
521 // Get the default cols x rows and set component size
523 SwingTerminal
.this.sessionInfo
=
524 new SwingSessionInfo(SwingTerminal
.this.swing
,
525 SwingTerminal
.this.textWidth
,
526 SwingTerminal
.this.textHeight
);
529 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
531 } catch (InterruptedException e
) {
535 this.listener
= listener
;
539 eventQueue
= new ArrayList
<TInputEvent
>();
541 // Add listeners to Swing.
542 swing
.addKeyListener(this);
543 swing
.addWindowListener(this);
544 swing
.addComponentListener(this);
545 swing
.addMouseListener(this);
546 swing
.addMouseMotionListener(this);
547 swing
.addMouseWheelListener(this);
550 // ------------------------------------------------------------------------
551 // LogicalScreen ----------------------------------------------------------
552 // ------------------------------------------------------------------------
555 * Set the window title.
557 * @param title the new title
560 public void setTitle(final String title
) {
561 swing
.setTitle(title
);
565 * Push the logical screen to the physical device.
568 public void flushPhysical() {
569 // See if it is time to flip the blink time.
570 long nowTime
= System
.currentTimeMillis();
571 if (nowTime
>= blinkMillis
+ lastBlinkTime
) {
572 lastBlinkTime
= nowTime
;
573 cursorBlinkVisible
= !cursorBlinkVisible
;
574 // System.err.println("New lastBlinkTime: " + lastBlinkTime);
577 if ((swing
.getFrame() != null)
578 && (swing
.getBufferStrategy() != null)
585 * Under Windows and Mac (I think?), there was a problem
586 * with the screen not updating on the initial load.
587 * Adding clearPhysical() below "fixed" it, but at a
588 * horrible performance penalty on Linux which I am no
589 * longer willing to accept.
591 * Fix this in the "right" way for Windows/OSX such that
592 * the entire screen does not require a full redraw.
596 } while (swing
.getBufferStrategy().contentsRestored());
598 swing
.getBufferStrategy().show();
599 Toolkit
.getDefaultToolkit().sync();
600 } while (swing
.getBufferStrategy().contentsLost());
603 // Non-triple-buffered, call drawToSwing() once
608 // ------------------------------------------------------------------------
609 // TerminalReader ---------------------------------------------------------
610 // ------------------------------------------------------------------------
613 * Check if there are events in the queue.
615 * @return if true, getEvents() has something to return to the backend
617 public boolean hasEvents() {
618 synchronized (eventQueue
) {
619 return (eventQueue
.size() > 0);
624 * Return any events in the IO queue.
626 * @param queue list to append new events to
628 public void getEvents(final List
<TInputEvent
> queue
) {
629 synchronized (eventQueue
) {
630 if (eventQueue
.size() > 0) {
631 synchronized (queue
) {
632 queue
.addAll(eventQueue
);
640 * Restore terminal to normal state.
642 public void closeTerminal() {
647 * Set listener to a different Object.
649 * @param listener the new listening object that run() wakes up on new
652 public void setListener(final Object listener
) {
653 this.listener
= listener
;
657 * Reload options from System properties.
659 public void reloadOptions() {
660 // Figure out my cursor style.
661 String cursorStyleString
= System
.getProperty(
662 "jexer.Swing.cursorStyle", "underline").toLowerCase();
663 if (cursorStyleString
.equals("underline")) {
664 cursorStyle
= CursorStyle
.UNDERLINE
;
665 } else if (cursorStyleString
.equals("outline")) {
666 cursorStyle
= CursorStyle
.OUTLINE
;
667 } else if (cursorStyleString
.equals("block")) {
668 cursorStyle
= CursorStyle
.BLOCK
;
669 } else if (cursorStyleString
.equals("verticalbar")) {
670 cursorStyle
= CursorStyle
.VERTICAL_BAR
;
673 // Pull the system property for triple buffering.
674 if (System
.getProperty("jexer.Swing.tripleBuffer",
675 "true").equals("true")
677 SwingComponent
.tripleBuffer
= true;
679 SwingComponent
.tripleBuffer
= false;
683 setCustomSystemColors();
686 // ------------------------------------------------------------------------
687 // SwingTerminal ----------------------------------------------------------
688 // ------------------------------------------------------------------------
691 * Get the width of a character cell in pixels.
693 * @return the width in pixels of a character cell
695 public int getTextWidth() {
700 * Get the height of a character cell in pixels.
702 * @return the height in pixels of a character cell
704 public int getTextHeight() {
709 * Setup Swing colors to match DOS color palette.
711 private static void setDOSColors() {
715 MYBLACK
= new Color(0x00, 0x00, 0x00);
716 MYRED
= new Color(0xa8, 0x00, 0x00);
717 MYGREEN
= new Color(0x00, 0xa8, 0x00);
718 MYYELLOW
= new Color(0xa8, 0x54, 0x00);
719 MYBLUE
= new Color(0x00, 0x00, 0xa8);
720 MYMAGENTA
= new Color(0xa8, 0x00, 0xa8);
721 MYCYAN
= new Color(0x00, 0xa8, 0xa8);
722 MYWHITE
= new Color(0xa8, 0xa8, 0xa8);
723 MYBOLD_BLACK
= new Color(0x54, 0x54, 0x54);
724 MYBOLD_RED
= new Color(0xfc, 0x54, 0x54);
725 MYBOLD_GREEN
= new Color(0x54, 0xfc, 0x54);
726 MYBOLD_YELLOW
= new Color(0xfc, 0xfc, 0x54);
727 MYBOLD_BLUE
= new Color(0x54, 0x54, 0xfc);
728 MYBOLD_MAGENTA
= new Color(0xfc, 0x54, 0xfc);
729 MYBOLD_CYAN
= new Color(0x54, 0xfc, 0xfc);
730 MYBOLD_WHITE
= new Color(0xfc, 0xfc, 0xfc);
736 * Setup Swing colors to match those provided in system properties.
738 private static void setCustomSystemColors() {
739 synchronized (SwingTerminal
.class) {
740 MYBLACK
= getCustomColor("jexer.Swing.color0", MYBLACK
);
741 MYRED
= getCustomColor("jexer.Swing.color1", MYRED
);
742 MYGREEN
= getCustomColor("jexer.Swing.color2", MYGREEN
);
743 MYYELLOW
= getCustomColor("jexer.Swing.color3", MYYELLOW
);
744 MYBLUE
= getCustomColor("jexer.Swing.color4", MYBLUE
);
745 MYMAGENTA
= getCustomColor("jexer.Swing.color5", MYMAGENTA
);
746 MYCYAN
= getCustomColor("jexer.Swing.color6", MYCYAN
);
747 MYWHITE
= getCustomColor("jexer.Swing.color7", MYWHITE
);
748 MYBOLD_BLACK
= getCustomColor("jexer.Swing.color8", MYBOLD_BLACK
);
749 MYBOLD_RED
= getCustomColor("jexer.Swing.color9", MYBOLD_RED
);
750 MYBOLD_GREEN
= getCustomColor("jexer.Swing.color10", MYBOLD_GREEN
);
751 MYBOLD_YELLOW
= getCustomColor("jexer.Swing.color11", MYBOLD_YELLOW
);
752 MYBOLD_BLUE
= getCustomColor("jexer.Swing.color12", MYBOLD_BLUE
);
753 MYBOLD_MAGENTA
= getCustomColor("jexer.Swing.color13", MYBOLD_MAGENTA
);
754 MYBOLD_CYAN
= getCustomColor("jexer.Swing.color14", MYBOLD_CYAN
);
755 MYBOLD_WHITE
= getCustomColor("jexer.Swing.color15", MYBOLD_WHITE
);
760 * Setup one Swing color to match the RGB value provided in system
763 * @param key the system property key
764 * @param defaultColor the default color to return if key is not set, or
766 * @return a color from the RGB string, or defaultColor
768 private static Color
getCustomColor(final String key
,
769 final Color defaultColor
) {
771 String rgb
= System
.getProperty(key
);
775 if (rgb
.startsWith("#")) {
776 rgb
= rgb
.substring(1);
780 rgbInt
= Integer
.parseInt(rgb
, 16);
781 } catch (NumberFormatException e
) {
784 Color color
= new Color((rgbInt
& 0xFF0000) >>> 16,
785 (rgbInt
& 0x00FF00) >>> 8,
786 (rgbInt
& 0x0000FF));
792 * Get the number of millis to wait before switching the blink from
793 * visible to invisible.
795 * @return the number of milli to wait before switching the blink from
796 * visible to invisible
798 public long getBlinkMillis() {
803 * Get the current status of the blink flag.
805 * @return true if the cursor and blinking text should be visible
807 public boolean getCursorBlinkVisible() {
808 return cursorBlinkVisible
;
812 * Get the font size in points.
814 * @return font size in points
816 public int getFontSize() {
821 * Set the font size in points.
823 * @param fontSize font size in points
825 public void setFontSize(final int fontSize
) {
826 this.fontSize
= fontSize
;
827 Font newFont
= font
.deriveFont((float) fontSize
);
832 * Set to a new font, and resize the screen to match its dimensions.
834 * @param font the new font
836 public void setFont(final Font font
) {
837 if (!SwingUtilities
.isEventDispatchThread()) {
838 // Not in the Swing thread: force this inside the Swing thread.
840 SwingUtilities
.invokeAndWait(new Runnable() {
842 synchronized (this) {
843 SwingTerminal
.this.font
= font
;
846 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
847 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
848 resizeToScreen(true);
852 } catch (InterruptedException e
) {
854 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
858 synchronized (this) {
859 SwingTerminal
.this.font
= font
;
862 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
863 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
864 resizeToScreen(true);
870 * Get the font this screen was last set to.
874 public Font
getFont() {
879 * Set the font to Terminus, the best all-around font for both CP437 and
882 public void setDefaultFont() {
884 ClassLoader loader
= Thread
.currentThread().getContextClassLoader();
885 InputStream in
= loader
.getResourceAsStream(FONTFILE
);
886 Font terminusRoot
= Font
.createFont(Font
.TRUETYPE_FONT
, in
);
887 Font terminus
= terminusRoot
.deriveFont(Font
.PLAIN
, fontSize
);
889 } catch (java
.awt
.FontFormatException e
) {
891 font
= new Font(Font
.MONOSPACED
, Font
.PLAIN
, fontSize
);
892 } catch (java
.io
.IOException e
) {
894 font
= new Font(Font
.MONOSPACED
, Font
.PLAIN
, fontSize
);
901 * Get the X text adjustment.
903 * @return X text adjustment
905 public int getTextAdjustX() {
910 * Set the X text adjustment.
912 * @param textAdjustX the X text adjustment
914 public void setTextAdjustX(final int textAdjustX
) {
915 synchronized (this) {
916 this.textAdjustX
= textAdjustX
;
917 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
918 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
924 * Get the Y text adjustment.
926 * @return Y text adjustment
928 public int getTextAdjustY() {
933 * Set the Y text adjustment.
935 * @param textAdjustY the Y text adjustment
937 public void setTextAdjustY(final int textAdjustY
) {
938 synchronized (this) {
939 this.textAdjustY
= textAdjustY
;
940 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
941 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
947 * Get the height text adjustment.
949 * @return height text adjustment
951 public int getTextAdjustHeight() {
952 return textAdjustHeight
;
956 * Set the height text adjustment.
958 * @param textAdjustHeight the height text adjustment
960 public void setTextAdjustHeight(final int textAdjustHeight
) {
961 synchronized (this) {
962 this.textAdjustHeight
= textAdjustHeight
;
963 textHeight
= fontTextHeight
+ textAdjustHeight
;
964 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
965 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
971 * Get the width text adjustment.
973 * @return width text adjustment
975 public int getTextAdjustWidth() {
976 return textAdjustWidth
;
980 * Set the width text adjustment.
982 * @param textAdjustWidth the width text adjustment
984 public void setTextAdjustWidth(final int textAdjustWidth
) {
985 synchronized (this) {
986 this.textAdjustWidth
= textAdjustWidth
;
987 textWidth
= fontTextWidth
+ textAdjustWidth
;
988 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
989 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
995 * Convert a CellAttributes foreground color to an Swing Color.
997 * @param attr the text attributes
998 * @return the Swing Color
1000 public static Color
attrToForegroundColor(final CellAttributes attr
) {
1001 int rgb
= attr
.getForeColorRGB();
1003 int red
= (rgb
>> 16) & 0xFF;
1004 int green
= (rgb
>> 8) & 0xFF;
1005 int blue
= rgb
& 0xFF;
1007 return new Color(red
, green
, blue
);
1010 if (attr
.isBold()) {
1011 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
1012 return MYBOLD_BLACK
;
1013 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
1015 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
1017 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
1018 return MYBOLD_GREEN
;
1019 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
1020 return MYBOLD_YELLOW
;
1021 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
1023 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
1024 return MYBOLD_MAGENTA
;
1025 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
1026 return MYBOLD_WHITE
;
1029 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
1031 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
1033 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
1035 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
1037 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
1039 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
1041 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
1043 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
1047 throw new IllegalArgumentException("Invalid color: " +
1048 attr
.getForeColor().getValue());
1052 * Convert a CellAttributes background color to an Swing Color.
1054 * @param attr the text attributes
1055 * @return the Swing Color
1057 public static Color
attrToBackgroundColor(final CellAttributes attr
) {
1058 int rgb
= attr
.getBackColorRGB();
1060 int red
= (rgb
>> 16) & 0xFF;
1061 int green
= (rgb
>> 8) & 0xFF;
1062 int blue
= rgb
& 0xFF;
1064 return new Color(red
, green
, blue
);
1067 if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLACK
)) {
1069 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.RED
)) {
1071 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLUE
)) {
1073 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.GREEN
)) {
1075 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.YELLOW
)) {
1077 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.CYAN
)) {
1079 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
1081 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.WHITE
)) {
1084 throw new IllegalArgumentException("Invalid color: " +
1085 attr
.getBackColor().getValue());
1089 * Figure out what textAdjustX, textAdjustY, textAdjustHeight, and
1090 * textAdjustWidth should be, based on the location of a vertical bar and
1093 private void getFontAdjustments() {
1094 BufferedImage image
= null;
1096 // What SHOULD happen is that the topmost/leftmost white pixel is at
1097 // position (gr2x, gr2y). But it might also be off by a pixel in
1098 // either direction.
1100 Graphics2D gr2
= null;
1103 image
= new BufferedImage(fontTextWidth
* 2, fontTextHeight
* 2,
1104 BufferedImage
.TYPE_INT_ARGB
);
1106 gr2
= image
.createGraphics();
1107 gr2
.setFont(swing
.getFont());
1108 gr2
.setColor(java
.awt
.Color
.BLACK
);
1109 gr2
.fillRect(0, 0, fontTextWidth
* 2, fontTextHeight
* 2);
1110 gr2
.setColor(java
.awt
.Color
.WHITE
);
1111 char [] chars
= new char[1];
1112 chars
[0] = jexer
.bits
.GraphicsChars
.SINGLE_BAR
;
1113 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ fontTextHeight
- maxDescent
);
1114 chars
[0] = jexer
.bits
.GraphicsChars
.VERTICAL_BAR
;
1115 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ fontTextHeight
- maxDescent
);
1118 int top
= fontTextHeight
* 2;
1120 int left
= fontTextWidth
* 2;
1124 textAdjustHeight
= 0;
1125 textAdjustWidth
= 0;
1127 for (int x
= 0; x
< fontTextWidth
* 2; x
++) {
1128 for (int y
= 0; y
< fontTextHeight
* 2; y
++) {
1131 System.err.println("H X: " + x + " Y: " + y + " " +
1132 image.getRGB(x, y));
1135 if ((image
.getRGB(x
, y
) & 0xFFFFFF) != 0) {
1136 // Pixel is present.
1153 textAdjustX
= (gr2x
- left
);
1154 textAdjustWidth
= fontTextWidth
- (right
- left
+ 1);
1157 textAdjustY
= (gr2y
- top
);
1158 textAdjustHeight
= fontTextHeight
- (bottom
- top
+ 1);
1160 // System.err.println("top " + top + " bottom " + bottom);
1161 // System.err.println("left " + left + " right " + right);
1163 // Special case: do not believe fonts that claim to be wider than
1165 if (fontTextWidth
>= fontTextHeight
) {
1167 textAdjustWidth
= 0;
1168 fontTextWidth
= fontTextHeight
/ 2;
1173 * Figure out my font dimensions. This code path works OK for the JFrame
1174 * case, and can be called immediately after JFrame creation.
1176 private void getFontDimensions() {
1177 swing
.setFont(font
);
1178 Graphics gr
= swing
.getGraphics();
1182 getFontDimensions(gr
);
1186 * Figure out my font dimensions. This code path is needed to lazy-load
1187 * the information inside paint().
1189 * @param gr Graphics object to use
1191 private void getFontDimensions(final Graphics gr
) {
1192 swing
.setFont(font
);
1193 FontMetrics fm
= gr
.getFontMetrics();
1194 maxDescent
= fm
.getMaxDescent();
1195 Rectangle2D bounds
= fm
.getMaxCharBounds(gr
);
1196 int leading
= fm
.getLeading();
1197 fontTextWidth
= (int)Math
.round(bounds
.getWidth());
1198 // fontTextHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
1200 // This produces the same number, but works better for ugly
1202 fontTextHeight
= fm
.getMaxAscent() + maxDescent
- leading
;
1204 getFontAdjustments();
1205 textHeight
= fontTextHeight
+ textAdjustHeight
;
1206 textWidth
= fontTextWidth
+ textAdjustWidth
;
1208 if (sessionInfo
!= null) {
1209 sessionInfo
.setTextCellDimensions(textWidth
, textHeight
);
1211 gotFontDimensions
= true;
1215 * Resize the physical screen to match the logical screen dimensions.
1217 * @param resizeComponent if true, resize the Swing component
1219 private void resizeToScreen(final boolean resizeComponent
) {
1220 if (resizeComponent
) {
1221 swing
.setDimensions(textWidth
* width
, textHeight
* height
);
1227 * Resize the physical screen to match the logical screen dimensions.
1230 public void resizeToScreen() {
1231 resizeToScreen(false);
1235 * Draw one cell's image to the screen.
1237 * @param gr the Swing Graphics context
1238 * @param cell the Cell to draw
1239 * @param xPixel the x-coordinate to render to. 0 means the
1240 * left-most pixel column.
1241 * @param yPixel the y-coordinate to render to. 0 means the top-most
1244 private void drawImage(final Graphics gr
, final Cell cell
,
1245 final int xPixel
, final int yPixel
) {
1248 System.err.println("drawImage(): " + xPixel + " " + yPixel +
1252 // Draw the background rectangle, then the foreground character.
1253 assert (cell
.isImage());
1255 // Enable anti-aliasing
1256 if (gr
instanceof Graphics2D
) {
1257 ((Graphics2D
) gr
).setRenderingHint(RenderingHints
.KEY_ANTIALIASING
,
1258 RenderingHints
.VALUE_ANTIALIAS_ON
);
1259 ((Graphics2D
) gr
).setRenderingHint(RenderingHints
.KEY_RENDERING
,
1260 RenderingHints
.VALUE_RENDER_QUALITY
);
1263 gr
.setColor(cell
.getBackground());
1264 gr
.fillRect(xPixel
, yPixel
, textWidth
, textHeight
);
1266 BufferedImage image
= cell
.getImage();
1267 if (image
!= null) {
1268 if (swing
.getFrame() != null) {
1269 gr
.drawImage(image
, xPixel
, yPixel
, getTextWidth(),
1270 getTextHeight(), swing
.getFrame());
1272 gr
.drawImage(image
, xPixel
, yPixel
, getTextWidth(),
1273 getTextHeight(),swing
.getComponent());
1280 * Draw one glyph to the screen.
1282 * @param gr the Swing Graphics context
1283 * @param cell the Cell to draw
1284 * @param xPixel the x-coordinate to render to. 0 means the
1285 * left-most pixel column.
1286 * @param yPixel the y-coordinate to render to. 0 means the top-most
1289 private void drawGlyph(final Graphics gr
, final Cell cell
,
1290 final int xPixel
, final int yPixel
) {
1293 System.err.println("drawGlyph(): " + xPixel + " " + yPixel +
1297 BufferedImage image
= null;
1298 if (cell
.isBlink() && !cursorBlinkVisible
) {
1299 image
= glyphCacheBlink
.get(cell
);
1301 image
= glyphCache
.get(cell
);
1303 if (image
!= null) {
1304 if (swing
.getFrame() != null) {
1305 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
1307 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
1312 // Generate glyph and draw it.
1313 Graphics2D gr2
= null;
1316 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
1317 image
= new BufferedImage(textWidth
, textHeight
,
1318 BufferedImage
.TYPE_INT_ARGB
);
1319 gr2
= image
.createGraphics();
1320 gr2
.setFont(swing
.getFont());
1324 gr2
= (Graphics2D
) gr
;
1327 Cell cellColor
= new Cell(cell
);
1329 // Check for reverse
1330 if (cell
.isReverse()) {
1331 cellColor
.setForeColor(cell
.getBackColor());
1332 cellColor
.setBackColor(cell
.getForeColor());
1335 // Enable anti-aliasing
1336 if (gr
instanceof Graphics2D
) {
1337 ((Graphics2D
) gr
).setRenderingHint(RenderingHints
.KEY_ANTIALIASING
,
1338 RenderingHints
.VALUE_ANTIALIAS_ON
);
1339 ((Graphics2D
) gr
).setRenderingHint(RenderingHints
.KEY_RENDERING
,
1340 RenderingHints
.VALUE_RENDER_QUALITY
);
1343 // Draw the background rectangle, then the foreground character.
1344 gr2
.setColor(attrToBackgroundColor(cellColor
));
1345 gr2
.fillRect(gr2x
, gr2y
, textWidth
, textHeight
);
1348 // Handle blink and underline
1350 || (cell
.isBlink() && cursorBlinkVisible
)
1352 gr2
.setColor(attrToForegroundColor(cellColor
));
1353 char [] chars
= Character
.toChars(cell
.getChar());
1354 gr2
.drawChars(chars
, 0, chars
.length
, gr2x
+ textAdjustX
,
1355 gr2y
+ textHeight
- maxDescent
+ textAdjustY
);
1357 if (cell
.isUnderline()) {
1358 gr2
.fillRect(gr2x
, gr2y
+ textHeight
- 2, textWidth
, 2);
1362 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
1365 // We need a new key that will not be mutated by
1367 Cell key
= new Cell(cell
);
1368 if (cell
.isBlink() && !cursorBlinkVisible
) {
1369 glyphCacheBlink
.put(key
, image
);
1371 glyphCache
.put(key
, image
);
1374 if (swing
.getFrame() != null) {
1375 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
1377 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
1384 * Check if the cursor is visible, and if so draw it.
1386 * @param gr the Swing Graphics context
1388 private void drawCursor(final Graphics gr
) {
1393 && (cursorY
<= height
- 1)
1394 && (cursorX
<= width
- 1)
1395 && cursorBlinkVisible
1397 int xPixel
= cursorX
* textWidth
+ left
;
1398 int yPixel
= cursorY
* textHeight
+ top
;
1399 Cell lCell
= logical
[cursorX
][cursorY
];
1400 int cursorWidth
= textWidth
;
1401 switch (lCell
.getWidth()) {
1410 xPixel
-= textWidth
;
1413 gr
.setColor(attrToForegroundColor(lCell
));
1414 switch (cursorStyle
) {
1418 gr
.fillRect(xPixel
, yPixel
+ textHeight
- 2, cursorWidth
, 2);
1421 gr
.fillRect(xPixel
, yPixel
, cursorWidth
, textHeight
);
1424 gr
.drawRect(xPixel
, yPixel
, cursorWidth
- 1, textHeight
- 1);
1427 gr
.fillRect(xPixel
, yPixel
, 2, textHeight
);
1434 * Reset the blink timer.
1436 private void resetBlinkTimer() {
1437 lastBlinkTime
= System
.currentTimeMillis();
1438 cursorBlinkVisible
= true;
1442 * Paint redraws the whole screen.
1444 * @param gr the Swing Graphics context
1446 public void paint(final Graphics gr
) {
1448 if (gotFontDimensions
== false) {
1449 // Lazy-load the text width/height
1450 getFontDimensions(gr
);
1452 System.err.println("textWidth " + textWidth +
1453 " textHeight " + textHeight);
1454 System.err.println("FONT: " + swing.getFont() + " font " + font);
1458 if ((swing
.getFrame() != null)
1459 && (swing
.getBufferStrategy() != null)
1460 && (SwingUtilities
.isEventDispatchThread())
1462 // System.err.println("paint(), skip first paint on swing thread");
1467 int xCellMax
= width
;
1469 int yCellMax
= height
;
1471 Rectangle bounds
= gr
.getClipBounds();
1472 if (bounds
!= null) {
1473 // Only update what is in the bounds
1474 xCellMin
= textColumn(bounds
.x
);
1475 xCellMax
= textColumn(bounds
.x
+ bounds
.width
) + 1;
1476 if (xCellMax
> width
) {
1479 if (xCellMin
>= xCellMax
) {
1480 xCellMin
= xCellMax
- 2;
1485 yCellMin
= textRow(bounds
.y
);
1486 yCellMax
= textRow(bounds
.y
+ bounds
.height
) + 1;
1487 if (yCellMax
> height
) {
1490 if (yCellMin
>= yCellMax
) {
1491 yCellMin
= yCellMax
- 2;
1497 // We need a total repaint
1498 reallyCleared
= true;
1501 // Prevent updates to the screen's data from the TApplication
1503 synchronized (this) {
1506 System.err.printf("bounds %s X %d %d Y %d %d\n",
1507 bounds, xCellMin, xCellMax, yCellMin, yCellMax);
1510 for (int y
= yCellMin
; y
< yCellMax
; y
++) {
1511 for (int x
= xCellMin
; x
< xCellMax
; x
++) {
1513 int xPixel
= x
* textWidth
+ left
;
1514 int yPixel
= y
* textHeight
+ top
;
1516 Cell lCell
= logical
[x
][y
];
1517 Cell pCell
= physical
[x
][y
];
1519 if (!lCell
.equals(pCell
)
1522 || (swing
.getFrame() == null)) {
1524 if (lCell
.isImage()) {
1525 drawImage(gr
, lCell
, xPixel
, yPixel
);
1527 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
1530 // Physical is always updated
1531 physical
[x
][y
].setTo(lCell
);
1537 reallyCleared
= false;
1538 } // synchronized (this)
1542 * Restore terminal to normal state.
1544 public void shutdown() {
1549 * Push the logical screen to the physical device.
1551 private void drawToSwing() {
1554 System.err.printf("drawToSwing(): reallyCleared %s dirty %s\n",
1555 reallyCleared, dirty);
1558 // If reallyCleared is set, we have to draw everything.
1559 if ((swing
.getFrame() != null)
1560 && (swing
.getBufferStrategy() != null)
1561 && (reallyCleared
== true)
1563 // Triple-buffering: we have to redraw everything on this thread.
1564 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1567 swing
.getBufferStrategy().show();
1568 Toolkit
.getDefaultToolkit().sync();
1570 } else if (((swing
.getFrame() != null)
1571 && (swing
.getBufferStrategy() == null))
1572 || (reallyCleared
== true)
1574 // Repaint everything on the Swing thread.
1575 // System.err.println("REPAINT ALL");
1580 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
1581 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1583 synchronized (this) {
1584 for (int y
= 0; y
< height
; y
++) {
1585 for (int x
= 0; x
< width
; x
++) {
1586 Cell lCell
= logical
[x
][y
];
1587 Cell pCell
= physical
[x
][y
];
1589 int xPixel
= x
* textWidth
+ left
;
1590 int yPixel
= y
* textHeight
+ top
;
1592 if (!lCell
.equals(pCell
)
1596 || (lCell
.isBlink())
1598 if (lCell
.isImage()) {
1599 drawImage(gr
, lCell
, xPixel
, yPixel
);
1601 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
1603 physical
[x
][y
].setTo(lCell
);
1608 } // synchronized (this)
1611 swing
.getBufferStrategy().show();
1612 Toolkit
.getDefaultToolkit().sync();
1616 // Swing thread version: request a repaint, but limit it to the area
1617 // that has changed.
1619 // Find the minimum-size damaged region.
1620 int xMin
= swing
.getWidth();
1622 int yMin
= swing
.getHeight();
1625 synchronized (this) {
1626 for (int y
= 0; y
< height
; y
++) {
1627 for (int x
= 0; x
< width
; x
++) {
1628 Cell lCell
= logical
[x
][y
];
1629 Cell pCell
= physical
[x
][y
];
1631 int xPixel
= x
* textWidth
+ left
;
1632 int yPixel
= y
* textHeight
+ top
;
1634 if (!lCell
.equals(pCell
)
1640 if (xPixel
< xMin
) {
1643 if (xPixel
+ textWidth
> xMax
) {
1644 xMax
= xPixel
+ textWidth
;
1646 if (yPixel
< yMin
) {
1649 if (yPixel
+ textHeight
> yMax
) {
1650 yMax
= yPixel
+ textHeight
;
1656 if (xMin
+ textWidth
>= xMax
) {
1659 if (yMin
+ textHeight
>= yMax
) {
1663 // Repaint the desired area
1665 System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax,
1669 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
1670 // This path should never be taken, but is left here for
1672 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1673 Rectangle bounds
= new Rectangle(xMin
, yMin
, xMax
- xMin
,
1678 swing
.getBufferStrategy().show();
1679 Toolkit
.getDefaultToolkit().sync();
1681 // Repaint on the Swing thread.
1682 swing
.repaint(xMin
, yMin
, xMax
- xMin
, yMax
- yMin
);
1687 * Convert pixel column position to text cell column position.
1689 * @param x pixel column position
1690 * @return text cell column position
1692 public int textColumn(final int x
) {
1693 int column
= ((x
- left
) / textWidth
);
1697 if (column
> width
- 1) {
1704 * Convert pixel row position to text cell row position.
1706 * @param y pixel row position
1707 * @return text cell row position
1709 public int textRow(final int y
) {
1710 int row
= ((y
- top
) / textHeight
);
1714 if (row
> height
- 1) {
1721 * Getter for sessionInfo.
1723 * @return the SessionInfo
1725 public SessionInfo
getSessionInfo() {
1730 * Getter for the underlying Swing component.
1732 * @return the SwingComponent
1734 public SwingComponent
getSwingComponent() {
1738 // ------------------------------------------------------------------------
1739 // KeyListener ------------------------------------------------------------
1740 // ------------------------------------------------------------------------
1743 * Pass Swing keystrokes into the event queue.
1745 * @param key keystroke received
1747 public void keyReleased(final KeyEvent key
) {
1748 // Ignore release events
1752 * Pass Swing keystrokes into the event queue.
1754 * @param key keystroke received
1756 public void keyTyped(final KeyEvent key
) {
1757 // Ignore typed events
1761 * Pass Swing keystrokes into the event queue.
1763 * @param key keystroke received
1765 public void keyPressed(final KeyEvent key
) {
1766 boolean alt
= false;
1767 boolean shift
= false;
1768 boolean ctrl
= false;
1770 boolean isKey
= false;
1771 if (key
.isActionKey()) {
1774 ch
= key
.getKeyChar();
1776 alt
= key
.isAltDown();
1777 ctrl
= key
.isControlDown();
1778 shift
= key
.isShiftDown();
1781 System.err.printf("Swing Key: %s\n", key);
1782 System.err.printf(" isKey: %s\n", isKey);
1783 System.err.printf(" alt: %s\n", alt);
1784 System.err.printf(" ctrl: %s\n", ctrl);
1785 System.err.printf(" shift: %s\n", shift);
1786 System.err.printf(" ch: %s\n", ch);
1789 // Special case: not return the bare modifier presses
1790 switch (key
.getKeyCode()) {
1791 case KeyEvent
.VK_ALT
:
1793 case KeyEvent
.VK_ALT_GRAPH
:
1795 case KeyEvent
.VK_CONTROL
:
1797 case KeyEvent
.VK_SHIFT
:
1799 case KeyEvent
.VK_META
:
1805 TKeypress keypress
= null;
1807 switch (key
.getKeyCode()) {
1808 case KeyEvent
.VK_F1
:
1809 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
1812 case KeyEvent
.VK_F2
:
1813 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
1816 case KeyEvent
.VK_F3
:
1817 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
1820 case KeyEvent
.VK_F4
:
1821 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
1824 case KeyEvent
.VK_F5
:
1825 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
1828 case KeyEvent
.VK_F6
:
1829 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
1832 case KeyEvent
.VK_F7
:
1833 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
1836 case KeyEvent
.VK_F8
:
1837 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
1840 case KeyEvent
.VK_F9
:
1841 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
1844 case KeyEvent
.VK_F10
:
1845 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
1848 case KeyEvent
.VK_F11
:
1849 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
1852 case KeyEvent
.VK_F12
:
1853 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
1856 case KeyEvent
.VK_HOME
:
1857 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
1860 case KeyEvent
.VK_END
:
1861 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
1864 case KeyEvent
.VK_PAGE_UP
:
1865 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
1868 case KeyEvent
.VK_PAGE_DOWN
:
1869 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
1872 case KeyEvent
.VK_INSERT
:
1873 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
1876 case KeyEvent
.VK_DELETE
:
1877 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
1880 case KeyEvent
.VK_RIGHT
:
1881 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
1884 case KeyEvent
.VK_LEFT
:
1885 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
1888 case KeyEvent
.VK_UP
:
1889 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
1892 case KeyEvent
.VK_DOWN
:
1893 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
1896 case KeyEvent
.VK_TAB
:
1897 // Special case: distinguish TAB vs BTAB
1899 keypress
= kbShiftTab
;
1904 case KeyEvent
.VK_ENTER
:
1905 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
1908 case KeyEvent
.VK_ESCAPE
:
1909 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
1912 case KeyEvent
.VK_BACK_SPACE
:
1913 keypress
= kbBackspace
;
1916 // Unsupported, ignore
1921 if (keypress
== null) {
1924 // Disambiguate ^H from Backspace.
1925 if (KeyEvent
.getKeyText(key
.getKeyCode()).equals("H")) {
1927 keypress
= kbBackspace
;
1929 // We are emulating Xterm here, where the backspace key
1930 // on the keyboard returns ^?.
1931 keypress
= kbBackspaceDel
;
1945 keypress
= kbShiftTab
;
1954 if (!alt
&& ctrl
&& !shift
) {
1955 // Control character, replace ch with 'A', 'B', etc.
1956 ch
= KeyEvent
.getKeyText(key
.getKeyCode()).charAt(0);
1958 // Not a special key, put it together
1959 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
1963 // Save it and we are done.
1964 synchronized (eventQueue
) {
1965 eventQueue
.add(new TKeypressEvent(keypress
));
1968 if (listener
!= null) {
1969 synchronized (listener
) {
1970 listener
.notifyAll();
1975 // ------------------------------------------------------------------------
1976 // WindowListener ---------------------------------------------------------
1977 // ------------------------------------------------------------------------
1980 * Pass window events into the event queue.
1982 * @param event window event received
1984 public void windowActivated(final WindowEvent event
) {
1985 // Force a total repaint
1986 synchronized (this) {
1992 * Pass window events into the event queue.
1994 * @param event window event received
1996 public void windowClosed(final WindowEvent event
) {
2001 * Pass window events into the event queue.
2003 * @param event window event received
2005 public void windowClosing(final WindowEvent event
) {
2006 // Drop a cmBackendDisconnect and walk away
2007 synchronized (eventQueue
) {
2008 eventQueue
.add(new TCommandEvent(cmBackendDisconnect
));
2011 if (listener
!= null) {
2012 synchronized (listener
) {
2013 listener
.notifyAll();
2019 * Pass window events into the event queue.
2021 * @param event window event received
2023 public void windowDeactivated(final WindowEvent event
) {
2028 * Pass window events into the event queue.
2030 * @param event window event received
2032 public void windowDeiconified(final WindowEvent event
) {
2037 * Pass window events into the event queue.
2039 * @param event window event received
2041 public void windowIconified(final WindowEvent event
) {
2046 * Pass window events into the event queue.
2048 * @param event window event received
2050 public void windowOpened(final WindowEvent event
) {
2054 // ------------------------------------------------------------------------
2055 // ComponentListener ------------------------------------------------------
2056 // ------------------------------------------------------------------------
2059 * Pass component events into the event queue.
2061 * @param event component event received
2063 public void componentHidden(final ComponentEvent event
) {
2068 * Pass component events into the event queue.
2070 * @param event component event received
2072 public void componentShown(final ComponentEvent event
) {
2077 * Pass component events into the event queue.
2079 * @param event component event received
2081 public void componentMoved(final ComponentEvent event
) {
2086 * Pass component events into the event queue.
2088 * @param event component event received
2090 public void componentResized(final ComponentEvent event
) {
2091 if (gotFontDimensions
== false) {
2092 // We are still waiting to get font information. Don't pass a
2094 // System.err.println("size " + swing.getComponent().getSize());
2098 if (sessionInfo
== null) {
2099 // This is the initial component resize in construction, bail
2104 // Drop a new TResizeEvent into the queue
2105 sessionInfo
.queryWindowSize();
2106 synchronized (eventQueue
) {
2107 TResizeEvent windowResize
= new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
2108 sessionInfo
.getWindowWidth(), sessionInfo
.getWindowHeight());
2109 eventQueue
.add(windowResize
);
2112 System.err.println("Add resize event: " + windowResize.getWidth() +
2113 " x " + windowResize.getHeight());
2116 if (listener
!= null) {
2117 synchronized (listener
) {
2118 listener
.notifyAll();
2123 // ------------------------------------------------------------------------
2124 // MouseMotionListener ----------------------------------------------------
2125 // ------------------------------------------------------------------------
2128 * Pass mouse events into the event queue.
2130 * @param mouse mouse event received
2132 public void mouseDragged(final MouseEvent mouse
) {
2133 int modifiers
= mouse
.getModifiersEx();
2134 boolean eventMouse1
= false;
2135 boolean eventMouse2
= false;
2136 boolean eventMouse3
= false;
2137 boolean eventAlt
= false;
2138 boolean eventCtrl
= false;
2139 boolean eventShift
= false;
2141 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2144 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2147 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2150 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2153 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2156 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2160 mouse1
= eventMouse1
;
2161 mouse2
= eventMouse2
;
2162 mouse3
= eventMouse3
;
2163 int x
= textColumn(mouse
.getX());
2164 int y
= textRow(mouse
.getY());
2166 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
2167 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false,
2168 eventAlt
, eventCtrl
, eventShift
);
2170 synchronized (eventQueue
) {
2171 eventQueue
.add(mouseEvent
);
2174 if (listener
!= null) {
2175 synchronized (listener
) {
2176 listener
.notifyAll();
2182 * Pass mouse events into the event queue.
2184 * @param mouse mouse event received
2186 public void mouseMoved(final MouseEvent mouse
) {
2187 int x
= textColumn(mouse
.getX());
2188 int y
= textRow(mouse
.getY());
2189 if ((x
== oldMouseX
) && (y
== oldMouseY
)) {
2190 // Bail out, we've moved some pixels but not a whole text cell.
2196 boolean eventAlt
= false;
2197 boolean eventCtrl
= false;
2198 boolean eventShift
= false;
2200 int modifiers
= mouse
.getModifiersEx();
2201 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2204 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2207 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2211 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
2212 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false,
2213 eventAlt
, eventCtrl
, eventShift
);
2215 synchronized (eventQueue
) {
2216 eventQueue
.add(mouseEvent
);
2219 if (listener
!= null) {
2220 synchronized (listener
) {
2221 listener
.notifyAll();
2226 // ------------------------------------------------------------------------
2227 // MouseListener ----------------------------------------------------------
2228 // ------------------------------------------------------------------------
2231 * Pass mouse events into the event queue.
2233 * @param mouse mouse event received
2235 public void mouseClicked(final MouseEvent mouse
) {
2240 * Pass mouse events into the event queue.
2242 * @param mouse mouse event received
2244 public void mouseEntered(final MouseEvent mouse
) {
2245 swing
.requestFocusInWindow();
2249 * Pass mouse events into the event queue.
2251 * @param mouse mouse event received
2253 public void mouseExited(final MouseEvent mouse
) {
2258 * Pass mouse events into the event queue.
2260 * @param mouse mouse event received
2262 public void mousePressed(final MouseEvent mouse
) {
2263 int modifiers
= mouse
.getModifiersEx();
2264 boolean eventMouse1
= false;
2265 boolean eventMouse2
= false;
2266 boolean eventMouse3
= false;
2267 boolean eventAlt
= false;
2268 boolean eventCtrl
= false;
2269 boolean eventShift
= false;
2271 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2274 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2277 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2280 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2283 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2286 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2290 mouse1
= eventMouse1
;
2291 mouse2
= eventMouse2
;
2292 mouse3
= eventMouse3
;
2293 int x
= textColumn(mouse
.getX());
2294 int y
= textRow(mouse
.getY());
2296 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
2297 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false,
2298 eventAlt
, eventCtrl
, eventShift
);
2300 synchronized (eventQueue
) {
2301 eventQueue
.add(mouseEvent
);
2304 if (listener
!= null) {
2305 synchronized (listener
) {
2306 listener
.notifyAll();
2312 * Pass mouse events into the event queue.
2314 * @param mouse mouse event received
2316 public void mouseReleased(final MouseEvent mouse
) {
2317 int modifiers
= mouse
.getModifiersEx();
2318 boolean eventMouse1
= false;
2319 boolean eventMouse2
= false;
2320 boolean eventMouse3
= false;
2321 boolean eventAlt
= false;
2322 boolean eventCtrl
= false;
2323 boolean eventShift
= false;
2325 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2328 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2331 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2334 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2337 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2340 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2356 int x
= textColumn(mouse
.getX());
2357 int y
= textRow(mouse
.getY());
2359 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
2360 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false,
2361 eventAlt
, eventCtrl
, eventShift
);
2363 synchronized (eventQueue
) {
2364 eventQueue
.add(mouseEvent
);
2367 if (listener
!= null) {
2368 synchronized (listener
) {
2369 listener
.notifyAll();
2374 // ------------------------------------------------------------------------
2375 // MouseWheelListener -----------------------------------------------------
2376 // ------------------------------------------------------------------------
2379 * Pass mouse events into the event queue.
2381 * @param mouse mouse event received
2383 public void mouseWheelMoved(final MouseWheelEvent mouse
) {
2384 int modifiers
= mouse
.getModifiersEx();
2385 boolean eventMouse1
= false;
2386 boolean eventMouse2
= false;
2387 boolean eventMouse3
= false;
2388 boolean mouseWheelUp
= false;
2389 boolean mouseWheelDown
= false;
2390 boolean eventAlt
= false;
2391 boolean eventCtrl
= false;
2392 boolean eventShift
= false;
2394 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2397 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2400 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2403 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2406 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2409 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2413 mouse1
= eventMouse1
;
2414 mouse2
= eventMouse2
;
2415 mouse3
= eventMouse3
;
2416 int x
= textColumn(mouse
.getX());
2417 int y
= textRow(mouse
.getY());
2418 if (mouse
.getWheelRotation() > 0) {
2419 mouseWheelDown
= true;
2421 if (mouse
.getWheelRotation() < 0) {
2422 mouseWheelUp
= true;
2425 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
2426 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
,
2427 eventAlt
, eventCtrl
, eventShift
);
2429 synchronized (eventQueue
) {
2430 eventQueue
.add(mouseEvent
);
2433 if (listener
!= null) {
2434 synchronized (listener
) {
2435 listener
.notifyAll();