All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.ui.rtextarea.ConfigurableCaret Maven / Gradle / Ivy

Go to download

RSyntaxTextArea is the syntax highlighting text editor for Swing applications. Features include syntax highlighting for 40+ languages, code folding, code completion, regex find and replace, macros, code templates, undo/redo, line numbering and bracket matching.

There is a newer version: 3.5.1
Show newest version
/*
 * 12/21/2004
 *
 * ConfigurableCaret.java - The caret used by RTextArea.
 * Copyright (C) 2004 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
 */
package org.fife.ui.rtextarea;

import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.event.ActionEvent;
import java.io.*;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.text.*;


/**
 * The caret used by {@link RTextArea}.  This caret has all of the properties
 * that javax.swing.text.DefaultCaret does, as well as adding the
 * following niceties:
 *
 * 
    *
  • This caret can paint itself several different ways: *
      *
    1. As a vertical line (like DefaultCaret)
    2. *
    3. As a slightly thicker vertical line (like Eclipse)
    4. *
    5. As an underline
    6. *
    7. As a "block caret"
    8. *
    9. As a rectangle around the current character
    10. *
  • *
  • On Microsoft Windows and other operating systems that do not * support system selection (i.e., selecting text, then pasting * via the middle mouse button), clicking the middle mouse button * will cause a regular paste operation to occur. On systems * that support system selection (i.e., all UNIX variants), * the middle mouse button will behave normally.
  • *
* * @author Robert Futrell * @version 0.6 */ public class ConfigurableCaret extends DefaultCaret { /** * The minimum value of a caret style. */ public static final int MIN_STYLE = 0; /** * The vertical line style. */ public static final int VERTICAL_LINE_STYLE = 0; /** * The horizontal line style. */ public static final int UNDERLINE_STYLE = 1; /** * The block style. */ public static final int BLOCK_STYLE = 2; /** * The block border style. */ public static final int BLOCK_BORDER_STYLE = 3; /** * A thicker vertical line (2 pixels instead of 1). */ public static final int THICK_VERTICAL_LINE_STYLE = 4; /** * The maximum value of a caret style. */ public static final int MAX_STYLE = THICK_VERTICAL_LINE_STYLE; /** * Action used to select a word on a double click. */ static private transient Action selectWord = null; /** * Action used to select a line on a triple click. */ static private transient Action selectLine = null; /** * holds last MouseEvent which caused the word selection */ private transient MouseEvent selectedWordEvent = null; /** * Used for fastest-possible retrieval of the character at the * caret's position in the document. */ private transient Segment seg; /** * Whether the caret is a vertical line, a horizontal line, or a block. */ private int style; /** * The selection painter. By default this paints selections with the * text area's selection color. */ private ChangeableHighlightPainter selectionPainter; /** * Whether this is Java 1.4. */ /* TODO: Remove me when 1.4 support is removed. */ private static final boolean IS_JAVA_1_4 = "1.4".equals(System.getProperty("java.specification.version")); /** * Creates the caret using {@link #VERTICAL_LINE_STYLE}. */ public ConfigurableCaret() { this(VERTICAL_LINE_STYLE); } /** * Constructs a new ConfigurableCaret. * * @param style The style to use when painting the caret. If this is * invalid, then {@link #VERTICAL_LINE_STYLE} is used. */ public ConfigurableCaret(int style) { seg = new Segment(); setStyle(style); selectionPainter = new ChangeableHighlightPainter(); } /** * Adjusts the caret location based on the MouseEvent. */ private void adjustCaret(MouseEvent e) { if ((e.getModifiers()&ActionEvent.SHIFT_MASK)!=0 && getDot()!=-1) moveCaret(e); else positionCaret(e); } /** * Adjusts the focus, if necessary. * * @param inWindow if true indicates requestFocusInWindow should be used */ private void adjustFocus(boolean inWindow) { RTextArea textArea = getTextArea(); if ((textArea != null) && textArea.isEnabled() && textArea.isRequestFocusEnabled()) { if (inWindow) textArea.requestFocusInWindow(); else textArea.requestFocus(); } } /** * Overridden to damage the correct width of the caret, since this caret * can be different sizes. * * @param r The current location of the caret. */ protected synchronized void damage(Rectangle r) { if (r != null) { validateWidth(r); // Check for "0" or "1" caret width x = r.x - 1; y = r.y; width = r.width + 4; height = r.height; repaint(); } } /** * Called when the UI is being removed from the * interface of a JTextComponent. This is used to * unregister any listeners that were attached. * * @param c The text component. If this is not an * RTextArea, an Exception * will be thrown. * @see Caret#deinstall */ public void deinstall(JTextComponent c) { if (!(c instanceof RTextArea)) throw new IllegalArgumentException( "c must be instance of RTextArea"); super.deinstall(c); } /** * Gets the text editor component that this caret is bound to. * * @return The RTextArea. */ protected RTextArea getTextArea() { return (RTextArea)getComponent(); } /** * Returns whether this caret's selection uses rounded edges. * * @return Whether this caret's edges are rounded. * @see #setRoundedSelectionEdges */ public boolean getRoundedSelectionEdges() { return ((ChangeableHighlightPainter)getSelectionPainter()). getRoundedEdges(); } /** * Gets the painter for the Highlighter. This is overridden to return * our custom selection painter. * * @return The painter. */ protected Highlighter.HighlightPainter getSelectionPainter() { return selectionPainter; } /** * Gets the current style of this caret. * * @return The caret's style. * @see #setStyle(int) */ public int getStyle() { return style; } /** * Installs this caret on a text component. * * @param c The text component. If this is not an {@link RTextArea}, * an Exception will be thrown. * @see Caret#install */ public void install(JTextComponent c) { if (!(c instanceof RTextArea)) throw new IllegalArgumentException( "c must be instance of RTextArea"); super.install(c); } /** * Called when the mouse is clicked. If the click was generated from * button1, a double click selects a word, and a triple click the * current line. * * @param e the mouse event * @see MouseListener#mouseClicked */ public void mouseClicked(MouseEvent e) { if (! e.isConsumed()) { RTextArea textArea = getTextArea(); int nclicks = e.getClickCount(); if (SwingUtilities.isLeftMouseButton(e)) { if (nclicks<=2) { // Only handle these clicks for 1.4. In 1.5 the word // selection is (also?) handled in mousePressed, and if we // handle it here, our word selection gets doubled-up. if (IS_JAVA_1_4) { if (nclicks==1) { selectedWordEvent = null; } else { // 2 selectWord(e); selectedWordEvent = null; } } } else { nclicks %= 2; // Alternate selecting word/line. switch (nclicks) { case 0: selectWord(e); selectedWordEvent = null; break; case 1: Action a = null; ActionMap map = textArea.getActionMap(); if (map != null) a = map.get(RTextAreaEditorKit.selectLineAction); if (a == null) { if (selectLine == null) { selectLine = new RTextAreaEditorKit.SelectLineAction(); } a = selectLine; } a.actionPerformed(new ActionEvent(textArea, ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers())); } } } else if (SwingUtilities.isMiddleMouseButton(e)) { if (nclicks == 1 && textArea.isEditable() && textArea.isEnabled()) { // Paste the system selection, if it exists (e.g., on UNIX // platforms, the user can select text, the middle-mouse click // to paste it; this doesn't work on Windows). If the system // doesn't support system selection, just do a normal paste. JTextComponent c = (JTextComponent) e.getSource(); if (c != null) { try { Toolkit tk = c.getToolkit(); Clipboard buffer = tk.getSystemSelection(); // If the system supports system selections, (e.g. UNIX), // try to do it. if (buffer != null) { adjustCaret(e); TransferHandler th = c.getTransferHandler(); if (th != null) { Transferable trans = buffer.getContents(null); if (trans != null) th.importData(c, trans); } adjustFocus(true); } // If the system doesn't support system selections // (e.g. Windows), just do a normal paste. else { textArea.paste(); } } catch (HeadlessException he) { // do nothing... there is no system clipboard } } // if (c!=null) } // if (nclicks == 1 && component.isEditable() && component.isEnabled()) } // else if (SwingUtilities.isMiddleMouseButton(e)) } // if (!c.isConsumed()) } /** * Paints the cursor. * * @param g The graphics context in which to paint. */ public void paint(Graphics g) { // If the cursor is currently visible... if (isVisible()) { try { RTextArea textArea = getTextArea(); g.setColor(textArea.getCaretColor()); TextUI mapper = textArea.getUI(); Rectangle r = mapper.modelToView(textArea, getDot()); // "Correct" the value of rect.width (takes into // account caret being at EOL (and thus rect.width==1), // etc. // We do this even for LINE_STYLE because // if they change from that caret to block/underline, // the first time they do so width==1, so it will take // one caret flash to paint correctly (wider). If we // do this every time, then it's painted correctly the // first blink. validateWidth(r); // Need to subtract 2 from height, otherwise // the caret will expand too far vertically. r.height -= 2; switch (style) { // Draw a big rectangle, and xor the foreground color. case BLOCK_STYLE: g.setXORMode(Color.WHITE); // fills x==r.x to x==(r.x+(r.width)-1), inclusive. g.fillRect(r.x,r.y, r.width,r.height); break; // Draw a rectangular border. case BLOCK_BORDER_STYLE: // fills x==r.x to x==(r.x+(r.width-1)), inclusive. g.drawRect(r.x,r.y, r.width-1,r.height); break; // Draw an "underline" below the current position. case UNDERLINE_STYLE: g.setXORMode(Color.WHITE); int y = r.y + r.height; g.drawLine(r.x,y, r.x+r.width-1,y); break; // Draw a vertical line. default: case VERTICAL_LINE_STYLE: g.drawLine(r.x,r.y, r.x,r.y+r.height); break; // A thicker vertical line. case THICK_VERTICAL_LINE_STYLE: g.drawLine(r.x,r.y, r.x,r.y+r.height); r.x++; g.drawLine(r.x,r.y, r.x,r.y+r.height); break; } // End of switch (style). } catch (BadLocationException ble) { ble.printStackTrace(); } } // End of if (isVisible()). } /** * Deserializes a caret. This is overridden to read the caret's style. * * @param s The stream to read from. * @throws ClassNotFoundException * @throws IOException */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); setStyle(s.readInt()); seg = new Segment(); } /** * Selects word based on the MouseEvent */ private void selectWord(MouseEvent e) { if (selectedWordEvent != null && selectedWordEvent.getX() == e.getX() && selectedWordEvent.getY() == e.getY()) { // We've already the done selection for this. return; } Action a = null; RTextArea textArea = getTextArea(); ActionMap map = textArea.getActionMap(); if (map != null) { a = map.get(RTextAreaEditorKit.selectWordAction); } if (a == null) { if (selectWord == null) { selectWord = new RTextAreaEditorKit.SelectWordAction(); } a = selectWord; } a.actionPerformed(new ActionEvent(textArea, ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e.getModifiers())); selectedWordEvent = e; } /** * Sets whether this caret's selection should have rounded edges. * * @param rounded Whether it should have rounded edges. * @see #getRoundedSelectionEdges() */ public void setRoundedSelectionEdges(boolean rounded) { ((ChangeableHighlightPainter)getSelectionPainter()). setRoundedEdges(rounded); } /** * Overridden to always render the selection, even when the text component * loses focus. * * @param visible Whether the selection should be visible. This parameter * is ignored. */ public void setSelectionVisible(boolean visible) { super.setSelectionVisible(true); } /** * Sets the style used when painting the caret. * * @param style The style to use. If this isn't one of * VERTICAL_LINE_STYLE, UNDERLINE_STYLE, * or BLOCK_STYLE, then * VERTICAL_LINE_STYLE is used. * @see #getStyle() */ public void setStyle(int style) { if (styleMAX_STYLE) style = VERTICAL_LINE_STYLE; this.style = style; repaint(); } /** * Helper function used by the block and underline carets to ensure the * width of the painted caret is valid. This is done for the following * reasons: * *
    *
  • The View classes in the javax.swing.text package * always return a width of "1" when modelToView is * called. We'll be needing the actual width.
  • *
  • Even in smart views, such as RSyntaxTextArea's * SyntaxView and WrappedSyntaxView that * return the width of the current character, if the caret is at the * end of a line for example, the width returned from * modelToView will be 0 (as the width of unprintable * characters such as '\n' is calculated as 0). In this case, we'll * use a default width value.
  • *
* * @param rect The rectangle returned by the current * View's modelToView * method for the caret position. */ private void validateWidth(Rectangle rect) { // If the width value > 1, we assume the View is // a "smart" view that returned the proper width. // So only worry about this stuff if width <= 1. if (rect!=null && rect.width<=1) { // The width is either 1 (most likely, we're using a "dumb" view // like those in javax.swing.text) or 0 (most likely, we're using // a "smart" view like org.fife.ui.rsyntaxtextarea.SyntaxView, // we're at the end of a line, and the width of '\n' is being // computed as 0). try { // Try to get a width for the character at the caret // position. We use the text area's font instead of g's // because g's may vary in an RSyntaxTextArea. RTextArea textArea = getTextArea(); textArea.getDocument().getText(getDot(),1, seg); Font font = textArea.getFont(); FontMetrics fm = textArea.getFontMetrics(font); rect.width = fm.charWidth(seg.array[seg.offset]); // This width being returned 0 likely means that it is an // unprintable character (which is almost 100% to be a // newline char, i.e., we're at the end of a line). So, // just use the width of a space. if (rect.width==0) { rect.width = fm.charWidth(' '); } } catch (BadLocationException ble) { // This shouldn't ever happen. ble.printStackTrace(); rect.width = 8; } } // End of if (rect!=null && rect.width<=1). } /** * Serializes this caret. This is overridden to write the style of the * caret. * * @param s The stream to write to. * @throws IOException If an IO error occurs. */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeInt(getStyle()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy