package com.drawbridge.text; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; public class DBCaret { private final DBDocument mTextComponent; private boolean mVisible; private Rectangle mBounds; // Dot is the last drop position private Point mDotPixelPosition; public Point mDotCharPosition; // Mark is the initial click position public Point mMarkPixelPosition; public Point mMarkCharPosition; private Blinker mBlinker; private Thread mBlinkerThread; private int mBlinkDelay = 500; // default is 600 private final int mCaretThickness = 2; public DBCaret(DBDocument textComponent, Rectangle bounds) { mTextComponent = textComponent; mBounds = bounds; mVisible = false; } public void setDotPositionFromClick(int panelX, int panelY) { panelX = panelX - mTextComponent.getInsets().left; // get nearest valid line int closestYLine = mTextComponent.getLinePosition(panelY); Point closestCharPos = mTextComponent.getDocumentModel().getCharPosFromMouseClick(panelX, closestYLine); Point closestPixelPos = mTextComponent.charPosToPixelPos(closestCharPos.x, closestCharPos.y); setDotPosition(closestPixelPos, closestCharPos); restartDotAnimation(); } public void setMarkPositionFromClick(int panelX, int panelY) { panelX = panelX - mTextComponent.getInsets().left; // get nearest valid line int closestYLine = mTextComponent.getLinePosition(panelY); Point closestCharPos = mTextComponent.getDocumentModel().getCharPosFromMouseClick(panelX, closestYLine); Point closestPixelPos = mTextComponent.charPosToPixelPos(closestCharPos.x, closestCharPos.y); setMarkPosition(closestPixelPos, closestCharPos); restartDotAnimation(); } public Blinker getBlinker() { return mBlinker; } public void Paint(Graphics g) { if (mBlinker == null || mBlinkerThread == null) { mVisible = false; mBlinker = new Blinker(); mBlinkerThread = new Thread(mBlinker); restartDotAnimation(); DBCaret.this.setDotPosition(new Point(0, 0), new Point(0, 0)); } // Draw Caret if (mVisible && mBounds != null && mDotPixelPosition != null) { int xPos = mBounds.x + mDotPixelPosition.x; int yPosTop = mBounds.y + mDotPixelPosition.y; int yPosBottom = yPosTop + mTextComponent.getLineHeight() + mTextComponent.getLineSpacing(); Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.black); g2.setStroke(new BasicStroke(mCaretThickness)); g2.drawLine(xPos, yPosTop, xPos, yPosBottom); } } /** * Controls the caret animation */ class Blinker implements Runnable { boolean cancelled = false; @Override public void run() { DBCaret.this.mVisible = false; do { try { DBCaret.this.mVisible = !DBCaret.this.mVisible; mTextComponent.repaint(); Thread.sleep(DBCaret.this.mBlinkDelay); } catch (InterruptedException e) { e.printStackTrace(); } } while (!cancelled && mTextComponent.hasFocus()); } } public void restartDotAnimation() { if (mBlinkerThread != null && mBlinker != null && mBlinkerThread.isAlive()) { mBlinker.cancelled = true; } mVisible = true; mBlinker = new Blinker(); mBlinkerThread = new Thread(mBlinker); mBlinkerThread.start(); } public void setBlinkRate(int blinkRateMilliseconds) { mBlinkDelay = blinkRateMilliseconds; } public void setVisible(boolean b) { mVisible = b; } public Point getDotPixelPosition() { return mDotPixelPosition; } public Point getDotCharPosition() { return mDotCharPosition; } public void setDotPosition(Point newPixelPosition, Point newCharPosition) { this.mDotPixelPosition = newPixelPosition; this.mDotCharPosition = newCharPosition; } public void setMarkPosition(Point newPixelPosition, Point newCharPosition) { this.mMarkPixelPosition = newPixelPosition; this.mMarkCharPosition = newCharPosition; } @Override public String toString() { return "DBCaret Position: (" + mDotPixelPosition.x + ", " + mDotPixelPosition.y + ")"; } public void setDotCharPosition(Point charPos) { mDotCharPosition = charPos; mDotPixelPosition = mTextComponent.charPosToPixelPos(charPos.x, charPos.y); this.restartDotAnimation(); } public void setMarkCharPosition(Point charPos) { mMarkCharPosition = charPos; mMarkPixelPosition = mTextComponent.charPosToPixelPos(charPos.x, charPos.y); this.restartDotAnimation(); } public Point getMarkCharPosition() { return mMarkCharPosition; } public Point getMarkPixelPosition() { return mMarkPixelPosition; } public String toStringMarkChar() { return "(" + this.mMarkCharPosition.x + ", " + this.mMarkCharPosition.y + ")"; } public String toStringDotChar() { return "(" + this.mDotCharPosition.x + ", " + this.mDotCharPosition.y + ")"; } }