From a9e3ea40a583700282addee3d4abbe3dc1fe6b35 Mon Sep 17 00:00:00 2001
From: Kevin McGoldrick
- * EasySSLProtocolSocketFactory can be used to creats SSL {@link Socket}s that accept self-signed certificates.
- *
- * This socket factory SHOULD NOT be used for productive systems due to security reasons, unless it is a concious
- * decision and you are perfectly aware of security implications of accepting self-signed certificates
- *
- * Example of using custom protocol socket factory for a specific host:
- *
- *
- * Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
- *
- * HttpClient client = new HttpClient();
- * client.getHostConfiguration().setHost("localhost", 443, easyhttps);
- * // use relative url only
- * GetMethod httpget = new GetMethod("/");
- * client.executeMethod(httpget);
- *
- *
- *
- * Example of using custom protocol socket factory per default instead of the standard one: - * - *
- * Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443); - * Protocol.registerProtocol("https", easyhttps); - * - * HttpClient client = new HttpClient(); - * GetMethod httpget = new GetMethod("https://localhost/"); - * client.executeMethod(httpget); - *- * - * - * - * @author Oleg Kalnichevski - * - *
- * DISCLAIMER: HttpClient developers DO NOT actively support this component. The component is provided as a - * reference material, which may be inappropriate for use without additional customization. - *
- */ - -@SuppressWarnings({ "restriction", "deprecation" }) -public class EasySSLProtocolSocketFactory implements SecureProtocolSocketFactory { - - /** Log object for this class. */ - private static final Log LOG = LogFactory.getLog(EasySSLProtocolSocketFactory.class); - - private SSLContext sslcontext = null; - - /** - * Constructor for EasySSLProtocolSocketFactory. - */ - public EasySSLProtocolSocketFactory() { - super(); - } - - private static SSLContext createEasySSLContext() { - try { - SSLContext context = SSLContext.getInstance("SSL"); - context.init( - null, - new TrustManager[] { new EasyX509TrustManager(null) }, - null); - return context; - } catch (Exception e) { - LOG.error(e.getMessage(), e); - throw new HttpClientError(e.toString()); - } - } - - private SSLContext getSSLContext() { - if (this.sslcontext == null) { - this.sslcontext = createEasySSLContext(); - } - return this.sslcontext; - } - - /** - * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) - */ - public Socket createSocket( - String host, - int port, - InetAddress clientHost, - int clientPort) - throws IOException, UnknownHostException { - - return getSSLContext().getSocketFactory().createSocket( - host, - port, - clientHost, - clientPort - ); - } - - /** - * Attempts to get a new socket connection to the given host within the given time limit. - *- * To circumvent the limitations of older JREs that do not support connect timeout a controller thread is executed. - * The controller thread attempts to create a new socket within the given limit of time. If socket constructor does - * not return until the timeout expires, the controller terminates and throws an {@link ConnectTimeoutException} - *
- * - * @param host - * the host name/IP - * @param port - * the port on the host - * @param clientHost - * the local host name/IP to bind the socket to - * @param clientPort - * the port on the local machine - * @param params - * {@link HttpConnectionParams Http connection parameters} - * - * @return Socket a new socket - * - * @throws IOException - * if an I/O error occurs while creating the socket - * @throws UnknownHostException - * if the IP address of the host cannot be determined - */ - public Socket createSocket( - final String host, - final int port, - final InetAddress localAddress, - final int localPort, - final HttpConnectionParams params - ) throws IOException, UnknownHostException, ConnectTimeoutException { - if (params == null) { - throw new IllegalArgumentException("Parameters may not be null"); - } - int timeout = params.getConnectionTimeout(); - if (timeout == 0) { - return createSocket(host, port, localAddress, localPort); - } else { - // To be eventually deprecated when migrated to Java 1.4 or above - return ControllerThreadSocketFactory.createSocket( - this, host, port, localAddress, localPort, timeout); - } - } - - /** - * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int) - */ - public Socket createSocket(String host, int port) - throws IOException, UnknownHostException { - return getSSLContext().getSocketFactory().createSocket( - host, - port - ); - } - - /** - * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean) - */ - public Socket createSocket( - Socket socket, - String host, - int port, - boolean autoClose) - throws IOException, UnknownHostException { - return getSSLContext().getSocketFactory().createSocket( - socket, - host, - port, - autoClose - ); - } - - public boolean equals(Object obj) { - return ((obj != null) && obj.getClass().equals(EasySSLProtocolSocketFactory.class)); - } - - public int hashCode() { - return EasySSLProtocolSocketFactory.class.hashCode(); - } - -} diff --git a/agent/apiharness/src/main/java/com/intuit/tank/harness/ssl/EasyX509TrustManager.java b/agent/apiharness/src/main/java/com/intuit/tank/harness/ssl/EasyX509TrustManager.java deleted file mode 100644 index 435a1dddd..000000000 --- a/agent/apiharness/src/main/java/com/intuit/tank/harness/ssl/EasyX509TrustManager.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.intuit.tank.harness.ssl; - -/* - * #%L - * Intuit Tank Agent (apiharness) - * %% - * Copyright (C) 2011 - 2015 Intuit Inc. - * %% - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * #L% - */ - -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.intuit.tank.harness.logging.LogUtil; -import com.intuit.tank.logging.LogEventType; -import com.sun.net.ssl.TrustManager; -import com.sun.net.ssl.TrustManagerFactory; -import com.sun.net.ssl.X509TrustManager; - -/** - *- * EasyX509TrustManager unlike default {@link X509TrustManager} accepts self-signed certificates. - *
- *- * This trust manager SHOULD NOT be used for productive systems due to security reasons, unless it is a concious - * decision and you are perfectly aware of security implications of accepting self-signed certificates - *
- * - * @author Adrian Sutton - * @author Oleg Kalnichevski - * - *- * DISCLAIMER: HttpClient developers DO NOT actively support this component. The component is provided as a - * reference material, which may be inappropriate for use without additional customization. - *
- */ - -@SuppressWarnings({ "deprecation", "restriction" }) -public class EasyX509TrustManager implements X509TrustManager { - private static final Logger LOG = LogManager.getLogger(EasyX509TrustManager.class); - private X509TrustManager standardTrustManager = null; - - /** - * Constructor for EasyX509TrustManager. - */ - public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException { - super(); - TrustManagerFactory factory = TrustManagerFactory.getInstance("SunX509"); - factory.init(keystore); - TrustManager[] trustmanagers = factory.getTrustManagers(); - if (trustmanagers.length == 0) { - throw new NoSuchAlgorithmException("SunX509 trust manager not supported"); - } - this.standardTrustManager = (X509TrustManager) trustmanagers[0]; - } - - /** - * @see com.sun.net.ssl.X509TrustManager#isClientTrusted(X509Certificate[]) - */ - public boolean isClientTrusted(X509Certificate[] certificates) { - // return this.standardTrustManager.isClientTrusted(certificates); - boolean clientTrusted = this.standardTrustManager.isClientTrusted(certificates); - if (!clientTrusted) { - LOG.warn("Client not natively trusted. Ignoring and Trusting anyway.", - LogEventType.System); - } - return true; - } - - /** - * @see com.sun.net.ssl.X509TrustManager#isServerTrusted(X509Certificate[]) - */ - public boolean isServerTrusted(X509Certificate[] certificates) { - // if ((certificates != null) && LOG.isDebugEnabled()) { - // LOG.debug("Server certificate chain:"); - // for (int i = 0; i < certificates.length; i++) { - // LOG.debug("X509Certificate[" + i + "]=" + certificates[i]); - // } - // } - if ((certificates != null) && (certificates.length == 1)) { - X509Certificate certificate = certificates[0]; - try { - certificate.checkValidity(); - } catch (CertificateException e) { - LOG.warn(LogUtil.getLogMessage("Sertver not natively trusted. Ignoring and Trusting anyway. Reason: " - + e.toString(), LogEventType.System)); - // return false; - } - } else { - boolean serverTrusted = this.standardTrustManager.isServerTrusted(certificates); - if (!serverTrusted) { - LOG.warn("Server not natively trusted. Ignoring and Trusting anyway."); - } - } - return true; - } - - /** - * @see com.sun.net.ssl.X509TrustManager#getAcceptedIssuers() - */ - public X509Certificate[] getAcceptedIssuers() { - return this.standardTrustManager.getAcceptedIssuers(); - } -} diff --git a/agent/apiharness_pkg/pom.xml b/agent/apiharness_pkg/pom.xml index 16b6b6ec7..48428616f 100644 --- a/agent/apiharness_pkg/pom.xml +++ b/agent/apiharness_pkg/pom.xml @@ -6,7 +6,7 @@Reader
for javax.swing.text.Document
objects.
- *
+ *
* @author Robert Futrell
* @version 1.0
*/
public class DocumentReader extends Reader {
- /**
- * The stream's position in the document.
- */
- private long position;
-
- /**
- * A remembered position in the document.
- */
- private long mark;
-
- /**
- * The document we're working on.
- */
- private Document document;
-
- /**
- * Used for fast character access.
- */
- private Segment segment;
-
- /**
- * Constructor.
- *
- * @param document
- * The document we're 'reading'.
- */
- public DocumentReader(Document document) {
- position = 0;
- mark = -1;
- this.document = document;
- this.segment = new Segment();
- }
-
- /**
- * This currently does nothing...
- */
- public void close() {
- }
-
- /**
- * Marks the present position in the stream. Subsequent calls to reset()
will reposition the stream to
- * this point.
- *
- * @param readAheadLimit
- * Ignored.
- */
- public void mark(int readAheadLimit) {
- mark = position;
- }
-
- /**
- * Tells whether this reader supports the mark
operation. This always returns true
for
- * DocumentReader
.
- */
- public boolean markSupported() {
- return true;
- }
-
- /**
- * Reads the single character at the current position in the document.
- */
- public int read() {
- if (position >= document.getLength()) {
- return -1; // Read past end of document.
- }
- try {
- document.getText((int) position, 1, segment);
- position++;
- return segment.array[segment.offset];
- } catch (BadLocationException ble) {
- /* Should never happen?? */
- ble.printStackTrace();
- return -1;
- }
- }
-
- /**
- * Read array.length
characters from the beginning of the document into array
.
- *
- * @param array
- * The array to read characters into.
- * @return The number of characters read.
- */
- public int read(char array[]) {
- return read(array, 0, array.length);
- }
-
- /**
- * Reads characters into a portion of an array.
- *
- * @param cbuf
- * The destination buffer.
- * @param off
- * Offset at which to start storing characters.
- * @param len
- * Maximum number of characters to read.
- * @return The number of characters read, or -1
if the end of the stream (document) has been reached.
- */
- public int read(char cbuf[], int off, int len) {
- int k;
- if (position >= document.getLength()) {
- return -1; // Read past end of document.
- }
- k = len;
- if ((position + k) >= document.getLength())
- k = document.getLength() - (int) position;
- if (off + k >= cbuf.length)
- k = cbuf.length - off;
- try {
- document.getText((int) position, k, segment);
- position += k;
- System.arraycopy(segment.array, segment.offset,
- cbuf, off,
- k);
- return k;
- } catch (BadLocationException ble) {
- /* Should never happen ? */
- return -1;
- }
- }
-
- /**
- * Tells whether this reader is ready to be read without blocking for input. DocumentReader
will always
- * return true.
- *
- * @return true
if the next read operation will return without blocking.
- */
- public boolean ready() {
- return true;
- }
-
- /**
- * Resets the stream. If the stream has been marked, then attempt to reposition it at the mark. If the stream has
- * not been marked, then move it to the beginning of the document.
- */
- public void reset() {
- if (mark == -1) {
- position = 0;
- }
- else {
- position = mark;
- mark = -1;
- }
- }
-
- /**
- * Skips characters. This will not 'skip' past the end of the document.
- *
- * @param n
- * The number of characters to skip.
- * @return The number of characters actually skipped.
- */
- public long skip(long n) {
- if (position + n <= document.getLength()) {
- position += n;
- return n;
- }
- long temp = position;
- position = document.getLength();
- return document.getLength() - temp;
- }
-
- /**
- * Move to the specified position in the document. If pos
is greater than the document's length, the
- * stream's position is moved to the end of the document.
- *
- * @param pos
- * The position in the document to move to.
- */
- public void seek(long pos) {
- position = Math.min(pos, document.getLength());
- }
-
-}
\ No newline at end of file
+ /**
+ * The stream's position in the document.
+ */
+ private long position;
+
+ /**
+ * A remembered position in the document.
+ */
+ private long mark;
+
+ /**
+ * The document we're working on.
+ */
+ private Document document;
+
+ /**
+ * Used for fast character access.
+ */
+ private Segment segment;
+
+
+ /**
+ * Constructor.
+ *
+ * @param document The document we're 'reading'.
+ */
+ public DocumentReader(Document document) {
+ position = 0;
+ mark = -1;
+ this.document = document;
+ this.segment = new Segment();
+ }
+
+
+ /**
+ * This currently does nothing...
+ */
+ @Override
+ public void close() {
+ }
+
+
+ /**
+ * Marks the present position in the stream. Subsequent calls to
+ * reset()
will reposition the stream to this point.
+ *
+ * @param readAheadLimit Ignored.
+ */
+ @Override
+ public void mark(int readAheadLimit) {
+ mark = position;
+ }
+
+
+ /**
+ * Tells whether this reader supports the mark
operation.
+ * This always returns true
for DocumentReader
.
+ */
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+
+ /**
+ * Reads the single character at the current position in the document.
+ */
+ @Override
+ public int read() {
+ if(position>=document.getLength()) {
+ return -1; // Read past end of document.
+ }
+ try {
+ document.getText((int)position,1, segment);
+ position++;
+ return segment.array[segment.offset];
+ } catch (BadLocationException ble) {
+ /* Should never happen?? */
+ ble.printStackTrace();
+ return -1;
+ }
+ }
+
+
+ /**
+ * Read array.length
characters from the beginning
+ * of the document into array
.
+ *
+ * @param array The array to read characters into.
+ * @return The number of characters read.
+ */
+ @Override
+ public int read(char[] array) {
+ return read(array, 0, array.length);
+ }
+
+
+ /**
+ * Reads characters into a portion of an array.
+ *
+ * @param cbuf The destination buffer.
+ * @param off Offset at which to start storing characters.
+ * @param len Maximum number of characters to read.
+ * @return The number of characters read, or -1
if the
+ * end of the stream (document) has been reached.
+ */
+ @Override
+ public int read(char[] cbuf, int off, int len) {
+ int k;
+ if(position>=document.getLength()) {
+ return -1; // Read past end of document.
+ }
+ k = len;
+ if((position+k)>=document.getLength()) {
+ k = document.getLength() - (int)position;
+ }
+ if(off + k >= cbuf.length) {
+ k = cbuf.length - off;
+ }
+ try {
+ document.getText((int)position, k, segment);
+ position += k;
+ System.arraycopy(segment.array,segment.offset,
+ cbuf,off,
+ k);
+ return k;
+ } catch (BadLocationException ble) {
+ /* Should never happen ? */
+ return -1;
+ }
+ }
+
+
+ /**
+ * Tells whether this reader is ready to be read without
+ * blocking for input. DocumentReader
will
+ * always return true.
+ *
+ * @return true
if the next read operation will
+ * return without blocking.
+ */
+ @Override
+ public boolean ready() {
+ return true;
+ }
+
+
+ /**
+ * Resets the stream. If the stream has been marked, then attempt to
+ * reposition it at the mark. If the stream has not been marked, then
+ * move it to the beginning of the document.
+ */
+ @Override
+ public void reset() {
+ if(mark==-1) {
+ position = 0;
+ }
+ else {
+ position = mark;
+ mark = -1;
+ }
+ }
+
+
+ /**
+ * Skips characters. This will not 'skip' past the end of the document.
+ *
+ * @param n The number of characters to skip.
+ * @return The number of characters actually skipped.
+ */
+ @Override
+ public long skip(long n) {
+ if (position+n<=document.getLength()) {
+ position += n;
+ return n;
+ }
+ long temp = position;
+ position = document.getLength();
+ return document.getLength() - temp;
+ }
+
+
+ /**
+ * Move to the specified position in the document. If pos
+ * is greater than the document's length, the stream's position is moved
+ * to the end of the document.
+ *
+ * @param pos The position in the document to move to.
+ */
+ public void seek(long pos) {
+ position = Math.min(pos, document.getLength());
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/io/UnicodeReader.java b/tools/agent_debugger/src/main/java/org/fife/io/UnicodeReader.java
old mode 100644
new mode 100755
index f87e3e2ad..d3b8f4a3e
--- a/tools/agent_debugger/src/main/java/org/fife/io/UnicodeReader.java
+++ b/tools/agent_debugger/src/main/java/org/fife/io/UnicodeReader.java
@@ -3,272 +3,260 @@
*
* UnicodeReader.java - A reader for Unicode input streams that is capable of
* discerning which particular encoding is being used via the BOM.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.io;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.PushbackInputStream;
import java.io.Reader;
+
/**
- * A reader capable of identifying Unicode streams by their BOMs. This class will recognize the following encodings:
+ * A reader capable of identifying Unicode streams by their BOMs. This class
+ * will recognize the following encodings:
*
- *
- * For optimum performance, it is recommended that you wrap all instances of UnicodeReader
with a
- * java.io.BufferedReader
.
- *
- * - * This class is mostly ripped off from the workaround in the description of Java Bug 4508058. - * + * If the stream is not found to be any of the above, then a default encoding + * is used for reading. The user can specify this default encoding, or a system + * default will be used.
+ *
+ * For optimum performance, it is recommended that you wrap all instances of
+ * UnicodeReader
with a java.io.BufferedReader
.
+ *
+ * This class is mostly ripped off from the workaround in the description of
+ * Java Bug 4508058.
+ *
* @author Robert Futrell
* @version 0.9
*/
+@SuppressWarnings({ "checkstyle:magicnumber" })
public class UnicodeReader extends Reader {
- /**
- * The input stream from which we're really reading.
- */
- private InputStreamReader internalIn = null;
-
- /**
- * The encoding being used. We keep our own instead of using the string returned by
- * java.io.InputStreamReader
since that class does not return user-friendly names.
- */
- private String encoding;
-
- /**
- * The size of a BOM.
- */
- private static final int BOM_SIZE = 4;
-
- /**
- * This utility constructor is here because you will usually use a UnicodeReader
on files.
- *
- * Creates a reader using the encoding specified by the BOM in the file; if there is no recognized BOM, then a
- * system default encoding is used.
- *
- * @param file
- * The file from which you want to read.
- * @throws IOException
- * If an error occurs when checking for/reading the BOM.
- * @throws FileNotFoundException
- * If the file does not exist, is a directory, or cannot be opened for reading.
- * @throws SecurityException
- * If a security manager exists and its checkRead method denies read access to the file.
- */
- public UnicodeReader(String file) throws IOException,
- FileNotFoundException, SecurityException {
- this(new File(file));
- }
-
- /**
- * This utility constructor is here because you will usually use a UnicodeReader
on files.
- *
- * Creates a reader using the encoding specified by the BOM in the file; if there is no recognized BOM, then a
- * system default encoding is used.
- *
- * @param file
- * The file from which you want to read.
- * @throws IOException
- * If an error occurs when checking for/reading the BOM.
- * @throws FileNotFoundException
- * If the file does not exist, is a directory, or cannot be opened for reading.
- * @throws SecurityException
- * If a security manager exists and its checkRead method denies read access to the file.
- */
- public UnicodeReader(File file) throws IOException, FileNotFoundException,
- SecurityException {
- this(new FileInputStream(file));
- }
-
- /**
- * This utility constructor is here because you will usually use a UnicodeReader
on files.
- *
- * Creates a reader using the encoding specified by the BOM in the file; if there is no recognized BOM, then a
- * specified default encoding is used.
- *
- * @param file
- * The file from which you want to read.
- * @param defaultEncoding
- * The encoding to use if no BOM is found. If this value is null
, a system default is used.
- * @throws IOException
- * If an error occurs when checking for/reading the BOM.
- * @throws FileNotFoundException
- * If the file does not exist, is a directory, or cannot be opened for reading.
- * @throws SecurityException
- * If a security manager exists and its checkRead method denies read access to the file.
- */
- public UnicodeReader(File file, String defaultEncoding)
- throws IOException, FileNotFoundException,
- SecurityException {
- this(new FileInputStream(file), defaultEncoding);
- }
-
- /**
- * Creates a reader using the encoding specified by the BOM in the file; if there is no recognized BOM, then a
- * system default encoding is used.
- *
- * @param in
- * The input stream from which to read.
- * @throws IOException
- * If an error occurs when checking for/reading the BOM.
- */
- public UnicodeReader(InputStream in) throws IOException {
- this(in, null);
- }
-
- /**
- * Creates a reader using the encoding specified by the BOM in the file; if there is no recognized BOM, then
- * defaultEncoding
is used.
- *
- * @param in
- * The input stream from which to read.
- * @param defaultEncoding
- * The encoding to use if no recognized BOM is found. If this value is null
, a system
- * default is used.
- * @throws IOException
- * If an error occurs when checking for/reading the BOM.
- */
- public UnicodeReader(InputStream in, String defaultEncoding)
- throws IOException {
- init(in, defaultEncoding);
- }
-
- /**
- * Closes this reader.
- */
- public void close() throws IOException {
- internalIn.close();
- }
-
- /**
- * Returns the encoding being used to read this input stream (i.e., the encoding of the file). If a BOM was
- * recognized, then the specific Unicode type is returned; otherwise, either the default encoding passed into the
- * constructor or the system default is returned.
- *
- * @return The encoding of the stream.
- */
- public String getEncoding() {
- return encoding;
- }
-
- /**
- * Read-ahead four bytes and check for BOM marks. Extra bytes are unread back to the stream, only BOM bytes are
- * skipped.
- *
- * @param defaultEncoding
- * The encoding to use if no BOM was recognized. If this value is null
, then a system
- * default is used.
- * @throws IOException
- * If an error occurs when trying to read a BOM.
- */
- protected void init(InputStream in, String defaultEncoding)
- throws IOException {
-
- PushbackInputStream tempIn = new PushbackInputStream(in, BOM_SIZE);
-
- byte bom[] = new byte[BOM_SIZE];
- int n, unread;
- n = tempIn.read(bom, 0, bom.length);
-
- if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) &&
- (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
- encoding = "UTF-32BE";
- unread = n - 4;
- }
-
- else if (n == BOM_SIZE && // Last 2 bytes are 0; could be an empty UTF-16
- (bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) &&
- (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
- encoding = "UTF-32LE";
- unread = n - 4;
- }
-
- else if ((bom[0] == (byte) 0xEF) &&
- (bom[1] == (byte) 0xBB) &&
- (bom[2] == (byte) 0xBF)) {
- encoding = "UTF-8";
- unread = n - 3;
- }
-
- else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
- encoding = "UTF-16BE";
- unread = n - 2;
- }
-
- else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
- encoding = "UTF-16LE";
- unread = n - 2;
- }
-
- else {
- // Unicode BOM mark not found, unread all bytes
- encoding = defaultEncoding;
- unread = n;
- }
-
- if (unread > 0)
- tempIn.unread(bom, (n - unread), unread);
- else if (unread < -1)
- tempIn.unread(bom, 0, 0);
-
- // Use given encoding
- if (encoding == null) {
- internalIn = new InputStreamReader(tempIn);
- encoding = internalIn.getEncoding(); // Get the default.
- }
- else {
- internalIn = new InputStreamReader(tempIn, encoding);
- }
-
- }
-
- /**
- * Read characters into a portion of an array. This method will block until some input is available, an I/O error
- * occurs, or the end of the stream is reached.
- *
- * @param cbuf
- * The buffer into which to read.
- * @param off
- * The offset at which to start storing characters.
- * @param len
- * The maximum number of characters to read.
- *
- * @return The number of characters read, or -1
if the end of the stream has been reached.
- */
- public int read(char[] cbuf, int off, int len) throws IOException {
- return internalIn.read(cbuf, off, len);
- }
-
-}
\ No newline at end of file
+ /**
+ * The input stream from which we're really reading.
+ */
+ private InputStreamReader internalIn = null;
+
+ /**
+ * The encoding being used. We keep our own instead of using the string
+ * returned by java.io.InputStreamReader
since that class
+ * does not return user-friendly names.
+ */
+ private String encoding;
+
+ /**
+ * The size of a BOM.
+ */
+ private static final int BOM_SIZE = 4;
+
+
+ /**
+ * This utility constructor is here because you will usually use a
+ * UnicodeReader
on files.
+ * Creates a reader using the encoding specified by the BOM in the file;
+ * if there is no recognized BOM, then a system default encoding is used.
+ *
+ * @param file The file from which you want to read.
+ * @throws IOException If an error occurs when checking for/reading the
+ * BOM.
+ * @throws SecurityException If a security manager exists and its
+ * checkRead method denies read access to the file.
+ */
+ public UnicodeReader(String file) throws IOException {
+ this(new File(file));
+ }
+
+
+ /**
+ * This utility constructor is here because you will usually use a
+ * UnicodeReader
on files.
+ * Creates a reader using the encoding specified by the BOM in the file;
+ * if there is no recognized BOM, then a system default encoding is used.
+ *
+ * @param file The file from which you want to read.
+ * @throws IOException If an error occurs when checking for/reading the
+ * BOM.
+ * @throws SecurityException If a security manager exists and its
+ * checkRead method denies read access to the file.
+ */
+ public UnicodeReader(File file) throws IOException {
+ this(new FileInputStream(file));
+ }
+
+
+ /**
+ * This utility constructor is here because you will usually use a
+ * UnicodeReader
on files.
+ * Creates a reader using the encoding specified by the BOM in the file;
+ * if there is no recognized BOM, then a specified default encoding is
+ * used.
+ *
+ * @param file The file from which you want to read.
+ * @param defaultEncoding The encoding to use if no BOM is found. If
+ * this value is null
, a system default is used.
+ * @throws IOException If an error occurs when checking for/reading the
+ * BOM.
+ * @throws SecurityException If a security manager exists and its
+ * checkRead method denies read access to the file.
+ */
+ public UnicodeReader(File file, String defaultEncoding) throws IOException {
+ this(new FileInputStream(file), defaultEncoding);
+ }
+
+
+ /**
+ * Creates a reader using the encoding specified by the BOM in the file;
+ * if there is no recognized BOM, then a system default encoding is used.
+ *
+ * @param in The input stream from which to read.
+ * @throws IOException If an error occurs when checking for/reading the
+ * BOM.
+ */
+ public UnicodeReader(InputStream in) throws IOException {
+ this(in, null);
+ }
+
+
+ /**
+ * Creates a reader using the encoding specified by the BOM in the file;
+ * if there is no recognized BOM, then defaultEncoding
is
+ * used.
+ *
+ * @param in The input stream from which to read.
+ * @param defaultEncoding The encoding to use if no recognized BOM is
+ * found. If this value is null
, a system default
+ * is used.
+ * @throws IOException If an error occurs when checking for/reading the
+ * BOM.
+ */
+ public UnicodeReader(InputStream in, String defaultEncoding)
+ throws IOException {
+ init(in, defaultEncoding);
+ }
+
+
+ /**
+ * Closes this reader.
+ */
+ @Override
+ public void close() throws IOException {
+ internalIn.close();
+ }
+
+
+ /**
+ * Returns the encoding being used to read this input stream (i.e., the
+ * encoding of the file). If a BOM was recognized, then the specific
+ * Unicode type is returned; otherwise, either the default encoding passed
+ * into the constructor or the system default is returned.
+ *
+ * @return The encoding of the stream.
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+
+ /**
+ * Read-ahead four bytes and check for BOM marks. Extra bytes are
+ * unread back to the stream, only BOM bytes are skipped.
+ *
+ * @param defaultEncoding The encoding to use if no BOM was recognized. If
+ * this value is null
, then a system default is used.
+ * @throws IOException If an error occurs when trying to read a BOM.
+ */
+ protected void init(InputStream in, String defaultEncoding)
+ throws IOException {
+
+ PushbackInputStream tempIn = new PushbackInputStream(in, BOM_SIZE);
+
+ byte[] bom = new byte[BOM_SIZE];
+ int n, unread;
+ n = tempIn.read(bom, 0, bom.length);
+
+ if ((bom[0]==(byte)0x00) && (bom[1]==(byte)0x00) &&
+ (bom[2]==(byte)0xFE) && (bom[3]==(byte)0xFF)) {
+ encoding = "UTF-32BE";
+ unread = n - 4;
+ }
+
+ else if (n==BOM_SIZE && // Last 2 bytes are 0; could be an empty UTF-16
+ (bom[0]==(byte)0xFF) && (bom[1]==(byte)0xFE) &&
+ (bom[2]==(byte)0x00) && (bom[3]==(byte)0x00)) {
+ encoding = "UTF-32LE";
+ unread = n - 4;
+ }
+
+ else if ((bom[0]==(byte)0xEF) &&
+ (bom[1]==(byte)0xBB) &&
+ (bom[2]==(byte)0xBF)) {
+ encoding = "UTF-8";
+ unread = n - 3;
+ }
+
+ else if ((bom[0]==(byte)0xFE) && (bom[1] == (byte)0xFF)) {
+ encoding = "UTF-16BE";
+ unread = n - 2;
+ }
+
+ else if ((bom[0]==(byte)0xFF) && (bom[1]== (byte)0xFE)) {
+ encoding = "UTF-16LE";
+ unread = n - 2;
+ }
+
+ else {
+ // Unicode BOM mark not found, unread all bytes
+ encoding = defaultEncoding;
+ unread = n;
+ }
+
+ if (unread > 0) {
+ tempIn.unread(bom, (n - unread), unread);
+ }
+ else if (unread < -1) {
+ tempIn.unread(bom, 0, 0);
+ }
+
+ // Use given encoding
+ if (encoding == null) {
+ internalIn = new InputStreamReader(tempIn);
+ encoding = internalIn.getEncoding(); // Get the default.
+ }
+ else {
+ internalIn = new InputStreamReader(tempIn, encoding);
+ }
+
+ }
+
+
+ /**
+ * Read characters into a portion of an array. This method will block until
+ * some input is available, an I/O error occurs, or the end of the stream
+ * is reached.
+ *
+ * @param cbuf The buffer into which to read.
+ * @param off The offset at which to start storing characters.
+ * @param len The maximum number of characters to read.
+ *
+ * @return The number of characters read, or -1
if the end
+ * of the stream has been reached.
+ */
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ return internalIn.read(cbuf, off, len);
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/io/UnicodeWriter.java b/tools/agent_debugger/src/main/java/org/fife/io/UnicodeWriter.java
old mode 100644
new mode 100755
index 929b8a942..28bfd01bc
--- a/tools/agent_debugger/src/main/java/org/fife/io/UnicodeWriter.java
+++ b/tools/agent_debugger/src/main/java/org/fife/io/UnicodeWriter.java
@@ -2,23 +2,9 @@
* 09/24/2004
*
* UnicodeWriter.java - Writes Unicode output with the proper BOM.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.io;
@@ -27,243 +13,249 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
import java.io.Writer;
+
/**
- * Writes Unicode text to an output stream. If the specified encoding is a Unicode, then the text is preceeded by the
- * proper Unicode BOM. If it is any other encoding, this class behaves just like OutputStreamWriter
. This
- * class is here because Java's OutputStreamWriter
apparently doesn't believe in writing BOMs.
+ * Writes Unicode text to an output stream. If the specified encoding is a
+ * Unicode, then the text is preceded by the proper Unicode BOM. If it is any
+ * other encoding, this class behaves just like OutputStreamWriter
.
+ * This class is here because Java's OutputStreamWriter
apparently
+ * doesn't believe in writing BOMs.
*
- *
- * For optimum performance, it is recommended that you wrap all instances of
- *
+ * An abstract implementation of the
+ * {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should
+ * be overridden for every language for which you want to provide
+ * syntax highlighting.
+ *
* @see Token
- *
+ *
* @author Robert Futrell
* @version 0.2
*/
public abstract class AbstractTokenMaker extends TokenMakerBase {
- /**
- * Hash table of words to highlight and what token type they are. The keys are the words to highlight, and their
- * values are the token types, for example,
- *
- * See the
+ *
+ * See the
- *
- * All methods in this class are synchronized for thread safety, but as a best practice, you should probably only modify
- * the templates known to a
+ *
+ * All methods in this class are synchronized for thread safety, but as a
+ * best practice, you should probably only modify the templates known to a
+ *
+ *
+ * For more flexible boilerplate code insertion, consider using the
+ *
+ * TemplateCompletion class in the
+ * AutoComplete
+ * add-on library.
+ *
* @author Robert Futrell
- * @version 0.1
+ * @version 1.0
*/
public class CodeTemplateManager {
- private int maxTemplateIDLength;
- private List
- *
- * The current implementation paints as follows:
- *
- *
- * A problem with this implementation is that FontMetrics.charsWidth() is still used to calculate the width of a group
- * of chars painted. Thus, the group of characters will be painted with the rendering hints specified, but the following
- * tab (or group of characters if the current group was the end of a token) will not necessarily be painted at the
- * proper x-coordinate (as FontMetrics.charsWidth() returns an
- * . This method will return the document position "closest" to the x-coordinate (i.e., if they click on the
- * "right-half" of the
- *
- * @param numChars
- * The number of characters for which to get the width.
- * @param textArea
- * The text area in which this token is being painted.
- * @param e
- * How to expand tabs. This value cannot be
- *
- * NOTE: This class should only be used by {@link TokenMaker}; nobody else needs it!
- *
+ * This class generates tokens for a {@link TokenMaker}. This class is here
+ * because it reuses tokens when they aren't needed anymore to prevent
+ * This class doesn't actually create new tokens every time
+ *
+ *
+ * NOTE: This class should only be used by {@link TokenMaker}; nobody else
+ * needs it!
+ *
* @author Robert Futrell
* @version 0.1
*/
class DefaultTokenFactory implements TokenFactory {
- private int size;
- private int increment;
- private Token[] tokenList;
- private int currentFreeToken;
-
- protected static final int DEFAULT_START_SIZE = 30;
- protected static final int DEFAULT_INCREMENT = 10;
-
- /**
- * Cosnstructor.
- */
- public DefaultTokenFactory() {
- this(DEFAULT_START_SIZE, DEFAULT_INCREMENT);
- }
-
- /**
- * Constructor.
- *
- * @param size
- * The initial number of tokens in this factory.
- * @param increment
- * How many tokens to increment by when the stack gets empty.
- */
- public DefaultTokenFactory(int size, int increment) {
-
- this.size = size;
- this.increment = increment;
- this.currentFreeToken = 0;
-
- // Give us some tokens to initially work with.
- tokenList = new Token[size];
- for (int i = 0; i < size; i++)
- tokenList[i] = createInternalUseOnlyToken();
-
- }
-
- /**
- * Adds tokens to the internal token list. This is called whenever a request is made and no more tokens are
- * available.
- */
- private final void augmentTokenList() {
- Token[] temp = new Token[size + increment];
- System.arraycopy(tokenList, 0, temp, 0, size);
- size += increment;
- tokenList = temp;
- for (int i = 0; i < increment; i++) {
- tokenList[size - i - 1] = createInternalUseOnlyToken();
- }
- // System.err.println("... size up to: " + size);
- }
-
- /**
- * Creates a token for use internally by this token factory. This method should NOT be called externally; only by
- * this class and possibly subclasses.
- *
- * @return A token to add to this token factory's internal stack. If a subclass wants to produce a stack of a token
- * other than {@link DefaultToken}, then this method can be overridden to return a new instance of the
- * desired token type.
- */
- protected Token createInternalUseOnlyToken() {
- return new DefaultToken();
- }
-
- /**
- * Returns a null token.
- *
- * @return A null token.
- */
- public Token createToken() {
- Token token = tokenList[currentFreeToken];
- token.text = null;
- token.type = Token.NULL;
- token.offset = -1;
- token.setNextToken(null);
- currentFreeToken++;
- if (currentFreeToken == size)
- augmentTokenList();
- return token;
- }
-
- /**
- * Returns a token.
- *
- * @param line
- * The segment from which to get the token's text.
- * @param beg
- * The starting offset of the token's text in the segment.
- * @param end
- * The ending offset of the token's text in the segment.
- * @param startOffset
- * The offset in the document of the token.
- * @param type
- * The type of token.
- * @return The token.
- */
- public Token createToken(final Segment line, final int beg,
- final int end, final int startOffset, final int type) {
- return createToken(line.array, beg, end, startOffset, type);
- }
-
- /**
- * Returns a token.
- *
- * @param line
- * The segment from which to get the token's text.
- * @param beg
- * The starting offset of the token's text in the segment.
- * @param end
- * The ending offset of the token's text in the segment.
- * @param startOffset
- * The offset in the document of the token.
- * @param type
- * The type of token.
- * @return The token.
- */
- public Token createToken(final char[] line, final int beg,
- final int end, final int startOffset, final int type) {
- Token token = tokenList[currentFreeToken];
- token.set(line, beg, end, startOffset, type);
- currentFreeToken++;
- if (currentFreeToken == size)
- augmentTokenList();
- return token;
- }
-
- /**
- * Resets the state of this token maker. This method should be called by the
+ *
+ *
+ *
* An
+ *
+ * Currently, this method only checks for tag names on the same line as
+ * the caret, for simplicity. In the future it could check prior lines
+ * until the tag name is found.
+ *
+ * @param textArea The text area.
+ * @param occurrenceMarker The occurrence marker.
+ * @return The token to mark occurrences of. Note that, if the
+ * specified occurrence marker identifies tokens other than
+ * tag names, these other element types may be returned.
+ */
+ public static final Token getTagNameTokenForCaretOffset(
+ RSyntaxTextArea textArea, OccurrenceMarker occurrenceMarker) {
+
+ // Get the tag name token.
+ // For now, we only check for tags on the current line, for simplicity.
+
+ int dot = textArea.getCaretPosition();
+ Token t = textArea.getTokenListForLine(textArea.getCaretLineNumber());
+ Token toMark = null;
+
+ while (t!=null && t.isPaintable()) {
+ if (t.getType()==Token.MARKUP_TAG_NAME) {
+ toMark = t;
+ }
+ // Check for the token containing the caret before checking
+ // if it's the close token.
+ if (t.getEndOffset()==dot || t.containsPosition(dot)) {
+ // Some languages, like PHP, mark functions/variables (PHP,
+ // JavaScirpt) as well as HTML tags.
+ if (occurrenceMarker.isValidType(textArea, t) &&
+ t.getType()!=Token.MARKUP_TAG_NAME) {
+ return t;
+ }
+ if (t.containsPosition(dot)) {
+ break;
+ }
+ }
+ if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
+ if (t.isSingleChar('>') || t.is(TAG_SELF_CLOSE)) {
+ toMark = null;
+ }
+ }
+ t = t.getNextToken();
+ }
+
+ return toMark;
+
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Token getTokenToMark(RSyntaxTextArea textArea) {
+ return getTagNameTokenForCaretOffset(textArea, this);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isValidType(RSyntaxTextArea textArea, Token t) {
+ return textArea.getMarkOccurrencesOfTokenType(t.getType());
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void markOccurrences(RSyntaxDocument doc, Token t,
+ RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
+
+ if (t.getType()!=Token.MARKUP_TAG_NAME) {
+ DefaultOccurrenceMarker.markOccurrencesOfToken(doc, t, h, p);
+ return;
+ }
+
+ String lexemeStr = t.getLexeme();
+ char[] lexeme = lexemeStr.toCharArray();
+ lexemeStr = lexemeStr.toLowerCase();
+ int tokenOffs = t.getOffset();
+ Element root = doc.getDefaultRootElement();
+ int lineCount = root.getElementCount();
+ int curLine = root.getElementIndex(t.getOffset());
+ int depth = 0;
+
+ // For now, we only check for tags on the current line, for
+ // simplicity. Tags spanning multiple lines aren't common anyway.
+ boolean found = false;
+ boolean forward = true;
+ t = doc.getTokenListForLine(curLine);
+ while (t!=null && t.isPaintable()) {
+ if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
+ if (t.isSingleChar('<') && t.getOffset()+1==tokenOffs) {
+ // Don't try to match a tag that is optionally closed (or
+ // closing is forbidden entirely).
+ if (TAGS_REQUIRING_CLOSING.contains(lexemeStr)) {
+ found = true;
+ }
+ break;
+ }
+ else if (t.is(CLOSE_TAG_START) && t.getOffset()+2==tokenOffs) {
+ // Searching backward, we assume we can find the opening
+ // tag. Don't really care if it's valid or not.
+ found = true;
+ forward = false;
+ break;
+ }
+ }
+ t = t.getNextToken();
+ }
+
+ if (!found) {
+ return;
+ }
+
+ if (forward) {
+
+ t = t.getNextToken().getNextToken();
+
+ do {
+
+ while (t!=null && t.isPaintable()) {
+ if (t.getType()==Token.MARKUP_TAG_DELIMITER) {
+ if (t.is(CLOSE_TAG_START)) {
+ Token match = t.getNextToken();
+ if (match!=null && match.is(lexeme)) {
+ if (depth>0) {
+ depth--;
+ }
+ else {
+ try {
+ int end = match.getOffset() + match.length();
+ h.addMarkedOccurrenceHighlight(match.getOffset(), end, p);
+ end = tokenOffs + match.length();
+ h.addMarkedOccurrenceHighlight(tokenOffs, end, p);
+ } catch (BadLocationException ble) {
+ ble.printStackTrace(); // Never happens
+ }
+ return; // We're done!
+ }
+ }
+ }
+ else if (t.isSingleChar('<')) {
+ t = t.getNextToken();
+ if (t!=null && t.is(lexeme)) {
+ depth++;
+ }
+ }
+ }
+ t = t==null ? null : t.getNextToken();
+ }
+
+ if (++curLine
+ *
+ * This interface is typically used by applications providing advanced support
+ * for programming languages, such as IDEs. For example, an implementation of
+ * this class could identify the token under the mouse position as a "variable,"
+ * and the hyperlink returned would select the variable's declaration in the
+ * document.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public interface LinkGenerator {
+
+
+ /**
+ * If a region of text under the mouse position should be considered a
+ * hyperlink, a result object is returned. This object describes what
+ * region of text is the link, and what action to perform if the link is
+ * clicked.
+ *
+ * @param textArea The text component.
+ * @param offs The offset in the document under the mouse position.
+ * @return The link information, or
+ *
+ * Callers should not call this method directly, but should rather prefer
+ * {@link #doMarkOccurrences()} to mark occurrences.
+ *
+ * @param e The event.
+ * @see #doMarkOccurrences()
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ // Don't do anything if they are selecting text.
+ Caret c = textArea.getCaret();
+ if (c.getDot()!=c.getMark()) {
+ return;
+ }
+
+ RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
+ OccurrenceMarker occurrenceMarker = doc.getOccurrenceMarker();
+ boolean occurrencesChanged = false;
+
+ if (occurrenceMarker!=null) {
+
+ doc.readLock();
+ try {
+
+ Token t = occurrenceMarker.getTokenToMark(textArea);
+
+ if (t!=null && occurrenceMarker.isValidType(textArea, t) &&
+ !RSyntaxUtilities.isNonWordChar(t)) {
+ clear();
+ RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
+ textArea.getHighlighter();
+ occurrenceMarker.markOccurrences(doc, t, h, p);
+ //textArea.repaint();
+ // TODO: Do a textArea.repaint() instead of repainting each
+ // marker as it's added if count is huge
+ occurrencesChanged = true;
+ } else {
+ clear();
+ }
+
+ } finally {
+ doc.readUnlock();
+ //time = System.currentTimeMillis() - time;
+ //System.out.println("MarkOccurrencesSupport took: " + time + " ms");
+ }
+
+ }
+
+ if (occurrencesChanged) {
+ textArea.fireMarkedOccurrencesChanged();
+ }
+
+ }
+
+
+ /**
+ * Called when the caret moves in the text area.
+ *
+ * @param e The event.
+ */
+ @Override
+ public void caretUpdate(CaretEvent e) {
+ timer.restart();
+ }
+
+
+ /**
+ * Removes all highlights added to the text area by this listener.
+ */
+ void clear() {
+ if (textArea!=null) {
+ RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
+ textArea.getHighlighter();
+ h.clearMarkOccurrencesHighlights();
+ }
+ }
+
+
+ /**
+ * Immediately marks all occurrences of the token at the current caret
+ * position.
+ */
+ public void doMarkOccurrences() {
+ timer.stop();
+ actionPerformed(null);
+ }
+
+
+ /**
+ * Returns the color being used to mark occurrences.
+ *
+ * @return The color being used.
+ * @see #setColor(Color)
+ */
+ public Color getColor() {
+ return (Color)p.getPaint();
+ }
+
+
+ /**
+ * Returns the delay, in milliseconds.
+ *
+ * @return The delay.
+ * @see #setDelay(int)
+ */
+ public int getDelay() {
+ return timer.getDelay();
+ }
+
+
+ /**
+ * Returns whether a border is painted around marked occurrences.
+ *
+ * @return Whether a border is painted.
+ * @see #setPaintBorder(boolean)
+ * @see #getColor()
+ */
+ public boolean getPaintBorder() {
+ return p.getPaintBorder();
+ }
+
+
+ /**
+ * Installs this listener on a text area. If it is already installed on
+ * another text area, it is uninstalled first.
+ *
+ * @param textArea The text area to install on.
+ */
+ public void install(RSyntaxTextArea textArea) {
+ if (this.textArea!=null) {
+ uninstall();
+ }
+ this.textArea = textArea;
+ textArea.addCaretListener(this);
+ if (textArea.getMarkOccurrencesColor()!=null) {
+ setColor(textArea.getMarkOccurrencesColor());
+ }
+ }
+
+
+ /**
+ * Sets the color to use when marking occurrences.
+ *
+ * @param color The color to use.
+ * @see #getColor()
+ * @see #setPaintBorder(boolean)
+ */
+ public void setColor(Color color) {
+ p.setPaint(color);
+ if (textArea!=null) {
+ clear();
+ caretUpdate(null); // Force a highlight repaint.
+ }
+ }
+
+
+ /**
+ * Sets the delay between the last caret position change and when the
+ * text is scanned for matching identifiers. A delay is needed to prevent
+ * repeated scanning while the user is typing.
+ *
+ * @param delay The new delay.
+ * @see #getDelay()
+ */
+ public void setDelay(int delay) {
+ timer.setInitialDelay(delay);
+ }
+
+
+ /**
+ * Toggles whether a border is painted around marked highlights.
+ *
+ * @param paint Whether to paint a border.
+ * @see #getPaintBorder()
+ * @see #setColor(Color)
+ */
+ public void setPaintBorder(boolean paint) {
+ if (paint!=p.getPaintBorder()) {
+ p.setPaintBorder(paint);
+ if (textArea!=null) {
+ textArea.repaint();
+ }
+ }
+ }
+
+
+ /**
+ * Uninstalls this listener from the current text area. Does nothing if
+ * it not currently installed on any text area.
+ *
+ * @see #install(RSyntaxTextArea)
+ */
+ public void uninstall() {
+ if (textArea!=null) {
+ clear();
+ textArea.removeCaretListener(this);
+ }
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MatchedBracketPopup.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MatchedBracketPopup.java
new file mode 100755
index 000000000..a40748d44
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MatchedBracketPopup.java
@@ -0,0 +1,245 @@
+/*
+ * 07/03/2016
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Point;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JWindow;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.text.BadLocationException;
+
+import org.fife.ui.rsyntaxtextarea.focusabletip.TipUtil;
+
+
+/**
+ * A tool tip-like popup that shows the line of code containing the bracket
+ * matched to that at the caret position, if it is scrolled out of the user's
+ * viewport.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+class MatchedBracketPopup extends JWindow {
+
+ private RSyntaxTextArea textArea;
+
+ private transient Listener listener;
+
+ private static final int LEFT_EMPTY_BORDER = 5;
+
+
+ MatchedBracketPopup(Window parent, RSyntaxTextArea textArea, int
+ offsToRender) {
+
+ super(parent);
+ this.textArea = textArea;
+ JPanel cp = new JPanel(new BorderLayout());
+ cp.setBorder(BorderFactory.createCompoundBorder(
+ TipUtil.getToolTipBorder(),
+ BorderFactory.createEmptyBorder(2, LEFT_EMPTY_BORDER, 5, 5)));
+ cp.setBackground(TipUtil.getToolTipBackground());
+ setContentPane(cp);
+
+ cp.add(new JLabel(getText(offsToRender)));
+
+ installKeyBindings();
+ listener = new Listener();
+ setLocation();
+
+ }
+
+
+ /**
+ * Overridden to ensure this popup stays in a specific size range.
+ */
+ @Override
+ public Dimension getPreferredSize() {
+ Dimension size = super.getPreferredSize();
+ if (size!=null) {
+ size.width = Math.min(size.width, 800);
+ }
+ return size;
+ }
+
+
+ private String getText(int offsToRender) {
+
+ int line = 0;
+ try {
+ line = textArea.getLineOfOffset(offsToRender);
+ } catch (BadLocationException ble) {
+ ble.printStackTrace(); // Never happens
+ return null;
+ }
+
+ int lastLine = line + 1;
+
+ // Render prior line if the open brace line has no other text on it
+ if (line > 0) {
+ try {
+ int startOffs = textArea.getLineStartOffset(line);
+ int length = textArea.getLineEndOffset(line) - startOffs;
+ String text = textArea.getText(startOffs, length);
+ if (text.trim().length() == 1) {
+ line--;
+ }
+ } catch (BadLocationException ble) {
+ UIManager.getLookAndFeel().provideErrorFeedback(textArea);
+ ble.printStackTrace();
+ }
+ }
+
+ Font font = textArea.getFontForTokenType(TokenTypes.IDENTIFIER);
+ StringBuilder sb = new StringBuilder("");
+ sb.append("
- *
- * This method can be useful when a
+ *
+ * This method can be useful when a
- *
- * This method is quicker than using traditional
+ *
+ * This method is quicker than using traditional
+ *
+ *
+ * This method is quicker than using traditional
+ *
- *
- * Instances of
- *
- * To change the language being syntax highlighted at any time, you merely have to call {@link #setSyntaxStyle}. Other
- * than that, this document can be treated like any other save one caveat: all
+ *
+ * Instances of
+ *
+ * To change the language being syntax highlighted at any time, you merely have
+ * to call {@link #setSyntaxStyle}. Other than that, this document can be
+ * treated like any other save one caveat: all
- * The syntax highlighting stuff has to be here instead of in
- * The syntax-highlighting updates need to be done here (as opposed to an override of
- * Now that the text is actually inserted into the content and element structure, we can update our token elements
- * and "last tokens on lines" structure.
- *
- * @param chng
- * The change that occurred.
- * @see #removeUpdate
- */
- protected void fireRemoveUpdate(DocumentEvent chng) {
-
- Element lineMap = getDefaultRootElement();
- int numLines = lineMap.getElementCount();
-
- DocumentEvent.ElementChange change = chng.getChange(lineMap);
- Element[] removed = change == null ? null : change.getChildrenRemoved();
-
- // If entire lines were removed...
- if (removed != null && removed.length > 0) {
-
- int line = change.getIndex(); // First line entirely removed.
- int previousLine = line - 1; // Line before that.
- int previousTokenType = (previousLine > -1 ?
- lastTokensOnLines.get(previousLine) : Token.NULL);
-
- Element[] added = change.getChildrenAdded();
- int numAdded = added == null ? 0 : added.length;
-
- // Remove the cached last-token values for the removed lines.
- int endBefore = line + removed.length - numAdded;
- // System.err.println("... removing lines: " + line + " - " + (endBefore-1));
- // System.err.println("... added: " + numAdded + ", removed: " + removed.length);
-
- lastTokensOnLines.removeRange(line, endBefore); // Removing values for lines [line-(endBefore-1)].
- // System.err.println("--------- lastTokensOnLines.size() == " + lastTokensOnLines.getSize());
-
- // Update last tokens for lines below until they've stopped changing.
- updateLastTokensBelow(line, numLines, previousTokenType);
-
- } // End of if (removed!=null && removed.size()>0).
-
- // Otherwise, text was removed from just one line...
- else {
-
- int line = lineMap.getElementIndex(chng.getOffset());
- if (line >= lastTokensOnLines.getSize())
- return; // If we're editing the last line in a document...
-
- int previousLine = line - 1;
- int previousTokenType = (previousLine > -1 ?
- lastTokensOnLines.get(previousLine) : Token.NULL);
- // System.err.println("previousTokenType for line : " + previousLine + " is " + previousTokenType);
- // Update last tokens for lines below until they've stopped changing.
- updateLastTokensBelow(line, numLines, previousTokenType);
-
- }
-
- // Let all of our listeners know about the removal.
- super.fireRemoveUpdate(chng);
-
- }
-
- /**
- * Returns whether closing markup tags should be automatically completed. This method only returns
- *
- * This is called internally whenever the syntax style changes.
- */
- protected void updateSyntaxHighlightingInformation() {
-
- // Reinitialize the "last token on each line" array. Note that since
- // the actual text in the document isn't changing, the number of lines
- // is the same.
- Element map = getDefaultRootElement();
- int numLines = map.getElementCount();
- int lastTokenType = Token.NULL;
- for (int i = 0; i < numLines; i++) {
- setSharedSegment(i);
- lastTokenType = tokenMaker.getLastTokenTypeOnLine(s, lastTokenType);
- lastTokensOnLines.set(i, lastTokenType);
- }
-
- // Let everybody know that syntax styles have (probably) changed.
- fireChangedUpdate(new DefaultDocumentEvent(
- 0, numLines - 1, DocumentEvent.EventType.CHANGE));
-
- }
-
- /**
- * Document content that provides access to individual characters.
- *
- * @author Robert Futrell
- * @version 1.0
- */
- private static class RGapContent extends GapContent {
-
- public RGapContent() {
- }
-
- public char charAt(int offset) throws BadLocationException {
- if (offset < 0 || offset >= length()) {
- throw new BadLocationException("Invalid offset", offset);
- }
- int g0 = getGapStart();
- char[] array = (char[]) getArray();
- if (offset < g0) { // below gap
- return array[offset];
- }
- return array[getGapEnd() + offset - g0]; // above gap
- }
-
- }
-
-}
\ No newline at end of file
+public class RSyntaxDocument extends RDocument implements Iterable
+ * The syntax highlighting stuff has to be here instead of in
+ *
+ * The syntax-highlighting updates need to be done here (as opposed to
+ * an override of
+ * Now that the text is actually inserted into the content and element
+ * structure, we can update our token elements and "last tokens on
+ * lines" structure.
+ *
+ * @param chng The change that occurred.
+ * @see #removeUpdate
+ */
+ @Override
+ protected void fireRemoveUpdate(DocumentEvent chng) {
+
+ cachedTokenList = null;
+ Element lineMap = getDefaultRootElement();
+ int numLines = lineMap.getElementCount();
+
+ DocumentEvent.ElementChange change = chng.getChange(lineMap);
+ Element[] removed = change==null ? null : change.getChildrenRemoved();
+
+ // If entire lines were removed...
+ if (removed!=null && removed.length>0) {
+
+ int line = change.getIndex(); // First line entirely removed.
+ int previousLine = line - 1; // Line before that.
+ int previousTokenType = (previousLine>-1 ?
+ lastTokensOnLines.get(previousLine) : Token.NULL);
+
+ Element[] added = change.getChildrenAdded();
+ int numAdded = added==null ? 0 : added.length;
+
+ // Remove the cached last-token values for the removed lines.
+ int endBefore = line + removed.length - numAdded;
+ //System.err.println("... removing lines: " + line + " - " + (endBefore-1));
+ //System.err.println("... added: " + numAdded + ", removed: " + removed.length);
+
+ lastTokensOnLines.removeRange(line, endBefore); // Removing values for lines [line-(endBefore-1)].
+ //System.err.println("--------- lastTokensOnLines.size() == " + lastTokensOnLines.getSize());
+
+ // Update last tokens for lines below until they've stopped changing.
+ updateLastTokensBelow(line, numLines, previousTokenType);
+
+ } // End of if (removed!=null && removed.size()>0).
+
+ // Otherwise, text was removed from just one line...
+ else {
+
+ int line = lineMap.getElementIndex(chng.getOffset());
+ if (line>=lastTokensOnLines.getSize()) {
+ return; // If we're editing the last line in a document...
+ }
+
+ int previousLine = line - 1;
+ int previousTokenType = (previousLine>-1 ?
+ lastTokensOnLines.get(previousLine) : Token.NULL);
+ //System.err.println("previousTokenType for line : " + previousLine + " is " + previousTokenType);
+ // Update last tokens for lines below until they've stopped changing.
+ updateLastTokensBelow(line, numLines, previousTokenType);
+
+ }
+
+ // Let all of our listeners know about the removal.
+ super.fireRemoveUpdate(chng);
+
+ }
+
+
+ /**
+ * Returns the closest {@link TokenTypes "standard" token type} for a given
+ * "internal" token type (e.g. one whose value is
+ *
+ * The
+ *
+ * This is called internally whenever the syntax style changes.
+ */
+ private void updateSyntaxHighlightingInformation() {
+
+ // Reinitialize the "last token on each line" array. Note that since
+ // the actual text in the document isn't changing, the number of lines
+ // is the same.
+ Element map = getDefaultRootElement();
+ int numLines = map.getElementCount();
+ int lastTokenType = Token.NULL;
+ for (int i=0; i
- *
- * This method can be useful when a
- *
- * Note that basic users of
- *
- * This method fires a property change event of type {@link #CLOSE_CURLY_BRACES_PROPERTY}.
- *
- * @param close
- * Whether curly braces should be automatically closed.
- * @see #getCloseCurlyBraces()
- */
- public void setCloseCurlyBraces(boolean close) {
- if (close != closeCurlyBraces) {
- closeCurlyBraces = close;
- firePropertyChange(CLOSE_CURLY_BRACES_PROPERTY, !close, close);
- }
- }
-
- /**
- * Sets whether closing markup tags should be automatically completed when "
- *
- * This method fires a property change event of type {@link #CLOSE_MARKUP_TAGS_PROPERTY}.
- *
- * @param close
- * Whether closing markup tags should be automatically completed.
- * @see #getCloseMarkupTags()
- */
- public void setCloseMarkupTags(boolean close) {
- if (close != closeMarkupTags) {
- closeMarkupTags = close;
- firePropertyChange(CLOSE_MARKUP_TAGS_PROPERTY, !close, close);
- }
- }
-
- /**
- * Sets the document used by this text area. This is overridden so that only instances of {@link RSyntaxDocument}
- * are accepted; for all others, an exception will be thrown.
- *
- * @param document
- * The new document for this text area.
- * @throws IllegalArgumentException
- * If the document is not an
- *
- * This method fires a property change event of type {@link #SYNTAX_SCHEME_PROPERTY}.
- *
- * @param scheme
- * The instance of
- *
- * Templates are a set of "shorthand identifiers" that you can configure so that you only have to type a short
- * identifier (such as "forb") to insert a larger amount of code into the document (such as:
- *
- *
- *
+ *
+ * This method can be useful when a
+ *
+ * For more flexible boilerplate code insertion, consider using the
+ *
+ * TemplateCompletion class in the
+ * AutoComplete
+ * add-on library.
+ *
+ * @return Whether templates are enabled.
+ * @see #saveTemplates()
+ * @see #setTemplateDirectory(String)
+ * @see #setTemplatesEnabled(boolean)
+ */
+ public static synchronized boolean getTemplatesEnabled() {
+ return templatesEnabled;
+ }
+
+ private byte[] getTextAsRtf(int start, int end) {
+
+ // Create the RTF selection.
+ RtfGenerator gen = new RtfGenerator(getBackground());
+ Token tokenList = getTokenListFor(start, end);
+ for (Token t = tokenList; t != null; t = t.getNextToken()) {
+ if (t.isPaintable()) {
+ if (t.length() == 1 && t.charAt(0) == '\n') {
+ gen.appendNewline();
+ } else {
+ Font font = getFontForTokenType(t.getType());
+ Color bg = getBackgroundForToken(t);
+ boolean underline = getUnderlineForToken(t);
+ // Small optimization - don't print fg color if this
+ // is a whitespace color. Saves on RTF size.
+ if (t.isWhitespace()) {
+ gen.appendToDocNoFG(t.getLexeme(), font, bg, underline);
+ } else {
+ Color fg = getForegroundForToken(t);
+ gen.appendToDoc(t.getLexeme(), font, fg, bg, underline);
+ }
+ }
+ }
+ }
+
+ // RTF text is 7-bit ASCII so this should cover us
+ return gen.getRtf().getBytes(StandardCharsets.UTF_8);
+ }
+
+
+ /**
+ * Returns a token list for the given range in the document.
+ *
+ * @param startOffs The starting offset in the document.
+ * @param endOffs The end offset in the document.
+ * @return The first token in the token list.
+ */
+ public Token getTokenListFor(int startOffs, int endOffs) {
+
+ TokenImpl tokenList = null;
+ TokenImpl lastToken = null;
+
+ Element map = getDocument().getDefaultRootElement();
+ int startLine = map.getElementIndex(startOffs);
+ int endLine = map.getElementIndex(endOffs);
+
+ for (int line=startLine; line<=endLine; line++) {
+ TokenImpl t = (TokenImpl)getTokenListForLine(line);
+ t = cloneTokenList(t);
+ if (tokenList==null) {
+ tokenList = t;
+ lastToken = tokenList;
+ }
+ else {
+ lastToken.setNextToken(t);
+ }
+ while (lastToken.getNextToken()!=null &&
+ lastToken.getNextToken().isPaintable()) {
+ lastToken = (TokenImpl)lastToken.getNextToken();
+ }
+ if (line
+ *
+ * Note that basic users of
+ *
+ * This method fires a property change event of type
+ * {@link #CLOSE_CURLY_BRACES_PROPERTY}.
+ *
+ * @param close Whether curly braces should be automatically closed.
+ * @see #getCloseCurlyBraces()
+ */
+ public void setCloseCurlyBraces(boolean close) {
+ if (close!=closeCurlyBraces) {
+ closeCurlyBraces = close;
+ firePropertyChange(CLOSE_CURLY_BRACES_PROPERTY, !close, close);
+ }
+ }
+
+
+ /**
+ * Sets whether closing markup tags should be automatically completed
+ * when "
+ *
+ * This method fires a property change event of type
+ * {@link #CLOSE_MARKUP_TAGS_PROPERTY}.
+ *
+ * @param close Whether closing markup tags should be automatically
+ * completed.
+ * @see #getCloseMarkupTags()
+ */
+ public void setCloseMarkupTags(boolean close) {
+ if (close!=closeMarkupTags) {
+ closeMarkupTags = close;
+ firePropertyChange(CLOSE_MARKUP_TAGS_PROPERTY, !close, close);
+ }
+ }
+
+
+ /**
+ * Sets whether code folding is enabled. Note that only certain
+ * languages will support code folding out of the box. Those languages
+ * which do not support folding will ignore this property.
+ * This method fires a property change event of type
+ * {@link #CODE_FOLDING_PROPERTY}.
+ *
+ * @param enabled Whether code folding should be enabled.
+ * @see #isCodeFoldingEnabled()
+ */
+ public void setCodeFoldingEnabled(boolean enabled) {
+ if (enabled!=foldManager.isCodeFoldingEnabled()) {
+ foldManager.setCodeFoldingEnabled(enabled);
+ firePropertyChange(CODE_FOLDING_PROPERTY, !enabled, enabled);
+ }
+ }
+
+
+ /**
+ * Sets anti-aliasing to whatever the user's desktop value is.
+ *
+ * @see #getAntiAliasingEnabled()
+ */
+ private void setDefaultAntiAliasingState() {
+
+ // Most accurate technique, but not available on all OSes.
+ aaHints = RSyntaxUtilities.getDesktopAntiAliasHints();
+ if (aaHints==null) {
+
+ Map
+ *
+ * Note that this value will be ignored if
+ * {@link #setHyperlinksEnabled(boolean)} is called and set to
+ * {@code false}. If you wish to disable hyperlinks, use that
+ * method rather than changing this mask value.
+ *
+ * @param mask The mask to use. This should be some bitwise combination of
+ * {@link InputEvent#CTRL_DOWN_MASK},
+ * {@link InputEvent#ALT_DOWN_MASK},
+ * {@link InputEvent#SHIFT_DOWN_MASK} or
+ * {@link InputEvent#META_DOWN_MASK}.
+ * For invalid values, behavior is undefined.
+ * @see InputEvent
+ */
+ public void setLinkScanningMask(int mask) {
+ mask &= (InputEvent.CTRL_DOWN_MASK|InputEvent.META_DOWN_MASK|
+ InputEvent.ALT_DOWN_MASK|InputEvent.SHIFT_DOWN_MASK);
+ if (mask==0) {
+ throw new IllegalArgumentException("mask argument should be " +
+ "some combination of InputEvent.*_DOWN_MASK fields");
+ }
+ linkScanningMask = mask;
+ }
+
+
+ /**
+ * Toggles 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()
+ * @see #setMarkOccurrencesColor(Color)
+ */
+ public void setMarkOccurrences(boolean markOccurrences) {
+ if (markOccurrences) {
+ if (markOccurrencesSupport==null) {
+ markOccurrencesSupport = new MarkOccurrencesSupport();
+ markOccurrencesSupport.install(this);
+ firePropertyChange(MARK_OCCURRENCES_PROPERTY, false, true);
+ }
+ }
+ else {
+ if (markOccurrencesSupport!=null) {
+ markOccurrencesSupport.uninstall();
+ markOccurrencesSupport = null;
+ firePropertyChange(MARK_OCCURRENCES_PROPERTY, true, false);
+ }
+ }
+ }
+
+
+ /**
+ * Sets the "mark occurrences" color.
+ *
+ * @param color The new color. This cannot be
+ *
+ * This method fires a property change event of type
+ * {@link #PAINT_MATCHED_BRACKET_PAIR_PROPERTY}.
+ *
+ * @param paintPair Whether both brackets in a bracket pair should be
+ * highlighted when bracket matching is enabled.
+ * @see #getPaintMatchedBracketPair()
+ * @see #isBracketMatchingEnabled()
+ * @see #setBracketMatchingEnabled(boolean)
+ */
+ public void setPaintMatchedBracketPair(boolean paintPair) {
+ if (paintPair!=paintMatchedBracketPair) {
+ paintMatchedBracketPair = paintPair;
+ doBracketMatching();
+ repaint();
+ firePropertyChange(PAINT_MATCHED_BRACKET_PAIR_PROPERTY,
+ !paintMatchedBracketPair, paintMatchedBracketPair);
+ }
+ }
+
+
+ /**
+ * Toggles whether tab lines are painted. This method fires a property
+ * change event of type {@link #TAB_LINES_PROPERTY}.
+ *
+ * @param paint Whether tab lines are painted.
+ * @see #getPaintTabLines()
+ * @see #setTabLineColor(Color)
+ */
+ public void setPaintTabLines(boolean paint) {
+ if (paint!=paintTabLines) {
+ paintTabLines = paint;
+ repaint();
+ firePropertyChange(TAB_LINES_PROPERTY, !paint, paint);
+ }
+ }
+
+
+ /**
+ * Sets the parser delay. This is the delay that must occur between edits
+ * for any registered {@link Parser}s to run.
+ *
+ * @param millis The new parser delay, in milliseconds. This must be
+ * greater than zero.
+ * @see #getParserDelay()
+ */
+ public void setParserDelay(int millis) {
+ if (parserManager==null) {
+ parserManager = new ParserManager(this);
+ }
+ parserManager.setDelay(millis);
+ }
+
+
+ /**
+ * Applications typically have no need to modify this value.
+ *
+ * Workaround for JTextComponents allowing the caret to be rendered
+ * entirely off-screen if the entire "previous" character fit entirely.
+ *
+ * @param rhsCorrection The amount of space to add to the x-axis preferred
+ * span. This should be non-negative.
+ * @see #getRightHandSideCorrection()
+ */
+ public void setRightHandSideCorrection(int rhsCorrection) {
+ if (rhsCorrection<0) {
+ throw new IllegalArgumentException("correction should be > 0");
+ }
+ if (rhsCorrection!=this.rhsCorrection) {
+ this.rhsCorrection = rhsCorrection;
+ revalidate();
+ repaint();
+ }
+ }
+
+
+ /**
+ * Sets the background color to use for a secondary language.
+ *
+ * @param index The language index. Note that these are 1-based, not
+ * 0-based, and should be in the range
+ *
+ *
+ * This method fires a property change event of type
+ * {@link #SYNTAX_SCHEME_PROPERTY}.
+ *
+ * @param scheme The instance of
+ *
+ * Templates are a set of "shorthand identifiers" that you can configure
+ * so that you only have to type a short identifier (such as "forb") to
+ * insert a larger amount of code into the document (such as:
+ *
+ *
+ *
+ * For more flexible boilerplate code insertion, consider using the
+ * TemplateCompletion
+ * class in the
+ * AutoComplete
+ * add-on library.
+ *
+ * @param enabled Whether or not templates should be enabled.
+ * @see #getTemplatesEnabled()
+ */
+ public static synchronized void setTemplatesEnabled(boolean enabled) {
+ templatesEnabled = enabled;
+ }
+
+
+ /**
+ * Sets the color use to paint tab lines. This method fires a property
+ * change event of type {@link #TAB_LINE_COLOR_PROPERTY}.
+ *
+ * @param c The color. If this value is
- *
- * Most of this code is copied from javax.swing.text.DefaultHighlighter; unfortunately, we cannot re-use much of it
- * since it is package private.
- *
+ * The highlighter implementation used by {@link RSyntaxTextArea}s. It knows to
+ * always paint "marked occurrences" highlights below selection highlights,
+ * and squiggle underline highlights above all other highlights.
+ *
+ * Most of this code is copied from javax.swing.text.DefaultHighlighter;
+ * unfortunately, we cannot re-use much of it since it is package private.
+ *
* @author Robert Futrell
* @version 1.0
*/
-public class RSyntaxTextAreaHighlighter extends BasicHighlighter {
-
- /**
- * The text component we are the highlighter for.
- */
- private RTextArea textArea;
-
- /**
- * Marked occurrences in the document (to be painted separately from other highlights).
- */
- private List markedOccurrences;
-
- /**
- * Highlights from document parsers. These should be painted "on top of" all other highlights to ensure they are
- * always above the selection.
- */
- private List parserHighlights;
-
- /**
- * The default color used for parser notices when none is specified.
- */
- private static final Color DEFAULT_PARSER_NOTICE_COLOR = Color.RED;
-
- /**
- * Constructor.
- */
- public RSyntaxTextAreaHighlighter() {
- markedOccurrences = new ArrayList();
- parserHighlights = new ArrayList(0); // Often unused
- }
-
- /**
- * Adds a special "marked occurrence" highlight.
- *
- * @param start
- * @param end
- * @param p
- * @return
- * @throws BadLocationException
- * @see {@link #removeMarkOccurrencesHighlight(Object)}
- */
- Object addMarkedOccurrenceHighlight(int start, int end,
- MarkOccurrencesHighlightPainter p) throws BadLocationException {
- Document doc = textArea.getDocument();
- TextUI mapper = textArea.getUI();
- // Always layered highlights for marked occurrences.
- HighlightInfo i = new LayeredHighlightInfo();
- i.painter = p;
- i.p0 = doc.createPosition(start);
- // HACK: Use "end-1" to prevent chars the user types at the "end" of
- // the highlight to be absorbed into the highlight (default Highlight
- // behavior).
- i.p1 = doc.createPosition(end - 1);
- markedOccurrences.add(i);
- mapper.damageRange(textArea, start, end);
- return i;
- }
-
- /**
- * Adds a special "marked occurrence" highlight.
- *
- * @param notice
- * The notice from a {@link Parser}.
- * @return A tag with which to reference the highlight.
- * @throws BadLocationException
- * @see {@link #clearParserHighlights()}
- */
- Object addParserHighlight(ParserNotice notice, HighlightPainter p)
- throws BadLocationException {
-
- Document doc = textArea.getDocument();
- TextUI mapper = textArea.getUI();
-
- int start = notice.getOffset();
- int end = 0;
- if (start == -1) { // Could just define an invalid line number
- int line = notice.getLine();
- Element root = doc.getDefaultRootElement();
- if (line >= 0 && line < root.getElementCount()) {
- Element elem = root.getElement(line);
- start = elem.getStartOffset();
- end = elem.getEndOffset();
- }
- }
- else {
- end = start + notice.getLength();
- }
-
- // Always layered highlights for parser highlights.
- HighlightInfo i = new LayeredHighlightInfo();
- i.painter = p;
- i.p0 = doc.createPosition(start);
- i.p1 = doc.createPosition(end);
- i.notice = notice;// i.color = notice.getColor();
-
- parserHighlights.add(i);
- mapper.damageRange(textArea, start, end);
- return i;
-
- }
-
- /**
- * Removes all parser highlights.
- *
- * @see #addParserHighlight(int, int, Color, javax.swing.text.Highlighter.HighlightPainter)
- */
- void clearParserHighlights() {
-
- for (Object tag : parserHighlights) {
-
- if (tag instanceof LayeredHighlightInfo) {
- LayeredHighlightInfo lhi = (LayeredHighlightInfo) tag;
- if (lhi.width > 0 && lhi.height > 0) {
- textArea.repaint(lhi.x, lhi.y, lhi.width, lhi.height);
- }
- } else {
- HighlightInfo info = (HighlightInfo) tag;
- TextUI ui = textArea.getUI();
- ui.damageRange(textArea, info.getStartOffset(), info.getEndOffset());
- // safeDamageRange(info.p0, info.p1);
- }
-
- }
-
- parserHighlights.clear();
-
- }
-
- /**
- * Removes all of the highlights for a specific parser.
- *
- * @param parser
- * The parser.
- */
- public void clearParserHighlights(Parser parser) {
-
- for (Iterator i = parserHighlights.iterator(); i.hasNext();) {
-
- HighlightInfo info = (HighlightInfo) i.next();
-
- if (info.notice.getParser() == parser) {
- if (info instanceof LayeredHighlightInfo) {
- LayeredHighlightInfo lhi = (LayeredHighlightInfo) info;
- if (lhi.width > 0 && lhi.height > 0) {
- textArea.repaint(lhi.x, lhi.y, lhi.width, lhi.height);
- }
- }
- else {
- TextUI ui = textArea.getUI();
- ui.damageRange(textArea, info.getStartOffset(), info.getEndOffset());
- // safeDamageRange(info.p0, info.p1);
- }
- i.remove();
- }
-
- }
-
- }
-
- /**
- * {@inheritDoc}
- */
- public void deinstall(JTextComponent c) {
- this.textArea = null;
- markedOccurrences.clear();
- parserHighlights.clear();
- }
-
- /**
- * Returns a list of "marked occurrences" in the text area. If there are no marked occurrences, this will be an
- * empty list.
- *
- * @return The list of marked occurrences.
- */
- public List getMarkedOccurrences() {
- List list = new ArrayList(markedOccurrences.size());
- for (Object markedOccurrence : markedOccurrences) {
- HighlightInfo info = (HighlightInfo) markedOccurrence;
- int start = info.getStartOffset();
- int end = info.getEndOffset() + 1; // HACK
- DocumentRange range = new DocumentRangeImpl(start, end);
- list.add(range);
- }
- return list;
- }
-
- /**
- * {@inheritDoc}
- */
- public void install(JTextComponent c) {
- super.install(c);
- this.textArea = (RTextArea) c;
- }
-
- /**
- * Renders the highlights.
- *
- * @param g
- * the graphics context
- */
- public void paint(Graphics g) {
- paintList(g, markedOccurrences);
- super.paint(g);
- paintList(g, parserHighlights);
- }
-
- private void paintList(Graphics g, List highlights) {
-
- int len = highlights.size();
-
- for (int i = 0; i < len; i++) {
- HighlightInfo info = (HighlightInfo) highlights.get(i);
- if (!(info instanceof LayeredHighlightInfo)) {
- // Avoid allocating unless we need it.
- Rectangle a = textArea.getBounds();
- Insets insets = textArea.getInsets();
- a.x = insets.left;
- a.y = insets.top;
- a.width -= insets.left + insets.right;
- a.height -= insets.top + insets.bottom;
- for (; i < len; i++) {
- info = (HighlightInfo) markedOccurrences.get(i);
- if (!(info instanceof LayeredHighlightInfo)) {
- Color c = info.getColor();
- Highlighter.HighlightPainter p = info.getPainter();
- if (c != null && p instanceof ChangeableColorHighlightPainter) {
- ((ChangeableColorHighlightPainter) p).setColor(c);
- }
- p.paint(g, info.getStartOffset(), info.getEndOffset(),
- a, textArea);
- }
- }
- }
- }
-
- }
-
- /**
- * When leaf Views (such as LabelView) are rendering they should call into this method. If a highlight is in the
- * given region it will be drawn immediately.
- *
- * @param g
- * Graphics used to draw
- * @param p0
- * starting offset of view
- * @param p1
- * ending offset of view
- * @param viewBounds
- * Bounds of View
- * @param editor
- * JTextComponent
- * @param view
- * View instance being rendered
- */
- public void paintLayeredHighlights(Graphics g, int p0, int p1,
- Shape viewBounds, JTextComponent editor, View view) {
- paintListLayered(g, p0, p1, viewBounds, editor, view, markedOccurrences);
- super.paintLayeredHighlights(g, p0, p1, viewBounds, editor, view);
- paintListLayered(g, p0, p1, viewBounds, editor, view, parserHighlights);
- }
-
- private void paintListLayered(Graphics g, int p0, int p1, Shape viewBounds,
- JTextComponent editor, View view, List highlights) {
- for (int i = highlights.size() - 1; i >= 0; i--) {
- Object tag = highlights.get(i);
- if (tag instanceof LayeredHighlightInfo) {
- LayeredHighlightInfo lhi = (LayeredHighlightInfo) tag;
- int start = lhi.getStartOffset();
- int end = lhi.getEndOffset();
- if ((p0 < start && p1 > start) ||
- (p0 >= start && p0 < end)) {
- lhi.paintLayeredHighlights(g, p0, p1, viewBounds,
- editor, view);
- }
- }
- }
- }
-
- private void removeListHighlight(List list, Object tag) {
- if (tag instanceof LayeredHighlightInfo) {
- LayeredHighlightInfo lhi = (LayeredHighlightInfo) tag;
- if (lhi.width > 0 && lhi.height > 0) {
- textArea.repaint(lhi.x, lhi.y, lhi.width, lhi.height);
- }
- }
- else {
- HighlightInfo info = (HighlightInfo) tag;
- TextUI ui = textArea.getUI();
- ui.damageRange(textArea, info.getStartOffset(), info.getEndOffset());
- // safeDamageRange(info.p0, info.p1);
- }
- list.remove(tag);
- }
-
- /**
- * Removes a "marked occurrences" highlight from the view.
- *
- * @param tag
- * The reference to the highlight
- * @see #addMarkedOccurrenceHighlight(int, int, javax.swing.text.Highlighter.HighlightPainter)
- */
- void removeMarkOccurrencesHighlight(Object tag) {
- removeListHighlight(markedOccurrences, tag);
- }
-
- /**
- * Removes a parser highlight from this view.
- *
- * @param tag
- * The reference to the highlight.
- * @see #addParserHighlight(int, int, Color, javax.swing.text.Highlighter.HighlightPainter)
- */
- void removeParserHighlight(Object tag) {
- removeListHighlight(parserHighlights, tag);
- }
-
- private static class DocumentRangeImpl implements DocumentRange {
-
- private int startOffs;
- private int endOffs;
-
- public DocumentRangeImpl(int startOffs, int endOffs) {
- this.startOffs = startOffs;
- this.endOffs = endOffs;
- }
-
- public int getEndOffset() {
- return endOffs;
- }
-
- public int getStartOffset() {
- return startOffs;
- }
-
- }
-
- private static class HighlightInfo implements Highlighter.Highlight {
-
- private Position p0;
- private Position p1;
- protected Highlighter.HighlightPainter painter;
- private ParserNotice notice;// Color color; // Used only by Parser highlights.
-
- public Color getColor() {
- // return color;
- Color color = null;
- if (notice != null) {
- color = notice.getColor();
- if (color == null) {
- color = DEFAULT_PARSER_NOTICE_COLOR;
- }
- }
- return color;
- }
-
- public int getStartOffset() {
- return p0.getOffset();
- }
-
- public int getEndOffset() {
- return p1.getOffset();
- }
-
- public Highlighter.HighlightPainter getPainter() {
- return painter;
- }
-
- }
-
- private static class LayeredHighlightInfo extends HighlightInfo {
-
- private int x;
- private int y;
- private int width;
- private int height;
-
- void union(Shape bounds) {
- if (bounds == null) {
- return;
- }
- Rectangle alloc = (bounds instanceof Rectangle) ?
- (Rectangle) bounds : bounds.getBounds();
- if (width == 0 || height == 0) {
- x = alloc.x;
- y = alloc.y;
- width = alloc.width;
- height = alloc.height;
- }
- else {
- width = Math.max(x + width, alloc.x + alloc.width);
- height = Math.max(y + height, alloc.y + alloc.height);
- x = Math.min(x, alloc.x);
- width -= x;
- y = Math.min(y, alloc.y);
- height -= y;
- }
- }
-
- /**
- * Restricts the region based on the receivers offsets and messages the painter to paint the region.
- */
- void paintLayeredHighlights(Graphics g, int p0, int p1,
- Shape viewBounds, JTextComponent editor,
- View view) {
- int start = getStartOffset();
- int end = getEndOffset();
- // Restrict the region to what we represent
- p0 = Math.max(start, p0);
- p1 = Math.min(end, p1);
- if (getColor() != null &&
- (painter instanceof ChangeableColorHighlightPainter)) {
- ((ChangeableColorHighlightPainter) painter).setColor(getColor());
- }
- // Paint the appropriate region using the painter and union
- // the effected region with our bounds.
- union(((LayeredHighlighter.LayerPainter) painter).paintLayer
- (g, p0, p1, viewBounds, editor, view));
- }
-
- }
-
-}
\ No newline at end of file
+public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
+
+ /**
+ * Marked occurrences in the document (to be painted separately from
+ * other highlights).
+ */
+ private List
- *
- * This method is not named
+ *
+ * This method is not named
- *
- * This is faster than calling
+ *
+ * This method is quicker than using traditional
+ *
+ *
+ * This is faster than calling
- *
- * NOTE: You should only call this method if the passed-in
- *
- * This method also assumes that the passed-in token list begins at x-pixel
- *
- * @param tokenList
- * The token list list representing the text.
- * @param textArea
- * The text area in which this token list resides.
- * @param e
- * The tab expander. This value cannot be
- *
- * This method can be useful if you are only interested in part of a token list (i.e., the line it represents), but
- * you don't want to modify the token list yourself.
- *
- * @param tokenList
- * The list to make start at the specified position. This parameter is modified.
- * @param pos
- * The position at which the new token list is to start. If this position is not in the passed-in token
- * list, returned token list will either be
+ *
+ * This method also assumes that the passed-in token list begins at
+ * x-pixel
+ *
+ * @param tokenList The token list list representing the text.
+ * @param textArea The text area in which this token list resides.
+ * @param e The tab expander. This value cannot be
- *
- * This is used by
+ *
+ * This is used by
- *
+ * Generates RTF text via a simple Java API.
+ *
* The following RTF features are supported:
*
- *
- * If the font is not in the list, it is added, and its new index is returned.
- *
- * @param list
- * The list (possibly) containing the font.
- * @param font
- * The font to get the index of.
- * @return The index of the font.
- */
- private static int getFontIndex(List list, Font font) {
- String fontName = font.getFamily();
- for (int i = 0; i < list.size(); i++) {
- Font font2 = (Font) list.get(i);
- if (font2.getFamily().equals(fontName)) {
- return i;
- }
- }
- list.add(font);
- return list.size() - 1;
- }
-
- private String getFontTableRtf() {
-
- // Example:
- // "{\\fonttbl{\\f0\\fmodern\\fcharset0 Courier;}}"
-
- // Workaround for text areas using the Java logical font "Monospaced"
- // by default. There's no way to know what it's mapped to, so we
- // just search for a monospaced font on the system.
- String monoFamilyName = getMonospacedFontName();
-
- StringBuilder sb = new StringBuilder("{\\fonttbl{\\f0\\fnil\\fcharset0 " + monoFamilyName + ";}");
- for (int i = 0; i < fontList.size(); i++) {
- Font f = (Font) fontList.get(i);
- String familyName = f.getFamily();
- if (familyName.equals("Monospaced")) {
- familyName = monoFamilyName;
- }
- sb.append("{\\f").append(i + 1).append("\\fnil\\fcharset0 ");
- sb.append(familyName).append(";}");
- }
- sb.append('}');
-
- return sb.toString();
-
- }
-
- /**
- * Returns the index of the specified item in a list. If the item is not in the list, it is added, and its new index
- * is returned.
- *
- * @param list
- * The list (possibly) containing the item.
- * @param item
- * The item to get the index of.
- * @return The index of the item.
- */
- private static int getIndex(List list, Object item) {
- int pos = list.indexOf(item);
- if (pos == -1) {
- list.add(item);
- pos = list.size() - 1;
- }
- return pos;
- }
-
- /**
- * Try to pick a monospaced font installed on this system. We try to check for monospaced fonts that are commonly
- * installed on different OS's. This information was gleaned from
- * http://www.codestyle.org/css/font-family/sampler-Monospace.shtml.
- *
- * @return The name of a monospaced font.
- */
- private String getMonospacedFontName() {
-
- if (monospacedFontName == null) {
-
- GraphicsEnvironment ge = GraphicsEnvironment.
- getLocalGraphicsEnvironment();
- String[] familyNames = ge.getAvailableFontFamilyNames();
- Arrays.sort(familyNames);
- boolean windows = System.getProperty("os.name").toLowerCase().
- indexOf("windows") >= 0;
-
- // "Monaco" is the "standard" monospaced font on OS X. We'll
- // check for it first so on Macs we don't get stuck with the
- // uglier Courier New. It'll look funny on Windows though, so
- // don't pick it if we're on Windows.
- // It's found on Windows 1.76% of the time, OS X 96.73%
- // of the time, and UNIX 00.00% (?) of the time.
- if (!windows && Arrays.binarySearch(familyNames, "Monaco") >= 0) {
- monospacedFontName = "Monaco";
- }
-
- // "Courier New" is found on Windows 96.48% of the time,
- // OS X 92.38% of the time, and UNIX 61.95% of the time.
- else if (Arrays.binarySearch(familyNames, "Courier New") >= 0) {
- monospacedFontName = "Courier New";
- }
-
- // "Courier" is found on Windows ??.??% of the time,
- // OS X 96.27% of the time, and UNIX 74.04% of the time.
- else if (Arrays.binarySearch(familyNames, "Courier") >= 0) {
- monospacedFontName = "Courier";
- }
-
- // "Nimbus Mono L" is on Windows 00.00% (?) of the time,
- // OS X 00.00% (?) of the time, but on UNIX 88.79% of the time.
- else if (Arrays.binarySearch(familyNames, "Nimbus Mono L") >= 0) {
- monospacedFontName = "Nimbus Mono L";
- }
-
- // "Lucida Sans Typewriter" is on Windows 49.37% of the time,
- // OS X 90.43% of the time, and UNIX 00.00% (?) of the time.
- else if (Arrays.binarySearch(familyNames, "Lucida Sans Typewriter") >= 0) {
- monospacedFontName = "Lucida Sans Typewriter";
- }
-
- // "Bitstream Vera Sans Mono" is on Windows 29.81% of the time,
- // OS X 25.53% of the time, and UNIX 80.71% of the time.
- else if (Arrays.binarySearch(familyNames, "Bitstream Vera Sans Mono") >= 0) {
- monospacedFontName = "Bitstream Vera Sans Mono";
- }
-
- // Windows: 34.16% of the time, OS X: 00.00% (?) of the time,
- // UNIX: 33.92% of the time.
- if (monospacedFontName == null) {
- monospacedFontName = "Terminal";
- }
-
- }
-
- return monospacedFontName;
-
- }
-
- /**
- * Returns the RTF document created by this generator.
- *
- * @return The RTF document, as a
+ *
+ * If the font is not in the list, it is added, and its new index is
+ * returned.
+ *
+ * @param list The list (possibly) containing the font.
+ * @param font The font to get the index of.
+ * @return The index of the font.
+ */
+ private static int getFontIndex(List list, Font font) {
+ String fontName = font.getFamily();
+ for (int i=0; i
- *
+ * Highlight painter that paints a squiggly underline underneath text, similar
+ * to what popular IDE's such as Visual Studio and Eclipse do to indicate
+ * errors, warnings, etc.
+ *
* This class must be used as a
- *
- * This method is implemented, since {@link #equals(Object)} is implemented, to keep FindBugs happy.
- *
- * @return The hash code.
- */
- public int hashCode() {
- int hashCode = underline ? 1 : 0;
- if (foreground != null) {
- hashCode ^= foreground.hashCode();
- }
- if (background != null) {
- hashCode ^= background.hashCode();
- }
- return hashCode;
- }
-
- /**
- * Returns a string representation of this style.
- *
- * @return A string representation of this style.
- */
- public String toString() {
- return "[Style: foreground: " + foreground +
- ", background: " + background + ", underline: " +
- underline + ", font: " + font + "]";
- }
-
-}
\ No newline at end of file
+ public static final Color DEFAULT_FOREGROUND = Color.BLACK;
+ public static final Color DEFAULT_BACKGROUND = null;
+ public static final Font DEFAULT_FONT = null;
+
+ public Color foreground;
+ public Color background;
+ public boolean underline;
+ public Font font;
+
+ public FontMetrics fontMetrics;
+
+
+ /**
+ * Creates a new style defaulting to black foreground, no
+ * background, and no styling.
+ */
+ public Style() {
+ this(DEFAULT_FOREGROUND);
+ }
+
+
+ /**
+ * Creates a new style with the specified foreground and no styling.
+ *
+ * @param fg The foreground color to use.
+ */
+ public Style(Color fg) {
+ this(fg, DEFAULT_BACKGROUND);
+ }
+
+
+ /**
+ * Creates a new style with the specified colors and no styling.
+ *
+ * @param fg The foreground color to use.
+ * @param bg The background color to use.
+ */
+ public Style(Color fg, Color bg) {
+ this(fg, bg, DEFAULT_FONT);
+ }
+
+
+ /**
+ * Creates a new style.
+ *
+ * @param fg The foreground color to use.
+ * @param bg The background color to use.
+ * @param font The font for this syntax scheme.
+ */
+ public Style(Color fg, Color bg, Font font) {
+ this(fg, bg, font, false);
+ }
+
+
+ /**
+ * Creates a new style.
+ *
+ * @param fg The foreground color to use.
+ * @param bg The background color to use.
+ * @param font The font for this syntax scheme.
+ * @param underline Whether or not to underline tokens with this style.
+ */
+ public Style(Color fg, Color bg, Font font, boolean underline) {
+ foreground = fg;
+ background = bg;
+ this.font = font;
+ this.underline = underline;
+ this.fontMetrics = font==null ? null :
+ new JPanel().getFontMetrics(font); // Default, no rendering hints!
+ }
+
+
+ /**
+ * Returns whether or not two (possibly
+ *
+ * This method is implemented, since {@link #equals(Object)} is implemented,
+ * to keep FindBugs happy.
+ *
+ * @return The hash code.
+ */
+ @Override
+ public int hashCode() {
+ int hashCode = underline ? 1 : 0;
+ if (foreground!=null) {
+ hashCode ^= foreground.hashCode();
+ }
+ if (background!=null) {
+ hashCode ^= background.hashCode();
+ }
+ return hashCode;
+ }
+
+
+ /**
+ * Returns a string representation of this style.
+ *
+ * @return A string representation of this style.
+ */
+ @Override
+ public String toString() {
+ return "[Style: foreground: " + foreground +
+ ", background: " + background + ", underline: " +
+ underline + ", font: " + font + "]";
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/StyledTextTransferable.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/StyledTextTransferable.java
new file mode 100644
index 000000000..48d16045f
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/StyledTextTransferable.java
@@ -0,0 +1,110 @@
+/*
+ * 07/28/2008
+ *
+ * StyledTextTransferable.java - Used during drag-and-drop to represent RTF text.
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringReader;
+
+
+/**
+ * Object used during copy/paste and DnD operations to represent styled text.
+ * It can return the text being moved as HTML, RTF or plain text. This
+ * class is basically the same as
+ *
- *
- * By default, all
+ *
+ * By default, all
+ * You can use this class to programmatically set the fonts and colors used in
+ * an RSyntaxTextArea, but for more powerful, externalized control, consider
+ * using {@link Theme}s instead.
+ *
* @author Robert Futrell
* @version 1.0
+ * @see Theme
*/
-public class SyntaxScheme implements Cloneable {
-
- public Style[] styles;
-
- private static final String VERSION = "*ver1";
-
- /**
- * Creates a color scheme that either has all color values set to a default value or set to
+ * Note that the returned array is not a copy of the style data; editing the
+ * array will modify the styles used by any
+ *
+ * Consider using the {@link Theme} class for saving and loading RSTA
+ * styles rather than using this API.
+ *
+ * @param baseFont The font to use as the "base" for the syntax scheme.
+ * If this is
+ *
+ * Consider using the {@link Theme} class for saving and loading RSTA
+ * styles rather than using this API.
+ *
+ * @param string A string generated from {@link #toCommaSeparatedString()}.
+ * @return A color scheme.
+ * @see #toCommaSeparatedString()
+ */
+ public static SyntaxScheme loadFromString(String string) {
+ return loadFromString(string, DEFAULT_NUM_TOKEN_TYPES);
+ }
+
+
+ /**
+ * Loads a syntax highlighting color scheme from a string created from
+ *
+ *
+ * Consider using the {@link Theme} class for saving and loading RSTA
+ * styles rather than using this API.
+ *
+ * @param string A string generated from {@link #toCommaSeparatedString()}.
+ * @param tokenTypeCount The number of token types saved in this string.
+ * This should be the number of token types saved by your custom
+ * SyntaxScheme subclass,
+ * or {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES} if you used the
+ * standard implementation (which most people will).
+ * @return A color scheme.
+ * @see #loadFromString(String)
+ * @see #toCommaSeparatedString()
+ */
+ public static SyntaxScheme loadFromString(String string,
+ int tokenTypeCount) {
+
+ SyntaxScheme scheme = new SyntaxScheme(true);
+
+ try {
+
+ if (string!=null) {
+
+ String[] tokens = string.split(",", -1);
+
+ // Check the version string, use defaults if incompatible
+ if (tokens.length==0 || !VERSION.equals(tokens[0])) {
+ return scheme; // Still set to defaults
+ }
+
+ int tokenCount = tokenTypeCount*7 + 1; // Version string
+ if (tokens.length!=tokenCount) {
+ throw new Exception(
+ "Not enough tokens in packed color scheme: expected " +
+ tokenCount + ", found " + tokens.length);
+ }
+
+ // Use StyleContext to create fonts to get composite fonts for
+ // Asian glyphs.
+ StyleContext sc = StyleContext.getDefaultStyleContext();
+
+ // Loop through each token style. Format:
+ // "index,(fg|-),(bg|-),(t|f),((font,style,size)|(-,,))"
+ for (int i=0; i
- *
- * You don't really have to do anything to use this class, as {@link RSyntaxTextAreaUI} automatically sets the text
- * area's view to be an instance of this class if word wrap is disabled.
- *
- *
- * The tokens that specify how to paint the syntax-highlighted text are gleaned from the text area's
- * {@link RSyntaxDocument}.
- *
+ * The
+ *
+ * You don't really have to do anything to use this class, as
+ * {@link RSyntaxTextAreaUI} automatically sets the text area's view to be
+ * an instance of this class if word wrap is disabled.
+ *
+ * The tokens that specify how to paint the syntax-highlighted text are gleaned
+ * from the text area's {@link RSyntaxDocument}.
+ *
* @author Robert Futrell
* @version 0.3
*/
public class SyntaxView extends View implements TabExpander,
- TokenOrientedView, RSTAView {
-
- /**
- * The default font used by the text area. If this changes we need to recalculate the longest line.
- */
- Font font;
-
- /**
- * Font metrics for the current font.
- */
- protected FontMetrics metrics;
-
- /**
- * The current longest line. This is used to calculate the preferred width of the view. Since the calculation is
- * potentially expensive, we try to avoid it by stashing which line is currently the longest.
- */
- Element longLine;
- float longLineWidth;
-
- private int tabSize;
- protected int tabBase;
-
- /**
- * Cached for each paint() call so each drawLine() call has access to it.
- */
- private RSyntaxTextArea host;
-
- /**
- * Cached values to speed up the painting a tad.
- */
- private int lineHeight = 0;
- private int ascent;
- private int clipStart;
- private int clipEnd;
-
- // /**
- // * The end-of-line marker.
- // */
- // private static final char[] eolMarker = { '.' };
-
- /**
- * Constructs a new
- *
- * This is implemented to subtract the width of the second character, as this view's
- * Both local and remote files (e.g. ftp) are supported. See the {@link FileLocation} class for more information.
- *
+ *
+ * Loading and saving is also built into the editor.
+ *
+ * When saving UTF-8 files, whether or not a BOM is written is controlled by
+ * the {@link UnicodeWriter} class.
+ * Use {@link UnicodeWriter#setWriteUtf8BOM(boolean)} to toggle writing BOMs
+ * for UTF-8 files.
+ *
+ * Both local and remote files (e.g. ftp) are supported. See the
+ * {@link FileLocation} class for more information.
+ *
* @author Robert Futrell
* @version 1.0
* @see FileLocation
*/
public class TextEditorPane extends RSyntaxTextArea implements
- DocumentListener {
-
- private static final long serialVersionUID = 1L;
-
- public static final String FULL_PATH_PROPERTY = "TextEditorPane.fileFullPath";
- public static final String DIRTY_PROPERTY = "TextEditorPane.dirty";
- public static final String READ_ONLY_PROPERTY = "TextEditorPane.readOnly";
-
- /**
- * The location of the file being edited.
- */
- private FileLocation loc;
-
- /**
- * The charset to use when reading or writing this file.
- */
- private String charSet;
-
- /**
- * Whether the file should be treated as read-only.
- */
- private boolean readOnly;
-
- /**
- * Whether the file is dirty.
- */
- private boolean dirty;
-
- /**
- * The last time this file was modified on disk, for local files. For remote files, this value should always be
- * {@link #LAST_MODIFIED_UNKNOWN}.
- */
- private long lastSaveOrLoadTime;
-
- /**
- * The value returned by {@link #getLastSaveOrLoadTime()} for remote files.
- */
- public static final long LAST_MODIFIED_UNKNOWN = 0;
-
- /**
- * The default name given to files if none is specified in a constructor.
- */
- private static final String DEFAULT_FILE_NAME = "Untitled.txt";
-
- /**
- * Constructor. The file will be given a default name.
- */
- public TextEditorPane() {
- this(INSERT_MODE);
- }
-
- /**
- * Constructor. The file will be given a default name.
- *
- * @param textMode
- * Either
- *
- * For remote files, this method will always return {@link #LAST_MODIFIED_UNKNOWN}.
- *
- * @return The timestamp when this file was last loaded or saved by this editor pane, if it is a local file, or
- * {@link #LAST_MODIFIED_UNKNOWN} if it is a remote file.
- * @see #isModifiedOutsideEditor()
- */
- public long getLastSaveOrLoadTime() {
- return lastSaveOrLoadTime;
- }
-
- /**
- * Returns the line separator used when writing this file (e.g. "
- *
- * Note that this value is an
- *
- * This method may be used by applications to implement a reloading feature, where the user is prompted to reload a
- * file if it has been modified since their last open or save.
- *
- * @return Whether the text file has been modified outside of this editor.
- * @see #getLastSaveOrLoadTime()
- */
- public boolean isModifiedOutsideEditor() {
- return loc.getActualLastModified() > getLastSaveOrLoadTime();
- }
-
- /**
- * Returns whether or not the text area should be treated as read-only.
- *
- * @return Whether or not the text area should be treated as read-only.
- * @see #setReadOnly(boolean)
- */
- public boolean isReadOnly() {
- return readOnly;
- }
-
- /**
- * Loads the specified file in this editor.
- *
- * @param loc
- * The location of the file to load. This cannot be
- *
- * The file's "dirty" state will be set to
- *
- * Note that if the file has been modified on disk, and is now a Unicode encoding when before it wasn't (or if it is
- * a different Unicode now), this will cause this {@link TextEditorPane}'s encoding to change. Otherwise, the file's
- * encoding will stay the same.
- *
- * @throws IOException
- * If the file does not exist, or if an IO error occurs reading the file.
- * @see #isLocalAndExists()
- */
- public void reload() throws IOException {
- String oldEncoding = getEncoding();
- UnicodeReader ur = new UnicodeReader(loc.getInputStream(), oldEncoding);
- String encoding = ur.getEncoding();
- BufferedReader r = new BufferedReader(ur);
- try {
- read(r, null); // Dumps old contents.
- } finally {
- r.close();
- }
- setEncoding(encoding);
- setDirty(false);
- syncLastSaveOrLoadTimeToActualFile();
- discardAllEdits(); // Prevent user from being able to undo the reload
- }
-
- /**
- * Called whenever text is removed from this editor.
- *
- * @param e
- * The document event.
- */
- public void removeUpdate(DocumentEvent e) {
- if (!dirty) {
- setDirty(true);
- }
- }
-
- /**
- * Saves the file in its current encoding.
- *
- *
- * The text area's "dirty" state is set to
- *
- * You normally do not have to call this method, as the "last saved or loaded" time for {@link TextEditorPane}s is
- * kept up-to-date internally during such operations as {@link #save()}, {@link #reload()}, etc.
- *
- * @see #getLastSaveOrLoadTime()
- * @see #isModifiedOutsideEditor()
- */
- public void syncLastSaveOrLoadTimeToActualFile() {
- if (loc.isLocalAndExists()) {
- lastSaveOrLoadTime = loc.getActualLastModified();
- }
- }
-
-}
\ No newline at end of file
+ DocumentListener {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Property change event fired when the file path this text area references
+ * is updated.
+ *
+ * @see #load(FileLocation, String)
+ * @see #saveAs(FileLocation)
+ */
+ public static final String FULL_PATH_PROPERTY = "TextEditorPane.fileFullPath";
+
+ /**
+ * Property change event fired when the text area's dirty flag changes.
+ *
+ * @see #setDirty(boolean)
+ */
+ public static final String DIRTY_PROPERTY = "TextEditorPane.dirty";
+
+ /**
+ * Property change event fired when the text area should be treated as
+ * read-only, and previously it should not, or vice-versa.
+ *
+ * @see #setReadOnly(boolean)
+ */
+ public static final String READ_ONLY_PROPERTY = "TextEditorPane.readOnly";
+
+ /**
+ * Property change event fired when the text area's encoding changes.
+ *
+ * @see #setEncoding(String)
+ */
+ public static final String ENCODING_PROPERTY = "TextEditorPane.encoding";
+
+ /**
+ * The location of the file being edited.
+ */
+ private FileLocation loc;
+
+ /**
+ * The charset to use when reading or writing this file.
+ */
+ private String charSet;
+
+ /**
+ * Whether the file should be treated as read-only.
+ */
+ private boolean readOnly;
+
+ /**
+ * Whether the file is dirty.
+ */
+ private boolean dirty;
+
+ /**
+ * The last time this file was modified on disk, for local files.
+ * For remote files, this value should always be
+ * {@link #LAST_MODIFIED_UNKNOWN}.
+ */
+ private long lastSaveOrLoadTime;
+
+ /**
+ * The value returned by {@link #getLastSaveOrLoadTime()} for remote files.
+ */
+ public static final long LAST_MODIFIED_UNKNOWN = 0;
+
+ /**
+ * The default name given to files if none is specified in a constructor.
+ */
+ private static final String DEFAULT_FILE_NAME = "Untitled.txt";
+
+
+ /**
+ * Constructor. The file will be given a default name.
+ */
+ public TextEditorPane() {
+ this(INSERT_MODE);
+ }
+
+
+ /**
+ * Constructor. The file will be given a default name.
+ *
+ * @param textMode Either
+ *
+ * For remote files, this method will always return
+ * {@link #LAST_MODIFIED_UNKNOWN}.
+ *
+ * @return The timestamp when this file was last loaded or saved by this
+ * editor pane, if it is a local file, or
+ * {@link #LAST_MODIFIED_UNKNOWN} if it is a remote file.
+ * @see #isModifiedOutsideEditor()
+ */
+ public long getLastSaveOrLoadTime() {
+ return lastSaveOrLoadTime;
+ }
+
+
+ /**
+ * Returns the line separator used when writing this file (e.g.
+ * "
+ *
+ * Note that this value is an
+ *
+ * This method may be used by applications to implement a reloading
+ * feature, where the user is prompted to reload a file if it has been
+ * modified since their last open or save.
+ *
+ * @return Whether the text file has been modified outside of this
+ * editor.
+ * @see #getLastSaveOrLoadTime()
+ */
+ public boolean isModifiedOutsideEditor() {
+ return loc.getActualLastModified()>getLastSaveOrLoadTime();
+ }
+
+
+ /**
+ * Returns whether or not the text area should be treated as read-only.
+ *
+ * @return Whether or not the text area should be treated as read-only.
+ * @see #setReadOnly(boolean)
+ */
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+
+ /**
+ * Loads the specified file in this editor. This method fires a property
+ * change event of type {@link #FULL_PATH_PROPERTY}.
+ *
+ * @param loc The location of the file to load. This cannot be
+ *
+ *
+ * The file's "dirty" state will be set to
+ *
+ * Note that if the file has been modified on disk, and is now a Unicode
+ * encoding when before it wasn't (or if it is a different Unicode now),
+ * this will cause this {@link TextEditorPane}'s encoding to change.
+ * Otherwise, the file's encoding will stay the same.
+ *
+ * @throws IOException If the file does not exist, or if an IO error
+ * occurs reading the file.
+ * @see #isLocalAndExists()
+ */
+ public void reload() throws IOException {
+ String oldEncoding = getEncoding();
+ UnicodeReader ur = new UnicodeReader(loc.getInputStream(), oldEncoding);
+ String encoding = ur.getEncoding();
+ try (BufferedReader r = new BufferedReader(ur)) {
+ read(r, null); // Dumps old contents.
+ }
+ setEncoding(encoding);
+ setDirty(false);
+ syncLastSaveOrLoadTimeToActualFile();
+ discardAllEdits(); // Prevent user from being able to undo the reload
+ }
+
+
+ /**
+ * Called whenever text is removed from this editor.
+ *
+ * @param e The document event.
+ */
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ if (!dirty) {
+ setDirty(true);
+ }
+ }
+
+
+ /**
+ * Saves the file in its current encoding.
+ *
+ * The text area's "dirty" state is set to
+ *
+ * Applications will usually have no need to call this method directly; the
+ * only time you might have a need to call this method directly is if you
+ * have to initialize an instance of TextEditorPane with content that does
+ * not come from a file.
+ *
+ * You normally do not have to call this method, as the "last saved or
+ * loaded" time for {@link TextEditorPane}s is kept up-to-date internally
+ * during such operations as {@link #save()}, {@link #reload()}, etc.
+ *
+ * @see #getLastSaveOrLoadTime()
+ * @see #isModifiedOutsideEditor()
+ */
+ public void syncLastSaveOrLoadTimeToActualFile() {
+ if (loc.isLocalAndExists()) {
+ lastSaveOrLoadTime = loc.getActualLastModified();
+ }
+ }
+
+
+ public static void main(String[] args) throws Exception {
+ try {
+ TextEditorPane textArea = new TextEditorPane();
+ textArea.load(FileLocation.create("d:/temp/test.txt"), "UTF-8");
+ JPanel cp = new JPanel();
+ cp.setPreferredSize(new java.awt.Dimension(300, 300));
+ cp.setLayout(new java.awt.BorderLayout());
+ cp.add(new JScrollPane(textArea));
+ JFrame frame = new JFrame();
+ frame.setContentPane(cp);
+ frame.pack();
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setLocationByPlatform(true);
+ frame.setVisible(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/Theme.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/Theme.java
new file mode 100755
index 000000000..2c4e5906a
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/Theme.java
@@ -0,0 +1,882 @@
+/*
+ * 10/30/2011
+ *
+ * Theme.java - A color theme for RSyntaxTextArea.
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.SystemColor;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+
+import javax.swing.UIManager;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.text.StyleContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.fife.io.UnicodeWriter;
+import org.fife.ui.rtextarea.Gutter;
+import org.fife.ui.rtextarea.RTextArea;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * A theme is a set of fonts and colors to use to style RSyntaxTextArea and
+ * RTextScrollPane. Themes are defined in XML files that are validated against
+ *
+ *
+ * Sample themes are included in the source tree in the
+ *
+ *
+ * All fields are public to facilitate programmatic manipulation, but typically
+ * you won't need to reference any fields directly, rather using the
+ *
+ *
+ * Note that to save a
- *
- * A
- *
+ * A generic token that functions as a node in a linked list of syntax
+ * highlighted tokens for some language.
+ *
+ * A
*
- *
+ * would be broken into 8
+ *
+ * Note: The tokens returned by {@link RSyntaxDocument}s are pooled and
+ * should always be treated as immutable. Modifying tokens you did not create
+ * yourself can and will result in rendering issues and/or runtime exceptions.
+ * You have been warned!
+ *
* @author Robert Futrell
* @version 0.3
*/
-public abstract class Token {
-
- /**
- * The text this token represents. This is implemented as a segment so we can point directly to the text in the
- * document without having to make a copy of it.
- */
- public char[] text;
- public int textOffset;
- public int textCount;
-
- /**
- * The offset into the document at which this token resides.
- */
- public int offset;
-
- /**
- * The type of token this is; for example, {@link #FUNCTION}.
- */
- public int type;
-
- /**
- * Whether this token is a hyperlink.
- */
- private boolean hyperlink;
-
- /**
- * The next token in this linked list.
- */
- private Token nextToken;
-
- /**
- * Rectangle used for filling token backgrounds.
- */
- private Rectangle2D.Float bgRect;
-
- // NOTE: All valid token types are >= 0, so extensions of the TokenMaker
- // class are free to internally use all ints < 0 ONLY for "end-of-line"
- // style markers; they are ignored by painting implementations.
-
- public static final int NULL = 0; // Marks EOL with no multiline token at end.
-
- public static final int COMMENT_EOL = 1;
- public static final int COMMENT_MULTILINE = 2;
- public static final int COMMENT_DOCUMENTATION = 3;
-
- public static final int RESERVED_WORD = 4;
-
- public static final int FUNCTION = 5;
-
- public static final int LITERAL_BOOLEAN = 6;
- public static final int LITERAL_NUMBER_DECIMAL_INT = 7;
- public static final int LITERAL_NUMBER_FLOAT = 8;
- public static final int LITERAL_NUMBER_HEXADECIMAL = 9;
- public static final int LITERAL_STRING_DOUBLE_QUOTE = 10;
- public static final int LITERAL_CHAR = 11; // Char or single-quote string.
- public static final int LITERAL_BACKQUOTE = 12; // Used in UNIX/Perl scripts.
-
- public static final int DATA_TYPE = 13;
-
- public static final int VARIABLE = 14;
-
- public static final int IDENTIFIER = 15;
-
- public static final int WHITESPACE = 16;
-
- public static final int SEPARATOR = 17;
-
- public static final int OPERATOR = 18;
-
- public static final int PREPROCESSOR = 19;
-
- public static final int MARKUP_TAG_DELIMITER = 20;
- public static final int MARKUP_TAG_NAME = 21;
- public static final int MARKUP_TAG_ATTRIBUTE = 22;
-
- public static final int ERROR_IDENTIFIER = 23;
- public static final int ERROR_NUMBER_FORMAT = 24;
- public static final int ERROR_STRING_DOUBLE = 25;
- public static final int ERROR_CHAR = 26; // Char or single-quote string.
-
- public static final int NUM_TOKEN_TYPES = 27;
-
- /**
- * Creates a "null token." The token itself is not null; rather, it signifies that it is the last token in a linked
- * list of tokens and that it is not part of a "multi-line token."
- */
- public Token() {
- this.text = null;
- this.textOffset = -1;
- this.textCount = -1;
- this.type = NULL;
- offset = -1;
- hyperlink = false;
- nextToken = null;
- bgRect = new Rectangle2D.Float();
- }
-
- /**
- * Constructor.
- *
- * @param line
- * The segment from which to get the token.
- * @param beg
- * The first character's position in UnicodeWriter
with a
- * java.io.BufferedWriter
.
- *
+ *
+ * For optimum performance, it is recommended that you wrap all instances of
+ * UnicodeWriter
with a java.io.BufferedWriter
.
+ *
* @author Robert Futrell
* @version 0.7
*/
public class UnicodeWriter extends Writer {
- /**
- * If this system property evaluates to "false
", ignoring case, files written out as UTF-8 will not
- * have a BOM written for them. Otherwise (even if the property is not set), UTF-8 files will have a BOM written.
- */
- public static final String PROPERTY_WRITE_UTF8_BOM =
- "UnicodeWriter.writeUtf8BOM";
-
- /**
- * The writer actually doing the writing.
- */
- private OutputStreamWriter internalOut;
-
- private static final byte[] UTF8_BOM = new byte[] {
- (byte) 0xEF,
- (byte) 0xBB,
- (byte) 0xBF
- };
-
- private static final byte[] UTF16LE_BOM = new byte[] {
- (byte) 0xFF,
- (byte) 0xFE
- };
-
- private static final byte[] UTF16BE_BOM = new byte[] {
- (byte) 0xFE,
- (byte) 0xFF
- };
-
- private static final byte[] UTF32LE_BOM = new byte[] {
- (byte) 0xFF,
- (byte) 0xFE,
- (byte) 0x00,
- (byte) 0x00
- };
-
- private static final byte[] UTF32BE_BOM = new byte[] {
- (byte) 0x00,
- (byte) 0x00,
- (byte) 0xFE,
- (byte) 0xFF
- };
-
- /**
- * This is a utility constructor since the vast majority of the time, this class will be used to write Unicode
- * files.
- *
- * @param fileName
- * The file to which to write the Unicode output.
- * @param encoding
- * The encoding to use.
- * @throws UnsupportedEncodingException
- * If the specified encoding is not supported.
- * @throws IOException
- * If an IO exception occurs.
- */
- public UnicodeWriter(String fileName, String encoding)
- throws UnsupportedEncodingException, IOException {
- this(new FileOutputStream(fileName), encoding);
- }
-
- /**
- * This is a utility constructor since the vast majority of the time, this class will be used to write Unicode
- * files.
- *
- * @param file
- * The file to which to write the Unicode output.
- * @param encoding
- * The encoding to use.
- * @throws UnsupportedEncodingException
- * If the specified encoding is not supported.
- * @throws IOException
- * If an IO exception occurs.
- */
- public UnicodeWriter(File file, String encoding)
- throws UnsupportedEncodingException, IOException {
- this(new FileOutputStream(file), encoding);
- }
-
- /**
- * Creates a new writer.
- *
- * @param out
- * The output stream to write.
- * @param encoding
- * The encoding to use.
- * @throws UnsupportedEncodingException
- * If the specified encoding is not supported.
- * @throws IOException
- * If an IO exception occurs.
- */
- public UnicodeWriter(OutputStream out, String encoding)
- throws UnsupportedEncodingException, IOException {
- init(out, encoding);
- }
-
- /**
- * Closes this writer.
- *
- * @throws IOException
- * If an IO exception occurs.
- */
- public void close() throws IOException {
- internalOut.close();
- }
-
- /**
- * Flushes the stream.
- *
- * @throws IOException
- * If an IO exception occurs.
- */
- public void flush() throws IOException {
- internalOut.flush();
- }
-
- /**
- * Returns the encoding being used to write this output stream (i.e., the encoding of the file).
- *
- * @return The encoding of the stream.
- */
- public String getEncoding() {
- return internalOut.getEncoding();
- }
-
- /**
- * Returns whether UTF-8 files should have a BOM in them when written.
- *
- * @return Whether to write a BOM for UTF-8 files.
- */
- public static boolean getWriteUtf8BOM() {
- String prop = System.getProperty(PROPERTY_WRITE_UTF8_BOM);
- if (prop != null && Boolean.valueOf(prop).equals(Boolean.FALSE)) {
- return false;
- }
- return true;
- }
-
- /**
- * Initializes the internal output stream and writes the BOM if the specified encoding is a Unicode encoding.
- *
- * @param out
- * The output stream we are writing.
- * @param encoding
- * The encoding in which to write.
- * @throws UnsupportedEncodingException
- * If the specified encoding isn't supported.
- * @throws IOException
- * If an I/O error occurs while writing a BOM.
- */
- private void init(OutputStream out, String encoding)
- throws UnsupportedEncodingException, IOException {
-
- internalOut = new OutputStreamWriter(out, encoding);
-
- // Write the proper BOM if they specified a Unicode encoding.
- // NOTE: Creating an OutputStreamWriter with encoding "UTF-16" DOES
- // DOES write out the BOM; "UTF-16LE", "UTF-16BE", "UTF-32", "UTF-32LE"
- // and "UTF-32BE" don't.
- if ("UTF-8".equals(encoding)) {
- if (getWriteUtf8BOM()) {
- out.write(UTF8_BOM, 0, UTF8_BOM.length);
- }
- }
- else if ("UTF-16LE".equals(encoding)) {
- out.write(UTF16LE_BOM, 0, UTF16LE_BOM.length);
- }
- else if (/* "UTF-16".equals(encoding) || */"UTF-16BE".equals(encoding)) {
- out.write(UTF16BE_BOM, 0, UTF16BE_BOM.length);
- }
- else if ("UTF-32LE".equals(encoding)) {
- out.write(UTF32LE_BOM, 0, UTF32LE_BOM.length);
- }
- else if ("UTF-32".equals(encoding) || "UTF-32BE".equals(encoding)) {
- out.write(UTF32BE_BOM, 0, UTF32BE_BOM.length);
- }
-
- }
-
- /**
- * Writes a portion of an array of characters.
- *
- * @param cbuf
- * The buffer of characters.
- * @param off
- * The offset from which to start writing characters.
- * @param len
- * The number of characters to write.
- * @throws IOException
- * If an I/O error occurs.
- */
- public void write(char[] cbuf, int off, int len) throws IOException {
- internalOut.write(cbuf, off, len);
- }
-
- /**
- * Writes a single character.
- *
- * @param c
- * An integer specifying the character to write.
- * @throws IOException
- * If an IO error occurs.
- */
- public void write(int c) throws IOException {
- internalOut.write(c);
- }
-
- /**
- * Writes a portion of a string.
- *
- * @param str
- * The string from which to write.
- * @param off
- * The offset from which to start writing characters.
- * @param len
- * The number of characters to write.
- * @throws IOException
- * If an IO error occurs.
- */
- public void write(String str, int off, int len) throws IOException {
- internalOut.write(str, off, len);
- }
-
-}
\ No newline at end of file
+ /**
+ * If this system property evaluates to "false
", ignoring
+ * case, files written out as UTF-8 will not have a BOM written for them.
+ * Otherwise (even if the property is not set), UTF-8 files will have a
+ * BOM written.
+ */
+ public static final String PROPERTY_WRITE_UTF8_BOM =
+ "UnicodeWriter.writeUtf8BOM";
+
+
+ /**
+ * The writer actually doing the writing.
+ */
+ private OutputStreamWriter internalOut;
+
+ private static final byte[] UTF8_BOM = new byte[] {
+ (byte)0xEF,
+ (byte)0xBB,
+ (byte)0xBF
+ };
+
+ private static final byte[] UTF16LE_BOM = new byte[] {
+ (byte)0xFF,
+ (byte)0xFE
+ };
+
+ private static final byte[] UTF16BE_BOM = new byte[] {
+ (byte)0xFE,
+ (byte)0xFF
+ };
+
+ private static final byte[] UTF32LE_BOM = new byte[] {
+ (byte)0xFF,
+ (byte)0xFE,
+ (byte)0x00,
+ (byte)0x00
+ };
+
+ private static final byte[] UTF32BE_BOM = new byte[] {
+ (byte)0x00,
+ (byte)0x00,
+ (byte)0xFE,
+ (byte)0xFF
+ };
+
+
+ /**
+ * This is a utility constructor since the vast majority of the time, this
+ * class will be used to write Unicode files.
+ *
+ * @param fileName The file to which to write the Unicode output.
+ * @param encoding The encoding to use.
+ * @throws IOException If an IO exception occurs.
+ */
+ public UnicodeWriter(String fileName, String encoding) throws IOException {
+ this(new FileOutputStream(fileName), encoding);
+ }
+
+
+
+ /**
+ * This is a utility constructor since the vast majority of the time, this
+ * class will be used to write Unicode files.
+ *
+ * @param file The file to which to write the Unicode output.
+ * @param encoding The encoding to use.
+ * @throws IOException If an IO exception occurs.
+ */
+ public UnicodeWriter(File file, String encoding) throws IOException {
+ this(new FileOutputStream(file), encoding);
+ }
+
+
+
+ /**
+ * Creates a new writer.
+ *
+ * @param out The output stream to write.
+ * @param encoding The encoding to use.
+ * @throws IOException If an IO exception occurs.
+ */
+ public UnicodeWriter(OutputStream out, String encoding) throws IOException {
+ init(out, encoding);
+ }
+
+
+ /**
+ * Closes this writer.
+ *
+ * @throws IOException If an IO exception occurs.
+ */
+ @Override
+ public void close() throws IOException {
+ internalOut.close();
+ }
+
+
+ /**
+ * Flushes the stream.
+ *
+ * @throws IOException If an IO exception occurs.
+ */
+ @Override
+ public void flush() throws IOException {
+ internalOut.flush();
+ }
+
+
+ /**
+ * Returns the encoding being used to write this output stream (i.e., the
+ * encoding of the file).
+ *
+ * @return The encoding of the stream.
+ */
+ public String getEncoding() {
+ return internalOut.getEncoding();
+ }
+
+
+ /**
+ * Returns whether UTF-8 files should have a BOM in them when written.
+ *
+ * @return Whether to write a BOM for UTF-8 files.
+ * @see #setWriteUtf8BOM(boolean)
+ * @see UnicodeWriter
+ */
+ public static boolean getWriteUtf8BOM() {
+ String prop = System.getProperty(PROPERTY_WRITE_UTF8_BOM);
+ // We default to writing the BOM, for some reason.
+ if (prop!=null && Boolean.valueOf(prop).equals(Boolean.FALSE)) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Initializes the internal output stream and writes the BOM if the
+ * specified encoding is a Unicode encoding.
+ *
+ * @param out The output stream we are writing.
+ * @param encoding The encoding in which to write.
+ * @throws IOException If an I/O error occurs while writing a BOM.
+ */
+ private void init(OutputStream out, String encoding) throws IOException {
+
+ internalOut = new OutputStreamWriter(out, encoding);
+
+ // Write the proper BOM if they specified a Unicode encoding.
+ // NOTE: Creating an OutputStreamWriter with encoding "UTF-16" DOES
+ // DOES write out the BOM; "UTF-16LE", "UTF-16BE", "UTF-32", "UTF-32LE"
+ // and "UTF-32BE" don't.
+ if ("UTF-8".equals(encoding)) {
+ if (getWriteUtf8BOM()) {
+ out.write(UTF8_BOM, 0, UTF8_BOM.length);
+ }
+ }
+ else if ("UTF-16LE".equals(encoding)) {
+ out.write(UTF16LE_BOM, 0, UTF16LE_BOM.length);
+ }
+ else if (/*"UTF-16".equals(encoding) || */"UTF-16BE".equals(encoding)) {
+ out.write(UTF16BE_BOM, 0, UTF16BE_BOM.length);
+ }
+ else if ("UTF-32LE".equals(encoding)) {
+ out.write(UTF32LE_BOM, 0, UTF32LE_BOM.length);
+ }
+ else if ("UTF-32".equals(encoding) || "UTF-32BE".equals(encoding)) {
+ out.write(UTF32BE_BOM, 0, UTF32BE_BOM.length);
+ }
+
+ }
+
+
+ /**
+ * Sets whether UTF-8 files should have a BOM written in them.
+ *
+ * @param write Whether to write a BOM.
+ * @see #getWriteUtf8BOM()
+ * @see UnicodeWriter
+ */
+ public static void setWriteUtf8BOM(boolean write) {
+ System.setProperty(UnicodeWriter.PROPERTY_WRITE_UTF8_BOM,
+ Boolean.toString(write));
+ }
+
+
+ /**
+ * Writes a portion of an array of characters.
+ *
+ * @param cbuf The buffer of characters.
+ * @param off The offset from which to start writing characters.
+ * @param len The number of characters to write.
+ * @throws IOException If an I/O error occurs.
+ */
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ internalOut.write(cbuf, off, len);
+ }
+
+
+ /**
+ * Writes a single character.
+ *
+ * @param c An integer specifying the character to write.
+ * @throws IOException If an IO error occurs.
+ */
+ @Override
+ public void write(int c) throws IOException {
+ internalOut.write(c);
+ }
+
+
+ /**
+ * Writes a portion of a string.
+ *
+ * @param str The string from which to write.
+ * @param off The offset from which to start writing characters.
+ * @param len The number of characters to write.
+ * @throws IOException If an IO error occurs.
+ */
+ @Override
+ public void write(String str, int off, int len) throws IOException {
+ internalOut.write(str, off, len);
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/print/RPrintUtilities.java b/tools/agent_debugger/src/main/java/org/fife/print/RPrintUtilities.java
old mode 100644
new mode 100755
index 0c55ab4f1..364bb4a33
--- a/tools/agent_debugger/src/main/java/org/fife/print/RPrintUtilities.java
+++ b/tools/agent_debugger/src/main/java/org/fife/print/RPrintUtilities.java
@@ -3,545 +3,556 @@
*
* RPrintUtilities.java - A collection of static methods useful for printing
* text from Swing text components.
- * Copyright (C) 2003 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.print;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
-import javax.swing.text.Segment;
import javax.swing.text.JTextComponent;
+import javax.swing.text.Segment;
import javax.swing.text.TabExpander;
import javax.swing.text.Utilities;
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.print.*;
+
+
/**
* A collection of static methods useful for printing text from Swing text components.
- *
+ *
* @author Robert Futrell
* @version 1.0
*/
public abstract class RPrintUtilities {
- private static int currentDocLineNumber; // The line number in the document we are currently on.
- private static int numDocLines; // The number of lines in the current document.
- private static Element rootElement; // The first Element (line) in the current document.
-
- // The characters at which to break a line if implementing word wrap.
- private static final char[] breakChars = { ' ', '\t', ',', '.', ';', '?', '!' };
-
- // These variables are 'global' because RPrintTabExpander uses them.
- private static int xOffset; // The x-offset (for the page margin) when printing.
- private static int tabSizeInSpaces; // The length of a tab, in spaces.
- private static FontMetrics fm; // The metrics of the font currently being used to print.
-
- /**
- * Returns the position closest to, but before, position maxCharsPerLine
in line
of one of
- * the chars in breakChars
, or simply returns maxCharsPerLine-1
if none of the
- * breakChars
comes before that position. This position represents the logical line break for this
- * java.lang.String
if it is being printed in a monospaced font when lines can only be
- * maxCharsPerLine
characters long.
- *
- * @param line
- * The text being printed.
- * @param maxCharsPerLine
- * Only up-to this many characters from line
can be printed on one line.
- * @return The logical position at which to stop printing line
to simulate word wrap.
- */
- private static int getLineBreakPoint(String line, final int maxCharsPerLine) {
-
- int breakPoint = -1;
- for (char breakChar : breakChars) {
- int breakCharPos = line.lastIndexOf(breakChar, maxCharsPerLine - 1);
- if (breakCharPos > breakPoint)
- breakPoint = breakCharPos;
- }
-
- return (breakPoint == -1 ? maxCharsPerLine - 1 : breakPoint);
-
- }
-
- /**
- * Prints a Document
using a monospaced font, and does no word wrapping (ie, words will wrap mid-word
- * to the next line). This method is expected to be called from Printable 'print(Graphics g)' functions.
- *
- * @param g
- * The graphics context to write to.
- * @param doc
- * The javax.swing.text.Document
to print.
- * @param fontSize
- * the point size to use for the monospaced font.
- * @param pageIndex
- * The page number to print.
- * @param pageFormat
- * The format to print the page with.
- * @param tabSize
- * The number of spaces to expand tabs to.
- *
- * @see #printDocumentMonospacedWordWrap
- */
- public static int printDocumentMonospaced(Graphics g, Document doc, int fontSize, int pageIndex,
- PageFormat pageFormat, int tabSize) {
-
- g.setColor(Color.BLACK);
- g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
-
- // Initialize our static variables (these are used by our tab expander below).
- tabSizeInSpaces = tabSize;
- fm = g.getFontMetrics();
-
- // Create our tab expander.
- // RPrintTabExpander tabExpander = new RPrintTabExpander();
-
- // Get width and height of characters in this monospaced font.
- int fontWidth = fm.charWidth('w'); // Any character will do as font is monospaced.
- int fontHeight = fm.getHeight();
-
- int MAX_CHARS_PER_LINE = (int) pageFormat.getImageableWidth() / fontWidth;
- int MAX_LINES_PER_PAGE = (int) pageFormat.getImageableHeight() / fontHeight;
-
- final int STARTING_LINE_NUMBER = MAX_LINES_PER_PAGE * pageIndex;
-
- // The (x,y) coordinate to print at (in pixels, not characters).
- // Since y is the baseline of where we'll start printing (not the top-left
- // corner), we offset it by the font's ascent ( + 1 just for good measure).
- xOffset = (int) pageFormat.getImageableX();
- int y = (int) pageFormat.getImageableY() + fm.getAscent() + 1;
-
- // A counter to keep track of the number of lines that WOULD HAVE been
- // printed if we were printing all lines.
- int numPrintedLines = 0;
-
- // Keep going while there are more lines in the document.
- currentDocLineNumber = 0; // The line number of the document we're currently on.
- rootElement = doc.getDefaultRootElement(); // To shorten accesses in our loop.
- numDocLines = rootElement.getElementCount(); // The number of lines in our document.
- while (currentDocLineNumber < numDocLines) {
-
- // Get the line we are going to print.
- String curLineString;
- Element currentLine = rootElement.getElement(currentDocLineNumber);
- int startOffs = currentLine.getStartOffset();
- try {
- curLineString = doc.getText(startOffs, currentLine.getEndOffset() - startOffs);
- } catch (BadLocationException ble) { // Never happens
- ble.printStackTrace();
- return Printable.NO_SUCH_PAGE;
- }
-
- // Get rid of newlines, because they end up as boxes if you don't; this is a monospaced font.
- curLineString = curLineString.replaceAll("\n", "");
-
- // Replace tabs with how many spaces they should be.
- if (tabSizeInSpaces == 0) {
- curLineString = curLineString.replaceAll("\t", "");
- }
- else {
- int tabIndex = curLineString.indexOf('\t');
- while (tabIndex > -1) {
- int spacesNeeded = tabSizeInSpaces - (tabIndex % tabSizeInSpaces);
- String replacementString = "";
- for (int i = 0; i < spacesNeeded; i++)
- replacementString += ' ';
- // Note that "\t" is actually a regex for this method.
- curLineString = curLineString.replaceFirst("\t", replacementString);
- tabIndex = curLineString.indexOf('\t');
- }
- }
-
- // If this document line is too long to fit on one printed line on the page,
- // break it up into multpile lines.
- while (curLineString.length() > MAX_CHARS_PER_LINE) {
-
- numPrintedLines++;
- if (numPrintedLines > STARTING_LINE_NUMBER) {
- g.drawString(curLineString.substring(0, MAX_CHARS_PER_LINE), xOffset, y);
- y += fontHeight;
- if (numPrintedLines == STARTING_LINE_NUMBER + MAX_LINES_PER_PAGE)
- return Printable.PAGE_EXISTS;
- }
-
- curLineString = curLineString.substring(MAX_CHARS_PER_LINE, curLineString.length());
-
- }
-
- currentDocLineNumber += 1; // We have printed one more line from the document.
-
- numPrintedLines++;
- if (numPrintedLines > STARTING_LINE_NUMBER) {
- g.drawString(curLineString, xOffset, y);
- y += fontHeight;
- if (numPrintedLines == STARTING_LINE_NUMBER + MAX_LINES_PER_PAGE)
- return Printable.PAGE_EXISTS;
- }
-
- }
-
- // Now, the whole document has been "printed." Decide if this page had any text on it or not.
- if (numPrintedLines > STARTING_LINE_NUMBER)
- return Printable.PAGE_EXISTS;
- return Printable.NO_SUCH_PAGE;
-
- }
-
- /**
- * Prints a Document
using a monospaced font, word wrapping on the characters ' ', '\t', '\n', ',',
- * '.', and ';'. This method is expected to be called from Printable 'print(Graphics g)' functions.
- *
- * @param g
- * The graphics context to write to.
- * @param doc
- * The javax.swing.text.Document
to print.
- * @param fontSize
- * the point size to use for the monospaced font.
- * @param pageIndex
- * The page number to print.
- * @param pageFormat
- * The format to print the page with.
- * @param tabSize
- * The number of spaces to expand tabs to.
- *
- * @see #printDocumentMonospaced
- */
- public static int printDocumentMonospacedWordWrap(Graphics g, Document doc,
- int fontSize, int pageIndex,
- PageFormat pageFormat, int tabSize) {
-
- g.setColor(Color.BLACK);
- g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
-
- // Initialize our static variables (these are used by our tab expander below).
- tabSizeInSpaces = tabSize;
- fm = g.getFontMetrics();
-
- // Create our tab expander.
- // RPrintTabExpander tabExpander = new RPrintTabExpander();
-
- // Get width and height of characters in this monospaced font.
- int fontWidth = fm.charWidth('w'); // Any character will do here, since font is monospaced.
- int fontHeight = fm.getHeight();
-
- int MAX_CHARS_PER_LINE = (int) pageFormat.getImageableWidth() / fontWidth;
- int MAX_LINES_PER_PAGE = (int) pageFormat.getImageableHeight() / fontHeight;
-
- final int STARTING_LINE_NUMBER = MAX_LINES_PER_PAGE * pageIndex;
-
- // The (x,y) coordinate to print at (in pixels, not characters).
- // Since y is the baseline of where we'll start printing (not the top-left
- // corner), we offset it by the font's ascent ( + 1 just for good measure).
- xOffset = (int) pageFormat.getImageableX();
- int y = (int) pageFormat.getImageableY() + fm.getAscent() + 1;
-
- // A counter to keep track of the number of lines that WOULD HAVE been
- // printed if we were printing all lines.
- int numPrintedLines = 0;
-
- // Keep going while there are more lines in the document.
- currentDocLineNumber = 0; // The line number of the document we're currently on.
- rootElement = doc.getDefaultRootElement(); // To shorten accesses in our loop.
- numDocLines = rootElement.getElementCount(); // The number of lines in our document.
- while (currentDocLineNumber < numDocLines) {
-
- // Get the line we are going to print.
- String curLineString;
- Element currentLine = rootElement.getElement(currentDocLineNumber);
- int startOffs = currentLine.getStartOffset();
- try {
- curLineString = doc.getText(startOffs, currentLine.getEndOffset() - startOffs);
- } catch (BadLocationException ble) { // Never happens
- ble.printStackTrace();
- return Printable.NO_SUCH_PAGE;
- }
-
- // Remove newlines, because they end up as boxes if you don't; this is a monospaced font.
- curLineString = curLineString.replaceAll("\n", "");
-
- // Replace tabs with how many spaces they should be.
- if (tabSizeInSpaces == 0) {
- curLineString = curLineString.replaceAll("\t", "");
- }
- else {
- int tabIndex = curLineString.indexOf('\t');
- while (tabIndex > -1) {
- int spacesNeeded = tabSizeInSpaces - (tabIndex % tabSizeInSpaces);
- String replacementString = "";
- for (int i = 0; i < spacesNeeded; i++)
- replacementString += ' ';
- // Note that "\t" is actually a regex for this method.
- curLineString = curLineString.replaceFirst("\t", replacementString);
- tabIndex = curLineString.indexOf('\t');
- }
- }
-
- // If this document line is too long to fit on one printed line on the page,
- // break it up into multpile lines.
- while (curLineString.length() > MAX_CHARS_PER_LINE) {
-
- int breakPoint = getLineBreakPoint(curLineString, MAX_CHARS_PER_LINE) + 1;
-
- numPrintedLines++;
- if (numPrintedLines > STARTING_LINE_NUMBER) {
- g.drawString(curLineString.substring(0, breakPoint), xOffset, y);
- y += fontHeight;
- if (numPrintedLines == STARTING_LINE_NUMBER + MAX_LINES_PER_PAGE)
- return Printable.PAGE_EXISTS;
- }
-
- curLineString = curLineString.substring(breakPoint, curLineString.length());
-
- }
-
- currentDocLineNumber += 1; // We have printed one more line from the document.
-
- numPrintedLines++;
- if (numPrintedLines > STARTING_LINE_NUMBER) {
- g.drawString(curLineString, xOffset, y);
- y += fontHeight;
- if (numPrintedLines == STARTING_LINE_NUMBER + MAX_LINES_PER_PAGE)
- return Printable.PAGE_EXISTS;
- }
-
- }
-
- // Now, the whole document has been "printed." Decide if this page had any text on it or not.
- if (numPrintedLines > STARTING_LINE_NUMBER)
- return Printable.PAGE_EXISTS;
- return Printable.NO_SUCH_PAGE;
-
- }
-
- /**
- * Prints a Document
using the specified font, word wrapping on the characters ' ', '\t', '\n', ',',
- * '.', and ';'. This method is expected to be called from Printable 'print(Graphics g)' functions.
- *
- * @param g
- * The graphics context to write to.
- * @param textComponent
- * The javax.swing.text.JTextComponent
whose text you're printing.
- * @param font
- * The font to use for printing. If null
, then textComponent
's font is used.
- * @param pageIndex
- * The page number to print.
- * @param pageFormat
- * The format to print the page with.
- * @param tabSize
- * The number of spaces to convert tabs to.
- *
- */
- public static int printDocumentWordWrap(Graphics g, JTextComponent textComponent,
- Font font, int pageIndex,
- PageFormat pageFormat,
- int tabSize) {
-
- // Initialize our graphics object.
- g.setColor(Color.BLACK);
- g.setFont(font != null ? font : textComponent.getFont());
-
- // Initialize our static variables (these are used by our tab expander below).
- tabSizeInSpaces = tabSize;
- fm = g.getFontMetrics();
- int fontHeight = fm.getHeight();
-
- final int LINE_LENGTH_IN_PIXELS = (int) pageFormat.getImageableWidth();
- final int MAX_LINES_PER_PAGE = (int) pageFormat.getImageableHeight() / fontHeight;
-
- final int STARTING_LINE_NUMBER = MAX_LINES_PER_PAGE * pageIndex;
-
- // Create our tab expander.
- RPrintTabExpander tabExpander = new RPrintTabExpander();
-
- // The (x,y) coordinate to print at (in pixels, not characters).
- // Since y is the baseline of where we'll start printing (not the top-left
- // corner), we offset it by the font's ascent ( + 1 just for good measure).
- xOffset = (int) pageFormat.getImageableX();
- int y = (int) pageFormat.getImageableY() + fm.getAscent() + 1;
-
- // A counter to keep track of the number of lines that WOULD HAVE been
- // printed if we were printing all lines.
- int numPrintedLines = 0;
-
- // Keep going while there are more lines in the document.
- Document doc = textComponent.getDocument();
- rootElement = doc.getDefaultRootElement();
- numDocLines = rootElement.getElementCount(); // The number of lines in our document.
- currentDocLineNumber = 0; // The line number of the document we're currently on.
- int startingOffset = 0; // Used when a line is so long it has to be wrapped.
- while (currentDocLineNumber < numDocLines) {
-
- Segment currentLineSeg = new Segment();
-
- // Get the current line (as an Element), and its starting and ending offset in doc.
- Element currentLine = rootElement.getElement(currentDocLineNumber);
- int currentLineStart = currentLine.getStartOffset();
- int currentLineEnd = currentLine.getEndOffset();
-
- // Put the chars of this line in currentLineSeg, but only starting at our desired offset
- // (because this line may be the second part of a wrapped line, so we'd start after the part
- // that has already been printed).
- try {
- doc.getText(currentLineStart + startingOffset, currentLineEnd - (currentLineStart + startingOffset),
- currentLineSeg);
- } catch (BadLocationException ble) {
- System.err.println("BadLocationException in print (where there shouldn't be one!): " + ble);
- return Printable.NO_SUCH_PAGE;
- }
-
- // Remove any spaces and/or tabs from the end of the segment (would cause problems if you left 'em).
- currentLineSeg = removeEndingWhitespace(currentLineSeg);
-
- // Figger out how long the line is, in pixels.
- int currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander, 0);
-
- // System.err.println("'" + currentLineSeg + "' - " + currentLineLengthInPixels + "/" +
- // LINE_LENGTH_IN_PIXELS);
- // If it'll fit by itself on a printed line, great.
- if (currentLineLengthInPixels <= LINE_LENGTH_IN_PIXELS) {
- currentDocLineNumber += 1; // We (will) have printed one more line from the document.
- startingOffset = 0; // Start at the first character in the new document line.
- }
-
- // If it won't fit on a printed line by itself (i.e., it needs to be wrapped)...
- else {
-
- // Loop while the current line is too long to fit on a printed line.
- int currentPos = -1;
- while (currentLineLengthInPixels > LINE_LENGTH_IN_PIXELS) {
-
- // System.err.println("'" + currentLineSeg + "' - " + currentLineLengthInPixels + "/" +
- // LINE_LENGTH_IN_PIXELS);
-
- // Remove any spaces and/or tabs from the end of the segment (would cause problems if you left 'em).
- currentLineSeg = removeEndingWhitespace(currentLineSeg);
-
- // currentPos will be the last position in the current text of a "line break character."
- currentPos = -1;
- String currentLineString = currentLineSeg.toString();
- for (char breakChar : breakChars) {
- // "+1" below so we include the character on the line.
- int pos = currentLineString.lastIndexOf(breakChar) + 1;
- // if (pos>-1 && pos>currentPos)
- // currentPos = pos;
- if (pos > 0 && pos > currentPos & pos != currentLineString.length())
- currentPos = pos;
- }
-
- // If we didn't find a line break character, we'll simply break the line at
- // the last character that fits on a printed line.
- // So here, we set currentPos to be the position of the last character that fits
- // on the current printed line.
- if (currentPos == -1) {
-
- // Fix currentLineSeg so that it contains exactly enough text to fit in
- // LINE_LENGTH_IN_PIXELS pixels...
- currentPos = 0;
- do {
- currentPos++;
- try {
- doc.getText(currentLineStart + startingOffset, currentPos, currentLineSeg);
- } catch (BadLocationException ble) {
- System.err.println(ble);
- return Printable.NO_SUCH_PAGE;
- }
- currentLineLengthInPixels = Utilities.
- getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander, 0);
- } while (currentLineLengthInPixels <= LINE_LENGTH_IN_PIXELS);
- currentPos--;
-
- }
-
- try {
- doc.getText((currentLineStart + startingOffset), currentPos, currentLineSeg);
- } catch (BadLocationException ble) {
- System.err.println("BadLocationException in print (a):");
- System.err.println("==> currentLineStart: " + currentLineStart +
- "; startingOffset: " + startingOffset + "; currentPos: " + currentPos);
- System.err.println("==> Range: " + (currentLineStart + startingOffset) + " - " +
- (currentLineStart + startingOffset + currentPos));
- ble.printStackTrace();
- return Printable.NO_SUCH_PAGE;
- }
-
- currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander, 0);
- } // End of while (currentLineLengthInPixels > LINE_LENGTH_IN_PIXELS).
-
- startingOffset += currentPos; // Where to start (offset from line's start), since this line wraps.
-
- } // End of else.
-
- numPrintedLines++;
- if (numPrintedLines > STARTING_LINE_NUMBER) {
- // g.drawString(currentLineSeg.toString(), xOffset,y);
- Utilities.drawTabbedText(currentLineSeg, xOffset, y, g, tabExpander, 0);
- y += fontHeight;
- if (numPrintedLines == STARTING_LINE_NUMBER + MAX_LINES_PER_PAGE)
- return Printable.PAGE_EXISTS;
- }
-
- }
-
- // Now, the whole document has been "printed." Decide if this page had any text on it or not.
- if (numPrintedLines > STARTING_LINE_NUMBER)
- return Printable.PAGE_EXISTS;
- return Printable.NO_SUCH_PAGE;
-
- }
-
- /**
- * Removes any spaces or tabs from the end of the segment.
- *
- * @param segment
- * The segment from which to remove tailing whitespace.
- * @return segment
with trailing whitespace removed.
- */
- private static Segment removeEndingWhitespace(Segment segment) {
- int toTrim = 0;
- char currentChar = segment.setIndex(segment.getEndIndex() - 1);
- while ((currentChar == ' ' || currentChar == '\t') && currentChar != Segment.DONE) {
- toTrim++;
- currentChar = segment.previous();
- }
- String stringVal = segment.toString();
- String newStringVal = stringVal.substring(0, stringVal.length() - toTrim);
- return new Segment(newStringVal.toCharArray(), 0, newStringVal.length());
- }
-
- /**
- * A tab expander for the document currently being printed with the font being used for the printing.
- */
- private static class RPrintTabExpander implements TabExpander {
-
- RPrintTabExpander() {
- }
-
- public float nextTabStop(float x, int tabOffset) {
- if (tabSizeInSpaces == 0)
- return x;
- int tabSizeInPixels = tabSizeInSpaces * fm.charWidth(' ');
- int ntabs = (((int) x) - xOffset) / tabSizeInPixels;
- return xOffset + ((ntabs + 1) * tabSizeInPixels);
- }
-
- }
-
-}
\ No newline at end of file
+ private static int currentDocLineNumber; // The line number in the document we are currently on.
+ private static int numDocLines; // The number of lines in the current document.
+ private static Element rootElement; // The first Element (line) in the current document.
+
+ // The characters at which to break a line if implementing word wrap.
+ private static final char [] BREAK_CHARS = { ' ', '\t', ',', '.', ';', '?', '!' };
+
+ // These variables are 'global' because RPrintTabExpander uses them.
+
+ /**
+ * The x-offset (for the page margin) when printing.
+ */
+ private static int xOffset;
+
+ /**
+ * The length of a tab, in spaces.
+ */
+ private static int tabSizeInSpaces;
+
+ /**
+ * The metrics of the font currently being used to print.
+ */
+ private static FontMetrics fm;
+
+
+ /**
+ * Returns the position closest to, but before, position maxCharsPerLine
in
+ * line
of one of the chars in breakChars
, or simply returns
+ * maxCharsPerLine-1
if none of the breakChars
comes before
+ * that position. This position represents the logical line break for this java.lang.String
+ * if it is being printed in a monospaced font when lines can only be maxCharsPerLine
+ * characters long.
+ *
+ * @param line The text being printed.
+ * @param maxCharsPerLine Only up-to this many characters from
+ * line
can be printed on one line.
+ * @return The logical position at which to stop printing line
+ * to simulate word wrap.
+ */
+ private static int getLineBreakPoint(String line, final int maxCharsPerLine) {
+
+ int breakPoint = -1;
+ for (char breakChar : BREAK_CHARS) {
+ int breakCharPos = line.lastIndexOf(breakChar, maxCharsPerLine - 1);
+ if (breakCharPos > breakPoint) {
+ breakPoint = breakCharPos;
+ }
+ }
+
+ return (breakPoint==-1 ? maxCharsPerLine-1 : breakPoint);
+
+ }
+
+
+ /**
+ * Prints a Document
using a monospaced font, and does no word wrapping (ie,
+ * words will wrap mid-word to the next line). This method is expected to be called from
+ * Printable 'print(Graphics g)' functions.
+ *
+ * @param g The graphics context to write to.
+ * @param doc The javax.swing.text.Document
to print.
+ * @param fontSize the point size to use for the monospaced font.
+ * @param pageIndex The page number to print.
+ * @param pageFormat The format to print the page with.
+ * @param tabSize The number of spaces to expand tabs to.
+ *
+ * @see #printDocumentMonospacedWordWrap
+ */
+ public static int printDocumentMonospaced(Graphics g, Document doc, int fontSize, int pageIndex,
+ PageFormat pageFormat, int tabSize) {
+
+ g.setColor(Color.BLACK);
+ g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
+
+ // Initialize our static variables (these are used by our tab expander below).
+ tabSizeInSpaces = tabSize;
+ fm = g.getFontMetrics();
+
+ // Create our tab expander.
+ //RPrintTabExpander tabExpander = new RPrintTabExpander();
+
+ // Get width and height of characters in this monospaced font.
+ int fontWidth = fm.charWidth('w'); // Any character will do as font is monospaced.
+ int fontHeight = fm.getHeight();
+
+ int maxCharsPerLine = (int)pageFormat.getImageableWidth() / fontWidth;
+ int maxLinesPerPage = (int)pageFormat.getImageableHeight() / fontHeight;
+
+ final int startingLineNumber = maxLinesPerPage * pageIndex;
+
+ // The (x,y) coordinate to print at (in pixels, not characters).
+ // Since y is the baseline of where we'll start printing (not the top-left
+ // corner), we offset it by the font's ascent ( + 1 just for good measure).
+ xOffset = (int)pageFormat.getImageableX();
+ int y = (int)pageFormat.getImageableY() + fm.getAscent() + 1;
+
+ // A counter to keep track of the number of lines that WOULD HAVE been
+ // printed if we were printing all lines.
+ int numPrintedLines = 0;
+
+ // Keep going while there are more lines in the document.
+ currentDocLineNumber = 0; // The line number of the document we're currently on.
+ rootElement = doc.getDefaultRootElement(); // To shorten accesses in our loop.
+ numDocLines = rootElement.getElementCount(); // The number of lines in our document.
+ while (currentDocLineNumberDocument
using a monospaced font, word wrapping on
+ * the characters ' ', '\t', '\n', ',', '.', and ';'. This method is
+ * expected to be called from Printable 'print(Graphics g)' functions.
+ *
+ * @param g The graphics context to write to.
+ * @param doc The javax.swing.text.Document
to print.
+ * @param fontSize the point size to use for the monospaced font.
+ * @param pageIndex The page number to print.
+ * @param pageFormat The format to print the page with.
+ * @param tabSize The number of spaces to expand tabs to.
+ *
+ * @see #printDocumentMonospaced
+ */
+ public static int printDocumentMonospacedWordWrap(Graphics g, Document doc,
+ int fontSize, int pageIndex,
+ PageFormat pageFormat, int tabSize) {
+
+ g.setColor(Color.BLACK);
+ g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
+
+ // Initialize our static variables (these are used by our tab expander below).
+ tabSizeInSpaces = tabSize;
+ fm = g.getFontMetrics();
+
+ // Create our tab expander.
+ //RPrintTabExpander tabExpander = new RPrintTabExpander();
+
+ // Get width and height of characters in this monospaced font.
+ int fontWidth = fm.charWidth('w'); // Any character will do here, since font is monospaced.
+ int fontHeight = fm.getHeight();
+
+ int maxCharsPerLine = (int)pageFormat.getImageableWidth() / fontWidth;
+ int maxLinesPerPage = (int)pageFormat.getImageableHeight() / fontHeight;
+
+ final int startingLineNumber = maxLinesPerPage * pageIndex;
+
+ // The (x,y) coordinate to print at (in pixels, not characters).
+ // Since y is the baseline of where we'll start printing (not the top-left
+ // corner), we offset it by the font's ascent ( + 1 just for good measure).
+ xOffset = (int)pageFormat.getImageableX();
+ int y = (int)pageFormat.getImageableY() + fm.getAscent() + 1;
+
+ // A counter to keep track of the number of lines that WOULD HAVE been
+ // printed if we were printing all lines.
+ int numPrintedLines = 0;
+
+ // Keep going while there are more lines in the document.
+ currentDocLineNumber = 0; // The line number of the document we're currently on.
+ rootElement = doc.getDefaultRootElement(); // To shorten accesses in our loop.
+ numDocLines = rootElement.getElementCount(); // The number of lines in our document.
+ while (currentDocLineNumberDocument
using the specified font, word wrapping
+ * on the characters ' ', '\t', '\n', ',', '.', and ';'. This method is
+ * expected to be called from Printable 'print(Graphics g)' functions.
+ *
+ * @param g The graphics context to write to.
+ * @param textComponent The javax.swing.text.JTextComponent
+ * whose text you're printing.
+ * @param font The font to use for printing. If null
, then
+ * textComponent
's font is used.
+ * @param pageIndex The page number to print.
+ * @param pageFormat The format to print the page with.
+ * @param tabSize The number of spaces to convert tabs to.
+ *
+ */
+ public static int printDocumentWordWrap(Graphics g, JTextComponent textComponent,
+ Font font, int pageIndex,
+ PageFormat pageFormat,
+ int tabSize) {
+
+ // Initialize our graphics object.
+ g.setColor(Color.BLACK);
+ g.setFont(font!=null ? font : textComponent.getFont());
+
+ // Initialize our static variables (these are used by our tab expander below).
+ tabSizeInSpaces = tabSize;
+ fm = g.getFontMetrics();
+ int fontHeight = fm.getHeight();
+
+ final int lineLengthInPixels = (int)pageFormat.getImageableWidth();
+ final int maxLinesPerPage = (int)pageFormat.getImageableHeight() / fontHeight;
+
+ final int startingLineNumber = maxLinesPerPage * pageIndex;
+
+ // Create our tab expander.
+ RPrintTabExpander tabExpander = new RPrintTabExpander();
+
+ // The (x,y) coordinate to print at (in pixels, not characters).
+ // Since y is the baseline of where we'll start printing (not the top-left
+ // corner), we offset it by the font's ascent ( + 1 just for good measure).
+ xOffset = (int)pageFormat.getImageableX();
+ int y = (int)pageFormat.getImageableY() + fm.getAscent() + 1;
+
+ // A counter to keep track of the number of lines that WOULD HAVE been
+ // printed if we were printing all lines.
+ int numPrintedLines = 0;
+
+ // Keep going while there are more lines in the document.
+ Document doc = textComponent.getDocument();
+ rootElement = doc.getDefaultRootElement();
+ numDocLines = rootElement.getElementCount(); // The number of lines in our document.
+ currentDocLineNumber = 0; // The line number of the document we're currently on.
+ int startingOffset = 0; // Used when a line is so long it has to be wrapped.
+ while (currentDocLineNumbersegment
with trailing whitespace removed.
+ */
+ private static Segment removeEndingWhitespace(Segment segment) {
+ int toTrim = 0;
+ char currentChar = segment.setIndex(segment.getEndIndex()-1);
+ while ((currentChar==' ' || currentChar=='\t') && currentChar!=Segment.DONE) {
+ toTrim++;
+ currentChar = segment.previous();
+ }
+ String stringVal = segment.toString();
+ String newStringVal = stringVal.substring(0,stringVal.length()-toTrim);
+ return new Segment(newStringVal.toCharArray(), 0, newStringVal.length());
+ }
+
+
+ /**
+ * A tab expander for the document currently being printed with the
+ * font being used for the printing.
+ */
+ private static class RPrintTabExpander implements TabExpander {
+
+ RPrintTabExpander() {
+ }
+
+ @Override
+ public float nextTabStop(float x, int tabOffset) {
+ if (tabSizeInSpaces == 0) {
+ return x;
+ }
+ int tabSizeInPixels = tabSizeInSpaces * fm.charWidth(' ');
+ int ntabs = (((int) x) - xOffset) / tabSizeInPixels;
+ return xOffset + ((ntabs + 1f) * tabSizeInPixels);
+ }
+
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractJFlexCTokenMaker.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractJFlexCTokenMaker.java
old mode 100644
new mode 100755
index 85bfdb747..cedfb7d20
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractJFlexCTokenMaker.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractJFlexCTokenMaker.java
@@ -3,23 +3,9 @@
*
* AbstractJFlexCTokenMaker.java - Base class for token makers that use curly
* braces to denote code blocks, such as C, C++, Java, Perl, etc.
- * Copyright (C) 2009 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -32,181 +18,271 @@
import org.fife.ui.rtextarea.RTextArea;
+
/**
- * Base class for JFlex-based token makers using C-style syntax. This class knows how to auto-indent after opening
- * braces and parens.
- *
+ * Base class for JFlex-based token makers using C-style syntax. This class
+ * knows how to:
+ *
+ *
+ *
+ *
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
- protected static final Action INSERT_BREAK_ACTION = new InsertBreakAction();
-
- /**
- * Returns true
always as C-style languages use curly braces to denote code blocks.
- *
- * @return true
always.
- */
- public boolean getCurlyBracesDenoteCodeBlocks() {
- return true;
- }
-
- /**
- * Returns an action to handle "insert break" key presses (i.e. Enter). An action is returned that handles newlines
- * differently in multi-line comments.
- *
- * @return The action.
- */
- public Action getInsertBreakAction() {
- return INSERT_BREAK_ACTION;
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean getShouldIndentNextLineAfter(Token t) {
- if (t != null && t.textCount == 1) {
- char ch = t.text[t.textOffset];
- return ch == '{' || ch == '(';
- }
- return false;
- }
-
- /**
- * Action that knows how to special-case inserting a newline in a multi-line comment for languages like C and Java.
- */
- private static class InsertBreakAction extends
- RSyntaxTextAreaEditorKit.InsertBreakAction {
-
- private static final Pattern p =
- Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- if (!textArea.isEditable() || !textArea.isEnabled()) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- return;
- }
-
- RSyntaxTextArea rsta = (RSyntaxTextArea) getTextComponent(e);
- RSyntaxDocument doc = (RSyntaxDocument) rsta.getDocument();
-
- int line = textArea.getCaretLineNumber();
- int type = doc.getLastTokenTypeOnLine(line);
-
- // Only in MLC's should we try this
- if (type == Token.COMMENT_DOCUMENTATION ||
- type == Token.COMMENT_MULTILINE) {
- insertBreakInMLC(e, rsta, line);
- }
- else {
- handleInsertBreak(rsta, true);
- }
-
- }
-
- /**
- * Returns whether the MLC token containing offs
appears to have a "nested" comment (i.e., contains
- * "/*
" somewhere inside of it). This implies that it is likely a "new" MLC and needs to be closed.
- * While not foolproof, this is usually good enough of a sign.
- *
- * @param textArea
- * @param line
- * @param offs
- * @return Whether a comment appears to be nested inside this one.
- */
- private boolean appearsNested(RSyntaxTextArea textArea,
- int line, int offs) {
-
- final int firstLine = line; // Remember the line we start at.
-
- while (line < textArea.getLineCount()) {
- Token t = textArea.getTokenListForLine(line);
- int i = 0;
- // If examining the first line, start at offs.
- if (line++ == firstLine) {
- t = RSyntaxUtilities.getTokenAtOffset(t, offs);
- if (t == null) { // offs was at end of the line
- continue;
- }
- i = t.documentToToken(offs);
- }
- else {
- i = t.textOffset;
- }
- while (i < t.textOffset + t.textCount - 1) {
- if (t.text[i] == '/' && t.text[i + 1] == '*') {
- return true;
- }
- i++;
- }
- // If tokens come after this one on this line, our MLC ended.
- if (t.getNextToken() != null) {
- return false;
- }
- }
-
- return true; // No match - MLC goes to the end of the file
-
- }
-
- private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea,
- int line) {
-
- Matcher m = null;
- int start = -1;
- int end = -1;
- try {
- start = textArea.getLineStartOffset(line);
- end = textArea.getLineEndOffset(line);
- String text = textArea.getText(start, end - start);
- m = p.matcher(text);
- } catch (BadLocationException ble) { // Never happens
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- ble.printStackTrace();
- return;
- }
-
- if (m.lookingAt()) {
-
- String leadingWS = m.group(1);
- String mlcMarker = m.group(2);
-
- // If the caret is "inside" any leading whitespace or MLC
- // marker, move it to the end of the line.
- int dot = textArea.getCaretPosition();
- if (dot >= start &&
- dot < start + leadingWS.length() + mlcMarker.length()) {
- // If we're in the whitespace before the very start of the
- // MLC though, just insert a normal newline
- if (mlcMarker.charAt(0) == '/') {
- handleInsertBreak(textArea, true);
- return;
- }
- textArea.setCaretPosition(end - 1);
- }
-
- boolean firstMlcLine = mlcMarker.charAt(0) == '/';
- boolean nested = appearsNested(textArea, line,
- start + leadingWS.length() + 2);
- String header = leadingWS +
- (firstMlcLine ? " * " : "*") +
- m.group(3);
- textArea.replaceSelection("\n" + header);
- if (nested) {
- dot = textArea.getCaretPosition(); // Has changed
- textArea.insert("\n" + leadingWS + " */", dot);
- textArea.setCaretPosition(dot);
- }
-
- }
- else {
- handleInsertBreak(textArea, true);
- }
-
- }
-
- }
-
-}
\ No newline at end of file
+ private final Action INSERT_BREAK_ACTION;
+
+ private static final Pattern MLC_PATTERN =
+ Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
+
+
+ protected AbstractJFlexCTokenMaker() {
+ INSERT_BREAK_ACTION = createInsertBreakAction();
+ }
+
+
+ /**
+ * Creates and returns the action to use when the user inserts a newline.
+ * The default implementation intelligently closes multi-line comments.
+ * Subclasses can override.
+ *
+ * @return The action.
+ * @see #getInsertBreakAction()
+ */
+ protected Action createInsertBreakAction() {
+ return new CStyleInsertBreakAction();
+ }
+
+
+ /**
+ * Returns true
always as C-style languages use curly braces
+ * to denote code blocks.
+ *
+ * @return true
always.
+ */
+ @Override
+ public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
+ return true;
+ }
+
+
+ /**
+ * Returns an action to handle "insert break" key presses (i.e. Enter).
+ * An action is returned that handles newlines differently in multi-line
+ * comments.
+ *
+ * @return The action.
+ */
+ @Override
+ public Action getInsertBreakAction() {
+ return INSERT_BREAK_ACTION;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean getMarkOccurrencesOfTokenType(int type) {
+ return type==Token.IDENTIFIER || type==Token.FUNCTION;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean getShouldIndentNextLineAfter(Token t) {
+ if (t!=null && t.length()==1) {
+ char ch = t.charAt(0);
+ return ch=='{' || ch=='(';
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns whether a given token is an internal token type that represents
+ * an MLC or documentation comment continuing on to the next line. This is
+ * done by languages such as JavaScript that are a little more verbose
+ * than necessary so that their code can be copy-and-pasted into other
+ * TokenMaker
s that use them as nested languages (such as
+ * HTML, JSP, etc.).
+ *
+ * @param t The token to check. This cannot be null
.
+ * @return Whether the token is an internal token representing the end of
+ * a line for an MLC/doc comment continuing on to the next line.
+ */
+ private boolean isInternalEolTokenForMLCs(Token t) {
+ int type = t.getType();
+ if (type<0) {
+ type = getClosestStandardTokenTypeForInternalType(type);
+ return type==TokenTypes.COMMENT_MULTILINE ||
+ type==TokenTypes.COMMENT_DOCUMENTATION;
+ }
+ return false;
+ }
+
+
+ /**
+ * Action that knows how to special-case inserting a newline in a
+ * multi-line comment for languages like C and Java.
+ */
+ protected class CStyleInsertBreakAction extends
+ RSyntaxTextAreaEditorKit.InsertBreakAction {
+
+ @Override
+ public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
+
+ if (!textArea.isEditable() || !textArea.isEnabled()) {
+ UIManager.getLookAndFeel().provideErrorFeedback(textArea);
+ return;
+ }
+
+ RSyntaxTextArea rsta = (RSyntaxTextArea)getTextComponent(e);
+ RSyntaxDocument doc = (RSyntaxDocument)rsta.getDocument();
+
+ int line = textArea.getCaretLineNumber();
+ int type = doc.getLastTokenTypeOnLine(line);
+ if (type<0) {
+ type = doc.getClosestStandardTokenTypeForInternalType(type);
+ }
+
+ // Only in MLC's should we try this
+ if (type==Token.COMMENT_DOCUMENTATION ||
+ type==Token.COMMENT_MULTILINE) {
+ insertBreakInMLC(e, rsta, line);
+ }
+ else {
+ handleInsertBreak(rsta, true);
+ }
+
+ }
+
+
+ /**
+ * Returns whether the MLC token containing offs
appears
+ * to have a "nested" comment (i.e., contains "/*
"
+ * somewhere inside of it). This implies that it is likely a "new" MLC
+ * and needs to be closed. While not foolproof, this is usually good
+ * enough of a sign.
+ *
+ * @param textArea
+ * @param line
+ * @param offs
+ * @return Whether a comment appears to be nested inside this one.
+ */
+ private boolean appearsNested(RSyntaxTextArea textArea,
+ int line, int offs) {
+
+ final int firstLine = line; // Remember the line we start at.
+
+ while (lineToken.RESERVED_WORD
or Token.FUNCTION
.
- */
- protected TokenMap wordsToHighlight;
+ /**
+ * Hash table of words to highlight and what token type they are.
+ * The keys are the words to highlight, and their values are the
+ * token types, for example, Token.RESERVED_WORD
or
+ * Token.FUNCTION
.
+ */
+ protected TokenMap wordsToHighlight;
+
+
+ /**
+ * Constructor.
+ */
+ public AbstractTokenMaker() {
+ wordsToHighlight = getWordsToHighlight();
+ }
+
+
+ /**
+ * Returns the words to highlight for this programming language.
+ *
+ * @return A TokenMap
containing the words to highlight for
+ * this programming language.
+ */
+ public abstract TokenMap getWordsToHighlight();
- /**
- * Constructor.
- */
- public AbstractTokenMaker() {
- wordsToHighlight = getWordsToHighlight();
- }
- /**
- * Returns the words to highlight for this programming language.
- *
- * @return A TokenMap
containing the words to highlight for this programming language.
- */
- public abstract TokenMap getWordsToHighlight();
+ /**
+ * Removes the token last added from the linked list of tokens. The
+ * programmer should never have to call this directly; it can be called
+ * by subclasses of TokenMaker
if necessary.
+ */
+ public void removeLastToken() {
+ if (previousToken==null) {
+ firstToken = currentToken = null;
+ }
+ else {
+ currentToken = previousToken;
+ currentToken.setNextToken(null);
+ }
+ }
- /**
- * Removes the token last added from the linked list of tokens. The programmer should never have to call this
- * directly; it can be called by subclasses of TokenMaker
if necessary.
- */
- public void removeLastToken() {
- if (previousToken == null) {
- firstToken = currentToken = null;
- }
- else {
- currentToken = previousToken;
- currentToken.setNextToken(null);
- }
- }
-}
\ No newline at end of file
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractTokenMakerFactory.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractTokenMakerFactory.java
old mode 100644
new mode 100755
index 7825614a6..7df3841e9
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractTokenMakerFactory.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/AbstractTokenMakerFactory.java
@@ -2,100 +2,133 @@
* 12/14/08
*
* AbstractTokenMakerFactory.java - Base class for TokenMaker implementations.
- * Copyright (C) 2008 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+
/**
- * Base class for {@link TokenMakerFactory} implementations. A java.util.Map
maps keys to the names of
- * {@link TokenMaker} classes.
- *
+ * Base class for {@link TokenMakerFactory} implementations. A mapping from
+ * language keys to the names of {@link TokenMaker} classes is stored.
+ *
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
- /**
- * A mapping from keys to the names of {@link TokenMaker} implementation class names. When
- * {@link #getTokenMaker(String)} is called with a key defined in this map, a TokenMaker
of the
- * corresponding type is returned.
- */
- private Map tokenMakerMap;
-
- /**
- * Constructor.
- */
- protected AbstractTokenMakerFactory() {
- tokenMakerMap = createTokenMakerKeyToClassNameMap();
- }
-
- /**
- * Creates and returns a mapping from keys to the names of {@link TokenMaker} implementation classes. When
- * {@link #getTokenMaker(String)} is called with a key defined in this map, a TokenMaker
of the
- * corresponding type is returned.
- *
- * @return The map.
- */
- protected abstract Map createTokenMakerKeyToClassNameMap();
-
- /**
- * Returns a {@link TokenMaker} for the specified key.
- *
- * @param key
- * The key.
- * @return The corresponding TokenMaker
, or null
if none matches the specified key.
- */
- protected TokenMaker getTokenMakerImpl(String key) {
- String clazz = (String) tokenMakerMap.get(key);
- if (clazz != null) {
- try {
- return (TokenMaker) Class.forName(clazz).newInstance();
- } catch (RuntimeException re) { // FindBugs
- throw re;
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return null;
- }
-
- /**
- * {@inheritDoc}
- */
- public Set keySet() {
- return tokenMakerMap.keySet();
- }
-
- /**
- * Adds a mapping from a key to a TokenMaker
implementation class name.
- *
- * @param key
- * The key.
- * @param className
- * The TokenMaker
class name.
- * @return The previous value for the specified key, or null
if there was none.
- */
- public String putMapping(String key, String className) {
- return (String) tokenMakerMap.put(key, className);
- }
-
-}
\ No newline at end of file
+ /**
+ * A mapping from keys to the names of {@link TokenMaker} implementation
+ * class names. When {@link #getTokenMaker(String)} is called with a key
+ * defined in this map, a TokenMaker
of the corresponding type
+ * is returned.
+ */
+ private MapTokenMaker
, or null
+ * if none matches the specified key.
+ */
+ @Override
+ protected TokenMaker getTokenMakerImpl(String key) {
+ TokenMakerCreator tmc = (TokenMakerCreator)tokenMakerMap.get(key);
+ if (tmc!=null) {
+ try {
+ return tmc.create();
+ } catch (RuntimeException re) { // FindBugs
+ throw re;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Populates the mapping from keys to instances of
+ * TokenMakerCreator
s. Subclasses should override this method
+ * and call one of the putMapping
overloads to register
+ * {@link TokenMaker}s for syntax constants.
+ *
+ * @see #putMapping(String, String)
+ * @see #putMapping(String, String, ClassLoader)
+ */
+ protected abstract void initTokenMakerMap();
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SetTokenMaker
implementation
+ * class name.
+ *
+ * @param key The key.
+ * @param className The TokenMaker
class name.
+ * @see #putMapping(String, String, ClassLoader)
+ */
+ public void putMapping(String key, String className) {
+ putMapping(key, className, null);
+ }
+
+
+ /**
+ * Adds a mapping from a key to a TokenMaker
implementation
+ * class name.
+ *
+ * @param key The key.
+ * @param className The TokenMaker
class name.
+ * @param cl The class loader to use when loading the class.
+ * @see #putMapping(String, String)
+ */
+ public void putMapping(String key, String className, ClassLoader cl) {
+ tokenMakerMap.put(key, new TokenMakerCreator(className, cl));
+ }
+
+
+ /**
+ * Wrapper that handles the creation of TokenMaker instances.
+ */
+ private static class TokenMakerCreator {
+
+ private String className;
+ private ClassLoader cl;
+
+ public TokenMakerCreator(String className, ClassLoader cl) {
+ this.className = className;
+ this.cl = cl!=null ? cl : getClass().getClassLoader();
+ }
+
+ public TokenMaker create() throws Exception {
+ return (TokenMaker)Class.forName(className, true, cl).newInstance();
+ }
+
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeEvent.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeEvent.java
old mode 100644
new mode 100755
index 698429b3e..c6b221798
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeEvent.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeEvent.java
@@ -3,73 +3,66 @@
*
* ActiveLineRangeEvent.java - Notifies listeners of an "active line range"
* change in an RSyntaxTextArea.
- * Copyright (C) 2011 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.EventObject;
+
/**
- * The event fired by {@link RSyntaxTextArea}s when the active line range changes.
- *
+ * The event fired by {@link RSyntaxTextArea}s when the active line range
+ * changes.
+ *
* @author Robert Futrell
* @version 1.0
*/
public class ActiveLineRangeEvent extends EventObject {
- private int min;
- private int max;
+ private int min;
+ private int max;
+
+
+ /**
+ * Constructor.
+ *
+ * @param source The text area.
+ * @param min The first line in the active line range, or
+ * -1
if the line range is being cleared.
+ * @param max The last line in the active line range, or
+ * -1
if the line range is being cleared.
+ */
+ public ActiveLineRangeEvent(RSyntaxTextArea source, int min, int max) {
+ super(source);
+ this.min = min;
+ this.max = max;
+ }
+
+
+ /**
+ * Returns the last line in the active line range.
+ *
+ * @return The last line, or -1
if the range is being
+ * cleared.
+ * @see #getMin()
+ */
+ public int getMax() {
+ return max;
+ }
- /**
- * Constructor.
- *
- * @param source
- * The text area.
- * @param min
- * The first line in the active line range, or -1
if the line range is being cleared.
- * @param max
- * The last line in the active line range, or -1
if the line range is being cleared.
- */
- public ActiveLineRangeEvent(RSyntaxTextArea source, int min, int max) {
- super(source);
- this.min = min;
- this.max = max;
- }
- /**
- * Returns the last line in the active line range.
- *
- * @return The last line, or -1
if the range is being cleared.
- * @see #getMin()
- */
- public int getMax() {
- return max;
- }
+ /**
+ * Returns the first line in the active line range.
+ *
+ * @return The first line, or -1
if the range is being
+ * cleared.
+ * @see #getMax()
+ */
+ public int getMin() {
+ return min;
+ }
- /**
- * Returns the first line in the active line range.
- *
- * @return The first line, or -1
if the range is being cleared.
- * @see #getMax()
- */
- public int getMin() {
- return min;
- }
-}
\ No newline at end of file
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeListener.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeListener.java
old mode 100644
new mode 100755
index f51feb30b..54917697f
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeListener.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ActiveLineRangeListener.java
@@ -3,50 +3,42 @@
*
* ActiveLineRangeListener.java - Listens for "active line range" changes
* in an RSyntaxTextArea.
- * Copyright (C) 2011 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.util.EventListener;
+
/**
- * Listens for "active line range" events from an RSyntaxTextArea
. If a text area contains some semantic
- * knowledge of the programming language being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the caret
- * moves into a new "block" of code. Listeners can listen for these events and respond accordingly.
- * RSTALanguageSupport
project at http://fifesoft.com for some
- * LanguageSupport
implementations that may broadcast these events. Note that if an RSTA/LanguageSupport
- * does not support broadcasting these events, the listener will simply never receive any notifications.
- *
+ * Listens for "active line range" events from an RSyntaxTextArea
.
+ * If a text area contains some semantic knowledge of the programming language
+ * being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the
+ * caret moves into a new "block" of code. Listeners can listen for these
+ * events and respond accordingly.RSTALanguageSupport
project at
+ * http://fifesoft.com for some
+ * LanguageSupport
implementations that may broadcast these
+ * events. Note that if an RSTA/LanguageSupport does not support broadcasting
+ * these events, the listener will simply never receive any notifications.
+ *
* @author Robert Futrell
* @version 1.0
*/
public interface ActiveLineRangeListener extends EventListener {
- /**
- * Called whenever the "active line range" changes.
- *
- * @param e
- * Information about the line range change. If there is no longer an "active line range," the "minimum"
- * and "maximum" line values should both be -1
.
- */
- public void activeLineRangeChanged(ActiveLineRangeEvent e);
-}
\ No newline at end of file
+ /**
+ * Called whenever the "active line range" changes.
+ *
+ * @param e Information about the line range change. If there is no longer
+ * an "active line range," the "minimum" and "maximum" line values
+ * should both be -1
.
+ */
+ void activeLineRangeChanged(ActiveLineRangeEvent e);
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ChangeableColorHighlightPainter.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ChangeableColorHighlightPainter.java
deleted file mode 100644
index 47638f1d3..000000000
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ChangeableColorHighlightPainter.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 07/23/2009
- *
- * ChangeableColorHighlightPainter.java - A highlighter whose color you can
- * change.
- * Copyright (C) 2009 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-package org.fife.ui.rsyntaxtextarea;
-
-import java.awt.Color;
-import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter;
-
-/**
- * A highlighter whose color you can change.
- *
- * @author Robert Futrell
- * @version 1.0
- */
-class ChangeableColorHighlightPainter extends DefaultHighlightPainter {
-
- /**
- * DefaultHighlightPainter doesn't allow changing color, so we must cache ours here.
- */
- private Color color;
-
- /**
- * Constructor.
- *
- * @param color
- * The initial color to use. This cannot be null
.
- */
- public ChangeableColorHighlightPainter(Color color) {
- super(color);
- setColor(color);
- }
-
- /**
- * Returns the color to paint with.
- *
- * @return The color.
- * @see #setColor(Color)
- */
- public Color getColor() {
- return color;
- }
-
- /**
- * Sets the color to paint the bounding boxes with.
- *
- * @param color
- * The new color. This cannot be null
.
- * @see #getColor()
- */
- public void setColor(Color color) {
- if (color == null) {
- throw new IllegalArgumentException("color cannot be null");
- }
- this.color = color;
- }
-
-}
\ No newline at end of file
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/CodeTemplateManager.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/CodeTemplateManager.java
old mode 100644
new mode 100755
index 22f44f7f2..aa7994b5f
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/CodeTemplateManager.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/CodeTemplateManager.java
@@ -2,28 +2,12 @@
* 02/21/2005
*
* CodeTemplateManager.java - manages code templates.
- * Copyright (C) 2005 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
@@ -34,420 +18,383 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
-import java.util.*;
-import javax.swing.KeyStroke;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate;
+
/**
- * Manages "code templates."
- * CodeTemplateManager
on the EDT. Modifying a CodeTemplate
retrieved
- * from a CodeTemplateManager
while not on the EDT could cause problems.
- *
+ * Manages "code templates."CodeTemplateManager
on the EDT. Modifying a
+ * CodeTemplate
retrieved from a CodeTemplateManager
+ * while not on the EDT could cause problems.template
is null
.
- * @see #removeTemplate(CodeTemplate)
- * @see #removeTemplate(String)
- */
- public synchronized void addTemplate(CodeTemplate template) {
- if (template == null) {
- throw new IllegalArgumentException("template cannot be null");
- }
- templates.add(template);
- sortTemplates();
- }
-
- /**
- * Returns the keystroke that is the "insert trigger" for templates; that is, the character that, when inserted into
- * an instance of RSyntaxTextArea
, triggers the search for a template matching the token ending at the
- * caret position.
- *
- * @return The insert trigger.
- * @see #getInsertTriggerString()
- * @see #setInsertTrigger(KeyStroke)
- */
- /*
- * FIXME: This text IS what's inserted if the trigger character is pressed in a text area but no template matches,
- * but it is NOT the trigger character used in the text areas. This is because space (" ") is hard-coded into
- * RSyntaxTextAreaDefaultInputMap.java. We need to make this dynamic somehow. See
- * RSyntaxTextAreaDefaultInputMap.java.
- */
- public KeyStroke getInsertTrigger() {
- return insertTrigger;
- }
-
- /**
- * Returns the "insert trigger" for templates; that is, the character that, when inserted into an instance of
- * RSyntaxTextArea
, triggers the search for a template matching the token ending at the caret position.
- *
- * @return The insert trigger character.
- * @see #getInsertTrigger()
- * @see #setInsertTrigger(KeyStroke)
- */
- /*
- * FIXME: This text IS what's inserted if the trigger character is pressed in a text area but no template matches,
- * but it is NOT the trigger character used in the text areas. This is because space (" ") is hard-coded into
- * RSyntaxTextAreaDefaultInputMap.java. We need to make this dynamic somehow. See
- * RSyntaxTextAreaDefaultInputMap.java.
- */
- public String getInsertTriggerString() {
- return insertTriggerString;
- }
-
- /**
- * Returns the template that should be inserted at the current caret position, assuming the trigger character was
- * pressed.
- *
- * @param textArea
- * The text area that's getting text inserted into it.
- * @return A template that should be inserted, if appropriate, or null
if no template should be
- * inserted.
- */
- public synchronized CodeTemplate getTemplate(RSyntaxTextArea textArea) {
- int caretPos = textArea.getCaretPosition();
- int charsToGet = Math.min(caretPos, maxTemplateIDLength);
- try {
- Document doc = textArea.getDocument();
- doc.getText(caretPos - charsToGet, charsToGet, s);
- int index = Collections.binarySearch(templates, s, comparator);
- return index >= 0 ? (CodeTemplate) templates.get(index) : null;
- } catch (BadLocationException ble) {
- ble.printStackTrace();
- throw new InternalError("Error in CodeTemplateManager");
- }
- }
-
- /**
- * Returns the number of templates this manager knows about.
- *
- * @return The template count.
- */
- public synchronized int getTemplateCount() {
- return templates.size();
- }
-
- /**
- * Returns the templates currently available.
- *
- * @return The templates available.
- */
- public synchronized CodeTemplate[] getTemplates() {
- CodeTemplate[] temp = new CodeTemplate[templates.size()];
- return (CodeTemplate[]) templates.toArray(temp);
- }
-
- /**
- * Returns whether the specified character is a valid character for a CodeTemplate
id.
- *
- * @param ch
- * The character to check.
- * @return Whether the character is a valid template character.
- */
- public static final boolean isValidChar(char ch) {
- return RSyntaxUtilities.isLetterOrDigit(ch) || ch == '_';
- }
-
- /**
- * Returns the specified code template.
- *
- * @param template
- * The template to remove.
- * @return true
if the template was removed, false
if the template was not in this
- * template manager.
- * @throws IllegalArgumentException
- * If template
is null
.
- * @see #removeTemplate(String)
- * @see #addTemplate(CodeTemplate)
- */
- public synchronized boolean removeTemplate(CodeTemplate template) {
-
- if (template == null) {
- throw new IllegalArgumentException("template cannot be null");
- }
-
- // TODO: Do a binary search
- return templates.remove(template);
-
- }
-
- /**
- * Returns the code template with the specified id.
- *
- * @param id
- * The id to check for.
- * @return The code template that was removed, or null
if there was no template with the specified ID.
- * @throws IllegalArgumentException
- * If id
is null
.
- * @see #removeTemplate(CodeTemplate)
- * @see #addTemplate(CodeTemplate)
- */
- public synchronized CodeTemplate removeTemplate(String id) {
-
- if (id == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
-
- // TODO: Do a binary search
- for (Iterator i = templates.iterator(); i.hasNext();) {
- CodeTemplate template = (CodeTemplate) i.next();
- if (id.equals(template.getID())) {
- i.remove();
- return template;
- }
- }
-
- return null;
-
- }
-
- /**
- * Replaces the current set of available templates with the ones specified.
- *
- * @param newTemplates
- * The new set of templates. Note that we will be taking a shallow copy of these and sorting them.
- */
- public synchronized void replaceTemplates(CodeTemplate[] newTemplates) {
- templates.clear();
- if (newTemplates != null) {
- Collections.addAll(templates, newTemplates);
- }
- sortTemplates(); // Also recomputes maxTemplateIDLength.
- }
-
- /**
- * Saves all templates as XML files in the current template directory.
- *
- * @return Whether or not the save was successful.
- */
- public synchronized boolean saveTemplates() {
-
- if (templates == null)
- return true;
- if (directory == null || !directory.isDirectory())
- return false;
-
- // Blow away all old XML files to start anew, as some might be from
- // templates we're removed from the template manager.
- File[] oldXMLFiles = directory.listFiles(new XMLFileFilter());
- if (oldXMLFiles == null)
- return false; // Either an IOException or it isn't a directory.
- int count = oldXMLFiles.length;
- for (File oldXMLFile : oldXMLFiles) {
- /* boolean deleted = */
- oldXMLFile.delete();
- }
-
- // Save all current templates as XML.
- boolean wasSuccessful = true;
- for (CodeTemplate template : templates) {
- File xmlFile = new File(directory, template.getID() + ".xml");
- try {
- XMLEncoder e = new XMLEncoder(new BufferedOutputStream(
- new FileOutputStream(xmlFile)));
- e.writeObject(template);
- e.close();
- } catch (IOException ioe) {
- ioe.printStackTrace();
- wasSuccessful = false;
- }
- }
-
- return wasSuccessful;
-
- }
-
- /**
- * Sets the "trigger" character for templates.
- *
- * @param trigger
- * The trigger character to set for templates. This means that when this character is pressed in an
- * RSyntaxTextArea
, the last-typed token is found, and is checked against all template ID's
- * to see if a template should be inserted. If a template ID matches, that template is inserted; if not,
- * the trigger character is inserted. If this parameter is null
, no change is made to the
- * trigger character.
- * @see #getInsertTrigger()
- * @see #getInsertTriggerString()
- */
- /*
- * FIXME: The trigger set here IS inserted when no matching template is found, but a space character (" ") is always
- * used as the "trigger" to look for templates. This is because it is hard-coded in RSyntaxTextArea's input map this
- * way. We need to change this. See RSyntaxTextAreaDefaultInputMap.java.
- */
- public void setInsertTrigger(KeyStroke trigger) {
- if (trigger != null) {
- insertTrigger = trigger;
- insertTriggerString = Character.toString(trigger.getKeyChar());
- }
- }
-
- /**
- * Sets the directory in which to look for templates. Calling this method adds any new templates found in the
- * specified directory to the templates already registered.
- *
- * @param dir
- * The new directory in which to look for templates.
- * @return The new number of templates in this template manager, or -1
if the specified directory does
- * not exist.
- */
- public synchronized int setTemplateDirectory(File dir) {
-
- if (dir != null && dir.isDirectory()) {
-
- this.directory = dir;
-
- File[] files = dir.listFiles(new XMLFileFilter());
- int newCount = files == null ? 0 : files.length;
- int oldCount = templates.size();
-
- List temp = new ArrayList(oldCount + newCount);
- temp.addAll(templates);
-
- for (int i = 0; i < newCount; i++) {
- try {
- XMLDecoder d = new XMLDecoder(new BufferedInputStream(
- new FileInputStream(files[i])));
- Object obj = d.readObject();
- if (!(obj instanceof CodeTemplate)) {
- throw new IOException("Not a CodeTemplate: " +
- files[i].getAbsolutePath());
- }
- temp.add(obj);
- d.close();
- } catch (/* IO, NoSuchElement */Exception e) {
- // NoSuchElementException can be thrown when reading
- // an XML file not in the format expected by XMLDecoder.
- // (e.g. CodeTemplates in an old format).
- e.printStackTrace();
- }
- }
- templates = temp;
- sortTemplates();
-
- return getTemplateCount();
-
- }
-
- return -1;
-
- }
-
- /**
- * Removes any null entries in the current set of templates (if any), sorts the remaining templates, and computes
- * the new maximum template ID length.
- */
- private synchronized void sortTemplates() {
-
- // Get the maximum length of a template ID.
- maxTemplateIDLength = 0;
-
- // Remove any null entries (should only happen because of
- // IOExceptions, etc. when loading from files), and sort
- // the remaining list.
- for (IteratorCodeTemplate
as its first parameter and a Segment
as its
- * second, and knows to compare the template's ID to the segment's text.
- */
- private static class TemplateComparator implements Comparator, Serializable {
-
- public int compare(Object template, Object segment) {
-
- // Get template start index (0) and length.
- CodeTemplate t = (CodeTemplate) template;
- final char[] templateArray = t.getID().toCharArray();
- int i = 0;
- int len1 = templateArray.length;
-
- // Find "token" part of segment and get its offset and length.
- Segment s = (Segment) segment;
- char[] segArray = s.array;
- int len2 = s.count;
- int j = s.offset + len2 - 1;
- while (j >= s.offset && isValidChar(segArray[j])) {
- j--;
- }
- j++;
- int segShift = j - s.offset;
- len2 -= segShift;
-
- int n = Math.min(len1, len2);
- while (n-- != 0) {
- char c1 = templateArray[i++];
- char c2 = segArray[j++];
- if (c1 != c2)
- return c1 - c2;
- }
- return len1 - len2;
-
- }
-
- }
-
- /**
- * A file filter for File.listFiles() (NOT for JFileChoosers!) that accepts only XML files.
- */
- private static class XMLFileFilter implements FileFilter {
- public boolean accept(File f) {
- return f.getName().toLowerCase().endsWith(".xml");
- }
- }
-
-}
\ No newline at end of file
+ private int maxTemplateIDLength;
+ private Listtemplate
is
+ * null
.
+ * @see #removeTemplate(CodeTemplate)
+ * @see #removeTemplate(String)
+ */
+ public synchronized void addTemplate(CodeTemplate template) {
+ if (template==null) {
+ throw new IllegalArgumentException("template cannot be null");
+ }
+ templates.add(template);
+ sortTemplates();
+ }
+
+
+ /**
+ * Returns the template that should be inserted at the current caret
+ * position, assuming the trigger character was pressed.
+ *
+ * @param textArea The text area that's getting text inserted into it.
+ * @return A template that should be inserted, if appropriate, or
+ * null
if no template should be inserted.
+ */
+ public synchronized CodeTemplate getTemplate(RSyntaxTextArea textArea) {
+ int caretPos = textArea.getCaretPosition();
+ int charsToGet = Math.min(caretPos, maxTemplateIDLength);
+ try {
+ Document doc = textArea.getDocument();
+ doc.getText(caretPos-charsToGet, charsToGet, s);
+ @SuppressWarnings("unchecked")
+ int index = Collections.binarySearch(templates, s, comparator);
+ return index>=0 ? templates.get(index) : null;
+ } catch (BadLocationException ble) {
+ ble.printStackTrace();
+ throw new InternalError("Error in CodeTemplateManager");
+ }
+ }
+
+
+ /**
+ * Returns the number of templates this manager knows about.
+ *
+ * @return The template count.
+ */
+ public synchronized int getTemplateCount() {
+ return templates.size();
+ }
+
+
+ /**
+ * Returns the templates currently available.
+ *
+ * @return The templates available.
+ */
+ public synchronized CodeTemplate[] getTemplates() {
+ CodeTemplate[] temp = new CodeTemplate[templates.size()];
+ return templates.toArray(temp);
+ }
+
+
+ /**
+ * Returns whether the specified character is a valid character for a
+ * CodeTemplate
id.
+ *
+ * @param ch The character to check.
+ * @return Whether the character is a valid template character.
+ */
+ public static boolean isValidChar(char ch) {
+ return RSyntaxUtilities.isLetterOrDigit(ch) || ch=='_';
+ }
+
+
+ /**
+ * Returns the specified code template.
+ *
+ * @param template The template to remove.
+ * @return true
if the template was removed, false
+ * if the template was not in this template manager.
+ * @throws IllegalArgumentException If template
is
+ * null
.
+ * @see #removeTemplate(String)
+ * @see #addTemplate(CodeTemplate)
+ */
+ public synchronized boolean removeTemplate(CodeTemplate template) {
+
+ if (template==null) {
+ throw new IllegalArgumentException("template cannot be null");
+ }
+
+ // TODO: Do a binary search
+ return templates.remove(template);
+
+ }
+
+
+ /**
+ * Returns the code template with the specified id.
+ *
+ * @param id The id to check for.
+ * @return The code template that was removed, or null
if
+ * there was no template with the specified ID.
+ * @throws IllegalArgumentException If id
is null
.
+ * @see #removeTemplate(CodeTemplate)
+ * @see #addTemplate(CodeTemplate)
+ */
+ public synchronized CodeTemplate removeTemplate(String id) {
+
+ if (id==null) {
+ throw new IllegalArgumentException("id cannot be null");
+ }
+
+ // TODO: Do a binary search
+ for (Iterator-1
if the specified directory does not exist.
+ */
+ public synchronized int setTemplateDirectory(File dir) {
+
+ if (dir!=null && dir.isDirectory()) {
+
+ this.directory = dir;
+
+ File[] files = dir.listFiles(new XMLFileFilter());
+ int newCount = files==null ? 0 : files.length;
+ int oldCount = templates.size();
+
+ ListCodeTemplate
as its first
+ * parameter and a Segment
as its second, and knows
+ * to compare the template's ID to the segment's text.
+ */
+ @SuppressWarnings("rawtypes")
+ private static class TemplateComparator implements Comparator, Serializable{
+
+ @Override
+ public int compare(Object template, Object segment) {
+
+ // Get template start index (0) and length.
+ CodeTemplate t = (CodeTemplate)template;
+ final char[] templateArray = t.getID().toCharArray();
+ int i = 0;
+ int len1 = templateArray.length;
+
+ // Find "token" part of segment and get its offset and length.
+ Segment s = (Segment)segment;
+ char[] segArray = s.array;
+ int len2 = s.count;
+ int j = s.offset + len2 - 1;
+ while (j>=s.offset && isValidChar(segArray[j])) {
+ j--;
+ }
+ j++;
+ int segShift = j - s.offset;
+ len2 -= segShift;
+
+ int n = Math.min(len1, len2);
+ while (n-- != 0) {
+ char c1 = templateArray[i++];
+ char c2 = segArray[j++];
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ }
+ return len1 - len2;
+
+ }
+
+ }
+
+
+ /**
+ * A file filter that accepts only XML files.
+ */
+ private static class XMLFileFilter implements FileFilter {
+ @Override
+ public boolean accept(File f) {
+ return f.getName().toLowerCase().endsWith(".xml");
+ }
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultOccurrenceMarker.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultOccurrenceMarker.java
new file mode 100755
index 000000000..df7b36254
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultOccurrenceMarker.java
@@ -0,0 +1,112 @@
+/*
+ * 03/09/2013
+ *
+ * DefaultOccurrenceMarker - Marks occurrences of the current token for most
+ * languages.
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+
+import org.fife.ui.rtextarea.SmartHighlightPainter;
+
+
+/**
+ * The default implementation of {@link OccurrenceMarker}. It goes through
+ * the document and marks all instances of the specified token.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+class DefaultOccurrenceMarker implements OccurrenceMarker {
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Token getTokenToMark(RSyntaxTextArea textArea) {
+
+ // Get the token at the caret position.
+ int line = textArea.getCaretLineNumber();
+ Token tokenList = textArea.getTokenListForLine(line);
+ Caret c = textArea.getCaret();
+ int dot = c.getDot();
+
+ Token t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
+ if (t==null /* EOL */ || !isValidType(textArea, t) ||
+ RSyntaxUtilities.isNonWordChar(t)) {
+ // Try to the "left" of the caret.
+ dot--;
+ try {
+ if (dot>=textArea.getLineStartOffset(line)) {
+ t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
+ }
+ } catch (BadLocationException ble) {
+ ble.printStackTrace(); // Never happens
+ }
+ }
+
+ return t;
+
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isValidType(RSyntaxTextArea textArea, Token t) {
+ return textArea.getMarkOccurrencesOfTokenType(t.getType());
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void markOccurrences(RSyntaxDocument doc, Token t,
+ RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
+ markOccurrencesOfToken(doc, t, h, p);
+ }
+
+
+ /**
+ * Highlights all instances of tokens identical to t
in the
+ * specified document.
+ *
+ * @param doc The document.
+ * @param t The document whose relevant occurrences should be marked.
+ * @param h The highlighter to add the highlights to.
+ * @param p The painter for the highlights.
+ */
+ public static void markOccurrencesOfToken(RSyntaxDocument doc,
+ Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
+
+ char[] lexeme = t.getLexeme().toCharArray();
+ int type = t.getType();
+ int lineCount = doc.getDefaultRootElement().getElementCount();
+
+ for (int i=0; i
- *
- * This method allows for rendering hints to be honored, since all possible characters are painted in a group. However,
- * adjacent tokens will not have their "touching" characters rendered with rendering hints.
- * int
and not a float
). The way
- * around this would be to calculate the token's width in such a way that a float is returned (Font.getStringBounds()?).
- *
- * @author Robert Futrell
- * @version 0.5
- * @see Token
- * @see VisibleWhitespaceToken
- */
-public class DefaultToken extends Token {
-
- /**
- * Creates a "null token." The token itself is not null; rather, it signifies that it is the last token in a linked
- * list of tokens and that it is not part of a "multi-line token."
- */
- public DefaultToken() {
- super();
- }
-
- /**
- * Constructor.
- *
- * @param line
- * The segment from which to get the token.
- * @param beg
- * The first character's position in line
.
- * @param end
- * The last character's position in line
.
- * @param startOffset
- * The offset into the document at which this token begins.
- * @param type
- * A token type listed as "generic" above.
- */
- public DefaultToken(final Segment line, final int beg, final int end,
- final int startOffset, final int type) {
- this(line.array, beg, end, startOffset, type);
- }
-
- /**
- * Constructor.
- *
- * @param line
- * The segment from which to get the token.
- * @param beg
- * The first character's position in line
.
- * @param end
- * The last character's position in line
.
- * @param startOffset
- * The offset into the document at which this token begins.
- * @param type
- * A token type listed as "generic" above.
- */
- public DefaultToken(final char[] line, final int beg, final int end,
- final int startOffset, final int type) {
- super(line, beg, end, startOffset, type);
- }
-
- /**
- * Determines the offset into this token list (i.e., into the document) that covers pixel location x
if
- * the token list starts at pixel location x0
- * w
in awe
, the caret will be placed in between the w
- * and e
; similarly, clicking on the left-half places the caret between the a
and
- * w
). This makes it useful for methods such as viewToModel
found in
- * javax.swing.text.View
subclasses.
- *
- * @param textArea
- * The text area from which the token list was derived.
- * @param e
- * How to expand tabs.
- * @param x0
- * The pixel x-location that is the beginning of tokenList
.
- * @param x
- * The pixel-position for which you want to get the corresponding offset.
- * @return The position (in the document, NOT into the token list!) that covers the pixel location. If
- * tokenList
is null
or has type Token.NULL
, then -1
= x)
- return offset;
-
- float currX = x0; // x-coordinate of current char.
- float nextX = x0; // x-coordinate of next char.
- float stableX = x0; // Cached ending x-coord. of last tab or token.
- Token token = this;
- int last = offset;
- FontMetrics fm = null;
-
- while (token != null && token.isPaintable()) {
-
- fm = textArea.getFontMetricsForTokenType(token.type);
- char[] text = token.text;
- int start = token.textOffset;
- int end = start + token.textCount;
-
- for (int i = start; i < end; i++) {
- currX = nextX;
- if (text[i] == '\t') {
- nextX = e.nextTabStop(nextX, 0);
- stableX = nextX; // Cache ending x-coord. of tab.
- start = i + 1; // Do charsWidth() from next char.
- }
- else {
- nextX = stableX + fm.charsWidth(text, start, i - start + 1);
- }
- if (x >= currX && x < nextX) {
- if ((x - currX) < (nextX - x)) {
- return last + i - token.textOffset;
- }
- return last + i + 1 - token.textOffset;
- }
- }
-
- stableX = nextX; // Cache ending x-coordinate of token.
- last += token.textCount;
- token = token.getNextToken();
-
- }
-
- // If we didn't find anything, return the end position of the text.
- return last;
-
- }
-
- /**
- * Returns the width of a specified number of characters in this token. For example, for the token "while",
- * specifying a value of 3
here returns the width of the "whi" portion of the token.
- * null
.
- * @param x0
- * The pixel-location at which this token begins. This is needed because of tabs.
- * @return The width of the specified number of characters in this token.
- * @see #getWidth
- */
- public float getWidthUpTo(int numChars, RSyntaxTextArea textArea,
- TabExpander e, float x0) {
- float width = x0;
- FontMetrics fm = textArea.getFontMetricsForTokenType(type);
- if (fm != null) {
- int w;
- int currentStart = textOffset;
- int endBefore = textOffset + numChars;
- for (int i = currentStart; i < endBefore; i++) {
- if (text[i] == '\t') {
- // Since TokenMaker implementations usually group all
- // adjacent whitespace into a single token, there
- // aren't usually any characters to compute a width
- // for here, so we check before calling.
- w = i - currentStart;
- if (w > 0)
- width += fm.charsWidth(text, currentStart, w);
- currentStart = i + 1;
- width = e.nextTabStop(width, 0);
- }
- }
- // Most (non-whitespace) tokens will have characters at this
- // point to get the widths for, so we don't check for w>0 (mini-
- // optimization).
- w = endBefore - currentStart;
- width += fm.charsWidth(text, currentStart, w);
- }
- return width - x0;
- }
-
- /**
- * Returns the bounding box for the specified document location. The location must be in the specified token list.
- *
- * @param textArea
- * The text area from which the token list was derived.
- * @param e
- * How to expand tabs.
- * @param pos
- * The position in the document for which to get the bounding box in the view.
- * @param x0
- * The pixel x-location that is the beginning of tokenList
.
- * @param rect
- * The rectangle in which we'll be returning the results. This object is reused to keep from frequent
- * memory allocations.
- * @return The bounding box for the specified position in the model.
- */
- public Rectangle listOffsetToView(RSyntaxTextArea textArea, TabExpander e,
- int pos, int x0, Rectangle rect) {
-
- int stableX = x0; // Cached ending x-coord. of last tab or token.
- Token token = this;
- FontMetrics fm = null;
- Segment s = new Segment();
-
- while (token != null && token.isPaintable()) {
-
- fm = textArea.getFontMetricsForTokenType(token.type);
- if (fm == null) {
- return rect; // Don't return null as things'll error.
- }
- char[] text = token.text;
- int start = token.textOffset;
- int end = start + token.textCount;
-
- // If this token contains the position for which to get the
- // bounding box...
- if (token.containsPosition(pos)) {
-
- s.array = token.text;
- s.offset = token.textOffset;
- s.count = pos - token.offset;
-
- // Must use this (actually fm.charWidth()), and not
- // fm.charsWidth() for returned value to match up with where
- // text is actually painted on OS X!
- int w = Utilities.getTabbedTextWidth(s, fm, stableX, e,
- token.offset);
- rect.x = stableX + w;
- end = token.documentToToken(pos);
-
- if (text[end] == '\t') {
- rect.width = fm.charWidth(' ');
- }
- else {
- rect.width = fm.charWidth(text[end]);
- }
-
- return rect;
-
- }
-
- // If this token does not contain the position for which to get
- // the bounding box...
- else {
- s.array = token.text;
- s.offset = token.textOffset;
- s.count = token.textCount;
- stableX += Utilities.getTabbedTextWidth(s, fm, stableX, e,
- token.offset);
- }
-
- token = token.getNextToken();
-
- }
-
- // If we didn't find anything, we're at the end of the line. Return
- // a width of 1 (so selection highlights don't extend way past line's
- // text). A ConfigurableCaret will know to paint itself with a larger
- // width.
- rect.x = stableX;
- rect.width = 1;
- return rect;
-
- }
-
- /**
- * Paints this token.
- *
- * @param g
- * The graphics context in which to paint.
- * @param x
- * The x-coordinate at which to paint.
- * @param y
- * The y-coordinate at which to paint.
- * @param host
- * The text area this token is in.
- * @param e
- * How to expand tabs.
- * @param clipStart
- * The left boundary of the clip rectangle in which we're painting. This optimizes painting by allowing
- * us to not paint when this token is "to the left" of the clip rectangle.
- * @return The x-coordinate representing the end of the painted text.
- */
- public float paint(Graphics2D g, float x, float y, RSyntaxTextArea host,
- TabExpander e, float clipStart) {
-
- int origX = (int) x;
- int end = textOffset + textCount;
- float nextX = x;
- int flushLen = 0;
- int flushIndex = textOffset;
- Color fg = host.getForegroundForToken(this);
- Color bg = host.getBackgroundForTokenType(type);
- g.setFont(host.getFontForTokenType(type));
- FontMetrics fm = host.getFontMetricsForTokenType(type);
-
- for (int i = textOffset; i < end; i++) {
- switch (text[i]) {
- case '\t':
- nextX = e.nextTabStop(
- x + fm.charsWidth(text, flushIndex, flushLen), 0);
- if (bg != null) {
- paintBackground(x, y, nextX - x, fm.getHeight(),
- g, fm.getAscent(), host, bg);
- }
- if (flushLen > 0) {
- g.setColor(fg);
- g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
- flushLen = 0;
- }
- flushIndex = i + 1;
- x = nextX;
- break;
- default:
- flushLen += 1;
- break;
- }
- }
-
- nextX = x + fm.charsWidth(text, flushIndex, flushLen);
-
- if (flushLen > 0 && nextX >= clipStart) {
- if (bg != null) {
- paintBackground(x, y, nextX - x, fm.getHeight(),
- g, fm.getAscent(), host, bg);
- }
- g.setColor(fg);
- g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
- }
-
- if (host.getUnderlineForToken(this)) {
- g.setColor(fg);
- int y2 = (int) (y + 1);
- g.drawLine(origX, y2, (int) nextX, y2);
- }
-
- return nextX;
-
- }
-
-}
\ No newline at end of file
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultTokenFactory.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultTokenFactory.java
old mode 100644
new mode 100755
index ad81f930b..591c45d9d
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultTokenFactory.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultTokenFactory.java
@@ -2,175 +2,142 @@
* 10/28/2004
*
* DefaultTokenFactory.java - Default token factory.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.text.Segment;
+
/**
- * This class generates tokens for a {@link TokenMaker}. This class is here because it reuses tokens when they aren't
- * needed anymore to prevent This class doesn't actually create new tokens every time createToken
is
- * called. Instead, it internally keeps a stack of available already-created tokens. When more tokens are needed to
- * properly display a line, more tokens are added to the available stack. This saves from needless repetitive memory
- * allocation. However, it makes it IMPERATIVE that users call resetTokenList
when creating a new token
- * list so that the token maker can keep an accurate list of available tokens.
- * createToken
is called. Instead, it internally keeps a stack of
+ * available already-created tokens. When more tokens are needed to properly
+ * display a line, more tokens are added to the available stack. This saves
+ * from needless repetitive memory allocation. However, it makes it IMPERATIVE
+ * that users call resetTokenList
when creating a new token list so
+ * that the token maker can keep an accurate list of available tokens.TokenMaker
every time a
- * token list is generated for a new line so the tokens can be reused.
- */
- public void resetAllTokens() {
- currentFreeToken = 0;
- }
-
-}
\ No newline at end of file
+ private int size;
+ private int increment;
+ private TokenImpl[] tokenList;
+ private int currentFreeToken;
+
+ protected static final int DEFAULT_START_SIZE = 30;
+ protected static final int DEFAULT_INCREMENT = 10;
+
+
+ /**
+ * Constructor.
+ */
+ DefaultTokenFactory() {
+ this(DEFAULT_START_SIZE, DEFAULT_INCREMENT);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param size The initial number of tokens in this factory.
+ * @param increment How many tokens to increment by when the stack gets
+ * empty.
+ */
+ DefaultTokenFactory(int size, int increment) {
+
+ this.size = size;
+ this.increment = increment;
+ this.currentFreeToken = 0;
+
+ // Give us some tokens to initially work with.
+ tokenList = new TokenImpl[size];
+ for (int i=0; iTokenMakerFactory
. This factory can create {@link TokenMaker}s for all
- * languages known to {@link RSyntaxTextArea}.
- *
+ * The default implementation of TokenMakerFactory
. This factory
+ * can create {@link TokenMaker}s for all languages known to
+ * {@link RSyntaxTextArea}.
+ *
* @author Robert Futrell
* @version 1.0
*/
class DefaultTokenMakerFactory extends AbstractTokenMakerFactory
- implements SyntaxConstants {
+ implements SyntaxConstants {
- /**
- * Creates and returns a mapping from keys to the names of {@link TokenMaker} implementation classes. When
- * {@link #getTokenMaker(String)} is called with a key defined in this map, a TokenMaker
of the
- * corresponding type is returned.
- *
- * @return The map.
- */
- protected Map createTokenMakerKeyToClassNameMap() {
- HashMap map = new HashMap();
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void initTokenMakerMap() {
- String pkg = "org.fife.ui.rsyntaxtextarea.modes.";
+ String pkg = "org.fife.ui.rsyntaxtextarea.modes.";
- map.put(SYNTAX_STYLE_NONE, pkg + "PlainTextTokenMaker");
- map.put(SYNTAX_STYLE_ACTIONSCRIPT, pkg + "ActionScriptTokenMaker");
- map.put(SYNTAX_STYLE_ASSEMBLER_X86, pkg + "AssemblerX86TokenMaker");
- map.put(SYNTAX_STYLE_BBCODE, pkg + "BBCodeTokenMaker");
- map.put(SYNTAX_STYLE_C, pkg + "CTokenMaker");
- map.put(SYNTAX_STYLE_CLOJURE, pkg + "ClojureTokenMaker");
- map.put(SYNTAX_STYLE_CPLUSPLUS, pkg + "CPlusPlusTokenMaker");
- map.put(SYNTAX_STYLE_CSHARP, pkg + "CSharpTokenMaker");
- map.put(SYNTAX_STYLE_CSS, pkg + "CSSTokenMaker");
- map.put(SYNTAX_STYLE_DELPHI, pkg + "DelphiTokenMaker");
- map.put(SYNTAX_STYLE_FORTRAN, pkg + "FortranTokenMaker");
- map.put(SYNTAX_STYLE_GROOVY, pkg + "GroovyTokenMaker");
- map.put(SYNTAX_STYLE_HTML, pkg + "HTMLTokenMaker");
- map.put(SYNTAX_STYLE_JAVA, pkg + "JavaTokenMaker");
- map.put(SYNTAX_STYLE_JAVASCRIPT, pkg + "JavaScriptTokenMaker");
- map.put(SYNTAX_STYLE_JSP, pkg + "JSPTokenMaker");
- map.put(SYNTAX_STYLE_LISP, pkg + "LispTokenMaker");
- map.put(SYNTAX_STYLE_LUA, pkg + "LuaTokenMaker");
- map.put(SYNTAX_STYLE_MAKEFILE, pkg + "MakefileTokenMaker");
- map.put(SYNTAX_STYLE_MXML, pkg + "MxmlTokenMaker");
- map.put(SYNTAX_STYLE_PERL, pkg + "PerlTokenMaker");
- map.put(SYNTAX_STYLE_PHP, pkg + "PHPTokenMaker");
- map.put(SYNTAX_STYLE_PROPERTIES_FILE, pkg + "PropertiesFileTokenMaker");
- map.put(SYNTAX_STYLE_PYTHON, pkg + "PythonTokenMaker");
- map.put(SYNTAX_STYLE_RUBY, pkg + "RubyTokenMaker");
- map.put(SYNTAX_STYLE_SAS, pkg + "SASTokenMaker");
- map.put(SYNTAX_STYLE_SCALA, pkg + "ScalaTokenMaker");
- map.put(SYNTAX_STYLE_SQL, pkg + "SQLTokenMaker");
- map.put(SYNTAX_STYLE_TCL, pkg + "TclTokenMaker");
- map.put(SYNTAX_STYLE_UNIX_SHELL, pkg + "UnixShellTokenMaker");
- map.put(SYNTAX_STYLE_WINDOWS_BATCH, pkg + "WindowsBatchTokenMaker");
- map.put(SYNTAX_STYLE_XML, pkg + "XMLTokenMaker");
+ putMapping(SYNTAX_STYLE_NONE, pkg + "PlainTextTokenMaker");
+ putMapping(SYNTAX_STYLE_ACTIONSCRIPT, pkg + "ActionScriptTokenMaker");
+ putMapping(SYNTAX_STYLE_ASSEMBLER_X86, pkg + "AssemblerX86TokenMaker");
+ putMapping(SYNTAX_STYLE_BBCODE, pkg + "BBCodeTokenMaker");
+ putMapping(SYNTAX_STYLE_C, pkg + "CTokenMaker");
+ putMapping(SYNTAX_STYLE_CLOJURE, pkg + "ClojureTokenMaker");
+ putMapping(SYNTAX_STYLE_CPLUSPLUS, pkg + "CPlusPlusTokenMaker");
+ putMapping(SYNTAX_STYLE_CSHARP, pkg + "CSharpTokenMaker");
+ putMapping(SYNTAX_STYLE_CSS, pkg + "CSSTokenMaker");
+ putMapping(SYNTAX_STYLE_CSV, pkg + "CsvTokenMaker");
+ putMapping(SYNTAX_STYLE_D, pkg + "DTokenMaker");
+ putMapping(SYNTAX_STYLE_DART, pkg + "DartTokenMaker");
+ putMapping(SYNTAX_STYLE_DELPHI, pkg + "DelphiTokenMaker");
+ putMapping(SYNTAX_STYLE_DOCKERFILE, pkg + "DockerTokenMaker");
+ putMapping(SYNTAX_STYLE_DTD, pkg + "DtdTokenMaker");
+ putMapping(SYNTAX_STYLE_FORTRAN, pkg + "FortranTokenMaker");
+ putMapping(SYNTAX_STYLE_GO, pkg + "GoTokenMaker");
+ putMapping(SYNTAX_STYLE_GROOVY, pkg + "GroovyTokenMaker");
+ putMapping(SYNTAX_STYLE_HOSTS, pkg + "HostsTokenMaker");
+ putMapping(SYNTAX_STYLE_HTACCESS, pkg + "HtaccessTokenMaker");
+ putMapping(SYNTAX_STYLE_HTML, pkg + "HTMLTokenMaker");
+ putMapping(SYNTAX_STYLE_INI, pkg + "IniTokenMaker");
+ putMapping(SYNTAX_STYLE_JAVA, pkg + "JavaTokenMaker");
+ putMapping(SYNTAX_STYLE_JAVASCRIPT, pkg + "JavaScriptTokenMaker");
+ putMapping(SYNTAX_STYLE_JSON_WITH_COMMENTS, pkg + "JshintrcTokenMaker");
+ putMapping(SYNTAX_STYLE_JSON, pkg + "JsonTokenMaker");
+ putMapping(SYNTAX_STYLE_JSP, pkg + "JSPTokenMaker");
+ putMapping(SYNTAX_STYLE_LATEX, pkg + "LatexTokenMaker");
+ putMapping(SYNTAX_STYLE_LESS, pkg + "LessTokenMaker");
+ putMapping(SYNTAX_STYLE_LISP, pkg + "LispTokenMaker");
+ putMapping(SYNTAX_STYLE_LUA, pkg + "LuaTokenMaker");
+ putMapping(SYNTAX_STYLE_MAKEFILE, pkg + "MakefileTokenMaker");
+ putMapping(SYNTAX_STYLE_MXML, pkg + "MxmlTokenMaker");
+ putMapping(SYNTAX_STYLE_NSIS, pkg + "NSISTokenMaker");
+ putMapping(SYNTAX_STYLE_PERL, pkg + "PerlTokenMaker");
+ putMapping(SYNTAX_STYLE_PHP, pkg + "PHPTokenMaker");
+ putMapping(SYNTAX_STYLE_PROPERTIES_FILE,pkg + "PropertiesFileTokenMaker");
+ putMapping(SYNTAX_STYLE_PYTHON, pkg + "PythonTokenMaker");
+ putMapping(SYNTAX_STYLE_RUBY, pkg + "RubyTokenMaker");
+ putMapping(SYNTAX_STYLE_SAS, pkg + "SASTokenMaker");
+ putMapping(SYNTAX_STYLE_SCALA, pkg + "ScalaTokenMaker");
+ putMapping(SYNTAX_STYLE_SQL, pkg + "SQLTokenMaker");
+ putMapping(SYNTAX_STYLE_TCL, pkg + "TclTokenMaker");
+ putMapping(SYNTAX_STYLE_TYPESCRIPT, pkg + "TypeScriptTokenMaker");
+ putMapping(SYNTAX_STYLE_UNIX_SHELL, pkg + "UnixShellTokenMaker");
+ putMapping(SYNTAX_STYLE_VISUAL_BASIC, pkg + "VisualBasicTokenMaker");
+ putMapping(SYNTAX_STYLE_WINDOWS_BATCH, pkg + "WindowsBatchTokenMaker");
+ putMapping(SYNTAX_STYLE_XML, pkg + "XMLTokenMaker");
+ putMapping(SYNTAX_STYLE_YAML, pkg + "YamlTokenMaker");
- return map;
+ }
- }
-}
\ No newline at end of file
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultTokenPainter.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultTokenPainter.java
new file mode 100755
index 000000000..152323040
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/DefaultTokenPainter.java
@@ -0,0 +1,279 @@
+/*
+ * 03/16/2013
+ *
+ * DefaultTokenPainter - Standard implementation of a token painter.
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+
+import javax.swing.text.TabExpander;
+
+
+/**
+ * Standard implementation of a token painter.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+class DefaultTokenPainter implements TokenPainter {
+
+ /**
+ * Rectangle used for filling token backgrounds.
+ */
+ private Rectangle2D.Float bgRect;
+
+ /**
+ * Micro-optimization; buffer used to compute tab width. If the width is
+ * correct it's not re-allocated, to prevent lots of very small garbage.
+ * Only used when painting tab lines.
+ */
+ private static char[] tabBuf;
+
+
+ DefaultTokenPainter() {
+ bgRect = new Rectangle2D.Float();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final float paint(Token token, Graphics2D g, float x, float y,
+ RSyntaxTextArea host, TabExpander e) {
+ return paint(token, g, x,y, host, e, 0);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public float paint(Token token, Graphics2D g, float x, float y,
+ RSyntaxTextArea host, TabExpander e, float clipStart) {
+ return paintImpl(token, g, x, y, host, e, clipStart, false, false);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public float paint(Token token, Graphics2D g, float x, float y,
+ RSyntaxTextArea host, TabExpander e, float clipStart,
+ boolean paintBG) {
+ return paintImpl(token, g, x, y, host, e, clipStart, !paintBG, false);
+ }
+
+
+ /**
+ * Paints the background of a token.
+ *
+ * @param x The x-coordinate of the token.
+ * @param y The y-coordinate of the token.
+ * @param width The width of the token (actually, the width of the part of
+ * the token to paint).
+ * @param height The height of the token.
+ * @param g The graphics context with which to paint.
+ * @param fontAscent The ascent of the token's font.
+ * @param host The text area.
+ * @param color The color with which to paint.
+ */
+ protected void paintBackground(float x, float y, float width, float height,
+ Graphics2D g, int fontAscent, RSyntaxTextArea host,
+ Color color) {
+ g.setColor(color);
+ bgRect.setRect(x,y-fontAscent, width,height);
+ //g.fill(bgRect);
+ g.fillRect((int)x, (int)(y-fontAscent), (int)width, (int)height);
+ }
+
+
+ /**
+ * Does the dirty-work of actually painting the token.
+ */
+ protected float paintImpl(Token token, Graphics2D g, float x, float y,
+ RSyntaxTextArea host, TabExpander e, float clipStart,
+ boolean selected, boolean useSTC) {
+
+ int origX = (int)x;
+ int textOffs = token.getTextOffset();
+ char[] text = token.getTextArray();
+ int end = textOffs + token.length();
+ float nextX = x;
+ int flushLen = 0;
+ int flushIndex = textOffs;
+ Color fg = useSTC ? host.getSelectedTextColor() :
+ host.getForegroundForToken(token);
+ Color bg = selected ? null : host.getBackgroundForToken(token);
+ g.setFont(host.getFontForTokenType(token.getType()));
+ FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
+
+ for (int i=textOffs; iendOffs
is less than
+ * startOffs
, or either argument is less than zero.
+ */
+ public DocumentRange(int startOffs, int endOffs) {
+ set(startOffs, endOffs);
+ }
+
+
+ /**
+ * Compares this document range to another.
+ *
+ * @param other Another document range.
+ * @return How the two should be sorted relative to each other.
+ */
+ @Override
+ public int compareTo(DocumentRange other) {
+ if (other==null) {
+ return 1;
+ }
+ int diff = startOffs - other.startOffs;
+ if (diff!=0) {
+ return diff;
+ }
+ return endOffs - other.endOffs;
+ }
+
+
+ /**
+ * Returns whether this document range is equal to another one.
+ *
+ * @param other Another object, presumably a document range.
+ * @return Whether other
is also a document range, and equal
+ * to this one.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other==this) {
+ return true;
+ }
+ if (other instanceof DocumentRange) {
+ return this.compareTo((DocumentRange)other)==0;
+ }
+ return false;
+ }
+
+
+ /**
+ * Gets the end offset of the range.
+ *
+ * @return The end offset.
+ * @see #getStartOffset()
+ */
+ public int getEndOffset() {
+ return endOffs;
+ }
+
+
+ /**
+ * Gets the starting offset of the range.
+ *
+ * @return The starting offset.
+ * @see #getEndOffset()
+ */
+ public int getStartOffset() {
+ return startOffs;
+ }
+
+
+ /**
+ * Overridden simply as a best practice, since {@link #equals(Object)} is
+ * overridden.
+ *
+ * @return The hash code for this object.
+ */
+ @Override
+ public int hashCode() {
+ return startOffs + endOffs;
+ }
+
+
+ /**
+ * Returns whether this document range has zero length. This can happen,
+ * for example, with regex searches of forms like
+ * "foo|"
, where the right-hand sub-expression matches empty
+ * strings.
+ *
+ * @return Whether this document range has zero length.
+ */
+ public boolean isZeroLength() {
+ return startOffs == endOffs;
+ }
+
+
+ /**
+ * Sets the document range.
+ *
+ * @param start The new start value, inclusive.
+ * @param end The new end value, exclusive.
+ * @throws IllegalArgumentException If end
is less than
+ * start
, or either argument is less than zero.
+ */
+ public void set(int start, int end) {
+ if (start<0 || end<0) {
+ throw new IllegalArgumentException(
+ "start and end must be >= 0 (" + start + "-" + end + ")");
+ }
+ if (endErrorStrip
s display ParserNotice
s from
+ * {@link Parser}s. Currently, the only way to get lines flagged in this
+ * component is to register a Parser
on an RSyntaxTextArea and
+ * return ParserNotice
s for each line to display an icon for.
+ * The severity of each notice must be at least the threshold set by
+ * {@link #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)}
+ * to be displayed in this error strip. The default threshold is
+ * {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.ErrorStrip
can be added to a UI like so:
- *
*
* textArea = createTextArea();
+ * textArea.addParser(new MyParser(textArea)); // Identifies lines to display
* scrollPane = new RTextScrollPane(textArea, true);
* ErrorStrip es = new ErrorStrip(textArea);
* JPanel temp = new JPanel(new BorderLayout());
* temp.add(scrollPane);
* temp.add(es, BorderLayout.LINE_END);
*
- *
+ *
* @author Robert Futrell
- * @version 0.1
- */
-/*
- * Possible improvements: 1. Handle marked occurrence changes separately from parser changes. For each property change,
- * call a method that removes the notices being reloaded from the Markers (removing any Markers that are now "empty").
- * 2. When 1.4 support is dropped, replace new Integer(int) with Integer.valueOf(int).
+ * @version 0.5
*/
-public class ErrorStrip extends JComponent {
-
- /**
- * The text area.
- */
- private RSyntaxTextArea textArea;
-
- /**
- * Listens for events in this component.
- */
- private Listener listener;
-
- /**
- * Whether "marked occurrences" in the text area should be shown in this error strip.
- */
- private boolean showMarkedOccurrences;
-
- /**
- * Mapping of colors to brighter colors. This is kept to prevent unnecessary creation of the same Colors over and
- * over.
- */
- private Map brighterColors;
-
- /**
- * Only notices of this severity (or worse) will be displayed in this error strip.
- */
- private int levelThreshold;
-
- /**
- * Whether the caret marker's location should be rendered.
- */
- private boolean followCaret;
-
- /**
- * The color to use for the caret marker.
- */
- private Color caretMarkerColor;
-
- /**
- * Where we paint the caret marker.
- */
- private int caretLineY;
-
- /**
- * The last location of the caret marker.
- */
- private int lastLineY;
-
- /**
- * The preferred width of this component.
- */
- private static final int PREFERRED_WIDTH = 14;
-
- private static final String MSG = "org.fife.ui.rsyntaxtextarea.ErrorStrip";
- private static final ResourceBundle msg = ResourceBundle.getBundle(MSG);
-
- /**
- * Constructor.
- *
- * @param textArea
- * The text area we are examining.
- */
- public ErrorStrip(RSyntaxTextArea textArea) {
- this.textArea = textArea;
- listener = new Listener();
- ToolTipManager.sharedInstance().registerComponent(this);
- setLayout(null); // Manually layout Markers as they can overlap
- addMouseListener(listener);
- setShowMarkedOccurrences(true);
- setLevelThreshold(ParserNotice.WARNING);
- setFollowCaret(true);
- setCaretMarkerColor(Color.BLACK);
- }
-
- /**
- * Overridden so we only start listening for parser notices when this component (and presumably the text area) are
- * visible.
- */
- public void addNotify() {
- super.addNotify();
- textArea.addCaretListener(listener);
- textArea.addPropertyChangeListener(
- RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener);
- textArea.addPropertyChangeListener(
- RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener);
- textArea.addPropertyChangeListener(
- RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);
- refreshMarkers();
- }
-
- /**
- * Manually manages layout since this component uses no layout manager.
- */
- public void doLayout() {
- for (int i = 0; i < getComponentCount(); i++) {
- Marker m = (Marker) getComponent(i);
- m.updateLocation();
- }
- listener.caretUpdate(null); // Force recalculation of caret line pos
- }
-
- /**
- * Returns a "brighter" color.
- *
- * @param c
- * The color.
- * @return A brighter color.
- */
- private Color getBrighterColor(Color c) {
- if (brighterColors == null) {
- brighterColors = new HashMap(5); // Usually small
- }
- Color brighter = (Color) brighterColors.get(c);
- if (brighter == null) {
- // Don't use c.brighter() as it doesn't work well for blue, and
- // also doesn't return something brighter "enough."
- int r = possiblyBrighter(c.getRed());
- int g = possiblyBrighter(c.getGreen());
- int b = possiblyBrighter(c.getBlue());
- brighter = new Color(r, g, b);
- brighterColors.put(c, brighter);
- }
- return brighter;
- }
-
- /**
- * returns the color to use when painting the caret marker.
- *
- * @return The caret marker color.
- * @see #setCaretMarkerColor(Color)
- */
- public Color getCaretMarkerColor() {
- return caretMarkerColor;
- }
-
- /**
- * Returns whether the caret's position should be drawn.
- *
- * @return Whether the caret's position should be drawn.
- * @see #setFollowCaret(boolean)
- */
- public boolean getFollowCaret() {
- return followCaret;
- }
-
- /**
- * {@inheritDoc}
- */
- public Dimension getPreferredSize() {
- int height = textArea.getPreferredScrollableViewportSize().height;
- return new Dimension(PREFERRED_WIDTH, height);
- }
-
- /**
- * Returns the minimum severity a parser notice must be for it to be displayed in this error strip.
- *
- * @return The minimum severity.
- * @see #setLevelThreshold(int)
- */
- public int getLevelThreshold() {
- return levelThreshold;
- }
-
- /**
- * Returns whether marked occurrences are shown in this error strip.
- *
- * @return Whether marked occurrences are shown.
- * @see #setShowMarkedOccurrences(boolean)
- */
- public boolean getShowMarkedOccurrences() {
- return showMarkedOccurrences;
- }
-
- /**
- * {@inheritDoc}
- */
- public String getToolTipText(MouseEvent e) {
- String text = null;
- int line = yToLine(e.getY());
- if (line > -1) {
- text = msg.getString("Line");
- // TODO: 1.5: Use Integer.valueOf(line)
- text = MessageFormat.format(text,
- new Object[] { new Integer(line) });
- }
- return text;
- }
-
- /**
- * Returns the y-offset in this component corresponding to a line in the text component.
- *
- * @param line
- * The line.
- * @return The y-offset.
- * @see #yToLine(int)
- */
- private int lineToY(int line) {
- int h = textArea.getVisibleRect().height;
- float lineCount = textArea.getLineCount();
- return (int) ((line / lineCount) * h) - 2;
- }
-
- /**
- * Overridden to (possibly) draw the caret's position.
- *
- * @param g
- * The graphics context.
- */
- protected void paintComponent(Graphics g) {
- super.paintComponent(g);
- if (caretLineY > -1) {
- g.setColor(getCaretMarkerColor());
- g.fillRect(0, caretLineY, getWidth(), 2);
- }
- }
-
- /**
- * Returns a possibly brighter component for a color.
- *
- * @param i
- * An RGB component for a color (0-255).
- * @return A possibly brighter value for the component.
- */
- private static final int possiblyBrighter(int i) {
- if (i < 255) {
- i += (int) ((255 - i) * 0.8f);
- }
- return i;
- }
-
- /**
- * Refreshes the markers displayed in this error strip.
- */
- private void refreshMarkers() {
-
- removeAll(); // listener is removed in Marker.removeNotify()
- Map markerMap = new HashMap();
-
- List notices = textArea.getParserNotices();
- for (Object notice1 : notices) {
- ParserNotice notice = (ParserNotice) notice1;
- if (notice.getLevel() <= levelThreshold ||
- (notice instanceof TaskNotice)) {
- // 1.5: Use Integer.valueOf(notice.getLine())
- Integer key = new Integer(notice.getLine());
- Marker m = (Marker) markerMap.get(key);
- if (m == null) {
- m = new Marker(notice);
- m.addMouseListener(listener);
- markerMap.put(key, m);
- add(m);
- } else {
- m.addNotice(notice);
- }
- }
- }
-
- if (getShowMarkedOccurrences() && textArea.getMarkOccurrences()) {
- List occurrences = textArea.getMarkedOccurrences();
- for (Object occurrence : occurrences) {
- DocumentRange range = (DocumentRange) occurrence;
- int line = 0;
- try {
- line = textArea.getLineOfOffset(range.getStartOffset());
- } catch (BadLocationException ble) { // Never happens
- continue;
- }
- ParserNotice notice = new MarkedOccurrenceNotice(range);
- // 1.5: Use Integer.valueOf(notice.getLine())
- Integer key = new Integer(line);
- Marker m = (Marker) markerMap.get(key);
- if (m == null) {
- m = new Marker(notice);
- m.addMouseListener(listener);
- markerMap.put(key, m);
- add(m);
- } else {
- if (!m.containsMarkedOccurence()) {
- m.addNotice(notice);
- }
- }
- }
- }
-
- revalidate();
- repaint();
-
- }
-
- /**
- * {@inheritDoc}
- */
- public void removeNotify() {
- super.removeNotify();
- textArea.removeCaretListener(listener);
- textArea.removePropertyChangeListener(
- RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener);
- textArea.removePropertyChangeListener(
- RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener);
- textArea.removePropertyChangeListener(
- RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);
- }
-
- /**
- * Sets the color to use when painting the caret marker.
- *
- * @param color
- * The new caret marker color.
- * @see #getCaretMarkerColor()
- */
- public void setCaretMarkerColor(Color color) {
- if (color != null) {
- caretMarkerColor = color;
- listener.caretUpdate(null); // Force repaint
- }
- }
-
- /**
- * Toggles whether the caret's current location should be drawn.
- *
- * @param follow
- * Whether the caret's current location should be followed.
- * @see #getFollowCaret()
- */
- public void setFollowCaret(boolean follow) {
- if (followCaret != follow) {
- if (followCaret) {
- repaint(0, caretLineY, getWidth(), 2); // Erase
- }
- caretLineY = -1;
- lastLineY = -1;
- followCaret = follow;
- listener.caretUpdate(null); // Possibly repaint
- }
- }
-
- /**
- * Sets the minimum severity a parser notice must be for it to be displayed in this error strip. The default value
- * is {@link ParserNotice#WARNING}.
- *
- * @param level
- * The new severity threshold.
- * @see #getLevelThreshold()
- */
- public void setLevelThreshold(int level) {
- levelThreshold = level;
- if (isDisplayable()) {
- refreshMarkers();
- }
- }
-
- /**
- * Sets whether marked occurrences are shown in this error strip.
- *
- * @param show
- * Whether to show marked occurrences.
- * @see #getShowMarkedOccurrences()
- */
- public void setShowMarkedOccurrences(boolean show) {
- if (show != showMarkedOccurrences) {
- showMarkedOccurrences = show;
- if (isDisplayable()) { // Skip this when we're first created
- refreshMarkers();
- }
- }
- }
-
- /**
- * Returns the line in the text area corresponding to a y-offset in this component.
- *
- * @param y
- * The y-offset.
- * @return The line.
- * @see #lineToY(int)
- */
- private final int yToLine(int y) {
- int line = -1;
- int h = textArea.getVisibleRect().height;
- if (y < h) {
- float at = y / (float) h;
- line = (int) (textArea.getLineCount() * at);
- }
- return line;
- }
-
- /**
- * Listens for events in the error strip and its markers.
- */
- private class Listener extends MouseAdapter
- implements PropertyChangeListener, CaretListener {
-
- private Rectangle visibleRect = new Rectangle();
-
- public void caretUpdate(CaretEvent e) {
- if (getFollowCaret()) {
- int line = textArea.getCaretLineNumber();
- float percent = line / ((float) textArea.getLineCount());
- textArea.computeVisibleRect(visibleRect);
- caretLineY = (int) (visibleRect.height * percent);
- if (caretLineY != lastLineY) {
- repaint(0, lastLineY, getWidth(), 2); // Erase old position
- repaint(0, caretLineY, getWidth(), 2);
- lastLineY = caretLineY;
- }
- }
- }
-
- public void mouseClicked(MouseEvent e) {
-
- Component source = (Component) e.getSource();
- if (source instanceof Marker) {
- ((Marker) source).mouseClicked(e);
- return;
- }
-
- int line = yToLine(e.getY());
- if (line > -1) {
- try {
- int offs = textArea.getLineStartOffset(line);
- textArea.setCaretPosition(offs);
- } catch (BadLocationException ble) { // Never happens
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- }
- }
-
- }
-
- public void propertyChange(PropertyChangeEvent e) {
-
- String propName = e.getPropertyName();
-
- // If they change whether marked occurrences are visible in editor
- if (RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY.equals(propName)) {
- if (getShowMarkedOccurrences()) {
- refreshMarkers();
- }
- }
-
- // If parser notices changed.
- else if (RSyntaxTextArea.PARSER_NOTICES_PROPERTY.equals(propName)) {
- refreshMarkers();
- }
-
- // If marked occurrences changed.
- else if (RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY.
- equals(propName)) {
- if (getShowMarkedOccurrences()) {
- refreshMarkers();
- }
- }
-
- }
-
- }
-
- private static final Color COLOR = new Color(220, 220, 220);
-
- /**
- * A notice that wraps a "marked occurrence."
- */
- private class MarkedOccurrenceNotice implements ParserNotice {
-
- private DocumentRange range;
-
- public MarkedOccurrenceNotice(DocumentRange range) {
- this.range = range;
- }
-
- public int compareTo(Object o) {
- return 0; // Value doesn't matter
- }
-
- public boolean containsPosition(int pos) {
- return pos >= range.getStartOffset() && pos < range.getEndOffset();
- }
-
- public boolean equals(Object o) {
- // FindBugs - Define equals() when defining compareTo()
- return compareTo(o) == 0;
- }
-
- public Color getColor() {
- return COLOR;
- // return textArea.getMarkOccurrencesColor();
- }
-
- public int getLength() {
- return range.getEndOffset() - range.getStartOffset();
- }
-
- public int getLevel() {
- return INFO; // Won't matter
- }
-
- public int getLine() {
- try {
- return textArea.getLineOfOffset(range.getStartOffset());
- } catch (BadLocationException ble) {
- return 0;
- }
- }
-
- public String getMessage() {
- String text = null;
- try {
- String word = textArea.getText(range.getStartOffset(),
- getLength());
- text = msg.getString("OccurrenceOf");
- text = MessageFormat.format(text, new String[] { word });
- } catch (BadLocationException ble) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- }
- return text;
- }
-
- public int getOffset() {
- return range.getStartOffset();
- }
-
- public Parser getParser() {
- return null;
- }
-
- public boolean getShowInEditor() {
- return false; // Value doesn't matter
- }
-
- public String getToolTipText() {
- return null;
- }
-
- public int hashCode() { // FindBugs, since we override equals()
- return 0; // Value doesn't matter for us.
- }
-
- }
-
- /**
- * A "marker" in this error strip, representing one or more notices.
- */
- private class Marker extends JComponent {
-
- private List notices;
-
- public Marker(ParserNotice notice) {
- notices = new ArrayList(1); // Usually just 1
- addNotice(notice);
- setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
- setSize(getPreferredSize());
- ToolTipManager.sharedInstance().registerComponent(this);
- }
-
- public void addNotice(ParserNotice notice) {
- notices.add(notice);
- }
-
- public boolean containsMarkedOccurence() {
- return IntStream.range(0, notices.size()).anyMatch(i -> notices.get(i) instanceof MarkedOccurrenceNotice);
- }
-
- public Color getColor() {
- // Return the color for the highest-level parser.
- Color c = null;
- int lowestLevel = Integer.MAX_VALUE; // ERROR is 0
- for (Object notice1 : notices) {
- ParserNotice notice = (ParserNotice) notice1;
- if (notice.getLevel() < lowestLevel) {
- lowestLevel = notice.getLevel();
- c = notice.getColor();
- }
- }
- return c;
- }
-
- public Dimension getPreferredSize() {
- int w = PREFERRED_WIDTH - 4; // 2-pixel empty border
- return new Dimension(w, 5);
- }
-
- public String getToolTipText() {
-
- String text = null;
-
- if (notices.size() == 1) {
- text = ((ParserNotice) notices.get(0)).getMessage();
- }
- else { // > 1
- StringBuffer sb = new StringBuffer("");
- sb.append(msg.getString("MultipleMarkers"));
- sb.append("
");
- for (Object notice : notices) {
- ParserNotice pn = (ParserNotice) notice;
- sb.append(" - ");
- sb.append(pn.getMessage());
- sb.append("
");
- }
- text = sb.toString();
- }
-
- return text;
-
- }
-
- protected void mouseClicked(MouseEvent e) {
- ParserNotice pn = (ParserNotice) notices.get(0);
- int offs = pn.getOffset();
- int len = pn.getLength();
- if (offs > -1 && len > -1) { // These values are optional
- textArea.setSelectionStart(offs);
- textArea.setSelectionEnd(offs + len);
- }
- else {
- int line = pn.getLine();
- try {
- offs = textArea.getLineStartOffset(line);
- textArea.setCaretPosition(offs);
- } catch (BadLocationException ble) { // Never happens
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- }
- }
- }
-
- protected void paintComponent(Graphics g) {
-
- // TODO: Give "priorities" and always pick color of a notice with
- // highest priority (e.g. parsing errors will usually be red).
-
- Color borderColor = getColor();
- if (borderColor == null) {
- borderColor = Color.DARK_GRAY;
- }
- Color fillColor = getBrighterColor(borderColor);
-
- int w = getWidth();
- int h = getHeight();
-
- g.setColor(fillColor);
- g.fillRect(0, 0, w, h);
-
- g.setColor(borderColor);
- g.drawRect(0, 0, w - 1, h - 1);
-
- }
-
- public void removeNotify() {
- super.removeNotify();
- ToolTipManager.sharedInstance().unregisterComponent(this);
- removeMouseListener(listener);
- }
-
- public void updateLocation() {
- int line = ((ParserNotice) notices.get(0)).getLine();
- int y = lineToY(line);
- setLocation(2, y);
- }
-
- }
-
-}
\ No newline at end of file
+// Possible improvements:
+// 1. Handle marked occurrence changes & "mark all" changes separately from
+// parser changes. For each property change, call a method that removes
+// the notices being reloaded from the Markers (removing any Markers that
+// are now "empty").
+//
+public class ErrorStrip extends JPanel {
+
+ /**
+ * The text area.
+ */
+ private RSyntaxTextArea textArea;
+
+ /**
+ * Listens for events in this component.
+ */
+ private transient Listener listener;
+
+ /**
+ * Whether "marked occurrences" in the text area should be shown in this
+ * error strip.
+ */
+ private boolean showMarkedOccurrences;
+
+ /**
+ * Whether markers for "mark all" highlights should be shown in this
+ * error strip.
+ */
+ private boolean showMarkAll;
+
+ /**
+ * Mapping of colors to brighter colors. This is kept to prevent
+ * unnecessary creation of the same Colors over and over.
+ */
+ private MapParserNotice
class.
+ *
+ * @return The minimum severity.
+ * @see #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)
+ */
+ public ParserNotice.Level getLevelThreshold() {
+ return levelThreshold;
+ }
+
+
+ /**
+ * Returns whether "mark all" highlights are shown in this error strip.
+ *
+ * @return Whether markers are shown for "mark all" highlights.
+ * @see #setShowMarkAll(boolean)
+ */
+ public boolean getShowMarkAll() {
+ return showMarkAll;
+ }
+
+
+ /**
+ * Returns whether marked occurrences are shown in this error strip.
+ *
+ * @return Whether marked occurrences are shown.
+ * @see #setShowMarkedOccurrences(boolean)
+ */
+ public boolean getShowMarkedOccurrences() {
+ return showMarkedOccurrences;
+ }
+
+
+ @Override
+ public String getToolTipText(MouseEvent e) {
+ String text = null;
+ int line = yToLine(e.getY());
+ if (line>-1) {
+ text = MSG.getString("Line");
+ text = MessageFormat.format(text, line + 1);
+ }
+ return text;
+ }
+
+
+ /**
+ * Returns the y-offset in this component corresponding to a line in the
+ * text component.
+ *
+ * @param line The line.
+ * @return The y-offset.
+ * @see #yToLine(int)
+ */
+ private int lineToY(int line) {
+ int h = textArea.getVisibleRect().height;
+ float lineCount = textArea.getLineCount();
+ return (int)(((line-1)/(lineCount-1)) * (h-2));
+ }
+
+
+ /**
+ * Overridden to (possibly) draw the caret's position.
+ *
+ * @param g The graphics context.
+ */
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (caretLineY>-1) {
+ g.setColor(getCaretMarkerColor());
+ g.fillRect(0, caretLineY, getWidth(), 2);
+ }
+ }
+
+
+ /**
+ * Returns a possibly brighter component for a color.
+ *
+ * @param i An RGB component for a color (0-255).
+ * @return A possibly brighter value for the component.
+ */
+ private static int possiblyBrighter(int i) {
+ if (i<255) {
+ i += (int)((255-i)*0.8f);
+ }
+ return i;
+ }
+
+
+ /**
+ * Refreshes the markers displayed in this error strip.
+ */
+ private void refreshMarkers() {
+
+ removeAll(); // listener is removed in Marker.removeNotify()
+ MapMarker
.
+ * @param color The color to use for the markers.
+ */
+ private void addMarkersForRanges(ListParserNotice
class. The default value is
+ * {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
+ *
+ * @param level The new severity threshold.
+ * @see #getLevelThreshold()
+ * @see ParserNotice
+ */
+ public void setLevelThreshold(ParserNotice.Level level) {
+ levelThreshold = level;
+ if (isDisplayable()) {
+ refreshMarkers();
+ }
+ }
+
+
+ /**
+ * Sets the provider of tool tips for markers in this error strip.
+ * Applications can use this method to control the content and format of
+ * the tool tip descriptions of line markers.
+ *
+ * @param provider The provider. If this is null
, a default
+ * implementation will be used.
+ */
+ public void setMarkerToolTipProvider(ErrorStripMarkerToolTipProvider provider) {
+ markerToolTipProvider = provider != null ? provider :
+ new DefaultErrorStripMarkerToolTipProvider();
+ }
+
+
+ /**
+ * Sets whether "mark all" highlights are shown in this error strip.
+ *
+ * @param show Whether to show markers for "mark all" highlights.
+ * @see #getShowMarkAll()
+ */
+ public void setShowMarkAll(boolean show) {
+ if (show!=showMarkAll) {
+ showMarkAll = show;
+ if (isDisplayable()) { // Skip this when we're first created
+ refreshMarkers();
+ }
+ }
+ }
+
+
+ /**
+ * Sets whether marked occurrences are shown in this error strip.
+ *
+ * @param show Whether to show marked occurrences.
+ * @see #getShowMarkedOccurrences()
+ */
+ public void setShowMarkedOccurrences(boolean show) {
+ if (show!=showMarkedOccurrences) {
+ showMarkedOccurrences = show;
+ if (isDisplayable()) { // Skip this when we're first created
+ refreshMarkers();
+ }
+ }
+ }
+
+
+ public void updateUI() {
+
+ super.updateUI();
+
+ if (caretMarkerColor instanceof ColorUIResource) {
+ setCaretMarkerColor(getDefaultCaretMarkerColor());
+ }
+ }
+
+ /**
+ * Returns the line in the text area corresponding to a y-offset in this
+ * component.
+ *
+ * @param y The y-offset.
+ * @return The line.
+ * @see #lineToY(int)
+ */
+ private int yToLine(int y) {
+ int line = -1;
+ int h = textArea.getVisibleRect().height;
+ if (y
");
+ for (ParserNotice pn : notices) {
+ sb.append(" - ");
+ sb.append(pn.getMessage());
+ sb.append("
");
+ }
+ text = sb.toString();
+ }
+
+ return text;
+
+ }
+
+ }
+
+
+ /**
+ * Returns tool tip text for the markers in an {@link ErrorStrip} that
+ * denote one or more parser notices.
+ *
+ * @author predi
+ */
+ public interface ErrorStripMarkerToolTipProvider {
+
+ /**
+ * Returns the tool tip text for a marker in an ErrorStrip
+ * that denotes a given list of parser notices.
+ *
+ * @param notices The list of parser notices.
+ * @return The tool tip text. This may be HTML. Returning
+ * null
will result in no tool tip being displayed.
+ */
+ String getToolTipText(Listtrue
.
+ *
+ * @return true
always.
+ * @see #isLocalAndExists()
+ */
+ @Override
+ public boolean isLocal() {
+ return true;
+ }
+
+
+ /**
+ * Since file locations of this type are guaranteed to be local, this
+ * method returns whether the file exists.
+ *
+ * @return Whether this local file actually exists.
+ * @see #isLocal()
+ */
+ @Override
+ public boolean isLocalAndExists() {
+ return file.exists();
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/FileLocation.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/FileLocation.java
old mode 100644
new mode 100755
index 7d1ce5d2d..d527f6b93
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/FileLocation.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/FileLocation.java
@@ -2,23 +2,9 @@
* 11/13/2008
*
* FileLocation.java - Holds the location of a local or remote file.
- * Copyright (C) 2008 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -26,118 +12,141 @@
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.net.MalformedURLException;
import java.net.URL;
+
/**
- * Holds the location of a local or remote file.
- *
+ * Holds the location of a local or remote file. This provides a common way
+ * to read, write, and check properties of both local and remote files.
+ *
* @author Robert Futrell
* @version 1.0
*/
public abstract class FileLocation {
- /**
- * Creates a {@link FileLocation} instance for the specified local file.
- *
- * @param fileFullPath
- * The full path to a local file.
- * @return The file's location.
- */
- public static FileLocation create(String fileFullPath) {
- return new FileFileLocation(new File(fileFullPath));
- }
-
- /**
- * Creates a {@link FileLocation} instance for the specified local file.
- *
- * @param file
- * A local file.
- * @return The file's location.
- */
- public static FileLocation create(File file) {
- return new FileFileLocation(file);
- }
-
- /**
- * Creates a {@link FileLocation} instance for the specified file.
- *
- * @param url
- * The URL of a file.
- * @return The file's location.
- */
- public static FileLocation create(URL url) {
- if ("file".equalsIgnoreCase(url.getProtocol())) {
- return new FileFileLocation(new File(url.getPath()));
- }
- return new URLFileLocation(url);
- }
-
- /**
- * Returns the last time this file was modified, or {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value
- * cannot be computed (such as for a remote file).
- *
- * @return The last time this file was modified.
- */
- protected abstract long getActualLastModified();
-
- /**
- * Returns the full path to the file. This will be stripped of sensitive information such as passwords for remote
- * files.
- *
- * @return The full path to the file.
- * @see #getFileName()
- */
- public abstract String getFileFullPath();
-
- /**
- * Returns the name of the file.
- *
- * @return The name of the file.
- * @see #getFileFullPath()
- */
- public abstract String getFileName();
-
- /**
- * Opens an input stream for reading from this file.
- *
- * @return The input stream.
- * @throws IOException
- * If the file does not exist, or some other IO error occurs.
- */
- protected abstract InputStream getInputStream() throws IOException;
-
- /**
- * Opens an output stream for writing this file.
- *
- * @return An output stream.
- * @throws IOException
- * If an IO error occurs.
- */
- protected abstract OutputStream getOutputStream() throws IOException;
-
- /**
- * Returns whether this file location is a local file.
- *
- * @return Whether this is a local file.
- * @see #isLocalAndExists()
- */
- public abstract boolean isLocal();
-
- /**
- * Returns whether this file location is a local file that already exists.
- *
- * @return Whether this file is local and actually exists.
- * @see #isLocal()
- */
- public abstract boolean isLocalAndExists();
-
- /**
- * Returns whether this file location is a remote location.
- *
- * @return Whether this is a remote file location.
- */
- public boolean isRemote() {
- return !isLocal();
- }
-
-}
\ No newline at end of file
+
+ /**
+ * Creates a {@link FileLocation} instance for the specified local file.
+ *
+ * @param fileFullPath The full path to a local file.
+ * @return The file's location.
+ */
+ public static FileLocation create(String fileFullPath) {
+ if (fileFullPath.startsWith("http://") ||
+ fileFullPath.startsWith("https://") ||
+ fileFullPath.startsWith("ftp://")) {
+ try {
+ return new URLFileLocation(new URL(fileFullPath));
+ } catch (MalformedURLException mue) {
+ throw new IllegalArgumentException(
+ "Not a valid URL: " + fileFullPath, mue);
+ }
+ }
+ return new FileFileLocation(new File(fileFullPath));
+ }
+
+
+ /**
+ * Creates a {@link FileLocation} instance for the specified local file.
+ *
+ * @param file A local file.
+ * @return The file's location.
+ */
+ public static FileLocation create(File file) {
+ return new FileFileLocation(file);
+ }
+
+
+ /**
+ * Creates a {@link FileLocation} instance for the specified file.
+ *
+ * @param url The URL of a file.
+ * @return The file's location.
+ */
+ public static FileLocation create(URL url) {
+ if ("file".equalsIgnoreCase(url.getProtocol())) {
+ return new FileFileLocation(new File(url.getPath()));
+ }
+ return new URLFileLocation(url);
+ }
+
+
+ /**
+ * Returns the last time this file was modified, or
+ * {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be
+ * computed (such as for a remote file).
+ *
+ * @return The last time this file was modified.
+ */
+ protected abstract long getActualLastModified();
+
+
+ /**
+ * Returns the full path to the file. This will be stripped of
+ * sensitive information such as passwords for remote files.
+ *
+ * @return The full path to the file.
+ * @see #getFileName()
+ */
+ public abstract String getFileFullPath();
+
+
+ /**
+ * Returns the name of the file.
+ *
+ * @return The name of the file.
+ * @see #getFileFullPath()
+ */
+ public abstract String getFileName();
+
+
+ /**
+ * Opens an input stream for reading from this file.
+ *
+ * @return The input stream.
+ * @throws IOException If the file does not exist, or some other IO error
+ * occurs.
+ */
+ protected abstract InputStream getInputStream() throws IOException;
+
+
+ /**
+ * Opens an output stream for writing this file.
+ *
+ * @return An output stream.
+ * @throws IOException If an IO error occurs.
+ */
+ protected abstract OutputStream getOutputStream() throws IOException;
+
+
+ /**
+ * Returns whether this file location is a local file.
+ *
+ * @return Whether this is a local file.
+ * @see #isLocalAndExists()
+ */
+ public abstract boolean isLocal();
+
+
+ /**
+ * Returns whether this file location is a local file that already
+ * exists.
+ *
+ * @return Whether this file is local and actually exists.
+ * @see #isLocal()
+ */
+ public abstract boolean isLocalAndExists();
+
+
+ /**
+ * Returns whether this file location is a remote location.
+ *
+ * @return Whether this is a remote file location.
+ */
+ public boolean isRemote() {
+ return !isLocal();
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/FoldingAwareIconRowHeader.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/FoldingAwareIconRowHeader.java
new file mode 100755
index 000000000..d497e9d69
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/FoldingAwareIconRowHeader.java
@@ -0,0 +1,251 @@
+/*
+ * 03/07/2012
+ *
+ * FoldingAwareIconRowHeader - Icon row header that paints itself correctly
+ * even when code folding is enabled.
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Point;
+import javax.swing.Icon;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+
+import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
+import org.fife.ui.rtextarea.GutterIconInfo;
+import org.fife.ui.rtextarea.IconRowHeader;
+
+
+/**
+ * A row header component that takes code folding into account when painting
+ * itself.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public class FoldingAwareIconRowHeader extends IconRowHeader {
+
+
+ /**
+ * Constructor.
+ *
+ * @param textArea The parent text area.
+ */
+ public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
+ super(textArea);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void paintComponent(Graphics g) {
+
+ // When line wrap is not enabled, take the faster code path.
+ if (textArea==null) {
+ return;
+ }
+ RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
+ FoldManager fm = rsta.getFoldManager();
+ if (!fm.isCodeFoldingSupportedAndEnabled()) {
+ super.paintComponent(g);
+ return;
+ }
+
+ visibleRect = g.getClipBounds(visibleRect);
+ if (visibleRect==null) { // ???
+ visibleRect = getVisibleRect();
+ }
+ //System.out.println("IconRowHeader repainting: " + visibleRect);
+ if (visibleRect==null) {
+ return;
+ }
+ paintBackgroundImpl(g, visibleRect);
+
+ if (textArea.getLineWrap()) {
+ paintComponentWrapped(g);
+ return;
+ }
+
+ Document doc = textArea.getDocument();
+ Element root = doc.getDefaultRootElement();
+ textAreaInsets = textArea.getInsets(textAreaInsets);
+ if (visibleRect.ynull
is returned.<
, >
and &
) replaced
+ * by their HTML escape sequences.
+ *
+ * @param s The input string.
+ * @param newlineReplacement What to replace newline characters with.
+ * If this is null
, they are simply removed.
+ * @param inPreBlock Whether this HTML will be in within pre
+ * tags. If this is true
, spaces will be kept as-is;
+ * otherwise, they will be converted to "
".
+ * @return The escaped version of s
.
+ */
+ public static String escapeForHtml(String s, String newlineReplacement,
+ boolean inPreBlock) {
+
+ if (newlineReplacement==null) {
+ newlineReplacement = "";
+ }
+ String tabString = " ";
+
+ StringBuilder sb = new StringBuilder();
+
+ for (int i=0; i#rrggbb
",
+ * or null
if c
is null
.
+ */
+ public static String getHexString(Color c) {
+
+ if (c == null) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder("#");
+
+ int r = c.getRed();
+ if (r<16) {
+ sb.append('0');
+ }
+ sb.append(Integer.toHexString(r));
+ int g = c.getGreen();
+ if (g<16) {
+ sb.append('0');
+ }
+ sb.append(Integer.toHexString(g));
+ int b = c.getBlue();
+ if (b<16) {
+ sb.append('0');
+ }
+ sb.append(Integer.toHexString(b));
+
+ return sb.toString();
+ }
+
+ public static String getTextAsHtml(RSyntaxTextArea textArea, int start, int end) {
+
+ // Create the selection as HTML
+ StringBuilder sb = new StringBuilder("");
+ }
+
+ Token token = textArea.getTokenListFor(start, end);
+ for (Token t = token; t != null; t = t.getNextToken()) {
+
+ if (t.isPaintable()) {
+
+ if (t.isSingleChar('\n')) {
+ sb.append("
");
+ return sb.toString();
+ }
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/LinkGenerator.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/LinkGenerator.java
new file mode 100755
index 000000000..08caf51e1
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/LinkGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * 02/16/2012
+ *
+ * Copyright (C) 2013 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+
+/**
+ * Generates hyperlinks in a document. If one of these is installed on an
+ *
");
+ }
+ else {
+ sb.append(TokenUtils.tokenToHtml(textArea, t));
+ }
+ }
+ }
+
+ sb.append("RSyntaxTextArea
it is queried when the mouse is moved and
+ * hyperlinks are enabled. If the user is not hovering over a "real" hyperlink
+ * (e.g. "http://www.google.com"), the link generator is asked if a text region
+ * at the mouse position should be considered a hyperlink. If so, a result
+ * object is returned, describing exactly what region of text is the link, and
+ * where it goes to.null
if no link is at the
+ * specified offset.
+ */
+ LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offs);
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/LinkGeneratorResult.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/LinkGeneratorResult.java
new file mode 100755
index 000000000..c2ec5f65f
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/LinkGeneratorResult.java
@@ -0,0 +1,53 @@
+/*
+ * 02/16/2012
+ *
+ * Copyright (C) 2013 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import javax.swing.event.HyperlinkEvent;
+
+
+/**
+ * A result object from a {@link LinkGenerator}. Implementations of this class
+ * specify what action to execute when the user clicks on the "link" specified
+ * by the LinkGenerator
. Typically, this will do something like
+ * select another region of text in the document (the declaration of the
+ * variable at the mouse position), or open another file in the parent
+ * application, etc.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ * @see SelectRegionLinkGeneratorResult
+ */
+public interface LinkGeneratorResult {
+
+
+ /**
+ * Executes the action associated with this object. If the result is a
+ * URL to open, a standard hyperlink event can be returned. Alternatively,
+ * null
can be returned and the action performed in this
+ * method itself.
+ *
+ * @return The hyperlink event to broadcast from the text area, or
+ * null
if the action's behavior occurs in this method
+ * directly.
+ */
+ HyperlinkEvent execute();
+
+
+ /**
+ * Returns the starting offset of the link specified by the parent
+ * LinkGenerator
.
+ *
+ * @return The offset.
+ */
+ int getSourceOffset();
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MarkOccurrencesHighlightPainter.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MarkOccurrencesHighlightPainter.java
deleted file mode 100644
index 2e0e32734..000000000
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MarkOccurrencesHighlightPainter.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 10/01/2009
- *
- * MarkOccurrencesHighlightPainter.java - Renders "marked occurrences."
- * Copyright (C) 2009 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-package org.fife.ui.rsyntaxtextarea;
-
-//import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.Shape;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.JTextComponent;
-import javax.swing.text.Position;
-import javax.swing.text.View;
-
-/**
- * Highlight painter that renders "mark occurrences."
- *
- * @author Robert Futrell
- * @version 1.0
- */
-/*
- * NOTE: This implementation is a "hack" so typing at the "end" of the highlight does not extend it to include the
- * newly-typed chars, which is the standard behavior of Swing Highlights.
- */
-class MarkOccurrencesHighlightPainter extends ChangeableColorHighlightPainter {
-
- private Color borderColor;
-
- // private BasicStroke stroke;
-
- /**
- * Constructor.
- *
- * @param color
- * The color to draw the bounding boxes with. This cannot be null
.
- */
- public MarkOccurrencesHighlightPainter() {
- super(Color.BLUE);
- // float[] dash = { 6, 4 };
- // stroke = new BasicStroke(1, BasicStroke.CAP_BUTT,
- // BasicStroke.JOIN_MITER, 1, dash, 0);
- }
-
- /**
- * {@inheritDoc}
- */
- public Shape paintLayer(Graphics g, int p0, int p1, Shape viewBounds,
- JTextComponent c, View view) {
-
- g.setColor(getColor());
- p1++; // Workaround for Java Highlight issues.
-
- // This special case isn't needed for most standard Swing Views (which
- // always return a width of 1 for modelToView() calls), but it is
- // needed for RSTA views, which actually return the width of chars for
- // modelToView calls. But this should be faster anyway, as we
- // short-circuit and do only one modelToView() for one offset.
- if (p0 == p1) {
- try {
- Shape s = view.modelToView(p0, viewBounds,
- Position.Bias.Forward);
- Rectangle r = s.getBounds();
- g.drawLine(r.x, r.y, r.x, r.y + r.height);
- return r;
- } catch (BadLocationException ble) {
- ble.printStackTrace(); // Never happens
- return null;
- }
- }
-
- if (p0 == view.getStartOffset() && p1 == view.getEndOffset()) {
- // Contained in view, can just use bounds.
- Rectangle alloc;
- if (viewBounds instanceof Rectangle) {
- alloc = (Rectangle) viewBounds;
- } else {
- alloc = viewBounds.getBounds();
- }
- g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height);
- return alloc;
- }
-
- // Should only render part of View.
- Graphics2D g2d = (Graphics2D) g;
- try {
- // --- determine locations ---
- Shape shape = view.modelToView(p0, Position.Bias.Forward, p1,
- Position.Bias.Backward, viewBounds);
- Rectangle r = (shape instanceof Rectangle) ? (Rectangle) shape
- : shape.getBounds();
- g2d.fillRect(r.x, r.y, r.width, r.height);
- g2d.setColor(borderColor);
- // Stroke oldStroke = g2d.getStroke();
- // g2d.setStroke(stroke);
- g2d.drawRect(r.x, r.y, r.width - 1, r.height - 1);
- // g2d.setStroke(oldStroke);
- return r;
- } catch (BadLocationException e) { // Never happens
- e.printStackTrace();
- return null;
- }
-
- }
-
- /**
- * {@inheritDoc}
- */
- public void setColor(Color c) {
- super.setColor(c);
- borderColor = c.darker();
- }
-
-}
\ No newline at end of file
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MarkOccurrencesSupport.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MarkOccurrencesSupport.java
old mode 100644
new mode 100755
index b497b1e61..736125807
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MarkOccurrencesSupport.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/MarkOccurrencesSupport.java
@@ -3,288 +3,289 @@
*
* MarkOccurrencesSupport.java - Handles marking all occurrences of the
* currently selected identifier in a text area.
- * Copyright (C) 2009 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
-import java.awt.Paint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.List;
+
import javax.swing.Timer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
-import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
+import org.fife.ui.rtextarea.SmartHighlightPainter;
+
+
/**
- * Marks all occurrences of the token at the current caret position, if it is an identifier.
- *
+ * Marks all occurrences of the token at the current caret position, if it is
+ * an identifier.
+ *
* @author Robert Futrell
* @version 1.0
+ * @see OccurrenceMarker
*/
class MarkOccurrencesSupport implements CaretListener, ActionListener {
- private RSyntaxTextArea textArea;
- private Timer timer;
- private MarkOccurrencesHighlightPainter p;
- private List tags;
-
- /**
- * The default delay.
- */
- private static final int DEFAULT_DELAY_MS = 1000;
-
- /**
- * Constructor. Creates a listener with a 1 second delay.
- */
- public MarkOccurrencesSupport() {
- this(DEFAULT_DELAY_MS);
- }
-
- /**
- * Constructor.
- *
- * @param delay
- * The delay between when the caret last moves and when the text should be scanned for matching
- * occurrences. This should be in milliseconds.
- */
- public MarkOccurrencesSupport(int delay) {
- this(delay, new Color(224, 224, 224));
- }
-
- /**
- * Constructor.
- *
- * @param delay
- * The delay between when the caret last moves and when the text should be scanned for matching
- * occurrences. This should be in milliseconds.
- * @param color
- * The color to use to mark the occurrences. This cannot be null
.
- */
- public MarkOccurrencesSupport(int delay, Color color) {
- timer = new Timer(delay, this);
- timer.setRepeats(false);
- p = new MarkOccurrencesHighlightPainter();
- setColor(color);
- tags = new ArrayList();
- }
-
- /**
- * Called after the caret has been moved and a fixed time delay has elapsed. This locates and highlights all
- * occurrences of the identifier at the caret position, if any.
- *
- * @param e
- * The event.
- */
- public void actionPerformed(ActionEvent e) {
-
- // Don't do anything if they are selecting text.
- Caret c = textArea.getCaret();
- if (c.getDot() != c.getMark()) {
- return;
- }
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- // long time = System.currentTimeMillis();
- doc.readLock();
- try {
-
- // Remove old highlights
- removeHighlights();
-
- // Get the token at the caret position.
- int line = textArea.getCaretLineNumber();
- Token tokenList = textArea.getTokenListForLine(line);
- int dot = c.getDot();
- Token t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
- if (t == null /* EOL */|| !isValidType(t) || isNonWordChar(t)) {
- // Try to the "left" of the caret.
- dot--;
- try {
- if (dot >= textArea.getLineStartOffset(line)) {
- t = RSyntaxUtilities.getTokenAtOffset(tokenList, dot);
- }
- } catch (BadLocationException ble) {
- ble.printStackTrace(); // Never happens
- }
- }
-
- // Add new highlights if an identifier is selected.
- if (t != null && isValidType(t) && !isNonWordChar(t)) {
- RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.
- getHighlighter();
- String lexeme = t.getLexeme();
- int type = t.type;
- for (int i = 0; i < textArea.getLineCount(); i++) {
- Token temp = textArea.getTokenListForLine(i);
- while (temp != null && temp.isPaintable()) {
- if (temp.is(type, lexeme)) {
- try {
- int end = temp.offset + temp.textCount;
- Object tag = h.addMarkedOccurrenceHighlight(temp.offset, end, p);
- // end--; // HACK to prevent typed chars from being added
- // Object tag = h.addHighlight(temp.offset, end,p);
- tags.add(tag);
- // // HACK again, to ensure repaint of last char rendered.
- // textArea.getUI().damageRange(textArea, end+1, end+1);
- } catch (BadLocationException ble) {
- ble.printStackTrace(); // Never happens
- }
- }
- temp = temp.getNextToken();
- }
- }
- }
-
- } finally {
- doc.readUnlock();
- // time = System.currentTimeMillis() - time;
- // System.out.println("Took: " + time + " ms");
- }
-
- textArea.fireMarkedOccurrencesChanged();
-
- }
-
- /**
- * Called when the caret moves in the text area.
- *
- * @param e
- * The event.
- */
- public void caretUpdate(CaretEvent e) {
- timer.restart();
- }
-
- /**
- * Returns the color being used to mark occurrences.
- *
- * @return The color being used.
- * @see #setColor(Paint)
- */
- public Color getColor() {
- return p.getColor();
- }
-
- /**
- * Returns the delay, in milliseconds.
- *
- * @return The delay.
- * @see #setDelay(int)
- */
- public int getDelay() {
- return timer.getDelay();
- }
-
- /**
- * Installs this listener on a text area. If it is already installed on another text area, it is uninstalled first.
- *
- * @param textArea
- * The text area to install on.
- */
- public void install(RSyntaxTextArea textArea) {
- if (this.textArea != null) {
- uninstall();
- }
- this.textArea = textArea;
- textArea.addCaretListener(this);
- }
-
- /**
- * Returns whether the specified token is a single non-word char (e.g. not in [A-Za-z]. This is a HACK to
- * work around the fact that many standard token makers return things like semicolons and periods as
- * {@link Token#IDENTIFIER}s just to make the syntax highlighting coloring look a little better.
- *
- * @param t
- * The token to check. This cannot be null.
- * @return Whether the token is a single non-word char.
- */
- private static final boolean isNonWordChar(Token t) {
- return t.textCount == 1 &&
- !RSyntaxUtilities.isLetter(t.text[t.textOffset]);
- }
-
- /**
- * Returns whether the specified token is a type that we can do a "mark occurrences" on.
- *
- * @param t
- * The token.
- * @return Whether we should mark all occurrences of this token.
- */
- private boolean isValidType(Token t) {
- return textArea.getMarkOccurrencesOfTokenType(t.type);
- }
-
- /**
- * Removes all highlights added to the text area by this listener.
- */
- private void removeHighlights() {
- if (textArea != null) {
- RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
- textArea.getHighlighter();
- for (Object tag : tags) {
- h.removeMarkOccurrencesHighlight(tag);
- }
- }
- tags.clear();
- }
-
- /**
- * Sets the color to use when marking occurrences.
- *
- * @param color
- * The color to use.
- * @see #getColor()
- */
- public void setColor(Color color) {
- p.setColor(color);
- if (textArea != null) {
- removeHighlights();
- caretUpdate(null); // Force a highlight repaint.
- }
- }
-
- /**
- * Sets the delay between the last caret position change and when the text is scanned for matching identifiers. A
- * delay is needed to prevent repeated scanning while the user is typing.
- *
- * @param delay
- * The new delay.
- * @see #getDelay()
- */
- public void setDelay(int delay) {
- timer.setDelay(delay);
- }
-
- /**
- * Uninstalls this listener from the current text area. Does nothing if it not currently installed on any text area.
- *
- * @see #install(RSyntaxTextArea)
- */
- public void uninstall() {
- if (textArea != null) {
- removeHighlights();
- textArea.removeCaretListener(this);
- }
- }
-
-}
\ No newline at end of file
+ private RSyntaxTextArea textArea;
+ private Timer timer;
+ private SmartHighlightPainter p;
+
+ /**
+ * The default color used to mark occurrences.
+ */
+ static final Color DEFAULT_COLOR = new Color(224, 224, 224);
+
+ /**
+ * The default delay.
+ */
+ static final int DEFAULT_DELAY_MS = 1000;
+
+
+ /**
+ * Constructor. Creates a listener with a 1 second delay.
+ */
+ MarkOccurrencesSupport() {
+ this(DEFAULT_DELAY_MS);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param delay The delay between when the caret last moves and when the
+ * text should be scanned for matching occurrences. This should
+ * be in milliseconds.
+ */
+ MarkOccurrencesSupport(int delay) {
+ this(delay, DEFAULT_COLOR);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param delay The delay between when the caret last moves and when the
+ * text should be scanned for matching occurrences. This should
+ * be in milliseconds.
+ * @param color The color to use to mark the occurrences. This cannot be
+ * null
.
+ */
+ MarkOccurrencesSupport(int delay, Color color) {
+ timer = new Timer(delay, this);
+ timer.setRepeats(false);
+ p = new SmartHighlightPainter();
+ setColor(color);
+ }
+
+
+ /**
+ * Called after the caret has been moved and a fixed time delay has
+ * elapsed. This locates and highlights all occurrences of the identifier
+ * at the caret position, if any.
");
+ line++;
+ }
+
+ return sb.toString();
+
+ }
+
+
+ /**
+ * Adds key bindings to this popup.
+ */
+ private void installKeyBindings() {
+
+ InputMap im = getRootPane().getInputMap(
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ ActionMap am = getRootPane().getActionMap();
+
+ KeyStroke escapeKS = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
+ im.put(escapeKS, "onEscape");
+ am.put("onEscape", new EscapeAction());
+ }
+
+
+ /**
+ * Positions this popup to be in the top right-hand corner of the parent
+ * editor.
+ */
+ private void setLocation() {
+ Point topLeft = textArea.getVisibleRect().getLocation();
+ SwingUtilities.convertPointToScreen(topLeft, textArea);
+ topLeft.y = Math.max(topLeft.y - 24, 0);
+ setLocation(topLeft.x - LEFT_EMPTY_BORDER, topLeft.y);
+ }
+
+
+ /**
+ * Action performed when Escape is pressed in this popup.
+ */
+ private class EscapeAction extends AbstractAction {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ listener.uninstallAndHide();
+ }
+
+ }
+
+
+ /**
+ * Listens for events in this popup.
+ */
+ private class Listener extends WindowAdapter implements ComponentListener {
+
+ Listener() {
+
+ addWindowFocusListener(this);
+
+ // If anything happens to the "parent" window, hide this popup
+ Window parent = (Window)getParent();
+ parent.addWindowFocusListener(this);
+ parent.addWindowListener(this);
+ parent.addComponentListener(this);
+
+ }
+
+ @Override
+ public void componentResized(ComponentEvent e) {
+ uninstallAndHide();
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ uninstallAndHide();
+ }
+
+ @Override
+ public void componentShown(ComponentEvent e) {
+ uninstallAndHide();
+ }
+
+ @Override
+ public void componentHidden(ComponentEvent e) {
+ uninstallAndHide();
+ }
+
+ @Override
+ public void windowActivated(WindowEvent e) {
+ checkForParentWindowEvent(e);
+ }
+
+ @Override
+ public void windowLostFocus(WindowEvent e) {
+ uninstallAndHide();
+ }
+
+ @Override
+ public void windowIconified(WindowEvent e) {
+ checkForParentWindowEvent(e);
+ }
+
+ private boolean checkForParentWindowEvent(WindowEvent e) {
+ if (e.getSource()==getParent()) {
+ uninstallAndHide();
+ return true;
+ }
+ return false;
+ }
+
+ private void uninstallAndHide() {
+ Window parent = (Window)getParent();
+ parent.removeWindowFocusListener(this);
+ parent.removeWindowListener(this);
+ parent.removeComponentListener(this);
+ removeWindowFocusListener(this);
+ setVisible(false);
+ dispose();
+ }
+
+ }
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/OccurrenceMarker.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/OccurrenceMarker.java
new file mode 100755
index 000000000..b9b642040
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/OccurrenceMarker.java
@@ -0,0 +1,63 @@
+/*
+ * 03/09/2013
+ *
+ * OccurrenceMarker - Marks occurrences of the current token.
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import org.fife.ui.rtextarea.SmartHighlightPainter;
+
+
+/**
+ * An OccurrenceMarker
is called when the caret stops moving after
+ * a short period. If the current {@link TokenMaker} returns an instance of
+ * this class, it is told to mark all occurrences of the identifier at the
+ * caret position.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+public interface OccurrenceMarker {
+
+
+ /**
+ * Returns the token to mark occurrences, of, provided it matches the
+ * criteria put forth by {@link #isValidType(RSyntaxTextArea, Token)}.
+ * For most languages, this method should return the token at the caret
+ * position.
+ *
+ * @param textArea The text area.
+ * @return The token to (possibly) mark occurrences of, or
+ * null
if none.
+ */
+ Token getTokenToMark(RSyntaxTextArea textArea);
+
+
+ /**
+ * Returns whether the specified token is a type that we can do a
+ * "mark occurrences" of. Typically, this will delegate to
+ * {@link RSyntaxTextArea#getMarkOccurrencesOfTokenType(int)}.
+ *
+ * @param textArea The text area.
+ * @param t The token.
+ * @return Whether we should mark all occurrences of this token.
+ */
+ boolean isValidType(RSyntaxTextArea textArea, Token t);
+
+
+ /**
+ * Called when occurrences of a token should be marked.
+ *
+ * @param doc The document.
+ * @param t The document whose relevant occurrences should be marked.
+ * @param h The highlighter to add the highlights to.
+ * @param p The painter for the highlights.
+ */
+ void markOccurrences(RSyntaxDocument doc, Token t,
+ RSyntaxTextAreaHighlighter h, SmartHighlightPainter p);
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ParserManager.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ParserManager.java
old mode 100644
new mode 100755
index 7ba1a5ca4..b977cc5fc
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ParserManager.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/ParserManager.java
@@ -3,36 +3,26 @@
*
* ParserManager.java - Manages the parsing of an RSyntaxTextArea's document,
* if necessary.
- * Copyright (C) 2005 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
+import java.awt.Point;
+import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.net.URL;
+import java.security.AccessControlException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
+
import javax.swing.Timer;
import javax.swing.ToolTipManager;
import javax.swing.event.DocumentEvent;
@@ -40,619 +30,794 @@
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Position;
-import org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip;
import org.fife.ui.rsyntaxtextarea.parser.ParseResult;
import org.fife.ui.rsyntaxtextarea.parser.Parser;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.fife.ui.rsyntaxtextarea.parser.ToolTipInfo;
+import org.fife.ui.rtextarea.RDocument;
+import org.fife.ui.rtextarea.RTextAreaHighlighter.HighlightInfo;
+
+
/**
* Manages running a parser object for an RSyntaxTextArea
.
- *
+ *
* @author Robert Futrell
- * @version 0.8
+ * @version 0.9
*/
class ParserManager implements DocumentListener, ActionListener,
- HyperlinkListener {
-
- private RSyntaxTextArea textArea;
- private List parsers;
- private Timer timer;
- private boolean running;
- private Map noticesToHighlights;
- private Parser parserForTip;
- private Position firstOffsetModded;
- private Position lastOffsetModded;
-
- /**
- * Painter used to underline errors.
- */
- private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter =
- new SquiggleUnderlineHighlightPainter(Color.RED);
-
- /**
- * If this system property is set to true
, debug messages will be printed to stdout to help diagnose
- * parsing issues.
- */
- private static final String PROPERTY_DEBUG_PARSING = "rsta.debugParsing";
-
- private static final boolean DEBUG_PARSING = Boolean.getBoolean(
- PROPERTY_DEBUG_PARSING);
-
- /**
- * The default delay between the last key press and when the document is parsed, in milliseconds.
- */
- private static final int DEFAULT_DELAY_MS = 1250;
-
- /**
- * Constructor.
- *
- * @param textArea
- * The text area whose document the parser will be parsing.
- */
- public ParserManager(RSyntaxTextArea textArea) {
- this(DEFAULT_DELAY_MS, textArea);
- }
-
- /**
- * Constructor.
- *
- * @param delay
- * The delay between the last key press and when the document is parsed.
- * @param textArea
- * The text area whose document the parser will be parsing.
- */
- public ParserManager(int delay, RSyntaxTextArea textArea) {
- this.textArea = textArea;
- textArea.getDocument().addDocumentListener(this);
- parsers = new ArrayList(1); // Usually small
- timer = new Timer(delay, this);
- timer.setRepeats(false);
- running = true;
- }
-
- /**
- * Called when the timer fires (e.g. it's time to parse the document).
- *
- * @param e
- * The event.
- */
- public void actionPerformed(ActionEvent e) {
-
- // Sanity check - should have >1 parser if event is fired.
- int parserCount = getParserCount();
- if (parserCount == 0) {
- return;
- }
-
- long begin = 0;
- if (DEBUG_PARSING) {
- begin = System.currentTimeMillis();
- }
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
-
- Element root = doc.getDefaultRootElement();
- int firstLine = firstOffsetModded == null ? 0 : root.getElementIndex(firstOffsetModded.getOffset());
- int lastLine = lastOffsetModded == null ? root.getElementCount() - 1 : root.getElementIndex(lastOffsetModded
- .getOffset());
- firstOffsetModded = lastOffsetModded = null;
- if (DEBUG_PARSING) {
- System.out.println("[DEBUG]: Minimum lines to parse: " + firstLine + "-" + lastLine);
- }
-
- String style = textArea.getSyntaxEditingStyle();
- doc.readLock();
- try {
- for (int i = 0; i < parserCount; i++) {
- Parser parser = getParser(i);
- if (parser.isEnabled()) {
- ParseResult res = parser.parse(doc, style);
- addParserNoticeHighlights(res);
- }
- else {
- clearParserNoticeHighlights(parser);
- }
- }
- textArea.fireParserNoticesChange();
- } finally {
- doc.readUnlock();
- }
-
- if (DEBUG_PARSING) {
- float time = (System.currentTimeMillis() - begin) / 1000f;
- System.err.println("Total parsing time: " + time + " seconds");
- }
-
- }
-
- /**
- * Adds a parser for the text area.
- *
- * @param parser
- * The new parser. If this is null
, nothing happens.
- * @see #getParser(int)
- * @see #removeParser(Parser)
- */
- public void addParser(Parser parser) {
- if (parser != null && !parsers.contains(parser)) {
- if (running) {
- timer.stop();
- }
- parsers.add(parser);
- if (parsers.size() == 1) {
- // Okay to call more than once.
- ToolTipManager.sharedInstance().registerComponent(textArea);
- }
- if (running) {
- timer.restart();
- }
- }
- }
-
- /**
- * Adds highlights for a list of parser notices. Any current notices from the same Parser, in the same parsed range,
- * are removed.
- *
- * @param res
- * The result of a parsing.
- * @see #clearParserNoticeHighlights()
- */
- private void addParserNoticeHighlights(ParseResult res) {
-
- if (DEBUG_PARSING) {
- System.out.println("[DEBUG]: Adding parser notices from " +
- res.getParser());
- }
-
- if (noticesToHighlights == null) {
- noticesToHighlights = new HashMap();
- }
-
- removeParserNotices(res);
-
- List notices = res.getNotices();
- if (notices.size() > 0) { // Guaranteed non-null
-
- RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
- textArea.getHighlighter();
-
- for (Object notice1 : notices) {
- ParserNotice notice = (ParserNotice) notice1;
- if (DEBUG_PARSING) {
- System.out.println("[DEBUG]: ... adding: " + res);
- }
- try {
- Object highlight = null;
- if (notice.getShowInEditor()) {
- highlight = h.addParserHighlight(notice,
- parserErrorHighlightPainter);
- }
- noticesToHighlights.put(notice, highlight);
- } catch (BadLocationException ble) { // Never happens
- ble.printStackTrace();
- }
- }
-
- }
-
- if (DEBUG_PARSING) {
- System.out.println("[DEBUG]: Done adding parser notices from " +
- res.getParser());
- }
-
- }
-
- /**
- * Called when the document is modified.
- *
- * @param e
- * The document event.
- */
- public void changedUpdate(DocumentEvent e) {
- }
-
- private void clearParserNoticeHighlights() {
- RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
- textArea.getHighlighter();
- if (h != null) {
- h.clearParserHighlights();
- }
- if (noticesToHighlights != null) {
- noticesToHighlights.clear();
- }
- }
-
- /**
- * Removes all parser notice highlights for a specific parser.
- *
- * @param parser
- * The parser whose highlights to remove.
- */
- private void clearParserNoticeHighlights(Parser parser) {
- RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
- textArea.getHighlighter();
- if (h != null) {
- h.clearParserHighlights(parser);
- }
- if (noticesToHighlights != null) {
- for (Iterator i = noticesToHighlights.entrySet().iterator(); i.hasNext();) {
- Map.Entry entry = (Map.Entry) i.next();
- ParserNotice notice = (ParserNotice) entry.getKey();
- if (notice.getParser() == parser) {
- i.remove();
- }
- }
- }
- }
-
- /**
- * Removes all parsers and any highlights they have created.
- *
- * @see #addParser(Parser)
- */
- public void clearParsers() {
- timer.stop();
- clearParserNoticeHighlights();
- parsers.clear();
- textArea.fireParserNoticesChange();
- }
-
- /**
- * Forces the given {@link Parser} to re-parse the content of this text area.
- * Parser
can be configured as to what notices it returns. For
- * example, if a Java language parser can be configured to set whether no serialVersionUID is a warning, error, or
- * ignored, this method can be called after changing the expected notice type to have the document re-parsed.
- *
- * @param parser
- * The index of the Parser
to re-run.
- * @see #getParser(int)
- */
- public void forceReparsing(int parser) {
- Parser p = getParser(parser);
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- String style = textArea.getSyntaxEditingStyle();
- doc.readLock();
- try {
- if (p.isEnabled()) {
- ParseResult res = p.parse(doc, style);
- addParserNoticeHighlights(res);
- }
- else {
- clearParserNoticeHighlights(p);
- }
- textArea.fireParserNoticesChange();
- } finally {
- doc.readUnlock();
- }
- }
-
- /**
- * Returns the delay between the last "concurrent" edit and when the document is re-parsed.
- *
- * @return The delay, in milliseconds.
- * @see #setDelay(int)
- */
- public int getDelay() {
- return timer.getDelay();
- }
-
- /**
- * Returns the specified parser.
- *
- * @param index
- * The index of the parser.
- * @return The parser.
- * @see #getParserCount()
- * @see #addParser(Parser)
- * @see #removeParser(Parser)
- */
- public Parser getParser(int index) {
- return (Parser) parsers.get(index);
- }
-
- /**
- * Returns the number of registered parsers.
- *
- * @return The number of registered parsers.
- */
- public int getParserCount() {
- return parsers.size();
- }
-
- /**
- * Returns a list of the current parser notices for this text area. This method (like most Swing methods) should
- * only be called on the EDT.
- *
- * @return The list of notices. This will be an empty list if there are none.
- */
- public List getParserNotices() {
- List notices = new ArrayList();
- if (noticesToHighlights != null) {
- for (Object o : noticesToHighlights.keySet()) {
- ParserNotice notice = (ParserNotice) o;
- notices.add(notice);
- }
- }
- return notices;
- }
-
- /**
- * Returns the tool tip to display for a mouse event at the given location. This method is overridden to give a
- * registered parser a chance to display a tool tip (such as an error description when the mouse is over an error
- * highlight).
- *
- * @param e
- * The mouse event.
- * @return The tool tip to display, and possibly a hyperlink event handler.
- */
- public ToolTipInfo getToolTipText(MouseEvent e) {
-
- String tip = null;
- HyperlinkListener listener = null;
- parserForTip = null;
-
- // try {
- int pos = textArea.viewToModel(e.getPoint());
- /*
- * Highlighter.Highlight[] highlights = textArea.getHighlighter(). getHighlights(); for (int i=0;
- * i0
.
- * @see #getDelay()
- */
- public void setDelay(int millis) {
- if (running) {
- timer.stop();
- }
- timer.setDelay(millis);
- if (running) {
- timer.start();
- }
- }
-
- /**
- * Returns whether a parser notice should be removed, based on a parse result.
- *
- * @param notice
- * The notice in question.
- * @param res
- * The result.
- * @return Whether the notice should be removed.
- */
- private final boolean shouldRemoveNotice(ParserNotice notice,
- ParseResult res) {
-
- if (DEBUG_PARSING) {
- System.out.println("[DEBUG]: ... ... shouldRemoveNotice " +
- notice + ": " + (notice.getParser() == res.getParser()));
- }
-
- // NOTE: We must currently remove all notices for the parser. Parser
- // implementors are required to parse the entire document each parsing
- // request, as RSTA is not yet sophisticated enough to determine the
- // minimum range of text to parse (and ParserNotices' locations aren't
- // updated when the Document is mutated, which would be a requirement
- // for this as well).
- // return same_parser && (in_reparsed_range || in_deleted_end_of_doc)
- return notice.getParser() == res.getParser();
-
- }
-
- /**
- * Stops parsing the document.
- *
- * @see #restartParsing()
- */
- public void stopParsing() {
- timer.stop();
- running = false;
- }
-
-}
\ No newline at end of file
+ HyperlinkListener, PropertyChangeListener {
+
+ private RSyntaxTextArea textArea;
+ private ListParserNotice
s that compare
+ * equally via equals()
. Real-world example: The Perl
+ * compiler will return 2+ identical error messages if the same error is
+ * committed in a single line more than once.
+ */
+ private Listtrue
, debug messages
+ * will be printed to stdout to help diagnose parsing issues.
+ */
+ private static final String PROPERTY_DEBUG_PARSING = "rsta.debugParsing";
+
+ /**
+ * Whether to print debug messages while running parsers.
+ */
+ private static final boolean DEBUG_PARSING;
+
+ /**
+ * The default delay between the last key press and when the document
+ * is parsed, in milliseconds.
+ */
+ private static final int DEFAULT_DELAY_MS = 1250;
+
+
+ /**
+ * Constructor.
+ *
+ * @param textArea The text area whose document the parser will be
+ * parsing.
+ */
+ ParserManager(RSyntaxTextArea textArea) {
+ this(DEFAULT_DELAY_MS, textArea);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param delay The delay between the last key press and when the document
+ * is parsed.
+ * @param textArea The text area whose document the parser will be
+ * parsing.
+ */
+ ParserManager(int delay, RSyntaxTextArea textArea) {
+ this.textArea = textArea;
+ textArea.getDocument().addDocumentListener(this);
+ textArea.addPropertyChangeListener("document", this);
+ parsers = new ArrayList<>(1); // Usually small
+ timer = new Timer(delay, this);
+ timer.setRepeats(false);
+ running = true;
+ }
+
+
+ /**
+ * Called when the timer fires (e.g. it's time to parse the document).
+ *
+ * @param e The event.
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ // Sanity check - should have >1 parser if event is fired.
+ int parserCount = getParserCount();
+ if (parserCount==0) {
+ return;
+ }
+
+ long begin = 0;
+ if (DEBUG_PARSING) {
+ begin = System.currentTimeMillis();
+ }
+
+ RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
+
+ Element root = doc.getDefaultRootElement();
+ int firstLine = firstOffsetModded==null ? 0 :
+ root.getElementIndex(firstOffsetModded.getOffset());
+ int lastLine = lastOffsetModded==null ? root.getElementCount()-1 :
+ root.getElementIndex(lastOffsetModded.getOffset());
+ firstOffsetModded = lastOffsetModded = null;
+ if (DEBUG_PARSING) {
+ System.out.println("[DEBUG]: Minimum lines to parse: " + firstLine + "-" + lastLine);
+ }
+
+ String style = textArea.getSyntaxEditingStyle();
+ doc.readLock();
+ try {
+ for (int i=0; iParser
can be configured
+ * as to what notices it returns. For example, if a Java language parser
+ * can be configured to set whether no serialVersionUID is a warning,
+ * error, or ignored, this method can be called after changing the expected
+ * notice type to have the document re-parsed.
+ *
+ * @param parser The index of the Parser
to re-run.
+ * @see #getParser(int)
+ */
+ public void forceReparsing(int parser) {
+ Parser p = getParser(parser);
+ RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
+ String style = textArea.getSyntaxEditingStyle();
+ doc.readLock();
+ try {
+ if (p.isEnabled()) {
+ ParseResult res = p.parse(doc, style);
+ addParserNoticeHighlights(res);
+ }
+ else {
+ clearParserNoticeHighlights(p);
+ }
+ textArea.fireParserNoticesChange();
+ } finally {
+ doc.readUnlock();
+ }
+ }
+
+
+ /**
+ * Returns the delay between the last "concurrent" edit and when the
+ * document is re-parsed.
+ *
+ * @return The delay, in milliseconds.
+ * @see #setDelay(int)
+ */
+ public int getDelay() {
+ return timer.getDelay();
+ }
+
+
+ /**
+ * Returns the specified parser.
+ *
+ * @param index The index of the parser.
+ * @return The parser.
+ * @see #getParserCount()
+ * @see #addParser(Parser)
+ * @see #removeParser(Parser)
+ */
+ public Parser getParser(int index) {
+ return parsers.get(index);
+ }
+
+
+ /**
+ * Returns the number of registered parsers.
+ *
+ * @return The number of registered parsers.
+ */
+ public int getParserCount() {
+ return parsers.size();
+ }
+
+
+ /**
+ * Returns a list of the current parser notices for this text area.
+ * This method (like most Swing methods) should only be called on the
+ * EDT.
+ *
+ * @return The list of notices. This will be an empty list if there are
+ * none.
+ */
+ public List0
.
+ * @see #getDelay()
+ */
+ public void setDelay(int millis) {
+ if (running) {
+ timer.stop();
+ }
+ timer.setInitialDelay(millis);
+ timer.setDelay(millis);
+ if (running) {
+ timer.start();
+ }
+ }
+
+
+ /**
+ * Returns whether a parser notice should be removed, based on a parse
+ * result.
+ *
+ * @param notice The notice in question.
+ * @param res The result.
+ * @return Whether the notice should be removed.
+ */
+ private boolean shouldRemoveNotice(ParserNotice notice,
+ ParseResult res) {
+
+ if (DEBUG_PARSING) {
+ System.out.println("[DEBUG]: ... ... shouldRemoveNotice " +
+ notice + ": " + (notice.getParser()==res.getParser()));
+ }
+
+ // NOTE: We must currently remove all notices for the parser. Parser
+ // implementors are required to parse the entire document each parsing
+ // request, as RSTA is not yet sophisticated enough to determine the
+ // minimum range of text to parse (and ParserNotices' locations aren't
+ // updated when the Document is mutated, which would be a requirement
+ // for this as well).
+ // return same_parser && (in_reparsed_range || in_deleted_end_of_doc)
+ return notice.getParser()==res.getParser();
+
+ }
+
+
+ /**
+ * Stops parsing the document.
+ *
+ * @see #restartParsing()
+ */
+ public void stopParsing() {
+ timer.stop();
+ running = false;
+ }
+
+
+ /**
+ * Mapping of a parser notice to its highlight in the editor.
+ */
+ private static class NoticeHighlightPair {
+
+ private ParserNotice notice;
+ private HighlightInfo highlight;
+
+ NoticeHighlightPair(ParserNotice notice, HighlightInfo highlight) {
+ this.notice = notice;
+ this.highlight = highlight;
+ }
+
+ }
+
+
+ static {
+ boolean debugParsing = false;
+ try {
+ debugParsing = Boolean.getBoolean(PROPERTY_DEBUG_PARSING);
+ } catch (AccessControlException ace) {
+ // Likely an applet's security manager.
+ debugParsing = false; // FindBugs
+ }
+ DEBUG_PARSING = debugParsing;
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/PopupWindowDecorator.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/PopupWindowDecorator.java
old mode 100644
new mode 100755
index 84adbf1e5..5a3874f22
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/PopupWindowDecorator.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/PopupWindowDecorator.java
@@ -3,70 +3,66 @@
*
* PopupWindowDecorator.java - Hook allowing hosting applications to decorate
* JWindows created by the AutoComplete library.
- * Copyright (C) 2011 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import javax.swing.JWindow;
+
/**
- * A hook allowing hosting applications to decorate JWindows created by the AutoComplete library. For example, you could
- * use the JGoodies library to add drop shadows to the windows.
- *
+ * A hook allowing hosting applications to decorate JWindows created by the
+ * AutoComplete library. For example, you could use the
+ * JGoodies library to add drop shadows
+ * to the windows.
+ *
* @author Robert Futrell
* @version 1.0
*/
public abstract class PopupWindowDecorator {
- /**
- * The singleton instance of this class.
- */
- private static PopupWindowDecorator decorator;
+ /**
+ * The singleton instance of this class.
+ */
+ private static PopupWindowDecorator decorator;
+
+
+ /**
+ * Callback called whenever an appropriate JWindow is created by the
+ * AutoComplete library. Implementations can decorate the window however
+ * they see fit.
+ *
+ * @param window The newly-created window.
+ */
+ public abstract void decorate(JWindow window);
+
+
+ /**
+ * Returns the singleton instance of this class. This should only be
+ * called on the EDT.
+ *
+ * @return The singleton instance of this class, or null
+ * for none.
+ * @see #set(PopupWindowDecorator)
+ */
+ public static PopupWindowDecorator get() {
+ return decorator;
+ }
- /**
- * Callback called whenever an appropriate JWindow is created by the AutoComplete library. Implementations can
- * decorate the window however they see fit.
- *
- * @param window
- * The newly-created window.
- */
- public abstract void decorate(JWindow window);
- /**
- * Returns the singleton instance of this class. This should only be called on the EDT.
- *
- * @return The singleton instance of this class, or null
for none.
- * @see #set(PopupWindowDecorator)
- */
- public static PopupWindowDecorator get() {
- return decorator;
- }
+ /**
+ * Sets the singleton instance of this class. This should only be called
+ * on the EDT.
+ *
+ * @param decorator The new instance of this class. This may be
+ * null
.
+ * @see #get()
+ */
+ public static void set(PopupWindowDecorator decorator) {
+ PopupWindowDecorator.decorator = decorator;
+ }
- /**
- * Sets the singleton instance of this class. This should only be called on the EDT.
- *
- * @param decorator
- * The new instance of this class. This may be null
.
- * @see #get()
- */
- public static void set(PopupWindowDecorator decorator) {
- PopupWindowDecorator.decorator = decorator;
- }
-}
\ No newline at end of file
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSTAView.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSTAView.java
old mode 100644
new mode 100755
index 00defb6c7..dda6c8c85
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSTAView.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSTAView.java
@@ -2,23 +2,9 @@
* 02/10/2009
*
* RSTAView.java - An RSyntaxTextArea
view.
- * Copyright (C) 2003 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -26,31 +12,51 @@
import javax.swing.text.BadLocationException;
+
/**
* Utility methods for RSyntaxTextArea's views.
- *
+ *
* @author Robert Futrell
* @version 1.0
*/
interface RSTAView {
- /**
- * Returns the y-coordinate of the line containing a specified offset.
- * modelToView(int)
calls, as the entire bounding box
- * isn't computed.
- *
- * @param alloc
- * The area the text area can render into.
- * @param offs
- * The offset info the document.
- * @return The y-coordinate of the top of the offset, or -1
if this text area doesn't yet have a
- * positive size.
- * @throws BadLocationException
- * If offs
isn't a valid offset into the document.
- */
- public int yForLineContaining(Rectangle alloc, int offs)
- throws BadLocationException;
-
-}
\ No newline at end of file
+
+ /**
+ * Returns the y-coordinate of the specified line.modelToView(int)
calls, as the entire bounding box isn't
+ * computed.
+ *
+ * @param alloc The area the text area can render into.
+ * @param line The line number.
+ * @return The y-coordinate of the top of the line, or -1
if
+ * this text area doesn't yet have a positive size or the line is
+ * hidden (i.e. from folding).
+ * @throws BadLocationException If line
isn't a valid line
+ * number for this document.
+ */
+ int yForLine(Rectangle alloc, int line) throws BadLocationException;
+
+
+ /**
+ * Returns the y-coordinate of the line containing a specified offset.modelToView(int)
calls, as the entire bounding box isn't
+ * computed.
+ *
+ * @param alloc The area the text area can render into.
+ * @param offs The offset info the document.
+ * @return The y-coordinate of the top of the offset, or -1
if
+ * this text area doesn't yet have a positive size or the line is
+ * hidden (i.e. from folding).
+ * @throws BadLocationException If offs
isn't a valid offset
+ * into the document.
+ */
+ int yForLineContaining(Rectangle alloc, int offs)
+ throws BadLocationException;
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxDocument.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxDocument.java
old mode 100644
new mode 100755
index 6293de0db..a84a49145
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxDocument.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxDocument.java
@@ -3,576 +3,679 @@
*
* RSyntaxDocument.java - A document capable of syntax highlighting, used by
* RSyntaxTextArea.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
+import java.util.Iterator;
+
import javax.swing.Action;
-import javax.swing.event.*;
-import javax.swing.text.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Element;
+import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.modes.AbstractMarkupTokenMaker;
+import org.fife.ui.rtextarea.RDocument;
import org.fife.util.DynamicIntArray;
+
/**
- * The document used by {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextArea}. This document is like
- * javax.swing.text.PlainDocument
except that it also keeps track of syntax highlighting in the document.
- * It has a "style" attribute associated with it that determines how syntax highlighting is done (i.e., what language is
- * being highlighted).
- * RSyntaxTextArea
will only accept instances of RSyntaxDocument
, since it is
- * this document that keeps track of syntax highlighting. All others will cause an exception to be thrown.
- * DocumentEvent
s of type
- * CHANGE
use their offset and length values to represent the first and last lines, respectively, that have
- * had their syntax coloring change. This is really a hack to increase the speed of the painting code and should really
- * be corrected, but oh well.
- *
+ * The document used by {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextArea}.
+ * This document is like javax.swing.text.PlainDocument
except that
+ * it also keeps track of syntax highlighting in the document. It has a "style"
+ * attribute associated with it that determines how syntax highlighting is done
+ * (i.e., what language is being highlighted).RSyntaxTextArea
will only accept instances of
+ * RSyntaxDocument
, since it is this document that keeps
+ * track of syntax highlighting. All others will cause an exception to be
+ * thrown.DocumentEvent
s of
+ * type CHANGE
use their offset and length values to represent the
+ * first and last lines, respectively, that have had their syntax coloring
+ * change. This is really a hack to increase the speed of the painting code
+ * and should really be corrected, but oh well.
+ *
* @author Robert Futrell
- * @version 0.1
+ * @version 1.0
*/
-public class RSyntaxDocument extends PlainDocument implements SyntaxConstants {
-
- /**
- * Creates a {@link TokenMaker} appropriate for a given programming language.
- */
- private transient TokenMakerFactory tokenMakerFactory;
-
- /**
- * Splits text into tokens for the current programming language.
- */
- private TokenMaker tokenMaker;
-
- /**
- * Array of values representing the "last token type" on each line. This is used in cases such as multiline
- * comments: if the previous line ended with an (unclosed) multiline comment, we can use this knowledge and start
- * the current line's syntax highlighting in multiline comment state.
- */
- protected DynamicIntArray lastTokensOnLines;
-
- private transient Segment s;
-
- /**
- * Constructs a plain text document. A default root element is created, and the tab size set to 5.
- *
- * @param syntaxStyle
- * The syntax highlighting scheme to use.
- */
- public RSyntaxDocument(String syntaxStyle) {
- this(null, syntaxStyle);
- }
-
- /**
- * Constructs a plain text document. A default root element is created, and the tab size set to 5.
- *
- * @param tmf
- * The TokenMakerFactory
for this document. If this is null
, a default factory
- * is used.
- * @param syntaxStyle
- * The syntax highlighting scheme to use.
- */
- public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) {
- super(new RGapContent());
- putProperty(tabSizeAttribute, new Integer(5));
- lastTokensOnLines = new DynamicIntArray(400);
- lastTokensOnLines.add(Token.NULL); // Initial (empty) line.
- s = new Segment();
- setTokenMakerFactory(tmf);
- setSyntaxStyle(syntaxStyle);
- }
-
- /**
- * Returns the character in the document at the specified offset.
- *
- * @param offset
- * The offset of the character.
- * @return The character.
- * @throws BadLocationException
- * If the offset is invalid.
- */
- public char charAt(int offset) throws BadLocationException {
- return ((RGapContent) getContent()).charAt(offset);
- }
-
- /**
- * Alerts all listeners to this document of an insertion. This is overridden so we can update our syntax
- * highlighting stuff.
- * insertUpdate
because
- * insertUpdate
is not called by the undo/redo actions, but this method is.
- *
- * @param e
- * The change.
- */
- protected void fireInsertUpdate(DocumentEvent e) {
-
- /*
- * Now that the text is actually inserted into the content and element structure, we can update our token
- * elements and "last tokens on lines" structure.
- */
-
- Element lineMap = getDefaultRootElement();
- DocumentEvent.ElementChange change = e.getChange(lineMap);
- Element[] added = change == null ? null : change.getChildrenAdded();
-
- int numLines = lineMap.getElementCount();
- int line = lineMap.getElementIndex(e.getOffset());
- int previousLine = line - 1;
- int previousTokenType = (previousLine > -1 ?
- lastTokensOnLines.get(previousLine) : Token.NULL);
-
- // If entire lines were added...
- if (added != null && added.length > 0) {
-
- Element[] removed = change.getChildrenRemoved();
- int numRemoved = removed != null ? removed.length : 0;
-
- int endBefore = line + added.length - numRemoved;
- // System.err.println("... adding lines: " + line + " - " + (endBefore-1));
- // System.err.println("... ... added: " + added.length + ", removed:" + numRemoved);
- for (int i = line; i < endBefore; i++) {
-
- setSharedSegment(i); // Loads line i's text into s.
-
- int tokenType = tokenMaker.getLastTokenTypeOnLine(s, previousTokenType);
- lastTokensOnLines.add(i, tokenType);
- // System.err.println("--------- lastTokensOnLines.size() == " + lastTokensOnLines.getSize());
-
- previousTokenType = tokenType;
-
- } // End of for (int i=line; ipostRemoveUpdate
)
- * as this method is called in response to undo/redo events, whereas postRemoveUpdate
is not.
- * true
- * if {@link #getLanguageIsMarkup()} also returns true
.
- *
- * @return Whether markup closing tags should be automatically completed.
- * @see #getLanguageIsMarkup()
- */
- public boolean getCompleteMarkupCloseTags() {
- // TODO: Remove terrible dependency on AbstractMarkupTokenMaker
- return getLanguageIsMarkup() &&
- ((AbstractMarkupTokenMaker) tokenMaker).getCompleteCloseTags();
- }
-
- /**
- * Returns whether the current programming language uses curly braces ('{' and '}') to denote code
- * blocks.
- *
- * @return Whether curly braces denote code blocks.
- */
- public boolean getCurlyBracesDenoteCodeBlocks() {
- return tokenMaker.getCurlyBracesDenoteCodeBlocks();
- }
-
- /**
- * Returns whether the current language is a markup language, such as HTML, XML or PHP.
- *
- * @return Whether the current language is a markup language.
- */
- public boolean getLanguageIsMarkup() {
- return tokenMaker.isMarkupLanguage();
- }
-
- /**
- * Returns the token type of the last token on the given line.
- *
- * @param line
- * The line to inspect.
- * @return The token type of the last token on the specified line. If the line is invalid, an exception is thrown.
- */
- public int getLastTokenTypeOnLine(int line) {
- return lastTokensOnLines.get(line);
- }
-
- /**
- * Returns the text to place at the beginning and end of a line to "comment" it in the current programming language.
- *
- * @return The start and end strings to add to a line to "comment" it out. A null
value for either
- * means there is no string to add for that part. A value of null
for the array means this
- * language does not support commenting/uncommenting lines.
- */
- public String[] getLineCommentStartAndEnd() {
- return tokenMaker.getLineCommentStartAndEnd();
- }
-
- /**
- * Returns whether tokens of the specified type should have "mark occurrences" enabled for the current programming
- * language.
- *
- * @param type
- * The token type.
- * @return Whether tokens of this type should have "mark occurrences" enabled.
- */
- boolean getMarkOccurrencesOfTokenType(int type) {
- return tokenMaker.getMarkOccurrencesOfTokenType(type);
- }
-
- /**
- * This method returns whether auto indentation should be done if Enter is pressed at the end of the specified line.
- *
- * @param line
- * The line to check.
- * @return Whether an extra indentation should be done.
- */
- public boolean getShouldIndentNextLine(int line) {
- Token t = getTokenListForLine(line);
- t = t.getLastNonCommentNonWhitespaceToken();
- return tokenMaker.getShouldIndentNextLineAfter(t);
- }
-
- /**
- * Returns a token list for the specified segment of text representing the specified line number. This method is
- * basically a wrapper for tokenMaker.getTokenList
that takes into account the last token on the
- * previous line to assure token accuracy.
- *
- * @param line
- * The line number of text
in the document, >= 0.
- * @return A token list representing the specified line.
- */
- public final Token getTokenListForLine(int line) {
- Element map = getDefaultRootElement();
- Element elem = map.getElement(line);
- int startOffset = elem.getStartOffset();
- // int endOffset = (line==map.getElementCount()-1 ? elem.getEndOffset() - 1:
- // elem.getEndOffset() - 1);
- int endOffset = elem.getEndOffset() - 1; // Why always "-1"?
- try {
- getText(startOffset, endOffset - startOffset, s);
- } catch (BadLocationException ble) {
- ble.printStackTrace();
- return null;
- }
- int initialTokenType = line == 0 ? Token.NULL :
- getLastTokenTypeOnLine(line - 1);
- return tokenMaker.getTokenList(s, initialTokenType, startOffset);
- }
-
- boolean insertBreakSpecialHandling(ActionEvent e) {
- Action a = tokenMaker.getInsertBreakAction();
- if (a != null) {
- a.actionPerformed(e);
- return true;
- }
- return false;
- }
-
- /**
- * Deserializes a document.
- *
- * @param s
- * The stream to read from.
- * @throws ClassNotFoundException
- * @throws IOException
- */
- private void readObject(ObjectInputStream s)
- throws ClassNotFoundException, IOException {
- s.defaultReadObject();
- this.s = new Segment();
- }
-
- /**
- * Makes our private Segment s
point to the text in our document referenced by the specified element.
- * Note that line
MUST be a valid line number in the document.
- *
- * @param line
- * The line number you want to get.
- */
- private final void setSharedSegment(int line) {
-
- Element map = getDefaultRootElement();
- // int numLines = map.getElementCount();
-
- Element element = map.getElement(line);
- if (element == null)
- throw new InternalError("Invalid line number: " + line);
- int startOffset = element.getStartOffset();
- // int endOffset = (line==numLines-1 ?
- // element.getEndOffset()-1 : element.getEndOffset() - 1);
- int endOffset = element.getEndOffset() - 1; // Why always "-1"?
- try {
- getText(startOffset, endOffset - startOffset, s);
- } catch (BadLocationException ble) {
- throw new InternalError("Text range not in document: " +
- startOffset + "-" + endOffset);
- }
-
- }
-
- /**
- * Sets the syntax style being used for syntax highlighting in this document. What styles are supported by a
- * document is determined by its {@link TokenMakerFactory}. By default, all RSyntaxDocument
s support
- * all languages built into RSyntaxTextArea
.
- *
- * @param styleKey
- * The new style to use, such as {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is not known or
- * supported by this document, then {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
- */
- public void setSyntaxStyle(String styleKey) {
- tokenMaker = tokenMakerFactory.getTokenMaker(styleKey);
- updateSyntaxHighlightingInformation();
- }
-
- /**
- * Sets the syntax style being used for syntax highlighting in this document. You should call this method if you've
- * created a custom token maker for a language not normally supported by RSyntaxTextArea
.
- *
- * @param tokenMaker
- * The new token maker to use.
- */
- public void setSyntaxStyle(TokenMaker tokenMaker) {
- this.tokenMaker = tokenMaker;
- updateSyntaxHighlightingInformation();
- }
-
- /**
- * Sets the token maker factory used by this document.
- *
- * @param tmf
- * The TokenMakerFactory
for this document. If this is null
, a default factory
- * is used.
- */
- public void setTokenMakerFactory(TokenMakerFactory tmf) {
- tokenMakerFactory = tmf != null ? tmf :
- TokenMakerFactory.getDefaultInstance();
- }
-
- /**
- * Sets whether whitespace is visible. This property is actually setting whether the tokens generated from this
- * document "paint" something when they represent whitespace.
- *
- * @param visible
- * Whether whitespace should be visible.
- */
- public void setWhitespaceVisible(boolean visible, RSyntaxTextArea textArea) {
- tokenMaker.setWhitespaceVisible(visible, textArea);
- }
-
- /**
- * Loops through the last-tokens-on-lines array from a specified point onward, updating last-token values until they
- * stop changing. This should be called when lines are updated/inserted/removed, as doing so may cause lines below
- * to change color.
- *
- * @param line
- * The first line to check for a change in last-token value.
- * @param numLines
- * The number of lines in the document.
- * @param previousTokenType
- * The last-token value of the line just before line
.
- * @return The last line that needs repainting.
- */
- private int updateLastTokensBelow(int line, int numLines, int previousTokenType) {
-
- int firstLine = line;
-
- // Loop through all lines past our starting point. Update even the last
- // line's info, even though there aren't any lines after it that depend
- // on it changing for them to be changed, as its state may be used
- // elsewhere in the library.
- int end = numLines;
- // System.err.println("--- end==" + end + " (numLines==" + numLines + ")");
- while (line < end) {
-
- setSharedSegment(line); // Sets s's text to that of line 'line' in the document.
-
- int oldTokenType = lastTokensOnLines.get(line);
- int newTokenType = tokenMaker.getLastTokenTypeOnLine(s, previousTokenType);
- // System.err.println("---------------- line " + line + "; oldTokenType==" + oldTokenType +
- // ", newTokenType==" + newTokenType + ", s=='" + s + "'");
-
- // If this line's end-token value didn't change, stop here. Note
- // that we're saying this line needs repainting; this is because
- // the beginning of this line did indeed change color, but the
- // end didn't.
- if (oldTokenType == newTokenType) {
- // System.err.println("... ... ... repainting lines " + firstLine + "-" + line);
- fireChangedUpdate(new DefaultDocumentEvent(firstLine, line, DocumentEvent.EventType.CHANGE));
- return line;
- }
-
- // If the line's end-token value did change, update it and
- // keep going.
- // NOTE: "setUnsafe" is okay here as the bounds checking was
- // already done in lastTokensOnLines.get(line) above.
- lastTokensOnLines.setUnsafe(line, newTokenType);
- previousTokenType = newTokenType;
- line++;
-
- } // End of while (linetrue
, debug information about how much
+ * token caching is helping is printed to stdout.
+ */
+ private static final boolean DEBUG_TOKEN_CACHING = false;
+
+
+ /**
+ * Constructs a plain text document. A default root element is created,
+ * and the tab size set to 5.
+ *
+ * @param syntaxStyle The syntax highlighting scheme to use.
+ */
+ public RSyntaxDocument(String syntaxStyle) {
+ this(null, syntaxStyle);
+ }
+
+
+ /**
+ * Constructs a plain text document. A default root element is created,
+ * and the tab size set to 5.
+ *
+ * @param tmf The TokenMakerFactory
for this document. If
+ * this is null
, a default factory is used.
+ * @param syntaxStyle The syntax highlighting scheme to use.
+ */
+ public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) {
+ putProperty(tabSizeAttribute, 5);
+ lastTokensOnLines = new DynamicIntArray(400);
+ lastTokensOnLines.add(Token.NULL); // Initial (empty) line.
+ s = new Segment();
+ setTokenMakerFactory(tmf);
+ setSyntaxStyle(syntaxStyle);
+ }
+
+
+ /**
+ * Alerts all listeners to this document of an insertion. This is
+ * overridden so we can update our syntax highlighting stuff.insertUpdate
because insertUpdate
is not
+ * called by the undo/redo actions, but this method is.
+ *
+ * @param e The change.
+ */
+ @Override
+ protected void fireInsertUpdate(DocumentEvent e) {
+
+ cachedTokenList = null;
+
+ /*
+ * Now that the text is actually inserted into the content and
+ * element structure, we can update our token elements and "last
+ * tokens on lines" structure.
+ */
+
+ Element lineMap = getDefaultRootElement();
+ DocumentEvent.ElementChange change = e.getChange(lineMap);
+ Element[] added = change==null ? null : change.getChildrenAdded();
+
+ int numLines = lineMap.getElementCount();
+ int line = lineMap.getElementIndex(e.getOffset());
+ int previousLine = line - 1;
+ int previousTokenType = (previousLine>-1 ?
+ lastTokensOnLines.get(previousLine) : Token.NULL);
+
+ // If entire lines were added...
+ if (added!=null && added.length>0) {
+
+ Element[] removed = change.getChildrenRemoved();
+ int numRemoved = removed!=null ? removed.length : 0;
+
+ int endBefore = line + added.length - numRemoved;
+ //System.err.println("... adding lines: " + line + " - " + (endBefore-1));
+ //System.err.println("... ... added: " + added.length + ", removed:" + numRemoved);
+ for (int i=line; ipostRemoveUpdate
) as this method is called
+ * in response to undo/redo events, whereas postRemoveUpdate
+ * is not.< 0
).
+ *
+ * @param type The token type.
+ * @return The closest "standard" token type. If a mapping is not defined
+ * for this language, then type
is returned.
+ */
+ public int getClosestStandardTokenTypeForInternalType(int type) {
+ return tokenMaker.getClosestStandardTokenTypeForInternalType(type);
+ }
+
+
+ /**
+ * Returns whether closing markup tags should be automatically completed.
+ * This method only returns true
if
+ * {@link #getLanguageIsMarkup()} also returns true
.
+ *
+ * @return Whether markup closing tags should be automatically completed.
+ * @see #getLanguageIsMarkup()
+ */
+ public boolean getCompleteMarkupCloseTags() {
+ // TODO: Remove terrible dependency on AbstractMarkupTokenMaker
+ return getLanguageIsMarkup() &&
+ ((AbstractMarkupTokenMaker)tokenMaker).getCompleteCloseTags();
+ }
+
+
+ /**
+ * Returns whether the current programming language uses curly braces
+ * ('{
' and '}
') to denote code blocks.
+ *
+ * @param languageIndex The language index at the offset in question.
+ * Since some TokenMaker
s effectively have nested
+ * languages (such as JavaScript in HTML), this parameter tells the
+ * TokenMaker
what sub-language to look at.
+ * @return Whether curly braces denote code blocks.
+ */
+ public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
+ return tokenMaker.getCurlyBracesDenoteCodeBlocks(languageIndex);
+ }
+
+
+ /**
+ * Returns whether the current language is a markup language, such as
+ * HTML, XML or PHP.
+ *
+ * @return Whether the current language is a markup language.
+ */
+ public boolean getLanguageIsMarkup() {
+ return tokenMaker.isMarkupLanguage();
+ }
+
+
+ /**
+ * Returns the token type of the last token on the given line.
+ *
+ * @param line The line to inspect.
+ * @return The token type of the last token on the specified line. If
+ * the line is invalid, an exception is thrown.
+ */
+ public int getLastTokenTypeOnLine(int line) {
+ return lastTokensOnLines.get(line);
+ }
+
+
+ /**
+ * Returns the text to place at the beginning and end of a
+ * line to "comment" it in this programming language.
+ *
+ * @return The start and end strings to add to a line to "comment"
+ * it out. A null
value for either means there
+ * is no string to add for that part. A value of
+ * null
for the array means this language
+ * does not support commenting/uncommenting lines.
+ */
+ public String[] getLineCommentStartAndEnd(int languageIndex) {
+ return tokenMaker.getLineCommentStartAndEnd(languageIndex);
+ }
+
+
+ /**
+ * Returns whether tokens of the specified type should have "mark
+ * occurrences" enabled for the current programming language.
+ *
+ * @param type The token type.
+ * @return Whether tokens of this type should have "mark occurrences"
+ * enabled.
+ */
+ boolean getMarkOccurrencesOfTokenType(int type) {
+ return tokenMaker.getMarkOccurrencesOfTokenType(type);
+ }
+
+
+ /**
+ * Returns the occurrence marker for the current language.
+ *
+ * @return The occurrence marker.
+ */
+ OccurrenceMarker getOccurrenceMarker() {
+ return tokenMaker.getOccurrenceMarker();
+ }
+
+
+ /**
+ * This method returns whether auto indentation should be done if Enter
+ * is pressed at the end of the specified line.
+ *
+ * @param line The line to check.
+ * @return Whether an extra indentation should be done.
+ */
+ public boolean getShouldIndentNextLine(int line) {
+ Token t = getTokenListForLine(line);
+ t = t.getLastNonCommentNonWhitespaceToken();
+ return tokenMaker.getShouldIndentNextLineAfter(t);
+ }
+
+
+ /**
+ * Returns the syntax style being used.
+ *
+ * @return The syntax style.
+ * @see #setSyntaxStyle(String)
+ */
+ public String getSyntaxStyle() {
+ return syntaxStyle;
+ }
+
+
+ /**
+ * Returns a token list for the specified segment of text representing
+ * the specified line number. This method is basically a wrapper for
+ * tokenMaker.getTokenList
that takes into account the last
+ * token on the previous line to assure token accuracy.
+ *
+ * @param line The line number of text
in the document,
+ * >= 0.
+ * @return A token list representing the specified line.
+ */
+ public final Token getTokenListForLine(int line) {
+
+ tokenRetrievalCount++;
+ if (line==lastLine && cachedTokenList!=null) {
+ if (DEBUG_TOKEN_CACHING) {
+ useCacheCount++;
+ System.err.println("--- Using cached line; ratio now: " +
+ useCacheCount + "/" + tokenRetrievalCount);
+ }
+ return cachedTokenList;
+ }
+ lastLine = line;
+
+ Element map = getDefaultRootElement();
+ Element elem = map.getElement(line);
+ int startOffset = elem.getStartOffset();
+ //int endOffset = (line==map.getElementCount()-1 ? elem.getEndOffset() - 1:
+ // elem.getEndOffset() - 1);
+ int endOffset = elem.getEndOffset() - 1; // Why always "-1"?
+ try {
+ getText(startOffset,endOffset-startOffset, s);
+ } catch (BadLocationException ble) {
+ ble.printStackTrace();
+ return null;
+ }
+ int initialTokenType = line==0 ? Token.NULL :
+ getLastTokenTypeOnLine(line-1);
+
+ //return tokenMaker.getTokenList(s, initialTokenType, startOffset);
+ cachedTokenList = tokenMaker.getTokenList(s, initialTokenType, startOffset);
+ return cachedTokenList;
+
+ }
+
+
+ boolean insertBreakSpecialHandling(ActionEvent e) {
+ Action a = tokenMaker.getInsertBreakAction();
+ if (a!=null) {
+ a.actionPerformed(e);
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns whether a character could be part of an "identifier" token
+ * in a specific language. This is used to identify such things as the
+ * bounds of the "word" to select on double-clicking.
+ *
+ * @param languageIndex The language index the character was found in.
+ * @param ch The character.
+ * @return Whether the character could be part of an "identifier" token.
+ */
+ public boolean isIdentifierChar(int languageIndex, char ch) {
+ return tokenMaker.isIdentifierChar(languageIndex, ch);
+ }
+
+
+ /**
+ * Returns an iterator over the paintable tokens in this document. Results
+ * are undefined if this document is modified while the iterator is being
+ * iterated through, so this should only be used on the EDT.remove()
method of the returned iterator will throw
+ * an UnsupportedOperationException
.
+ *
+ * @return An iterator.
+ */
+ @Override
+ public IteratorSegment s
point to the text in our
+ * document referenced by the specified element. Note that
+ * line
MUST be a valid line number in the document.
+ *
+ * @param line The line number you want to get.
+ */
+ private void setSharedSegment(int line) {
+
+ Element map = getDefaultRootElement();
+ //int numLines = map.getElementCount();
+
+ Element element = map.getElement(line);
+ if (element==null) {
+ throw new InternalError("Invalid line number: " + line);
+ }
+ int startOffset = element.getStartOffset();
+ //int endOffset = (line==numLines-1 ?
+ // element.getEndOffset()-1 : element.getEndOffset() - 1);
+ int endOffset = element.getEndOffset()-1; // Why always "-1"?
+ try {
+ getText(startOffset, endOffset-startOffset, s);
+ } catch (BadLocationException ble) {
+ throw new InternalError("Text range not in document: " +
+ startOffset + "-" + endOffset);
+ }
+
+ }
+
+
+ /**
+ * Sets the syntax style being used for syntax highlighting in this
+ * document. What styles are supported by a document is determined by its
+ * {@link TokenMakerFactory}. By default, all RSyntaxDocument
s
+ * support all languages built into RSyntaxTextArea
.
+ *
+ * @param styleKey The new style to use, such as
+ * {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is not
+ * known or supported by this document, then
+ * {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
+ * @see #setSyntaxStyle(TokenMaker)
+ * @see #getSyntaxStyle()
+ */
+ public void setSyntaxStyle(String styleKey) {
+ tokenMaker = tokenMakerFactory.getTokenMaker(styleKey);
+ updateSyntaxHighlightingInformation();
+ this.syntaxStyle = styleKey;
+ }
+
+
+ /**
+ * Sets the syntax style being used for syntax highlighting in this
+ * document. You should call this method if you've created a custom token
+ * maker for a language not normally supported by
+ * RSyntaxTextArea
.
+ *
+ * @param tokenMaker The new token maker to use.
+ * @see #setSyntaxStyle(String)
+ */
+ public void setSyntaxStyle(TokenMaker tokenMaker) {
+ this.tokenMaker = tokenMaker;
+ updateSyntaxHighlightingInformation();
+ this.syntaxStyle = "text/unknown"; // TODO: Make me public?
+ }
+
+
+ /**
+ * Sets the token maker factory used by this document.
+ *
+ * @param tmf The TokenMakerFactory
for this document. If
+ * this is null
, a default factory is used.
+ */
+ public void setTokenMakerFactory(TokenMakerFactory tmf) {
+ tokenMakerFactory = tmf!=null ? tmf :
+ TokenMakerFactory.getDefaultInstance();
+ }
+
+
+ /**
+ * Loops through the last-tokens-on-lines array from a specified point
+ * onward, updating last-token values until they stop changing. This
+ * should be called when lines are updated/inserted/removed, as doing
+ * so may cause lines below to change color.
+ *
+ * @param line The first line to check for a change in last-token value.
+ * @param numLines The number of lines in the document.
+ * @param previousTokenType The last-token value of the line just before
+ * line
.
+ * @return The last line that needs repainting.
+ */
+ private int updateLastTokensBelow(int line, int numLines, int previousTokenType) {
+
+ int firstLine = line;
+
+ // Loop through all lines past our starting point. Update even the last
+ // line's info, even though there aren't any lines after it that depend
+ // on it changing for them to be changed, as its state may be used
+ // elsewhere in the library.
+ int end = numLines;
+ //System.err.println("--- end==" + end + " (numLines==" + numLines + ")");
+ while (line
- *
- *
+ * An extension of RTextArea
that adds syntax highlighting
+ * of certain programming languages to its list of features. Languages
+ * currently supported include:
+ *
+ *
+ *
+ *
* Other added features include:
- *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
- *
+ *
- *
- * It is recommended that you use an instance of {@link org.fife.ui.rtextarea.RTextScrollPane} instead of a regular
- * JScrollPane
as this class allows you to add line numbers and bookmarks easily to your text area.
- *
+ *
+ * It is recommended that you use an instance of
+ * {@link org.fife.ui.rtextarea.RTextScrollPane} instead of a regular
+ * JScrollPane
as this class allows you to add line numbers and
+ * bookmarks easily to your text area.
+ *
* @author Robert Futrell
- * @version 1.3
+ * @version 3.0.0
+ * @see TextEditorPane
*/
public class RSyntaxTextArea extends RTextArea implements SyntaxConstants {
- public static final String ANIMATE_BRACKET_MATCHING_PROPERTY = "RSTA.animateBracketMatching";
- public static final String ANTIALIAS_PROPERTY = "RSTA.antiAlias";
- public static final String AUTO_INDENT_PROPERTY = "RSTA.autoIndent";
- public static final String BRACKET_MATCHING_PROPERTY = "RSTA.bracketMatching";
- public static final String CLEAR_WHITESPACE_LINES_PROPERTY = "RSTA.clearWhitespaceLines";
- public static final String CLOSE_CURLY_BRACES_PROPERTY = "RSTA.closeCurlyBraces";
- public static final String CLOSE_MARKUP_TAGS_PROPERTY = "RSTA.closeMarkupTags";
- public static final String EOL_VISIBLE_PROPERTY = "RSTA.eolMarkersVisible";
- public static final String FOCUSABLE_TIPS_PROPERTY = "RSTA.focusableTips";
- public static final String FRACTIONAL_FONTMETRICS_PROPERTY = "RSTA.fractionalFontMetrics";
- public static final String HYPERLINKS_ENABLED_PROPERTY = "RSTA.hyperlinksEnabled";
- public static final String MARK_OCCURRENCES_PROPERTY = "RSTA.markOccurrences";
- public static final String MARKED_OCCURRENCES_CHANGED_PROPERTY = "RSTA.markedOccurrencesChanged";
- public static final String PARSER_NOTICES_PROPERTY = "RSTA.parserNotices";
- public static final String SYNTAX_SCHEME_PROPERTY = "RSTA.syntaxScheme";
- public static final String SYNTAX_STYLE_PROPERTY = "RSTA.syntaxStyle";
- public static final String VISIBLE_WHITESPACE_PROPERTY = "RSTA.visibleWhitespace";
-
- private static final Color DEFAULT_BRACKET_MATCH_BG_COLOR = new Color(234, 234, 255);
- private static final Color DEFAULT_BRACKET_MATCH_BORDER_COLOR = new Color(0, 0, 128);
- private static final Color DEFAULT_SELECTION_COLOR = new Color(200, 200, 255);
-
- /**
- * The key for the syntax style to be highlighting.
- */
- private String syntaxStyleKey;
-
- /**
- * The colors used for syntax highlighting.
- */
- private SyntaxScheme syntaxScheme;
-
- /**
- * Handles code templates.
- */
- private static CodeTemplateManager codeTemplateManager;
-
- /**
- * Whether or not templates are enabled.
- */
- private static boolean templatesEnabled;
-
- /**
- * The rectangle surrounding the "matched bracket" if bracket matching is enabled.
- */
- Rectangle match;
-
- /**
- * Colors used for the "matched bracket" if bracket matching is enabled.
- */
- private Color matchedBracketBGColor;
- private Color matchedBracketBorderColor;
-
- /**
- * The location of the last matched bracket.
- */
- private int lastBracketMatchPos;
-
- /**
- * Whether or not bracket matching is enabled.
- */
- private boolean bracketMatchingEnabled;
-
- /**
- * Whether or not bracket matching is animated.
- */
- private boolean animateBracketMatching;
-
- private BracketMatchingTimer bracketRepaintTimer;
-
- /**
- * Whether or not auto-indent is on.
- */
- private boolean autoIndentEnabled;
-
- /**
- * Whether curly braces should be closed on Enter key presses, (if the current language supports it).
- */
- private boolean closeCurlyBraces;
-
- /**
- * Whether closing markup tags should be automatically completed when "</
" is typed (if the current
- * language is a markup language).
- */
- private boolean closeMarkupTags;
-
- /**
- * Whether or not lines with nothing but whitespace are "made empty."
- */
- private boolean clearWhitespaceLines;
-
- /**
- * Whether we are displaying visible whitespace (spaces and tabs).
- */
- private boolean whitespaceVisible;
-
- /**
- * Whether EOL markers should be visible at the end of each line.
- */
- private boolean eolMarkersVisible;
-
- /**
- * Whether hyperlinks are enabled (must be supported by the syntax scheme being used).
- */
- private boolean hyperlinksEnabled;
-
- /**
- * The color to use when painting hyperlinks.
- */
- private Color hyperlinkFG;
-
- /**
- * Mask used to determine if the correct key is being held down to scan for hyperlinks (ctrl, meta, etc.).
- */
- private int linkScanningMask;
-
- /**
- * Used during "Copy as RTF" operations.
- */
- private RtfGenerator rtfGenerator;
-
- /**
- * Handles "mark occurrences" support.
- */
- private MarkOccurrencesSupport markOccurrencesSupport;
-
- /**
- * The color used to render "marked occurrences."
- */
- private Color markOccurrencesColor;
-
- /**
- * Metrics of the text area's font.
- */
- private FontMetrics defaultFontMetrics;
-
- /**
- * Manages running the parser.
- */
- private ParserManager parserManager;
-
- /**
- * Whether the editor is currently scanning for hyperlinks on mouse movement.
- */
- private boolean isScanningForLinks;
-
- private int hoveredOverLinkOffset;
-
- /**
- * Whether "focusable" tool tips are used instead of standard ones.
- */
- private boolean useFocusableTips;
-
- /**
- * The last focusable tip displayed.
- */
- private FocusableTip focusableTip;
-
- private int lineHeight; // Height of a line of text; same for default, bold & italic.
- private int maxAscent;
-
- public int getMaxAscent() {
- return maxAscent;
- }
-
- private String aaHintFieldName;
- private Object aaHint;
- private boolean fractionalFontMetricsEnabled;
-
- /**
- * Constructor.
- */
- public RSyntaxTextArea() {
- init();
- }
-
- /**
- * Constructor.
- *
- * @param doc
- * The document for the editor.
- */
- public RSyntaxTextArea(RSyntaxDocument doc) {
- super(doc);
- init();
- }
-
- /**
- * Constructor.
- *
- * @param text
- * The initial text to display.
- */
- public RSyntaxTextArea(String text) {
- super(text);
- init();
- }
-
- /**
- * Constructor.
- *
- * @param rows
- * The number of rows to display.
- * @param cols
- * The number of columns to display.
- * @throws IllegalArgumentException
- * If either rows
or cols
is negative.
- */
- public RSyntaxTextArea(int rows, int cols) {
- super(rows, cols);
- init();
- }
-
- /**
- * Constructor.
- *
- * @param text
- * The initial text to display.
- * @param rows
- * The number of rows to display.
- * @param cols
- * The number of columns to display.
- * @throws IllegalArgumentException
- * If either rows
or cols
is negative.
- */
- public RSyntaxTextArea(String text, int rows, int cols) {
- super(text, rows, cols);
- init();
- }
-
- /**
- * Constructor.
- *
- * @param doc
- * The document for the editor.
- * @param text
- * The initial text to display.
- * @param rows
- * The number of rows to display.
- * @param cols
- * The number of columns to display.
- * @throws IllegalArgumentException
- * If either rows
or cols
is negative.
- */
- public RSyntaxTextArea(RSyntaxDocument doc, String text, int rows, int cols) {
- super(doc, text, rows, cols);
- init();
- }
-
- /**
- * Creates a new RSyntaxTextArea
.
- *
- * @param textMode
- * Either INSERT_MODE
or OVERWRITE_MODE
.
- */
- public RSyntaxTextArea(int textMode) {
- super(textMode);
- init();
- }
-
- /**
- * Adds an "active line range" listener to this text area.
- *
- * @param l
- * The listener to add.
- * @see #removeActiveLineRangeListener(ActiveLineRangeListener)
- */
- public void addActiveLineRangeListener(ActiveLineRangeListener l) {
- listenerList.add(ActiveLineRangeListener.class, l);
- }
-
- /**
- * Adds a hyperlink listener to this text area.
- *
- * @param l
- * The listener to add.
- * @see #removeHyperlinkListener(HyperlinkListener)
- */
- public void addHyperlinkListener(HyperlinkListener l) {
- listenerList.add(HyperlinkListener.class, l);
- }
-
- /**
- * Updates the font metrics the first time we're displayed.
- */
- public void addNotify() {
-
- super.addNotify();
-
- // We know we've just been connected to a screen resource (by
- // definition), so initialize our font metrics objects.
- refreshFontMetrics(getGraphics2D(getGraphics()));
-
- // Re-start parsing if we were removed from one container and added
- // to another
- if (parserManager != null) {
- parserManager.restartParsing();
- }
-
- }
-
- /**
- * Adds the parser to "validate" the source code in this text area. This can be anything from a spell checker to a
- * "compiler" that verifies source code.
- *
- * @param parser
- * The new parser. A value of null
will do nothing.
- * @see #getParser(int)
- * @see #getParserCount()
- * @see #removeParser(Parser)
- */
- public void addParser(Parser parser) {
- if (parserManager == null) {
- parserManager = new ParserManager(this);
- }
- parserManager.addParser(parser);
- }
-
- /**
- * Recalculates the height of a line in this text area and the maximum ascent of all fonts displayed.
- */
- private void calculateLineHeight() {
-
- lineHeight = maxAscent = 0;
-
- // Each token style.
- for (int i = 0; i < syntaxScheme.styles.length; i++) {
- Style ss = syntaxScheme.styles[i];
- if (ss != null && ss.font != null) {
- FontMetrics fm = getFontMetrics(ss.font);
- int height = fm.getHeight();
- if (height > lineHeight)
- lineHeight = height;
- int ascent = fm.getMaxAscent();
- if (ascent > maxAscent)
- maxAscent = ascent;
- }
- }
-
- // The text area's (default) font).
- Font temp = getFont();
- FontMetrics fm = getFontMetrics(temp);
- int height = fm.getHeight();
- if (height > lineHeight) {
- lineHeight = height;
- }
- int ascent = fm.getMaxAscent();
- if (ascent > maxAscent) {
- maxAscent = ascent;
- }
-
- }
-
- /**
- * Removes all parsers from this text area.
- *
- * @see #removeParser(Parser)
- */
- public void clearParsers() {
- if (parserManager != null) {
- parserManager.clearParsers();
- }
- }
-
- /**
- * Clones a token list. This is necessary as tokens are reused in {@link RSyntaxDocument}, so we can't simply use
- * the ones we are handed from it.
- *
- * @param t
- * The token list to clone.
- * @return The clone of the token list.
- */
- private Token cloneTokenList(Token t) {
-
- if (t == null) {
- return null;
- }
-
- Token clone = new DefaultToken();
- clone.copyFrom(t);
- Token cloneEnd = clone;
-
- while ((t = t.getNextToken()) != null) {
- Token temp = new DefaultToken();
- temp.copyFrom(t);
- cloneEnd.setNextToken(temp);
- cloneEnd = temp;
- }
-
- return clone;
-
- }
-
- /**
- * Copies the currently selected text to the system clipboard, with any necessary style information (font,
- * foreground color and background color). Does nothing for null
selections.
- */
- public void copyAsRtf() {
-
- int selStart = getSelectionStart();
- int selEnd = getSelectionEnd();
- if (selStart == selEnd) {
- return;
- }
-
- // Make sure there is a system clipboard, and that we can write
- // to it.
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- try {
- sm.checkSystemClipboardAccess();
- } catch (SecurityException se) {
- UIManager.getLookAndFeel().provideErrorFeedback(null);
- return;
- }
- }
- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
-
- // Create the RTF selection.
- RtfGenerator gen = getRTFGenerator();
- Token tokenList = getTokenListFor(selStart, selEnd);
- for (Token t = tokenList; t != null; t = t.getNextToken()) {
- if (t.isPaintable()) {
- if (t.textCount == 1 && t.text[t.textOffset] == '\n') {
- gen.appendNewline();
- }
- else {
- Font font = getFontForTokenType(t.type);
- Color bg = getBackgroundForTokenType(t.type);
- boolean underline = getUnderlineForToken(t);
- // Small optimization - don't print fg color if this
- // is a whitespace color. Saves on RTF size.
- if (t.isWhitespace()) {
- gen.appendToDocNoFG(t.getLexeme(), font, bg, underline);
- }
- else {
- Color fg = getForegroundForToken(t);
- gen.appendToDoc(t.getLexeme(), font, fg, bg, underline);
- }
- }
- }
- }
-
- // Set the system clipboard contents to the RTF selection.
- RtfTransferable contents = new RtfTransferable(gen.getRtf().getBytes());
- // System.out.println("*** " + new String(gen.getRtf().getBytes()));
- try {
- cb.setContents(contents, null);
- } catch (IllegalStateException ise) {
- UIManager.getLookAndFeel().provideErrorFeedback(null);
- return;
- }
-
- }
-
- /**
- * Returns the document to use for an RSyntaxTextArea
- *
- * @return The document.
- */
- protected Document createDefaultModel() {
- return new RSyntaxDocument(SYNTAX_STYLE_NONE);
- }
-
- /**
- * Returns the caret event/mouse listener for RTextArea
s.
- *
- * @return The caret event/mouse listener.
- */
- protected RTAMouseListener createMouseListener() {
- return new RSyntaxTextAreaMutableCaretEvent(this);
- }
-
- /**
- * Returns the a real UI to install on this text area.
- *
- * @return The UI.
- */
- protected RTextAreaUI createRTextAreaUI() {
- return new RSyntaxTextAreaUI(this);
- }
-
- /**
- * If the caret is on a bracket, this method finds the matching bracket, and if it exists, highlights it.
- */
- protected final void doBracketMatching() {
-
- // We always need to repaint the "matched bracket" highlight if it
- // exists.
- if (match != null) {
- repaint(match);
- }
-
- // If a matching bracket is found, get its bounds and paint it!
- int pos = RSyntaxUtilities.getMatchingBracketPosition(this);
- if (pos > -1 && pos != lastBracketMatchPos) {
- try {
- match = modelToView(pos);
- if (match != null) { // Happens if we're not yet visible
- if (getAnimateBracketMatching()) {
- bracketRepaintTimer.restart();
- }
- repaint(match);
- }
- } catch (BadLocationException ble) {
- ble.printStackTrace(); // Shouldn't happen.
- }
- }
- else if (pos == -1) {
- // Set match to null so the old value isn't still repainted.
- match = null;
- bracketRepaintTimer.stop();
- }
- lastBracketMatchPos = pos;
-
- }
-
- /**
- * Notifies all listeners that a caret change has occurred.
- *
- * @param e
- * The caret event.
- */
- protected void fireCaretUpdate(CaretEvent e) {
- super.fireCaretUpdate(e);
- if (isBracketMatchingEnabled()) {
- doBracketMatching();
- }
- }
-
- /**
- * Notifies all listeners that the active line range has changed.
- *
- * @param min
- * The minimum "active" line, or -1
.
- * @param max
- * The maximum "active" line, or -1
.
- */
- private void fireActiveLineRangeEvent(int min, int max) {
- ActiveLineRangeEvent e = null; // Lazily created
- // 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] == ActiveLineRangeListener.class) {
- if (e == null) {
- e = new ActiveLineRangeEvent(this, min, max);
- }
- ((ActiveLineRangeListener) listeners[i + 1]).activeLineRangeChanged(e);
- }
- }
- }
-
- /**
- * Notifies all listeners that have registered interest for notification on this event type. The listener list is
- * processed last to first.
- *
- * @param e
- * The event to fire.
- * @see EventListenerList
- */
- private void fireHyperlinkUpdate(HyperlinkEvent e) {
- // 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] == HyperlinkListener.class) {
- ((HyperlinkListener) listeners[i + 1]).hyperlinkUpdate(e);
- }
- }
- }
-
- /**
- * Notifies listeners that the marked occurrences for this text area have changed.
- */
- void fireMarkedOccurrencesChanged() {
- firePropertyChange(RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY,
- null, null);
- }
-
- /**
- * Fires a notification that the parser notices for this text area have changed.
- */
- void fireParserNoticesChange() {
- firePropertyChange(PARSER_NOTICES_PROPERTY, null, null);
- }
-
- /**
- * Forces the given {@link Parser} to re-parse the content of this text area.
- * Parser
can be configured as to what notices it returns. For
- * example, if a Java language parser can be configured to set whether no serialVersionUID is a warning, error, or
- * ignored, this method can be called after changing the expected notice type to have the document re-parsed.
- *
- * @param parser
- * The index of the Parser
to re-run.
- * @see #getParser(int)
- */
- public void forceReparsing(int parser) {
- parserManager.forceReparsing(parser);
- }
-
- /**
- * Forces re-parsing with a specific parser. Note that if this parser is not installed on this text area, nothing
- * will happen.
- *
- * @param parser
- * The parser that should re-parse this text area's contents. This should be installed on this text area.
- * @return Whether the parser was installed on this text area.
- * @see #forceReparsing(int)
- */
- public boolean forceReparsing(Parser parser) {
- for (int i = 0; i < getParserCount(); i++) {
- if (getParser(i) == parser) {
- forceReparsing(i);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns whether bracket matching should be animated.
- *
- * @return Whether bracket matching should be animated.
- * @see #setAnimateBracketMatching(boolean)
- */
- public boolean getAnimateBracketMatching() {
- return animateBracketMatching;
- }
-
- /**
- * Returns the background color for tokens of the specified type.
- *
- * @param type
- * The type of token.
- * @return The background color to use for that token type. If this value is null
then this token type
- * has no special background color.
- * @see #getForegroundForToken(Token)
- */
- public Color getBackgroundForTokenType(int type) {
- // Don't default to this.getBackground(), as Tokens simply don't
- // paint a background if they get a null Color.
- return syntaxScheme.styles[type].background;
- }
-
- /**
- * Returns whether curly braces should be automatically closed when a newline is entered after an opening curly
- * brace. Note that this property is only honored for languages that use curly braces to denote code blocks.
- *
- * @return Whether curly braces should be automatically closed.
- * @see #setCloseCurlyBraces(boolean)
- */
- public boolean getCloseCurlyBraces() {
- return closeCurlyBraces;
- }
-
- /**
- * Returns whether closing markup tags should be automatically completed when "</
" is typed. Note
- * that this property is only honored for markup languages, such as HTML, XML and PHP.
- *
- * @return Whether closing markup tags should be automatically completed.
- * @see #setCloseMarkupTags(boolean)
- */
- public boolean getCloseMarkupTags() {
- return closeMarkupTags;
- }
-
- /**
- * Returns the code template manager for all instances of RSyntaxTextArea
. The manager is lazily
- * created.
- *
- * @return The code template manager.
- * @see #setTemplatesEnabled(boolean)
- */
- public static synchronized CodeTemplateManager getCodeTemplateManager() {
- if (codeTemplateManager == null) {
- codeTemplateManager = new CodeTemplateManager();
- }
- return codeTemplateManager;
- }
-
- /**
- * Returns the default bracket-match background color.
- *
- * @return The color.
- * @see #getDefaultBracketMatchBorderColor
- */
- public static final Color getDefaultBracketMatchBGColor() {
- return DEFAULT_BRACKET_MATCH_BG_COLOR;
- }
-
- /**
- * Returns the default bracket-match border color.
- *
- * @return The color.
- * @see #getDefaultBracketMatchBGColor
- */
- public static final Color getDefaultBracketMatchBorderColor() {
- return DEFAULT_BRACKET_MATCH_BORDER_COLOR;
- }
-
- /**
- * Returns the default selection color for this text area. This color was chosen because it's light and
- * RSyntaxTextArea
does not change text color between selected/unselected text for contrast like
- * regular JTextArea
s do.
- *
- * @return The default selection color.
- */
- public static Color getDefaultSelectionColor() {
- return DEFAULT_SELECTION_COLOR;
- }
-
- /**
- * Returns the "default" syntax highlighting color scheme. The colors used are somewhat standard among syntax
- * highlighting text editors.
- *
- * @return The default syntax highlighting color scheme.
- * @see #restoreDefaultSyntaxScheme()
- * @see #getSyntaxScheme()
- * @see #setSyntaxScheme(SyntaxScheme)
- */
- public SyntaxScheme getDefaultSyntaxScheme() {
- return new SyntaxScheme(getFont());
- }
-
- /**
- * Returns whether an EOL marker should be drawn at the end of each line.
- *
- * @return Whether EOL markers should be visible.
- * @see #setEOLMarkersVisible(boolean)
- * @see #isWhitespaceVisible()
- */
- public boolean getEOLMarkersVisible() {
- return eolMarkersVisible;
- }
-
- /**
- * Returns the font for tokens of the specified type.
- *
- * @param type
- * The type of token.
- * @return The font to use for that token type.
- * @see #getFontMetricsForTokenType(int)
- */
- public Font getFontForTokenType(int type) {
- Font f = syntaxScheme.styles[type].font;
- return f != null ? f : getFont();
- }
-
- /**
- * Returns the font metrics for tokens of the specified type.
- *
- * @param type
- * The type of token.
- * @return The font metrics to use for that token type.
- * @see #getFontForTokenType(int)
- */
- public FontMetrics getFontMetricsForTokenType(int type) {
- FontMetrics fm = syntaxScheme.styles[type].fontMetrics;
- return fm != null ? fm : defaultFontMetrics;
- }
-
- /**
- * Returns the foreground color to use when painting a token.
- *
- * @param t
- * The token.
- * @return The foreground color to use for that token. This value is never null
.
- * @see #getBackgroundForTokenType(int)
- */
- public Color getForegroundForToken(Token t) {
- if (getHyperlinksEnabled() && t.isHyperlink() &&
- hoveredOverLinkOffset == t.offset) {
- return hyperlinkFG;
- }
- return getForegroundForTokenType(t.type);
- }
-
- /**
- * Returns the foreground color to use when painting a token. This does not take into account whether the token is a
- * hyperlink.
- *
- * @param type
- * The token type.
- * @return The foreground color to use for that token. This value is never null
.
- * @see #getForegroundForToken(Token)
- */
- public Color getForegroundForTokenType(int type) {
- Color fg = syntaxScheme.styles[type].foreground;
- return fg != null ? fg : getForeground();
- }
-
- /**
- * Returns whether fractional font metrics are enabled for this text area.
- *
- * @return Whether fractional font metrics are enabled.
- * @see #setFractionalFontMetricsEnabled
- * @see #getTextAntiAliasHint
- */
- public boolean getFractionalFontMetricsEnabled() {
- return fractionalFontMetricsEnabled;
- }
-
- /**
- * Returns a Graphics2D
version of the specified graphics that has been initialized with the proper
- * rendering hints.
- *
- * @param g
- * The graphics context for which to get a Graphics2D
.
- * @return The Graphics2D
.
- */
- private final Graphics2D getGraphics2D(Graphics g) {
- Graphics2D g2d = (Graphics2D) g;
- if (aaHint != null) {
- g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
- aaHint);
- }
- if (fractionalFontMetricsEnabled) {
- g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
- RenderingHints.VALUE_FRACTIONALMETRICS_ON);
- }
- return g2d;
- }
-
- /**
- * Returns the color to use when painting hyperlinks.
- *
- * @return The color to use when painting hyperlinks.
- * @see #setHyperlinkForeground(Color)
- * @see #getHyperlinksEnabled()
- */
- public Color getHyperlinkForeground() {
- return hyperlinkFG;
- }
-
- /**
- * Returns whether hyperlinks are enabled for this text area.
- *
- * @return Whether hyperlinks are enabled for this text area.
- * @see #setHyperlinksEnabled(boolean)
- */
- public boolean getHyperlinksEnabled() {
- return hyperlinksEnabled;
- }
-
- /**
- * Returns the height to use for a line of text in this text area.
- *
- * @return The height of a line of text in this text area.
- */
- public int getLineHeight() {
- // System.err.println("... getLineHeight() returning " + lineHeight);
- return lineHeight;
- }
-
- /**
- * Returns a list of "marked occurrences" in the text area. If there are no marked occurrences, this will be an
- * empty list.
- *
- * @return The list of marked occurrences.
- */
- public List getMarkedOccurrences() {
- return ((RSyntaxTextAreaHighlighter) getHighlighter()).
- getMarkedOccurrences();
- }
-
- /**
- * Returns whether "Mark Occurrences" is enabled.
- *
- * @return Whether "Mark Occurrences" is enabled.
- * @see #setMarkOccurrences(boolean)
- */
- public boolean getMarkOccurrences() {
- return markOccurrencesSupport != null;
- }
-
- /**
- * Returns the color used to "mark occurrences."
- *
- * @return The mark occurrences color.
- * @see #setMarkOccurrencesColor(Color)
- */
- public Color getMarkOccurrencesColor() {
- return markOccurrencesColor;
- }
-
- /**
- * Returns whether tokens of the specified type should have "mark occurrences" enabled for the current programming
- * language.
- *
- * @param type
- * The token type.
- * @return Whether tokens of this type should have "mark occurrences" enabled.
- */
- boolean getMarkOccurrencesOfTokenType(int type) {
- RSyntaxDocument doc = (RSyntaxDocument) getDocument();
- return doc.getMarkOccurrencesOfTokenType(type);
- }
-
- /**
- * Gets the color used as the background for a matched bracket.
- *
- * @return The color used.
- * @see #setMatchedBracketBGColor
- * @see #getMatchedBracketBorderColor
- */
- public Color getMatchedBracketBGColor() {
- return matchedBracketBGColor;
- }
-
- /**
- * Gets the color used as the border for a matched bracket.
- *
- * @return The color used.
- * @see #setMatchedBracketBorderColor
- * @see #getMatchedBracketBGColor
- */
- public Color getMatchedBracketBorderColor() {
- return matchedBracketBorderColor;
- }
-
- /**
- * Returns the matched bracket's rectangle, or null
if there is currently no matched bracket. Note that
- * this shouldn't ever be called by the user.
- *
- * @return The rectangle surrounding the matched bracket.
- */
- public final Rectangle getMatchRectangle() {
- return match;
- }
-
- /**
- * Returns the specified parser.
- *
- * @param index
- * The {@link Parser} to retrieve.
- * @return The Parser
.
- * @see #getParserCount()
- * @see #addParser(Parser)
- */
- public Parser getParser(int index) {
- return parserManager.getParser(index);
- }
-
- /**
- * Returns the number of parsers operating on this text area.
- *
- * @return The parser count.
- * @see #addParser(Parser)
- */
- public int getParserCount() {
- return parserManager == null ? 0 : parserManager.getParserCount();
- }
-
- /**
- * Returns a list of the current parser notices for this text area. This method (like most Swing methods) should
- * only be called on the EDT.
- *
- * @return The list of notices. This will be an empty list if there are none.
- */
- public List getParserNotices() {
- return parserManager == null ? new ArrayList(0) :
- parserManager.getParserNotices();
- }
-
- /**
- * Returns the RTF generator for this text area, lazily creating it if necessary.
- *
- * @return The RTF generator.
- */
- private RtfGenerator getRTFGenerator() {
- if (rtfGenerator == null) {
- rtfGenerator = new RtfGenerator();
- }
- else {
- rtfGenerator.reset();
- }
- return rtfGenerator;
- }
-
- /**
- * If auto-indent is enabled, this method returns whether a new line after this one should be indented (based on the
- * standard indentation rules for the current programming language). For example, in Java, for a line containing:
- *
- *
- * for (int i=0; i<10; i++) {
- *
- *
- * the following line should be indented.
- *
- * @param line
- * The line to check.
- * @return Whether a line inserted after this one should be auto-indented. If auto-indentation is disabled, this
- * will always return false
.
- * @see #isAutoIndentEnabled()
- */
- public boolean getShouldIndentNextLine(int line) {
- if (isAutoIndentEnabled()) {
- RSyntaxDocument doc = (RSyntaxDocument) getDocument();
- return doc.getShouldIndentNextLine(line);
- }
- return false;
- }
-
- /**
- * Returns what type of syntax highlighting this editor is doing.
- *
- * @return The style being used, such as {@link SyntaxConstants#SYNTAX_STYLE_JAVA}.
- * @see #setSyntaxEditingStyle(String)
- * @see SyntaxConstants
- */
- public String getSyntaxEditingStyle() {
- return syntaxStyleKey;
- }
-
- /**
- * Returns all of the colors currently being used in syntax highlighting by this text component.
- *
- * @return An instance of SyntaxScheme
that represents the colors currently being used for syntax
- * highlighting.
- * @see #setSyntaxScheme(SyntaxScheme)
- */
- public SyntaxScheme getSyntaxScheme() {
- return syntaxScheme;
- }
-
- /**
- * Returns whether or not templates are enabled for all instances of RSyntaxTextArea
.
- *
- * @return Whether templates are enabled.
- * @see #saveTemplates()
- * @see #setTemplateDirectory(String)
- * @see #setTemplatesEnabled(boolean)
- */
- public static synchronized boolean getTemplatesEnabled() {
- return templatesEnabled;
- }
-
- /**
- * Returns the rendering hint used when antialiasing text in this editor.
- *
- * @return The name of a field in java.awt.RenderingHints
, or null
if no text antialiasing
- * is being done.
- * @see #setTextAntiAliasHint(String)
- * @see #getFractionalFontMetricsEnabled()
- */
- public String getTextAntiAliasHint() {
- return aaHintFieldName;
- }
-
- /**
- * Returns a token list for the given range in the document.
- *
- * @param startOffs
- * The starting offset in the document.
- * @param endOffs
- * The end offset in the document.
- * @return The first token in the token list.
- */
- private Token getTokenListFor(int startOffs, int endOffs) {
-
- Token tokenList = null;
- Token lastToken = null;
-
- Element map = getDocument().getDefaultRootElement();
- int startLine = map.getElementIndex(startOffs);
- int endLine = map.getElementIndex(endOffs);
-
- for (int line = startLine; line <= endLine; line++) {
- Token t = getTokenListForLine(line);
- t = cloneTokenList(t);
- if (tokenList == null) {
- tokenList = t;
- lastToken = tokenList;
- }
- else {
- lastToken.setNextToken(t);
- }
- while (lastToken.getNextToken() != null &&
- lastToken.getNextToken().isPaintable()) {
- lastToken = lastToken.getNextToken();
- }
- if (line < endLine) {
- // Document offset MUST be correct to prevent exceptions
- // in getTokenListFor()
- int docOffs = map.getElement(line).getEndOffset() - 1;
- t = new DefaultToken(new char[] { '\n' }, 0, 0, docOffs,
- Token.WHITESPACE);
- lastToken.setNextToken(t);
- lastToken = t;
- }
- }
-
- // Trim the beginning and end of the token list so that it starts
- // at startOffs and ends at endOffs.
-
- // Be careful and check that startOffs is actually in the list.
- // startOffs can be < the token list's start if the end "newline"
- // character of a line is the first character selected (the token
- // list returned for that line will be null, so the first token in
- // the final token list will be from the next line and have a
- // starting offset > startOffs?).
- if (startOffs >= tokenList.offset) {
- while (!tokenList.containsPosition(startOffs)) {
- tokenList = tokenList.getNextToken();
- }
- tokenList.makeStartAt(startOffs);
- }
-
- Token temp = tokenList;
- // Be careful to check temp for null here. It is possible that no
- // token contains endOffs, if endOffs is at the end of a line.
- while (temp != null && !temp.containsPosition(endOffs)) {
- temp = temp.getNextToken();
- }
- if (temp != null) {
- temp.textCount = endOffs - temp.offset;
- temp.setNextToken(null);
- }
-
- return tokenList;
-
- }
-
- /**
- * Returns a list of tokens representing the given line.
- *
- * @param line
- * The line number to get tokens for.
- * @return A linked list of tokens representing the line's text.
- */
- public Token getTokenListForLine(int line) {
- return ((RSyntaxDocument) getDocument()).getTokenListForLine(line);
- }
-
- /**
- * Returns the tool tip to display for a mouse event at the given location. This method is overridden to give a
- * registered parser a chance to display a tool tip (such as an error description when the mouse is over an error
- * highlight).
- *
- * @param e
- * The mouse event.
- */
- public String getToolTipText(MouseEvent e) {
-
- // Check parsers for tool tips first.
- String text = null;
- URL imageBase = null;
- if (parserManager != null) {
- ToolTipInfo info = parserManager.getToolTipText(e);
- if (info != null) { // Should always be true
- text = info.getToolTipText(); // May be null
- imageBase = info.getImageBase(); // May be null
- }
- }
- if (text == null) {
- text = super.getToolTipText(e);
- }
-
- // Do we want to use "focusable" tips?
- if (getUseFocusableTips()) {
- if (text != null) {
- if (focusableTip == null) {
- focusableTip = new FocusableTip(this, parserManager);
- }
- focusableTip.setImageBase(imageBase);
- focusableTip.toolTipRequested(e, text);
- }
- // No tooltip text at new location - hide tip window if one is
- // currently visible
- else if (focusableTip != null) {
- focusableTip.possiblyDisposeOfTipWindow();
- }
- return null;
- }
-
- return text; // Standard tool tips
-
- }
-
- /**
- * Returns whether the specified token should be underlined. A token is underlined if its syntax style includes
- * underlining, or if it is a hyperlink and hyperlinks are enabled.
- *
- * @param t
- * The token.
- * @return Whether the specified token should be underlined.
- */
- public boolean getUnderlineForToken(Token t) {
- return (t.isHyperlink() && getHyperlinksEnabled()) ||
- syntaxScheme.styles[t.type].underline;
- }
-
- /**
- * Returns whether "focusable" tool tips are used instead of standard ones. Focusable tool tips are tool tips that
- * the user can click on, resize, copy from, and click links in.
- *
- * @return Whether to use focusable tool tips.
- * @see #setUseFocusableTips(boolean)
- * @see FocusableTip
- */
- public boolean getUseFocusableTips() {
- return useFocusableTips;
- }
-
- /**
- * Called by constructors to initialize common properties of the text editor.
- */
- protected void init() {
-
- // Set some RSyntaxTextArea default values.
- syntaxStyleKey = SYNTAX_STYLE_NONE;
- setMatchedBracketBGColor(getDefaultBracketMatchBGColor());
- setMatchedBracketBorderColor(getDefaultBracketMatchBorderColor());
- setBracketMatchingEnabled(true);
- setAnimateBracketMatching(true);
- lastBracketMatchPos = -1;
- setSelectionColor(getDefaultSelectionColor());
-
- // Set auto-indent related stuff.
- setAutoIndentEnabled(true);
- setCloseCurlyBraces(true);
- setCloseMarkupTags(true);
- setClearWhitespaceLinesEnabled(true);
-
- setHyperlinksEnabled(true);
- setLinkScanningMask(InputEvent.CTRL_DOWN_MASK);
- setHyperlinkForeground(Color.BLUE);
- isScanningForLinks = false;
- setUseFocusableTips(true);
-
- restoreDefaultSyntaxScheme();
-
- }
-
- /**
- * Returns whether or not auto-indent is enabled.
- *
- * @return Whether or not auto-indent is enabled.
- * @see #setAutoIndentEnabled(boolean)
- */
- public boolean isAutoIndentEnabled() {
- return autoIndentEnabled;
- }
-
- /**
- * Returns whether or not bracket matching is enabled.
- *
- * @return true
iff bracket matching is enabled.
- * @see #setBracketMatchingEnabled
- */
- public final boolean isBracketMatchingEnabled() {
- return bracketMatchingEnabled;
- }
-
- /**
- * Returns whether or not lines containing nothing but whitespace are made into blank lines when Enter is pressed in
- * them.
- *
- * @return Whether or not whitespace-only lines are cleared when the user presses Enter on them.
- * @see #setClearWhitespaceLinesEnabled(boolean)
- */
- public boolean isClearWhitespaceLinesEnabled() {
- return clearWhitespaceLines;
- }
-
- /**
- * Returns whether whitespace (spaces and tabs) is visible.
- *
- * @return Whether whitespace is visible.
- * @see #setWhitespaceVisible(boolean)
- * @see #getEOLMarkersVisible()
- */
- public boolean isWhitespaceVisible() {
- return whitespaceVisible;
- }
-
- /**
- * Returns the token at the specified position in the model.
- *
- * @param offs
- * The position in the model.
- * @return The token, or null
if no token is at that position.
- * @see #viewToToken(Point)
- */
- private Token modelToToken(int offs) {
- if (offs >= 0) {
- try {
- int line = getLineOfOffset(offs);
- Token t = getTokenListForLine(line);
- while (t != null && t.isPaintable()) {
- if (t.containsPosition(offs)) {
- return t;
- }
- t = t.getNextToken();
- }
- } catch (BadLocationException ble) {
- ble.printStackTrace(); // Never happens
- }
- }
- return null;
- }
-
- /**
- * The paintComponent
method is overridden so we apply any necessary rendering hints to the Graphics
- * object.
- */
- protected void paintComponent(Graphics g) {
- super.paintComponent(getGraphics2D(g));
- }
-
- private void refreshFontMetrics(Graphics2D g2d) {
- // It is assumed that any rendering hints are already applied to g2d.
- defaultFontMetrics = g2d.getFontMetrics(getFont());
- syntaxScheme.refreshFontMetrics(g2d);
- if (getLineWrap() == false) {
- // HORRIBLE HACK! The un-wrapped view needs to refresh its cached
- // longest line information.
- SyntaxView sv = (SyntaxView) getUI().getRootView(this).getView(0);
- sv.calculateLongestLine();
- }
- }
-
- /**
- * Removes an "active line range" listener from this text area.
- *
- * @param l
- * The listener to remove.
- * @see #removeActiveLineRangeListener(ActiveLineRangeListener)
- */
- public void removeActiveLineRangeListener(ActiveLineRangeListener l) {
- listenerList.remove(ActiveLineRangeListener.class, l);
- }
-
- /**
- * Removes a hyperlink listener from this text area.
- *
- * @param l
- * The listener to remove.
- * @see #addHyperlinkListener(HyperlinkListener)
- */
- public void removeHyperlinkListener(HyperlinkListener l) {
- listenerList.remove(HyperlinkListener.class, l);
- }
-
- /**
- * Overridden so we stop this text area's parsers, if any.
- */
- public void removeNotify() {
- if (parserManager != null) {
- parserManager.stopParsing();
- }
- super.removeNotify();
- }
-
- /**
- * Removes a parser from this text area.
- *
- * @param parser
- * The {@link Parser} to remove.
- * @return Whether the parser was found and removed.
- * @see #clearParsers()
- * @see #addParser(Parser)
- * @see #getParser(int)
- */
- public boolean removeParser(Parser parser) {
- boolean removed = false;
- if (parserManager != null) {
- removed = parserManager.removeParser(parser);
- }
- return removed;
- }
-
- /**
- * Sets the colors used for syntax highlighting to their defaults.
- *
- * @see #setSyntaxScheme(SyntaxScheme)
- * @see #getSyntaxScheme()
- * @see #getDefaultSyntaxScheme()
- */
- public void restoreDefaultSyntaxScheme() {
- setSyntaxScheme(getDefaultSyntaxScheme());
- }
-
- /**
- * Attempts to save all currently-known templates to the current template directory, as set by
- * setTemplateDirectory
. Templates will be saved as XML files with names equal to their abbreviations;
- * for example, a template that expands on the word "forb" will be saved as forb.xml
.
- *
- * @return Whether or not the save was successful. The save will be unsuccessful if the template directory does not
- * exist or if it has not been set (i.e., you have not yet called setTemplateDirectory
).
- * @see #getTemplatesEnabled
- * @see #setTemplateDirectory
- * @see #setTemplatesEnabled
- */
- public static synchronized boolean saveTemplates() {
- if (!getTemplatesEnabled()) {
- return false;
- }
- return getCodeTemplateManager().saveTemplates();
- }
-
- /**
- * Sets the "active line range." Note that this RSyntaxTextArea
itself does nothing with this
- * information, but if it is contained inside an {@link RTextScrollPane}, the active line range may be displayed in
- * the icon area of the {@link Gutter}.
- * RSyntaxTextArea
will not call this method directly; rather, it is usually
- * called by instances of LanguageSupport
in the RSTALangaugeSupport
library. See http://fifesoft.com for more information about this library.
- *
- * @param min
- * The "minimum" line in the active line range, or -1
if the range is being cleared.
- * @param max
- * The "maximum" line in the active line range, or -1
if the range is being cleared.
- * @see #addActiveLineRangeListener(ActiveLineRangeListener)
- */
- public void setActiveLineRange(int min, int max) {
- if (min == -1) {
- max = -1; // Force max to be -1 if min is.
- }
- fireActiveLineRangeEvent(min, max);
- }
-
- /**
- * Sets whether bracket matching should be animated. This fires a property change event of type
- * {@link #ANIMATE_BRACKET_MATCHING_PROPERTY}.
- *
- * @param animate
- * Whether to animate bracket matching.
- * @see #getAnimateBracketMatching()
- */
- public void setAnimateBracketMatching(boolean animate) {
- if (animate != animateBracketMatching) {
- animateBracketMatching = animate;
- if (animate && bracketRepaintTimer == null) {
- bracketRepaintTimer = new BracketMatchingTimer();
- }
- firePropertyChange(ANIMATE_BRACKET_MATCHING_PROPERTY,
- !animate, animate);
- }
- }
-
- /**
- * Sets whether or not auto-indent is enabled. This fires a property change event of type
- * {@link #AUTO_INDENT_PROPERTY}.
- *
- * @param enabled
- * Whether or not auto-indent is enabled.
- * @see #isAutoIndentEnabled()
- */
- public void setAutoIndentEnabled(boolean enabled) {
- if (autoIndentEnabled != enabled) {
- autoIndentEnabled = enabled;
- firePropertyChange(AUTO_INDENT_PROPERTY, !enabled, enabled);
- }
- }
-
- /**
- * Sets whether bracket matching is enabled. This fires a property change event of type
- * {@link #BRACKET_MATCHING_PROPERTY}.
- *
- * @param enabled
- * Whether or not bracket matching should be enabled.
- * @see #isBracketMatchingEnabled()
- */
- public void setBracketMatchingEnabled(boolean enabled) {
- if (enabled != bracketMatchingEnabled) {
- bracketMatchingEnabled = enabled;
- repaint();
- firePropertyChange(BRACKET_MATCHING_PROPERTY, !enabled, enabled);
- }
- }
-
- /**
- * Sets whether or not lines containing nothing but whitespace are made into blank lines when Enter is pressed in
- * them. This method fires a property change event of type {@link #CLEAR_WHITESPACE_LINES_PROPERTY}.
- *
- * @param enabled
- * Whether or not whitespace-only lines are cleared when the user presses Enter on them.
- * @see #isClearWhitespaceLinesEnabled()
- */
- public void setClearWhitespaceLinesEnabled(boolean enabled) {
- if (enabled != clearWhitespaceLines) {
- clearWhitespaceLines = enabled;
- firePropertyChange(CLEAR_WHITESPACE_LINES_PROPERTY,
- !enabled, enabled);
- }
- }
-
- /**
- * Toggles whether curly braces should be automatically closed when a newline is entered after an opening curly
- * brace. Note that this property is only honored for languages that use curly braces to denote code blocks.
- * </
" is typed. Note that
- * this property is only honored for markup languages, such as HTML, XML and PHP.
- * RSyntaxDocument
.
- */
- public void setDocument(Document document) {
- if (!(document instanceof RSyntaxDocument))
- throw new IllegalArgumentException("Documents for " +
- "RSyntaxTextArea must be instances of " +
- "RSyntaxDocument!");
- super.setDocument(document);
- }
-
- /**
- * Sets whether EOL markers are visible at the end of each line. This method fires a property change of type
- * {@link #EOL_VISIBLE_PROPERTY}.
- *
- * @param visible
- * Whether EOL markers are visible.
- * @see #getEOLMarkersVisible()
- * @see #setWhitespaceVisible(boolean)
- */
- public void setEOLMarkersVisible(boolean visible) {
- if (visible != eolMarkersVisible) {
- eolMarkersVisible = visible;
- repaint();
- firePropertyChange(EOL_VISIBLE_PROPERTY, !visible, visible);
- }
- }
-
- /**
- * Sets the font used by this text area. Note that this method does not alter the appearance of an
- * RSyntaxTextArea
since it uses different fonts for each token type.
- *
- * @param font
- * The font.
- */
- public void setFont(Font font) {
-
- Font old = super.getFont();
- super.setFont(font); // Do this first.
-
- // Usually programmers keep a single font for all token types, but
- // may use bold or italic for styling some.
- SyntaxScheme scheme = getSyntaxScheme();
- if (scheme != null && old != null) {
- scheme.changeBaseFont(old, font);
- calculateLineHeight();
- }
-
- // We must be connected to a screen resource for our
- // graphics to be non-null.
- if (isDisplayable()) {
- refreshFontMetrics(getGraphics2D(getGraphics()));
- // Updates the margin line.
- updateMarginLineX();
- // Force the current line highlight to be repainted, even
- // though the caret's location hasn't changed.
- forceCurrentLineHighlightRepaint();
- // Get line number border in text area to repaint again
- // since line heights have updated.
- firePropertyChange("font", old, font);
- // So parent JScrollPane will have its scrollbars updated.
- revalidate();
- }
-
- }
-
- /**
- * Sets whether fractional font metrics are enabled. This method fires a property change event of type
- * {@link #FRACTIONAL_FONTMETRICS_PROPERTY}.
- *
- * @param enabled
- * Whether fractional font metrics are enabled.
- * @see #getFractionalFontMetricsEnabled()
- */
- public void setFractionalFontMetricsEnabled(boolean enabled) {
- if (fractionalFontMetricsEnabled != enabled) {
- fractionalFontMetricsEnabled = enabled;
- // We must be connected to a screen resource for our graphics to be
- // non-null.
- if (isDisplayable()) {
- refreshFontMetrics(getGraphics2D(getGraphics()));
- }
- firePropertyChange(FRACTIONAL_FONTMETRICS_PROPERTY,
- !enabled, enabled);
- }
- }
-
- /**
- * Sets the highlighter used by this text area.
- *
- * @param h
- * The highlighter.
- * @throws IllegalArgumentException
- * If h
is not an instance of {@link RSyntaxTextAreaHighlighter}.
- */
- public void setHighlighter(Highlighter h) {
- if (!(h instanceof RSyntaxTextAreaHighlighter)) {
- throw new IllegalArgumentException("RSyntaxTextArea requires " +
- "an RSyntaxTextAreaHighlighter for its Highlighter");
- }
- super.setHighlighter(h);
- }
-
- /**
- * Sets the color to use when painting hyperlinks.
- *
- * @param fg
- * The color to use when painting hyperlinks.
- * @throws NullPointerException
- * If fg
is null
.
- * @see #getHyperlinkForeground()
- * @see #setHyperlinksEnabled(boolean)
- */
- public void setHyperlinkForeground(Color fg) {
- if (fg == null) {
- throw new NullPointerException("fg cannot be null");
- }
- hyperlinkFG = fg;
- }
-
- /**
- * Sets whether hyperlinks are enabled for this text area. This method fires a property change event of type
- * {@link #HYPERLINKS_ENABLED_PROPERTY}.
- *
- * @param enabled
- * Whether hyperlinks are enabled.
- * @see #getHyperlinksEnabled()
- */
- public void setHyperlinksEnabled(boolean enabled) {
- if (this.hyperlinksEnabled != enabled) {
- this.hyperlinksEnabled = enabled;
- repaint();
- firePropertyChange(HYPERLINKS_ENABLED_PROPERTY, !enabled, enabled);
- }
- }
-
- /**
- * Sets the mask for the key used to toggle whether we are scanning for hyperlinks with mouse hovering.
- *
- * @param mask
- * The mask to use. This should be a value such as {@link InputEvent#CTRL_DOWN_MASK} or
- * {@link InputEvent#META_DOWN_MASK}. For invalid values, behavior is undefined.
- * @see InputEvent
- */
- public void setLinkScanningMask(int mask) {
- if (mask == InputEvent.CTRL_DOWN_MASK ||
- mask == InputEvent.META_DOWN_MASK ||
- mask == InputEvent.ALT_DOWN_MASK ||
- mask == InputEvent.SHIFT_DOWN_MASK) {
- linkScanningMask = mask;
- }
- }
-
- /**
- * Toggles 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()
- * @see #setMarkOccurrencesColor(Color)
- */
- public void setMarkOccurrences(boolean markOccurrences) {
- if (markOccurrences) {
- if (markOccurrencesSupport == null) {
- markOccurrencesSupport = new MarkOccurrencesSupport();
- markOccurrencesSupport.install(this);
- firePropertyChange(MARK_OCCURRENCES_PROPERTY, false, true);
- }
- }
- else {
- if (markOccurrencesSupport != null) {
- markOccurrencesSupport.uninstall();
- markOccurrencesSupport = null;
- firePropertyChange(MARK_OCCURRENCES_PROPERTY, true, false);
- }
- }
- }
-
- /**
- * Sets the "mark occurrences" color.
- *
- * @param color
- * The new color. This cannot be null
.
- * @see #getMarkOccurrencesColor()
- * @see #setMarkOccurrences(boolean)
- */
- public void setMarkOccurrencesColor(Color color) {
- markOccurrencesColor = color;
- if (markOccurrencesSupport != null) {
- markOccurrencesSupport.setColor(color);
- }
- }
-
- /**
- * Sets the color used as the background for a matched bracket.
- *
- * @param color
- * The color to use.
- * @see #getMatchedBracketBGColor
- * @see #setMatchedBracketBorderColor
- */
- public void setMatchedBracketBGColor(Color color) {
- matchedBracketBGColor = color;
- if (match != null)
- repaint();
- }
-
- /**
- * Sets the color used as the border for a matched bracket.
- *
- * @param color
- * The color to use.
- * @see #getMatchedBracketBorderColor
- * @see #setMatchedBracketBGColor
- */
- public void setMatchedBracketBorderColor(Color color) {
- matchedBracketBorderColor = color;
- if (match != null)
- repaint();
- }
-
- /**
- * Sets what type of syntax highlighting this editor is doing. This method fires a property change of type
- * {@link #SYNTAX_STYLE_PROPERTY}.
- *
- * @param styleKey
- * The syntax editing style to use, for example, {@link SyntaxConstants#SYNTAX_STYLE_NONE} or
- * {@link SyntaxConstants#SYNTAX_STYLE_JAVA}.
- * @see #getSyntaxEditingStyle()
- * @see SyntaxConstants
- */
- public void setSyntaxEditingStyle(String styleKey) {
- if (styleKey == null) {
- styleKey = SYNTAX_STYLE_NONE;
- }
- if (!styleKey.equals(syntaxStyleKey)) {
- String oldStyle = syntaxStyleKey;
- syntaxStyleKey = styleKey;
- ((RSyntaxDocument) getDocument()).setSyntaxStyle(styleKey);
- firePropertyChange(SYNTAX_STYLE_PROPERTY, oldStyle, styleKey);
- }
-
- }
-
- /**
- * Sets all of the colors used in syntax highlighting to the colors specified. This uses a shallow copy of the color
- * scheme so that multiple text areas can share the same color scheme and have their properties changed
- * simultaneously.
- * SyntaxScheme
to use.
- * @see #getSyntaxScheme()
- */
- public void setSyntaxScheme(SyntaxScheme scheme) {
-
- // NOTE: We don't check whether colorScheme is the same as the
- // current scheme because DecreaseFontSizeAction and
- // IncreaseFontSizeAction need it this way.
- // FIXME: Find a way around this.
-
- SyntaxScheme old = this.syntaxScheme;
- this.syntaxScheme = scheme;
-
- // Recalculate the line height. We do this here instead of in
- // refreshFontMetrics() as this method is called less often and we
- // don't need the rendering hints to get the font's height.
- calculateLineHeight();
-
- if (isDisplayable()) {
- refreshFontMetrics(getGraphics2D(getGraphics()));
- }
-
- // Updates the margin line.
- updateMarginLineX();
-
- // Force the current line highlight to be repainted, even though
- // the caret's location hasn't changed.
- forceCurrentLineHighlightRepaint();
-
- // So encompassing JScrollPane will have its scrollbars updated.
- revalidate();
-
- firePropertyChange(SYNTAX_SCHEME_PROPERTY, old, this.syntaxScheme);
-
- }
-
- /**
- * If templates are enabled, all currently-known templates are forgotten and all templates are loaded from all files
- * in the specified directory ending in "*.xml". If templates aren't enabled, nothing happens.
- *
- * @param dir
- * The directory containing files ending in extension .xml
that contain templates to load.
- * @return true
if the load was successful; false
if either templates aren't currently
- * enabled or the load failed somehow (most likely, the directory doesn't exist).
- * @see #getTemplatesEnabled
- * @see #setTemplatesEnabled
- * @see #saveTemplates
- */
- public static synchronized boolean setTemplateDirectory(String dir) {
- if (getTemplatesEnabled() && dir != null) {
- File directory = new File(dir);
- if (directory.isDirectory()) {
- return getCodeTemplateManager().
- setTemplateDirectory(directory) > -1;
- }
- boolean created = directory.mkdir();
- if (created) {
- return getCodeTemplateManager().
- setTemplateDirectory(directory) > -1;
- }
- }
- return false;
- }
-
- /**
- * Enables or disables templates.
- *
- * for (<caret>) {
- *
- * }
- *
- *
- * Templates are a shared resource among all instances of RSyntaxTextArea
; that is, templates can only
- * be enabled/disabled for all text areas globally, not individually, and all text areas have access of the same
- * templates. This should not be an issue; rather, it should be beneficial as it promotes uniformity among all text
- * areas in an application.
- *
- * @param enabled
- * Whether or not templates should be enabled.
- * @see #getTemplatesEnabled()
- */
- public static synchronized void setTemplatesEnabled(boolean enabled) {
- templatesEnabled = enabled;
- }
-
- /**
- * Sets the rendering hint to use when anti-aliasing text in this editor.
- *
- * @param aaHintFieldName
- * The name of a field in java.awt.RenderingHints
. If an unknown or unsupported field name
- * is specified (such as a 1.6+ hint being specified when this is a 1.4/1.5 JVM), null
is
- * used instead. A value of null
means "no antialiasing."
- * @see #getTextAntiAliasHint()
- */
- public void setTextAntiAliasHint(String aaHintFieldName) {
-
- // System.out.println("Trying to set AA hint to: " + aaHintFieldName);
-
- // If the new AA hint is null, disable text anti-aliasing.
- if (aaHintFieldName == null && this.aaHintFieldName != null) {
- String old = this.aaHintFieldName;
- this.aaHint = null;
- this.aaHintFieldName = null;
- // We must be connected to a screen resource for our graphics
- // to be non-null.
- if (isDisplayable()) {
- refreshFontMetrics(getGraphics2D(getGraphics()));
- }
- firePropertyChange(ANTIALIAS_PROPERTY, old, null);
- repaint();
- }
-
- // Otherwise, if they're specifying a new hint type, use it instead.
- else if (aaHintFieldName != null &&
- !aaHintFieldName.equals(this.aaHintFieldName)) {
- String old = this.aaHintFieldName;
- try {
- Field f = RenderingHints.class.getField(aaHintFieldName);
- this.aaHint = f.get(null);
- this.aaHintFieldName = aaHintFieldName;
- } catch (RuntimeException re) {
- // Re-throw (keep FindBugs happy)
- } catch (/* NoSuchField|IllegalAccess */Exception e) {
- this.aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
- this.aaHintFieldName = "VALUE_TEXT_ANTIALIAS_OFF";
- }
- // We must be connected to a screen resource for our graphics
- // to be non-null.
- if (isDisplayable()) {
- refreshFontMetrics(getGraphics2D(getGraphics()));
- }
- firePropertyChange(ANTIALIAS_PROPERTY, old, this.aaHintFieldName);
- repaint();
- }
-
- // System.out.println("... Actual new value: " + this.aaHintFieldName);
-
- }
-
- /**
- * Sets whether "focusable" tool tips are used instead of standard ones. Focusable tool tips are tool tips that the
- * user can click on, resize, copy from, and clink links in. This method fires a property change event of type
- * {@link #FOCUSABLE_TIPS_PROPERTY}.
- *
- * @param use
- * Whether to use focusable tool tips.
- * @see #getUseFocusableTips()
- * @see FocusableTip
- */
- public void setUseFocusableTips(boolean use) {
- if (use != useFocusableTips) {
- useFocusableTips = use;
- firePropertyChange(FOCUSABLE_TIPS_PROPERTY, !use, use);
- }
- }
-
- /**
- * Sets whether whitespace is visible. This method fires a property change of type
- * {@link #VISIBLE_WHITESPACE_PROPERTY}.
- *
- * @param visible
- * Whether whitespace should be visible.
- * @see #isWhitespaceVisible
- */
- public void setWhitespaceVisible(boolean visible) {
- if (whitespaceVisible != visible) {
- whitespaceVisible = visible;
- ((RSyntaxDocument) getDocument()).setWhitespaceVisible(
- visible, this);
- repaint();
- firePropertyChange(VISIBLE_WHITESPACE_PROPERTY,
- !visible, visible);
- }
- }
-
- /**
- * Returns the token at the specified position in the view.
- *
- * @param p
- * The position in the view.
- * @return The token, or null
if no token is at that position.
- * @see #modelToToken(int)
- */
- /*
- * TODO: This is a little inefficient. This should convert view coordinates to the underlying token (if any). The
- * way things currently are, we're calling getTokenListForLine() twice (once in viewToModel() and once here).
- */
- private Token viewToToken(Point p) {
- return modelToToken(viewToModel(p));
- }
-
- /**
- * A timer that animates the "bracket matching" animation.
- */
- private class BracketMatchingTimer extends Timer implements ActionListener {
-
- private int pulseCount;
-
- public BracketMatchingTimer() {
- super(20, null);
- addActionListener(this);
- setCoalesce(false);
- }
-
- public void actionPerformed(ActionEvent e) {
- if (isBracketMatchingEnabled()) {
- if (match != null) {
- if (pulseCount < 5) {
- pulseCount++;
- match.x--;
- match.y--;
- match.width += 2;
- match.height += 2;
- repaint(match.x, match.y, match.width, match.height);
- }
- else if (pulseCount < 7) {
- pulseCount++;
- match.x++;
- match.y++;
- match.width -= 2;
- match.height -= 2;
- repaint(match.x - 2, match.y - 2, match.width + 5, match.height + 5);
- }
- else {
- stop();
- pulseCount = 0;
- }
- }
- }
- }
-
- public void start() {
- match.x += 3;
- match.y += 3;
- match.width -= 6;
- match.height -= 6; // So animation can "grow" match
- pulseCount = 0;
- super.start();
- }
-
- }
-
- /**
- * Handles hyperlinks.
- */
- private class RSyntaxTextAreaMutableCaretEvent
- extends RTextAreaMutableCaretEvent {
-
- protected RSyntaxTextAreaMutableCaretEvent(RTextArea textArea) {
- super(textArea);
- }
-
- public void mouseClicked(MouseEvent e) {
- if (getHyperlinksEnabled() && isScanningForLinks &&
- hoveredOverLinkOffset > -1) {
- Token t = modelToToken(hoveredOverLinkOffset);
- URL url = null;
- String desc = null;
- try {
- String temp = t.getLexeme();
- // URI's need "http://" prefix for web URL's to work.
- if (temp.startsWith("www.")) {
- temp = "http://" + temp;
- }
- url = new URL(temp);
- } catch (MalformedURLException mue) {
- desc = mue.getMessage();
- }
- HyperlinkEvent he = new HyperlinkEvent(this,
- HyperlinkEvent.EventType.ACTIVATED,
- url, desc);
- fireHyperlinkUpdate(he);
- }
- }
-
- public void mouseMoved(MouseEvent e) {
- super.mouseMoved(e);
- if (getHyperlinksEnabled()) {
- if ((e.getModifiersEx() & linkScanningMask) != 0) {
- isScanningForLinks = true;
- Token t = viewToToken(e.getPoint());
- Cursor c2 = null;
- if (t != null && t.isHyperlink()) {
- hoveredOverLinkOffset = t.offset;
- c2 = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
- }
- else {
- c2 = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
- hoveredOverLinkOffset = -1;
- }
- if (getCursor() != c2) {
- setCursor(c2);
- // TODO: Repaint just the affected line(s).
- repaint(); // Link either left or went into.
- }
- }
- else {
- if (isScanningForLinks) {
- Cursor c = getCursor();
- isScanningForLinks = false;
- hoveredOverLinkOffset = -1;
- if (c != null && c.getType() == Cursor.HAND_CURSOR) {
- setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
- repaint(); // TODO: Repaint just the affected line.
- }
- }
- }
- }
- }
-
- }
-
-}
\ No newline at end of file
+ public static final String ANIMATE_BRACKET_MATCHING_PROPERTY = "RSTA.animateBracketMatching";
+ public static final String ANTIALIAS_PROPERTY = "RSTA.antiAlias";
+ public static final String AUTO_INDENT_PROPERTY = "RSTA.autoIndent";
+ public static final String BRACKET_MATCHING_PROPERTY = "RSTA.bracketMatching";
+ public static final String CLEAR_WHITESPACE_LINES_PROPERTY = "RSTA.clearWhitespaceLines";
+ public static final String CLOSE_CURLY_BRACES_PROPERTY = "RSTA.closeCurlyBraces";
+ public static final String CLOSE_MARKUP_TAGS_PROPERTY = "RSTA.closeMarkupTags";
+ public static final String CODE_FOLDING_PROPERTY = "RSTA.codeFolding";
+ public static final String EOL_VISIBLE_PROPERTY = "RSTA.eolMarkersVisible";
+ public static final String FOCUSABLE_TIPS_PROPERTY = "RSTA.focusableTips";
+ public static final String FRACTIONAL_FONTMETRICS_PROPERTY = "RSTA.fractionalFontMetrics";
+ public static final String HIGHLIGHT_SECONDARY_LANGUAGES_PROPERTY = "RSTA.highlightSecondaryLanguages";
+ public static final String HYPERLINKS_ENABLED_PROPERTY = "RSTA.hyperlinksEnabled";
+ public static final String MARK_OCCURRENCES_PROPERTY = "RSTA.markOccurrences";
+ public static final String MARKED_OCCURRENCES_CHANGED_PROPERTY = "RSTA.markedOccurrencesChanged";
+ public static final String PAINT_MATCHED_BRACKET_PAIR_PROPERTY = "RSTA.paintMatchedBracketPair";
+ public static final String PARSER_NOTICES_PROPERTY = "RSTA.parserNotices";
+ public static final String SYNTAX_SCHEME_PROPERTY = "RSTA.syntaxScheme";
+ public static final String SYNTAX_STYLE_PROPERTY = "RSTA.syntaxStyle";
+ public static final String TAB_LINE_COLOR_PROPERTY = "RSTA.tabLineColor";
+ public static final String TAB_LINES_PROPERTY = "RSTA.tabLines";
+ public static final String USE_SELECTED_TEXT_COLOR_PROPERTY = "RSTA.useSelectedTextColor";
+ public static final String VISIBLE_WHITESPACE_PROPERTY = "RSTA.visibleWhitespace";
+
+ private static final Color DEFAULT_BRACKET_MATCH_BG_COLOR = new Color(234,234,255);
+ private static final Color DEFAULT_BRACKET_MATCH_BORDER_COLOR = new Color(0,0,128);
+ private static final Color DEFAULT_SELECTION_COLOR = new Color(200,200,255);
+
+ private static final String MSG = "org.fife.ui.rsyntaxtextarea.RSyntaxTextArea";
+
+ private JMenu foldingMenu;
+ private static RecordableTextAction toggleCurrentFoldAction;
+ private static RecordableTextAction collapseAllCommentFoldsAction;
+ private static RecordableTextAction collapseAllFoldsAction;
+ private static RecordableTextAction expandAllFoldsAction;
+
+ /** The key for the syntax style to be highlighting. */
+ private String syntaxStyleKey;
+
+ /** The colors used for syntax highlighting. */
+ private SyntaxScheme syntaxScheme;
+
+ /** Handles code templates. */
+ private static CodeTemplateManager codeTemplateManager;
+
+ /** Whether or not templates are enabled. */
+ private static boolean templatesEnabled;
+
+ /**
+ * The rectangle surrounding the "matched bracket" if bracket matching
+ * is enabled.
+ */
+ private Rectangle match;
+
+ /**
+ * The rectangle surrounding the current offset if both bracket matching and
+ * "match both brackets" are enabled.
+ */
+ private Rectangle dotRect;
+
+ /**
+ * Used to store the location of the bracket at the caret position (either
+ * just before or just after it) and the location of its match.
+ */
+ private Point bracketInfo;
+
+ /**
+ * Colors used for the "matched bracket" if bracket matching is enabled.
+ */
+ private Color matchedBracketBGColor;
+ private Color matchedBracketBorderColor;
+
+ /** The location of the last matched bracket. */
+ private int lastBracketMatchPos;
+
+ /** Whether or not bracket matching is enabled. */
+ private boolean bracketMatchingEnabled;
+
+ /** Whether or not bracket matching is animated. */
+ private boolean animateBracketMatching;
+
+ /** Whether both brackets are highlighted when bracket matching. */
+ private boolean paintMatchedBracketPair;
+
+ private BracketMatchingTimer bracketRepaintTimer;
+
+ private MatchedBracketPopupTimer matchedBracketPopupTimer;
+
+ private boolean metricsNeverRefreshed;
+
+ /**
+ * Whether or not auto-indent is on.
+ */
+ private boolean autoIndentEnabled;
+
+ /**
+ * Whether curly braces should be closed on Enter key presses, (if the
+ * current language supports it).
+ */
+ private boolean closeCurlyBraces;
+
+ /**
+ * Whether closing markup tags should be automatically completed when
+ * "</
" is typed (if the current language is a markup
+ * language).
+ */
+ private boolean closeMarkupTags;
+
+ /**
+ * Whether or not lines with nothing but whitespace are "made empty".
+ */
+ private boolean clearWhitespaceLines;
+
+ /** Whether we are displaying visible whitespace (spaces and tabs). */
+ private boolean whitespaceVisible;
+
+ /** Whether EOL markers should be visible at the end of each line. */
+ private boolean eolMarkersVisible;
+
+ /** Whether tab lines are enabled. */
+ private boolean paintTabLines;
+
+ /** The color to use when painting tab lines. */
+ private Color tabLineColor;
+
+ /**
+ * Whether hyperlinks are enabled (must be supported by the syntax
+ * scheme being used).
+ */
+ private boolean hyperlinksEnabled;
+
+ /** The color to use when painting hyperlinks. */
+ private Color hyperlinkFG;
+
+ /**
+ * Mask used to determine if the correct key is being held down to scan
+ * for hyperlinks (ctrl, meta, etc.).
+ */
+ private int linkScanningMask;
+
+ /** Whether secondary languages have their backgrounds colored. */
+ private boolean highlightSecondaryLanguages;
+
+ /** Whether the "selected text" color should be used with selected text. */
+ private boolean useSelectedTextColor;
+
+ /** Handles "mark occurrences" support. */
+ private MarkOccurrencesSupport markOccurrencesSupport;
+
+ /** The color used to render "marked occurrences". */
+ private Color markOccurrencesColor;
+
+ /** The delay before occurrences are marked in the editor. */
+ private int markOccurrencesDelay;
+
+ /** Whether a border should be painted around marked occurrences. */
+ private boolean paintMarkOccurrencesBorder;
+
+ /** Metrics of the text area's font. */
+ private FontMetrics defaultFontMetrics;
+
+ /** Manages running the parser. */
+ private ParserManager parserManager;
+
+ private String cachedTip;
+ /** Used to work around an issue with Apple JVMs. */
+ private Point cachedTipLoc;
+
+ /**
+ * Whether the editor is currently scanning for hyperlinks on mouse
+ * movement.
+ */
+ private boolean isScanningForLinks;
+
+ private int hoveredOverLinkOffset;
+
+ private LinkGenerator linkGenerator;
+ private LinkGeneratorResult linkGeneratorResult;
+
+ private int rhsCorrection;
+
+ private FoldManager foldManager;
+
+ /** Whether "focusable" tool tips are used instead of standard ones. */
+ private boolean useFocusableTips;
+
+ /** The last focusable tip displayed. */
+ private FocusableTip focusableTip;
+
+ /** Cached desktop anti-aliasing hints, if anti-aliasing is enabled. */
+ private Map,?> aaHints;
+
+ /** Renders tokens. */
+ private TokenPainter tokenPainter;
+
+ /** Whether a popup showing matched bracket lines when they're off-screen. */
+ private boolean showMatchedBracketPopup;
+
+private int lineHeight; // Height of a line of text; same for default, bold & italic.
+private int maxAscent;
+private boolean fractionalFontMetricsEnabled;
+
+ private Color[] secondaryLanguageBackgrounds;
+
+
+ /**
+ * Constructor.
+ */
+ public RSyntaxTextArea() {
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document for the editor.
+ */
+ public RSyntaxTextArea(RSyntaxDocument doc) {
+ super(doc);
+ setSyntaxEditingStyle(doc.getSyntaxStyle());
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param text The initial text to display.
+ */
+ public RSyntaxTextArea(String text) {
+ super(text);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param rows The number of rows to display.
+ * @param cols The number of columns to display.
+ * @throws IllegalArgumentException If either rows
or
+ * cols
is negative.
+ */
+ public RSyntaxTextArea(int rows, int cols) {
+ super(rows, cols);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param text The initial text to display.
+ * @param rows The number of rows to display.
+ * @param cols The number of columns to display.
+ * @throws IllegalArgumentException If either rows
or
+ * cols
is negative.
+ */
+ public RSyntaxTextArea(String text, int rows, int cols) {
+ super(text, rows, cols);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document for the editor.
+ * @param text The initial text to display.
+ * @param rows The number of rows to display.
+ * @param cols The number of columns to display.
+ * @throws IllegalArgumentException If either rows
or
+ * cols
is negative.
+ */
+ public RSyntaxTextArea(RSyntaxDocument doc, String text,int rows,int cols) {
+ super(doc, text, rows, cols);
+ }
+
+
+ /**
+ * Creates a new RSyntaxTextArea
.
+ *
+ * @param textMode Either INSERT_MODE
or
+ * OVERWRITE_MODE
.
+ */
+ public RSyntaxTextArea(int textMode) {
+ super(textMode);
+ }
+
+
+ /**
+ * Adds an "active line range" listener to this text area.
+ *
+ * @param l The listener to add.
+ * @see #removeActiveLineRangeListener(ActiveLineRangeListener)
+ */
+ public void addActiveLineRangeListener(ActiveLineRangeListener l) {
+ listenerList.add(ActiveLineRangeListener.class, l);
+ }
+
+
+ /**
+ * Adds a hyperlink listener to this text area.
+ *
+ * @param l The listener to add.
+ * @see #removeHyperlinkListener(HyperlinkListener)
+ */
+ public void addHyperlinkListener(HyperlinkListener l) {
+ listenerList.add(HyperlinkListener.class, l);
+ }
+
+
+ /**
+ * Updates the font metrics the first time we're displayed.
+ */
+ @Override
+ public void addNotify() {
+
+ super.addNotify();
+
+ // Some LookAndFeels (e.g. WebLaF) for some reason have a 0x0 parent
+ // window initially (perhaps something to do with them fading in?),
+ // which will cause an exception from getGraphics(), so we must be
+ // careful here.
+ if (metricsNeverRefreshed) {
+ Window parent = SwingUtilities.getWindowAncestor(this);
+ if (parent!=null && parent.getWidth()>0 && parent.getHeight()>0) {
+ refreshFontMetrics(getGraphics2D(getGraphics()));
+ metricsNeverRefreshed = false;
+ }
+ }
+
+ // Re-start parsing if we were removed from one container and added
+ // to another
+ if (parserManager!=null) {
+ parserManager.restartParsing();
+ }
+
+ }
+
+
+ /**
+ * Adds the parser to "validate" the source code in this text area. This
+ * can be anything from a spell checker to a "compiler" that verifies
+ * source code.
+ *
+ * @param parser The new parser. A value of null
will
+ * do nothing.
+ * @see #getParser(int)
+ * @see #getParserCount()
+ * @see #removeParser(Parser)
+ */
+ public void addParser(Parser parser) {
+ if (parserManager==null) {
+ parserManager = new ParserManager(this);
+ }
+ parserManager.addParser(parser);
+ }
+
+
+ /**
+ * Appends a submenu with code folding options to this text component's
+ * popup menu.
+ *
+ * @param popup The popup menu to append to.
+ * @see #createPopupMenu()
+ */
+ protected void appendFoldingMenu(JPopupMenu popup) {
+ popup.addSeparator();
+ ResourceBundle bundle = ResourceBundle.getBundle(MSG);
+ foldingMenu = new JMenu(bundle.getString("ContextMenu.Folding"));
+ foldingMenu.add(createPopupMenuItem(toggleCurrentFoldAction));
+ foldingMenu.add(createPopupMenuItem(collapseAllCommentFoldsAction));
+ foldingMenu.add(createPopupMenuItem(collapseAllFoldsAction));
+ foldingMenu.add(createPopupMenuItem(expandAllFoldsAction));
+ popup.add(foldingMenu);
+
+ }
+
+
+ /**
+ * Recalculates the height of a line in this text area and the
+ * maximum ascent of all fonts displayed.
+ */
+ private void calculateLineHeight() {
+
+ lineHeight = maxAscent = 0;
+
+ // Each token style.
+ for (int i=0; inull
.
+ * @see #createPopupMenu()
+ * @see #setPopupMenu(JPopupMenu)
+ */
+ @Override
+ protected void configurePopupMenu(JPopupMenu popupMenu) {
+
+ super.configurePopupMenu(popupMenu);
+
+ // They may have overridden createPopupMenu()...
+ if (popupMenu!=null && popupMenu.getComponentCount()>0 &&
+ foldingMenu!=null) {
+ foldingMenu.setEnabled(foldManager.
+ isCodeFoldingSupportedAndEnabled());
+ }
+ }
+
+
+ /**
+ * Copies the currently selected text to the system clipboard, with style
+ * information from the specified theme. Does nothing for {@code null} or
+ * empty selections.
+ *
+ * @param theme The theme to use for the color and font information.
+ * This may be {@code null}, in which case this text area's
+ * current styles are used.
+ * @see #copyAsStyledText()
+ */
+ public void copyAsStyledText(Theme theme) {
+
+ // It's more performant to call the no-arg overload
+ if (theme == null) {
+ copyAsStyledText();
+ return;
+ }
+
+ Theme origTheme = new Theme(this);
+
+ theme.apply(this);
+ try {
+ copyAsStyledText();
+ } finally {
+ origTheme.apply(this);
+ }
+ }
+
+ /**
+ * Copies the currently selected text to the system clipboard, with
+ * any necessary style information (font, foreground color and background
+ * color). Does nothing for {@code null} or empty selections.
+ *
+ * @see #copyAsStyledText(Theme)
+ */
+ public void copyAsStyledText() {
+
+ int selStart = getSelectionStart();
+ int selEnd = getSelectionEnd();
+ if (selStart==selEnd) {
+ return;
+ }
+
+ // Get the selection as HTML
+ String html = HtmlUtil.getTextAsHtml(this, selStart, selEnd);
+
+ // Get the selection as RTF
+ byte[] rtfBytes = getTextAsRtf(selStart, selEnd);
+
+ // Set the system clipboard contents to the RTF selection.
+ StyledTextTransferable contents = new StyledTextTransferable(html, rtfBytes);
+
+ Clipboard cb = getToolkit().getSystemClipboard();
+ try {
+ cb.setContents(contents, null);
+ } catch (IllegalStateException ise) {
+ UIManager.getLookAndFeel().provideErrorFeedback(null);
+ }
+ }
+
+
+ /**
+ * Returns the document to use for an RSyntaxTextArea
.
+ *
+ * @return The document.
+ */
+ @Override
+ protected Document createDefaultModel() {
+ return new RSyntaxDocument(SYNTAX_STYLE_NONE);
+ }
+
+
+ /**
+ * Returns the caret event/mouse listener for RTextArea
s.
+ *
+ * @return The caret event/mouse listener.
+ */
+ @Override
+ protected RTAMouseListener createMouseListener() {
+ return new RSyntaxTextAreaMutableCaretEvent(this);
+ }
+
+
+ /**
+ * Overridden to add menu items related to cold folding.
+ *
+ * @return The popup menu.
+ * @see #appendFoldingMenu(JPopupMenu)
+ */
+ @Override
+ protected JPopupMenu createPopupMenu() {
+ JPopupMenu popup = super.createPopupMenu();
+ appendFoldingMenu(popup);
+ return popup;
+ }
+
+
+ /**
+ * See createPopupMenuActions() in RTextArea.
+ * TODO: Remove these horrible hacks and move localizing of actions into
+ * the editor kits, where it should be! The context menu should contain
+ * actions from the editor kits.
+ */
+ private static void createRstaPopupMenuActions() {
+
+ ResourceBundle msg = ResourceBundle.getBundle(MSG);
+
+ toggleCurrentFoldAction = new RSyntaxTextAreaEditorKit.
+ ToggleCurrentFoldAction();
+ toggleCurrentFoldAction.setProperties(msg, "Action.ToggleCurrentFold");
+
+ collapseAllCommentFoldsAction = new RSyntaxTextAreaEditorKit.
+ CollapseAllCommentFoldsAction();
+ collapseAllCommentFoldsAction.setProperties(msg, "Action.CollapseCommentFolds");
+
+ collapseAllFoldsAction = new RSyntaxTextAreaEditorKit.CollapseAllFoldsAction(true);
+ expandAllFoldsAction = new RSyntaxTextAreaEditorKit.ExpandAllFoldsAction(true);
+
+ }
+
+
+ /**
+ * Returns the a real UI to install on this text area.
+ *
+ * @return The UI.
+ */
+ @Override
+ protected RTextAreaUI createRTextAreaUI() {
+ return new RSyntaxTextAreaUI(this);
+ }
+
+
+ /**
+ * If the caret is on a bracket, this method finds the matching bracket,
+ * and if it exists, highlights it.
+ */
+ protected final void doBracketMatching() {
+
+ // We always need to repaint the "matched bracket" highlight if it
+ // exists.
+ if (match!=null) {
+ repaint(match);
+ if (dotRect!=null) {
+ repaint(dotRect);
+ }
+ }
+
+ // If a matching bracket is found, get its bounds and paint it!
+ int lastCaretBracketPos = bracketInfo==null ? -1 : bracketInfo.x;
+ bracketInfo = RSyntaxUtilities.getMatchingBracketPosition(this,
+ bracketInfo);
+ if (bracketInfo.y>-1 &&
+ (bracketInfo.y!=lastBracketMatchPos ||
+ bracketInfo.x!=lastCaretBracketPos)) {
+ try {
+ match = modelToView(bracketInfo.y);
+ if (match!=null) { // Happens if we're not yet visible
+ if (getPaintMatchedBracketPair()) {
+ dotRect = modelToView(bracketInfo.x);
+ }
+ else {
+ dotRect = null;
+ }
+ if (getAnimateBracketMatching()) {
+ bracketRepaintTimer.restart();
+ }
+ repaint(match);
+ if (dotRect!=null) {
+ repaint(dotRect);
+ }
+
+ if (getShowMatchedBracketPopup()) {
+ Container parent = getParent();
+ if (parent instanceof JViewport) {
+ Rectangle visibleRect = this.getVisibleRect();
+ if (match.y + match.height < visibleRect.getY()) {
+ if (matchedBracketPopupTimer == null) {
+ matchedBracketPopupTimer =
+ new MatchedBracketPopupTimer();
+ }
+ matchedBracketPopupTimer.restart(bracketInfo.y);
+ }
+ }
+ }
+
+ }
+ } catch (BadLocationException ble) {
+ ble.printStackTrace(); // Shouldn't happen.
+ }
+ }
+ else if (bracketInfo.y==-1) {
+ // Set match to null so the old value isn't still repainted.
+ match = null;
+ dotRect = null;
+ bracketRepaintTimer.stop();
+ }
+ lastBracketMatchPos = bracketInfo.y;
+
+ }
+
+
+ /**
+ * Notifies all listeners that a caret change has occurred.
+ *
+ * @param e The caret event.
+ */
+ @Override
+ protected void fireCaretUpdate(CaretEvent e) {
+ super.fireCaretUpdate(e);
+ if (isBracketMatchingEnabled()) {
+ doBracketMatching();
+ }
+ }
+
+
+ /**
+ * Notifies all listeners that the active line range has changed.
+ *
+ * @param min The minimum "active" line, or -1
.
+ * @param max The maximum "active" line, or -1
.
+ */
+ private void fireActiveLineRangeEvent(int min, int max) {
+ ActiveLineRangeEvent e = null; // Lazily created
+ // 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]==ActiveLineRangeListener.class) {
+ if (e==null) {
+ e = new ActiveLineRangeEvent(this, min, max);
+ }
+ ((ActiveLineRangeListener)listeners[i+1]).activeLineRangeChanged(e);
+ }
+ }
+ }
+
+
+ /**
+ * Notifies all listeners that have registered interest for notification
+ * on this event type. The listener list is processed last to first.
+ *
+ * @param e The event to fire.
+ */
+ private void fireHyperlinkUpdate(HyperlinkEvent e) {
+ // 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]==HyperlinkListener.class) {
+ ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e);
+ }
+ }
+ }
+
+
+ /**
+ * Notifies listeners that the marked occurrences for this text area
+ * have changed.
+ */
+ void fireMarkedOccurrencesChanged() {
+ firePropertyChange(RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY,
+ null, null);
+ }
+
+
+ /**
+ * Fires a notification that the parser notices for this text area have
+ * changed.
+ */
+ void fireParserNoticesChange() {
+ firePropertyChange(PARSER_NOTICES_PROPERTY, null, null);
+ }
+
+
+ /**
+ * Called whenever a fold is collapsed or expanded. This causes the
+ * text editor to revalidate. This method is here because of poor design
+ * and should be removed.
+ *
+ * @param fold The fold that was collapsed or expanded.
+ */
+ public void foldToggled(Fold fold) {
+ match = null; // TODO: Update the bracket rect rather than hide it
+ dotRect = null;
+ if (getLineWrap()) {
+ // NOTE: Without doing this later, the caret position is out of
+ // sync with the Element structure when word wrap is enabled, and
+ // causes BadLocationExceptions when an entire folded region is
+ // deleted (see GitHub issue #22:
+ // https://github.com/bobbylight/RSyntaxTextArea/issues/22)
+ SwingUtilities.invokeLater(this::possiblyUpdateCurrentLineHighlightLocation);
+ }
+ else {
+ possiblyUpdateCurrentLineHighlightLocation();
+ }
+ revalidate();
+ repaint();
+ }
+
+
+ /**
+ * Forces the given {@link Parser} to re-parse the content of this text
+ * area.Parser
can be configured
+ * as to what notices it returns. For example, if a Java language parser
+ * can be configured to set whether no serialVersionUID is a warning,
+ * error, or ignored, this method can be called after changing the expected
+ * notice type to have the document re-parsed.
+ *
+ * @param parser The index of the Parser
to re-run.
+ * @see #getParser(int)
+ */
+ public void forceReparsing(int parser) {
+ parserManager.forceReparsing(parser);
+ }
+
+
+ /**
+ * Forces re-parsing with a specific parser. Note that if this parser is
+ * not installed on this text area, nothing will happen.
+ *
+ * @param parser The parser that should re-parse this text area's contents.
+ * This should be installed on this text area.
+ * @return Whether the parser was installed on this text area.
+ * @see #forceReparsing(int)
+ */
+ public boolean forceReparsing(Parser parser) {
+ for (int i=0; iRSyntaxTextArea
. The manager is lazily created.
+ *
+ * @return The code template manager.
+ * @see #setTemplatesEnabled(boolean)
+ */
+ public static synchronized CodeTemplateManager getCodeTemplateManager() {
+ if (codeTemplateManager==null) {
+ codeTemplateManager = new CodeTemplateManager();
+ }
+ return codeTemplateManager;
+ }
+
+
+ /**
+ * Returns the default bracket-match background color.
+ *
+ * @return The color.
+ * @see #getDefaultBracketMatchBorderColor
+ */
+ public static Color getDefaultBracketMatchBGColor() {
+ return DEFAULT_BRACKET_MATCH_BG_COLOR;
+ }
+
+
+ /**
+ * Returns the default bracket-match border color.
+ *
+ * @return The color.
+ * @see #getDefaultBracketMatchBGColor
+ */
+ public static Color getDefaultBracketMatchBorderColor() {
+ return DEFAULT_BRACKET_MATCH_BORDER_COLOR;
+ }
+
+
+ /**
+ * Returns the default selection color for this text area. This
+ * color was chosen because it's light and RSyntaxTextArea
+ * does not change text color between selected/unselected text for
+ * contrast like regular JTextArea
s do.
+ *
+ * @return The default selection color.
+ */
+ public static Color getDefaultSelectionColor() {
+ return DEFAULT_SELECTION_COLOR;
+ }
+
+
+ /**
+ * Returns the "default" syntax highlighting color scheme. The colors
+ * used are somewhat standard among syntax highlighting text editors.
+ *
+ * @return The default syntax highlighting color scheme.
+ * @see #restoreDefaultSyntaxScheme()
+ * @see #getSyntaxScheme()
+ * @see #setSyntaxScheme(SyntaxScheme)
+ */
+ public SyntaxScheme getDefaultSyntaxScheme() {
+ return new SyntaxScheme(getFont());
+ }
+
+
+ /**
+ * Returns whether an EOL marker should be drawn at the end of each line.
+ *
+ * @return Whether EOL markers should be visible.
+ * @see #setEOLMarkersVisible(boolean)
+ * @see #isWhitespaceVisible()
+ */
+ public boolean getEOLMarkersVisible() {
+ return eolMarkersVisible;
+ }
+
+
+ /**
+ * Returns the fold manager for this text area.
+ *
+ * @return The fold manager.
+ */
+ public FoldManager getFoldManager() {
+ return foldManager;
+ }
+
+
+ /**
+ * Returns the font for tokens of the specified type.
+ *
+ * @param type The type of token.
+ * @return The font to use for that token type.
+ * @see #getFontMetricsForTokenType(int)
+ */
+ public Font getFontForTokenType(int type) {
+ Font f = syntaxScheme.getStyle(type).font;
+ return f!=null ? f : getFont();
+ }
+
+
+ /**
+ * Returns the font metrics for tokens of the specified type.
+ *
+ * @param type The type of token.
+ * @return The font metrics to use for that token type.
+ * @see #getFontForTokenType(int)
+ */
+ public FontMetrics getFontMetricsForTokenType(int type) {
+ FontMetrics fm = syntaxScheme.getStyle(type).fontMetrics;
+ return fm!=null ? fm : defaultFontMetrics;
+ }
+
+
+ /**
+ * Returns the foreground color to use when painting a token.
+ *
+ * @param t The token.
+ * @return The foreground color to use for that token. This
+ * value is never null
.
+ * @see #getBackgroundForToken(Token)
+ */
+ public Color getForegroundForToken(Token t) {
+ if (getHyperlinksEnabled() && hoveredOverLinkOffset==t.getOffset() &&
+ (t.isHyperlink() || linkGeneratorResult!=null)) {
+ return hyperlinkFG;
+ }
+ return getForegroundForTokenType(t.getType());
+ }
+
+
+ /**
+ * Returns the foreground color to use when painting a token. This does
+ * not take into account whether the token is a hyperlink.
+ *
+ * @param type The token type.
+ * @return The foreground color to use for that token. This
+ * value is never null
.
+ * @see #getForegroundForToken(Token)
+ */
+ public Color getForegroundForTokenType(int type) {
+ Color fg = syntaxScheme.getStyle(type).foreground;
+ return fg!=null ? fg : getForeground();
+ }
+
+
+ /**
+ * Returns whether fractional font metrics are enabled for this text area.
+ *
+ * @return Whether fractional font metrics are enabled.
+ * @see #setFractionalFontMetricsEnabled
+ * @see #getAntiAliasingEnabled()
+ */
+ public boolean getFractionalFontMetricsEnabled() {
+ return fractionalFontMetricsEnabled;
+ }
+
+
+ /**
+ * Returns a Graphics2D
version of the specified graphics
+ * that has been initialized with the proper rendering hints.
+ *
+ * @param g The graphics context for which to get a
+ * Graphics2D
.
+ * @return The Graphics2D
.
+ */
+ private Graphics2D getGraphics2D(Graphics g) {
+ Graphics2D g2d = (Graphics2D)g;
+ if (aaHints!=null) {
+ g2d.addRenderingHints(aaHints);
+ }
+ if (fractionalFontMetricsEnabled) {
+ g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
+ RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ }
+ return g2d;
+ }
+
+
+ /**
+ * Returns whether "secondary" languages should have their backgrounds
+ * colored differently to visually differentiate them. This feature
+ * imposes a fair performance penalty.
+ *
+ * @return Whether secondary languages have their backgrounds colored
+ * differently.
+ * @see #setHighlightSecondaryLanguages(boolean)
+ * @see #getSecondaryLanguageBackground(int)
+ * @see #getSecondaryLanguageCount()
+ * @see #setSecondaryLanguageBackground(int, Color)
+ */
+ public boolean getHighlightSecondaryLanguages() {
+ return highlightSecondaryLanguages;
+ }
+
+
+ /**
+ * Returns the color to use when painting hyperlinks.
+ *
+ * @return The color to use when painting hyperlinks.
+ * @see #setHyperlinkForeground(Color)
+ * @see #getHyperlinksEnabled()
+ */
+ public Color getHyperlinkForeground() {
+ return hyperlinkFG;
+ }
+
+
+ /**
+ * Returns whether hyperlinks are enabled for this text area.
+ *
+ * @return Whether hyperlinks are enabled for this text area.
+ * @see #setHyperlinksEnabled(boolean)
+ */
+ public boolean getHyperlinksEnabled() {
+ return hyperlinksEnabled;
+ }
+
+
+ /**
+ * Returns the last visible offset in this text area. This may not be the
+ * length of the document if code folding is enabled.
+ *
+ * @return The last visible offset in this text area.
+ */
+ public int getLastVisibleOffset() {
+ if (isCodeFoldingEnabled()) {
+ int lastVisibleLine = foldManager.getLastVisibleLine();
+ if (lastVisibleLinenull
, no special
+ * background is painted behind a matched bracket.
+ * @see #setMatchedBracketBGColor
+ * @see #getMatchedBracketBorderColor
+ */
+ public Color getMatchedBracketBGColor() {
+ return matchedBracketBGColor;
+ }
+
+
+ /**
+ * Gets the color used as the border for a matched bracket.
+ *
+ * @return The color used.
+ * @see #setMatchedBracketBorderColor
+ * @see #getMatchedBracketBGColor
+ */
+ public Color getMatchedBracketBorderColor() {
+ return matchedBracketBorderColor;
+ }
+
+
+ /**
+ * Returns the caret's offset's rectangle, or null
if there
+ * is currently no matched bracket, bracket matching is disabled, or "paint
+ * both matched brackets" is disabled. This should never be called by the
+ * programmer directly.
+ *
+ * @return The rectangle surrounding the matched bracket.
+ * @see #getMatchRectangle()
+ */
+ Rectangle getDotRectangle() {
+ return dotRect;
+ }
+
+
+ /**
+ * Returns the matched bracket's rectangle, or null
if there
+ * is currently no matched bracket. This should never be called by the
+ * programmer directly.
+ *
+ * @return The rectangle surrounding the matched bracket.
+ * @see #getDotRectangle()
+ */
+ Rectangle getMatchRectangle() {
+ return match;
+ }
+
+
+ /**
+ * Overridden to return the max ascent for any font used in the editor.
+ *
+ * @return The max ascent value.
+ */
+ @Override
+ public int getMaxAscent() {
+ return maxAscent;
+ }
+
+
+ /**
+ * Returns whether the bracket at the caret position is painted as a
+ * "match" when a matched bracket is found. Note that this property does
+ * nothing if {@link #isBracketMatchingEnabled()} returns
+ * false
.
+ *
+ * @return Whether both brackets in a bracket pair are highlighted when
+ * bracket matching is enabled.
+ * @see #setPaintMatchedBracketPair(boolean)
+ * @see #isBracketMatchingEnabled()
+ * @see #setBracketMatchingEnabled(boolean)
+ */
+ public boolean getPaintMatchedBracketPair() {
+ return paintMatchedBracketPair;
+ }
+
+
+ /**
+ * Returns whether tab lines are painted.
+ *
+ * @return Whether tab lines are painted.
+ * @see #setPaintTabLines(boolean)
+ * @see #getTabLineColor()
+ */
+ public boolean getPaintTabLines() {
+ return paintTabLines;
+ }
+
+
+ /**
+ * Returns whether to paint the backgrounds of tokens on the specified
+ * line (assuming they are not obstructed by e.g. selection).
+ *
+ * @param line The line number.
+ * @param y The y-offset of the line. This is used when line wrap is
+ * enabled, since each logical line can be rendered as several
+ * physical lines.
+ * @return Whether to paint the token backgrounds on this line.
+ */
+ boolean getPaintTokenBackgrounds(int line, float y) {
+ //System.out.println(y + ", " + getCurrentCaretY() + "-" + (getCurrentCaretY() + getLineHeight()));
+ int iy = (int)y;
+ int curCaretY = getCurrentCaretY();
+ return iyParser
.
+ * @see #getParserCount()
+ * @see #addParser(Parser)
+ */
+ public Parser getParser(int index) {
+ return parserManager.getParser(index);
+ }
+
+
+ /**
+ * Returns the number of parsers operating on this text area.
+ *
+ * @return The parser count.
+ * @see #addParser(Parser)
+ */
+ public int getParserCount() {
+ return parserManager==null ? 0 : parserManager.getParserCount();
+ }
+
+
+ /**
+ * Returns the currently set parser delay. This is the delay that must
+ * occur between edits for any registered {@link Parser}s to run.
+ *
+ * @return The currently set parser delay, in milliseconds.
+ * @see #setParserDelay(int)
+ */
+ public int getParserDelay() {
+ return parserManager.getDelay();
+ }
+
+
+ /**
+ * Returns a list of the current parser notices for this text area.
+ * This method (like most Swing methods) should only be called on the
+ * EDT.
+ *
+ * @return The list of notices. This will be an empty list if there are
+ * none.
+ */
+ public List
+ * for (int i=0; i<10; i++) {
+ *
+ *
+ * the following line should be indented.
+ *
+ * @param line The line to check.
+ * @return Whether a line inserted after this one should be auto-indented.
+ * If auto-indentation is disabled, this will always return
+ * false
.
+ * @see #isAutoIndentEnabled()
+ */
+ public boolean getShouldIndentNextLine(int line) {
+ if (isAutoIndentEnabled()) {
+ RSyntaxDocument doc = (RSyntaxDocument)getDocument();
+ return doc.getShouldIndentNextLine(line);
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns whether a small popup window should display the text on the
+ * line containing a matched bracket whenever a matched bracket is off-
+ * screen.
+ *
+ * @return Whether to show the popup.
+ * @see #setShowMatchedBracketPopup(boolean)
+ */
+ public boolean getShowMatchedBracketPopup() {
+ return showMatchedBracketPopup;
+ }
+
+
+ /**
+ * Returns what type of syntax highlighting this editor is doing.
+ *
+ * @return The style being used, such as
+ * {@link SyntaxConstants#SYNTAX_STYLE_JAVA}.
+ * @see #setSyntaxEditingStyle(String)
+ * @see SyntaxConstants
+ */
+ public String getSyntaxEditingStyle() {
+ return syntaxStyleKey;
+ }
+
+
+ /**
+ * Returns all of the colors currently being used in syntax highlighting
+ * by this text component.
+ *
+ * @return An instance of SyntaxScheme
that represents
+ * the colors currently being used for syntax highlighting.
+ * @see #setSyntaxScheme(SyntaxScheme)
+ */
+ public SyntaxScheme getSyntaxScheme() {
+ return syntaxScheme;
+ }
+
+
+ /**
+ * Returns the color used to paint tab lines.
+ *
+ * @return The color used to paint tab lines.
+ * @see #setTabLineColor(Color)
+ * @see #getPaintTabLines()
+ * @see #setPaintTabLines(boolean)
+ */
+ public Color getTabLineColor() {
+ return tabLineColor;
+ }
+
+
+ /**
+ * Returns whether a border is painted around marked occurrences.
+ *
+ * @return Whether a border is painted.
+ * @see #setPaintMarkOccurrencesBorder(boolean)
+ * @see #getMarkOccurrencesColor()
+ * @see #getMarkOccurrences()
+ */
+ public boolean getPaintMarkOccurrencesBorder() {
+ return paintMarkOccurrencesBorder;
+ }
+
+
+ /**
+ * Returns the background color for the specified secondary language.
+ *
+ * @param index The language index. Note that these are 1-based, not
+ * 0-based, and should be in the range
+ * 1-getSecondaryLanguageCount()
, inclusive.
+ * @return The color, or null
if none.
+ * @see #getSecondaryLanguageCount()
+ * @see #setSecondaryLanguageBackground(int, Color)
+ * @see #getHighlightSecondaryLanguages()
+ */
+ public Color getSecondaryLanguageBackground(int index) {
+ return secondaryLanguageBackgrounds[index - 1];
+ }
+
+
+ /**
+ * Returns the number of secondary language backgrounds.
+ *
+ * @return The number of secondary language backgrounds.
+ * @see #getSecondaryLanguageBackground(int)
+ * @see #setSecondaryLanguageBackground(int, Color)
+ * @see #getHighlightSecondaryLanguages()
+ */
+ public int getSecondaryLanguageCount() {
+ return secondaryLanguageBackgrounds.length;
+ }
+
+
+ /**
+ * Returns whether or not templates are enabled for all instances
+ * of RSyntaxTextArea
.true
iff bracket matching is enabled.
+ * @see #setBracketMatchingEnabled
+ */
+ public final boolean isBracketMatchingEnabled() {
+ return bracketMatchingEnabled;
+ }
+
+
+ /**
+ * Returns whether or not lines containing nothing but whitespace are made
+ * into blank lines when Enter is pressed in them.
+ *
+ * @return Whether or not whitespace-only lines are cleared when
+ * the user presses Enter on them.
+ * @see #setClearWhitespaceLinesEnabled(boolean)
+ */
+ public boolean isClearWhitespaceLinesEnabled() {
+ return clearWhitespaceLines;
+ }
+
+
+ /**
+ * Returns whether code folding is enabled. Note that only certain
+ * languages support code folding; those that do not will ignore this
+ * property.
+ *
+ * @return Whether code folding is enabled.
+ * @see #setCodeFoldingEnabled(boolean)
+ */
+ public boolean isCodeFoldingEnabled() {
+ return foldManager.isCodeFoldingEnabled();
+ }
+
+
+ /**
+ * Returns whether whitespace (spaces and tabs) is visible.
+ *
+ * @return Whether whitespace is visible.
+ * @see #setWhitespaceVisible(boolean)
+ * @see #getEOLMarkersVisible()
+ */
+ public boolean isWhitespaceVisible() {
+ return whitespaceVisible;
+ }
+
+
+ /**
+ * Returns the token at the specified position in the model.
+ *
+ * @param offs The position in the model.
+ * @return The token, or null
if no token is at that
+ * position.
+ * @see #viewToToken(Point)
+ */
+ public Token modelToToken(int offs) {
+ if (offs>=0) {
+ try {
+ int line = getLineOfOffset(offs);
+ Token t = getTokenListForLine(line);
+ return RSyntaxUtilities.getTokenAtOffset(t, offs);
+ } catch (BadLocationException ble) {
+ ble.printStackTrace(); // Never happens
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * The paintComponent
method is overridden so we
+ * apply any necessary rendering hints to the Graphics object.
+ */
+ @Override
+ protected void paintComponent(Graphics g) {
+
+ // A call to refreshFontMetrics() used to be in addNotify(), but
+ // unfortunately we cannot always get the graphics context there. If
+ // the parent frame/dialog is LAF-decorated, there is a chance that the
+ // window's width and/or height is still == 0 at addNotify() (e.g.
+ // WebLaF). So unfortunately it's safest to do this here, with a flag
+ // to only allow it to happen once.
+ if (metricsNeverRefreshed) {
+ refreshFontMetrics(getGraphics2D(getGraphics()));
+ metricsNeverRefreshed = false;
+ }
+
+ super.paintComponent(getGraphics2D(g));
+ }
+
+
+ private void refreshFontMetrics(Graphics2D g2d) {
+ // It is assumed that any rendering hints are already applied to g2d.
+ defaultFontMetrics = g2d.getFontMetrics(getFont());
+ syntaxScheme.refreshFontMetrics(g2d);
+ if (!getLineWrap()) {
+ // HORRIBLE HACK! The un-wrapped view needs to refresh its cached
+ // longest line information.
+ SyntaxView sv = (SyntaxView)getUI().getRootView(this).getView(0);
+ sv.calculateLongestLine();
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void redoLastAction() {
+ super.redoLastAction();
+ // Occasionally marked occurrences' Positions are in invalid states
+ // due to how javax.swing.text.AbstractDocument tracks the start and
+ // end offsets. This is usually not needed, but can be when the last
+ // token in the Document is a marked occurrence, and an undo or redo
+ // occurs which clears most of the document text. In that case it is
+ // possible for the end Position to be reset to something small, but
+ // the start offset to be its prior valid (start > end).
+ ((RSyntaxTextAreaHighlighter)getHighlighter()).
+ clearMarkOccurrencesHighlights();
+ }
+
+
+ /**
+ * Removes an "active line range" listener from this text area.
+ *
+ * @param l The listener to remove.
+ * @see #removeActiveLineRangeListener(ActiveLineRangeListener)
+ */
+ public void removeActiveLineRangeListener(ActiveLineRangeListener l) {
+ listenerList.remove(ActiveLineRangeListener.class, l);
+ }
+
+
+ /**
+ * Removes a hyperlink listener from this text area.
+ *
+ * @param l The listener to remove.
+ * @see #addHyperlinkListener(HyperlinkListener)
+ */
+ public void removeHyperlinkListener(HyperlinkListener l) {
+ listenerList.remove(HyperlinkListener.class, l);
+ }
+
+
+ /**
+ * Overridden so we stop this text area's parsers, if any.
+ */
+ @Override
+ public void removeNotify() {
+ if (parserManager!=null) {
+ parserManager.stopParsing();
+ }
+ super.removeNotify();
+ }
+
+
+ /**
+ * Removes a parser from this text area.
+ *
+ * @param parser The {@link Parser} to remove.
+ * @return Whether the parser was found and removed.
+ * @see #clearParsers()
+ * @see #addParser(Parser)
+ * @see #getParser(int)
+ */
+ public boolean removeParser(Parser parser) {
+ boolean removed = false;
+ if (parserManager!=null) {
+ removed = parserManager.removeParser(parser);
+ }
+ return removed;
+ }
+
+
+ /**
+ * Sets the colors used for syntax highlighting to their defaults.
+ *
+ * @see #setSyntaxScheme(SyntaxScheme)
+ * @see #getSyntaxScheme()
+ * @see #getDefaultSyntaxScheme()
+ */
+ public void restoreDefaultSyntaxScheme() {
+ setSyntaxScheme(getDefaultSyntaxScheme());
+ }
+
+
+ /**
+ * Attempts to save all currently-known templates to the current template
+ * directory, as set by setTemplateDirectory
. Templates
+ * will be saved as XML files with names equal to their abbreviations; for
+ * example, a template that expands on the word "forb" will be saved as
+ * forb.xml
.
+ *
+ * @return Whether or not the save was successful. The save will
+ * be unsuccessful if the template directory does not exist or
+ * if it has not been set (i.e., you have not yet called
+ * setTemplateDirectory
).
+ * @see #getTemplatesEnabled
+ * @see #setTemplateDirectory
+ * @see #setTemplatesEnabled
+ */
+ public static synchronized boolean saveTemplates() {
+ if (!getTemplatesEnabled()) {
+ return false;
+ }
+ return getCodeTemplateManager().saveTemplates();
+ }
+
+
+ /**
+ * Sets the "active line range." Note that this
+ * RSyntaxTextArea
itself does nothing with this information,
+ * but if it is contained inside an {@link org.fife.ui.rtextarea.RTextScrollPane},
+ * the active line range may be displayed in the icon area of the
+ * {@link org.fife.ui.rtextarea.Gutter}.RSyntaxTextArea
will not call this
+ * method directly; rather, it is usually called by instances of
+ * LanguageSupport
in the RSTALangaugeSupport
+ * library. See http://fifesoft.com
+ * for more information about this library.
+ *
+ * @param min The "minimum" line in the active line range, or
+ * -1
if the range is being cleared.
+ * @param max The "maximum" line in the active line range, or
+ * -1
if the range is being cleared.
+ * @see #addActiveLineRangeListener(ActiveLineRangeListener)
+ */
+ public void setActiveLineRange(int min, int max) {
+ if (min==-1) {
+ max = -1; // Force max to be -1 if min is.
+ }
+ fireActiveLineRangeEvent(min, max);
+ }
+
+
+ /**
+ * Sets whether bracket matching should be animated. This fires a property
+ * change event of type {@link #ANIMATE_BRACKET_MATCHING_PROPERTY}.
+ *
+ * @param animate Whether to animate bracket matching.
+ * @see #getAnimateBracketMatching()
+ */
+ public void setAnimateBracketMatching(boolean animate) {
+ if (animate!=animateBracketMatching) {
+ animateBracketMatching = animate;
+ if (animate && bracketRepaintTimer==null) {
+ bracketRepaintTimer = new BracketMatchingTimer();
+ }
+ firePropertyChange(ANIMATE_BRACKET_MATCHING_PROPERTY,
+ !animate, animate);
+ }
+ }
+
+
+ /**
+ * Sets whether anti-aliasing is enabled in this editor. This method
+ * fires a property change event of type {@link #ANTIALIAS_PROPERTY}.
+ *
+ * @param enabled Whether anti-aliasing is enabled.
+ * @see #getAntiAliasingEnabled()
+ */
+ public void setAntiAliasingEnabled(boolean enabled) {
+
+ boolean currentlyEnabled = aaHints!=null;
+
+ if (enabled!=currentlyEnabled) {
+
+ if (enabled) {
+ aaHints = RSyntaxUtilities.getDesktopAntiAliasHints();
+ // If the desktop query method comes up empty, use the standard
+ // Java2D greyscale method. Note this will likely NOT be as
+ // nice as what would be used if the getDesktopAntiAliasHints()
+ // call worked.
+ if (aaHints==null) {
+ Map</
" is typed. Note that this property is only
+ * honored for markup languages, such as HTML, XML and PHP.RSyntaxDocument
.
+ */
+ @Override
+ public void setDocument(Document document) {
+ if (!(document instanceof RSyntaxDocument)) {
+ throw new IllegalArgumentException("Documents for " +
+ "RSyntaxTextArea must be instances of " +
+ "RSyntaxDocument!");
+ }
+ if (markOccurrencesSupport != null) {
+ markOccurrencesSupport.clear();
+ }
+ super.setDocument(document);
+ setSyntaxEditingStyle(((RSyntaxDocument)document).getSyntaxStyle());
+ if (markOccurrencesSupport != null) {
+ markOccurrencesSupport.doMarkOccurrences();
+ }
+ }
+
+
+ /**
+ * Sets whether EOL markers are visible at the end of each line. This
+ * method fires a property change of type {@link #EOL_VISIBLE_PROPERTY}.
+ *
+ * @param visible Whether EOL markers are visible.
+ * @see #getEOLMarkersVisible()
+ * @see #setWhitespaceVisible(boolean)
+ */
+ public void setEOLMarkersVisible(boolean visible) {
+ if (visible!=eolMarkersVisible) {
+ eolMarkersVisible = visible;
+ repaint();
+ firePropertyChange(EOL_VISIBLE_PROPERTY, !visible, visible);
+ }
+ }
+
+
+ /**
+ * Sets the font used by this text area. Note that if some token styles
+ * are using a different font, they will not be changed by calling this
+ * method. To set different fonts on individual token types, use the
+ * text area's SyntaxScheme
.
+ *
+ * @param font The font.
+ * @see SyntaxScheme#getStyle(int)
+ */
+ @Override
+ public void setFont(Font font) {
+
+ Font old = super.getFont();
+ super.setFont(font); // Do this first.
+
+ // Usually programmers keep a single font for all token types, but
+ // may use bold or italic for styling some.
+ SyntaxScheme scheme = getSyntaxScheme();
+ if (scheme!=null && old!=null) {
+ scheme.changeBaseFont(old, font);
+ calculateLineHeight();
+ }
+
+ // We must be connected to a screen resource for our
+ // graphics to be non-null.
+ if (isDisplayable()) {
+ refreshFontMetrics(getGraphics2D(getGraphics()));
+ // Updates the margin line.
+ updateMarginLineX();
+ // Force the current line highlight to be repainted, even
+ // though the caret's location hasn't changed.
+ forceCurrentLineHighlightRepaint();
+ // Get line number border in text area to repaint again
+ // since line heights have updated.
+ firePropertyChange("font", old, font);
+ // So parent JScrollPane will have its scrollbars updated.
+ revalidate();
+ }
+
+ }
+
+
+ /**
+ * Sets whether fractional font metrics are enabled. This method fires
+ * a property change event of type {@link #FRACTIONAL_FONTMETRICS_PROPERTY}.
+ *
+ * @param enabled Whether fractional font metrics are enabled.
+ * @see #getFractionalFontMetricsEnabled()
+ */
+ public void setFractionalFontMetricsEnabled(boolean enabled) {
+ if (fractionalFontMetricsEnabled!=enabled) {
+ fractionalFontMetricsEnabled = enabled;
+ // We must be connected to a screen resource for our graphics to be
+ // non-null.
+ if (isDisplayable()) {
+ refreshFontMetrics(getGraphics2D(getGraphics()));
+ }
+ firePropertyChange(FRACTIONAL_FONTMETRICS_PROPERTY,
+ !enabled, enabled);
+ }
+ }
+
+
+ /**
+ * Sets the highlighter used by this text area.
+ *
+ * @param h The highlighter.
+ * @throws IllegalArgumentException If h
is not an instance
+ * of {@link RSyntaxTextAreaHighlighter}.
+ */
+ @Override
+ public void setHighlighter(Highlighter h) {
+
+ // Ugh, many RSTA methods assume a non-null highlighter. This is kind
+ // of icky, but most applications never *don't* want a highlighter.
+ // See #189 - BasicTextUI clears highlighter by setting it to null there
+ if (h == null) {
+ h = new RSyntaxTextAreaHighlighter();
+ }
+
+ if (!(h instanceof RSyntaxTextAreaHighlighter)) {
+ throw new IllegalArgumentException("RSyntaxTextArea requires " +
+ "an RSyntaxTextAreaHighlighter for its Highlighter");
+ }
+ super.setHighlighter(h);
+ }
+
+
+ /**
+ * Sets whether "secondary" languages should have their backgrounds
+ * colored differently to visually differentiate them. This feature
+ * imposes a fair performance penalty. This method fires a property change
+ * event of type {@link #HIGHLIGHT_SECONDARY_LANGUAGES_PROPERTY}.
+ *
+ * @see #getHighlightSecondaryLanguages()
+ * @see #setSecondaryLanguageBackground(int, Color)
+ * @see #getSecondaryLanguageCount()
+ */
+ public void setHighlightSecondaryLanguages(boolean highlight) {
+ if (this.highlightSecondaryLanguages!=highlight) {
+ highlightSecondaryLanguages = highlight;
+ repaint();
+ firePropertyChange(HIGHLIGHT_SECONDARY_LANGUAGES_PROPERTY,
+ !highlight, highlight);
+ }
+ }
+
+
+ /**
+ * Sets the color to use when painting hyperlinks.
+ *
+ * @param fg The color to use when painting hyperlinks.
+ * @throws NullPointerException If fg
is null
.
+ * @see #getHyperlinkForeground()
+ * @see #setHyperlinksEnabled(boolean)
+ */
+ public void setHyperlinkForeground(Color fg) {
+ if (fg==null) {
+ throw new NullPointerException("fg cannot be null");
+ }
+ hyperlinkFG = fg;
+ }
+
+
+ /**
+ * Sets whether hyperlinks are enabled for this text area. This method
+ * fires a property change event of type
+ * {@link #HYPERLINKS_ENABLED_PROPERTY}.
+ *
+ * @param enabled Whether hyperlinks are enabled.
+ * @see #getHyperlinksEnabled()
+ * @see #setLinkScanningMask(int)
+ */
+ public void setHyperlinksEnabled(boolean enabled) {
+ if (this.hyperlinksEnabled!=enabled) {
+ this.hyperlinksEnabled = enabled;
+ repaint();
+ firePropertyChange(HYPERLINKS_ENABLED_PROPERTY, !enabled, enabled);
+ }
+ }
+
+
+ public void setLinkGenerator(LinkGenerator generator) {
+ this.linkGenerator = generator;
+ }
+
+
+ /**
+ * Sets the mask for the key used to toggle whether we are scanning for
+ * hyperlinks with mouse hovering. The default value is
+ * {@code CTRL_DOWN_MASK}.null
.
+ * @see #getMarkOccurrencesColor()
+ * @see #setMarkOccurrences(boolean)
+ */
+ public void setMarkOccurrencesColor(Color color) {
+ markOccurrencesColor = color;
+ if (markOccurrencesSupport!=null) {
+ markOccurrencesSupport.setColor(color);
+ }
+ }
+
+
+ /**
+ * Sets the delay between when the caret is moved and when "marked
+ * occurrences" are highlighted.
+ *
+ * @param delay The new delay. This must be greater than {@code 0}.
+ * @see #getMarkOccurrencesDelay()
+ * @see #getMarkOccurrences()
+ */
+ public void setMarkOccurrencesDelay(int delay) {
+ if (delay <= 0) {
+ throw new IllegalArgumentException("Delay must be > 0");
+ }
+ if (delay != this.markOccurrencesDelay) {
+ this.markOccurrencesDelay = delay;
+ if (markOccurrencesSupport != null) {
+ markOccurrencesSupport.setDelay(delay);
+ }
+ }
+ }
+
+
+ /**
+ * Sets the color used as the background for a matched bracket.
+ *
+ * @param color The color to use. If this is null
, then no
+ * special background is painted behind a matched bracket.
+ * @see #getMatchedBracketBGColor
+ * @see #setMatchedBracketBorderColor
+ * @see #setPaintMarkOccurrencesBorder(boolean)
+ */
+ public void setMatchedBracketBGColor(Color color) {
+ matchedBracketBGColor = color;
+ if (match!=null) {
+ repaint();
+ }
+ }
+
+
+ /**
+ * Sets the color used as the border for a matched bracket.
+ *
+ * @param color The color to use.
+ * @see #getMatchedBracketBorderColor
+ * @see #setMatchedBracketBGColor
+ */
+ public void setMatchedBracketBorderColor(Color color) {
+ matchedBracketBorderColor = color;
+ if (match!=null) {
+ repaint();
+ }
+ }
+
+
+ /**
+ * Toggles whether a border should be painted around marked occurrences.
+ *
+ * @param paintBorder Whether to paint a border.
+ * @see #getPaintMarkOccurrencesBorder()
+ * @see #setMarkOccurrencesColor(Color)
+ * @see #setMarkOccurrences(boolean)
+ */
+ public void setPaintMarkOccurrencesBorder(boolean paintBorder) {
+ paintMarkOccurrencesBorder = paintBorder;
+ if (markOccurrencesSupport!=null) {
+ markOccurrencesSupport.setPaintBorder(paintBorder);
+ }
+ }
+
+
+ /**
+ * Sets whether the bracket at the caret position is painted as a "match"
+ * when a matched bracket is found. Note that this property does nothing
+ * if {@link #isBracketMatchingEnabled()} returns false
.1-getSecondaryLanguageCount()
, inclusive.
+ * @param color The new color, or null
for none.
+ * @see #getSecondaryLanguageBackground(int)
+ * @see #getSecondaryLanguageCount()
+ */
+ public void setSecondaryLanguageBackground(int index, Color color) {
+ index--;
+ Color old = secondaryLanguageBackgrounds[index];
+ if ((color==null && old!=null) || (color!=null && !color.equals(old))) {
+ secondaryLanguageBackgrounds[index] = color;
+ if (getHighlightSecondaryLanguages()) {
+ repaint();
+ }
+ }
+ }
+
+
+ /**
+ * Sets whether a small popup window should display the text on the
+ * line containing a matched bracket whenever a matched bracket is off-
+ * screen.
+ *
+ * @param show Whether to show the popup.
+ * @see #getShowMatchedBracketPopup()
+ */
+ public void setShowMatchedBracketPopup(boolean show) {
+ showMatchedBracketPopup = show;
+ }
+
+
+ /**
+ * Sets what type of syntax highlighting this editor is doing. This method
+ * fires a property change of type {@link #SYNTAX_STYLE_PROPERTY}.
+ *
+ * @param styleKey The syntax editing style to use, for example,
+ * {@link SyntaxConstants#SYNTAX_STYLE_NONE} or
+ * {@link SyntaxConstants#SYNTAX_STYLE_JAVA}.
+ * @see #getSyntaxEditingStyle()
+ * @see SyntaxConstants
+ */
+ public void setSyntaxEditingStyle(String styleKey) {
+ if (styleKey==null) {
+ styleKey = SYNTAX_STYLE_NONE;
+ }
+ if (!styleKey.equals(syntaxStyleKey)) {
+ String oldStyle = syntaxStyleKey;
+ syntaxStyleKey = styleKey;
+ ((RSyntaxDocument)getDocument()).setSyntaxStyle(styleKey);
+ firePropertyChange(SYNTAX_STYLE_PROPERTY, oldStyle, styleKey);
+ setActiveLineRange(-1, -1);
+ }
+
+ }
+
+
+ /**
+ * Sets all of the colors used in syntax highlighting to the colors
+ * specified. This uses a shallow copy of the color scheme so that
+ * multiple text areas can share the same color scheme and have their
+ * properties changed simultaneously.SyntaxScheme
to use.
+ * @see #getSyntaxScheme()
+ */
+ public void setSyntaxScheme(SyntaxScheme scheme) {
+
+ // NOTE: We don't check whether colorScheme is the same as the
+ // current scheme because DecreaseFontSizeAction and
+ // IncreaseFontSizeAction need it this way.
+ // FIXME: Find a way around this.
+
+ SyntaxScheme old = this.syntaxScheme;
+ this.syntaxScheme = scheme;
+
+ // Recalculate the line height. We do this here instead of in
+ // refreshFontMetrics() as this method is called less often and we
+ // don't need the rendering hints to get the font's height.
+ calculateLineHeight();
+
+ if (isDisplayable()) {
+ refreshFontMetrics(getGraphics2D(getGraphics()));
+ }
+
+ // Updates the margin line and "matched bracket" highlight
+ updateMarginLineX();
+ lastBracketMatchPos = -1;
+ doBracketMatching();
+
+ // Force the current line highlight to be repainted, even though
+ // the caret's location hasn't changed.
+ forceCurrentLineHighlightRepaint();
+
+ // So encompassing JScrollPane will have its scrollbars updated.
+ revalidate();
+
+ firePropertyChange(SYNTAX_SCHEME_PROPERTY, old, this.syntaxScheme);
+
+ }
+
+
+ /**
+ * If templates are enabled, all currently-known templates are forgotten
+ * and all templates are loaded from all files in the specified directory
+ * ending in "*.xml". If templates aren't enabled, nothing happens.
+ *
+ * @param dir The directory containing files ending in extension
+ * .xml
that contain templates to load.
+ * @return true
if the load was successful;
+ * false
if either templates aren't currently
+ * enabled or the load failed somehow (most likely, the
+ * directory doesn't exist).
+ * @see #getTemplatesEnabled
+ * @see #setTemplatesEnabled
+ * @see #saveTemplates
+ */
+ public static synchronized boolean setTemplateDirectory(String dir) {
+ if (getTemplatesEnabled() && dir!=null) {
+ File directory = new File(dir);
+ if (directory.isDirectory()) {
+ return getCodeTemplateManager().
+ setTemplateDirectory(directory)>-1;
+ }
+ boolean created = directory.mkdir();
+ if (created) {
+ return getCodeTemplateManager().
+ setTemplateDirectory(directory)>-1;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Enables or disables templates.
+ * for (<caret>) {
+ *
+ * }
+ *
+ *
+ * Templates are a shared resource among all instances of
+ * RSyntaxTextArea
; that is, templates can only be
+ * enabled/disabled for all text areas globally, not individually, and
+ * all text areas have access of the same templates. This should not
+ * be an issue; rather, it should be beneficial as it promotes
+ * uniformity among all text areas in an application.null
, the default
+ * (gray) is used.
+ * @see #getTabLineColor()
+ * @see #setPaintTabLines(boolean)
+ * @see #getPaintTabLines()
+ */
+ public void setTabLineColor(Color c) {
+
+ if (c==null) {
+ c = Color.gray;
+ }
+
+ if (!c.equals(tabLineColor)) {
+ Color old = tabLineColor;
+ tabLineColor = c;
+ if (getPaintTabLines()) {
+ repaint();
+ }
+ firePropertyChange(TAB_LINE_COLOR_PROPERTY, old, tabLineColor);
+ }
+
+ }
+
+
+ /**
+ * Sets whether "focusable" tool tips are used instead of standard ones.
+ * Focusable tool tips are tool tips that the user can click on,
+ * resize, copy from, and clink links in. This method fires a property
+ * change event of type {@link #FOCUSABLE_TIPS_PROPERTY}.
+ *
+ * @param use Whether to use focusable tool tips.
+ * @see #getUseFocusableTips()
+ * @see FocusableTip
+ */
+ public void setUseFocusableTips(boolean use) {
+ if (use!=useFocusableTips) {
+ useFocusableTips = use;
+ firePropertyChange(FOCUSABLE_TIPS_PROPERTY, !use, use);
+ }
+ }
+
+
+ /**
+ * Sets whether selected text should use the "selected text color" property
+ * (set via {@link #setSelectedTextColor(Color)}). This is the typical
+ * behavior of text components. By default, RSyntaxTextArea does not do
+ * this, so that token styles are visible even in selected regions of text.
+ * This method fires a property change event of type
+ * {@link #USE_SELECTED_TEXT_COLOR_PROPERTY}.
+ *
+ * @param use Whether to use the "selected text" color when painting text
+ * in selected regions.
+ * @see #getUseSelectedTextColor()
+ */
+ public void setUseSelectedTextColor(boolean use) {
+ if (use!=useSelectedTextColor) {
+ useSelectedTextColor = use;
+ firePropertyChange(USE_SELECTED_TEXT_COLOR_PROPERTY, !use, use);
+ }
+ }
+
+
+ /**
+ * Sets whether whitespace is visible. This method fires a property change
+ * of type {@link #VISIBLE_WHITESPACE_PROPERTY}.
+ *
+ * @param visible Whether whitespace should be visible.
+ * @see #isWhitespaceVisible()
+ */
+ public void setWhitespaceVisible(boolean visible) {
+ if (whitespaceVisible!=visible) {
+ this.whitespaceVisible = visible;
+ tokenPainter = visible ? new VisibleWhitespaceTokenPainter() :
+ new DefaultTokenPainter();
+ repaint();
+ firePropertyChange(VISIBLE_WHITESPACE_PROPERTY, !visible, visible);
+ }
+ }
+
+
+ /**
+ * Resets the editor state after the user clicks on a hyperlink or releases
+ * the hyperlink modifier.
+ */
+ private void stopScanningForLinks() {
+ if (isScanningForLinks) {
+ Cursor c = getCursor();
+ isScanningForLinks = false;
+ linkGeneratorResult = null;
+ hoveredOverLinkOffset = -1;
+ if (c!=null && c.getType()==Cursor.HAND_CURSOR) {
+ setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+ repaint(); // TODO: Repaint just the affected line.
+ }
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void undoLastAction() {
+ super.undoLastAction();
+ // Occasionally marked occurrences' Positions are in invalid states
+ // due to how javax.swing.text.AbstractDocument tracks the start and
+ // end offsets. This is usually not needed, but can be when the last
+ // token in the Document is a marked occurrence, and an undo or redo
+ // occurs which clears most of the document text. In that case it is
+ // possible for the end Position to be reset to something small, but
+ // the start offset to be its prior valid (start > end).
+ ((RSyntaxTextAreaHighlighter)getHighlighter()).
+ clearMarkOccurrencesHighlights();
+ }
+
+
+ /**
+ * Returns the token at the specified position in the view.
+ *
+ * @param p The position in the view.
+ * @return The token, or null
if no token is at that
+ * position.
+ * @see #modelToToken(int)
+ */
+ /*
+ * TODO: This is a little inefficient. This should convert view
+ * coordinates to the underlying token (if any). The way things currently
+ * are, we're calling getTokenListForLine() twice (once in viewToModel()
+ * and once here).
+ */
+ public Token viewToToken(Point p) {
+ return modelToToken(viewToModel(p));
+ }
+
+ /**
+ * Renders the text on the line containing the "matched bracket" after a
+ * delay.
+ */
+ private final class MatchedBracketPopupTimer extends Timer
+ implements ActionListener, CaretListener {
+
+ private MatchedBracketPopup popup;
+ private int origDot;
+ private int matchedBracketOffs;
+
+ private MatchedBracketPopupTimer() {
+ super(350, null);
+ addActionListener(this);
+ setRepeats(false);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ if (popup != null) {
+ popup.dispose();
+ }
+
+ Window window = SwingUtilities.getWindowAncestor(RSyntaxTextArea.this);
+ popup = new MatchedBracketPopup(window, RSyntaxTextArea.this, matchedBracketOffs);
+ popup.pack();
+ popup.setVisible(true);
+
+ }
+
+ @Override
+ public void caretUpdate(CaretEvent e) {
+ int dot = e.getDot();
+ if (dot != origDot) {
+ stop();
+ removeCaretListener(this);
+ if (popup != null) {
+ popup.dispose();
+ }
+ }
+ }
+
+ /**
+ * Restarts this timer, and stores a new offset to paint.
+ *
+ * @param matchedBracketOffs The offset of the new matched bracket.
+ */
+ public void restart(int matchedBracketOffs) {
+ this.origDot = getCaretPosition();
+ this.matchedBracketOffs = matchedBracketOffs;
+ this.restart();
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ addCaretListener(this);
+ }
+
+ }
+
+
+ /**
+ * A timer that animates the "bracket matching" animation.
+ */
+ private class BracketMatchingTimer extends Timer implements ActionListener {
+
+ private int pulseCount;
+
+ BracketMatchingTimer() {
+ super(20, null);
+ addActionListener(this);
+ setCoalesce(false);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (isBracketMatchingEnabled()) {
+ if (match!=null) {
+ updateAndInvalidate(match);
+ }
+ if (dotRect!=null && getPaintMatchedBracketPair()) {
+ updateAndInvalidate(dotRect);
+ }
+ if (++pulseCount==8) {
+ pulseCount = 0;
+ stop();
+ }
+ }
+ }
+
+ private void init(Rectangle r) {
+ r.x += 3;
+ r.y += 3;
+ r.width -= 6;
+ r.height -= 6; // So animation can "grow" match
+ }
+
+ @Override
+ public void start() {
+ init(match);
+ if (dotRect!=null && getPaintMatchedBracketPair()) {
+ init(dotRect);
+ }
+ pulseCount = 0;
+ super.start();
+ }
+
+ private void updateAndInvalidate(Rectangle r) {
+ if (pulseCount<5) {
+ r.x--;
+ r.y--;
+ r.width += 2;
+ r.height += 2;
+ repaint(r.x,r.y, r.width,r.height);
+ }
+ else if (pulseCount<7) {
+ r.x++;
+ r.y++;
+ r.width -= 2;
+ r.height -= 2;
+ repaint(r.x-2,r.y-2, r.width+5,r.height+5);
+ }
+ }
+
+ }
+
+
+ /**
+ * Handles hyperlinks.
+ */
+ private class RSyntaxTextAreaMutableCaretEvent
+ extends RTextAreaMutableCaretEvent {
+
+ private Insets insets;
+
+ protected RSyntaxTextAreaMutableCaretEvent(RTextArea textArea) {
+ super(textArea);
+ insets = new Insets(0, 0, 0, 0);
+ }
+
+ private HyperlinkEvent createHyperlinkEvent() {
+ HyperlinkEvent he = null;
+ if (linkGeneratorResult!=null) {
+ he = linkGeneratorResult.execute();
+ linkGeneratorResult = null;
+ }
+ else {
+ Token t = modelToToken(hoveredOverLinkOffset);
+ URL url = null;
+ String desc = null;
+ try {
+ String temp = t.getLexeme();
+ // URI's need "http://" prefix for web URL's to work.
+ if (temp.startsWith("www.")) {
+ temp = "http://" + temp;
+ }
+ url = new URL(temp);
+ } catch (MalformedURLException mue) {
+ desc = mue.getMessage();
+ }
+ he = new HyperlinkEvent(RSyntaxTextArea.this,
+ HyperlinkEvent.EventType.ACTIVATED,
+ url, desc);
+ }
+ return he;
+ }
+
+ private boolean equal(LinkGeneratorResult e1,
+ LinkGeneratorResult e2) {
+ return e1.getSourceOffset()==e2.getSourceOffset();
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (getHyperlinksEnabled() && isScanningForLinks &&
+ hoveredOverLinkOffset>-1) {
+ HyperlinkEvent he = createHyperlinkEvent();
+ if (he!=null) {
+ fireHyperlinkUpdate(he);
+ }
+ stopScanningForLinks();
+ }
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent e) {
+
+ super.mouseMoved(e);
+
+ if (!getHyperlinksEnabled()) {
+ return;
+ }
+
+ // If our link scanning mask is pressed...
+ if ((e.getModifiersEx()&linkScanningMask)==linkScanningMask) {
+
+ // GitHub issue #25 - links identified at "edges" of editor
+ // should not be activated if mouse is in margin insets.
+ insets = getInsets(insets);
+ if (insets!=null) {
+ int x = e.getX();
+ int y = e.getY();
+ if (x<=insets.left || yRSyntaxTextArea
.
+ * Currently, the new key bindings include:
*
- *
- *
+ *
* @author Robert Futrell
* @version 1.0
*/
+@SuppressWarnings({ "checkstyle:linelength" })
public class RSyntaxTextAreaDefaultInputMap extends RTADefaultInputMap {
- /**
- * Constructs the default input map for an RSyntaxTextArea
.
- */
- public RSyntaxTextAreaDefaultInputMap() {
+ /**
+ * Constructs the default input map for an RSyntaxTextArea
.
+ */
+ public RSyntaxTextAreaDefaultInputMap() {
+
+ int defaultMod = getDefaultModifier();
+ int shift = InputEvent.SHIFT_DOWN_MASK;
+ int defaultShift = defaultMod|shift;
+
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
+ put(KeyStroke.getKeyStroke('}'), RSyntaxTextAreaEditorKit.rstaCloseCurlyBraceAction);
+
+ put(KeyStroke.getKeyStroke('/'), RSyntaxTextAreaEditorKit.rstaCloseMarkupTagAction);
+ int os = RSyntaxUtilities.getOS();
+ if (os==RSyntaxUtilities.OS_WINDOWS || os==RSyntaxUtilities.OS_MAC_OSX) {
+ // *nix causes trouble with CloseMarkupTagAction and ToggleCommentAction.
+ // It triggers both KEY_PRESSED ctrl+'/' and KEY_TYPED '/' events when the
+ // user presses ctrl+'/', but Windows and OS X do not. If we try to "move"
+ // the KEY_TYPED event for '/' to KEY_PRESSED, it'll work for Linux boxes
+ // with QWERTY keyboard layouts, but non-QUERTY users won't be able to type
+ // a '/' character at all then (!). Rather than try to hack together a
+ // solution by trying to detect the IM locale and do different things for
+ // different OSes & keyboard layouts, we do the simplest thing and
+ // (unfortunately) don't have a ToggleCommentAction for *nix out-of-the-box.
+ // Applications can add one easily enough if they want one.
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, defaultMod), RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
+ }
- int defaultMod = getDefaultModifier();
- // int ctrl = InputEvent.CTRL_MASK;
- int shift = InputEvent.SHIFT_MASK;
- // int alt = InputEvent.ALT_MASK;
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, defaultMod), RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction);
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseFoldAction);
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandFoldAction);
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseAllFoldsAction);
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandAllFoldsAction);
- put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), RSyntaxTextAreaEditorKit.rstaDecreaseIndentAction);
- put(KeyStroke.getKeyStroke('}'), RSyntaxTextAreaEditorKit.rstaCloseCurlyBraceAction);
- put(KeyStroke.getKeyStroke('/'), RSyntaxTextAreaEditorKit.rstaCloseMarkupTagAction);
- put(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, defaultMod), RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
- put(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, defaultMod),
- RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction);
+ // NOTE: no modifiers => mapped to keyTyped. If we had "0" as a second
+ // second parameter, we'd get the template action (keyPressed) AND the
+ // default space action (keyTyped).
+ //put(KeyStroke.getKeyStroke(' '), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
+ put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, defaultShift), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
- // FIXME: The keystroke associated with this action should be dynamic and
- // configurable and synchronized with the "trigger" defined in RSyntaxTextArea's
- // CodeTemplateManager.
- // NOTE: no modifiers => mapped to keyTyped. If we had "0" as a second
- // second parameter, we'd get the template action (keyPressed) AND the
- // default space action (keyTyped).
- // put(KeyStroke.getKeyStroke(' '), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
- put(CodeTemplateManager.TEMPLATE_KEYSTROKE, RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
+ }
- }
-}
\ No newline at end of file
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxTextAreaEditorKit.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxTextAreaEditorKit.java
old mode 100644
new mode 100755
index 79f53b26c..d690369ae
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxTextAreaEditorKit.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxTextAreaEditorKit.java
@@ -2,1448 +2,2118 @@
* 08/29/2004
*
* RSyntaxTextAreaEditorKit.java - The editor kit used by RSyntaxTextArea.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
-import java.awt.*;
-import java.awt.event.*;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.text.CharacterIterator;
+import java.util.ResourceBundle;
import java.util.Stack;
-import java.util.stream.IntStream;
-import javax.swing.*;
-import javax.swing.text.*;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JScrollPane;
+import javax.swing.KeyStroke;
+import javax.swing.UIManager;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.Segment;
+import javax.swing.text.TextAction;
+
+import org.fife.ui.rsyntaxtextarea.folding.Fold;
+import org.fife.ui.rsyntaxtextarea.folding.FoldCollapser;
+import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate;
-import org.fife.ui.rtextarea.RecordableTextAction;
+import org.fife.ui.rtextarea.IconRowHeader;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaEditorKit;
+import org.fife.ui.rtextarea.RecordableTextAction;
+
/**
- * An extension of RTextAreaEditorKit
that adds functionality for programming-specific stuff. There are
- * currently subclasses to handle:
- *
+ * An extension of RTextAreaEditorKit
that adds functionality for
+ * programming-specific stuff. There are currently subclasses to handle:
+ *
*
- *
- *
+ *
* @author Robert Futrell
* @version 0.5
*/
+@SuppressWarnings({ "checkstyle:constantname" })
public class RSyntaxTextAreaEditorKit extends RTextAreaEditorKit {
- private static final long serialVersionUID = 1L;
-
- public static final String rstaCloseCurlyBraceAction = "RSTA.CloseCurlyBraceAction";
- public static final String rstaCloseMarkupTagAction = "RSTA.CloseMarkupTagAction";
- public static final String rstaCopyAsRtfAction = "RSTA.CopyAsRtfAction";
- public static final String rstaDecreaseIndentAction = "RSTA.DecreaseIndentAction";
- public static final String rstaGoToMatchingBracketAction = "RSTA.GoToMatchingBracketAction";
- public static final String rstaPossiblyInsertTemplateAction = "RSTA.TemplateAction";
- public static final String rstaToggleCommentAction = "RSTA.ToggleCommentAction";
-
- /**
- * The actions that RSyntaxTextAreaEditorKit
adds to those of RTextAreaEditorKit
.
- */
- private static final Action[] defaultActions = {
- new CloseCurlyBraceAction(),
- new CloseMarkupTagAction(),
- new BeginWordAction(beginWordAction, false),
- new BeginWordAction(selectionBeginWordAction, true),
- new CopyAsRtfAction(),
- // new DecreaseFontSizeAction(),
- new DecreaseIndentAction(),
- new EndWordAction(endWordAction, false),
- new EndWordAction(endWordAction, true),
- new GoToMatchingBracketAction(),
- new InsertBreakAction(),
- // new IncreaseFontSizeAction(),
- new InsertTabAction(),
- new NextWordAction(nextWordAction, false),
- new NextWordAction(selectionNextWordAction, true),
- new PossiblyInsertTemplateAction(),
- new PreviousWordAction(previousWordAction, false),
- new PreviousWordAction(selectionPreviousWordAction, true),
- new SelectWordAction(),
- new ToggleCommentAction(),
- };
-
- /**
- * Constructor.
- */
- public RSyntaxTextAreaEditorKit() {
- }
-
- /**
- * Returns the default document used by RSyntaxTextArea
s.
- *
- * @return The document.
- */
- public Document createDefaultDocument() {
- return new RSyntaxDocument(SyntaxConstants.SYNTAX_STYLE_NONE);
- }
-
- /**
- * Fetches the set of commands that can be used on a text component that is using a model and view produced by this
- * kit.
- *
- * @return the command list
- */
- public Action[] getActions() {
- return TextAction.augmentList(super.getActions(),
- RSyntaxTextAreaEditorKit.defaultActions);
- }
-
- /**
- * Positions the caret at the beginning of the word. This class is here to better handle finding the
- * "beginning of the word" for programming languages.
- */
- protected static class BeginWordAction
- extends RTextAreaEditorKit.BeginWordAction {
-
- private Segment seg;
-
- protected BeginWordAction(String name, boolean select) {
- super(name, select);
- seg = new Segment();
- }
-
- protected int getWordStart(RTextArea textArea, int offs)
- throws BadLocationException {
-
- if (offs == 0) {
- return offs;
- }
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- int line = textArea.getLineOfOffset(offs);
- int start = textArea.getLineStartOffset(line);
- if (offs == start) {
- return start;
- }
- int end = textArea.getLineEndOffset(line);
- if (line != textArea.getLineCount() - 1) {
- end--;
- }
- doc.getText(start, end - start, seg);
-
- // Determine the "type" of char at offs - lower case, upper case,
- // whitespace or other. We take special care here as we're starting
- // in the middle of the Segment to check whether we're already at
- // the "beginning" of a word.
- int firstIndex = seg.getBeginIndex() + (offs - start) - 1;
- seg.setIndex(firstIndex);
- char ch = seg.current();
- char nextCh = offs == end ? 0 : seg.array[seg.getIndex() + 1];
-
- // The "word" is a group of letters and/or digits
- if (Character.isLetterOrDigit(ch)) {
- if (offs != end && !Character.isLetterOrDigit(nextCh)) {
- return offs;
- }
- do {
- ch = seg.previous();
- } while (Character.isLetterOrDigit(ch));
- }
-
- // The "word" is whitespace
- else if (Character.isWhitespace(ch)) {
- if (offs != end && !Character.isWhitespace(nextCh)) {
- return offs;
- }
- do {
- ch = seg.previous();
- } while (Character.isWhitespace(ch));
- }
-
- // Otherwise, the "word" a single "something else" char (operator,
- // etc.).
-
- offs -= firstIndex - seg.getIndex() + 1;// seg.getEndIndex() - seg.getIndex();
- if (ch != Segment.DONE && nextCh != '\n') {
- offs++;
- }
-
- return offs;
-
- }
-
- }
-
- /**
- * Action that (optionally) aligns a closing curly brace with the line containing its matching opening curly brace.
- */
- public static class CloseCurlyBraceAction extends RecordableTextAction {
-
- private static final long serialVersionUID = 1L;
-
- private Segment seg;
-
- public CloseCurlyBraceAction() {
- super(rstaCloseCurlyBraceAction);
- seg = new Segment();
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
- RSyntaxDocument doc = (RSyntaxDocument) rsta.getDocument();
- boolean alignCurlyBraces = rsta.isAutoIndentEnabled() &&
- doc.getCurlyBracesDenoteCodeBlocks();
-
- if (alignCurlyBraces) {
- textArea.beginAtomicEdit();
- }
-
- try {
-
- textArea.replaceSelection("}");
-
- // If the user wants to align curly braces...
- if (alignCurlyBraces) {
-
- Element root = doc.getDefaultRootElement();
- int dot = rsta.getCaretPosition() - 1; // Start before '{'
- int line = root.getElementIndex(dot);
- Element elem = root.getElement(line);
- int start = elem.getStartOffset();
-
- // Get the current line's text up to the '}' entered.
- try {
- doc.getText(start, dot - start, seg);
- } catch (BadLocationException ble) { // Never happens
- ble.printStackTrace();
- return;
- }
-
- // Only attempt to align if there's only whitespace up to
- // the '}' entered.
- for (int i = 0; i < seg.count; i++) {
- char ch = seg.array[seg.offset + i];
- if (!Character.isWhitespace(ch)) {
- return;
- }
- }
-
- // Locate the matching '{' bracket, and replace the leading
- // whitespace for the '}' to match that of the '{' char's line.
- int match = RSyntaxUtilities.getMatchingBracketPosition(rsta);
- if (match > -1) {
- elem = root.getElement(root.getElementIndex(match));
- int start2 = elem.getStartOffset();
- int end = elem.getEndOffset() - 1;
- String text = null;
- try {
- text = doc.getText(start2, end - start2);
- } catch (BadLocationException ble) { // Never happens
- ble.printStackTrace();
- return;
- }
- String ws = RSyntaxUtilities.getLeadingWhitespace(text);
- rsta.replaceRange(ws, start, dot);
- }
-
- }
-
- } finally {
- if (alignCurlyBraces) {
- textArea.endAtomicEdit();
- }
- }
-
- }
-
- public final String getMacroID() {
- return rstaCloseCurlyBraceAction;
- }
-
- }
-
- /**
- * (Optionally) completes a closing markup tag.
- */
- public static class CloseMarkupTagAction extends RecordableTextAction {
-
- private static final long serialVersionUID = 1L;
-
- public CloseMarkupTagAction() {
- super(rstaCloseMarkupTagAction);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- if (!textArea.isEditable() || !textArea.isEnabled()) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- return;
- }
-
- RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
- RSyntaxDocument doc = (RSyntaxDocument) rsta.getDocument();
-
- Caret c = rsta.getCaret();
- boolean selection = c.getDot() != c.getMark();
- rsta.replaceSelection("/");
-
- // Don't automatically complete a tag if there was a selection
- int dot = c.getDot();
-
- if (doc.getLanguageIsMarkup() &&
- doc.getCompleteMarkupCloseTags() &&
- !selection && rsta.getCloseMarkupTags() && dot > 1) {
-
- try {
-
- // Check actual char before token type, since it's quicker
- char ch = doc.charAt(dot - 2);
- if (ch == '<' || ch == '[') {
-
- Token t = doc.getTokenListForLine(
- rsta.getCaretLineNumber());
- t = RSyntaxUtilities.getTokenAtOffset(t, dot - 1);
- if (t != null && t.type == Token.MARKUP_TAG_DELIMITER) {
- // System.out.println("Huzzah - closing tag!");
- String tagName = discoverTagName(doc, dot);
- if (tagName != null) {
- rsta.replaceSelection(tagName + (char) (ch + 2));
- }
- }
-
- }
-
- } catch (BadLocationException ble) { // Never happens
- UIManager.getLookAndFeel().provideErrorFeedback(rsta);
- ble.printStackTrace();
- }
-
- }
-
- }
-
- /**
- * Discovers the name of the tag being closed. Assumes standard SGML-style markup tags.
- *
- * @param doc
- * The document to parse.
- * @param dot
- * The location of the caret. This should be right after the start of a closing tag token (e.g. "
- * </
" or "[
" in the case of BBCode).
- * @return The name of the tag to close, or null
if it could not be determined.
- */
- private String discoverTagName(RSyntaxDocument doc, int dot) {
-
- Stack stack = new Stack();
-
- Element root = doc.getDefaultRootElement();
- int curLine = root.getElementIndex(dot);
-
- for (int i = 0; i <= curLine; i++) {
-
- Token t = doc.getTokenListForLine(i);
- while (t != null && t.isPaintable()) {
-
- if (t.type == Token.MARKUP_TAG_DELIMITER) {
- if (t.isSingleChar('<') || t.isSingleChar('[')) {
- t = t.getNextToken();
- while (t != null && t.isPaintable()) {
- if (t.type == Token.MARKUP_TAG_NAME ||
- // Being lenient here and also checking
- // for attributes, in case they
- // (incorrectly) have whitespace between
- // the '<' char and the element name.
- t.type == Token.MARKUP_TAG_ATTRIBUTE) {
- stack.push(t.getLexeme());
- break;
- }
- t = t.getNextToken();
- }
- }
- else if (t.textCount == 2 && t.text[t.textOffset] == '/' &&
- (t.text[t.textOffset + 1] == '>' ||
- t.text[t.textOffset + 1] == ']')) {
- if (!stack.isEmpty()) { // Always true for valid XML
- stack.pop();
- }
- }
- else if (t.textCount == 2 &&
- (t.text[t.textOffset] == '<' || t.text[t.textOffset] == '[') &&
- t.text[t.textOffset + 1] == '/') {
- String tagName = null;
- if (!stack.isEmpty()) { // Always true for valid XML
- tagName = (String) stack.pop();
- }
- if (t.offset + t.textCount >= dot) {
- return tagName;
- }
- }
- }
-
- t = t.getNextToken();
-
- }
-
- }
-
- return null; // Should never happen
-
- }
-
- public String getMacroID() {
- return getName();
- }
-
- }
-
- /**
- * Action for copying text as RTF.
- */
- public static class CopyAsRtfAction extends RecordableTextAction {
-
- private static final long serialVersionUID = 1L;
-
- public CopyAsRtfAction() {
- super(rstaCopyAsRtfAction);
- }
-
- public CopyAsRtfAction(String name, Icon icon, String desc,
- Integer mnemonic, KeyStroke accelerator) {
- super(name, icon, desc, mnemonic, accelerator);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
- ((RSyntaxTextArea) textArea).copyAsRtf();
- textArea.requestFocusInWindow();
- }
-
- public final String getMacroID() {
- return getName();
- }
-
- }
-
- /**
- * Action for decreasing the font size of all fonts in the text area.
- */
- public static class DecreaseFontSizeAction
- extends RTextAreaEditorKit.DecreaseFontSizeAction {
-
- private static final long serialVersionUID = 1L;
-
- public DecreaseFontSizeAction() {
- super();
- }
-
- public DecreaseFontSizeAction(String name, Icon icon, String desc,
- Integer mnemonic, KeyStroke accelerator) {
- super(name, icon, desc, mnemonic, accelerator);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
- SyntaxScheme scheme = rsta.getSyntaxScheme();
-
- // All we need to do is update all of the fonts in syntax
- // schemes, then call setSyntaxHighlightingColorScheme with the
- // same scheme already being used. This relies on the fact that
- // that method does not check whether the new scheme is different
- // from the old scheme before updating.
-
- boolean changed = false;
- int count = scheme.styles.length;
- for (int i = 0; i < count; i++) {
- Style ss = scheme.styles[i];
- if (ss != null) {
- Font font = ss.font;
- if (font != null) {
- float oldSize = font.getSize2D();
- float newSize = oldSize - decreaseAmount;
- if (newSize >= MINIMUM_SIZE) {
- // Shrink by decreaseAmount.
- ss.font = font.deriveFont(newSize);
- changed = true;
- }
- else if (oldSize > MINIMUM_SIZE) {
- // Can't shrink by full decreaseAmount, but
- // can shrink a little bit.
- ss.font = font.deriveFont(MINIMUM_SIZE);
- changed = true;
- }
- }
- }
- }
-
- // Do the text area's font also.
- Font font = rsta.getFont();
- float oldSize = font.getSize2D();
- float newSize = oldSize - decreaseAmount;
- if (newSize >= MINIMUM_SIZE) {
- // Shrink by decreaseAmount.
- rsta.setFont(font.deriveFont(newSize));
- changed = true;
- }
- else if (oldSize > MINIMUM_SIZE) {
- // Can't shrink by full decreaseAmount, but
- // can shrink a little bit.
- rsta.setFont(font.deriveFont(MINIMUM_SIZE));
- changed = true;
- }
-
- // If we updated at least one font, update the screen. If
- // all of the fonts were already the minimum size, beep.
- if (changed) {
- rsta.setSyntaxScheme(scheme);
- // NOTE: This is a hack to get an encompassing
- // RTextScrollPane to repaint its line numbers to account
- // for a change in line height due to a font change. I'm
- // not sure why we need to do this here but not when we
- // change the syntax highlighting color scheme via the
- // Options dialog... setSyntaxHighlightingColorScheme()
- // calls revalidate() which won't repaint the scroll pane
- // if scrollbars don't change, which is why we need this.
- Component parent = rsta.getParent();
- if (parent instanceof javax.swing.JViewport) {
- parent = parent.getParent();
- if (parent instanceof JScrollPane) {
- parent.repaint();
- }
- }
- }
- else
- UIManager.getLookAndFeel().provideErrorFeedback(rsta);
-
- }
-
- }
-
- /**
- * Action for when un-indenting lines (either the current line if there is selection, or all selected lines if there
- * is one).
- */
- public static class DecreaseIndentAction extends RecordableTextAction {
-
- private static final long serialVersionUID = 1L;
-
- private Segment s;
-
- public DecreaseIndentAction() {
- this(rstaDecreaseIndentAction);
- }
-
- public DecreaseIndentAction(String name) {
- super(name);
- s = new Segment();
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- if (!textArea.isEditable() || !textArea.isEnabled()) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- return;
- }
-
- Document document = textArea.getDocument();
- Element map = document.getDefaultRootElement();
- Caret c = textArea.getCaret();
- int dot = c.getDot();
- int mark = c.getMark();
- int line1 = map.getElementIndex(dot);
- int tabSize = textArea.getTabSize();
-
- // If there is a selection, indent all lines in the selection.
- // Otherwise, indent the line the caret is on.
- if (dot != mark) {
- // Note that we cheaply reuse variables here, so don't
- // take their names to mean what they are.
- int line2 = map.getElementIndex(mark);
- dot = Math.min(line1, line2);
- mark = Math.max(line1, line2);
- Element elem;
- try {
- for (line1 = dot; line1 < mark; line1++) {
- elem = map.getElement(line1);
- handleDecreaseIndent(elem, document, tabSize);
- }
- // Don't do the last line if the caret is at its
- // beginning. We must call getDot() again and not just
- // use 'dot' as the caret's position may have changed
- // due to the insertion of the tabs above.
- elem = map.getElement(mark);
- int start = elem.getStartOffset();
- if (Math.max(c.getDot(), c.getMark()) != start) {
- handleDecreaseIndent(elem, document, tabSize);
- }
- } catch (BadLocationException ble) {
- ble.printStackTrace();
- UIManager.getLookAndFeel().
- provideErrorFeedback(textArea);
- }
- }
- else {
- Element elem = map.getElement(line1);
- try {
- handleDecreaseIndent(elem, document, tabSize);
- } catch (BadLocationException ble) {
- ble.printStackTrace();
- UIManager.getLookAndFeel().
- provideErrorFeedback(textArea);
- }
- }
-
- }
-
- public final String getMacroID() {
- return rstaDecreaseIndentAction;
- }
-
- /**
- * Actually does the "de-indentation." This method finds where the given element's leading whitespace ends,
- * then, if there is indeed leading whitespace, removes either the last char in it (if it is a tab), or removes
- * up to the number of spaces equal to a tab in the specified document (i.e., if the tab size was 5 and there
- * were 3 spaces at the end of the leading whitespace, the three will be removed; if there were 8 spaces, only
- * the first 5 would be removed).
- *
- * @param elem
- * The element to "de-indent."
- * @param doc
- * The document containing the specified element.
- * @param tabSize
- * The size of a tab, in spaces.
- */
- private final void handleDecreaseIndent(Element elem, Document doc,
- int tabSize)
- throws BadLocationException {
- int start = elem.getStartOffset();
- int end = elem.getEndOffset() - 1; // Why always true??
- doc.getText(start, end - start, s);
- int i = s.offset;
- end = i + s.count;
- if (end > i) {
- // If the first character is a tab, remove it.
- if (s.array[i] == '\t') {
- doc.remove(start, 1);
- }
- // Otherwise, see if the first character is a space. If it
- // is, remove all contiguous whitespaces at the beginning of
- // this line, up to the tab size.
- else if (s.array[i] == ' ') {
- i++;
- int toRemove = 1;
- while (i < end && s.array[i] == ' ' && toRemove < tabSize) {
- i++;
- toRemove++;
- }
- doc.remove(start, toRemove);
- }
- }
- }
-
- }
-
- /**
- * Positions the caret at the end of the word. This class is here to better handle finding the "end of the word" in
- * programming languages.
- */
- protected static class EndWordAction
- extends RTextAreaEditorKit.EndWordAction {
-
- private Segment seg;
-
- protected EndWordAction(String name, boolean select) {
- super(name, select);
- seg = new Segment();
- }
-
- protected int getWordEnd(RTextArea textArea, int offs)
- throws BadLocationException {
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- if (offs == doc.getLength()) {
- return offs;
- }
-
- int line = textArea.getLineOfOffset(offs);
- int end = textArea.getLineEndOffset(line);
- if (line != textArea.getLineCount() - 1) {
- end--; // Hide newline
- }
- if (offs == end) {
- return end;
- }
- doc.getText(offs, end - offs, seg);
-
- // Determine the "type" of char at offs - letter/digit,
- // whitespace or other
- char ch = seg.first();
-
- // The "word" is a group of letters and/or digits
- if (Character.isLetterOrDigit(ch)) {
- do {
- ch = seg.next();
- } while (Character.isLetterOrDigit(ch));
- }
-
- // The "word" is whitespace.
- else if (Character.isWhitespace(ch)) {
-
- do {
- ch = seg.next();
- } while (Character.isWhitespace(ch));
- }
-
- // Otherwise, the "word" is a single character of some other type
- // (operator, etc.).
-
- offs += seg.getIndex() - seg.getBeginIndex();
- return offs;
-
- }
-
- }
-
- /**
- * Action for moving the caret to the "matching bracket" of the bracket at the caret position (either before or
- * after).
- */
- public static class GoToMatchingBracketAction
- extends RecordableTextAction {
-
- private static final long serialVersionUID = 1L;
-
- public GoToMatchingBracketAction() {
- super(rstaGoToMatchingBracketAction);
- }
-
- public GoToMatchingBracketAction(String name, Icon icon, String desc,
- Integer mnemonic, KeyStroke accelerator) {
- super(name, icon, desc, mnemonic, accelerator);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
- RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
- int pos = RSyntaxUtilities.getMatchingBracketPosition(rsta);
- if (pos > -1) {
- // Go to the position AFTER the bracket so the previous
- // bracket (which we were just on) is highlighted.
- rsta.setCaretPosition(pos + 1);
- }
- else {
- UIManager.getLookAndFeel().provideErrorFeedback(rsta);
- }
- }
-
- public final String getMacroID() {
- return rstaGoToMatchingBracketAction;
- }
-
- }
-
- /**
- * Action for increasing the font size of all fonts in the text area.
- */
- public static class IncreaseFontSizeAction
- extends RTextAreaEditorKit.IncreaseFontSizeAction {
-
- private static final long serialVersionUID = 1L;
-
- public IncreaseFontSizeAction() {
- super();
- }
-
- public IncreaseFontSizeAction(String name, Icon icon, String desc,
- Integer mnemonic, KeyStroke accelerator) {
- super(name, icon, desc, mnemonic, accelerator);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
- SyntaxScheme scheme = rsta.getSyntaxScheme();
-
- // All we need to do is update all of the fonts in syntax
- // schemes, then call setSyntaxHighlightingColorScheme with the
- // same scheme already being used. This relies on the fact that
- // that method does not check whether the new scheme is different
- // from the old scheme before updating.
-
- boolean changed = false;
- int count = scheme.styles.length;
- for (int i = 0; i < count; i++) {
- Style ss = scheme.styles[i];
- if (ss != null) {
- Font font = ss.font;
- if (font != null) {
- float oldSize = font.getSize2D();
- float newSize = oldSize + increaseAmount;
- if (newSize <= MAXIMUM_SIZE) {
- // Grow by increaseAmount.
- ss.font = font.deriveFont(newSize);
- changed = true;
- }
- else if (oldSize < MAXIMUM_SIZE) {
- // Can't grow by full increaseAmount, but
- // can grow a little bit.
- ss.font = font.deriveFont(MAXIMUM_SIZE);
- changed = true;
- }
- }
- }
- }
-
- // Do the text area's font also.
- Font font = rsta.getFont();
- float oldSize = font.getSize2D();
- float newSize = oldSize + increaseAmount;
- if (newSize <= MAXIMUM_SIZE) {
- // Grow by increaseAmount.
- rsta.setFont(font.deriveFont(newSize));
- changed = true;
- }
- else if (oldSize < MAXIMUM_SIZE) {
- // Can't grow by full increaseAmount, but
- // can grow a little bit.
- rsta.setFont(font.deriveFont(MAXIMUM_SIZE));
- changed = true;
- }
-
- // If we updated at least one font, update the screen. If
- // all of the fonts were already the minimum size, beep.
- if (changed) {
- rsta.setSyntaxScheme(scheme);
- // NOTE: This is a hack to get an encompassing
- // RTextScrollPane to repaint its line numbers to account
- // for a change in line height due to a font change. I'm
- // not sure why we need to do this here but not when we
- // change the syntax highlighting color scheme via the
- // Options dialog... setSyntaxHighlightingColorScheme()
- // calls revalidate() which won't repaint the scroll pane
- // if scrollbars don't change, which is why we need this.
- Component parent = rsta.getParent();
- if (parent instanceof javax.swing.JViewport) {
- parent = parent.getParent();
- if (parent instanceof JScrollPane) {
- parent.repaint();
- }
- }
- }
- else
- UIManager.getLookAndFeel().provideErrorFeedback(rsta);
-
- }
-
- }
-
- /**
- * Action for when the user presses the Enter key. This is here so we can be smart and "auto-indent" for programming
- * languages.
- */
- public static class InsertBreakAction
- extends RTextAreaEditorKit.InsertBreakAction {
-
- private static final long serialVersionUID = 1L;
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- if (!textArea.isEditable() || !textArea.isEnabled()) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- return;
- }
-
- RSyntaxTextArea sta = (RSyntaxTextArea) textArea;
- boolean noSelection = sta.getSelectionStart() == sta.getSelectionEnd();
-
- // First, see if this language wants to handle inserting newlines
- // itself.
- boolean handled = false;
- if (noSelection) {
- RSyntaxDocument doc = (RSyntaxDocument) sta.getDocument();
- handled = doc.insertBreakSpecialHandling(e);
- }
-
- // If not...
- if (!handled) {
- handleInsertBreak(sta, noSelection);
- }
-
- }
-
- /**
- * @return The first location in the string past pos
that is NOT a whitespace char, or
- * -1
if only whitespace chars follow pos
(or it is the end position in the
- * string).
- */
- private static final int atEndOfLine(int pos, String s, int sLen) {
- return IntStream.range(pos, sLen).filter(i -> !RSyntaxUtilities.isWhitespace(s.charAt(i))).findFirst().orElse(-1);
- }
-
- private static final int getOpenBraceCount(RSyntaxDocument doc) {
- int openCount = 0;
- Element root = doc.getDefaultRootElement();
- int lineCount = root.getElementCount();
- for (int i = 0; i < lineCount; i++) {
- Token t = doc.getTokenListForLine(i);
- while (t != null && t.isPaintable()) {
- if (t.type == Token.SEPARATOR && t.textCount == 1) {
- char ch = t.text[t.textOffset];
- if (ch == '{') {
- openCount++;
- }
- else if (ch == '}') {
- openCount--;
- }
- }
- t = t.getNextToken();
- }
- }
- return openCount;
- }
-
- /**
- * Actually inserts the newline into the document, and auto-indents if appropriate. This method can be called by
- * token makers who implement a custom action for inserting newlines.
- *
- * @param textArea
- * @param noSelection
- * Whether there is no selection.
- */
- protected void handleInsertBreak(RSyntaxTextArea textArea,
- boolean noSelection) {
- // If we're auto-indenting...
- if (noSelection && textArea.isAutoIndentEnabled()) {
- insertNewlineWithAutoIndent(textArea);
- }
- else {
- textArea.replaceSelection("\n");
- if (noSelection) {
- possiblyCloseCurlyBrace(textArea, null);
- }
- }
- }
-
- private void insertNewlineWithAutoIndent(RSyntaxTextArea sta) {
-
- try {
-
- int caretPos = sta.getCaretPosition();
- Document doc = sta.getDocument();
- Element map = doc.getDefaultRootElement();
- int lineNum = map.getElementIndex(caretPos);
- Element line = map.getElement(lineNum);
- int start = line.getStartOffset();
- int end = line.getEndOffset() - 1; // Why always "-1"?
- int len = end - start;
- String s = doc.getText(start, len);
-
- // endWS is the end of the leading whitespace of the
- // current line.
- String leadingWS = RSyntaxUtilities.getLeadingWhitespace(s);
- StringBuffer sb = new StringBuffer("\n");
- sb.append(leadingWS);
-
- // If there is only whitespace between the caret and
- // the EOL, pressing Enter auto-indents the new line to
- // the same place as the previous line.
- int nonWhitespacePos = atEndOfLine(caretPos - start, s, len);
- if (nonWhitespacePos == -1) {
- if (leadingWS.length() == len &&
- sta.isClearWhitespaceLinesEnabled()) {
- // If the line was nothing but whitespace, select it
- // so its contents get removed.
- sta.setSelectionStart(start);
- sta.setSelectionEnd(end);
- }
- sta.replaceSelection(sb.toString());
- }
-
- // If there is non-whitespace between the caret and the
- // EOL, pressing Enter takes that text to the next line
- // and auto-indents it to the same place as the last
- // line.
- else {
- sb.append(s.substring(nonWhitespacePos));
- sta.replaceRange(sb.toString(), caretPos, end);
- sta.setCaretPosition(caretPos + leadingWS.length() + 1);
- }
-
- // Must do it after everything else, as the "smart indent"
- // calculation depends on the previous line's state
- // AFTER the Enter press (stuff may have been moved down).
- if (sta.getShouldIndentNextLine(lineNum)) {
- sta.replaceSelection("\t");
- }
-
- possiblyCloseCurlyBrace(sta, leadingWS);
-
- } catch (BadLocationException ble) { // Never happens
- sta.replaceSelection("\n");
- ble.printStackTrace();
- }
-
- }
-
- private void possiblyCloseCurlyBrace(RSyntaxTextArea textArea,
- String leadingWS) {
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
-
- if (textArea.getCloseCurlyBraces() &&
- doc.getCurlyBracesDenoteCodeBlocks()) {
-
- int line = textArea.getCaretLineNumber();
- Token t = doc.getTokenListForLine(line - 1);
- t = t.getLastNonCommentNonWhitespaceToken();
-
- if (t != null && t.isLeftCurly()) {
-
- if (getOpenBraceCount(doc) > 0) {
- StringBuffer sb = new StringBuffer();
- if (line == textArea.getLineCount() - 1) {
- sb.append('\n');
- }
- if (leadingWS != null) {
- sb.append(leadingWS);
- }
- sb.append("}\n");
- int dot = textArea.getCaretPosition();
- int end = textArea.getLineEndOffsetOfCurrentLine();
- // Insert at end of line, not at dot: they may have
- // pressed Enter in the middle of the line and brought
- // some text (though it must be whitespace and/or
- // comments) down onto the new line.
- textArea.insert(sb.toString(), end);
- textArea.setCaretPosition(dot); // Caret may have moved
- }
-
- }
-
- }
-
- }
-
- }
-
- /**
- * Action for inserting tabs. This is extended to "block indent" a group of contiguous lines if they are selected.
- */
- public static class InsertTabAction extends RecordableTextAction {
-
- private static final long serialVersionUID = 1L;
-
- public InsertTabAction() {
- super(insertTabAction);
- }
-
- public InsertTabAction(String name) {
- super(name);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- if (!textArea.isEditable() || !textArea.isEnabled()) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- return;
- }
-
- Document document = textArea.getDocument();
- Element map = document.getDefaultRootElement();
- Caret c = textArea.getCaret();
- int dot = c.getDot();
- int mark = c.getMark();
- int dotLine = map.getElementIndex(dot);
- int markLine = map.getElementIndex(mark);
-
- // If there is a multiline selection, indent all lines in
- // the selection.
- if (dotLine != markLine) {
- int first = Math.min(dotLine, markLine);
- int last = Math.max(dotLine, markLine);
- Element elem;
- int start;
- try {
- for (int i = first; i < last; i++) {
- elem = map.getElement(i);
- start = elem.getStartOffset();
- document.insertString(start, "\t", null);
- }
- // Don't do the last line if the caret is at its
- // beginning. We must call getDot() again and not just
- // use 'dot' as the caret's position may have changed
- // due to the insertion of the tabs above.
- elem = map.getElement(last);
- start = elem.getStartOffset();
- if (Math.max(c.getDot(), c.getMark()) != start) {
- document.insertString(start, "\t", null);
- }
- } catch (BadLocationException ble) { // Never happens.
- ble.printStackTrace();
- UIManager.getLookAndFeel().
- provideErrorFeedback(textArea);
- }
- }
- else {
- textArea.replaceSelection("\t");
- }
-
- }
-
- public final String getMacroID() {
- return insertTabAction;
- }
-
- }
-
- /**
- * Action to move the selection and/or caret. Constructor indicates direction to use. This class overrides the
- * behavior defined in {@link RTextAreaEditorKit} to better skip "words" in source code.
- */
- public static class NextWordAction
- extends RTextAreaEditorKit.NextWordAction {
-
- private Segment seg;
-
- public NextWordAction(String nm, boolean select) {
- super(nm, select);
- seg = new Segment();
- }
-
- /**
- * Overridden to do better with skipping "words" in code.
- */
- protected int getNextWord(RTextArea textArea, int offs)
- throws BadLocationException {
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- if (offs == doc.getLength()) {
- return offs;
- }
-
- int line = textArea.getLineOfOffset(offs);
- int end = textArea.getLineEndOffset(line);
- if (offs == end) {
- return offs + 1; // Start of next line.
- }
- doc.getText(offs, end - offs, seg);
-
- // Determine the "type" of char at offs - letter/digit,
- // whitespace or other
- char ch = seg.first();
-
- // Skip the group of letters and/or digits
- if (Character.isLetterOrDigit(ch)) {
- do {
- ch = seg.next();
- } while (Character.isLetterOrDigit(ch));
- }
-
- // Skip groups of "anything else" (operators, etc.).
- else if (!Character.isWhitespace(ch)) {
- do {
- ch = seg.next();
- } while (ch != Segment.DONE &&
- !(Character.isLetterOrDigit(ch) ||
- Character.isWhitespace(ch)));
- }
-
- // Skip any trailing whitespace
- while (Character.isWhitespace(ch)) {
- ch = seg.next();
- }
-
- offs += seg.getIndex() - seg.getBeginIndex();
- return offs;
-
- }
-
- }
-
- /**
- * Action for when the user tries to insert a template (that is, they've typed a template ID and pressed the trigger
- * character (a space) in an attempt to do the substitution).
- */
- public static class PossiblyInsertTemplateAction extends RecordableTextAction {
-
- private static final long serialVersionUID = 1L;
-
- public PossiblyInsertTemplateAction() {
- super(rstaPossiblyInsertTemplateAction);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- if (!textArea.isEditable() || !textArea.isEnabled())
- return;
-
- RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
-
- if (RSyntaxTextArea.getTemplatesEnabled()) {
-
- Document doc = textArea.getDocument();
- if (doc != null) {
-
- try {
-
- CodeTemplateManager manager = RSyntaxTextArea.
- getCodeTemplateManager();
- CodeTemplate template = manager == null ? null :
- manager.getTemplate(rsta);
-
- // A non-null template means modify the text to insert!
- if (template != null) {
- template.invoke(rsta);
- }
-
- // No template - insert default text. This is
- // exactly what DefaultKeyTypedAction does.
- else {
- doDefaultInsert(rsta);
- }
-
- } catch (BadLocationException ble) {
- UIManager.getLookAndFeel().
- provideErrorFeedback(textArea);
- }
-
- } // End of if (doc!=null).
-
- } // End of if (textArea.getTemplatesEnabled()).
-
- // If templates aren't enabled, just insert the text as usual.
- else {
- doDefaultInsert(rsta);
- }
-
- }
-
- private final void doDefaultInsert(RTextArea textArea) {
- // FIXME: We need a way to get the "trigger string" (i.e.,
- // the text that was just typed); however, the text area's
- // template manager might be null (if templates are disabled).
- // Also, the manager's trigger string doesn't yet match up with
- // that defined in RSyntaxTextAreaEditorKit.java (which is
- // hardcoded as a space)...
- // String str = manager.getInsertTriggerString();
- // int mod = manager.getInsertTrigger().getModifiers();
- // if (str!=null && str.length()>0 &&
- // ((mod&ActionEvent.ALT_MASK)==(mod&ActionEvent.CTRL_MASK))) {
- // char ch = str.charAt(0);
- // if (ch>=0x20 && ch!=0x7F)
- // textArea.replaceSelection(str);
- // }
- textArea.replaceSelection(" ");
- }
-
- public final String getMacroID() {
- return rstaPossiblyInsertTemplateAction;
- }
-
- }
-
- /**
- * Action to move the selection and/or caret. Constructor indicates direction to use. This class overrides the
- * behavior defined in {@link RTextAreaEditorKit} to better skip "words" in source code.
- */
- public static class PreviousWordAction
- extends RTextAreaEditorKit.PreviousWordAction {
-
- private Segment seg;
-
- public PreviousWordAction(String nm, boolean select) {
- super(nm, select);
- seg = new Segment();
- }
-
- /**
- * Overridden to do better with skipping "words" in code.
- */
- protected int getPreviousWord(RTextArea textArea, int offs)
- throws BadLocationException {
-
- if (offs == 0) {
- return offs;
- }
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- int line = textArea.getLineOfOffset(offs);
- int start = textArea.getLineStartOffset(line);
- if (offs == start) {
- return start - 1; // End of previous line.
- }
- doc.getText(start, offs - start, seg);
-
- // Determine the "type" of char at offs - lower case, upper case,
- // whitespace or other
- char ch = seg.last();
-
- // Skip any "leading" whitespace
- while (Character.isWhitespace(ch)) {
- ch = seg.previous();
- }
-
- // Skip the group of letters and/or digits
- if (Character.isLetterOrDigit(ch)) {
- do {
- ch = seg.previous();
- } while (Character.isLetterOrDigit(ch));
- }
-
- // Skip groups of "anything else" (operators, etc.).
- else if (!Character.isWhitespace(ch)) {
- do {
- ch = seg.previous();
- } while (ch != Segment.DONE &&
- !(Character.isLetterOrDigit(ch) ||
- Character.isWhitespace(ch)));
- }
-
- offs -= seg.getEndIndex() - seg.getIndex();
- if (ch != Segment.DONE) {
- offs++;
- }
-
- return offs;
-
- }
-
- }
-
- /**
- * Selects the word around the caret. This class is here to better handle selecting "words" in programming
- * languages.
- */
- public static class SelectWordAction
- extends RTextAreaEditorKit.SelectWordAction {
-
- protected void createActions() {
- start = new BeginWordAction("pigdog", false);
- end = new EndWordAction("pigdog", true);
- }
-
- }
-
- /**
- * Action that toggles whether the currently selected lines are commented.
- */
- public static class ToggleCommentAction extends RecordableTextAction {
-
- public ToggleCommentAction() {
- super(rstaToggleCommentAction);
- }
-
- public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
-
- if (!textArea.isEditable() || !textArea.isEnabled()) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- return;
- }
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- String[] startEnd = doc.getLineCommentStartAndEnd();
-
- if (startEnd == null) {
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- return;
- }
-
- Element map = doc.getDefaultRootElement();
- Caret c = textArea.getCaret();
- int dot = c.getDot();
- int mark = c.getMark();
- int line1 = map.getElementIndex(dot);
- int line2 = map.getElementIndex(mark);
- int start = Math.min(line1, line2);
- int end = Math.max(line1, line2);
-
- // Don't toggle comment on last line if there is no
- // text selected on it.
- if (start != end) {
- Element elem = map.getElement(end);
- if (Math.max(dot, mark) == elem.getStartOffset()) {
- end--;
- }
- }
-
- textArea.beginAtomicEdit();
- try {
- boolean add = getDoAdd(doc, map, start, end, startEnd);
- for (line1 = start; line1 <= end; line1++) {
- Element elem = map.getElement(line1);
- handleToggleComment(elem, doc, startEnd, add);
- }
- } catch (BadLocationException ble) {
- ble.printStackTrace();
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- } finally {
- textArea.endAtomicEdit();
- }
-
- }
-
- private boolean getDoAdd(Document doc, Element map, int startLine,
- int endLine, String[] startEnd)
- throws BadLocationException {
- boolean doAdd = false;
- for (int i = startLine; i <= endLine; i++) {
- Element elem = map.getElement(i);
- int start = elem.getStartOffset();
- String t = doc.getText(start, elem.getEndOffset() - start - 1);
- if (!t.startsWith(startEnd[0]) ||
- (startEnd[1] != null && !t.endsWith(startEnd[1]))) {
- doAdd = true;
- break;
- }
- }
- return doAdd;
- }
-
- private void handleToggleComment(Element elem, Document doc,
- String[] startEnd, boolean add) throws BadLocationException {
- int start = elem.getStartOffset();
- int end = elem.getEndOffset() - 1;
- if (add) {
- doc.insertString(start, startEnd[0], null);
- if (startEnd[1] != null) {
- doc.insertString(end + startEnd[0].length(), startEnd[1],
- null);
- }
- }
- else {
- doc.remove(start, startEnd[0].length());
- if (startEnd[1] != null) {
- int temp = startEnd[1].length();
- doc.remove(end - startEnd[0].length() - temp, temp);
- }
- }
- }
-
- public final String getMacroID() {
- return rstaToggleCommentAction;
- }
-
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = 1L;
+
+ public static final String rstaCloseCurlyBraceAction = "RSTA.CloseCurlyBraceAction";
+ public static final String rstaCloseMarkupTagAction = "RSTA.CloseMarkupTagAction";
+ public static final String rstaCollapseAllFoldsAction = "RSTA.CollapseAllFoldsAction";
+ public static final String rstaCollapseAllCommentFoldsAction = "RSTA.CollapseAllCommentFoldsAction";
+ public static final String rstaCollapseFoldAction = "RSTA.CollapseFoldAction";
+ public static final String rstaCopyAsStyledTextAction = "RSTA.CopyAsStyledTextAction";
+ public static final String rstaDecreaseIndentAction = "RSTA.DecreaseIndentAction";
+ public static final String rstaExpandAllFoldsAction = "RSTA.ExpandAllFoldsAction";
+ public static final String rstaExpandFoldAction = "RSTA.ExpandFoldAction";
+ public static final String rstaGoToMatchingBracketAction = "RSTA.GoToMatchingBracketAction";
+ public static final String rstaPossiblyInsertTemplateAction = "RSTA.TemplateAction";
+ public static final String rstaToggleCommentAction = "RSTA.ToggleCommentAction";
+ public static final String rstaToggleCurrentFoldAction = "RSTA.ToggleCurrentFoldAction";
+
+ private static final String MSG = "org.fife.ui.rsyntaxtextarea.RSyntaxTextArea";
+ private static final ResourceBundle msg = ResourceBundle.getBundle(MSG);
+
+
+ /**
+ * The actions that RSyntaxTextAreaEditorKit
adds to those of
+ * RTextAreaEditorKit
.
+ */
+ private static final Action[] defaultActions = {
+ new CloseCurlyBraceAction(),
+ new CloseMarkupTagAction(),
+ new BeginWordAction(beginWordAction, false),
+ new BeginWordAction(selectionBeginWordAction, true),
+ new ChangeFoldStateAction(rstaCollapseFoldAction, true),
+ new ChangeFoldStateAction(rstaExpandFoldAction, false),
+ new CollapseAllFoldsAction(),
+ new CopyAsStyledTextAction(),
+ //new DecreaseFontSizeAction(),
+ new DecreaseIndentAction(),
+ new DeletePrevWordAction(),
+ new DumbCompleteWordAction(),
+ new EndAction(endAction, false),
+ new EndAction(selectionEndAction, true),
+ new EndWordAction(endWordAction, false),
+ new EndWordAction(endWordAction, true),
+ new ExpandAllFoldsAction(),
+ new GoToMatchingBracketAction(),
+ new InsertBreakAction(),
+ //new IncreaseFontSizeAction(),
+ new InsertTabAction(),
+ new NextWordAction(nextWordAction, false),
+ new NextWordAction(selectionNextWordAction, true),
+ new PossiblyInsertTemplateAction(),
+ new PreviousWordAction(previousWordAction, false),
+ new PreviousWordAction(selectionPreviousWordAction, true),
+ new SelectWordAction(),
+ new ToggleCommentAction(),
+ };
+
+
+ /**
+ * Constructor.
+ */
+ public RSyntaxTextAreaEditorKit() {
+ }
+
+
+ /**
+ * Returns the default document used by RSyntaxTextArea
s.
+ *
+ * @return The document.
+ */
+ @Override
+ public Document createDefaultDocument() {
+ return new RSyntaxDocument(SyntaxConstants.SYNTAX_STYLE_NONE);
+ }
+
+
+ /**
+ * Overridden to return a row header that is aware of folding.
+ *
+ * @param textArea The text area.
+ * @return The icon row header.
+ */
+ @Override
+ public IconRowHeader createIconRowHeader(RTextArea textArea) {
+ return new FoldingAwareIconRowHeader((RSyntaxTextArea)textArea);
+ }
+
+
+ /**
+ * Fetches the set of commands that can be used
+ * on a text component that is using a model and
+ * view produced by this kit.
+ *
+ * @return the command list
+ */
+ @Override
+ public Action[] getActions() {
+ return TextAction.augmentList(super.getActions(),
+ RSyntaxTextAreaEditorKit.defaultActions);
+ }
+
+
+ /**
+ * Returns localized text for an action. There's definitely a better place
+ * for this functionality.
+ *
+ * @param key The key into the action resource bundle.
+ * @return The localized text.
+ */
+ public static String getString(String key) {
+ return msg.getString(key);
+ }
+
+
+ /**
+ * Positions the caret at the beginning of the word. This class is here
+ * to better handle finding the "beginning of the word" for programming
+ * languages.
+ */
+ protected static class BeginWordAction
+ extends RTextAreaEditorKit.BeginWordAction {
+
+ private Segment seg;
+
+ protected BeginWordAction(String name, boolean select) {
+ super(name, select);
+ seg = new Segment();
+ }
+
+ @Override
+ protected int getWordStart(RTextArea textArea, int offs)
+ throws BadLocationException {
+
+ if (offs==0) {
+ return offs;
+ }
+
+ RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
+ int line = textArea.getLineOfOffset(offs);
+ int start = textArea.getLineStartOffset(line);
+ if (offs==start) {
+ return start;
+ }
+ int end = textArea.getLineEndOffset(line);
+ if (line!=textArea.getLineCount()-1) {
+ end--;
+ }
+ doc.getText(start, end-start, seg);
+
+ // Determine the "type" of char at offs - lower case, upper case,
+ // whitespace or other. We take special care here as we're starting
+ // in the middle of the Segment to check whether we're already at
+ // the "beginning" of a word.
+ int firstIndex = seg.getBeginIndex() + (offs-start) - 1;
+ seg.setIndex(firstIndex);
+ char ch = seg.current();
+ char nextCh = offs==end ? 0 : seg.array[seg.getIndex() + 1];
+
+ // The "word" is a group of letters and/or digits
+ int languageIndex = 0; // TODO
+ if (doc.isIdentifierChar(languageIndex, ch)) {
+ if (offs!=end && !doc.isIdentifierChar(languageIndex, nextCh)) {
+ return offs;
+ }
+ do {
+ ch = seg.previous();
+ } while (doc.isIdentifierChar(languageIndex, ch) && ch != CharacterIterator.DONE);
+ }
+
+ // The "word" is whitespace
+ else if (Character.isWhitespace(ch)) {
+ if (offs!=end && !Character.isWhitespace(nextCh)) {
+ return offs;
+ }
+ do {
+ ch = seg.previous();
+ } while (Character.isWhitespace(ch));
+ }
+
+ // Otherwise, the "word" a single "something else" char (operator,
+ // etc.).
+
+ offs -= firstIndex - seg.getIndex() + 1;//seg.getEndIndex() - seg.getIndex();
+ if (ch!=Segment.DONE && nextCh!='\n') {
+ offs++;
+ }
+
+ return offs;
+
+ }
+
+ }
+
+
+ /**
+ * Expands or collapses the nearest fold.
+ */
+ public static class ChangeFoldStateAction extends FoldRelatedAction {
+
+ private boolean collapse;
+
+ public ChangeFoldStateAction(String name, boolean collapse) {
+ super(name);
+ this.collapse = collapse;
+ }
+
+ public ChangeFoldStateAction(String name, Icon icon,
+ String desc, Integer mnemonic, KeyStroke accelerator) {
+ super(name, icon, desc, mnemonic, accelerator);
+ }
+
+ @Override
+ public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
+ RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
+ if (rsta.isCodeFoldingEnabled()) {
+ Fold fold = getClosestFold(rsta);
+ if (fold!=null) {
+ fold.setCollapsed(collapse);
+ }
+ RSyntaxUtilities.possiblyRepaintGutter(textArea);
+ }
+ else {
+ UIManager.getLookAndFeel().provideErrorFeedback(rsta);
+ }
+ }
+
+ @Override
+ public final String getMacroID() {
+ return getName();
+ }
+
+ }
+
+
+ /**
+ * Action that (optionally) aligns a closing curly brace with the line
+ * containing its matching opening curly brace.
+ */
+ public static class CloseCurlyBraceAction extends RecordableTextAction {
+
+ private static final long serialVersionUID = 1L;
+
+ private Point bracketInfo;
+ private Segment seg;
+
+ public CloseCurlyBraceAction() {
+ super(rstaCloseCurlyBraceAction);
+ seg = new Segment();
+ }
+
+ @Override
+ public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
+
+ RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
+ RSyntaxDocument doc = (RSyntaxDocument)rsta.getDocument();
+
+ int languageIndex = 0;
+ int dot = textArea.getCaretPosition();
+ if (dot>0) {
+ Token t = RSyntaxUtilities.getTokenAtOffset(rsta, dot-1);
+ languageIndex = t==null ? 0 : t.getLanguageIndex();
+ }
+ boolean alignCurlyBraces = rsta.isAutoIndentEnabled() &&
+ doc.getCurlyBracesDenoteCodeBlocks(languageIndex);
+
+ if (alignCurlyBraces) {
+ textArea.beginAtomicEdit();
+ }
+
+ try {
+
+ textArea.replaceSelection("}");
+
+ // If the user wants to align curly braces...
+ if (alignCurlyBraces) {
+
+ Element root = doc.getDefaultRootElement();
+ dot = rsta.getCaretPosition() - 1; // Start before '}'
+ int line = root.getElementIndex(dot);
+ Element elem = root.getElement(line);
+ int start = elem.getStartOffset();
+
+ // Get the current line's text up to the '}' entered.
+ try {
+ doc.getText(start, dot-start, seg);
+ } catch (BadLocationException ble) { // Never happens
+ ble.printStackTrace();
+ return;
+ }
+
+ // Only attempt to align if there's only whitespace up to
+ // the '}' entered.
+ for (int i=0; i</
"
+ * or "[
" in the case of BBCode).
+ * @return The name of the tag to close, or null
if it
+ * could not be determined.
+ */
+ private String discoverTagName(RSyntaxDocument doc, int dot) {
+
+ Stack-1
if only
+ * whitespace chars follow pos
(or it is the end
+ * position in the string).
+ */
+ private static int atEndOfLine(int pos, String s, int sLen) {
+ for (int i=pos; iRSyntaxTextArea
. This allows us to implement syntax highlighting.
- *
+ * UI used by RSyntaxTextArea
. This allows us to implement
+ * syntax highlighting.
+ *
* @author Robert Futrell
* @version 0.1
*/
public class RSyntaxTextAreaUI extends RTextAreaUI {
- private static final String SHARED_ACTION_MAP_NAME = "RSyntaxTextAreaUI.actionMap";
- private static final String SHARED_INPUT_MAP_NAME = "RSyntaxTextAreaUI.inputMap";
- private static final EditorKit defaultKit = new RSyntaxTextAreaEditorKit();
+ private static final String SHARED_ACTION_MAP_NAME = "RSyntaxTextAreaUI.actionMap";
+ private static final String SHARED_INPUT_MAP_NAME = "RSyntaxTextAreaUI.inputMap";
+ private static final EditorKit DEFAULT_KIT = new RSyntaxTextAreaEditorKit();
- public static ComponentUI createUI(JComponent ta) {
- return new RSyntaxTextAreaUI(ta);
- }
- /**
- * Constructor.
- */
- public RSyntaxTextAreaUI(JComponent rSyntaxTextArea) {
- super(rSyntaxTextArea);
- }
+ public static ComponentUI createUI(JComponent ta) {
+ return new RSyntaxTextAreaUI(ta);
+ }
+
+
+ /**
+ * Constructor.
+ */
+ public RSyntaxTextAreaUI(JComponent rSyntaxTextArea) {
+ super(rSyntaxTextArea);
+ }
+
/**
* Creates the view for an element.
- *
- * @param elem
- * The element.
+ *
+ * @param elem The element.
* @return The view.
*/
- public View create(Element elem) {
- RTextArea c = getRTextArea();
- if (c instanceof RSyntaxTextArea) {
- RSyntaxTextArea area = (RSyntaxTextArea) c;
- View v;
- if (area.getLineWrap())
- v = new WrappedSyntaxView(elem);
- else
- v = new SyntaxView(elem);
- return v;
- }
- return null;
- }
+ @Override
+ public View create(Element elem) {
+ RTextArea c = getRTextArea();
+ if (c instanceof RSyntaxTextArea) {
+ RSyntaxTextArea area = (RSyntaxTextArea) c;
+ View v;
+ if (area.getLineWrap()) {
+ v = new WrappedSyntaxView(elem);
+ }
+ else {
+ v = new SyntaxView(elem);
+ }
+ return v;
+ }
+ return null;
+ }
- /**
- * Creates the highlighter to use for syntax text areas.
- *
- * @return The highlighter.
- */
- protected Highlighter createHighlighter() {
- return new RSyntaxTextAreaHighlighter();
- }
- /**
- * Returns the name to use to cache/fetch the shared action map. This should be overridden by subclasses if the
- * subclass has its own custom editor kit to install, so its actions get picked up.
- *
- * @return The name of the cached action map.
- */
- protected String getActionMapName() {
- return SHARED_ACTION_MAP_NAME;
- }
+ /**
+ * Creates the highlighter to use for syntax text areas.
+ *
+ * @return The highlighter.
+ */
+ @Override
+ protected Highlighter createHighlighter() {
+ return new RSyntaxTextAreaHighlighter();
+ }
- /**
- * Fetches the EditorKit for the UI.
- *
- * @param tc
- * The text component for which this UI is installed.
- * @return The editor capabilities.
- * @see javax.swing.plaf.TextUI#getEditorKit
- */
- public EditorKit getEditorKit(JTextComponent tc) {
- return defaultKit;
- }
- /**
- * Get the InputMap to use for the UI.
- * getInputMap()
because there is a package-private method in
- * BasicTextAreaUI
with that name. Thus, creating a new method with that name causes certain compilers
- * to issue warnings that you are not actually overriding the original method (since it is package-private).
- */
- protected InputMap getRTextAreaInputMap() {
- InputMap map = new InputMapUIResource();
- InputMap shared = (InputMap) UIManager.get(SHARED_INPUT_MAP_NAME);
- if (shared == null) {
- shared = new RSyntaxTextAreaDefaultInputMap();
- UIManager.put(SHARED_INPUT_MAP_NAME, shared);
- }
- // KeyStroke[] keys = shared.allKeys();
- // for (int i=0; iRSyntaxTextArea
.
- *
- * @param e
- * The property change event.
- */
- protected void propertyChange(PropertyChangeEvent e) {
- String name = e.getPropertyName();
+ /**
+ * Get the InputMap to use for the UI.getInputMap()
because there is
+ * a package-private method in BasicTextAreaUI
with that name.
+ * Thus, creating a new method with that name causes certain compilers to
+ * issue warnings that you are not actually overriding the original method
+ * (since it is package-private).
+ */
+ @Override
+ protected InputMap getRTextAreaInputMap() {
+ InputMap map = new InputMapUIResource();
+ InputMap shared = (InputMap)UIManager.get(SHARED_INPUT_MAP_NAME);
+ if (shared==null) {
+ shared = new RSyntaxTextAreaDefaultInputMap();
+ UIManager.put(SHARED_INPUT_MAP_NAME, shared);
+ }
+ //KeyStroke[] keys = shared.allKeys();
+ //for (int i=0; iRSyntaxTextArea
changes its syntax
- * editing style.
- */
- public void refreshSyntaxHighlighting() {
- modelChanged();
- }
+ /**
+ * Paints the "matched bracket", if any.
+ *
+ * @param g The graphics context.
+ */
+ protected void paintMatchedBracket(Graphics g) {
+ RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
+ if (rsta.isBracketMatchingEnabled()) {
+ Rectangle match = rsta.getMatchRectangle();
+ if (match!=null) {
+ paintMatchedBracketImpl(g, rsta, match);
+ }
+ if (rsta.getPaintMatchedBracketPair()) {
+ Rectangle dotRect = rsta.getDotRectangle();
+ if (dotRect!=null) { // should always be true
+ paintMatchedBracketImpl(g, rsta, dotRect);
+ }
+ }
+ }
+ }
- /**
- * Returns the y-coordinate of the line containing a specified offset.
- * modelToView(offs).y
, so it is preferred if you do not need the actual
- * bounding box.
- *
- * @param offs
- * The offset info the document.
- * @return The y-coordinate of the top of the offset, or -1
if this text area doesn't yet have a
- * positive size.
- * @throws BadLocationException
- * If offs
isn't a valid offset into the document.
- */
- public int yForLineContaining(int offs) throws BadLocationException {
- Rectangle alloc = getVisibleEditorRect();
- if (alloc != null) {
- RSTAView view = (RSTAView) getRootView(textArea).getView(0);
- return view.yForLineContaining(alloc, offs);
- }
- return -1;
- }
-
-}
\ No newline at end of file
+
+ protected void paintMatchedBracketImpl(Graphics g, RSyntaxTextArea rsta,
+ Rectangle r) {
+ // We must add "-1" to the height because otherwise we'll paint below
+ // the region that gets invalidated.
+ if (rsta.getAnimateBracketMatching()) {
+ Color bg = rsta.getMatchedBracketBGColor();
+ final int arcWH = 5;
+ if (bg!=null) {
+ g.setColor(bg);
+ g.fillRoundRect(r.x,r.y, r.width,r.height-1, arcWH, arcWH);
+ }
+ g.setColor(rsta.getMatchedBracketBorderColor());
+ g.drawRoundRect(r.x,r.y, r.width,r.height-1, arcWH, arcWH);
+ }
+ else {
+ Color bg = rsta.getMatchedBracketBGColor();
+ if (bg!=null) {
+ g.setColor(bg);
+ g.fillRect(r.x,r.y, r.width,r.height-1);
+ }
+ g.setColor(rsta.getMatchedBracketBorderColor());
+ g.drawRect(r.x,r.y, r.width,r.height-1);
+ }
+ }
+
+
+ /**
+ * Gets called whenever a bound property is changed on this UI's
+ * RSyntaxTextArea
.
+ *
+ * @param e The property change event.
+ */
+ @Override
+ protected void propertyChange(PropertyChangeEvent e) {
+
+ String name = e.getPropertyName();
+
+ // If they change the syntax scheme, we must do this so that
+ // WrappedSyntaxView(_TEST) updates its child views properly.
+ if (name.equals(RSyntaxTextArea.SYNTAX_SCHEME_PROPERTY)) {
+ modelChanged();
+ }
+
+ // Everything else is general to all RTextAreas.
+ else {
+ super.propertyChange(e);
+ }
+
+ }
+
+
+ /**
+ * Updates the view. This should be called when the underlying
+ * RSyntaxTextArea
changes its syntax editing style.
+ */
+ public void refreshSyntaxHighlighting() {
+ modelChanged();
+ }
+
+
+ /**
+ * Returns the y-coordinate of the specified line.modelToView(int)
calls, as the entire bounding box isn't
+ * computed.
+ */
+ @Override
+ public int yForLine(int line) throws BadLocationException {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc!=null) {
+ RSTAView view = (RSTAView)getRootView(textArea).getView(0);
+ return view.yForLine(alloc, line);
+ }
+ return -1;
+ }
+
+
+ /**
+ * Returns the y-coordinate of the line containing a specified offset.modelToView(offs).y
, so it is
+ * preferred if you do not need the actual bounding box.
+ */
+ @Override
+ public int yForLineContaining(int offs) throws BadLocationException {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc!=null) {
+ RSTAView view = (RSTAView)getRootView(textArea).getView(0);
+ return view.yForLineContaining(alloc, offs);
+ }
+ return -1;
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java
old mode 100644
new mode 100755
index 2d62ad6bd..ef2f5a8d9
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java
@@ -3,30 +3,28 @@
*
* RSyntaxUtilities.java - Utility methods used by RSyntaxTextArea and its
* views.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
-import javax.swing.*;
+import java.awt.Toolkit;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javax.swing.JLabel;
+import javax.swing.JTextArea;
+import javax.swing.JViewport;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.Document;
@@ -36,908 +34,1533 @@
import javax.swing.text.TabExpander;
import javax.swing.text.View;
+import org.fife.ui.rsyntaxtextarea.TokenUtils.TokenSubList;
+import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
+import org.fife.ui.rtextarea.Gutter;
+import org.fife.ui.rtextarea.RTextArea;
+import org.fife.ui.rtextarea.RTextScrollPane;
+
+
/**
- * Utility methods used by RSyntaxTextArea
and its associated classes.
- *
+ * Utility methods used by RSyntaxTextArea
and its associated
+ * classes.
+ *
* @author Robert Futrell
* @version 0.2
*/
-public class RSyntaxUtilities implements SwingConstants {
-
- // private static final int DIGIT_MASK = 1;
- private static final int LETTER_MASK = 2;
- // private static final int WHITESPACE_MASK = 4;
- // private static final int UPPER_CASE_MASK = 8;
- private static final int HEX_CHARACTER_MASK = 16;
- private static final int LETTER_OR_DIGIT_MASK = 32;
- private static final int BRACKET_MASK = 64;
- private static final int JAVA_OPERATOR_MASK = 128;
-
- /**
- * A lookup table used to quickly decide if a 16-bit Java char is a US-ASCII letter (A-Z or a-z), a digit, a
- * whitespace char (either space (0x0020) or tab (0x0009)), etc. This method should be faster than
- * Character.isLetter
, Character.isDigit
, and Character.isWhitespace
because
- * we know we are dealing with ASCII chars and so don't have to worry about code planes, etc.
- */
- private static final int[] dataTable = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, // 0-15
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
- 4, 128, 0, 0, 0, 128, 128, 0, 64, 64, 128, 128, 0, 128, 0, 128, // 32-47
- 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 128, 0, 128, 128, 128, 128, // 48-63
- 0, 58, 58, 58, 58, 58, 58, 42, 42, 42, 42, 42, 42, 42, 42, 42, // 64-79
- 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 0, 64, 128, 0, // 80-95
- 0, 50, 50, 50, 50, 50, 50, 34, 34, 34, 34, 34, 34, 34, 34, 34, // 96-111
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 64, 128, 64, 128, 0, // 112-127
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240-255.
- };
-
- /**
- * Returns the leading whitespace of a string.
- *
- * @param text
- * The String to check.
- * @return The leading whitespace.
- */
- public static String getLeadingWhitespace(String text) {
- int count = 0;
- int len = text.length();
- while (count < len && RSyntaxUtilities.isWhitespace(text.charAt(count))) {
- count++;
- }
- return text.substring(0, count);
- }
-
- private static final Element getLineElem(Document d, int offs) {
- Element map = d.getDefaultRootElement();
- int index = map.getElementIndex(offs);
- Element elem = map.getElement(index);
- if ((offs >= elem.getStartOffset()) && (offs < elem.getEndOffset())) {
- return elem;
- }
- return null;
- }
-
- /**
- * Returns the bounding box (in the current view) of a specified position in the model. This method is designed for
- * line-wrapped views to use, as it allows you to specify a "starting position" in the line, from which the x-value
- * is assumed to be zero. The idea is that you specify the first character in a physical line as p0
, as
- * this is the character where the x-pixel value is 0.
- *
- * @param textArea
- * The text area containing the text.
- * @param s
- * A segment in which to load the line. This is passed in so we don't have to reallocate a new
- * Segment
for each call.
- * @param p0
- * The starting position in the physical line in the document.
- * @param p1
- * The position for which to get the bounding box in the view.
- * @param e
- * How to expand tabs.
- * @param rect
- * The rectangle whose x- and width-values are changed to represent the bounding box of p1
.
- * This is reused to keep from needlessly reallocating Rectangles.
- * @param x0
- * The x-coordinate (pixel) marking the left-hand border of the text. This is useful if the text area has
- * a border, for example.
- * @return The bounding box in the view of the character p1
.
- * @throws BadLocationException
- * If p0
or p1
is not a valid location in the specified text area's document.
- * @throws IllegalArgumentException
- * If p0
and p1
are not on the same line.
- */
- public static Rectangle getLineWidthUpTo(RSyntaxTextArea textArea,
- Segment s, int p0, int p1,
- TabExpander e, Rectangle rect,
- int x0)
- throws BadLocationException {
-
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
-
- // Ensure p0 and p1 are valid document positions.
- if (p0 < 0)
- throw new BadLocationException("Invalid document position", p0);
- else if (p1 > doc.getLength())
- throw new BadLocationException("Invalid document position", p1);
-
- // Ensure p0 and p1 are in the same line, and get the start/end
- // offsets for that line.
- Element map = doc.getDefaultRootElement();
- int lineNum = map.getElementIndex(p0);
- // We do ">1" because p1 might be the first position on the next line
- // or the last position on the previous one.
- // if (lineNum!=map.getElementIndex(p1))
- if (Math.abs(lineNum - map.getElementIndex(p1)) > 1)
- throw new IllegalArgumentException("p0 and p1 are not on the " +
- "same line (" + p0 + ", " + p1 + ").");
-
- // Get the token list.
- Token t = doc.getTokenListForLine(lineNum);
-
- // Modify the token list 't' to begin at p0 (but still have correct
- // token types, etc.), and get the x-location (in pixels) of the
- // beginning of this new token list.
- makeTokenListStartAt(t, p0, e, textArea, 0);
-
- rect = t.listOffsetToView(textArea, e, p1, x0, rect);
- return rect;
-
- }
-
- /**
- * Returns the location of the bracket paired with the one at the current caret position.
- *
- * @param textArea
- * The text area.
- * @return The location of the matching bracket in the document, or -1
if there isn't a matching
- * bracket (or the caret isn't on a bracket).
- */
- private static Segment charSegment = new Segment();
-
- public static int getMatchingBracketPosition(RSyntaxTextArea textArea) {
-
- try {
-
- // Actually position just BEFORE caret.
- int caretPosition = textArea.getCaretPosition() - 1;
- if (caretPosition > -1) {
-
- // Some variables that will be used later.
- Token token;
- Element map;
- int curLine;
- Element line;
- int start, end;
- RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
- char bracket = doc.charAt(caretPosition);
-
- // First, see if the previous char was a bracket
- // ('{', '}', '(', ')', '[', ']').
- // If it was, then make sure this bracket isn't sitting in
- // the middle of a comment or string. If it isn't, then
- // initialize some stuff so we can continue on.
- char bracketMatch;
- boolean goForward;
- switch (bracket) {
-
- case '{':
- case '(':
- case '[':
-
- // Ensure this bracket isn't in a comment.
- map = doc.getDefaultRootElement();
- curLine = map.getElementIndex(caretPosition);
- line = map.getElement(curLine);
- start = line.getStartOffset();
- end = line.getEndOffset();
- token = doc.getTokenListForLine(curLine);
- token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition);
- // All brackets are always returned as "separators."
- if (token.type != Token.SEPARATOR) {
- return -1;
- }
- bracketMatch = bracket == '{' ? '}' : (bracket == '(' ? ')' : ']');
- goForward = true;
- break;
-
- case '}':
- case ')':
- case ']':
-
- // Ensure this bracket isn't in a comment.
- map = doc.getDefaultRootElement();
- curLine = map.getElementIndex(caretPosition);
- line = map.getElement(curLine);
- start = line.getStartOffset();
- end = line.getEndOffset();
- token = doc.getTokenListForLine(curLine);
- token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition);
- // All brackets are always returned as "separators."
- if (token.type != Token.SEPARATOR) {
- return -1;
- }
- bracketMatch = bracket == '}' ? '{' : (bracket == ')' ? '(' : '[');
- goForward = false;
- break;
-
- default:
- return -1;
-
- }
-
- if (goForward) {
-
- int lastLine = map.getElementCount();
-
- // Start just after the found bracket since we're sure
- // we're not in a comment.
- start = caretPosition + 1;
- int numEmbedded = 0;
- boolean haveTokenList = false;
-
- while (true) {
-
- doc.getText(start, end - start, charSegment);
- int segOffset = charSegment.offset;
-
- for (int i = segOffset; i < segOffset + charSegment.count; i++) {
-
- char ch = charSegment.array[i];
-
- if (ch == bracket) {
- if (haveTokenList == false) {
- token = doc.getTokenListForLine(curLine);
- haveTokenList = true;
- }
- int offset = start + (i - segOffset);
- token = RSyntaxUtilities.getTokenAtOffset(token, offset);
- if (token.type == Token.SEPARATOR)
- numEmbedded++;
- }
-
- else if (ch == bracketMatch) {
- if (haveTokenList == false) {
- token = doc.getTokenListForLine(curLine);
- haveTokenList = true;
- }
- int offset = start + (i - segOffset);
- token = RSyntaxUtilities.getTokenAtOffset(token, offset);
- if (token.type == Token.SEPARATOR) {
- if (numEmbedded == 0)
- return offset;
- numEmbedded--;
- }
- }
-
- } // End of for (int i=segOffset; ijavax.swing.text.View
is an instance of
- * {@link TokenOrientedView} and javax.swing.text.TabExpander
; otherwise, a
- * ClassCastException
could be thrown.
- *
- * @param pos
- * the position to convert >= 0
- * @param a
- * the allocated region in which to render
- * @param direction
- * the direction from the current position that can be thought of as the arrow keys typically found on a
- * keyboard. This will be one of the following values:
- *
- *
- * @return the location within the model that best represents the next location visual position
- * @exception BadLocationException
- * @exception IllegalArgumentException
- * if direction
doesn't have one of the legal values above
- */
- public static int getNextVisualPositionFrom(int pos, Position.Bias b,
- Shape a, int direction,
- Position.Bias[] biasRet, View view)
- throws BadLocationException {
-
- biasRet[0] = Position.Bias.Forward;
-
- // Do we want the "next position" above, below, to the left or right?
- switch (direction) {
-
- case NORTH:
- case SOUTH:
- if (pos == -1) {
- pos = (direction == NORTH) ?
- Math.max(0, view.getEndOffset() - 1) :
- view.getStartOffset();
- break;
- }
- RSyntaxTextArea target = (RSyntaxTextArea) view.
- getContainer();
- Caret c = (target != null) ? target.getCaret() : null;
- // YECK! Ideally, the x location from the magic caret
- // position would be passed in.
- Point mcp;
- if (c != null)
- mcp = c.getMagicCaretPosition();
- else
- mcp = null;
- int x;
- if (mcp == null) {
- Rectangle loc = target.modelToView(pos);
- x = (loc == null) ? 0 : loc.x;
- }
- else {
- x = mcp.x;
- }
- if (direction == NORTH)
- pos = getPositionAbove(target, pos, x, (TabExpander) view);
- else
- pos = getPositionBelow(target, pos, x, (TabExpander) view);
- break;
-
- case WEST:
- if (pos == -1)
- pos = Math.max(0, view.getEndOffset() - 1);
- else
- pos = Math.max(0, pos - 1);
- break;
-
- case EAST:
- if (pos == -1)
- pos = view.getStartOffset();
- else
- pos = Math.min(pos + 1, view.getDocument().
- getLength());
- break;
-
- default:
- throw new IllegalArgumentException(
- "Bad direction: " + direction);
- }
-
- return pos;
-
- }
-
- /**
- * Determines the position in the model that is closest to the given view location in the row above. The component
- * given must have a size to compute the result. If the component doesn't have a size a value of -1 will be
- * returned.
- *
- * @param c
- * the editor
- * @param offs
- * the offset in the document >= 0
- * @param x
- * the X coordinate >= 0
- * @return the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
- * @exception BadLocationException
- * if the offset is out of range
- */
- public static final int getPositionAbove(RSyntaxTextArea c, int offs,
- float x, TabExpander e) throws BadLocationException {
-
- TokenOrientedView tov = (TokenOrientedView) e;
- Token token = tov.getTokenListForPhysicalLineAbove(offs);
- if (token == null)
- return -1;
-
- // A line containing only Token.NULL is an empty line.
- else if (token.type == Token.NULL) {
- int line = c.getLineOfOffset(offs); // Sure to be >0 ??
- return c.getLineStartOffset(line - 1);
- }
-
- else {
- return token.getListOffset(c, e, 0, x);
- }
-
- }
-
- /**
- * Determines the position in the model that is closest to the given view location in the row below. The component
- * given must have a size to compute the result. If the component doesn't have a size a value of -1 will be
- * returned.
- *
- * @param c
- * the editor
- * @param offs
- * the offset in the document >= 0
- * @param x
- * the X coordinate >= 0
- * @return the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
- * @exception BadLocationException
- * if the offset is out of range
- */
- public static final int getPositionBelow(RSyntaxTextArea c, int offs,
- float x, TabExpander e) throws BadLocationException {
-
- TokenOrientedView tov = (TokenOrientedView) e;
- Token token = tov.getTokenListForPhysicalLineBelow(offs);
- if (token == null)
- return -1;
-
- // A line containing only Token.NULL is an empty line.
- else if (token.type == Token.NULL) {
- int line = c.getLineOfOffset(offs); // Sure to be > c.getLineCount()-1 ??
- return c.getLineStartOffset(line + 1);
- }
-
- else {
- return token.getListOffset(c, e, 0, x);
- }
-
- }
-
- /**
- * Returns the token at the specified index, or null
if the given offset isn't in this token list's
- * range.
- * Note that this method does NOT check to see if tokenList
is null; callers should check for
- * themselves.
- *
- * @param tokenList
- * The list of tokens in which to search.
- * @param offset
- * The offset at which to get the token.
- * @return The token at offset
, or null
if none of the tokens are at that offset.
- */
- public static final Token getTokenAtOffset(Token tokenList, int offset) {
- for (Token t = tokenList; t != null; t = t.getNextToken()) {
- if (t.containsPosition(offset))
- return t;
- }
- return null;
- }
-
- /**
- * Returns the end of the word at the given offset.
- *
- * @param textArea
- * The text area.
- * @param offs
- * The offset into the text area's content.
- * @return The end offset of the word.
- * @throws BadLocationException
- * If offs
is invalid.
- * @see #getWordStart(RSyntaxTextArea, int)
- */
- public static int getWordEnd(RSyntaxTextArea textArea, int offs)
- throws BadLocationException {
-
- Document doc = textArea.getDocument();
- int endOffs = textArea.getLineEndOffsetOfCurrentLine();
- int lineEnd = Math.min(endOffs, doc.getLength());
- if (offs == lineEnd) { // End of the line.
- return offs;
- }
-
- String s = doc.getText(offs, lineEnd - offs - 1);
- if (s != null && s.length() > 0) { // Should always be true
- int i = 0;
- int count = s.length();
- char ch = s.charAt(i);
- if (Character.isWhitespace(ch)) {
- while (i < count && Character.isWhitespace(s.charAt(i++)))
- ;
- }
- else if (Character.isLetterOrDigit(ch)) {
- while (i < count && Character.isLetterOrDigit(s.charAt(i++)))
- ;
- }
- else {
- i = 2;
- }
- offs += i - 1;
- }
-
- return offs;
-
- }
-
- /**
- * Returns the start of the word at the given offset.
- *
- * @param textArea
- * The text area.
- * @param offs
- * The offset into the text area's content.
- * @return The start offset of the word.
- * @throws BadLocationException
- * If offs
is invalid.
- * @see #getWordEnd(RSyntaxTextArea, int)
- */
- public static int getWordStart(RSyntaxTextArea textArea, int offs)
- throws BadLocationException {
-
- Document doc = textArea.getDocument();
- Element line = getLineElem(doc, offs);
- if (line == null) {
- throw new BadLocationException("No word at " + offs, offs);
- }
-
- int lineStart = line.getStartOffset();
- if (offs == lineStart) { // Start of the line.
- return offs;
- }
-
- int endOffs = Math.min(offs + 1, doc.getLength());
- String s = doc.getText(lineStart, endOffs - lineStart);
- if (s != null && s.length() > 0) {
- int i = s.length() - 1;
- char ch = s.charAt(i);
- if (Character.isWhitespace(ch)) {
- while (i > 0 && Character.isWhitespace(s.charAt(i - 1))) {
- i--;
- }
- offs = lineStart + i;
- }
- else if (Character.isLetterOrDigit(ch)) {
- while (i > 0 && Character.isLetterOrDigit(s.charAt(i - 1))) {
- i--;
- }
- offs = lineStart + i;
- }
-
- }
-
- return offs;
-
- }
-
- /**
- * Determines the width of the given token list taking tabs into consideration. This is implemented in a 1.1 style
- * coordinate system where ints are used and 72dpi is assumed.
- * 0
in the view (for tab
- * purposes).
- *
- * @param tokenList
- * The tokenList list representing the text.
- * @param textArea
- * The text area in which this token list resides.
- * @param e
- * The tab expander. This value cannot be null
.
- * @return The width of the token list, in pixels.
- */
- public static final float getTokenListWidth(Token tokenList,
- RSyntaxTextArea textArea,
- TabExpander e) {
- return getTokenListWidth(tokenList, textArea, e, 0);
- }
-
- /**
- * Determines the width of the given token list taking tabs into consideration. This is implemented in a 1.1 style
- * coordinate system where ints are used and 72dpi is assumed.
- * null
.
- * @param x0
- * The x-pixel coordinate of the start of the token list.
- * @return The width of the token list, in pixels.
- * @see #getTokenListWidthUpTo
- */
- public static final float getTokenListWidth(final Token tokenList,
- RSyntaxTextArea textArea,
- TabExpander e, float x0) {
- float width = x0;
- for (Token t = tokenList; t != null && t.isPaintable(); t = t.getNextToken()) {
- width += t.getWidth(textArea, e, width);
- }
- return width - x0;
- }
-
- /**
- * Determines the width of the given token list taking tabs into consideration and only up to the given index in the
- * document (exclusive).
- *
- * @param tokenList
- * The token list representing the text.
- * @param textArea
- * The text area in which this token list resides.
- * @param e
- * The tab expander. This value cannot be null
.
- * @param x0
- * The x-pixel coordinate of the start of the token list.
- * @param upTo
- * The document position at which you want to stop, exclusive. If this position is before the starting
- * position of the token list, a width of 0
will be returned; similarly, if this position
- * comes after the entire token list, the width of the entire token list is returned.
- * @return The width of the token list, in pixels, up to, but not including, the character at position
- * upTo
.
- * @see #getTokenListWidth
- */
- public static final float getTokenListWidthUpTo(final Token tokenList,
- RSyntaxTextArea textArea, TabExpander e,
- float x0, int upTo) {
- float width = 0;
- for (Token t = tokenList; t != null && t.isPaintable(); t = t.getNextToken()) {
- if (t.containsPosition(upTo)) {
- return width + t.getWidthUpTo(upTo - t.offset, textArea, e,
- x0 + width);
- }
- width += t.getWidth(textArea, e, x0 + width);
- }
- return width;
- }
-
- /**
- * Returns whether or not this character is a "bracket" to be matched by such programming languages as C, C++, and
- * Java.
- *
- * @param ch
- * The character to check.
- * @return Whether or not the character is a "bracket" - one of '(', ')', '[', ']', '{', and '}'.
- */
- public static final boolean isBracket(char ch) {
- // We need the first condition as it might be that ch>255, and thus
- // not in our table. '}' is the highest-valued char in the bracket
- // set.
- return ch <= '}' && (dataTable[ch] & BRACKET_MASK) > 0;
- }
-
- /**
- * Returns whether or not a character is a digit (0-9).
- *
- * @param ch
- * The character to check.
- * @return Whether or not the character is a digit.
- */
- public static final boolean isDigit(char ch) {
- // We do it this way as we'd need to do two conditions anyway (first
- // to check that ch<255 so it can index into our table, then whether
- // that table position has the digit mask).
- return ch >= '0' && ch <= '9';
- }
-
- /**
- * Returns whether or not this character is a hex character. This method accepts both upper- and lower-case letters
- * a-f.
- *
- * @param ch
- * The character to check.
- * @return Whether or not the character is a hex character 0-9, a-f, or A-F.
- */
- public static final boolean isHexCharacter(char ch) {
- // We need the first condition as it could be that ch>255 (and thus
- // not a valid index into our table). 'f' is the highest-valued
- // char that is a valid hex character.
- return (ch <= 'f') && (dataTable[ch] & HEX_CHARACTER_MASK) > 0;
- }
-
- /**
- * Returns whether a character is a Java operator. Note that C and C++ operators are the same as Java operators.
- *
- * @param ch
- * The character to check.
- * @return Whether or not the character is a Java operator.
- */
- public static final boolean isJavaOperator(char ch) {
- // We need the first condition as it could be that ch>255 (and thus
- // not a valid index into our table). '~' is the highest-valued
- // char that is a valid Java operator.
- return (ch <= '~') && (dataTable[ch] & JAVA_OPERATOR_MASK) > 0;
- }
-
- /**
- * Returns whether a character is a US-ASCII letter (A-Z or a-z).
- *
- * @param ch
- * The character to check.
- * @return Whether or not the character is a US-ASCII letter.
- */
- public static final boolean isLetter(char ch) {
- // We need the first condition as it could be that ch>255 (and thus
- // not a valid index into our table).
- return (ch <= 'z') && (dataTable[ch] & LETTER_MASK) > 0;
- }
-
- /**
- * Returns whether or not a character is a US-ASCII letter or a digit.
- *
- * @param ch
- * The character to check.
- * @return Whether or not the character is a US-ASCII letter or a digit.
- */
- public static final boolean isLetterOrDigit(char ch) {
- // We need the first condition as it could be that ch>255 (and thus
- // not a valid index into our table).
- return (ch <= 'z') && (dataTable[ch] & LETTER_OR_DIGIT_MASK) > 0;
- }
-
- /**
- * Returns whether or not a character is a whitespace character (either a space ' ' or tab '\t'). This checks for
- * the Unicode character values 0x0020 and 0x0009.
- *
- * @param ch
- * The character to check.
- * @return Whether or not the character is a whitespace character.
- */
- public static final boolean isWhitespace(char ch) {
- // We do it this way as we'd need to do two conditions anyway (first
- // to check that ch<255 so it can index into our table, then whether
- // that table position has the whitespace mask).
- return ch == ' ' || ch == '\t';
- }
-
- /**
- * Modifies the passed-in token list to start at the specified offset. For example, if the token list covered
- * positions 20-60 in the document (inclusive) like so:
- *
- *
- * [token1] -> [token2] -> [token3] -> [token4]
- * 20 30 31 40 41 50 51 60
- *
- *
- * and you used this method to make the token list start at position 44, then the token list would be modified to be
- * the following:
- *
- *
- * [part-of-old-token3] -> [token4]
- * 44 50 51 60
- *
- *
- * Tokens that come before the specified position are forever lost, and the token containing that position is made
- * to begin at that position if necessary. All token types remain the same as they were originally.
- * null
or the unpaintable token(s) at the end of
- * the passed-in token list.
- * @param e
- * How to expand tabs.
- * @param textArea
- * The text area from which the token list came.
- * @param x0
- * The initial x-pixel position of the old token list.
- * @return The width, in pixels, of the part of the token list "removed from the
- * front." This way, you know the x-offset of the "new" token list.
- */
- public static float makeTokenListStartAt(Token tokenList, int pos,
- TabExpander e,
- final RSyntaxTextArea textArea,
- float x0) {
-
- Token t = tokenList;
-
- // Loop through the token list until you find the one that contains
- // pos. Remember the cumulative width of all of these tokens.
- while (t != null && t.isPaintable() && !t.containsPosition(pos)) {
- x0 += t.getWidth(textArea, e, x0);
- t = t.getNextToken();
- }
-
- // Make the token that contains pos start at pos.
- if (t != null && t.isPaintable() && t.offset != pos) {
- // Number of chars between p0 and token start.
- int difference = pos - t.offset;
- x0 += t.getWidthUpTo(t.textCount - difference + 1, textArea, e, x0);
- t.makeStartAt(pos);
- }
-
- // Make the passed-in token list point to the proper place.
- // t can be null, for example, if line ends with unended MLC.
- if (t != null && t.isPaintable())
- tokenList.copyFrom(t);
- else
- tokenList = null;
- t = null;
-
- // Return the x-offset (in pixels) of the newly-modified t.
- return x0;
-
- }
-
- /**
- * If the character is an upper-case US-ASCII letter, it returns the lower-case version of that letter; otherwise,
- * it just returns the character.
- *
- * @param ch
- * The character to lower-case (if it is a US-ASCII upper-case character).
- * @return The lower-case version of the character.
- */
- public static final char toLowerCase(char ch) {
- // We can logical OR with 32 because A-Z are 65-90 in the ASCII table
- // and none of them have the 6th bit (32) set, and a-z are 97-122 in
- // the ASCII table, which is 32 over from A-Z.
- // We do it this way as we'd need to do two conditions anyway (first
- // to check that ch<255 so it can index into our table, then whether
- // that table position has the upper-case mask).
- if (ch >= 'A' && ch <= 'Z')
- return (char) (ch | 0x20);
- return ch;
- }
-
-}
\ No newline at end of file
+public final class RSyntaxUtilities implements SwingConstants {
+
+ /**
+ * Integer constant representing a Windows-variant OS.
+ */
+ public static final int OS_WINDOWS = 1;
+
+ /**
+ * Integer constant representing Mac OS X.
+ */
+ public static final int OS_MAC_OSX = 2;
+
+ /**
+ * Integer constant representing Linux.
+ */
+ public static final int OS_LINUX = 4;
+
+ /**
+ * Integer constant representing an "unknown" OS. 99.99% of the
+ * time, this means some UNIX variant (AIX, SunOS, etc.).
+ */
+ public static final int OS_OTHER = 8;
+
+ /**
+ * Used for the color of hyperlinks when a LookAndFeel uses light text
+ * against a dark background.
+ */
+ private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff);
+
+ private static final int OS = getOSImpl();
+
+ //private static final int DIGIT_MASK = 1;
+ private static final int LETTER_MASK = 2;
+ //private static final int WHITESPACE_MASK = 4;
+ //private static final int UPPER_CASE_MASK = 8;
+ private static final int HEX_CHARACTER_MASK = 16;
+ private static final int LETTER_OR_DIGIT_MASK = 32;
+ private static final int BRACKET_MASK = 64;
+ private static final int JAVA_OPERATOR_MASK = 128;
+
+ /**
+ * A lookup table used to quickly decide if a 16-bit Java char is a
+ * US-ASCII letter (A-Z or a-z), a digit, a whitespace char (either space
+ * (0x0020) or tab (0x0009)), etc. This method should be faster
+ * than Character.isLetter
, Character.isDigit
,
+ * and Character.isWhitespace
because we know we are dealing
+ * with ASCII chars and so don't have to worry about code planes, etc.
+ */
+ private static final int[] DATA_TABLE = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, // 0-15
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
+ 4, 128, 0, 0, 0, 128, 128, 0, 64, 64, 128, 128, 0, 128, 0, 128, // 32-47
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 128, 0, 128, 128, 128, 128, // 48-63
+ 0, 58, 58, 58, 58, 58, 58, 42, 42, 42, 42, 42, 42, 42, 42, 42, // 64-79
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 0, 64, 128, 0, // 80-95
+ 0, 50, 50, 50, 50, 50, 50, 34, 34, 34, 34, 34, 34, 34, 34, 34, // 96-111
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 64, 128, 64, 128, 0, // 112-127
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240-255.
+ };
+
+ /**
+ * Used in bracket matching methods.
+ */
+ private static Segment charSegment = new Segment();
+
+ /**
+ * Used in token list manipulation methods.
+ */
+ private static final TokenImpl TEMP_TOKEN = new TokenImpl();
+
+ /**
+ * Used internally.
+ */
+ private static final char[] JS_KEYWORD_RETURN = { 'r', 'e', 't', 'u', 'r', 'n' };
+ private static final char[] JS_AND = { '&', '&' };
+ private static final char[] JS_OR = { '|', '|' };
+
+ /**
+ * Used internally.
+ */
+ private static final String BRACKETS = "{([})]";
+
+
+ /**
+ * An unused constructor to prevent instantiation, and keep static analysis
+ * tools happy.
+ */
+ private RSyntaxUtilities() { // NOSONAR
+ // Private constructor to prevent instantiation.
+ }
+
+
+ /**
+ * Returns a string with characters that are special to HTML (such as
+ * <
, >
and &
) replaced
+ * by their HTML escape sequences.
+ *
+ * @param s The input string.
+ * @param newlineReplacement What to replace newline characters with.
+ * If this is null
, they are simply removed.
+ * @param inPreBlock Whether this HTML will be in within pre
+ * tags. If this is true
, spaces will be kept as-is;
+ * otherwise, they will be converted to "
".
+ * @return The escaped version of s
.
+ */
+ public static String escapeForHtml(String s,
+ String newlineReplacement, boolean inPreBlock) {
+
+ if (s==null) {
+ return null;
+ }
+ if (newlineReplacement==null) {
+ newlineReplacement = "";
+ }
+ final String tabString = " ";
+ boolean lastWasSpace = false;
+
+ StringBuilder sb = new StringBuilder();
+
+ for (int i=0; inull
if they cannot be
+ * determined.
+ */
+ public static Map,?> getDesktopAntiAliasHints() {
+ return (Map,?>)Toolkit.getDefaultToolkit().
+ getDesktopProperty("awt.font.desktophints");
+ }
+
+
+ /**
+ * Returns the color to use for the line underneath a folded region line.
+ *
+ * @param textArea The text area.
+ * @return The color to use.
+ */
+ public static Color getFoldedLineBottomColor(RSyntaxTextArea textArea) {
+ Color color = Color.gray;
+ Gutter gutter = RSyntaxUtilities.getGutter(textArea);
+ if (gutter!=null) {
+ color = gutter.getFoldIndicatorForeground();
+ }
+ return color;
+ }
+
+
+ /**
+ * Returns the gutter component of the scroll pane containing a text
+ * area, if any.
+ *
+ * @param textArea The text area.
+ * @return The gutter, or null
if the text area is not in
+ * an {@link RTextScrollPane}.
+ * @see RTextScrollPane#getGutter()
+ */
+ public static Gutter getGutter(RTextArea textArea) {
+ Gutter gutter = null;
+ Container parent = textArea.getParent();
+ if (parent instanceof JViewport) {
+ parent = parent.getParent();
+ if (parent instanceof RTextScrollPane) {
+ RTextScrollPane sp = (RTextScrollPane)parent;
+ gutter = sp.getGutter(); // Should always be non-null
+ }
+ }
+ return gutter;
+ }
+
+
+ /**
+ * Returns the color to use for hyperlink-style components. This method
+ * will return Color.blue
unless it appears that the current
+ * LookAndFeel uses light text on a dark background, in which case a
+ * brighter alternative is returned.
+ *
+ * @return The color to use for hyperlinks.
+ * @see #isLightForeground(Color)
+ */
+ public static Color getHyperlinkForeground() {
+
+ // This property is defined by all standard LaFs, even Nimbus (!),
+ // but you never know what crazy LaFs there are...
+ Color fg = UIManager.getColor("Label.foreground");
+ if (fg==null) {
+ fg = new JLabel().getForeground();
+ }
+
+ return isLightForeground(fg) ? LIGHT_HYPERLINK_FG : Color.blue;
+
+ }
+
+
+ /**
+ * Returns the leading whitespace of a string.
+ *
+ * @param text The String to check.
+ * @return The leading whitespace.
+ * @see #getLeadingWhitespace(Document, int)
+ */
+ public static String getLeadingWhitespace(String text) {
+ int count = 0;
+ int len = text.length();
+ while (countSegment
for each
+ * call.
+ * @param p0 The starting position in the physical line in the document.
+ * @param p1 The position for which to get the bounding box in the view.
+ * @param e How to expand tabs.
+ * @param rect The rectangle whose x- and width-values are changed to
+ * represent the bounding box of p1
. This is reused
+ * to keep from needlessly reallocating Rectangles.
+ * @param x0 The x-coordinate (pixel) marking the left-hand border of the
+ * text. This is useful if the text area has a border, for example.
+ * @return The bounding box in the view of the character p1
.
+ * @throws BadLocationException If p0
or p1
is
+ * not a valid location in the specified text area's document.
+ * @throws IllegalArgumentException If p0
and p1
+ * are not on the same line.
+ */
+ public static Rectangle getLineWidthUpTo(RSyntaxTextArea textArea,
+ Segment s, int p0, int p1,
+ TabExpander e, Rectangle rect,
+ int x0)
+ throws BadLocationException {
+
+ RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
+
+ // Ensure p0 and p1 are valid document positions.
+ if (p0<0) {
+ throw new BadLocationException("Invalid document position", p0);
+ }
+ else if (p1>doc.getLength()) {
+ throw new BadLocationException("Invalid document position", p1);
+ }
+
+ // Ensure p0 and p1 are in the same line, and get the start/end
+ // offsets for that line.
+ Element map = doc.getDefaultRootElement();
+ int lineNum = map.getElementIndex(p0);
+ // We do ">1" because p1 might be the first position on the next line
+ // or the last position on the previous one.
+ // if (lineNum!=map.getElementIndex(p1))
+ if (Math.abs(lineNum-map.getElementIndex(p1))>1) {
+ throw new IllegalArgumentException("p0 and p1 are not on the " +
+ "same line (" + p0 + ", " + p1 + ").");
+ }
+
+ // Get the token list.
+ Token t = doc.getTokenListForLine(lineNum);
+
+ // Modify the token list 't' to begin at p0 (but still have correct
+ // token types, etc.), and get the x-location (in pixels) of the
+ // beginning of this new token list.
+ TokenSubList subList = TokenUtils.getSubTokenList(t, p0, e, textArea,
+ 0, TEMP_TOKEN);
+ t = subList.tokenList;
+
+ rect = t.listOffsetToView(textArea, e, p1, x0, rect);
+ return rect;
+
+ }
+
+
+ /**
+ * Returns the location of the bracket paired with the one at the current
+ * caret position.
+ *
+ * @param textArea The text area.
+ * @param input A point to use as the return value. If this is
+ * null
, a new object is created and returned.
+ * @return A point representing the matched bracket info. The "x" field
+ * is the offset of the bracket at the caret position (either just
+ * before or just after the caret), and the "y" field is the offset
+ * of the matched bracket. Both "x" and "y" will be
+ * -1
if there isn't a matching bracket (or the caret
+ * isn't on a bracket).
+ */
+ public static Point getMatchingBracketPosition(RSyntaxTextArea textArea,
+ Point input) {
+
+ if (input==null) {
+ input = new Point();
+ }
+ input.setLocation(-1, -1);
+
+ try {
+
+ // Actually position just BEFORE caret.
+ int caretPosition = textArea.getCaretPosition() - 1;
+ RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
+ char bracket = 0;
+
+ // If the caret was at offset 0, we can't check "to its left."
+ if (caretPosition>=0) {
+ bracket = doc.charAt(caretPosition);
+ }
+
+ // Try to match a bracket "to the right" of the caret if one
+ // was not found on the left.
+ int index = BRACKETS.indexOf(bracket);
+ if (index==-1 && caretPositiont
).
+ * @return The next non-whitespace, non-comment token, or null
+ * if there isn't one.
+ * @see #getPreviousImportantToken(RSyntaxDocument, int)
+ * @see #getPreviousImportantTokenFromOffs(RSyntaxDocument, int)
+ */
+ public static Token getNextImportantToken(Token t,
+ RSyntaxTextArea textArea, int line) {
+ while (t!=null && t.isPaintable() && t.isCommentOrWhitespace()) {
+ t = t.getNextToken();
+ }
+ if ((t==null || !t.isPaintable()) && linejavax.swing.text.View
is an instance of
+ * {@link TokenOrientedView} and javax.swing.text.TabExpander
;
+ * otherwise, a ClassCastException
could be thrown.
+ *
+ * @param pos the position to convert >= 0
+ * @param a the allocated region in which to render
+ * @param direction the direction from the current position that can
+ * be thought of as the arrow keys typically found on a keyboard.
+ * This will be one of the following values:
+ *
+ *
+ * @return the location within the model that best represents the next
+ * location visual position
+ * @throws BadLocationException if {@code pos} is invalid.
+ * @throws IllegalArgumentException if direction
+ * doesn't have one of the legal values above
+ */
+ public static int getNextVisualPositionFrom(int pos, Position.Bias b,
+ Shape a, int direction,
+ Position.Bias[] biasRet, View view)
+ throws BadLocationException {
+
+ RSyntaxTextArea target = (RSyntaxTextArea)view.getContainer();
+ biasRet[0] = Position.Bias.Forward;
+
+ // Do we want the "next position" above, below, to the left or right?
+ switch (direction) {
+
+ case NORTH:
+ case SOUTH:
+ if (pos == -1) {
+ pos = (direction == NORTH) ?
+ Math.max(0, view.getEndOffset() - 1) :
+ view.getStartOffset();
+ break;
+ }
+ Caret c = (target != null) ? target.getCaret() : null;
+ // YECK! Ideally, the x location from the magic caret
+ // position would be passed in.
+ Point mcp;
+ if (c != null) {
+ mcp = c.getMagicCaretPosition();
+ }
+ else {
+ mcp = null;
+ }
+ int x;
+ if (mcp == null) {
+ Rectangle loc = target.modelToView(pos);
+ x = (loc == null) ? 0 : loc.x;
+ }
+ else {
+ x = mcp.x;
+ }
+ if (direction == NORTH) {
+ pos = getPositionAbove(target,pos,x,(TabExpander)view);
+ }
+ else {
+ pos = getPositionBelow(target,pos,x,(TabExpander)view);
+ }
+ break;
+
+ case WEST:
+ int endOffs = view.getEndOffset();
+ if(pos == -1) {
+ pos = Math.max(0, endOffs - 1);
+ }
+ else {
+ pos = Math.max(0, pos - 1);
+ if (target.isCodeFoldingEnabled()) {
+ int last = pos==endOffs-1 ? target.getLineCount()-1 :
+ target.getLineOfOffset(pos+1);
+ int current = target.getLineOfOffset(pos);
+ if (last!=current) { // If moving up a line...
+ FoldManager fm = target.getFoldManager();
+ if (fm.isLineHidden(current)) {
+ while (--current>0 && fm.isLineHidden(current));
+ pos = target.getLineEndOffset(current) - 1;
+ }
+ }
+ }
+ }
+ break;
+
+ case EAST:
+ if(pos == -1) {
+ pos = view.getStartOffset();
+ }
+ else {
+ pos = Math.min(pos + 1, view.getDocument().getLength());
+ if (target.isCodeFoldingEnabled()) {
+ int last = pos==0 ? 0 : target.getLineOfOffset(pos-1);
+ int current = target.getLineOfOffset(pos);
+ if (last!=current) { // If moving down a line...
+ FoldManager fm = target.getFoldManager();
+ if (fm.isLineHidden(current)) {
+ int lineCount = target.getLineCount();
+ while (++currentnull
+ * if there isn't one.
+ * @see #getNextImportantToken(Token, RSyntaxTextArea, int)
+ * @see #getPreviousImportantTokenFromOffs(RSyntaxDocument, int)
+ */
+ public static Token getPreviousImportantToken(RSyntaxDocument doc,
+ int line) {
+ if (line<0) {
+ return null;
+ }
+ Token t = doc.getTokenListForLine(line);
+ if (t!=null) {
+ t = t.getLastNonCommentNonWhitespaceToken();
+ if (t!=null) {
+ return t;
+ }
+ }
+ return getPreviousImportantToken(doc, line-1);
+ }
+
+
+ /**
+ * Returns the last non-whitespace, non-comment token, before the
+ * specified offset.
+ *
+ * @param doc The document.
+ * @param offs The ending offset for the search.
+ * @return The last non-whitespace, non-comment token, or null
+ * if there isn't one.
+ * @see #getPreviousImportantToken(RSyntaxDocument, int)
+ * @see #getNextImportantToken(Token, RSyntaxTextArea, int)
+ */
+ public static Token getPreviousImportantTokenFromOffs(
+ RSyntaxDocument doc, int offs) {
+
+ Element root = doc.getDefaultRootElement();
+ int line = root.getElementIndex(offs);
+ Token t = doc.getTokenListForLine(line);
+
+ // Check line containing offs
+ Token target = null;
+ while (t!=null && t.isPaintable() && !t.containsPosition(offs)) {
+ if (!t.isCommentOrWhitespace()) {
+ target = t;
+ }
+ t = t.getNextToken();
+ }
+
+ // Check previous line(s)
+ if (target==null) {
+ target = RSyntaxUtilities.getPreviousImportantToken(doc, line-1);
+ }
+
+ return target;
+
+ }
+
+
+ /**
+ * Returns the token at the specified offset.
+ *
+ * @param textArea The text area.
+ * @param offset The offset of the token.
+ * @return The token, or null
if the offset is not valid.
+ * @see #getTokenAtOffset(RSyntaxDocument, int)
+ * @see #getTokenAtOffset(Token, int)
+ */
+ public static Token getTokenAtOffset(RSyntaxTextArea textArea,
+ int offset) {
+ RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
+ return RSyntaxUtilities.getTokenAtOffset(doc, offset);
+ }
+
+
+ /**
+ * Returns the token at the specified offset.
+ *
+ * @param doc The document.
+ * @param offset The offset of the token.
+ * @return The token, or null
if the offset is not valid.
+ * @see #getTokenAtOffset(RSyntaxTextArea, int)
+ * @see #getTokenAtOffset(Token, int)
+ */
+ public static Token getTokenAtOffset(RSyntaxDocument doc,
+ int offset) {
+ Element root = doc.getDefaultRootElement();
+ int lineIndex = root.getElementIndex(offset);
+ Token t = doc.getTokenListForLine(lineIndex);
+ return RSyntaxUtilities.getTokenAtOffset(t, offset);
+ }
+
+
+ /**
+ * Returns the token at the specified index, or null
if
+ * the given offset isn't in this token list's range.
+ * Note that this method does NOT check to see if tokenList
+ * is null; callers should check for themselves.
+ *
+ * @param tokenList The list of tokens in which to search.
+ * @param offset The offset at which to get the token.
+ * @return The token at offset
, or null
if
+ * none of the tokens are at that offset.
+ * @see #getTokenAtOffset(RSyntaxTextArea, int)
+ * @see #getTokenAtOffset(RSyntaxDocument, int)
+ */
+ public static Token getTokenAtOffset(Token tokenList, int offset) {
+ for (Token t=tokenList; t!=null && t.isPaintable(); t=t.getNextToken()){
+ if (t.containsPosition(offset)) {
+ return t;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Returns the end of the word at the given offset.
+ *
+ * @param textArea The text area.
+ * @param offs The offset into the text area's content.
+ * @return The end offset of the word.
+ * @throws BadLocationException If offs
is invalid.
+ * @see #getWordStart(RSyntaxTextArea, int)
+ */
+ public static int getWordEnd(RSyntaxTextArea textArea, int offs)
+ throws BadLocationException {
+
+ Document doc = textArea.getDocument();
+ int endOffs = textArea.getLineEndOffsetOfCurrentLine();
+ int lineEnd = Math.min(endOffs, doc.getLength());
+ if (offs == lineEnd) { // End of the line.
+ return offs;
+ }
+
+ String s = doc.getText(offs, lineEnd-offs-1);
+ if (s!=null && s.length()>0) { // Should always be true
+ int i = 0;
+ int count = s.length();
+ char ch = s.charAt(i);
+ if (Character.isWhitespace(ch)) {
+ while (i0
in the view (for tab purposes).
+ *
+ * @param tokenList The tokenList list representing the text.
+ * @param textArea The text area in which this token list resides.
+ * @param e The tab expander. This value cannot be null
.
+ * @return The width of the token list, in pixels.
+ */
+ public static float getTokenListWidth(Token tokenList,
+ RSyntaxTextArea textArea,
+ TabExpander e) {
+ return getTokenListWidth(tokenList, textArea, e, 0);
+ }
+
+
+ /**
+ * Determines the width of the given token list taking tabs
+ * into consideration. This is implemented in a 1.1 style coordinate
+ * system where ints are used and 72dpi is assumed.null
.
+ * @param x0 The x-pixel coordinate of the start of the token list.
+ * @return The width of the token list, in pixels.
+ * @see #getTokenListWidthUpTo
+ */
+ public static float getTokenListWidth(final Token tokenList,
+ RSyntaxTextArea textArea,
+ TabExpander e, float x0) {
+ float width = x0;
+ for (Token t=tokenList; t!=null&&t.isPaintable(); t=t.getNextToken()) {
+ width += t.getWidth(textArea, e, width);
+ }
+ return width - x0;
+ }
+
+
+ /**
+ * Determines the width of the given token list taking tabs into
+ * consideration and only up to the given index in the document
+ * (exclusive).
+ *
+ * @param tokenList The token list representing the text.
+ * @param textArea The text area in which this token list resides.
+ * @param e The tab expander. This value cannot be null
.
+ * @param x0 The x-pixel coordinate of the start of the token list.
+ * @param upTo The document position at which you want to stop,
+ * exclusive. If this position is before the starting position
+ * of the token list, a width of 0
will be
+ * returned; similarly, if this position comes after the entire
+ * token list, the width of the entire token list is returned.
+ * @return The width of the token list, in pixels, up to, but not
+ * including, the character at position upTo
.
+ * @see #getTokenListWidth
+ */
+ public static float getTokenListWidthUpTo(final Token tokenList,
+ RSyntaxTextArea textArea, TabExpander e,
+ float x0, int upTo) {
+ float width = 0;
+ for (Token t=tokenList; t!=null&&t.isPaintable(); t=t.getNextToken()) {
+ if (t.containsPosition(upTo)) {
+ return width + t.getWidthUpTo(upTo-t.getOffset(), textArea, e,
+ x0+width);
+ }
+ width += t.getWidth(textArea, e, x0+width);
+ }
+ return width;
+ }
+
+
+ /**
+ * Returns whether or not this character is a "bracket" to be matched by
+ * such programming languages as C, C++, and Java.
+ *
+ * @param ch The character to check.
+ * @return Whether or not the character is a "bracket" - one of '(', ')',
+ * '[', ']', '{', and '}'.
+ */
+ public static boolean isBracket(char ch) {
+ // We need the first condition as it might be that ch>255, and thus
+ // not in our table. '}' is the highest-valued char in the bracket
+ // set.
+ return ch<='}' && (DATA_TABLE[ch]&BRACKET_MASK)>0;
+ }
+
+
+ /**
+ * Returns whether or not a character is a digit (0-9).
+ *
+ * @param ch The character to check.
+ * @return Whether or not the character is a digit.
+ */
+ public static boolean isDigit(char ch) {
+ // We do it this way as we'd need to do two conditions anyway (first
+ // to check that ch<255 so it can index into our table, then whether
+ // that table position has the digit mask).
+ return ch>='0' && ch<='9';
+ }
+
+
+ /**
+ * Returns whether or not this character is a hex character. This method
+ * accepts both upper- and lower-case letters a-f.
+ *
+ * @param ch The character to check.
+ * @return Whether or not the character is a hex character 0-9, a-f, or
+ * A-F.
+ */
+ public static boolean isHexCharacter(char ch) {
+ // We need the first condition as it could be that ch>255 (and thus
+ // not a valid index into our table). 'f' is the highest-valued
+ // char that is a valid hex character.
+ return (ch<='f') && (DATA_TABLE[ch]&HEX_CHARACTER_MASK)>0;
+ }
+
+
+ /**
+ * Returns whether a character is a Java operator. Note that C and C++
+ * operators are the same as Java operators.
+ *
+ * @param ch The character to check.
+ * @return Whether or not the character is a Java operator.
+ */
+ public static boolean isJavaOperator(char ch) {
+ // We need the first condition as it could be that ch>255 (and thus
+ // not a valid index into our table). '~' is the highest-valued
+ // char that is a valid Java operator.
+ return (ch<='~') && (DATA_TABLE[ch]&JAVA_OPERATOR_MASK)>0;
+ }
+
+
+ /**
+ * Returns whether a character is a US-ASCII letter (A-Z or a-z).
+ *
+ * @param ch The character to check.
+ * @return Whether or not the character is a US-ASCII letter.
+ */
+ public static boolean isLetter(char ch) {
+ // We need the first condition as it could be that ch>255 (and thus
+ // not a valid index into our table).
+ return (ch<='z') && (DATA_TABLE[ch]&LETTER_MASK)>0;
+ }
+
+
+ /**
+ * Returns whether or not a character is a US-ASCII letter or a digit.
+ *
+ * @param ch The character to check.
+ * @return Whether or not the character is a US-ASCII letter or a digit.
+ */
+ public static boolean isLetterOrDigit(char ch) {
+ // We need the first condition as it could be that ch>255 (and thus
+ // not a valid index into our table).
+ return (ch<='z') && (DATA_TABLE[ch]&LETTER_OR_DIGIT_MASK)>0;
+ }
+
+
+ /**
+ * Returns whether the specified color is "light" to use as a foreground.
+ * Colors that return true
indicate that the current Look and
+ * Feel probably uses light text colors on a dark background.
+ *
+ * @param fg The foreground color.
+ * @return Whether it is a "light" foreground color.
+ * @see #getHyperlinkForeground()
+ */
+ public static boolean isLightForeground(Color fg) {
+ return fg.getRed()>0xa0 && fg.getGreen()>0xa0 && fg.getBlue()>0xa0;
+ }
+
+
+ /**
+ * Returns whether the specified token is a single non-word char (e.g. not
+ * in [A-Za-z]
. This is a HACK to work around the fact that
+ * many standard token makers return things like semicolons and periods as
+ * {@link Token#IDENTIFIER}s just to make the syntax highlighting coloring
+ * look a little better.
+ *
+ * @param t The token to check. This cannot be null
.
+ * @return Whether the token is a single non-word char.
+ */
+ public static boolean isNonWordChar(Token t) {
+ return t.length()==1 && !RSyntaxUtilities.isLetter(t.charAt(0));
+ }
+
+
+ /**
+ * Returns whether or not a character is a whitespace character (either
+ * a space ' ' or tab '\t'). This checks for the Unicode character values
+ * 0x0020 and 0x0009.
+ *
+ * @param ch The character to check.
+ * @return Whether or not the character is a whitespace character.
+ */
+ public static boolean isWhitespace(char ch) {
+ // We do it this way as we'd need to do two conditions anyway (first
+ // to check that ch<255 so it can index into our table, then whether
+ // that table position has the whitespace mask).
+ return ch==' ' || ch=='\t';
+ }
+
+
+ /**
+ * Repaints the gutter in a text area's scroll pane, if necessary.
+ *
+ * @param textArea The text area.
+ */
+ public static void possiblyRepaintGutter(RTextArea textArea) {
+ Gutter gutter = RSyntaxUtilities.getGutter(textArea);
+ if (gutter!=null) {
+ gutter.repaint();
+ }
+ }
+
+
+ /**
+ * Returns whether a regular expression token can follow the specified
+ * token in JavaScript.
+ *
+ * @param t The token to check, which may be null
.
+ * @return Whether a regular expression token may follow this one in
+ * JavaScript.
+ */
+ public static boolean regexCanFollowInJavaScript(Token t) {
+ char ch;
+ // We basically try to mimic Eclipse's JS editor's behavior here.
+ return t==null ||
+ //t.isOperator() ||
+ (t.length()==1 && (
+ (ch=t.charAt(0))=='=' ||
+ ch=='(' ||
+ ch==',' ||
+ ch=='?' ||
+ ch==':' ||
+ ch=='[' ||
+ ch=='!' ||
+ ch=='&'
+ )) ||
+ /* Operators "==", "===", "!=", "!==", "&&", "||" */
+ (t.getType()==Token.OPERATOR &&
+ (t.charAt(t.length()-1)=='=' ||
+ t.is(JS_AND) || t.is(JS_OR))) ||
+ t.is(Token.RESERVED_WORD_2, JS_KEYWORD_RETURN);
+ }
+
+
+ /**
+ * Selects a range of text in a text component. If the new selection is
+ * outside of the previous viewable rectangle, then the view is centered
+ * around the new selection.
+ *
+ * @param textArea The text component whose selection is to be centered.
+ * @param range The range to select.
+ */
+ public static void selectAndPossiblyCenter(JTextArea textArea,
+ DocumentRange range, boolean select) {
+
+ int start = range.getStartOffset();
+ int end = range.getEndOffset();
+
+ boolean foldsExpanded = false;
+ if (textArea instanceof RSyntaxTextArea) {
+ RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
+ FoldManager fm = rsta.getFoldManager();
+ if (fm.isCodeFoldingSupportedAndEnabled()) {
+ foldsExpanded = fm.ensureOffsetNotInClosedFold(start);
+ foldsExpanded |= fm.ensureOffsetNotInClosedFold(end);
+ }
+ }
+
+ if (select) {
+ textArea.setSelectionStart(start);
+ textArea.setSelectionEnd(end);
+ }
+
+ Rectangle r = null;
+ try {
+ r = textArea.modelToView(start);
+ if (r==null) { // Not yet visible; i.e. JUnit tests
+ return;
+ }
+ if (end!=start) {
+ r = r.union(textArea.modelToView(end));
+ }
+ } catch (BadLocationException ble) { // Never happens
+ ble.printStackTrace();
+ if (select) {
+ textArea.setSelectionStart(start);
+ textArea.setSelectionEnd(end);
+ }
+ return;
+ }
+
+ Rectangle visible = textArea.getVisibleRect();
+
+ // If the new selection is already in the view, don't scroll,
+ // as that is visually jarring.
+ if (!foldsExpanded && visible.contains(r)) {
+ if (select) {
+ textArea.setSelectionStart(start);
+ textArea.setSelectionEnd(end);
+ }
+ return;
+ }
+
+ visible.x = r.x - (visible.width - r.width) / 2;
+ visible.y = r.y - (visible.height - r.height) / 2;
+
+ Rectangle bounds = textArea.getBounds();
+ Insets i = textArea.getInsets();
+ bounds.x = i.left;
+ bounds.y = i.top;
+ bounds.width -= i.left + i.right;
+ bounds.height -= i.top + i.bottom;
+
+ if (visible.x < bounds.x) {
+ visible.x = bounds.x;
+ }
+
+ if (visible.x + visible.width > bounds.x + bounds.width) {
+ visible.x = bounds.x + bounds.width - visible.width;
+ }
+
+ if (visible.y < bounds.y) {
+ visible.y = bounds.y;
+ }
+
+ if (visible.y + visible.height > bounds.y + bounds.height) {
+ visible.y = bounds.y + bounds.height - visible.height;
+ }
+
+ textArea.scrollRectToVisible(visible);
+
+ }
+
+
+ /**
+ * If the character is an upper-case US-ASCII letter, it returns the
+ * lower-case version of that letter; otherwise, it just returns the
+ * character.
+ *
+ * @param ch The character to lower-case (if it is a US-ASCII upper-case
+ * character).
+ * @return The lower-case version of the character.
+ */
+ public static char toLowerCase(char ch) {
+ // We can logical OR with 32 because A-Z are 65-90 in the ASCII table
+ // and none of them have the 6th bit (32) set, and a-z are 97-122 in
+ // the ASCII table, which is 32 over from A-Z.
+ // We do it this way as we'd need to do two conditions anyway (first
+ // to check that ch<255 so it can index into our table, then whether
+ // that table position has the upper-case mask).
+ if (ch>='A' && ch<='Z') {
+ return (char)(ch | 0x20);
+ }
+ return ch;
+ }
+
+
+ /**
+ * Creates a regular expression pattern that matches a "wildcard" pattern.
+ *
+ * @param wildcard The wildcard pattern.
+ * @param matchCase Whether the pattern should be case sensitive.
+ * @param escapeStartChar Whether to escape a starting '^'
+ * character.
+ * @return The pattern.
+ */
+ public static Pattern wildcardToPattern(String wildcard, boolean matchCase,
+ boolean escapeStartChar) {
+
+ int flags = RSyntaxUtilities.getPatternFlags(matchCase, 0);
+
+ StringBuilder sb = new StringBuilder();
+ for (int i=0; iRtfTransferable
to return the plain text version of the transferable when the receiver
- * does not support RTF.
- *
+ * Gets the plain text version of RTF documents.StyledTextTransferable
to return the plain text
+ * version of the transferable when the receiver does not support RTF.
+ *
* @author Robert Futrell
* @version 1.0
*/
-class RtfToText {
-
- private Reader r;
- private StringBuffer sb;
- private StringBuffer controlWord;
- private int blockCount;
- private boolean inControlWord;
-
- /**
- * Private constructor.
- *
- * @param r
- * The reader to read RTF text from.
- */
- private RtfToText(Reader r) {
- this.r = r;
- sb = new StringBuffer();
- controlWord = new StringBuffer();
- blockCount = 0;
- inControlWord = false;
- }
-
- /**
- * Converts the RTF text read from this converter's Reader
into plain text. It is the caller's
- * responsibility to close the reader after this method is called.
- *
- * @return The plain text.
- * @throws IOException
- * If an IO error occurs.
- */
- private String convert() throws IOException {
-
- // Skip over first curly brace as the whole file is in '{' and '}'
- int i = r.read();
- if (i != '{') {
- throw new IOException("Invalid RTF file");
- }
-
- while ((i = r.read()) != -1) {
-
- char ch = (char) i;
- switch (ch) {
- case '{':
- if (inControlWord && controlWord.length() == 0) { // "\{"
- sb.append('{');
- controlWord.setLength(0);
- inControlWord = false;
- }
- else {
- blockCount++;
- }
- break;
- case '}':
- if (inControlWord && controlWord.length() == 0) { // "\}"
- sb.append('}');
- controlWord.setLength(0);
- inControlWord = false;
- }
- else {
- blockCount--;
- }
- break;
- case '\\':
- if (blockCount == 0) {
- if (inControlWord) {
- if (controlWord.length() == 0) { // "\\"
- sb.append('\\');
- controlWord.setLength(0);
- inControlWord = false;
- }
- else {
- endControlWord();
- }
- }
- inControlWord = true;
- }
- break;
- case ' ':
- if (blockCount == 0) {
- if (inControlWord) {
- endControlWord();
- }
- else {
- sb.append(' ');
- }
- }
- break;
- case '\r':
- case '\n':
- if (blockCount == 0) {
- if (inControlWord) {
- endControlWord();
- }
- // Otherwise, ignore
- }
- break;
- default:
- if (blockCount == 0) {
- if (inControlWord) {
- controlWord.append(ch);
- }
- else {
- sb.append(ch);
- }
- }
- break;
- }
-
- }
-
- return sb.toString();
-
- }
-
- /**
- * Ends a control word. Checks whether it is a common one that affects the plain text output (such as "
- * par
" or "tab
") and updates the text buffer accordingly.
- */
- private void endControlWord() {
- String word = controlWord.toString();
- if ("par".equals(word)) {
- sb.append('\n');
- }
- else if ("tab".equals(word)) {
- sb.append('\t');
- }
- controlWord.setLength(0);
- inControlWord = false;
- }
-
- /**
- * Converts the contents of the specified byte array representing an RTF document into plain text.
- *
- * @param rtf
- * The byte array representing an RTF document.
- * @return The contents of the RTF document, in plain text.
- * @throws IOException
- * If an IO error occurs.
- */
- public static String getPlainText(byte[] rtf) throws IOException {
- return getPlainText(new ByteArrayInputStream(rtf));
- }
-
- /**
- * Converts the contents of the specified RTF file to plain text.
- *
- * @param file
- * The RTF file to convert.
- * @return The contents of the file, in plain text.
- * @throws IOException
- * If an IO error occurs.
- */
- public static String getPlainText(File file) throws IOException {
- return getPlainText(new BufferedReader(new FileReader(file)));
- }
-
- /**
- * Converts the contents of the specified input stream to plain text. The input stream will be closed when this
- * method returns.
- *
- * @param in
- * The input stream to convert.
- * @return The contents of the stream, in plain text.
- * @throws IOException
- * If an IO error occurs.
- */
- public static String getPlainText(InputStream in) throws IOException {
- return getPlainText(new InputStreamReader(in, "US-ASCII"));
- }
-
- /**
- * Converts the contents of the specified Reader
to plain text.
- *
- * @param r
- * The Reader
.
- * @return The contents of the Reader
, in plain text.
- * @throws IOException
- * If an IO error occurs.
- */
- private static String getPlainText(Reader r) throws IOException {
- try {
- RtfToText converter = new RtfToText(r);
- return converter.convert();
- } finally {
- r.close();
- }
- }
-
- /**
- * Converts the contents of the specified String to plain text.
- *
- * @param rtf
- * A string whose contents represent an RTF document.
- * @return The contents of the String, in plain text.
- * @throws IOException
- * If an IO error occurs.
- */
- public static String getPlainText(String rtf) throws IOException {
- return getPlainText(new StringReader(rtf));
- }
-
-}
\ No newline at end of file
+final class RtfToText {
+
+ private Reader r;
+ private StringBuilder sb;
+ private StringBuilder controlWord;
+ private int blockCount;
+ private boolean inControlWord;
+
+
+ /**
+ * Private constructor.
+ *
+ * @param r The reader to read RTF text from.
+ */
+ private RtfToText(Reader r) {
+ this.r = r;
+ sb = new StringBuilder();
+ controlWord = new StringBuilder();
+ blockCount = 0;
+ inControlWord = false;
+ }
+
+
+ /**
+ * Converts the RTF text read from this converter's Reader
+ * into plain text. It is the caller's responsibility to close the
+ * reader after this method is called.
+ *
+ * @return The plain text.
+ * @throws IOException If an IO error occurs.
+ */
+ private String convert() throws IOException {
+
+ // Skip over first curly brace as the whole file is in '{' and '}'
+ int i = r.read();
+ if (i!='{') {
+ throw new IOException("Invalid RTF file");
+ }
+
+ while ((i=r.read())!=-1) {
+
+ char ch = (char)i;
+ switch (ch) {
+ case '{':
+ if (inControlWord && controlWord.length()==0) { // "\{"
+ sb.append('{');
+ controlWord.setLength(0);
+ inControlWord = false;
+ }
+ else {
+ blockCount++;
+ }
+ break;
+ case '}':
+ if (inControlWord && controlWord.length()==0) { // "\}"
+ sb.append('}');
+ controlWord.setLength(0);
+ inControlWord = false;
+ }
+ else {
+ blockCount--;
+ }
+ break;
+ case '\\':
+ if (blockCount==0) {
+ if (inControlWord) {
+ if (controlWord.length()==0) { // "\\"
+ sb.append('\\');
+ controlWord.setLength(0);
+ inControlWord = false;
+ }
+ else {
+ endControlWord();
+ inControlWord = true;
+ }
+ }
+ else {
+ inControlWord = true;
+ }
+ }
+ break;
+ case ' ':
+ if (blockCount==0) {
+ if (inControlWord) {
+ endControlWord();
+ }
+ else {
+ sb.append(' ');
+ }
+ }
+ break;
+ case '\r':
+ case '\n':
+ if (blockCount==0) {
+ if (inControlWord) {
+ endControlWord();
+ }
+ // Otherwise, ignore
+ }
+ break;
+ default:
+ if (blockCount==0) {
+ if (inControlWord) {
+ controlWord.append(ch);
+ }
+ else {
+ sb.append(ch);
+ }
+ }
+ break;
+ }
+
+ }
+
+ return sb.toString();
+
+ }
+
+
+ /**
+ * Ends a control word. Checks whether it is a common one that affects
+ * the plain text output (such as "par
" or "tab
")
+ * and updates the text buffer accordingly.
+ */
+ private void endControlWord() {
+ String word = controlWord.toString();
+ if ("par".equals(word) || "line".equals(word)) {
+ sb.append('\n');
+ }
+ else if ("tab".equals(word)) {
+ sb.append('\t');
+ }
+ controlWord.setLength(0);
+ inControlWord = false;
+ }
+
+
+ /**
+ * Converts the contents of the specified byte array representing
+ * an RTF document into plain text.
+ *
+ * @param rtf The byte array representing an RTF document.
+ * @return The contents of the RTF document, in plain text.
+ * @throws IOException If an IO error occurs.
+ */
+ public static String getPlainText(byte[] rtf) throws IOException {
+ return getPlainText(new ByteArrayInputStream(rtf));
+ }
+
+
+ /**
+ * Converts the contents of the specified RTF file to plain text.
+ *
+ * @param file The RTF file to convert.
+ * @return The contents of the file, in plain text.
+ * @throws IOException If an IO error occurs.
+ */
+ public static String getPlainText(File file) throws IOException {
+ return getPlainText(new BufferedReader(new FileReader(file)));
+ }
+
+
+ /**
+ * Converts the contents of the specified input stream to plain text.
+ * The input stream will be closed when this method returns.
+ *
+ * @param in The input stream to convert. This will be closed when this
+ * method returns.
+ * @return The contents of the stream, in plain text.
+ * @throws IOException If an IO error occurs.
+ */
+ public static String getPlainText(InputStream in) throws IOException {
+ return getPlainText(new InputStreamReader(in, StandardCharsets.US_ASCII));
+ }
+
+
+ /**
+ * Converts the contents of the specified Reader
to plain text.
+ *
+ * @param r The Reader
. This will be closed when this method
+ * returns.
+ * @return The contents of the Reader
, in plain text.
+ * @throws IOException If an IO error occurs.
+ */
+ private static String getPlainText(Reader r) throws IOException {
+ try {
+ RtfToText converter = new RtfToText(r);
+ return converter.convert();
+ } finally {
+ r.close();
+ }
+ }
+
+
+ /**
+ * Converts the contents of the specified String to plain text.
+ *
+ * @param rtf A string whose contents represent an RTF document.
+ * @return The contents of the String, in plain text.
+ * @throws IOException If an IO error occurs.
+ */
+ public static String getPlainText(String rtf) throws IOException {
+ return getPlainText(new StringReader(rtf));
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RtfGenerator.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RtfGenerator.java
old mode 100644
new mode 100755
index 5b05cfb65..ddef7504e
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RtfGenerator.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/RtfGenerator.java
@@ -2,515 +2,493 @@
* 07/28/2008
*
* RtfGenerator.java - Generates RTF via a simple Java API.
- * Copyright (C) 2008 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Font;
-import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
+
+import org.fife.ui.rtextarea.RTextArea;
+
/**
- * Generates RTF text via a simple Java API.
- *
- *
- *
- * The RTF generated isn't really "optimized," but it will do, especially for small amounts of text, such as what's
- * common when copy-and-pasting. It tries to be sufficient for the use case of copying syntax highlighted code:
+ *
+ * The RTF generated isn't really "optimized," but it will do, especially for
+ * small amounts of text, such as what's common when copy-and-pasting. It
+ * tries to be sufficient for the use case of copying syntax highlighted
+ * code:
*
- *
- *
+ *
* @author Robert Futrell
- * @version 1.0
+ * @version 1.1
*/
public class RtfGenerator {
- private List fontList;
- private List colorList;
- private StringBuffer document;
- private boolean lastWasControlWord;
- private int lastFontIndex;
- private int lastFGIndex;
- private boolean lastBold;
- private boolean lastItalic;
- private int lastFontSize;
- private String monospacedFontName;
-
- /**
- * Java2D assumes a 72 dpi screen resolution, but on Windows the screen resolution is either 96 dpi or 120 dpi,
- * depending on your font display settings. This is an attempt to make the RTF generated match the size of what's
- * displayed in the RSyntaxTextArea.
- */
- private int screenRes;
-
- /**
- * The default font size for RTF. This is point size, in half points.
- */
- private static final int DEFAULT_FONT_SIZE = 12;// 24;
-
- /**
- * Constructor.
- */
- public RtfGenerator() {
- fontList = new ArrayList(1); // Usually only 1.
- colorList = new ArrayList(1); // Usually only 1.
- document = new StringBuffer();
- reset();
- }
-
- /**
- * Adds a newline to the RTF document.
- *
- * @see #appendToDoc(String, Font, Color, Color)
- */
- public void appendNewline() {
- document.append("\\par");
- document.append('\n'); // Just for ease of reading RTF.
- lastWasControlWord = false;
- }
-
- /**
- * Appends styled text to the RTF document being generated.
- *
- * @param text
- * The text to append.
- * @param f
- * The font of the text. If this is null
, the default font is used.
- * @param fg
- * The foreground of the text. If this is null
, the default foreground color is used.
- * @param bg
- * The background color of the text. If this is null
, the default background color is used.
- * @see #appendNewline()
- */
- public void appendToDoc(String text, Font f, Color fg, Color bg) {
- appendToDoc(text, f, fg, bg, false);
- }
-
- /**
- * Appends styled text to the RTF document being generated.
- *
- * @param text
- * The text to append.
- * @param f
- * The font of the text. If this is null
, the default font is used.
- * @param bg
- * The background color of the text. If this is null
, the default background color is used.
- * @param underline
- * Whether the text should be underlined.
- * @see #appendNewline()
- */
- public void appendToDocNoFG(String text, Font f, Color bg,
- boolean underline) {
- appendToDoc(text, f, null, bg, underline, false);
- }
-
- /**
- * Appends styled text to the RTF document being generated.
- *
- * @param text
- * The text to append.
- * @param f
- * The font of the text. If this is null
, the default font is used.
- * @param fg
- * The foreground of the text. If this is null
, the default foreground color is used.
- * @param bg
- * The background color of the text. If this is null
, the default background color is used.
- * @param underline
- * Whether the text should be underlined.
- * @see #appendNewline()
- */
- public void appendToDoc(String text, Font f, Color fg, Color bg,
- boolean underline) {
- appendToDoc(text, f, fg, bg, underline, true);
- }
-
- /**
- * Appends styled text to the RTF document being generated.
- *
- * @param text
- * The text to append.
- * @param f
- * The font of the text. If this is null
, the default font is used.
- * @param fg
- * The foreground of the text. If this is null
, the default foreground color is used.
- * @param bg
- * The background color of the text. If this is null
, the default background color is used.
- * @param underline
- * Whether the text should be underlined.
- * @param setFG
- * Whether the foreground specified by fg
should be honored (if it is non-null
- * ).
- * @see #appendNewline()
- */
- public void appendToDoc(String text, Font f, Color fg, Color bg,
- boolean underline, boolean setFG) {
-
- if (text != null) {
-
- // Set font to use, if different from last addition.
- int fontIndex = f == null ? 0 : (getFontIndex(fontList, f) + 1);
- if (fontIndex != lastFontIndex) {
- document.append("\\f").append(fontIndex);
- lastFontIndex = fontIndex;
- lastWasControlWord = true;
- }
-
- // Set styles to use.
- if (f != null) {
- int fontSize = fixFontSize(f.getSize2D()); // Half points
- if (fontSize != lastFontSize) {
- document.append("\\fs").append(fontSize);
- lastFontSize = fontSize;
- lastWasControlWord = true;
- }
- if (f.isBold() != lastBold) {
- document.append(lastBold ? "\\b0" : "\\b");
- lastBold = !lastBold;
- lastWasControlWord = true;
- }
- if (f.isItalic() != lastItalic) {
- document.append(lastItalic ? "\\i0" : "\\i");
- lastItalic = !lastItalic;
- lastWasControlWord = true;
- }
- }
- else { // No font specified - assume neither bold nor italic.
- if (lastFontSize != DEFAULT_FONT_SIZE) {
- document.append("\\fs").append(DEFAULT_FONT_SIZE);
- lastFontSize = DEFAULT_FONT_SIZE;
- lastWasControlWord = true;
- }
- if (lastBold) {
- document.append("\\b0");
- lastBold = false;
- lastWasControlWord = true;
- }
- if (lastItalic) {
- document.append("\\i0");
- lastItalic = false;
- lastWasControlWord = true;
- }
- }
- if (underline) {
- document.append("\\ul");
- lastWasControlWord = true;
- }
-
- // Set the foreground color.
- if (setFG) {
- int fgIndex = 0;
- if (fg != null) { // null => fg color index 0
- fgIndex = getIndex(colorList, fg) + 1;
- }
- if (fgIndex != lastFGIndex) {
- document.append("\\cf").append(fgIndex);
- lastFGIndex = fgIndex;
- lastWasControlWord = true;
- }
- }
-
- // Set the background color.
- if (bg != null) {
- int pos = getIndex(colorList, bg);
- document.append("\\highlight").append(pos + 1);
- lastWasControlWord = true;
- }
-
- if (lastWasControlWord) {
- document.append(' '); // Delimiter
- lastWasControlWord = false;
- }
- escapeAndAdd(document, text);
-
- // Reset everything that was set for this text fragment.
- if (bg != null) {
- document.append("\\highlight0");
- lastWasControlWord = true;
- }
- if (underline) {
- document.append("\\ul0");
- lastWasControlWord = true;
- }
-
- }
-
- }
-
- /**
- * Appends some text to a buffer, with special care taken for special characters as defined by the RTF spec:
- *
- *
- *
- *
- * @param text
- * The text to append (with tab chars substituted).
- * @param sb
- * The buffer to append to.
- */
- private final void escapeAndAdd(StringBuffer sb, String text) {
- // TODO: On the move to 1.5 use StringBuffer append() overloads that
- // can take a CharSequence and a range of that CharSequence to speed
- // things up.
- // int last = 0;
- int count = text.length();
- for (int i = 0; i < count; i++) {
- char ch = text.charAt(i);
- switch (ch) {
- case '\t':
- // Micro-optimization: for syntax highlighting with
- // tab indentation, there are often multiple tabs
- // back-to-back at the start of lines, so don't put
- // spaces between each "\tab".
- sb.append("\\tab");
- while ((++i < count) && text.charAt(i) == '\t') {
- sb.append("\\tab");
- }
- sb.append(' ');
- i--; // We read one too far.
- break;
- case '\\':
- case '{':
- case '}':
- sb.append('\\').append(ch);
- break;
- default:
- sb.append(ch);
- break;
- }
- }
- }
-
- /**
- * Returns a font point size adjusted for the current screen resolution. Java2D assumes 72 dpi. On systems with
- * larger dpi (Windows, GTK, etc.), font rendering will appear to small if we simply return a Java "Font" object's
- * getSize() value. We need to adjust it for the screen resolution.
- *
- * @param pointSize
- * A Java Font's point size, as returned from \tab
"
- * getSize2D()
.
- * @return The font point size, adjusted for the current screen resolution. This will allow other applications to
- * render fonts the same size as they appear in the Java application.
- */
- private int fixFontSize(float pointSize) {
- if (screenRes != 72) { // Java2D assumes 72 dpi
- pointSize = (int) Math.round(pointSize * screenRes / 72.0);
- }
- return (int) pointSize;
- }
-
- private String getColorTableRtf() {
- // Example:
- // "{\\colortbl ;\\red255\\green0\\blue0;\\red0\\green0\\blue255; }"
- return IntStream.range(0, colorList.size()).mapToObj(i -> (Color) colorList.get(i)).map(c -> "\\red" + c.getRed() + "\\green" + c.getGreen() + "\\blue" + c.getBlue() + ';').collect(Collectors.joining("", "{\\colortbl ;", "}"));
- }
-
- /**
- * Returns the index of the specified font in a list of fonts. This method only checks for a font by its family
- * name; its attributes such as bold and italic are ignored.
- * String
.
- */
- public String getRtf() {
- return "{" +
- // Header
- "\\rtf1\\ansi\\ansicpg1252" +
- "\\deff0" + // First font in font table is the default
- "\\deflang1033" +
- "\\viewkind4" + // "Normal" view
- "\\uc\\pard\\f0" +
- "\\fs20" + // Font size in half-points (default 24)
- getFontTableRtf() + '\n' +
- getColorTableRtf() + '\n' +
- // Content
- document +
- "}";
- }
-
- /**
- * Resets this generator. All document information and content is cleared.
- */
- public void reset() {
- fontList.clear();
- colorList.clear();
- document.setLength(0);
- lastWasControlWord = false;
- lastFontIndex = 0;
- lastFGIndex = 0;
- lastBold = false;
- lastItalic = false;
- lastFontSize = DEFAULT_FONT_SIZE;
- screenRes = Toolkit.getDefaultToolkit().getScreenResolution();
- }
-
-}
\ No newline at end of file
+ private Color mainBG;
+ private List fontList;
+ private Listnull
, the
+ * default font is used.
+ * @param fg The foreground of the text. If this is null
,
+ * the default foreground color is used.
+ * @param bg The background color of the text. If this is
+ * null
, the default background color is used.
+ * @see #appendNewline()
+ */
+ public void appendToDoc(String text, Font f, Color fg, Color bg) {
+ appendToDoc(text, f, fg, bg, false);
+ }
+
+
+ /**
+ * Appends styled text to the RTF document being generated.
+ *
+ * @param text The text to append.
+ * @param f The font of the text. If this is null
, the
+ * default font is used.
+ * @param bg The background color of the text. If this is
+ * null
, the default background color is used.
+ * @param underline Whether the text should be underlined.
+ * @see #appendNewline()
+ */
+ public void appendToDocNoFG(String text, Font f, Color bg,
+ boolean underline) {
+ appendToDoc(text, f, null, bg, underline, false);
+ }
+
+
+ /**
+ * Appends styled text to the RTF document being generated.
+ *
+ * @param text The text to append.
+ * @param f The font of the text. If this is null
, the
+ * default font is used.
+ * @param fg The foreground of the text. If this is null
,
+ * the default foreground color is used.
+ * @param bg The background color of the text. If this is
+ * null
, the default background color is used.
+ * @param underline Whether the text should be underlined.
+ * @see #appendNewline()
+ */
+ public void appendToDoc(String text, Font f, Color fg, Color bg,
+ boolean underline) {
+ appendToDoc(text, f, fg, bg, underline, true);
+ }
+
+
+ /**
+ * Appends styled text to the RTF document being generated.
+ *
+ * @param text The text to append.
+ * @param f The font of the text. If this is null
, the
+ * default font is used.
+ * @param fg The foreground of the text. If this is null
,
+ * the default foreground color is used.
+ * @param bg The background color of the text. If this is
+ * null
, the default background color is used.
+ * @param underline Whether the text should be underlined.
+ * @param setFG Whether the foreground specified by fg
should
+ * be honored (if it is non-null
).
+ * @see #appendNewline()
+ */
+ public void appendToDoc(String text, Font f, Color fg, Color bg,
+ boolean underline, boolean setFG) {
+
+ if (text!=null) {
+
+ // Set font to use, if different from last addition.
+ int fontIndex = f==null ? 0 : (getFontIndex(fontList, f)+1);
+ if (fontIndex!=lastFontIndex) {
+ document.append("\\f").append(fontIndex);
+ lastFontIndex = fontIndex;
+ lastWasControlWord = true;
+ }
+
+ // Set styles to use.
+ if (f!=null) {
+ int fontSize = fixFontSize(f.getSize2D()*2); // Half points!
+ if (fontSize!=lastFontSize) {
+ document.append("\\fs").append(fontSize);
+ lastFontSize = fontSize;
+ lastWasControlWord = true;
+ }
+ if (f.isBold()!=lastBold) {
+ document.append(lastBold ? "\\b0" : "\\b");
+ lastBold = !lastBold;
+ lastWasControlWord = true;
+ }
+ if (f.isItalic()!=lastItalic) {
+ document.append(lastItalic ? "\\i0" : "\\i");
+ lastItalic = !lastItalic;
+ lastWasControlWord = true;
+ }
+ }
+ else { // No font specified - assume neither bold nor italic.
+ if (lastFontSize!=DEFAULT_FONT_SIZE) {
+ document.append("\\fs").append(DEFAULT_FONT_SIZE);
+ lastFontSize = DEFAULT_FONT_SIZE;
+ lastWasControlWord = true;
+ }
+ if (lastBold) {
+ document.append("\\b0");
+ lastBold = false;
+ lastWasControlWord = true;
+ }
+ if (lastItalic) {
+ document.append("\\i0");
+ lastItalic = false;
+ lastWasControlWord = true;
+ }
+ }
+ if (underline) {
+ document.append("\\ul");
+ lastWasControlWord = true;
+ }
+
+ // Set the foreground color.
+ if (setFG) {
+ int fgIndex = 0;
+ if (fg!=null) { // null => fg color index 0
+ fgIndex = getColorIndex(colorList, fg)+1;
+ }
+ if (fgIndex!=lastFGIndex) {
+ document.append("\\cf").append(fgIndex);
+ lastFGIndex = fgIndex;
+ lastWasControlWord = true;
+ }
+ }
+
+ // Set the background color.
+ if (bg!=null) {
+ int pos = getColorIndex(colorList, bg);
+ document.append("\\highlight").append(pos+1);
+ lastWasControlWord = true;
+ }
+
+ if (lastWasControlWord) {
+ document.append(' '); // Delimiter
+ lastWasControlWord = false;
+ }
+ escapeAndAdd(document, text);
+
+ // Reset everything that was set for this text fragment.
+ if (bg!=null) {
+ document.append("\\highlight0");
+ lastWasControlWord = true;
+ }
+ if (underline) {
+ document.append("\\ul0");
+ lastWasControlWord = true;
+ }
+
+ }
+
+ }
+
+
+ /**
+ * Appends some text to a buffer, with special care taken for special
+ * characters as defined by the RTF spec.
+ *
+ *
+ *
+ *
+ * @param text The text to append (with tab chars substituted).
+ * @param sb The buffer to append to.
+ */
+ private void escapeAndAdd(StringBuilder sb, String text) {
+ int count = text.length();
+ for (int i=0; i\tab
"
+ * getSize2D()
.
+ * @return The font point size, adjusted for the current screen resolution.
+ * This will allow other applications to render fonts the same
+ * size as they appear in the Java application.
+ */
+ private int fixFontSize(float pointSize) {
+ if (screenRes!=72) { // Java2D assumes 72 dpi
+ pointSize = Math.round(pointSize*72f/screenRes);
+ }
+ return (int)pointSize;
+ }
+
+
+ /**
+ * Returns the index of the specified item in a list. If the item
+ * is not in the list, it is added, and its new index is returned.
+ *
+ * @param list The list (possibly) containing the item.
+ * @param item The item to get the index of.
+ * @return The index of the item.
+ */
+ private static int getColorIndex(Listjava.awt.datatransfer.StringSelection
, except
- * that it can also return the text as RTF.
- *
- * @author Robert Futrell
- * @version 1.0
- */
-class RtfTransferable implements Transferable {
-
- /**
- * The RTF data, in bytes (the RTF is 7-bit ascii).
- */
- private byte[] data;
-
- /**
- * The "flavors" the text can be returned as.
- */
- private final DataFlavor[] FLAVORS = {
- new DataFlavor("text/rtf", "RTF"),
- DataFlavor.stringFlavor,
- DataFlavor.plainTextFlavor // deprecated
- };
-
- /**
- * Constructor.
- *
- * @param data
- * The RTF data.
- */
- public RtfTransferable(byte[] data) {
- this.data = data;
- }
-
- public Object getTransferData(DataFlavor flavor)
- throws UnsupportedFlavorException, IOException {
- if (flavor.equals(FLAVORS[0])) { // RTF
- return new ByteArrayInputStream(data == null ? new byte[0] : data);
- }
- else if (flavor.equals(FLAVORS[1])) { // stringFlavor
- return data == null ? "" : RtfToText.getPlainText(data);
- }
- else if (flavor.equals(FLAVORS[2])) { // plainTextFlavor (deprecated)
- String text = ""; // Valid if data==null
- if (data != null) {
- text = RtfToText.getPlainText(data);
- }
- return new StringReader(text);
- }
- else {
- throw new UnsupportedFlavorException(flavor);
- }
- }
-
- public DataFlavor[] getTransferDataFlavors() {
- return (DataFlavor[]) FLAVORS.clone();
- }
-
- public boolean isDataFlavorSupported(DataFlavor flavor) {
- return IntStream.range(0, FLAVORS.length).anyMatch(i -> flavor.equals(FLAVORS[i]));
- }
-
-}
\ No newline at end of file
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SelectRegionLinkGeneratorResult.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SelectRegionLinkGeneratorResult.java
new file mode 100755
index 000000000..26ee092dc
--- /dev/null
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SelectRegionLinkGeneratorResult.java
@@ -0,0 +1,61 @@
+/*
+ * 02/16/2012
+ *
+ * Copyright (C) 2013 Robert Futrell
+ * robert_futrell at users.sourceforge.net
+ * http://fifesoft.com/rsyntaxtextarea
+ *
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
+ */
+package org.fife.ui.rsyntaxtextarea;
+
+import javax.swing.event.HyperlinkEvent;
+
+
+/**
+ * A link generator result that selects a region of text in the text area.
+ * This will typically be used by IDE-style applications, to provide support
+ * for "linking" the use of a variable in a document to its declaration.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ * @see LinkGenerator
+ */
+public class SelectRegionLinkGeneratorResult implements LinkGeneratorResult {
+
+ private RSyntaxTextArea textArea;
+ private int sourceOffset;
+ private int selStart;
+ private int selEnd;
+
+
+ public SelectRegionLinkGeneratorResult(RSyntaxTextArea textArea,
+ int sourceOffset, int selStart, int selEnd) {
+ this.textArea = textArea;
+ this.sourceOffset = sourceOffset;
+ this.selStart = selStart;
+ this.selEnd = selEnd;
+ }
+
+
+ /**
+ * Selects the text in the text area.
+ */
+ @Override
+ public HyperlinkEvent execute() {
+ textArea.select(selStart, selEnd);
+ return null;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getSourceOffset() {
+ return sourceOffset;
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SquiggleUnderlineHighlightPainter.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SquiggleUnderlineHighlightPainter.java
old mode 100644
new mode 100755
index 5b7b2718f..05c5bf551
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SquiggleUnderlineHighlightPainter.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SquiggleUnderlineHighlightPainter.java
@@ -4,23 +4,9 @@
* SquiggleUnderlineHighlightPainter.java - Highlighter that draws a squiggle
* underline under "highlighted" text, similar to error markers in Microsoft
* Visual Studio or Eclipse.
- * Copyright (C) 2005 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -33,102 +19,104 @@
import javax.swing.text.Position;
import javax.swing.text.View;
+import org.fife.ui.rtextarea.ChangeableHighlightPainter;
+
+
/**
- * Highlight painter that paints a squiggly underline underneath text, similar to what popular IDE's such as Visual
- * Studio and Eclipse do to indicate errors, warnings, etc.
- * LayerPainter
.
- *
+ *
* @author Robert Futrell
* @version 1.0
*/
public class SquiggleUnderlineHighlightPainter
- extends ChangeableColorHighlightPainter {
-
- private static final int AMT = 2;
-
- /**
- * Constructor.
- *
- * @param color
- * The color of the squiggle. This cannot be null
.
- */
- public SquiggleUnderlineHighlightPainter(Color color) {
- super(color);
- setColor(color);
- }
-
- /**
- * Paints a portion of a highlight.
- *
- * @param g
- * the graphics context
- * @param offs0
- * the starting model offset >= 0
- * @param offs1
- * the ending model offset >= offs1
- * @param bounds
- * the bounding box of the view, which is not necessarily the region to paint.
- * @param c
- * the editor
- * @param view
- * View painting for
- * @return region drawing occurred in
- */
- public Shape paintLayer(Graphics g, int offs0, int offs1,
- Shape bounds, JTextComponent c, View view) {
-
- g.setColor(getColor());
-
- if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
- // Contained in view, can just use bounds.
- Rectangle alloc;
- if (bounds instanceof Rectangle)
- alloc = (Rectangle) bounds;
- else
- alloc = bounds.getBounds();
- paintSquiggle(g, alloc);
- return alloc;
- }
-
- // Otherwise, should only render part of View.
- try {
- // --- determine locations ---
- Shape shape = view.modelToView(offs0, Position.Bias.Forward,
- offs1, Position.Bias.Backward,
- bounds);
- Rectangle r = (shape instanceof Rectangle) ?
- (Rectangle) shape : shape.getBounds();
- paintSquiggle(g, r);
- return r;
- } catch (BadLocationException e) {
- e.printStackTrace(); // can't render
- }
-
- // Only if exception
- return null;
-
- }
-
- /**
- * Paints a squiggle underneath text in the specified rectangle.
- *
- * @param g
- * The graphics context with which to paint.
- * @param r
- * The rectangle containing the text.
- */
- protected void paintSquiggle(Graphics g, Rectangle r) {
- int x = r.x;
- int y = r.y + r.height - 1;
- int delta = -AMT;
- while (x < r.x + r.width) {
- g.drawLine(x, y, x + AMT, y + delta);
- y += delta;
- delta = -delta;
- x += AMT;
- }
- }
-
-}
\ No newline at end of file
+ extends ChangeableHighlightPainter {
+
+ private static final int AMT = 2;
+
+ /**
+ * Constructor.
+ *
+ * @param color The color of the squiggle. This cannot be
+ * null
.
+ */
+ public SquiggleUnderlineHighlightPainter(Color color) {
+ super(color);
+ setPaint(color);
+ }
+
+
+ /**
+ * Paints a portion of a highlight.
+ *
+ * @param g the graphics context
+ * @param offs0 the starting model offset >= 0
+ * @param offs1 the ending model offset >= offs1
+ * @param bounds the bounding box of the view, which is not
+ * necessarily the region to paint.
+ * @param c the editor
+ * @param view View painting for
+ * @return region drawing occurred in
+ */
+ @Override
+ public Shape paintLayer(Graphics g, int offs0, int offs1,
+ Shape bounds, JTextComponent c, View view) {
+
+ g.setColor((Color)getPaint());
+
+ if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
+ // Contained in view, can just use bounds.
+ Rectangle alloc;
+ if (bounds instanceof Rectangle) {
+ alloc = (Rectangle)bounds;
+ }
+ else {
+ alloc = bounds.getBounds();
+ }
+ paintSquiggle(g, alloc);
+ return alloc;
+ }
+
+ // Otherwise, should only render part of View.
+ try {
+ // --- determine locations ---
+ Shape shape = view.modelToView(offs0, Position.Bias.Forward,
+ offs1,Position.Bias.Backward,
+ bounds);
+ Rectangle r = (shape instanceof Rectangle) ?
+ (Rectangle)shape : shape.getBounds();
+ paintSquiggle(g, r);
+ return r;
+ } catch (BadLocationException e) {
+ e.printStackTrace(); // can't render
+ }
+
+ // Only if exception
+ return null;
+
+ }
+
+
+ /**
+ * Paints a squiggle underneath text in the specified rectangle.
+ *
+ * @param g The graphics context with which to paint.
+ * @param r The rectangle containing the text.
+ */
+ protected void paintSquiggle(Graphics g, Rectangle r) {
+ int x = r.x;
+ int y = r.y + r.height - AMT;
+ int delta = -AMT;
+ while (xStyle
; this Style
tells us the following things:
- *
+ * The color and style information for a token type. Each token type in an
+ * RSyntaxTextArea
has a corresponding Style
; this
+ * Style
tells us the following things:
+ *
*
- *
- *
+ *
* @author Robert Futrell
* @version 0.6
*/
+@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public class Style implements Cloneable {
- public static final Color DEFAULT_FOREGROUND = Color.BLACK;
- public static final Color DEFAULT_BACKGROUND = null;
- public static final Font DEFAULT_FONT = null;
-
- public Color foreground;
- public Color background;
- public boolean underline;
- public Font font;
-
- FontMetrics fontMetrics;
-
- /**
- * Creates a new syntax scheme defaulting to black foreground, no background, and no styling.
- */
- public Style() {
- this(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
- }
-
- /**
- * Creates a new syntax scheme with the specified colors and no styling.
- *
- * @param fg
- * The foreground color to use.
- * @param bg
- * The background color to use.
- */
- public Style(Color fg, Color bg) {
- this(fg, bg, DEFAULT_FONT);
- }
-
- /**
- * Creates a new syntax scheme.
- *
- * @param fg
- * The foreground color to use.
- * @param bg
- * The background color to use.
- * @param font
- * The font for this syntax scheme.
- */
- public Style(Color fg, Color bg, Font font) {
- this(fg, bg, font, false);
- }
-
- /**
- * Creates a new syntax scheme.
- *
- * @param fg
- * The foreground color to use.
- * @param bg
- * The background color to use.
- * @param font
- * The font for this syntax scheme.
- * @param underline
- * Whether or not to underline tokens with this style.
- */
- public Style(Color fg, Color bg, Font font, boolean underline) {
- foreground = fg;
- background = bg;
- this.font = font;
- this.underline = underline;
- this.fontMetrics = font == null ? null :
- new JPanel().getFontMetrics(font); // Default, no rendering hints!
- }
-
- /**
- * Returns whether or not two (possibly null
) objects are equal.
- */
- private boolean areEqual(Object o1, Object o2) {
- return (o1 == null && o2 == null) || (o1 != null && o1.equals(o2));
- }
-
- /**
- * Returns a deep copy of this object.
- *
- * @return The copy.
- */
- public Object clone() {
- Style clone = null;
- try {
- clone = (Style) super.clone();
- } catch (CloneNotSupportedException cnse) { // Never happens
- cnse.printStackTrace();
- return null;
- }
- clone.foreground = foreground;
- clone.background = background;
- clone.font = font;
- clone.underline = underline;
- clone.fontMetrics = fontMetrics;
- return clone;
- }
-
- /**
- * Returns whether or not two syntax schemes are equal.
- *
- * @param o2
- * The object with which to compare this syntax scheme.
- * @return Whether or not these two syntax schemes represent the same scheme.
- */
- public boolean equals(Object o2) {
- if (o2 instanceof Style) {
- Style ss2 = (Style) o2;
- if (this.underline == ss2.underline &&
- areEqual(foreground, ss2.foreground) &&
- areEqual(background, ss2.background) &&
- areEqual(font, ss2.font) &&
- areEqual(fontMetrics, ss2.fontMetrics))
- return true;
- }
- return false;
- }
-
- /**
- * Computes the hash code to use when adding this syntax scheme to hash tables.
- * null
) objects are
+ * equal.
+ */
+ private boolean areEqual(Object o1, Object o2) {
+ return (o1==null && o2==null) || (o1!=null && o1.equals(o2));
+ }
+
+
+ /**
+ * Returns a deep copy of this object.
+ *
+ * @return The copy.
+ */
+ @Override
+ public Object clone() {
+ Style clone = null;
+ try {
+ clone = (Style)super.clone();
+ } catch (CloneNotSupportedException cnse) { // Never happens
+ cnse.printStackTrace();
+ return null;
+ }
+ clone.foreground = foreground;
+ clone.background = background;
+ clone.font = font;
+ clone.underline = underline;
+ clone.fontMetrics = fontMetrics;
+ return clone;
+ }
+
+
+ /**
+ * Returns whether or not two syntax schemes are equal.
+ *
+ * @param o2 The object with which to compare this syntax scheme.
+ * @return Whether or not these two syntax schemes represent the same
+ * scheme.
+ */
+ @Override
+ public boolean equals(Object o2) {
+ if (o2 instanceof Style) {
+ Style ss2 = (Style)o2;
+ if (this.underline==ss2.underline &&
+ areEqual(foreground, ss2.foreground) &&
+ areEqual(background, ss2.background) &&
+ areEqual(font, ss2.font) &&
+ areEqual(fontMetrics, ss2.fontMetrics)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Computes the hash code to use when adding this syntax scheme to
+ * hash tables.java.awt.datatransfer.StringSelection
, except that it can also
+ * return the text in a couple of styled text formats.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+class StyledTextTransferable implements Transferable {
+
+ /**
+ * The transferred text, as HTML.
+ */
+ private String html;
+
+ /**
+ * The RTF data, in bytes (the RTF is 7-bit ascii).
+ */
+ private byte[] rtfBytes;
+
+
+ /**
+ * The "flavors" the text can be returned as.
+ */
+ private static final DataFlavor[] FLAVORS = {
+ DataFlavor.fragmentHtmlFlavor,
+ new DataFlavor("text/rtf", "RTF"),
+ DataFlavor.stringFlavor,
+ DataFlavor.plainTextFlavor // deprecated
+ };
+
+
+ /**
+ * Constructor.
+ *
+ * @param html The transferred text, as HTML.
+ * @param rtfBytes The transferred text, as RTF bytes.
+ */
+ StyledTextTransferable(String html, byte[] rtfBytes) {
+ this.html = html;
+ this.rtfBytes = rtfBytes;
+ }
+
+
+ @Override
+ public Object getTransferData(DataFlavor flavor)
+ throws UnsupportedFlavorException, IOException {
+
+ if (flavor.equals(FLAVORS[0])) { // HTML
+ return html;
+ }
+
+ else if (flavor.equals(FLAVORS[1])) { // RTF
+ return new ByteArrayInputStream(rtfBytes ==null ? new byte[0] : rtfBytes);
+ }
+
+ else if (flavor.equals(FLAVORS[2])) { // stringFlavor
+ return rtfBytes ==null ? "" : RtfToText.getPlainText(rtfBytes);
+ }
+
+ else if (flavor.equals(FLAVORS[3])) { // plainTextFlavor (deprecated)
+ String text = ""; // Valid if data==null
+ if (rtfBytes !=null) {
+ text = RtfToText.getPlainText(rtfBytes);
+ }
+ return new StringReader(text);
+ }
+
+ throw new UnsupportedFlavorException(flavor);
+ }
+
+
+ @Override
+ public DataFlavor[] getTransferDataFlavors() {
+ return FLAVORS.clone();
+ }
+
+
+ @Override
+ public boolean isDataFlavorSupported(DataFlavor flavor) {
+ for (DataFlavor flavor1 : FLAVORS) {
+ if (flavor.equals(flavor1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxConstants.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxConstants.java
old mode 100644
new mode 100755
index 23c162066..e0ce8330f
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxConstants.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxConstants.java
@@ -2,199 +2,325 @@
* 03/08/2004
*
* SyntaxConstants.java - Constants used by RSyntaxTextArea and friends.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
+
/**
- * Constants that define the different programming languages understood by RSyntaxTextArea
. These constants
- * are the values you can pass to {@link RSyntaxTextArea#setSyntaxEditingStyle(String)} to get syntax highlighting.
- * RSyntaxTextArea
s can render all of these languages, but this can be changed (the list
- * can be augmented or completely overwritten) on a per-text area basis. What languages can be rendered is actually
- * managed by the {@link TokenMakerFactory} installed on the text area's {@link RSyntaxDocument}. By default, all
- * RSyntaxDocumenet
s have a factory installed capable of handling all of these languages.
- *
+ * Constants that define the different programming languages understood by
+ * RSyntaxTextArea
. These constants are the values you can pass
+ * to {@link RSyntaxTextArea#setSyntaxEditingStyle(String)} to get syntax
+ * highlighting.RSyntaxTextArea
s can render all of these
+ * languages, but this can be changed (the list can be augmented or completely
+ * overwritten) on a per-text area basis. What languages can be rendered is
+ * actually managed by the {@link TokenMakerFactory} installed on the text
+ * area's {@link RSyntaxDocument}. By default, all
+ * RSyntaxDocumenet
s have a factory installed capable of handling
+ * all of these languages.
+ *
* @author Robert Futrell
* @version 1.0
*/
public interface SyntaxConstants {
- /**
- * Style meaning don't syntax highlight anything.
- */
- public static final String SYNTAX_STYLE_NONE = "text/plain";
-
- /**
- * Style for highlighting ActionScript.
- */
- public static final String SYNTAX_STYLE_ACTIONSCRIPT = "text/actionscript";
-
- /**
- * Style for highlighting x86 assembler.
- */
- public static final String SYNTAX_STYLE_ASSEMBLER_X86 = "text/asm";
-
- /**
- * Style for highlighting BBCode.
- */
- public static final String SYNTAX_STYLE_BBCODE = "text/bbcode";
-
- /**
- * Style for highlighting C.
- */
- public static final String SYNTAX_STYLE_C = "text/c";
-
- /**
- * Style for highlighting Clojure.
- */
- public static final String SYNTAX_STYLE_CLOJURE = "text/clojure";
-
- /**
- * Style for highlighting C++.
- */
- public static final String SYNTAX_STYLE_CPLUSPLUS = "text/cpp";
-
- /**
- * Style for highlighting C#.
- */
- public static final String SYNTAX_STYLE_CSHARP = "text/cs";
-
- /**
- * Style for highlighting CSS.
- */
- public static final String SYNTAX_STYLE_CSS = "text/css";
-
- /**
- * Style for highlighting Delphi/Pascal.
- */
- public static final String SYNTAX_STYLE_DELPHI = "text/delphi";
-
- /**
- * Style for highlighting Fortran.
- */
- public static final String SYNTAX_STYLE_FORTRAN = "text/fortran";
-
- /**
- * Style for highlighting Groovy.
- */
- public static final String SYNTAX_STYLE_GROOVY = "text/groovy";
-
- /**
- * Style for highlighting HTML.
- */
- public static final String SYNTAX_STYLE_HTML = "text/html";
-
- /**
- * Style for highlighting Java.
- */
- public static final String SYNTAX_STYLE_JAVA = "text/java";
-
- /**
- * Style for highlighting JavaScript.
- */
- public static final String SYNTAX_STYLE_JAVASCRIPT = "text/javascript";
-
- /**
- * Style for highlighting JSP.
- */
- public static final String SYNTAX_STYLE_JSP = "text/jsp";
-
- /**
- * Style for highlighting Lisp.
- */
- public static final String SYNTAX_STYLE_LISP = "text/lisp";
-
- /**
- * Style for highlighting Lua.
- */
- public static final String SYNTAX_STYLE_LUA = "text/lua";
-
- /**
- * Style for highlighting makefiles.
- */
- public static final String SYNTAX_STYLE_MAKEFILE = "text/makefile";
-
- /**
- * Style for highlighting MXML.
- */
- public static final String SYNTAX_STYLE_MXML = "text/mxml";
-
- /**
- * Style for highlighting Perl.
- */
- public static final String SYNTAX_STYLE_PERL = "text/perl";
-
- /**
- * Style for highlighting PHP.
- */
- public static final String SYNTAX_STYLE_PHP = "text/php";
-
- /**
- * Style for highlighting properties files.
- */
- public static final String SYNTAX_STYLE_PROPERTIES_FILE = "text/properties";
-
- /**
- * Style for highlighting Python.
- */
- public static final String SYNTAX_STYLE_PYTHON = "text/python";
-
- /**
- * Style for highlighting Ruby.
- */
- public static final String SYNTAX_STYLE_RUBY = "text/ruby";
-
- /**
- * Style for highlighting SAS keywords.
- */
- public static final String SYNTAX_STYLE_SAS = "text/sas";
-
- /**
- * Style for highlighting Scala.
- */
- public static final String SYNTAX_STYLE_SCALA = "text/scala";
-
- /**
- * Style for highlighting SQL.
- */
- public static final String SYNTAX_STYLE_SQL = "text/sql";
-
- /**
- * Style for highlighting Tcl.
- */
- public static final String SYNTAX_STYLE_TCL = "text/tcl";
-
- /**
- * Style for highlighting UNIX shell keywords.
- */
- public static final String SYNTAX_STYLE_UNIX_SHELL = "text/unix";
-
- /**
- * Style for highlighting Windows batch files.
- */
- public static final String SYNTAX_STYLE_WINDOWS_BATCH = "text/bat";
-
- /**
- * Style for highlighting XML.
- */
- public static final String SYNTAX_STYLE_XML = "text/xml";
-
-}
\ No newline at end of file
+ /**
+ * Style meaning don't syntax highlight anything.
+ */
+ String SYNTAX_STYLE_NONE = "text/plain";
+
+
+ /**
+ * Style for highlighting ActionScript.
+ */
+ String SYNTAX_STYLE_ACTIONSCRIPT = "text/actionscript";
+
+
+ /**
+ * Style for highlighting x86 assembler.
+ */
+ String SYNTAX_STYLE_ASSEMBLER_X86 = "text/asm";
+
+
+ /**
+ * Style for highlighting BBCode.
+ */
+ String SYNTAX_STYLE_BBCODE = "text/bbcode";
+
+
+ /**
+ * Style for highlighting C.
+ */
+ String SYNTAX_STYLE_C = "text/c";
+
+
+ /**
+ * Style for highlighting Clojure.
+ */
+ String SYNTAX_STYLE_CLOJURE = "text/clojure";
+
+
+ /**
+ * Style for highlighting C++.
+ */
+ String SYNTAX_STYLE_CPLUSPLUS = "text/cpp";
+
+
+ /**
+ * Style for highlighting C#.
+ */
+ String SYNTAX_STYLE_CSHARP = "text/cs";
+
+
+ /**
+ * Style for highlighting CSS.
+ */
+ String SYNTAX_STYLE_CSS = "text/css";
+
+
+ /**
+ * Style for highlighting CSV.
+ */
+ String SYNTAX_STYLE_CSV = "text/csv";
+
+
+ /**
+ * Syntax style for highlighting D.
+ */
+ String SYNTAX_STYLE_D = "text/d";
+
+
+ /**
+ * Syntax style for highlighting Dockerfiles.
+ */
+ String SYNTAX_STYLE_DOCKERFILE = "text/dockerfile";
+
+
+ /**
+ * Style for highlighting Dart.
+ */
+ String SYNTAX_STYLE_DART = "text/dart";
+
+
+ /**
+ * Style for highlighting Delphi/Pascal.
+ */
+ String SYNTAX_STYLE_DELPHI = "text/delphi";
+
+
+ /**
+ * Style for highlighting DTD files.
+ */
+ String SYNTAX_STYLE_DTD = "text/dtd";
+
+
+ /**
+ * Style for highlighting Fortran.
+ */
+ String SYNTAX_STYLE_FORTRAN = "text/fortran";
+
+
+ /**
+ * Style for highlighting go.
+ */
+ String SYNTAX_STYLE_GO = "text/golang";
+
+
+ /**
+ * Style for highlighting Groovy.
+ */
+ String SYNTAX_STYLE_GROOVY = "text/groovy";
+
+
+ /**
+ * Style for highlighting hosts files.
+ */
+ String SYNTAX_STYLE_HOSTS = "text/hosts";
+
+
+ /**
+ * Style for highlighting .htaccess files.
+ */
+ String SYNTAX_STYLE_HTACCESS = "text/htaccess";
+
+
+ /**
+ * Style for highlighting HTML.
+ */
+ String SYNTAX_STYLE_HTML = "text/html";
+
+
+ /**
+ * Style for highlighting INI files.
+ */
+ String SYNTAX_STYLE_INI = "text/ini";
+
+
+ /**
+ * Style for highlighting Java.
+ */
+ String SYNTAX_STYLE_JAVA = "text/java";
+
+
+ /**
+ * Style for highlighting JavaScript.
+ */
+ String SYNTAX_STYLE_JAVASCRIPT = "text/javascript";
+
+
+ /**
+ * Style for highlighting JSON.
+ */
+ String SYNTAX_STYLE_JSON = "text/json";
+
+
+ /**
+ * Style for highlighting .jshintrc files (JSON with comments, so can be
+ * used for other times when you want this behavior).
+ */
+ String SYNTAX_STYLE_JSON_WITH_COMMENTS = "text/jshintrc";
+
+
+ /**
+ * Style for highlighting JSP.
+ */
+ String SYNTAX_STYLE_JSP = "text/jsp";
+
+
+ /**
+ * Style for highlighting LaTeX.
+ */
+ String SYNTAX_STYLE_LATEX = "text/latex";
+
+
+ /**
+ * Style for highlighting Less.
+ */
+ String SYNTAX_STYLE_LESS = "text/less";
+
+
+ /**
+ * Style for highlighting Lisp.
+ */
+ String SYNTAX_STYLE_LISP = "text/lisp";
+
+
+ /**
+ * Style for highlighting Lua.
+ */
+ String SYNTAX_STYLE_LUA = "text/lua";
+
+
+ /**
+ * Style for highlighting makefiles.
+ */
+ String SYNTAX_STYLE_MAKEFILE = "text/makefile";
+
+
+ /**
+ * Style for highlighting MXML.
+ */
+ String SYNTAX_STYLE_MXML = "text/mxml";
+
+
+ /**
+ * Style for highlighting NSIS install scripts.
+ */
+ String SYNTAX_STYLE_NSIS = "text/nsis";
+
+
+ /**
+ * Style for highlighting Perl.
+ */
+ String SYNTAX_STYLE_PERL = "text/perl";
+
+
+ /**
+ * Style for highlighting PHP.
+ */
+ String SYNTAX_STYLE_PHP = "text/php";
+
+
+ /**
+ * Style for highlighting properties files.
+ */
+ String SYNTAX_STYLE_PROPERTIES_FILE = "text/properties";
+
+
+ /**
+ * Style for highlighting Python.
+ */
+ String SYNTAX_STYLE_PYTHON = "text/python";
+
+
+ /**
+ * Style for highlighting Ruby.
+ */
+ String SYNTAX_STYLE_RUBY = "text/ruby";
+
+
+ /**
+ * Style for highlighting SAS keywords.
+ */
+ String SYNTAX_STYLE_SAS = "text/sas";
+
+
+ /**
+ * Style for highlighting Scala.
+ */
+ String SYNTAX_STYLE_SCALA = "text/scala";
+
+
+ /**
+ * Style for highlighting SQL.
+ */
+ String SYNTAX_STYLE_SQL = "text/sql";
+
+
+ /**
+ * Style for highlighting Tcl.
+ */
+ String SYNTAX_STYLE_TCL = "text/tcl";
+
+
+ /**
+ * Style for highlighting TypeScript.
+ */
+ String SYNTAX_STYLE_TYPESCRIPT = "text/typescript";
+
+
+ /**
+ * Style for highlighting UNIX shell keywords.
+ */
+ String SYNTAX_STYLE_UNIX_SHELL = "text/unix";
+
+
+ /**
+ * Style for highlighting Visual Basic.
+ */
+ String SYNTAX_STYLE_VISUAL_BASIC = "text/vb";
+
+
+ /**
+ * Style for highlighting Windows batch files.
+ */
+ String SYNTAX_STYLE_WINDOWS_BATCH = "text/bat";
+
+
+ /**
+ * Style for highlighting XML.
+ */
+ String SYNTAX_STYLE_XML = "text/xml";
+
+
+ /**
+ * Syntax style for highlighting YAML.
+ */
+ String SYNTAX_STYLE_YAML = "text/yaml";
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxScheme.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxScheme.java
old mode 100644
new mode 100755
index 9e00de98f..6c5b9ea93
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxScheme.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/SyntaxScheme.java
@@ -3,23 +3,9 @@
*
* SyntaxScheme.java - The set of colors and tokens used by an RSyntaxTextArea
* to color tokens.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
@@ -29,7 +15,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
+
import javax.swing.text.StyleContext;
+
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -37,548 +25,703 @@
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
+
/**
- * The set of colors and styles used by an RSyntaxTextArea
to color tokens.
- *
+ * The set of colors and styles used by an RSyntaxTextArea
to
+ * color tokens.null
.
- *
- * @param useDefaults
- * If true
, all color values will be set to default colors; if false
, all
- * colors will be initially null
.
- */
- public SyntaxScheme(boolean useDefaults) {
- styles = new Style[Token.NUM_TOKEN_TYPES];
- if (useDefaults) {
- restoreDefaults(null);
- }
- }
-
- /**
- * Creates a default color scheme.
- *
- * @param baseFont
- * The base font to use. Keywords will be a bold version of this font, and comments will be an italicized
- * version of this font.
- */
- public SyntaxScheme(Font baseFont) {
- styles = new Style[Token.NUM_TOKEN_TYPES];
- restoreDefaults(baseFont);
- }
-
- /**
- * Changes the "base font" for this syntax scheme. This is called by RSyntaxTextArea
when its font
- * changes via setFont()
. This looks for tokens that use a derivative of the text area's old font (but
- * bolded and/or italicized) and make them use the new font with those stylings instead. This is desirable because
- * most programmers prefer a single font to be used in their text editor, but might want bold (say for keywords) or
- * italics.
- *
- * @param old
- * The old font of the text area.
- * @param font
- * The new font of the text area.
- */
- void changeBaseFont(Font old, Font font) {
- for (Style style : styles) {
- if (style != null && style.font != null) {
- if (style.font.getFamily().equals(old.getFamily()) &&
- style.font.getSize() == old.getSize()) {
- int s = style.font.getStyle(); // Keep bold or italic
- StyleContext sc = StyleContext.getDefaultStyleContext();
- style.font = sc.getFont(font.getFamily(), s, font.getSize());
- }
- }
- }
- }
-
- /**
- * Returns a deep copy of this color scheme.
- *
- * @return The copy.
- */
- public Object clone() {
- SyntaxScheme shcs = null;
- try {
- shcs = (SyntaxScheme) super.clone();
- } catch (CloneNotSupportedException cnse) { // Never happens
- cnse.printStackTrace();
- return null;
- }
- shcs.styles = new Style[Token.NUM_TOKEN_TYPES];
- for (int i = 0; i < Token.NUM_TOKEN_TYPES; i++) {
- Style s = styles[i];
- if (s != null) {
- shcs.styles[i] = (Style) s.clone();
- }
- }
- return shcs;
- }
-
- /**
- * Tests whether this color scheme is the same as another color scheme.
- *
- * @param otherScheme
- * The color scheme to compare to.
- * @return true
if this color scheme and otherScheme
are the same scheme;
- * false
otherwise.
- */
- public boolean equals(Object otherScheme) {
-
- // No need for null check; instanceof takes care of this for us,
- // i.e. "if (!(null instanceof Foo))" evaluates to "true".
- if (!(otherScheme instanceof SyntaxScheme)) {
- return false;
- }
-
- Style[] otherSchemes = ((SyntaxScheme) otherScheme).styles;
-
- int length = styles.length;
- for (int i = 0; i < length; i++) {
- if (styles[i] == null) {
- if (otherSchemes[i] != null) {
- return false;
- }
- }
- else if (!styles[i].equals(otherSchemes[i])) {
- return false;
- }
- }
- return true;
-
- }
-
- /**
- * Returns a hex string representing an RGB color, of the form "$rrggbb"
.
- *
- * @param c
- * The color.
- * @return The string representation of the color.
- */
- private static final String getHexString(Color c) {
- return "$" + Integer.toHexString((c.getRGB() & 0xffffff) + 0x1000000).
- substring(1);
- }
-
- /**
- * This is implemented to be consistent with {@link #equals(Object)}. This is a requirement to keep FindBugs happy.
- *
- * @return The hash code for this object.
- */
- public int hashCode() {
- // Keep me fast. Iterating over *all* syntax schemes contained is
- // probably much slower than a "bad" hash code here.
- int hashCode = 0;
- int count = styles.length;
- for (Style style : styles) {
- if (style != null) {
- hashCode ^= style.hashCode();
- break;
- }
- }
- return hashCode;
- }
-
- /**
- * Loads a syntax scheme from an input stream.
- *
- * @param baseFont
- * The font to use as the "base" for the syntax scheme. If this is null
, a default
- * monospaced font is used.
- * @param in
- * The stream to load from. It is up to the caller to close this stream when they are done.
- * @return The syntax scheme.
- * @throws IOException
- * If an IO error occurs.
- */
- public static SyntaxScheme load(Font baseFont, InputStream in)
- throws IOException {
- if (baseFont == null) {
- baseFont = RSyntaxTextArea.getDefaultFont();
- }
- return XmlParser.load(baseFont, in);
- }
-
- /**
- * Loads a syntax highlighting color scheme from a string created from toCommaSeparatedString
. This
- * method is useful for saving and restoring color schemes.
- *
- * @param string
- * A string generated from {@link #toCommaSeparatedString()}.
- * @return A color scheme.
- */
- public static SyntaxScheme loadFromString(String string) {
-
- SyntaxScheme scheme = new SyntaxScheme(true);
-
- try {
-
- if (string != null) {
-
- String[] tokens = string.split(",", -1);
-
- // Check the version string, use defaults if incompatible
- if (tokens.length == 0 || !VERSION.equals(tokens[0])) {
- return scheme; // Still set to defaults
- }
-
- int tokenTypeCount = Token.NUM_TOKEN_TYPES;
- int tokenCount = tokenTypeCount * 7 + 1; // Version string
- if (tokens.length != tokenCount) {
- throw new Exception(
- "Not enough tokens in packed color scheme: expected " +
- tokenCount + ", found " + tokens.length);
- }
-
- // Loop through each token style. Format:
- // "index,(fg|-),(bg|-),(t|f),((font,style,size)|(-,,))"
- for (int i = 0; i < tokenTypeCount; i++) {
-
- int pos = i * 7 + 1;
- int integer = Integer.parseInt(tokens[pos]); // == i
- if (integer != i)
- throw new Exception("Expected " + i + ", found " +
- integer);
-
- Color fg = null;
- String temp = tokens[pos + 1];
- if (!"-".equals(temp)) { // "-" => keep fg as null
- fg = stringToColor(temp);
- }
- Color bg = null;
- temp = tokens[pos + 2];
- if (!"-".equals(temp)) { // "-" => keep bg as null
- bg = stringToColor(temp);
- }
-
- // Check for "true" or "false" since we don't want to
- // accidentally suck in an int representing the next
- // packed color, and any string != "true" means false.
- temp = tokens[pos + 3];
- if (!"t".equals(temp) && !"f".equals(temp))
- throw new Exception("Expected 't' or 'f', found " + temp);
- boolean underline = "t".equals(temp);
-
- Font font = null;
- String family = tokens[pos + 4];
- if (!"-".equals(family)) {
- font = new Font(family,
- Integer.parseInt(tokens[pos + 5]), // style
- Integer.parseInt(tokens[pos + 6])); // size
- }
- scheme.styles[i] = new Style(fg, bg, font, underline);
-
- }
-
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return scheme;
-
- }
-
- void refreshFontMetrics(Graphics2D g2d) {
- // It is assumed that any rendering hints are already applied to g2d.
- for (Style s : styles) {
- if (s != null) {
- s.fontMetrics = s.font == null ? null :
- g2d.getFontMetrics(s.font);
- }
- }
- }
-
- /**
- * Restores all colors and fonts to their default values.
- *
- * @param baseFont
- * The base font to use when creating this scheme. If this is null
, then a default
- * monospaced font is used.
- */
- public void restoreDefaults(Font baseFont) {
-
- // Colors used by tokens.
- Color comment = new Color(0, 128, 0);
- Color docComment = new Color(164, 0, 0);
- Color keyword = Color.BLUE;
- Color function = new Color(173, 128, 0);
- Color literalNumber = new Color(100, 0, 200);
- Color literalString = new Color(220, 0, 156);
- Color error = new Color(148, 148, 0);
-
- // Special fonts.
- if (baseFont == null) {
- baseFont = RSyntaxTextArea.getDefaultFont();
- }
- // WORKAROUND for Sun JRE bug 6282887 (Asian font bug in 1.4/1.5)
- StyleContext sc = StyleContext.getDefaultStyleContext();
- Font boldFont = sc.getFont(baseFont.getFamily(), Font.BOLD,
- baseFont.getSize());
- Font italicFont = sc.getFont(baseFont.getFamily(), Font.ITALIC,
- baseFont.getSize());
- Font commentFont = italicFont;// baseFont.deriveFont(Font.ITALIC);
- Font keywordFont = boldFont;// baseFont.deriveFont(Font.BOLD);
-
- styles[Token.COMMENT_EOL] = new Style(comment, null, commentFont);
- styles[Token.COMMENT_MULTILINE] = new Style(comment, null, commentFont);
- styles[Token.COMMENT_DOCUMENTATION] = new Style(docComment, null, commentFont);
- styles[Token.RESERVED_WORD] = new Style(keyword, null, keywordFont);
- styles[Token.FUNCTION] = new Style(function, null);
- styles[Token.LITERAL_BOOLEAN] = new Style(literalNumber, null);
- styles[Token.LITERAL_NUMBER_DECIMAL_INT] = new Style(literalNumber, null);
- styles[Token.LITERAL_NUMBER_FLOAT] = new Style(literalNumber, null);
- styles[Token.LITERAL_NUMBER_HEXADECIMAL] = new Style(literalNumber, null);
- styles[Token.LITERAL_STRING_DOUBLE_QUOTE] = new Style(literalString, null);
- styles[Token.LITERAL_CHAR] = new Style(literalString, null);
- styles[Token.LITERAL_BACKQUOTE] = new Style(literalString, null);
- styles[Token.DATA_TYPE] = new Style(new Color(0, 128, 128), null);
- styles[Token.VARIABLE] = new Style(new Color(255, 153, 0), null);
- styles[Token.IDENTIFIER] = new Style(null, null);
- styles[Token.WHITESPACE] = new Style(Color.gray, null);
- styles[Token.SEPARATOR] = new Style(Color.RED, null);
- styles[Token.OPERATOR] = new Style(new Color(128, 64, 64), null);
- styles[Token.PREPROCESSOR] = new Style(new Color(128, 128, 128), null);
- styles[Token.MARKUP_TAG_DELIMITER] = new Style(Color.RED, null);
- styles[Token.MARKUP_TAG_NAME] = new Style(Color.BLUE, null);
- styles[Token.MARKUP_TAG_ATTRIBUTE] = new Style(new Color(63, 127, 127), null);
- // styles[Token.ERROR] = null;
- styles[Token.ERROR_IDENTIFIER] = new Style(error, null);
- styles[Token.ERROR_NUMBER_FORMAT] = new Style(error, null);
- styles[Token.ERROR_STRING_DOUBLE] = new Style(error, null);
- styles[Token.ERROR_CHAR] = new Style(error, null);
-
- }
-
- /**
- * Sets a style to use when rendering a token type.
- *
- * @param type
- * The token type.
- * @param style
- * The style for the token type.
- */
- public void setStyle(int type, Style style) {
- styles[type] = style;
- }
-
- /**
- * Returns the color represented by a string. If the first char in the string is '$
', it is assumed to
- * be in hex, otherwise it is assumed to be decimal. So, for example, both of these:
- *
- *
- * "$00ff00"
- * "65280"
- *
- *
- * will return new Color(0, 255, 0)
.
- *
- * @param s
- * The string to evaluate.
- * @return The color.
- */
- private static final Color stringToColor(String s) {
- // Check for decimal as well as hex, for backward
- // compatibility (fix from GwynEvans on forums)
- char ch = s.charAt(0);
- return new Color((ch == '$' || ch == '#') ?
- Integer.parseInt(s.substring(1), 16) :
- Integer.parseInt(s));
- }
-
- /**
- * Returns this syntax highlighting scheme as a comma-separated list of values as follows:
- *
- *
- *
- * @return A string representing the rgb values of the colors.
- */
- public String toCommaSeparatedString() {
-
- StringBuffer sb = new StringBuffer(VERSION);
- sb.append(',');
-
- for (int i = 0; i < Token.NUM_TOKEN_TYPES; i++) {
-
- sb.append(i).append(',');
-
- Style ss = styles[i];
- if (ss == null) { // Only true for i==0 (NULL token)
- sb.append("-,-,f,-,,,");
- continue;
- }
-
- Color c = ss.foreground;
- sb.append(c != null ? (getHexString(c) + ",") : "-,");
- c = ss.background;
- sb.append(c != null ? (getHexString(c) + ",") : "-,");
-
- sb.append(ss.underline ? "t," : "f,");
-
- Font font = ss.font;
- if (font != null) {
- sb.append(font.getFamily()).append(',').
- append(font.getStyle()).append(',').
- append(font.getSize()).append(',');
- }
- else {
- sb.append("-,,,");
- }
-
- }
-
- return sb.substring(0, sb.length() - 1); // Take off final ','.
-
- }
-
- /**
- * Loads a ((r<<16) | (g<<8) | (b))
; if
- * it is null
, it is added as "-,".
- *
- *
- * i
is the index of the syntax scheme.
- * -
).
- * uline
is whether or not the font should be underlined, and is either t
or
- * f
.
- * style
is the family,style,size
triplet described above.
- * SyntaxScheme
from an XML file.
- */
- private static class XmlParser extends DefaultHandler {
-
- private Font baseFont;
- private SyntaxScheme scheme;
-
- public XmlParser(Font baseFont) {
- scheme = new SyntaxScheme(baseFont);
- }
-
- /**
- * Creates the XML reader to use. Note that in 1.4 JRE's, the reader class wasn't defined by default, but in
- * 1.5+ it is.
- *
- * @return The XML reader to use.
- */
- private static XMLReader createReader() throws IOException {
- XMLReader reader = null;
- try {
- reader = XMLReaderFactory.createXMLReader();
- } catch (SAXException e) {
- // Happens in JRE 1.4.x; 1.5+ define the reader class properly
- try {
- reader = XMLReaderFactory.createXMLReader(
- "org.apache.crimson.parser.XMLReaderImpl");
- } catch (SAXException se) {
- throw new IOException(se.toString());
- }
- }
- return reader;
- }
-
- public static SyntaxScheme load(Font baseFont,
- InputStream in) throws IOException {
- XMLReader reader = createReader();
- XmlParser parser = new XmlParser(baseFont);
- parser.baseFont = baseFont;
- reader.setContentHandler(parser);
- InputSource is = new InputSource(in);
- is.setEncoding("UTF-8");
- try {
- reader.parse(is);
- } catch (SAXException se) {
- throw new IOException(se.toString());
- }
- return parser.scheme;
- }
-
- public void startElement(String uri, String localName, String qName,
- Attributes attrs) {
-
- if ("style".equals(qName)) {
-
- String type = attrs.getValue("token");
- Field field = null;
- try {
- field = Token.class.getField(type);
- } catch (RuntimeException re) {
- throw re; // FindBugs
- } catch (Exception e) {
- System.err.println("Invalid token type: " + type);
- return;
- }
-
- if (field.getType() == int.class) {
-
- int index = 0;
- try {
- index = field.getInt(scheme);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- return;
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- return;
- }
-
- String fgStr = attrs.getValue("fg");
- if (fgStr != null) {
- Color fg = stringToColor(fgStr);
- scheme.styles[index].foreground = fg;
- }
-
- String bgStr = attrs.getValue("bg");
- if (bgStr != null) {
- scheme.styles[index].background = stringToColor(bgStr);
- }
-
- boolean styleSpecified = false;
- boolean bold = false;
- boolean italic = false;
- String boldStr = attrs.getValue("bold");
- if (boldStr != null) {
- bold = Boolean.valueOf(boldStr);
- styleSpecified = true;
- }
- String italicStr = attrs.getValue("italic");
- if (italicStr != null) {
- italic = Boolean.valueOf(italicStr);
- styleSpecified = true;
- }
- if (styleSpecified) {
- int style = 0;
- if (bold) {
- style |= Font.BOLD;
- }
- if (italic) {
- style |= Font.ITALIC;
- }
- scheme.styles[index].font = baseFont.deriveFont(style);
- }
-
- String ulineStr = attrs.getValue("underline");
- if (ulineStr != null) {
- boolean uline = Boolean.valueOf(ulineStr);
- scheme.styles[index].underline = uline;
- }
-
- }
-
- }
-
- }
-
- }
-
-}
\ No newline at end of file
+@SuppressWarnings({ "checkstyle:magicnumber" })
+public class SyntaxScheme implements Cloneable, TokenTypes {
+
+ private Style[] styles;
+
+ private static final String VERSION = "*ver1";
+
+
+ /**
+ * Creates a color scheme that either has all color values set to
+ * a default value or set to null
.
+ *
+ * @param useDefaults If true
, all color values will
+ * be set to default colors; if false
, all colors
+ * will be initially null
.
+ */
+ public SyntaxScheme(boolean useDefaults) {
+ styles = new Style[DEFAULT_NUM_TOKEN_TYPES];
+ if (useDefaults) {
+ restoreDefaults(null);
+ }
+ }
+
+
+ /**
+ * Creates a default color scheme.
+ *
+ * @param baseFont The base font to use. Keywords will be a bold version
+ * of this font, and comments will be an italicized version of this
+ * font.
+ */
+ public SyntaxScheme(Font baseFont) {
+ this(baseFont, true);
+ }
+
+
+ /**
+ * Creates a default color scheme.
+ *
+ * @param baseFont The base font to use. Keywords will be a bold version
+ * of this font, and comments will be an italicized version of this
+ * font.
+ * @param fontStyles Whether bold and italic should be used in the scheme
+ * (vs. all tokens using a plain font).
+ */
+ public SyntaxScheme(Font baseFont, boolean fontStyles) {
+ styles = new Style[DEFAULT_NUM_TOKEN_TYPES];
+ restoreDefaults(baseFont, fontStyles);
+ }
+
+
+ /**
+ * Changes the "base font" for this syntax scheme. This is called by
+ * RSyntaxTextArea
when its font changes via
+ * setFont()
. This looks for tokens that use a derivative of
+ * the text area's old font (but bolded and/or italicized) and make them
+ * use the new font with those stylings instead. This is desirable because
+ * most programmers prefer a single font to be used in their text editor,
+ * but might want bold (say for keywords) or italics.
+ *
+ * @param old The old font of the text area.
+ * @param font The new font of the text area.
+ */
+ void changeBaseFont(Font old, Font font) {
+ for (Style style : styles) {
+ if (style != null && style.font != null) {
+ if (style.font.getFamily().equals(old.getFamily()) &&
+ style.font.getSize() == old.getSize()) {
+ int s = style.font.getStyle(); // Keep bold or italic
+ StyleContext sc = StyleContext.getDefaultStyleContext();
+ style.font = sc.getFont(font.getFamily(), s, font.getSize());
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Returns a deep copy of this color scheme.
+ *
+ * @return The copy.
+ */
+ @Override
+ public Object clone() {
+ SyntaxScheme shcs = null;
+ try {
+ shcs = (SyntaxScheme)super.clone();
+ } catch (CloneNotSupportedException cnse) { // Never happens
+ cnse.printStackTrace();
+ return null;
+ }
+ shcs.styles = new Style[styles.length];
+ for (int i=0; iotherScheme
are the same scheme;
+ * false
otherwise.
+ */
+ @Override
+ public boolean equals(Object otherScheme) {
+
+ // No need for null check; instanceof takes care of this for us,
+ // i.e. "if (!(null instanceof Foo))" evaluates to "true".
+ if (!(otherScheme instanceof SyntaxScheme)) {
+ return false;
+ }
+
+ Style[] otherSchemes = ((SyntaxScheme)otherScheme).styles;
+
+ int length = styles.length;
+ for (int i=0; iRSyntaxTextArea
+ * using this scheme.
+ *
+ * @return The style array.
+ * @see #setStyles(Style[])
+ */
+ public Style[] getStyles() {
+ return styles;
+ }
+
+
+ /**
+ * This is implemented to be consistent with {@link #equals(Object)}.
+ * This is a requirement to keep FindBugs happy.
+ *
+ * @return The hash code for this object.
+ */
+ @Override
+ public int hashCode() {
+ // Keep me fast. Iterating over *all* syntax schemes contained is
+ // probably much slower than a "bad" hash code here.
+ int hashCode = 0;
+ int count = styles.length;
+ for (Style style : styles) {
+ if (style != null) {
+ hashCode ^= style.hashCode();
+ break;
+ }
+ }
+ return hashCode;
+ }
+
+
+ /**
+ * Loads a syntax scheme from an input stream.null
, a default monospaced font is used.
+ * @param in The stream to load from. It is up to the caller to close this
+ * stream when they are done.
+ * @return The syntax scheme.
+ * @throws IOException If an IO error occurs.
+ */
+ public static SyntaxScheme load(Font baseFont, InputStream in)
+ throws IOException {
+ if (baseFont==null) {
+ baseFont = RSyntaxTextArea.getDefaultFont();
+ }
+ return SyntaxSchemeLoader.load(baseFont, in);
+ }
+
+
+ /**
+ * Loads a syntax highlighting color scheme from a string created from
+ * toCommaSeparatedString
. This method is useful for saving
+ * and restoring color schemes.toCommaSeparatedString
. This method is useful for saving
+ * and restoring color schemes.null
, then a default monospaced font is
+ * used.
+ */
+ public void restoreDefaults(Font baseFont) {
+ restoreDefaults(baseFont, true);
+ }
+
+
+ /**
+ * Restores all colors and fonts to their default values.
+ *
+ * @param baseFont The base font to use when creating this scheme. If
+ * this is null
, then a default monospaced font is
+ * used.
+ * @param fontStyles Whether bold and italic should be used in the scheme
+ * (vs. all tokens using a plain font).
+ */
+ public void restoreDefaults(Font baseFont, boolean fontStyles) {
+
+ // Colors used by tokens.
+ Color comment = new Color(0,128,0);
+ Color docComment = new Color(164,0,0);
+ Color markupComment = new Color(0, 96, 0);
+ Color keyword = Color.BLUE;
+ Color dataType = new Color(0,128,128);
+ Color function = new Color(173,128,0);
+ Color preprocessor = new Color(128,128,128);
+ Color operator = new Color(128, 64, 64);
+ Color regex = new Color(0,128,164);
+ Color variable = new Color(255,153,0);
+ Color literalNumber = new Color(100,0,200);
+ Color literalString = new Color(220,0,156);
+ Color error = new Color(148,148,0);
+
+ // (Possible) special font styles for keywords and comments.
+ if (baseFont==null) {
+ baseFont = RSyntaxTextArea.getDefaultFont();
+ }
+ Font commentFont = baseFont;
+ Font keywordFont = baseFont;
+ if (fontStyles) {
+ // WORKAROUND for Sun JRE bug 6282887 (Asian font bug in 1.4/1.5)
+ // That bug seems to be hidden now, see 6289072 instead.
+ StyleContext sc = StyleContext.getDefaultStyleContext();
+ Font boldFont = sc.getFont(baseFont.getFamily(), Font.BOLD,
+ baseFont.getSize());
+ Font italicFont = sc.getFont(baseFont.getFamily(), Font.ITALIC,
+ baseFont.getSize());
+ commentFont = italicFont;//baseFont.deriveFont(Font.ITALIC);
+ keywordFont = boldFont;//baseFont.deriveFont(Font.BOLD);
+ }
+
+ styles[COMMENT_EOL] = new Style(comment, null, commentFont);
+ styles[COMMENT_MULTILINE] = new Style(comment, null, commentFont);
+ styles[COMMENT_DOCUMENTATION] = new Style(docComment, null, commentFont);
+ styles[COMMENT_KEYWORD] = new Style(new Color(255,152,0), null, commentFont);
+ styles[COMMENT_MARKUP] = new Style(Color.gray, null, commentFont);
+ styles[RESERVED_WORD] = new Style(keyword, null, keywordFont);
+ styles[RESERVED_WORD_2] = new Style(keyword, null, keywordFont);
+ styles[FUNCTION] = new Style(function);
+ styles[LITERAL_BOOLEAN] = new Style(literalNumber);
+ styles[LITERAL_NUMBER_DECIMAL_INT] = new Style(literalNumber);
+ styles[LITERAL_NUMBER_FLOAT] = new Style(literalNumber);
+ styles[LITERAL_NUMBER_HEXADECIMAL] = new Style(literalNumber);
+ styles[LITERAL_STRING_DOUBLE_QUOTE] = new Style(literalString);
+ styles[LITERAL_CHAR] = new Style(literalString);
+ styles[LITERAL_BACKQUOTE] = new Style(literalString);
+ styles[DATA_TYPE] = new Style(dataType, null, keywordFont);
+ styles[VARIABLE] = new Style(variable);
+ styles[REGEX] = new Style(regex);
+ styles[ANNOTATION] = new Style(Color.gray);
+ styles[IDENTIFIER] = new Style(null);
+ styles[WHITESPACE] = new Style(Color.gray);
+ styles[SEPARATOR] = new Style(Color.RED);
+ styles[OPERATOR] = new Style(operator);
+ styles[PREPROCESSOR] = new Style(preprocessor);
+ styles[MARKUP_TAG_DELIMITER] = new Style(Color.RED);
+ styles[MARKUP_TAG_NAME] = new Style(Color.BLUE);
+ styles[MARKUP_TAG_ATTRIBUTE] = new Style(new Color(63,127,127));
+ styles[MARKUP_TAG_ATTRIBUTE_VALUE]= new Style(literalString);
+ styles[MARKUP_COMMENT] = new Style(markupComment, null, commentFont);
+ styles[MARKUP_DTD] = new Style(function);
+ styles[MARKUP_PROCESSING_INSTRUCTION] = new Style(preprocessor);
+ styles[MARKUP_CDATA] = new Style(new Color(0xcc6600));
+ styles[MARKUP_CDATA_DELIMITER] = new Style(new Color(0x008080));
+ styles[MARKUP_ENTITY_REFERENCE] = new Style(dataType);
+ styles[ERROR_IDENTIFIER] = new Style(error);
+ styles[ERROR_NUMBER_FORMAT] = new Style(error);
+ styles[ERROR_STRING_DOUBLE] = new Style(error);
+ styles[ERROR_CHAR] = new Style(error);
+
+ // Issue #34: If an application modifies TokenTypes to add new built-in
+ // token types, we'll get NPEs if not all styles are initialized.
+ for (int i=0; i
+ * "$00ff00"
+ * "65280"
+ *
+ * will return new Color(0, 255, 0)
.
+ *
+ * @param s The string to evaluate.
+ * @return The color.
+ */
+ private static Color stringToColor(String s) {
+ // Check for decimal as well as hex, for backward
+ // compatibility (fix from GwynEvans on forums)
+ char ch = s.charAt(0);
+ return new Color((ch=='$' || ch=='#') ?
+ Integer.parseInt(s.substring(1),16) :
+ Integer.parseInt(s));
+ }
+
+
+ /**
+ * Returns this syntax highlighting scheme as a comma-separated list of
+ * values as follows:
+ *
+ *
+ *
+ * @return A string representing the rgb values of the colors.
+ * @see #loadFromString(String)
+ */
+ public String toCommaSeparatedString() {
+
+ StringBuilder sb = new StringBuilder(VERSION);
+ sb.append(',');
+
+ for (int i=0; i((r<*lt;16) | (g<*lt;8) | (b))
; if
+ * it is null
, it is added as "-,".
+ *
+ *
+ * i
is the index of the syntax scheme.
+ * -
).
+ * uline
is whether or not the font should be
+ * underlined, and is either t
or f
.
+ * style
is the family,style,size
+ * triplet described above.
+ * javax.swing.text.View
object used by {@link RSyntaxTextArea} when word wrap is disabled. It
- * implements syntax highlighting for programming languages using the colors and font styles specified by the
- * RSyntaxTextArea
.
- * javax.swing.text.View
object used by {@link RSyntaxTextArea}
+ * when word wrap is disabled. It implements syntax highlighting for
+ * programming languages using the colors and font styles specified by the
+ * RSyntaxTextArea
.SyntaxView
wrapped around an element.
- *
- * @param elem
- * The element representing the text to display.
- */
- public SyntaxView(Element elem) {
- super(elem);
- }
-
- /**
- * Iterate over the lines represented by the child elements of the element this view represents, looking for the
- * line that is the longest. The longLine variable is updated to represent the longest line contained. The
- * font variable is updated to indicate the font used to calculate the longest line.
- */
- void calculateLongestLine() {
- Component c = getContainer();
- font = c.getFont();
- metrics = c.getFontMetrics(font);
- tabSize = getTabSize() * metrics.charWidth(' ');
- Element lines = getElement();
- int n = lines.getElementCount();
- for (int i = 0; i < n; i++) {
- Element line = lines.getElement(i);
- float w = getLineWidth(i);
- if (w > longLineWidth) {
- longLineWidth = w;
- longLine = line;
- }
- }
- }
-
- /**
- * Gives notification from the document that attributes were changed in a location that this view is responsible
- * for.
- *
- * @param changes
- * the change information from the associated document
- * @param a
- * the current allocation of the view
- * @param f
- * the factory to use to rebuild if the view has children
- * @see View#changedUpdate
- */
- public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
- updateDamage(changes, a, f);
- }
-
- /**
- * Repaint the given line range.
- *
- * @param line0
- * The starting line number to repaint. This must be a valid line number in the model.
- * @param line1
- * The ending line number to repaint. This must be a valid line number in the model.
- * @param a
- * The region allocated for the view to render into.
- * @param host
- * The component hosting the view (used to call repaint).
- */
- protected void damageLineRange(int line0, int line1, Shape a,
- Component host) {
- if (a != null) {
- Rectangle area0 = lineToRect(a, line0);
- Rectangle area1 = lineToRect(a, line1);
- if ((area0 != null) && (area1 != null)) {
- Rectangle dmg = area0.union(area1); // damage.
- host.repaint(dmg.x, dmg.y, dmg.width, dmg.height);
- }
- else
- host.repaint();
- }
- }
-
- /**
- * Draws the passed-in text using syntax highlighting for the current language. The tokens used to decide how to
- * paint the syntax highlighting are grabbed from the text area's document.
- *
- * @param token
- * The list of tokens to draw.
- * @param g
- * The graphics context in which to draw.
- * @param x
- * The x-coordinate at which to draw.
- * @param y
- * The y-coordinate at which to draw.
- * @return The x-coordinate representing the end of the painted text.
- */
- public float drawLine(Token token, Graphics2D g, float x, float y) {
-
- float nextX = x; // The x-value at the end of our text.
-
- while (token != null && token.isPaintable() && nextX < clipEnd) {
- nextX = token.paint(g, nextX, y, host, this, clipStart);
- token = token.getNextToken();
- }
-
- // NOTE: We should re-use code from Token (paintBackground()) here,
- // but don't because I'm just too lazy.
- if (host.getEOLMarkersVisible()) {
- g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
- g.setFont(host.getFontForTokenType(Token.WHITESPACE));
- g.drawString("\u00B6", nextX, y);
- }
-
- // Return the x-coordinate at the end of the painted text.
- return nextX;
-
- }
-
- /**
- * Calculates the width of the line represented by the given element.
- *
- * @param line
- * The line for which to get the length.
- * @param lineNumber
- * The line number of the specified line in the document.
- * @return The width of the line.
- */
- private float getLineWidth(int lineNumber) {
- Token tokenList = ((RSyntaxDocument) getDocument()).
- getTokenListForLine(lineNumber);
- return RSyntaxUtilities.getTokenListWidth(tokenList,
- (RSyntaxTextArea) getContainer(),
- this);
- }
-
- /**
- * Provides a way to determine the next visually represented model location that one might place a caret. Some views
- * may not be visible, they might not be in the same order found in the model, or they just might not allow access
- * to some of the locations in the model.
- *
- * @param pos
- * the position to convert >= 0
- * @param a
- * the allocated region to render into
- * @param direction
- * the direction from the current position that can be thought of as the arrow keys typically found on a
- * keyboard. This may be SwingConstants.WEST, SwingConstants.EAST, SwingConstants.NORTH, or
- * SwingConstants.SOUTH.
- * @return the location within the model that best represents the next location visual position.
- * @exception BadLocationException
- * @exception IllegalArgumentException
- * for an invalid direction
- */
- public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
- int direction, Position.Bias[] biasRet)
- throws BadLocationException {
- return RSyntaxUtilities.getNextVisualPositionFrom(pos, b, a,
- direction, biasRet, this);
- }
-
- /**
- * Determines the preferred span for this view along an axis.
- *
- * @param axis
- * may be either View.X_AXIS or View.Y_AXIS
- * @return the span the view would like to be rendered into >= 0. Typically the view is told to render into the span
- * that is returned, although there is no guarantee. The parent may choose to resize or break the view.
- * @exception IllegalArgumentException
- * for an invalid axis
- */
- public float getPreferredSpan(int axis) {
- updateMetrics();
- switch (axis) {
- case View.X_AXIS:
- float span = longLineWidth + 10; // "fudge factor."
- if (host.getEOLMarkersVisible()) {
- span += metrics.charWidth('\u00B6');
- }
- return span;
- case View.Y_AXIS:
- // We update lineHeight here as when this method is first
- // called, lineHeight isn't initialized. If we don't do it
- // here, we get no vertical scrollbar (as lineHeight==0).
- lineHeight = host != null ? host.getLineHeight() : lineHeight;
- return getElement().getElementCount() * lineHeight;
- default:
- throw new IllegalArgumentException("Invalid axis: " + axis);
- }
- }
-
- /**
- * Returns the tab size set for the document, defaulting to 5.
- *
- * @return The tab size.
- */
- protected int getTabSize() {
- Integer i = (Integer) getDocument().getProperty(
- PlainDocument.tabSizeAttribute);
- return (i != null) ? i : 5;
- }
-
- /**
- * Returns a token list for the physical line above the physical line containing the specified offset into
- * the document. Note that for this plain (non-wrapped) view, this is simply the token list for the logical line
- * above the line containing offset
, since lines are not wrapped.
- *
- * @param offset
- * The offset in question.
- * @return A token list for the physical (and in this view, logical) line before this one. If offset
is
- * in the first line in the document, null
is returned.
- */
- public Token getTokenListForPhysicalLineAbove(int offset) {
- RSyntaxDocument document = (RSyntaxDocument) getDocument();
- Element map = document.getDefaultRootElement();
- int line = map.getElementIndex(offset) - 1;
- if (line >= 0)
- return document.getTokenListForLine(line);
- return null;
- }
-
- /**
- * Returns a token list for the physical line below the physical line containing the specified offset into
- * the document. Note that for this plain (non-wrapped) view, this is simply the token list for the logical line
- * below the line containing offset
, since lines are not wrapped.
- *
- * @param offset
- * The offset in question.
- * @return A token list for the physical (and in this view, logical) line after this one. If offset
is
- * in the last physical line in the document, null
is returned.
- */
- public Token getTokenListForPhysicalLineBelow(int offset) {
- RSyntaxDocument document = (RSyntaxDocument) getDocument();
- Element map = document.getDefaultRootElement();
- int line = map.getElementIndex(offset);
- int lineCount = map.getElementCount();
- if (line < lineCount - 1)
- return document.getTokenListForLine(line + 1);
- return null;
- }
-
- /**
- * Gives notification that something was inserted into the document in a location that this view is responsible for.
- *
- * @param changes
- * The change information from the associated document.
- * @param a
- * The current allocation of the view.
- * @param f
- * The factory to use to rebuild if the view has children.
- */
- public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
- updateDamage(changes, a, f);
- }
-
- /**
- * Determine the rectangle that represents the given line.
- *
- * @param a
- * The region allocated for the view to render into
- * @param line
- * The line number to find the region of. This must be a valid line number in the model.
- */
- protected Rectangle lineToRect(Shape a, int line) {
- Rectangle r = null;
- updateMetrics();
- if (metrics != null) {
- Rectangle alloc = a.getBounds();
- // NOTE: lineHeight is not initially set here, leading to the
- // current line not being highlighted when a document is first
- // opened. So, we set it here just in case.
- lineHeight = host != null ? host.getLineHeight() : lineHeight;
- r = new Rectangle(alloc.x, alloc.y + line * lineHeight,
- alloc.width, lineHeight);
- }
- return r;
- }
-
- /**
- * Provides a mapping from the document model coordinate space to the coordinate space of the view mapped to it.
- *
- * @param pos
- * the position to convert >= 0
- * @param a
- * the allocated region to render into
- * @return the bounding box of the given position
- * @exception BadLocationException
- * if the given position does not represent a valid location in the associated document
- * @see View#modelToView
- */
- public Shape modelToView(int pos, Shape a, Position.Bias b)
- throws BadLocationException {
-
- // line coordinates
- Element map = getElement();
- RSyntaxDocument doc = (RSyntaxDocument) getDocument();
- int lineIndex = map.getElementIndex(pos);
- Rectangle lineArea = lineToRect(a, lineIndex);
- tabBase = lineArea.x; // Used by listOffsetToView().
-
- Token tokenList = doc.getTokenListForLine(lineIndex);
-
- // int x = (int)RSyntaxUtilities.getTokenListWidthUpTo(tokenList,
- // (RSyntaxTextArea)getContainer(),
- // this, 0, pos);
- // We use this method instead as it returns the actual bounding box,
- // not just the x-coordinate.
- lineArea = tokenList.listOffsetToView(
- (RSyntaxTextArea) getContainer(), this, pos,
- tabBase, lineArea);
-
- return lineArea;
-
- }
-
- /**
- * Provides a mapping, for a given region, from the document model coordinate space to the view coordinate space.
- * The specified region is created as a union of the first and last character positions.
- * modelToView
- * actually returns the width of the character instead of "1" or "0" like the View implementations in
- * javax.swing.text
. Thus, if we don't override this method, the View
implementation will
- * return one character's width too much for its consumers (implementations of
- * javax.swing.text.Highlighter
).
- *
- * @param p0
- * the position of the first character (>=0)
- * @param b0
- * The bias of the first character position, toward the previous character or the next character
- * represented by the offset, in case the position is a boundary of two views; b0
will have
- * one of these values:
- *
- *
- * @param p1
- * the position of the last character (>=0)
- * @param b1
- * the bias for the second character position, defined one of the legal values shown above
- * @param a
- * the area of the view, which encompasses the requested region
- * @return the bounding box which is a union of the region specified by the first and last character positions
- * @exception BadLocationException
- * if the given position does not represent a valid location in the associated document
- * @exception IllegalArgumentException
- * if Position.Bias.Forward
Position.Bias.Backward
- * b0
or b1
are not one of the legal Position.Bias
values
- * listed above
- * @see View#viewToModel
- */
- public Shape modelToView(int p0, Position.Bias b0,
- int p1, Position.Bias b1,
- Shape a) throws BadLocationException {
-
- Shape s0 = modelToView(p0, a, b0);
- Shape s1;
- if (p1 == getEndOffset()) {
- try {
- s1 = modelToView(p1, a, b1);
- } catch (BadLocationException ble) {
- s1 = null;
- }
- if (s1 == null) {
- // Assume extends left to right.
- Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a :
- a.getBounds();
- s1 = new Rectangle(alloc.x + alloc.width - 1, alloc.y,
- 1, alloc.height);
- }
- }
- else {
- s1 = modelToView(p1, a, b1);
- }
- Rectangle r0 = s0.getBounds();
- Rectangle r1 = (s1 instanceof Rectangle) ? (Rectangle) s1 :
- s1.getBounds();
- if (r0.y != r1.y) {
- // If it spans lines, force it to be the width of the view.
- Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a :
- a.getBounds();
- r0.x = alloc.x;
- r0.width = alloc.width;
- }
-
- r0.add(r1);
- // The next line is the only difference between this method and
- // View's implementation. We're subtracting the width of the second
- // character. This is because this method is used by Highlighter
- // implementations to get the area to "highlight", and if we don't do
- // this, one character too many is highlighted thanks to our
- // modelToView() implementation returning the actual width of the
- // character requested!
- if (p1 > p0)
- r0.width -= r1.width;
-
- return r0;
-
- }
-
- /**
- * Returns the next tab stop position after a given reference position. This implementation does not support things
- * like centering so it ignores the tabOffset argument.
- *
- * @param x
- * the current position >= 0
- * @param tabOffset
- * the position within the text stream that the tab occurred at >= 0.
- * @return the tab stop, measured in points >= 0
- */
- public float nextTabStop(float x, int tabOffset) {
- if (tabSize == 0)
- return x;
- int ntabs = (((int) x) - tabBase) / tabSize;
- return tabBase + ((ntabs + 1) * tabSize);
- }
-
- /**
- * Actually paints the text area. Only lines that have been damaged are repainted.
- *
- * @param g
- * The graphics context with which to paint.
- * @param a
- * The allocated region in which to render.
- * @see #drawLine
- */
- public void paint(Graphics g, Shape a) {
-
- RSyntaxDocument document = (RSyntaxDocument) getDocument();
-
- Rectangle alloc = a.getBounds();
-
- tabBase = alloc.x;
- host = (RSyntaxTextArea) getContainer();
-
- Rectangle clip = g.getClipBounds();
- // An attempt to speed things up for files with long lines. Note that
- // this will actually slow things down a tad for the common case of
- // regular-length lines, but I don't think it'll make a difference
- // visually. We'll see...
- clipStart = clip.x;
- clipEnd = clipStart + clip.width;
-
- lineHeight = host.getLineHeight();
- ascent = host.getMaxAscent();// metrics.getAscent();
- int heightBelow = (alloc.y + alloc.height) - (clip.y + clip.height);
- int linesBelow = Math.max(0, heightBelow / lineHeight);
- int heightAbove = clip.y - alloc.y;
- int linesAbove = Math.max(0, heightAbove / lineHeight);
- int linesTotal = alloc.height / lineHeight;
-
- if (alloc.height % lineHeight != 0) {
- linesTotal++;
- }
-
- Rectangle lineArea = lineToRect(a, linesAbove);
- int y = lineArea.y + ascent;
- int x = lineArea.x;
- Element map = getElement();
- int lineCount = map.getElementCount();
- int endLine = Math.min(lineCount, linesTotal - linesBelow);
-
- RSyntaxTextAreaHighlighter h =
- (RSyntaxTextAreaHighlighter) host.getHighlighter();
-
- Graphics2D g2d = (Graphics2D) g;
- Token token;
- // System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1));
-
- for (int line = linesAbove; line < endLine; line++) {
-
- Element lineElement = map.getElement(line);
- int startOffset = lineElement.getStartOffset();
- // int endOffset = (line==lineCount ? lineElement.getEndOffset()-1 :
- // lineElement.getEndOffset()-1);
- int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"?
- h.paintLayeredHighlights(g2d, startOffset, endOffset,
- a, host, this);
-
- // Paint a line of text.
- token = document.getTokenListForLine(line);
- drawLine(token, g2d, x, y);
- y += lineHeight;
-
- }
-
- }
-
- /**
- * If the passed-in line is longer than the current longest line, then the longest line is updated.
- *
- * @param line
- * The line to test against the current longest.
- * @param lineNumber
- * The line number of the passed-in line.
- * @return true
iff the current longest line was updated.
- */
- protected boolean possiblyUpdateLongLine(Element line, int lineNumber) {
- float w = getLineWidth(lineNumber);
- if (w > longLineWidth) {
- longLineWidth = w;
- longLine = line;
- return true;
- }
- return false;
- }
-
- /**
- * Gives notification that something was removed from the document in a location that this view is responsible for.
- *
- * @param changes
- * the change information from the associated document
- * @param a
- * the current allocation of the view
- * @param f
- * the factory to use to rebuild if the view has children
- */
- public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
- updateDamage(changes, a, f);
- }
-
- public void setSize(float width, float height) {
- super.setSize(width, height);
- updateMetrics();
- }
-
- /**
- * Repaint the region of change covered by the given document event. Damages the line that begins the range to cover
- * the case when the insert/remove is only on one line. If lines are added or removed, damages the whole view. The
- * longest line is checked to see if it has changed.
- */
- protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f) {
- Component host = getContainer();
- updateMetrics();
- Element elem = getElement();
- DocumentEvent.ElementChange ec = changes.getChange(elem);
- Element[] added = (ec != null) ? ec.getChildrenAdded() : null;
- Element[] removed = (ec != null) ? ec.getChildrenRemoved() : null;
- if (((added != null) && (added.length > 0)) ||
- ((removed != null) && (removed.length > 0))) {
- // lines were added or removed...
- if (added != null) {
- int addedAt = ec.getIndex(); // FIXME: Is this correct?????
- for (int i = 0; i < added.length; i++)
- possiblyUpdateLongLine(added[i], addedAt + i);
- }
- if (removed != null) {
- for (Element aRemoved : removed) {
- if (aRemoved == longLine) {
- longLineWidth = -1; // Must do this!!
- calculateLongestLine();
- break;
- }
- }
- }
- preferenceChanged(null, true, true);
- host.repaint();
- }
-
- // This occurs when syntax highlighting only changes on lines
- // (i.e. beginning a multiline comment).
- else if (changes.getType() == DocumentEvent.EventType.CHANGE) {
- // System.err.println("Updating the damage due to a CHANGE event...");
- int startLine = changes.getOffset();
- int endLine = changes.getLength();
- damageLineRange(startLine, endLine, a, host);
- }
-
- else {
- Element map = getElement();
- int line = map.getElementIndex(changes.getOffset());
- damageLineRange(line, line, a, host);
- if (changes.getType() == DocumentEvent.EventType.INSERT) {
- // check to see if the line is longer than current
- // longest line.
- Element e = map.getElement(line);
- if (e == longLine) {
- // We must recalculate longest line's width here
- // because it has gotten longer.
- longLineWidth = getLineWidth(line);
- preferenceChanged(null, true, false);
- }
- else {
- // If long line gets updated, update the status bars too.
- if (possiblyUpdateLongLine(e, line))
- preferenceChanged(null, true, false);
- }
- }
- else if (changes.getType() == DocumentEvent.EventType.REMOVE) {
- if (map.getElement(line) == longLine) {
- // removed from longest line... recalc
- longLineWidth = -1; // Must do this!
- calculateLongestLine();
- preferenceChanged(null, true, false);
+ TokenOrientedView, RSTAView {
+
+ /**
+ * The default font used by the text area. If this changes we need to
+ * recalculate the longest line.
+ */
+ private Font font;
+
+ /**
+ * Font metrics for the current font.
+ */
+ private FontMetrics metrics;
+
+ /**
+ * The current longest line. This is used to calculate the preferred width
+ * of the view. Since the calculation is potentially expensive, we try to
+ * avoid it by stashing which line is currently the longest.
+ */
+ private Element longLine;
+ private float longLineWidth;
+
+ private int tabSize;
+ private int tabBase;
+
+ /**
+ * Cached for each paint() call so each drawLine() call has access to it.
+ */
+ private RSyntaxTextArea host;
+
+ /**
+ * Cached values to speed up the painting a tad.
+ */
+ private int lineHeight = 0;
+ private int ascent;
+ private int clipStart;
+ private int clipEnd;
+
+ /**
+ * Temporary token used when we need to "modify" tokens for rendering
+ * purposes. Since tokens returned from RSyntaxDocuments are treated as
+ * immutable, we use this temporary token to do that work.
+ */
+ private TokenImpl tempToken;
+
+
+ /**
+ * Constructs a new SyntaxView
wrapped around an element.
+ *
+ * @param elem The element representing the text to display.
+ */
+ public SyntaxView(Element elem) {
+ super(elem);
+ tempToken = new TokenImpl();
+ }
+
+
+ /**
+ * Iterate over the lines represented by the child elements
+ * of the element this view represents, looking for the line
+ * that is the longest. The longLine variable is updated to
+ * represent the longest line contained. The font variable
+ * is updated to indicate the font used to calculate the
+ * longest line.
+ */
+ void calculateLongestLine() {
+ Component c = getContainer();
+ font = c.getFont();
+ metrics = c.getFontMetrics(font);
+ tabSize = getTabSize() * metrics.charWidth(' ');
+ Element lines = getElement();
+ int n = lines.getElementCount();
+ for (int i=0; ioffset
, since lines
+ * are not wrapped.
+ *
+ * @param offset The offset in question.
+ * @return A token list for the physical (and in this view, logical) line
+ * before this one. If offset
is in the first line in
+ * the document, null
is returned.
+ */
+ @Override
+ public Token getTokenListForPhysicalLineAbove(int offset) {
+ RSyntaxDocument document = (RSyntaxDocument)getDocument();
+ Element map = document.getDefaultRootElement();
+int line = map.getElementIndex(offset);
+FoldManager fm = host.getFoldManager();
+if (fm==null) {
+ line--;
+ if (line>=0) {
+ return document.getTokenListForLine(line);
+ }
+}
+else {
+ line = fm.getVisibleLineAbove(line);
+ if (line>=0) {
+ return document.getTokenListForLine(line);
+ }
+}
+// int line = map.getElementIndex(offset) - 1;
+// if (line>=0)
+// return document.getTokenListForLine(line);
+ return null;
+ }
+
+
+ /**
+ * Returns a token list for the physical line below the physical
+ * line containing the specified offset into the document. Note that for
+ * this plain (non-wrapped) view, this is simply the token list for the
+ * logical line below the line containing offset
, since lines
+ * are not wrapped.
+ *
+ * @param offset The offset in question.
+ * @return A token list for the physical (and in this view, logical) line
+ * after this one. If offset
is in the last physical
+ * line in the document, null
is returned.
+ */
+ @Override
+ public Token getTokenListForPhysicalLineBelow(int offset) {
+ RSyntaxDocument document = (RSyntaxDocument)getDocument();
+ Element map = document.getDefaultRootElement();
+ int lineCount = map.getElementCount();
+int line = map.getElementIndex(offset);
+if (!host.isCodeFoldingEnabled()) {
+ if (linemodelToView
actually returns the width of the
+ * character instead of "1" or "0" like the View implementations in
+ * javax.swing.text
. Thus, if we don't override this method,
+ * the View
implementation will return one character's width
+ * too much for its consumers (implementations of
+ * javax.swing.text.Highlighter
).
+ *
+ * @param p0 the position of the first character (>=0)
+ * @param b0 The bias of the first character position, toward the previous
+ * character or the next character represented by the offset, in
+ * case the position is a boundary of two views; b0
+ * will have one of these values:
+ *
+ *
+ * @param p1 the position of the last character (>=0)
+ * @param b1 the bias for the second character position, defined
+ * one of the legal values shown above
+ * @param a the area of the view, which encompasses the requested region
+ * @return the bounding box which is a union of the region specified
+ * by the first and last character positions
+ * @exception BadLocationException if the given position does
+ * not represent a valid location in the associated document
+ * @exception IllegalArgumentException if Position.Bias.Forward
+ * Position.Bias.Backward
+ * b0
or
+ * b1
are not one of the
+ * legal Position.Bias
values listed above
+ * @see View#viewToModel
+ */
+ @Override
+ public Shape modelToView(int p0, Position.Bias b0,
+ int p1, Position.Bias b1,
+ Shape a) throws BadLocationException {
+
+ Shape s0 = modelToView(p0, a, b0);
+ Shape s1;
+ if (p1 ==getEndOffset()) {
+ try {
+ s1 = modelToView(p1, a, b1);
+ } catch (BadLocationException ble) {
+ s1 = null;
+ }
+ if (s1 == null) {
+ // Assume extends left to right.
+ Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
+ a.getBounds();
+ s1 = new Rectangle(alloc.x + alloc.width - 1, alloc.y,
+ 1, alloc.height);
+ }
+ }
+ else {
+ s1 = modelToView(p1, a, b1);
+ }
+ Rectangle r0 = s0 instanceof Rectangle ? (Rectangle)s0 : s0.getBounds();
+ Rectangle r1 = s1 instanceof Rectangle ? (Rectangle)s1 : s1.getBounds();
+ if (r0.y != r1.y) {
+ // If it spans lines, force it to be the width of the view.
+ Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
+ a.getBounds();
+ r0.x = alloc.x;
+ r0.width = alloc.width;
+ }
+
+ r0.add(r1);
+ // The next line is the only difference between this method and
+ // View's implementation. We're subtracting the width of the second
+ // character. This is because this method is used by Highlighter
+ // implementations to get the area to "highlight", and if we don't do
+ // this, one character too many is highlighted thanks to our
+ // modelToView() implementation returning the actual width of the
+ // character requested!
+ if (p1>p0) {
+ r0.width -= r1.width;
+ }
+
+ return r0;
+
+ }
+
+
+ /**
+ * Returns the next tab stop position after a given reference position.
+ * This implementation does not support things like centering so it
+ * ignores the tabOffset argument.
+ *
+ * @param x the current position >= 0
+ * @param tabOffset the position within the text stream
+ * that the tab occurred at >= 0.
+ * @return the tab stop, measured in points >= 0
+ */
+ @Override
+ public float nextTabStop(float x, int tabOffset) {
+ if (tabSize == 0) {
+ return x;
+ }
+ int ntabs = (((int)x) - tabBase) / tabSize;
+ return tabBase + ((ntabs + 1f) * tabSize);
+ }
+
+
+ /**
+ * Actually paints the text area. Only lines that have been damaged are
+ * repainted.
+ *
+ * @param g The graphics context with which to paint.
+ * @param a The allocated region in which to render.
+ */
+ @Override
+ public void paint(Graphics g, Shape a) {
+
+ RSyntaxDocument document = (RSyntaxDocument)getDocument();
+
+ Rectangle alloc = a.getBounds();
+
+ tabBase = alloc.x;
+ host = (RSyntaxTextArea)getContainer();
+
+ Rectangle clip = g.getClipBounds();
+ // An attempt to speed things up for files with long lines. Note that
+ // this will actually slow things down a bit for the common case of
+ // regular-length lines, but it doesn't make a perceivable difference.
+ clipStart = clip.x;
+ clipEnd = clipStart + clip.width;
+
+ lineHeight = host.getLineHeight();
+ ascent = host.getMaxAscent();//metrics.getAscent();
+ int heightAbove = clip.y - alloc.y;
+ int linesAbove = Math.max(0, heightAbove / lineHeight);
+
+ FoldManager fm = host.getFoldManager();
+ linesAbove += fm.getHiddenLineCountAbove(linesAbove, true);
+ Rectangle lineArea = lineToRect(a, linesAbove);
+ int y = lineArea.y + ascent;
+ int x = lineArea.x;
+ Element map = getElement();
+ int lineCount = map.getElementCount();
+
+ // Whether token styles should always be painted, even in selections
+ int selStart = host.getSelectionStart();
+ int selEnd = host.getSelectionEnd();
+
+ RSyntaxTextAreaHighlighter h =
+ (RSyntaxTextAreaHighlighter)host.getHighlighter();
+
+ Graphics2D g2d = (Graphics2D)g;
+ Token token;
+ //System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1));
+
+ TokenPainter painter = host.getTokenPainter();
+ int line = linesAbove;
+ //int count = 0;
+ while (y
- *
- *
- * Loading and saving is also built into the editor.
- * INSERT_MODE
or OVERWRITE_MODE
.
- */
- public TextEditorPane(int textMode) {
- this(textMode, false);
- }
-
- /**
- * Creates a new TextEditorPane
. The file will be given a default name.
- *
- * @param textMode
- * Either INSERT_MODE
or OVERWRITE_MODE
.
- * @param wordWrapEnabled
- * Whether or not to use word wrap in this pane.
- */
- public TextEditorPane(int textMode, boolean wordWrapEnabled) {
- super(textMode);
- setLineWrap(wordWrapEnabled);
- try {
- init(null, null);
- } catch (IOException ioe) { // Never happens
- ioe.printStackTrace();
- }
- }
-
- /**
- * Creates a new TextEditorPane
.
- *
- * @param textMode
- * Either INSERT_MODE
or OVERWRITE_MODE
.
- * @param wordWrapEnabled
- * Whether or not to use word wrap in this pane.
- * @param loc
- * The location of the text file being edited. If this value is null
, a file named
- * "Untitled.txt" in the current directory is used.
- * @throws IOException
- * If an IO error occurs reading the file at loc
. This of course won't happen if
- * loc
is null
.
- */
- public TextEditorPane(int textMode, boolean wordWrapEnabled,
- FileLocation loc) throws IOException {
- this(textMode, wordWrapEnabled, loc, null);
- }
-
- /**
- * Creates a new TextEditorPane
.
- *
- * @param textMode
- * Either INSERT_MODE
or OVERWRITE_MODE
.
- * @param wordWrapEnabled
- * Whether or not to use word wrap in this pane.
- * @param loc
- * The location of the text file being edited. If this value is null
, a file named
- * "Untitled.txt" in the current directory is used. This file is displayed as empty even if it actually
- * exists.
- * @param defaultEnc
- * The default encoding to use when opening the file, if the file is not Unicode. If this value is
- * null
, a system default value is used.
- * @throws IOException
- * If an IO error occurs reading the file at loc
. This of course won't happen if
- * loc
is null
.
- */
- public TextEditorPane(int textMode, boolean wordWrapEnabled,
- FileLocation loc, String defaultEnc) throws IOException {
- super(textMode);
- setLineWrap(wordWrapEnabled);
- init(loc, defaultEnc);
- }
-
- /**
- * Callback for when styles in the current document change. This method is never called.
- *
- * @param e
- * The document event.
- */
- public void changedUpdate(DocumentEvent e) {
- }
-
- /**
- * Returns the default encoding for this operating system.
- *
- * @return The default encoding.
- */
- private static final String getDefaultEncoding() {
- // TODO: Change to "Charset.defaultCharset().name()" when 1.4 support
- // is no longer needed.
- // NOTE: The "file.encoding" property is not guaranteed to be set by
- // the spec, so we cannot rely on it.
- String encoding = System.getProperty("file.encoding");
- if (encoding == null) {
- try {
- File f = File.createTempFile("rsta", null);
- FileWriter w = new FileWriter(f);
- encoding = w.getEncoding();
- w.close();
- f.deleteOnExit();// delete(); Keep FindBugs happy
- } catch (IOException ioe) {
- encoding = "US-ASCII";
- }
- }
- return encoding;
- }
-
- /**
- * Returns the encoding to use when reading or writing this file.
- *
- * @return The encoding.
- * @see #setEncoding(String)
- */
- public String getEncoding() {
- return charSet;
- }
-
- /**
- * Returns the full path to this document.
- *
- * @return The full path to the document.
- */
- public String getFileFullPath() {
- return loc.getFileFullPath();
- }
-
- /**
- * Returns the file name of this document.
- *
- * @return The file name.
- */
- public String getFileName() {
- return loc.getFileName();
- }
-
- /**
- * Returns the timestamp for when this file was last loaded or saved by this editor pane. If the file has
- * been modified on disk by another process after it was loaded into this editor pane, this method will not return
- * the actual file's last modified time.
- * \n
", "\r\n
", or "
- * \r
").
- * Object
and not a String
as that is the way the
- * {@link Document} interface defines its property values. If you always use {@link #setLineSeparator(String)} to
- * modify this value, then the value returned from this method will always be a String
.
- *
- * @return The line separator. If this value is null
, then the system default line separator is used
- * (usually the value of System.getProperty("line.separator")
).
- * @see #setLineSeparator(String)
- * @see #setLineSeparator(String, boolean)
- */
- public Object getLineSeparator() {
- return getDocument().getProperty(
- RTextAreaEditorKit.EndOfLineStringProperty);
- }
-
- /**
- * Initializes this editor with the specified file location.
- *
- * @param loc
- * The file location. If this is null
, a default location is used and an empty file is
- * displayed.
- * @param defaultEnc
- * The default encoding to use when opening the file, if the file is not Unicode. If this value is
- * null
, a system default value is used.
- * @throws IOException
- * If an IO error occurs reading from loc
. If loc
is null
, this
- * cannot happen.
- */
- private void init(FileLocation loc, String defaultEnc) throws IOException {
-
- if (loc == null) {
- // Don't call load() just in case Untitled.txt actually exists,
- // just to ensure there is no chance of an IOException being thrown
- // in the default case.
- this.loc = FileLocation.create(DEFAULT_FILE_NAME);
- charSet = defaultEnc == null ? getDefaultEncoding() : defaultEnc;
- // Ensure that line separator always has a value, even if the file
- // does not exist (or is the "default" file). This makes life
- // easier for host applications that want to display this value.
- setLineSeparator(System.getProperty("line.separator"));
- }
- else {
- load(loc, defaultEnc); // Sets this.loc
- }
-
- if (this.loc.isLocalAndExists()) {
- File file = new File(this.loc.getFileFullPath());
- lastSaveOrLoadTime = file.lastModified();
- setReadOnly(!file.canWrite());
- }
- else {
- lastSaveOrLoadTime = LAST_MODIFIED_UNKNOWN;
- setReadOnly(false);
- }
-
- setDirty(false);
-
- }
-
- /**
- * Callback for when text is inserted into the document.
- *
- * @param e
- * Information on the insertion.
- */
- public void insertUpdate(DocumentEvent e) {
- if (!dirty) {
- setDirty(true);
- }
- }
-
- /**
- * Returns whether or not the text in this editor has unsaved changes.
- *
- * @return Whether or not the text has unsaved changes.
- */
- public boolean isDirty() {
- return dirty;
- }
-
- /**
- * Returns whether this file is a local file.
- *
- * @return Whether this is a local file.
- */
- public boolean isLocal() {
- return loc.isLocal();
- }
-
- /**
- * Returns whether this is a local file that already exists.
- *
- * @return Whether this is a local file that already exists.
- */
- public boolean isLocalAndExists() {
- return loc.isLocalAndExists();
- }
-
- /**
- * Returns whether the text file has been modified outside of this editor since the last load or save operation.
- * Note that if this is a remote file, this method will always return false
.
- * null
.
- * @param defaultEnc
- * The encoding to use when loading/saving the file. This encoding will only be used if the file is not
- * Unicode. If this value is null
, the system default encoding is used.
- * @throws IOException
- * If an IO error occurs.
- * @see #save()
- * @see #saveAs(FileLocation)
- */
- public void load(FileLocation loc, String defaultEnc) throws IOException {
-
- this.loc = loc;
-
- // For new local files, just go with it.
- if (loc.isLocal() && !loc.isLocalAndExists()) {
- this.charSet = defaultEnc != null ? defaultEnc : getDefaultEncoding();
- return;
- }
-
- // Old local files and remote files, load 'em up. UnicodeReader will
- // check for BOMs and handle them correctly in all cases, then pass
- // rest of stream down to InputStreamReader.
- UnicodeReader ur = new UnicodeReader(loc.getInputStream(), defaultEnc);
- charSet = ur.getEncoding();
-
- // Remove listener so dirty flag doesn't get set when loading a file.
- Document doc = getDocument();
- doc.removeDocumentListener(this);
- BufferedReader r = new BufferedReader(ur);
- try {
- read(r, null);
- } finally {
- doc.addDocumentListener(this);
- r.close();
- }
-
- }
-
- /**
- * Reloads this file from disk. The file must exist for this operation to not throw an exception.
- * false
after this operation. If this is a local file, its
- * "last modified" time is updated to reflect that of the actual file.
- * false
, and if this is a local file, its "last modified" time
- * is updated.
- *
- * @throws IOException
- * If an IO error occurs.
- * @see #saveAs(FileLocation)
- * @see #load(FileLocation, String)
- */
- public void save() throws IOException {
- saveImpl(loc);
- setDirty(false);
- syncLastSaveOrLoadTimeToActualFile();
- }
-
- /**
- * Saves this file in a new local location. This method fires a property change event of type
- * {@link #FULL_PATH_PROPERTY}.
- *
- * @param loc
- * The location to save to.
- * @throws IOException
- * If an IO error occurs.
- * @see #save()
- * @see #load(FileLocation, String)
- */
- public void saveAs(FileLocation loc) throws IOException {
- saveImpl(loc);
- // No exception thrown - we can "rename" the file.
- String old = getFileFullPath();
- this.loc = loc;
- setDirty(false);
- lastSaveOrLoadTime = loc.getActualLastModified();
- firePropertyChange(FULL_PATH_PROPERTY, old, getFileFullPath());
- }
-
- /**
- * Saves the text in this editor to the specified location.
- *
- * @param loc
- * The location to save to.
- * @throws IOException
- * If an IO error occurs.
- */
- private void saveImpl(FileLocation loc) throws IOException {
- OutputStream out = loc.getOutputStream();
- PrintWriter w = new PrintWriter(
- new BufferedWriter(new UnicodeWriter(out, getEncoding())));
- try {
- write(w);
- } finally {
- w.close();
- }
- }
-
- /**
- * Sets whether or not this text in this editor has unsaved changes. This fires a property change event of type
- * {@link #DIRTY_PROPERTY}.
- *
- * @param dirty
- * Whether or not the text has beeen modified.
- * @see #isDirty()
- */
- private void setDirty(boolean dirty) {
- if (this.dirty != dirty) {
- this.dirty = dirty;
- firePropertyChange(DIRTY_PROPERTY, !dirty, dirty);
- }
- }
-
- /**
- * Sets the document for this editor.
- *
- * @param doc
- * The new document.
- */
- public void setDocument(Document doc) {
- Document old = getDocument();
- if (old != null) {
- old.removeDocumentListener(this);
- }
- super.setDocument(doc);
- doc.addDocumentListener(this);
- }
-
- /**
- * Sets the encoding to use when reading or writing this file. This method sets the editor's dirty flag when the
- * encoding is changed.
- *
- * @param encoding
- * The new encoding.
- * @throws UnsupportedCharsetException
- * If the encoding is not supported.
- * @throws NullPointerException
- * If encoding
is null
.
- * @see #getEncoding()
- */
- public void setEncoding(String encoding) {
- if (encoding == null) {
- throw new NullPointerException("encoding cannot be null");
- }
- else if (!Charset.isSupported(encoding)) {
- throw new UnsupportedCharsetException(encoding);
- }
- if (charSet == null || !charSet.equals(encoding)) {
- charSet = encoding;
- setDirty(true);
- }
- }
-
- /**
- * Sets the line separator sequence to use when this file is saved (e.g. "\n
", "\r\n
" or "
- * \r
").
- *
- * Besides parameter checking, this method is preferred over getDocument().putProperty()
because it
- * sets the editor's dirty flag when the line separator is changed.
- *
- * @param separator
- * The new line separator.
- * @throws NullPointerException
- * If separator
is null
.
- * @throws IllegalArgumentException
- * If separator
is not one of "\n
", "\r\n
" or "\r
".
- * @see #getLineSeparator()
- */
- public void setLineSeparator(String separator) {
- setLineSeparator(separator, true);
- }
-
- /**
- * Sets the line separator sequence to use when this file is saved (e.g. "\n
", "\r\n
" or "
- * \r
").
- *
- * Besides parameter checking, this method is preferred over getDocument().putProperty()
because can
- * set the editor's dirty flag when the line separator is changed.
- *
- * @param separator
- * The new line separator.
- * @param setDirty
- * Whether the dirty flag should be set if the line separator is changed.
- * @throws NullPointerException
- * If separator
is null
.
- * @throws IllegalArgumentException
- * If separator
is not one of "\n
", "\r\n
" or "\r
".
- * @see #getLineSeparator()
- */
- public void setLineSeparator(String separator, boolean setDirty) {
- if (separator == null) {
- throw new NullPointerException("terminator cannot be null");
- }
- if (!"\r\n".equals(separator) && !"\n".equals(separator) &&
- !"\r".equals(separator)) {
- throw new IllegalArgumentException("Invalid line terminator");
- }
- Document doc = getDocument();
- Object old = doc.getProperty(
- RTextAreaEditorKit.EndOfLineStringProperty);
- if (!separator.equals(old)) {
- doc.putProperty(RTextAreaEditorKit.EndOfLineStringProperty,
- separator);
- if (setDirty) {
- setDirty(true);
- }
- }
- }
-
- /**
- * Sets whether or not this text area should be treated as read-only. This fires a property change event of type
- * {@link #READ_ONLY_PROPERTY}.
- *
- * @param readOnly
- * Whether or not the document is read-only.
- * @see #isReadOnly()
- */
- public void setReadOnly(boolean readOnly) {
- if (this.readOnly != readOnly) {
- this.readOnly = readOnly;
- firePropertyChange(READ_ONLY_PROPERTY, !readOnly, readOnly);
- }
- }
-
- /**
- * Syncs this text area's "last saved or loaded" time to that of the file being edited, if that file is local and
- * exists. If the file is remote or is local but does not yet exist, nothing happens.
- * INSERT_MODE
or
+ * OVERWRITE_MODE
.
+ */
+ public TextEditorPane(int textMode) {
+ this(textMode, false);
+ }
+
+
+ /**
+ * Creates a new TextEditorPane
. The file will be given
+ * a default name.
+ *
+ * @param textMode Either INSERT_MODE
or
+ * OVERWRITE_MODE
.
+ * @param wordWrapEnabled Whether or not to use word wrap in this pane.
+ */
+ public TextEditorPane(int textMode, boolean wordWrapEnabled) {
+ super(textMode);
+ setLineWrap(wordWrapEnabled);
+ try {
+ init(null, null);
+ } catch (IOException ioe) { // Never happens
+ ioe.printStackTrace();
+ }
+ }
+
+
+ /**
+ * Creates a new TextEditorPane
.
+ *
+ * @param textMode Either INSERT_MODE
or
+ * OVERWRITE_MODE
.
+ * @param wordWrapEnabled Whether or not to use word wrap in this pane.
+ * @param loc The location of the text file being edited. If this value
+ * is null
, a file named "Untitled.txt" in the current
+ * directory is used.
+ * @throws IOException If an IO error occurs reading the file at
+ * loc
. This of course won't happen if
+ * loc
is null
.
+ */
+ public TextEditorPane(int textMode, boolean wordWrapEnabled,
+ FileLocation loc) throws IOException {
+ this(textMode, wordWrapEnabled, loc, null);
+ }
+
+
+ /**
+ * Creates a new TextEditorPane
.
+ *
+ * @param textMode Either INSERT_MODE
or
+ * OVERWRITE_MODE
.
+ * @param wordWrapEnabled Whether or not to use word wrap in this pane.
+ * @param loc The location of the text file being edited. If this value
+ * is null
, a file named "Untitled.txt" in the current
+ * directory is used. This file is displayed as empty even if it
+ * actually exists.
+ * @param defaultEnc The default encoding to use when opening the file,
+ * if the file is not Unicode. If this value is null
,
+ * a system default value is used.
+ * @throws IOException If an IO error occurs reading the file at
+ * loc
. This of course won't happen if
+ * loc
is null
.
+ */
+ public TextEditorPane(int textMode, boolean wordWrapEnabled,
+ FileLocation loc, String defaultEnc) throws IOException {
+ super(textMode);
+ setLineWrap(wordWrapEnabled);
+ init(loc, defaultEnc);
+ }
+
+
+ /**
+ * Callback for when styles in the current document change.
+ * This method is never called.
+ *
+ * @param e The document event.
+ */
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ }
+
+
+
+ /**
+ * Returns the default encoding for this operating system.
+ *
+ * @return The default encoding.
+ */
+ private static String getDefaultEncoding() {
+ // NOTE: The "file.encoding" system property is not guaranteed to be
+ // set by the spec, so we cannot rely on it.
+ return Charset.defaultCharset().name();
+ }
+
+
+ /**
+ * Returns the encoding to use when reading or writing this file.
+ *
+ * @return The encoding.
+ * @see #setEncoding(String)
+ */
+ public String getEncoding() {
+ return charSet;
+ }
+
+
+ /**
+ * Returns the full path to this document.
+ *
+ * @return The full path to the document.
+ */
+ public String getFileFullPath() {
+ return loc==null ? null : loc.getFileFullPath();
+ }
+
+
+ /**
+ * Returns the file name of this document.
+ *
+ * @return The file name.
+ */
+ public String getFileName() {
+ return loc==null ? null : loc.getFileName();
+ }
+
+
+ /**
+ * Returns the timestamp for when this file was last loaded or saved
+ * by this editor pane. If the file has been modified on disk by
+ * another process after it was loaded into this editor pane, this method
+ * will not return the actual file's last modified time.\n
", "\r\n
", or "\r
").Object
and not a
+ * String
as that is the way the {@link Document} interface
+ * defines its property values. If you always use
+ * {@link #setLineSeparator(String)} to modify this value, then the value
+ * returned from this method will always be a String
.
+ *
+ * @return The line separator. If this value is null
, then
+ * the system default line separator is used (usually the value
+ * of System.getProperty("line.separator")
).
+ * @see #setLineSeparator(String)
+ * @see #setLineSeparator(String, boolean)
+ */
+ public Object getLineSeparator() {
+ return getDocument().getProperty(
+ RTextAreaEditorKit.EndOfLineStringProperty);
+ }
+
+
+ /**
+ * Initializes this editor with the specified file location.
+ *
+ * @param loc The file location. If this is null
, a default
+ * location is used and an empty file is displayed.
+ * @param defaultEnc The default encoding to use when opening the file,
+ * if the file is not Unicode. If this value is null
,
+ * a system default value is used.
+ * @throws IOException If an IO error occurs reading from loc
.
+ * If loc
is null
, this cannot happen.
+ */
+ private void init(FileLocation loc, String defaultEnc) throws IOException {
+
+ if (loc==null) {
+ // Don't call load() just in case Untitled.txt actually exists,
+ // just to ensure there is no chance of an IOException being thrown
+ // in the default case.
+ this.loc = FileLocation.create(DEFAULT_FILE_NAME);
+ charSet = defaultEnc==null ? getDefaultEncoding() : defaultEnc;
+ // Ensure that line separator always has a value, even if the file
+ // does not exist (or is the "default" file). This makes life
+ // easier for host applications that want to display this value.
+ setLineSeparator(System.getProperty("line.separator"));
+ }
+ else {
+ load(loc, defaultEnc); // Sets this.loc
+ }
+
+ if (this.loc.isLocalAndExists()) {
+ File file = new File(this.loc.getFileFullPath());
+ lastSaveOrLoadTime = file.lastModified();
+ setReadOnly(!file.canWrite());
+ }
+ else {
+ lastSaveOrLoadTime = LAST_MODIFIED_UNKNOWN;
+ setReadOnly(false);
+ }
+
+ setDirty(false);
+
+ }
+
+
+ /**
+ * Callback for when text is inserted into the document.
+ *
+ * @param e Information on the insertion.
+ */
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ if (!dirty) {
+ setDirty(true);
+ }
+ }
+
+
+ /**
+ * Returns whether or not the text in this editor has unsaved changes.
+ *
+ * @return Whether or not the text has unsaved changes.
+ * @see #setDirty(boolean)
+ */
+ public boolean isDirty() {
+ return dirty;
+ }
+
+
+ /**
+ * Returns whether this file is a local file.
+ *
+ * @return Whether this is a local file.
+ */
+ public boolean isLocal() {
+ return loc.isLocal();
+ }
+
+
+ /**
+ * Returns whether this is a local file that already exists.
+ *
+ * @return Whether this is a local file that already exists.
+ */
+ public boolean isLocalAndExists() {
+ return loc.isLocalAndExists();
+ }
+
+
+ /**
+ * Returns whether the text file has been modified outside of this editor
+ * since the last load or save operation. Note that if this is a remote
+ * file, this method will always return false
.null
.
+ * @param defaultEnc The encoding to use when loading/saving the file.
+ * This encoding will only be used if the file is not Unicode.
+ * If this value is null
, the system default encoding
+ * is used.
+ * @throws IOException If an IO error occurs.
+ * @see #save()
+ * @see #saveAs(FileLocation)
+ */
+ public void load(FileLocation loc, String defaultEnc) throws IOException {
+
+ // For new local files, just go with it.
+ if (loc.isLocal() && !loc.isLocalAndExists()) {
+ this.charSet = defaultEnc!=null ? defaultEnc : getDefaultEncoding();
+ this.loc = loc;
+ setText(null);
+ discardAllEdits();
+ setDirty(false);
+ return;
+ }
+
+ // Old local files and remote files, load 'em up. UnicodeReader will
+ // check for BOMs and handle them correctly in all cases, then pass
+ // rest of stream down to InputStreamReader.
+ UnicodeReader ur = new UnicodeReader(loc.getInputStream(), defaultEnc);
+
+ // Remove listener so dirty flag doesn't get set when loading a file.
+ Document doc = getDocument();
+ doc.removeDocumentListener(this);
+ try (BufferedReader r = new BufferedReader(ur)) {
+ read(r, null);
+ } finally {
+ doc.addDocumentListener(this);
+ }
+
+ // No IOException thrown, so we can finally change the location.
+ charSet = ur.getEncoding();
+ String old = getFileFullPath();
+ this.loc = loc;
+ setDirty(false);
+ setCaretPosition(0);
+ discardAllEdits();
+ firePropertyChange(FULL_PATH_PROPERTY, old, getFileFullPath());
+
+ }
+
+
+ /**
+ * Reloads this file from disk. The file must exist for this operation
+ * to not throw an exception.false
after this
+ * operation. If this is a local file, its "last modified" time is
+ * updated to reflect that of the actual file.false
, and if
+ * this is a local file, its "last modified" time is updated.
+ *
+ * @throws IOException If an IO error occurs.
+ * @see #saveAs(FileLocation)
+ * @see #load(FileLocation, String)
+ */
+ public void save() throws IOException {
+ saveImpl(loc);
+ setDirty(false);
+ syncLastSaveOrLoadTimeToActualFile();
+ }
+
+
+ /**
+ * Saves this file in a new local location. This method fires a property
+ * change event of type {@link #FULL_PATH_PROPERTY}.
+ *
+ * @param loc The location to save to.
+ * @throws IOException If an IO error occurs.
+ * @see #save()
+ * @see #load(FileLocation, String)
+ */
+ public void saveAs(FileLocation loc) throws IOException {
+ saveImpl(loc);
+ // No exception thrown - we can "rename" the file.
+ String old = getFileFullPath();
+ this.loc = loc;
+ setDirty(false);
+ lastSaveOrLoadTime = loc.getActualLastModified();
+ firePropertyChange(FULL_PATH_PROPERTY, old, getFileFullPath());
+ }
+
+
+ /**
+ * Saves the text in this editor to the specified location.
+ *
+ * @param loc The location to save to.
+ * @throws IOException If an IO error occurs.
+ */
+ private void saveImpl(FileLocation loc) throws IOException {
+ OutputStream out = loc.getOutputStream();
+ try (BufferedWriter w = new BufferedWriter(
+ new UnicodeWriter(out, getEncoding()))) {
+ write(w);
+ }
+ }
+
+
+ /**
+ * Sets whether or not this text in this editor has unsaved changes.
+ * This fires a property change event of type {@link #DIRTY_PROPERTY}.TextEditorPane
automatically sets its
+ * own dirty flag when its content is edited, when its encoding is changed,
+ * or when its line ending property is changed. It is cleared whenever
+ * load()
, reload()
, save()
, or
+ * saveAs()
are called.
+ *
+ * @param dirty Whether or not the text has been modified.
+ * @see #isDirty()
+ */
+ public void setDirty(boolean dirty) {
+ if (this.dirty!=dirty) {
+ this.dirty = dirty;
+ firePropertyChange(DIRTY_PROPERTY, !dirty, dirty);
+ }
+ }
+
+
+ /**
+ * Sets the document for this editor.
+ *
+ * @param doc The new document.
+ */
+ @Override
+ public void setDocument(Document doc) {
+ Document old = getDocument();
+ if (old!=null) {
+ old.removeDocumentListener(this);
+ }
+ super.setDocument(doc);
+ doc.addDocumentListener(this);
+ }
+
+
+ /**
+ * Sets the encoding to use when reading or writing this file. This
+ * method sets the editor's dirty flag when the encoding is changed, and
+ * fires a property change event of type {@link #ENCODING_PROPERTY}.
+ *
+ * @param encoding The new encoding.
+ * @throws UnsupportedCharsetException If the encoding is not supported.
+ * @throws NullPointerException If encoding
is
+ * null
.
+ * @see #getEncoding()
+ */
+ public void setEncoding(String encoding) {
+ if (encoding==null) {
+ throw new NullPointerException("encoding cannot be null");
+ }
+ else if (!Charset.isSupported(encoding)) {
+ throw new UnsupportedCharsetException(encoding);
+ }
+ if (charSet==null || !charSet.equals(encoding)) {
+ String oldEncoding = charSet;
+ charSet = encoding;
+ firePropertyChange(ENCODING_PROPERTY, oldEncoding, charSet);
+ setDirty(true);
+ }
+ }
+
+
+ /**
+ * Sets the line separator sequence to use when this file is saved (e.g.
+ * "\n
", "\r\n
" or "\r
").
+ *
+ * Besides parameter checking, this method is preferred over
+ * getDocument().putProperty()
because it sets the editor's
+ * dirty flag when the line separator is changed.
+ *
+ * @param separator The new line separator.
+ * @throws NullPointerException If separator
is
+ * null
.
+ * @throws IllegalArgumentException If separator
is not one
+ * of "\n
", "\r\n
" or "\r
".
+ * @see #getLineSeparator()
+ */
+ public void setLineSeparator(String separator) {
+ setLineSeparator(separator, true);
+ }
+
+
+ /**
+ * Sets the line separator sequence to use when this file is saved (e.g.
+ * "\n
", "\r\n
" or "\r
").
+ *
+ * Besides parameter checking, this method is preferred over
+ * getDocument().putProperty()
because can set the editor's
+ * dirty flag when the line separator is changed.
+ *
+ * @param separator The new line separator.
+ * @param setDirty Whether the dirty flag should be set if the line
+ * separator is changed.
+ * @throws NullPointerException If separator
is
+ * null
.
+ * @throws IllegalArgumentException If separator
is not one
+ * of "\n
", "\r\n
" or "\r
".
+ * @see #getLineSeparator()
+ */
+ public void setLineSeparator(String separator, boolean setDirty) {
+ if (separator==null) {
+ throw new NullPointerException("terminator cannot be null");
+ }
+ if (!"\r\n".equals(separator) && !"\n".equals(separator) &&
+ !"\r".equals(separator)) {
+ throw new IllegalArgumentException("Invalid line terminator");
+ }
+ Document doc = getDocument();
+ Object old = doc.getProperty(
+ RTextAreaEditorKit.EndOfLineStringProperty);
+ if (!separator.equals(old)) {
+ doc.putProperty(RTextAreaEditorKit.EndOfLineStringProperty,
+ separator);
+ if (setDirty) {
+ setDirty(true);
+ }
+ }
+ }
+
+
+ /**
+ * Sets whether or not this text area should be treated as read-only.
+ * This fires a property change event of type {@link #READ_ONLY_PROPERTY}.
+ *
+ * @param readOnly Whether or not the document is read-only.
+ * @see #isReadOnly()
+ */
+ public void setReadOnly(boolean readOnly) {
+ if (this.readOnly!=readOnly) {
+ this.readOnly = readOnly;
+ firePropertyChange(READ_ONLY_PROPERTY, !readOnly, readOnly);
+ }
+ }
+
+
+ /**
+ * Syncs this text area's "last saved or loaded" time to that of the file
+ * being edited, if that file is local and exists. If the file is
+ * remote or is local but does not yet exist, nothing happens.org/fife/ui/rsyntaxtextarea/themes/theme.dtd
. This provides
+ * applications and other consumers with an easy way to style RSyntaxTextArea
+ * without having to use the API.org.fife.ui.rsyntaxtextarea.themes
package, and can be loaded
+ * via getClass().getResourceAsStream(...)
.load()
, save()
, and apply()
methods
+ * for various tasks.Theme
via {@link #save(OutputStream)},
+ * you must currently create a Theme
from a text area wrapped in
+ * an RTextScrollPane
, so that the color information for the
+ * gutter can be retrieved.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ */
+@SuppressWarnings({ "checkstyle:visibilitymodifier" })
+public class Theme {
+
+ public Font baseFont;
+ public Color bgColor;
+ public Color caretColor;
+ public boolean useSelctionFG;
+ public Color selectionFG;
+ public Color selectionBG;
+ public boolean selectionRoundedEdges;
+ public Color currentLineHighlight;
+ public boolean fadeCurrentLineHighlight;
+ public Color marginLineColor;
+ public Color markAllHighlightColor;
+ public Color markOccurrencesColor;
+ public boolean markOccurrencesBorder;
+ public Color matchedBracketFG;
+ public Color matchedBracketBG;
+ public boolean matchedBracketHighlightBoth;
+ public boolean matchedBracketAnimate;
+ public Color hyperlinkFG;
+ public Color[] secondaryLanguages;
+
+ public SyntaxScheme scheme;
+
+ public Color gutterBackgroundColor;
+ public Color gutterBorderColor;
+ public Color activeLineRangeColor;
+ public boolean iconRowHeaderInheritsGutterBG;
+ public Color lineNumberColor;
+ public String lineNumberFont;
+ public int lineNumberFontSize;
+ public Color foldIndicatorFG;
+ public Color foldBG;
+ public Color armedFoldBG;
+
+
+ /**
+ * Private constructor, used when loading from a stream.
+ *
+ * @param baseFont The default font to use for any "base font" properties
+ * not specified in the theme XML. If this is null
,
+ * a default monospaced font will be used.
+ */
+ private Theme(Font baseFont) {
+ // Optional fields that require a default value.
+ this.baseFont = baseFont!=null ? baseFont : RTextArea.getDefaultFont();
+ secondaryLanguages = new Color[3];
+ activeLineRangeColor = Gutter.DEFAULT_ACTIVE_LINE_RANGE_COLOR;
+ }
+
+
+ /**
+ * Creates a theme from an RSyntaxTextArea. It should be contained in
+ * an RTextScrollPane
to get all gutter color information.
+ *
+ * @param textArea The text area.
+ */
+ public Theme(RSyntaxTextArea textArea) {
+
+ baseFont = textArea.getFont();
+ bgColor = textArea.getBackground();
+ caretColor = textArea.getCaretColor();
+ useSelctionFG = textArea.getUseSelectedTextColor();
+ selectionFG = textArea.getSelectedTextColor();
+ selectionBG = textArea.getSelectionColor();
+ selectionRoundedEdges = textArea.getRoundedSelectionEdges();
+ currentLineHighlight = textArea.getCurrentLineHighlightColor();
+ fadeCurrentLineHighlight = textArea.getFadeCurrentLineHighlight();
+ marginLineColor = textArea.getMarginLineColor();
+ markAllHighlightColor = textArea.getMarkAllHighlightColor();
+ markOccurrencesColor = textArea.getMarkOccurrencesColor();
+ markOccurrencesBorder = textArea.getPaintMarkOccurrencesBorder();
+ matchedBracketBG = textArea.getMatchedBracketBGColor();
+ matchedBracketFG = textArea.getMatchedBracketBorderColor();
+ matchedBracketHighlightBoth = textArea.getPaintMatchedBracketPair();
+ matchedBracketAnimate = textArea.getAnimateBracketMatching();
+ hyperlinkFG = textArea.getHyperlinkForeground();
+
+ int count = textArea.getSecondaryLanguageCount();
+ secondaryLanguages = new Color[count];
+ for (int i=0; inull
,
+ * a default monospaced font will be used.
+ * @return The theme.
+ * @throws IOException If an IO error occurs.
+ * @see #save(OutputStream)
+ */
+ public static Theme load(InputStream in, Font baseFont) throws IOException {
+
+ Theme theme = new Theme(baseFont);
+
+ try (BufferedInputStream bin = new BufferedInputStream(in)) {
+ XmlHandler.load(theme, bin);
+ }
+
+ return theme;
+ }
+
+
+ /**
+ * Saves this theme to an output stream.
+ *
+ * @param out The output stream to write to.
+ * @throws IOException If an IO error occurs.
+ * @see #load(InputStream)
+ */
+ public void save(OutputStream out) throws IOException {
+
+ try (BufferedOutputStream bout = new BufferedOutputStream(out)) {
+
+ DocumentBuilder db = DocumentBuilderFactory.newInstance().
+ newDocumentBuilder();
+ DOMImplementation impl = db.getDOMImplementation();
+
+ Document doc = impl.createDocument(null, "RSyntaxTheme", null);
+ Element root = doc.getDocumentElement();
+ root.setAttribute("version", "1.0");
+
+ Element elem = doc.createElement("baseFont");
+ if (!baseFont.getFamily().equals(
+ RSyntaxTextArea.getDefaultFont().getFamily())) {
+ elem.setAttribute("family", baseFont.getFamily());
+ }
+ elem.setAttribute("size", Integer.toString(baseFont.getSize()));
+ root.appendChild(elem);
+
+ elem = doc.createElement("background");
+ elem.setAttribute("color", colorToString(bgColor));
+ root.appendChild(elem);
+
+ elem = doc.createElement("caret");
+ elem.setAttribute("color", colorToString(caretColor));
+ root.appendChild(elem);
+
+ elem = doc.createElement("selection");
+ elem.setAttribute("useFG", Boolean.toString(useSelctionFG));
+ elem.setAttribute("fg", colorToString(selectionFG));
+ elem.setAttribute("bg", colorToString(selectionBG));
+ elem.setAttribute("roundedEdges", Boolean.toString(selectionRoundedEdges));
+ root.appendChild(elem);
+
+ elem = doc.createElement("currentLineHighlight");
+ elem.setAttribute("color", colorToString(currentLineHighlight));
+ elem.setAttribute("fade", Boolean.toString(fadeCurrentLineHighlight));
+ root.appendChild(elem);
+
+ elem = doc.createElement("marginLine");
+ elem.setAttribute("fg", colorToString(marginLineColor));
+ root.appendChild(elem);
+
+ elem = doc.createElement("markAllHighlight");
+ elem.setAttribute("color", colorToString(markAllHighlightColor));
+ root.appendChild(elem);
+
+ elem = doc.createElement("markOccurrencesHighlight");
+ elem.setAttribute("color", colorToString(markOccurrencesColor));
+ elem.setAttribute("border", Boolean.toString(markOccurrencesBorder));
+ root.appendChild(elem);
+
+ elem = doc.createElement("matchedBracket");
+ elem.setAttribute("fg", colorToString(matchedBracketFG));
+ elem.setAttribute("bg", colorToString(matchedBracketBG));
+ elem.setAttribute("highlightBoth", Boolean.toString(matchedBracketHighlightBoth));
+ elem.setAttribute("animate", Boolean.toString(matchedBracketAnimate));
+ root.appendChild(elem);
+
+ elem = doc.createElement("hyperlinks");
+ elem.setAttribute("fg", colorToString(hyperlinkFG));
+ root.appendChild(elem);
+
+ elem = doc.createElement("secondaryLanguages");
+ for (int i = 0; i < secondaryLanguages.length; i++) {
+ Color color = secondaryLanguages[i];
+ Element elem2 = doc.createElement("language");
+ elem2.setAttribute("index", Integer.toString(i + 1));
+ elem2.setAttribute("bg", color == null ? "" : colorToString(color));
+ elem.appendChild(elem2);
+ }
+ root.appendChild(elem);
+
+ elem = doc.createElement("gutterBackground");
+ elem.setAttribute("color", colorToString(gutterBackgroundColor));
+ root.appendChild(elem);
+
+ elem = doc.createElement("gutterBorder");
+ elem.setAttribute("color", colorToString(gutterBorderColor));
+ root.appendChild(elem);
+
+ elem = doc.createElement("lineNumbers");
+ elem.setAttribute("fg", colorToString(lineNumberColor));
+ if (lineNumberFont != null) {
+ elem.setAttribute("fontFamily", lineNumberFont);
+ }
+ if (lineNumberFontSize > 0) {
+ elem.setAttribute("fontSize",
+ Integer.toString(lineNumberFontSize));
+ }
+ root.appendChild(elem);
+
+ elem = doc.createElement("foldIndicator");
+ elem.setAttribute("fg", colorToString(foldIndicatorFG));
+ elem.setAttribute("iconBg", colorToString(foldBG));
+ elem.setAttribute("iconArmedBg", colorToString(armedFoldBG));
+ root.appendChild(elem);
+
+ elem = doc.createElement("iconRowHeader");
+ elem.setAttribute("activeLineRange", colorToString(activeLineRangeColor));
+ elem.setAttribute("inheritsGutterBG", Boolean.toString(iconRowHeaderInheritsGutterBG));
+ root.appendChild(elem);
+
+ elem = doc.createElement("tokenStyles");
+ Field[] fields = TokenTypes.class.getFields();
+ for (Field field : fields) {
+ int value = field.getInt(null);
+ if (value != TokenTypes.DEFAULT_NUM_TOKEN_TYPES) {
+ Style style = scheme.getStyle(value);
+ if (style != null) {
+ Element elem2 = doc.createElement("style");
+ elem2.setAttribute("token", field.getName());
+ Color fg = style.foreground;
+ if (fg != null) {
+ elem2.setAttribute("fg", colorToString(fg));
+ }
+ Color bg = style.background;
+ if (bg != null) {
+ elem2.setAttribute("bg", colorToString(bg));
+ }
+ Font font = style.font;
+ if (font != null) {
+ if (!font.getFamily().equals(
+ baseFont.getFamily())) {
+ elem2.setAttribute("fontFamily", font.getFamily());
+ }
+ if (font.getSize() != baseFont.getSize()) {
+ elem2.setAttribute("fontSize", Integer.toString(font.getSize()));
+ }
+ if (font.isBold()) {
+ elem2.setAttribute("bold", "true");
+ }
+ if (font.isItalic()) {
+ elem2.setAttribute("italic", "true");
+ }
+ }
+ if (style.underline) {
+ elem2.setAttribute("underline", "true");
+ }
+ elem.appendChild(elem2);
+ }
+ }
+ }
+ root.appendChild(elem);
+
+ DOMSource source = new DOMSource(doc);
+ // Use a writer instead of OutputStream to allow pretty printing.
+ // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6337981
+ StreamResult result = new StreamResult(new PrintWriter(
+ new UnicodeWriter(bout, "UTF-8")));
+ TransformerFactory transFac = TransformerFactory.newInstance();
+ Transformer transformer = transFac.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "theme.dtd");
+ transformer.transform(source, result);
+
+ } catch (RuntimeException re) {
+ throw re; // FindBugs
+ } catch (Exception e) {
+ throw new IOException("Error generating XML: " + e.getMessage(), e);
+ }
+
+ }
+
+
+ /**
+ * Returns the color represented by a string. The input is expected to
+ * be a 6-digit hex string, optionally prefixed by a '$'. For example,
+ * either of the following:
+ *
+ * "$00ff00"
+ * "00ff00"
+ *
+ * will return new Color(0, 255, 0)
.
+ *
+ * @param s The string to evaluate.
+ * @return The color.
+ */
+ private static Color stringToColor(String s) {
+ return stringToColor(s, null);
+ }
+
+
+ /**
+ * Returns the color represented by a string. The input is expected to
+ * be a 6-digit hex string, optionally prefixed by a '$'. For example,
+ * either of the following:
+ *
+ * "$00ff00"
+ * "00ff00"
+ *
+ * will return new Color(0, 255, 0)
.
+ *
+ * @param s The string to evaluate.
+ * @param defaultVal The color to use if s
is
+ * "default
".
+ * @return The color.
+ */
+ private static Color stringToColor(String s, Color defaultVal) {
+ if (s==null || "default".equalsIgnoreCase(s)) {
+ return defaultVal;
+ }
+ if (s.length()==6 || s.length()==7) {
+ if (s.charAt(0)=='$') {
+ s = s.substring(1);
+ }
+ return new Color(Integer.parseInt(s, 16));
+ }
+ return null;
+ }
+
+
+ /**
+ * Loads a SyntaxScheme
from an XML file.
+ */
+ private static class XmlHandler extends DefaultHandler {
+
+ private Theme theme;
+
+ @Override
+ public void error(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ @Override
+ public void fatalError(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public static void load(Theme theme, InputStream in) throws IOException {
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ spf.setValidating(true);
+ try {
+ SAXParser parser = spf.newSAXParser();
+ XMLReader reader = parser.getXMLReader();
+ XmlHandler handler = new XmlHandler();
+ handler.theme = theme;
+ reader.setEntityResolver(handler);
+ reader.setContentHandler(handler);
+ reader.setDTDHandler(handler);
+ reader.setErrorHandler(handler);
+ InputSource is = new InputSource(in);
+ is.setEncoding("UTF-8");
+ reader.parse(is);
+ } catch (/*SAX|ParserConfiguration*/Exception se) {
+ throw new IOException(se.toString());
+ }
+ }
+
+ private static int parseInt(Attributes attrs, String attr,
+ int def) {
+ int value = def;
+ String temp = attrs.getValue(attr);
+ if (temp != null) {
+ try {
+ value = Integer.parseInt(temp);
+ } catch (NumberFormatException nfe) {
+ nfe.printStackTrace();
+ }
+ }
+ return value;
+ }
+
+ @Override
+ public InputSource resolveEntity(String publicID, String systemID) {
+ return new InputSource(getClass().
+ getResourceAsStream("themes/theme.dtd"));
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName,
+ Attributes attrs) {
+
+ if ("background".equals(qName)) {
+
+ String color = attrs.getValue("color");
+ if (color!=null) {
+ theme.bgColor = stringToColor(color, getDefaultBG());
+ theme.gutterBackgroundColor = theme.bgColor;
+ }
+ else {
+ String img = attrs.getValue("image");
+ if (img!=null) {
+ throw new IllegalArgumentException("Not yet implemented");
+ }
+ }
+ }
+
+ // The base font to use in the editor.
+ else if ("baseFont".equals(qName)) {
+ int size = theme.baseFont.getSize();
+ String sizeStr = attrs.getValue("size");
+ if (sizeStr!=null) {
+ size = Integer.parseInt(sizeStr);
+ }
+ String family = attrs.getValue("family");
+ if (family!=null) {
+ theme.baseFont = getFont(family, Font.PLAIN, size);
+ }
+ else if (sizeStr!=null) {
+ // No family specified, keep original family
+ theme.baseFont = theme.baseFont.deriveFont(size*1f);
+ }
+ }
+
+ else if ("caret".equals(qName)) {
+ String color = attrs.getValue("color");
+ theme.caretColor = stringToColor(color);
+ }
+
+ else if ("currentLineHighlight".equals(qName)) {
+ String color = attrs.getValue("color");
+ theme.currentLineHighlight = stringToColor(color);
+ String fadeStr = attrs.getValue("fade");
+ boolean fade = Boolean.parseBoolean(fadeStr);
+ theme.fadeCurrentLineHighlight = fade;
+ }
+
+ else if ("foldIndicator".equals(qName)) {
+ String color = attrs.getValue("fg");
+ theme.foldIndicatorFG = stringToColor(color);
+ color = attrs.getValue("iconBg");
+ theme.foldBG = stringToColor(color);
+ color = attrs.getValue("iconArmedBg");
+ theme.armedFoldBG = stringToColor(color);
+ }
+
+ else if ("gutterBackground".equals(qName)) {
+ String color = attrs.getValue("color");
+ if (color!=null) {
+ theme.gutterBackgroundColor = stringToColor(color);
+ }
+ }
+
+ else if ("gutterBorder".equals(qName)) {
+ String color = attrs.getValue("color");
+ theme.gutterBorderColor = stringToColor(color);
+ }
+
+ else if ("iconRowHeader".equals(qName)) {
+ String color = attrs.getValue("activeLineRange");
+ theme.activeLineRangeColor = stringToColor(color);
+ String inheritBGStr = attrs.getValue("inheritsGutterBG");
+ theme.iconRowHeaderInheritsGutterBG =
+ Boolean.parseBoolean(inheritBGStr);
+ }
+
+ else if ("lineNumbers".equals(qName)) {
+ String color = attrs.getValue("fg");
+ theme.lineNumberColor = stringToColor(color);
+ theme.lineNumberFont = attrs.getValue("fontFamily");
+ theme.lineNumberFontSize = parseInt(attrs, "fontSize", -1);
+ }
+
+ else if ("marginLine".equals(qName)) {
+ String color = attrs.getValue("fg");
+ theme.marginLineColor = stringToColor(color);
+ }
+
+ else if ("markAllHighlight".equals(qName)) {
+ String color = attrs.getValue("color");
+ theme.markAllHighlightColor = stringToColor(color);
+ }
+
+ else if ("markOccurrencesHighlight".equals(qName)) {
+ String color = attrs.getValue("color");
+ theme.markOccurrencesColor = stringToColor(color);
+ String border = attrs.getValue("border");
+ theme.markOccurrencesBorder = Boolean.parseBoolean(border);
+ }
+
+ else if ("matchedBracket".equals(qName)) {
+ String fg = attrs.getValue("fg");
+ theme.matchedBracketFG = stringToColor(fg);
+ String bg = attrs.getValue("bg");
+ theme.matchedBracketBG = stringToColor(bg);
+ String highlightBoth = attrs.getValue("highlightBoth");
+ theme.matchedBracketHighlightBoth = Boolean.parseBoolean(highlightBoth);
+ String animate = attrs.getValue("animate");
+ theme.matchedBracketAnimate = Boolean.parseBoolean(animate);
+ }
+
+ else if ("hyperlinks".equals(qName)) {
+ String fg = attrs.getValue("fg");
+ theme.hyperlinkFG = stringToColor(fg);
+ }
+
+ else if ("language".equals(qName)) {
+ String indexStr = attrs.getValue("index");
+ int index = Integer.parseInt(indexStr) - 1;
+ if (theme.secondaryLanguages.length>index) { // Sanity
+ Color bg = stringToColor(attrs.getValue("bg"));
+ theme.secondaryLanguages[index] = bg;
+ }
+ }
+
+ else if ("selection".equals(qName)) {
+ String useStr = attrs.getValue("useFG");
+ theme.useSelctionFG = Boolean.parseBoolean(useStr);
+ String color = attrs.getValue("fg");
+ theme.selectionFG = stringToColor(color,
+ getDefaultSelectionFG());
+ //System.out.println(theme.selectionFG);
+ color = attrs.getValue("bg");
+ theme.selectionBG = stringToColor(color,
+ getDefaultSelectionBG());
+ String roundedStr = attrs.getValue("roundedEdges");
+ theme.selectionRoundedEdges = Boolean.parseBoolean(roundedStr);
+ }
+
+ // Start of the syntax scheme definition
+ else if ("tokenStyles".equals(qName)) {
+ theme.scheme = new SyntaxScheme(theme.baseFont, false);
+ }
+
+ // A style in the syntax scheme
+ else if ("style".equals(qName)) {
+
+ String type = attrs.getValue("token");
+ Field field = null;
+ try {
+ field = Token.class.getField(type);
+ } catch (RuntimeException re) {
+ throw re; // FindBugs
+ } catch (Exception e) {
+ System.err.println("Invalid token type: " + type);
+ return;
+ }
+
+ if (field.getType()==int.class) {
+
+ int index = 0;
+ try {
+ index = field.getInt(theme.scheme);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ String fgStr = attrs.getValue("fg");
+ Color fg = stringToColor(fgStr);
+ theme.scheme.getStyle(index).foreground = fg;
+
+ String bgStr = attrs.getValue("bg");
+ Color bg = stringToColor(bgStr);
+ theme.scheme.getStyle(index).background = bg;
+
+ Font font = theme.baseFont;
+ String familyName = attrs.getValue("fontFamily");
+ if (familyName!=null) {
+ font = getFont(familyName, font.getStyle(),
+ font.getSize());
+ }
+ String sizeStr = attrs.getValue("fontSize");
+ if (sizeStr!=null) {
+ try {
+ float size = Float.parseFloat(sizeStr);
+ size = Math.max(size, 1f);
+ font = font.deriveFont(size);
+ } catch (NumberFormatException nfe) {
+ nfe.printStackTrace();
+ }
+ }
+ theme.scheme.getStyle(index).font = font;
+
+ boolean styleSpecified = false;
+ boolean bold = false;
+ boolean italic = false;
+ String boldStr = attrs.getValue("bold");
+ if (boldStr!=null) {
+ bold = Boolean.parseBoolean(boldStr);
+ styleSpecified = true;
+ }
+ String italicStr = attrs.getValue("italic");
+ if (italicStr!=null) {
+ italic = Boolean.parseBoolean(italicStr);
+ styleSpecified = true;
+ }
+ if (styleSpecified) {
+ int style = 0;
+ if (bold) {
+ style |= Font.BOLD;
+ }
+ if (italic) {
+ style |= Font.ITALIC;
+ }
+ Font orig = theme.scheme.getStyle(index).font;
+ theme.scheme.getStyle(index).font =
+ orig.deriveFont(style);
+ }
+
+ String ulineStr = attrs.getValue("underline");
+ if (ulineStr!=null) {
+ boolean uline = Boolean.parseBoolean(ulineStr);
+ theme.scheme.getStyle(index).underline = uline;
+ }
+
+ }
+
+ }
+
+ }
+
+ @Override
+ public void warning(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ }
+
+
+}
diff --git a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java
old mode 100644
new mode 100755
index 3290d9154..8ac6429ea
--- a/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java
+++ b/tools/agent_debugger/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java
@@ -2,837 +2,566 @@
* 02/21/2004
*
* Token.java - A token used in syntax highlighting.
- * Copyright (C) 2004 Robert Futrell
- * robert_futrell at users.sourceforge.net
- * http://fifesoft.com/rsyntaxtextarea
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE file for details.
*/
package org.fife.ui.rsyntaxtextarea;
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics2D;
import java.awt.Rectangle;
-import java.awt.geom.Rectangle2D;
+
import javax.swing.text.TabExpander;
+
/**
- * A generic token that functions as a node in a linked list of syntax highlighted tokens for some language.
- * Token
is a piece of text representing some logical token in source code for a programming language.
- * For example, the line of C code:
- * Token
is a piece of text representing some logical token in
+ * source code for a programming language. For example, the line of C code:
* int i = 0;
*
- *
- * would be broken into 8 Token
s: the first representing int
, the second whitespace, the third
- * i
, the fourth whitespace, the fifth =
, etc.
- * Token
s: the first representing
+ * int
, the second whitespace, the third i
, the fourth
+ * whitespace, the fifth =
, etc.line
.
- * @param end
- * The last character's position in line
.
- * @param startOffset
- * The offset into the document at which this token begins.
- * @param type
- * A token type listed as "generic" above.
- */
- public Token(final char[] line, final int beg, final int end,
- final int startOffset, final int type) {
- this();
- set(line, beg, end, startOffset, type);
- }
-
- /**
- * Creates this token as a deep copy of the passed-in token.
- *
- * @param t2
- * The token from which to make a copy.
- */
- public Token(Token t2) {
- this();
- copyFrom(t2);
- }
-
- /**
- * Appends HTML code for painting this token, using the given text area's color scheme.
- *
- * @param sb
- * The buffer to append to.
- * @param textArea
- * The text area whose color scheme to use.
- * @param fontFamily
- * Whether to include the font family in the HTML for this token. You can pass false
for
- * this parameter if, for example, you are making all your HTML be monospaced, and don't want any crazy
- * fonts being used in the editor to be reflected in your HTML.
- * @return The buffer appended to.
- * @see #getHTMLRepresentation(RSyntaxTextArea)
- */
- public StringBuffer appendHTMLRepresentation(StringBuffer sb,
- RSyntaxTextArea textArea,
- boolean fontFamily) {
-
- SyntaxScheme colorScheme = textArea.getSyntaxScheme();
- Style scheme = colorScheme.styles[type];
- Font font = textArea.getFontForTokenType(type);// scheme.font;
-
- if (font.isBold())
- sb.append("");
- if (font.isItalic())
- sb.append("