mirror of
https://github.com/deathmarine/Luyten.git
synced 2024-11-23 04:40:07 +00:00
Mavenize
This commit is contained in:
parent
95ea527811
commit
9fcd1e4b6a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/bin
|
||||
/target
|
||||
|
18
Assembly.xml
Normal file
18
Assembly.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<assembly>
|
||||
<id>${project.version}</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<unpack>true</unpack>
|
||||
<scope>runtime</scope>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.build.outputDirectory}</directory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
100
build.xml
100
build.xml
@ -1,100 +0,0 @@
|
||||
<project name="Luyten" default="package">
|
||||
<property environment="env" />
|
||||
<property name="env.BUILD_NUMBER" value="0" />
|
||||
<property name="debuglevel" value="source,lines,vars" />
|
||||
<property name="target" value="1.6" />
|
||||
<property name="source" value="1.6" />
|
||||
<property name="launch4j.dir" location="../../../launch4j" />
|
||||
|
||||
<path id="Model.classpath">
|
||||
<pathelement location="bin" />
|
||||
<fileset dir="lib">
|
||||
<include name="**/*.jar"/>
|
||||
</fileset>
|
||||
</path>
|
||||
|
||||
<taskdef name="launch4j"
|
||||
classname="net.sf.launch4j.ant.Launch4jTask"
|
||||
classpath="${launch4j.dir}/launch4j.jar
|
||||
:${launch4j.dir}/lib/xstream.jar" />
|
||||
|
||||
<target name="init" description="Initialize directories, etc.">
|
||||
<mkdir dir="bin" />
|
||||
<mkdir dir="dist" />
|
||||
<copy includeemptydirs="false" todir="bin">
|
||||
<fileset dir="src">
|
||||
<exclude name="**/*.java" />
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="clean" depends="init"
|
||||
description="Clean up all the temp stuff and previous builds">
|
||||
<delete>
|
||||
<fileset dir="bin" />
|
||||
<fileset dir="dist" />
|
||||
</delete>
|
||||
</target>
|
||||
<echo message="${ant.project.name}: ${ant.file}" />
|
||||
|
||||
<target name="compile" depends="clean, init" description="Compile all the code">
|
||||
<replace file="src/com/modcrafting/luyten/Model.java" token="JENKINSBUILDNUMBER" value="${env.BUILD_NUMBER}"/>
|
||||
<javac debug="true" debuglevel="${debuglevel}" destdir="bin"
|
||||
includeantruntime="false" source="${source}" target="${target}">
|
||||
<src path="src" />
|
||||
<classpath refid="Model.classpath" />
|
||||
<compilerarg value="-Xlint:none" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="package" depends="compile" description="Make the distributable jar file">
|
||||
<copy includeemptydirs="false" todir="bin/resources">
|
||||
<fileset dir="src/resources" includes="*.png" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false" todir="bin/themes">
|
||||
<fileset dir="src/themes" includes="*.xml" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false" todir="bin">
|
||||
<fileset dir="src" includes="theme.dtd" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false" todir="bin/org/fife/ui/rsyntaxtextarea">
|
||||
<fileset dir="src/org/fife/ui/rsyntaxtextarea" includes="*.properties" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false"
|
||||
todir="bin/org/fife/ui/rsyntaxtextarea/focusabletip">
|
||||
<fileset dir="src/org/fife/ui/rsyntaxtextarea/focusabletip"
|
||||
includes="*.properties" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false"
|
||||
todir="bin/org/fife/ui/rsyntaxtextarea/focusabletip">
|
||||
<fileset dir="src/org/fife/ui/rsyntaxtextarea/focusabletip"
|
||||
includes="*.png" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false" todir="bin/org/fife/ui/rsyntaxtextarea/modes">
|
||||
<fileset dir="src/org/fife/ui/rsyntaxtextarea/modes"
|
||||
includes="*.flex" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false" todir="bin/org/fife/ui/rtextarea">
|
||||
<fileset dir="src/org/fife/ui/rtextarea" includes="*.properties" />
|
||||
</copy>
|
||||
<copy includeemptydirs="false" todir="bin/distfiles">
|
||||
<fileset dir="distfiles" includes="*.txt" />
|
||||
</copy>
|
||||
|
||||
<jar basedir="bin" jarfile="dist/temp.jar" defaultexcludes="true">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="com.modcrafting.luyten.Model" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<zip destfile="dist/${ant.project.name}.jar">
|
||||
<zipgroupfileset dir="lib"
|
||||
includes="*.jar" />
|
||||
<zipgroupfileset dir="dist" includes="temp.jar" />
|
||||
</zip>
|
||||
<delete file="dist/temp.jar" />
|
||||
<launch4j configFile="./l4j.xml"/>
|
||||
|
||||
</target>
|
||||
|
||||
|
||||
</project>
|
22
l4j.xml
22
l4j.xml
@ -1,22 +0,0 @@
|
||||
<launch4jConfig>
|
||||
<dontWrapJar>false</dontWrapJar>
|
||||
<headerType>gui</headerType>
|
||||
<jar>.\dist\Luyten.jar</jar>
|
||||
<outfile>.\dist\Luyten.exe</outfile>
|
||||
<errTitle></errTitle>
|
||||
<cmdLine></cmdLine>
|
||||
<chdir></chdir>
|
||||
<priority>normal</priority>
|
||||
<downloadUrl>http://java.com/download</downloadUrl>
|
||||
<supportUrl></supportUrl>
|
||||
<customProcName>false</customProcName>
|
||||
<stayAlive>false</stayAlive>
|
||||
<manifest></manifest>
|
||||
<icon>.\Luyten.ico</icon>
|
||||
<jre>
|
||||
<path></path>
|
||||
<minVersion>1.6.0_0</minVersion>
|
||||
<maxVersion>1.8.0_0</maxVersion>
|
||||
<jdkPreference>preferJre</jdkPreference>
|
||||
</jre>
|
||||
</launch4jConfig>
|
Binary file not shown.
148
pom.xml
Normal file
148
pom.xml
Normal file
@ -0,0 +1,148 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>us.deathmarine</groupId>
|
||||
<artifactId>luyten</artifactId>
|
||||
<version>0.4.1</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fifesoft</groupId>
|
||||
<artifactId>rsyntaxtextarea</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bitbucket.mstrobel</groupId>
|
||||
<artifactId>procyon-core</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bitbucket.mstrobel</groupId>
|
||||
<artifactId>procyon-expressions</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bitbucket.mstrobel</groupId>
|
||||
<artifactId>procyon-reflection</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bitbucket.mstrobel</groupId>
|
||||
<artifactId>procyon-compilertools</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>central</id>
|
||||
<name>Central Repository</name>
|
||||
<url>http://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<finalName>${project.artifactId}-${project.version}-lib</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>Assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.modcrafting.luyten.Model</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.bluestemsoftware.open.maven.plugin</groupId>
|
||||
<artifactId>launch4j-plugin</artifactId>
|
||||
<version>1.5.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>l4j-gui</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>launch4j</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<headerType>gui</headerType>
|
||||
<outfile>target/${project.artifactId}-${project.version}.exe</outfile>
|
||||
<jar>target/${project.artifactId}-${project.version}.jar</jar>
|
||||
<errTitle>App Err</errTitle>
|
||||
<classPath>
|
||||
<mainClass>com.modcrafting.luyten.Model</mainClass>
|
||||
</classPath>
|
||||
<icon>Luyten.ico</icon>
|
||||
<jre>
|
||||
<minVersion>1.6.0</minVersion>
|
||||
<maxVersion>1.8.0</maxVersion>
|
||||
<initialHeapSize>128</initialHeapSize>
|
||||
<maxHeapSize>1024</maxHeapSize>
|
||||
</jre>
|
||||
<versionInfo>
|
||||
<fileVersion>0.${project.version}</fileVersion>
|
||||
<txtFileVersion>0.${project.version}</txtFileVersion>
|
||||
<fileDescription>Java Decompiler</fileDescription>
|
||||
<copyright>2014</copyright>
|
||||
<productVersion>0.${project.version}</productVersion>
|
||||
<txtProductVersion>0.${project.version}</txtProductVersion>
|
||||
<productName>${project.artifactId}</productName>
|
||||
<internalName>${project.artifactId}</internalName>
|
||||
<originalFilename>Luyten.exe</originalFilename>
|
||||
</versionInfo>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<configuration>
|
||||
<show>public</show>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<configuration>
|
||||
<show>public</show>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</project>
|
@ -21,6 +21,7 @@ import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
@ -34,13 +35,50 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TooManyListenersException;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.ButtonModel;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.JRadioButtonMenuItem;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.border.BevelBorder;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
@ -52,6 +90,7 @@ import javax.swing.tree.TreeSelectionModel;
|
||||
import com.strobel.assembler.metadata.*;
|
||||
import com.strobel.core.VerifyArgument;
|
||||
import com.strobel.decompiler.languages.Language;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.Theme;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
@ -59,7 +98,6 @@ import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
import com.strobel.assembler.InputTypeLoader;
|
||||
import com.strobel.core.StringUtilities;
|
||||
import com.strobel.decompiler.DecompilationOptions;
|
||||
import com.strobel.decompiler.DecompilerDriver;
|
||||
import com.strobel.decompiler.DecompilerSettings;
|
||||
import com.strobel.decompiler.PlainTextOutput;
|
||||
import com.strobel.decompiler.languages.Languages;
|
||||
@ -70,17 +108,7 @@ public class Model extends JFrame implements WindowListener {
|
||||
|
||||
final LuytenTypeLoader typeLoader = new LuytenTypeLoader();
|
||||
final Map<String, Language> languageLookup = new HashMap<String, Language>();
|
||||
|
||||
//
|
||||
// I'm caching this for performance reasons. There is a pretty substantial cost associated
|
||||
// with loading types from the disk. If you cache the metadata, decompilation should be much
|
||||
// faster after the first few classes.
|
||||
//
|
||||
// Ideally, this should be purged if the user elects to 'refresh' the view. It'll retain
|
||||
// metadata for types it has already seen, so if the user replaces the classes/jars they're
|
||||
// viewing and wants to see changes, a refresh feature that purges the MetadataSystem would
|
||||
// be useful.
|
||||
//
|
||||
static File base;
|
||||
MetadataSystem metadataSystem = new MetadataSystem(typeLoader);
|
||||
|
||||
JTree tree;
|
||||
@ -105,7 +133,6 @@ public class Model extends JFrame implements WindowListener {
|
||||
JLabel label;
|
||||
HashSet<OpenFile> hmap = new HashSet<OpenFile>();
|
||||
boolean open = false;
|
||||
public static final String JENKINS_BUILD = "JENKINSBUILDNUMBER";
|
||||
private ButtonGroup languagesGroup;
|
||||
private State state;
|
||||
|
||||
@ -385,7 +412,7 @@ public class Model extends JFrame implements WindowListener {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Luyten Gui Build#" + JENKINS_BUILD + "\n" +
|
||||
"Luyten Gui \n" +
|
||||
"by Deathmarine\n\n" +
|
||||
"Powered By\nProcyon\n" +
|
||||
"(c) 2013 Mike Strobel\n\n" +
|
||||
@ -411,17 +438,7 @@ public class Model extends JFrame implements WindowListener {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (args.length > 0) {
|
||||
if (Arrays.asList(args).contains("--nogui")) {
|
||||
List<String> list = new LinkedList<String>(Arrays.asList(args));
|
||||
list.remove("--nogui");
|
||||
DecompilerDriver.main(list.toArray(new String[]{}));
|
||||
} else {
|
||||
new Model(args[0]);
|
||||
}
|
||||
} else {
|
||||
new Model();
|
||||
}
|
||||
new Model();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -509,7 +526,7 @@ public class Model extends JFrame implements WindowListener {
|
||||
settings.setFlattenSwitchBlocks(flattenSwitchBlocks.isSelected());
|
||||
settings.setForceExplicitImports(forceExplicitImports.isSelected());
|
||||
settings.setShowSyntheticMembers(showSyntheticMembers.isSelected());
|
||||
settings.setShowNestedTypes(showNestedTypes.isSelected());
|
||||
settings.setExcludeNestedTypes(showNestedTypes.isSelected());
|
||||
settings.setForceExplicitTypeArguments(forceExplicitTypes.isSelected());
|
||||
settings.setRetainRedundantCasts(retainRedundantCasts.isSelected());
|
||||
settings.setIncludeErrorDiagnostics(showDebugInfo.isSelected());
|
||||
@ -1111,4 +1128,5 @@ public class Model extends JFrame implements WindowListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* 02/24/2004
|
||||
*
|
||||
* DocumentReader.java - A reader for javax.swing.text.Document
|
||||
* objects.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.io;
|
||||
|
||||
import java.io.Reader;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
|
||||
/**
|
||||
* A <code>Reader</code> for <code>javax.swing.text.Document</code> 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
|
||||
* <code>reset()</code> will reposition the stream to this point.
|
||||
*
|
||||
* @param readAheadLimit Ignored.
|
||||
*/
|
||||
public void mark(int readAheadLimit) {
|
||||
mark = position;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tells whether this reader supports the <code>mark</code> operation.
|
||||
* This always returns <code>true</code> for <code>DocumentReader</code>.
|
||||
*/
|
||||
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 <code>array.length</code> characters from the beginning
|
||||
* of the document into <code>array</code>.
|
||||
*
|
||||
* @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 <code>-1</code> 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. <code>DocumentReader</code> will
|
||||
* always return true.
|
||||
*
|
||||
* @return <code>true</code> 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 <code>pos</code>
|
||||
* 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
/*
|
||||
* 09/23/2004
|
||||
*
|
||||
* UnicodeReader.java - A reader for Unicode input streams that is capable of
|
||||
* discerning which particular encoding is being used via the BOM.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
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:
|
||||
* <ul>
|
||||
* <li>UTF-8
|
||||
* <li>UTF-16LE
|
||||
* <li>UTF-16BE
|
||||
* <li>UTF-32LE
|
||||
* <li>UTF-32BE
|
||||
* </ul>
|
||||
* 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.<p>
|
||||
*
|
||||
* For optimum performance, it is recommended that you wrap all instances of
|
||||
* <code>UnicodeReader</code> with a <code>java.io.BufferedReader</code>.<p>
|
||||
*
|
||||
* This class is mostly ripped off from the workaround in the description of
|
||||
* Java Bug 4508058.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.9
|
||||
*/
|
||||
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 <code>java.io.InputStreamReader</code> 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
|
||||
* <code>UnicodeReader</code> on files.<p>
|
||||
* 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
|
||||
* <code>UnicodeReader</code> on files.<p>
|
||||
* 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
|
||||
* <code>UnicodeReader</code> on files.<p>
|
||||
* 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 <code>null</code>, 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 <code>defaultEncoding</code> 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 <code>null</code>, 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 <code>null</code>, 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 <code>-1</code> 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,253 +0,0 @@
|
||||
/*
|
||||
* 09/24/2004
|
||||
*
|
||||
* UnicodeWriter.java - Writes Unicode output with the proper BOM.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
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 preceded by the proper Unicode BOM. If it is any
|
||||
* other encoding, this class behaves just like <code>OutputStreamWriter</code>.
|
||||
* This class is here because Java's <code>OutputStreamWriter</code> apparently
|
||||
* doesn't believe in writing BOMs.
|
||||
* <p>
|
||||
*
|
||||
* For optimum performance, it is recommended that you wrap all instances of
|
||||
* <code>UnicodeWriter</code> with a <code>java.io.BufferedWriter</code>.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.7
|
||||
*/
|
||||
public class UnicodeWriter extends Writer {
|
||||
|
||||
/**
|
||||
* If this system property evaluates to "<code>false</code>", 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,530 +0,0 @@
|
||||
/*
|
||||
* 11/14/2003
|
||||
*
|
||||
* RPrintUtilities.java - A collection of static methods useful for printing
|
||||
* text from Swing text components.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.print;
|
||||
|
||||
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.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 <code>maxCharsPerLine</code> in
|
||||
* <code>line</code> of one of the chars in <code>breakChars</code>, or simply returns
|
||||
* <code>maxCharsPerLine-1</code> if none of the <code>breakChars</code> comes before
|
||||
* that position. This position represents the logical line break for this <code>java.lang.String</code>
|
||||
* if it is being printed in a monospaced font when lines can only be <code>maxCharsPerLine</code>
|
||||
* characters long.
|
||||
*
|
||||
* @param line The text being printed.
|
||||
* @param maxCharsPerLine Only up-to this many characters from
|
||||
* <code>line</code> can be printed on one line.
|
||||
* @return The logical position at which to stop printing <code>line</code>
|
||||
* to simulate word wrap.
|
||||
*/
|
||||
private static int getLineBreakPoint(String line, final int maxCharsPerLine) {
|
||||
|
||||
int breakPoint = -1;
|
||||
for (int i=0; i<breakChars.length; i++) {
|
||||
int breakCharPos = line.lastIndexOf(breakChars[i], maxCharsPerLine-1);
|
||||
if (breakCharPos > breakPoint)
|
||||
breakPoint = breakCharPos;
|
||||
}
|
||||
|
||||
return (breakPoint==-1 ? maxCharsPerLine-1 : breakPoint);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints a <code>Document</code> 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 <code>javax.swing.text.Document</code> 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 <code>Document</code> 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 <code>javax.swing.text.Document</code> 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 <code>Document</code> 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 <code>javax.swing.text.JTextComponent</code>
|
||||
* whose text you're printing.
|
||||
* @param font The font to use for printing. If <code>null</code>, then
|
||||
* <code>textComponent</code>'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 (int i=0; i<breakChars.length; i++) {
|
||||
// "+1" below so we include the character on the line.
|
||||
int pos = currentLineString.lastIndexOf(breakChars[i]) + 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 <code>segment</code> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
/*
|
||||
* 01/25/2009
|
||||
*
|
||||
* AbstractJFlexCTokenMaker.java - Base class for token makers that use curly
|
||||
* braces to denote code blocks, such as C, C++, Java, Perl, etc.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
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.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
|
||||
|
||||
protected static final Action INSERT_BREAK_ACTION = new InsertBreakAction();
|
||||
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> always as C-style languages use curly braces
|
||||
* to denote code blocks.
|
||||
*
|
||||
* @return <code>true</code> 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 getMarkOccurrencesOfTokenType(int type) {
|
||||
return type==Token.IDENTIFIER || type==Token.FUNCTION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@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 <code>offs</code> appears
|
||||
* to have a "nested" comment (i.e., contains "<code>/*</code>"
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 03/23/2005
|
||||
*
|
||||
* AbstractJFlexTokenMaker.java - Base class for token makers generated from
|
||||
* programs such as JFlex.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for JFlex-generated token makers. This class attempts to factor
|
||||
* out all common code from these classes. Many methods <em>almost</em> could
|
||||
* be factored out into this class, but cannot because they reference JFlex
|
||||
* variables that we cannot access from this class.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.1
|
||||
*/
|
||||
public abstract class AbstractJFlexTokenMaker extends TokenMakerBase {
|
||||
|
||||
protected Segment s;
|
||||
|
||||
protected int start; // Just for states.
|
||||
protected int offsetShift; // As parser always starts at 0, but our line doesn't.
|
||||
|
||||
|
||||
/**
|
||||
* Declared here so we can define overloads that refer to this method.
|
||||
*
|
||||
* @param newState The new JFlex state to enter.
|
||||
*/
|
||||
public abstract void yybegin(int newState);
|
||||
|
||||
|
||||
/**
|
||||
* Starts a new JFlex state and changes the current language index.
|
||||
*
|
||||
* @param state The new JFlex state to enter.
|
||||
* @param languageIndex The new language index.
|
||||
*/
|
||||
protected void yybegin(int state, int languageIndex) {
|
||||
yybegin(state);
|
||||
setLanguageIndex(languageIndex);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 11/07/2004
|
||||
*
|
||||
* AbstractTokenMaker.java - An abstract implementation of TokenMaker.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
|
||||
/**
|
||||
* 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.<p>
|
||||
*
|
||||
* @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, <code>Token.RESERVED_WORD</code> or
|
||||
* <code>Token.FUNCTION</code>.
|
||||
*/
|
||||
protected TokenMap wordsToHighlight;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public AbstractTokenMaker() {
|
||||
wordsToHighlight = getWordsToHighlight();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the words to highlight for this programming language.
|
||||
*
|
||||
* @return A <code>TokenMap</code> 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 <code>TokenMaker</code> if necessary.
|
||||
*/
|
||||
public void removeLastToken() {
|
||||
if (previousToken==null) {
|
||||
firstToken = currentToken = null;
|
||||
}
|
||||
else {
|
||||
currentToken = previousToken;
|
||||
currentToken.setNextToken(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* 12/14/08
|
||||
*
|
||||
* AbstractTokenMakerFactory.java - Base class for TokenMaker implementations.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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 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 <code>TokenMaker</code> of the corresponding type
|
||||
* is returned.
|
||||
*/
|
||||
private Map tokenMakerMap;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
protected AbstractTokenMakerFactory() {
|
||||
tokenMakerMap = new HashMap();
|
||||
initTokenMakerMap();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link TokenMaker} for the specified key.
|
||||
*
|
||||
* @param key The key.
|
||||
* @return The corresponding <code>TokenMaker</code>, or <code>null</code>
|
||||
* if none matches the specified key.
|
||||
*/
|
||||
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
|
||||
* <code>TokenMakerCreator</code>s. Subclasses should override this method
|
||||
* and call one of the <code>putMapping</code> overloads to register
|
||||
* {@link TokenMaker}s for syntax constants.
|
||||
*
|
||||
* @see #putMapping(String, String)
|
||||
* @see #putMapping(String, String, ClassLoader)
|
||||
*/
|
||||
protected abstract void initTokenMakerMap();
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Set keySet() {
|
||||
return tokenMakerMap.keySet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
|
||||
* class name.
|
||||
*
|
||||
* @param key The key.
|
||||
* @param className The <code>TokenMaker</code> 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 <code>TokenMaker</code> implementation
|
||||
* class name.
|
||||
*
|
||||
* @param key The key.
|
||||
* @param className The <code>TokenMaker</code> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 02/06/2011
|
||||
*
|
||||
* ActiveLineRangeEvent.java - Notifies listeners of an "active line range"
|
||||
* change in an RSyntaxTextArea.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public class ActiveLineRangeEvent extends EventObject {
|
||||
|
||||
private int min;
|
||||
private int max;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param source The text area.
|
||||
* @param min The first line in the active line range, or
|
||||
* <code>-1</code> if the line range is being cleared.
|
||||
* @param max The last line in the active line range, or
|
||||
* <code>-1</code> 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 <code>-1</code> 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 <code>-1</code> if the range is being
|
||||
* cleared.
|
||||
* @see #getMax()
|
||||
*/
|
||||
public int getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 02/06/2011
|
||||
*
|
||||
* ActiveLineRangeListener.java - Listens for "active line range" changes
|
||||
* in an RSyntaxTextArea.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
|
||||
/**
|
||||
* Listens for "active line range" events from an <code>RSyntaxTextArea</code>.
|
||||
* 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.<p>
|
||||
*
|
||||
* See the <code>RSTALanguageSupport</code> project at
|
||||
* <a href="http://fifesoft.com">http://fifesoft.com</a> for some
|
||||
* <code>LanguageSupport</code> 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 <code>-1</code>.
|
||||
*/
|
||||
public void activeLineRangeChanged(ActiveLineRangeEvent e);
|
||||
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 07/23/2009
|
||||
*
|
||||
* ChangeableColorHighlightPainter.java - A highlighter whose color you can
|
||||
* change.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
*/
|
||||
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 <code>null</code>.
|
||||
* @see #getColor()
|
||||
*/
|
||||
public void setColor(Color color) {
|
||||
if (color==null) {
|
||||
throw new IllegalArgumentException("color cannot be null");
|
||||
}
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,470 +0,0 @@
|
||||
/*
|
||||
* 02/21/2005
|
||||
*
|
||||
* CodeTemplateManager.java - manages code templates.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import javax.swing.KeyStroke;
|
||||
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."<p>
|
||||
*
|
||||
* 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
|
||||
* <code>CodeTemplateManager</code> on the EDT. Modifying a
|
||||
* <code>CodeTemplate</code> retrieved from a <code>CodeTemplateManager</code>
|
||||
* while <em>not</em> on the EDT could cause problems.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.1
|
||||
*/
|
||||
public class CodeTemplateManager {
|
||||
|
||||
private int maxTemplateIDLength;
|
||||
private List templates;
|
||||
|
||||
private KeyStroke insertTrigger;
|
||||
private String insertTriggerString;
|
||||
private Segment s;
|
||||
private TemplateComparator comparator;
|
||||
private File directory;
|
||||
|
||||
private static final int mask = InputEvent.CTRL_MASK|InputEvent.SHIFT_MASK;
|
||||
static final KeyStroke TEMPLATE_KEYSTROKE = KeyStroke.
|
||||
getKeyStroke(KeyEvent.VK_SPACE, mask);
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CodeTemplateManager() {
|
||||
|
||||
// Default insert trigger is a space.
|
||||
// FIXME: See notes in RSyntaxTextAreaDefaultInputMap.
|
||||
setInsertTrigger(TEMPLATE_KEYSTROKE);
|
||||
|
||||
s = new Segment();
|
||||
comparator = new TemplateComparator();
|
||||
templates = new ArrayList();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers the specified template with this template manager.
|
||||
*
|
||||
* @param template The template to register.
|
||||
* @throws IllegalArgumentException If <code>template</code> is
|
||||
* <code>null</code>.
|
||||
* @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
|
||||
* <code>RSyntaxTextArea</code>, 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 <code>RSyntaxTextArea</code>,
|
||||
* 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
|
||||
* <code>null</code> 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
|
||||
* <code>CodeTemplate</code> 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 <code>true</code> if the template was removed, <code>false</code>
|
||||
* if the template was not in this template manager.
|
||||
* @throws IllegalArgumentException If <code>template</code> is
|
||||
* <code>null</code>.
|
||||
* @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 <code>null</code> if
|
||||
* there was no template with the specified ID.
|
||||
* @throws IllegalArgumentException If <code>id</code> is <code>null</code>.
|
||||
* @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) {
|
||||
for (int i=0; i<newTemplates.length; i++) {
|
||||
templates.add(newTemplates[i]);
|
||||
}
|
||||
}
|
||||
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 (int i=0; i<count; i++) {
|
||||
/*boolean deleted = */oldXMLFiles[i].delete();
|
||||
}
|
||||
|
||||
// Save all current templates as XML.
|
||||
boolean wasSuccessful = true;
|
||||
for (Iterator i=templates.iterator(); i.hasNext(); ) {
|
||||
CodeTemplate template = (CodeTemplate)i.next();
|
||||
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
|
||||
* <code>RSyntaxTextArea</code>, 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 <code>null</code>, 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
|
||||
* <code>-1</code> 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 (Iterator i=templates.iterator(); i.hasNext(); ) {
|
||||
CodeTemplate temp = (CodeTemplate)i.next();
|
||||
if (temp==null || temp.getID()==null) {
|
||||
i.remove();
|
||||
}
|
||||
else {
|
||||
maxTemplateIDLength = Math.max(maxTemplateIDLength,
|
||||
temp.getID().length());
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(templates);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A comparator that takes a <code>CodeTemplate</code> as its first
|
||||
* parameter and a <code>Segment</code> 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,379 +0,0 @@
|
||||
/*
|
||||
* 10/28/2004
|
||||
*
|
||||
* DefaultToken.java - The default token used in syntax highlighting.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import javax.swing.text.Segment;
|
||||
import javax.swing.text.TabExpander;
|
||||
import javax.swing.text.Utilities;
|
||||
|
||||
|
||||
/**
|
||||
* The default token used in the <code>org.fife.ui.rsyntaxtextarea</code> syntax
|
||||
* package. This token type paints itself as you would expect, and properly
|
||||
* accounts for rendering hints (anti-aliasing and fractional font metrics).<p>
|
||||
*
|
||||
* The current implementation paints as follows:
|
||||
* <ul>
|
||||
* <li>The first tab, if any, is found in the token.</li>
|
||||
* <li>All characters up to that tab, if it exists, are painted as a
|
||||
* group. If no tab was found, all characters in the token are
|
||||
* painted.</li>
|
||||
* <li>If a tab was found, its width is calculated and it is painted.</li>
|
||||
* <li>Repeat until all characters are painted.</li>
|
||||
* </ul>
|
||||
* 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.<p>
|
||||
*
|
||||
* 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 <code>int</code> and not a
|
||||
* <code>float</code>). 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 <code>line</code>.
|
||||
* @param end The last character's position in <code>line</code>.
|
||||
* @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 <code>line</code>.
|
||||
* @param end The last character's position in <code>line</code>.
|
||||
* @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 <code>x</code> if the token list
|
||||
* starts at pixel location <code>x0</code><p>.
|
||||
* This method will return the document position "closest" to the
|
||||
* x-coordinate (i.e., if they click on the "right-half" of the
|
||||
* <code>w</code> in <code>awe</code>, the caret will be placed in
|
||||
* between the <code>w</code> and <code>e</code>; similarly, clicking on
|
||||
* the left-half places the caret between the <code>a</code> and
|
||||
* <code>w</code>). This makes it useful for methods such as
|
||||
* <code>viewToModel</code> found in <code>javax.swing.text.View</code>
|
||||
* 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
|
||||
* <code>tokenList</code>.
|
||||
* @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 <code>tokenList</code> is
|
||||
* <code>null</code> or has type <code>Token.NULL</code>, then
|
||||
* <code>-1</code is returned; the caller should recognize this and
|
||||
* return the actual end position of the (empty) line.
|
||||
*/
|
||||
public int getListOffset(RSyntaxTextArea textArea, TabExpander e,
|
||||
float x0, float x) {
|
||||
|
||||
// If the coordinate in question is before this line's start, quit.
|
||||
if (x0 >= 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 <code>3</code>
|
||||
* here returns the width of the "whi" portion of the token.<p>
|
||||
*
|
||||
* @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 <code>null</code>.
|
||||
* @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
|
||||
* <code>tokenList</code>.
|
||||
* @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.getBackgroundForToken(this);
|
||||
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);
|
||||
}
|
||||
|
||||
// Don't check if it's whitespace - some TokenMakers may return types
|
||||
// other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
|
||||
// This also allows us to paint tab lines for MLC's.
|
||||
if (host.getPaintTabLines() && origX==host.getMargin().left) {// && isWhitespace()) {
|
||||
paintTabLines(origX, (int)y, (int)nextX, g, e, host);
|
||||
}
|
||||
|
||||
return nextX;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
/*
|
||||
* 10/28/2004
|
||||
*
|
||||
* DefaultTokenFactory.java - Default token factory.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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
|
||||
* <code>createToken</code> 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 <code>resetTokenList</code> when creating a new token list so
|
||||
* that the token maker can keep an accurate list of available tokens.<p>
|
||||
*
|
||||
* 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;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
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 <code>TokenMaker</code> every time a token list is generated for
|
||||
* a new line so the tokens can be reused.
|
||||
*/
|
||||
public void resetAllTokens() {
|
||||
currentFreeToken = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 12/14/2008
|
||||
*
|
||||
* DefaultTokenMakerFactory.java - The default TokenMaker factory.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
|
||||
/**
|
||||
* The default implementation of <code>TokenMakerFactory</code>. 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 {
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected void initTokenMakerMap() {
|
||||
|
||||
String pkg = "org.fife.ui.rsyntaxtextarea.modes.";
|
||||
|
||||
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_DELPHI, pkg + "DelphiTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_DTD, pkg + "DtdTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_FORTRAN, pkg + "FortranTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_GROOVY, pkg + "GroovyTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_HTML, pkg + "HTMLTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_JAVA, pkg + "JavaTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_JAVASCRIPT, pkg + "JavaScriptTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_JSON, pkg + "JsonTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_JSP, pkg + "JSPTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_LATEX, pkg + "LatexTokenMaker");
|
||||
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_UNIX_SHELL, pkg + "UnixShellTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_WINDOWS_BATCH, pkg + "WindowsBatchTokenMaker");
|
||||
putMapping(SYNTAX_STYLE_XML, pkg + "XMLTokenMaker");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 08/11/2009
|
||||
*
|
||||
* DocumentRange.java - A range of text in a document.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
|
||||
/**
|
||||
* A range of text in a document.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public class DocumentRange {
|
||||
|
||||
private int startOffs;
|
||||
private int endOffs;
|
||||
|
||||
|
||||
public DocumentRange(int startOffs, int endOffs) {
|
||||
this.startOffs = startOffs;
|
||||
this.endOffs = endOffs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a string representation of this object.
|
||||
*
|
||||
* @return A string representation of this object.
|
||||
*/
|
||||
public String toString() {
|
||||
return "[DocumentRange: " + startOffs + "-" + endOffs + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,766 +0,0 @@
|
||||
/*
|
||||
* 08/10/2009
|
||||
*
|
||||
* ErrorStrip.java - A component that can visually show Parser messages (syntax
|
||||
* errors, etc.) in an RSyntaxTextArea.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.ToolTipManager;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.event.CaretEvent;
|
||||
import javax.swing.event.CaretListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.parser.Parser;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.TaskTagParser.TaskNotice;
|
||||
|
||||
|
||||
/**
|
||||
* A component to sit alongside an {@link RSyntaxTextArea} that displays
|
||||
* colored markers for locations of interest (parser errors, marked
|
||||
* occurrences, etc.).<p>
|
||||
*
|
||||
* <code>ErrorStrip</code>s display <code>ParserNotice</code>s from
|
||||
* {@link Parser}s. Currently, the only way to get lines flagged in this
|
||||
* component is to register a <code>Parser</code> on an RSyntaxTextArea and
|
||||
* return <code>ParserNotice</code>s for each line to display an icon for.
|
||||
* The severity of each notice must be at least the threshold set by
|
||||
* {@link #setLevelThreshold(int)} to be displayed in this error strip. The
|
||||
* default threshold is {@link ParserNotice#WARNING}.<p>
|
||||
*
|
||||
* An <code>ErrorStrip</code> can be added to a UI like so:
|
||||
* <pre>
|
||||
* 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);
|
||||
* </pre>
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.5
|
||||
*/
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
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. This will be one of the constants
|
||||
* defined in the <code>ParserNotice</code> class.
|
||||
*
|
||||
* @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+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/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 (Iterator i=notices.iterator(); i.hasNext(); ) {
|
||||
ParserNotice notice = (ParserNotice)i.next();
|
||||
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 (Iterator i=occurrences.iterator(); i.hasNext(); ) {
|
||||
DocumentRange range = (DocumentRange)i.next();
|
||||
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. This should be one of the constants defined in
|
||||
* the <code>ParserNotice</code> class. The default value is
|
||||
* {@link ParserNotice#WARNING}.
|
||||
*
|
||||
* @param level The new severity threshold.
|
||||
* @see #getLevelThreshold()
|
||||
* @see ParserNotice
|
||||
*/
|
||||
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() {
|
||||
boolean result = false;
|
||||
for (int i=0; i<notices.size(); i++) {
|
||||
if (notices.get(i) instanceof MarkedOccurrenceNotice) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
// Return the color for the highest-level parser.
|
||||
Color c = null;
|
||||
int lowestLevel = Integer.MAX_VALUE; // ERROR is 0
|
||||
for (Iterator i=notices.iterator(); i.hasNext(); ) {
|
||||
ParserNotice notice = (ParserNotice)i.next();
|
||||
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("<html>");
|
||||
sb.append(msg.getString("MultipleMarkers"));
|
||||
sb.append("<br>");
|
||||
for (int i=0; i<notices.size(); i++) {
|
||||
ParserNotice pn = (ParserNotice)notices.get(i);
|
||||
sb.append(" - ");
|
||||
sb.append(pn.getMessage());
|
||||
sb.append("<br>");
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
Line=Line: {0}
|
||||
MultipleMarkers=Multiple markers at this line:
|
||||
OccurrenceOf=Occurrence of "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Line: {0}
|
||||
MultipleMarkers=\u0647\u0646\u0627\u0643 \u0639\u062f\u0629 \u062a\u062d\u062f\u064a\u062f\u0627\u062a \u0641\u064a \u0647\u0630\u0627 \u0627\u0644\u0633\u0637\u0631:
|
||||
OccurrenceOf=\u0639\u062f\u062f \u0645\u0631\u0627\u062a \u062d\u062f\u0648\u062b"{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Zeile: {0}
|
||||
MultipleMarkers=Mehrfache Markierungen in dieser Zeile:
|
||||
OccurrenceOf=Auftreten von "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Linea: {0}
|
||||
MultipleMarkers=M\u00faltiples marcadores en esta linea:
|
||||
OccurrenceOf=Ocurrencia de "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Ligne: {0}
|
||||
MultipleMarkers=Plusieurs rep\u00e8res sur cette ligne:
|
||||
OccurrenceOf=Pr\u00e9sence de "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Sor: {0}
|
||||
MultipleMarkers=T\u00f6bb jel\u00f6l\u0151 ebben a sorban:
|
||||
OccurrenceOf=A(z) "{0}" el\u0151fordul\u00e1sa
|
@ -1,3 +0,0 @@
|
||||
Line=Line: {0}
|
||||
MultipleMarkers=Multiple markers at this line:
|
||||
OccurrenceOf=Occurrence of "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Linea: {0}
|
||||
MultipleMarkers=Multipli punti a questa linea:
|
||||
OccurrenceOf=Occorrenze di "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=\u884c: {0}
|
||||
MultipleMarkers=\u3053\u306e\u884c\u306e\u4e2d\u306b\u3044\u304f\u3064\u306e\u30de\u30fc\u30ab\u30fc\u306f\u3042\u308a\u307e\u3059:
|
||||
OccurrenceOf=\u51fa\u73fe\u56de\u6570 "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=\ud589: {0}
|
||||
MultipleMarkers=\uc774 \ud589\uc5d0 \ubcf5\uc218\uc758 \ub9c8\ucee4 \uc874\uc7ac:
|
||||
OccurrenceOf="{0}" \ubc1c\uc0dd
|
@ -1,3 +0,0 @@
|
||||
Line=Line: {0}
|
||||
MultipleMarkers=Multiple markers at this line:
|
||||
OccurrenceOf=Occurrence of "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Linia: {0}
|
||||
MultipleMarkers=Wiele znacznik w tej linii:
|
||||
OccurrenceOf=Wyst\u0119powanie "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Linha: {0}
|
||||
MultipleMarkers=M\u00faltiplos marcadores nesta linha:
|
||||
OccurrenceOf=Ocorr\u00eancia de: "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=\u0421\u0442\u0440\u043e\u043a\u0430: {0}
|
||||
MultipleMarkers=\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u0440\u043a\u0435\u0440\u043e\u0432 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435:
|
||||
OccurrenceOf=\u0412\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0435 "{0}"
|
@ -1,3 +0,0 @@
|
||||
Line=Sat\u0131r: {0}
|
||||
MultipleMarkers=Bu sat\u0131rda birden fazla i\u015faret\u00e7i:
|
||||
OccurrenceOf="{0}"\u0131n bulunmas\u0131
|
@ -1,3 +0,0 @@
|
||||
Line=\u884c: {0}
|
||||
MultipleMarkers=\u6b64\u884c\u6709\u591a\u6807\u8bb0
|
||||
OccurrenceOf=\u6709"{0}"\u51fa\u73b0
|
@ -1,3 +0,0 @@
|
||||
Line=Line: {0}
|
||||
MultipleMarkers=Multiple markers at this line:
|
||||
OccurrenceOf=Occurrence of "{0}"
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* 11/13/2008
|
||||
*
|
||||
* FileFileLocation.java - The location of a local file.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* The location of a local file.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
class FileFileLocation extends FileLocation {
|
||||
|
||||
/**
|
||||
* The file. This may or may not actually exist.
|
||||
*/
|
||||
private File file;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param file The local file.
|
||||
*/
|
||||
public FileFileLocation(File file) {
|
||||
try {
|
||||
// Useful on Windows and OS X.
|
||||
this.file = file.getCanonicalFile();
|
||||
} catch (IOException ioe) {
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected long getActualLastModified() {
|
||||
return file.lastModified();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the full path to the file.
|
||||
*
|
||||
* @return The full path to the file.
|
||||
* @see #getFileName()
|
||||
*/
|
||||
public String getFileFullPath() {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public String getFileName() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected InputStream getInputStream() throws IOException {
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected OutputStream getOutputStream() throws IOException {
|
||||
return new FileOutputStream(file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Always returns <code>true</code>.
|
||||
*
|
||||
* @return <code>true</code> always.
|
||||
* @see #isLocalAndExists()
|
||||
*/
|
||||
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()
|
||||
*/
|
||||
public boolean isLocalAndExists() {
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
/*
|
||||
* 11/13/2008
|
||||
*
|
||||
* FileLocation.java - Holds the location of a local or remote file.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,237 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* RSyntaxTextArea.License.txt 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}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
g.setColor(getBackground());
|
||||
g.fillRect(0,visibleRect.y, width,visibleRect.height);
|
||||
|
||||
if (textArea.getLineWrap()) {
|
||||
paintComponentWrapped(g);
|
||||
return;
|
||||
}
|
||||
|
||||
Document doc = textArea.getDocument();
|
||||
Element root = doc.getDefaultRootElement();
|
||||
textAreaInsets = textArea.getInsets(textAreaInsets);
|
||||
|
||||
// Get the first line to paint.
|
||||
int cellHeight = textArea.getLineHeight();
|
||||
int topLine = (visibleRect.y-textAreaInsets.top)/cellHeight;
|
||||
|
||||
// Get where to start painting (top of the row).
|
||||
// We need to be "scrolled up" up just enough for the missing part of
|
||||
// the first line.
|
||||
int y = topLine*cellHeight + textAreaInsets.top;
|
||||
|
||||
// AFTER calculating visual offset to paint at, account for folding.
|
||||
topLine += fm.getHiddenLineCountAbove(topLine, true);
|
||||
|
||||
// Paint the active line range.
|
||||
if (activeLineRangeStart>-1 && activeLineRangeEnd>-1) {
|
||||
Color activeLineRangeColor = getActiveLineRangeColor();
|
||||
g.setColor(activeLineRangeColor);
|
||||
try {
|
||||
|
||||
int y1 = rsta.yForLine(activeLineRangeStart);
|
||||
if (y1>-1) { // Not in a collapsed fold...
|
||||
|
||||
int y2 = rsta.yForLine(activeLineRangeEnd);
|
||||
if (y2==-1) { // In a collapsed fold
|
||||
y2 = y1;
|
||||
}
|
||||
y2 += cellHeight - 1;
|
||||
|
||||
int j = y1;
|
||||
while (j<=y2) {
|
||||
int yEnd = Math.min(y2, j+getWidth());
|
||||
int xEnd = yEnd-j;
|
||||
g.drawLine(0,j, xEnd,yEnd);
|
||||
j += 2;
|
||||
}
|
||||
|
||||
int i = 2;
|
||||
while (i<getWidth()) {
|
||||
int yEnd = y1 + getWidth() - i;
|
||||
g.drawLine(i,y1, getWidth(),yEnd);
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (y1>=y && y1<y+visibleRect.height) {
|
||||
g.drawLine(0,y1, getWidth(),y1);
|
||||
}
|
||||
if (y2>=y && y2<y+visibleRect.height) {
|
||||
g.drawLine(0,y2, getWidth(),y2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (BadLocationException ble) {
|
||||
ble.printStackTrace(); // Never happens
|
||||
}
|
||||
}
|
||||
|
||||
// Paint icons
|
||||
if (trackingIcons!=null) {
|
||||
int lastLine = textArea.getLineCount() - 1;
|
||||
for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
|
||||
GutterIconInfo ti = getTrackingIcon(i);
|
||||
int offs = ti.getMarkedOffset();
|
||||
if (offs>=0 && offs<=doc.getLength()) {
|
||||
int line = root.getElementIndex(offs);
|
||||
if (line<=lastLine && line>=topLine) {
|
||||
try {
|
||||
Icon icon = ti.getIcon();
|
||||
if (icon!=null) {
|
||||
int lineY = rsta.yForLine(line);
|
||||
if (lineY>=y && lineY<=visibleRect.y+visibleRect.height) {
|
||||
int y2 = lineY + (cellHeight-icon.getIconHeight())/2;
|
||||
icon.paintIcon(this, g, 0, y2);
|
||||
lastLine = line-1; // Paint only 1 icon per line
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException ble) {
|
||||
ble.printStackTrace(); // Never happens
|
||||
}
|
||||
}
|
||||
else if (line<topLine) {
|
||||
break; // All other lines are above us, so quit now
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Paints icons when line wrapping is enabled. Note that this does not
|
||||
* override the parent class's implementation to avoid this version being
|
||||
* called when line wrapping is disabled.
|
||||
*/
|
||||
private void paintComponentWrapped(Graphics g) {
|
||||
|
||||
// The variables we use are as follows:
|
||||
// - visibleRect is the "visible" area of the text area; e.g.
|
||||
// [0,100, 300,100+(lineCount*cellHeight)-1].
|
||||
// actualTop.y is the topmost-pixel in the first logical line we
|
||||
// paint. Note that we may well not paint this part of the logical
|
||||
// line, as it may be broken into many physical lines, with the first
|
||||
// few physical lines scrolled past. Note also that this is NOT the
|
||||
// visible rect of this line number list; this line number list has
|
||||
// visible rect == [0,0, insets.left-1,visibleRect.height-1].
|
||||
|
||||
// We avoid using modelToView/viewToModel where possible, as these
|
||||
// methods trigger a parsing of the line into syntax tokens, which is
|
||||
// costly. It's cheaper to just grab the child views' bounds.
|
||||
|
||||
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
|
||||
// boolean currentLineHighlighted = textArea.getHighlightCurrentLine();
|
||||
Document doc = textArea.getDocument();
|
||||
Element root = doc.getDefaultRootElement();
|
||||
int topPosition = textArea.viewToModel(
|
||||
new Point(visibleRect.x,visibleRect.y));
|
||||
int topLine = root.getElementIndex(topPosition);
|
||||
|
||||
int topY = visibleRect.y;
|
||||
int bottomY = visibleRect.y + visibleRect.height;
|
||||
int cellHeight = textArea.getLineHeight();
|
||||
|
||||
// Paint icons
|
||||
if (trackingIcons!=null) {
|
||||
int lastLine = textArea.getLineCount() - 1;
|
||||
for (int i=trackingIcons.size()-1; i>=0; i--) { // Last to first
|
||||
GutterIconInfo ti = getTrackingIcon(i);
|
||||
int offs = ti.getMarkedOffset();
|
||||
if (offs>=0 && offs<=doc.getLength()) {
|
||||
int line = root.getElementIndex(offs);
|
||||
if (line<=lastLine && line>=topLine) {
|
||||
try {
|
||||
int lineY = rsta.yForLine(line);
|
||||
if (lineY>=topY && lineY<bottomY) {
|
||||
Icon icon = ti.getIcon();
|
||||
if (icon!=null) {
|
||||
int y2 = lineY + (cellHeight-icon.getIconHeight())/2;
|
||||
ti.getIcon().paintIcon(this, g, 0, y2);
|
||||
lastLine = line-1; // Paint only 1 icon per line
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException ble) {
|
||||
ble.printStackTrace(); // Never happens
|
||||
}
|
||||
}
|
||||
else if (line<topLine) {
|
||||
break; // All other lines are above us, so quit now
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
/*
|
||||
* 10/01/2009
|
||||
*
|
||||
* MarkOccurrencesHighlightPainter.java - Renders "marked occurrences."
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
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 boolean paintBorder;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a highlight painter that defaults to blue.
|
||||
*/
|
||||
public MarkOccurrencesHighlightPainter() {
|
||||
super(Color.BLUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether a border is painted around marked occurrences.
|
||||
*
|
||||
* @return Whether a border is painted.
|
||||
* @see #setPaintBorder(boolean)
|
||||
* @see #getColor()
|
||||
*/
|
||||
public boolean getPaintBorder() {
|
||||
return paintBorder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@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.
|
||||
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();
|
||||
g.fillRect(r.x, r.y, r.width, r.height);
|
||||
if (paintBorder) {
|
||||
g.setColor(borderColor);
|
||||
g.drawRect(r.x,r.y, r.width-1,r.height-1);
|
||||
}
|
||||
return r;
|
||||
} catch (BadLocationException e) { // Never happens
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setColor(Color c) {
|
||||
super.setColor(c);
|
||||
borderColor = c.darker();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggles whether a border is painted around highlights.
|
||||
*
|
||||
* @param paint Whether to paint a border.
|
||||
* @see #getPaintBorder()
|
||||
* @see #setColor(Color)
|
||||
*/
|
||||
public void setPaintBorder(boolean paint) {
|
||||
this.paintBorder = paint;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,320 +0,0 @@
|
||||
/*
|
||||
* 01/06/2009
|
||||
*
|
||||
* MarkOccurrencesSupport.java - Handles marking all occurrences of the
|
||||
* currently selected identifier in a text area.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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 javax.swing.Timer;
|
||||
import javax.swing.event.CaretEvent;
|
||||
import javax.swing.event.CaretListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Caret;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Marks all occurrences of the token at the current caret position, if it is
|
||||
* an identifier.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
class MarkOccurrencesSupport implements CaretListener, ActionListener {
|
||||
|
||||
private RSyntaxTextArea textArea;
|
||||
private Timer timer;
|
||||
private MarkOccurrencesHighlightPainter p;
|
||||
|
||||
/**
|
||||
* The default color used to mark occurrences.
|
||||
*/
|
||||
public static final Color DEFAULT_COLOR = new Color(224, 224, 224);
|
||||
|
||||
/**
|
||||
* 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, 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
|
||||
* <code>null</code>.
|
||||
*/
|
||||
public MarkOccurrencesSupport(int delay, Color color) {
|
||||
timer = new Timer(delay, this);
|
||||
timer.setRepeats(false);
|
||||
p = new MarkOccurrencesHighlightPainter();
|
||||
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.
|
||||
*
|
||||
* @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 {
|
||||
|
||||
// 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)) {
|
||||
removeHighlights();
|
||||
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
|
||||
textArea.getHighlighter();
|
||||
char[] lexeme = t.getLexeme().toCharArray();
|
||||
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;
|
||||
h.addMarkedOccurrenceHighlight(temp.offset, end, p);
|
||||
} catch (BadLocationException ble) {
|
||||
ble.printStackTrace(); // Never happens
|
||||
}
|
||||
}
|
||||
temp = temp.getNextToken();
|
||||
}
|
||||
}
|
||||
//textArea.repaint();
|
||||
// TODO: Do a textArea.repaint() instead of repainting each marker as it's added if count is huge
|
||||
}
|
||||
|
||||
} finally {
|
||||
doc.readUnlock();
|
||||
//time = System.currentTimeMillis() - time;
|
||||
//System.out.println("MarkOccurrencesSupport 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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the specified token is a single non-word char (e.g. not
|
||||
* in <tt>[A-Za-z]</tt>. 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 <tt>null</tt>.
|
||||
* @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();
|
||||
h.clearMarkOccurrencesHighlights();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
removeHighlights();
|
||||
textArea.removeCaretListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,707 +0,0 @@
|
||||
/*
|
||||
* 09/26/2005
|
||||
*
|
||||
* ParserManager.java - Manages the parsing of an RSyntaxTextArea's document,
|
||||
* if necessary.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.net.URL;
|
||||
import java.security.AccessControlException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.ToolTipManager;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
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;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Manages running a parser object for an <code>RSyntaxTextArea</code>.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.9
|
||||
*/
|
||||
class ParserManager implements DocumentListener, ActionListener,
|
||||
HyperlinkListener {
|
||||
|
||||
private RSyntaxTextArea textArea;
|
||||
private List parsers;
|
||||
private Timer timer;
|
||||
private boolean running;
|
||||
private Parser parserForTip;
|
||||
private Position firstOffsetModded;
|
||||
private Position lastOffsetModded;
|
||||
|
||||
/**
|
||||
* Mapping of notices to their highlights in the editor. Can't use a Map
|
||||
* since parsers could return two <code>ParserNotice</code>s that compare
|
||||
* equally via <code>equals()</code>. 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 List noticeHighlightPairs;
|
||||
|
||||
/**
|
||||
* Painter used to underline errors.
|
||||
*/
|
||||
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter =
|
||||
new SquiggleUnderlineHighlightPainter(Color.RED);
|
||||
|
||||
/**
|
||||
* If this system property is set to <code>true</code>, 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.
|
||||
*/
|
||||
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.out.println("Total parsing time: " + time + " seconds");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a parser for the text area.
|
||||
*
|
||||
* @param parser The new parser. If this is <code>null</code>, 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) {
|
||||
|
||||
// Parsers are supposed to return at least empty ParseResults, but
|
||||
// we'll be defensive here.
|
||||
if (res==null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG_PARSING) {
|
||||
System.out.println("[DEBUG]: Adding parser notices from " +
|
||||
res.getParser());
|
||||
}
|
||||
|
||||
if (noticeHighlightPairs==null) {
|
||||
noticeHighlightPairs = new ArrayList();
|
||||
}
|
||||
|
||||
removeParserNotices(res);
|
||||
|
||||
List notices = res.getNotices();
|
||||
if (notices.size()>0) { // Guaranteed non-null
|
||||
|
||||
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
|
||||
textArea.getHighlighter();
|
||||
|
||||
for (Iterator i=notices.iterator(); i.hasNext(); ) {
|
||||
ParserNotice notice = (ParserNotice)i.next();
|
||||
if (DEBUG_PARSING) {
|
||||
System.out.println("[DEBUG]: ... adding: " + notice);
|
||||
}
|
||||
try {
|
||||
Object highlight = null;
|
||||
if (notice.getShowInEditor()) {
|
||||
highlight = h.addParserHighlight(notice,
|
||||
parserErrorHighlightPainter);
|
||||
}
|
||||
noticeHighlightPairs.add(new NoticeHighlightPair(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 (noticeHighlightPairs!=null) {
|
||||
noticeHighlightPairs.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 (noticeHighlightPairs!=null) {
|
||||
for (Iterator i=noticeHighlightPairs.iterator(); i.hasNext(); ) {
|
||||
NoticeHighlightPair pair = (NoticeHighlightPair)i.next();
|
||||
if (pair.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.<p>
|
||||
*
|
||||
* This method can be useful when a <code>Parser</code> 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 <code>Parser</code> 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 (noticeHighlightPairs!=null) {
|
||||
for (Iterator i=noticeHighlightPairs.iterator(); i.hasNext(); ) {
|
||||
NoticeHighlightPair pair = (NoticeHighlightPair)i.next();
|
||||
notices.add(pair.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; i<highlights.length; i++) {
|
||||
Highlighter.Highlight h = highlights[i];
|
||||
//if (h instanceof ParserNoticeHighlight) {
|
||||
// ParserNoticeHighlight pnh = (ParserNoticeHighlight)h;
|
||||
int start = h.getStartOffset();
|
||||
int end = h.getEndOffset();
|
||||
if (start<=pos && end>=pos) {
|
||||
//return pnh.getMessage();
|
||||
return textArea.getText(start, end-start);
|
||||
}
|
||||
//}
|
||||
}
|
||||
*/
|
||||
if (noticeHighlightPairs!=null) {
|
||||
for (int j=0; j<noticeHighlightPairs.size(); j++) {
|
||||
NoticeHighlightPair pair =
|
||||
(NoticeHighlightPair)noticeHighlightPairs.get(j);
|
||||
ParserNotice notice = pair.notice;
|
||||
if (notice.containsPosition(pos)) {
|
||||
tip = notice.getToolTipText();
|
||||
parserForTip = notice.getParser();
|
||||
if (parserForTip instanceof HyperlinkListener) {
|
||||
listener = (HyperlinkListener)parserForTip;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// } catch (BadLocationException ble) {
|
||||
// ble.printStackTrace(); // Should never happen.
|
||||
// }
|
||||
|
||||
URL imageBase = parserForTip==null ? null : parserForTip.getImageBase();
|
||||
return new ToolTipInfo(tip, listener, imageBase);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the document is modified.
|
||||
*
|
||||
* @param e The document event.
|
||||
*/
|
||||
public void handleDocumentEvent(DocumentEvent e) {
|
||||
if (running && parsers.size()>0) {
|
||||
timer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the user clicks a hyperlink in a {@link FocusableTip}.
|
||||
*
|
||||
* @param e The event.
|
||||
*/
|
||||
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||
if (parserForTip!=null && parserForTip.getHyperlinkListener()!=null) {
|
||||
parserForTip.getHyperlinkListener().linkClicked(textArea, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the document is modified.
|
||||
*
|
||||
* @param e The document event.
|
||||
*/
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
|
||||
// Keep track of the first and last offset modified. Some parsers are
|
||||
// smart and will only re-parse this section of the file.
|
||||
try {
|
||||
int offs = e.getOffset();
|
||||
if (firstOffsetModded==null || offs<firstOffsetModded.getOffset()) {
|
||||
firstOffsetModded = e.getDocument().createPosition(offs);
|
||||
}
|
||||
offs = e.getOffset() + e.getLength();
|
||||
if (lastOffsetModded==null || offs>lastOffsetModded.getOffset()) {
|
||||
lastOffsetModded = e.getDocument().createPosition(offs);
|
||||
}
|
||||
} catch (BadLocationException ble) {
|
||||
ble.printStackTrace(); // Shouldn't happen
|
||||
}
|
||||
|
||||
handleDocumentEvent(e);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a parser.
|
||||
*
|
||||
* @param parser The parser to remove.
|
||||
* @return Whether the parser was found.
|
||||
* @see #addParser(Parser)
|
||||
* @see #getParser(int)
|
||||
*/
|
||||
public boolean removeParser(Parser parser) {
|
||||
removeParserNotices(parser);
|
||||
boolean removed = parsers.remove(parser);
|
||||
if (removed) {
|
||||
textArea.fireParserNoticesChange();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all parser notices (and clears highlights in the editor) from
|
||||
* a particular parser.
|
||||
*
|
||||
* @param parser The parser.
|
||||
*/
|
||||
private void removeParserNotices(Parser parser) {
|
||||
if (noticeHighlightPairs!=null) {
|
||||
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
|
||||
textArea.getHighlighter();
|
||||
for (Iterator i=noticeHighlightPairs.iterator(); i.hasNext(); ) {
|
||||
NoticeHighlightPair pair = (NoticeHighlightPair)i.next();
|
||||
if (pair.notice.getParser()==parser && pair.highlight!=null) {
|
||||
h.removeParserHighlight(pair.highlight);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes any currently stored notices (and the corresponding highlights
|
||||
* from the editor) from the same Parser, and in the given line range,
|
||||
* as in the results.
|
||||
*
|
||||
* @param res The results.
|
||||
*/
|
||||
private void removeParserNotices(ParseResult res) {
|
||||
if (noticeHighlightPairs!=null) {
|
||||
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter)
|
||||
textArea.getHighlighter();
|
||||
for (Iterator i=noticeHighlightPairs.iterator(); i.hasNext(); ) {
|
||||
NoticeHighlightPair pair = (NoticeHighlightPair)i.next();
|
||||
boolean removed = false;
|
||||
if (shouldRemoveNotice(pair.notice, res)) {
|
||||
if (pair.highlight!=null) {
|
||||
h.removeParserHighlight(pair.highlight);
|
||||
}
|
||||
i.remove();
|
||||
removed = true;
|
||||
}
|
||||
if (DEBUG_PARSING) {
|
||||
String text = removed ? "[DEBUG]: ... notice removed: " :
|
||||
"[DEBUG]: ... notice not removed: ";
|
||||
System.out.println(text + pair.notice);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the document is modified.
|
||||
*
|
||||
* @param e The document event.
|
||||
*/
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
|
||||
// Keep track of the first and last offset modified. Some parsers are
|
||||
// smart and will only re-parse this section of the file. Note that
|
||||
// for removals, only the line at the removal start needs to be
|
||||
// re-parsed.
|
||||
try {
|
||||
int offs = e.getOffset();
|
||||
if (firstOffsetModded==null || offs<firstOffsetModded.getOffset()) {
|
||||
firstOffsetModded = e.getDocument().createPosition(offs);
|
||||
}
|
||||
if (lastOffsetModded==null || offs>lastOffsetModded.getOffset()) {
|
||||
lastOffsetModded = e.getDocument().createPosition(offs);
|
||||
}
|
||||
} catch (BadLocationException ble) { // Never happens
|
||||
ble.printStackTrace();
|
||||
}
|
||||
|
||||
handleDocumentEvent(e);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restarts parsing the document.
|
||||
*
|
||||
* @see #stopParsing()
|
||||
*/
|
||||
public void restartParsing() {
|
||||
timer.restart();
|
||||
running = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the delay between the last "concurrent" edit and when the document
|
||||
* is re-parsed.
|
||||
*
|
||||
* @param millis The new delay, in milliseconds. This must be greater
|
||||
* than <code>0</code>.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mapping of a parser notice to its highlight in the editor.
|
||||
*/
|
||||
private static class NoticeHighlightPair {
|
||||
|
||||
public ParserNotice notice;
|
||||
public Object highlight;
|
||||
|
||||
public NoticeHighlightPair(ParserNotice notice, Object 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 01/11/2011
|
||||
*
|
||||
* PopupWindowDecorator.java - Hook allowing hosting applications to decorate
|
||||
* JWindows created by the AutoComplete library.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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
|
||||
* <a href="http://jgoodies.com/">JGoodies</a> 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;
|
||||
|
||||
|
||||
/**
|
||||
* 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 <code>null</code>
|
||||
* 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
|
||||
* <code>null</code>.
|
||||
* @see #get()
|
||||
*/
|
||||
public static void set(PopupWindowDecorator decorator) {
|
||||
PopupWindowDecorator.decorator = decorator;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 02/10/2009
|
||||
*
|
||||
* RSTAView.java - An <code>RSyntaxTextArea</code> view.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
|
||||
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 specified line.<p>
|
||||
*
|
||||
* This method is quicker than using traditional
|
||||
* <code>modelToView(int)</code> 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 <code>-1</code> if
|
||||
* this text area doesn't yet have a positive size or the line is
|
||||
* hidden (i.e. from folding).
|
||||
* @throws BadLocationException If <code>line</code> isn't a valid line
|
||||
* number for this document.
|
||||
*/
|
||||
public int yForLine(Rectangle alloc, int line) throws BadLocationException;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the y-coordinate of the line containing a specified offset.<p>
|
||||
*
|
||||
* This method is quicker than using traditional
|
||||
* <code>modelToView(int)</code> 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 <code>-1</code> if
|
||||
* this text area doesn't yet have a positive size or the line is
|
||||
* hidden (i.e. from folding).
|
||||
* @throws BadLocationException If <code>offs</code> isn't a valid offset
|
||||
* into the document.
|
||||
*/
|
||||
public int yForLineContaining(Rectangle alloc, int offs)
|
||||
throws BadLocationException;
|
||||
|
||||
|
||||
}
|
@ -1,605 +0,0 @@
|
||||
/*
|
||||
* 10/16/2004
|
||||
*
|
||||
* RSyntaxDocument.java - A document capable of syntax highlighting, used by
|
||||
* RSyntaxTextArea.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
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 <code>javax.swing.text.PlainDocument</code> 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).<p>
|
||||
*
|
||||
* Instances of <code>RSyntaxTextArea</code> will only accept instances of
|
||||
* <code>RSyntaxDocument</code>, since it is this document that keeps
|
||||
* track of syntax highlighting. All others will cause an exception to be
|
||||
* thrown.<p>
|
||||
*
|
||||
* 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 <code>DocumentEvent</code>s of
|
||||
* type <code>CHANGE</code> 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
|
||||
*/
|
||||
public class RSyntaxDocument extends RDocument 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 transient TokenMaker tokenMaker;
|
||||
|
||||
/**
|
||||
* The current syntax style. Only cached to keep this class serializable.
|
||||
*/
|
||||
private String syntaxStyle;
|
||||
|
||||
/**
|
||||
* Array of values representing the "last token type" on each line. This
|
||||
* is used in cases such as multi-line comments: if the previous line
|
||||
* ended with an (unclosed) multi-line comment, we can use this knowledge
|
||||
* and start the current line's syntax highlighting in multi-line comment
|
||||
* state.
|
||||
*/
|
||||
protected transient 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 <code>TokenMakerFactory</code> for this document. If
|
||||
* this is <code>null</code>, a default factory is used.
|
||||
* @param syntaxStyle The syntax highlighting scheme to use.
|
||||
*/
|
||||
public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) {
|
||||
putProperty(tabSizeAttribute, new Integer(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.<p>
|
||||
* The syntax highlighting stuff has to be here instead of in
|
||||
* <code>insertUpdate</code> because <code>insertUpdate</code> 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; i<endBefore; i++).
|
||||
|
||||
// Update last tokens for lines below until they stop changing.
|
||||
updateLastTokensBelow(endBefore, numLines, previousTokenType);
|
||||
|
||||
} // End of if (added!=null && added.length>0).
|
||||
|
||||
// Otherwise, text was inserted on a single line...
|
||||
else {
|
||||
|
||||
// Update last tokens for lines below until they stop changing.
|
||||
updateLastTokensBelow(line, numLines, previousTokenType);
|
||||
|
||||
} // End of else.
|
||||
|
||||
// Let all listeners know about the insertion.
|
||||
super.fireInsertUpdate(e);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is called AFTER the content has been inserted into the
|
||||
* document and the element structure has been updated.<p>
|
||||
* The syntax-highlighting updates need to be done here (as opposed to
|
||||
* an override of <code>postRemoveUpdate</code>) as this method is called
|
||||
* in response to undo/redo events, whereas <code>postRemoveUpdate</code>
|
||||
* is not.<p>
|
||||
* 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 <code>true</code> if
|
||||
* {@link #getLanguageIsMarkup()} also returns <code>true</code>.
|
||||
*
|
||||
* @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
|
||||
* ('<tt>{</tt>' and '<tt>}</tt>') 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 <code>null</code> value for either means there
|
||||
* is no string to add for that part. A value of
|
||||
* <code>null</code> 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
|
||||
* <code>tokenMaker.getTokenList</code> that takes into account the last
|
||||
* token on the previous line to assure token accuracy.
|
||||
*
|
||||
* @param line The line number of <code>text</code> 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether whitespace is visible.
|
||||
*
|
||||
* @return Whether whitespace is visible.
|
||||
* @see #setWhitespaceVisible(boolean)
|
||||
*/
|
||||
public boolean isWhitespaceVisible() {
|
||||
return tokenMaker==null ? false : tokenMaker.isWhitespaceVisible();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes a document.
|
||||
*
|
||||
* @param in The stream to read from.
|
||||
* @throws ClassNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void readObject(ObjectInputStream in)
|
||||
throws ClassNotFoundException, IOException {
|
||||
|
||||
in.defaultReadObject();
|
||||
|
||||
// Install default TokenMakerFactory. To support custom TokenMakers,
|
||||
// both JVM's should install default TokenMakerFactories that support
|
||||
// the language they want to use beforehand.
|
||||
setTokenMakerFactory(null);
|
||||
|
||||
// Handle other transient stuff
|
||||
this.s = new Segment();
|
||||
int lineCount = getDefaultRootElement().getElementCount();
|
||||
lastTokensOnLines = new DynamicIntArray(lineCount);
|
||||
setSyntaxStyle(syntaxStyle); // Actually install (transient) TokenMaker
|
||||
setWhitespaceVisible(in.readBoolean()); // Do after setSyntaxStyle()
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes our private <code>Segment s</code> point to the text in our
|
||||
* document referenced by the specified element. Note that
|
||||
* <code>line</code> 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 <code>RSyntaxDocument</code>s
|
||||
* support all languages built into <code>RSyntaxTextArea</code>.
|
||||
*
|
||||
* @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) {
|
||||
boolean wsVisible = isWhitespaceVisible();
|
||||
tokenMaker = tokenMakerFactory.getTokenMaker(styleKey);
|
||||
tokenMaker.setWhitespaceVisible(wsVisible);
|
||||
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
|
||||
* <code>RSyntaxTextArea</code>.
|
||||
*
|
||||
* @param tokenMaker The new token maker to use.
|
||||
*/
|
||||
public void setSyntaxStyle(TokenMaker tokenMaker) {
|
||||
tokenMaker.setWhitespaceVisible(isWhitespaceVisible());
|
||||
this.tokenMaker = tokenMaker;
|
||||
updateSyntaxHighlightingInformation();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the token maker factory used by this document.
|
||||
*
|
||||
* @param tmf The <code>TokenMakerFactory</code> for this document. If
|
||||
* this is <code>null</code>, 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.
|
||||
* @see #isWhitespaceVisible()
|
||||
*/
|
||||
public void setWhitespaceVisible(boolean visible) {
|
||||
tokenMaker.setWhitespaceVisible(visible);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* <code>line</code>.
|
||||
* @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 (line<numLines).
|
||||
|
||||
// If any lines had their token types changed, fire a changed update
|
||||
// for them. The view will repaint the area covered by the lines.
|
||||
// FIXME: We currently cheat and send the line range that needs to be
|
||||
// repainted as the "offset and length" of the change, since this is
|
||||
// what the view needs. We really should send the actual offset and
|
||||
// length.
|
||||
if (line>firstLine) {
|
||||
//System.err.println("... ... ... repainting lines " + firstLine + "-" + line);
|
||||
fireChangedUpdate(new DefaultDocumentEvent(firstLine, line,
|
||||
DocumentEvent.EventType.CHANGE));
|
||||
}
|
||||
|
||||
return line;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates internal state information; e.g. the "last tokens on lines"
|
||||
* data. After this, a changed update is fired to let listeners know that
|
||||
* the document's structure has changed.<p>
|
||||
*
|
||||
* 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));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overridden for custom serialization purposes.
|
||||
*
|
||||
* @param out The stream to write to.
|
||||
* @throws IOException If an IO error occurs.
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream out)throws IOException {
|
||||
out.defaultWriteObject();
|
||||
out.writeBoolean(isWhitespaceVisible());
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Folding
|
||||
|
||||
Action.ToggleCurrentFold.Name=Toggle Current Fold
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Toggles the fold at the caret position.
|
||||
Action.CollapseAllFolds.Name=Collapse All Folds
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Collapses all folds.
|
||||
Action.CollapseCommentFolds.Name=Collapse All Comments
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Collapses all comment folds.
|
||||
Action.ExpandAllFolds.Name=Expand All Folds
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Expands all folds.
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 10/27/2004
|
||||
*
|
||||
* RSyntaxTextAreaDefaultInputMap.java - The default input map for
|
||||
* RSyntaxTextAreas.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.fife.ui.rtextarea.RTADefaultInputMap;
|
||||
|
||||
|
||||
/**
|
||||
* The default input map for an <code>RSyntaxTextArea</code>.
|
||||
* Currently, the new key bindings include:
|
||||
* <ul>
|
||||
* <li>Shift+Tab indents the current line or currently selected lines
|
||||
* to the left.
|
||||
* </ul>
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public class RSyntaxTextAreaDefaultInputMap extends RTADefaultInputMap {
|
||||
|
||||
/**
|
||||
* Constructs the default input map for an <code>RSyntaxTextArea</code>.
|
||||
*/
|
||||
public RSyntaxTextAreaDefaultInputMap() {
|
||||
|
||||
int defaultMod = getDefaultModifier();
|
||||
//int ctrl = InputEvent.CTRL_MASK;
|
||||
int shift = InputEvent.SHIFT_MASK;
|
||||
//int alt = InputEvent.ALT_MASK;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,449 +0,0 @@
|
||||
/*
|
||||
* 04/23/2009
|
||||
*
|
||||
* RSyntaxTextAreaHighlighter.java - Highlighter for RTextAreas.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.swing.plaf.TextUI;
|
||||
import javax.swing.plaf.basic.BasicTextUI.BasicHighlighter;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.Element;
|
||||
import javax.swing.text.Highlighter;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import javax.swing.text.LayeredHighlighter;
|
||||
import javax.swing.text.Position;
|
||||
import javax.swing.text.View;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.parser.Parser;
|
||||
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
|
||||
import org.fife.ui.rtextarea.RTextArea;
|
||||
|
||||
|
||||
/**
|
||||
* 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.<p>
|
||||
*
|
||||
* 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 A tag to reference the highlight later.
|
||||
* @throws BadLocationException
|
||||
* @see {@link #clearMarkOccurrencesHighlights()}
|
||||
*/
|
||||
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 "marked occurrences" highlights from the view.
|
||||
*
|
||||
* @see #addMarkedOccurrenceHighlight(int, int, MarkOccurrencesHighlightPainter)
|
||||
*/
|
||||
void clearMarkOccurrencesHighlights() {
|
||||
// Don't remove via the iterator; since our List is an ArrayList, this
|
||||
// implies tons of System.arrayCopy()s
|
||||
for (Iterator i=markedOccurrences.iterator(); i.hasNext(); ) {
|
||||
repaintListHighlight(i.next());
|
||||
}
|
||||
markedOccurrences.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all parser highlights.
|
||||
*
|
||||
* @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter)
|
||||
*/
|
||||
void clearParserHighlights() {
|
||||
// Don't remove via an iterator; since our List is an ArrayList, this
|
||||
// implies tons of System.arrayCopy()s
|
||||
for (int i=0; i<parserHighlights.size(); i++) {
|
||||
repaintListHighlight(parserHighlights.get(i));
|
||||
}
|
||||
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, or an empty list if none. The
|
||||
* contents of this list will be of type {@link DocumentRange}.
|
||||
*/
|
||||
public List getMarkedOccurrences() {
|
||||
List list = new ArrayList(markedOccurrences.size());
|
||||
for (Iterator i=markedOccurrences.iterator(); i.hasNext(); ) {
|
||||
HighlightInfo info = (HighlightInfo)i.next();
|
||||
int start = info.getStartOffset();
|
||||
int end = info.getEndOffset() + 1; // HACK
|
||||
DocumentRange range = new DocumentRange(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 repaintListHighlight(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a parser highlight from this view.
|
||||
*
|
||||
* @param tag The reference to the highlight.
|
||||
* @see #addParserHighlight(ParserNotice, javax.swing.text.Highlighter.HighlightPainter)
|
||||
*/
|
||||
void removeParserHighlight(Object tag) {
|
||||
repaintListHighlight(tag);
|
||||
parserHighlights.remove(tag);
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
/*
|
||||
* 02/24/2004
|
||||
*
|
||||
* RSyntaxTextAreaUI.java - UI for an RSyntaxTextArea.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import javax.swing.InputMap;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.InputMapUIResource;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import org.fife.ui.rtextarea.RTextArea;
|
||||
import org.fife.ui.rtextarea.RTextAreaUI;
|
||||
|
||||
|
||||
/**
|
||||
* UI used by <code>RSyntaxTextArea</code>. 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();
|
||||
|
||||
|
||||
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.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.<p>
|
||||
*
|
||||
* This method is not named <code>getInputMap()</code> because there is
|
||||
* a package-private method in <code>BasicTextAreaUI</code> 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; i<keys.length; i++)
|
||||
// System.err.println(keys[i] + " -> " + shared.get(keys[i]));
|
||||
map.setParent(shared);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Paints the text area's background.
|
||||
*
|
||||
* @param g The graphics component on which to paint.
|
||||
*/
|
||||
protected void paintBackground(Graphics g) {
|
||||
super.paintBackground(g);
|
||||
paintMatchedBracket(g);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private 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();
|
||||
if (bg!=null) {
|
||||
g.setColor(bg);
|
||||
g.fillRoundRect(r.x,r.y, r.width,r.height-1, 5,5);
|
||||
}
|
||||
g.setColor(rsta.getMatchedBracketBorderColor());
|
||||
g.drawRoundRect(r.x,r.y, r.width,r.height-1, 5,5);
|
||||
}
|
||||
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
|
||||
* <code>RSyntaxTextArea</code>.
|
||||
*
|
||||
* @param e The property change event.
|
||||
*/
|
||||
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
|
||||
* <code>RSyntaxTextArea</code> changes its syntax editing style.
|
||||
*/
|
||||
public void refreshSyntaxHighlighting() {
|
||||
modelChanged();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the y-coordinate of the specified line.<p>
|
||||
*
|
||||
* This method is quicker than using traditional
|
||||
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
|
||||
* computed.
|
||||
*/
|
||||
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.<p>
|
||||
*
|
||||
* This is faster than calling <code>modelToView(offs).y</code>, so it is
|
||||
* preferred if you do not need the actual bounding box.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=\u0637\u0648\u064a
|
||||
|
||||
Action.ToggleCurrentFold.Name=\u063a\u064a\u0651\u0631 \u0648\u0636\u0639\u064a\u0629 \u0627\u0644\u0637\u0648\u064a\u0629 \u0627\u0644\u062d\u0627\u0644\u064a\u0629
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u063a\u064a\u064a\u0631 \u0648\u0636\u0639\u064a\u0629 \u0627\u0644\u0637\u0648\u064a\u0629 \u0641\u064a \u0645\u0648\u0642\u0639 \u0627\u0644\u0645\u0624\u0634\u0631 \u0627\u0644\u062d\u0627\u0644\u064a
|
||||
Action.CollapseAllFolds.Name=\u0642\u0644\u0635 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u0642\u0644\u064a\u0635 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a
|
||||
Action.CollapseCommentFolds.Name=\u0642\u0644\u0635 \u062c\u0645\u064a\u0639 \u0627\u0644\u062a\u0639\u0644\u064a\u0642\u0627\u062a
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u0642\u0644\u064a\u0635 \u062c\u0645\u064a\u0639 \u0637\u064a\u0627\u062a \u0627\u0644\u062a\u0639\u0644\u064a\u0642\u0627\u062a
|
||||
Action.ExpandAllFolds.Name=\u0648\u0633\u0639 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=\u0645\u0646 \u0623\u062c\u0644 \u062a\u0648\u0633\u064a\u0639 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u064a\u0627\u062a.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Klappung
|
||||
|
||||
Action.ToggleCurrentFold.Name=Aktuellen Block umschalten
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Schaltet den Block an der Position der Schreibmarke um.
|
||||
Action.CollapseAllFolds.Name=Action.CollapseAllFolds.Name=Alle Bl\u00f6cke zuklappen
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Klappt alle Bl\u00f6cke zu.
|
||||
Action.CollapseCommentFolds.Name=Alle Kommentarbl\u00f6cke zuklappen
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Klappt alle Kommentarbl\u00f6cke zu.
|
||||
Action.ExpandAllFolds.Name=Alle Bl\u00f6cke aufklappen
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Klappt alle Bl\u00f6cke auf.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Folding
|
||||
|
||||
Action.ToggleCurrentFold.Name=Ver Fold (solapa) actual
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Mostrar el fold en la posici\u00f3n del cursor
|
||||
Action.CollapseAllFolds.Name=Ocultar todos los folds
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Oculta todos los folds
|
||||
Action.CollapseCommentFolds.Name=Ocultar todos los comentarios
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Oculta todos los comentarios
|
||||
Action.ExpandAllFolds.Name=Expandir todos las solapas (folds)
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Expande todas las solapas (folds)
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Pliage
|
||||
|
||||
Action.ToggleCurrentFold.Name=Active ou d\u00e9sactive le pli actuel
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Active ou d\u00e9sactive le pli \u00e0 la position du curseur.
|
||||
Action.CollapseAllFolds.Name=Comprimer tous les plis
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Comprime tous les plis.
|
||||
Action.CollapseCommentFolds.Name=Comprimer tous les commentaires
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Comprime tous les plis commentaires.
|
||||
Action.ExpandAllFolds.Name=D\u00e9comrpimer tous les plis
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=D\u00e9comrpime tous les plis.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=\u00d6sszevon\u00e1s
|
||||
|
||||
Action.ToggleCurrentFold.Name=A jelenlegi \u00f6sszevon\u00e1s megjelen\u00edt\u00e9se/rejt\u00e9se.
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Megjelen\u00edti, illetve elt\u00fcnteti az \u00f6sszevon\u00e1st a kurzor poz\u00edci\u00f3j\u00e1ban.
|
||||
Action.CollapseAllFolds.Name=\u00d6sszevon\u00e1sok rejt\u00e9se.
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=\u00d6sszevon\u00e1sok rejt\u00e9se.
|
||||
Action.CollapseCommentFolds.Name=Az \u00f6sszes megjegyz\u00e9s elrejt\u00e9se
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Megjegyz\u00e9s \u00f6sszevon\u00e1sok rejt\u00e9se.
|
||||
Action.ExpandAllFolds.Name=\u00d6sszevon\u00e1sok kiterjeszt\u00e9se
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=\u00d6sszevon\u00e1sok kiterjeszt\u00e9se.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Folding
|
||||
|
||||
Action.ToggleCurrentFold.Name=Toggle Current Fold
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Toggles the fold at the caret position.
|
||||
Action.CollapseAllFolds.Name=Collapse All Folds
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Collapses all folds.
|
||||
Action.CollapseCommentFolds.Name=Collapse All Comments
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Collapses all comment folds.
|
||||
Action.ExpandAllFolds.Name=Expand All Folds
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Expands all folds.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Folding
|
||||
|
||||
Action.ToggleCurrentFold.Name=Imposta il fold corrente
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Imposta il fold alla posizione corrente.
|
||||
Action.CollapseAllFolds.Name=Raggruppa tutti i fold
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Raggruppa tutti i fold.
|
||||
Action.CollapseCommentFolds.Name=Raggruppa tutti i commenti
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Raggruppa tutti i commenti.
|
||||
Action.ExpandAllFolds.Name=Espandi tutti i fold
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Espandi tutti i fold.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=\u30d5\u30a9\u30eb\u30c7\u30a3\u30f3\u30b0
|
||||
|
||||
Action.ToggleCurrentFold.Name=\u30ad\u30e3\u30ec\u30c3\u30c8\u5074\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u30c8\u30b0\u30eb\u3059\u308b\u3002
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=\u30ad\u30e3\u30ec\u30c3\u30c8\u5074\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u30c8\u30b0\u30eb\u3059\u308b\u3002
|
||||
Action.CollapseAllFolds.Name=\u5168\u3066\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u6298\u308a\u305f\u305f\u3080
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=\u5168\u3066\u306e\u3075\u3049\u308b\u3069\u3092\u6298\u308a\u305f\u305f\u3080
|
||||
Action.CollapseCommentFolds.Name=\u5168\u3066\u306e\u30b3\u30e1\u30f3\u30c8\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u6298\u308a\u305f\u305f\u3080
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=\u5168\u3066\u306e\u30b3\u30e1\u30f3\u30c8\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u6298\u308a\u305f\u305f\u3080
|
||||
Action.ExpandAllFolds.Name=\u5168\u3066\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u5c55\u958b\u3059\u308b
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=\u5168\u3066\u306e\u30d5\u30a9\u30eb\u30c9\u3092\u5c55\u958b\u3059\u308b
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=\ucf54\ub4dc \uac10\ucd94\uae30
|
||||
|
||||
Action.ToggleCurrentFold.Name=\ud604\uc7ac \ucf54\ub4dc \uac10\ucd94\uae30 \ud1a0\uae00
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=\uce90\ub7ff \uc704\uce58\uc5d0\uc11c \ucf54\ub4dc \uac10\ucd94\uae30\ub97c \ud1a0\uae00\ud569\ub2c8\ub2e4.
|
||||
Action.CollapseAllFolds.Name=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30 \uc811\uae30
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30\ub97c \uc811\uc2b5\ub2c8\ub2e4.
|
||||
Action.CollapseCommentFolds.Name=\ubaa8\ub4e0 \uc8fc\uc11d \uac10\ucd94\uae30 \uc811\uae30
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=\ubaa8\ub4e0 \uc8fc\uc11d \uac10\ucd94\uae30\ub97c \uc811\uc2b5\ub2c8\ub2e4.
|
||||
Action.ExpandAllFolds.Name=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30 \ud3bc\uce58\uae30
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=\ubaa8\ub4e0 \ucf54\ub4dc \uac10\ucd94\uae30\ub97c \ud3bc\uce69\ub2c8\ub2e4.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Folding
|
||||
|
||||
Action.ToggleCurrentFold.Name=Toggle Current Fold
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Toggles the fold at the caret position.
|
||||
Action.CollapseAllFolds.Name=Collapse All Folds
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Collapses all folds.
|
||||
Action.CollapseCommentFolds.Name=Collapse All Comments
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Collapses all comment folds.
|
||||
Action.ExpandAllFolds.Name=Expand All Folds
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Expands all folds.
|
@ -1,15 +0,0 @@
|
||||
ContextMenu.Folding=Zwijanie
|
||||
|
||||
Action.ToggleCurrentFold.Name=Reguluj zwini\u0119cie
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Ustawia zwini\u0119cie na pozycji karetki.
|
||||
Action.CollapseAllFolds.Name=Zwi\u0144 wszystkie zwini\u0119cia
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Zwija wszystkie zwini\u0119cia.
|
||||
Action.CollapseCommentFolds.Name=Zwi\u0144 wszystkie komentarze
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Zwija wszystkie zwini\u0119cia z komentarzami.
|
||||
Action.ExpandAllFolds.Name=Rozwi\u0144 zwini\u0119cia
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Rozwija wszystkie zwini\u0119cia.
|
||||
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Dobramento de c\u00f3digo
|
||||
|
||||
Action.ToggleCurrentFold.Name=Alternar o dobramento atual
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Alterna o dobramento na posi\u00e7\u00e3o do cursor.
|
||||
Action.CollapseAllFolds.Name=Recolher o dobramento de c\u00f3digo
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Recolher o dobramento de c\u00f3digo.
|
||||
Action.CollapseCommentFolds.Name=Recolher o dobramento de coment\u00e1rios
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Recolher o dobramento de coment\u00e1rios.
|
||||
Action.ExpandAllFolds.Name=Expandir o dobramento de c\u00f3digo
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Expandir o dobramento de c\u00f3digo.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=\u0421\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u0435
|
||||
|
||||
Action.ToggleCurrentFold.Name=\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0431\u043b\u043e\u043a
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0431\u043b\u043e\u043a\u0430 \u043d\u0430 \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u043a\u0443\u0440\u0441\u043e\u0440\u0430
|
||||
Action.CollapseAllFolds.Name=\u0421\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u0441\u0435 \u0431\u043b\u043e\u043a\u0438
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=\u0421\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0431\u043b\u043e\u043a\u043e\u0432
|
||||
Action.CollapseCommentFolds.Name=\u0421\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u0441\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=\u0421\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432 \u0431\u043b\u043e\u043a\u0430
|
||||
Action.ExpandAllFolds.Name=\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u0441\u0435 \u0431\u043b\u043e\u043a\u0438
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=\u0420\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0431\u043b\u043e\u043a\u043e\u0432
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Katlama
|
||||
|
||||
Action.ToggleCurrentFold.Name=\u015eu Anki Kat\u0131 Geni\u015flet/Katla
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=\u0130\u015faret\u00e7inin bulundu\u011fu kat\u0131 geni\u015fletir/katlar.
|
||||
Action.CollapseAllFolds.Name=T\u00fcm Katlar\u0131 Katla
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=T\u00fcm katlar\u0131 katlar.
|
||||
Action.CollapseCommentFolds.Name=T\u00fcm Yorumlar\u0131 Katla
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=T\u00fcm yorumlar\u0131 katlar.
|
||||
Action.ExpandAllFolds.Name=T\u00fcm Katlar\u0131 Geni\u015flet
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=T\u00fcm katlar\u0131 geni\u015fletir.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=\u6298\u53e0
|
||||
|
||||
Action.ToggleCurrentFold.Name=\u6298\u53e0/\u5f00\u542f\u672c\u533a\u6bb5
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=\u6298\u53e0/\u5f00\u542f\u63d2\u5165\u7b26\u6240\u5728\u4f4d\u7f6e.
|
||||
Action.CollapseAllFolds.Name=\u53d6\u6d88\u6240\u6709\u4ee3\u7801\u6298\u53e0
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=\u53d6\u6d88\u6240\u6709\u4ee3\u7801\u6298\u53e0.
|
||||
Action.CollapseCommentFolds.Name=\u53d6\u6d88\u6ce8\u91ca\u4ee3\u7801\u6298\u53e0
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=\u53d6\u6d88\u6ce8\u91ca\u4ee3\u7801\u6298\u53e0.
|
||||
Action.ExpandAllFolds.Name=\u6253\u5f00\u6240\u6709\u4ee3\u7801\u6298\u53e0
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=\u6253\u5f00\u6240\u6709\u4ee3\u7801\u6298\u53e0.
|
@ -1,14 +0,0 @@
|
||||
ContextMenu.Folding=Folding
|
||||
|
||||
Action.ToggleCurrentFold.Name=Toggle Current Fold
|
||||
Action.ToggleCurrentFold.Mnemonic=F
|
||||
Action.ToggleCurrentFold.Desc=Toggles the fold at the caret position.
|
||||
Action.CollapseAllFolds.Name=Collapse All Folds
|
||||
Action.CollapseAllFolds.Mnemonic=O
|
||||
Action.CollapseAllFolds.Desc=Collapses all folds.
|
||||
Action.CollapseCommentFolds.Name=Collapse All Comments
|
||||
Action.CollapseCommentFolds.Mnemonic=C
|
||||
Action.CollapseCommentFolds.Desc=Collapses all comment folds.
|
||||
Action.ExpandAllFolds.Name=Expand All Folds
|
||||
Action.ExpandAllFolds.Mnemonic=E
|
||||
Action.ExpandAllFolds.Desc=Expands all folds.
|
File diff suppressed because it is too large
Load Diff
@ -1,224 +0,0 @@
|
||||
/*
|
||||
* 07/28/2008
|
||||
*
|
||||
* RtfToText.java - Returns the plain text version of RTF documents.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the plain text version of RTF documents.<p>
|
||||
*
|
||||
* This is used by <code>RtfTransferable</code> 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 <code>Reader</code>
|
||||
* 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 "<code>par</code>" or "<code>tab</code>")
|
||||
* 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 <code>Reader</code> to plain text.
|
||||
*
|
||||
* @param r The <code>Reader</code>.
|
||||
* @return The contents of the <code>Reader</code>, 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));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,541 +0,0 @@
|
||||
/*
|
||||
* 07/28/2008
|
||||
*
|
||||
* RtfGenerator.java - Generates RTF via a simple Java API.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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;
|
||||
|
||||
|
||||
/**
|
||||
* Generates RTF text via a simple Java API.<p>
|
||||
*
|
||||
* The following RTF features are supported:
|
||||
* <ul>
|
||||
* <li>Fonts
|
||||
* <li>Font sizes
|
||||
* <li>Foreground and background colors
|
||||
* <li>Bold, italic, and underline
|
||||
* </ul>
|
||||
*
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>It assumes that tokens changing foreground color often is fairly
|
||||
* common.
|
||||
* <li>It assumes that background highlighting is fairly uncommon.
|
||||
* </ul>
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
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 <code>null</code>, the
|
||||
* default font is used.
|
||||
* @param fg The foreground of the text. If this is <code>null</code>,
|
||||
* the default foreground color is used.
|
||||
* @param bg The background color of the text. If this is
|
||||
* <code>null</code>, 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 <code>null</code>, the
|
||||
* default font is used.
|
||||
* @param bg The background color of the text. If this is
|
||||
* <code>null</code>, 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 <code>null</code>, the
|
||||
* default font is used.
|
||||
* @param fg The foreground of the text. If this is <code>null</code>,
|
||||
* the default foreground color is used.
|
||||
* @param bg The background color of the text. If this is
|
||||
* <code>null</code>, 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 <code>null</code>, the
|
||||
* default font is used.
|
||||
* @param fg The foreground of the text. If this is <code>null</code>,
|
||||
* the default foreground color is used.
|
||||
* @param bg The background color of the text. If this is
|
||||
* <code>null</code>, the default background color is used.
|
||||
* @param underline Whether the text should be underlined.
|
||||
* @param setFG Whether the foreground specified by <code>fg</code> should
|
||||
* be honored (if it is non-<code>null</code>).
|
||||
* @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:
|
||||
*
|
||||
* <ul>
|
||||
* <li>All tab characters are replaced with the string
|
||||
* "<code>\tab</code>"
|
||||
* <li>'\', '{' and '}' are changed to "\\", "\{" and "\}"
|
||||
* </ul>
|
||||
*
|
||||
* @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
|
||||
* <code>getSize2D()</code>.
|
||||
* @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; }"
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append("{\\colortbl ;");
|
||||
for (int i=0; i<colorList.size(); i++) {
|
||||
Color c = (Color)colorList.get(i);
|
||||
sb.append("\\red").append(c.getRed());
|
||||
sb.append("\\green").append(c.getGreen());
|
||||
sb.append("\\blue").append(c.getBlue());
|
||||
sb.append(';');
|
||||
}
|
||||
sb.append("}");
|
||||
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.<p>
|
||||
*
|
||||
* 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;}}"
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
// 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();
|
||||
|
||||
sb.append("{\\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 <code>String</code>.
|
||||
*/
|
||||
public String getRtf() {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("{");
|
||||
|
||||
// Header
|
||||
sb.append("\\rtf1\\ansi\\ansicpg1252");
|
||||
sb.append("\\deff0"); // First font in font table is the default
|
||||
sb.append("\\deflang1033");
|
||||
sb.append("\\viewkind4"); // "Normal" view
|
||||
sb.append("\\uc\\pard\\f0");
|
||||
sb.append("\\fs20"); // Font size in half-points (default 24)
|
||||
sb.append(getFontTableRtf()).append('\n');
|
||||
sb.append(getColorTableRtf()).append('\n');
|
||||
|
||||
// Content
|
||||
sb.append(document);
|
||||
|
||||
sb.append("}");
|
||||
|
||||
//System.err.println("*** " + sb.length());
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* 07/28/2008
|
||||
*
|
||||
* RtfTransferable.java - Used during drag-and-drop to represent RTF text.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.datatransfer.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
|
||||
/**
|
||||
* Object used during copy/paste and DnD operations to represent RTF text.
|
||||
* It can return the text being moved as either RTF or plain text. This
|
||||
* class is basically the same as
|
||||
* <code>java.awt.datatransfer.StringSelection</code>, 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) {
|
||||
for (int i=0; i<FLAVORS.length; i++) {
|
||||
if (flavor.equals(FLAVORS[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* 09/13/2005
|
||||
*
|
||||
* SquiggleUnderlineHighlightPainter.java - Highlighter that draws a squiggle
|
||||
* underline under "highlighted" text, similar to error markers in Microsoft
|
||||
* Visual Studio or Eclipse.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
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 paints a squiggly underline underneath text, similar
|
||||
* to what popular IDE's such as Visual Studio and Eclipse do to indicate
|
||||
* errors, warnings, etc.<p>
|
||||
*
|
||||
* This class must be used as a <code>LayerPainter</code>.
|
||||
*
|
||||
* @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
|
||||
* <code>null</code>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
/*
|
||||
* 08/06/2004
|
||||
*
|
||||
* Style.java - A set of traits for a particular token type to use while
|
||||
* painting.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
|
||||
/**
|
||||
* The color and style information for a token type. Each token type in an
|
||||
* <code>RSyntaxTextArea</code> has a corresponding <code>Style</code>; this
|
||||
* <code>Style</code> tells us the following things:
|
||||
*
|
||||
* <ul>
|
||||
* <li>What foreground color to use for tokens of this type.</li>
|
||||
* <li>What background color to use.</li>
|
||||
* <li>The font to use.</li>
|
||||
* <li>Whether the token should be underlined.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.6
|
||||
*/
|
||||
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 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 <code>null</code>) 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.<p>
|
||||
*
|
||||
* 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 + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,247 +0,0 @@
|
||||
/*
|
||||
* 03/08/2004
|
||||
*
|
||||
* SyntaxConstants.java - Constants used by RSyntaxTextArea and friends.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
|
||||
/**
|
||||
* Constants that define the different programming languages understood by
|
||||
* <code>RSyntaxTextArea</code>. These constants are the values you can pass
|
||||
* to {@link RSyntaxTextArea#setSyntaxEditingStyle(String)} to get syntax
|
||||
* highlighting.<p>
|
||||
*
|
||||
* By default, all <code>RSyntaxTextArea</code>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
|
||||
* <code>RSyntaxDocumenet</code>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 DTD files.
|
||||
*/
|
||||
public static final String SYNTAX_STYLE_DTD = "text/dtd";
|
||||
|
||||
|
||||
/**
|
||||
* 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 JSON.
|
||||
*/
|
||||
public static final String SYNTAX_STYLE_JSON = "text/json";
|
||||
|
||||
|
||||
/**
|
||||
* Style for highlighting JSP.
|
||||
*/
|
||||
public static final String SYNTAX_STYLE_JSP = "text/jsp";
|
||||
|
||||
|
||||
/**
|
||||
* Style for highlighting LaTeX.
|
||||
*/
|
||||
public static final String SYNTAX_STYLE_LATEX = "text/latex";
|
||||
|
||||
|
||||
/**
|
||||
* 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 NSIS install scripts.
|
||||
*/
|
||||
public static final String SYNTAX_STYLE_NSIS = "text/nsis";
|
||||
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
|
||||
}
|
@ -1,662 +0,0 @@
|
||||
/*
|
||||
* 02/26/2004
|
||||
*
|
||||
* SyntaxScheme.java - The set of colors and tokens used by an RSyntaxTextArea
|
||||
* to color tokens.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
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;
|
||||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
import org.xml.sax.helpers.XMLReaderFactory;
|
||||
|
||||
|
||||
/**
|
||||
* The set of colors and styles used by an <code>RSyntaxTextArea</code> to
|
||||
* color tokens. 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, 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 <code>null</code>.
|
||||
*
|
||||
* @param useDefaults If <code>true</code>, all color values will
|
||||
* be set to default colors; if <code>false</code>, all colors
|
||||
* will be initially <code>null</code>.
|
||||
*/
|
||||
public SyntaxScheme(boolean useDefaults) {
|
||||
styles = new Style[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[NUM_TOKEN_TYPES];
|
||||
restoreDefaults(baseFont, fontStyles);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes the "base font" for this syntax scheme. This is called by
|
||||
* <code>RSyntaxTextArea</code> when its font changes via
|
||||
* <code>setFont()</code>. 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 (int i=0; i<styles.length; i++) {
|
||||
Style style = styles[i];
|
||||
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[NUM_TOKEN_TYPES];
|
||||
for (int i=0; i<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 <code>true</code> if this color scheme and
|
||||
* <code>otherScheme</code> are the same scheme;
|
||||
* <code>false</code> 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
|
||||
* <code>"$rrggbb"</code>.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the specified style.
|
||||
*
|
||||
* @param index The index of the style.
|
||||
* @return The style.
|
||||
* @see #setStyle(int, Style)
|
||||
* @see #getStyleCount()
|
||||
*/
|
||||
public Style getStyle(int index) {
|
||||
return styles[index];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of styles.
|
||||
*
|
||||
* @return The number of styles.
|
||||
* @see #getStyle(int)
|
||||
*/
|
||||
public int getStyleCount() {
|
||||
return styles.length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 (int i=0; i<count; i++) {
|
||||
if (styles[i]!=null) {
|
||||
hashCode ^= styles[i].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 <code>null</code>, 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
|
||||
* <code>toCommaSeparatedString</code>. 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 = 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);
|
||||
}
|
||||
|
||||
// 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<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 = sc.getFont(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 (int i=0; i<styles.length; i++) {
|
||||
Style s = styles[i];
|
||||
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 <code>null</code>, 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 <code>null</code>, 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 keyword = Color.BLUE;
|
||||
Color function = new Color(173,128,0);
|
||||
Color preprocessor = 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(new Color(0,128,128));
|
||||
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(preprocessor);
|
||||
styles[PREPROCESSOR] = new Style(Color.gray);
|
||||
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_PROCESSING_INSTRUCTION] = new Style(preprocessor);
|
||||
styles[MARKUP_CDATA] = new Style(variable);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a style to use when rendering a token type.
|
||||
*
|
||||
* @param type The token type.
|
||||
* @param style The style for the token type.
|
||||
* @see #getStyle(int)
|
||||
*/
|
||||
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 '<code>$</code>', it is assumed to be in hex, otherwise it is
|
||||
* assumed to be decimal. So, for example, both of these:
|
||||
* <pre>
|
||||
* "$00ff00"
|
||||
* "65280"
|
||||
* </pre>
|
||||
* will return <code>new Color(0, 255, 0)</code>.
|
||||
*
|
||||
* @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:
|
||||
* <ul>
|
||||
* <li>If a color is non-null, it is added as a 24-bit integer
|
||||
* of the form <code>((r<<16) | (g<<8) | (b))</code>; if it is
|
||||
* <code>null</code>, it is added as "<i>-,</i>".
|
||||
* <li>The font and style (bold/italic) is added as an integer like so:
|
||||
* "<i>family,</i> <i>style,</i> <i>size</i>".
|
||||
* <li>The entire syntax highlighting scheme is thus one long string of
|
||||
* color schemes of the format "<i>i,[fg],[bg],uline,[style]</i>,
|
||||
* where:
|
||||
* <ul>
|
||||
* <li><code>i</code> is the index of the syntax scheme.
|
||||
* <li><i>fg</i> and <i>bg</i> are the foreground and background
|
||||
* colors for the scheme, and may be null (represented by
|
||||
* <code>-</code>).
|
||||
* <li><code>uline</code> is whether or not the font should be
|
||||
* underlined, and is either <code>t</code> or <code>f</code>.
|
||||
* <li><code>style</code> is the <code>family,style,size</code>
|
||||
* triplet described above.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @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<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 <code>SyntaxScheme</code> 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) {
|
||||
Color bg = stringToColor(bgStr);
|
||||
scheme.styles[index].background = bg;
|
||||
}
|
||||
|
||||
boolean styleSpecified = false;
|
||||
boolean bold = false;
|
||||
boolean italic = false;
|
||||
String boldStr = attrs.getValue("bold");
|
||||
if (boldStr!=null) {
|
||||
bold = Boolean.valueOf(boldStr).booleanValue();
|
||||
styleSpecified = true;
|
||||
}
|
||||
String italicStr = attrs.getValue("italic");
|
||||
if (italicStr!=null) {
|
||||
italic = Boolean.valueOf(italicStr).booleanValue();
|
||||
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).booleanValue();
|
||||
scheme.styles[index].underline = uline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,871 +0,0 @@
|
||||
/*
|
||||
* 02/24/2004
|
||||
*
|
||||
* SyntaxView.java - The View object used by RSyntaxTextArea when word wrap is
|
||||
* disabled.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.folding.Fold;
|
||||
import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
|
||||
|
||||
|
||||
/**
|
||||
* The <code>javax.swing.text.View</code> 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
|
||||
* <code>RSyntaxTextArea</code>.<p>
|
||||
*
|
||||
* 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.<p>
|
||||
*
|
||||
* 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 <code>SyntaxView</code> 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 <em>longLine</em> variable is updated to
|
||||
* represent the longest line contained. The <em>font</em> 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;
|
||||
int visibleLineCount = getElement().getElementCount();
|
||||
if (host.isCodeFoldingEnabled()) {
|
||||
visibleLineCount -= host.getFoldManager().getHiddenLineCount();
|
||||
}
|
||||
return visibleLineCount * 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);
|
||||
int size = (i != null) ? i.intValue() : 5;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a token list for the <i>physical</i> 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 <code>offset</code>, 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 <code>offset</code> is in the first line in
|
||||
* the document, <code>null</code> is returned.
|
||||
*/
|
||||
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 <i>physical</i> 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 <code>offset</code>, 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 <code>offset</code> is in the last physical
|
||||
* line in the document, <code>null</code> is returned.
|
||||
*/
|
||||
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 (line<lineCount-1) {
|
||||
return document.getTokenListForLine(line+1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
FoldManager fm = host.getFoldManager();
|
||||
line = fm.getVisibleLineBelow(line);
|
||||
if (line>=0 && line<lineCount) {
|
||||
return document.getTokenListForLine(line);
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
if (host.isCodeFoldingEnabled()) {
|
||||
FoldManager fm = host.getFoldManager();
|
||||
int hiddenCount = fm.getHiddenLineCountAbove(line);
|
||||
line -= hiddenCount;
|
||||
}
|
||||
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);
|
||||
Token tokenList = doc.getTokenListForLine(lineIndex);
|
||||
Rectangle lineArea = lineToRect(a, lineIndex);
|
||||
tabBase = lineArea.x; // Used by listOffsetToView().
|
||||
|
||||
//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.<p>
|
||||
*
|
||||
* This is implemented to subtract the width of the second character, as
|
||||
* this view's <code>modelToView</code> actually returns the width of the
|
||||
* character instead of "1" or "0" like the View implementations in
|
||||
* <code>javax.swing.text</code>. Thus, if we don't override this method,
|
||||
* the <code>View</code> implementation will return one character's width
|
||||
* too much for its consumers (implementations of
|
||||
* <code>javax.swing.text.Highlighter</code>).
|
||||
*
|
||||
* @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; <code>b0</code>
|
||||
* will have one of these values:
|
||||
* <ul>
|
||||
* <li> <code>Position.Bias.Forward</code>
|
||||
* <li> <code>Position.Bias.Backward</code>
|
||||
* </ul>
|
||||
* @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 <code>b0</code> or
|
||||
* <code>b1</code> are not one of the
|
||||
* legal <code>Position.Bias</code> 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 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
|
||||
*/
|
||||
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 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();
|
||||
|
||||
RSyntaxTextAreaHighlighter h =
|
||||
(RSyntaxTextAreaHighlighter)host.getHighlighter();
|
||||
|
||||
Graphics2D g2d = (Graphics2D)g;
|
||||
Token token;
|
||||
//System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1));
|
||||
|
||||
|
||||
int line = linesAbove;
|
||||
//int count = 0;
|
||||
while (y<clip.y+clip.height+lineHeight && line<lineCount) {
|
||||
|
||||
Fold fold = fm.getFoldForLine(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);
|
||||
|
||||
if (fold!=null && fold.isCollapsed()) {
|
||||
|
||||
// Visible indicator of collapsed lines
|
||||
Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
|
||||
if (c!=null) {
|
||||
g.setColor(c);
|
||||
g.drawLine(x,y+lineHeight-ascent-1,
|
||||
alloc.width,y+lineHeight-ascent-1);
|
||||
}
|
||||
|
||||
// Skip to next line to paint, taking extra care for lines with
|
||||
// block ends and begins together, e.g. "} else {"
|
||||
do {
|
||||
int hiddenLineCount = fold.getLineCount();
|
||||
if (hiddenLineCount==0) {
|
||||
// Fold parser identified a zero-line fold region.
|
||||
// This is really a bug, but we'll be graceful here
|
||||
// and avoid an infinite loop.
|
||||
break;
|
||||
}
|
||||
line += hiddenLineCount;
|
||||
fold = fm.getFoldForLine(line);
|
||||
} while (fold!=null && fold.isCollapsed());
|
||||
|
||||
}
|
||||
|
||||
y += lineHeight;
|
||||
line++;
|
||||
//count++;
|
||||
|
||||
}
|
||||
|
||||
//System.out.println("SyntaxView: lines painted=" + count);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 <code>true</code> 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 (int i = 0; i < removed.length; i++) {
|
||||
if (removed[i] == 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see if the font metrics and longest line are up-to-date.
|
||||
*/
|
||||
protected void updateMetrics() {
|
||||
host = (RSyntaxTextArea)getContainer();
|
||||
Font f = host.getFont();
|
||||
if (font != f) {
|
||||
// The font changed, we need to recalculate the longest line!
|
||||
// This also updates cached font and tab size.
|
||||
calculateLongestLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides a mapping from the view coordinate space to the logical
|
||||
* coordinate space of the model.
|
||||
*
|
||||
* @param fx the X coordinate >= 0
|
||||
* @param fy the Y coordinate >= 0
|
||||
* @param a the allocated region to render into
|
||||
* @return the location within the model that best represents the
|
||||
* given point in the view >= 0
|
||||
*/
|
||||
public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
|
||||
|
||||
bias[0] = Position.Bias.Forward;
|
||||
|
||||
Rectangle alloc = a.getBounds();
|
||||
RSyntaxDocument doc = (RSyntaxDocument)getDocument();
|
||||
int x = (int) fx;
|
||||
int y = (int) fy;
|
||||
|
||||
// If they're asking about a view position above the area covered by
|
||||
// this view, then the position is assumed to be the starting position
|
||||
// of this view.
|
||||
if (y < alloc.y) {
|
||||
return getStartOffset();
|
||||
}
|
||||
|
||||
// If they're asking about a position below this view, the position
|
||||
// is assumed to be the ending position of this view.
|
||||
else if (y > alloc.y + alloc.height) {
|
||||
return host.getLastVisibleOffset();
|
||||
}
|
||||
|
||||
// They're asking about a position within the coverage of this view
|
||||
// vertically. So, we figure out which line the point corresponds to.
|
||||
// If the line is greater than the number of lines contained, then
|
||||
// simply use the last line as it represents the last possible place
|
||||
// we can position to.
|
||||
else {
|
||||
|
||||
Element map = doc.getDefaultRootElement();
|
||||
int lineIndex = Math.abs((y - alloc.y) / lineHeight);//metrics.getHeight() );
|
||||
FoldManager fm = host.getFoldManager();
|
||||
//System.out.print("--- " + lineIndex);
|
||||
lineIndex += fm.getHiddenLineCountAbove(lineIndex, true);
|
||||
//System.out.println(" => " + lineIndex);
|
||||
if (lineIndex >= map.getElementCount()) {
|
||||
return host.getLastVisibleOffset();
|
||||
}
|
||||
|
||||
Element line = map.getElement(lineIndex);
|
||||
|
||||
// If the point is to the left of the line...
|
||||
if (x < alloc.x)
|
||||
return line.getStartOffset();
|
||||
|
||||
// If the point is to the right of the line...
|
||||
else if (x > alloc.x + alloc.width)
|
||||
return line.getEndOffset() - 1;
|
||||
|
||||
else {
|
||||
// Determine the offset into the text
|
||||
int p0 = line.getStartOffset();
|
||||
Token tokenList = doc.getTokenListForLine(lineIndex);
|
||||
tabBase = alloc.x;
|
||||
int offs = tokenList.getListOffset(
|
||||
(RSyntaxTextArea)getContainer(),
|
||||
this, tabBase, x);
|
||||
return offs!=-1 ? offs : p0;
|
||||
}
|
||||
|
||||
} // End of else.
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int yForLine(Rectangle alloc, int line) throws BadLocationException {
|
||||
|
||||
//Rectangle lineArea = lineToRect(alloc, lineIndex);
|
||||
updateMetrics();
|
||||
if (metrics != null) {
|
||||
// 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;
|
||||
FoldManager fm = host.getFoldManager();
|
||||
if (!fm.isLineHidden(line)) {
|
||||
line -= fm.getHiddenLineCountAbove(line);
|
||||
return alloc.y + line*lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int yForLineContaining(Rectangle alloc, int offs)
|
||||
throws BadLocationException {
|
||||
Element map = getElement();
|
||||
int line = map.getElementIndex(offs);
|
||||
return yForLine(alloc, line);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,712 +0,0 @@
|
||||
/*
|
||||
* 11/25/2008
|
||||
*
|
||||
* TextEditorPane.java - A syntax highlighting text area that has knowledge of
|
||||
* the file it is editing on disk.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.Document;
|
||||
|
||||
import org.fife.io.UnicodeReader;
|
||||
import org.fife.io.UnicodeWriter;
|
||||
import org.fife.ui.rtextarea.RTextAreaEditorKit;
|
||||
|
||||
|
||||
/**
|
||||
* An extension of {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextArea}
|
||||
* that adds information about the file being edited, such as:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Its name and location.
|
||||
* <li>Is it dirty?
|
||||
* <li>Is it read-only?
|
||||
* <li>The last time it was loaded or saved to disk (local files only).
|
||||
* <li>The file's encoding on disk.
|
||||
* <li>Easy access to the line separator.
|
||||
* </ul>
|
||||
*
|
||||
* Loading and saving is also built into the editor.<p>
|
||||
* 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 <code>INSERT_MODE</code> or
|
||||
* <code>OVERWRITE_MODE</code>.
|
||||
*/
|
||||
public TextEditorPane(int textMode) {
|
||||
this(textMode, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new <code>TextEditorPane</code>. The file will be given
|
||||
* a default name.
|
||||
*
|
||||
* @param textMode Either <code>INSERT_MODE</code> or
|
||||
* <code>OVERWRITE_MODE</code>.
|
||||
* @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 <code>TextEditorPane</code>.
|
||||
*
|
||||
* @param textMode Either <code>INSERT_MODE</code> or
|
||||
* <code>OVERWRITE_MODE</code>.
|
||||
* @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 <code>null</code>, a file named "Untitled.txt" in the current
|
||||
* directory is used.
|
||||
* @throws IOException If an IO error occurs reading the file at
|
||||
* <code>loc</code>. This of course won't happen if
|
||||
* <code>loc</code> is <code>null</code>.
|
||||
*/
|
||||
public TextEditorPane(int textMode, boolean wordWrapEnabled,
|
||||
FileLocation loc) throws IOException {
|
||||
this(textMode, wordWrapEnabled, loc, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new <code>TextEditorPane</code>.
|
||||
*
|
||||
* @param textMode Either <code>INSERT_MODE</code> or
|
||||
* <code>OVERWRITE_MODE</code>.
|
||||
* @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 <code>null</code>, 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 <code>null</code>,
|
||||
* a system default value is used.
|
||||
* @throws IOException If an IO error occurs reading the file at
|
||||
* <code>loc</code>. This of course won't happen if
|
||||
* <code>loc</code> is <code>null</code>.
|
||||
*/
|
||||
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==null ? null : 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
|
||||
* <em>by this editor pane</em>. 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.<p>
|
||||
*
|
||||
* 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.
|
||||
* "<code>\n</code>", "<code>\r\n</code>", or "<code>\r</code>").<p>
|
||||
*
|
||||
* Note that this value is an <code>Object</code> and not a
|
||||
* <code>String</code> 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 <code>String</code>.
|
||||
*
|
||||
* @return The line separator. If this value is <code>null</code>, then
|
||||
* the system default line separator is used (usually the value
|
||||
* of <code>System.getProperty("line.separator")</code>).
|
||||
* @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 <code>null</code>, 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 <code>null</code>,
|
||||
* a system default value is used.
|
||||
* @throws IOException If an IO error occurs reading from <code>loc</code>.
|
||||
* If <code>loc</code> is <code>null</code>, 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.
|
||||
* @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 <code>false</code>.<p>
|
||||
*
|
||||
* 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
|
||||
* <code>null</code>.
|
||||
* @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 <code>null</code>, 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);
|
||||
BufferedReader r = new BufferedReader(ur);
|
||||
try {
|
||||
read(r, null);
|
||||
} finally {
|
||||
doc.addDocumentListener(this);
|
||||
r.close();
|
||||
}
|
||||
|
||||
// No IOException thrown, so we can finally change the location.
|
||||
charSet = ur.getEncoding();
|
||||
String old = getFileFullPath();
|
||||
this.loc = loc;
|
||||
setDirty(false);
|
||||
setCaretPosition(0);
|
||||
firePropertyChange(FULL_PATH_PROPERTY, old, getFileFullPath());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reloads this file from disk. The file must exist for this operation
|
||||
* to not throw an exception.<p>
|
||||
*
|
||||
* The file's "dirty" state will be set to <code>false</code> after this
|
||||
* operation. If this is a local file, its "last modified" time is
|
||||
* updated to reflect that of the actual file.<p>
|
||||
*
|
||||
* 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.<p>
|
||||
*
|
||||
* The text area's "dirty" state is set to <code>false</code>, 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();
|
||||
BufferedWriter w = 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}.<p>
|
||||
*
|
||||
* 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. <code>TextEditorPane</code> 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
|
||||
* <code>load()</code>, <code>reload()</code>, <code>save()</code>, or
|
||||
* <code>saveAs()</code> 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.
|
||||
*/
|
||||
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 <code>encoding</code> is
|
||||
* <code>null</code>.
|
||||
* @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.
|
||||
* "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>").
|
||||
*
|
||||
* Besides parameter checking, this method is preferred over
|
||||
* <code>getDocument().putProperty()</code> because it sets the editor's
|
||||
* dirty flag when the line separator is changed.
|
||||
*
|
||||
* @param separator The new line separator.
|
||||
* @throws NullPointerException If <code>separator</code> is
|
||||
* <code>null</code>.
|
||||
* @throws IllegalArgumentException If <code>separator</code> is not one
|
||||
* of "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>".
|
||||
* @see #getLineSeparator()
|
||||
*/
|
||||
public void setLineSeparator(String separator) {
|
||||
setLineSeparator(separator, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the line separator sequence to use when this file is saved (e.g.
|
||||
* "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>").
|
||||
*
|
||||
* Besides parameter checking, this method is preferred over
|
||||
* <code>getDocument().putProperty()</code> 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 <code>separator</code> is
|
||||
* <code>null</code>.
|
||||
* @throws IllegalArgumentException If <code>separator</code> is not one
|
||||
* of "<code>\n</code>", "<code>\r\n</code>" or "<code>\r</code>".
|
||||
* @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.<p>
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,705 +0,0 @@
|
||||
/*
|
||||
* 10/30/2011
|
||||
*
|
||||
* Theme.java - A color theme for RSyntaxTextArea.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
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.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.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;
|
||||
|
||||
import org.fife.io.UnicodeWriter;
|
||||
import org.fife.ui.rtextarea.Gutter;
|
||||
|
||||
|
||||
/**
|
||||
* A theme is a set of fonts and colors to use to style RSyntaxTextArea.
|
||||
* Themes are defined in XML files that are validated against
|
||||
* <code>themes.dtd</code>. This provides applications and other consumers
|
||||
* with an easy way to style RSyntaxTextArea without having to use the API.<p>
|
||||
*
|
||||
* Sample themes are included in the source tree under the <code>/themes</code>
|
||||
* folder, but are not a part of the built RSyntaxTextArea jar. Hosting
|
||||
* applications are free to ship and use these themes as-is, modify them, or
|
||||
* create their own.<p>
|
||||
*
|
||||
* Note that to save a <code>Theme</code> via {@link #save(OutputStream)},
|
||||
* you must currently create a <code>Theme</code> from a text area wrapped in
|
||||
* an <code>RTextScrollPane</code>, so that the color information for the
|
||||
* gutter can be retrieved.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Theme {
|
||||
|
||||
private Font baseFont;
|
||||
private Color bgColor;
|
||||
private Color caretColor;
|
||||
private Color selectionBG;
|
||||
private boolean selectionRoundedEdges;
|
||||
private Color currentLineHighlight;
|
||||
private boolean fadeCurrentLineHighlight;
|
||||
private Color marginLineColor;
|
||||
private Color markAllHighlightColor;
|
||||
private Color markOccurrencesColor;
|
||||
private boolean markOccurrencesBorder;
|
||||
private Color matchedBracketFG;
|
||||
private Color matchedBracketBG;
|
||||
private boolean matchedBracketHighlightBoth;
|
||||
private boolean matchedBracketAnimate;
|
||||
private Color hyperlinkFG;
|
||||
private Color[] secondaryLanguages;
|
||||
|
||||
private SyntaxScheme scheme;
|
||||
|
||||
private Color gutterBorderColor;
|
||||
private Color lineNumberColor;
|
||||
private String lineNumberFont;
|
||||
private int lineNumberFontSize;
|
||||
private Color foldIndicatorFG;
|
||||
private Color foldBG;
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor, used when loading from a stream.
|
||||
*/
|
||||
private Theme() {
|
||||
secondaryLanguages = new Color[3];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a theme from an RSyntaxTextArea. It should be contained in
|
||||
* an <code>RTextScrollPane</code> to get all gutter color information.
|
||||
*
|
||||
* @param textArea The text area.
|
||||
*/
|
||||
public Theme(RSyntaxTextArea textArea) {
|
||||
|
||||
baseFont = textArea.getFont();
|
||||
bgColor = textArea.getBackground();
|
||||
caretColor = textArea.getCaretColor();
|
||||
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; i<count; i++) {
|
||||
secondaryLanguages[i]= textArea.getSecondaryLanguageBackground(i+1);
|
||||
}
|
||||
|
||||
scheme = textArea.getSyntaxScheme();
|
||||
|
||||
Gutter gutter = RSyntaxUtilities.getGutter(textArea);
|
||||
if (gutter!=null) {
|
||||
bgColor = gutter.getBackground();
|
||||
gutterBorderColor = gutter.getBorderColor();
|
||||
lineNumberColor = gutter.getLineNumberColor();
|
||||
lineNumberFont = gutter.getLineNumberFont().getFamily();
|
||||
lineNumberFontSize = gutter.getLineNumberFont().getSize();
|
||||
foldIndicatorFG = gutter.getFoldIndicatorForeground();
|
||||
foldBG = gutter.getFoldBackground();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies this theme to a text area.
|
||||
*
|
||||
* @param textArea The text area to apply this theme to.
|
||||
*/
|
||||
public void apply(RSyntaxTextArea textArea) {
|
||||
|
||||
textArea.setFont(baseFont);
|
||||
textArea.setBackground(bgColor);
|
||||
textArea.setCaretColor(caretColor);
|
||||
textArea.setSelectionColor(selectionBG);
|
||||
textArea.setRoundedSelectionEdges(selectionRoundedEdges);
|
||||
textArea.setCurrentLineHighlightColor(currentLineHighlight);
|
||||
textArea.setFadeCurrentLineHighlight(fadeCurrentLineHighlight);
|
||||
textArea.setMarginLineColor(marginLineColor);
|
||||
textArea.setMarkAllHighlightColor(markAllHighlightColor);
|
||||
textArea.setMarkOccurrencesColor(markOccurrencesColor);
|
||||
textArea.setPaintMarkOccurrencesBorder(markOccurrencesBorder);
|
||||
textArea.setMatchedBracketBGColor(matchedBracketBG);
|
||||
textArea.setMatchedBracketBorderColor(matchedBracketFG);
|
||||
textArea.setPaintMatchedBracketPair(matchedBracketHighlightBoth);
|
||||
textArea.setAnimateBracketMatching(matchedBracketAnimate);
|
||||
textArea.setHyperlinkForeground(hyperlinkFG);
|
||||
|
||||
int count = secondaryLanguages.length;
|
||||
for (int i=0; i<count; i++) {
|
||||
textArea.setSecondaryLanguageBackground(i+1, secondaryLanguages[i]);
|
||||
}
|
||||
|
||||
textArea.setSyntaxScheme(scheme);
|
||||
|
||||
Gutter gutter = RSyntaxUtilities.getGutter(textArea);
|
||||
if (gutter!=null) {
|
||||
gutter.setBackground(bgColor);
|
||||
gutter.setBorderColor(gutterBorderColor);
|
||||
gutter.setLineNumberColor(lineNumberColor);
|
||||
String fontName = lineNumberFont!=null ? lineNumberFont :
|
||||
baseFont.getFamily();
|
||||
int fontSize = lineNumberFontSize>0 ? lineNumberFontSize :
|
||||
baseFont.getSize();
|
||||
Font font = getFont(fontName, Font.PLAIN, fontSize);
|
||||
gutter.setLineNumberFont(font);
|
||||
gutter.setFoldIndicatorForeground(foldIndicatorFG);
|
||||
gutter.setFoldBackground(foldBG);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static final String colorToString(Color c) {
|
||||
int color = c.getRGB() & 0xffffff;
|
||||
String str = Integer.toHexString(color);
|
||||
while (str.length()<6) {
|
||||
str = "0" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the specified font.
|
||||
*
|
||||
* @param family The font family.
|
||||
* @param style The style of font.
|
||||
* @param size The size of the font.
|
||||
* @return The font.
|
||||
*/
|
||||
private static Font getFont(String family, int style, int size) {
|
||||
// Use StyleContext to get a composite font for Asian glyphs.
|
||||
StyleContext sc = StyleContext.getDefaultStyleContext();
|
||||
return sc.getFont(family, style, size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads a theme.
|
||||
*
|
||||
* @param in The input stream to read from. This will be closed when this
|
||||
* method returns.
|
||||
* @return The theme.
|
||||
* @throws IOException If an IO error occurs.
|
||||
* @see #save(OutputStream)
|
||||
*/
|
||||
public static Theme load(InputStream in) throws IOException {
|
||||
|
||||
Theme theme = new Theme();
|
||||
|
||||
BufferedInputStream bin = new BufferedInputStream(in);
|
||||
try {
|
||||
XmlHandler.load(theme, bin);
|
||||
} finally {
|
||||
bin.close();
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
BufferedOutputStream bout = new BufferedOutputStream(out);
|
||||
try {
|
||||
|
||||
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("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 = 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("lineNumberFont", lineNumberFont);
|
||||
}
|
||||
if (lineNumberFontSize>0) {
|
||||
elem.setAttribute("lineNumberFontSize",
|
||||
Integer.toString(lineNumberFontSize));
|
||||
}
|
||||
root.appendChild(elem);
|
||||
|
||||
elem = doc.createElement("foldIndicator");
|
||||
elem.setAttribute("fg", colorToString(foldIndicatorFG));
|
||||
elem.setAttribute("iconBg", colorToString(foldBG));
|
||||
root.appendChild(elem);
|
||||
|
||||
elem = doc.createElement("tokenStyles");
|
||||
Field[] fields = TokenTypes.class.getFields();
|
||||
for (int i=0; i<fields.length; i++) {
|
||||
Field field = fields[i];
|
||||
int value = field.getInt(null);
|
||||
if (value!=TokenTypes.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) {
|
||||
e.printStackTrace();
|
||||
throw new IOException("Error generating XML: " + e.getMessage());
|
||||
// When Java 6 is minimum required version
|
||||
//throw new IOException("Error generating XML: " + e.getMessage(), e);
|
||||
} finally {
|
||||
bout.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* <pre>
|
||||
* "$00ff00"
|
||||
* "00ff00"
|
||||
* </pre>
|
||||
* will return <code>new Color(0, 255, 0)</code>.
|
||||
*
|
||||
* @param s The string to evaluate.
|
||||
* @return The color.
|
||||
*/
|
||||
private static final Color stringToColor(String s) {
|
||||
if (s!=null && (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 <code>SyntaxScheme</code> from an XML file.
|
||||
*/
|
||||
private static class XmlHandler extends DefaultHandler {
|
||||
|
||||
private Theme theme;
|
||||
|
||||
public void error(SAXParseException e) throws SAXException {
|
||||
throw e;
|
||||
}
|
||||
|
||||
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) {
|
||||
se.printStackTrace();
|
||||
throw new IOException(se.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static final 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;
|
||||
}
|
||||
|
||||
public InputSource resolveEntity(String publicID,
|
||||
String systemID) throws SAXException {
|
||||
return new InputSource(getClass().
|
||||
getResourceAsStream("/theme.dtd"));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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)) {
|
||||
String family = attrs.getValue("family");
|
||||
int size = Integer.parseInt(attrs.getValue("size"));
|
||||
if (family!=null) {
|
||||
theme.baseFont = getFont(family, Font.PLAIN, size);
|
||||
}
|
||||
else {
|
||||
theme.baseFont = RSyntaxTextArea.getDefaultFont();
|
||||
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.valueOf(fadeStr).booleanValue();
|
||||
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);
|
||||
}
|
||||
|
||||
else if ("gutterBorder".equals(qName)) {
|
||||
String color = attrs.getValue("color");
|
||||
theme.gutterBorderColor = stringToColor(color);
|
||||
}
|
||||
|
||||
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.valueOf(border).booleanValue();
|
||||
}
|
||||
|
||||
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.valueOf(highlightBoth).booleanValue();
|
||||
String animate = attrs.getValue("animate");
|
||||
theme.matchedBracketAnimate = Boolean.valueOf(animate).booleanValue();
|
||||
}
|
||||
|
||||
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 color = attrs.getValue("bg");
|
||||
theme.selectionBG = stringToColor(color);
|
||||
String roundedStr = attrs.getValue("roundedEdges");
|
||||
theme.selectionRoundedEdges = Boolean.valueOf(roundedStr).booleanValue();
|
||||
}
|
||||
|
||||
// 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 e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
} catch (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.valueOf(boldStr).booleanValue();
|
||||
styleSpecified = true;
|
||||
}
|
||||
String italicStr = attrs.getValue("italic");
|
||||
if (italicStr!=null) {
|
||||
italic = Boolean.valueOf(italicStr).booleanValue();
|
||||
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.valueOf(ulineStr).booleanValue();
|
||||
theme.scheme.getStyle(index).underline = uline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void warning(SAXParseException e) throws SAXException {
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 10/28/2004
|
||||
*
|
||||
* TokenFactory.java - Interface for a class that generates tokens of some type.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a class that generates tokens somehow.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.1
|
||||
*/
|
||||
interface TokenFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Returns a null token.
|
||||
*
|
||||
* @return A null token.
|
||||
*/
|
||||
public Token createToken();
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a token.
|
||||
*
|
||||
* @param line The char array from which to get the token's text.
|
||||
* @param beg The starting offset of the token's text in the char array.
|
||||
* @param end The ending offset of the token's text in the char array.
|
||||
* @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);
|
||||
|
||||
|
||||
/**
|
||||
* Resets the state of this token maker, if necessary.
|
||||
* FIXME: Improve documentation.
|
||||
*/
|
||||
public void resetAllTokens();
|
||||
|
||||
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
/*
|
||||
* 02/24/2004
|
||||
*
|
||||
* TokenMaker.java - An object that can take a chunk of text and return a
|
||||
* linked list of <code>Token</code>s representing it.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of <code>TokenMaker</code> is a class that turns text into
|
||||
* a linked list of <code>Token</code>s for syntax highlighting
|
||||
* in a particular language.
|
||||
*
|
||||
* @see Token
|
||||
* @see AbstractTokenMaker
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.2
|
||||
*/
|
||||
public interface TokenMaker {
|
||||
|
||||
|
||||
/**
|
||||
* Adds a null token to the end of the current linked list of tokens.
|
||||
* This should be put at the end of the linked list whenever the last
|
||||
* token on the current line is NOT a multi-line token.
|
||||
*/
|
||||
public void addNullToken();
|
||||
|
||||
|
||||
/**
|
||||
* Adds the token specified to the current linked list of tokens.
|
||||
*
|
||||
* @param array The character array from which to get the text.
|
||||
* @param start Start offset in <code>segment</code> of token.
|
||||
* @param end End offset in <code>segment</code> of token.
|
||||
* @param tokenType The token's type.
|
||||
* @param startOffset The offset in the document at which this token
|
||||
* occurs.
|
||||
*/
|
||||
public void addToken(char[] array, int start, int end, int tokenType,
|
||||
int startOffset);
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this programming language uses curly braces
|
||||
* ('<tt>{</tt>' and '<tt>}</tt>') to denote code blocks.
|
||||
*
|
||||
* @return Whether curly braces denote code blocks.
|
||||
*/
|
||||
public boolean getCurlyBracesDenoteCodeBlocks();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the last token on this line's type if the token is "unfinished",
|
||||
* or {@link Token#NULL} if it was finished. For example, if C-style
|
||||
* syntax highlighting is being implemented, and <code>text</code>
|
||||
* contained a line of code that contained the beginning of a comment but
|
||||
* no end-comment marker ("*\/"), then this method would return
|
||||
* {@link Token#COMMENT_MULTILINE} for that line. This is useful
|
||||
* for doing syntax highlighting.
|
||||
*
|
||||
* @param text The line of tokens to examine.
|
||||
* @param initialTokenType The token type to start with (i.e., the value
|
||||
* of <code>getLastTokenTypeOnLine</code> for the line before
|
||||
* <code>text</code>).
|
||||
* @return The last token on this line's type, or {@link Token#NULL}
|
||||
* if the line was completed.
|
||||
*/
|
||||
public int getLastTokenTypeOnLine(Segment text, int initialTokenType);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the text to place at the beginning and end of a
|
||||
* line to "comment" it in a this programming language.
|
||||
*
|
||||
* @return The start and end strings to add to a line to "comment"
|
||||
* it out. A <code>null</code> value for either means there
|
||||
* is no string to add for that part. A value of
|
||||
* <code>null</code> for the array means this language
|
||||
* does not support commenting/uncommenting lines.
|
||||
*/
|
||||
public String[] getLineCommentStartAndEnd();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an action to handle "insert break" key presses (i.e. Enter).
|
||||
*
|
||||
* @return The action, or <code>null</code> if the default action should
|
||||
* be used.
|
||||
*/
|
||||
public Action getInsertBreakAction();
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public boolean getMarkOccurrencesOfTokenType(int type);
|
||||
|
||||
|
||||
/**
|
||||
* If a line ends in the specified token, this method returns whether
|
||||
* a new line inserted after that line should be indented.
|
||||
*
|
||||
* @param token The token the previous line ends with.
|
||||
* @return Whether the next line should be indented.
|
||||
*/
|
||||
public boolean getShouldIndentNextLineAfter(Token token);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first token in the linked list of tokens generated
|
||||
* from <code>text</code>. This method must be implemented by
|
||||
* subclasses so they can correctly implement syntax highlighting.
|
||||
*
|
||||
* @param text The text from which to get tokens.
|
||||
* @param initialTokenType The token type we should start with.
|
||||
* @param startOffset The offset into the document at which
|
||||
* <code>text</code> starts.
|
||||
* @return The first <code>Token</code> in a linked list representing
|
||||
* the syntax highlighted text.
|
||||
*/
|
||||
public Token getTokenList(Segment text, int initialTokenType,
|
||||
int startOffset);
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this language is a markup language.
|
||||
*
|
||||
* @return Whether this language is markup.
|
||||
*/
|
||||
public boolean isMarkupLanguage();
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether whitespace is visible.
|
||||
*
|
||||
* @return Whether whitespace is visible.
|
||||
* @see #setWhitespaceVisible(boolean)
|
||||
*/
|
||||
public boolean isWhitespaceVisible();
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether tokens are generated that "show" whitespace.
|
||||
*
|
||||
* @param visible Whether whitespace should be visible.
|
||||
* @see #isWhitespaceVisible()
|
||||
*/
|
||||
public void setWhitespaceVisible(boolean visible);
|
||||
|
||||
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
/*
|
||||
* 08/26/2004
|
||||
*
|
||||
* TokenMakerBase.java - A base class for token makers.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for token makers.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
abstract class TokenMakerBase implements TokenMaker {
|
||||
|
||||
/**
|
||||
* The first token in the returned linked list.
|
||||
*/
|
||||
protected Token firstToken;
|
||||
|
||||
/**
|
||||
* Used in the creation of the linked list.
|
||||
*/
|
||||
protected Token currentToken;
|
||||
|
||||
/**
|
||||
* Used in the creation of the linked list.
|
||||
*/
|
||||
protected Token previousToken;
|
||||
|
||||
/**
|
||||
* The factory that gives us our tokens to use.
|
||||
*/
|
||||
private TokenFactory tokenFactory;
|
||||
|
||||
/**
|
||||
* "0" implies this is the "main" language being highlighted. Positive
|
||||
* values imply various "secondary" or "embedded" languages, such as CSS
|
||||
* or JavaScript in HTML. While this value is non-zero, tokens will be
|
||||
* generated with this language index so they can (possibly) be painted
|
||||
* differently, so "embedded" languages can be rendered with a special
|
||||
* background.
|
||||
*/
|
||||
private int languageIndex;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public TokenMakerBase() {
|
||||
firstToken = currentToken = previousToken = null;
|
||||
tokenFactory = new DefaultTokenFactory();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void addNullToken() {
|
||||
if (firstToken==null) {
|
||||
firstToken = tokenFactory.createToken();
|
||||
currentToken = firstToken;
|
||||
}
|
||||
else {
|
||||
currentToken.setNextToken(tokenFactory.createToken());
|
||||
previousToken = currentToken;
|
||||
currentToken = currentToken.getNextToken();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the token specified to the current linked list of tokens.
|
||||
*
|
||||
* @param segment <code>Segment</code> to get text from.
|
||||
* @param start Start offset in <code>segment</code> of token.
|
||||
* @param end End offset in <code>segment</code> of token.
|
||||
* @param tokenType The token's type.
|
||||
* @param startOffset The offset in the document at which this token
|
||||
* occurs.
|
||||
*/
|
||||
public void addToken(Segment segment, int start, int end, int tokenType,
|
||||
int startOffset) {
|
||||
addToken(segment.array, start,end, tokenType, startOffset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void addToken(char[] array, int start, int end, int tokenType,
|
||||
int startOffset) {
|
||||
addToken(array, start, end, tokenType, startOffset, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the token specified to the current linked list of tokens.
|
||||
*
|
||||
* @param array The character array.
|
||||
* @param start The starting offset in the array.
|
||||
* @param end The ending offset in the array.
|
||||
* @param tokenType The token's type.
|
||||
* @param startOffset The offset in the document at which this token
|
||||
* occurs.
|
||||
* @param hyperlink Whether this token is a hyperlink.
|
||||
*/
|
||||
public void addToken(char[] array, int start, int end, int tokenType,
|
||||
int startOffset, boolean hyperlink) {
|
||||
|
||||
if (firstToken==null) {
|
||||
firstToken = tokenFactory.createToken(array, start, end,
|
||||
startOffset, tokenType);
|
||||
currentToken = firstToken; // previous token is still null.
|
||||
}
|
||||
else {
|
||||
currentToken.setNextToken(tokenFactory.createToken(array,
|
||||
start,end, startOffset, tokenType));
|
||||
previousToken = currentToken;
|
||||
currentToken = currentToken.getNextToken();
|
||||
}
|
||||
|
||||
currentToken.setLanguageIndex(languageIndex);
|
||||
currentToken.setHyperlink(hyperlink);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this programming language uses curly braces
|
||||
* ('<tt>{</tt>' and '<tt>}</tt>') to denote code blocks.<p>
|
||||
*
|
||||
* The default implementation returns <code>false</code>; subclasses can
|
||||
* override this method if necessary.
|
||||
*
|
||||
* @return Whether curly braces denote code blocks.
|
||||
*/
|
||||
public boolean getCurlyBracesDenoteCodeBlocks() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an action to handle "insert break" key presses (i.e. Enter).
|
||||
* The default implementation returns <code>null</code>. Subclasses
|
||||
* can override.
|
||||
*
|
||||
* @return The default implementation always returns <code>null</code>.
|
||||
*/
|
||||
public Action getInsertBreakAction() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int getLastTokenTypeOnLine(Segment text, int initialTokenType) {
|
||||
|
||||
// Last parameter doesn't matter if we're not painting.
|
||||
Token t = getTokenList(text, initialTokenType, 0);
|
||||
|
||||
while (t.getNextToken()!=null)
|
||||
t = t.getNextToken();
|
||||
|
||||
return t.type;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public String[] getLineCommentStartAndEnd() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether tokens of the specified type should have "mark
|
||||
* occurrences" enabled for the current programming language. The default
|
||||
* implementation returns true if <tt>type</tt> is {@link Token#IDENTIFIER}.
|
||||
* Subclasses can override this method to support other token types, such
|
||||
* as {@link Token#VARIABLE}.
|
||||
*
|
||||
* @param type The token type.
|
||||
* @return Whether tokens of this type should have "mark occurrences"
|
||||
* enabled.
|
||||
*/
|
||||
public boolean getMarkOccurrencesOfTokenType(int type) {
|
||||
return type==Token.IDENTIFIER;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The default implementation returns <code>false</code> always. Languages
|
||||
* that wish to better support auto-indentation can override this method.
|
||||
*
|
||||
* @param token The token the previous line ends with.
|
||||
* @return Whether the next line should be indented.
|
||||
*/
|
||||
public boolean getShouldIndentNextLineAfter(Token token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The default implementation returns <code>false</code> always.
|
||||
* Subclasses that are highlighting a markup language should override this
|
||||
* method to return <code>true</code>.
|
||||
*
|
||||
* @return <code>false</code> always.
|
||||
*/
|
||||
public boolean isMarkupLanguage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isWhitespaceVisible() {
|
||||
return tokenFactory instanceof VisibleWhitespaceTokenFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes the linked list of tokens so we can begin anew. This should
|
||||
* never have to be called by the programmer, as it is automatically
|
||||
* called whenever the user calls
|
||||
* {@link #getLastTokenTypeOnLine(Segment, int)} or
|
||||
* {@link #getTokenList(Segment, int, int)}.
|
||||
*/
|
||||
protected void resetTokenList() {
|
||||
firstToken = currentToken = previousToken = null;
|
||||
tokenFactory.resetAllTokens();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the language index to assign to tokens moving forward. This
|
||||
* property is used to designate tokens as being in "secondary" languages
|
||||
* (such as CSS or JavaScript in HTML).
|
||||
*
|
||||
* @param languageIndex The new language index. A value of
|
||||
* <code>0</code> denotes the "main" language, any positive value
|
||||
* denotes a specific secondary language. Negative values will
|
||||
* be treated as <code>0</code>.
|
||||
*/
|
||||
public void setLanguageIndex(int languageIndex) {
|
||||
this.languageIndex = Math.max(0, languageIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setWhitespaceVisible(boolean visible) {
|
||||
// FIXME: Initialize with the proper sizes.
|
||||
tokenFactory = visible ? new VisibleWhitespaceTokenFactory() :
|
||||
new DefaultTokenFactory();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* 12/12/2008
|
||||
*
|
||||
* TokenMakerFactory.java - A factory for TokenMakers.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.modes.PlainTextTokenMaker;
|
||||
|
||||
|
||||
/**
|
||||
* A factory that maps syntax styles to {@link TokenMaker}s capable of splitting
|
||||
* text into tokens for those syntax styles.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class TokenMakerFactory {
|
||||
|
||||
/**
|
||||
* If this system property is set, a custom <code>TokenMakerFactory</code>
|
||||
* of the specified class will be used as the default token maker factory.
|
||||
*/
|
||||
public static final String PROPERTY_DEFAULT_TOKEN_MAKER_FACTORY =
|
||||
"TokenMakerFactory";
|
||||
|
||||
/**
|
||||
* The singleton default <code>TokenMakerFactory</code> instance.
|
||||
*/
|
||||
private static TokenMakerFactory DEFAULT_INSTANCE;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default <code>TokenMakerFactory</code> instance. This is
|
||||
* the factory used by all {@link RSyntaxDocument}s by default.
|
||||
*
|
||||
* @return The factory.
|
||||
* @see #setDefaultInstance(TokenMakerFactory)
|
||||
*/
|
||||
public static synchronized TokenMakerFactory getDefaultInstance() {
|
||||
if (DEFAULT_INSTANCE==null) {
|
||||
String clazz = null;
|
||||
try {
|
||||
clazz= System.getProperty(PROPERTY_DEFAULT_TOKEN_MAKER_FACTORY);
|
||||
} catch (java.security.AccessControlException ace) {
|
||||
clazz = null; // We're in an applet; take default.
|
||||
}
|
||||
if (clazz==null) {
|
||||
clazz = "org.fife.ui.rsyntaxtextarea.DefaultTokenMakerFactory";
|
||||
}
|
||||
try {
|
||||
DEFAULT_INSTANCE = (TokenMakerFactory)Class.forName(clazz).
|
||||
newInstance();
|
||||
} catch (RuntimeException re) { // FindBugs
|
||||
throw re;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new InternalError("Cannot find TokenMakerFactory: " +
|
||||
clazz);
|
||||
}
|
||||
}
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link TokenMaker} for the specified key.
|
||||
*
|
||||
* @param key The key.
|
||||
* @return The corresponding <code>TokenMaker</code>, or
|
||||
* {@link PlainTextTokenMaker} if none matches the specified key.
|
||||
*/
|
||||
public final TokenMaker getTokenMaker(String key) {
|
||||
TokenMaker tm = getTokenMakerImpl(key);
|
||||
if (tm==null) {
|
||||
tm = new PlainTextTokenMaker();
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link TokenMaker} for the specified key.
|
||||
*
|
||||
* @param key The key.
|
||||
* @return The corresponding <code>TokenMaker</code>, or <code>null</code>
|
||||
* if none matches the specified key.
|
||||
*/
|
||||
protected abstract TokenMaker getTokenMakerImpl(String key);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the set of keys that this factory maps to token makers.
|
||||
*
|
||||
* @return The set of keys.
|
||||
*/
|
||||
public abstract Set keySet();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the default <code>TokenMakerFactory</code> instance. This is
|
||||
* the factory used by all future {@link RSyntaxDocument}s by default.
|
||||
* <code>RSyntaxDocument</code>s that have already been created are not
|
||||
* affected.
|
||||
*
|
||||
* @param tmf The factory.
|
||||
* @throws IllegalArgumentException If <code>tmf</code> is
|
||||
* <code>null</code>.
|
||||
* @see #getDefaultInstance()
|
||||
*/
|
||||
public static synchronized void setDefaultInstance(TokenMakerFactory tmf) {
|
||||
if (tmf==null) {
|
||||
throw new IllegalArgumentException("tmf cannot be null");
|
||||
}
|
||||
DEFAULT_INSTANCE = tmf;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,276 +0,0 @@
|
||||
/*
|
||||
* 08/26/2004
|
||||
*
|
||||
* TokenMap.java - Similar to a Map in Java, only designed specifically for
|
||||
* org.fife.ui.rsyntaxtextarea.Tokens.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
|
||||
/**
|
||||
* A hash table for reserved words, etc. defined by a {@link TokenMaker}.
|
||||
* This class is designed for the quick lookup of tokens, as it can compare
|
||||
* <code>Segment</code>s without the need to allocate a new string.<p>
|
||||
*
|
||||
* The <code>org.fife.ui.rsyntaxtextarea</code> package uses this class to help
|
||||
* identify reserved words in programming languages. An instance of
|
||||
* {@link TokenMaker} will create and initialize an instance of this class
|
||||
* containing all reserved words, data types, and all other words that need to
|
||||
* be syntax-highlighted for that particular language. When the token maker
|
||||
* parses a line and identifies an individual token, it is looked up in the
|
||||
* <code>TokenMap</code> to see if it should be syntax-highlighted.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.6
|
||||
*/
|
||||
public class TokenMap {
|
||||
|
||||
private int size;
|
||||
private TokenMapToken[] tokenMap;
|
||||
private boolean ignoreCase;
|
||||
|
||||
private static final int DEFAULT_TOKEN_MAP_SIZE = 52;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new token map that is case-sensitive.
|
||||
*/
|
||||
public TokenMap() {
|
||||
this(DEFAULT_TOKEN_MAP_SIZE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new token map that is case-sensitive.
|
||||
*
|
||||
* @param size The size of the token map.
|
||||
*/
|
||||
public TokenMap(int size) {
|
||||
this(size, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new token map.
|
||||
*
|
||||
* @param ignoreCase Whether or not this token map should ignore case
|
||||
* when comparing tokens.
|
||||
*/
|
||||
public TokenMap(boolean ignoreCase) {
|
||||
this(DEFAULT_TOKEN_MAP_SIZE, ignoreCase);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new token map.
|
||||
*
|
||||
* @param size The size of the token map.
|
||||
* @param ignoreCase Whether or not this token map should ignore case
|
||||
* when comparing tokens.
|
||||
*/
|
||||
public TokenMap(int size, boolean ignoreCase) {
|
||||
this.size = size;
|
||||
tokenMap = new TokenMapToken[size];
|
||||
this.ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a token to a specified bucket in the token map.
|
||||
*
|
||||
* @param bucket The bucket in which to add the token.
|
||||
* @param token The token to add.
|
||||
*/
|
||||
private void addTokenToBucket(int bucket, TokenMapToken token) {
|
||||
TokenMapToken old = tokenMap[bucket];
|
||||
token.nextToken = old;
|
||||
tokenMap[bucket] = token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the token type associated with the given text, if the given
|
||||
* text is in this token map. If it isn't, <code>-1</code> is returned.
|
||||
*
|
||||
* @param text The segment from which to get the text to compare.
|
||||
* @param start The starting index in the segment of the text.
|
||||
* @param end The ending index in the segment of the text.
|
||||
* @return The token type associated with the given text, or
|
||||
* <code>-1</code> if this token was not specified in this map.
|
||||
*/
|
||||
public int get(Segment text, int start, int end) {
|
||||
return get(text.array, start, end);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the token type associated with the given text, if the given
|
||||
* text is in this token map. If it isn't, <code>-1</code> is returned.
|
||||
*
|
||||
* @param array1 An array of characters containing the text.
|
||||
* @param start The starting index in the array of the text.
|
||||
* @param end The ending index in the array of the text.
|
||||
* @return The token type associated with the given text, or
|
||||
* <code>-1</code> if this token was not specified in this map.
|
||||
*/
|
||||
public int get(char[] array1, int start, int end) {
|
||||
|
||||
int length1 = end - start + 1;
|
||||
|
||||
int hash = getHashCode(array1, start, length1);
|
||||
TokenMapToken token = tokenMap[hash];
|
||||
|
||||
char[] array2;
|
||||
int offset2;
|
||||
int offset1;
|
||||
int length;
|
||||
|
||||
/* We check whether or not to ignore case before doing any looping to
|
||||
* minimize the number of extraneous comparisons we do. This makes
|
||||
* for slightly redundant code, but it'll be a little more efficient.
|
||||
*/
|
||||
|
||||
// If matches are case-sensitive (C, C++, Java, etc.)...
|
||||
if (ignoreCase==false) {
|
||||
|
||||
mainLoop:
|
||||
while (token!=null) {
|
||||
if (token.length==length1) {
|
||||
array2 = token.text;
|
||||
offset2 = token.offset;
|
||||
offset1 = start;
|
||||
length = length1;
|
||||
while (length-- > 0) {
|
||||
if (array1[offset1++]!=array2[offset2++]) {
|
||||
token = token.nextToken;
|
||||
continue mainLoop;
|
||||
}
|
||||
}
|
||||
return token.tokenType;
|
||||
}
|
||||
token = token.nextToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If matches are NOT case-sensitive (HTML)...
|
||||
// Note that all tokens saved in this map were converted to
|
||||
// lower-case already.
|
||||
else {
|
||||
|
||||
mainLoop2:
|
||||
while (token!=null) {
|
||||
if (token.length==length1) {
|
||||
array2 = token.text;
|
||||
offset2 = token.offset;
|
||||
offset1 = start;
|
||||
length = length1;
|
||||
while (length-- > 0) {
|
||||
if (RSyntaxUtilities.toLowerCase(
|
||||
array1[offset1++]) != array2[offset2++]) {
|
||||
token = token.nextToken;
|
||||
continue mainLoop2;
|
||||
}
|
||||
}
|
||||
return token.tokenType;
|
||||
}
|
||||
token = token.nextToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Didn't match any of the tokens in the bucket.
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the hash code for a given string.
|
||||
*
|
||||
* @param text The text to hash.
|
||||
* @param offset The offset into the text at which to start hashing.
|
||||
* @param length The last character in the text to hash.
|
||||
* @return The hash code.
|
||||
*/
|
||||
private final int getHashCode(char[] text, int offset, int length) {
|
||||
return (RSyntaxUtilities.toLowerCase(text[offset]) +
|
||||
RSyntaxUtilities.toLowerCase(text[offset+length-1])) % size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this token map ignores case when checking for tokens.
|
||||
* This property is set in the constructor and cannot be changed, as this
|
||||
* is an intrinsic property of a particular programming language.
|
||||
*
|
||||
* @return Whether or not this token maker is ignoring case.
|
||||
*/
|
||||
protected boolean isIgnoringCase() {
|
||||
return ignoreCase;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a string to this token map.
|
||||
*
|
||||
* @param string The string to add.
|
||||
* @param tokenType The type of token the string is.
|
||||
*/
|
||||
public void put(final String string, final int tokenType) {
|
||||
if (isIgnoringCase())
|
||||
put(string.toLowerCase().toCharArray(), tokenType);
|
||||
else
|
||||
put(string.toCharArray(), tokenType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a string to this token map. The char array passed-in will be used
|
||||
* as the actual data for the token, so it may well be modified (such as
|
||||
* lower-casing it if <code>ignoreCase</code> is <code>true</code>). This
|
||||
* shouldn't be an issue though as this method is only called from the
|
||||
* public <code>put</code> method, which allocates a new char array.
|
||||
*
|
||||
* @param string The string to add.
|
||||
* @param tokenType The type of token the string is.
|
||||
*/
|
||||
private void put(char[] string, int tokenType) {
|
||||
int hashCode = getHashCode(string, 0, string.length);
|
||||
addTokenToBucket(hashCode, new TokenMapToken(string, tokenType));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The "token" used by a token map. Note that this isn't the same thing
|
||||
* as the {@link Token} class, but it's basically a 1-1 correspondence
|
||||
* for reserved words, etc.
|
||||
*/
|
||||
private static class TokenMapToken {
|
||||
|
||||
char[] text;
|
||||
int offset;
|
||||
int length;
|
||||
int tokenType;
|
||||
TokenMapToken nextToken;
|
||||
|
||||
TokenMapToken(char[] text, int tokenType) {
|
||||
this.text = text;
|
||||
this.offset = 0;
|
||||
this.length = text.length;
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[TokenMapToken: " + new String(text,offset,length) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* 08/06/2004
|
||||
*
|
||||
* TokenOrientedView.java - An interface for the syntax-highlighting token-
|
||||
* oriented views for token-oriented methods.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
|
||||
/**
|
||||
* An interface for the syntax-highlighting token oriented views for
|
||||
* token-oriented methods. This way callers won't need to know what specific
|
||||
* class a view is an instance of to access its tokens.<p>
|
||||
*
|
||||
* Currently, this interface is only useful for obtaining token lists for
|
||||
* "physical lines" (i.e., a word-wrapped view's logical lines may be
|
||||
* represented as several physical lines, thus getting the "physical line" above
|
||||
* a given position may prove complicated).
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.1
|
||||
*/
|
||||
public interface TokenOrientedView {
|
||||
|
||||
|
||||
/**
|
||||
* Returns a token list for the <i>physical</i> line above the physical
|
||||
* line containing the specified offset into the document. Note that for
|
||||
* a plain (non-wrapped) view, this is simply the token list for the
|
||||
* logical line above the line containing <code>offset</code>, since lines
|
||||
* are not wrapped. For a wrapped view, this may or may not be tokens from
|
||||
* the same line.
|
||||
*
|
||||
* @param offset The offset in question.
|
||||
* @return A token list for the physical (and in this view, logical) line
|
||||
* before this one. If no physical line is above the one
|
||||
* containing <code>offset</code>, <code>null</code> is returned.
|
||||
*/
|
||||
public Token getTokenListForPhysicalLineAbove(int offset);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a token list for the <i>physical</i> line below the physical
|
||||
* line containing the specified offset into the document. Note that for
|
||||
* a plain (non-wrapped) view, this is simply the token list for the
|
||||
* logical line below the line containing <code>offset</code>, since lines
|
||||
* are not wrapped. For a wrapped view, this may or may not be tokens from
|
||||
* the same line.
|
||||
*
|
||||
* @param offset The offset in question.
|
||||
* @return A token list for the physical (and in this view, logical) line
|
||||
* after this one. If no physical line is after the one
|
||||
* containing <code>offset</code>, <code>null</code> is returned.
|
||||
*/
|
||||
public Token getTokenListForPhysicalLineBelow(int offset);
|
||||
|
||||
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 12/04/2011
|
||||
*
|
||||
* TokenTypes.java - All token types supported by RSyntaxTextArea.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
|
||||
/**
|
||||
* All token types supported by RSyntaxTextArea.<p>
|
||||
*
|
||||
* Note that 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.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface TokenTypes {
|
||||
|
||||
/**
|
||||
* Tokens of type <code>NULL</code> mark the end of lines with no
|
||||
* multi-line token at the end (such as a block comment in C++).
|
||||
*/
|
||||
public static final int NULL = 0;
|
||||
|
||||
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 COMMENT_KEYWORD = 4;
|
||||
public static final int COMMENT_MARKUP = 5;
|
||||
|
||||
public static final int RESERVED_WORD = 6;
|
||||
public static final int RESERVED_WORD_2 = 7;
|
||||
|
||||
public static final int FUNCTION = 8;
|
||||
|
||||
public static final int LITERAL_BOOLEAN = 9;
|
||||
public static final int LITERAL_NUMBER_DECIMAL_INT = 10;
|
||||
public static final int LITERAL_NUMBER_FLOAT = 11;
|
||||
public static final int LITERAL_NUMBER_HEXADECIMAL = 12;
|
||||
public static final int LITERAL_STRING_DOUBLE_QUOTE = 13;
|
||||
public static final int LITERAL_CHAR = 14;
|
||||
public static final int LITERAL_BACKQUOTE = 15;
|
||||
|
||||
public static final int DATA_TYPE = 16;
|
||||
|
||||
public static final int VARIABLE = 17;
|
||||
|
||||
public static final int REGEX = 18;
|
||||
|
||||
public static final int ANNOTATION = 19;
|
||||
|
||||
public static final int IDENTIFIER = 20;
|
||||
|
||||
public static final int WHITESPACE = 21;
|
||||
|
||||
public static final int SEPARATOR = 22;
|
||||
|
||||
public static final int OPERATOR = 23;
|
||||
|
||||
public static final int PREPROCESSOR = 24;
|
||||
|
||||
public static final int MARKUP_TAG_DELIMITER = 25;
|
||||
public static final int MARKUP_TAG_NAME = 26;
|
||||
public static final int MARKUP_TAG_ATTRIBUTE = 27;
|
||||
public static final int MARKUP_TAG_ATTRIBUTE_VALUE = 28;
|
||||
public static final int MARKUP_PROCESSING_INSTRUCTION = 29;
|
||||
public static final int MARKUP_CDATA = 30;
|
||||
|
||||
public static final int ERROR_IDENTIFIER = 31;
|
||||
public static final int ERROR_NUMBER_FORMAT = 32;
|
||||
public static final int ERROR_STRING_DOUBLE = 33;
|
||||
public static final int ERROR_CHAR = 34;
|
||||
|
||||
public static final int NUM_TOKEN_TYPES = 35;
|
||||
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
/*
|
||||
* 11/13/2008
|
||||
*
|
||||
* URLFileLocation.java - The location of a file at a (remote) URL.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
/**
|
||||
* The location of a file at a (remote) URL.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
class URLFileLocation extends FileLocation {
|
||||
|
||||
/**
|
||||
* URL of the remote file.
|
||||
*/
|
||||
private URL url;
|
||||
|
||||
/**
|
||||
* A prettied-up full path of the URL (password removed, etc.).
|
||||
*/
|
||||
private String fileFullPath;
|
||||
|
||||
/**
|
||||
* A prettied-up filename (leading slash, and possibly "<code>%2F</code>",
|
||||
* removed).
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param url The URL of the file.
|
||||
*/
|
||||
URLFileLocation(URL url) {
|
||||
this.url = url;
|
||||
fileFullPath = createFileFullPath();
|
||||
fileName = createFileName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a "prettied-up" URL to use. This will be stripped of
|
||||
* sensitive information such as passwords.
|
||||
*
|
||||
* @return The full path to use.
|
||||
*/
|
||||
private String createFileFullPath() {
|
||||
String fullPath = url.toString();
|
||||
fullPath = fullPath.replaceFirst("://([^:]+)(?:.+)@", "://$1@");
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the "prettied-up" filename to use.
|
||||
*
|
||||
* @return The base name of the file of this URL.
|
||||
*/
|
||||
private String createFileName() {
|
||||
String fileName = url.getPath();
|
||||
if (fileName.startsWith("/%2F/")) { // Absolute path
|
||||
fileName = fileName.substring(4);
|
||||
}
|
||||
else if (fileName.startsWith("/")) { // All others
|
||||
fileName = fileName.substring(1);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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. This will always be
|
||||
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} for URL's.
|
||||
*/
|
||||
protected long getActualLastModified() {
|
||||
return TextEditorPane.LAST_MODIFIED_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public String getFileFullPath() {
|
||||
return fileFullPath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected InputStream getInputStream() throws IOException {
|
||||
return url.openStream();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected OutputStream getOutputStream() throws IOException {
|
||||
return url.openConnection().getOutputStream();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this file location is a local file.
|
||||
*
|
||||
* @return Whether this is a local file.
|
||||
* @see #isLocalAndExists()
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return "file".equalsIgnoreCase(url.getProtocol());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this file location is a local file and already
|
||||
* exists. This method always returns <code>false</code> since we
|
||||
* cannot check this value easily.
|
||||
*
|
||||
* @return <code>false</code> always.
|
||||
* @see #isLocal()
|
||||
*/
|
||||
public boolean isLocalAndExists() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
/*
|
||||
* 10/28/2004
|
||||
*
|
||||
* VisibleWhitespaceToken.java - Token that paints special symbols for its
|
||||
* whitespace characters (space and tab).
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt 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.Segment;
|
||||
import javax.swing.text.TabExpander;
|
||||
|
||||
|
||||
/**
|
||||
* This token class paints spaces and tabs with special symbols so the user
|
||||
* can see the whitespace in his document. Rendering hints are honored.<p>
|
||||
*
|
||||
* The current implementation paints as follows:
|
||||
* <ul>
|
||||
* <li>The first tab or space, if any, is found in the token.</li>
|
||||
* <li>If a tab was found, all characters up to it are painted as a
|
||||
* group.</li>
|
||||
* <li>If a space was found, all characters up to and including it are
|
||||
* painted (it is painted with a special symbol to denote it as
|
||||
* a space).</li>
|
||||
* <li>If neither a tab nor a whitespace was found, all characters in the
|
||||
* token are painted.</li>
|
||||
* <li>Repeat until all characters are painted.</li>
|
||||
* </ul>
|
||||
* This means that rendering hints are applied to all groups of characters
|
||||
* within a token, excluding whitespace and tabs.<p>
|
||||
*
|
||||
* 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 <code>int</code> and not a
|
||||
* <code>float</code>). 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 DefaultToken
|
||||
*/
|
||||
public class VisibleWhitespaceToken extends DefaultToken {
|
||||
|
||||
private Rectangle2D.Float dotRect;
|
||||
|
||||
|
||||
/**
|
||||
* 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 VisibleWhitespaceToken() {
|
||||
super();
|
||||
dotRect = new Rectangle2D.Float(0,0, 1,1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param line The segment from which to get the token.
|
||||
* @param beg The first character's position in <code>line</code>.
|
||||
* @param end The last character's position in <code>line</code>.
|
||||
* @param startOffset The offset into the document at which this
|
||||
* token begins.
|
||||
* @param type A token type listed as "generic" above.
|
||||
*/
|
||||
public VisibleWhitespaceToken(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 <code>line</code>.
|
||||
* @param end The last character's position in <code>line</code>.
|
||||
* @param startOffset The offset into the document at which this
|
||||
* token begins.
|
||||
* @param type A token type listed as "generic" above.
|
||||
*/
|
||||
public VisibleWhitespaceToken(final char[] line, final int beg,
|
||||
final int end, final int startOffset, final int type) {
|
||||
super(line, beg,end, startOffset, type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Paints this token, using special symbols for whitespace characters.
|
||||
*
|
||||
* @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
|
||||
* 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 final 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.getBackgroundForToken(this);
|
||||
g.setFont(host.getFontForTokenType(type));
|
||||
FontMetrics fm = host.getFontMetricsForTokenType(type);
|
||||
|
||||
int ascent = fm.getAscent();
|
||||
int height = fm.getHeight();
|
||||
|
||||
for (int i=textOffset; i<end; i++) {
|
||||
|
||||
switch (text[i]) {
|
||||
|
||||
case '\t':
|
||||
|
||||
// Fill in background.
|
||||
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
|
||||
float nextNextX = e.nextTabStop(nextX, 0);
|
||||
if (bg!=null) {
|
||||
paintBackground(x,y, nextNextX-x,height, g,
|
||||
ascent, host, bg);
|
||||
}
|
||||
g.setColor(fg);
|
||||
|
||||
// Paint chars cached before the tab.
|
||||
if (flushLen > 0) {
|
||||
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
|
||||
flushLen = 0;
|
||||
}
|
||||
flushIndex = i + 1;
|
||||
|
||||
// Draw an arrow representing the tab.
|
||||
int halfHeight = height / 2;
|
||||
int quarterHeight = halfHeight / 2;
|
||||
int ymid = (int)y - ascent + halfHeight;
|
||||
g.drawLine((int)nextX,ymid, (int)nextNextX,ymid);
|
||||
g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid-quarterHeight);
|
||||
g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid+quarterHeight);
|
||||
|
||||
x = nextNextX;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
|
||||
// NOTE: There is a little bit of a "fudge factor"
|
||||
// here when "smooth text" is enabled, as "width"
|
||||
// below may well not be the width given to the space
|
||||
// by fm.charsWidth() (it depends on how it places the
|
||||
// space with respect to the preceding character).
|
||||
// But, we assume the approximation is close enough for
|
||||
// our drawing a dot for the space.
|
||||
|
||||
// "flushLen+1" ensures text is aligned correctly (or,
|
||||
// aligned the same as in getWidth()).
|
||||
nextX = x+fm.charsWidth(text, flushIndex,flushLen+1);
|
||||
int width = fm.charWidth(' ');
|
||||
|
||||
// Paint background.
|
||||
if (bg!=null) {
|
||||
paintBackground(x,y, nextX-x,height, g,
|
||||
ascent, host, bg);
|
||||
}
|
||||
g.setColor(fg);
|
||||
|
||||
// Paint chars before space.
|
||||
if (flushLen>0) {
|
||||
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
|
||||
flushLen = 0;
|
||||
}
|
||||
|
||||
// Paint a dot representing the space.
|
||||
dotRect.x = nextX - width/2.0f; // "2.0f" for FindBugs
|
||||
dotRect.y = y - ascent + height/2.0f; // Ditto
|
||||
g.fill(dotRect);
|
||||
flushIndex = i + 1;
|
||||
x = nextX;
|
||||
break;
|
||||
|
||||
|
||||
case '\f':
|
||||
// ???
|
||||
// fall-through for now.
|
||||
|
||||
default:
|
||||
flushLen += 1;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
|
||||
|
||||
if (flushLen>0 && nextX>=clipStart) {
|
||||
if (bg!=null) {
|
||||
paintBackground(x,y, nextX-x,height, g,
|
||||
ascent, 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);
|
||||
}
|
||||
|
||||
// Don't check if it's whitespace - some TokenMakers may return types
|
||||
// other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
|
||||
// This also allows us to paint tab lines for MLC's.
|
||||
if (host.getPaintTabLines() && origX==host.getMargin().left) {// && isWhitespace()) {
|
||||
paintTabLines(origX, (int)y, (int)nextX, g, e, host);
|
||||
}
|
||||
|
||||
return nextX;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 10/28/2004
|
||||
*
|
||||
* VisibleWhitespaceTokenFactory.java - Visible whitespace token factory.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
|
||||
/**
|
||||
* Token factory that generates tokens that display special symbols for the
|
||||
* whitespace characters space and tab.<p>
|
||||
*
|
||||
* NOTE: This class should only be used by {@link TokenMaker}; nobody else
|
||||
* needs it!
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 0.1
|
||||
*/
|
||||
class VisibleWhitespaceTokenFactory extends DefaultTokenFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Cosnstructor.
|
||||
*/
|
||||
public VisibleWhitespaceTokenFactory() {
|
||||
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 VisibleWhitespaceTokenFactory(int size, int increment) {
|
||||
super(size, increment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected Token createInternalUseOnlyToken() {
|
||||
return new VisibleWhitespaceToken();
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,157 +0,0 @@
|
||||
package org.fife.ui.rsyntaxtextarea;
|
||||
|
||||
//import java.awt.Graphics;
|
||||
//import java.awt.Rectangle;
|
||||
//import java.awt.Shape;
|
||||
|
||||
//import javax.swing.text.CompositeView;
|
||||
import javax.swing.text.Element;
|
||||
//import javax.swing.text.View;
|
||||
|
||||
|
||||
/**
|
||||
* Replacement for the old <code>WrappedSyntaxView</code> class, designed to
|
||||
* be faster with large wrapped documents. Heavily based off of
|
||||
* <code>BoxView</code>, but streamlined to only care about the y-axis, and
|
||||
* takes code folding into account.<p>
|
||||
*
|
||||
* This class is not currently used.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public class WrappedSyntaxView2 {//extends CompositeView {
|
||||
|
||||
// private Rectangle tempRect;
|
||||
// private int[] cachedOffsets;
|
||||
// private int[] cachedSpans;
|
||||
// private boolean sizeRequirementsValid;
|
||||
|
||||
|
||||
public WrappedSyntaxView2(Element root) {
|
||||
//super(root);
|
||||
// tempRect = new Rectangle();
|
||||
// cachedOffsets = new int[0];
|
||||
// cachedSpans = new int[0];
|
||||
// sizeRequirementsValid = false;
|
||||
}
|
||||
|
||||
|
||||
// protected void childAllocation(int index, Rectangle alloc) {
|
||||
// alloc.y += getOffset(index);
|
||||
// alloc.height = getHeight(index);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private int getHeight(int childIndex) {
|
||||
// return cachedSpans[childIndex];
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private int getOffset(int childIndex) {
|
||||
// return cachedOffsets[childIndex];
|
||||
// }
|
||||
//
|
||||
//
|
||||
// protected View getViewAtPoint(int x, int y, Rectangle alloc) {
|
||||
// // TODO Auto-generated method stub
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * @param alloc The allocated region; this is the area inside of the insets
|
||||
// * @return Whether the point lies after the region.
|
||||
// */
|
||||
// protected boolean isAfter(int x, int y, Rectangle alloc) {
|
||||
// return y > (alloc.y + alloc.height);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * @param alloc The allocated region; this is the area inside of the insets
|
||||
// * @return Whether the point lies before the region.
|
||||
// */
|
||||
// protected boolean isBefore(int x, int y, Rectangle alloc) {
|
||||
// return y < alloc.y;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public float getPreferredSpan(int axis) {
|
||||
// if (axis==X_AXIS) {
|
||||
// return preferredWidth + getLeftInset() + getRightInset();
|
||||
// }
|
||||
// else {
|
||||
// return preferredHeight + getTopInset() + getBottomInset();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void paint(Graphics g, Shape allocation) {
|
||||
//
|
||||
// Rectangle alloc = (allocation instanceof Rectangle) ?
|
||||
// (Rectangle)allocation : allocation.getBounds();
|
||||
// int n = getViewCount();
|
||||
//
|
||||
// int x = alloc.x + getLeftInset();
|
||||
// int y = alloc.y + getTopInset();
|
||||
// Rectangle clip = g.getClipBounds();
|
||||
// int preferredWidth = (int)getPreferredSpan(X_AXIS);
|
||||
//
|
||||
// for (int i = 0; i < n; i++) {
|
||||
// tempRect.x = x;
|
||||
// tempRect.y = y + getOffset(i);
|
||||
// tempRect.width = preferredWidth;
|
||||
// tempRect.height = getHeight(i);
|
||||
// if (tempRect.intersects(clip)) {
|
||||
// paintChild(g, tempRect, i);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Called when a child view's preferred span changes. This invalidates
|
||||
// * our layout cache and calls the super implementation.
|
||||
// */
|
||||
// public void preferenceChanged(View child, boolean widthPreferenceChanged,
|
||||
// boolean heightPreferenceChanged) {
|
||||
//
|
||||
// if (heightPreferenceChanged) {
|
||||
// sizeRequirementsValid = false;
|
||||
//// majorAllocValid = false;
|
||||
// }
|
||||
//// if (width) {
|
||||
//// minorReqValid = false;
|
||||
//// minorAllocValid = false;
|
||||
//// }
|
||||
//
|
||||
// super.preferenceChanged(child, widthPreferenceChanged, heightPreferenceChanged);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public void replace(int index, int length, View[] elems) {
|
||||
//
|
||||
// super.replace(index, length, elems);
|
||||
//
|
||||
// // Invalidate cache
|
||||
// int insertCount = elems==null ? 0 : elems.length;
|
||||
// cachedOffsets = updateLayoutArray(cachedOffsets, index, insertCount);
|
||||
// majorReqValid = false;
|
||||
// majorAllocValid = false;
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
|
||||
// int n = getViewCount(); // Called after super.replace() so this is accurate
|
||||
// int[] newArray = new int[n];
|
||||
// System.arraycopy(oldArray, 0, newArray, 0, offset);
|
||||
// System.arraycopy(oldArray, offset,
|
||||
// newArray, offset + nInserted, n - nInserted - offset);
|
||||
// return newArray;
|
||||
// }
|
||||
//
|
||||
|
||||
}
|
@ -1,344 +0,0 @@
|
||||
/*
|
||||
* 07/29/2009
|
||||
*
|
||||
* FocusableTip.java - A focusable tool tip, like those in Eclipse.
|
||||
*
|
||||
* This library is distributed under a modified BSD license. See the included
|
||||
* RSyntaxTextArea.License.txt file for details.
|
||||
*/
|
||||
package org.fife.ui.rsyntaxtextarea.focusabletip;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.ComponentOrientation;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.CaretEvent;
|
||||
import javax.swing.event.CaretListener;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import javax.swing.event.MouseInputAdapter;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
|
||||
|
||||
|
||||
/**
|
||||
* A focusable tool tip, similar to those found in Eclipse. The user
|
||||
* can click in the tip and it becomes a "real," resizable window.
|
||||
*
|
||||
* @author Robert Futrell
|
||||
* @version 1.0
|
||||
*/
|
||||
public class FocusableTip {
|
||||
|
||||
private JTextArea textArea;
|
||||
private TipWindow tipWindow;
|
||||
private URL imageBase;
|
||||
private TextAreaListener textAreaListener;
|
||||
private HyperlinkListener hyperlinkListener;
|
||||
private String lastText;
|
||||
|
||||
/**
|
||||
* The screen bounds in which the mouse has to stay for the currently
|
||||
* displayed tip to stay visible.
|
||||
*/
|
||||
private Rectangle tipVisibleBounds;
|
||||
|
||||
/**
|
||||
* Margin from mouse cursor at which to draw focusable tip.
|
||||
*/
|
||||
private static final int X_MARGIN = 18;
|
||||
|
||||
/**
|
||||
* Margin from mouse cursor at which to draw focusable tip.
|
||||
*/
|
||||
private static final int Y_MARGIN = 12;
|
||||
|
||||
private static final String MSG =
|
||||
"org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip";
|
||||
private static final ResourceBundle msg = ResourceBundle.getBundle(MSG);
|
||||
|
||||
|
||||
public FocusableTip(JTextArea textArea, HyperlinkListener listener) {
|
||||
setTextArea(textArea);
|
||||
this.hyperlinkListener = listener;
|
||||
textAreaListener = new TextAreaListener();
|
||||
tipVisibleBounds = new Rectangle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the bounds in which the user can move the mouse without the
|
||||
* tip window disappearing.
|
||||
*/
|
||||
private void computeTipVisibleBounds() {
|
||||
// Compute area that the mouse can move in without hiding the
|
||||
// tip window. Note that Java 1.4 can only detect mouse events
|
||||
// in Java windows, not globally.
|
||||
Rectangle r = tipWindow.getBounds();
|
||||
Point p = r.getLocation();
|
||||
SwingUtilities.convertPointFromScreen(p, textArea);
|
||||
r.setLocation(p);
|
||||
tipVisibleBounds.setBounds(r.x,r.y-15, r.width,r.height+15*2);
|
||||
}
|
||||
|
||||
|
||||
private void createAndShowTipWindow(final MouseEvent e, final String text) {
|
||||
|
||||
Window owner = SwingUtilities.getWindowAncestor(textArea);
|
||||
tipWindow = new TipWindow(owner, this, text);
|
||||
tipWindow.setHyperlinkListener(hyperlinkListener);
|
||||
|
||||
// Give apps a chance to decorate us with drop shadows, etc.
|
||||
PopupWindowDecorator decorator = PopupWindowDecorator.get();
|
||||
if (decorator!=null) {
|
||||
decorator.decorate(tipWindow);
|
||||
}
|
||||
|
||||
// TODO: Position tip window better (handle RTL, edges of screen, etc.).
|
||||
// Wrap in an invokeLater() to work around a JEditorPane issue where it
|
||||
// doesn't return its proper preferred size until after it is displayed.
|
||||
// See http://forums.sun.com/thread.jspa?forumID=57&threadID=574810
|
||||
// for a discussion.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
|
||||
// If a new FocusableTip is requested while another one is
|
||||
// *focused* and visible, the focused tip (i.e. "tipWindow")
|
||||
// will be disposed of. If this Runnable is run after the
|
||||
// dispose(), tipWindow will be null. All of this is done on
|
||||
// the EDT so no synchronization should be necessary.
|
||||
if (tipWindow==null) {
|
||||
return;
|
||||
}
|
||||
|
||||
tipWindow.fixSize();
|
||||
ComponentOrientation o = textArea.getComponentOrientation();
|
||||
|
||||
Point p = e.getPoint();
|
||||
SwingUtilities.convertPointToScreen(p, textArea);
|
||||
|
||||
// Ensure tool tip is in the window bounds.
|
||||
// Multi-monitor support - make sure the completion window (and
|
||||
// description window, if applicable) both fit in the same
|
||||
// window in a multi-monitor environment. To do this, we decide
|
||||
// which monitor the rectangle "p" is in, and use that one.
|
||||
Rectangle sb = TipUtil.getScreenBoundsForPoint(p.x, p.y);
|
||||
//Dimension ss = tipWindow.getToolkit().getScreenSize();
|
||||
|
||||
// Try putting our stuff "below" the mouse first.
|
||||
int y = p.y + Y_MARGIN;
|
||||
if (y+tipWindow.getHeight()>=sb.y+sb.height) {
|
||||
y = p.y - Y_MARGIN - tipWindow.getHeight();
|
||||
}
|
||||
|
||||
// Get x-coordinate of completions. Try to align left edge
|
||||
// with the mouse first (with a slight margin).
|
||||
int x = p.x - X_MARGIN; // ltr
|
||||
if (!o.isLeftToRight()) {
|
||||
x = p.x - tipWindow.getWidth() + X_MARGIN;
|
||||
}
|
||||
if (x<sb.x) {
|
||||
x = sb.x;
|
||||
}
|
||||
else if (x+tipWindow.getWidth()>sb.x+sb.width) { // completions don't fit
|
||||
x = sb.x + sb.width - tipWindow.getWidth();
|
||||
}
|
||||
|
||||
tipWindow.setLocation(x, y);
|
||||
tipWindow.setVisible(true);
|
||||
|
||||
computeTipVisibleBounds(); // Do after tip is visible
|
||||
textAreaListener.install(textArea);
|
||||
lastText = text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base URL to use when loading images in this focusable tip.
|
||||
*
|
||||
* @return The base URL to use.
|
||||
* @see #setImageBase(URL)
|
||||
*/
|
||||
public URL getImageBase() {
|
||||
return imageBase;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns localized text for the given key.
|
||||
*
|
||||
* @param key The key into the resource bundle.
|
||||
* @return The localized text.
|
||||
*/
|
||||
static String getString(String key) {
|
||||
return msg.getString(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disposes of the focusable tip currently displayed, if any.
|
||||
*/
|
||||
public void possiblyDisposeOfTipWindow() {
|
||||
if (tipWindow != null) {
|
||||
tipWindow.dispose();
|
||||
tipWindow = null;
|
||||
textAreaListener.uninstall();
|
||||
tipVisibleBounds.setBounds(-1, -1, 0, 0);
|
||||
lastText = null;
|
||||
textArea.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void removeListeners() {
|
||||
//System.out.println("DEBUG: Removing text area listeners");
|
||||
textAreaListener.uninstall();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the base URL to use when loading images in this focusable tip.
|
||||
*
|
||||
* @param url The base URL to use.
|
||||
* @see #getImageBase()
|
||||
*/
|
||||
public void setImageBase(URL url) {
|
||||
imageBase = url;
|
||||
}
|
||||
|
||||
|
||||
private void setTextArea(JTextArea textArea) {
|
||||
this.textArea = textArea;
|
||||
// Is okay to do multiple times.
|
||||
ToolTipManager.sharedInstance().registerComponent(textArea);
|
||||
}
|
||||
|
||||
|
||||
public void toolTipRequested(MouseEvent e, String text) {
|
||||
|
||||
if (text==null || text.length()==0) {
|
||||
possiblyDisposeOfTipWindow();
|
||||
lastText = text;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastText==null || text.length()!=lastText.length()
|
||||
|| !text.equals(lastText)) {
|
||||
possiblyDisposeOfTipWindow();
|
||||
createAndShowTipWindow(e, text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class TextAreaListener extends MouseInputAdapter implements
|
||||
CaretListener, ComponentListener, FocusListener, KeyListener {
|
||||
|
||||
public void caretUpdate(CaretEvent e) {
|
||||
Object source = e.getSource();
|
||||
if (source == textArea) {
|
||||
possiblyDisposeOfTipWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
handleComponentEvent(e);
|
||||
}
|
||||
|
||||
public void componentMoved(ComponentEvent e) {
|
||||
handleComponentEvent(e);
|
||||
}
|
||||
|
||||
public void componentResized(ComponentEvent e) {
|
||||
handleComponentEvent(e);
|
||||
}
|
||||
|
||||
public void componentShown(ComponentEvent e) {
|
||||
handleComponentEvent(e);
|
||||
}
|
||||
|
||||
public void focusGained(FocusEvent e) {
|
||||
}
|
||||
|
||||
public void focusLost(FocusEvent e) {
|
||||
// Only dispose of tip if it wasn't the TipWindow that was clicked
|
||||
// "c" can be null, at least on OS X, so we must check that before
|
||||
// calling SwingUtilities.getWindowAncestor() to guard against an
|
||||
// NPE.
|
||||
Component c = e.getOppositeComponent();
|
||||
boolean tipClicked = (c instanceof TipWindow) ||
|
||||
(c!=null &&
|
||||
SwingUtilities.getWindowAncestor(c) instanceof TipWindow);
|
||||
if (!tipClicked) {
|
||||
possiblyDisposeOfTipWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleComponentEvent(ComponentEvent e) {
|
||||
possiblyDisposeOfTipWindow();
|
||||
}
|
||||
|
||||
public void install(JTextArea textArea) {
|
||||
textArea.addCaretListener(this);
|
||||
textArea.addComponentListener(this);
|
||||
textArea.addFocusListener(this);
|
||||
textArea.addKeyListener(this);
|
||||
textArea.addMouseListener(this);
|
||||
textArea.addMouseMotionListener(this);
|
||||
}
|
||||
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode()==KeyEvent.VK_ESCAPE) {
|
||||
possiblyDisposeOfTipWindow();
|
||||
}
|
||||
else if (e.getKeyCode()==KeyEvent.VK_F2) {
|
||||
if (tipWindow!=null && !tipWindow.getFocusableWindowState()) {
|
||||
tipWindow.actionPerformed(null);
|
||||
e.consume(); // Don't do bookmarking stuff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent e) {
|
||||
// possiblyDisposeOfTipWindow();
|
||||
}
|
||||
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
if (tipVisibleBounds==null ||
|
||||
!tipVisibleBounds.contains(e.getPoint())) {
|
||||
possiblyDisposeOfTipWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public void uninstall() {
|
||||
textArea.removeCaretListener(this);
|
||||
textArea.removeComponentListener(this);
|
||||
textArea.removeFocusListener(this);
|
||||
textArea.removeKeyListener(this);
|
||||
textArea.removeMouseListener(this);
|
||||
textArea.removeMouseMotionListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
FocusHotkey=Press 'F2' for focus
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user