xtn5250mg/net/infordata/em/crt/XICrt.java
2026-01-16 15:47:16 +01:00

1238 lines
31 KiB
Java

/*
Copyright 2007 Infordata S.p.A.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
!!V 08/04/97 rel. 0.92a- new update method that avoid screen flashing.
18/04/97 rel. 0.92b- setFreeze() method changed.
14/05/97 rel. 1.00 - first release.
23/05/97 rel. 1.00a- improved recalcFontSize() method.
05/06/97 rel. 1.00c- reference cursor.
08/07/97 rel. 1.01c- if the crt is freezed then the cursor is never drawed.
14/07/97 rel. 1.02 - setCrtSize() method added.
15/07/97 rel. 1.02c- finalize() method added.
17/07/97 rel. 1.02e- revisited setFont().
25/07/97 rel. 1.03a- revisited setFont().
30/07/97 rel. 1.03b- bugs.
06/07/97 rel. 1.03c- double-buffering.
28/08/97 rel. 1.04 - bug in setFreeze().
24/09/97 rel. 1.05 - DNCX project.
14/01/98 rel. 1.06 - asynchronous paint on off-screen image.
03/03/98 rel. _.___- SWING and reorganization.
***
30/06/98 rel. _.___- Swing, JBuilder2 e VSS.
04/02/99 rel. 1.11 - Swing 1.1, bug in recalcFontSize() and jdk 1.2 support.
11/06/99 rel. 1.12a- CursorShape interface has been introduced, some rework
on cursor handling.
29/07/99 rel. 1.14 - Rework on 3d look&feel.
27/02/01 rel. _.__ - Rework on cursor handling.
*/
package net.infordata.em.crt;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.VolatileImage;
import java.awt.FontFormatException;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.ObjectInputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import net.infordata.em.util.XIUtil;
///////////////////////////////////////////////////////////////////////////////
/**
* Implements a generic monospaced-character panel.
* It uses an out off screen image buffer to speed-up painting operations.
* Character coordinate are zero based.
*
* @see XICrtBuffer
*
* @author Valentino Proietti - Infordata S.p.A.
*/
public class XICrt extends JComponent implements Serializable {
private static final long serialVersionUID = 1L;
// Debug level 0 = none, 1 = , 2 = detailed
static final int DEBUG = 0;
/**
* Minimum accepted font size.
* @see #setFont
*/
public static final int MIN_FONT_SIZE = 1;
public static final int MAX_FONT_SIZE = 30; //!!V 03/03/98
//
transient private VolatileImage ivImage;
// The offscreen buffer.
private XICrtBuffer ivCrtBuffer;
// repaint is freezed
transient private boolean ivFreeze = false;
transient private int ivRepaintCount;
//!!0.92b
transient private Rectangle ivSumRect;
// cursor blinking thread
transient private CursorBlinkingThread ivBlinkThread;
// used to calculate minimum-size
transient private int ivMinCharW;
transient private int ivMinCharH;
transient private boolean ivInitialized;
//
private Font ivFont;
//!!V 03/03/98
transient private FontsCache ivFontsCache;
//!!1.11 properties
public static final String CRT_SIZE = "crtSize";
//!!1.12a
transient public Cursor ivCursor = new Cursor();
private static final CursorShape cvDefaultCursorShape =
new DefaultCursorShape();
private static final CursorShape cvVoidCursorShape =
new VoidCursorShape();
public static Font loadFontFromResource(Class theClass, String fontFileName, float fontSize) {
float fntSize = 12f;
if (fontSize >= 1f) {
fntSize = fontSize;
}
InputStream is = null;
is = theClass.getResourceAsStream("/res/ubuntu-mono-regular.ttf");
Font font = null;
try {
System.err.println(" createFont Testing res/" + fontFileName + " ... ");
font = Font.createFont(Font.TRUETYPE_FONT, is);
System.err.println(" createFont OK ");
font = font.deriveFont(fntSize);
} catch (FontFormatException | IOException ex) {
System.err.println(" Font err : " + ex.getMessage());
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
System.err.println("Close error : " + ex.getMessage());
}
}
}
return font;
}
/**
* Default constructor.
*/
public XICrt() {
setFreeze(true);
setOpaque(true);
Font inconsolata = loadFontFromResource(XICrt.class, "MonoSpaced", MIN_FONT_SIZE);
setFont(inconsolata);
//setFont(new Font("Monospaced", Font.PLAIN, MIN_FONT_SIZE));
setLayout(null);
setCrtBuffer(createCrtBuffer(80, 24));
}
/**
* Factory method for XICrtBuffer creation.
* @see XICrtBuffer
*/
protected XICrtBuffer createCrtBuffer(int nCols, int nRows) {
return new XICrtBuffer(nCols, nRows);
}
/**
* Changes the panel font.
* Only monospaced fonts are accepted.
*/
@Override
public void setFont(Font aFont) {
FontMetrics fontMetrics = getFontMetrics(aFont);
if (fontMetrics.charWidth('W') != fontMetrics.charWidth('i'))
{
System.out.println(fontMetrics.charWidth('W'));
System.out.println(fontMetrics.charWidth('i'));
//throw new IllegalArgumentException("We Accept only monospaced font");
}
if (aFont.getSize() < MIN_FONT_SIZE)
throw new IllegalArgumentException("Font too small");
if (aFont.getSize() > MAX_FONT_SIZE)
throw new IllegalArgumentException("Font too great");
if (aFont.equals(ivFont))
return;
if (ivFont == null ||
!aFont.getName().equals(ivFont.getName()) ||
aFont.getStyle() != ivFont.getStyle())
ivFontsCache = new FontsCache(aFont);
ivFont = aFont;
super.setFont(ivFont);
if (ivInitialized)
initializeCrtBuffer();
}
/**
*/
@Override
public Font getFont() {
return ivFont;
}
private Graphics initializeVolatileImage() {
Font font = getFont();
FontMetrics fontMetrics = getFontMetrics(font);
int ww = fontMetrics.charWidth('W');
int hh = fontMetrics.getHeight();
ivImage = createVolatileImage(ivCrtBuffer.getCrtSize().width * ww,
ivCrtBuffer.getCrtSize().height * hh);
Graphics gr = ivImage.getGraphics();
gr.setFont(font);
fontMetrics = getFontMetrics(new Font(font.getName(), font.getStyle(), MIN_FONT_SIZE));
ivMinCharW = fontMetrics.charWidth('W');
ivMinCharH = fontMetrics.getHeight();
return gr;
}
/**
*/
private void initializeCrtBuffer() {
synchronized (this) {
ivCrtBuffer.setGraphics(initializeVolatileImage());
repaint();
}
// request parent layout recalc.
super.invalidate();
//!!0.96 it' s up to the parent decide when validate itself
//!!0.96 getParent().validate();
}
/**
* If initialized calls recalcFontSize().
* @see #recalcFontSize
*/
@Override
public void invalidate() {
if (ivInitialized)
recalcFontSize();
super.invalidate();
}
/**
* Redefines Panel.addNotify() to do some initializations when called for
* the first time.
*/
@Override
public void addNotify() {
super.addNotify();
if (!ivInitialized) {
ivInitialized = true;
initializeCrtBuffer();
//setFont(ivStartFont);
setFreeze(false);
//setBlinkingCursor(false);
}
}
/**
*/
@Override
public void removeNotify() {
ivInitialized = false;
setFreeze(true);
super.removeNotify();
ivCrtBuffer.setGraphics(null);
Font inconsolata = loadFontFromResource(XICrt.class, "MonoSpaced", MIN_FONT_SIZE);
setFont(inconsolata);
//setFont(new Font("Monospaced", Font.PLAIN, MIN_FONT_SIZE));
}
/**
* Returns the preferred size based on the current font dimension.
*/
@Override
public Dimension getPreferredSize() {
return ivCrtBuffer.getSize();
}
/**
* Returns the minimum size based on minimum size of the current font.
*/
@Override
public Dimension getMinimumSize() {
return new Dimension(ivMinCharW * ivCrtBuffer.getCrtSize().width,
ivMinCharH * ivCrtBuffer.getCrtSize().height);
}
/**
* Redefined to handle the freeze attribute.
* @see #setFreeze
*/
@Override
public void repaint(long tm, int x, int y, int width, int height) {
if (!ivFreeze)
super.repaint(tm, x, y, width, height);
else {
++ivRepaintCount;
ivSumRect = ivSumRect.union(new Rectangle(x, y, width, height));
}
}
/**
* Freezes the screen, can be used to group more than one repaint.
* Do not forget to restore freeze to false when done.
* <pre>
* setFreeze(true);
* try {
* ... // repaints to be grouped
* }
* finally {
* setFreeze(false);
* }
* </pre>
*/
public synchronized void setFreeze(boolean bb) {
if (bb == ivFreeze)
return;
ivFreeze = bb; //!!1.04
if (!ivFreeze) {
if (ivRepaintCount > 0)
repaint(ivSumRect.x, ivSumRect.y, ivSumRect.width, ivSumRect.height);
ivSumRect = null;
ivRepaintCount = 0;
ivCursor.resync();
}
else {
ivSumRect = new Rectangle();
//!!1.04 avoids cursor refresh
//!!1.04 drawCursor(null, false);
}
}
/**
*/
public final boolean isFreeze() {
return ivFreeze;
}
/**
*/
@Override
public final void paint(Graphics g) {
// remove cursor from screen
//!!V cannot check if the cursor intersects the clipping area, because the
// cursor shape can exceed the cursor bounding rectangle (5250 reference
// cursor
ivCursor.beforePaint(g);
try {
super.paint(g);
}
finally {
ivCursor.afterPaint(g);
}
}
/**
* do nothing.
*/
@Override
protected void paintBorder(Graphics g) {
}
/**
* Subclasses must redefine foregroundPaint to add painting.
* @see #foregroundPaint
*/
@Override
public synchronized void paintComponent(Graphics g) {
if (ivImage != null) {
GraphicsConfiguration gconf = getGraphicsConfiguration(); // acquires a tree-lock
synchronized (ivCrtBuffer) {
do {
int returnCode = ivImage.validate(gconf);
if (returnCode == VolatileImage.IMAGE_RESTORED) {
// Contents need to be restored
ivCrtBuffer.invalidateAll();
}
else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) {
// old vImg doesn't work with new GraphicsConfig; re-create it
ivCrtBuffer.setGraphics(initializeVolatileImage());
ivCrtBuffer.invalidateAll();
}
ivCrtBuffer.sync(); //!!1.06
g.drawImage(ivImage, 0, 0, null);
if (true) break;
} while (ivImage.contentsLost());
}
}
if (isOpaque()) {
Dimension crtBuf = getCrtBufferSize();
Dimension crt = getSize();
g.setColor(getBackground());
g.fillRect(crtBuf.width, 0, crt.width - crtBuf.width, crt.height);
g.fillRect(0, crtBuf.height, crt.width, crt.height - crtBuf.height);
}
foregroundPaint(g);
if (DEBUG >= 2) {
Rectangle rt = g.getClipBounds();
g.setColor(Color.red);
g.drawRect(rt.x, rt.y, rt.width - 1, rt.height - 1);
}
}
/**
* Subclasses must redefine this method to add paintings instead of paint()
*/
protected void foregroundPaint(Graphics g) {
super.paintComponent(g);
}
/**
* Clears the panel.
*/
public void clear() {
ivCrtBuffer.clear();
repaint();
}
/**
* Scrolls a portion of the panel.
*/
public void scroll(boolean down, int row1, int row2, int nLines) {
if (down)
ivCrtBuffer.scrollDown(row1, row2, nLines);
else
ivCrtBuffer.scrollUp(row1, row2, nLines);
repaint(0, row1 * ivCrtBuffer.getCharSize().height,
ivCrtBuffer.getSize().width,
(row2 - row1) * ivCrtBuffer.getCharSize().height);
}
/**
* Draws a string using the default attribute.
*/
public void drawString(String str, int col, int row) {
drawString(str, col, row, ivCrtBuffer.getDefAttr());
}
/**
* Draws a string with the given attribute.
*/
public void drawString(String str, int col, int row, int aAttr) {
ivCrtBuffer.drawString(str, col, row, aAttr);
repaint(col * ivCrtBuffer.getCharSize().width, row * ivCrtBuffer.getCharSize().height,
str.length() * ivCrtBuffer.getCharSize().width, ivCrtBuffer.getCharSize().height);
}
/**
* Can be used to verify the presence of a string in the buffer.
* @see String#indexOf
*/
public String getString(int col, int row, int nChars) {
return ivCrtBuffer.getString(col, row, nChars);
}
/**
* Can be used to verify the presence of a string in the buffer.
* @see String#indexOf
*/
public String getString() {
return ivCrtBuffer.getString();
}
/**
* Returns the attribute at the given position.
*/
public int getAttr(int col, int row) {
return ivCrtBuffer.getAttr(col, row);
}
/**
* Returns the character present at the given position.
*/
public char getChar(int col, int row) {
return ivCrtBuffer.getChar(col, row);
}
/**
* Sets the default attribute.
*/
public void setDefAttr(int aAttr) {
ivCrtBuffer.setDefAttr(aAttr);
}
/**
* Forces the column coord in crt bounds
*/
protected final int assureColIn(int aCol) {
return Math.max(0, Math.min(ivCrtBuffer.getCrtSize().width - 1, aCol));
}
/**
* Forces the row coord in crt bounds
*/
protected final int assureRowIn(int aRow) {
return Math.max(0, Math.min(ivCrtBuffer.getCrtSize().height - 1, aRow));
}
/**
* Moves the cursor at the give position.
*/
public void setCursorPos(int aCol, int aRow) {
ivCursor.setPosition(assureColIn(aCol), assureRowIn(aRow));
}
/**
* Returns the cursor column position.
*/
public int getCursorCol() {
return ivCursor.getCol();
}
/**
* Returns the cursor row position.
*/
public int getCursorRow() {
return ivCursor.getRow();
}
/**
* Sets the dimensions in chars.
* The screen is cleared and the cursor is moved to (0, 0).
*/
public void setCrtSize(int nCols, int nRows) {
synchronized (this) {
Dimension dim = ivCrtBuffer.getCrtSize();
if (dim.width == nCols && dim.height == nRows)
return;
XICrtBuffer newCrtBuffer = createCrtBuffer(nCols, nRows);
setCrtBuffer(newCrtBuffer);
if (ivInitialized) //!!1.04
ivCrtBuffer.setGraphics(ivImage.getGraphics());
}
setCursorPos(0, 0);
repaint();
invalidate();
firePropertyChange(CRT_SIZE, null, null);
}
/**
* Returns the dimensions in chars.
*/
public Dimension getCrtSize() {
return ivCrtBuffer.getCrtSize();
}
/**
* Sets the offscreen buffer.
*/
protected final void setCrtBuffer(XICrtBuffer aCrt) {
if (aCrt == ivCrtBuffer)
return;
//!!1.14 if (ivInitialized)
//!!1.14 throw new IllegalArgumentException("Crt already initialized");
if (aCrt.getCrt() != null)
throw new IllegalArgumentException("Buffer already associated with a crt");
if (ivCrtBuffer != null)
ivCrtBuffer.setCrt(null);
ivCrtBuffer = aCrt;
if (ivCrtBuffer != null)
ivCrtBuffer.setCrt(this);
}
/**
*/
protected final XICrtBuffer getCrtBuffer() {
return ivCrtBuffer;
}
/**
* Returns the dimension in pixels of the off-screen buffer.
*/
public Dimension getCrtBufferSize() {
return ivCrtBuffer.getSize();
}
/**
* Returns the current char size in pixels.
*/
public Dimension getCharSize() {
return ivCrtBuffer.getCharSize();
}
/**
* Returns the minimum char size in pixels.
*/
public Dimension getMinCharSize() {
return new Dimension(ivMinCharW, ivMinCharH);
}
/**
* Returns the cursor bounding rectangle.
*/
protected Rectangle getCursorRect() {
return ivCursor.getBoundingRect();
}
/**
*/
public void setCursorVisible(boolean aFlag) {
ivCursor.setVisible(aFlag);
}
/**
*/
public final boolean isCursorVisible() {
return ivCursor.isVisible();
}
/**
*/
public synchronized void setBlinkingCursor(boolean flag) {
if (flag == (ivBlinkThread != null))
return;
if (flag) {
ivBlinkThread = new CursorBlinkingThread();
ivBlinkThread.setPriority(Thread.NORM_PRIORITY - 1);
ivBlinkThread.start();
}
else {
ivBlinkThread.terminate();
ivBlinkThread = null;
}
ivCursor.resync();
}
/**
*/
public boolean isBlinkingCursor() {
return ivBlinkThread != null;
}
/**
* Used by recalcFontSize.
* Can be changed by subclasses to take care, for example, of the status-bar presence.
* @see recalcFontSize
*/
protected Dimension getTestSize(Font aFont) {
FontMetrics fm = getFontMetrics(aFont);
Dimension res = new Dimension(fm.charWidth('W') * getCrtSize().width,
fm.getHeight() * getCrtSize().height);
return res;
}
/**
* Found a font able to cover as much as possible of the off-screen
* buffer surface.
*/
private void recalcFontSize() {
Font font = getFont();
Dimension size = getSize();
int i = 0;
Font xFont = font;
Font yFont = font;
Font ft;
int startIdx = 1;
if (size.width < ivCrtBuffer.getSize().width) {
for (i = xFont.getSize(); i >= MIN_FONT_SIZE; i -= 4) {
ft = ivFontsCache.getFont(i);
if (getTestSize(ft).width <= size.width) {
xFont = ft;
break;
}
else
xFont = ft;
}
startIdx = 0;
}
// found max font (x dimension)
// to speed up the search uses a two step algorythm
for (int j = startIdx; j >= 0; j--) {
for (i = xFont.getSize(); i <= 30; i += (1 + j * 3)) {
ft = ivFontsCache.getFont(i);
if (getTestSize(ft).width <= size.width)
xFont = ft;
else
break;
}
}
// found max font (y dimension)
startIdx = 1;
if (size.height < ivCrtBuffer.getSize().height) {
for (i = yFont.getSize(); i >= MIN_FONT_SIZE; i -= 4) {
ft = ivFontsCache.getFont(i);
if (getTestSize(ft).height <= size.height) {
yFont = ft;
break;
}
else
yFont = ft;
}
startIdx = 0;
}
for (int j = startIdx; j >= 0; j--) {
for (i = yFont.getSize(); i <= 30; i += (1 + j * 3)) {
ft = ivFontsCache.getFont(i);
if (getTestSize(ft).height <= size.height)
yFont = ft;
else
break;
}
}
// try to use the smaller one
setFont(ivFontsCache.getFont(Math.min(xFont.getSize(), yFont.getSize())));
}
/**
* From char coords to point coords.
*/
public final Point toPoints(int aCol, int aRow) {
return ivCrtBuffer.toPoints(aCol, aRow);
}
/**
* From char coords to point coords.
*/
public final Rectangle toPoints(int aCol, int aRow, int aNCols, int aNRows) {
return ivCrtBuffer.toPoints(aCol, aRow, aNCols, aNRows);
}
/**
*/
@Override
protected void finalize() throws Throwable {
setBlinkingCursor(false);
super.finalize();
}
/**
*/
void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}
void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
}
/**
*/
protected CursorShape getCursorShape() {
return cvDefaultCursorShape;
}
/**
*/
protected CursorShape getFixedCursorShape() {
return null;
}
/**
* Used only for test purposes.
*/
public static void main(String[] argv) {
JFrame frm = new JFrame();
XICrt crt = new XICrt();
crt.drawString("eccome" + '!', 0, 1);
crt.setBackground(Color.yellow);
frm.getContentPane().add(crt);
frm.setBounds(0, 0, 600, 500);
frm.setVisible(true);
crt.setCursorVisible(true);
crt.drawString("CIAO", 0, 0);
crt.drawString("X", 79, 23);
System.out.println(crt.ivCrtBuffer.getChar(79, 23));
try {
Thread.sleep(5000);
}
catch (Exception ex) {
}
crt.drawString("ARICIAO", 20, 0);
System.out.println("end.");
}
//////////////////////////////////////////////////////////////////////////////
private class FontsCache {
private Font[] ivFonts = new Font[MAX_FONT_SIZE - MIN_FONT_SIZE + 1];
private Font ivFont;
public FontsCache(Font font) {
ivFont = font;
}
public Font getFont(int size) {
if (ivFonts[size - MIN_FONT_SIZE] == null) {
ivFonts[size - MIN_FONT_SIZE] = new Font(ivFont.getName(),
ivFont.getStyle(),
size);
}
return ivFonts[size - MIN_FONT_SIZE];
}
}
//////////////////////////////////////////////////////////////////////////////
private class CursorBlinkingThread extends Thread {
private volatile boolean ivTerminate = false;
public CursorBlinkingThread() {
super("XICrt cursor blinking thread");
}
public void terminate() {
ivTerminate = true;
interrupt();
}
@Override
public void run() {
while (!ivTerminate) {
if (isCursorVisible())
ivCursor.blink();
try {
Thread.sleep(700);
}
catch (Exception ex) {
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
/**
* A cursor shape cannot change its shape, but you can switch to a different
* cursor shape.
*/
public static interface CursorShape {
/**
*/
public void drawCursorShape(Graphics gc, Rectangle rt);
}
//////////////////////////////////////////////////////////////////////////////
/**
*/
public static class VoidCursorShape implements CursorShape {
/**
*/
public void drawCursorShape(Graphics gc, Rectangle rt) {
}
}
//////////////////////////////////////////////////////////////////////////////
/**
*/
public static class DefaultCursorShape implements CursorShape {
/**
*/
public void drawCursorShape(Graphics gc, Rectangle rt) {
gc.setColor(Color.white);
gc.setXORMode(Color.black);
gc.fillRect(rt.x, rt.y, rt.width, rt.height);
gc.setPaintMode();
}
}
//////////////////////////////////////////////////////////////////////////////
/**
*/
private class Cursor implements Serializable {
private static final long serialVersionUID = 1L;
transient private List<CursorPlaceHolder> ivCursorsPH = new ArrayList<CursorPlaceHolder>(10);
private CursorPlaceHolder ivCurrentCursorPH = new CursorPlaceHolder(0, 0);
private boolean ivVisible = true;
transient private int ivPending = 0;
transient private int ivPendingBlink = 0;
transient private Runnable ivPendingEvent = new Runnable() {
public void run() {
ivPending = 0;
sync(false, null, true);
}
};
transient private Runnable ivPendingBlinkEvent = new Runnable() {
private boolean flag;
public void run() {
ivPendingBlink = 0;
flag = !flag;
sync(true, null, flag);
}
};
/**
*/
public void resync() {
if (SwingUtilities.isEventDispatchThread())
ivPendingEvent.run();
else
if ((ivPending++) == 0)
SwingUtilities.invokeLater(ivPendingEvent);
}
/**
*/
public void blink() {
if (SwingUtilities.isEventDispatchThread())
ivPendingBlinkEvent.run();
else
if ((ivPendingBlink++) == 0)
SwingUtilities.invokeLater(ivPendingBlinkEvent);
}
/**
*/
public void setPosition(int col, int row) {
synchronized (ivCursorsPH) {
if (col == getCol() && row == getRow())
return;
ivCursorsPH.add(ivCurrentCursorPH);
ivCurrentCursorPH = new CursorPlaceHolder(col, row);
}
resync();
}
/**
*/
public void setVisible(boolean flag) {
synchronized (ivCursorsPH) {
if (flag == ivVisible)
return;
ivVisible = flag;
ivCursorsPH.add(ivCurrentCursorPH);
ivCurrentCursorPH = new CursorPlaceHolder(getCol(), getRow());
}
resync();
}
/**
*/
public final int getCol() { return ivCurrentCursorPH.getCol(); }
public final int getRow() { return ivCurrentCursorPH.getRow(); }
/**
*/
public final boolean isVisible() { return ivVisible; }
/**
*/
public Rectangle getBoundingRect() {
return ivCurrentCursorPH.getBoundingRect();
}
/**
*/
private void sync(boolean blinkingShapeOnly, Graphics aGc, boolean showIt) {
if (DEBUG >= 1) {
if (!SwingUtilities.isEventDispatchThread())
throw new IllegalStateException();
}
synchronized (ivCursorsPH) {
Graphics gc = (aGc != null) ? aGc :
(XIUtil.is1dot2 && !XIUtil.is1dot3) ? null : getGraphics();
try {
// remove old place-holders
for (Iterator<CursorPlaceHolder> e = ivCursorsPH.iterator(); e.hasNext(); ) {
e.next().drawShapes(false, gc, false);
}
ivCursorsPH.clear();
// draw (or hide) the current one
ivCurrentCursorPH.drawShapes(blinkingShapeOnly, gc, showIt);
}
finally {
if (aGc == null && gc != null)
gc.dispose();
}
}
}
/**
*/
protected void beforePaint(Graphics g) {
// do nothing, cursor place-holders are removed in painting areas
}
/**
*/
protected void afterPaint(Graphics g) {
// restore cursor place-holders in painting areas, if they were visible
if (DEBUG >= 1) {
if (!SwingUtilities.isEventDispatchThread())
throw new IllegalStateException();
}
// remove old place-holders
for (Iterator<CursorPlaceHolder> e = ivCursorsPH.iterator(); e.hasNext(); ) {
e.next().syncShapesAfterPaint(g);
}
ivCurrentCursorPH.syncShapesAfterPaint(g);
}
}
//////////////////////////////////////////////////////////////////////////////
/**
*/
private class CursorPlaceHolder implements Serializable {
private static final long serialVersionUID = 1L;
private int ivCol;
private int ivRow;
transient private CursorShape ivCursorShape;
transient private CursorShape ivFixedCursorShape;
transient private boolean ivFixedCursorDrawed;
transient private boolean ivCursorDrawed;
/**
*/
public CursorPlaceHolder(int col, int row) {
ivCol = col;
ivRow = row;
}
public final int getCol() { return ivCol; }
public final int getRow() { return ivRow; }
/**
*/
public Rectangle getBoundingRect() {
Dimension sz = ivCrtBuffer.getCharSize();
Point pt = ivCrtBuffer.toPoint(ivCol, ivRow);
return new Rectangle(pt.x, pt.y - sz.height, sz.width, sz.height);
}
/**
*/
private void retrieveShapes() {
// a cursorPlaceHolder is bounded always to the same shape
if (ivCursorShape == null) {
ivCursorShape = getCursorShape();
if (ivCursorShape == null)
ivCursorShape = cvVoidCursorShape;
}
if (ivFixedCursorShape == null) {
ivFixedCursorShape = getFixedCursorShape();
if (ivFixedCursorShape == null)
ivFixedCursorShape = cvVoidCursorShape;
}
}
/**
* Draws the cursor calling drawCursorShape and drawFixedCursorShape.
*/
public void drawShapes(boolean blinkingShapeOnly, Graphics aGc,
boolean showIt) {
showIt = showIt && isCursorVisible();
if (showIt == ivCursorDrawed && showIt == ivFixedCursorDrawed)
return;
// a cursorPlaceHolder is bounded always to the same shape
retrieveShapes();
Graphics gc = (aGc != null) ? aGc : getGraphics();
if (gc == null)
return;
try {
if (ivCursorDrawed != showIt) {
ivCursorShape.drawCursorShape(gc, getBoundingRect());
ivCursorDrawed = showIt;
}
if (ivFixedCursorDrawed != showIt && !blinkingShapeOnly) {
//!!V1.2 workaround
// there are problems with jdk 1.2.1, lines drawed outside the paint
// method are translated of 1 pixel respect lines drawed inside the
// paint method !!??
if (XIUtil.is1dot2 && !XIUtil.is1dot3 && aGc == null)
gc.translate(-1, -1);
ivFixedCursorShape.drawCursorShape(gc, getBoundingRect());
ivFixedCursorDrawed = showIt;
}
}
finally {
if (aGc == null)
gc.dispose();
}
}
/**
* Must be called always in the paint event.
*/
public void syncShapesAfterPaint(Graphics gc) {
if (gc == null)
throw new IllegalArgumentException();
// a cursorPlaceHolder is bounded always to the same shape
retrieveShapes();
if (ivCursorDrawed) {
ivCursorShape.drawCursorShape(gc, getBoundingRect());
}
if (ivFixedCursorDrawed) {
ivFixedCursorShape.drawCursorShape(gc, getBoundingRect());
}
}
}
}