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
.HashMap
;
55 import java
.util
.LinkedList
;
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 private 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 // ------------------------------------------------------------------------
124 // Variables --------------------------------------------------------------
125 // ------------------------------------------------------------------------
127 // Colors to map DOS colors to AWT colors.
128 private static Color MYBLACK
;
129 private static Color MYRED
;
130 private static Color MYGREEN
;
131 private static Color MYYELLOW
;
132 private static Color MYBLUE
;
133 private static Color MYMAGENTA
;
134 private static Color MYCYAN
;
135 private static Color MYWHITE
;
136 private static Color MYBOLD_BLACK
;
137 private static Color MYBOLD_RED
;
138 private static Color MYBOLD_GREEN
;
139 private static Color MYBOLD_YELLOW
;
140 private static Color MYBOLD_BLUE
;
141 private static Color MYBOLD_MAGENTA
;
142 private static Color MYBOLD_CYAN
;
143 private static Color MYBOLD_WHITE
;
146 * When true, all the MYBLACK, MYRED, etc. colors are set.
148 private static boolean dosColors
= false;
151 * The Swing component or frame to draw to.
153 private SwingComponent swing
;
156 * A cache of previously-rendered glyphs for blinking text, when it is
159 private Map
<Cell
, BufferedImage
> glyphCacheBlink
;
162 * A cache of previously-rendered glyphs for non-blinking, or
163 * blinking-and-visible, text.
165 private Map
<Cell
, BufferedImage
> glyphCache
;
168 * If true, we were successful getting Terminus.
170 private boolean gotTerminus
= false;
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
= 1;
193 * Height of a character cell in pixels.
195 private int textHeight
= 1;
198 * Descent of a character cell in pixels.
200 private int maxDescent
= 0;
203 * System-dependent Y adjustment for text in the character cell.
205 private int textAdjustY
= 0;
208 * System-dependent X adjustment for text in the character cell.
210 private int textAdjustX
= 0;
213 * Top pixel absolute location.
215 private int top
= 30;
218 * Left pixel absolute location.
220 private int left
= 30;
223 * The cursor style to draw.
225 private CursorStyle cursorStyle
= CursorStyle
.UNDERLINE
;
228 * The number of millis to wait before switching the blink from visible
229 * to invisible. Set to 0 or negative to disable blinking.
231 private long blinkMillis
= 500;
234 * If true, the cursor should be visible right now based on the blink
237 private boolean cursorBlinkVisible
= true;
240 * The time that the blink last flipped from visible to invisible or
241 * from invisible to visible.
243 private long lastBlinkTime
= 0;
246 * The session information.
248 private SwingSessionInfo sessionInfo
;
251 * The listening object that run() wakes up on new input.
253 private Object listener
;
256 * The event queue, filled up by a thread reading on input.
258 private List
<TInputEvent
> eventQueue
;
261 * The last reported mouse X position.
263 private int oldMouseX
= -1;
266 * The last reported mouse Y position.
268 private int oldMouseY
= -1;
271 * true if mouse1 was down. Used to report mouse1 on the release event.
273 private boolean mouse1
= false;
276 * true if mouse2 was down. Used to report mouse2 on the release event.
278 private boolean mouse2
= false;
281 * true if mouse3 was down. Used to report mouse3 on the release event.
283 private boolean mouse3
= false;
285 // ------------------------------------------------------------------------
286 // Constructors -----------------------------------------------------------
287 // ------------------------------------------------------------------------
290 * Public constructor creates a new JFrame to render to.
292 * @param windowWidth the number of text columns to start with
293 * @param windowHeight the number of text rows to start with
294 * @param fontSize the size in points. Good values to pick are: 16, 20,
296 * @param listener the object this backend needs to wake up when new
299 public SwingTerminal(final int windowWidth
, final int windowHeight
,
300 final int fontSize
, final Object listener
) {
302 this.fontSize
= fontSize
;
306 // Figure out my cursor style.
307 String cursorStyleString
= System
.getProperty(
308 "jexer.Swing.cursorStyle", "underline").toLowerCase();
309 if (cursorStyleString
.equals("underline")) {
310 cursorStyle
= CursorStyle
.UNDERLINE
;
311 } else if (cursorStyleString
.equals("outline")) {
312 cursorStyle
= CursorStyle
.OUTLINE
;
313 } else if (cursorStyleString
.equals("block")) {
314 cursorStyle
= CursorStyle
.BLOCK
;
317 // Pull the system property for triple buffering.
318 if (System
.getProperty("jexer.Swing.tripleBuffer") != null) {
319 if (System
.getProperty("jexer.Swing.tripleBuffer").equals("true")) {
320 SwingComponent
.tripleBuffer
= true;
322 SwingComponent
.tripleBuffer
= false;
327 SwingUtilities
.invokeAndWait(new Runnable() {
330 JFrame frame
= new JFrame() {
333 * Serializable version.
335 private static final long serialVersionUID
= 1;
338 * The code that performs the actual drawing.
340 public SwingTerminal screen
= null;
343 * Anonymous class initializer saves the screen
344 * reference, so that paint() and the like call out
348 this.screen
= SwingTerminal
.this;
352 * Update redraws the whole screen.
354 * @param gr the Swing Graphics context
357 public void update(final Graphics gr
) {
358 // The default update clears the area. Don't do
359 // that, instead just paint it directly.
364 * Paint redraws the whole screen.
366 * @param gr the Swing Graphics context
369 public void paint(final Graphics gr
) {
370 if (screen
!= null) {
377 ClassLoader loader
= Thread
.currentThread().
378 getContextClassLoader();
379 frame
.setIconImage((new ImageIcon(loader
.
380 getResource(ICONFILE
))).getImage());
382 // Get the Swing component
383 SwingTerminal
.this.swing
= new SwingComponent(frame
);
385 // Hang onto top and left for drawing.
386 Insets insets
= SwingTerminal
.this.swing
.getInsets();
387 SwingTerminal
.this.left
= insets
.left
;
388 SwingTerminal
.this.top
= insets
.top
;
390 // Load the font so that we can set sessionInfo.
393 // Get the default cols x rows and set component size
395 SwingTerminal
.this.sessionInfo
=
396 new SwingSessionInfo(SwingTerminal
.this.swing
,
397 SwingTerminal
.this.textWidth
,
398 SwingTerminal
.this.textHeight
,
399 windowWidth
, windowHeight
);
401 SwingTerminal
.this.setDimensions(sessionInfo
.
402 getWindowWidth(), sessionInfo
.getWindowHeight());
404 SwingTerminal
.this.resizeToScreen();
405 SwingTerminal
.this.swing
.setVisible(true);
408 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
410 } catch (InterruptedException e
) {
414 this.listener
= listener
;
418 eventQueue
= new LinkedList
<TInputEvent
>();
420 // Add listeners to Swing.
421 swing
.addKeyListener(this);
422 swing
.addWindowListener(this);
423 swing
.addComponentListener(this);
424 swing
.addMouseListener(this);
425 swing
.addMouseMotionListener(this);
426 swing
.addMouseWheelListener(this);
430 * Public constructor renders to an existing JComponent.
432 * @param component the Swing component to render to
433 * @param windowWidth the number of text columns to start with
434 * @param windowHeight the number of text rows to start with
435 * @param fontSize the size in points. Good values to pick are: 16, 20,
437 * @param listener the object this backend needs to wake up when new
440 public SwingTerminal(final JComponent component
, final int windowWidth
,
441 final int windowHeight
, final int fontSize
, final Object listener
) {
443 this.fontSize
= fontSize
;
447 // Figure out my cursor style.
448 String cursorStyleString
= System
.getProperty(
449 "jexer.Swing.cursorStyle", "underline").toLowerCase();
450 if (cursorStyleString
.equals("underline")) {
451 cursorStyle
= CursorStyle
.UNDERLINE
;
452 } else if (cursorStyleString
.equals("outline")) {
453 cursorStyle
= CursorStyle
.OUTLINE
;
454 } else if (cursorStyleString
.equals("block")) {
455 cursorStyle
= CursorStyle
.BLOCK
;
459 SwingUtilities
.invokeAndWait(new Runnable() {
462 JComponent newComponent
= new JComponent() {
465 * Serializable version.
467 private static final long serialVersionUID
= 1;
470 * The code that performs the actual drawing.
472 public SwingTerminal screen
= null;
475 * Anonymous class initializer saves the screen
476 * reference, so that paint() and the like call out
480 this.screen
= SwingTerminal
.this;
484 * Update redraws the whole screen.
486 * @param gr the Swing Graphics context
489 public void update(final Graphics gr
) {
490 // The default update clears the area. Don't do
491 // that, instead just paint it directly.
496 * Paint redraws the whole screen.
498 * @param gr the Swing Graphics context
501 public void paint(final Graphics gr
) {
502 if (screen
!= null) {
507 component
.setLayout(new BorderLayout());
508 component
.add(newComponent
);
510 // Allow key events to be received
511 component
.setFocusable(true);
513 // Get the Swing component
514 SwingTerminal
.this.swing
= new SwingComponent(component
);
516 // Hang onto top and left for drawing.
517 Insets insets
= SwingTerminal
.this.swing
.getInsets();
518 SwingTerminal
.this.left
= insets
.left
;
519 SwingTerminal
.this.top
= insets
.top
;
521 // Load the font so that we can set sessionInfo.
524 // Get the default cols x rows and set component size
526 SwingTerminal
.this.sessionInfo
=
527 new SwingSessionInfo(SwingTerminal
.this.swing
,
528 SwingTerminal
.this.textWidth
,
529 SwingTerminal
.this.textHeight
);
532 } catch (java
.lang
.reflect
.InvocationTargetException e
) {
534 } catch (InterruptedException e
) {
538 this.listener
= listener
;
542 eventQueue
= new LinkedList
<TInputEvent
>();
544 // Add listeners to Swing.
545 swing
.addKeyListener(this);
546 swing
.addWindowListener(this);
547 swing
.addComponentListener(this);
548 swing
.addMouseListener(this);
549 swing
.addMouseMotionListener(this);
550 swing
.addMouseWheelListener(this);
553 // ------------------------------------------------------------------------
554 // LogicalScreen ----------------------------------------------------------
555 // ------------------------------------------------------------------------
558 * Set the window title.
560 * @param title the new title
563 public void setTitle(final String title
) {
564 swing
.setTitle(title
);
568 * Push the logical screen to the physical device.
571 public void flushPhysical() {
572 // See if it is time to flip the blink time.
573 long nowTime
= System
.currentTimeMillis();
574 if (nowTime
>= blinkMillis
+ lastBlinkTime
) {
575 lastBlinkTime
= nowTime
;
576 cursorBlinkVisible
= !cursorBlinkVisible
;
577 // System.err.println("New lastBlinkTime: " + lastBlinkTime);
580 if ((swing
.getFrame() != null)
581 && (swing
.getBufferStrategy() != null)
586 } while (swing
.getBufferStrategy().contentsRestored());
588 swing
.getBufferStrategy().show();
589 Toolkit
.getDefaultToolkit().sync();
590 } while (swing
.getBufferStrategy().contentsLost());
593 // Non-triple-buffered, call drawToSwing() once
598 // ------------------------------------------------------------------------
599 // TerminalReader ---------------------------------------------------------
600 // ------------------------------------------------------------------------
603 * Check if there are events in the queue.
605 * @return if true, getEvents() has something to return to the backend
607 public boolean hasEvents() {
608 synchronized (eventQueue
) {
609 return (eventQueue
.size() > 0);
614 * Return any events in the IO queue.
616 * @param queue list to append new events to
618 public void getEvents(final List
<TInputEvent
> queue
) {
619 synchronized (eventQueue
) {
620 if (eventQueue
.size() > 0) {
621 synchronized (queue
) {
622 queue
.addAll(eventQueue
);
630 * Restore terminal to normal state.
632 public void closeTerminal() {
637 * Set listener to a different Object.
639 * @param listener the new listening object that run() wakes up on new
642 public void setListener(final Object listener
) {
643 this.listener
= listener
;
646 // ------------------------------------------------------------------------
647 // SwingTerminal ----------------------------------------------------------
648 // ------------------------------------------------------------------------
651 * Get the width of a character cell in pixels.
653 * @return the width in pixels of a character cell
655 public int getTextWidth() {
660 * Get the height of a character cell in pixels.
662 * @return the height in pixels of a character cell
664 public int getTextHeight() {
669 * Setup Swing colors to match DOS color palette.
671 private static void setDOSColors() {
675 MYBLACK
= new Color(0x00, 0x00, 0x00);
676 MYRED
= new Color(0xa8, 0x00, 0x00);
677 MYGREEN
= new Color(0x00, 0xa8, 0x00);
678 MYYELLOW
= new Color(0xa8, 0x54, 0x00);
679 MYBLUE
= new Color(0x00, 0x00, 0xa8);
680 MYMAGENTA
= new Color(0xa8, 0x00, 0xa8);
681 MYCYAN
= new Color(0x00, 0xa8, 0xa8);
682 MYWHITE
= new Color(0xa8, 0xa8, 0xa8);
683 MYBOLD_BLACK
= new Color(0x54, 0x54, 0x54);
684 MYBOLD_RED
= new Color(0xfc, 0x54, 0x54);
685 MYBOLD_GREEN
= new Color(0x54, 0xfc, 0x54);
686 MYBOLD_YELLOW
= new Color(0xfc, 0xfc, 0x54);
687 MYBOLD_BLUE
= new Color(0x54, 0x54, 0xfc);
688 MYBOLD_MAGENTA
= new Color(0xfc, 0x54, 0xfc);
689 MYBOLD_CYAN
= new Color(0x54, 0xfc, 0xfc);
690 MYBOLD_WHITE
= new Color(0xfc, 0xfc, 0xfc);
696 * Get the number of millis to wait before switching the blink from
697 * visible to invisible.
699 * @return the number of milli to wait before switching the blink from
700 * visible to invisible
702 public long getBlinkMillis() {
707 * Get the font size in points.
709 * @return font size in points
711 public int getFontSize() {
716 * Set the font size in points.
718 * @param fontSize font size in points
720 public void setFontSize(final int fontSize
) {
721 this.fontSize
= fontSize
;
722 Font newFont
= font
.deriveFont((float) fontSize
);
727 * Set to a new font, and resize the screen to match its dimensions.
729 * @param font the new font
731 public void setFont(final Font font
) {
735 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
736 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
741 * Set the font to Terminus, the best all-around font for both CP437 and
744 public void getDefaultFont() {
746 ClassLoader loader
= Thread
.currentThread().getContextClassLoader();
747 InputStream in
= loader
.getResourceAsStream(FONTFILE
);
748 Font terminusRoot
= Font
.createFont(Font
.TRUETYPE_FONT
, in
);
749 Font terminus
= terminusRoot
.deriveFont(Font
.PLAIN
, fontSize
);
752 } catch (java
.awt
.FontFormatException e
) {
754 font
= new Font(Font
.MONOSPACED
, Font
.PLAIN
, fontSize
);
755 } catch (java
.io
.IOException e
) {
757 font
= new Font(Font
.MONOSPACED
, Font
.PLAIN
, fontSize
);
764 * Convert a CellAttributes foreground color to an Swing Color.
766 * @param attr the text attributes
767 * @return the Swing Color
769 private Color
attrToForegroundColor(final CellAttributes attr
) {
770 int rgb
= attr
.getForeColorRGB();
772 int red
= (rgb
>> 16) & 0xFF;
773 int green
= (rgb
>> 8) & 0xFF;
774 int blue
= rgb
& 0xFF;
776 return new Color(red
, green
, blue
);
780 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
782 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
784 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
786 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
788 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
789 return MYBOLD_YELLOW
;
790 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
792 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
793 return MYBOLD_MAGENTA
;
794 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
798 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
800 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
802 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
804 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
806 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
808 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
810 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
812 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
816 throw new IllegalArgumentException("Invalid color: " +
817 attr
.getForeColor().getValue());
821 * Convert a CellAttributes background color to an Swing Color.
823 * @param attr the text attributes
824 * @return the Swing Color
826 private Color
attrToBackgroundColor(final CellAttributes attr
) {
827 int rgb
= attr
.getBackColorRGB();
829 int red
= (rgb
>> 16) & 0xFF;
830 int green
= (rgb
>> 8) & 0xFF;
831 int blue
= rgb
& 0xFF;
833 return new Color(red
, green
, blue
);
836 if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLACK
)) {
838 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.RED
)) {
840 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLUE
)) {
842 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.GREEN
)) {
844 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.YELLOW
)) {
846 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.CYAN
)) {
848 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
850 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.WHITE
)) {
853 throw new IllegalArgumentException("Invalid color: " +
854 attr
.getBackColor().getValue());
858 * Figure out what textAdjustX and textAdjustY should be, based on the
859 * location of a vertical bar (to find textAdjustY) and a horizontal bar
860 * (to find textAdjustX).
862 * @return true if textAdjustX and textAdjustY were guessed at correctly
864 private boolean getFontAdjustments() {
865 BufferedImage image
= null;
867 // What SHOULD happen is that the topmost/leftmost white pixel is at
868 // position (gr2x, gr2y). But it might also be off by a pixel in
871 Graphics2D gr2
= null;
874 image
= new BufferedImage(textWidth
* 2, textHeight
* 2,
875 BufferedImage
.TYPE_INT_ARGB
);
877 gr2
= image
.createGraphics();
878 gr2
.setFont(swing
.getFont());
879 gr2
.setColor(java
.awt
.Color
.BLACK
);
880 gr2
.fillRect(0, 0, textWidth
* 2, textHeight
* 2);
881 gr2
.setColor(java
.awt
.Color
.WHITE
);
882 char [] chars
= new char[1];
883 chars
[0] = jexer
.bits
.GraphicsChars
.VERTICAL_BAR
;
884 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ textHeight
- maxDescent
);
887 for (int x
= 0; x
< textWidth
; x
++) {
888 for (int y
= 0; y
< textHeight
; y
++) {
891 System.err.println("X: " + x + " Y: " + y + " " +
895 if ((image
.getRGB(x
, y
) & 0xFFFFFF) != 0) {
896 textAdjustY
= (gr2y
- y
);
898 // System.err.println("textAdjustY: " + textAdjustY);
905 gr2
= image
.createGraphics();
906 gr2
.setFont(swing
.getFont());
907 gr2
.setColor(java
.awt
.Color
.BLACK
);
908 gr2
.fillRect(0, 0, textWidth
* 2, textHeight
* 2);
909 gr2
.setColor(java
.awt
.Color
.WHITE
);
910 chars
[0] = jexer
.bits
.GraphicsChars
.SINGLE_BAR
;
911 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ textHeight
- maxDescent
);
914 for (int x
= 0; x
< textWidth
; x
++) {
915 for (int y
= 0; y
< textHeight
; y
++) {
918 System.err.println("X: " + x + " Y: " + y + " " +
922 if ((image
.getRGB(x
, y
) & 0xFFFFFF) != 0) {
923 textAdjustX
= (gr2x
- x
);
925 // System.err.println("textAdjustX: " + textAdjustX);
931 // Something weird happened, don't rely on this function.
932 // System.err.println("getFontAdjustments: false");
937 * Figure out my font dimensions. This code path works OK for the JFrame
938 * case, and can be called immediately after JFrame creation.
940 private void getFontDimensions() {
942 Graphics gr
= swing
.getGraphics();
946 getFontDimensions(gr
);
950 * Figure out my font dimensions. This code path is needed to lazy-load
951 * the information inside paint().
953 * @param gr Graphics object to use
955 private void getFontDimensions(final Graphics gr
) {
957 FontMetrics fm
= gr
.getFontMetrics();
958 maxDescent
= fm
.getMaxDescent();
959 Rectangle2D bounds
= fm
.getMaxCharBounds(gr
);
960 int leading
= fm
.getLeading();
961 textWidth
= (int)Math
.round(bounds
.getWidth());
962 // textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
964 // This produces the same number, but works better for ugly
966 textHeight
= fm
.getMaxAscent() + maxDescent
- leading
;
968 if (gotTerminus
== true) {
972 if (getFontAdjustments() == false) {
973 // We were unable to programmatically determine textAdjustX and
974 // textAdjustY, so try some guesses based on VM vendor.
975 String runtime
= System
.getProperty("java.runtime.name");
976 if ((runtime
!= null) && (runtime
.contains("Java(TM)"))) {
982 if (sessionInfo
!= null) {
983 sessionInfo
.setTextCellDimensions(textWidth
, textHeight
);
985 gotFontDimensions
= true;
989 * Resize to font dimensions.
991 public void resizeToScreen() {
992 swing
.setDimensions(textWidth
* (width
+ 1), textHeight
* (height
+ 1));
996 * Draw one cell's image to the screen.
998 * @param gr the Swing Graphics context
999 * @param cell the Cell to draw
1000 * @param xPixel the x-coordinate to render to. 0 means the
1001 * left-most pixel column.
1002 * @param yPixel the y-coordinate to render to. 0 means the top-most
1005 private void drawImage(final Graphics gr
, final Cell cell
,
1006 final int xPixel
, final int yPixel
) {
1009 System.err.println("drawImage(): " + xPixel + " " + yPixel +
1013 // Draw the background rectangle, then the foreground character.
1014 assert (cell
.isImage());
1015 gr
.setColor(cell
.getBackground());
1016 gr
.fillRect(xPixel
, yPixel
, textWidth
, textHeight
);
1018 BufferedImage image
= cell
.getImage();
1019 if (image
!= null) {
1020 if (swing
.getFrame() != null) {
1021 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
1023 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
1030 * Draw one glyph to the screen.
1032 * @param gr the Swing Graphics context
1033 * @param cell the Cell to draw
1034 * @param xPixel the x-coordinate to render to. 0 means the
1035 * left-most pixel column.
1036 * @param yPixel the y-coordinate to render to. 0 means the top-most
1039 private void drawGlyph(final Graphics gr
, final Cell cell
,
1040 final int xPixel
, final int yPixel
) {
1043 System.err.println("drawGlyph(): " + xPixel + " " + yPixel +
1047 BufferedImage image
= null;
1048 if (cell
.isBlink() && !cursorBlinkVisible
) {
1049 image
= glyphCacheBlink
.get(cell
);
1051 image
= glyphCache
.get(cell
);
1053 if (image
!= null) {
1054 if (swing
.getFrame() != null) {
1055 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
1057 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
1062 // Generate glyph and draw it.
1063 Graphics2D gr2
= null;
1066 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
1067 image
= new BufferedImage(textWidth
, textHeight
,
1068 BufferedImage
.TYPE_INT_ARGB
);
1069 gr2
= image
.createGraphics();
1070 gr2
.setFont(swing
.getFont());
1074 gr2
= (Graphics2D
) gr
;
1077 Cell cellColor
= new Cell();
1078 cellColor
.setTo(cell
);
1080 // Check for reverse
1081 if (cell
.isReverse()) {
1082 cellColor
.setForeColor(cell
.getBackColor());
1083 cellColor
.setBackColor(cell
.getForeColor());
1086 // Draw the background rectangle, then the foreground character.
1087 gr2
.setColor(attrToBackgroundColor(cellColor
));
1088 gr2
.fillRect(gr2x
, gr2y
, textWidth
, textHeight
);
1090 // Handle blink and underline
1092 || (cell
.isBlink() && cursorBlinkVisible
)
1094 gr2
.setColor(attrToForegroundColor(cellColor
));
1095 char [] chars
= new char[1];
1096 chars
[0] = cell
.getChar();
1097 gr2
.drawChars(chars
, 0, 1, gr2x
+ textAdjustX
,
1098 gr2y
+ textHeight
- maxDescent
+ textAdjustY
);
1100 if (cell
.isUnderline()) {
1101 gr2
.fillRect(gr2x
, gr2y
+ textHeight
- 2, textWidth
, 2);
1105 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
1108 // We need a new key that will not be mutated by
1110 Cell key
= new Cell();
1112 if (cell
.isBlink() && !cursorBlinkVisible
) {
1113 glyphCacheBlink
.put(key
, image
);
1115 glyphCache
.put(key
, image
);
1118 if (swing
.getFrame() != null) {
1119 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
1121 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
1128 * Check if the cursor is visible, and if so draw it.
1130 * @param gr the Swing Graphics context
1132 private void drawCursor(final Graphics gr
) {
1137 && (cursorY
<= height
- 1)
1138 && (cursorX
<= width
- 1)
1139 && cursorBlinkVisible
1141 int xPixel
= cursorX
* textWidth
+ left
;
1142 int yPixel
= cursorY
* textHeight
+ top
;
1143 Cell lCell
= logical
[cursorX
][cursorY
];
1144 gr
.setColor(attrToForegroundColor(lCell
));
1145 switch (cursorStyle
) {
1149 gr
.fillRect(xPixel
, yPixel
+ textHeight
- 2, textWidth
, 2);
1152 gr
.fillRect(xPixel
, yPixel
, textWidth
, textHeight
);
1155 gr
.drawRect(xPixel
, yPixel
, textWidth
- 1, textHeight
- 1);
1162 * Reset the blink timer.
1164 private void resetBlinkTimer() {
1165 lastBlinkTime
= System
.currentTimeMillis();
1166 cursorBlinkVisible
= true;
1170 * Paint redraws the whole screen.
1172 * @param gr the Swing Graphics context
1174 public void paint(final Graphics gr
) {
1176 if (gotFontDimensions
== false) {
1177 // Lazy-load the text width/height
1178 getFontDimensions(gr
);
1180 System.err.println("textWidth " + textWidth +
1181 " textHeight " + textHeight);
1182 System.err.println("FONT: " + swing.getFont() + " font " + font);
1186 if ((swing
.getFrame() != null)
1187 && (swing
.getBufferStrategy() != null)
1188 && (SwingUtilities
.isEventDispatchThread())
1190 // System.err.println("paint(), skip first paint on swing thread");
1195 int xCellMax
= width
;
1197 int yCellMax
= height
;
1199 Rectangle bounds
= gr
.getClipBounds();
1200 if (bounds
!= null) {
1201 // Only update what is in the bounds
1202 xCellMin
= textColumn(bounds
.x
);
1203 xCellMax
= textColumn(bounds
.x
+ bounds
.width
);
1204 if (xCellMax
> width
) {
1207 if (xCellMin
>= xCellMax
) {
1208 xCellMin
= xCellMax
- 2;
1213 yCellMin
= textRow(bounds
.y
);
1214 yCellMax
= textRow(bounds
.y
+ bounds
.height
);
1215 if (yCellMax
> height
) {
1218 if (yCellMin
>= yCellMax
) {
1219 yCellMin
= yCellMax
- 2;
1225 // We need a total repaint
1226 reallyCleared
= true;
1229 // Prevent updates to the screen's data from the TApplication
1231 synchronized (this) {
1234 System.err.printf("bounds %s X %d %d Y %d %d\n",
1235 bounds, xCellMin, xCellMax, yCellMin, yCellMax);
1238 for (int y
= yCellMin
; y
< yCellMax
; y
++) {
1239 for (int x
= xCellMin
; x
< xCellMax
; x
++) {
1241 int xPixel
= x
* textWidth
+ left
;
1242 int yPixel
= y
* textHeight
+ top
;
1244 Cell lCell
= logical
[x
][y
];
1245 Cell pCell
= physical
[x
][y
];
1247 if (!lCell
.equals(pCell
)
1250 || (swing
.getFrame() == null)) {
1252 if (lCell
.isImage()) {
1253 drawImage(gr
, lCell
, xPixel
, yPixel
);
1255 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
1258 // Physical is always updated
1259 physical
[x
][y
].setTo(lCell
);
1265 reallyCleared
= false;
1266 } // synchronized (this)
1270 * Restore terminal to normal state.
1272 public void shutdown() {
1277 * Push the logical screen to the physical device.
1279 private void drawToSwing() {
1282 System.err.printf("drawToSwing(): reallyCleared %s dirty %s\n",
1283 reallyCleared, dirty);
1286 // If reallyCleared is set, we have to draw everything.
1287 if ((swing
.getFrame() != null)
1288 && (swing
.getBufferStrategy() != null)
1289 && (reallyCleared
== true)
1291 // Triple-buffering: we have to redraw everything on this thread.
1292 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1295 swing
.getBufferStrategy().show();
1296 Toolkit
.getDefaultToolkit().sync();
1298 } else if (((swing
.getFrame() != null)
1299 && (swing
.getBufferStrategy() == null))
1300 || (reallyCleared
== true)
1302 // Repaint everything on the Swing thread.
1303 // System.err.println("REPAINT ALL");
1308 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
1309 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1311 synchronized (this) {
1312 for (int y
= 0; y
< height
; y
++) {
1313 for (int x
= 0; x
< width
; x
++) {
1314 Cell lCell
= logical
[x
][y
];
1315 Cell pCell
= physical
[x
][y
];
1317 int xPixel
= x
* textWidth
+ left
;
1318 int yPixel
= y
* textHeight
+ top
;
1320 if (!lCell
.equals(pCell
)
1324 || (lCell
.isBlink())
1326 if (lCell
.isImage()) {
1327 drawImage(gr
, lCell
, xPixel
, yPixel
);
1329 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
1331 physical
[x
][y
].setTo(lCell
);
1336 } // synchronized (this)
1339 swing
.getBufferStrategy().show();
1340 Toolkit
.getDefaultToolkit().sync();
1344 // Swing thread version: request a repaint, but limit it to the area
1345 // that has changed.
1347 // Find the minimum-size damaged region.
1348 int xMin
= swing
.getWidth();
1350 int yMin
= swing
.getHeight();
1353 synchronized (this) {
1354 for (int y
= 0; y
< height
; y
++) {
1355 for (int x
= 0; x
< width
; x
++) {
1356 Cell lCell
= logical
[x
][y
];
1357 Cell pCell
= physical
[x
][y
];
1359 int xPixel
= x
* textWidth
+ left
;
1360 int yPixel
= y
* textHeight
+ top
;
1362 if (!lCell
.equals(pCell
)
1368 if (xPixel
< xMin
) {
1371 if (xPixel
+ textWidth
> xMax
) {
1372 xMax
= xPixel
+ textWidth
;
1374 if (yPixel
< yMin
) {
1377 if (yPixel
+ textHeight
> yMax
) {
1378 yMax
= yPixel
+ textHeight
;
1384 if (xMin
+ textWidth
>= xMax
) {
1387 if (yMin
+ textHeight
>= yMax
) {
1391 // Repaint the desired area
1393 System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax,
1397 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
1398 // This path should never be taken, but is left here for
1400 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
1401 Rectangle bounds
= new Rectangle(xMin
, yMin
, xMax
- xMin
,
1406 swing
.getBufferStrategy().show();
1407 Toolkit
.getDefaultToolkit().sync();
1409 // Repaint on the Swing thread.
1410 swing
.repaint(xMin
, yMin
, xMax
- xMin
, yMax
- yMin
);
1415 * Convert pixel column position to text cell column position.
1417 * @param x pixel column position
1418 * @return text cell column position
1420 public int textColumn(final int x
) {
1421 int column
= ((x
- left
) / textWidth
);
1425 if (column
> width
- 1) {
1432 * Convert pixel row position to text cell row position.
1434 * @param y pixel row position
1435 * @return text cell row position
1437 public int textRow(final int y
) {
1438 int row
= ((y
- top
) / textHeight
);
1442 if (row
> height
- 1) {
1449 * Getter for sessionInfo.
1451 * @return the SessionInfo
1453 public SessionInfo
getSessionInfo() {
1458 * Getter for the underlying Swing component.
1460 * @return the SwingComponent
1462 public SwingComponent
getSwingComponent() {
1466 // ------------------------------------------------------------------------
1467 // KeyListener ------------------------------------------------------------
1468 // ------------------------------------------------------------------------
1471 * Pass Swing keystrokes into the event queue.
1473 * @param key keystroke received
1475 public void keyReleased(final KeyEvent key
) {
1476 // Ignore release events
1480 * Pass Swing keystrokes into the event queue.
1482 * @param key keystroke received
1484 public void keyTyped(final KeyEvent key
) {
1485 // Ignore typed events
1489 * Pass Swing keystrokes into the event queue.
1491 * @param key keystroke received
1493 public void keyPressed(final KeyEvent key
) {
1494 boolean alt
= false;
1495 boolean shift
= false;
1496 boolean ctrl
= false;
1498 boolean isKey
= false;
1499 if (key
.isActionKey()) {
1502 ch
= key
.getKeyChar();
1504 alt
= key
.isAltDown();
1505 ctrl
= key
.isControlDown();
1506 shift
= key
.isShiftDown();
1509 System.err.printf("Swing Key: %s\n", key);
1510 System.err.printf(" isKey: %s\n", isKey);
1511 System.err.printf(" alt: %s\n", alt);
1512 System.err.printf(" ctrl: %s\n", ctrl);
1513 System.err.printf(" shift: %s\n", shift);
1514 System.err.printf(" ch: %s\n", ch);
1517 // Special case: not return the bare modifier presses
1518 switch (key
.getKeyCode()) {
1519 case KeyEvent
.VK_ALT
:
1521 case KeyEvent
.VK_ALT_GRAPH
:
1523 case KeyEvent
.VK_CONTROL
:
1525 case KeyEvent
.VK_SHIFT
:
1527 case KeyEvent
.VK_META
:
1533 TKeypress keypress
= null;
1535 switch (key
.getKeyCode()) {
1536 case KeyEvent
.VK_F1
:
1537 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
1540 case KeyEvent
.VK_F2
:
1541 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
1544 case KeyEvent
.VK_F3
:
1545 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
1548 case KeyEvent
.VK_F4
:
1549 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
1552 case KeyEvent
.VK_F5
:
1553 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
1556 case KeyEvent
.VK_F6
:
1557 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
1560 case KeyEvent
.VK_F7
:
1561 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
1564 case KeyEvent
.VK_F8
:
1565 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
1568 case KeyEvent
.VK_F9
:
1569 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
1572 case KeyEvent
.VK_F10
:
1573 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
1576 case KeyEvent
.VK_F11
:
1577 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
1580 case KeyEvent
.VK_F12
:
1581 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
1584 case KeyEvent
.VK_HOME
:
1585 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
1588 case KeyEvent
.VK_END
:
1589 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
1592 case KeyEvent
.VK_PAGE_UP
:
1593 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
1596 case KeyEvent
.VK_PAGE_DOWN
:
1597 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
1600 case KeyEvent
.VK_INSERT
:
1601 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
1604 case KeyEvent
.VK_DELETE
:
1605 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
1608 case KeyEvent
.VK_RIGHT
:
1609 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
1612 case KeyEvent
.VK_LEFT
:
1613 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
1616 case KeyEvent
.VK_UP
:
1617 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
1620 case KeyEvent
.VK_DOWN
:
1621 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
1624 case KeyEvent
.VK_TAB
:
1625 // Special case: distinguish TAB vs BTAB
1627 keypress
= kbShiftTab
;
1632 case KeyEvent
.VK_ENTER
:
1633 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
1636 case KeyEvent
.VK_ESCAPE
:
1637 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
1640 case KeyEvent
.VK_BACK_SPACE
:
1641 keypress
= kbBackspace
;
1644 // Unsupported, ignore
1649 if (keypress
== null) {
1652 // Disambiguate ^H from Backspace.
1653 if (KeyEvent
.getKeyText(key
.getKeyCode()).equals("H")) {
1655 keypress
= kbBackspace
;
1657 // We are emulating Xterm here, where the backspace key
1658 // on the keyboard returns ^?.
1659 keypress
= kbBackspaceDel
;
1673 keypress
= kbShiftTab
;
1682 if (!alt
&& ctrl
&& !shift
) {
1683 ch
= KeyEvent
.getKeyText(key
.getKeyCode()).charAt(0);
1685 // Not a special key, put it together
1686 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
1690 // Save it and we are done.
1691 synchronized (eventQueue
) {
1692 eventQueue
.add(new TKeypressEvent(keypress
));
1695 if (listener
!= null) {
1696 synchronized (listener
) {
1697 listener
.notifyAll();
1702 // ------------------------------------------------------------------------
1703 // WindowListener ---------------------------------------------------------
1704 // ------------------------------------------------------------------------
1707 * Pass window events into the event queue.
1709 * @param event window event received
1711 public void windowActivated(final WindowEvent event
) {
1712 // Force a total repaint
1713 synchronized (this) {
1719 * Pass window events into the event queue.
1721 * @param event window event received
1723 public void windowClosed(final WindowEvent event
) {
1728 * Pass window events into the event queue.
1730 * @param event window event received
1732 public void windowClosing(final WindowEvent event
) {
1733 // Drop a cmAbort and walk away
1734 synchronized (eventQueue
) {
1735 eventQueue
.add(new TCommandEvent(cmAbort
));
1738 if (listener
!= null) {
1739 synchronized (listener
) {
1740 listener
.notifyAll();
1746 * Pass window events into the event queue.
1748 * @param event window event received
1750 public void windowDeactivated(final WindowEvent event
) {
1755 * Pass window events into the event queue.
1757 * @param event window event received
1759 public void windowDeiconified(final WindowEvent event
) {
1764 * Pass window events into the event queue.
1766 * @param event window event received
1768 public void windowIconified(final WindowEvent event
) {
1773 * Pass window events into the event queue.
1775 * @param event window event received
1777 public void windowOpened(final WindowEvent event
) {
1781 // ------------------------------------------------------------------------
1782 // ComponentListener ------------------------------------------------------
1783 // ------------------------------------------------------------------------
1786 * Pass component events into the event queue.
1788 * @param event component event received
1790 public void componentHidden(final ComponentEvent event
) {
1795 * Pass component events into the event queue.
1797 * @param event component event received
1799 public void componentShown(final ComponentEvent event
) {
1804 * Pass component events into the event queue.
1806 * @param event component event received
1808 public void componentMoved(final ComponentEvent event
) {
1813 * Pass component events into the event queue.
1815 * @param event component event received
1817 public void componentResized(final ComponentEvent event
) {
1818 if (gotFontDimensions
== false) {
1819 // We are still waiting to get font information. Don't pass a
1821 // System.err.println("size " + swing.getComponent().getSize());
1825 // Drop a new TResizeEvent into the queue
1826 sessionInfo
.queryWindowSize();
1827 synchronized (eventQueue
) {
1828 TResizeEvent windowResize
= new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
1829 sessionInfo
.getWindowWidth(), sessionInfo
.getWindowHeight());
1830 eventQueue
.add(windowResize
);
1833 System.err.println("Add resize event: " + windowResize.getWidth() +
1834 " x " + windowResize.getHeight());
1837 if (listener
!= null) {
1838 synchronized (listener
) {
1839 listener
.notifyAll();
1844 // ------------------------------------------------------------------------
1845 // MouseMotionListener ----------------------------------------------------
1846 // ------------------------------------------------------------------------
1849 * Pass mouse events into the event queue.
1851 * @param mouse mouse event received
1853 public void mouseDragged(final MouseEvent mouse
) {
1854 int modifiers
= mouse
.getModifiersEx();
1855 boolean eventMouse1
= false;
1856 boolean eventMouse2
= false;
1857 boolean eventMouse3
= false;
1858 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
1861 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
1864 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
1867 mouse1
= eventMouse1
;
1868 mouse2
= eventMouse2
;
1869 mouse3
= eventMouse3
;
1870 int x
= textColumn(mouse
.getX());
1871 int y
= textRow(mouse
.getY());
1873 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
1874 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
1876 synchronized (eventQueue
) {
1877 eventQueue
.add(mouseEvent
);
1880 if (listener
!= null) {
1881 synchronized (listener
) {
1882 listener
.notifyAll();
1888 * Pass mouse events into the event queue.
1890 * @param mouse mouse event received
1892 public void mouseMoved(final MouseEvent mouse
) {
1893 int x
= textColumn(mouse
.getX());
1894 int y
= textRow(mouse
.getY());
1895 if ((x
== oldMouseX
) && (y
== oldMouseY
)) {
1896 // Bail out, we've moved some pixels but not a whole text cell.
1902 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
1903 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
1905 synchronized (eventQueue
) {
1906 eventQueue
.add(mouseEvent
);
1909 if (listener
!= null) {
1910 synchronized (listener
) {
1911 listener
.notifyAll();
1916 // ------------------------------------------------------------------------
1917 // MouseListener ----------------------------------------------------------
1918 // ------------------------------------------------------------------------
1921 * Pass mouse events into the event queue.
1923 * @param mouse mouse event received
1925 public void mouseClicked(final MouseEvent mouse
) {
1930 * Pass mouse events into the event queue.
1932 * @param mouse mouse event received
1934 public void mouseEntered(final MouseEvent mouse
) {
1939 * Pass mouse events into the event queue.
1941 * @param mouse mouse event received
1943 public void mouseExited(final MouseEvent mouse
) {
1948 * Pass mouse events into the event queue.
1950 * @param mouse mouse event received
1952 public void mousePressed(final MouseEvent mouse
) {
1953 int modifiers
= mouse
.getModifiersEx();
1954 boolean eventMouse1
= false;
1955 boolean eventMouse2
= false;
1956 boolean eventMouse3
= false;
1957 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
1960 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
1963 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
1966 mouse1
= eventMouse1
;
1967 mouse2
= eventMouse2
;
1968 mouse3
= eventMouse3
;
1969 int x
= textColumn(mouse
.getX());
1970 int y
= textRow(mouse
.getY());
1972 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
1973 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
1975 synchronized (eventQueue
) {
1976 eventQueue
.add(mouseEvent
);
1979 if (listener
!= null) {
1980 synchronized (listener
) {
1981 listener
.notifyAll();
1987 * Pass mouse events into the event queue.
1989 * @param mouse mouse event received
1991 public void mouseReleased(final MouseEvent mouse
) {
1992 int modifiers
= mouse
.getModifiersEx();
1993 boolean eventMouse1
= false;
1994 boolean eventMouse2
= false;
1995 boolean eventMouse3
= false;
1996 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
1999 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2002 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2017 int x
= textColumn(mouse
.getX());
2018 int y
= textRow(mouse
.getY());
2020 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
2021 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false);
2023 synchronized (eventQueue
) {
2024 eventQueue
.add(mouseEvent
);
2027 if (listener
!= null) {
2028 synchronized (listener
) {
2029 listener
.notifyAll();
2034 // ------------------------------------------------------------------------
2035 // MouseWheelListener -----------------------------------------------------
2036 // ------------------------------------------------------------------------
2039 * Pass mouse events into the event queue.
2041 * @param mouse mouse event received
2043 public void mouseWheelMoved(final MouseWheelEvent mouse
) {
2044 int modifiers
= mouse
.getModifiersEx();
2045 boolean eventMouse1
= false;
2046 boolean eventMouse2
= false;
2047 boolean eventMouse3
= false;
2048 boolean mouseWheelUp
= false;
2049 boolean mouseWheelDown
= false;
2050 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
2053 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
2056 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
2059 mouse1
= eventMouse1
;
2060 mouse2
= eventMouse2
;
2061 mouse3
= eventMouse3
;
2062 int x
= textColumn(mouse
.getX());
2063 int y
= textRow(mouse
.getY());
2064 if (mouse
.getWheelRotation() > 0) {
2065 mouseWheelDown
= true;
2067 if (mouse
.getWheelRotation() < 0) {
2068 mouseWheelUp
= true;
2071 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
2072 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
);
2074 synchronized (eventQueue
) {
2075 eventQueue
.add(mouseEvent
);
2078 if (listener
!= null) {
2079 synchronized (listener
) {
2080 listener
.notifyAll();