/* * 04/07/2005 * * RTextAreaBase.java - The base class for an RTextArea. * Copyright (C) 2005 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.AWTEvent; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import javax.swing.JTextArea; import javax.swing.event.CaretEvent; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.TextUI; import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; /** * This is the base class for <code>RTextArea</code>; basically it's just an extension of * <code>javax.swing.JTextArea</code> adding a bunch of properties. * <p> * * This class is only supposed to be overridden by <code>RTextArea</code>. * * @author Robert Futrell * @version 0.8 */ abstract class RTextAreaBase extends JTextArea { public static final String BACKGROUND_IMAGE_PROPERTY = "background.image"; public static final String CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY = "RTA.currentLineHighlightColor"; public static final String CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY = "RTA.currentLineHighlightFade"; public static final String HIGHLIGHT_CURRENT_LINE_PROPERTY = "RTA.currentLineHighlight"; public static final String ROUNDED_SELECTION_PROPERTY = "RTA.roundedSelection"; private boolean tabsEmulatedWithSpaces; // If true, tabs will be expanded to spaces. private boolean highlightCurrentLine; // If true, the current line is highlighted. private Color currentLineColor; // The color used to highlight the current line. private boolean marginLineEnabled; // If true, paint a "margin line." private Color marginLineColor; // The color used to paint the margin line. private int marginLineX; // The x-location of the margin line. private int marginSizeInChars; // How many 'm' widths the margin line is over. private boolean fadeCurrentLineHighlight; // "Fade effect" for current line highlight. private boolean roundedSelectionEdges; private int previousCaretY; int currentCaretY; // Used to know when to rehighlight current line. private BackgroundPainterStrategy backgroundPainter; // Paints the background. private RTAMouseListener mouseListener; private static final Color DEFAULT_CARET_COLOR = new ColorUIResource(255, 51, 51); private static final Color DEFAULT_CURRENT_LINE_HIGHLIGHT_COLOR = new Color(255, 255, 170); private static final Color DEFAULT_MARGIN_LINE_COLOR = new Color(255, 224, 224); private static final int DEFAULT_TAB_SIZE = 4; private static final int DEFAULT_MARGIN_LINE_POSITION = 80; /** * Constructor. */ public RTextAreaBase() { init(); } /** * Constructor. * * @param doc * The document for the editor. */ public RTextAreaBase(AbstractDocument doc) { super(doc); init(); } /** * Constructor. * * @param text * The initial text to display. */ public RTextAreaBase(String text) { super(text); init(); } /** * Constructor. * * @param rows * The number of rows to display. * @param cols * The number of columns to display. * @throws IllegalArgumentException * If either <code>rows</code> or <code>cols</code> is negative. */ public RTextAreaBase(int rows, int cols) { super(rows, cols); init(); } /** * Constructor. * * @param text * The initial text to display. * @param rows * The number of rows to display. * @param cols * The number of columns to display. * @throws IllegalArgumentException * If either <code>rows</code> or <code>cols</code> is negative. */ public RTextAreaBase(String text, int rows, int cols) { super(text, rows, cols); init(); } /** * Constructor. * * @param doc * The document for the editor. * @param text * The initial text to display. * @param rows * The number of rows to display. * @param cols * The number of columns to display. * @throws IllegalArgumentException * If either <code>rows</code> or <code>cols</code> is negative. */ public RTextAreaBase(AbstractDocument doc, String text, int rows, int cols) { super(doc, text, rows, cols); init(); } /** * Adds listeners that listen for changes to the current line, so we can update our "current line highlight." This * is needed only because of an apparent difference between the JRE 1.4.2 and 1.5.0 (needed on 1.4.2, not needed on * 1.5.0). */ protected void addCurrentLineHighlightListeners() { boolean add = true; MouseMotionListener[] mouseMotionListeners = getMouseMotionListeners(); for (int i = 0; i < mouseMotionListeners.length; i++) { if (mouseMotionListeners[i] == mouseListener) { add = false; break; } } if (add == true) { // System.err.println("Adding mouse motion listener!"); addMouseMotionListener(mouseListener); } MouseListener[] mouseListeners = getMouseListeners(); for (int i = 0; i < mouseListeners.length; i++) { if (mouseListeners[i] == mouseListener) { add = false; break; } } if (add == true) { // System.err.println("Adding mouse listener!"); addMouseListener(mouseListener); } } /* * TODO: Figure out why RTextArea doesn't work with RTL orientation! */ // public void applyComponentOrientation(ComponentOrientation orientation) { // super.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); // } /** * Converts all instances of a number of spaces equal to a tab size into a tab in this text area. * * @see #convertTabsToSpaces * @see #getTabsEmulated * @see #setTabsEmulated */ public void convertSpacesToTabs() { // FIXME: This is inefficient and will yield an OutOfMemoryError if // done on a large document. We should scan 1 line at a time and // replace; it'll be slower but safer. int caretPosition = getCaretPosition(); int tabSize = getTabSize(); String tabInSpaces = ""; for (int i = 0; i < tabSize; i++) tabInSpaces += " "; String text = getText(); setText(text.replaceAll(tabInSpaces, "\t")); int newDocumentLength = getDocument().getLength(); // Place the caret back in its proper position. if (caretPosition < newDocumentLength) setCaretPosition(caretPosition); else setCaretPosition(newDocumentLength - 1); } /** * Converts all instances of a tab into a number of spaces equivalent to a tab in this text area. * * @see #convertSpacesToTabs * @see #getTabsEmulated * @see #setTabsEmulated */ public void convertTabsToSpaces() { // FIXME: This is inefficient and will yield an OutOfMemoryError if // done on a large document. We should scan 1 line at a time and // replace; it'll be slower but safer. int caretPosition = getCaretPosition(); int tabSize = getTabSize(); StringBuffer tabInSpaces = new StringBuffer(); for (int i = 0; i < tabSize; i++) tabInSpaces.append(' '); String text = getText(); setText(text.replaceAll("\t", tabInSpaces.toString())); // Put caret back at same place in document. setCaretPosition(caretPosition); } /** * Returns the caret event/mouse listener for <code>RTextArea</code>s. * * @return The caret event/mouse listener. */ protected abstract RTAMouseListener createMouseListener(); /** * Returns the a real UI to install on this text component. Subclasses can override this method to return an * extended version of <code>RTextAreaUI</code>. * * @return The UI. */ protected abstract RTextAreaUI createRTextAreaUI(); /** * Forces the current line highlight to be repainted. This hack is necessary for those situations when the view * (appearance) changes but the caret's location hasn't (and thus the current line highlight coordinates won't get * changed). Examples of this are when word wrap is toggled and when syntax styles are changed in an * <code>RSyntaxTextArea</code>. */ protected void forceCurrentLineHighlightRepaint() { // Check isShowing() to prevent BadLocationException // in constructor if linewrap is set to true. if (isShowing()) { // Changing previousCaretY makes us sure to get a repaint. previousCaretY = -1; // Trick it into checking for the need to repaint by firing // a false caret event. fireCaretUpdate(mouseListener); } } /** * Returns the <code>java.awt.Color</code> used as the background color for this text area. If a * <code>java.awt.Image</code> image is currently being used instead, <code>null</code> is returned. * * @return The current background color, or <code>null</code> if an image is currently the background. */ public final Color getBackground() { Object bg = getBackgroundObject(); return (bg instanceof Color) ? (Color) bg : null; } /** * Returns the image currently used for the background. If the current background is currently a * <code>java.awt.Color</code> and not a <code>java.awt.Image</code>, then <code>null</code> is returned. * * @return A <code>java.awt.Image</code> used for the background, or <code>null</code> if the background is not an * image. * @see #setBackgroundImage */ public final Image getBackgroundImage() { Object bg = getBackgroundObject(); return (bg instanceof Image) ? (Image) bg : null; } /** * Returns the <code>Object</code> representing the background for all documents in this tabbed pane; either a * <code>java.awt.Color</code> or a <code>java.lang.Image</code> containing the image used as the background for * this text area. * * @return The <code>Object</code> used for the background. * @see #setBackgroundObject(Object newBackground) */ public final Object getBackgroundObject() { if (backgroundPainter == null) return null; return (backgroundPainter instanceof ImageBackgroundPainterStrategy) ? (Object) ((ImageBackgroundPainterStrategy) backgroundPainter). getMasterImage() : (Object) ((ColorBackgroundPainterStrategy) backgroundPainter). getColor(); } /** * Gets the line number that the caret is on. * * @return The zero-based line number that the caret is on. */ public final int getCaretLineNumber() { try { return getLineOfOffset(getCaretPosition()); } catch (BadLocationException ble) { return 0; // Never happens } } /** * Gets the position from the beginning of the current line that the caret is on. * * @return The zero-based position from the beginning of the current line that the caret is on. */ public final int getCaretOffsetFromLineStart() { try { int pos = getCaretPosition(); return pos - getLineStartOffset(getLineOfOffset(pos)); } catch (BadLocationException ble) { return 0; // Never happens } } /** * Returns the color being used to highlight the current line. Note that if highlighting the current line is turned * off, you will not be seeing this highlight. * * @return The color being used to highlight the current line. * @see #getHighlightCurrentLine() * @see #setHighlightCurrentLine(boolean) * @see #setCurrentLineHighlightColor */ public Color getCurrentLineHighlightColor() { return currentLineColor; } /** * Returns the default caret color. * * @return The default caret color. */ public static final Color getDefaultCaretColor() { return DEFAULT_CARET_COLOR; } /** * Returns the "default" color for highlighting the current line. Note that this color was chosen only because it * looks nice (to me) against a white background. * * @return The default color for highlighting the current line. */ public static final Color getDefaultCurrentLineHighlightColor() { return DEFAULT_CURRENT_LINE_HIGHLIGHT_COLOR; } /** * Returns the default font for text areas. * * @return The default font. */ public static final Font getDefaultFont() { Font font = null; if (isOSX()) { // Snow Leopard (1.6) uses Menlo as default monospaced font, // pre-Snow Leopard used Monaco. font = new Font("Menlo", Font.PLAIN, 12); if (!"Menlo".equals(font.getFamily())) { font = new Font("Monaco", Font.PLAIN, 12); if (!"Monaco".equals(font.getFamily())) { // Shouldn't happen font = new Font("Monospaced", Font.PLAIN, 13); } } } else { font = new Font("Monospaced", Font.PLAIN, 13); } // System.out.println(font.getFamily() + ", " + font.getName()); return font; } /** * Returns the default foreground color for text in this text area. * * @return The default foreground color. */ public static final Color getDefaultForeground() { return Color.BLACK; } /** * Returns the default color for the margin line. * * @return The default margin line color. * @see #getMarginLineColor() * @see #setMarginLineColor(Color) */ public static final Color getDefaultMarginLineColor() { return DEFAULT_MARGIN_LINE_COLOR; } /** * Returns the default margin line position. * * @return The default margin line position. * @see #getMarginLinePosition * @see #setMarginLinePosition */ public static final int getDefaultMarginLinePosition() { return DEFAULT_MARGIN_LINE_POSITION; } /** * Returns the default tab size, in spaces. * * @return The default tab size. */ public static final int getDefaultTabSize() { return DEFAULT_TAB_SIZE; } /** * Returns whether the current line highlight is faded. * * @return Whether the current line highlight is faded. * @see #setFadeCurrentLineHighlight */ public boolean getFadeCurrentLineHighlight() { return fadeCurrentLineHighlight; } /** * Returns whether or not the current line is highlighted. * * @return Whether or the current line is highlighted. * @see #setHighlightCurrentLine(boolean) * @see #getCurrentLineHighlightColor * @see #setCurrentLineHighlightColor */ public boolean getHighlightCurrentLine() { return highlightCurrentLine; } /** * Returns the offset of the last character of the line that the caret is on. * * @return The last offset of the line that the caret is currently on. */ public final int getLineEndOffsetOfCurrentLine() { try { return getLineEndOffset(getCaretLineNumber()); } catch (BadLocationException ble) { return 0; // Never happens } } /** * Returns the height of a line of text in this text area. * * @return The height of a line of text. */ public int getLineHeight() { return getRowHeight(); } /** * Returns the offset of the first character of the line that the caret is on. * * @return The first offset of the line that the caret is currently on. */ public final int getLineStartOffsetOfCurrentLine() { try { return getLineStartOffset(getCaretLineNumber()); } catch (BadLocationException ble) { return 0; // Never happens } } /** * Returns the color used to paint the margin line. * * @return The margin line color. * @see #setMarginLineColor(Color) */ public Color getMarginLineColor() { return marginLineColor; } /** * Returns the margin line position (in pixels) from the left-hand side of the text area. * * @return The margin line position. * @see #getDefaultMarginLinePosition * @see #setMarginLinePosition */ public int getMarginLinePixelLocation() { return marginLineX; } /** * Returns the margin line position (which is the number of 'm' widths in the current font the margin line is over). * * @return The margin line position. * @see #getDefaultMarginLinePosition * @see #setMarginLinePosition */ public int getMarginLinePosition() { return marginSizeInChars; } /** * Returns whether selection edges are rounded in this text area. * * @return Whether selection edges are rounded. * @see #setRoundedSelectionEdges(boolean) */ public boolean getRoundedSelectionEdges() { return roundedSelectionEdges; } /** * Returns whether or not tabs are emulated with spaces (i.e., "soft" tabs). * * @return <code>true</code> if tabs are emulated with spaces; <code>false</code> if they aren't. * @see #setTabsEmulated */ public boolean getTabsEmulated() { return tabsEmulatedWithSpaces; } /** * Initializes this text area. */ private void init() { // Sets the UI. Note that setUI() is overridden in RTextArea to only // update the popup menu; this method must be called to set the real // UI. This is done because the look and feel of an RTextArea is // independent of the installed Java look and feels. setRTextAreaUI(createRTextAreaUI()); // So we get notified when the component is resized. enableEvents(AWTEvent.COMPONENT_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); // Defaults for various properties. setHighlightCurrentLine(true); setCurrentLineHighlightColor(getDefaultCurrentLineHighlightColor()); setMarginLineEnabled(false); setMarginLineColor(getDefaultMarginLineColor()); setMarginLinePosition(getDefaultMarginLinePosition()); setBackgroundObject(Color.WHITE); setWrapStyleWord(true);// We only support wrapping at word boundaries. setTabSize(5); setForeground(Color.BLACK); setTabsEmulated(false); // Stuff needed by the caret listener below. previousCaretY = currentCaretY = getInsets().top; // Stuff to highlight the current line. mouseListener = createMouseListener(); // Also acts as a focus listener so we can update our shared actions // (cut, copy, etc. on the popup menu). addFocusListener(mouseListener); addCurrentLineHighlightListeners(); } /** * Returns whether or not the margin line is being painted. * * @return Whether or not the margin line is being painted. * @see #setMarginLineEnabled */ public boolean isMarginLineEnabled() { return marginLineEnabled; } /** * Returns whether the OS we're running on is OS X. * * @return Whether the OS we're running on is OS X. */ public static boolean isOSX() { // Recommended at: // http://developer.apple.com/mac/library/technotes/tn2002/tn2110.html String osName = System.getProperty("os.name").toLowerCase(); return osName.startsWith("mac os x"); } /** * Paints the text area. * * @param g * The graphics context with which to paint. */ protected void paintComponent(Graphics g) { // long startTime = System.currentTimeMillis(); backgroundPainter.paint(g, getVisibleRect()); // Paint the main part of the text area. TextUI ui = getUI(); if (ui != null) { // Not allowed to modify g, so make a copy. Graphics scratchGraphics = g.create(); try { ui.update(scratchGraphics, this); } finally { scratchGraphics.dispose(); } } // long endTime = System.currentTimeMillis(); // System.err.println(endTime-startTime); } /** * Updates the current line highlight location. */ protected void possiblyUpdateCurrentLineHighlightLocation() { int width = getWidth(); int lineHeight = getLineHeight(); int dot = getCaretPosition(); // If we're wrapping lines we need to check the actual y-coordinate // of the caret, not just the line number, since a single logical // line can span multiple physical lines. if (getLineWrap()) { try { Rectangle temp = modelToView(dot); if (temp != null) { currentCaretY = temp.y; } } catch (BadLocationException ble) { ble.printStackTrace(); // Should never happen. } } // No line wrap - we can simply check the line number (quicker). else { Document doc = getDocument(); if (doc != null) { Element map = doc.getDefaultRootElement(); int caretLine = map.getElementIndex(dot); Rectangle alloc = ((RTextAreaUI) getUI()). getVisibleEditorRect(); if (alloc != null) currentCaretY = alloc.y + caretLine * lineHeight; } } // Repaint current line (to fill in entire highlight), and old line // (to erase entire old highlight) if necessary. Always repaint // current line in case selection is added or removed. repaint(0, currentCaretY, width, lineHeight); if (previousCaretY != currentCaretY) { repaint(0, previousCaretY, width, lineHeight); } previousCaretY = currentCaretY; } /** * Overridden so we can tell when the text area is resized and update the current-line highlight, if necessary * (i.e., if it is enabled and if lineWrap is enabled. * * @param e * The component event about to be sent to all registered <code>ComponentListener</code>s. */ protected void processComponentEvent(ComponentEvent e) { // In line wrap mode, resizing the text area means that the caret's // "line" could change - not to a different logical line, but a // different physical one. So, here we force a repaint of the current // line's highlight if necessary. if (e.getID() == ComponentEvent.COMPONENT_RESIZED && getLineWrap() == true && getHighlightCurrentLine()) { previousCaretY = -1; // So we are sure to repaint. fireCaretUpdate(mouseListener); } super.processComponentEvent(e); } /** * Sets the background color of this text editor. Note that this is equivalent to calling * <code>setBackgroundObject(bg)</code>. * * NOTE: the opaque property is set to <code>true</code> when the background is set to a color (by this method). * When an image is used for the background, opaque is set to false. This is because we perform better when * setOpaque is true, but if we use an image for the background when opaque is true, we get on-screen garbage when * the user scrolls via the arrow keys. Thus we need setOpaque to be false in that case. * <p> * You never have to change the opaque property yourself; it is always done for you. * * @param bg * The color to use as the background color. */ public void setBackground(Color bg) { Object oldBG = getBackgroundObject(); if (oldBG instanceof Color) { // Just change color of strategy. ((ColorBackgroundPainterStrategy) backgroundPainter). setColor(bg); } else { // Was an image painter... backgroundPainter = new ColorBackgroundPainterStrategy(bg); } setOpaque(true); firePropertyChange("background", oldBG, bg); repaint(); } /** * Sets this image as the background image. This method fires a property change event of type * {@link #BACKGROUND_IMAGE_PROPERTY}. * <p> * * NOTE: the opaque property is set to <code>true</code> when the background is set to a color. When an image is * used for the background (by this method), opaque is set to false. This is because we perform better when * setOpaque is true, but if we use an image for the background when opaque is true, we get on-screen garbage when * the user scrolls via the arrow keys. Thus we need setOpaque to be false in that case. * <p> * You never have to change the opaque property yourself; it is always done for you. * * @param image * The image to use as this text area's background. * @see #getBackgroundImage */ public void setBackgroundImage(Image image) { Object oldBG = getBackgroundObject(); if (oldBG instanceof Image) { // Just change image being displayed. ((BufferedImageBackgroundPainterStrategy) backgroundPainter). setImage(image); } else { // Was a color strategy... BufferedImageBackgroundPainterStrategy strategy = new BufferedImageBackgroundPainterStrategy(this); strategy.setImage(image); backgroundPainter = strategy; } setOpaque(false); firePropertyChange(BACKGROUND_IMAGE_PROPERTY, oldBG, image); repaint(); } /** * Makes the background into this <code>Object</code>. * * @param newBackground * The <code>java.awt.Color</code> or <code>java.awt.Image</code> object. If <code>newBackground</code> * is not either of these, the background is set to plain white. */ public void setBackgroundObject(Object newBackground) { if (newBackground instanceof Color) { setBackground((Color) newBackground); } else if (newBackground instanceof Image) { setBackgroundImage((Image) newBackground); } else { setBackground(Color.WHITE); } } /* * TODO: Figure out why RTextArea doesn't work with RTL (e.g. Arabic) and fix it! */ // public void setComponentOrientation(ComponentOrientation o) { // if (!o.isLeftToRight()) { // o = ComponentOrientation.LEFT_TO_RIGHT; // } // super.setComponentOrientation(o); // } /** * Sets the color to use to highlight the current line. Note that if highlighting the current line is turned off, * you will not be able to see this highlight. This method fires a property change of type * {@link #CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY}. * * @param color * The color to use to highlight the current line. * @throws NullPointerException * if <code>color</code> is <code>null</code>. * @see #getHighlightCurrentLine() * @see #setHighlightCurrentLine(boolean) * @see #getCurrentLineHighlightColor */ public void setCurrentLineHighlightColor(Color color) { if (color == null) throw new NullPointerException(); if (!color.equals(currentLineColor)) { Color old = currentLineColor; currentLineColor = color; firePropertyChange(CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY, old, color); } } /** * Sets whether the current line highlight should have a "fade" effect. This method fires a property change event of * type <code>CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY</code>. * * @param fade * Whether the fade effect should be enabled. * @see #getFadeCurrentLineHighlight */ public void setFadeCurrentLineHighlight(boolean fade) { if (fade != fadeCurrentLineHighlight) { fadeCurrentLineHighlight = fade; if (getHighlightCurrentLine()) forceCurrentLineHighlightRepaint(); firePropertyChange(CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY, !fade, fade); } } /** * Sets the font for this text area. This is overridden only so that we can update the size of the * "current line highlight" and the location of the "margin line," if necessary. * * @param font * The font to use for this text component. */ public void setFont(Font font) { super.setFont(font); updateMarginLineX(); if (highlightCurrentLine) possiblyUpdateCurrentLineHighlightLocation(); } /** * Sets whether or not the current line is highlighted. This method fires a property change of type * {@link #HIGHLIGHT_CURRENT_LINE_PROPERTY}. * * @param highlight * Whether or not to highlight the current line. * @see #getHighlightCurrentLine() * @see #getCurrentLineHighlightColor * @see #setCurrentLineHighlightColor */ public void setHighlightCurrentLine(boolean highlight) { if (highlight != highlightCurrentLine) { highlightCurrentLine = highlight; firePropertyChange(HIGHLIGHT_CURRENT_LINE_PROPERTY, !highlight, highlight); repaint(); // Repaint entire width of line. } } /** * Sets whether or not word wrap is enabled. This is overridden so that the "current line highlight" gets updated if * it needs to be. * * @param wrap * Whether or not word wrap should be enabled. */ public void setLineWrap(boolean wrap) { super.setLineWrap(wrap); forceCurrentLineHighlightRepaint(); } /** * Sets the color used to paint the margin line. * * @param color * The new margin line color. * @see #getDefaultMarginLineColor() * @see #getMarginLineColor() */ public void setMarginLineColor(Color color) { marginLineColor = color; if (marginLineEnabled) { Rectangle visibleRect = getVisibleRect(); repaint(marginLineX, visibleRect.y, 1, visibleRect.height); } } /** * Enables or disables the margin line. * * @param enabled * Whether or not the margin line should be enabled. * @see #isMarginLineEnabled */ public void setMarginLineEnabled(boolean enabled) { if (enabled != marginLineEnabled) { marginLineEnabled = enabled; if (marginLineEnabled) { Rectangle visibleRect = getVisibleRect(); repaint(marginLineX, visibleRect.y, 1, visibleRect.height); } } } /** * Sets the number of 'm' widths the margin line is over. * * @param size * The margin size. #see #getDefaultMarginLinePosition * @see #getMarginLinePosition */ public void setMarginLinePosition(int size) { marginSizeInChars = size; if (marginLineEnabled) { Rectangle visibleRect = getVisibleRect(); repaint(marginLineX, visibleRect.y, 1, visibleRect.height); updateMarginLineX(); repaint(marginLineX, visibleRect.y, 1, visibleRect.height); } } /** * Sets whether the edges of selections are rounded in this text area. This method fires a property change of type * {@link #ROUNDED_SELECTION_PROPERTY}. * * @param rounded * Whether selection edges should be rounded. * @see #getRoundedSelectionEdges() */ public void setRoundedSelectionEdges(boolean rounded) { if (roundedSelectionEdges != rounded) { roundedSelectionEdges = rounded; ConfigurableCaret cc = (ConfigurableCaret) getCaret(); cc.setRoundedSelectionEdges(rounded); if (cc.getDot() != cc.getMark()) { // ie, if there is a selection repaint(); } firePropertyChange(ROUNDED_SELECTION_PROPERTY, !rounded, rounded); } } /** * Sets the UI for this <code>RTextArea</code>. Note that, for instances of <code>RTextArea</code>, * <code>setUI</code> only updates the popup menu; this is because <code>RTextArea</code>s' look and feels are * independent of the Java Look and Feel. This method is here so subclasses can set a UI (subclass of * <code>RTextAreaUI</code>) if they have to. * * @param ui * The new UI. * @see #setUI */ protected void setRTextAreaUI(RTextAreaUI ui) { super.setUI(ui); // Workaround as setUI makes the text area opaque, even if we don't // want it to be. setOpaque(getBackgroundObject() instanceof Color); } /** * Changes whether or not tabs should be emulated with spaces (i.e., soft tabs). Note that this affects all tabs * inserted AFTER this call, not tabs already in the document. For that, see {@link #convertTabsToSpaces} and * {@link #convertSpacesToTabs}. * * @param areEmulated * Whether or not tabs should be emulated with spaces. * @see #convertSpacesToTabs * @see #convertTabsToSpaces * @see #getTabsEmulated */ public void setTabsEmulated(boolean areEmulated) { tabsEmulatedWithSpaces = areEmulated; } /** * Workaround, since in JDK1.4 it appears that <code>setTabSize()</code> doesn't work for a <code>JTextArea</code> * unless you use the constructor specifying the number of rows and columns... * <p> * Sets the number of characters to expand tabs to. This will be multiplied by the maximum advance for variable * width fonts. A PropertyChange event ("tabSize") is fired when the tab size changes. * * @param size * Number of characters to expand to. */ public void setTabSize(int size) { super.setTabSize(size); boolean b = getLineWrap(); setLineWrap(!b); setLineWrap(b); } /** * This is here so subclasses such as <code>RSyntaxTextArea</code> that have multiple fonts can define exactly what * it means, for example, for the margin line to be "80 characters" over. */ protected void updateMarginLineX() { marginLineX = getFontMetrics(getFont()).charWidth('m') * marginSizeInChars; } /** * Returns the y-coordinate of the line containing an offset. * <p> * * The default implementation is equivalent to: * * <pre> * int line = textArea.getLineOfOffset(offs); * int startOffs = textArea.getLineStartOffset(line); * return modelToView(startOffs).y</code> * </pre> * * Subclasses that can calculate this value more quickly than traditional {@link #modelToView(int)} calls should * override this method to do so. This method may be used when the entire bounding box isn't needed, to speed up * rendering. * * @param offs * The offset info the document. * @return The y-coordinate of the top of the offset, or <code>-1</code> if this text area doesn't yet have a * positive size. * @throws BadLocationException * If <code>offs</code> isn't a valid offset into the document. */ public int yForLineContaining(int offs) throws BadLocationException { return ((RTextAreaUI) getUI()).yForLineContaining(offs); } protected class RTAMouseListener extends CaretEvent implements MouseListener, MouseMotionListener, FocusListener { RTAMouseListener(RTextAreaBase textArea) { super(textArea); } public void focusGained(FocusEvent e) { } public void focusLost(FocusEvent e) { } public void mouseDragged(MouseEvent e) { } public void mouseMoved(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public int getDot() { return dot; } public int getMark() { return mark; } protected int dot; protected int mark; } }