/*
* 03/19/2004
*
* AbstractMainView.java - Abstract class representing a collection of
* RTextEditorPanes. This class contains all logic that would be common to
* different implementations (i.e., everything except the view parts).
* Copyright (C) 2004 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://rtext.fifesoft.com
*
* This file is a part of RText.
*
* RText is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* RText 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.fife.rtext;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.Timer;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.EventListenerList;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import org.fife.io.UnicodeWriter;
import org.fife.rtext.actions.CapsLockAction;
import org.fife.rtext.actions.ToggleTextModeAction;
import org.fife.rtext.lang.LanguageSupport;
import org.fife.rtext.lang.LanguageSupportFactory;
import org.fife.ui.UIUtil;
import org.fife.ui.rsyntaxtextarea.ErrorStrip;
import org.fife.ui.rsyntaxtextarea.FileLocation;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.fife.ui.rtextarea.Gutter;
import org.fife.ui.rtextarea.Macro;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaEditorKit;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.fife.ui.rtextfilechooser.RTextFileChooser;
import org.fife.ui.search.*;
/**
* Abstract class representing a collection of RTextEditorPanes. This class
* contains all logic that would be common to different implementations (i.e.,
* everything except the "view" parts).<p>
*
* An implementation of this class must fire a property change event of type
* {@link #CURRENT_DOCUMENT_PROPERTY} whenever the currently-active document
* changes so that other pieces of RText can function properly.<p>
*
* RText plugins may wish to register to be
* <code>CurrentTextAreaListener</code>s if they want to be notified whenever
* a property of the currently-active text area (or the text area itself)
* changes.
*
* @author Robert Futrell
* @version 0.5
*/
public abstract class AbstractMainView extends JPanel
implements PropertyChangeListener, ActionListener,
FindInFilesListener, HyperlinkListener {
public static final int DOCUMENT_SELECT_TOP = JTabbedPane.TOP;
public static final int DOCUMENT_SELECT_LEFT = JTabbedPane.LEFT;
public static final int DOCUMENT_SELECT_BOTTOM = JTabbedPane.BOTTOM;
public static final int DOCUMENT_SELECT_RIGHT = JTabbedPane.RIGHT;
public static final String CURRENT_DOCUMENT_PROPERTY = "MainView.currentDocument";
public static final String DEFAULT_ENCODING_PROPERTY = "MainView.defaultEncoding";
public static final String FILE_SIZE_CHECK_PROPERTY = "MainView.fileSizeCheck";
public static final String FRACTIONAL_METRICS_PROPERTY = "MainView.fractionalMetrics";
public static final String MARK_ALL_COLOR_PROPERTY = "MainView.markAllColor";
public static final String MARK_OCCURRENCES_COLOR_PROPERTY = "MainView.markOccurrencesColor";
public static final String MARK_OCCURRENCES_PROPERTY = "MainView.markOccurrences";
public static final String MAX_FILE_SIZE_PROPERTY = "MainView.maxFileSize";
public static final String ROUNDED_SELECTION_PROPERTY = "MainView.roundedSelection";
public static final String SMOOTH_TEXT_PROPERTY = "MainView.smoothText";
public static final String TEXT_AREA_ADDED_PROPERTY = "MainView.textAreaAdded";
public static final String TEXT_AREA_REMOVED_PROPERTY = "MainView.textAreaRemoved";
private RTextEditorPane currentTextArea; // Currently active text area.
public FindDialog findDialog; // The dialog that lets you search for text.
public ReplaceDialog replaceDialog; // The dialog that lets you replace text.
public Vector searchStrings; // The strings to go in the "Find What" combo boxes.
public boolean searchingForward; // Whether to search forward.
public boolean searchMatchCase; // If true, match case while searching.
public boolean searchWholeWord; // Whether searches look for spaces around matches.
public boolean searchRegExpression; // If true, searchString is a Java regular expression.
public boolean searchMarkAll; // If true, all search results will be marked.
private boolean lineNumbersEnabled; // If true, line numbers are visible on the documents.
private boolean lineWrapEnabled; // If true, word wrap is enabled for all documents.
private String defaultLineTerminator; // Line terminator of new text files.
private String defaultEncoding; // Encoding of new text files.
private boolean guessFileContentType;
public FindInFilesDialog findInFilesDialog; // Dialog for searching for text in files.
public ReplaceInFilesDialog replaceInFilesDialog;
public GoToDialog goToDialog; // Dialog that lets you go to a certain line number.
private int textMode; // Either INSERT_MODE or OVERWRITE_MODE.
private int tabSize; // The size (in spaces) tabs are.
private boolean emulateTabsWithWhitespace; // If true, tabs are emulated with spaces.
private Font printFont; // The font to use when printing a document.
private Color caretColor; // The color used for carets.
private Color selectionColor; // The color used for selections.
private Object backgroundObject; // Object used to draw text areas' backgrounds.
private float imageAlpha; // Alpha value used to make the bg image translucent.
private String backgroundImageFileName; // Background image, or null if background is a color.
protected RText owner; // The owner of this tabbed panel.
private SyntaxFilters syntaxFilters; // Used to decide how to syntax highlight a file.
private boolean highlightCurrentLine; // Whether or not the current line is highlighted.
private Color currentLineColor; // The color with which to highlight the current line.
private boolean highlightModifiedDocDisplayNames; // Color display names of modified files differently?
private Color modifiedDocumentDisplayNameColor; // Color to color display names of modified editors.
private boolean checkForModification; // Check for files being changed outside of RText?
private long modificationCheckDelay = 10000; // Delay in milliseconds.
private boolean bracketMatchingEnabled;
private Color matchedBracketBGColor;
private Color matchedBracketBorderColor;
private boolean marginLineEnabled;
private int marginLinePosition;
private Color marginLineColor;
private boolean hyperlinksEnabled;
private Color hyperlinkColor;
private int hyperlinkModifierKey;
private boolean whitespaceVisible;
private boolean showEOLMarkers;
private String aaHintFieldName; // Whether text is anti-aliased.
private boolean fractionalMetricsEnabled; // Whether fractional fontmetrics are used.
private Color markAllHighlightColor;
private boolean markOccurrences;
private Color markOccurrencesColor;
private boolean roundedSelectionEdges;
private int caretBlinkRate;
private int[] carets; // index 0=>insert, 1=>overwrite.
private boolean doFileSizeCheck;
private float maxFileSize; // In MB.
private Font textAreaFont;
private boolean textAreaUnderline;
private Color textAreaForeground;
private ComponentOrientation textAreaOrientation;
private EventListenerList listenerList;
private Icon bookmarkIcon;
private boolean bookmarksEnabled;
private Font lineNumberFont;
private Color lineNumberColor;
private Color gutterBorderColor;
private SpellingSupport spellingSupport;
private ToggleTextModeAction toggleTextModeAction;
private CapsLockAction capsLockAction;
/**
* The cursor used when recording a macro.
*/
private static Cursor macroCursor;
/**
* Constructor.<p>
* You should call {@link #initialize} right after this.
*/
public AbstractMainView() {
listenerList = new EventListenerList();
ClassLoader cl = getClass().getClassLoader();
URL url = cl.getResource("org/fife/rtext/graphics/bookmark.png");
if (url!=null) {
bookmarkIcon = new ImageIcon(url);
}
checkForModification = true;
Timer t = new Timer();
t.schedule(new TimerTask() {
public void run() {
checkFilesForOutsideModification();
}
},
modificationCheckDelay, // Initial delay.
modificationCheckDelay); // Check for files modified outside
// of the editor every 30 seconds.
}
// Callback for various actions.
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
// If they click the "Find" button on the Find/Replace dialog...
if (command.equals("FindNext")) {
owner.getAction(RText.FIND_NEXT_ACTION).actionPerformed(null);
}
// If they click on the "Replace" button on the Replace dialog...
else if (command.equals("Replace")) {
owner.getAction(RText.REPLACE_NEXT_ACTION).actionPerformed(null);
}
// If they clicked on the "Replace All" button on the Replace dialog...
else if (command.equals("ReplaceAll")) {
owner.getAction(RText.REPLACE_ALL_ACTION).actionPerformed(null);
}
// If a file was found to be modified outside of the editor...
else if (command.startsWith("FileModified. ")) {
handleFileModifiedEvent(command);
}
}
/**
* Adds a current text area listener.
*
* @param l The listener to add.
* @see #removeCurrentTextAreaListener
*/
public void addCurrentTextAreaListener(CurrentTextAreaListener l) {
listenerList.add(CurrentTextAreaListener.class, l);
}
/**
* Adds an empty text file to this tabbed pane. This method is
* synchronized so it doesn't interfere with the thread checking for
* files being modified outside of the editor.
*
* @param fileNameAndPath The full path and name of the file to add.
* @param encoding The encoding in which the file is to be saved. If
* an invalid value is passed in, the system default encoding
* is used.
*/
private synchronized void addNewEmptyFile(String fileNameAndPath,
String encoding) {
// Ensure the encoding is a proper value.
if (encoding==null) {
encoding = RTextFileChooser.getDefaultEncoding();
}
// Actually create the file on disk.
if (!fileNameAndPath.equals(getDefaultFileName())) {
try {
new File(fileNameAndPath).createNewFile();
} catch (IOException ioe) {
String text = owner.getString("ErrorWritingFile",
fileNameAndPath, ioe.getMessage());
JOptionPane.showMessageDialog(this, text,
owner.getString("ErrorDialogTitle"),
JOptionPane.ERROR_MESSAGE);
}
}
// Set pointers for easy reference to new document.
try {
currentTextArea = createRTextEditorPane(fileNameAndPath, encoding);
} catch (IOException ioe) {
owner.displayException(ioe);
ensureFilesAreOpened();
return;
}
// Add new text file to tabbed pane.
RTextScrollPane scrollPane = createScrollPane(currentTextArea);
currentTextArea.applyComponentOrientation(getTextAreaOrientation());
addTextAreaImpl(currentTextArea.getFileName(), scrollPane,
currentTextArea.getFileFullPath());
// Let anybody who cares know we've opened this file.
firePropertyChange(TEXT_AREA_ADDED_PROPERTY, null, currentTextArea);
}
/**
* Adds an empty text file with a default name to this panel. This method
* is synchronized so it doesn't interfere with the thread checking for
* files being modified outside of the editor.
*/
public synchronized void addNewEmptyUntitledFile() {
addNewEmptyFile(getDefaultFileName(), getDefaultEncoding());
}
/**
* Adds a text area to this view. This method fires a property change
* event of type {@link #OLD_FILE_ADDED_PROPERTY}.
*
* @param textArea The text area to add.
* @see #addTextAreaImpl(String, Component, String)
*/
private void addTextArea(RTextEditorPane textArea) {
// This is needed because the text area's undoManager picked up
// the read() call above and added it as an insertion edit. We
// don't want the user to be able to undo this, however.
textArea.discardAllEdits();
// Add the new document into our tabbed pane.
// This sets currentTextArea==tempTextArea.
RTextScrollPane scrollPane = createScrollPane(textArea);
textArea.applyComponentOrientation(getTextAreaOrientation());
addTextAreaImpl(textArea.getFileName(), scrollPane,
textArea.getFileFullPath());
// REMEMBER: currentTextArea has just been updated by
// addTextAreaImpl() above!!
// Let anybody who cares know we've opened this file.
firePropertyChange(TEXT_AREA_ADDED_PROPERTY, null, currentTextArea);
moveToTopOfCurrentDocument();
}
/**
* Adds a text area visually to this panel.
*
* @param title The name of the document to display.
* @param component The component to add (usually an RTextScrollPane).
* @param fileFullPath The full path to the file being displayed by the
* component.
*/
protected abstract void addTextAreaImpl(String title,
Component component, String fileFullPath);
/**
* Overridden so we ensure text areas keep their special LTR or RTL
* orientaitons.
*
* @param o The new component orientation.
*/
public void applyComponentOrientation(ComponentOrientation o) {
super.applyComponentOrientation(o);
// Force a reset of textAreaOrientation since the
// applyComponentOrientation() above will trickle down to the
// text areas and override their special orientations.
ComponentOrientation temp = getTextAreaOrientation();
textAreaOrientation = null;
setTextAreaOrientation(temp);
}
/**
* Returns whether or not tabs are emulated with spaces.
*
* @return <code>true</code> iff tabs are emulated with spaces.
*/
public boolean areTabsEmulated() {
return emulateTabsWithWhitespace;
}
/**
* Checks the "modified" timestamps for open files against the last known
* "modified" timestamps to see if any files have been modified outside of
* this RText instance. This method is synchronized so that it isn't
* called while the user is loading or saving a file.
*/
public synchronized void checkFilesForOutsideModification() {
// If we're currently not waiting on the user to decide about a
// previous "another program modified..." message...
if (checkForModification==true) {
// Flag so that if the user takes to long deciding, messages
// don't pile up about the same file being modified.
// NOTE: This is theoretically not thread-safe, but the
// delay is set at 10 seconds, so it should be more than
// enough to get to and complete this line).
checkForModification = false;
int numDocuments = getNumDocuments();
StringBuffer sb = new StringBuffer();
for (int i=0; i<numDocuments; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
if (textArea.isModifiedOutsideEditor()) {
sb.append(' ').append(i);
}
}
// If no documents were modified outside the editor, allow the
// thread to check again; otherwise, remember to prompt the user
// about all of the documents that changed outside of the editor.
if (sb.length()==0) {
checkForModification = true;
}
else {
final String actionCommand = "FileModified." + sb.toString();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
actionPerformed(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, actionCommand));
}});
}
} // End of if (checkForModification==true).
}
/**
* Attempts to close all currently active documents.
*
* @return <code>true</code> if all active documents were closed, and
* <code>false</code> if they weren't (i.e., the user hit cancel).
*/
public boolean closeAllDocuments() {
return closeAllDocumentsExcept(-1);
}
/**
* Attempts to close all currently active documents except the one
* specified.
*
* @return <code>true</code> if the documents were all closed, and
* <code>false</code> if they weren't (i.e., the user hit cancel).
*/
public boolean closeAllDocumentsExcept(int except) {
int numDocuments = getNumDocuments();
setSelectedIndex(numDocuments-1); // Start at the back.
// Cycle through each document, one by one.
for (int i=numDocuments-1; i>=0; i--) {
if (i==except) {
// Instead of removing this document, set focus to the
// "first" document, and continue closing documents with the
// next iteration. Since we're only keeping around 1
// document, this keeps it open.
if (i>0) {
setSelectedIndex(0);
}
}
else {
// Try to close the document.
boolean closed = closeCurrentDocument();
// If the user cancels out of it, quit the whole schibang.
if (!closed) {
// If the newly-active file is read-only, say so in the status bar.
owner.setStatusBarReadOnlyIndicatorEnabled(
currentTextArea==null ? false
: currentTextArea.isReadOnly());
return false;
}
}
} // End of for (int i=tabCount-1; i>=0; i--).
// If we got this far, then all documents were closed.
// We'll just have an empty default-named file out there.
return true;
}
/**
* Attempts to close the current document.
*
* @return Whether the file was closed (e.g. the user didn't cancel the
* operation). This will also return <code>false</code> if an
* IO error occurs saving the file, if the user chooses to do so.
*/
public final boolean closeCurrentDocument() {
RTextEditorPane old = currentTextArea;
boolean closed = closeCurrentDocumentImpl();
if (closed) {
old.clearParsers();
// Remove the old language support.
LanguageSupportFactory lsf = LanguageSupportFactory.get();
LanguageSupport support = lsf.getSupport(old.getSyntaxEditingStyle());
if (support!=null) {
support.uninstall(old);
}
firePropertyChange(TEXT_AREA_REMOVED_PROPERTY, null, old);
}
return closed;
}
/**
* Attempts to close the current document. Any implementation of this
* method <i>must be synchronized</i> so it doesn't interfere with the
* thread checking for files being modified outside of the editor.
*
* @return Whether the document was closed (e.g. the user didn't cancel the
* operation).
*/
protected abstract boolean closeCurrentDocumentImpl();
/**
* Converts all instances of a number of spaces equal to a tab in all open
* documents into tabs.
*
* @see #convertOpenFilesTabsToSpaces
*/
public void convertOpenFilesSpacesToTabs() {
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).convertSpacesToTabs();
}
/**
* Converts all tabs in all open documents into an equivalent number of
* spaces.
*
* @see #convertOpenFilesSpacesToTabs
*/
public void convertOpenFilesTabsToSpaces() {
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).convertTabsToSpaces();
}
public void copyData(AbstractMainView fromPanel) {
currentTextArea = fromPanel.currentTextArea;
findDialog = fromPanel.findDialog;
replaceDialog = fromPanel.replaceDialog;
searchStrings = fromPanel.searchStrings;
searchingForward = fromPanel.searchingForward;
searchMatchCase = fromPanel.searchMatchCase;
searchWholeWord = fromPanel.searchWholeWord;
searchRegExpression = fromPanel.searchRegExpression;
searchMarkAll = fromPanel.searchMarkAll;
lineNumbersEnabled = fromPanel.lineNumbersEnabled;
lineWrapEnabled = fromPanel.lineWrapEnabled;
findInFilesDialog = fromPanel.findInFilesDialog;
if (findInFilesDialog!=null) {
findInFilesDialog.removeFindInFilesListener(fromPanel);
findInFilesDialog.addFindInFilesListener(this);
}
replaceInFilesDialog = fromPanel.replaceInFilesDialog;
if (replaceInFilesDialog!=null) {
replaceInFilesDialog.removeFindInFilesListener(fromPanel);
replaceInFilesDialog.addFindInFilesListener(this);
}
goToDialog = fromPanel.goToDialog;
textMode = fromPanel.textMode;
tabSize = fromPanel.tabSize;
emulateTabsWithWhitespace = fromPanel.emulateTabsWithWhitespace;
printFont = fromPanel.printFont;
caretColor = fromPanel.caretColor;
selectionColor = fromPanel.selectionColor;
backgroundObject = fromPanel.backgroundObject;
imageAlpha = fromPanel.imageAlpha;
backgroundImageFileName = fromPanel.backgroundImageFileName;
owner = fromPanel.owner;
syntaxFilters = fromPanel.syntaxFilters;
setPreferredSize(fromPanel.getPreferredSize());
int numDocuments = fromPanel.getNumDocuments();
int fromSelectedIndex = fromPanel.getSelectedIndex();
ArrayList scrollPanes = new ArrayList(numDocuments);
for (int i=0; i<numDocuments; i++) {
scrollPanes.add(fromPanel.getRTextScrollPaneAt(0));
fromPanel.removeComponentAt(0);
}
for (int i=0; i<numDocuments; i++) {
RTextScrollPane scrollPane = (RTextScrollPane)scrollPanes.get(i);
RTextEditorPane editorPane = (RTextEditorPane)scrollPane.getTextArea();
addTextAreaImpl(editorPane.getFileName(), scrollPane,
editorPane.getFileFullPath());
editorPane.removePropertyChangeListener(fromPanel);
editorPane.removeHyperlinkListener(fromPanel);
editorPane.addPropertyChangeListener(this);
editorPane.addHyperlinkListener(this);
}
removeComponentAt(0); // Remove the default-named file.
renumberDisplayNames(); // In case the same document is opened multiple times.
setSelectedIndex(fromSelectedIndex);
spellingSupport = fromPanel.spellingSupport;
// "Move over" all current text area listeners.
// Remember "listeners" is guaranteed to be non-null.
Object[] listeners = fromPanel.listenerList.getListenerList();
Class ctalClass = CurrentTextAreaListener.class;
for (int i=0; i<listeners.length; i+=2) {
if (listeners[i]==ctalClass) {
CurrentTextAreaListener l =
(CurrentTextAreaListener)listeners[i+1];
fromPanel.listenerList.remove(ctalClass, l);
listenerList.add(ctalClass, l);
}
}
}
protected ErrorStrip createErrorStrip(RTextEditorPane textArea) {
ErrorStrip strip = new ErrorStrip(textArea);
strip.setLevelThreshold(ParserNotice.WARNING);
return strip;
}
/**
* Returns an editor pane to add to this main view.
*
* @param fileName The name of the file to add.
* @param encoding The encoding of the file.
* @return An editor pane.
* @throws IOException If an IO error occurs reading the file to load.
*/
private RTextEditorPane createRTextEditorPane(String fileName,
String encoding) throws IOException {
return createRTextEditorPane(FileLocation.create(fileName), encoding);
}
/**
* Returns an editor pane to add to this main view.
*
* @param loc The location of the file to add.
* @param encoding The encoding of the file.
* @return An editor pane.
* @throws IOException If an IO error occurs reading the file to load.
*/
private RTextEditorPane createRTextEditorPane(FileLocation loc,
String encoding) throws IOException {
String style = syntaxFilters.getSyntaxStyleForFile(loc.getFileName());
RTextEditorPane pane = new RTextEditorPane(owner, lineWrapEnabled,
textMode, loc, encoding);
// Set some properties.
pane.setFont(getTextAreaFont());
//pane.setUnderline(textAreaUnderline);
pane.setForeground(getTextAreaForeground());
pane.setBackgroundObject(getBackgroundObject());
pane.setTabSize(getTabSize());
pane.setHighlightCurrentLine(highlightCurrentLine);
pane.setCurrentLineHighlightColor(getCurrentLineHighlightColor());
pane.setMarginLineEnabled(marginLineEnabled);
pane.setMarginLinePosition(getMarginLinePosition());
pane.setMarginLineColor(getMarginLineColor());
pane.setMarkAllHighlightColor(getMarkAllHighlightColor());
pane.setMarkOccurrences(getMarkOccurrences());
pane.setMarkOccurrencesColor(getMarkOccurrencesColor());
setSyntaxStyle(pane, style);
pane.setBracketMatchingEnabled(isBracketMatchingEnabled());
pane.setMatchedBracketBGColor(getMatchedBracketBGColor());
pane.setMatchedBracketBorderColor(getMatchedBracketBorderColor());
if (defaultLineTerminator!=null &&
pane.getDocument().getLength()==0) {
// Empty (or new) file => use default line terminator.
pane.setLineSeparator(defaultLineTerminator, false);
}
pane.setWhitespaceVisible(isWhitespaceVisible());
pane.setEOLMarkersVisible(getShowEOLMarkers());
pane.setCaretColor(getCaretColor());
pane.setSelectionColor(getSelectionColor());
pane.setSyntaxScheme(owner.getSyntaxScheme());
pane.setHyperlinksEnabled(getHyperlinksEnabled());
pane.setHyperlinkForeground(getHyperlinkColor());
pane.setLinkScanningMask(getHyperlinkModifierKey());
pane.setRoundedSelectionEdges(getRoundedSelectionEdges());
pane.setCaretStyle(RTextEditorPane.INSERT_MODE,
carets[RTextEditorPane.INSERT_MODE]);
pane.setCaretStyle(RTextEditorPane.OVERWRITE_MODE,
carets[RTextEditorPane.OVERWRITE_MODE]);
pane.getCaret().setBlinkRate(getCaretBlinkRate());
//pane.setFadeCurrentLineHighlight(fadeCurrentLineHighlight);
// If we're in the middle of recording a macro, make the cursor
// appropriate on this guy.
if (RTextEditorPane.isRecordingMacro()) {
pane.setCursor(getMacroCursor());
}
// Other properties.
pane.setTabsEmulated(emulateTabsWithWhitespace);
pane.setTextAntiAliasHint(getTextAAHintName());
pane.setFractionalFontMetricsEnabled(isFractionalFontMetricsEnabled());
// orientation is done later to override scrollpane's
// applyComponentOrientation(...).
//pane.applyComponentOrientation(getTextAreaOrientation());
// Listeners.
pane.addPropertyChangeListener(owner);
pane.addPropertyChangeListener((StatusBar)owner.getStatusBar());
pane.addPropertyChangeListener(this);
pane.addHyperlinkListener(this);
//CompletionProvider provider = new WordCompletionProvider(C_FUNCTIONS);
//AutoCompletion ac = new AutoCompletion(provider);
//ac.install(pane);
// Add any parsers.
if (spellingSupport.isSpellCheckingEnabled()) {
pane.addParser(spellingSupport.getSpellingParser());
}
// Override the default Insert key action to one that toggles the text
// mode for all text editors.
InputMap im = pane.getInputMap();
ActionMap am = pane.getActionMap();
am.put(RTextAreaEditorKit.rtaToggleTextModeAction, toggleTextModeAction);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_CAPS_LOCK, 0), "OnCapsLock");
am.put("OnCapsLock", capsLockAction);
// Return him.
return pane;
}
/**
* Creates and returns a scroll pane containing a text area.
*
* @param textArea The text area.
* @return The scroll pane.
*/
private RTextScrollPane createScrollPane(RTextEditorPane textArea) {
RTextScrollPane scrollPane = new RTextScrollPane(textArea,
lineNumbersEnabled, null);
scrollPane.applyComponentOrientation(getComponentOrientation());
Gutter gutter = scrollPane.getGutter();
gutter.setBookmarkIcon(bookmarkIcon);
gutter.setBookmarkingEnabled(bookmarksEnabled);
gutter.setLineNumberFont(lineNumberFont);
gutter.setLineNumberColor(lineNumberColor);
gutter.setBorderColor(gutterBorderColor);
// Always visible, makes life easier
scrollPane.setIconRowHeaderEnabled(true);
return scrollPane;
}
/**
* Disposes of this view. This is called when the user changes the main
* view style. The default implementation does nothing; subclasses can
* override to dispose of anything they want.
*/
public void dispose() {
}
/**
* Ensures at least 1 file is open.
*/
private void ensureFilesAreOpened() {
if (getNumDocuments()==0) {
addNewEmptyUntitledFile();
}
}
/**
* Called when the user selects a file in a listened-to find-in-files
* dialog.
*
* @param e The event received from the <code>FindInFilesDialog</code>.
*/
public void findInFilesFileSelected(FindInFilesEvent e) {
String fileName = e.getFileName();
// "null" encoding means check for Unicode before using default.
// "true" means reuse an already-opened copy of the file if
// one exists.
if (!openFile(fileName, null, true)) {
JOptionPane.showMessageDialog(findInFilesDialog,
owner.getString("ErrorReloadFNF"),
owner.getString("ErrorDialogTitle"),
JOptionPane.ERROR_MESSAGE);
return;
}
FindInFilesDialog fnfd = (FindInFilesDialog)e.getSource();
String desc = owner.getString("FileOpened", fileName);
fnfd.setStatusText(desc);
int line = e.getLine();
if (line!=-1) {
try {
// currentTextArea is updated here. Highlight the searched-for
// text.
int start = currentTextArea.getLineStartOffset(line-1);
int end = currentTextArea.getLineEndOffset(line-1) - 1;
currentTextArea.setCaretPosition(end);
currentTextArea.moveCaretPosition(start);
currentTextArea.getCaret().setSelectionVisible(true);
} catch (Exception exc) {
owner.displayException(exc);
moveToTopOfCurrentDocument();
}
}
else
moveToTopOfCurrentDocument();
}
/**
* Notifies all registered <code>CurrentTextAreaListener</code>s of a
* change in the current text area.
*
* @param type The type of event to fire.
* @param oldValue The old value.
* @param newValue The new value.
*/
protected void fireCurrentTextAreaEvent(int type, Object oldValue,
Object newValue) {
// Guaranteed to return a non-null array.
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event.
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==CurrentTextAreaListener.class) {
((CurrentTextAreaListener)listeners[i+1]).
currentTextAreaPropertyChanged(
new CurrentTextAreaEvent(this, type,
oldValue, newValue));
}
}
}
/**
* Returns the alpha value used to make the background image translucent.
* This value does NOT change the background if no image is used (i.e., if
* the background is just a color).
*
* @return The alpha value used to make a background image translucent.
* This value will be in the range 0.0f to 1.0f.
* @see #setBackgroundImageAlpha
*/
public float getBackgroundImageAlpha() {
return imageAlpha;
}
/**
* Returns the full path to the file containing the current background
* image.
*
* @return The path, or <code>null</code> if the current background is a
* color.
* @see #setBackgroundImageFileName
*/
public String getBackgroundImageFileName() {
return backgroundImageFileName;
}
/**
* 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.String</code>.
*/
public Object getBackgroundObject() {
return backgroundObject;
}
/**
* Returns whether bookmarks are enabled.
*
* @return Whether bookmarks are enabled.
* @see #setBookmarksEnabled(boolean)
*/
public boolean getBookmarksEnabled() {
return bookmarksEnabled;
}
/**
* Returns the blink rate for all text areas.
*
* @return The blink rate.
* @see #setCaretBlinkRate
*/
public int getCaretBlinkRate() {
return caretBlinkRate;
}
/**
* Returns the color being used for the carets of all text areas in this
* main view.
*
* @return The <code>java.awt.Color</code> being used for all carets.
*/
public Color getCaretColor() {
return caretColor;
}
/**
* Returns the caret style for either the insert or overwrite caret.
*
* @param mode Either <code>RTextArea.INSERT_MODE</code> or
* <code>RTextArea.OVERWRITE_MODE</code>.
* @return The style of that caret, such as
* <code>ConfigurableCaret.VERTICAL_LINE_STYLE</code>.
* @see #setCaretStyle
* @see org.fife.ui.rtextarea.ConfigurableCaret
*/
public int getCaretStyle(int mode) {
return carets[mode];
}
/**
* 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 #isCurrentLineHighlightEnabled
* @see #setCurrentLineHighlightEnabled
* @see #setCurrentLineHighlightColor
*/
public Color getCurrentLineHighlightColor() {
return currentLineColor;
}
/**
* Returns the currently active text area.
*
* @return The currently active text area.
*/
public RTextEditorPane getCurrentTextArea() {
return currentTextArea;
}
/**
* Returns the default encoding of new text files.
*
* @return The default encoding.
* @see #setDefaultEncoding(String)
*/
public String getDefaultEncoding() {
return defaultEncoding;
}
/**
* Returns the name and path of the "default file;" that is, the file
* that is created when the user selects "New".
*
* @return The new, empty file's name.
*/
private final String getDefaultFileName() {
return owner.getWorkingDirectory() +
System.getProperty("file.separator") + owner.getNewFileName();
}
/**
* Returns whether a file's size should be checked before it is opened.
*
* @return Whether a file's size is checked before it is opened.
* @see #setDoFileSizeCheck(boolean)
*/
public boolean getDoFileSizeCheck() {
return doFileSizeCheck;
}
/**
* Returns the name being displayed for the document. For example, in a
* tabbed pane subclass, this could be the text on the tab for this
* document.
*
* @param index The index at which to find the name. If the index is
* invalid, <code>null</code> is returned.
* @return The name being displayed for this document.
*/
public abstract String getDocumentDisplayNameAt(int index);
/**
* Returns the location of the document selection area of this component.
*
* @return The location of the document selection area.
*/
public abstract int getDocumentSelectionPlacement();
/**
* Returns the index of the specified document.
*
* @return The index of the specified file, or <code>-1</code> if the file
* is not being edited.
*/
public int getFileIndex(String fileFullPath) {
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
if (getRTextEditorPaneAt(i).getFileFullPath().equals(fileFullPath))
return i;
}
return -1;
}
/**
* If the user has set a maximum file size to open, they are prompted
* whether they are "sure" they want to open the file if it is over
* their set size.
*
* @param fileName The file to check.
* @return If they do not want to check files of a certain size, this
* method will return <code>false</code>. Otherwise, it will
* return <code>true</code> if and only if this file is larger
* than their threshold and they chose not to open it.
*/
private boolean getFileIsTooLarge(String fileName) {
if (getDoFileSizeCheck()) {
File file = new File(fileName);
float fileSizeMB = file.length() / 1000000.0f;
float maxFileSizeMB = getMaxFileSize();
if (fileSizeMB>maxFileSizeMB) {
String desc = owner.getString("OpeningLargeFile",
file.getAbsolutePath());
int rc = JOptionPane.showConfirmDialog(this, desc,
owner.getString("ConfDialogTitle"),
JOptionPane.YES_NO_OPTION);
if (rc!=JOptionPane.YES_OPTION) {
// Keep at least 1 document open.
ensureFilesAreOpened();
return true;
}
}
}
return false;
}
/**
* Returns whether files with no extension have their content type guessed
* at via whether they have a "<code>#!</code>" in their first line.
*
* @return Whether to guess the content type of files with no
* extension.
* @see #setGuessFileContentType(boolean)
*/
public boolean getGuessFileContentType() {
return guessFileContentType;
}
/**
* Returns the color to use for the gutter border color.
*
* @return The gutter border color.
* @see #setGutterBorderColor(Color)
*/
public Color getGutterBorderColor() {
return gutterBorderColor;
}
/**
* Returns the color editors use for hyperlinks.
*
* @return The color used.
* @see #setHyperlinkColor(Color)
*/
public Color getHyperlinkColor() {
return hyperlinkColor;
}
/**
* Returns the modifier key editors use to scan for hyperlinks.
*
* @return The modifier key(s).
* @see #setHyperlinkModifierKey(int)
* @see java.awt.event.InputEvent
*/
public int getHyperlinkModifierKey() {
return hyperlinkModifierKey;
}
/**
* Returns whether hyperlinks are enabled in text editors.
*
* @return Whether hyperlinks are enabled.
* @see #setHyperlinksEnabled(boolean)
*/
public boolean getHyperlinksEnabled() {
return hyperlinksEnabled;
}
/**
* Returns the system icon associated with the file being edited in the
* given scroll pane (actually, in the text area inside of it). This
* method is called by subclasses that want to display a system icon for
* open files.
*
* @param scrollPane The scroll pane.
* @return The icon.
*/
protected Icon getIconFor(RTextScrollPane scrollPane) {
RTextEditorPane textArea = (RTextEditorPane)scrollPane.
getTextArea();
return FileTypeIconManager.getInstance().getIconFor(textArea);
}
/**
* Sets the color used for line numbers.
*
* @return The line number color.
* @see #setLineNumberColor(Color)
*/
public Color getLineNumberColor() {
return lineNumberColor;
}
/**
* Returns the font used for line numbers.
*
* @return The line number font.
* @see #setLineNumberFont(Font)
*/
public Font getLineNumberFont() {
return lineNumberFont;
}
/**
* Returns whether or not line numbers are visible in the open documents.
*/
public boolean getLineNumbersEnabled() {
return lineNumbersEnabled;
}
/**
* Returns the line terminator to use for new text files.
*
* @return The line terminator.
* @see #setLineTerminator(String)
*/
public String getLineTerminator() {
return defaultLineTerminator;
}
/**
* Returns whether or not line (word) wrap is enabled for the open
* documents.
*/
public boolean getLineWrap() {
return lineWrapEnabled;
}
/**
* Returns the cursor to use when a macro is being recorded.
*
* @return The cursor for text areas when a macro is being recorded.
*/
private static synchronized final Cursor getMacroCursor() {
if (macroCursor==null) {
// OS's will force the cursor to be a certain size (e.g., Windows
// will make it 32x32 usually), even if you try to set the cursor
// to some other size. So, we create a cursor that is the "best"
// cursor size to prevent our cursor image from being stretched.
try {
java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
java.awt.Dimension dim = toolkit.getBestCursorSize(16,16);
BufferedImage transparentImage = new BufferedImage(
dim.width, dim.height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = transparentImage.createGraphics();
BufferedImage image = null;
ClassLoader cl = AbstractMainView.class.getClassLoader();
image = ImageIO.read(cl.getResource(
"org/fife/rtext/graphics/macrocursor.gif"));
g2d.drawImage(image,0,0,null);
g2d.dispose();
Point hotspot = new Point(0,0);
macroCursor = toolkit.createCustomCursor(transparentImage,
hotspot, "macroCursor");
// If something fails (such as the cursor image not being found),
// just use the crosshair cursor.
} catch (RuntimeException re) {
throw re; // Keep FindBugs happy - don't mask RuntimeExceptions
} catch (Exception e) {
macroCursor = Cursor.getPredefinedCursor(
Cursor.CROSSHAIR_CURSOR);
}
} // End of if (macroCursor==null).
return macroCursor;
}
/**
* Returns the margin line's color.
*
* @return The color of the margin (even if it is not enabled).
* @see #setMarginLineColor
*/
public Color getMarginLineColor() {
return marginLineColor;
}
/**
* Returns the position of the margin line (even if it is not enabled).
*
* @return The position of the margin line.
* @see #setMarginLinePosition
*/
public int getMarginLinePosition() {
return marginLinePosition;
}
/**
* Returns the color selected by the user for "mark all."
*
* @return The color.
* @see #setMarkAllHighlightColor
*/
public Color getMarkAllHighlightColor() {
return markAllHighlightColor;
}
/**
* Returns whether "mark occurrences" is enabled.
*
* @return Whether "mark occurrences" is enabled.
* @see #setMarkOccurrences(boolean)
*/
public boolean getMarkOccurrences() {
return markOccurrences;
}
/**
* Returns the color to use to "mark occurrences."
*
* @return The color.
* @see #setMarkOccurrencesColor(Color)
* @see #getMarkOccurrences()
*/
public Color getMarkOccurrencesColor() {
return markOccurrencesColor;
}
/**
* Returns the background color used in bracket matching.
*
* @return The background color used when highlighting a bracket.
* @see #setMatchedBracketBGColor
*/
public Color getMatchedBracketBGColor() {
return matchedBracketBGColor;
}
/**
* Returns the border color used in bracket matching.
*
* @return The border color used when highlighting a bracket.
* @see #setMatchedBracketBorderColor
*/
public Color getMatchedBracketBorderColor() {
return matchedBracketBorderColor;
}
/**
* If file-size checking is enabled, this is the maximum size a
* file can be before the user is prompted before opening it.
*
* @return The maximum file size.
* @see #setMaxFileSize(float)
* @see #getDoFileSizeCheck()
*/
public float getMaxFileSize() {
return maxFileSize;
}
/**
* Gets the color used to highlight modified documents' display names.
*
* @return color The color used.
* @see #setModifiedDocumentDisplayNamesColor
* @see #highlightModifiedDocumentDisplayNames
* @see #setHighlightModifiedDocumentDisplayNames
*/
public Color getModifiedDocumentDisplayNamesColor() {
return modifiedDocumentDisplayNameColor;
}
/**
* Returns the number of documents open in this container.
*
* @return The number of open documents.
*/
public abstract int getNumDocuments();
/**
* Returns all of the files opened in this main view.
*
* @return An array of files representing the files being edited in this
* main view. If no documents are open, <code>null</code> is
* returned.
*/
public File[] getOpenFiles() {
int num = getNumDocuments();
if (num==0)
return null;
File[] files = new File[num];
for (int i=0; i<num; i++) {
files[i] = new File(getRTextEditorPaneAt(i).getFileFullPath());
}
return files;
}
/**
* Returns the <code>java.awt.Font</code> currently used to print documents.
*
* @return The font used to print documents. If <code>null</code> is
* returned, that means the current RText font is being used to
* print documents.
* @see #setPrintFont
*/
public Font getPrintFont() {
return printFont;
}
/**
* Returns whether selection edges are rounded in text areas.
*
* @return Whether selection edges are rounded.
* @see #setRoundedSelectionEdges
*/
public boolean getRoundedSelectionEdges() {
return roundedSelectionEdges;
}
/**
* Returns the <code>org.fife.rtext.RTextEditorPane</code> on the
* specified tab. This is a convenience method for
* <code>(RTextEditorPane)getRTextScrollPaneAt(i).textArea</code>.
*
* @param index The tab for which you want to get the
* {@link org.fife.rtext.RTextEditorPane}.
* @return The corresponding {@link org.fife.rtext.RTextEditorPane}.
*/
public RTextEditorPane getRTextEditorPaneAt(int index) {
RTextScrollPane sp = getRTextScrollPaneAt(index);
return sp!=null ? (RTextEditorPane)sp.getTextArea() : null;
}
/**
* Returns the <code>org.fife.rtext.RTextScrollPane</code> at the given
* index.
*
* @param index The tab for which you want to get the
* <code>org.fife.rtext.RTextScrollPane</code>.
* @return The scroll pane.
*/
public abstract RTextScrollPane getRTextScrollPaneAt(int index);
/**
* Returns the currently active component.
*
* @return The component.
*/
public abstract Component getSelectedComponent();
/**
* Returns the currently selected document's index.
*
* @return The index of the currently selected document.
*/
public abstract int getSelectedIndex();
/**
* Returns the color being used for selections in all text areas in this
* main view.
*
* @return The <code>java.awt.Color</code> being used for all selections in
* all text areas.
*/
public Color getSelectionColor() {
return selectionColor;
}
/**
* Returns whether EOL markers are visible in the text areas.
*
* @return Whether EOL markers are visible.
* @see #setShowEOLMarkers(boolean)
* @see #isWhitespaceVisible()
*/
public boolean getShowEOLMarkers() {
return showEOLMarkers;
}
/**
* Returns the spell checking support for RText.
*
* @return The spell checking support.
*/
public SpellingSupport getSpellingSupport() {
return spellingSupport;
}
/**
* Returns the syntax filters being used to open documents (i.e., to decide
* what syntax highlighting color scheme, if any, to use when opening
* documents).
*
* @return The filters being used.
*/
public SyntaxFilters getSyntaxFilters() {
return syntaxFilters;
}
/**
* Returns the size of a tab, in spaces.
*
* @return The tab size (in spaces) currently being used by all documents
* in this tabbed pane.
*/
public int getTabSize() {
return tabSize;
}
/**
* Returns the name of the scheme being used to antialias the text
* editors' text, if any.
*
* @return The name of a field of <code>java.awt.RenderingHints</code>
* that is being used for text AA. A <code>null</code> value
* means no text AA is being done.
* @see #isFractionalFontMetricsEnabled
* @see #setTextAAHintName(String)
*/
public String getTextAAHintName() {
return aaHintFieldName;
}
/**
* Returns the default font to use in text areas.
*
* @return The default font.
* @see #getTextAreaUnderline()
* @see #setTextAreaFont(Font, boolean)
*/
public Font getTextAreaFont() {
return textAreaFont;
}
/**
* Returns the default foreground color for text areas.
*
* @return The default foreground color.
* @see #setTextAreaForeground(Color)
*/
public Color getTextAreaForeground() {
return textAreaForeground;
}
/**
* Returns the orientation of the text editors.
*
* @return The orientation.
* @see #setTextAreaOrientation(ComponentOrientation)
*/
public ComponentOrientation getTextAreaOrientation() {
return textAreaOrientation;
}
/**
* Returns whether text areas' default fonts should be underlined.
*
* @return Whether to underline the default font.
* @see #getTextAreaFont()
* @see #setTextAreaFont(Font, boolean)
*/
public boolean getTextAreaUnderline() {
return textAreaUnderline;
}
/**
* Returns the text mode we're in.
*
* @return <code>RTextEditorPane.INSERT_MODE</code>
* or <code>RTextEditorPane.OVERWRITE_MODE</code>.
* @see #setTextMode
*/
public int getTextMode() {
return textMode;
}
/**
* Returns a translucent version of a given <code>java.awt.Image</code>.
*
* @param image The <code>java.awt.Image</code> on which to apply the
* alpha filter.
* @param alpha The alpha value to use when defining how translucent you
* want the image to be. This should be in the range 0.0f to 1.0f.
*/
private BufferedImage getTranslucentImage(Image image, float alpha) {
BufferedImage buffer = null;
int width = image.getWidth(null);
int height = image.getHeight(null);
MediaTracker tracker = new MediaTracker(this);
buffer = new BufferedImage(width,height, BufferedImage.TYPE_INT_ARGB_PRE);
tracker.addImage(buffer, 0);
Graphics2D g2d = buffer.createGraphics();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.drawImage(image, 0,0, null);
try {
tracker.waitForID(0);
} catch (InterruptedException e) {
owner.displayException(e);
}
tracker.removeImage(buffer, 0);
g2d.dispose();
return buffer;
}
/**
* Returns whether BOM's are written for UTF-8 files.
*
* @return Whether BOM's are written for UTF-8 files.
* @see #setWriteBOMInUtf8Files(boolean)
*/
public boolean getWriteBOMInUtf8Files() {
return UnicodeWriter.getWriteUtf8BOM();
}
/**
* Sets the pane's highlighting style based on its content (e.g. whether
* it contains "<code>#!</code>" at the top).
*
* @param pane The pane to examine.
*/
private void guessContentType(RTextEditorPane pane) {
String style = SyntaxConstants.SYNTAX_STYLE_NONE;
String firstLine = null;
try {
int endOffs = pane.getLineEndOffset(0);
if (pane.getLineCount()>1) {
endOffs--;
}
firstLine = pane.getText(0, endOffs);
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
return;
}
if (firstLine.startsWith("#!")) {
// Determine the program name. Take special care for
// the case of "#!/usr/bin/env progname".
int space = firstLine.indexOf(' ', 2);
if (space>-1) {
if (firstLine.startsWith("#!/usr/bin/env")) {
int space2 = firstLine.indexOf(' ', space+1);
if (space2==-1) { // Never happens in "correct" #!'s
space2 = firstLine.length();
}
firstLine = firstLine.substring(space+1, space2);
}
else {
firstLine = firstLine.substring(2, space);
}
}
if (firstLine.endsWith("sh")) { // ksh, bash, sh, ...
style = SyntaxConstants.SYNTAX_STYLE_UNIX_SHELL;
}
else if (firstLine.endsWith("perl")) {
style = SyntaxConstants.SYNTAX_STYLE_PERL;
}
else if (firstLine.endsWith("php")) {
style = SyntaxConstants.SYNTAX_STYLE_PHP;
}
else if (firstLine.endsWith("python")) {
style = SyntaxConstants.SYNTAX_STYLE_PYTHON;
}
else if (firstLine.endsWith("lua")) {
style = SyntaxConstants.SYNTAX_STYLE_LUA;
}
else if (firstLine.endsWith("ruby")) {
style = SyntaxConstants.SYNTAX_STYLE_RUBY;
}
}
pane.setSyntaxEditingStyle(style);
}
/**
* Handles <code>IOException</code>s thrown when adding an old text file
* to RText. This method displays a dialog showing the error message,
* prettied-up if possible.
*
* @param loc The location of the file being opened or saved.
* @param ioe The exception thrown.
* @param load Whether this was a load operation. If this value is
* <code>false</code>, then this was a save operation.
*/
private void handleAddTextFileIOException(FileLocation loc,
IOException ioe, boolean load) {
String desc = null;
String title = owner.getString("ErrorDialogTitle");
if ("sun.net.ftp.FtpLoginException".equals(ioe.getClass().getName())){
desc = owner.getString("ErrorCredentials");
JOptionPane.showMessageDialog(this, desc, title,
JOptionPane.ERROR_MESSAGE);
}
else if (ioe instanceof java.net.ConnectException) {
desc = owner.getString("ErrorConnectionRefused");
JOptionPane.showMessageDialog(this, desc, title,
JOptionPane.ERROR_MESSAGE);
}
else if (ioe instanceof java.net.UnknownHostException) {
String host = ioe.getMessage(); // Usually the hostname.
if (host==null) {
host = "";
}
desc = owner.getString("ErrorUnknownHost", host);
JOptionPane.showMessageDialog(this, desc, title,
JOptionPane.ERROR_MESSAGE);
}
else if (ioe instanceof java.io.FileNotFoundException) {
// For save operations, FileNotFoundException usually means
// "Access denied" or some other hard-to-decipher-from-a-stack-
// trace message, so for saves, just print the exception's
// message instead of a pretty one.
if (load) {
desc = owner.getString("ErrorFileNotFound",
loc.getFileName());
JOptionPane.showMessageDialog(this, desc, title,
JOptionPane.ERROR_MESSAGE);
}
else { // Prevent "An unexpected error occurred"
desc = ioe.getMessage();
if (desc==null) {
desc = ioe.toString();
}
owner.displayException(ioe, desc);
}
}
else {
desc = ioe.getMessage();
if (desc==null) {
desc = ioe.toString();
}
owner.displayException(ioe, desc);
}
}
private void handleFileModifiedEvent(String text) {
StringTokenizer tokenizer = new StringTokenizer(
text.substring(text.indexOf(' ')));
int origTab = getSelectedIndex();
// Loop while there are still documents to prompt for.
while (tokenizer.hasMoreTokens()) {
// Should be the number of the next modified document.
String token = tokenizer.nextToken();
int docNumber = 0;
try {
docNumber = Integer.parseInt(token);
} catch (NumberFormatException nfe) { // Should never happen
continue;
}
setSelectedIndex(docNumber);
// We must get it as a regular expression because
// replaceFirst expects one.
int rc = JOptionPane.NO_OPTION;
String temp = owner.getString("DocModifiedMessage",
currentTextArea.getFileName());
rc = JOptionPane.showConfirmDialog(owner, temp,
owner.getString("ConfDialogTitle"),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
// If they want to, reload the file.
if (rc==JOptionPane.YES_OPTION) {
try {
File f = new File(currentTextArea.getFileFullPath());
if (f.isFile()) { // Should always be true.
int line = currentTextArea.getLineOfOffset(
currentTextArea.getCaretPosition());
currentTextArea.reload();
int lineCount = currentTextArea.getLineCount();
line = Math.min(line, lineCount-1);
int offs = currentTextArea.getLineStartOffset(line);
currentTextArea.setCaretPosition(offs);
}
else {
JOptionPane.showMessageDialog(owner,
owner.getString("ErrorReloadFNF"),
owner.getString("ErrorDialogTitle"),
JOptionPane.ERROR_MESSAGE);
}
} catch (Exception ioe) {
JOptionPane.showMessageDialog(owner,
owner.getString("ErrorReadingFile") + ioe,
owner.getString("ErrorDialogTitle"),
JOptionPane.ERROR_MESSAGE);
}
} // End of if (rc==JOptionPane.YES_OPTION)
// Whether or not we reload, we need to update the "last
// modified" time for this document, so we don't keep
// bugging them about the same outside modification.
currentTextArea.syncLastSaveOrLoadTimeToActualFile();
} // End of while (token != null).
// It's okay to start checking for modifications again.
checkForModification = true;
// Switch back to the tab that was being edited originally.
setSelectedIndex(origTab);
}
/**
* Returns whether this panel is highlighting modified documents' display
* names with a different color.
*
* @see #setHighlightModifiedDocumentDisplayNames
* @see #getModifiedDocumentDisplayNamesColor
* @see #setModifiedDocumentDisplayNamesColor
*/
public boolean highlightModifiedDocumentDisplayNames() {
return highlightModifiedDocDisplayNames;
}
/**
* Called when a hyperlink is entered, exited, or clicked in the
* current text area.
*
* @param e The hyperlink event.
*/
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType()==HyperlinkEvent.EventType.ACTIVATED) {
URL url = e.getURL();
if (url!=null) {
if (!UIUtil.browse(url.toString())) {
UIManager.getLookAndFeel().provideErrorFeedback(this);
}
}
else {
UIManager.getLookAndFeel().provideErrorFeedback(this);
}
}
}
/**
* Initializes this component.
*
* @param owner The <code>RText</code> that this tabbed pane sits in.
* @param filesToOpen Array of strings representing files to open. If
* this parameter is null, a single file with a default name is
* opened.
* @param prefs A properties object used to initialize some fields on
* this main view.
*/
protected void initialize(RText owner, String[] filesToOpen,
RTextPreferences prefs) {
// Remember the owner of this tabbed pane.
this.owner = owner;
// Initialize some stuff from prefs.
printFont = prefs.printFont;
tabSize = prefs.tabSize;
textMode = prefs.textMode;
emulateTabsWithWhitespace = prefs.emulateTabsWithSpaces;
setDocumentSelectionPlacement(prefs.tabPlacement);
lineNumbersEnabled = prefs.lineNumbersVisible;
setBackgroundImageAlpha(prefs.imageAlpha);
Object prefsBackgroundObject = prefs.backgroundObject;
if (prefsBackgroundObject instanceof String) {
Image image = RTextUtilities.getImageFromFile(
(String)prefsBackgroundObject);
if (image!=null) {
setBackgroundObject(image);
setBackgroundImageFileName((String)prefsBackgroundObject);
}
else { // This is when the file passed in no longer exists.
setBackgroundObject(Color.WHITE);
setBackgroundImageFileName(null);
}
}
else { // It must be a color here.
setBackgroundObject(prefsBackgroundObject);
setBackgroundImageFileName(null);
}
setCaretColor(prefs.caretColor);
setSelectionColor(prefs.selectionColor);
setLineWrap(prefs.wordWrap);
setCurrentLineHighlightEnabled(prefs.currentLineHighlightEnabled);
setCurrentLineHighlightColor(prefs.currentLineHighlightColor);
setBracketMatchingEnabled(prefs.bracketMatchingEnabled);
setMatchedBracketBGColor(prefs.matchedBracketBGColor);
setMatchedBracketBorderColor(prefs.matchedBracketBorderColor);
setMarginLineEnabled(prefs.marginLineEnabled);
setMarginLinePosition(prefs.marginLinePosition);
setMarginLineColor(prefs.marginLineColor);
setHyperlinksEnabled(prefs.hyperlinksEnabled);
setHyperlinkColor(prefs.hyperlinkColor);
setHyperlinkModifierKey(prefs.hyperlinkModifierKey);
setWriteBOMInUtf8Files(prefs.bomInUtf8);
syntaxFilters = new SyntaxFilters(prefs.syntaxFiltersString);
searchStrings = new Vector(0);
searchingForward = true; // Default to searching forward.
searchMatchCase = false; // Default is don't match case.
searchWholeWord = false; // Default is not whole word.
searchMarkAll = false; // Default is not to mark all.
setHighlightModifiedDocumentDisplayNames(prefs.highlightModifiedDocNames);
setModifiedDocumentDisplayNamesColor(prefs.modifiedDocumentNamesColor);
setWhitespaceVisible(prefs.visibleWhitespace);
setShowEOLMarkers(prefs.showEOLMarkers);
setTextAAHintName(prefs.textAAHintFieldName);
setFractionalFontMetricsEnabled(prefs.fractionalMetricsEnabled);
setMarkAllHighlightColor(prefs.markAllHighlightColor);
setMarkOccurrences(prefs.markOccurrences);
setMarkOccurrencesColor(prefs.markOccurrencesColor);
setRoundedSelectionEdges(prefs.roundedSelectionEdges);
carets = new int[2];
setCaretStyle(RTextArea.INSERT_MODE, prefs.carets[0]);
setCaretStyle(RTextArea.OVERWRITE_MODE, prefs.carets[1]);
setCaretBlinkRate(prefs.caretBlinkRate);
setLineTerminator(prefs.defaultLineTerminator);
setDefaultEncoding(prefs.defaultEncoding);
setGuessFileContentType(prefs.guessFileContentType);
setDoFileSizeCheck(prefs.doFileSizeCheck);
setMaxFileSize(prefs.maxFileSize);
setTextAreaFont(prefs.textAreaFont, prefs.textAreaUnderline);
setTextAreaForeground(prefs.textAreaForeground);
setTextAreaOrientation(prefs.textAreaOrientation);
setBookmarksEnabled(prefs.bookmarksEnabled);
setLineNumberFont(prefs.lineNumberFont);
setLineNumberColor(prefs.lineNumberColor);
setGutterBorderColor(prefs.gutterBorderColor);
spellingSupport = new SpellingSupport(owner);
spellingSupport.configure(prefs); // Do this BEFORE opening any files!
toggleTextModeAction = new ToggleTextModeAction(owner);
capsLockAction = new CapsLockAction(owner);
// Start us out with whatever files they passed in.
if (filesToOpen==null) {
addNewEmptyUntitledFile();
}
else {
for (int i=0; i<filesToOpen.length; i++) {
// The "null" encoding means they'll be checked for Unicode.
openFile(filesToOpen[i], null);
}
}
setSelectedIndex(0);
// Update the title of the RText window.
owner.setMessages(currentTextArea.getFileFullPath(), null);
}
/**
* Returns whether or not bracket matching is enabled.
*
* @return <code>true</code> iff bracket matching is enabled.
* @see #setBracketMatchingEnabled
*/
public boolean isBracketMatchingEnabled() {
return bracketMatchingEnabled;
}
/**
* Returns whether or not the current line is highlighted.
*
* @return Whether or the current line is highlighted.
* @see #setCurrentLineHighlightEnabled
* @see #getCurrentLineHighlightColor
* @see #setCurrentLineHighlightColor
*/
public boolean isCurrentLineHighlightEnabled() {
return highlightCurrentLine;
}
/**
* Returns whether fractional fontmetrics is enabled.
*
* @return Whether fractional fontmetrics is enabled.
* @see #getTextAAHintName()
* @see #setFractionalFontMetricsEnabled
*/
public boolean isFractionalFontMetricsEnabled() {
return fractionalMetricsEnabled;
}
/**
* Returns whether or not the margin line is enabled
*
* @return Whether or not the margin line is enabled.
* @see #setMarginLineEnabled
*/
public boolean isMarginLineEnabled() {
return marginLineEnabled;
}
/**
* Returns whether whitespace is visible in the text areas in this panel.
*
* @return Whether whitespace is visible.
* @see #setWhitespaceVisible
*/
public boolean isWhitespaceVisible() {
return whitespaceVisible;
}
/**
* Loads a macro. This macro will be loaded into all currently-open
* <code>RTextEditorPane</code>s, as well as all editor panes opened
* afterward, until a new macro is loaded.
*
* @param file The file containing the macro to load. If this file does
* not exist or is otherwise invalid, no macro is loaded.
*/
public void loadMacro(File file) {
try {
RTextEditorPane.loadMacro(new Macro(file));
} catch (Exception e) {
owner.displayException(e);
}
}
/**
* Scrolls to the top of the current document, and places the cursor there.
*/
public void moveToTopOfCurrentDocument() {
currentTextArea.setCaretPosition(0);
}
/**
* Adds a file to this tabbed pane. This method is synchronized so it
* doesn't interfere with the thread checking for files being modified
* outside of the editor.
*
* @param loc The location of the file to add.
* @param charSet The encoding to use when reading/writing this file.
* If this value is <code>null</code>, the file is checked for
* Unicode; if it is Unicode, it is opened properly. If it is not
* Unicode, a system default encoding is used.
* @param reuse If the file is already open, whether to simply switch
* focus to that old copy (vs. opening a new copy).
* @return <code>true</code> if the file was opened (or switched to),
* <code>false</code> otherwise (if the file does not exist and
* the user chose NOT to create it, for example).
* @throws InvalidCharSetException If the specified character set is
* invalid.
*/
public boolean openFile(FileLocation loc, String charSet, boolean reuse) {
// If the only document open is untitled and empty, remove
// (and thus replace) replace it.
if (getNumDocuments()==1 &&
currentTextArea.getFileName().equals(owner.getNewFileName()) &&
currentTextArea.getDocument().getLength()==0 &&
currentTextArea.isDirty()==false) {
removeComponentAt(0);
}
// If desired, reuse a text area already opened to this file if
// there is one.
if (reuse) {
String fileNameAndPath = loc.getFileFullPath();
int count = getNumDocuments(); // May have changed from above.
for (int i=0; i<count; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
if (textArea.getFileFullPath().equals(fileNameAndPath)) {
setSelectedIndex(i);
return true;
}
}
}
String fileFullPath = loc.getFileFullPath();
// If opening is a local file that exists, or a remote file...
if (loc.isLocalAndExists() || loc.isRemote()) {
if (loc.isLocal() && getFileIsTooLarge(fileFullPath)) {
return false;
}
try {
RTextEditorPane tempTextArea = createRTextEditorPane(
loc, charSet);
addTextArea(tempTextArea);
} catch (IOException ioe) {
handleAddTextFileIOException(loc, ioe, true);
ensureFilesAreOpened();
return false;
} catch (OutOfMemoryError oome) {
owner.displayException(oome);
ensureFilesAreOpened();
return false;
}
return true;
} // End of if (loc.isLocalAndExists())
// If the file is local but doesn't exist...
else {//if (loc.isLocal()) {
String temp = owner.getString("FileNECreateItMsg", fileFullPath);
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this, temp,
owner.getString("ConfDialogTitle"),
JOptionPane.YES_NO_OPTION)) {
addNewEmptyFile(fileFullPath, charSet);
return true;
}
ensureFilesAreOpened(); // Keep at least 1 document open.
return false; // Nothing was opened.
}
}
/**
* Adds an already-created text file to this tabbed pane. This method is
* synchronized so it doesn't interfere with the thread checking for files
* being modified outside of the editor.
*
* @param fileNameAndPath The full path and name of the file to add.
* @param charSet The encoding to use when reading/writing this file.
* If this value is <code>null</code>, the file is checked for
* Unicode; if it is Unicode, it is opened properly. If it is not
* Unicode, a system default encoding is used.
* @return <code>true</code> if the file was opened (or switched to),
* <code>false</code> otherwise (if the file does not exist and
* the user chose NOT to create it, for example).
* @throws InvalidCharSetException If the specified character set is
* invalid.
*/
public boolean openFile(String fileNameAndPath, String charSet) {
return openFile(fileNameAndPath, charSet, false);
}
/**
* Adds an already-created text file to this tabbed pane. This method is
* synchronized so it doesn't interfere with the thread checking for files
* being modified outside of the editor.
*
* @param fileNameAndPath The full path and name of the file to add.
* @param charSet The encoding to use when reading/writing this file.
* If this value is <code>null</code>, the file is checked for
* Unicode; if it is Unicode, it is opened properly. If it is not
* Unicode, a system default encoding is used.
* @param reuse If the file is already open, whether to simply switch
* focus to that old copy (vs. opening a new copy).
* @return <code>true</code> if the file was opened (or switched to),
* <code>false</code> otherwise (if the file does not exist and
* the user chose NOT to create it, for example).
* @throws InvalidCharSetException If the specified character set is
* invalid.
*/
public boolean openFile(String fileNameAndPath, String charSet,
boolean reuse) {
return openFile(FileLocation.create(fileNameAndPath),
charSet, reuse);
}
/**
* If the current editor is dirty, the user is prompted whether they want
* to save it. If they choose "yes", the file is saved, otherwise it is
* not.
*
* @return <code>JOptionPane.YES_OPTION</code> if the file was saved,
* <code>NO_OPTION</code> if the user chose not to save, and
* <code>CANCEL_OPTION</code> if the user canceled the save
* dialog, or an IO error occurs.
*/
protected int promptToSaveBeforeClosingIfDirty() {
int rc = JOptionPane.YES_OPTION;
// If the current document has been modified, prompt them to save it.
if (currentTextArea.isDirty()) {
String temp = owner.getString("SaveChangesPrompt",
currentTextArea.getFileName());
// The prompting dialog.
rc = JOptionPane.showConfirmDialog(owner, temp,
owner.getString("ConfDialogTitle"),
JOptionPane.YES_NO_CANCEL_OPTION);
switch (rc) {
// If they decide to save...
case JOptionPane.YES_OPTION:
// false on IO error or user cancels saving changes
if (!saveCurrentFile()) {
return JOptionPane.CANCEL_OPTION;
}
break;
case JOptionPane.CANCEL_OPTION:
case JOptionPane.CLOSED_OPTION:
// If they choose to Cancel (NOT "No" to saving), quit.
return JOptionPane.CANCEL_OPTION;
}
}
return rc; // Either YES_OPTION or NO_OPTION
}
/**
* Called whenever a property changes on a component we're listening to.
*/
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
// If the file's path is changing (must be caused by the file being
// saved(?))...
if (propertyName.equals(RTextEditorPane.FULL_PATH_PROPERTY)) {
setDocumentDisplayNameAt(getSelectedIndex(), currentTextArea.getFileName());
fireCurrentTextAreaEvent(CurrentTextAreaEvent.FILE_NAME_CHANGED,
e.getOldValue(), e.getNewValue());
}
// If the file's modification status is changing...
else if (propertyName.equals(RTextEditorPane.DIRTY_PROPERTY)) {
int selectedIndex = getSelectedIndex();
String oldTitle = getDocumentDisplayNameAt(selectedIndex);
if ( ((Boolean)e.getNewValue()).booleanValue()==true )
setDocumentDisplayNameAt(selectedIndex, oldTitle+"*");
else {
setDocumentDisplayNameAt(selectedIndex,
oldTitle.substring(0,oldTitle.length()-1)); // Get rid of the "*".
}
fireCurrentTextAreaEvent(
CurrentTextAreaEvent.IS_MODIFIED_CHANGED,
e.getOldValue(), e.getNewValue());
}
// If the highlighting style of the current file changed...
else if (propertyName.equals(RTextEditorPane.SYNTAX_STYLE_PROPERTY)) {
fireCurrentTextAreaEvent(
CurrentTextAreaEvent.SYNTAX_STYLE_CNANGED,
e.getOldValue(), e.getNewValue());
}
// If the user changed any search options via a search dialog...
else if (propertyName.equals(FindDialog.MATCH_CASE_PROPERTY)) {
searchMatchCase = ((Boolean)e.getNewValue()).booleanValue();
}
else if (propertyName.equals(FindDialog.MATCH_WHOLE_WORD_PROPERTY)) {
searchWholeWord = ((Boolean)e.getNewValue()).booleanValue();
}
else if (propertyName.equals(FindDialog.USE_REG_EX_PROPERTY)) {
searchRegExpression = ((Boolean)e.getNewValue()).booleanValue();
}
else if (propertyName.equals(FindDialog.SEARCH_DOWNWARD_PROPERTY)) {
searchingForward = ((Boolean)e.getNewValue()).booleanValue();
}
else if (propertyName.equals(FindDialog.MARK_ALL_PROPERTY)) {
currentTextArea.clearMarkAllHighlights();
searchMarkAll = ((Boolean)e.getNewValue()).booleanValue();
}
// This is exclusive to the FindInFilesDialog.
else if (propertyName.equals(FindInFilesDialog.SEARCH_STRINGS_PROPERTY)) {
searchStrings = (Vector)e.getNewValue();
}
}
/**
* Repaints the display names for open documents.
*/
public abstract void refreshDisplayNames();
/**
* Looks for duplicate open documents (documents opened more than once)
* and adds numbers to the display names for these documents to
* differentiate them.
*/
public void renumberDisplayNames() {
// Go through and renumber any tab headings, if necessary.
int numDocuments = getNumDocuments();
if (numDocuments>0) {
boolean[] doneYet = new boolean[numDocuments];
for (int i=0; i<numDocuments; i++) {
if (doneYet[i]==true)
continue;
RTextEditorPane pane_I = getRTextEditorPaneAt(i);
String fileFullPath = pane_I.getFileFullPath();
int count = 1;
for (int j=i+1; j<numDocuments; j++) {
RTextEditorPane pane = getRTextEditorPaneAt(j);
if (!doneYet[j] && pane.getFileFullPath().equals(fileFullPath)) {
String title = pane.getFileName() + " (" + (++count) + ")";
if (pane.isDirty()==true)
title = title + "*";
setDocumentDisplayNameAt(j, title);
doneYet[j] = true;
}
}
if (count>1) {
String title = pane_I.getFileName() + " (1)";
if (pane_I.isDirty()==true)
title = title + "*";
setDocumentDisplayNameAt(i, title);
}
else {
String title = pane_I.getFileName();
if (pane_I.isDirty()==true)
title = title + "*";
setDocumentDisplayNameAt(i, title);
}
doneYet[i] = true;
} // End of for (int i=0; i<numDocuments; i++).
} // End of if (numDocuments>0).
}
/**
* Removes a component from this container.
*
* @param index The index of the component to remove.
*/
protected abstract void removeComponentAt(int index);
/**
* Removes a current text area listener.
*
* @param l The listener to remove.
* @see #addCurrentTextAreaListener
*/
public void removeCurrentTextAreaListener(CurrentTextAreaListener l) {
listenerList.remove(CurrentTextAreaListener.class, l);
}
/**
* Attempts to save all currently-opened files. If any files have unsaved
* changes, the user is prompted whether to save them.
*
* @return Whether all files were successfully saved. This will be
* <code>false</code> if an IO error occurs, or if a file has
* unsaved changes and the user selects "Cancel" when prompted
* to save them.
* @see #saveCurrentFile()
* @see #saveCurrentFileAs()
* @see #saveCurrentFileAs(FileLocation)
*/
public synchronized boolean saveAllFiles() {
boolean allSaved = true;
// Remember the number for the tab they are currently working on.
int currentTab = getSelectedIndex();
// Cycle through each document, one by one.
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
// Save this document, if it is not read-only
if (getRTextEditorPaneAt(i).isReadOnly()==false) {
setSelectedIndex(i);
allSaved |= saveCurrentFile();
}
}
// Set the active document to the one originally being working on.
setSelectedIndex(currentTab);
return allSaved;
}
/**
* Attempts to save the currently active file as it's current name.
* If the file is named "Untitled.txt", this action is essentially a
* "Save As" - the user is then prompted for a name to save with.<p>
*
* If an IO error occurs, the user is notified.
*
* @return <code>true</code> if the save is successful, <code>false</code>
* if the user cancels the save operation or an IO error occurs.
* @see #saveAllFiles()
* @see #saveCurrentFileAs()
* @see #saveCurrentFileAs(FileLocation)
*/
public synchronized boolean saveCurrentFile() {
// If this file is named "Untitled.txt", prompt them for a new name.
if (currentTextArea.getFileName().equals(owner.getNewFileName())) {
return saveCurrentFileAs();
}
// Try and write output to the current filename.
try {
currentTextArea.save();
return true;
} catch (Exception e) {
String temp = owner.getString("ErrorWritingFile",
currentTextArea.getFileFullPath(), e.getMessage());
JOptionPane.showMessageDialog(this, temp,
owner.getString("ErrorDialogTitle"), JOptionPane.ERROR_MESSAGE);
owner.setMessages(null, "ERROR: Could not save file!");
return false;
}
}
/**
* Attempts to save the currently active file. The user will be prompted
* for a new file name to save with.
*
* @return <code>true</code> if the save is successful, <code>false</code>
* if the user cancels the save operation or an IO error occurs.
* @see #saveCurrentFileAs(FileLocation)
* @see #saveCurrentFile()
* @see #saveAllFiles()
*/
public synchronized boolean saveCurrentFileAs() {
// Ensures text area gets focus after save for saves that don't bring
// up an extra window (Save As, etc.). Without this, the text area
// would lose focus.
currentTextArea.requestFocusInWindow();
// Get the new filename they'd like to use.
RTextFileChooser chooser = owner.getFileChooser();
chooser.setMultiSelectionEnabled(false); // Disable multiple file selection.
File initialSelection = new File(currentTextArea.getFileFullPath());
chooser.setSelectedFile(initialSelection);
chooser.setOpenedFiles(getOpenFiles());
// Set encoding to what it was read-in or last saved as.
chooser.setEncoding(currentTextArea.getEncoding());
int returnVal = chooser.showSaveDialog(owner);
// If they entered a new filename and clicked "OK", save the flie!
if(returnVal == RTextFileChooser.APPROVE_OPTION) {
File chosenFile = chooser.getSelectedFile();
String chosenFileName = chosenFile.getName();
String chosenFilePath = chosenFile.getAbsolutePath();
String encoding = chooser.getEncoding();
// If the current file filter has an obvious extension
// associated with it, use it if the specified filename has
// no extension. Get the extension from the filter by
// checking whether the filter is of the form
// "Foobar Files (*.foo)", and it if is, use the ".foo"
// extension.
String extension = chooser.getFileFilter().getDescription();
int leftParen = extension.indexOf("(*");
if (leftParen>-1) {
int start = leftParen + 2; // Skip "(*".
int end = extension.indexOf(')', start);
int comma = extension.indexOf(',', start);
if (comma>-1 && comma<end)
end = comma;
if (end>start+1) { // Ensure a ')' or ',' was found.
extension = extension.substring(start, end);
// If the file name they entered has no extension,
// add this extension to it.
if (chosenFileName.indexOf('.')==-1) {
chosenFileName = chosenFileName + extension;
chosenFilePath = chosenFilePath + extension;
chosenFile = new File(chosenFilePath);
}
}
}
// If the file already exists, prompt them to see whether
// or not they want to overwrite it.
if (chosenFile.exists()) {
String temp = owner.getString("FileAlreadyExists",
chosenFile.getName());
if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(
this, temp, owner.getString("ConfDialogTitle"),
JOptionPane.YES_NO_OPTION)) {
return false;
}
}
// If necessary, change the current file's encoding.
String oldEncoding = currentTextArea.getEncoding();
if (encoding!=null && !encoding.equals(oldEncoding))
currentTextArea.setEncoding(encoding);
// Try to save the file with a new name.
return saveCurrentFileAs(FileLocation.create(chosenFilePath));
} // End of if(returnVal == RTextFileChooser.APPROVE_OPTION).
// If they cancel the save...
return false;
}
/**
* Attempts to save the currently active file with a new name. If the
* save is successful, the current editor is renamed to the new name; if
* the save fails, the name doesn't change.
*
* @param loc The location to save the file to.
* @return <code>true</code> if the save is successful, <code>false</code>
* if an IO error occurs.
*/
public synchronized boolean saveCurrentFileAs(FileLocation loc) {
// Try and write output to the current filename.
try {
currentTextArea.saveAs(loc);
} catch (IOException ioe) {
handleAddTextFileIOException(loc, ioe, false);
ensureFilesAreOpened();
return false;
} catch (OutOfMemoryError oome) {
owner.displayException(oome);
ensureFilesAreOpened();
return false;
}
// Decide if we need to update the UI for syntax highlighting
// purposes (i.e., if the user saves a .txt file as a .java or a
// .c => .cpp, etc.).
String newStyle = syntaxFilters.getSyntaxStyleForFile(loc.getFileName());
setSyntaxStyle(currentTextArea, newStyle);
// If they had the same file opened twice (i.e., the "foo (1)"
// and "foo (2)"), and did "Save As..." on one of them, the other
// needs its number removed. So we'll renumber filenames.
renumberDisplayNames();
return true;
}
/**
* Sets the alpha value used to make a background image translucent. Note
* that if the background being used is simply a color and not a JPG or GIF
* image, this value does nothing.
*
* @param alpha The new alpha value to use to make background images
* translucent. This value should be between 0.0f and 1.0f.
* If it is less than 0.0f, it will be rounded up to 0.0f; if
* it is greater than 1.0f, it will be rounded down to 1.0f.
* @see #getBackgroundImageAlpha
*/
public void setBackgroundImageAlpha(float alpha) {
if (alpha<0.0f)
alpha = 0.0f;
else if (alpha>1.0f)
alpha = 1.0f;
imageAlpha = alpha;
}
/**
* Sets the path to the file containing the current background image.
* Note that this should only be called in conjunction with
* <code>setBackgroundObject</code> when the <code>Object</code> is an
* instance of <code>java.awt.Image</code> If you are setting the
* background to a <code>java.awt.Color</code>, you should pass
* <code>null</code> to this method.
*
* @param path The path to the file containing the current background
* image, or <code>null</code> if the current background is an
* image.
* @see #getBackgroundImageFileName
*/
public void setBackgroundImageFileName(String path) {
backgroundImageFileName = path;
}
/**
* Makes the background into this <code>Object</code> (either a
* <code>java.awt.Color</code> or a <code>java.awt.Image</code>).
*
* @param newBackground The <code>java.awt.Color</code> or
* <code>java.awt.Image</code> object.
*/
public void setBackgroundObject(Object newBackground) {
// If they passed in a valid type, remember the object.
if (newBackground instanceof Color) {
backgroundObject = newBackground;
}
else if (newBackground instanceof Image) {
backgroundObject = getTranslucentImage(
(Image)newBackground, imageAlpha);
}
// If they didn't pass in a valid type...
else {
// Tell them we're defaulting to basic white.
ResourceBundle msg = owner.getResourceBundle();
JOptionPane.showMessageDialog(this,
"Invalid background Object type:\n" + newBackground,
msg.getString("ErrorDialogTitle"),
JOptionPane.ERROR_MESSAGE);
// Default to basic white.
backgroundObject = Color.WHITE;
}
// Now, implement that background.
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setBackgroundObject(backgroundObject);
}
/**
* Toggles whether bookmarks are enabled.
*
* @param enabled Whether bookmarks are enabled.
* @see #getBookmarksEnabled()
*/
public void setBookmarksEnabled(boolean enabled) {
if (enabled!=bookmarksEnabled) {
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
Gutter g = getRTextScrollPaneAt(i).getGutter();
g.setBookmarkingEnabled(enabled);
//g.setIconRowHeaderVisible(enabled);
}
bookmarksEnabled = enabled;
}
}
/**
* Sets whether or not bracket matching is enabled.
*
* @param enabled Whether or not bracket matching should be enabled.
* @see #isBracketMatchingEnabled
*/
public void setBracketMatchingEnabled(boolean enabled) {
if (enabled!=bracketMatchingEnabled) {
bracketMatchingEnabled = enabled;
int num = getNumDocuments();
for (int i=0; i<num; i++)
getRTextEditorPaneAt(i).setBracketMatchingEnabled(
bracketMatchingEnabled);
}
}
/**
* Sets the blink rate for carets in all text areas.
*
* @param blinkRate The new blink rate. If this value is invalid (< 0),
* nothing happens.
* @see #getCaretBlinkRate
*/
public void setCaretBlinkRate(int blinkRate) {
if (blinkRate>=0 && blinkRate!=caretBlinkRate) {
caretBlinkRate = blinkRate;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
Caret c = getRTextEditorPaneAt(i).getCaret();
if (c!=null)
c.setBlinkRate(caretBlinkRate);
}
}
}
/**
* Sets the color of the caret used by all text areas in this tabbed pane.
*
* @param color The color to use for the caret. If <code>null</code> is
* passed in, there is no change to the caret color being used.
*/
public void setCaretColor(final Color color) {
if (color!=null && color!=caretColor) {
caretColor = color;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setCaretColor(color);
}
}
/**
* Sets the caret style for either the insert or overwrite caret.
*
* @param mode Either <code>RTextArea.INSERT_MODE</code> or
* <code>RTextArea.OVERWRITE_MODE</code>.
* @param style The style for the specified caret, such as
* <code>ConfigurableCaret.VERTICAL_LINE_STYLE</code>.
* @see #getCaretStyle
*/
public void setCaretStyle(int mode, int style) {
if (mode!=RTextArea.INSERT_MODE &&
mode!=RTextArea.OVERWRITE_MODE)
return;
if (carets[mode]!=style) {
carets[mode] = style;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setCaretStyle(mode, style);
}
}
/**
* 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.
*
* @param color The color to use to highlight the current line.
* @throws NullPointerException if <code>color</code> is <code>null</code>.
* @see #isCurrentLineHighlightEnabled
* @see #setCurrentLineHighlightEnabled
* @see #getCurrentLineHighlightColor
*/
public void setCurrentLineHighlightColor(Color color) {
if (color==null)
throw new NullPointerException();
currentLineColor = color;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
textArea.setCurrentLineHighlightColor(currentLineColor);
}
}
/**
* Sets whether or not the current line is highlighted.
*
* @param enabled Whether or not to highlight the current line.
* @see #isCurrentLineHighlightEnabled
* @see #getCurrentLineHighlightColor
* @see #setCurrentLineHighlightColor
*/
public void setCurrentLineHighlightEnabled(boolean enabled) {
highlightCurrentLine = enabled;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
textArea.setHighlightCurrentLine(highlightCurrentLine);
}
}
/**
* Sets the "currently active" text area. This should only be called
* by subclasses. After this is called, subclasses should call
* {@link #fireCurrentTextAreaEvent(int, Object, Object)} to notify any
* registered listeners of the change.
*
* @param textArea The new text area.
* @see #getCurrentTextArea()
*/
/*
* TODO: Make this method fire the event.
*/
protected void setCurrentTextArea(RTextEditorPane textArea) {
currentTextArea = textArea;
}
/**
* Sets the default encoding of new text files to the specified value.
* This method fires a property change event of type
* {@link #DEFAULT_ENCODING_PROPERTY}.
*
* @param encoding The new default encoding. A value of <code>null</code>
* means to use the system default encoding.
* @see #getDefaultEncoding()
*/
public void setDefaultEncoding(String encoding) {
if ((defaultEncoding!=null && !defaultEncoding.equals(encoding)) ||
(defaultEncoding==null && encoding!=null)) {
String old = this.defaultEncoding;
this.defaultEncoding = encoding;
firePropertyChange(DEFAULT_ENCODING_PROPERTY, old, encoding);
}
}
/**
* Sets the name being displayed for a given document. For example, in a
* tabbed pane subclass implementation, this could be the name on the tab
* of the document.
*
* @param index The index for the document for which to set the short name.
* If <code>index</code> is invalid, this method does nothing.
* @param displayName The name to display.
* @see #getDocumentDisplayNameAt
*/
public abstract void setDocumentDisplayNameAt(int index, String displayName);
/**
* Changes the location of the document selection area of this component.
*
* @param location The location to use; (<code>TOP</code>,
* <code>LEFT</code>, <code>BOTTOM</code>, or <code>RIGHT</code>.
* If this value is invalid, nothing happens.
*/
public abstract void setDocumentSelectionPlacement(int location);
/**
* Sets whether a file's size is checked before it is opened. This
* method fires a property change event of type
* {@link #FILE_SIZE_CHECK_PROPERTY}.
*
* @param doCheck Whether to check a file's size.
* @see #getDoFileSizeCheck()
*/
public void setDoFileSizeCheck(boolean doCheck) {
if (doCheck!=doFileSizeCheck) {
doFileSizeCheck = doCheck;
firePropertyChange(FILE_SIZE_CHECK_PROPERTY, !doCheck, doCheck);
}
}
/**
* Sets whether fractional fontmetrics is enabled. This method fires a
* property change of type {@link #FRACTIONAL_METRICS_PROPERTY}.
*
* @param enabled Whether fractional fontmetrics should be enabled.
* @see #isFractionalFontMetricsEnabled
*/
public void setFractionalFontMetricsEnabled(boolean enabled) {
if (fractionalMetricsEnabled!=enabled) {
fractionalMetricsEnabled = enabled;
int count = getNumDocuments();
for (int i=0; i<count; i++)
getRTextEditorPaneAt(i).setFractionalFontMetricsEnabled(
enabled);
firePropertyChange(FRACTIONAL_METRICS_PROPERTY,
!enabled, enabled);
}
}
/**
* Sets whether files with no extension have their content type guessed
* at via whether they have a "<code>#!</code>" in their first line.
*
* @param guess Whether to guess the content type of files with no
* extension.
* @see #getGuessFileContentType()
*/
public void setGuessFileContentType(boolean guess) {
if (guess!=guessFileContentType) {
guessFileContentType = guess;
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
String style = syntaxFilters.getSyntaxStyleForFile(
textArea.getFileName());
setSyntaxStyle(textArea, style);
}
}
}
/**
* Sets the color to use for the gutter's border.
*
* @param c The new gutter border color. This cannot be <code>null</code>.
* @see #getGutterBorderColor()
*/
public void setGutterBorderColor(Color c) {
if (c!=null && !c.equals(gutterBorderColor)) {
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
Gutter g = getRTextScrollPaneAt(i).getGutter();
g.setBorderColor(c);
}
gutterBorderColor = c;
}
}
/**
* Sets whether this panel will highlight modified documents' display
* names with a different color.
*
* @param highlight Whether or not to highlight modified documents' display
* names.
* @see #highlightModifiedDocumentDisplayNames
* @see #getModifiedDocumentDisplayNamesColor
* @see #setModifiedDocumentDisplayNamesColor
*/
public void setHighlightModifiedDocumentDisplayNames(boolean highlight) {
if (highlight!=highlightModifiedDocDisplayNames) {
highlightModifiedDocDisplayNames = highlight;
refreshDisplayNames();
}
}
/**
* Sets the color hyperlinks are displayed with in text editors.
*
* @param c The color to display. This cannot be <code>null</code>.
* @see #getHyperlinkColor()
*/
public void setHyperlinkColor(Color c) {
if (c!=null && !c.equals(getHyperlinkColor())) {
this.hyperlinkColor = c;
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
getRTextEditorPaneAt(i).setHyperlinkForeground(c);
}
}
}
/**
* Sets the modifier key used to start hyperlink scanning in text
* editors.
*
* @param key The modifier key(s). If an invalid value is passed in,
* {@link java.awt.event.InputEvent#CTRL_DOWN_MASK} is used.
* @see #getHyperlinkModifierKey()
* @see java.awt.event.InputEvent
*/
public void setHyperlinkModifierKey(int key) {
switch (key) {
case InputEvent.CTRL_DOWN_MASK:
case InputEvent.META_DOWN_MASK:
case InputEvent.SHIFT_DOWN_MASK:
case InputEvent.ALT_DOWN_MASK:
break;
default: // Prevent invalid values.
key = InputEvent.CTRL_DOWN_MASK;
}
if (key!=hyperlinkModifierKey) {
hyperlinkModifierKey = key;
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
getRTextEditorPaneAt(i).setLinkScanningMask(key);
}
}
}
/**
* Sets whether hyperlinks are enabled in text editors.
*
* @param enabled Whether hyperlinks should be enabled.
* @see #getHyperlinksEnabled()
*/
public void setHyperlinksEnabled(boolean enabled) {
if (enabled!=hyperlinksEnabled) {
hyperlinksEnabled = enabled;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
getRTextEditorPaneAt(i).setHyperlinksEnabled(enabled);
}
}
}
/**
* Sets the color used for line numbers.
*
* @param c The new line number color. This cannot be <code>null</code>.
* @see #getLineNumberColor()
*/
public void setLineNumberColor(Color c) {
if (c!=null && !c.equals(lineNumberColor)) {
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
Gutter g = getRTextScrollPaneAt(i).getGutter();
g.setLineNumberColor(c);
}
lineNumberColor = c;
}
}
/**
* Sets the font used for line numbers.
*
* @param f The new line number font. This cannot be <code>null</code>.
* @see #getLineNumberFont()
*/
public void setLineNumberFont(Font f) {
if (f!=null && !f.equals(lineNumberFont)) {
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
Gutter g = getRTextScrollPaneAt(i).getGutter();
g.setLineNumberFont(f);
}
lineNumberFont = f;
}
}
/**
* Enables/disables the line numbers for the open documents.
*
* @param enabled Whether or not line numbers should be enabled.
*/
public void setLineNumbersEnabled(boolean enabled) {
if (enabled!=lineNumbersEnabled) {
lineNumbersEnabled = enabled;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
getRTextScrollPaneAt(i).setLineNumbersEnabled(enabled);
}
}
}
/**
* Sets the line terminator to use for new text files.
*
* @param terminator The line terminator to use.
* @see #getLineTerminator()
*/
public void setLineTerminator(String terminator) {
if (terminator!=null &&
terminator.equals(System.getProperty("line.separator"))) {
terminator = null;
}
defaultLineTerminator = terminator;
}
/**
* Enables/disables word wrap of the open documents.
*
* @param enabled Whether or not word wrap should be enabled.
*/
public void setLineWrap(boolean enabled) {
if (enabled!=lineWrapEnabled) {
lineWrapEnabled = enabled;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setLineWrap(enabled);
}
}
/**
* Sets the color used for the margin line in text areas. If this value
* is the same as the current value, nothing happens.
*
* @param color The new color to use.
* @see #getMarginLineColor
*/
public void setMarginLineColor(Color color) {
if (!color.equals(marginLineColor)) {
marginLineColor = color;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setMarginLineColor(marginLineColor);
}
}
/**
* Sets whether or not the margin line is enabled in all text areas. If
* this value is the same as the current value, nothing happens.
*
* @param enabled Whether or not the margin line should be enabled.
* @see #isMarginLineEnabled
*/
public void setMarginLineEnabled(boolean enabled) {
if (marginLineEnabled != enabled) {
marginLineEnabled = enabled;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setMarginLineEnabled(enabled);
}
}
/**
* Sets the margin line position in all text areas. If this value is
* the same as the current value, nothing happens.
*
* @param position The new margin line position.
* @see #getMarginLinePosition
*/
public void setMarginLinePosition(int position) {
if (marginLinePosition!=position) {
marginLinePosition = position;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setMarginLinePosition(marginLinePosition);
}
}
/**
* Sets the color selected for "mark all." This method fires a property
* change of type {@link #MARK_ALL_COLOR_PROPERTY}.
*
* @param color The color to have selected.
* @see #getMarkAllHighlightColor
*/
public void setMarkAllHighlightColor(Color color) {
if (color!=null && !color.equals(markAllHighlightColor)) {
int count = getNumDocuments();
for (int i=0; i<count; i++)
getRTextEditorPaneAt(i).setMarkAllHighlightColor(color);
Color oldColor = markAllHighlightColor;
markAllHighlightColor = color;
firePropertyChange(MARK_ALL_COLOR_PROPERTY, oldColor, color);
}
}
/**
* Sets whether "mark occurrences" is enabled. This method fires a
* property change event of type {@link #MARK_OCCURRENCES_PROPERTY}.
*
* @param markOccurrences Whether "mark occurrences" should be enabled.
* @see #getMarkOccurrences()
*/
public void setMarkOccurrences(boolean markOccurrences) {
if (markOccurrences!=this.markOccurrences) {
this.markOccurrences = markOccurrences;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
getRTextEditorPaneAt(i).setMarkOccurrences(markOccurrences);
}
firePropertyChange(MARK_OCCURRENCES_PROPERTY,
!markOccurrences, markOccurrences);
}
}
/**
* Sets the color to use to "mark occurrences." This method fires a
* property change event of type {@link #MARK_OCCURRENCES_COLOR_PROPERTY}.
*
* @param color The color.
* @see #getMarkOccurrencesColor()
* @see #setMarkOccurrences(boolean)
*/
public void setMarkOccurrencesColor(Color color) {
if (color!=null && markOccurrencesColor!=color) {
Color old = markOccurrencesColor;
markOccurrencesColor = color;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
getRTextEditorPaneAt(i).setMarkOccurrencesColor(color);
}
firePropertyChange(MARK_OCCURRENCES_COLOR_PROPERTY, old, color);
}
}
/**
* Sets the background color to use in bracket matching.
*
* @param color The background color to use when highlighting a bracket.
* @see #getMatchedBracketBGColor
*/
public void setMatchedBracketBGColor(Color color) {
if (color!=matchedBracketBGColor) {
matchedBracketBGColor = color;
int num = getNumDocuments();
for (int i=0; i<num; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
// Will repaint if necessary.
textArea.setMatchedBracketBGColor(matchedBracketBGColor);
}
}
}
/**
* Sets the border color to use in bracket matching.
*
* @param color The border color to use when highlighting a bracket.
* @see #getMatchedBracketBorderColor
*/
public void setMatchedBracketBorderColor(Color color) {
if (color!=matchedBracketBorderColor) {
matchedBracketBorderColor = color;
int num = getNumDocuments();
for (int i=0; i<num; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
// Will repaint if necessary.
textArea.setMatchedBracketBorderColor(matchedBracketBorderColor);
}
}
}
/**
* If file size checking is enabled, this is the maximum size a file
* can be before the user is prompted before opening it. This
* method fires a property change event of type
* {@link #MAX_FILE_SIZE_PROPERTY}.
*
* @param size The new maximum size for a file before the user is
* prompted before opening it.
* @see #getMaxFileSize()
* @see #setDoFileSizeCheck(boolean)
*/
public void setMaxFileSize(float size) {
if (maxFileSize!=size) {
float old = maxFileSize;
maxFileSize = size;
firePropertyChange(MAX_FILE_SIZE_PROPERTY, old, maxFileSize);
}
}
/**
* Sets the color used to highlight modified documents' display names.
*
* @param color The color to use.
* @throws NullPointerException If <code>color</code> is <code>null</code>.
* @see #getModifiedDocumentDisplayNamesColor
* @see #highlightModifiedDocumentDisplayNames
* @see #setHighlightModifiedDocumentDisplayNames
*/
public void setModifiedDocumentDisplayNamesColor(Color color) {
if (color==null)
throw new NullPointerException();
modifiedDocumentDisplayNameColor = color;
refreshDisplayNames(); // So the color change takes effect.
}
/**
* Sets the font to use when printing documents.
*
* @param newPrintFont The font to use when printing documents. If
* <code>null</code>, then the current RText font will be used.
* @see #getPrintFont
*/
public void setPrintFont(Font newPrintFont) {
printFont = newPrintFont;
}
/**
* This method is called by {@link BeginRecordingMacroAction} and
* {@link EndRecordingMacroAction} so we can change the cursor used by all
* open text areas when a macro is being recorded.
*
* @param recording Whether a macro is being recorded.
*/
void setRecordingMacro(boolean recording) {
Cursor cursor = (recording ? getMacroCursor() :
Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
textArea.setCursor(cursor);
}
}
/**
* Sets whether selection edges are rounded in text areas.
*
* @param rounded Whether selection edges are to be rounded.
* @see #getRoundedSelectionEdges
*/
public void setRoundedSelectionEdges(boolean rounded) {
if (rounded!=roundedSelectionEdges) {
roundedSelectionEdges = rounded;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
textArea.setRoundedSelectionEdges(rounded);
}
firePropertyChange(ROUNDED_SELECTION_PROPERTY,
!rounded, rounded);
}
}
/**
* Sets the currently active document.
*
* @param index The index of the document to make the active document.
* If this value is invalid, nothing happens.
*/
public abstract void setSelectedIndex(int index);
/**
* Sets the currently active text area.
*
* @param textArea The text area to make active.
* @return Whether the text area was made active. This will be
* <code>false</code> if the text area is not contained in this
* view.
* @see #setSelectedIndex(int)
*/
public boolean setSelectedTextArea(RTextEditorPane textArea) {
for (int i=0; i<getNumDocuments(); i++) {
RTextEditorPane ta2 = getRTextEditorPaneAt(i);
if (ta2!=null && ta2==textArea) {
setSelectedIndex(i);
return true;
}
}
return false;
}
/**
* Sets the color of selections in all text areas in this tabbed pane.
*
* @param color The color to use for the selections. If <code>null</code>
* is passed in, there is no change to the selection color being
* used.
*/
public void setSelectionColor(Color color) {
if (color!=null && color!=selectionColor) {
selectionColor = color;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setSelectionColor(color);
}
}
/**
* Toggles whether EOL markers are visible in the text areas.
*
* @param show Whether EOL markers should be shown.
* @see #setWhitespaceVisible(boolean)
* @see #getShowEOLMarkers()
*/
public void setShowEOLMarkers(boolean show) {
if (show!=showEOLMarkers) {
showEOLMarkers = show;
for (int i=0; i<getNumDocuments(); i++) {
getRTextEditorPaneAt(i).setEOLMarkersVisible(showEOLMarkers);
}
}
}
/**
* Sets the file filters used when opening documents to decide how to
* syntax highlight documents. All currently open text files have their
* color schemes updated, if necessary.
*
* @param syntaxFilters The filter set to use.
* @see #getSyntaxFilters
*/
public void setSyntaxFilters(SyntaxFilters syntaxFilters) {
this.syntaxFilters = syntaxFilters;
// Reset all open files' color schemes if necessary.
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
String oldStyle = textArea.getSyntaxEditingStyle();
String newStyle = syntaxFilters.getSyntaxStyleForFile(
textArea.getFileName());
if (!oldStyle.equals(newStyle)) {
setSyntaxStyle(textArea, newStyle);
if (textArea==currentTextArea) {
textArea.repaint();
}
}
}
}
/**
* Sets the syntax highlighting color scheme being used.
*
* @param colorScheme The new color scheme to use. If <code>null</code>,
* the default color scheme is set.
*/
public void setSyntaxScheme(SyntaxScheme colorScheme) {
if (currentTextArea==null) {
addNewEmptyUntitledFile();
}
int numDocuments = getNumDocuments();
if (colorScheme!=null) {
for (int i=0; i<numDocuments; i++) {
getRTextEditorPaneAt(i).setSyntaxScheme(colorScheme);
}
}
else {
for (int i=0; i<numDocuments; i++) {
getRTextEditorPaneAt(i).restoreDefaultSyntaxScheme();
}
}
}
/**
* Sets the syntax style on a text area, guessing it based on its content
* if necessary.
*
* @param pane The text area.
* @param style The style for the text area.
*/
private void setSyntaxStyle(RTextEditorPane pane, String style) {
// Remove the old language support.
LanguageSupportFactory lsf = LanguageSupportFactory.get();
LanguageSupport support = lsf.getSupport(pane.getSyntaxEditingStyle());
if (support!=null) {
support.uninstall(pane);
}
// If there was no extension on the file name, guess the content
// type for highlighting (but don't override content type if already
// assigned, e.g. "makefile" does this).
if (getGuessFileContentType() &&
pane.getFileName().indexOf('.')==-1 &&
SyntaxConstants.SYNTAX_STYLE_NONE.equals(style)) {
guessContentType(pane);
}
else {
// Doesn't change style if it's already being used.
pane.setSyntaxEditingStyle(style);
}
// If the syntax style changed, what text is a "comment" also changed,
// so we need to re-do the spell check.
spellingSupport.forceSpellCheck(pane);
// Add the new support.
support = lsf.getSupport(pane.getSyntaxEditingStyle());
if (support!=null) {
support.install(pane);
}
}
/**
* Changes whether or not tabs should be emulated with spaces (i.e., soft
* tabs).
*
* @param areEmulated Whether or not tabs should be emulated with spaces.
*/
public void setTabsEmulated(boolean areEmulated) {
if (areEmulated!=emulateTabsWithWhitespace) {
emulateTabsWithWhitespace = areEmulated;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setTabsEmulated(areEmulated);
}
}
/**
* Sets the tab size to be used on all documents.
*
* @param newSize The tab size to use.
*/
public void setTabSize(int newSize) {
// Do nothing if the tab size is invalid (SHOULD THROW AN EXCEPTION).
if (newSize<0)
//throw new TabSizeException();
return;
// If the new tab size is different from the current one...
if (newSize!=tabSize) {
tabSize = newSize;
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setTabSize(newSize);
}
}
/**
* Sets the rendering hint to use when anti-aliasing text in the text
* editors. This method fires a property change of type
* {@link #SMOOTH_TEXT_PROPERTY}.
*
* @param aaHintFieldName The name of a field in
* <code>java.awt.RenderingHints</code>. The name of the hint is
* used so that, if new values are added in the future, they can
* be used. Invalid/unsupported values default to
* <code>null</code>. A <code>null</code> value indicates that
* no text anti-aliasing should be done.
* @see #getTextAAHintName()
*/
public void setTextAAHintName(String aaHintFieldName) {
String old = this.aaHintFieldName;
// If they are setting the AA hint to null (no AA)...
if (aaHintFieldName==null && old!=null) {
this.aaHintFieldName = aaHintFieldName;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
getRTextEditorPaneAt(i).setTextAntiAliasHint(
this.aaHintFieldName);
}
firePropertyChange(SMOOTH_TEXT_PROPERTY,
old, this.aaHintFieldName);
}
else if (aaHintFieldName!=null && !aaHintFieldName.equals(old)) {
this.aaHintFieldName = aaHintFieldName;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
getRTextEditorPaneAt(i).setTextAntiAliasHint(
aaHintFieldName);
}
firePropertyChange(SMOOTH_TEXT_PROPERTY,
old, this.aaHintFieldName);
}
}
/**
* Sets the default font for text areas.
*
* @param font The font.
* @param underline Whether the font is underlined.
* @see #getTextAreaFont()
* @see #getTextAreaUnderline()
*/
public void setTextAreaFont(Font font, boolean underline) {
if (font==null) {
font = new Font("Monospaced", Font.PLAIN, 13);
}
if (!font.equals(textAreaFont) || underline!=textAreaUnderline) {
int docCount = getNumDocuments();
for (int i=0; i<docCount; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
textArea.setFont(font);
//textArea.setUnderline(underline);
}
}
// Always set our values so text areas created later have them.
textAreaFont = font;
textAreaUnderline = underline;
}
/**
* Sets the default foreground color for text areas.
*
* @param fg The new default foreground color.
* @see #getTextAreaForeground()
*/
public void setTextAreaForeground(Color fg) {
if (fg!=null && !fg.equals(textAreaForeground)) {
textAreaForeground = fg;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
textArea.setForeground(textAreaForeground);
}
}
}
/**
* Sets the component orientation of the text areas.
*
* @param o The new orientation.
* @see #getTextAreaOrientation()
*/
public void setTextAreaOrientation(ComponentOrientation o) {
if (o==null) {
o = ComponentOrientation.LEFT_TO_RIGHT;
}
if (textAreaOrientation==null ||
o.isLeftToRight()!=textAreaOrientation.isLeftToRight()) {
textAreaOrientation = o;
int count = getNumDocuments();
for (int i=0; i<count; i++) {
RTextEditorPane textArea = getRTextEditorPaneAt(i);
textArea.applyComponentOrientation(textAreaOrientation);
}
}
}
/**
* Enables either insert mode or overwrite mode for the text documents.
*
* @param mode Either <code>RTextEditorPane.INSERT_MODE</code> or
* <code>RTextEditorPane.OVERWRITE_MODE</code>.
* @throws IllegalArgumentException If <code>mode</code> is invalid.
* @see #getTextMode()
*/
public void setTextMode(int mode) {
if (mode!=RTextEditorPane.INSERT_MODE &&
mode!=RTextEditorPane.OVERWRITE_MODE) {
throw new IllegalArgumentException("Invalid mode: " + mode);
}
textMode = mode;
for (int i=0; i<getNumDocuments(); i++) {
getRTextEditorPaneAt(i).setTextMode(mode);
}
}
/**
* Sets whether whitespace is visible in all open text areas.<p>
* This method will not change anything if the value of
* <code>visible</code> is already the whitespace-visibility state.
*
* @param visible Whether whitespace should be visible.
*/
public void setWhitespaceVisible(boolean visible) {
if (whitespaceVisible != visible) {
whitespaceVisible = visible;
int count = getNumDocuments();
for (int i=0; i<count; i++)
getRTextEditorPaneAt(i).setWhitespaceVisible(visible);
}
}
/**
* Sets whether BOM's should be written for UTF-8 files.
*
* @param write Whether to write BOM's for UTF-8 files.
* @see #getWriteBOMInUtf8Files()
*/
public void setWriteBOMInUtf8Files(boolean write) {
System.setProperty(UnicodeWriter.PROPERTY_WRITE_UTF8_BOM,
Boolean.toString(write));
}
/**
* Updates the look and feel of objects that the parent <code>RText</code>
* can't get to. This should be called whenever the look and feel is
* changed while <code>RText</code> is running.
*/
public void updateLookAndFeel() {
// Remember, the find and replace dialogs are created together.
if (findDialog != null) {
SwingUtilities.updateComponentTreeUI(findDialog);
// Unique to findDialog, NOT all JDialogs.
findDialog.updateUI();
findDialog.pack();
SwingUtilities.updateComponentTreeUI(replaceDialog);
// Also unique to replaceDialog, NOT all JDialogs.
replaceDialog.updateUI();
replaceDialog.pack();
}
// Update the GoTo dialog, if it has been created yet.
if (goToDialog != null) {
SwingUtilities.updateComponentTreeUI(goToDialog);
goToDialog.pack();
}
// Update the Find In Files dialog, if it exists.
if (findInFilesDialog != null) {
SwingUtilities.updateComponentTreeUI(findInFilesDialog);
// Also unique to findInFilesDialog, NOT all JDialogs.
findInFilesDialog.updateUI();
findInFilesDialog.pack();
}
// Update all open files to ensure that they keep the "correct"
// background. We need to do this because in RText's
// updateLookAndFeel(), each text area's updateUI() is called, which
// resets their background to white, evidently.
int numDocuments = getNumDocuments();
for (int i=0; i<numDocuments; i++)
getRTextEditorPaneAt(i).setBackgroundObject(backgroundObject);
if (currentTextArea != null)
currentTextArea.repaint();
}
/**
* Updates the status bar's read-only indicator and line/column indicator.
* This should be called whenever the currently active document changes.
*/
protected void updateStatusBar() {
StatusBar statusBar = (StatusBar)owner.getStatusBar();
if (statusBar!=null) {
statusBar.setReadOnlyIndicatorEnabled(currentTextArea.isReadOnly());
int lineNumber = currentTextArea.getCaretLineNumber();
int lineStartOffset = currentTextArea.getLineStartOffsetOfCurrentLine();
statusBar.setRowAndColumn(lineNumber+1,
currentTextArea.getCaretPosition()-lineStartOffset+1);
}
}
}
|