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
.Toolkit
;
40 import java
.awt
.event
.ComponentEvent
;
41 import java
.awt
.event
.ComponentListener
;
42 import java
.awt
.event
.KeyEvent
;
43 import java
.awt
.event
.KeyListener
;
44 import java
.awt
.event
.MouseEvent
;
45 import java
.awt
.event
.MouseListener
;
46 import java
.awt
.event
.MouseMotionListener
;
47 import java
.awt
.event
.MouseWheelEvent
;
48 import java
.awt
.event
.MouseWheelListener
;
49 import java
.awt
.event
.WindowEvent
;
50 import java
.awt
.event
.WindowListener
;
51 import java
.awt
.geom
.Rectangle2D
;
52 import java
.awt
.image
.BufferedImage
;
53 import java
.io
.InputStream
;
54 import java
.util
.ArrayList
;
55 import java
.util
.HashMap
;
56 import java
.util
.List
;
58 import javax
.swing
.JComponent
;
59 import javax
.swing
.JFrame
;
60 import javax
.swing
.ImageIcon
;
61 import javax
.swing
.SwingUtilities
;
63 import jexer
.TKeypress
;
64 import jexer
.bits
.Cell
;
65 import jexer
.bits
.CellAttributes
;
66 import jexer
.event
.TCommandEvent
;
67 import jexer
.event
.TInputEvent
;
68 import jexer
.event
.TKeypressEvent
;
69 import jexer
.event
.TMouseEvent
;
70 import jexer
.event
.TResizeEvent
;
71 import static jexer
.TCommand
.*;
72 import static jexer
.TKeypress
.*;
75 * This Screen backend reads keystrokes and mouse events and draws to either
76 * a Java Swing JFrame (potentially triple-buffered) or a JComponent.
78 * This class is a bit of an inversion of typical GUI classes. It performs
79 * all of the drawing logic from SwingTerminal (which is not a Swing class),
80 * and uses a SwingComponent wrapper class to call the JFrame or JComponent
83 public class SwingTerminal
extends LogicalScreen
84 implements TerminalReader
,
85 ComponentListener
, KeyListener
,
86 MouseListener
, MouseMotionListener
,
87 MouseWheelListener
, WindowListener
{
89 // ------------------------------------------------------------------------
90 // Constants --------------------------------------------------------------
91 // ------------------------------------------------------------------------
94 * The icon image location.
96 private static final String ICONFILE
= "jexer_logo_128.png";
99 * The terminus font resource filename.
101 public static final String FONTFILE
= "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
104 * Cursor style to draw.
106 public enum CursorStyle
{
108 * Use an underscore for the cursor.
113 * Use a solid block for the cursor.
118 * Use an outlined block for the cursor.
123 * Use a vertical bar for the cursor.
128 // ------------------------------------------------------------------------
129 // Variables --------------------------------------------------------------
130 // ------------------------------------------------------------------------
132 // Colors to map DOS colors to AWT colors.
133 private static Color MYBLACK
;
134 private static Color MYRED
;
135 private static Color MYGREEN
;
136 private static Color MYYELLOW
;
137 private static Color MYBLUE
;
138 private static Color MYMAGENTA
;
139 private static Color MYCYAN
;
140 private static Color MYWHITE
;
141 private static Color MYBOLD_BLACK
;
142 private static Color MYBOLD_RED
;
143 private static Color MYBOLD_GREEN
;
144 private static Color MYBOLD_YELLOW
;
145 private static Color MYBOLD_BLUE
;
146 private static Color MYBOLD_MAGENTA
;
147 private static Color MYBOLD_CYAN
;
148 private static Color MYBOLD_WHITE
;
151 * When true, all the MYBLACK, MYRED, etc. colors are set.
153 private static boolean dosColors
= false;
156 * The Swing component or frame to draw to.
158 private SwingComponent swing
;
161 * A cache of previously-rendered glyphs for blinking text, when it is
164 private Map
<Cell
, BufferedImage
> glyphCacheBlink
;
167 * A cache of previously-rendered glyphs for non-blinking, or
168 * blinking-and-visible, text.
170 private Map
<Cell
, BufferedImage
> glyphCache
;
173 * If true, we were successful at getting the font dimensions.
175 private boolean gotFontDimensions
= false;
178 * The currently selected font.
180 private Font font
= null;
183 * The currently selected font size in points.
185 private int fontSize
= 16;
188 * Width of a character cell in pixels.
190 private int textWidth
= 16;
193 * Height of a character cell in pixels.
195 private int textHeight
= 20;
198 * Width of a character cell in pixels, as reported by font.
200 private int fontTextWidth
= 1;
203 * Height of a character cell in pixels, as reported by font.
205 private int fontTextHeight
= 1;
208 * Descent of a character cell in pixels.
210 private int maxDescent
= 0;
213 * System-dependent Y adjustment for text in the character cell.
215 private int textAdjustY
= 0;
218 * System-dependent X adjustment for text in the character cell.
220 private int textAdjustX
= 0;
223 * System-dependent height adjustment for text in the character cell.
225 private int textAdjustHeight
= 0;
228 * System-dependent width adjustment for text in the character cell.
230 private int textAdjustWidth
= 0;
233 * Top pixel absolute location.
235 private int top
= 30;
238 * Left pixel absolute location.
240 private int left
= 30;
243 * The cursor style to draw.
245 private CursorStyle cursorStyle
= CursorStyle
.UNDERLINE
;
248 * The number of millis to wait before switching the blink from visible
249 * to invisible. Set to 0 or negative to disable blinking.
251 private long blinkMillis
= 500;
254 * If true, the cursor should be visible right now based on the blink
257 private boolean cursorBlinkVisible
= true;
260 * The time that the blink last flipped from visible to invisible or
261 * from invisible to visible.
263 private long lastBlinkTime
= 0;
266 * The session information.
268 private SwingSessionInfo sessionInfo
;
271 * The listening object that run() wakes up on new input.
273 private Object listener
;
276 * The event queue, filled up by a thread reading on input.
278 private List
<TInputEvent
> eventQueue
;
281 * The last reported mouse X position.
283 private int oldMouseX
= -1;
286 * The last reported mouse Y position.
288 private int oldMouseY
= -1;
291 * true if mouse1 was down. Used to report mouse1 on the release event.
293 private boolean mouse1
= false;
296 * true if mouse2 was down. Used to report mouse2 on the release event.
298 private boolean mouse2
= false;
301 * true if mouse3 was down. Used to report mouse3 on the release event.
303 private boolean mouse3
= false;
305 // ------------------------------------------------------------------------
306 // Constructors -----------------------------------------------------------
307 // ------------------------------------------------------------------------
310 * Static constructor.
317 * Public constructor creates a new JFrame to render to.
319 * @param windowWidth the number of text columns to start with
320 * @param windowHeight the number of text rows to start with
321 * @param fontSize the size in points. Good values to pick are: 16, 20,
323 * @param listener the object this backend needs to wake up when new
326 public SwingTerminal(final int windowWidth
, final int windowHeight
,
327 final int fontSize
, final Object listener
) {
329 this.fontSize
= fontSize
;
334 SwingUtilities
.invokeAndWait(new Runnable() {
337 JFrame frame
= new JFrame() {
340 * Serializable version.
342 private static final long serialVersionUID
= 1;
345 * The code that performs the actual drawing.
347 public SwingTerminal screen
= null;
350 * Anonymous class initializer saves the screen
351 * reference, so that paint() and the like call out
355 this.screen
= SwingTerminal
.this;
359 * Update redraws the whole screen.
361 * @param gr the Swing Graphics context
364 public void update(final Graphics gr
) {
365 // The default update clears the area. Don't do
366 // that, instead just paint it directly.
371 * Paint redraws the whole screen.
373 * @param gr the Swing Graphics context
376 public void paint(final Graphics gr
) {
377 if (screen
!= null) {
384 ClassLoader loader
= Thread
.currentThread().
385 getContextClassLoader();
386 frame
.setIconImage((new ImageIcon(loader
.
387 getResource(ICONFILE
))).getImage());
389 // Get the Swing component
390 SwingTerminal
.this.swing
= new SwingComponent(frame
);
392 // Hang onto top and left for drawing.
393 Insets insets
= SwingTerminal
.this.swing
.getInsets();
394 SwingTerminal
.this.left
= insets
.left
;
395 SwingTerminal
.this.top
= insets
.top
;
397 // Load the font so that we can set sessionInfo.
400 // Get the default cols x rows and set component size
402 SwingTerminal
.this.sessionInfo
=
403 new SwingSessionInfo(SwingTerminal
.this.swing
,
404 SwingTerminal
.this.textWidth
,
405 SwingTerminal
.this.textHeight
,
406 windowWidth
, windowHeight
);
408 SwingTerminal
.this.setDimensions(sessionInfo
.
409 getWindowWidth(), sessionInfo
.getWindowHeight());
411 SwingTerminal
.this.resizeToScreen(true);
412 SwingTerminal
.this.swing
.setVisible(true);
415 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
417 } catch (InterruptedException e
) {
421 this.listener
= listener
;
425 eventQueue
= new ArrayList
<TInputEvent
>();
427 // Add listeners to Swing.
428 swing
.addKeyListener(this);
429 swing
.addWindowListener(this);
430 swing
.addComponentListener(this);
431 swing
.addMouseListener(this);
432 swing
.addMouseMotionListener(this);
433 swing
.addMouseWheelListener(this);
437 * Public constructor renders to an existing JComponent.
439 * @param component the Swing component to render to
440 * @param windowWidth the number of text columns to start with
441 * @param windowHeight the number of text rows to start with
442 * @param fontSize the size in points. Good values to pick are: 16, 20,
444 * @param listener the object this backend needs to wake up when new
447 public SwingTerminal(final JComponent component
, final int windowWidth
,
448 final int windowHeight
, final int fontSize
, final Object listener
) {
450 this.fontSize
= fontSize
;
455 SwingUtilities
.invokeAndWait(new Runnable() {
458 JComponent newComponent
= new JComponent() {
461 * Serializable version.
463 private static final long serialVersionUID
= 1;
466 * The code that performs the actual drawing.
468 public SwingTerminal screen
= null;
471 * Anonymous class initializer saves the screen
472 * reference, so that paint() and the like call out
476 this.screen
= SwingTerminal
.this;
480 * Update redraws the whole screen.
482 * @param gr the Swing Graphics context
485 public void update(final Graphics gr
) {
486 // The default update clears the area. Don't do
487 // that, instead just paint it directly.
492 * Paint redraws the whole screen.
494 * @param gr the Swing Graphics context
497 public void paint(final Graphics gr
) {
498 if (screen
!= null) {
503 component
.setLayout(new BorderLayout());
504 component
.add(newComponent
);
506 // Allow key events to be received
507 component
.setFocusable(true);
509 // Get the Swing component
510 SwingTerminal
.this.swing
= new SwingComponent(component
);
512 // Hang onto top and left for drawing.
513 Insets insets
= SwingTerminal
.this.swing
.getInsets();
514 SwingTerminal
.this.left
= insets
.left
;
515 SwingTerminal
.this.top
= insets
.top
;
517 // Load the font so that we can set sessionInfo.
520 // Get the default cols x rows and set component size
522 SwingTerminal
.this.sessionInfo
=
523 new SwingSessionInfo(SwingTerminal
.this.swing
,
524 SwingTerminal
.this.textWidth
,
525 SwingTerminal
.this.textHeight
);
528 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
530 } catch (InterruptedException e
) {
534 this.listener
= listener
;
538 eventQueue
= new ArrayList
<TInputEvent
>();
540 // Add listeners to Swing.
541 swing
.addKeyListener(this);
542 swing
.addWindowListener(this);
543 swing
.addComponentListener(this);
544 swing
.addMouseListener(this);
545 swing
.addMouseMotionListener(this);
546 swing
.addMouseWheelListener(this);
549 // ------------------------------------------------------------------------
550 // LogicalScreen ----------------------------------------------------------
551 // ------------------------------------------------------------------------
554 * Set the window title.
556 * @param title the new title
559 public void setTitle(final String title
) {
560 swing
.setTitle(title
);
564 * Push the logical screen to the physical device.
567 public void flushPhysical() {
568 // See if it is time to flip the blink time.
569 long nowTime
= System
.currentTimeMillis();
570 if (nowTime
>= blinkMillis
+ lastBlinkTime
) {
571 lastBlinkTime
= nowTime
;
572 cursorBlinkVisible
= !cursorBlinkVisible
;
573 // System.err.println("New lastBlinkTime: " + lastBlinkTime);
576 if ((swing
.getFrame() != null)
577 && (swing
.getBufferStrategy() != null)
583 } while (swing
.getBufferStrategy().contentsRestored());
585 swing
.getBufferStrategy().show();
586 Toolkit
.getDefaultToolkit().sync();
587 } while (swing
.getBufferStrategy().contentsLost());
590 // Non-triple-buffered, call drawToSwing() once
595 // ------------------------------------------------------------------------
596 // TerminalReader ---------------------------------------------------------
597 // ------------------------------------------------------------------------
600 * Check if there are events in the queue.
602 * @return if true, getEvents() has something to return to the backend
604 public boolean hasEvents() {
605 synchronized (eventQueue
) {
606 return (eventQueue
.size() > 0);
611 * Return any events in the IO queue.
613 * @param queue list to append new events to
615 public void getEvents(final List
<TInputEvent
> queue
) {
616 synchronized (eventQueue
) {
617 if (eventQueue
.size() > 0) {
618 synchronized (queue
) {
619 queue
.addAll(eventQueue
);
627 * Restore terminal to normal state.
629 public void closeTerminal() {
634 * Set listener to a different Object.
636 * @param listener the new listening object that run() wakes up on new
639 public void setListener(final Object listener
) {
640 this.listener
= listener
;
644 * Reload options from System properties.
646 public void reloadOptions() {
647 // Figure out my cursor style.
648 String cursorStyleString
= System
.getProperty(
649 "jexer.Swing.cursorStyle", "underline").toLowerCase();
650 if (cursorStyleString
.equals("underline")) {
651 cursorStyle
= CursorStyle
.UNDERLINE
;
652 } else if (cursorStyleString
.equals("outline")) {
653 cursorStyle
= CursorStyle
.OUTLINE
;
654 } else if (cursorStyleString
.equals("block")) {
655 cursorStyle
= CursorStyle
.BLOCK
;
656 } else if (cursorStyleString
.equals("verticalbar")) {
657 cursorStyle
= CursorStyle
.VERTICAL_BAR
;
660 // Pull the system property for triple buffering.
661 if (System
.getProperty("jexer.Swing.tripleBuffer",
662 "true").equals("true")
664 SwingComponent
.tripleBuffer
= true;
666 SwingComponent
.tripleBuffer
= false;
670 setCustomSystemColors();
673 // ------------------------------------------------------------------------
674 // SwingTerminal ----------------------------------------------------------
675 // ------------------------------------------------------------------------
678 * Get the width of a character cell in pixels.
680 * @return the width in pixels of a character cell
682 public int getTextWidth() {
687 * Get the height of a character cell in pixels.
689 * @return the height in pixels of a character cell
691 public int getTextHeight() {
696 * Setup Swing colors to match DOS color palette.
698 private static void setDOSColors() {
702 MYBLACK
= new Color(0x00, 0x00, 0x00);
703 MYRED
= new Color(0xa8, 0x00, 0x00);
704 MYGREEN
= new Color(0x00, 0xa8, 0x00);
705 MYYELLOW
= new Color(0xa8, 0x54, 0x00);
706 MYBLUE
= new Color(0x00, 0x00, 0xa8);
707 MYMAGENTA
= new Color(0xa8, 0x00, 0xa8);
708 MYCYAN
= new Color(0x00, 0xa8, 0xa8);
709 MYWHITE
= new Color(0xa8, 0xa8, 0xa8);
710 MYBOLD_BLACK
= new Color(0x54, 0x54, 0x54);
711 MYBOLD_RED
= new Color(0xfc, 0x54, 0x54);
712 MYBOLD_GREEN
= new Color(0x54, 0xfc, 0x54);
713 MYBOLD_YELLOW
= new Color(0xfc, 0xfc, 0x54);
714 MYBOLD_BLUE
= new Color(0x54, 0x54, 0xfc);
715 MYBOLD_MAGENTA
= new Color(0xfc, 0x54, 0xfc);
716 MYBOLD_CYAN
= new Color(0x54, 0xfc, 0xfc);
717 MYBOLD_WHITE
= new Color(0xfc, 0xfc, 0xfc);
723 * Setup Swing colors to match those provided in system properties.
725 private static void setCustomSystemColors() {
726 synchronized (SwingTerminal
.class) {
727 MYBLACK
= getCustomColor("jexer.Swing.color0", MYBLACK
);
728 MYRED
= getCustomColor("jexer.Swing.color1", MYRED
);
729 MYGREEN
= getCustomColor("jexer.Swing.color2", MYGREEN
);
730 MYYELLOW
= getCustomColor("jexer.Swing.color3", MYYELLOW
);
731 MYBLUE
= getCustomColor("jexer.Swing.color4", MYBLUE
);
732 MYMAGENTA
= getCustomColor("jexer.Swing.color5", MYMAGENTA
);
733 MYCYAN
= getCustomColor("jexer.Swing.color6", MYCYAN
);
734 MYWHITE
= getCustomColor("jexer.Swing.color7", MYWHITE
);
735 MYBOLD_BLACK
= getCustomColor("jexer.Swing.color8", MYBOLD_BLACK
);
736 MYBOLD_RED
= getCustomColor("jexer.Swing.color9", MYBOLD_RED
);
737 MYBOLD_GREEN
= getCustomColor("jexer.Swing.color10", MYBOLD_GREEN
);
738 MYBOLD_YELLOW
= getCustomColor("jexer.Swing.color11", MYBOLD_YELLOW
);
739 MYBOLD_BLUE
= getCustomColor("jexer.Swing.color12", MYBOLD_BLUE
);
740 MYBOLD_MAGENTA
= getCustomColor("jexer.Swing.color13", MYBOLD_MAGENTA
);
741 MYBOLD_CYAN
= getCustomColor("jexer.Swing.color14", MYBOLD_CYAN
);
742 MYBOLD_WHITE
= getCustomColor("jexer.Swing.color15", MYBOLD_WHITE
);
747 * Setup one Swing color to match the RGB value provided in system
750 * @param key the system property key
751 * @param defaultColor the default color to return if key is not set, or
753 * @return a color from the RGB string, or defaultColor
755 private static Color
getCustomColor(final String key
,
756 final Color defaultColor
) {
758 String rgb
= System
.getProperty(key
);
762 if (rgb
.startsWith("#")) {
763 rgb
= rgb
.substring(1);
767 rgbInt
= Integer
.parseInt(rgb
, 16);
768 } catch (NumberFormatException e
) {
771 Color color
= new Color((rgbInt
& 0xFF0000) >>> 16,
772 (rgbInt
& 0x00FF00) >>> 8,
773 (rgbInt
& 0x0000FF));
779 * Get the number of millis to wait before switching the blink from
780 * visible to invisible.
782 * @return the number of milli to wait before switching the blink from
783 * visible to invisible
785 public long getBlinkMillis() {
790 * Get the current status of the blink flag.
792 * @return true if the cursor and blinking text should be visible
794 public boolean getCursorBlinkVisible() {
795 return cursorBlinkVisible
;
799 * Get the font size in points.
801 * @return font size in points
803 public int getFontSize() {
808 * Set the font size in points.
810 * @param fontSize font size in points
812 public void setFontSize(final int fontSize
) {
813 this.fontSize
= fontSize
;
814 Font newFont
= font
.deriveFont((float) fontSize
);
819 * Set to a new font, and resize the screen to match its dimensions.
821 * @param font the new font
823 public void setFont(final Font font
) {
824 if (!SwingUtilities
.isEventDispatchThread()) {
825 // Not in the Swing thread: force this inside the Swing thread.
827 SwingUtilities
.invokeAndWait(new Runnable() {
829 synchronized (this) {
830 SwingTerminal
.this.font
= font
;
833 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
834 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
835 resizeToScreen(true);
839 } catch (InterruptedException e
) {
841 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
845 synchronized (this) {
846 SwingTerminal
.this.font
= font
;
849 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
850 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
851 resizeToScreen(true);
857 * Get the font this screen was last set to.
861 public Font
getFont() {
866 * Set the font to Terminus, the best all-around font for both CP437 and
869 public void setDefaultFont() {
871 ClassLoader loader
= Thread
.currentThread().getContextClassLoader();
872 InputStream in
= loader
.getResourceAsStream(FONTFILE
);
873 Font terminusRoot
= Font
.createFont(Font
.TRUETYPE_FONT
, in
);
874 Font terminus
= terminusRoot
.deriveFont(Font
.PLAIN
, fontSize
);
876 } catch (java
.awt
.FontFormatException e
) {
878 font
= new Font(Font
.MONOSPACED
, Font
.PLAIN
, fontSize
);
879 } catch (java
.io
.IOException e
) {
881 font
= new Font(Font
.MONOSPACED
, Font
.PLAIN
, fontSize
);
888 * Get the X text adjustment.
890 * @return X text adjustment
892 public int getTextAdjustX() {
897 * Set the X text adjustment.
899 * @param textAdjustX the X text adjustment
901 public void setTextAdjustX(final int textAdjustX
) {
902 synchronized (this) {
903 this.textAdjustX
= textAdjustX
;
904 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
905 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
911 * Get the Y text adjustment.
913 * @return Y text adjustment
915 public int getTextAdjustY() {
920 * Set the Y text adjustment.
922 * @param textAdjustY the Y text adjustment
924 public void setTextAdjustY(final int textAdjustY
) {
925 synchronized (this) {
926 this.textAdjustY
= textAdjustY
;
927 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
928 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
934 * Get the height text adjustment.
936 * @return height text adjustment
938 public int getTextAdjustHeight() {
939 return textAdjustHeight
;
943 * Set the height text adjustment.
945 * @param textAdjustHeight the height text adjustment
947 public void setTextAdjustHeight(final int textAdjustHeight
) {
948 synchronized (this) {
949 this.textAdjustHeight
= textAdjustHeight
;
950 textHeight
= fontTextHeight
+ textAdjustHeight
;
951 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
952 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
958 * Get the width text adjustment.
960 * @return width text adjustment
962 public int getTextAdjustWidth() {
963 return textAdjustWidth
;
967 * Set the width text adjustment.
969 * @param textAdjustWidth the width text adjustment
971 public void setTextAdjustWidth(final int textAdjustWidth
) {
972 synchronized (this) {
973 this.textAdjustWidth
= textAdjustWidth
;
974 textWidth
= fontTextWidth
+ textAdjustWidth
;
975 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
976 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
982 * Convert a CellAttributes foreground color to an Swing Color.
984 * @param attr the text attributes
985 * @return the Swing Color
987 public static Color
attrToForegroundColor(final CellAttributes attr
) {
988 int rgb
= attr
.getForeColorRGB();
990 int red
= (rgb
>> 16) & 0xFF;
991 int green
= (rgb
>> 8) & 0xFF;
992 int blue
= rgb
& 0xFF;
994 return new Color(red
, green
, blue
);
998 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
1000 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
1002 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
1004 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
1005 return MYBOLD_GREEN
;
1006 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
1007 return MYBOLD_YELLOW
;
1008 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
1010 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
1011 return MYBOLD_MAGENTA
;
1012 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
1013 return MYBOLD_WHITE
;
1016 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
1018 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
1020 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
1022 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
1024 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
1026 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
1028 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
1030 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
1034 throw new IllegalArgumentException("Invalid color: " +
1035 attr
.getForeColor().getValue());
1039 * Convert a CellAttributes background color to an Swing Color.
1041 * @param attr the text attributes
1042 * @return the Swing Color
1044 public static Color
attrToBackgroundColor(final CellAttributes attr
) {
1045 int rgb
= attr
.getBackColorRGB();
1047 int red
= (rgb
>> 16) & 0xFF;
1048 int green
= (rgb
>> 8) & 0xFF;
1049 int blue
= rgb
& 0xFF;
1051 return new Color(red
, green
, blue
);
1054 if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLACK
)) {
1056 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.RED
)) {
1058 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLUE
)) {
1060 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.GREEN
)) {
1062 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.YELLOW
)) {
1064 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.CYAN
)) {
1066 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
1068 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.WHITE
)) {
1071 throw new IllegalArgumentException("Invalid color: " +
1072 attr
.getBackColor().getValue());
1076 * Figure out what textAdjustX, textAdjustY, textAdjustHeight, and
1077 * textAdjustWidth should be, based on the location of a vertical bar and
1080 private void getFontAdjustments() {
1081 BufferedImage image
= null;
1083 // What SHOULD happen is that the topmost/leftmost white pixel is at
1084 // position (gr2x, gr2y). But it might also be off by a pixel in
1085 // either direction.
1087 Graphics2D gr2
= null;
1090 image
= new BufferedImage(fontTextWidth
* 2, fontTextHeight
* 2,
1091 BufferedImage
.TYPE_INT_ARGB
);
1093 gr2
= image
.createGraphics();
1094 gr2
.setFont(swing
.getFont());
1095 gr2
.setColor(java
.awt
.Color
.BLACK
);
1096 gr2
.fillRect(0, 0, fontTextWidth
* 2, fontTextHeight
* 2);
1097 gr2
.setColor(java
.awt
.Color
.WHITE
);
1098 char [] chars
= new char[1];
1099 chars
[0] = jexer
.bits
.GraphicsChars
.SINGLE_BAR
;
1100 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ fontTextHeight
- maxDescent
);
1101 chars
[0] = jexer
.bits
.GraphicsChars
.VERTICAL_BAR
;
1102 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ fontTextHeight
- maxDescent
);
1105 int top
= fontTextHeight
* 2;
1107 int left
= fontTextWidth
* 2;
1111 textAdjustHeight
= 0;
1112 textAdjustWidth
= 0;
1114 for (int x
= 0; x
< fontTextWidth
* 2; x
++) {
1115 for (int y
= 0; y
< fontTextHeight
* 2; y
++) {
1118 System.err.println("H X: " + x + " Y: " + y + " " +
1119 image.getRGB(x, y));
1122 if ((image
.getRGB(x
, y
) & 0xFFFFFF) != 0) {
1123 // Pixel is present.
1140 textAdjustX
= (gr2x
- left
);
1141 textAdjustWidth
= fontTextWidth
- (right
- left
+ 1);
1144 textAdjustY
= (gr2y
- top
);
1145 textAdjustHeight
= fontTextHeight
- (bottom
- top
+ 1);
1147 // System.err.println("top " + top + " bottom " + bottom);
1148 // System.err.println("left " + left + " right " + right);
1150 // Special case: do not believe fonts that claim to be wider than
1152 if (fontTextWidth
>= fontTextHeight
) {
1154 textAdjustWidth
= 0;
1155 fontTextWidth
= fontTextHeight
/ 2;
1160 * Figure out my font dimensions. This code path works OK for the JFrame
1161 * case, and can be called immediately after JFrame creation.
1163 private void getFontDimensions() {
1164 swing
.setFont(font
);
1165 Graphics gr
= swing
.getGraphics();
1169 getFontDimensions(gr
);
1173 * Figure out my font dimensions. This code path is needed to lazy-load
1174 * the information inside paint().
1176 * @param gr Graphics object to use
1178 private void getFontDimensions(final Graphics gr
) {
1179 swing
.setFont(font
);
1180 FontMetrics fm
= gr
.getFontMetrics();
1181 maxDescent
= fm
.getMaxDescent();
1182 Rectangle2D bounds
= fm
.getMaxCharBounds(gr
);
1183 int leading
= fm
.getLeading();
1184 fontTextWidth
= (int)Math
.round(bounds
.getWidth());
1185 // fontTextHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
1187 // This produces the same number, but works better for ugly
1189 fontTextHeight
= fm
.getMaxAscent() + maxDescent
- leading
;
1191 getFontAdjustments();
1192 textHeight
= fontTextHeight
+ textAdjustHeight
;
1193 textWidth
= fontTextWidth
+ textAdjustWidth
;
1195 if (sessionInfo
!= null) {
1196 sessionInfo
.setTextCellDimensions(textWidth
, textHeight
);
1198 gotFontDimensions
= true;
1202 * Resize the physical screen to match the logical screen dimensions.
1204 * @param resizeComponent if true, resize the Swing component
1206 private void resizeToScreen(final boolean resizeComponent
) {
1207 if (resizeComponent
) {
1208 swing
.setDimensions(textWidth
* width
, textHeight
* height
);
1214 * Resize the physical screen to match the logical screen dimensions.
1217 public void resizeToScreen() {
1218 resizeToScreen(false);
1222 * Draw one cell's image to the screen.
1224 * @param gr the Swing Graphics context
1225 * @param cell the Cell to draw
1226 * @param xPixel the x-coordinate to render to. 0 means the
1227 * left-most pixel column.
1228 * @param yPixel the y-coordinate to render to. 0 means the top-most
1231 private void drawImage(final Graphics gr
, final Cell cell
,
1232 final int xPixel
, final int yPixel
) {
1235 System.err.println("drawImage(): " + xPixel + " " + yPixel +
1239 // Draw the background rectangle, then the foreground character.
1240 assert (cell
.isImage());
1241 gr
.setColor(cell
.getBackground());
1242 gr
.fillRect(xPixel
, yPixel
, textWidth
, textHeight
);
1244 BufferedImage image
= cell
.getImage();
1245 if (image
!= null) {
1246 if (swing
.getFrame() != null) {
1247 gr
.drawImage(image
, xPixel
, yPixel
, getTextWidth(),
1248 getTextHeight(), swing
.getFrame());
1250 gr
.drawImage(image
, xPixel
, yPixel
, getTextWidth(),
1251 getTextHeight(),swing
.getComponent());
1258 * Draw one glyph to the screen.
1260 * @param gr the Swing Graphics context
1261 * @param cell the Cell to draw
1262 * @param xPixel the x-coordinate to render to. 0 means the
1263 * left-most pixel column.
1264 * @param yPixel the y-coordinate to render to. 0 means the top-most
1267 private void drawGlyph(final Graphics gr
, final Cell cell
,
1268 final int xPixel
, final int yPixel
) {
1271 System.err.println("drawGlyph(): " + xPixel + " " + yPixel +
1275 BufferedImage image
= null;
1276 if (cell
.isBlink() && !cursorBlinkVisible
) {
1277 image
= glyphCacheBlink
.get(cell
);
1279 image
= glyphCache
.get(cell
);
1281 if (image
!= null) {
1282 if (swing
.getFrame() != null) {
1283 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
1285 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
1290 // Generate glyph and draw it.
1291 Graphics2D gr2
= null;
1294 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
1295 image
= new BufferedImage(textWidth
, textHeight
,
1296 BufferedImage
.TYPE_INT_ARGB
);
1297 gr2
= image
.createGraphics();
1298 gr2
.setFont(swing
.getFont());
1302 gr2
= (Graphics2D
) gr
;
1305 Cell cellColor
= new Cell(cell
);
1307 // Check for reverse
1308 if (cell
.isReverse()) {
1309 cellColor
.setForeColor(cell
.getBackColor());
1310 cellColor
.setBackColor(cell
.getForeColor());
1313 // Draw the background rectangle, then the foreground character.
1314 gr2
.setColor(attrToBackgroundColor(cellColor
));
1315 gr2
.fillRect(gr2x
, gr2y
, textWidth
, textHeight
);
1317 // Handle blink and underline
1319 || (cell
.isBlink() && cursorBlinkVisible
)
1321 gr2
.setColor(attrToForegroundColor(cellColor
));
1322 char [] chars
= Character
.toChars(cell
.getChar());
1323 gr2
.drawChars(chars
, 0, chars
.length
, gr2x
+ textAdjustX
,
1324 gr2y
+ textHeight
- maxDescent
+ textAdjustY
);
1326 if (cell
.isUnderline()) {
1327 gr2
.fillRect(gr2x
, gr2y
+ textHeight
- 2, textWidth
, 2);
1331 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
1334 // We need a new key that will not be mutated by
1336 Cell key
= new Cell(cell
);
1337 if (cell
.isBlink() && !cursorBlinkVisible
) {
1338 glyphCacheBlink
.put(key
, image
);
1340 glyphCache
.put(key
, image
);
1343 if (swing
.getFrame() != null) {
1344 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
1346 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
1353 * Check if the cursor is visible, and if so draw it.
1355 * @param gr the Swing Graphics context
1357 private void drawCursor(final Graphics gr
) {
1362 && (cursorY
<= height
- 1)
1363 && (cursorX
<= width
- 1)
1364 && cursorBlinkVisible
1366 int xPixel
= cursorX
* textWidth
+ left
;
1367 int yPixel
= cursorY
* textHeight
+ top
;
1368 Cell lCell
= logical
[cursorX
][cursorY
];
1369 int cursorWidth
= textWidth
;
1370 switch (lCell
.getWidth()) {
1379 xPixel
-= textWidth
;
1382 gr
.setColor(attrToForegroundColor(lCell
));
1383 switch (cursorStyle
) {
1387 gr
.fillRect(xPixel
, yPixel
+ textHeight
- 2, cursorWidth
, 2);
1390 gr
.fillRect(xPixel
, yPixel
, cursorWidth
, textHeight
);
1393 gr
.drawRect(xPixel
, yPixel
, cursorWidth
- 1, textHeight
- 1);
1396 gr
.fillRect(xPixel
, yPixel
, 2, textHeight
);
1403 * Reset the blink timer.
1405 private void resetBlinkTimer() {
1406 lastBlinkTime
= System
.currentTimeMillis();
1407 cursorBlinkVisible
= true;
1411 * Paint redraws the whole screen.
1413 * @param gr the Swing Graphics context
1415 public void paint(final Graphics gr
) {
1417 if (gotFontDimensions
== false) {
1418 // Lazy-load the text width/height
1419 getFontDimensions(gr
);
1421 System.err.println("textWidth " + textWidth +
1422 " textHeight " + textHeight);
1423 System.err.println("FONT: " + swing.getFont() + " font " + font);
1427 if ((swing
.getFrame() != null)
1428 && (swing
.getBufferStrategy() != null)
1429 && (SwingUtilities
.isEventDispatchThread())
1431 // System.err.println("paint(), skip first paint on swing thread");
1436 int xCellMax
= width
;
1438 int yCellMax
= height
;
1440 Rectangle bounds
= gr
.getClipBounds();
1441 if (bounds
!= null) {
1442 // Only update what is in the bounds
1443 xCellMin
= textColumn(bounds
.x
);
1444 xCellMax
= textColumn(bounds
.x
+ bounds
.width
) + 1;
1445 if (xCellMax
> width
) {
1448 if (xCellMin
>= xCellMax
) {
1449 xCellMin
= xCellMax
- 2;
1454 yCellMin
= textRow(bounds
.y
);
1455 yCellMax
= textRow(bounds
.y
+ bounds
.height
) + 1;
1456 if (yCellMax
> height
) {
1459 if (yCellMin
>= yCellMax
) {
1460 yCellMin
= yCellMax
- 2;
1466 // We need a total repaint
1467 reallyCleared
= true;
1470 // Prevent updates to the screen's data from the TApplication
1472 synchronized (this) {
1475 System.err.printf("bounds %s X %d %d Y %d %d\n",
1476 bounds, xCellMin, xCellMax, yCellMin, yCellMax);
1479 for (int y
= yCellMin
; y
< yCellMax
; y
++) {
1480 for (int x
= xCellMin
; x
< xCellMax
; x
++) {
1482 int xPixel
= x
* textWidth
+ left
;
1483 int yPixel
= y
* textHeight
+ top
;
1485 Cell lCell
= logical
[x
][y
];
1486 Cell pCell
= physical
[x
][y
];
1488 if (!lCell
.equals(pCell
)
1491 || (swing
.getFrame() == null)) {
1493 if (lCell
.isImage()) {
1494 drawImage(gr
, lCell
, xPixel
, yPixel
);
1496 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
1499 // Physical is always updated
1500 physical
[x
][y
].setTo(lCell
);
1506 reallyCleared
= false;
1507 } // synchronized (this)
1511 * Restore terminal to normal state.
1513 public void shutdown() {
1518 * Push the logical screen to the physical device.
1520 private void drawToSwing() {
1523 System.err.printf("drawToSwing(): reallyCleared %s dirty %s\n",
1524 reallyCleared, dirty);
1527 // If reallyCleared is set, we have to draw everything.
1528 if ((swing
.getFrame() != null)
1529 && (swing
.getBufferStrategy() != null)
1530 && (reallyCleared
== true)
1532 // Triple-buffering: we have to redraw everything on this thread.
1533 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1536 swing
.getBufferStrategy().show();
1537 Toolkit
.getDefaultToolkit().sync();
1539 } else if (((swing
.getFrame() != null)
1540 && (swing
.getBufferStrategy() == null))
1541 || (reallyCleared
== true)
1543 // Repaint everything on the Swing thread.
1544 // System.err.println("REPAINT ALL");
1549 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
1550 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1552 synchronized (this) {
1553 for (int y
= 0; y
< height
; y
++) {
1554 for (int x
= 0; x
< width
; x
++) {
1555 Cell lCell
= logical
[x
][y
];
1556 Cell pCell
= physical
[x
][y
];
1558 int xPixel
= x
* textWidth
+ left
;
1559 int yPixel
= y
* textHeight
+ top
;
1561 if (!lCell
.equals(pCell
)
1565 || (lCell
.isBlink())
1567 if (lCell
.isImage()) {
1568 drawImage(gr
, lCell
, xPixel
, yPixel
);
1570 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
1572 physical
[x
][y
].setTo(lCell
);
1577 } // synchronized (this)
1580 swing
.getBufferStrategy().show();
1581 Toolkit
.getDefaultToolkit().sync();
1585 // Swing thread version: request a repaint, but limit it to the area
1586 // that has changed.
1588 // Find the minimum-size damaged region.
1589 int xMin
= swing
.getWidth();
1591 int yMin
= swing
.getHeight();
1594 synchronized (this) {
1595 for (int y
= 0; y
< height
; y
++) {
1596 for (int x
= 0; x
< width
; x
++) {
1597 Cell lCell
= logical
[x
][y
];
1598 Cell pCell
= physical
[x
][y
];
1600 int xPixel
= x
* textWidth
+ left
;
1601 int yPixel
= y
* textHeight
+ top
;
1603 if (!lCell
.equals(pCell
)
1609 if (xPixel
< xMin
) {
1612 if (xPixel
+ textWidth
> xMax
) {
1613 xMax
= xPixel
+ textWidth
;
1615 if (yPixel
< yMin
) {
1618 if (yPixel
+ textHeight
> yMax
) {
1619 yMax
= yPixel
+ textHeight
;
1625 if (xMin
+ textWidth
>= xMax
) {
1628 if (yMin
+ textHeight
>= yMax
) {
1632 // Repaint the desired area
1634 System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax,
1638 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
1639 // This path should never be taken, but is left here for
1641 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1642 Rectangle bounds
= new Rectangle(xMin
, yMin
, xMax
- xMin
,
1647 swing
.getBufferStrategy().show();
1648 Toolkit
.getDefaultToolkit().sync();
1650 // Repaint on the Swing thread.
1651 swing
.repaint(xMin
, yMin
, xMax
- xMin
, yMax
- yMin
);
1656 * Convert pixel column position to text cell column position.
1658 * @param x pixel column position
1659 * @return text cell column position
1661 public int textColumn(final int x
) {
1662 int column
= ((x
- left
) / textWidth
);
1666 if (column
> width
- 1) {
1673 * Convert pixel row position to text cell row position.
1675 * @param y pixel row position
1676 * @return text cell row position
1678 public int textRow(final int y
) {
1679 int row
= ((y
- top
) / textHeight
);
1683 if (row
> height
- 1) {
1690 * Getter for sessionInfo.
1692 * @return the SessionInfo
1694 public SessionInfo
getSessionInfo() {
1699 * Getter for the underlying Swing component.
1701 * @return the SwingComponent
1703 public SwingComponent
getSwingComponent() {
1707 // ------------------------------------------------------------------------
1708 // KeyListener ------------------------------------------------------------
1709 // ------------------------------------------------------------------------
1712 * Pass Swing keystrokes into the event queue.
1714 * @param key keystroke received
1716 public void keyReleased(final KeyEvent key
) {
1717 // Ignore release events
1721 * Pass Swing keystrokes into the event queue.
1723 * @param key keystroke received
1725 public void keyTyped(final KeyEvent key
) {
1726 // Ignore typed events
1730 * Pass Swing keystrokes into the event queue.
1732 * @param key keystroke received
1734 public void keyPressed(final KeyEvent key
) {
1735 boolean alt
= false;
1736 boolean shift
= false;
1737 boolean ctrl
= false;
1739 boolean isKey
= false;
1740 if (key
.isActionKey()) {
1743 ch
= key
.getKeyChar();
1745 alt
= key
.isAltDown();
1746 ctrl
= key
.isControlDown();
1747 shift
= key
.isShiftDown();
1750 System.err.printf("Swing Key: %s\n", key);
1751 System.err.printf(" isKey: %s\n", isKey);
1752 System.err.printf(" alt: %s\n", alt);
1753 System.err.printf(" ctrl: %s\n", ctrl);
1754 System.err.printf(" shift: %s\n", shift);
1755 System.err.printf(" ch: %s\n", ch);
1758 // Special case: not return the bare modifier presses
1759 switch (key
.getKeyCode()) {
1760 case KeyEvent
.VK_ALT
:
1762 case KeyEvent
.VK_ALT_GRAPH
:
1764 case KeyEvent
.VK_CONTROL
:
1766 case KeyEvent
.VK_SHIFT
:
1768 case KeyEvent
.VK_META
:
1774 TKeypress keypress
= null;
1776 switch (key
.getKeyCode()) {
1777 case KeyEvent
.VK_F1
:
1778 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
1781 case KeyEvent
.VK_F2
:
1782 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
1785 case KeyEvent
.VK_F3
:
1786 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
1789 case KeyEvent
.VK_F4
:
1790 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
1793 case KeyEvent
.VK_F5
:
1794 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
1797 case KeyEvent
.VK_F6
:
1798 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
1801 case KeyEvent
.VK_F7
:
1802 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
1805 case KeyEvent
.VK_F8
:
1806 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
1809 case KeyEvent
.VK_F9
:
1810 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
1813 case KeyEvent
.VK_F10
:
1814 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
1817 case KeyEvent
.VK_F11
:
1818 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
1821 case KeyEvent
.VK_F12
:
1822 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
1825 case KeyEvent
.VK_HOME
:
1826 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
1829 case KeyEvent
.VK_END
:
1830 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
1833 case KeyEvent
.VK_PAGE_UP
:
1834 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
1837 case KeyEvent
.VK_PAGE_DOWN
:
1838 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
1841 case KeyEvent
.VK_INSERT
:
1842 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
1845 case KeyEvent
.VK_DELETE
:
1846 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
1849 case KeyEvent
.VK_RIGHT
:
1850 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
1853 case KeyEvent
.VK_LEFT
:
1854 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
1857 case KeyEvent
.VK_UP
:
1858 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
1861 case KeyEvent
.VK_DOWN
:
1862 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
1865 case KeyEvent
.VK_TAB
:
1866 // Special case: distinguish TAB vs BTAB
1868 keypress
= kbShiftTab
;
1873 case KeyEvent
.VK_ENTER
:
1874 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
1877 case KeyEvent
.VK_ESCAPE
:
1878 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
1881 case KeyEvent
.VK_BACK_SPACE
:
1882 keypress
= kbBackspace
;
1885 // Unsupported, ignore
1890 if (keypress
== null) {
1893 // Disambiguate ^H from Backspace.
1894 if (KeyEvent
.getKeyText(key
.getKeyCode()).equals("H")) {
1896 keypress
= kbBackspace
;
1898 // We are emulating Xterm here, where the backspace key
1899 // on the keyboard returns ^?.
1900 keypress
= kbBackspaceDel
;
1914 keypress
= kbShiftTab
;
1923 if (!alt
&& ctrl
&& !shift
) {
1924 // Control character, replace ch with 'A', 'B', etc.
1925 ch
= KeyEvent
.getKeyText(key
.getKeyCode()).charAt(0);
1927 // Not a special key, put it together
1928 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
1932 // Save it and we are done.
1933 synchronized (eventQueue
) {
1934 eventQueue
.add(new TKeypressEvent(keypress
));
1937 if (listener
!= null) {
1938 synchronized (listener
) {
1939 listener
.notifyAll();
1944 // ------------------------------------------------------------------------
1945 // WindowListener ---------------------------------------------------------
1946 // ------------------------------------------------------------------------
1949 * Pass window events into the event queue.
1951 * @param event window event received
1953 public void windowActivated(final WindowEvent event
) {
1954 // Force a total repaint
1955 synchronized (this) {
1961 * Pass window events into the event queue.
1963 * @param event window event received
1965 public void windowClosed(final WindowEvent event
) {
1970 * Pass window events into the event queue.
1972 * @param event window event received
1974 public void windowClosing(final WindowEvent event
) {
1975 // Drop a cmBackendDisconnect and walk away
1976 synchronized (eventQueue
) {
1977 eventQueue
.add(new TCommandEvent(cmBackendDisconnect
));
1980 if (listener
!= null) {
1981 synchronized (listener
) {
1982 listener
.notifyAll();
1988 * Pass window events into the event queue.
1990 * @param event window event received
1992 public void windowDeactivated(final WindowEvent event
) {
1997 * Pass window events into the event queue.
1999 * @param event window event received
2001 public void windowDeiconified(final WindowEvent event
) {
2006 * Pass window events into the event queue.
2008 * @param event window event received
2010 public void windowIconified(final WindowEvent event
) {
2015 * Pass window events into the event queue.
2017 * @param event window event received
2019 public void windowOpened(final WindowEvent event
) {
2023 // ------------------------------------------------------------------------
2024 // ComponentListener ------------------------------------------------------
2025 // ------------------------------------------------------------------------
2028 * Pass component events into the event queue.
2030 * @param event component event received
2032 public void componentHidden(final ComponentEvent event
) {
2037 * Pass component events into the event queue.
2039 * @param event component event received
2041 public void componentShown(final ComponentEvent event
) {
2046 * Pass component events into the event queue.
2048 * @param event component event received
2050 public void componentMoved(final ComponentEvent event
) {
2055 * Pass component events into the event queue.
2057 * @param event component event received
2059 public void componentResized(final ComponentEvent event
) {
2060 if (gotFontDimensions
== false) {
2061 // We are still waiting to get font information. Don't pass a
2063 // System.err.println("size " + swing.getComponent().getSize());
2067 if (sessionInfo
== null) {
2068 // This is the initial component resize in construction, bail
2073 // Drop a new TResizeEvent into the queue
2074 sessionInfo
.queryWindowSize();
2075 synchronized (eventQueue
) {
2076 TResizeEvent windowResize
= new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
2077 sessionInfo
.getWindowWidth(), sessionInfo
.getWindowHeight());
2078 eventQueue
.add(windowResize
);
2081 System.err.println("Add resize event: " + windowResize.getWidth() +
2082 " x " + windowResize.getHeight());
2085 if (listener
!= null) {
2086 synchronized (listener
) {
2087 listener
.notifyAll();
2092 // ------------------------------------------------------------------------
2093 // MouseMotionListener ----------------------------------------------------
2094 // ------------------------------------------------------------------------
2097 * Pass mouse events into the event queue.
2099 * @param mouse mouse event received
2101 public void mouseDragged(final MouseEvent mouse
) {
2102 int modifiers
= mouse
.getModifiersEx();
2103 boolean eventMouse1
= false;
2104 boolean eventMouse2
= false;
2105 boolean eventMouse3
= false;
2106 boolean eventAlt
= false;
2107 boolean eventCtrl
= false;
2108 boolean eventShift
= false;
2110 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2113 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2116 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2119 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2122 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2125 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2129 mouse1
= eventMouse1
;
2130 mouse2
= eventMouse2
;
2131 mouse3
= eventMouse3
;
2132 int x
= textColumn(mouse
.getX());
2133 int y
= textRow(mouse
.getY());
2135 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
2136 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false,
2137 eventAlt
, eventCtrl
, eventShift
);
2139 synchronized (eventQueue
) {
2140 eventQueue
.add(mouseEvent
);
2143 if (listener
!= null) {
2144 synchronized (listener
) {
2145 listener
.notifyAll();
2151 * Pass mouse events into the event queue.
2153 * @param mouse mouse event received
2155 public void mouseMoved(final MouseEvent mouse
) {
2156 int x
= textColumn(mouse
.getX());
2157 int y
= textRow(mouse
.getY());
2158 if ((x
== oldMouseX
) && (y
== oldMouseY
)) {
2159 // Bail out, we've moved some pixels but not a whole text cell.
2165 boolean eventAlt
= false;
2166 boolean eventCtrl
= false;
2167 boolean eventShift
= false;
2169 int modifiers
= mouse
.getModifiersEx();
2170 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2173 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2176 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2180 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
2181 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false,
2182 eventAlt
, eventCtrl
, eventShift
);
2184 synchronized (eventQueue
) {
2185 eventQueue
.add(mouseEvent
);
2188 if (listener
!= null) {
2189 synchronized (listener
) {
2190 listener
.notifyAll();
2195 // ------------------------------------------------------------------------
2196 // MouseListener ----------------------------------------------------------
2197 // ------------------------------------------------------------------------
2200 * Pass mouse events into the event queue.
2202 * @param mouse mouse event received
2204 public void mouseClicked(final MouseEvent mouse
) {
2209 * Pass mouse events into the event queue.
2211 * @param mouse mouse event received
2213 public void mouseEntered(final MouseEvent mouse
) {
2214 swing
.requestFocusInWindow();
2218 * Pass mouse events into the event queue.
2220 * @param mouse mouse event received
2222 public void mouseExited(final MouseEvent mouse
) {
2227 * Pass mouse events into the event queue.
2229 * @param mouse mouse event received
2231 public void mousePressed(final MouseEvent mouse
) {
2232 int modifiers
= mouse
.getModifiersEx();
2233 boolean eventMouse1
= false;
2234 boolean eventMouse2
= false;
2235 boolean eventMouse3
= false;
2236 boolean eventAlt
= false;
2237 boolean eventCtrl
= false;
2238 boolean eventShift
= false;
2240 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2243 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2246 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2249 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2252 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2255 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2259 mouse1
= eventMouse1
;
2260 mouse2
= eventMouse2
;
2261 mouse3
= eventMouse3
;
2262 int x
= textColumn(mouse
.getX());
2263 int y
= textRow(mouse
.getY());
2265 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
2266 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false,
2267 eventAlt
, eventCtrl
, eventShift
);
2269 synchronized (eventQueue
) {
2270 eventQueue
.add(mouseEvent
);
2273 if (listener
!= null) {
2274 synchronized (listener
) {
2275 listener
.notifyAll();
2281 * Pass mouse events into the event queue.
2283 * @param mouse mouse event received
2285 public void mouseReleased(final MouseEvent mouse
) {
2286 int modifiers
= mouse
.getModifiersEx();
2287 boolean eventMouse1
= false;
2288 boolean eventMouse2
= false;
2289 boolean eventMouse3
= false;
2290 boolean eventAlt
= false;
2291 boolean eventCtrl
= false;
2292 boolean eventShift
= false;
2294 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2297 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2300 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2303 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2306 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2309 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2325 int x
= textColumn(mouse
.getX());
2326 int y
= textRow(mouse
.getY());
2328 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
2329 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false,
2330 eventAlt
, eventCtrl
, eventShift
);
2332 synchronized (eventQueue
) {
2333 eventQueue
.add(mouseEvent
);
2336 if (listener
!= null) {
2337 synchronized (listener
) {
2338 listener
.notifyAll();
2343 // ------------------------------------------------------------------------
2344 // MouseWheelListener -----------------------------------------------------
2345 // ------------------------------------------------------------------------
2348 * Pass mouse events into the event queue.
2350 * @param mouse mouse event received
2352 public void mouseWheelMoved(final MouseWheelEvent mouse
) {
2353 int modifiers
= mouse
.getModifiersEx();
2354 boolean eventMouse1
= false;
2355 boolean eventMouse2
= false;
2356 boolean eventMouse3
= false;
2357 boolean mouseWheelUp
= false;
2358 boolean mouseWheelDown
= false;
2359 boolean eventAlt
= false;
2360 boolean eventCtrl
= false;
2361 boolean eventShift
= false;
2363 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2366 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2369 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2372 if ((modifiers
& MouseEvent
.ALT_DOWN_MASK
) != 0) {
2375 if ((modifiers
& MouseEvent
.CTRL_DOWN_MASK
) != 0) {
2378 if ((modifiers
& MouseEvent
.SHIFT_DOWN_MASK
) != 0) {
2382 mouse1
= eventMouse1
;
2383 mouse2
= eventMouse2
;
2384 mouse3
= eventMouse3
;
2385 int x
= textColumn(mouse
.getX());
2386 int y
= textRow(mouse
.getY());
2387 if (mouse
.getWheelRotation() > 0) {
2388 mouseWheelDown
= true;
2390 if (mouse
.getWheelRotation() < 0) {
2391 mouseWheelUp
= true;
2394 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
2395 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
,
2396 eventAlt
, eventCtrl
, eventShift
);
2398 synchronized (eventQueue
) {
2399 eventQueue
.add(mouseEvent
);
2402 if (listener
!= null) {
2403 synchronized (listener
) {
2404 listener
.notifyAll();