#40 twidget API updates
[fanfix.git] / src / jexer / backend / SwingTerminal.java
CommitLineData
42873e30
KL
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
42873e30
KL
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer.backend;
30
31import java.awt.BorderLayout;
32import java.awt.Color;
33import java.awt.Font;
34import java.awt.FontMetrics;
35import java.awt.Graphics2D;
36import java.awt.Graphics;
37import java.awt.Insets;
38import java.awt.Rectangle;
be72cb5c 39import java.awt.Toolkit;
42873e30
KL
40import java.awt.event.ComponentEvent;
41import java.awt.event.ComponentListener;
42import java.awt.event.KeyEvent;
43import java.awt.event.KeyListener;
44import java.awt.event.MouseEvent;
45import java.awt.event.MouseListener;
46import java.awt.event.MouseMotionListener;
47import java.awt.event.MouseWheelEvent;
48import java.awt.event.MouseWheelListener;
49import java.awt.event.WindowEvent;
50import java.awt.event.WindowListener;
51import java.awt.geom.Rectangle2D;
52import java.awt.image.BufferedImage;
53import java.io.InputStream;
63bb9478 54import java.util.ArrayList;
42873e30 55import java.util.HashMap;
42873e30 56import java.util.List;
d36057df 57import java.util.Map;
42873e30
KL
58import javax.swing.JComponent;
59import javax.swing.JFrame;
d36057df 60import javax.swing.ImageIcon;
42873e30
KL
61import javax.swing.SwingUtilities;
62
63import jexer.TKeypress;
64import jexer.bits.Cell;
65import jexer.bits.CellAttributes;
66import jexer.event.TCommandEvent;
67import jexer.event.TInputEvent;
68import jexer.event.TKeypressEvent;
69import jexer.event.TMouseEvent;
70import jexer.event.TResizeEvent;
71import static jexer.TCommand.*;
72import static jexer.TKeypress.*;
73
74/**
75 * This Screen backend reads keystrokes and mouse events and draws to either
76 * a Java Swing JFrame (potentially triple-buffered) or a JComponent.
77 *
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
81 * methods.
82 */
051e2913
KL
83public class SwingTerminal extends LogicalScreen
84 implements TerminalReader,
85 ComponentListener, KeyListener,
86 MouseListener, MouseMotionListener,
87 MouseWheelListener, WindowListener {
42873e30 88
d36057df
KL
89 // ------------------------------------------------------------------------
90 // Constants --------------------------------------------------------------
91 // ------------------------------------------------------------------------
92
42873e30 93 /**
d36057df 94 * The icon image location.
42873e30 95 */
d36057df 96 private static final String ICONFILE = "jexer_logo_128.png";
42873e30 97
d36057df
KL
98 /**
99 * The terminus font resource filename.
100 */
101 private static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
42873e30
KL
102
103 /**
104 * Cursor style to draw.
105 */
106 public enum CursorStyle {
107 /**
108 * Use an underscore for the cursor.
109 */
110 UNDERLINE,
111
112 /**
113 * Use a solid block for the cursor.
114 */
115 BLOCK,
116
117 /**
118 * Use an outlined block for the cursor.
119 */
120 OUTLINE
121 }
122
d36057df
KL
123 // ------------------------------------------------------------------------
124 // Variables --------------------------------------------------------------
125 // ------------------------------------------------------------------------
42873e30
KL
126
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;
144
145 /**
146 * When true, all the MYBLACK, MYRED, etc. colors are set.
147 */
148 private static boolean dosColors = false;
149
150 /**
d36057df 151 * The Swing component or frame to draw to.
42873e30 152 */
d36057df 153 private SwingComponent swing;
42873e30 154
d36057df
KL
155 /**
156 * A cache of previously-rendered glyphs for blinking text, when it is
157 * not visible.
158 */
159 private Map<Cell, BufferedImage> glyphCacheBlink;
42873e30
KL
160
161 /**
d36057df
KL
162 * A cache of previously-rendered glyphs for non-blinking, or
163 * blinking-and-visible, text.
42873e30 164 */
d36057df 165 private Map<Cell, BufferedImage> glyphCache;
42873e30 166
42873e30
KL
167 /**
168 * If true, we were successful at getting the font dimensions.
169 */
170 private boolean gotFontDimensions = false;
171
172 /**
173 * The currently selected font.
174 */
175 private Font font = null;
176
177 /**
178 * The currently selected font size in points.
179 */
180 private int fontSize = 16;
181
182 /**
183 * Width of a character cell in pixels.
184 */
185 private int textWidth = 1;
186
187 /**
188 * Height of a character cell in pixels.
189 */
190 private int textHeight = 1;
191
e23ea538
KL
192 /**
193 * Width of a character cell in pixels, as reported by font.
194 */
195 private int fontTextWidth = 1;
196
197 /**
198 * Height of a character cell in pixels, as reported by font.
199 */
200 private int fontTextHeight = 1;
201
42873e30
KL
202 /**
203 * Descent of a character cell in pixels.
204 */
205 private int maxDescent = 0;
206
207 /**
208 * System-dependent Y adjustment for text in the character cell.
209 */
210 private int textAdjustY = 0;
211
212 /**
213 * System-dependent X adjustment for text in the character cell.
214 */
215 private int textAdjustX = 0;
216
e23ea538
KL
217 /**
218 * System-dependent height adjustment for text in the character cell.
219 */
220 private int textAdjustHeight = 0;
221
222 /**
223 * System-dependent width adjustment for text in the character cell.
224 */
225 private int textAdjustWidth = 0;
226
42873e30
KL
227 /**
228 * Top pixel absolute location.
229 */
230 private int top = 30;
231
232 /**
233 * Left pixel absolute location.
234 */
235 private int left = 30;
236
237 /**
238 * The cursor style to draw.
239 */
240 private CursorStyle cursorStyle = CursorStyle.UNDERLINE;
241
242 /**
be72cb5c
KL
243 * The number of millis to wait before switching the blink from visible
244 * to invisible. Set to 0 or negative to disable blinking.
42873e30
KL
245 */
246 private long blinkMillis = 500;
247
248 /**
249 * If true, the cursor should be visible right now based on the blink
250 * time.
251 */
252 private boolean cursorBlinkVisible = true;
253
254 /**
255 * The time that the blink last flipped from visible to invisible or
256 * from invisible to visible.
257 */
258 private long lastBlinkTime = 0;
259
260 /**
d36057df 261 * The session information.
42873e30 262 */
d36057df 263 private SwingSessionInfo sessionInfo;
42873e30
KL
264
265 /**
d36057df 266 * The listening object that run() wakes up on new input.
42873e30 267 */
d36057df 268 private Object listener;
42873e30
KL
269
270 /**
d36057df 271 * The event queue, filled up by a thread reading on input.
42873e30 272 */
d36057df 273 private List<TInputEvent> eventQueue;
42873e30
KL
274
275 /**
d36057df 276 * The last reported mouse X position.
42873e30 277 */
d36057df 278 private int oldMouseX = -1;
42873e30
KL
279
280 /**
d36057df 281 * The last reported mouse Y position.
42873e30 282 */
d36057df 283 private int oldMouseY = -1;
42873e30
KL
284
285 /**
d36057df 286 * true if mouse1 was down. Used to report mouse1 on the release event.
42873e30 287 */
d36057df 288 private boolean mouse1 = false;
42873e30
KL
289
290 /**
d36057df 291 * true if mouse2 was down. Used to report mouse2 on the release event.
42873e30 292 */
d36057df 293 private boolean mouse2 = false;
42873e30 294
d36057df
KL
295 /**
296 * true if mouse3 was down. Used to report mouse3 on the release event.
297 */
298 private boolean mouse3 = false;
42873e30 299
d36057df
KL
300 // ------------------------------------------------------------------------
301 // Constructors -----------------------------------------------------------
302 // ------------------------------------------------------------------------
42873e30 303
d36057df
KL
304 /**
305 * Public constructor creates a new JFrame to render to.
306 *
307 * @param windowWidth the number of text columns to start with
308 * @param windowHeight the number of text rows to start with
309 * @param fontSize the size in points. Good values to pick are: 16, 20,
310 * 22, and 24.
311 * @param listener the object this backend needs to wake up when new
312 * input comes in
313 */
314 public SwingTerminal(final int windowWidth, final int windowHeight,
315 final int fontSize, final Object listener) {
42873e30 316
d36057df 317 this.fontSize = fontSize;
42873e30 318
d36057df 319 setDOSColors();
e23ea538 320 reloadOptions();
42873e30 321
d36057df
KL
322 try {
323 SwingUtilities.invokeAndWait(new Runnable() {
324 public void run() {
42873e30 325
d36057df 326 JFrame frame = new JFrame() {
42873e30 327
d36057df
KL
328 /**
329 * Serializable version.
330 */
331 private static final long serialVersionUID = 1;
42873e30 332
d36057df
KL
333 /**
334 * The code that performs the actual drawing.
335 */
336 public SwingTerminal screen = null;
42873e30 337
d36057df
KL
338 /*
339 * Anonymous class initializer saves the screen
340 * reference, so that paint() and the like call out
341 * to SwingTerminal.
342 */
343 {
344 this.screen = SwingTerminal.this;
345 }
42873e30 346
d36057df
KL
347 /**
348 * Update redraws the whole screen.
349 *
350 * @param gr the Swing Graphics context
351 */
352 @Override
353 public void update(final Graphics gr) {
354 // The default update clears the area. Don't do
355 // that, instead just paint it directly.
356 paint(gr);
357 }
42873e30 358
d36057df
KL
359 /**
360 * Paint redraws the whole screen.
361 *
362 * @param gr the Swing Graphics context
363 */
364 @Override
365 public void paint(final Graphics gr) {
366 if (screen != null) {
367 screen.paint(gr);
368 }
369 }
370 };
42873e30 371
d36057df
KL
372 // Set icon
373 ClassLoader loader = Thread.currentThread().
374 getContextClassLoader();
375 frame.setIconImage((new ImageIcon(loader.
376 getResource(ICONFILE))).getImage());
42873e30 377
d36057df
KL
378 // Get the Swing component
379 SwingTerminal.this.swing = new SwingComponent(frame);
42873e30 380
d36057df
KL
381 // Hang onto top and left for drawing.
382 Insets insets = SwingTerminal.this.swing.getInsets();
383 SwingTerminal.this.left = insets.left;
384 SwingTerminal.this.top = insets.top;
42873e30 385
d36057df 386 // Load the font so that we can set sessionInfo.
e23ea538 387 setDefaultFont();
42873e30 388
d36057df
KL
389 // Get the default cols x rows and set component size
390 // accordingly.
391 SwingTerminal.this.sessionInfo =
392 new SwingSessionInfo(SwingTerminal.this.swing,
393 SwingTerminal.this.textWidth,
394 SwingTerminal.this.textHeight,
395 windowWidth, windowHeight);
396
a69ed767
KL
397 SwingTerminal.this.setDimensions(sessionInfo.
398 getWindowWidth(), sessionInfo.getWindowHeight());
d36057df
KL
399
400 SwingTerminal.this.resizeToScreen();
401 SwingTerminal.this.swing.setVisible(true);
402 }
403 });
a69ed767
KL
404 } catch (java.lang.reflect.InvocationTargetException e) {
405 e.printStackTrace();
406 } catch (InterruptedException e) {
d36057df 407 e.printStackTrace();
42873e30 408 }
42873e30 409
d36057df
KL
410 this.listener = listener;
411 mouse1 = false;
412 mouse2 = false;
413 mouse3 = false;
63bb9478 414 eventQueue = new ArrayList<TInputEvent>();
d36057df
KL
415
416 // Add listeners to Swing.
417 swing.addKeyListener(this);
418 swing.addWindowListener(this);
419 swing.addComponentListener(this);
420 swing.addMouseListener(this);
421 swing.addMouseMotionListener(this);
422 swing.addMouseWheelListener(this);
42873e30
KL
423 }
424
425 /**
d36057df 426 * Public constructor renders to an existing JComponent.
42873e30 427 *
d36057df
KL
428 * @param component the Swing component to render to
429 * @param windowWidth the number of text columns to start with
430 * @param windowHeight the number of text rows to start with
431 * @param fontSize the size in points. Good values to pick are: 16, 20,
432 * 22, and 24.
433 * @param listener the object this backend needs to wake up when new
434 * input comes in
42873e30 435 */
d36057df
KL
436 public SwingTerminal(final JComponent component, final int windowWidth,
437 final int windowHeight, final int fontSize, final Object listener) {
42873e30 438
d36057df 439 this.fontSize = fontSize;
42873e30 440
d36057df 441 setDOSColors();
e23ea538 442 reloadOptions();
42873e30 443
d36057df
KL
444 try {
445 SwingUtilities.invokeAndWait(new Runnable() {
446 public void run() {
42873e30 447
d36057df 448 JComponent newComponent = new JComponent() {
42873e30 449
d36057df
KL
450 /**
451 * Serializable version.
452 */
453 private static final long serialVersionUID = 1;
42873e30 454
d36057df
KL
455 /**
456 * The code that performs the actual drawing.
457 */
458 public SwingTerminal screen = null;
42873e30 459
d36057df
KL
460 /*
461 * Anonymous class initializer saves the screen
462 * reference, so that paint() and the like call out
463 * to SwingTerminal.
464 */
465 {
466 this.screen = SwingTerminal.this;
467 }
42873e30 468
d36057df
KL
469 /**
470 * Update redraws the whole screen.
471 *
472 * @param gr the Swing Graphics context
473 */
474 @Override
475 public void update(final Graphics gr) {
476 // The default update clears the area. Don't do
477 // that, instead just paint it directly.
478 paint(gr);
479 }
42873e30 480
d36057df
KL
481 /**
482 * Paint redraws the whole screen.
483 *
484 * @param gr the Swing Graphics context
485 */
486 @Override
487 public void paint(final Graphics gr) {
488 if (screen != null) {
489 screen.paint(gr);
490 }
491 }
492 };
493 component.setLayout(new BorderLayout());
494 component.add(newComponent);
42873e30 495
d36057df
KL
496 // Allow key events to be received
497 component.setFocusable(true);
e8a11f98 498
d36057df
KL
499 // Get the Swing component
500 SwingTerminal.this.swing = new SwingComponent(component);
42873e30 501
d36057df
KL
502 // Hang onto top and left for drawing.
503 Insets insets = SwingTerminal.this.swing.getInsets();
504 SwingTerminal.this.left = insets.left;
505 SwingTerminal.this.top = insets.top;
42873e30 506
d36057df 507 // Load the font so that we can set sessionInfo.
e23ea538 508 setDefaultFont();
42873e30 509
d36057df
KL
510 // Get the default cols x rows and set component size
511 // accordingly.
512 SwingTerminal.this.sessionInfo =
513 new SwingSessionInfo(SwingTerminal.this.swing,
514 SwingTerminal.this.textWidth,
515 SwingTerminal.this.textHeight);
516 }
517 });
a69ed767
KL
518 } catch (java.lang.reflect.InvocationTargetException e) {
519 e.printStackTrace();
520 } catch (InterruptedException e) {
d36057df 521 e.printStackTrace();
42873e30
KL
522 }
523
d36057df
KL
524 this.listener = listener;
525 mouse1 = false;
526 mouse2 = false;
527 mouse3 = false;
63bb9478 528 eventQueue = new ArrayList<TInputEvent>();
42873e30 529
d36057df
KL
530 // Add listeners to Swing.
531 swing.addKeyListener(this);
532 swing.addWindowListener(this);
533 swing.addComponentListener(this);
534 swing.addMouseListener(this);
535 swing.addMouseMotionListener(this);
536 swing.addMouseWheelListener(this);
42873e30
KL
537 }
538
d36057df
KL
539 // ------------------------------------------------------------------------
540 // LogicalScreen ----------------------------------------------------------
541 // ------------------------------------------------------------------------
542
42873e30 543 /**
d36057df
KL
544 * Set the window title.
545 *
546 * @param title the new title
42873e30 547 */
d36057df
KL
548 @Override
549 public void setTitle(final String title) {
550 swing.setTitle(title);
42873e30
KL
551 }
552
553 /**
554 * Push the logical screen to the physical device.
555 */
556 @Override
557 public void flushPhysical() {
be72cb5c
KL
558 // See if it is time to flip the blink time.
559 long nowTime = System.currentTimeMillis();
560 if (nowTime >= blinkMillis + lastBlinkTime) {
561 lastBlinkTime = nowTime;
562 cursorBlinkVisible = !cursorBlinkVisible;
563 // System.err.println("New lastBlinkTime: " + lastBlinkTime);
564 }
565
566 if ((swing.getFrame() != null)
567 && (swing.getBufferStrategy() != null)
568 ) {
569 do {
f4dcc690 570 clearPhysical();
be72cb5c
KL
571 do {
572 drawToSwing();
573 } while (swing.getBufferStrategy().contentsRestored());
574
575 swing.getBufferStrategy().show();
576 Toolkit.getDefaultToolkit().sync();
577 } while (swing.getBufferStrategy().contentsLost());
578
579 } else {
580 // Non-triple-buffered, call drawToSwing() once
581 drawToSwing();
582 }
583 }
584
d36057df
KL
585 // ------------------------------------------------------------------------
586 // TerminalReader ---------------------------------------------------------
587 // ------------------------------------------------------------------------
588
be72cb5c 589 /**
d36057df
KL
590 * Check if there are events in the queue.
591 *
592 * @return if true, getEvents() has something to return to the backend
be72cb5c 593 */
d36057df
KL
594 public boolean hasEvents() {
595 synchronized (eventQueue) {
596 return (eventQueue.size() > 0);
597 }
598 }
42873e30 599
d36057df
KL
600 /**
601 * Return any events in the IO queue.
602 *
603 * @param queue list to append new events to
604 */
605 public void getEvents(final List<TInputEvent> queue) {
606 synchronized (eventQueue) {
607 if (eventQueue.size() > 0) {
608 synchronized (queue) {
609 queue.addAll(eventQueue);
610 }
611 eventQueue.clear();
612 }
613 }
614 }
42873e30 615
d36057df
KL
616 /**
617 * Restore terminal to normal state.
618 */
619 public void closeTerminal() {
620 shutdown();
621 }
622
623 /**
624 * Set listener to a different Object.
625 *
626 * @param listener the new listening object that run() wakes up on new
627 * input
628 */
629 public void setListener(final Object listener) {
630 this.listener = listener;
631 }
632
e23ea538
KL
633 /**
634 * Reload options from System properties.
635 */
636 public void reloadOptions() {
637 // Figure out my cursor style.
638 String cursorStyleString = System.getProperty(
639 "jexer.Swing.cursorStyle", "underline").toLowerCase();
640 if (cursorStyleString.equals("underline")) {
641 cursorStyle = CursorStyle.UNDERLINE;
642 } else if (cursorStyleString.equals("outline")) {
643 cursorStyle = CursorStyle.OUTLINE;
644 } else if (cursorStyleString.equals("block")) {
645 cursorStyle = CursorStyle.BLOCK;
646 }
647
648 // Pull the system property for triple buffering.
649 if (System.getProperty("jexer.Swing.tripleBuffer",
650 "true").equals("true")
651 ) {
652 SwingComponent.tripleBuffer = true;
653 } else {
654 SwingComponent.tripleBuffer = false;
655 }
656 }
657
d36057df
KL
658 // ------------------------------------------------------------------------
659 // SwingTerminal ----------------------------------------------------------
660 // ------------------------------------------------------------------------
661
a69ed767
KL
662 /**
663 * Get the width of a character cell in pixels.
664 *
665 * @return the width in pixels of a character cell
666 */
667 public int getTextWidth() {
668 return textWidth;
669 }
670
671 /**
672 * Get the height of a character cell in pixels.
673 *
674 * @return the height in pixels of a character cell
675 */
676 public int getTextHeight() {
677 return textHeight;
678 }
679
d36057df
KL
680 /**
681 * Setup Swing colors to match DOS color palette.
682 */
683 private static void setDOSColors() {
684 if (dosColors) {
42873e30
KL
685 return;
686 }
d36057df
KL
687 MYBLACK = new Color(0x00, 0x00, 0x00);
688 MYRED = new Color(0xa8, 0x00, 0x00);
689 MYGREEN = new Color(0x00, 0xa8, 0x00);
690 MYYELLOW = new Color(0xa8, 0x54, 0x00);
691 MYBLUE = new Color(0x00, 0x00, 0xa8);
692 MYMAGENTA = new Color(0xa8, 0x00, 0xa8);
693 MYCYAN = new Color(0x00, 0xa8, 0xa8);
694 MYWHITE = new Color(0xa8, 0xa8, 0xa8);
695 MYBOLD_BLACK = new Color(0x54, 0x54, 0x54);
696 MYBOLD_RED = new Color(0xfc, 0x54, 0x54);
697 MYBOLD_GREEN = new Color(0x54, 0xfc, 0x54);
698 MYBOLD_YELLOW = new Color(0xfc, 0xfc, 0x54);
699 MYBOLD_BLUE = new Color(0x54, 0x54, 0xfc);
700 MYBOLD_MAGENTA = new Color(0xfc, 0x54, 0xfc);
701 MYBOLD_CYAN = new Color(0x54, 0xfc, 0xfc);
702 MYBOLD_WHITE = new Color(0xfc, 0xfc, 0xfc);
42873e30 703
d36057df
KL
704 dosColors = true;
705 }
42873e30 706
d36057df
KL
707 /**
708 * Get the number of millis to wait before switching the blink from
709 * visible to invisible.
710 *
711 * @return the number of milli to wait before switching the blink from
712 * visible to invisible
713 */
714 public long getBlinkMillis() {
715 return blinkMillis;
716 }
42873e30 717
d36057df
KL
718 /**
719 * Get the font size in points.
720 *
721 * @return font size in points
722 */
723 public int getFontSize() {
724 return fontSize;
725 }
42873e30 726
d36057df
KL
727 /**
728 * Set the font size in points.
729 *
730 * @param fontSize font size in points
731 */
732 public void setFontSize(final int fontSize) {
733 this.fontSize = fontSize;
734 Font newFont = font.deriveFont((float) fontSize);
735 setFont(newFont);
736 }
42873e30 737
d36057df
KL
738 /**
739 * Set to a new font, and resize the screen to match its dimensions.
740 *
741 * @param font the new font
742 */
743 public void setFont(final Font font) {
e23ea538
KL
744 synchronized (this) {
745 this.font = font;
746 getFontDimensions();
747 swing.setFont(font);
748 glyphCacheBlink = new HashMap<Cell, BufferedImage>();
749 glyphCache = new HashMap<Cell, BufferedImage>();
750 resizeToScreen();
751 }
752 }
753
754 /**
755 * Get the font this screen was last set to.
756 *
757 * @return the font
758 */
759 public Font getFont() {
760 return font;
d36057df
KL
761 }
762
763 /**
764 * Set the font to Terminus, the best all-around font for both CP437 and
765 * ISO8859-1.
766 */
e23ea538 767 public void setDefaultFont() {
d36057df
KL
768 try {
769 ClassLoader loader = Thread.currentThread().getContextClassLoader();
770 InputStream in = loader.getResourceAsStream(FONTFILE);
771 Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in);
772 Font terminus = terminusRoot.deriveFont(Font.PLAIN, fontSize);
d36057df 773 font = terminus;
a69ed767
KL
774 } catch (java.awt.FontFormatException e) {
775 e.printStackTrace();
776 font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
777 } catch (java.io.IOException e) {
d36057df
KL
778 e.printStackTrace();
779 font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
780 }
781
782 setFont(font);
783 }
784
e23ea538
KL
785 /**
786 * Get the X text adjustment.
787 *
788 * @return X text adjustment
789 */
790 public int getTextAdjustX() {
791 return textAdjustX;
792 }
793
794 /**
795 * Set the X text adjustment.
796 *
797 * @param textAdjustX the X text adjustment
798 */
799 public void setTextAdjustX(final int textAdjustX) {
800 synchronized (this) {
801 this.textAdjustX = textAdjustX;
802 glyphCacheBlink = new HashMap<Cell, BufferedImage>();
803 glyphCache = new HashMap<Cell, BufferedImage>();
804 clearPhysical();
805 }
806 }
807
808 /**
809 * Get the Y text adjustment.
810 *
811 * @return Y text adjustment
812 */
813 public int getTextAdjustY() {
814 return textAdjustY;
815 }
816
817 /**
818 * Set the Y text adjustment.
819 *
820 * @param textAdjustY the Y text adjustment
821 */
822 public void setTextAdjustY(final int textAdjustY) {
823 synchronized (this) {
824 this.textAdjustY = textAdjustY;
825 glyphCacheBlink = new HashMap<Cell, BufferedImage>();
826 glyphCache = new HashMap<Cell, BufferedImage>();
827 clearPhysical();
828 }
829 }
830
831 /**
832 * Get the height text adjustment.
833 *
834 * @return height text adjustment
835 */
836 public int getTextAdjustHeight() {
837 return textAdjustHeight;
838 }
839
840 /**
841 * Set the height text adjustment.
842 *
843 * @param textAdjustHeight the height text adjustment
844 */
845 public void setTextAdjustHeight(final int textAdjustHeight) {
846 synchronized (this) {
847 this.textAdjustHeight = textAdjustHeight;
848 textHeight = fontTextHeight + textAdjustHeight;
849 glyphCacheBlink = new HashMap<Cell, BufferedImage>();
850 glyphCache = new HashMap<Cell, BufferedImage>();
851 clearPhysical();
852 }
853 }
854
855 /**
856 * Get the width text adjustment.
857 *
858 * @return width text adjustment
859 */
860 public int getTextAdjustWidth() {
861 return textAdjustWidth;
862 }
863
864 /**
865 * Set the width text adjustment.
866 *
867 * @param textAdjustWidth the width text adjustment
868 */
869 public void setTextAdjustWidth(final int textAdjustWidth) {
870 synchronized (this) {
871 this.textAdjustWidth = textAdjustWidth;
872 textWidth = fontTextWidth + textAdjustWidth;
873 glyphCacheBlink = new HashMap<Cell, BufferedImage>();
874 glyphCache = new HashMap<Cell, BufferedImage>();
875 clearPhysical();
876 }
877 }
878
d36057df
KL
879 /**
880 * Convert a CellAttributes foreground color to an Swing Color.
881 *
882 * @param attr the text attributes
883 * @return the Swing Color
884 */
885 private Color attrToForegroundColor(final CellAttributes attr) {
051e2913
KL
886 int rgb = attr.getForeColorRGB();
887 if (rgb >= 0) {
888 int red = (rgb >> 16) & 0xFF;
889 int green = (rgb >> 8) & 0xFF;
890 int blue = rgb & 0xFF;
891
892 return new Color(red, green, blue);
893 }
894
d36057df
KL
895 if (attr.isBold()) {
896 if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
897 return MYBOLD_BLACK;
898 } else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
899 return MYBOLD_RED;
900 } else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
901 return MYBOLD_BLUE;
902 } else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
903 return MYBOLD_GREEN;
904 } else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
905 return MYBOLD_YELLOW;
906 } else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
907 return MYBOLD_CYAN;
908 } else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
909 return MYBOLD_MAGENTA;
910 } else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
911 return MYBOLD_WHITE;
912 }
913 } else {
914 if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
915 return MYBLACK;
916 } else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
917 return MYRED;
918 } else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
919 return MYBLUE;
920 } else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
921 return MYGREEN;
922 } else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
923 return MYYELLOW;
924 } else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
925 return MYCYAN;
926 } else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
927 return MYMAGENTA;
928 } else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
929 return MYWHITE;
930 }
931 }
932 throw new IllegalArgumentException("Invalid color: " +
933 attr.getForeColor().getValue());
934 }
935
936 /**
937 * Convert a CellAttributes background color to an Swing Color.
938 *
939 * @param attr the text attributes
940 * @return the Swing Color
941 */
942 private Color attrToBackgroundColor(final CellAttributes attr) {
051e2913
KL
943 int rgb = attr.getBackColorRGB();
944 if (rgb >= 0) {
945 int red = (rgb >> 16) & 0xFF;
946 int green = (rgb >> 8) & 0xFF;
947 int blue = rgb & 0xFF;
948
949 return new Color(red, green, blue);
950 }
951
d36057df
KL
952 if (attr.getBackColor().equals(jexer.bits.Color.BLACK)) {
953 return MYBLACK;
954 } else if (attr.getBackColor().equals(jexer.bits.Color.RED)) {
955 return MYRED;
956 } else if (attr.getBackColor().equals(jexer.bits.Color.BLUE)) {
957 return MYBLUE;
958 } else if (attr.getBackColor().equals(jexer.bits.Color.GREEN)) {
959 return MYGREEN;
960 } else if (attr.getBackColor().equals(jexer.bits.Color.YELLOW)) {
961 return MYYELLOW;
962 } else if (attr.getBackColor().equals(jexer.bits.Color.CYAN)) {
963 return MYCYAN;
964 } else if (attr.getBackColor().equals(jexer.bits.Color.MAGENTA)) {
965 return MYMAGENTA;
966 } else if (attr.getBackColor().equals(jexer.bits.Color.WHITE)) {
967 return MYWHITE;
968 }
969 throw new IllegalArgumentException("Invalid color: " +
970 attr.getBackColor().getValue());
971 }
972
973 /**
e23ea538
KL
974 * Figure out what textAdjustX, textAdjustY, textAdjustHeight, and
975 * textAdjustWidth should be, based on the location of a vertical bar and
976 * a horizontal bar.
d36057df 977 */
e23ea538 978 private void getFontAdjustments() {
d36057df
KL
979 BufferedImage image = null;
980
981 // What SHOULD happen is that the topmost/leftmost white pixel is at
982 // position (gr2x, gr2y). But it might also be off by a pixel in
983 // either direction.
984
985 Graphics2D gr2 = null;
986 int gr2x = 3;
987 int gr2y = 3;
e23ea538 988 image = new BufferedImage(fontTextWidth * 2, fontTextHeight * 2,
d36057df
KL
989 BufferedImage.TYPE_INT_ARGB);
990
991 gr2 = image.createGraphics();
992 gr2.setFont(swing.getFont());
993 gr2.setColor(java.awt.Color.BLACK);
e23ea538 994 gr2.fillRect(0, 0, fontTextWidth * 2, fontTextHeight * 2);
d36057df
KL
995 gr2.setColor(java.awt.Color.WHITE);
996 char [] chars = new char[1];
e23ea538
KL
997 chars[0] = jexer.bits.GraphicsChars.SINGLE_BAR;
998 gr2.drawChars(chars, 0, 1, gr2x, gr2y + fontTextHeight - maxDescent);
d36057df 999 chars[0] = jexer.bits.GraphicsChars.VERTICAL_BAR;
e23ea538 1000 gr2.drawChars(chars, 0, 1, gr2x, gr2y + fontTextHeight - maxDescent);
d36057df
KL
1001 gr2.dispose();
1002
e23ea538
KL
1003 int top = fontTextHeight * 2;
1004 int bottom = -1;
1005 int left = fontTextWidth * 2;
1006 int right = -1;
1007 textAdjustX = 0;
1008 textAdjustY = 0;
1009 textAdjustHeight = 0;
1010 textAdjustWidth = 0;
42873e30 1011
e23ea538
KL
1012 for (int x = 0; x < fontTextWidth * 2; x++) {
1013 for (int y = 0; y < fontTextHeight * 2; y++) {
42873e30 1014
d36057df 1015 /*
e23ea538 1016 System.err.println("H X: " + x + " Y: " + y + " " +
d36057df 1017 image.getRGB(x, y));
e23ea538 1018 */
42873e30 1019
d36057df 1020 if ((image.getRGB(x, y) & 0xFFFFFF) != 0) {
e23ea538
KL
1021 // Pixel is present.
1022 if (y < top) {
1023 top = y;
1024 }
1025 if (y > bottom) {
1026 bottom = y;
1027 }
1028 if (x < left) {
1029 left = x;
1030 }
1031 if (x > right) {
1032 right = x;
1033 }
42873e30
KL
1034 }
1035 }
1036 }
e23ea538
KL
1037 if (left < right) {
1038 textAdjustX = (gr2x - left);
1039 textAdjustWidth = fontTextWidth - (right - left + 1);
1040 }
1041 if (top < bottom) {
1042 textAdjustY = (gr2y - top);
1043 textAdjustHeight = fontTextHeight - (bottom - top + 1);
1044 }
1045 // System.err.println("top " + top + " bottom " + bottom);
1046 // System.err.println("left " + left + " right " + right);
1047
1048 // Special case: do not believe fonts that claim to be wider than
1049 // they are tall.
1050 if (fontTextWidth >= fontTextHeight) {
1051 textAdjustX = 0;
1052 textAdjustWidth = 0;
1053 fontTextWidth = fontTextHeight / 2;
1054 }
42873e30
KL
1055 }
1056
42873e30 1057 /**
d36057df
KL
1058 * Figure out my font dimensions. This code path works OK for the JFrame
1059 * case, and can be called immediately after JFrame creation.
42873e30 1060 */
d36057df
KL
1061 private void getFontDimensions() {
1062 swing.setFont(font);
1063 Graphics gr = swing.getGraphics();
1064 if (gr == null) {
1065 return;
1066 }
1067 getFontDimensions(gr);
42873e30
KL
1068 }
1069
1070 /**
d36057df
KL
1071 * Figure out my font dimensions. This code path is needed to lazy-load
1072 * the information inside paint().
42873e30 1073 *
d36057df 1074 * @param gr Graphics object to use
42873e30 1075 */
d36057df
KL
1076 private void getFontDimensions(final Graphics gr) {
1077 swing.setFont(font);
1078 FontMetrics fm = gr.getFontMetrics();
1079 maxDescent = fm.getMaxDescent();
1080 Rectangle2D bounds = fm.getMaxCharBounds(gr);
1081 int leading = fm.getLeading();
e23ea538
KL
1082 fontTextWidth = (int)Math.round(bounds.getWidth());
1083 // fontTextHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
42873e30 1084
d36057df
KL
1085 // This produces the same number, but works better for ugly
1086 // monospace.
e23ea538 1087 fontTextHeight = fm.getMaxAscent() + maxDescent - leading;
42873e30 1088
e23ea538
KL
1089 getFontAdjustments();
1090 textHeight = fontTextHeight + textAdjustHeight;
1091 textWidth = fontTextWidth + textAdjustWidth;
42873e30 1092
d36057df
KL
1093 if (sessionInfo != null) {
1094 sessionInfo.setTextCellDimensions(textWidth, textHeight);
1095 }
1096 gotFontDimensions = true;
42873e30
KL
1097 }
1098
1099 /**
9696a8f6 1100 * Resize the physical screen to match the logical screen dimensions.
42873e30 1101 */
9696a8f6 1102 @Override
d36057df 1103 public void resizeToScreen() {
9696a8f6 1104 swing.setDimensions(textWidth * width, textHeight * height);
f4dcc690 1105 clearPhysical();
a69ed767
KL
1106 }
1107
1108 /**
1109 * Draw one cell's image to the screen.
1110 *
1111 * @param gr the Swing Graphics context
1112 * @param cell the Cell to draw
1113 * @param xPixel the x-coordinate to render to. 0 means the
1114 * left-most pixel column.
1115 * @param yPixel the y-coordinate to render to. 0 means the top-most
1116 * pixel row.
1117 */
1118 private void drawImage(final Graphics gr, final Cell cell,
1119 final int xPixel, final int yPixel) {
1120
1121 /*
1122 System.err.println("drawImage(): " + xPixel + " " + yPixel +
1123 " " + cell);
1124 */
1125
1126 // Draw the background rectangle, then the foreground character.
1127 assert (cell.isImage());
1128 gr.setColor(cell.getBackground());
1129 gr.fillRect(xPixel, yPixel, textWidth, textHeight);
1130
1131 BufferedImage image = cell.getImage();
1132 if (image != null) {
1133 if (swing.getFrame() != null) {
1134 gr.drawImage(image, xPixel, yPixel, swing.getFrame());
1135 } else {
1136 gr.drawImage(image, xPixel, yPixel, swing.getComponent());
1137 }
1138 return;
1139 }
d36057df 1140 }
42873e30
KL
1141
1142 /**
d36057df 1143 * Draw one glyph to the screen.
42873e30 1144 *
d36057df
KL
1145 * @param gr the Swing Graphics context
1146 * @param cell the Cell to draw
1147 * @param xPixel the x-coordinate to render to. 0 means the
1148 * left-most pixel column.
1149 * @param yPixel the y-coordinate to render to. 0 means the top-most
1150 * pixel row.
42873e30 1151 */
d36057df
KL
1152 private void drawGlyph(final Graphics gr, final Cell cell,
1153 final int xPixel, final int yPixel) {
42873e30 1154
d36057df
KL
1155 /*
1156 System.err.println("drawGlyph(): " + xPixel + " " + yPixel +
1157 " " + cell);
1158 */
1159
1160 BufferedImage image = null;
1161 if (cell.isBlink() && !cursorBlinkVisible) {
1162 image = glyphCacheBlink.get(cell);
1163 } else {
1164 image = glyphCache.get(cell);
1165 }
1166 if (image != null) {
1167 if (swing.getFrame() != null) {
1168 gr.drawImage(image, xPixel, yPixel, swing.getFrame());
1169 } else {
1170 gr.drawImage(image, xPixel, yPixel, swing.getComponent());
1171 }
1172 return;
1173 }
1174
1175 // Generate glyph and draw it.
1176 Graphics2D gr2 = null;
1177 int gr2x = xPixel;
1178 int gr2y = yPixel;
1179 if ((SwingComponent.tripleBuffer) && (swing.getFrame() != null)) {
1180 image = new BufferedImage(textWidth, textHeight,
1181 BufferedImage.TYPE_INT_ARGB);
1182 gr2 = image.createGraphics();
1183 gr2.setFont(swing.getFont());
1184 gr2x = 0;
1185 gr2y = 0;
1186 } else {
1187 gr2 = (Graphics2D) gr;
1188 }
1189
1190 Cell cellColor = new Cell();
1191 cellColor.setTo(cell);
1192
1193 // Check for reverse
1194 if (cell.isReverse()) {
1195 cellColor.setForeColor(cell.getBackColor());
1196 cellColor.setBackColor(cell.getForeColor());
1197 }
1198
1199 // Draw the background rectangle, then the foreground character.
1200 gr2.setColor(attrToBackgroundColor(cellColor));
1201 gr2.fillRect(gr2x, gr2y, textWidth, textHeight);
1202
1203 // Handle blink and underline
1204 if (!cell.isBlink()
1205 || (cell.isBlink() && cursorBlinkVisible)
1206 ) {
1207 gr2.setColor(attrToForegroundColor(cellColor));
1208 char [] chars = new char[1];
1209 chars[0] = cell.getChar();
1210 gr2.drawChars(chars, 0, 1, gr2x + textAdjustX,
1211 gr2y + textHeight - maxDescent + textAdjustY);
1212
1213 if (cell.isUnderline()) {
1214 gr2.fillRect(gr2x, gr2y + textHeight - 2, textWidth, 2);
1215 }
1216 }
1217
1218 if ((SwingComponent.tripleBuffer) && (swing.getFrame() != null)) {
1219 gr2.dispose();
1220
1221 // We need a new key that will not be mutated by
1222 // invertCell().
1223 Cell key = new Cell();
1224 key.setTo(cell);
1225 if (cell.isBlink() && !cursorBlinkVisible) {
1226 glyphCacheBlink.put(key, image);
1227 } else {
1228 glyphCache.put(key, image);
1229 }
42873e30 1230
d36057df
KL
1231 if (swing.getFrame() != null) {
1232 gr.drawImage(image, xPixel, yPixel, swing.getFrame());
1233 } else {
1234 gr.drawImage(image, xPixel, yPixel, swing.getComponent());
1235 }
1236 }
42873e30 1237
d36057df 1238 }
42873e30
KL
1239
1240 /**
d36057df
KL
1241 * Check if the cursor is visible, and if so draw it.
1242 *
1243 * @param gr the Swing Graphics context
42873e30 1244 */
d36057df 1245 private void drawCursor(final Graphics gr) {
42873e30 1246
d36057df
KL
1247 if (cursorVisible
1248 && (cursorY >= 0)
1249 && (cursorX >= 0)
1250 && (cursorY <= height - 1)
1251 && (cursorX <= width - 1)
1252 && cursorBlinkVisible
1253 ) {
1254 int xPixel = cursorX * textWidth + left;
1255 int yPixel = cursorY * textHeight + top;
1256 Cell lCell = logical[cursorX][cursorY];
1257 gr.setColor(attrToForegroundColor(lCell));
1258 switch (cursorStyle) {
1259 default:
1260 // Fall through...
1261 case UNDERLINE:
1262 gr.fillRect(xPixel, yPixel + textHeight - 2, textWidth, 2);
1263 break;
1264 case BLOCK:
1265 gr.fillRect(xPixel, yPixel, textWidth, textHeight);
1266 break;
1267 case OUTLINE:
1268 gr.drawRect(xPixel, yPixel, textWidth - 1, textHeight - 1);
1269 break;
1270 }
1271 }
1272 }
42873e30
KL
1273
1274 /**
d36057df 1275 * Reset the blink timer.
42873e30 1276 */
d36057df
KL
1277 private void resetBlinkTimer() {
1278 lastBlinkTime = System.currentTimeMillis();
1279 cursorBlinkVisible = true;
1280 }
42873e30
KL
1281
1282 /**
d36057df 1283 * Paint redraws the whole screen.
42873e30 1284 *
d36057df 1285 * @param gr the Swing Graphics context
42873e30 1286 */
d36057df 1287 public void paint(final Graphics gr) {
42873e30 1288
d36057df
KL
1289 if (gotFontDimensions == false) {
1290 // Lazy-load the text width/height
1291 getFontDimensions(gr);
1292 /*
1293 System.err.println("textWidth " + textWidth +
1294 " textHeight " + textHeight);
1295 System.err.println("FONT: " + swing.getFont() + " font " + font);
1296 */
42873e30
KL
1297 }
1298
051e2913
KL
1299 if ((swing.getFrame() != null)
1300 && (swing.getBufferStrategy() != null)
d36057df
KL
1301 && (SwingUtilities.isEventDispatchThread())
1302 ) {
1303 // System.err.println("paint(), skip first paint on swing thread");
1304 return;
42873e30
KL
1305 }
1306
d36057df
KL
1307 int xCellMin = 0;
1308 int xCellMax = width;
1309 int yCellMin = 0;
1310 int yCellMax = height;
42873e30 1311
d36057df
KL
1312 Rectangle bounds = gr.getClipBounds();
1313 if (bounds != null) {
1314 // Only update what is in the bounds
1315 xCellMin = textColumn(bounds.x);
1316 xCellMax = textColumn(bounds.x + bounds.width);
1317 if (xCellMax > width) {
1318 xCellMax = width;
1319 }
1320 if (xCellMin >= xCellMax) {
1321 xCellMin = xCellMax - 2;
1322 }
1323 if (xCellMin < 0) {
1324 xCellMin = 0;
1325 }
1326 yCellMin = textRow(bounds.y);
1327 yCellMax = textRow(bounds.y + bounds.height);
1328 if (yCellMax > height) {
1329 yCellMax = height;
1330 }
1331 if (yCellMin >= yCellMax) {
1332 yCellMin = yCellMax - 2;
1333 }
1334 if (yCellMin < 0) {
1335 yCellMin = 0;
1336 }
1337 } else {
1338 // We need a total repaint
1339 reallyCleared = true;
1340 }
42873e30 1341
d36057df
KL
1342 // Prevent updates to the screen's data from the TApplication
1343 // threads.
1344 synchronized (this) {
42873e30 1345
d36057df
KL
1346 /*
1347 System.err.printf("bounds %s X %d %d Y %d %d\n",
1348 bounds, xCellMin, xCellMax, yCellMin, yCellMax);
1349 */
42873e30 1350
d36057df
KL
1351 for (int y = yCellMin; y < yCellMax; y++) {
1352 for (int x = xCellMin; x < xCellMax; x++) {
42873e30 1353
d36057df
KL
1354 int xPixel = x * textWidth + left;
1355 int yPixel = y * textHeight + top;
42873e30 1356
d36057df
KL
1357 Cell lCell = logical[x][y];
1358 Cell pCell = physical[x][y];
42873e30 1359
d36057df
KL
1360 if (!lCell.equals(pCell)
1361 || lCell.isBlink()
1362 || reallyCleared
1363 || (swing.getFrame() == null)) {
42873e30 1364
a69ed767
KL
1365 if (lCell.isImage()) {
1366 drawImage(gr, lCell, xPixel, yPixel);
1367 } else {
1368 drawGlyph(gr, lCell, xPixel, yPixel);
1369 }
42873e30 1370
d36057df
KL
1371 // Physical is always updated
1372 physical[x][y].setTo(lCell);
1373 }
42873e30 1374 }
d36057df
KL
1375 }
1376 drawCursor(gr);
42873e30 1377
d36057df
KL
1378 reallyCleared = false;
1379 } // synchronized (this)
42873e30
KL
1380 }
1381
1382 /**
d36057df 1383 * Restore terminal to normal state.
42873e30 1384 */
d36057df
KL
1385 public void shutdown() {
1386 swing.dispose();
1387 }
42873e30 1388
d36057df
KL
1389 /**
1390 * Push the logical screen to the physical device.
1391 */
1392 private void drawToSwing() {
42873e30 1393
d36057df
KL
1394 /*
1395 System.err.printf("drawToSwing(): reallyCleared %s dirty %s\n",
1396 reallyCleared, dirty);
1397 */
42873e30 1398
d36057df
KL
1399 // If reallyCleared is set, we have to draw everything.
1400 if ((swing.getFrame() != null)
1401 && (swing.getBufferStrategy() != null)
1402 && (reallyCleared == true)
1403 ) {
1404 // Triple-buffering: we have to redraw everything on this thread.
1405 Graphics gr = swing.getBufferStrategy().getDrawGraphics();
1406 swing.paint(gr);
1407 gr.dispose();
1408 swing.getBufferStrategy().show();
1409 Toolkit.getDefaultToolkit().sync();
1410 return;
1411 } else if (((swing.getFrame() != null)
1412 && (swing.getBufferStrategy() == null))
1413 || (reallyCleared == true)
1414 ) {
1415 // Repaint everything on the Swing thread.
1416 // System.err.println("REPAINT ALL");
1417 swing.repaint();
1418 return;
42873e30
KL
1419 }
1420
d36057df
KL
1421 if ((swing.getFrame() != null) && (swing.getBufferStrategy() != null)) {
1422 Graphics gr = swing.getBufferStrategy().getDrawGraphics();
42873e30 1423
d36057df
KL
1424 synchronized (this) {
1425 for (int y = 0; y < height; y++) {
1426 for (int x = 0; x < width; x++) {
1427 Cell lCell = logical[x][y];
1428 Cell pCell = physical[x][y];
42873e30 1429
d36057df
KL
1430 int xPixel = x * textWidth + left;
1431 int yPixel = y * textHeight + top;
42873e30 1432
d36057df
KL
1433 if (!lCell.equals(pCell)
1434 || ((x == cursorX)
1435 && (y == cursorY)
1436 && cursorVisible)
1437 || (lCell.isBlink())
1438 ) {
a69ed767
KL
1439 if (lCell.isImage()) {
1440 drawImage(gr, lCell, xPixel, yPixel);
1441 } else {
1442 drawGlyph(gr, lCell, xPixel, yPixel);
1443 }
d36057df 1444 physical[x][y].setTo(lCell);
42873e30 1445 }
d36057df
KL
1446 }
1447 }
1448 drawCursor(gr);
1449 } // synchronized (this)
42873e30 1450
d36057df
KL
1451 gr.dispose();
1452 swing.getBufferStrategy().show();
1453 Toolkit.getDefaultToolkit().sync();
1454 return;
1455 }
42873e30 1456
d36057df
KL
1457 // Swing thread version: request a repaint, but limit it to the area
1458 // that has changed.
be72cb5c 1459
d36057df
KL
1460 // Find the minimum-size damaged region.
1461 int xMin = swing.getWidth();
1462 int xMax = 0;
1463 int yMin = swing.getHeight();
1464 int yMax = 0;
42873e30 1465
d36057df
KL
1466 synchronized (this) {
1467 for (int y = 0; y < height; y++) {
1468 for (int x = 0; x < width; x++) {
1469 Cell lCell = logical[x][y];
1470 Cell pCell = physical[x][y];
42873e30 1471
d36057df
KL
1472 int xPixel = x * textWidth + left;
1473 int yPixel = y * textHeight + top;
42873e30 1474
d36057df
KL
1475 if (!lCell.equals(pCell)
1476 || ((x == cursorX)
1477 && (y == cursorY)
1478 && cursorVisible)
1479 || lCell.isBlink()
1480 ) {
1481 if (xPixel < xMin) {
1482 xMin = xPixel;
1483 }
1484 if (xPixel + textWidth > xMax) {
1485 xMax = xPixel + textWidth;
1486 }
1487 if (yPixel < yMin) {
1488 yMin = yPixel;
1489 }
1490 if (yPixel + textHeight > yMax) {
1491 yMax = yPixel + textHeight;
1492 }
1493 }
42873e30 1494 }
d36057df
KL
1495 }
1496 }
1497 if (xMin + textWidth >= xMax) {
1498 xMax += textWidth;
1499 }
1500 if (yMin + textHeight >= yMax) {
1501 yMax += textHeight;
42873e30
KL
1502 }
1503
d36057df
KL
1504 // Repaint the desired area
1505 /*
1506 System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax,
1507 yMin, yMax);
1508 */
42873e30 1509
d36057df
KL
1510 if ((swing.getFrame() != null) && (swing.getBufferStrategy() != null)) {
1511 // This path should never be taken, but is left here for
1512 // completeness.
1513 Graphics gr = swing.getBufferStrategy().getDrawGraphics();
1514 Rectangle bounds = new Rectangle(xMin, yMin, xMax - xMin,
1515 yMax - yMin);
1516 gr.setClip(bounds);
1517 swing.paint(gr);
1518 gr.dispose();
1519 swing.getBufferStrategy().show();
1520 Toolkit.getDefaultToolkit().sync();
1521 } else {
1522 // Repaint on the Swing thread.
1523 swing.repaint(xMin, yMin, xMax - xMin, yMax - yMin);
1524 }
42873e30
KL
1525 }
1526
1527 /**
d36057df 1528 * Convert pixel column position to text cell column position.
42873e30 1529 *
d36057df
KL
1530 * @param x pixel column position
1531 * @return text cell column position
42873e30 1532 */
d36057df
KL
1533 public int textColumn(final int x) {
1534 int column = ((x - left) / textWidth);
1535 if (column < 0) {
1536 column = 0;
42873e30 1537 }
d36057df
KL
1538 if (column > width - 1) {
1539 column = width - 1;
1540 }
1541 return column;
42873e30
KL
1542 }
1543
1544 /**
d36057df 1545 * Convert pixel row position to text cell row position.
42873e30 1546 *
d36057df
KL
1547 * @param y pixel row position
1548 * @return text cell row position
42873e30 1549 */
d36057df
KL
1550 public int textRow(final int y) {
1551 int row = ((y - top) / textHeight);
1552 if (row < 0) {
1553 row = 0;
1554 }
1555 if (row > height - 1) {
1556 row = height - 1;
42873e30 1557 }
d36057df 1558 return row;
42873e30
KL
1559 }
1560
1561 /**
d36057df
KL
1562 * Getter for sessionInfo.
1563 *
1564 * @return the SessionInfo
42873e30 1565 */
d36057df
KL
1566 public SessionInfo getSessionInfo() {
1567 return sessionInfo;
42873e30
KL
1568 }
1569
a2855f1d
KL
1570 /**
1571 * Getter for the underlying Swing component.
1572 *
1573 * @return the SwingComponent
1574 */
1575 public SwingComponent getSwingComponent() {
1576 return swing;
1577 }
1578
d36057df
KL
1579 // ------------------------------------------------------------------------
1580 // KeyListener ------------------------------------------------------------
1581 // ------------------------------------------------------------------------
1582
42873e30
KL
1583 /**
1584 * Pass Swing keystrokes into the event queue.
1585 *
1586 * @param key keystroke received
1587 */
1588 public void keyReleased(final KeyEvent key) {
1589 // Ignore release events
1590 }
1591
1592 /**
1593 * Pass Swing keystrokes into the event queue.
1594 *
1595 * @param key keystroke received
1596 */
1597 public void keyTyped(final KeyEvent key) {
1598 // Ignore typed events
1599 }
1600
1601 /**
1602 * Pass Swing keystrokes into the event queue.
1603 *
1604 * @param key keystroke received
1605 */
1606 public void keyPressed(final KeyEvent key) {
1607 boolean alt = false;
1608 boolean shift = false;
1609 boolean ctrl = false;
1610 char ch = ' ';
1611 boolean isKey = false;
1612 if (key.isActionKey()) {
1613 isKey = true;
1614 } else {
1615 ch = key.getKeyChar();
1616 }
1617 alt = key.isAltDown();
1618 ctrl = key.isControlDown();
1619 shift = key.isShiftDown();
1620
1621 /*
1622 System.err.printf("Swing Key: %s\n", key);
1623 System.err.printf(" isKey: %s\n", isKey);
1624 System.err.printf(" alt: %s\n", alt);
1625 System.err.printf(" ctrl: %s\n", ctrl);
1626 System.err.printf(" shift: %s\n", shift);
1627 System.err.printf(" ch: %s\n", ch);
1628 */
1629
1630 // Special case: not return the bare modifier presses
1631 switch (key.getKeyCode()) {
1632 case KeyEvent.VK_ALT:
1633 return;
1634 case KeyEvent.VK_ALT_GRAPH:
1635 return;
1636 case KeyEvent.VK_CONTROL:
1637 return;
1638 case KeyEvent.VK_SHIFT:
1639 return;
1640 case KeyEvent.VK_META:
1641 return;
1642 default:
1643 break;
1644 }
1645
1646 TKeypress keypress = null;
1647 if (isKey) {
1648 switch (key.getKeyCode()) {
1649 case KeyEvent.VK_F1:
1650 keypress = new TKeypress(true, TKeypress.F1, ' ',
1651 alt, ctrl, shift);
1652 break;
1653 case KeyEvent.VK_F2:
1654 keypress = new TKeypress(true, TKeypress.F2, ' ',
1655 alt, ctrl, shift);
1656 break;
1657 case KeyEvent.VK_F3:
1658 keypress = new TKeypress(true, TKeypress.F3, ' ',
1659 alt, ctrl, shift);
1660 break;
1661 case KeyEvent.VK_F4:
1662 keypress = new TKeypress(true, TKeypress.F4, ' ',
1663 alt, ctrl, shift);
1664 break;
1665 case KeyEvent.VK_F5:
1666 keypress = new TKeypress(true, TKeypress.F5, ' ',
1667 alt, ctrl, shift);
1668 break;
1669 case KeyEvent.VK_F6:
1670 keypress = new TKeypress(true, TKeypress.F6, ' ',
1671 alt, ctrl, shift);
1672 break;
1673 case KeyEvent.VK_F7:
1674 keypress = new TKeypress(true, TKeypress.F7, ' ',
1675 alt, ctrl, shift);
1676 break;
1677 case KeyEvent.VK_F8:
1678 keypress = new TKeypress(true, TKeypress.F8, ' ',
1679 alt, ctrl, shift);
1680 break;
1681 case KeyEvent.VK_F9:
1682 keypress = new TKeypress(true, TKeypress.F9, ' ',
1683 alt, ctrl, shift);
1684 break;
1685 case KeyEvent.VK_F10:
1686 keypress = new TKeypress(true, TKeypress.F10, ' ',
1687 alt, ctrl, shift);
1688 break;
1689 case KeyEvent.VK_F11:
1690 keypress = new TKeypress(true, TKeypress.F11, ' ',
1691 alt, ctrl, shift);
1692 break;
1693 case KeyEvent.VK_F12:
1694 keypress = new TKeypress(true, TKeypress.F12, ' ',
1695 alt, ctrl, shift);
1696 break;
1697 case KeyEvent.VK_HOME:
1698 keypress = new TKeypress(true, TKeypress.HOME, ' ',
1699 alt, ctrl, shift);
1700 break;
1701 case KeyEvent.VK_END:
1702 keypress = new TKeypress(true, TKeypress.END, ' ',
1703 alt, ctrl, shift);
1704 break;
1705 case KeyEvent.VK_PAGE_UP:
1706 keypress = new TKeypress(true, TKeypress.PGUP, ' ',
1707 alt, ctrl, shift);
1708 break;
1709 case KeyEvent.VK_PAGE_DOWN:
1710 keypress = new TKeypress(true, TKeypress.PGDN, ' ',
1711 alt, ctrl, shift);
1712 break;
1713 case KeyEvent.VK_INSERT:
1714 keypress = new TKeypress(true, TKeypress.INS, ' ',
1715 alt, ctrl, shift);
1716 break;
1717 case KeyEvent.VK_DELETE:
1718 keypress = new TKeypress(true, TKeypress.DEL, ' ',
1719 alt, ctrl, shift);
1720 break;
1721 case KeyEvent.VK_RIGHT:
1722 keypress = new TKeypress(true, TKeypress.RIGHT, ' ',
1723 alt, ctrl, shift);
1724 break;
1725 case KeyEvent.VK_LEFT:
1726 keypress = new TKeypress(true, TKeypress.LEFT, ' ',
1727 alt, ctrl, shift);
1728 break;
1729 case KeyEvent.VK_UP:
1730 keypress = new TKeypress(true, TKeypress.UP, ' ',
1731 alt, ctrl, shift);
1732 break;
1733 case KeyEvent.VK_DOWN:
1734 keypress = new TKeypress(true, TKeypress.DOWN, ' ',
1735 alt, ctrl, shift);
1736 break;
1737 case KeyEvent.VK_TAB:
1738 // Special case: distinguish TAB vs BTAB
1739 if (shift) {
1740 keypress = kbShiftTab;
1741 } else {
1742 keypress = kbTab;
1743 }
1744 break;
1745 case KeyEvent.VK_ENTER:
1746 keypress = new TKeypress(true, TKeypress.ENTER, ' ',
1747 alt, ctrl, shift);
1748 break;
1749 case KeyEvent.VK_ESCAPE:
1750 keypress = new TKeypress(true, TKeypress.ESC, ' ',
1751 alt, ctrl, shift);
1752 break;
1753 case KeyEvent.VK_BACK_SPACE:
be998723 1754 keypress = kbBackspace;
42873e30
KL
1755 break;
1756 default:
1757 // Unsupported, ignore
1758 return;
1759 }
1760 }
1761
1762 if (keypress == null) {
1763 switch (ch) {
1764 case 0x08:
be998723
KL
1765 // Disambiguate ^H from Backspace.
1766 if (KeyEvent.getKeyText(key.getKeyCode()).equals("H")) {
1767 // This is ^H.
1768 keypress = kbBackspace;
1769 } else {
1770 // We are emulating Xterm here, where the backspace key
1771 // on the keyboard returns ^?.
1772 keypress = kbBackspaceDel;
1773 }
42873e30
KL
1774 break;
1775 case 0x0A:
1776 keypress = kbEnter;
1777 break;
1778 case 0x1B:
1779 keypress = kbEsc;
1780 break;
1781 case 0x0D:
1782 keypress = kbEnter;
1783 break;
1784 case 0x09:
1785 if (shift) {
1786 keypress = kbShiftTab;
1787 } else {
1788 keypress = kbTab;
1789 }
1790 break;
1791 case 0x7F:
1792 keypress = kbDel;
1793 break;
1794 default:
1795 if (!alt && ctrl && !shift) {
1796 ch = KeyEvent.getKeyText(key.getKeyCode()).charAt(0);
1797 }
1798 // Not a special key, put it together
1799 keypress = new TKeypress(false, 0, ch, alt, ctrl, shift);
1800 }
1801 }
1802
1803 // Save it and we are done.
1804 synchronized (eventQueue) {
1805 eventQueue.add(new TKeypressEvent(keypress));
e8a11f98 1806 resetBlinkTimer();
42873e30 1807 }
3e074355
KL
1808 if (listener != null) {
1809 synchronized (listener) {
1810 listener.notifyAll();
1811 }
42873e30
KL
1812 }
1813 }
1814
d36057df
KL
1815 // ------------------------------------------------------------------------
1816 // WindowListener ---------------------------------------------------------
1817 // ------------------------------------------------------------------------
1818
42873e30
KL
1819 /**
1820 * Pass window events into the event queue.
1821 *
1822 * @param event window event received
1823 */
1824 public void windowActivated(final WindowEvent event) {
1825 // Force a total repaint
1826 synchronized (this) {
1827 clearPhysical();
1828 }
1829 }
1830
1831 /**
1832 * Pass window events into the event queue.
1833 *
1834 * @param event window event received
1835 */
1836 public void windowClosed(final WindowEvent event) {
1837 // Ignore
1838 }
1839
1840 /**
1841 * Pass window events into the event queue.
1842 *
1843 * @param event window event received
1844 */
1845 public void windowClosing(final WindowEvent event) {
63bb9478 1846 // Drop a cmBackendDisconnect and walk away
42873e30 1847 synchronized (eventQueue) {
63bb9478 1848 eventQueue.add(new TCommandEvent(cmBackendDisconnect));
e8a11f98 1849 resetBlinkTimer();
42873e30 1850 }
3e074355
KL
1851 if (listener != null) {
1852 synchronized (listener) {
1853 listener.notifyAll();
1854 }
42873e30
KL
1855 }
1856 }
1857
1858 /**
1859 * Pass window events into the event queue.
1860 *
1861 * @param event window event received
1862 */
1863 public void windowDeactivated(final WindowEvent event) {
1864 // Ignore
1865 }
1866
1867 /**
1868 * Pass window events into the event queue.
1869 *
1870 * @param event window event received
1871 */
1872 public void windowDeiconified(final WindowEvent event) {
1873 // Ignore
1874 }
1875
1876 /**
1877 * Pass window events into the event queue.
1878 *
1879 * @param event window event received
1880 */
1881 public void windowIconified(final WindowEvent event) {
1882 // Ignore
1883 }
1884
1885 /**
1886 * Pass window events into the event queue.
1887 *
1888 * @param event window event received
1889 */
1890 public void windowOpened(final WindowEvent event) {
1891 // Ignore
1892 }
1893
d36057df
KL
1894 // ------------------------------------------------------------------------
1895 // ComponentListener ------------------------------------------------------
1896 // ------------------------------------------------------------------------
1897
42873e30
KL
1898 /**
1899 * Pass component events into the event queue.
1900 *
1901 * @param event component event received
1902 */
1903 public void componentHidden(final ComponentEvent event) {
1904 // Ignore
1905 }
1906
1907 /**
1908 * Pass component events into the event queue.
1909 *
1910 * @param event component event received
1911 */
1912 public void componentShown(final ComponentEvent event) {
1913 // Ignore
1914 }
1915
1916 /**
1917 * Pass component events into the event queue.
1918 *
1919 * @param event component event received
1920 */
1921 public void componentMoved(final ComponentEvent event) {
1922 // Ignore
1923 }
1924
1925 /**
1926 * Pass component events into the event queue.
1927 *
1928 * @param event component event received
1929 */
1930 public void componentResized(final ComponentEvent event) {
1931 if (gotFontDimensions == false) {
1932 // We are still waiting to get font information. Don't pass a
1933 // resize event up.
1934 // System.err.println("size " + swing.getComponent().getSize());
1935 return;
1936 }
1937
2a92cf97
KL
1938 if (sessionInfo == null) {
1939 // This is the initial component resize in construction, bail
1940 // out.
1941 return;
1942 }
1943
42873e30
KL
1944 // Drop a new TResizeEvent into the queue
1945 sessionInfo.queryWindowSize();
1946 synchronized (eventQueue) {
1947 TResizeEvent windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
1948 sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
1949 eventQueue.add(windowResize);
e8a11f98 1950 resetBlinkTimer();
d36057df
KL
1951 /*
1952 System.err.println("Add resize event: " + windowResize.getWidth() +
1953 " x " + windowResize.getHeight());
1954 */
42873e30 1955 }
3e074355
KL
1956 if (listener != null) {
1957 synchronized (listener) {
1958 listener.notifyAll();
1959 }
42873e30
KL
1960 }
1961 }
1962
d36057df
KL
1963 // ------------------------------------------------------------------------
1964 // MouseMotionListener ----------------------------------------------------
1965 // ------------------------------------------------------------------------
1966
42873e30
KL
1967 /**
1968 * Pass mouse events into the event queue.
1969 *
1970 * @param mouse mouse event received
1971 */
1972 public void mouseDragged(final MouseEvent mouse) {
1973 int modifiers = mouse.getModifiersEx();
1974 boolean eventMouse1 = false;
1975 boolean eventMouse2 = false;
1976 boolean eventMouse3 = false;
1977 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
1978 eventMouse1 = true;
1979 }
1980 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
1981 eventMouse2 = true;
1982 }
1983 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
1984 eventMouse3 = true;
1985 }
1986 mouse1 = eventMouse1;
1987 mouse2 = eventMouse2;
1988 mouse3 = eventMouse3;
1989 int x = textColumn(mouse.getX());
1990 int y = textRow(mouse.getY());
1991
1992 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION,
1993 x, y, x, y, mouse1, mouse2, mouse3, false, false);
1994
1995 synchronized (eventQueue) {
1996 eventQueue.add(mouseEvent);
e8a11f98 1997 resetBlinkTimer();
42873e30 1998 }
3e074355
KL
1999 if (listener != null) {
2000 synchronized (listener) {
2001 listener.notifyAll();
2002 }
42873e30
KL
2003 }
2004 }
2005
2006 /**
2007 * Pass mouse events into the event queue.
2008 *
2009 * @param mouse mouse event received
2010 */
2011 public void mouseMoved(final MouseEvent mouse) {
2012 int x = textColumn(mouse.getX());
2013 int y = textRow(mouse.getY());
2014 if ((x == oldMouseX) && (y == oldMouseY)) {
2015 // Bail out, we've moved some pixels but not a whole text cell.
2016 return;
2017 }
2018 oldMouseX = x;
2019 oldMouseY = y;
2020
2021 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION,
2022 x, y, x, y, mouse1, mouse2, mouse3, false, false);
2023
2024 synchronized (eventQueue) {
2025 eventQueue.add(mouseEvent);
e8a11f98 2026 resetBlinkTimer();
42873e30 2027 }
3e074355
KL
2028 if (listener != null) {
2029 synchronized (listener) {
2030 listener.notifyAll();
2031 }
42873e30
KL
2032 }
2033 }
2034
d36057df
KL
2035 // ------------------------------------------------------------------------
2036 // MouseListener ----------------------------------------------------------
2037 // ------------------------------------------------------------------------
2038
42873e30
KL
2039 /**
2040 * Pass mouse events into the event queue.
2041 *
2042 * @param mouse mouse event received
2043 */
2044 public void mouseClicked(final MouseEvent mouse) {
2045 // Ignore
2046 }
2047
2048 /**
2049 * Pass mouse events into the event queue.
2050 *
2051 * @param mouse mouse event received
2052 */
2053 public void mouseEntered(final MouseEvent mouse) {
2054 // Ignore
2055 }
2056
2057 /**
2058 * Pass mouse events into the event queue.
2059 *
2060 * @param mouse mouse event received
2061 */
2062 public void mouseExited(final MouseEvent mouse) {
2063 // Ignore
2064 }
2065
2066 /**
2067 * Pass mouse events into the event queue.
2068 *
2069 * @param mouse mouse event received
2070 */
2071 public void mousePressed(final MouseEvent mouse) {
2072 int modifiers = mouse.getModifiersEx();
2073 boolean eventMouse1 = false;
2074 boolean eventMouse2 = false;
2075 boolean eventMouse3 = false;
2076 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
2077 eventMouse1 = true;
2078 }
2079 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
2080 eventMouse2 = true;
2081 }
2082 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
2083 eventMouse3 = true;
2084 }
2085 mouse1 = eventMouse1;
2086 mouse2 = eventMouse2;
2087 mouse3 = eventMouse3;
2088 int x = textColumn(mouse.getX());
2089 int y = textRow(mouse.getY());
2090
2091 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN,
2092 x, y, x, y, mouse1, mouse2, mouse3, false, false);
2093
2094 synchronized (eventQueue) {
2095 eventQueue.add(mouseEvent);
e8a11f98 2096 resetBlinkTimer();
42873e30 2097 }
3e074355
KL
2098 if (listener != null) {
2099 synchronized (listener) {
2100 listener.notifyAll();
2101 }
42873e30
KL
2102 }
2103 }
2104
2105 /**
2106 * Pass mouse events into the event queue.
2107 *
2108 * @param mouse mouse event received
2109 */
2110 public void mouseReleased(final MouseEvent mouse) {
2111 int modifiers = mouse.getModifiersEx();
2112 boolean eventMouse1 = false;
2113 boolean eventMouse2 = false;
2114 boolean eventMouse3 = false;
2115 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
2116 eventMouse1 = true;
2117 }
2118 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
2119 eventMouse2 = true;
2120 }
2121 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
2122 eventMouse3 = true;
2123 }
2124 if (mouse1) {
2125 mouse1 = false;
2126 eventMouse1 = true;
2127 }
2128 if (mouse2) {
2129 mouse2 = false;
2130 eventMouse2 = true;
2131 }
2132 if (mouse3) {
2133 mouse3 = false;
2134 eventMouse3 = true;
2135 }
2136 int x = textColumn(mouse.getX());
2137 int y = textRow(mouse.getY());
2138
2139 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_UP,
2140 x, y, x, y, eventMouse1, eventMouse2, eventMouse3, false, false);
2141
2142 synchronized (eventQueue) {
2143 eventQueue.add(mouseEvent);
e8a11f98 2144 resetBlinkTimer();
42873e30 2145 }
3e074355
KL
2146 if (listener != null) {
2147 synchronized (listener) {
2148 listener.notifyAll();
2149 }
42873e30
KL
2150 }
2151 }
2152
d36057df
KL
2153 // ------------------------------------------------------------------------
2154 // MouseWheelListener -----------------------------------------------------
2155 // ------------------------------------------------------------------------
2156
42873e30
KL
2157 /**
2158 * Pass mouse events into the event queue.
2159 *
2160 * @param mouse mouse event received
2161 */
2162 public void mouseWheelMoved(final MouseWheelEvent mouse) {
2163 int modifiers = mouse.getModifiersEx();
2164 boolean eventMouse1 = false;
2165 boolean eventMouse2 = false;
2166 boolean eventMouse3 = false;
2167 boolean mouseWheelUp = false;
2168 boolean mouseWheelDown = false;
2169 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
2170 eventMouse1 = true;
2171 }
2172 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
2173 eventMouse2 = true;
2174 }
2175 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
2176 eventMouse3 = true;
2177 }
2178 mouse1 = eventMouse1;
2179 mouse2 = eventMouse2;
2180 mouse3 = eventMouse3;
2181 int x = textColumn(mouse.getX());
2182 int y = textRow(mouse.getY());
2183 if (mouse.getWheelRotation() > 0) {
2184 mouseWheelDown = true;
2185 }
2186 if (mouse.getWheelRotation() < 0) {
2187 mouseWheelUp = true;
2188 }
2189
2190 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN,
2191 x, y, x, y, mouse1, mouse2, mouse3, mouseWheelUp, mouseWheelDown);
2192
2193 synchronized (eventQueue) {
2194 eventQueue.add(mouseEvent);
e8a11f98 2195 resetBlinkTimer();
42873e30 2196 }
3e074355
KL
2197 if (listener != null) {
2198 synchronized (listener) {
2199 listener.notifyAll();
2200 }
42873e30
KL
2201 }
2202 }
2203
2204}