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

org.fife.ui.rtextarea.RTextArea 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
/*
 * 11/14/2003
 *
 * RTextArea.java - An extension of JTextArea that adds many features.
 * Copyright (C) 2003 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.Color;
import java.awt.ComponentOrientation;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.CaretEvent;
import javax.swing.plaf.TextUI;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Highlighter;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;

import org.fife.print.RPrintUtilities;
import org.fife.ui.rtextarea.Macro.MacroRecord;


/**
 * An extension of JTextArea that adds the following features:
 * 
    *
  • Insert/Overwrite modes (can be toggled via the Insert key) *
  • A right-click popup menu with standard editing options *
  • Macro support *
  • "Mark all" functionality. *
  • A way to change the background to an image (gif/png/jpg) *
  • Highlight the current line (can be toggled) *
  • An easy way to print its text (implements Printable) *
  • Hard/soft (emulated with spaces) tabs *
  • Fixes a bug with setTabSize *
  • Other handy new methods *
* NOTE: If the background for an RTextArea is set to a color, * its opaque property is set to true for performance reasons. If * the background is set to an image, then the opaque property is set to * false. This slows things down a little, but if it didn't happen * then we would see garbage on-screen when the user scrolled through a document * using the arrow keys (not the page-up/down keys though). You should never * have to set the opaque property yourself; it is always done for you. * * @author Robert Futrell * @version 1.0 */ public class RTextArea extends RTextAreaBase implements Printable, Serializable { /** * Constant representing insert mode. * * @see #setCaretStyle(int, int) */ public static final int INSERT_MODE = 0; /** * Constant representing overwrite mode. * * @see #setCaretStyle(int, int) */ public static final int OVERWRITE_MODE = 1; /** * The property fired when the "mark all" color changes. */ public static final String MARK_ALL_COLOR_PROPERTY = "RTA.markAllColor"; /* * Constants for all actions. */ private static final int MIN_ACTION_CONSTANT = 0; public static final int COPY_ACTION = 0; public static final int CUT_ACTION = 1; public static final int DELETE_ACTION = 2; public static final int PASTE_ACTION = 3; public static final int REDO_ACTION = 4; public static final int SELECT_ALL_ACTION = 5; public static final int UNDO_ACTION = 6; private static final int MAX_ACTION_CONSTANT = 6; private static final Color DEFAULT_MARK_ALL_COLOR = Color.ORANGE; /** * The current text mode ({@link #INSERT_MODE} or {@link #OVERWRITE_MODE}). */ private int textMode; // All macros are shared across all RTextAreas. private static boolean recordingMacro; // Whether we're recording a macro. private static Macro currentMacro; /** * This text area's popup menu. */ private JPopupMenu popupMenu; /** * Whether the popup menu has been created. */ private boolean popupMenuCreated; /** * Can return tool tips for this text area. Subclasses can install a * supplier as a means of adding custom tool tips without subclassing * RTextArea. {@link #getToolTipText()} checks this supplier * before calling the super class's version. */ private ToolTipSupplier toolTipSupplier; private static RecordableTextAction cutAction; private static RecordableTextAction copyAction; private static RecordableTextAction pasteAction; private static RecordableTextAction deleteAction; private static RecordableTextAction undoAction; private static RecordableTextAction redoAction; private static RecordableTextAction selectAllAction; private static IconGroup iconGroup; // Info on icons for actions. private transient RUndoManager undoManager; private transient LineHighlightManager lineHighlightManager; private ArrayList markAllHighlights; // Highlights from "mark all". private String markedWord; // Expression marked in "mark all." private ChangeableHighlightPainter markAllHighlightPainter; private int[] carets; // Index 0=>insert caret, 1=>overwrite. private static final String MSG = "org.fife.ui.rtextarea.RTextArea"; /** * Constructor. */ public RTextArea() { init(INSERT_MODE); } /** * Constructor. * * @param doc The document for the editor. */ public RTextArea(AbstractDocument doc) { super(doc); init(INSERT_MODE); } /** * Constructor. * * @param text The initial text to display. */ public RTextArea(String text) { super(text); init(INSERT_MODE); } /** * Constructor. * * @param rows The number of rows to display. * @param cols The number of columns to display. * @throws IllegalArgumentException If either rows or * cols is negative. */ public RTextArea(int rows, int cols) { super(rows, cols); init(INSERT_MODE); } /** * 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 rows or * cols is negative. */ public RTextArea(String text, int rows, int cols) { super(text, rows, cols); init(INSERT_MODE); } /** * 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 rows or * cols is negative. */ public RTextArea(AbstractDocument doc, String text, int rows, int cols) { super(doc, text, rows, cols); init(INSERT_MODE); } /** * Creates a new RTextArea. * * @param textMode Either INSERT_MODE or * OVERWRITE_MODE. */ public RTextArea(int textMode) { init(textMode); } /** * Adds an action event to the current macro. This shouldn't be called * directly, as it is called by the actions themselves. * * @param id The ID of the recordable text action. * @param actionCommand The "command" of the action event passed to it. */ static synchronized void addToCurrentMacro(String id, String actionCommand) { currentMacro.addMacroRecord(new Macro.MacroRecord(id, actionCommand)); } /** * Adds a line highlight. * * @param line The line to highlight. This is zero-based. * @param color The color to highlight the line with. * @throws BadLocationException If line is an invalid line * number. * @see #removeLineHighlight(Object) * @see #removeAllLineHighlights() */ public Object addLineHighlight(int line, Color color) throws BadLocationException { if (lineHighlightManager==null) { lineHighlightManager = new LineHighlightManager(this); } return lineHighlightManager.addLineHighlight(line, color); } /** * Begins an "atomic edit." All text editing operations between this call * and the next call to endAtomicEdit() will be treated as a * single operation by the undo manager.

* * Using this method should be done with great care. You should probably * wrap the call to endAtomicEdit() in a finally block: * *

	 * textArea.beginAtomicEdit();
	 * try {
	 *    // Do editing
	 * } finally {
	 *    textArea.endAtomicEdit();
	 * }
	 * 
* * @see #endAtomicEdit() */ public void beginAtomicEdit() { undoManager.beginInternalAtomicEdit(); } /** * Begins recording a macro. After this method is called, all input/caret * events, etc. are recorded until endMacroRecording is * called. If this method is called but the text component is already * recording a macro, nothing happens (but the macro keeps recording). * * @see #isRecordingMacro() * @see #endRecordingMacro() */ public static synchronized void beginRecordingMacro() { if (isRecordingMacro()) { //System.err.println("Macro already being recorded!"); return; } //JOptionPane.showMessageDialog(this, "Now recording a macro"); if (currentMacro!=null) currentMacro = null; // May help gc? currentMacro = new Macro(); recordingMacro = true; } /** * Clears any "mark all" highlights, if any. * * @see #markAll * @see #getMarkAllHighlightColor * @see #setMarkAllHighlightColor */ public void clearMarkAllHighlights() { Highlighter h = getHighlighter(); if (h!=null && markAllHighlights!=null) { int count = markAllHighlights.size(); for (int i=0; i * * The default implementation does nothing. * * @param popupMenu The popup menu. This will never be null. * @see #createPopupMenu() * @see #setPopupMenu(JPopupMenu) */ protected void configurePopupMenu(JPopupMenu popupMenu) { } /** * Returns the caret event/mouse listener for RTextAreas. * * @return The caret event/mouse listener. */ protected RTAMouseListener createMouseListener() { return new RTextAreaMutableCaretEvent(this); } /** * Creates the right-click popup menu. Subclasses can override this method * to replace or augment the popup menu returned. * * @return The popup menu. * @see #setPopupMenu(JPopupMenu) * @see #configurePopupMenu(JPopupMenu) */ protected JPopupMenu createPopupMenu() { JPopupMenu menu = new JPopupMenu(); JMenuItem menuItem; menuItem = new JMenuItem(undoAction); menuItem.setAccelerator(null); menuItem.setToolTipText(null); menu.add(menuItem); menuItem = new JMenuItem(redoAction); menuItem.setAccelerator(null); menuItem.setToolTipText(null); menu.add(menuItem); menu.addSeparator(); menuItem = new JMenuItem(cutAction); menuItem.setAccelerator(null); menuItem.setToolTipText(null); menu.add(menuItem); menuItem = new JMenuItem(copyAction); menuItem.setAccelerator(null); menuItem.setToolTipText(null); menu.add(menuItem); menuItem = new JMenuItem(pasteAction); menuItem.setAccelerator(null); menuItem.setToolTipText(null); menu.add(menuItem); menuItem = new JMenuItem(deleteAction); menuItem.setAccelerator(null); menuItem.setToolTipText(null); menu.add(menuItem); menu.addSeparator(); menuItem = new JMenuItem(selectAllAction); menuItem.setAccelerator(null); menuItem.setToolTipText(null); menu.add(menuItem); return menu; } /** * Creates the actions used in the popup menu and retrievable by * {@link #getAction(int)}. */ private static void createPopupMenuActions() { ResourceBundle bundle = ResourceBundle.getBundle(MSG); // Create actions for right-click popup menu. // 1.5.2004/pwy: Replaced the CTRL_MASK with the cross-platform version... int mod = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); String name = bundle.getString("CutName"); char mnemonic = bundle.getString("CutMnemonic").charAt(0); String desc = bundle.getString("CutDesc"); cutAction = new RTextAreaEditorKit.CutAction(name, null, desc, new Integer(mnemonic), KeyStroke.getKeyStroke(KeyEvent.VK_X, mod)); name = bundle.getString("CopyName"); mnemonic = bundle.getString("CopyMnemonic").charAt(0); desc = bundle.getString("CopyDesc"); copyAction = new RTextAreaEditorKit.CopyAction(name, null, desc, new Integer(mnemonic), KeyStroke.getKeyStroke(KeyEvent.VK_C, mod)); name = bundle.getString("PasteName"); mnemonic = bundle.getString("PasteMnemonic").charAt(0); desc = bundle.getString("PasteDesc"); pasteAction = new RTextAreaEditorKit.PasteAction(name, null, desc, new Integer(mnemonic), KeyStroke.getKeyStroke(KeyEvent.VK_V, mod)); name = bundle.getString("DeleteName"); mnemonic = bundle.getString("DeleteMnemonic").charAt(0); desc = bundle.getString("DeleteDesc"); deleteAction = new RTextAreaEditorKit.DeleteNextCharAction(name, null, desc, new Integer(mnemonic), KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); name = bundle.getString("CantUndoName"); mnemonic = bundle.getString("UndoMnemonic").charAt(0); desc = bundle.getString("UndoDesc"); undoAction = new RTextAreaEditorKit.UndoAction(name, null, desc, new Integer(mnemonic), KeyStroke.getKeyStroke(KeyEvent.VK_Z, mod)); name = bundle.getString("CantRedoName"); mnemonic = bundle.getString("RedoMnemonic").charAt(0); desc = bundle.getString("RedoDesc"); redoAction = new RTextAreaEditorKit.RedoAction(name, null, desc, new Integer(mnemonic), KeyStroke.getKeyStroke(KeyEvent.VK_Y, mod)); name = bundle.getString("SAName"); mnemonic = bundle.getString("SAMnemonic").charAt(0); desc = bundle.getString("SelectAllDesc"); selectAllAction = new RTextAreaEditorKit.SelectAllAction(name, null, desc, new Integer(mnemonic), KeyStroke.getKeyStroke(KeyEvent.VK_A, mod)); } /** * Returns the a real UI to install on this text area. * * @return The UI. */ protected RTextAreaUI createRTextAreaUI() { return new RTextAreaUI(this); } /** * Removes all undoable edits from this document's undo manager. This * method also makes the undo/redo actions disabled. */ /* * NOTE: For some reason, it appears I have to create an entirely new * undoManager for undo/redo to continue functioning * properly; if I don't, it only ever lets you do one undo. Not * too sure why this is... */ public void discardAllEdits() { undoManager.discardAllEdits(); getDocument().removeUndoableEditListener(undoManager); undoManager = new RUndoManager(this); getDocument().addUndoableEditListener(undoManager); undoManager.updateActions(); } /** * Completes an "atomic" edit. * * @see #beginAtomicEdit() */ public void endAtomicEdit() { undoManager.endInternalAtomicEdit(); } /** * Ends recording a macro. If this method is called but the text component * is not recording a macro, nothing happens. * * @see #isRecordingMacro() * @see #beginRecordingMacro() */ /* * FIXME: This should throw an exception if we're not recording a macro. */ public static synchronized void endRecordingMacro() { if (!isRecordingMacro()) { //System.err.println("Not recording a macro!"); return; } recordingMacro = false; } /** * Notifies all listeners that a caret change has occurred. * * @param e The caret event. */ protected void fireCaretUpdate(CaretEvent e) { // Decide whether we need to repaint the current line background. possiblyUpdateCurrentLineHighlightLocation(); // Now, if there is a highlighted region of text, allow them to cut // and copy. if (e!=null && e.getDot()!=e.getMark()) {// && !cutAction.isEnabled()) { cutAction.setEnabled(true); copyAction.setEnabled(true); } // Otherwise, if there is no highlighted region, don't let them cut // or copy. The condition here should speed things up, because this // way, we will only enable the actions the first time the selection // becomes nothing. else if (cutAction.isEnabled()) { cutAction.setEnabled(false); copyAction.setEnabled(false); } super.fireCaretUpdate(e); } /** * Removes the "Ctrl+H <=> Backspace" behavior that Java shows, for some * odd reason... */ private void fixCtrlH() { InputMap inputMap = getInputMap(); KeyStroke char010 = KeyStroke.getKeyStroke("typed \010"); InputMap parent = inputMap; while (parent != null) { parent.remove(char010); parent = parent.getParent(); } KeyStroke backspace = KeyStroke.getKeyStroke("BACK_SPACE"); inputMap.put(backspace, DefaultEditorKit.deletePrevCharAction); } /** * Provides a way to gain access to the editor actions on the right-click * popup menu. This way you can make toolbar/menu bar items use the actual * actions used by all RTextAreas, so that icons stay * synchronized and you don't have to worry about enabling/disabling them * yourself.

* Keep in mind that these actions are shared across all instances of * RTextArea, so a change to any action returned by this * method is global across all RTextArea editors in your * application. * * @param action The action to retrieve, such as {@link #CUT_ACTION}. * If the action name is invalid, null is returned. * @return The action, or null if an invalid action is * requested. */ public static RecordableTextAction getAction(int action) { if (actionMAX_ACTION_CONSTANT) return null; switch (action) { case COPY_ACTION: return copyAction; case CUT_ACTION: return cutAction; case DELETE_ACTION: return deleteAction; case PASTE_ACTION: return pasteAction; case REDO_ACTION: return redoAction; case SELECT_ALL_ACTION: return selectAllAction; case UNDO_ACTION: return undoAction; } return null; } /** * Returns the macro currently stored in this RTextArea. * Since macros are shared, all RTextAreas in the currently- * running application are using this macro. * * @return The current macro, or null if no macro has been * recorded/loaded. * @see #loadMacro(Macro) */ public static synchronized Macro getCurrentMacro() { return currentMacro; } /** * Returns the default color used for "mark all." * * @return The color. * @see #getMarkAllHighlightColor() * @see #setMarkAllHighlightColor(Color) */ public static final Color getDefaultMarkAllHighlightColor() { return DEFAULT_MARK_ALL_COLOR; } /** * Returns the icon group being used for the actions of this text area. * * @return The icon group. * @see #setIconGroup(IconGroup) */ public static IconGroup getIconGroup() { return iconGroup; } /** * Returns the line highlight manager. * * @return The line highlight manager. This may be null. */ LineHighlightManager getLineHighlightManager() { return lineHighlightManager; } /** * Returns the color used in "mark all." * * @return The color. * @see #setMarkAllHighlightColor(Color) */ public Color getMarkAllHighlightColor() { return (Color)markAllHighlightPainter.getPaint(); } /** * Returns the maximum ascent of all fonts used in this text area. In * the case of a standard RTextArea, this is simply the * ascent of the current font.

* * This value could be useful, for example, to implement a line-numbering * scheme. * * @return The ascent of the current font. */ public int getMaxAscent() { return getFontMetrics(getFont()).getAscent(); } /** * Returns the popup menu for this component, lazily creating it if * necessary. * * @return The popup menu. * @see #createPopupMenu() * @see #setPopupMenu(JPopupMenu) */ public JPopupMenu getPopupMenu() { if (!popupMenuCreated) { popupMenu = createPopupMenu(); if (popupMenu!=null) { ComponentOrientation orientation = ComponentOrientation. getOrientation(Locale.getDefault()); popupMenu.applyComponentOrientation(orientation); } popupMenuCreated = true; } return popupMenu; } /** * Returns the text mode this editor pane is currently in. * * @return Either {@link #INSERT_MODE} or {@link #OVERWRITE_MODE}. * @see #setTextMode(int) */ public final int getTextMode() { return textMode; } /** * Returns the tool tip supplier. * * @return The tool tip supplier, or null if one isn't * installed. * @see #setToolTipSupplier(ToolTipSupplier) */ public ToolTipSupplier getToolTipSupplier() { return toolTipSupplier; } /** * Returns the tooltip to display for a mouse event at the given * location. This method is overridden to check for a * {@link ToolTipSupplier}; if there is one installed, it is queried for * tool tip text before using the super class's implementation of this * method. * * @param e The mouse event. * @return The tool tip text, or null if none. * @see #getToolTipSupplier() * @see #setToolTipSupplier(ToolTipSupplier) */ public String getToolTipText(MouseEvent e) { String tip = null; if (getToolTipSupplier()!=null) { tip = getToolTipSupplier().getToolTipText(this, e); } return tip!=null ? tip : super.getToolTipText(); } /** * Does the actual dirty-work of replacing the selected text in this * text area (i.e., in its document). This method provides a hook for * subclasses to handle this in a different way. * * @param content The content to add. */ protected void handleReplaceSelection(String content) { // Call into super to handle composed text (1.5+ only though). super.replaceSelection(content); } /** * Initializes this text area. * * @param textMode The text mode. */ private void init(int textMode) { // NOTE: Our actions are created here instead of in a static block // so they are only created when the first RTextArea is instantiated, // not before. There have been reports of users calling static getters // (e.g. RSyntaxTextArea.getDefaultBracketMatchBGColor()) which would // cause these actions to be created and (possibly) incorrectly // localized, if they were in a static block. if (cutAction==null) { createPopupMenuActions(); } // Install the undo manager. undoManager = new RUndoManager(this); getDocument().addUndoableEditListener(undoManager); // Set the defaults for various stuff. Color markAllHighlightColor = getDefaultMarkAllHighlightColor(); markAllHighlightPainter = new ChangeableHighlightPainter( markAllHighlightColor); setMarkAllHighlightColor(markAllHighlightColor); carets = new int[2]; setCaretStyle(INSERT_MODE, ConfigurableCaret.THICK_VERTICAL_LINE_STYLE); setCaretStyle(OVERWRITE_MODE, ConfigurableCaret.BLOCK_STYLE); setDragEnabled(true); // Enable drag-and-drop. // Set values for stuff the user passed in. setTextMode(textMode); // carets array must be initialized first! // Fix the odd "Ctrl+H <=> Backspace" Java behavior. fixCtrlH(); } /** * Returns whether or not a macro is being recorded. * * @return Whether or not a macro is being recorded. * @see #beginRecordingMacro() * @see #endRecordingMacro() */ public static synchronized boolean isRecordingMacro() { return recordingMacro; } /** * Loads a macro to be used by all RTextAreas in the current * application. * * @param macro The macro to load. * @see #getCurrentMacro() */ public static synchronized void loadMacro(Macro macro) { currentMacro = macro; } /** * Marks all instances of the specified text in this text area. * * @param toMark The text to mark. * @param matchCase Whether the match should be case-sensitive. * @param wholeWord Whether the matches should be surrounded by spaces * or tabs. * @param regex Whether toMark is a Java regular expression. * @return The number of matches marked. * @see #clearMarkAllHighlights * @see #getMarkAllHighlightColor * @see #setMarkAllHighlightColor */ public int markAll(String toMark, boolean matchCase, boolean wholeWord, boolean regex) { Highlighter h = getHighlighter(); int numMarked = 0; if (toMark!=null && !toMark.equals(markedWord) && h!=null) { if (markAllHighlights!=null) clearMarkAllHighlights(); else markAllHighlights = new ArrayList(10); int caretPos = getCaretPosition(); markedWord = toMark; setCaretPosition(0); boolean found = SearchEngine.find(this, toMark, true, matchCase, wholeWord, regex); while (found) { int start = getSelectionStart(); int end = getSelectionEnd(); try { markAllHighlights.add(h.addHighlight(start, end, markAllHighlightPainter)); } catch (BadLocationException ble) { ble.printStackTrace(); } numMarked++; found = SearchEngine.find(this, toMark, true, matchCase, wholeWord, regex); } setCaretPosition(caretPos); repaint(); } return numMarked; } /** * {@inheritDoc} */ public void paste() { // Treat paste operations as atomic, otherwise the removal and // insertion are treated as two separate undo-able operations. beginAtomicEdit(); try { super.paste(); } finally { endAtomicEdit(); } } /** * "Plays back" the last recorded macro in this text area. */ public synchronized void playbackLastMacro() { if (currentMacro!=null) { Action[] actions = getActions(); int numActions = actions.length; List macroRecords = currentMacro.getMacroRecords(); int num = macroRecords.size(); if (num>0) { undoManager.beginInternalAtomicEdit(); try { for (int i=0; iDocument, thus requiring us to re-attach our Undo * manager. With this version we just replace the text. */ public void read(Reader in, Object desc) throws IOException { RTextAreaEditorKit kit = (RTextAreaEditorKit)getUI().getEditorKit(this); setText(null); Document doc = getDocument(); if (desc != null) doc.putProperty(Document.StreamDescriptionProperty, desc); try { // NOTE: Resets the "line separator" property. kit.read(in, doc, 0); } catch (BadLocationException e) { throw new IOException(e.getMessage()); } } /** * De-serializes a text area. * * @param s The stream to read from. * @throws ClassNotFoundException * @throws IOException */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); // UndoManagers cannot be serialized without Exceptions. See // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4275892 undoManager = new RUndoManager(this); getDocument().addUndoableEditListener(undoManager); lineHighlightManager = null; // Keep FindBugs happy. } /** * Attempt to redo the last action. * * @see #undoLastAction() */ public void redoLastAction() { // NOTE: The try/catch block shouldn't be necessary... try { if (undoManager.canRedo()) undoManager.redo(); } catch (CannotRedoException cre) { cre.printStackTrace(); } } /** * Removes all line highlights. * * @see #removeLineHighlight(Object) */ public void removeAllLineHighlights() { if (lineHighlightManager!=null) { lineHighlightManager.removeAllLineHighlights(); } } /** * Removes a line highlight. * * @param tag The tag of the line highlight to remove. * @see #removeAllLineHighlights() * @see #addLineHighlight(int, Color) */ public void removeLineHighlight(Object tag) { if (lineHighlightManager!=null) { lineHighlightManager.removeLineHighlight(tag); } } /** * Replaces text from the indicated start to end position with the * new text specified. Does nothing if the model is null. Simply * does a delete if the new string is null or empty. *

* This method is thread safe, although most Swing methods * are not.

* This method is overridden so that our Undo manager remembers it as a * single operation (it has trouble with this, especially for * RSyntaxTextArea and the "auto-indent" feature). * * @param str the text to use as the replacement * @param start the start position >= 0 * @param end the end position >= start * @exception IllegalArgumentException if part of the range is an * invalid position in the model * @see #insert(String, int) * @see #replaceRange(String, int, int) */ public void replaceRange(String str, int start, int end) { if (end < start) throw new IllegalArgumentException("end before start"); Document doc = getDocument(); if (doc != null) { try { // Without this, in some cases we'll have to do two undos // for one logical operation (for example, try editing a // Java source file in an RSyntaxTextArea, and moving a line // with text already on it down via Enter. Without this // line, doing a single "undo" moves all later text up, // but the first line moved down isn't there! Doing a // second undo puts it back. undoManager.beginInternalAtomicEdit(); ((AbstractDocument)doc).replace(start, end - start, str, null); } catch (BadLocationException e) { throw new IllegalArgumentException(e.getMessage()); } finally { undoManager.endInternalAtomicEdit(); } } } /** * This method overrides JTextComponent's * replaceSelection, so that if textMode is * {@link #OVERWRITE_MODE}, it actually overwrites. * * @param text The content to replace the selection with. */ public void replaceSelection(String text) { // It's legal for null to be used here... if (text==null) { handleReplaceSelection(text); return; } if (getTabsEmulated() && text.indexOf('\t')>-1) { text = replaceTabsWithSpaces(text); } // If the user wants to overwrite text... if (textMode==OVERWRITE_MODE && !"\n".equals(text)) { Caret caret = getCaret(); int caretPos = caret.getDot(); Document doc = getDocument(); Element map = doc.getDefaultRootElement(); int curLine = map.getElementIndex(caretPos); int lastLine = map.getElementCount() - 1; try { // If we're not at the end of a line, select the characters // that will be overwritten (otherwise JTextArea will simply // insert in front of them). int curLineEnd = getLineEndOffset(curLine); if (caretPos==caret.getMark() && caretPos!=curLineEnd) { if (curLine==lastLine) caretPos = Math.min(caretPos+text.length(), curLineEnd); else caretPos = Math.min(caretPos+text.length(), curLineEnd-1); caret.moveDot(caretPos);//moveCaretPosition(caretPos); } } catch (BadLocationException ble) { // Never happens UIManager.getLookAndFeel().provideErrorFeedback(this); ble.printStackTrace(); } } // End of if (textMode==OVERWRITE_MODE). // Now, actually do the inserting/replacing. Our undoManager will // take care of remembering the remove/insert as atomic if we are in // overwrite mode. handleReplaceSelection(text); } private StringBuffer repTabsSB; /** * Replaces all instances of the tab character in text with * the number of spaces equivalent to a tab in this text area.

* * This method should only be called from thread-safe methods, such as * {@link replaceSelection(String)}. * * @param text The java.lang.String in which to replace tabs * with spaces. This has already been verified to have at least * one tab character in it. * @return A java.lang.String just like text, * but with spaces instead of tabs. */ private final String replaceTabsWithSpaces(final String text) { String tabText = ""; int temp = getTabSize(); for (int i=0; i-1) { //repTabsSB.append(text, oldPos, pos); // Added in Java 1.5 if (pos>oldPos) { repTabsSB.append(array, oldPos, pos-oldPos); } repTabsSB.append(tabText); oldPos = pos + 1; } if (oldPosRTextArea only use {@link ConfigurableCaret}s. * To set the style of caret (vertical line, block, etc.) used for * insert or overwrite mode, use {@link #setCaretStyle(int, int)}. * * @param caret The caret to use. If this is not an instance of * ConfigurableCaret, an exception is thrown. * @throws IllegalArgumentException If the specified caret is not an * ConfigurableCaret. * @see #setCaretStyle(int, int) */ public void setCaret(Caret caret) { if (!(caret instanceof ConfigurableCaret)) { throw new IllegalArgumentException( "RTextArea needs ConfigurableCaret"); } super.setCaret(caret); if (carets!=null) { // Called by setUI() before carets is initialized ((ConfigurableCaret)caret).setStyle(carets[getTextMode()]); } } /** * Sets the style of caret used when in insert or overwrite mode. * * @param mode Either {@link #INSERT_MODE} or {@link #OVERWRITE_MODE}. * @param style The style for the caret (such as * {@link ConfigurableCaret#VERTICAL_LINE_STYLE}). * @see org.fife.ui.rtextarea.ConfigurableCaret */ public void setCaretStyle(int mode, int style) { style = (style>=ConfigurableCaret.MIN_STYLE && style<=ConfigurableCaret.MAX_STYLE ? style : ConfigurableCaret.THICK_VERTICAL_LINE_STYLE); carets[mode] = style; if (mode==getTextMode()) { // Will repaint the caret if necessary. ((ConfigurableCaret)getCaret()).setStyle(style); } } /** * Sets the document used by this text area. * * @param document The new document to use. * @throws IllegalArgumentException If the document is not an instance of * {@link AbstractDocument}. */ public void setDocument(Document document) { if (!(document instanceof AbstractDocument)) { throw new IllegalArgumentException("RTextArea requires " + "instances of AbstractDocument for its document"); } if (undoManager!=null) { // First time through, undoManager==null Document old = getDocument(); if (old!=null) { old.removeUndoableEditListener(undoManager); } } super.setDocument(document); if (undoManager!=null) { document.addUndoableEditListener(undoManager); discardAllEdits(); } } /** * Sets the path in which to find images to associate with the editor's * actions. The path MUST contain the following images (with the * appropriate extension as defined by the icon group):
*

    *
  • cut
  • *
  • copy
  • *
  • paste
  • *
  • delete
  • *
  • undo
  • *
  • redo
  • *
  • selectall
  • *
* If any of the above images don't exist, the corresponding action will * not have an icon. * * @param group The icon group to load. * @see #getIconGroup() */ public static synchronized void setIconGroup(IconGroup group) { Icon icon = group.getIcon("cut"); cutAction.putValue(Action.SMALL_ICON, icon); icon = group.getIcon("copy"); copyAction.putValue(Action.SMALL_ICON, icon); icon = group.getIcon("paste"); pasteAction.putValue(Action.SMALL_ICON, icon); icon = group.getIcon("delete"); deleteAction.putValue(Action.SMALL_ICON, icon); icon = group.getIcon("undo"); undoAction.putValue(Action.SMALL_ICON, icon); icon = group.getIcon("redo"); redoAction.putValue(Action.SMALL_ICON, icon); icon = group.getIcon("selectall"); selectAllAction.putValue(Action.SMALL_ICON, icon); iconGroup = group; } /** * Sets the color used for "mark all." This fires a property change of * type {@link #MARK_ALL_COLOR_PROPERTY}. * * @param color The color to use for "mark all." * @see #getMarkAllHighlightColor() */ public void setMarkAllHighlightColor(Color color) { Color old = (Color)markAllHighlightPainter.getPaint(); if (old!=null && !old.equals(color)) { markAllHighlightPainter.setPaint(color); if (markedWord!=null) repaint(); // Repaint if words are highlighted. firePropertyChange(MARK_ALL_COLOR_PROPERTY, old, color); } } /** * Sets the popup menu used by this text area. * * @param popupMenu The popup menu. If this is null, no * popup menu will be displayed. * @see #getPopupMenu() */ public void setPopupMenu(JPopupMenu popupMenu) { this.popupMenu = popupMenu; popupMenuCreated = true; } /** * {@inheritDoc} */ public void setRoundedSelectionEdges(boolean rounded) { if (getRoundedSelectionEdges()!=rounded) { markAllHighlightPainter.setRoundedEdges(rounded); super.setRoundedSelectionEdges(rounded); // Fires event. } } /** * Sets the text mode for this editor pane. * * @param mode Either {@link #INSERT_MODE} or {@link #OVERWRITE_MODE}. */ public void setTextMode(int mode) { if (mode!=INSERT_MODE && mode!=OVERWRITE_MODE) mode = INSERT_MODE; if (textMode != mode) { ConfigurableCaret cc = (ConfigurableCaret)getCaret(); cc.setStyle(carets[mode]); textMode = mode; } } /** * Sets the tool tip supplier. * * @param supplier The new tool tip supplier, or null if * there is to be no supplier. * @see #getToolTipSupplier() */ public void setToolTipSupplier(ToolTipSupplier supplier) { this.toolTipSupplier = supplier; } /** * Sets the UI used by this text area. This is overridden so only the * right-click popup menu's UI is updated. The look and feel of an * RTextArea is independent of the Java Look and Feel, and so * this method does not change the text area itself. Subclasses (such as * RSyntaxTextArea can call setRTextAreaUI if * they wish to install a new UI. * * @param ui This parameter is ignored. */ public final void setUI(TextUI ui) { // Update the popup menu's ui. if (popupMenu!=null) { SwingUtilities.updateComponentTreeUI(popupMenu); } // Set things like selection color, selected text color, etc. to // laf defaults (if values are null or UIResource instances). RTextAreaUI rtaui = (RTextAreaUI)getUI(); if (rtaui!=null) { rtaui.installDefaults(); } } /** * Attempt to undo an "action" done in this text area. * * @see #redoLastAction() */ public void undoLastAction() { // NOTE: that the try/catch block shouldn't be necessary... try { if (undoManager.canUndo()) undoManager.undo(); } catch (CannotUndoException cre) { cre.printStackTrace(); } } /** * Serializes this text area. * * @param s The stream to write to. * @throws IOException If an IO error occurs. */ private void writeObject(ObjectOutputStream s) throws IOException { // UndoManagers cannot be serialized without Exceptions. See // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4275892 getDocument().removeUndoableEditListener(undoManager); s.defaultWriteObject(); getDocument().addUndoableEditListener(undoManager); } /** * Modified from MutableCaretEvent in * JTextComponent so that mouse events get fired when the user * is selecting text with the mouse as well. This class also displays the * popup menu when the user right-clicks in the text area. */ protected class RTextAreaMutableCaretEvent extends RTAMouseListener { protected RTextAreaMutableCaretEvent(RTextArea textArea) { super(textArea); } public void focusGained(FocusEvent e) { Caret c = getCaret(); boolean enabled = c.getDot()!=c.getMark(); cutAction.setEnabled(enabled); copyAction.setEnabled(enabled); undoManager.updateActions(); // To reflect this text area. } public void focusLost(FocusEvent e) { } public void mouseDragged(MouseEvent e) { if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) { Caret caret = getCaret(); dot = caret.getDot(); mark = caret.getMark(); fireCaretUpdate(this); } } public void mousePressed(MouseEvent e) { // WORKAROUND: Since JTextComponent only updates the caret // location on mouse clicked and released, we'll do it on dragged // events when the left mouse button is clicked. if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) { Caret caret = getCaret(); dot = caret.getDot(); mark = caret.getMark(); fireCaretUpdate(this); } } public void mouseReleased(MouseEvent e) { if ((e.getModifiers()&MouseEvent.BUTTON3_MASK)!=0) showPopup(e); } /** * Shows a popup menu with cut, copy, paste, etc. options if the * user clicked the right button. * * @param e The mouse event that caused this method to be called. */ private void showPopup(MouseEvent e) { JPopupMenu popupMenu = getPopupMenu(); if (popupMenu!=null) { configurePopupMenu(popupMenu); popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy