package com.drawbridge.text; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; 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; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.ImageIcon; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.swing.Scrollable; import javax.swing.SwingUtilities; import com.drawbridge.Activity; import com.drawbridge.correspondance.RadialSpotlight; import com.drawbridge.correspondance.Spotlight; import com.drawbridge.correspondance.Spotlightable; import com.drawbridge.dm.DMSimplePanel; import com.drawbridge.jsengine.JsEngine; import com.drawbridge.text.DBCaret.Blinker; import com.drawbridge.utils.GraphicUtils; import com.drawbridge.utils.HighlightElement; import com.drawbridge.utils.JSUtils; import com.drawbridge.utils.Utils; import com.drawbridge.vl.VLPanel; public class DBDocument extends JComponent implements MouseListener, MouseMotionListener, KeyListener, FocusListener, Scrollable, ComponentListener, ClipboardOwner, Spotlightable { public class CopyAction extends AbstractAction { private static final long serialVersionUID = 1L; public CopyAction(String text, ImageIcon icon, String desc, Integer mnemonic) { super(text, icon); putValue(SHORT_DESCRIPTION, desc); putValue(MNEMONIC_KEY, mnemonic); } public void actionPerformed(ActionEvent e) { String string = mDocumentModel.getRawTextSubstring(mCaret.mMarkCharPosition, mCaret.mDotCharPosition); Utils.out.println("Copy String:" + string); StringSelection stringSelection = new StringSelection(string); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(stringSelection, DBDocument.this); } } public class CutAction extends AbstractAction { private static final long serialVersionUID = 1L; public CutAction(String text, ImageIcon icon, String desc, Integer mnemonic) { super(text, icon); putValue(SHORT_DESCRIPTION, desc); putValue(MNEMONIC_KEY, mnemonic); } public void actionPerformed(ActionEvent e) { String string = mDocumentModel.getRawTextSubstring(mCaret.mMarkCharPosition, mCaret.mDotCharPosition); StringSelection stringSelection = new StringSelection(string); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(stringSelection, DBDocument.this); mDocumentModel.replace(mCaret.mMarkCharPosition, mCaret.mDotCharPosition, ""); boolean useMark = DBDocumentModel.isCharPosLessThan(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition()); if (useMark) mCaret.setDotCharPosition(mCaret.getMarkCharPosition()); else mCaret.setMarkCharPosition(mCaret.getDotCharPosition()); updateUIOnChange(true); updateVLLineSelection(); } } public class SelectAllAction extends AbstractAction { private static final long serialVersionUID = 1l; public SelectAllAction(String text, ImageIcon icon, String desc, Integer mnemonic) { super(text, icon); putValue(SHORT_DESCRIPTION, desc); putValue(MNEMONIC_KEY, mnemonic); } public void actionPerformed(ActionEvent e) { mCaret.setMarkCharPosition(new Point(0, 0)); int lastPositionIndex = DBDocument.this.mDocumentModel.getRawText().length(); Point dotPoint = DBDocument.this.mDocumentModel.getCharPositionFromStringIndex(lastPositionIndex); mCaret.setDotCharPosition(dotPoint); updateUIOnChange(true); updateVLLineSelection(); } } public interface DBDocumentListener { public void onDocumentUpdate(String string); } public enum InputType { INPUT_SELECTION, INPUT_CARET } public class PasteAction extends AbstractAction { private static final long serialVersionUID = 1L; public PasteAction(String text, ImageIcon icon, String desc, Integer mnemonic) { super(text, icon); putValue(SHORT_DESCRIPTION, desc); putValue(MNEMONIC_KEY, mnemonic); } public void actionPerformed(ActionEvent e) { String result = ""; Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable contents = clipboard.getContents(null); boolean hasTransferableText = (contents != null) && contents.isDataFlavorSupported(DataFlavor.stringFlavor); if (hasTransferableText) { try { result = (String) contents.getTransferData(DataFlavor.stringFlavor); } catch (UnsupportedFlavorException ex) { // highly unlikely since we are using a standard DataFlavor Utils.out.println(this.getClass(), ex.toString()); ex.printStackTrace(); } catch (IOException ex) { Utils.out.println(this.getClass(), ex.toString()); ex.printStackTrace(); } } if (result != null) mDocumentModel.replace(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition(), result); Point finalDotPosition = mDocumentModel.getCharPositionFromStringIndex(mDocumentModel.getStringIndexFromCharPos(mCaret.getMarkCharPosition()) + result.length()); mCaret.setDotCharPosition(finalDotPosition); updateUIOnChange(true); updateVLLineSelection(); } } class PopUpDemo extends JPopupMenu { /** * */ private static final long serialVersionUID = 1L; JMenuItem anItem; public PopUpDemo(InputType selectionType) { JMenuItem cut = new JMenuItem(new CutAction("Cut", null, "Cut text", 10)); cut.setEnabled((selectionType == InputType.INPUT_SELECTION) ? true : false); JMenuItem copy = new JMenuItem(new CopyAction("Copy", null, "Copy text", 12)); copy.setEnabled((selectionType == InputType.INPUT_SELECTION) ? true : false); JMenuItem paste = new JMenuItem(new PasteAction("Paste", null, "Paste text", 14)); paste.setEnabled(true); JMenuItem selectAll = new JMenuItem(new SelectAllAction("SelectAll", null, "Select All", 14)); paste.setEnabled(true); add(cut); add(copy); add(paste); add(selectAll); } } private static final long serialVersionUID = 1L; /** Connected components providing overlay **/ private DBCaret mCaret = null; private DBSelection mSelection = null; /** Size constraints **/ private Dimension mPreferredDimension = new Dimension(500, 600); private Insets mInsets = new Insets(5, 20, 20, 20); private int mLineHeight = 7; private int mLineSpacing = 15; private DBDocumentModel mDocumentModel = null; private Integer mHoverIndex = null; private ArrayList<HighlightElement> mHighlightElements = null; private boolean mDrawCaretPosition = false; private boolean mLineHoverHighlight = false; private boolean mCaretLineHighlight = true; private BufferedImage mErrorImage = null; public Rectangle mErrorRect = null; private DocumentError mDocumentError; private boolean mDimBackground = false; private HashSet<RadialSpotlight> mSpotlights = null; public boolean mInitialised = false; public final Font mDocumentFont; public final boolean mCaretEnabled; public DBDocument(final String text, Font defaultFont, boolean caret) { mDocumentFont = defaultFont; mCaretEnabled = caret; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { while (DBDocument.this.getWidth() <= 0 || DBDocument.this.getHeight() <= 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } DBDocument.this.setLayout(null); if (text == null) { throw new RuntimeException("Text cannot be null"); } mDocumentModel = new DBDocumentModel(text, getPaddedWidth(), DBDocument.this); mLineHeight = DBDocument.this.getFontMetrics(DBDocument.this.getFont()).getHeight(); requestFocus(); if(mCaretEnabled){ addKeyListener(DBDocument.this); initKeyMappings(); addMouseListener(DBDocument.this); addMouseMotionListener(DBDocument.this); addFocusListener(DBDocument.this); } setFocusable(true); setVisible(true); setEnabled(true); addComponentListener(DBDocument.this); if(mCaretEnabled){ mCaret = new DBCaret(DBDocument.this, new Rectangle(mInsets.left, mInsets.top, getPaddedWidth(), getPaddedHeight())); mSelection = new DBSelection(DBDocument.this, Color.decode("#90C8DA")); mSelection.setVisible(true); mCaret.setDotPositionFromClick(mInsets.left, mInsets.top); mCaret.setMarkPositionFromClick(mInsets.left, mInsets.top); } mErrorImage = GraphicUtils.loadImageFromResource("/Assets/red-dot.png"); mInitialised = true; setOpaque(false); setBackground(Color.white); } }); } public Point charPosToPixelPos(int charX, int charY) { if (charY >= 0 && charY < mDocumentModel.getNumberOfLines()) { String subStr = mDocumentModel.getLines().get(charY).substring(0, Math.min(mDocumentModel.getLines().get(charY).length(), charX)); int width = this.getFontMetrics(mDocumentFont).stringWidth(subStr); int y = charY * (this.mLineHeight + this.mLineSpacing); return new Point(width, y); } else { Utils.err.println(getClass(), "No such char position :" + charX + ", " + charY + ", " + mDocumentModel.getNumberOfLines()); return null; } } @Override public void componentHidden(ComponentEvent arg0) { } @Override public void componentMoved(ComponentEvent arg0) { } @Override public void componentResized(ComponentEvent arg0) { if(mCaretEnabled){ // Get current caretPosition int markIndex = this.mDocumentModel.getStringIndexFromCharPos(mCaret.getMarkCharPosition()); int dotIndex = this.mDocumentModel.getStringIndexFromCharPos(mCaret.getDotCharPosition()); // Update line width this.mDocumentModel.setLineWidth(this.getPaddedWidth()); // Set current caretPosition Point markCharPos = this.mDocumentModel.getCharPositionFromStringIndex(markIndex); Point dotCharPos = this.mDocumentModel.getCharPositionFromStringIndex(dotIndex); mCaret.setMarkCharPosition(markCharPos); mCaret.setDotCharPosition(dotCharPos); } } @Override public void componentShown(ComponentEvent arg0) { } @Override public void focusGained(FocusEvent arg0) { } @Override public void focusLost(FocusEvent arg0) { if(mCaretEnabled){ mCaret.setVisible(false); Blinker b = mCaret.getBlinker(); b.cancelled = true; } getParent().repaint(); } public DBCaret getCaret() { return mCaret; } public HighlightElement getCharHighlightElement(int index) { int latestElement = -1; if (mHighlightElements != null) { for (int i = 0; i < mHighlightElements.size(); i++) { int start = mHighlightElements.get(i).mStartPos; int end = mHighlightElements.get(i).mEndPos; if (start <= index && index <= end) { latestElement = i; } } } if (latestElement != -1) return mHighlightElements.get(latestElement); return null; } public DBDocumentModel getDocumentModel() { return mDocumentModel; } public Insets getInsets() { return mInsets; } public int getLineHeight() { return this.mLineHeight; } /** * Accurately gives you the line position for a given mouse position * * @param x * @param y * @return */ public int getLinePosition(int y) { y = y - mInsets.top; int selectedLine = y / (this.getLineHeight() + this.getLineSpacing()); // get nearest valid line int closestYLine = 0; if (selectedLine < 0) { closestYLine = 0; } else if (selectedLine > (mDocumentModel.getNumberOfLines() - 1)) { closestYLine = mDocumentModel.getNumberOfLines() - 1; } else { closestYLine = selectedLine; } return closestYLine; } public int getLineSpacing() { return this.mLineSpacing; } @Override public Dimension getMaximumSize() { return mPreferredDimension; } @Override public Dimension getMinimumSize() { return mPreferredDimension; } public int getPaddedHeight() { return this.getSize().height - (mInsets.top + mInsets.bottom); } public int getPaddedWidth() { return this.getSize().width - (mInsets.left + mInsets.right); } @Override public Dimension getPreferredScrollableViewportSize() { return this.getSize(); } @Override public Dimension getPreferredSize() { return mPreferredDimension; } @Override public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) { return 0; } @Override public boolean getScrollableTracksViewportHeight() { return false; } @Override public boolean getScrollableTracksViewportWidth() { return false; } @Override public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) { return 0; } public String getText() { if (mDocumentModel != null) return mDocumentModel.getRawText(); else return null; } public int getTextWidth() { int width = this.getSize().width; width -= (mInsets.left + mInsets.right); return width; } private void initKeyMappings() { ActionMap actionMap = DBDocument.this.getActionMap(); int condition = JComponent.WHEN_FOCUSED; InputMap inputMap = DBDocument.this.getInputMap(condition); String tab = "tab", copy = "copy", paste = "paste", selectAll = "selectall", cut = "cut", enter = "enter", delete = "delete", backspace = "backspace", left = "left", right = "right", leftstart = "leftstart", rightend = "rightend", up = "up", down = "down"; inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), tab); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), cut); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), copy); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), paste); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), selectAll); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), delete); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), enter); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), backspace); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), left); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), right); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), leftstart); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), rightend); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), up); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), down); DBDocument.this.setFocusTraversalKeysEnabled(false); actionMap.put(tab, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { mDocumentModel.replace(mCaret.mMarkCharPosition, mCaret.mDotCharPosition, " "); Point posRight = mDocumentModel.getCharPositionRight(mCaret.getMarkCharPosition()); posRight = mDocumentModel.getCharPositionRight(posRight); posRight = mDocumentModel.getCharPositionRight(posRight); posRight = mDocumentModel.getCharPositionRight(posRight); mCaret.setMarkCharPosition(posRight); mCaret.setDotCharPosition(posRight); updateUIOnChange(false); updateVLLineSelection(); } } }); actionMap.put(cut, new CutAction("Cut", null, "Cut text", 12)); actionMap.put(copy, new CopyAction("Copy", null, "Cut text", 12)); actionMap.put(paste, new PasteAction("Paste", null, "Cut text", 12)); actionMap.put(selectAll, new SelectAllAction("SelectAll", null, "Select All", 12)); actionMap.put(enter, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { mDocumentModel.remove(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition()); mDocumentModel.insertCharAt(DBDocumentModel.NEWLINE, mCaret.getMarkCharPosition()); Utils.err.println("DBDocumentModel.NEWLINE:" + DBDocumentModel.NEWLINE); mCaret.setDotCharPosition(new Point(0, mCaret.getMarkCharPosition().y + 1)); mCaret.setMarkCharPosition(new Point(0, mCaret.getMarkCharPosition().y + 1)); updateUIOnChange(true); updateVLLineSelection(); } } }); actionMap.put(delete, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { if (mCaret.getMarkCharPosition().equals(mCaret.getDotCharPosition())) { mDocumentModel.delete(mCaret.getMarkCharPosition()); mCaret.setDotCharPosition(mCaret.getMarkCharPosition()); } else { boolean markFirst = DBDocumentModel.isCharPosLessThan(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition()); if (markFirst) { mDocumentModel.replace(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition(), ""); mCaret.setDotCharPosition(mCaret.getMarkCharPosition()); } else { mDocumentModel.replace(mCaret.getDotCharPosition(), mCaret.getMarkCharPosition(), ""); mCaret.setMarkCharPosition(mCaret.getDotCharPosition()); } } updateUIOnChange(true); updateVLLineSelection(); } } }); actionMap.put(backspace, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { Point backspacePoint; if (!mCaret.getMarkCharPosition().equals(mCaret.getDotCharPosition())) { mDocumentModel.remove(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition()); backspacePoint = (DBDocumentModel.isCharPosLessThan(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition())) ? mCaret.getMarkCharPosition() : mCaret.getDotCharPosition(); } else { backspacePoint = mDocumentModel.backspace(mCaret.mMarkCharPosition); } mCaret.setDotCharPosition(backspacePoint); mCaret.setMarkCharPosition(backspacePoint); updateUIOnChange(true); updateVLLineSelection(); } } }); actionMap.put(left, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { Point posLeft = mDocumentModel.getCharPositionLeft(mCaret.mDotCharPosition); Utils.out.println(this.getClass(), "Left: " + posLeft.x + ", " + posLeft.y); mCaret.setDotCharPosition(posLeft); mCaret.setMarkCharPosition(posLeft); } } }); actionMap.put(leftstart, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { Point currentPoint = mCaret.mDotCharPosition; currentPoint.x = 0; mCaret.setDotCharPosition(currentPoint); mCaret.setMarkCharPosition(currentPoint); } } }); actionMap.put(right, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { Point posRight = mDocumentModel.getCharPositionRight(mCaret.mDotCharPosition); Utils.out.println(this.getClass(), "Right: " + posRight.x + ", " + posRight.y); mCaret.setDotCharPosition(posRight); mCaret.setMarkCharPosition(posRight); } } }); actionMap.put(rightend, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { Point currentPoint = mCaret.mDotCharPosition; currentPoint.x = mDocumentModel.getLines().get(currentPoint.y).length() - 1; mCaret.setDotCharPosition(currentPoint); mCaret.setMarkCharPosition(currentPoint); } } }); actionMap.put(up, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { Point posUp = mDocumentModel.getCharPositionUp(mCaret.mDotCharPosition); Utils.out.println(this.getClass(), "Up: " + posUp.x + ", " + posUp.y); mCaret.setDotCharPosition(posUp); mCaret.setMarkCharPosition(posUp); updateVLLineSelection(); } } }); actionMap.put(down, new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent ap) { if (mDocumentModel != null) { Point posDown = mDocumentModel.getCharPositionDown(mCaret.mDotCharPosition.x, mCaret.mDotCharPosition.y); Utils.out.println(this.getClass(), "Down: " + posDown.x + ", " + posDown.y); mCaret.setDotCharPosition(posDown); mCaret.setMarkCharPosition(posDown); updateVLLineSelection(); } } }); } @Override public void keyPressed(KeyEvent ke) { } @Override public void keyReleased(KeyEvent me) { } @Override public void keyTyped(KeyEvent ke) { if ((ke.getModifiersEx() == 0 || ke.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK) && ke.getKeyChar() != KeyEvent.VK_BACK_SPACE && ke.getKeyChar() != KeyEvent.VK_DELETE && ke.getKeyChar() != KeyEvent.VK_ENTER) { Utils.err.println("KEY EVENT"); mDocumentModel.remove(mCaret.getMarkCharPosition(), mCaret.getDotCharPosition()); char keyChar = ke.getKeyChar(); mDocumentModel.insertCharAt(keyChar + "", mCaret.getMarkCharPosition()); Point posRight = mDocumentModel.getCharPositionRight(mCaret.getMarkCharPosition()); mCaret.setDotCharPosition(posRight); mCaret.setMarkCharPosition(posRight); updateUIOnChange(true); } } @Override public void lostOwnership(Clipboard c, Transferable t) { } @Override public void mouseClicked(MouseEvent me) { } @Override public void mouseDragged(MouseEvent me) { mCaret.setDotPositionFromClick(me.getX(), me.getY()); } @Override public void mouseEntered(MouseEvent me) { } @Override public void mouseExited(MouseEvent me) { mHoverIndex = null; this.repaint(); } @Override public void mouseMoved(MouseEvent me) { mHoverIndex = new Integer(this.getLinePosition(me.getY())); this.repaint(); // If user hovers over error circle if (mErrorRect != null) { if (mErrorRect.contains(me.getPoint())) { Point activityPoint = SwingUtilities.convertPoint(DBDocument.this, mErrorRect.x + mErrorRect.width + 5, mErrorRect.y - this.mLineSpacing, Activity.getInstance()); Activity.getInstance().showErrorPopup(mDocumentError.errorTitle, mDocumentError.errorDescription, activityPoint); } else { Activity.getInstance().hideTipWindow(); } } } @Override public void mousePressed(MouseEvent me) { if (me.getButton() == MouseEvent.BUTTON1) { if (me.getClickCount() == 1) { // Select the caret position mCaret.setMarkPositionFromClick(me.getX(), me.getY()); mCaret.setDotPositionFromClick(me.getX(), me.getY()); updateVLLineSelection(); DBDocument.this.requestFocusInWindow(); mCaret.setVisible(false); this.repaint(); mCaret.restartDotAnimation(); } else if (me.getClickCount() == 2) { // Select the word Point nearestCharPos = mDocumentModel.getCharPosFromMouseClick(me.getX() - this.mInsets.left, getLinePosition(me.getY())); Point[] startAndFinishChar = mDocumentModel.getWordBoundaries(nearestCharPos); if (startAndFinishChar != null) { mCaret.setMarkPosition(this.charPosToPixelPos(startAndFinishChar[0].x, startAndFinishChar[0].y), startAndFinishChar[0]); mCaret.setDotPosition(this.charPosToPixelPos(startAndFinishChar[1].x, startAndFinishChar[1].y), startAndFinishChar[1]); } else { mCaret.setMarkCharPosition(nearestCharPos); mCaret.setDotCharPosition(nearestCharPos); } this.repaint(); } else if (me.getClickCount() == 3) { // Select the row Point nearestCharPos = mDocumentModel.getCharPosFromMouseClick(me.getX() - this.mInsets.left, getLinePosition(me.getY())); mCaret.setMarkPosition(this.charPosToPixelPos(0, nearestCharPos.y), nearestCharPos); mCaret.setDotPosition(this.charPosToPixelPos(0, nearestCharPos.y + 1), new Point(0, nearestCharPos.y + 1)); } } else if (me.getButton() == MouseEvent.BUTTON3) { PopUpDemo menu; if (mCaret.getDotCharPosition().equals(mCaret.getMarkCharPosition())) { menu = new PopUpDemo(InputType.INPUT_CARET); } else { menu = new PopUpDemo(InputType.INPUT_SELECTION); } menu.show(me.getComponent(), me.getX(), me.getY()); } } public void updateVLLineSelection(){ // Highlight corresponding line in VLJPanel if (VLPanel.hasInstance()) { int mCaretPos = mCaret.getDotCharPosition().y; VLPanel.getInstance().mCanvas.mCaretLine = mCaretPos; VLPanel.getInstance().mCanvas.repaint(); } } @Override public void mouseReleased(MouseEvent me) { } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if(!getParent().getParent().getParent().getBackground().equals(getBackground())){ //TextPanel background getParent().getParent().getParent().setBackground(getBackground()); } else if(!getParent().getParent().getBackground().equals(getBackground())){ //ScrollPane background getParent().getParent().setBackground(getBackground()); // else if(getBackground().getRed() == 220 && getBackground().getBlue() == 220 && getBackground().getGreen() == 220){ } g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(getForeground()); if (mInitialised) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setFont(mDocumentFont); // Do spotlighting if we need to if (mSpotlights != null) { for (RadialSpotlight ds : mSpotlights) { ds.paintRadialSpotlight(g2); } } g2.setColor(Color.black); // Paint Selected Line if (mHoverIndex != null && mLineHoverHighlight) { g2.setColor(new Color(50, 85, 120, 40)); g2.fillRect(mInsets.left, mInsets.top + ((mHoverIndex) * (this.mLineHeight + this.mLineSpacing)), this.getPaddedWidth(), this.mLineHeight + this.mLineSpacing); } if (mCaretLineHighlight && !mDimBackground && mCaretEnabled) { g2.setColor(Color.decode("#E8F2FF")); g2.fillRect(mInsets.left, mInsets.top + ((mCaret.mMarkCharPosition.y) * (this.mLineHeight + this.mLineSpacing)), this.getPaddedWidth(), this.mLineHeight + this.mLineSpacing); } // Selection if(mCaretEnabled) mSelection.Paint(g); // Draw Text g2.setClip(mInsets.left, mInsets.top, getPaddedWidth(), getPaddedHeight()); CopyOnWriteArrayList<String> textLines = mDocumentModel.getLines(); if (textLines != null) { for (int i = 0; i < textLines.size(); i++) { g2.setColor(Color.black); String text = textLines.get(i); int startIndex = mDocumentModel.getStringIndexFromCharPos(new Point(0, i)); printLine(text, g2, i, startIndex); } } g2.setClip(0, 0, this.getWidth(), this.getHeight()); // Paint caretPosition on topRight if (mDrawCaretPosition) { Point position = this.mCaret.mDotCharPosition; if (position != null) g2.drawString("(" + position.x + " " + position.y + ")", this.getWidth() - 65, 13); } if (mErrorImage != null && mDocumentError != null) { int totalLineHeight = mLineHeight + mLineSpacing; int y = mInsets.top + (mDocumentError.lineNumber * totalLineHeight) + ((totalLineHeight - mErrorImage.getHeight()) / 2); g2.drawImage(mErrorImage, 3, y + (mLineSpacing / 2) - 2, mErrorImage.getWidth(), mErrorImage.getHeight(), null); mErrorRect = new Rectangle(3, y + (mLineSpacing / 2) - 2, mErrorImage.getWidth(), mErrorImage.getHeight()); } else { mErrorRect = null; } // Caret if(mCaretEnabled) mCaret.Paint(g); } }; public void setDocumentError(DocumentError error) { mDocumentError = error; int totalLineHeight = mLineHeight + mLineSpacing; int y = mInsets.top + (mDocumentError.lineNumber * totalLineHeight) + ((totalLineHeight - mErrorImage.getHeight()) / 2); mErrorRect = new Rectangle(3, y + (mLineSpacing / 2) - 2, mErrorImage.getWidth(), mErrorImage.getHeight()); } public void printLine(String text, Graphics2D g2, int lineNumber, int startIndex) { FontMetrics fontmetrics = g2.getFontMetrics(); int currentWidth = 0; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); HighlightElement element = getCharHighlightElement(startIndex + i); g2.setColor(Color.black); if (element != null) { Color color = element.mColor; if (color != null) g2.setColor(color); } g2.drawString(c + "", this.mInsets.left + currentWidth, mInsets.top + ((mLineHeight + mLineSpacing) * lineNumber) + mLineHeight + (mLineHeight / 2)); currentWidth += fontmetrics.charWidth(c); } } public void setCaretPositionVisibile(boolean b) { mDrawCaretPosition = b; } @Override public void setDimBackground(boolean b) { mDimBackground = b; if (b) { Color bgc = new Color(220, 220, 220); setBackground(bgc); getParent().setBackground(bgc); } else { setBackground(Color.white); getParent().setBackground(Color.white); } getParent().repaint(); } public void setHighlightElements(ArrayList<HighlightElement> highlightElements) { mHighlightElements = highlightElements; this.repaint(); } public void setInsets(Insets insets) { mInsets = insets; } public void setPreferredSize(Dimension dimension) { mPreferredDimension = dimension; } @SuppressWarnings("unchecked") @Override public void setSpotlights(HashSet<? extends Spotlight> spotlights) { mSpotlights = (HashSet<RadialSpotlight>) spotlights; getParent().repaint(); } String textCache = ""; public void setText(final String text) { if (text == null) { throw new RuntimeException("Text cannot be null!"); } // DBSelection sel = getCacheDifference(text, textCache); new Thread(new Runnable() { @Override public void run() { int waitIterations = 0; while (mDocumentModel == null && waitIterations < 20) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } mDocumentModel.setText(text); updateUIOnChange(false); } }).start(); } public void updateUIOnChange(boolean parseUpdate) { if (this.mDocumentModel != null) { if (parseUpdate) { TextPanel.getInstance().resetErrors(); if(DMSimplePanel.hasInstance() && !DMSimplePanel.getInstance().isPlaying() && DMSimplePanel.getInstance().isStoppedAndEmpty()) JsEngine.getInstance().executeCode(TextPanel.getInstance(), TextPanel.getInstance().getDocument().getText(), true); else JsEngine.getInstance().executeCode(TextPanel.getInstance(), TextPanel.getInstance().getDocument().getText(), false); } int width = this.getWidth(); int height = this.mInsets.top + (this.mDocumentModel.getNumberOfLines() * (this.mLineHeight + this.mLineSpacing)) + this.mInsets.bottom; this.mPreferredDimension = new Dimension(width, height); this.setSize(mPreferredDimension); setHighlightElements(JSUtils.getHighlightElements(this.getText())); getParent().repaint(); } } public void clearDocumentError() { mDocumentError = null; mErrorRect = null; } public void setCaretPosition(int x, int y) { this.mCaret.setMarkCharPosition(new Point(x, y)); this.mCaret.setDotCharPosition(new Point(x, y)); } public void setLineHeight(int newLineHeight) { mLineHeight = newLineHeight; } }