2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 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
.event
.ComponentEvent
;
40 import java
.awt
.event
.ComponentListener
;
41 import java
.awt
.event
.KeyEvent
;
42 import java
.awt
.event
.KeyListener
;
43 import java
.awt
.event
.MouseEvent
;
44 import java
.awt
.event
.MouseListener
;
45 import java
.awt
.event
.MouseMotionListener
;
46 import java
.awt
.event
.MouseWheelEvent
;
47 import java
.awt
.event
.MouseWheelListener
;
48 import java
.awt
.event
.WindowEvent
;
49 import java
.awt
.event
.WindowListener
;
50 import java
.awt
.geom
.Rectangle2D
;
51 import java
.awt
.image
.BufferedImage
;
52 import java
.io
.InputStream
;
53 import java
.util
.Date
;
54 import java
.util
.HashMap
;
55 import java
.util
.LinkedList
;
56 import java
.util
.List
;
57 import javax
.swing
.JComponent
;
58 import javax
.swing
.JFrame
;
59 import javax
.swing
.SwingUtilities
;
61 import jexer
.TKeypress
;
62 import jexer
.bits
.Cell
;
63 import jexer
.bits
.CellAttributes
;
64 import jexer
.event
.TCommandEvent
;
65 import jexer
.event
.TInputEvent
;
66 import jexer
.event
.TKeypressEvent
;
67 import jexer
.event
.TMouseEvent
;
68 import jexer
.event
.TResizeEvent
;
69 import static jexer
.TCommand
.*;
70 import static jexer
.TKeypress
.*;
73 * This Screen backend reads keystrokes and mouse events and draws to either
74 * a Java Swing JFrame (potentially triple-buffered) or a JComponent.
76 * This class is a bit of an inversion of typical GUI classes. It performs
77 * all of the drawing logic from SwingTerminal (which is not a Swing class),
78 * and uses a SwingComponent wrapper class to call the JFrame or JComponent
81 public final class SwingTerminal
extends LogicalScreen
82 implements TerminalReader
,
83 ComponentListener
, KeyListener
,
84 MouseListener
, MouseMotionListener
,
85 MouseWheelListener
, WindowListener
{
88 * The Swing component or frame to draw to.
90 private SwingComponent swing
;
92 // ------------------------------------------------------------------------
93 // Screen -----------------------------------------------------------------
94 // ------------------------------------------------------------------------
97 * Cursor style to draw.
99 public enum CursorStyle
{
101 * Use an underscore for the cursor.
106 * Use a solid block for the cursor.
111 * Use an outlined block for the cursor.
117 * A cache of previously-rendered glyphs for blinking text, when it is
120 private HashMap
<Cell
, BufferedImage
> glyphCacheBlink
;
123 * A cache of previously-rendered glyphs for non-blinking, or
124 * blinking-and-visible, text.
126 private HashMap
<Cell
, BufferedImage
> glyphCache
;
128 // Colors to map DOS colors to AWT colors.
129 private static Color MYBLACK
;
130 private static Color MYRED
;
131 private static Color MYGREEN
;
132 private static Color MYYELLOW
;
133 private static Color MYBLUE
;
134 private static Color MYMAGENTA
;
135 private static Color MYCYAN
;
136 private static Color MYWHITE
;
137 private static Color MYBOLD_BLACK
;
138 private static Color MYBOLD_RED
;
139 private static Color MYBOLD_GREEN
;
140 private static Color MYBOLD_YELLOW
;
141 private static Color MYBOLD_BLUE
;
142 private static Color MYBOLD_MAGENTA
;
143 private static Color MYBOLD_CYAN
;
144 private static Color MYBOLD_WHITE
;
147 * When true, all the MYBLACK, MYRED, etc. colors are set.
149 private static boolean dosColors
= false;
152 * Setup Swing colors to match DOS color palette.
154 private static void setDOSColors() {
158 MYBLACK
= new Color(0x00, 0x00, 0x00);
159 MYRED
= new Color(0xa8, 0x00, 0x00);
160 MYGREEN
= new Color(0x00, 0xa8, 0x00);
161 MYYELLOW
= new Color(0xa8, 0x54, 0x00);
162 MYBLUE
= new Color(0x00, 0x00, 0xa8);
163 MYMAGENTA
= new Color(0xa8, 0x00, 0xa8);
164 MYCYAN
= new Color(0x00, 0xa8, 0xa8);
165 MYWHITE
= new Color(0xa8, 0xa8, 0xa8);
166 MYBOLD_BLACK
= new Color(0x54, 0x54, 0x54);
167 MYBOLD_RED
= new Color(0xfc, 0x54, 0x54);
168 MYBOLD_GREEN
= new Color(0x54, 0xfc, 0x54);
169 MYBOLD_YELLOW
= new Color(0xfc, 0xfc, 0x54);
170 MYBOLD_BLUE
= new Color(0x54, 0x54, 0xfc);
171 MYBOLD_MAGENTA
= new Color(0xfc, 0x54, 0xfc);
172 MYBOLD_CYAN
= new Color(0x54, 0xfc, 0xfc);
173 MYBOLD_WHITE
= new Color(0xfc, 0xfc, 0xfc);
179 * The terminus font resource filename.
181 private static final String FONTFILE
= "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
184 * If true, we were successful getting Terminus.
186 private boolean gotTerminus
= false;
189 * If true, we were successful at getting the font dimensions.
191 private boolean gotFontDimensions
= false;
194 * The currently selected font.
196 private Font font
= null;
199 * The currently selected font size in points.
201 private int fontSize
= 16;
204 * Width of a character cell in pixels.
206 private int textWidth
= 1;
209 * Height of a character cell in pixels.
211 private int textHeight
= 1;
214 * Descent of a character cell in pixels.
216 private int maxDescent
= 0;
219 * System-dependent Y adjustment for text in the character cell.
221 private int textAdjustY
= 0;
224 * System-dependent X adjustment for text in the character cell.
226 private int textAdjustX
= 0;
229 * Top pixel absolute location.
231 private int top
= 30;
234 * Left pixel absolute location.
236 private int left
= 30;
239 * The cursor style to draw.
241 private CursorStyle cursorStyle
= CursorStyle
.UNDERLINE
;
244 * The number of millis to wait before switching the blink from
245 * visible to invisible.
247 private long blinkMillis
= 500;
250 * If true, the cursor should be visible right now based on the blink
253 private boolean cursorBlinkVisible
= true;
256 * The time that the blink last flipped from visible to invisible or
257 * from invisible to visible.
259 private long lastBlinkTime
= 0;
262 * Get the font size in points.
264 * @return font size in points
266 public int getFontSize() {
271 * Set the font size in points.
273 * @param fontSize font size in points
275 public void setFontSize(final int fontSize
) {
276 this.fontSize
= fontSize
;
277 Font newFont
= font
.deriveFont((float) fontSize
);
282 * Set to a new font, and resize the screen to match its dimensions.
284 * @param font the new font
286 public void setFont(final Font font
) {
290 glyphCacheBlink
= new HashMap
<Cell
, BufferedImage
>();
291 glyphCache
= new HashMap
<Cell
, BufferedImage
>();
296 * Set the font to Terminus, the best all-around font for both CP437 and
299 public void getDefaultFont() {
301 ClassLoader loader
= Thread
.currentThread().
302 getContextClassLoader();
303 InputStream in
= loader
.getResourceAsStream(FONTFILE
);
304 Font terminusRoot
= Font
.createFont(Font
.TRUETYPE_FONT
, in
);
305 Font terminus
= terminusRoot
.deriveFont(Font
.PLAIN
, fontSize
);
308 } catch (Exception e
) {
310 font
= new Font(Font
.MONOSPACED
, Font
.PLAIN
, fontSize
);
317 * Convert a CellAttributes foreground color to an Swing Color.
319 * @param attr the text attributes
320 * @return the Swing Color
322 private Color
attrToForegroundColor(final CellAttributes attr
) {
324 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
326 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
328 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
330 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
332 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
333 return MYBOLD_YELLOW
;
334 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
336 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
337 return MYBOLD_MAGENTA
;
338 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
342 if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLACK
)) {
344 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.RED
)) {
346 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.BLUE
)) {
348 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.GREEN
)) {
350 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.YELLOW
)) {
352 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.CYAN
)) {
354 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
356 } else if (attr
.getForeColor().equals(jexer
.bits
.Color
.WHITE
)) {
360 throw new IllegalArgumentException("Invalid color: " +
361 attr
.getForeColor().getValue());
365 * Convert a CellAttributes background color to an Swing Color.
367 * @param attr the text attributes
368 * @return the Swing Color
370 private Color
attrToBackgroundColor(final CellAttributes attr
) {
371 if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLACK
)) {
373 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.RED
)) {
375 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.BLUE
)) {
377 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.GREEN
)) {
379 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.YELLOW
)) {
381 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.CYAN
)) {
383 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.MAGENTA
)) {
385 } else if (attr
.getBackColor().equals(jexer
.bits
.Color
.WHITE
)) {
388 throw new IllegalArgumentException("Invalid color: " +
389 attr
.getBackColor().getValue());
393 * Figure out what textAdjustX and textAdjustY should be, based on the
394 * location of a vertical bar (to find textAdjustY) and a horizontal bar
395 * (to find textAdjustX).
397 * @return true if textAdjustX and textAdjustY were guessed at correctly
399 private boolean getFontAdjustments() {
400 BufferedImage image
= null;
402 // What SHOULD happen is that the topmost/leftmost white pixel is at
403 // position (gr2x, gr2y). But it might also be off by a pixel in
406 Graphics2D gr2
= null;
409 image
= new BufferedImage(textWidth
* 2, textHeight
* 2,
410 BufferedImage
.TYPE_INT_ARGB
);
412 gr2
= image
.createGraphics();
413 gr2
.setFont(swing
.getFont());
414 gr2
.setColor(java
.awt
.Color
.BLACK
);
415 gr2
.fillRect(0, 0, textWidth
* 2, textHeight
* 2);
416 gr2
.setColor(java
.awt
.Color
.WHITE
);
417 char [] chars
= new char[1];
418 chars
[0] = jexer
.bits
.GraphicsChars
.VERTICAL_BAR
;
419 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ textHeight
- maxDescent
);
422 for (int x
= 0; x
< textWidth
; x
++) {
423 for (int y
= 0; y
< textHeight
; y
++) {
426 System.err.println("X: " + x + " Y: " + y + " " +
430 if ((image
.getRGB(x
, y
) & 0xFFFFFF) != 0) {
431 textAdjustY
= (gr2y
- y
);
433 // System.err.println("textAdjustY: " + textAdjustY);
440 gr2
= image
.createGraphics();
441 gr2
.setFont(swing
.getFont());
442 gr2
.setColor(java
.awt
.Color
.BLACK
);
443 gr2
.fillRect(0, 0, textWidth
* 2, textHeight
* 2);
444 gr2
.setColor(java
.awt
.Color
.WHITE
);
445 chars
[0] = jexer
.bits
.GraphicsChars
.SINGLE_BAR
;
446 gr2
.drawChars(chars
, 0, 1, gr2x
, gr2y
+ textHeight
- maxDescent
);
449 for (int x
= 0; x
< textWidth
; x
++) {
450 for (int y
= 0; y
< textHeight
; y
++) {
453 System.err.println("X: " + x + " Y: " + y + " " +
457 if ((image
.getRGB(x
, y
) & 0xFFFFFF) != 0) {
458 textAdjustX
= (gr2x
- x
);
460 // System.err.println("textAdjustX: " + textAdjustX);
466 // Something weird happened, don't rely on this function.
467 // System.err.println("getFontAdjustments: false");
472 * Figure out my font dimensions. This code path works OK for the JFrame
473 * case, and can be called immediately after JFrame creation.
475 private void getFontDimensions() {
477 Graphics gr
= swing
.getGraphics();
481 getFontDimensions(gr
);
485 * Figure out my font dimensions. This code path is needed to lazy-load
486 * the information inside paint().
488 * @param gr Graphics object to use
490 private void getFontDimensions(final Graphics gr
) {
492 FontMetrics fm
= gr
.getFontMetrics();
493 maxDescent
= fm
.getMaxDescent();
494 Rectangle2D bounds
= fm
.getMaxCharBounds(gr
);
495 int leading
= fm
.getLeading();
496 textWidth
= (int)Math
.round(bounds
.getWidth());
497 // textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
499 // This produces the same number, but works better for ugly
501 textHeight
= fm
.getMaxAscent() + maxDescent
- leading
;
503 if (gotTerminus
== true) {
507 if (getFontAdjustments() == false) {
508 // We were unable to programmatically determine textAdjustX and
509 // textAdjustY, so try some guesses based on VM vendor.
510 String runtime
= System
.getProperty("java.runtime.name");
511 if ((runtime
!= null) && (runtime
.contains("Java(TM)"))) {
517 if (sessionInfo
!= null) {
518 sessionInfo
.setTextCellDimensions(textWidth
, textHeight
);
520 gotFontDimensions
= true;
524 * Resize to font dimensions.
526 public void resizeToScreen() {
527 swing
.setDimensions(textWidth
* width
, textHeight
* height
);
531 * Draw one glyph to the screen.
533 * @param gr the Swing Graphics context
534 * @param cell the Cell to draw
535 * @param xPixel the x-coordinate to render to. 0 means the
536 * left-most pixel column.
537 * @param yPixel the y-coordinate to render to. 0 means the top-most
540 private void drawGlyph(final Graphics gr
, final Cell cell
,
541 final int xPixel
, final int yPixel
) {
544 System.err.println("drawGlyph(): " + xPixel + " " + yPixel +
548 BufferedImage image
= null;
549 if (cell
.isBlink() && !cursorBlinkVisible
) {
550 image
= glyphCacheBlink
.get(cell
);
552 image
= glyphCache
.get(cell
);
555 if (swing
.getFrame() != null) {
556 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
558 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
563 // Generate glyph and draw it.
564 Graphics2D gr2
= null;
567 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
568 image
= new BufferedImage(textWidth
, textHeight
,
569 BufferedImage
.TYPE_INT_ARGB
);
570 gr2
= image
.createGraphics();
571 gr2
.setFont(swing
.getFont());
575 gr2
= (Graphics2D
) gr
;
578 Cell cellColor
= new Cell();
579 cellColor
.setTo(cell
);
582 if (cell
.isReverse()) {
583 cellColor
.setForeColor(cell
.getBackColor());
584 cellColor
.setBackColor(cell
.getForeColor());
587 // Draw the background rectangle, then the foreground character.
588 gr2
.setColor(attrToBackgroundColor(cellColor
));
589 gr2
.fillRect(gr2x
, gr2y
, textWidth
, textHeight
);
591 // Handle blink and underline
593 || (cell
.isBlink() && cursorBlinkVisible
)
595 gr2
.setColor(attrToForegroundColor(cellColor
));
596 char [] chars
= new char[1];
597 chars
[0] = cell
.getChar();
598 gr2
.drawChars(chars
, 0, 1, gr2x
+ textAdjustX
,
599 gr2y
+ textHeight
- maxDescent
+ textAdjustY
);
601 if (cell
.isUnderline()) {
602 gr2
.fillRect(gr2x
, gr2y
+ textHeight
- 2, textWidth
, 2);
606 if ((SwingComponent
.tripleBuffer
) && (swing
.getFrame() != null)) {
609 // We need a new key that will not be mutated by
611 Cell key
= new Cell();
613 if (cell
.isBlink() && !cursorBlinkVisible
) {
614 glyphCacheBlink
.put(key
, image
);
616 glyphCache
.put(key
, image
);
619 if (swing
.getFrame() != null) {
620 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getFrame());
622 gr
.drawImage(image
, xPixel
, yPixel
, swing
.getComponent());
629 * Check if the cursor is visible, and if so draw it.
631 * @param gr the Swing Graphics context
633 private void drawCursor(final Graphics gr
) {
638 && (cursorY
<= height
- 1)
639 && (cursorX
<= width
- 1)
640 && cursorBlinkVisible
642 int xPixel
= cursorX
* textWidth
+ left
;
643 int yPixel
= cursorY
* textHeight
+ top
;
644 Cell lCell
= logical
[cursorX
][cursorY
];
645 gr
.setColor(attrToForegroundColor(lCell
));
646 switch (cursorStyle
) {
650 gr
.fillRect(xPixel
, yPixel
+ textHeight
- 2, textWidth
, 2);
653 gr
.fillRect(xPixel
, yPixel
, textWidth
, textHeight
);
656 gr
.drawRect(xPixel
, yPixel
, textWidth
- 1, textHeight
- 1);
663 * Reset the blink timer.
665 private void resetBlinkTimer() {
666 // See if it is time to flip the blink time.
667 long nowTime
= (new Date()).getTime();
668 lastBlinkTime
= nowTime
;
669 cursorBlinkVisible
= true;
673 * Paint redraws the whole screen.
675 * @param gr the Swing Graphics context
677 public void paint(final Graphics gr
) {
679 if (gotFontDimensions
== false) {
680 // Lazy-load the text width/height
681 // System.err.println("calling getFontDimensions...");
682 getFontDimensions(gr
);
684 System.err.println("textWidth " + textWidth +
685 " textHeight " + textHeight);
686 System.err.println("FONT: " + swing.getFont() + " font " + font);
691 // See if it is time to flip the blink time.
692 long nowTime
= (new Date()).getTime();
693 if (nowTime
> blinkMillis
+ lastBlinkTime
) {
694 lastBlinkTime
= nowTime
;
695 cursorBlinkVisible
= !cursorBlinkVisible
;
699 int xCellMax
= width
;
701 int yCellMax
= height
;
703 Rectangle bounds
= gr
.getClipBounds();
704 if (bounds
!= null) {
705 // Only update what is in the bounds
706 xCellMin
= textColumn(bounds
.x
);
707 xCellMax
= textColumn(bounds
.x
+ bounds
.width
);
708 if (xCellMax
> width
) {
711 if (xCellMin
>= xCellMax
) {
712 xCellMin
= xCellMax
- 2;
717 yCellMin
= textRow(bounds
.y
);
718 yCellMax
= textRow(bounds
.y
+ bounds
.height
);
719 if (yCellMax
> height
) {
722 if (yCellMin
>= yCellMax
) {
723 yCellMin
= yCellMax
- 2;
729 // We need a total repaint
730 reallyCleared
= true;
733 // Prevent updates to the screen's data from the TApplication
735 synchronized (this) {
738 System.err.printf("bounds %s X %d %d Y %d %d\n",
739 bounds, xCellMin, xCellMax, yCellMin, yCellMax);
742 for (int y
= yCellMin
; y
< yCellMax
; y
++) {
743 for (int x
= xCellMin
; x
< xCellMax
; x
++) {
745 int xPixel
= x
* textWidth
+ left
;
746 int yPixel
= y
* textHeight
+ top
;
748 Cell lCell
= logical
[x
][y
];
749 Cell pCell
= physical
[x
][y
];
751 if (!lCell
.equals(pCell
)
754 || (swing
.getFrame() == null)) {
756 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
758 // Physical is always updated
759 physical
[x
][y
].setTo(lCell
);
766 reallyCleared
= false;
767 } // synchronized (this)
771 * Restore terminal to normal state.
773 public void shutdown() {
778 * Push the logical screen to the physical device.
781 public void flushPhysical() {
784 System.err.printf("flushPhysical(): reallyCleared %s dirty %s\n",
785 reallyCleared, dirty);
788 // If reallyCleared is set, we have to draw everything.
789 if ((swing
.getFrame() != null)
790 && (swing
.getBufferStrategy() != null)
791 && (reallyCleared
== true)
793 // Triple-buffering: we have to redraw everything on this thread.
794 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
797 swing
.getBufferStrategy().show();
798 // sync() doesn't seem to help the tearing for me.
799 // Toolkit.getDefaultToolkit().sync();
801 } else if (((swing
.getFrame() != null)
802 && (swing
.getBufferStrategy() == null))
803 || (reallyCleared
== true)
805 // Repaint everything on the Swing thread.
806 // System.err.println("REPAINT ALL");
811 // Do nothing if nothing happened.
816 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
817 // See if it is time to flip the blink time.
818 long nowTime
= (new Date()).getTime();
819 if (nowTime
> blinkMillis
+ lastBlinkTime
) {
820 lastBlinkTime
= nowTime
;
821 cursorBlinkVisible
= !cursorBlinkVisible
;
824 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
826 synchronized (this) {
827 for (int y
= 0; y
< height
; y
++) {
828 for (int x
= 0; x
< width
; x
++) {
829 Cell lCell
= logical
[x
][y
];
830 Cell pCell
= physical
[x
][y
];
832 int xPixel
= x
* textWidth
+ left
;
833 int yPixel
= y
* textHeight
+ top
;
835 if (!lCell
.equals(pCell
)
841 drawGlyph(gr
, lCell
, xPixel
, yPixel
);
842 physical
[x
][y
].setTo(lCell
);
847 } // synchronized (this)
850 swing
.getBufferStrategy().show();
851 // sync() doesn't seem to help the tearing for me.
852 // Toolkit.getDefaultToolkit().sync();
856 // Swing thread version: request a repaint, but limit it to the area
859 // Find the minimum-size damaged region.
860 int xMin
= swing
.getWidth();
862 int yMin
= swing
.getHeight();
865 synchronized (this) {
866 for (int y
= 0; y
< height
; y
++) {
867 for (int x
= 0; x
< width
; x
++) {
868 Cell lCell
= logical
[x
][y
];
869 Cell pCell
= physical
[x
][y
];
871 int xPixel
= x
* textWidth
+ left
;
872 int yPixel
= y
* textHeight
+ top
;
874 if (!lCell
.equals(pCell
)
883 if (xPixel
+ textWidth
> xMax
) {
884 xMax
= xPixel
+ textWidth
;
889 if (yPixel
+ textHeight
> yMax
) {
890 yMax
= yPixel
+ textHeight
;
896 if (xMin
+ textWidth
>= xMax
) {
899 if (yMin
+ textHeight
>= yMax
) {
903 // Repaint the desired area
905 System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax,
909 if ((swing
.getFrame() != null) && (swing
.getBufferStrategy() != null)) {
910 // This path should never be taken, but is left here for
912 Graphics gr
= swing
.getBufferStrategy().getDrawGraphics();
913 Rectangle bounds
= new Rectangle(xMin
, yMin
, xMax
- xMin
,
918 swing
.getBufferStrategy().show();
919 // sync() doesn't seem to help the tearing for me.
920 // Toolkit.getDefaultToolkit().sync();
922 // Repaint on the Swing thread.
923 swing
.repaint(xMin
, yMin
, xMax
- xMin
, yMax
- yMin
);
928 * Put the cursor at (x,y).
930 * @param visible if true, the cursor should be visible
931 * @param x column coordinate to put the cursor on
932 * @param y row coordinate to put the cursor on
935 public void putCursor(final boolean visible
, final int x
, final int y
) {
937 if ((visible
== cursorVisible
) && ((x
== cursorX
) && (y
== cursorY
))) {
938 // See if it is time to flip the blink time.
939 long nowTime
= (new Date()).getTime();
940 if (nowTime
< blinkMillis
+ lastBlinkTime
) {
941 // Nothing has changed, so don't do anything.
949 && (cursorY
<= height
- 1)
950 && (cursorX
<= width
- 1)
952 // Make the current cursor position dirty
953 if (physical
[cursorX
][cursorY
].getChar() == 'Q') {
954 physical
[cursorX
][cursorY
].setChar('X');
956 physical
[cursorX
][cursorY
].setChar('Q');
960 super.putCursor(visible
, x
, y
);
964 * Convert pixel column position to text cell column position.
966 * @param x pixel column position
967 * @return text cell column position
969 public int textColumn(final int x
) {
970 return ((x
- left
) / textWidth
);
974 * Convert pixel row position to text cell row position.
976 * @param y pixel row position
977 * @return text cell row position
979 public int textRow(final int y
) {
980 return ((y
- top
) / textHeight
);
984 * Set the window title.
986 * @param title the new title
988 public void setTitle(final String title
) {
989 swing
.setTitle(title
);
992 // ------------------------------------------------------------------------
993 // TerminalReader ---------------------------------------------------------
994 // ------------------------------------------------------------------------
997 * The session information.
999 private SwingSessionInfo sessionInfo
;
1002 * Getter for sessionInfo.
1004 * @return the SessionInfo
1006 public SessionInfo
getSessionInfo() {
1011 * The listening object that run() wakes up on new input.
1013 private Object listener
;
1016 * Set listener to a different Object.
1018 * @param listener the new listening object that run() wakes up on new
1021 public void setListener(final Object listener
) {
1022 this.listener
= listener
;
1026 * The event queue, filled up by a thread reading on input.
1028 private List
<TInputEvent
> eventQueue
;
1031 * The last reported mouse X position.
1033 private int oldMouseX
= -1;
1036 * The last reported mouse Y position.
1038 private int oldMouseY
= -1;
1041 * true if mouse1 was down. Used to report mouse1 on the release event.
1043 private boolean mouse1
= false;
1046 * true if mouse2 was down. Used to report mouse2 on the release event.
1048 private boolean mouse2
= false;
1051 * true if mouse3 was down. Used to report mouse3 on the release event.
1053 private boolean mouse3
= false;
1056 * Public constructor creates a new JFrame to render to.
1058 * @param windowWidth the number of text columns to start with
1059 * @param windowHeight the number of text rows to start with
1060 * @param fontSize the size in points. Good values to pick are: 16, 20,
1062 * @param listener the object this backend needs to wake up when new
1065 public SwingTerminal(final int windowWidth
, final int windowHeight
,
1066 final int fontSize
, final Object listener
) {
1068 this.fontSize
= fontSize
;
1072 // Figure out my cursor style.
1073 String cursorStyleString
= System
.getProperty(
1074 "jexer.Swing.cursorStyle", "underline").toLowerCase();
1075 if (cursorStyleString
.equals("underline")) {
1076 cursorStyle
= CursorStyle
.UNDERLINE
;
1077 } else if (cursorStyleString
.equals("outline")) {
1078 cursorStyle
= CursorStyle
.OUTLINE
;
1079 } else if (cursorStyleString
.equals("block")) {
1080 cursorStyle
= CursorStyle
.BLOCK
;
1083 // Pull the system property for triple buffering.
1084 if (System
.getProperty("jexer.Swing.tripleBuffer") != null) {
1085 if (System
.getProperty("jexer.Swing.tripleBuffer").equals("true")) {
1086 SwingComponent
.tripleBuffer
= true;
1088 SwingComponent
.tripleBuffer
= false;
1093 SwingUtilities
.invokeAndWait(new Runnable() {
1096 JFrame frame
= new JFrame() {
1099 * Serializable version.
1101 private static final long serialVersionUID
= 1;
1104 * The code that performs the actual drawing.
1106 public SwingTerminal screen
= null;
1109 * Anonymous class initializer saves the screen
1110 * reference, so that paint() and the like call out
1114 this.screen
= SwingTerminal
.this;
1118 * Update redraws the whole screen.
1120 * @param gr the Swing Graphics context
1123 public void update(final Graphics gr
) {
1124 // The default update clears the area. Don't do
1125 // that, instead just paint it directly.
1130 * Paint redraws the whole screen.
1132 * @param gr the Swing Graphics context
1135 public void paint(final Graphics gr
) {
1136 if (screen
!= null) {
1142 // Get the Swing component
1143 SwingTerminal
.this.swing
= new SwingComponent(frame
);
1145 // Hang onto top and left for drawing.
1146 Insets insets
= SwingTerminal
.this.swing
.getInsets();
1147 SwingTerminal
.this.left
= insets
.left
;
1148 SwingTerminal
.this.top
= insets
.top
;
1150 // Load the font so that we can set sessionInfo.
1153 // Get the default cols x rows and set component size
1155 SwingTerminal
.this.sessionInfo
=
1156 new SwingSessionInfo(SwingTerminal
.this.swing
,
1157 SwingTerminal
.this.textWidth
,
1158 SwingTerminal
.this.textHeight
,
1159 windowWidth
, windowHeight
);
1161 SwingTerminal
.this.setDimensions(sessionInfo
.getWindowWidth(),
1162 sessionInfo
.getWindowHeight());
1164 SwingTerminal
.this.resizeToScreen();
1165 SwingTerminal
.this.swing
.setVisible(true);
1168 } catch (Exception e
) {
1169 e
.printStackTrace();
1172 this.listener
= listener
;
1176 eventQueue
= new LinkedList
<TInputEvent
>();
1178 // Add listeners to Swing.
1179 swing
.addKeyListener(this);
1180 swing
.addWindowListener(this);
1181 swing
.addComponentListener(this);
1182 swing
.addMouseListener(this);
1183 swing
.addMouseMotionListener(this);
1184 swing
.addMouseWheelListener(this);
1188 * Public constructor renders to an existing JComponent.
1190 * @param component the Swing component to render to
1191 * @param windowWidth the number of text columns to start with
1192 * @param windowHeight the number of text rows to start with
1193 * @param fontSize the size in points. Good values to pick are: 16, 20,
1195 * @param listener the object this backend needs to wake up when new
1198 public SwingTerminal(final JComponent component
, final int windowWidth
,
1199 final int windowHeight
, final int fontSize
, final Object listener
) {
1201 this.fontSize
= fontSize
;
1205 // Figure out my cursor style.
1206 String cursorStyleString
= System
.getProperty(
1207 "jexer.Swing.cursorStyle", "underline").toLowerCase();
1208 if (cursorStyleString
.equals("underline")) {
1209 cursorStyle
= CursorStyle
.UNDERLINE
;
1210 } else if (cursorStyleString
.equals("outline")) {
1211 cursorStyle
= CursorStyle
.OUTLINE
;
1212 } else if (cursorStyleString
.equals("block")) {
1213 cursorStyle
= CursorStyle
.BLOCK
;
1217 SwingUtilities
.invokeAndWait(new Runnable() {
1220 JComponent newComponent
= new JComponent() {
1223 * Serializable version.
1225 private static final long serialVersionUID
= 1;
1228 * The code that performs the actual drawing.
1230 public SwingTerminal screen
= null;
1233 * Anonymous class initializer saves the screen
1234 * reference, so that paint() and the like call out
1238 this.screen
= SwingTerminal
.this;
1242 * Update redraws the whole screen.
1244 * @param gr the Swing Graphics context
1247 public void update(final Graphics gr
) {
1248 // The default update clears the area. Don't do
1249 // that, instead just paint it directly.
1254 * Paint redraws the whole screen.
1256 * @param gr the Swing Graphics context
1259 public void paint(final Graphics gr
) {
1260 if (screen
!= null) {
1265 component
.setLayout(new BorderLayout());
1266 component
.add(newComponent
);
1268 // Get the Swing component
1269 SwingTerminal
.this.swing
= new SwingComponent(component
);
1271 // Hang onto top and left for drawing.
1272 Insets insets
= SwingTerminal
.this.swing
.getInsets();
1273 SwingTerminal
.this.left
= insets
.left
;
1274 SwingTerminal
.this.top
= insets
.top
;
1276 // Load the font so that we can set sessionInfo.
1279 // Get the default cols x rows and set component size
1281 SwingTerminal
.this.sessionInfo
=
1282 new SwingSessionInfo(SwingTerminal
.this.swing
,
1283 SwingTerminal
.this.textWidth
,
1284 SwingTerminal
.this.textHeight
);
1287 } catch (Exception e
) {
1288 e
.printStackTrace();
1291 this.listener
= listener
;
1295 eventQueue
= new LinkedList
<TInputEvent
>();
1297 // Add listeners to Swing.
1298 swing
.addKeyListener(this);
1299 swing
.addWindowListener(this);
1300 swing
.addComponentListener(this);
1301 swing
.addMouseListener(this);
1302 swing
.addMouseMotionListener(this);
1303 swing
.addMouseWheelListener(this);
1307 * Check if there are events in the queue.
1309 * @return if true, getEvents() has something to return to the backend
1311 public boolean hasEvents() {
1312 synchronized (eventQueue
) {
1313 return (eventQueue
.size() > 0);
1318 * Return any events in the IO queue.
1320 * @param queue list to append new events to
1322 public void getEvents(final List
<TInputEvent
> queue
) {
1323 synchronized (eventQueue
) {
1324 if (eventQueue
.size() > 0) {
1325 synchronized (queue
) {
1326 queue
.addAll(eventQueue
);
1334 * Restore terminal to normal state.
1336 public void closeTerminal() {
1341 * Pass Swing keystrokes into the event queue.
1343 * @param key keystroke received
1345 public void keyReleased(final KeyEvent key
) {
1346 // Ignore release events
1350 * Pass Swing keystrokes into the event queue.
1352 * @param key keystroke received
1354 public void keyTyped(final KeyEvent key
) {
1355 // Ignore typed events
1359 * Pass Swing keystrokes into the event queue.
1361 * @param key keystroke received
1363 public void keyPressed(final KeyEvent key
) {
1364 boolean alt
= false;
1365 boolean shift
= false;
1366 boolean ctrl
= false;
1368 boolean isKey
= false;
1369 if (key
.isActionKey()) {
1372 ch
= key
.getKeyChar();
1374 alt
= key
.isAltDown();
1375 ctrl
= key
.isControlDown();
1376 shift
= key
.isShiftDown();
1379 System.err.printf("Swing Key: %s\n", key);
1380 System.err.printf(" isKey: %s\n", isKey);
1381 System.err.printf(" alt: %s\n", alt);
1382 System.err.printf(" ctrl: %s\n", ctrl);
1383 System.err.printf(" shift: %s\n", shift);
1384 System.err.printf(" ch: %s\n", ch);
1387 // Special case: not return the bare modifier presses
1388 switch (key
.getKeyCode()) {
1389 case KeyEvent
.VK_ALT
:
1391 case KeyEvent
.VK_ALT_GRAPH
:
1393 case KeyEvent
.VK_CONTROL
:
1395 case KeyEvent
.VK_SHIFT
:
1397 case KeyEvent
.VK_META
:
1403 TKeypress keypress
= null;
1405 switch (key
.getKeyCode()) {
1406 case KeyEvent
.VK_F1
:
1407 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
1410 case KeyEvent
.VK_F2
:
1411 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
1414 case KeyEvent
.VK_F3
:
1415 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
1418 case KeyEvent
.VK_F4
:
1419 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
1422 case KeyEvent
.VK_F5
:
1423 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
1426 case KeyEvent
.VK_F6
:
1427 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
1430 case KeyEvent
.VK_F7
:
1431 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
1434 case KeyEvent
.VK_F8
:
1435 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
1438 case KeyEvent
.VK_F9
:
1439 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
1442 case KeyEvent
.VK_F10
:
1443 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
1446 case KeyEvent
.VK_F11
:
1447 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
1450 case KeyEvent
.VK_F12
:
1451 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
1454 case KeyEvent
.VK_HOME
:
1455 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
1458 case KeyEvent
.VK_END
:
1459 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
1462 case KeyEvent
.VK_PAGE_UP
:
1463 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
1466 case KeyEvent
.VK_PAGE_DOWN
:
1467 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
1470 case KeyEvent
.VK_INSERT
:
1471 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
1474 case KeyEvent
.VK_DELETE
:
1475 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
1478 case KeyEvent
.VK_RIGHT
:
1479 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
1482 case KeyEvent
.VK_LEFT
:
1483 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
1486 case KeyEvent
.VK_UP
:
1487 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
1490 case KeyEvent
.VK_DOWN
:
1491 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
1494 case KeyEvent
.VK_TAB
:
1495 // Special case: distinguish TAB vs BTAB
1497 keypress
= kbShiftTab
;
1502 case KeyEvent
.VK_ENTER
:
1503 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
1506 case KeyEvent
.VK_ESCAPE
:
1507 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
1510 case KeyEvent
.VK_BACK_SPACE
:
1511 // Special case: return it as kbBackspace (Ctrl-H)
1512 keypress
= new TKeypress(false, 0, 'H', false, true, false);
1515 // Unsupported, ignore
1520 if (keypress
== null) {
1523 keypress
= kbBackspace
;
1536 keypress
= kbShiftTab
;
1545 if (!alt
&& ctrl
&& !shift
) {
1546 ch
= KeyEvent
.getKeyText(key
.getKeyCode()).charAt(0);
1548 // Not a special key, put it together
1549 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
1553 // Save it and we are done.
1554 synchronized (eventQueue
) {
1555 eventQueue
.add(new TKeypressEvent(keypress
));
1558 if (listener
!= null) {
1559 synchronized (listener
) {
1560 listener
.notifyAll();
1566 * Pass window events into the event queue.
1568 * @param event window event received
1570 public void windowActivated(final WindowEvent event
) {
1571 // Force a total repaint
1572 synchronized (this) {
1578 * Pass window events into the event queue.
1580 * @param event window event received
1582 public void windowClosed(final WindowEvent event
) {
1587 * Pass window events into the event queue.
1589 * @param event window event received
1591 public void windowClosing(final WindowEvent event
) {
1592 // Drop a cmAbort and walk away
1593 synchronized (eventQueue
) {
1594 eventQueue
.add(new TCommandEvent(cmAbort
));
1597 if (listener
!= null) {
1598 synchronized (listener
) {
1599 listener
.notifyAll();
1605 * Pass window events into the event queue.
1607 * @param event window event received
1609 public void windowDeactivated(final WindowEvent event
) {
1614 * Pass window events into the event queue.
1616 * @param event window event received
1618 public void windowDeiconified(final WindowEvent event
) {
1623 * Pass window events into the event queue.
1625 * @param event window event received
1627 public void windowIconified(final WindowEvent event
) {
1632 * Pass window events into the event queue.
1634 * @param event window event received
1636 public void windowOpened(final WindowEvent event
) {
1641 * Pass component events into the event queue.
1643 * @param event component event received
1645 public void componentHidden(final ComponentEvent event
) {
1650 * Pass component events into the event queue.
1652 * @param event component event received
1654 public void componentShown(final ComponentEvent event
) {
1659 * Pass component events into the event queue.
1661 * @param event component event received
1663 public void componentMoved(final ComponentEvent event
) {
1668 * Pass component events into the event queue.
1670 * @param event component event received
1672 public void componentResized(final ComponentEvent event
) {
1673 if (gotFontDimensions
== false) {
1674 // We are still waiting to get font information. Don't pass a
1676 // System.err.println("size " + swing.getComponent().getSize());
1680 // Drop a new TResizeEvent into the queue
1681 sessionInfo
.queryWindowSize();
1682 synchronized (eventQueue
) {
1683 TResizeEvent windowResize
= new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
1684 sessionInfo
.getWindowWidth(), sessionInfo
.getWindowHeight());
1685 eventQueue
.add(windowResize
);
1688 if (listener
!= null) {
1689 synchronized (listener
) {
1690 listener
.notifyAll();
1696 * Pass mouse events into the event queue.
1698 * @param mouse mouse event received
1700 public void mouseDragged(final MouseEvent mouse
) {
1701 int modifiers
= mouse
.getModifiersEx();
1702 boolean eventMouse1
= false;
1703 boolean eventMouse2
= false;
1704 boolean eventMouse3
= false;
1705 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
1708 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
1711 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
1714 mouse1
= eventMouse1
;
1715 mouse2
= eventMouse2
;
1716 mouse3
= eventMouse3
;
1717 int x
= textColumn(mouse
.getX());
1718 int y
= textRow(mouse
.getY());
1720 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
1721 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
1723 synchronized (eventQueue
) {
1724 eventQueue
.add(mouseEvent
);
1727 if (listener
!= null) {
1728 synchronized (listener
) {
1729 listener
.notifyAll();
1735 * Pass mouse events into the event queue.
1737 * @param mouse mouse event received
1739 public void mouseMoved(final MouseEvent mouse
) {
1740 int x
= textColumn(mouse
.getX());
1741 int y
= textRow(mouse
.getY());
1742 if ((x
== oldMouseX
) && (y
== oldMouseY
)) {
1743 // Bail out, we've moved some pixels but not a whole text cell.
1749 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
1750 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
1752 synchronized (eventQueue
) {
1753 eventQueue
.add(mouseEvent
);
1756 if (listener
!= null) {
1757 synchronized (listener
) {
1758 listener
.notifyAll();
1764 * Pass mouse events into the event queue.
1766 * @param mouse mouse event received
1768 public void mouseClicked(final MouseEvent mouse
) {
1773 * Pass mouse events into the event queue.
1775 * @param mouse mouse event received
1777 public void mouseEntered(final MouseEvent mouse
) {
1782 * Pass mouse events into the event queue.
1784 * @param mouse mouse event received
1786 public void mouseExited(final MouseEvent mouse
) {
1791 * Pass mouse events into the event queue.
1793 * @param mouse mouse event received
1795 public void mousePressed(final MouseEvent mouse
) {
1796 int modifiers
= mouse
.getModifiersEx();
1797 boolean eventMouse1
= false;
1798 boolean eventMouse2
= false;
1799 boolean eventMouse3
= false;
1800 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
1803 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
1806 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
1809 mouse1
= eventMouse1
;
1810 mouse2
= eventMouse2
;
1811 mouse3
= eventMouse3
;
1812 int x
= textColumn(mouse
.getX());
1813 int y
= textRow(mouse
.getY());
1815 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
1816 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
1818 synchronized (eventQueue
) {
1819 eventQueue
.add(mouseEvent
);
1822 if (listener
!= null) {
1823 synchronized (listener
) {
1824 listener
.notifyAll();
1830 * Pass mouse events into the event queue.
1832 * @param mouse mouse event received
1834 public void mouseReleased(final MouseEvent mouse
) {
1835 int modifiers
= mouse
.getModifiersEx();
1836 boolean eventMouse1
= false;
1837 boolean eventMouse2
= false;
1838 boolean eventMouse3
= false;
1839 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
1842 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
1845 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
1860 int x
= textColumn(mouse
.getX());
1861 int y
= textRow(mouse
.getY());
1863 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
1864 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false);
1866 synchronized (eventQueue
) {
1867 eventQueue
.add(mouseEvent
);
1870 if (listener
!= null) {
1871 synchronized (listener
) {
1872 listener
.notifyAll();
1878 * Pass mouse events into the event queue.
1880 * @param mouse mouse event received
1882 public void mouseWheelMoved(final MouseWheelEvent mouse
) {
1883 int modifiers
= mouse
.getModifiersEx();
1884 boolean eventMouse1
= false;
1885 boolean eventMouse2
= false;
1886 boolean eventMouse3
= false;
1887 boolean mouseWheelUp
= false;
1888 boolean mouseWheelDown
= false;
1889 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
1892 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
1895 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
1898 mouse1
= eventMouse1
;
1899 mouse2
= eventMouse2
;
1900 mouse3
= eventMouse3
;
1901 int x
= textColumn(mouse
.getX());
1902 int y
= textRow(mouse
.getY());
1903 if (mouse
.getWheelRotation() > 0) {
1904 mouseWheelDown
= true;
1906 if (mouse
.getWheelRotation() < 0) {
1907 mouseWheelUp
= true;
1910 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
1911 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
);
1913 synchronized (eventQueue
) {
1914 eventQueue
.add(mouseEvent
);
1917 if (listener
!= null) {
1918 synchronized (listener
) {
1919 listener
.notifyAll();