/*
* This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
*
* (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved.
*
* Please visit http://javatelnet.org/ for updates and contact.
*
* --LICENSE NOTICE--
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* --LICENSE NOTICE--
*
*/
package de.mud.terminal;
import javax.swing.JScrollBar;
import java.awt.AWTEvent;
import java.awt.AWTEventMulticaster;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
/**
* Video Display Unit emulation for Swing/AWT. This class implements all necessary
* features of a character display unit, but not the actual terminal emulation.
* It can be used as the base for terminal emulations of any kind.
* <P>
* This is a lightweight component. It will render very badly if used
* in standard AWT components without overloaded update() method. The
* update() method must call paint() immediately without clearing the
* components graphics context or parts of the screen will simply
* disappear.
* <P>
* <B>Maintainer:</B> Matthias L. Jugel
*
* @version $Id: SwingTerminal.java 511 2005-11-18 19:36:06Z marcus $
* @author Matthias L. Jugel, Marcus Mei�ner
*/
public class SwingTerminal extends Component
implements VDUDisplay, KeyListener, MouseListener, MouseMotionListener {
private final static int debug = 0;
/** the VDU buffer */
private VDUBuffer buffer;
/** lightweight component definitions */
private final static long VDU_EVENTS = AWTEvent.KEY_EVENT_MASK
| AWTEvent.FOCUS_EVENT_MASK
| AWTEvent.ACTION_EVENT_MASK
| AWTEvent.MOUSE_MOTION_EVENT_MASK
| AWTEvent.MOUSE_EVENT_MASK;
private Insets insets; /* size of the border */
private boolean raised; /* indicator if the border is raised */
private Font normalFont; /* normal font */
private FontMetrics fm; /* current font metrics */
private int charWidth; /* current width of a char */
private int charHeight; /* current height of a char */
private int charDescent; /* base line descent */
private int resizeStrategy; /* current resizing strategy */
private Point selectBegin, selectEnd; /* selection coordinates */
private String selection; /* contains the selected text */
private JScrollBar scrollBar;
private SoftFont sf = new SoftFont();
private boolean colorPrinting = false; /* print display in color */
private Image backingStore = null;
/**
* Create a color representation that is brighter than the standard
* color but not what we would like to use for bold characters.
* @param clr the standard color
* @return the new brighter color
*/
private Color brighten(Color clr) {
int r,g,b;
r = (int) min(clr.getRed() * 1.2, 255.0);
g = (int) min(clr.getGreen() * 1.2, 255.0);
b = (int) min(clr.getBlue() * 1.2, 255.0);
return new Color(r, g, b);
}
/**
* Create a color representation that is darker than the standard
* color but not what we would like to use for bold characters.
* @param clr the standard color
* @return the new darker color
*/
private Color darken(Color clr) {
int r,g,b;
r = (int) max(clr.getRed() * 0.8, 0.0);
g = (int) max(clr.getGreen() * 0.8, 0.0);
b = (int) max(clr.getBlue() * 0.8, 0.0);
return new Color(r, g, b);
}
/** A list of colors used for representation of the display */
private Color color[] = {Color.black,
Color.red,
Color.green,
Color.yellow,
Color.blue,
Color.magenta,
Color.cyan,
Color.white,
null, // bold color
null, // inverted color
};
public final static int RESIZE_NONE = 0;
public final static int RESIZE_FONT = 1;
public final static int RESIZE_SCREEN = 2;
public final static int COLOR_BOLD = 8;
public final static int COLOR_INVERT = 9;
/* definitions of standards for the display unit */
private final static int COLOR_FG_STD = 7;
private final static int COLOR_BG_STD = 0;
/** User defineable cursor colors */
private Color cursorColorFG = null;
private Color cursorColorBG = null;
protected double max(double f1, double f2) {
return (f1 < f2) ? f2 : f1;
}
protected double min(double f1, double f2) {
return (f1 < f2) ? f1 : f2;
}
/**
* Create a new video display unit with the passed width and height in
* characters using a special font and font size. These features can
* be set independently using the appropriate properties.
* @param buffer a VDU buffer to be associated with the display
* @param font the font to be used (usually Monospaced)
*/
public SwingTerminal(VDUBuffer buffer, Font font) {
setVDUBuffer(buffer);
addKeyListener(this);
/* we have to make sure the tab key stays within the component */
String version = System.getProperty("java.version");
String versionStart = version.substring(0,3);
double ver = Double.parseDouble(versionStart);
if (ver >= 1.4) {
//if (version.startsWith("1.5")) {
try {
Class params[] = new Class[]{boolean.class};
SwingTerminal.class.getMethod("setFocusable", params).invoke(this, new Object[]{new Boolean(true)});
SwingTerminal.class.getMethod("setFocusTraversalKeysEnabled", params).invoke(this, new Object[]{new Boolean(false)});
} catch (Exception e) {
System.err.println("vt320: unable to reset focus handling for java version " + version);
e.printStackTrace();
}
}
// lightweight component handling
enableEvents(VDU_EVENTS);
// set the normal font to use
setFont(font);
// set the standard resize strategy
setResizeStrategy(RESIZE_FONT);
setForeground(Color.white);
setBackground(Color.black);
cursorColorFG = color[COLOR_FG_STD];
cursorColorBG = color[COLOR_BG_STD];
clearSelection();
addMouseListener(this);
addMouseMotionListener(this);
selection = null;
}
/**
* Create a display unit with size 80x24 and Font "Monospaced", size 12.
*/
public SwingTerminal(VDUBuffer buffer) {
this(buffer, new Font("Monospaced", Font.PLAIN, 11));
}
/**
* Set a new terminal (VDU) buffer.
* @param buffer new buffer
*/
public void setVDUBuffer(VDUBuffer buffer) {
this.buffer = buffer;
buffer.setDisplay(this);
}
/**
* Return the currently associated VDUBuffer.
* @return the current buffer
*/
public VDUBuffer getVDUBuffer() {
return buffer;
}
/**
* Set new color set for the display.
* @param colorset new color set
*/
public void setColorSet(Color[] colorset) {
System.arraycopy(colorset, 0, color, 0, 10);
buffer.update[0] = true;
redraw();
}
/**
* Get current color set.
* @return the color set currently associated
*/
public Color[] getColorSet() {
return color;
}
/**
* Set the font to be used for rendering the characters on screen.
* @param font the new font to be used.
*/
public void setFont(Font font) {
super.setFont(normalFont = font);
fm = getFontMetrics(font);
if (fm != null) {
charWidth = fm.charWidth('@');
charHeight = fm.getHeight();
charDescent = fm.getDescent();
}
if (buffer.update != null) buffer.update[0] = true;
redraw();
}
/**
* Set the strategy when window is resized.
* RESIZE_FONT is default.
* @param strategy the strategy
* @see #RESIZE_NONE
* @see #RESIZE_FONT
* @see #RESIZE_SCREEN
*/
public void setResizeStrategy(int strategy) {
resizeStrategy = strategy;
}
/**
* Set the border thickness and the border type.
* @param thickness border thickness in pixels, zero means no border
* @param raised a boolean indicating a raised or embossed border
*/
public void setBorder(int thickness, boolean raised) {
if (thickness == 0)
insets = null;
else
insets = new Insets(thickness + 1, thickness + 1,
thickness + 1, thickness + 1);
this.raised = raised;
}
/**
* Connect a scrollbar to the VDU. This should be done differently
* using a property change listener.
* @param scrollBar the scroll bar
*/
public void setScrollbar(JScrollBar scrollBar) {
if (scrollBar == null) return;
this.scrollBar = scrollBar;
this.scrollBar.setValues(buffer.windowBase, buffer.height, 0, buffer.bufSize - buffer.height);
this.scrollBar.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent evt) {
buffer.setWindowBase(evt.getValue());
}
});
}
/**
* Redraw marked lines.
*/
public void redraw() {
if (backingStore != null) {
redraw(backingStore.getGraphics());
repaint();
}
}
public void updateScrollBar() {
if (scrollBar == null) return;
scrollBar.setValues(buffer.windowBase, buffer.height, 0, buffer.bufSize);
}
protected void redraw(Graphics g) {
if (debug > 0) System.err.println("redraw()");
int xoffset = (super.getSize().width - buffer.width * charWidth) / 2;
int yoffset = (super.getSize().height - buffer.height * charHeight) / 2;
int selectStartLine = selectBegin.y - buffer.windowBase;
int selectEndLine = selectEnd.y - buffer.windowBase;
Color fg = darken(color[COLOR_FG_STD]);
Color bg = darken(color[COLOR_BG_STD]);
g.setFont(normalFont);
/* for debug only
if (update[0]) {
System.err.println("Redrawing all");
} else {
for (int l = 1; l < size.height+1; l++) {
if (update[l]) {
for (int c = 0; c < size.height-l;c++) {
if (!update[c+l]) {
System.err.println("Redrawing "+(l-1)+" - "+(l+c-2));
l=l+c;
break;
}
}
}
}
}
*/
for (int l = 0; l < buffer.height; l++) {
if (!buffer.update[0] && !buffer.update[l + 1]) continue;
buffer.update[l + 1] = false;
if (debug > 2) System.err.println("redraw(): line " + l);
for (int c = 0; c < buffer.width; c++) {
int addr = 0;
int currAttr = buffer.charAttributes[buffer.windowBase + l][c];
fg = darken(getForeground());
bg = darken(getBackground());
if ((currAttr & buffer.COLOR_FG) != 0)
fg = darken(color[((currAttr & buffer.COLOR_FG) >> buffer.COLOR_FG_SHIFT) - 1]);
if ((currAttr & buffer.COLOR_BG) != 0)
bg = darken(darken(color[((currAttr & buffer.COLOR_BG) >> buffer.COLOR_BG_SHIFT) - 1]));
if ((currAttr & VDUBuffer.BOLD) != 0) {
g.setFont(new Font(normalFont.getName(), Font.BOLD, normalFont.getSize()));
// does not work with IE6: g.setFont(normalFont.deriveFont(Font.BOLD));
if (null != color[COLOR_BOLD]) {
fg = color[COLOR_BOLD];
}
/*
if(fg.equals(Color.black)) {
fg = Color.gray;
} else {
fg = brighten(fg);
// bg = bg.brighter(); -- make some programs ugly
}
*/
} else {
g.setFont(normalFont);
}
if ((currAttr & VDUBuffer.LOW) != 0) {
fg = darken(fg);
}
if ((currAttr & VDUBuffer.INVERT) != 0) {
if (null == color[COLOR_INVERT]) {
Color swapc = bg;
bg = fg;
fg = swapc;
} else {
if (null == color[COLOR_BOLD]) {
fg = bg;
} else {
fg = color[COLOR_BOLD];
}
bg = color[COLOR_INVERT];
}
}
if (sf.inSoftFont(buffer.charArray[buffer.windowBase + l][c])) {
g.setColor(bg);
g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset,
charWidth, charHeight);
g.setColor(fg);
if ((currAttr & VDUBuffer.INVISIBLE) == 0)
sf.drawChar(g, buffer.charArray[buffer.windowBase + l][c], xoffset + c * charWidth,
l * charHeight + yoffset, charWidth, charHeight);
if ((currAttr & VDUBuffer.UNDERLINE) != 0)
g.drawLine(c * charWidth + xoffset,
(l + 1) * charHeight - charDescent / 2 + yoffset,
c * charWidth + charWidth + xoffset,
(l + 1) * charHeight - charDescent / 2 + yoffset);
continue;
}
// determine the maximum of characters we can print in one go
while ((c + addr < buffer.width) &&
((buffer.charArray[buffer.windowBase + l][c + addr] < ' ') ||
(buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr)) &&
!sf.inSoftFont(buffer.charArray[buffer.windowBase + l][c + addr])) {
if (buffer.charArray[buffer.windowBase + l][c + addr] < ' ') {
buffer.charArray[buffer.windowBase + l][c + addr] = ' ';
buffer.charAttributes[buffer.windowBase + l][c + addr] = 0;
continue;
}
addr++;
}
// clear the part of the screen we want to change (fill rectangle)
g.setColor(bg);
g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset,
addr * charWidth, charHeight);
g.setColor(fg);
// draw the characters, if not invisible.
if ((currAttr & VDUBuffer.INVISIBLE) == 0)
g.drawChars(buffer.charArray[buffer.windowBase + l], c, addr,
c * charWidth + xoffset,
(l + 1) * charHeight - charDescent + yoffset);
if ((currAttr & VDUBuffer.UNDERLINE) != 0)
g.drawLine(c * charWidth + xoffset,
(l + 1) * charHeight - charDescent / 2 + yoffset,
c * charWidth + addr * charWidth + xoffset,
(l + 1) * charHeight - charDescent / 2 + yoffset);
c += addr - 1;
}
// selection code, highlites line or part of it when it was
// selected previously
if (l >= selectStartLine && l <= selectEndLine) {
int selectStartColumn = (l == selectStartLine ? selectBegin.x : 0);
int selectEndColumn =
(l == selectEndLine ?
(l == selectStartLine ? selectEnd.x - selectStartColumn :
selectEnd.x) : buffer.width);
if (selectStartColumn != selectEndColumn) {
if (debug > 0)
System.err.println("select(" + selectStartColumn + "-"
+ selectEndColumn + ")");
g.setXORMode(bg);
g.fillRect(selectStartColumn * charWidth + xoffset,
l * charHeight + yoffset,
selectEndColumn * charWidth,
charHeight);
g.setPaintMode();
}
}
}
// draw cursor
if (buffer.showcursor && (
buffer.screenBase + buffer.cursorY >= buffer.windowBase &&
buffer.screenBase + buffer.cursorY < buffer.windowBase + buffer.height)
) {
g.setColor(cursorColorFG);
g.setXORMode(cursorColorBG);
g.fillRect(buffer.cursorX * charWidth + xoffset,
(buffer.cursorY + buffer.screenBase - buffer.windowBase) * charHeight + yoffset,
charWidth, charHeight);
g.setPaintMode();
g.setColor(color[COLOR_FG_STD]);
}
// draw border
if (insets != null) {
g.setColor(getBackground());
xoffset--;
yoffset--;
for (int i = insets.top - 1; i >= 0; i--)
g.draw3DRect(xoffset - i, yoffset - i,
charWidth * buffer.width + 1 + i * 2,
charHeight * buffer.height + 1 + i * 2,
raised);
}
buffer.update[0] = false;
}
/**
* Paint the current screen using the backing store image.
*/
public void paint(Graphics g) {
if (backingStore == null) {
Dimension size = super.getSize();
backingStore = createImage(size.width, size.height);
buffer.update[0] = true;
redraw();
}
if (debug > 1)
System.err.println("Clip region: " + g.getClipBounds());
g.drawImage(backingStore, 0, 0, this);
}
/*
public int print(Graphics g, PageFormat pf, int pi) throws PrinterException {
if(pi >= 1) {
return Printable.NO_SUCH_PAGE;
}
paint(g);
return Printable.PAGE_EXISTS;
}
*/
/**
* Set default for printing black&white or colorized as displayed on
* screen.
* @param colorPrint true = print in full color, default b&w only
*/
public void setColorPrinting(boolean colorPrint) {
colorPrinting = colorPrint;
}
public void print(Graphics g) {
if (debug > 0) System.err.println("DEBUG: print()");
for (int i = 0; i <= buffer.height; i++) buffer.update[i] = true;
Color fg = null, bg = null, colorSave[] = null;
if (!colorPrinting) {
fg = getForeground();
bg = getBackground();
setForeground(Color.black);
setBackground(Color.white);
colorSave = color;
color = new Color[]{Color.black,
Color.black,
Color.black,
Color.black,
Color.black,
Color.black,
Color.black,
Color.white,
null,
null,
};
}
redraw(g);
if (!colorPrinting) {
color = colorSave;
setForeground(fg);
setBackground(bg);
}
}
/**
* Convert Mouse Event coordinates into character cell coordinates
* @param evtpt the mouse point to be converted
* @return Character cell coordinate of passed point
*/
public Point mouseGetPos(Point evtpt) {
Point mousepos;
mousepos = new Point(0, 0);
int xoffset = (super.getSize().width - buffer.width * charWidth) / 2;
int yoffset = (super.getSize().height - buffer.height * charHeight) / 2;
mousepos.x = (evtpt.x - xoffset) / charWidth;
if (mousepos.x < 0) mousepos.x = 0;
if (mousepos.x >= buffer.width) mousepos.x = buffer.width - 1;
mousepos.y = (evtpt.y - yoffset) / charHeight;
if (mousepos.y < 0) mousepos.y = 0;
if (mousepos.y >= buffer.height) mousepos.y = buffer.height - 1;
return mousepos;
}
/**
* Set cursor FG and BG colors
* @param fg foreground color or null
* @param bg background color or null
*/
public void setCursorColors(Color fg, Color bg) {
if (fg == null)
cursorColorFG = color[COLOR_FG_STD];
else
cursorColorFG = fg;
if (bg == null)
cursorColorBG = color[COLOR_BG_STD];
else
cursorColorBG = bg;
repaint();
}
/**
* Reshape character display according to resize strategy.
* @see #setResizeStrategy
*/
public void setBounds(int x, int y, int w, int h) {
if (debug > 0)
System.err.println("VDU: setBounds(" + x + "," + y + "," + w + "," + h + ")");
super.setBounds(x, y, w, h);
// ignore zero bounds
if (x == 00 && y == 0 && w == 0 && h == 0) {
return;
}
if (insets != null) {
w -= insets.left + insets.right;
h -= insets.top + insets.bottom;
}
if (debug > 0)
System.err.println("VDU: looking for better match for " + normalFont);
Font tmpFont = normalFont;
String fontName = tmpFont.getName();
int fontStyle = tmpFont.getStyle();
fm = getFontMetrics(normalFont);
if (fm != null) {
charWidth = fm.charWidth('@');
charHeight = fm.getHeight();
}
switch (resizeStrategy) {
case RESIZE_SCREEN:
buffer.setScreenSize(w / charWidth, buffer.height = h / charHeight, true);
break;
case RESIZE_FONT:
int height = h / buffer.height;
int width = w / buffer.width;
fm = getFontMetrics(normalFont = new Font(fontName, fontStyle,
charHeight));
// adapt current font size (from small up to best fit)
if (fm.getHeight() < height || fm.charWidth('@') < width)
do {
fm = getFontMetrics(normalFont = new Font(fontName, fontStyle,
++charHeight));
} while (fm.getHeight() < height || fm.charWidth('@') < width);
// now check if we got a font that is too large
if (fm.getHeight() > height || fm.charWidth('@') > width)
do {
fm = getFontMetrics(normalFont = new Font(fontName, fontStyle,
--charHeight));
} while (charHeight > 1 &&
(fm.getHeight() > height ||
fm.charWidth('@') > width));
if (charHeight <= 1) {
System.err.println("VDU: error during resize, resetting");
normalFont = tmpFont;
System.err.println("VDU: disabling font/screen resize");
resizeStrategy = RESIZE_NONE;
}
setFont(normalFont);
fm = getFontMetrics(normalFont);
charWidth = fm.charWidth('@');
charHeight = fm.getHeight();
charDescent = fm.getDescent();
break;
case RESIZE_NONE:
default:
break;
}
if (debug > 0) {
System.err.println("VDU: charWidth=" + charWidth + ", " +
"charHeight=" + charHeight + ", " +
"charDescent=" + charDescent);
}
// delete the double buffer image and mark all lines
backingStore = null;
buffer.markLine(0, buffer.height);
}
/**
* Return the real size in points of the character display.
* @return Dimension the dimension of the display
* @see java.awt.Dimension
*/
public Dimension getSize() {
int xborder = 0, yborder = 0;
if (insets != null) {
xborder = insets.left + insets.right;
yborder = insets.top + insets.bottom;
}
return new Dimension(buffer.width * charWidth + xborder,
buffer.height * charHeight + yborder);
}
/**
* Return the preferred Size of the character display.
* This turns out to be the actual size.
* @return Dimension dimension of the display
* @see #size
*/
public Dimension getPreferredSize() {
return getSize();
}
/**
* The insets of the character display define the border.
* @return Insets border thickness in pixels
*/
public Insets getInsets() {
return insets;
}
public void clearSelection() {
selectBegin = new Point(0, 0);
selectEnd = new Point(0, 0);
selection = null;
}
public String getSelection() {
return selection;
}
private boolean buttonCheck(int modifiers, int mask) {
return (modifiers & mask) == mask;
}
public void mouseMoved(MouseEvent evt) {
/* nothing yet we do here */
}
public void mouseDragged(MouseEvent evt) {
if (buttonCheck(evt.getModifiers(), MouseEvent.BUTTON1_MASK) ||
// Windows NT/95 etc: returns 0, which is a bug
evt.getModifiers() == 0) {
int xoffset = (super.getSize().width - buffer.width * charWidth) / 2;
int yoffset = (super.getSize().height - buffer.height * charHeight) / 2;
int x = (evt.getX() - xoffset) / charWidth;
int y = (evt.getY() - yoffset) / charHeight + buffer.windowBase;
int oldx = selectEnd.x, oldy = selectEnd.y;
if ((x <= selectBegin.x && y <= selectBegin.y)) {
selectBegin.x = x;
selectBegin.y = y;
} else {
selectEnd.x = x;
selectEnd.y = y;
}
if (oldx != x || oldy != y) {
buffer.update[0] = true;
if (debug > 0)
System.err.println("select([" + selectBegin.x + "," + selectBegin.y + "]," +
"[" + selectEnd.x + "," + selectEnd.y + "])");
redraw();
}
}
}
public void mouseClicked(MouseEvent evt) {
/* nothing yet we do here */
}
public void mouseEntered(MouseEvent evt) {
/* nothing yet we do here */
}
public void mouseExited(MouseEvent evt) {
/* nothing yet we do here */
}
/**
* Handle mouse pressed events for copy & paste.
* @param evt the event that occured
* @see java.awt.event.MouseEvent
*/
public void mousePressed(MouseEvent evt) {
requestFocus();
int xoffset = (super.getSize().width - buffer.width * charWidth) / 2;
int yoffset = (super.getSize().height - buffer.height * charHeight) / 2;
if (buffer instanceof VDUInput) {
((VDUInput) buffer).mousePressed(xoffset, yoffset, evt.getModifiers());
}
// looks like we get no modifiers here ... ... We do? -Marcus
if (buttonCheck(evt.getModifiers(), MouseEvent.BUTTON1_MASK)) {
selectBegin.x = (evt.getX() - xoffset) / charWidth;
selectBegin.y = (evt.getY() - yoffset) / charHeight + buffer.windowBase;
selectEnd.x = selectBegin.x;
selectEnd.y = selectBegin.y;
}
}
/**
* Handle mouse released events for copy & paste.
* @param evt the mouse event
*/
public void mouseReleased(MouseEvent evt) {
int xoffset = (super.getSize().width - buffer.width * charWidth) / 2;
int yoffset = (super.getSize().height - buffer.height * charHeight) / 2;
if (buffer instanceof VDUInput) {
((VDUInput) buffer).mousePressed(xoffset, yoffset, evt.getModifiers());
}
if (buttonCheck(evt.getModifiers(), MouseEvent.BUTTON1_MASK)) {
mouseDragged(evt);
if (selectBegin.x == selectEnd.x && selectBegin.y == selectEnd.y) {
buffer.update[0] = true;
redraw();
return;
}
selection = "";
// fix end.x and end.y, they can get over the border
if (selectEnd.x < 0) selectEnd.x = 0;
if (selectEnd.y < 0) selectEnd.y = 0;
if (selectEnd.y >= buffer.charArray.length)
selectEnd.y = buffer.charArray.length - 1;
if (selectEnd.x > buffer.charArray[0].length)
selectEnd.x = buffer.charArray[0].length;
// Initial buffer space for selectEnd - selectBegin + 1 lines
// NOTE: Selection includes invisible text as spaces!
// (also leaves invisible non-whitespace selection ending as spaces)
StringBuffer selectionBuf =
new StringBuffer(buffer.charArray[0].length * (selectEnd.y - selectBegin.y + 1));
for (int l = selectBegin.y; l <= selectEnd.y; l++) {
int start, end;
start = (l == selectBegin.y ? start = selectBegin.x : 0);
end = (l == selectEnd.y ? end = selectEnd.x : buffer.charArray[l].length);
boolean newlineFound = false;
char ch = ' ';
for (int i = start; i < end; i++) {
if ((buffer.charAttributes[l][i] & VDUBuffer.INVISIBLE) != 0)
ch = ' ';
else
ch = buffer.charArray[l][i];
if (ch == '\n')
newlineFound = true;
selectionBuf.append(ch);
}
if (!newlineFound)
selectionBuf.append('\n');
// Trim all spaces from end of line, like xterm does.
selection += ("-" + (selectionBuf.toString())).trim().substring(1);
if (end == buffer.charArray[l].length)
selection += "\n";
}
}
}
public void keyTyped(KeyEvent e) {
if (buffer != null && buffer instanceof VDUInput)
((VDUInput) buffer).keyTyped(e.getKeyCode(), e.getKeyChar(), getModifiers(e));
}
public void keyPressed(KeyEvent e) {
if (buffer != null && buffer instanceof VDUInput)
((VDUInput) buffer).keyPressed(e.getKeyCode(), e.getKeyChar(), getModifiers(e));
}
public void keyReleased(KeyEvent e) {
// ignore
}
// lightweight component event handling
private MouseListener mouseListener;
/**
* Add a mouse listener to the VDU. This is the implementation for
* the lightweight event handling.
* @param listener the new mouse listener
*/
public void addMouseListener(MouseListener listener) {
mouseListener = AWTEventMulticaster.add(mouseListener, listener);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Remove a mouse listener to the VDU. This is the implementation for
* the lightweight event handling.
* @param listener the mouse listener to remove
*/
public void removeMouseListener(MouseListener listener) {
mouseListener = AWTEventMulticaster.remove(mouseListener, listener);
}
private MouseMotionListener mouseMotionListener;
/**
* Add a mouse motion listener to the VDU. This is the implementation for
* the lightweight event handling.
* @param listener the mouse motion listener
*/
public void addMouseMotionListener(MouseMotionListener listener) {
mouseMotionListener = AWTEventMulticaster.add(mouseMotionListener, listener);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Remove a mouse motion listener to the VDU. This is the implementation for
* the lightweight event handling.
* @param listener the mouse motion listener to remove
*/
public void removeMouseMotionListener(MouseMotionListener listener) {
mouseMotionListener =
AWTEventMulticaster.remove(mouseMotionListener, listener);
}
/**
* Process mouse events for this component. It will call the
* methods (mouseClicked() etc) in the added mouse listeners.
* @param evt the dispatched mouse event
*/
public void processMouseEvent(MouseEvent evt) {
// handle simple mouse events
if (mouseListener != null)
switch (evt.getID()) {
case MouseEvent.MOUSE_CLICKED:
mouseListener.mouseClicked(evt);
break;
case MouseEvent.MOUSE_ENTERED:
mouseListener.mouseEntered(evt);
break;
case MouseEvent.MOUSE_EXITED:
mouseListener.mouseExited(evt);
break;
case MouseEvent.MOUSE_PRESSED:
mouseListener.mousePressed(evt);
break;
case MouseEvent.MOUSE_RELEASED:
mouseListener.mouseReleased(evt);
break;
}
super.processMouseEvent(evt);
}
/**
* Process mouse motion events for this component. It will call the
* methods (mouseDragged() etc) in the added mouse motion listeners.
* @param evt the dispatched mouse event
*/
public void processMouseMotionEvent(MouseEvent evt) {
// handle mouse motion events
if (mouseMotionListener != null)
switch (evt.getID()) {
case MouseEvent.MOUSE_DRAGGED:
mouseMotionListener.mouseDragged(evt);
break;
case MouseEvent.MOUSE_MOVED:
mouseMotionListener.mouseMoved(evt);
break;
}
super.processMouseMotionEvent(evt);
}
private KeyListener keyListener;
/**
* Add a key listener to the VDU. This is necessary to be able to receive
* keyboard input from this component. It is a prerequisite for a
* lightweigh component.
* @param listener the key listener
*/
public void addKeyListener(KeyListener listener) {
keyListener = AWTEventMulticaster.add(keyListener, listener);
enableEvents(AWTEvent.KEY_EVENT_MASK);
}
/**
* Remove key listener from the VDU. It is a prerequisite for a
* lightweigh component.
* @param listener the key listener to remove
*/
public void removeKeyListener(KeyListener listener) {
keyListener = AWTEventMulticaster.remove(keyListener, listener);
}
/**
* Process key events for this component.
* @param evt the dispatched key event
*/
public void processKeyEvent(KeyEvent evt) {
if (keyListener != null)
switch (evt.getID()) {
case KeyEvent.KEY_PRESSED:
keyListener.keyPressed(evt);
break;
case KeyEvent.KEY_RELEASED:
keyListener.keyReleased(evt);
break;
case KeyEvent.KEY_TYPED:
keyListener.keyTyped(evt);
break;
}
// consume TAB keys if they originate from our component
if (evt.getKeyCode() == KeyEvent.VK_TAB && evt.getSource() == this)
evt.consume();
super.processKeyEvent(evt);
}
FocusListener focusListener;
public void addFocusListener(FocusListener listener) {
focusListener = AWTEventMulticaster.add(focusListener, listener);
}
public void removeFocusListener(FocusListener listener) {
focusListener = AWTEventMulticaster.remove(focusListener, listener);
}
public void processFocusEvent(FocusEvent evt) {
if (focusListener != null)
switch (evt.getID()) {
case FocusEvent.FOCUS_GAINED:
focusListener.focusGained(evt);
break;
case FocusEvent.FOCUS_LOST:
focusListener.focusLost(evt);
break;
}
super.processFocusEvent(evt);
}
private int getModifiers(KeyEvent e) {
return
(e.isControlDown() ? VDUInput.KEY_CONTROL : 0) |
(e.isShiftDown() ? VDUInput.KEY_SHIFT : 0) |
(e.isAltDown() ? VDUInput.KEY_ALT : 0) |
(e.isActionKey() ? VDUInput.KEY_ACTION : 0);
}
}