Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Fix several warnings in source and build file

AuthorDave Jarvis <email>
Date2020-05-15 18:41:13 GMT-0700
Commitc63db64eb5d5448d97bdba4b7a8f121fca809eda
Parent3aefdc0
.idea/compiler.xml
<project version="4">
<component name="CompilerConfiguration">
- <bytecodeTargetLevel target="1.8" />
+ <bytecodeTargetLevel target="11" />
</component>
</project>
.idea/misc.xml
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
- <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="JDK1.8" project-jdk-type="JavaSDK">
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="JDK1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
.idea/uiDesigner.xml
-
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Palette2">
+ <group name="Swing">
+ <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+ </item>
+ <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+ <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+ <initial-values>
+ <property name="text" value="Button" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="RadioButton" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="CheckBox" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="Label" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+ <preferred-size width="-1" height="20" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+ </item>
+ </group>
+ </component>
+</project>
.idea/workspace.xml
<component name="ChangeListManager">
<list default="true" id="3dcf7c8f-87b5-4d25-a804-39da40a621b8" name="Default Changelist" comment="">
- <change beforePath="$PROJECT_DIR$/.idea/jarRepositories.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/jarRepositories.xml" afterDir="false" />
+ <change afterPath="$PROJECT_DIR$/.idea/uiDesigner.xml" afterDir="false" />
+ <change afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/MainFx.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/.idea/compiler.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/compiler.xml" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/libs/renjin-script-engine-0.9.2726-jar-with-dependencies.jar" beforeDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/MainWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/MainWindow.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/editors/EditorPane.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/editors/EditorPane.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/editors/markdown/LinkVisitor.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/editors/markdown/LinkVisitor.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/FileEditorTab.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/FileEditorTab.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/Main.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/Main.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/controls/WebHyperlink.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/controls/WebHyperlink.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/editors/markdown/MarkdownEditorPane.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/editors/markdown/MarkdownEditorPane.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/processors/MarkdownProcessor.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/processors/MarkdownProcessor.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/processors/MarkdownCaretInsertionProcessor.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/processors/MarkdownCaretInsertionProcessor.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/java/com/scrivenvar/processors/XMLProcessor.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/scrivenvar/processors/XMLProcessor.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<activation />
</task>
- <projects_view />
+ <projects_view>
+ <tree_state>
+ <expand>
+ <path>
+ <item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
+ <item name="scrivenvar" type="f1a62948:ProjectNode" />
+ </path>
+ </expand>
+ <select />
+ </tree_state>
+ </projects_view>
</state>
</system>
+ </component>
+ <component name="FileTemplateManagerImpl">
+ <option name="RECENT_TEMPLATES">
+ <list>
+ <option value="Class" />
+ </list>
+ </option>
</component>
<component name="Git.Settings">
<property name="com.android.tools.idea.instantapp.provision.ProvisionBeforeRunTaskProvider.myTimeStamp" value="1541653415064" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
- <property name="settings.editor.selected.configurable" value="preferences.sourceCode.Java" />
+ <property name="settings.editor.selected.configurable" value="reference.settingsdialog.project.gradle" />
</component>
<component name="RunManager">
</component>
<component name="WindowStateProjectService">
- <state width="1573" height="321" key="GridCell.Tab.0.bottom" timestamp="1589575627707">
+ <state width="1573" height="321" key="GridCell.Tab.0.bottom" timestamp="1589593207664">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1573" height="321" key="GridCell.Tab.0.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1589575627707" />
- <state width="1573" height="321" key="GridCell.Tab.0.center" timestamp="1589575627707">
+ <state width="1573" height="321" key="GridCell.Tab.0.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1589593207664" />
+ <state width="1573" height="321" key="GridCell.Tab.0.center" timestamp="1589593207663">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1573" height="321" key="GridCell.Tab.0.center/0.28.2560.1529@0.28.2560.1529" timestamp="1589575627707" />
- <state width="1573" height="321" key="GridCell.Tab.0.left" timestamp="1589575627706">
+ <state width="1573" height="321" key="GridCell.Tab.0.center/0.28.2560.1529@0.28.2560.1529" timestamp="1589593207663" />
+ <state width="1573" height="321" key="GridCell.Tab.0.left" timestamp="1589593207663">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1573" height="321" key="GridCell.Tab.0.left/0.28.2560.1529@0.28.2560.1529" timestamp="1589575627706" />
- <state width="1573" height="321" key="GridCell.Tab.0.right" timestamp="1589575627707">
+ <state width="1573" height="321" key="GridCell.Tab.0.left/0.28.2560.1529@0.28.2560.1529" timestamp="1589593207663" />
+ <state width="1573" height="321" key="GridCell.Tab.0.right" timestamp="1589593207664">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1573" height="321" key="GridCell.Tab.0.right/0.28.2560.1529@0.28.2560.1529" timestamp="1589575627707" />
- <state x="490" y="327" width="672" height="678" key="search.everywhere.popup" timestamp="1589573707618">
+ <state width="1573" height="321" key="GridCell.Tab.0.right/0.28.2560.1529@0.28.2560.1529" timestamp="1589593207664" />
+ <state x="324" y="288" key="SettingsEditor" timestamp="1589576619807">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state x="490" y="327" width="672" height="678" key="search.everywhere.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1589573707618" />
+ <state x="324" y="288" key="SettingsEditor/0.28.2560.1529@0.28.2560.1529" timestamp="1589576619807" />
+ <state x="1071" y="397" width="1417" height="979" key="com.intellij.history.integration.ui.views.FileHistoryDialog" timestamp="1589582662731">
+ <screen x="0" y="28" width="2560" height="1529" />
+ </state>
+ <state x="1071" y="397" width="1417" height="979" key="com.intellij.history.integration.ui.views.FileHistoryDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1589582662731" />
+ <state x="490" y="304" key="run.anything.popup" timestamp="1589592158683">
+ <screen x="0" y="28" width="2560" height="1529" />
+ </state>
+ <state x="490" y="304" key="run.anything.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1589592158683" />
+ <state x="490" y="327" width="672" height="678" key="search.everywhere.popup" timestamp="1589586194905">
+ <screen x="0" y="28" width="2560" height="1529" />
+ </state>
+ <state x="490" y="327" width="672" height="678" key="search.everywhere.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1589586194905" />
</component>
<component name="masterDetails">
BUILD.md
Build the application as follows:
- gradle build
+ gradle jar
The application is built.
build.gradle
plugins {
id 'application'
- id 'java'
- id 'java-library-distribution'
id 'org.openjfx.javafxplugin' version '0.0.8'
-}
-
-javafx {
- version = "14"
- modules = [ 'javafx.controls', 'javafx.web' ]
}
url "https://nexus.bedatadriven.com/content/groups/public"
}
-}
-
-compileJava {
- options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
implementation 'com.vladsch.flexmark:flexmark-ext-superscript:0.61.28'
implementation 'com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.61.28'
- compile 'com.fasterxml.jackson.core:jackson-core:2.9.3'
- compile 'com.fasterxml.jackson.core:jackson-databind:2.9.3'
- compile 'com.fasterxml.jackson.core:jackson-annotations:2.9.3'
- compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.9.3'
- compile 'org.ahocorasick:ahocorasick:0.4.0'
- compile 'org.yaml:snakeyaml:1.19'
- compile 'com.ximpleware:vtd-xml:2.13.4'
- compile 'net.sf.saxon:Saxon-HE:9.8.0-7'
+ implementation 'com.fasterxml.jackson.core:jackson-core:2.11.0'
+ implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0'
+ implementation 'com.fasterxml.jackson.core:jackson-annotations:2.11.0'
+ implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.0'
+ implementation 'org.ahocorasick:ahocorasick:0.4.0'
+ implementation 'org.yaml:snakeyaml:1.26'
+ implementation 'com.ximpleware:vtd-xml:2.13.4'
+ implementation 'net.sf.saxon:Saxon-HE:10.1'
implementation 'org.apache.commons:commons-configuration2:2.7'
implementation 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
implementation 'de.jensd:fontawesomefx-commons:11.0'
implementation 'de.jensd:fontawesomefx-fontawesome:4.7.0-11'
implementation "org.renjin:renjin-script-engine:0.9.2726"
+
+ def os = ['win', 'linux', 'mac']
+ def fx = ['controls', 'graphics', 'web', 'fxml']
+
+ fx.each { fxitem ->
+ os.each { ositem ->
+ runtimeOnly "org.openjfx:javafx-${fxitem}:${javafx.version}:${ositem}"
+ }
+ }
}
-version = '1.3.8'
+javafx {
+ version = "14"
+ modules = ['javafx.controls', 'javafx.graphics', 'javafx.web']
+}
+
+compileJava {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+}
+
+version = '1.4.0'
applicationName = 'scrivenvar'
-mainClassName = 'com.scrivenvar.Main'
-sourceCompatibility = JavaVersion.VERSION_1_8
+mainClassName = 'com.scrivenvar.MainFx'
+def launcherClassName = 'com.scrivenvar.Main'
+sourceCompatibility = JavaVersion.VERSION_11
jar {
- baseName = applicationName
- archiveName = "${applicationName}.jar"
-
- doFirst {
- from {
- configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
+ manifest {
+ attributes 'Main-Class': launcherClassName
+ }
+
+ from {
+ (configurations.runtimeClasspath.findAll { !it.path.endsWith(".pom") }).collect {
+ it.isDirectory() ? it : zipTree(it)
}
}
- // Remove digital signature files to ensure an executable JAR file.
- exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'
+ archiveFileName = 'scrivenvar.jar'
- manifest {
- attributes 'Main-Class': mainClassName
- attributes 'Class-Path': configurations.compile.collect {
- 'libs/' + it.getName()
- }.join(' ')
- }
+ exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA'
}
distributions {
main {
- baseName = applicationName
+ distributionBaseName = applicationName
contents {
from { ['LICENSE.md', 'README.md'] }
- into( 'images' ) {
+ into('images') {
from { 'images' }
}
src/main/java/com/scrivenvar/FileEditorTab.java
import com.scrivenvar.service.events.Notification;
import com.scrivenvar.service.events.Notifier;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import static java.util.Locale.ENGLISH;
-import java.util.function.Consumer;
-import javafx.application.Platform;
-import javafx.beans.binding.Bindings;
-import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.ReadOnlyBooleanProperty;
-import javafx.beans.property.ReadOnlyBooleanWrapper;
-import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.value.ChangeListener;
-import javafx.beans.value.ObservableValue;
-import javafx.event.Event;
-import javafx.scene.Node;
-import javafx.scene.Scene;
-import javafx.scene.control.Tab;
-import javafx.scene.control.Tooltip;
-import javafx.scene.input.InputEvent;
-import javafx.scene.text.Text;
-import javafx.stage.Window;
-import org.fxmisc.richtext.StyleClassedTextArea;
-import static org.fxmisc.richtext.model.TwoDimensional.Bias.Forward;
-import org.fxmisc.richtext.model.TwoDimensional.Position;
-import org.fxmisc.undo.UndoManager;
-import org.fxmisc.wellbehaved.event.EventPattern;
-import org.fxmisc.wellbehaved.event.InputMap;
-import org.mozilla.universalchardet.UniversalDetector;
-
-/**
- * Editor for a single file.
- *
- * @author Karl Tauber and White Magic Software, Ltd.
- */
-public final class FileEditorTab extends Tab {
-
- /**
- *
- */
- private final Notifier alertService = Services.load( Notifier.class );
- private EditorPane editorPane;
-
- /**
- * Character encoding used by the file (or default encoding if none found).
- */
- private Charset encoding;
-
- private final ReadOnlyBooleanWrapper modified = new ReadOnlyBooleanWrapper();
- private final BooleanProperty canUndo = new SimpleBooleanProperty();
- private final BooleanProperty canRedo = new SimpleBooleanProperty();
-
- private Path path;
-
- public FileEditorTab( final Path path ) {
- setPath( path );
-
- this.modified.addListener( (observable, oldPath, newPath) -> updateTab() );
-
- setOnSelectionChanged( e -> {
- if( isSelected() ) {
- Platform.runLater( () -> activated() );
- }
- } );
- }
-
- private void updateTab() {
- setText( getTabTitle() );
- setGraphic( getModifiedMark() );
- setTooltip( getTabTooltip() );
- }
-
- /**
- * Returns the base filename (without the directory names).
- *
- * @return The untitled text if the path hasn't been set.
- */
- private String getTabTitle() {
- final Path filePath = getPath();
-
- return (filePath == null)
- ? Messages.get( "FileEditor.untitled" )
- : filePath.getFileName().toString();
- }
-
- /**
- * Returns the full filename represented by the path.
- *
- * @return The untitled text if the path hasn't been set.
- */
- private Tooltip getTabTooltip() {
- final Path filePath = getPath();
- return new Tooltip( filePath == null ? "" : filePath.toString() );
- }
-
- /**
- * Returns a marker to indicate whether the file has been modified.
- *
- * @return "*" when the file has changed; otherwise null.
- */
- private Text getModifiedMark() {
- return isModified() ? new Text( "*" ) : null;
- }
-
- /**
- * Called when the user switches tab.
- */
- private void activated() {
- // Tab is closed or no longer active.
- if( getTabPane() == null || !isSelected() ) {
- return;
- }
-
- // Switch to the tab without loading if the contents are already in memory.
- if( getContent() != null ) {
- getEditorPane().requestFocus();
- return;
- }
-
- // Load the text and update the preview before the undo manager.
- load();
-
- // Track undo requests -- can only be called *after* load.
- initUndoManager();
- initLayout();
- initFocus();
- }
-
- private void initLayout() {
- setContent( getScrollPane() );
- }
-
- private Node getScrollPane() {
- return getEditorPane().getScrollPane();
- }
-
- private void initFocus() {
- getEditorPane().requestFocus();
- }
-
- private void initUndoManager() {
- final UndoManager undoManager = getUndoManager();
-
- // Clear undo history after first load.
- undoManager.forgetHistory();
-
- // Bind the editor undo manager to the properties.
- modified.bind( Bindings.not( undoManager.atMarkedPositionProperty() ) );
- canUndo.bind( undoManager.undoAvailableProperty() );
- canRedo.bind( undoManager.redoAvailableProperty() );
- }
-
- /**
- * Searches from the caret position forward for the given string.
- *
- * @param needle The text string to match.
- */
- public void searchNext( final String needle ) {
- final String haystack = getEditorText();
- int index = haystack.indexOf( needle, getCaretPosition() );
-
- // Wrap around.
- if( index == -1 ) {
- index = haystack.indexOf( needle, 0 );
- }
-
- if( index >= 0 ) {
- setCaretPosition( index );
- getEditor().selectRange( index, index + needle.length() );
- }
- }
-
- /**
- * Returns the index into the text where the caret blinks happily away.
- *
- * @return A number from 0 to the editor's document text length.
- */
- public int getCaretPosition() {
- return getEditor().getCaretPosition();
- }
-
- /**
- * Moves the caret to a given offset.
- *
- * @param offset The new caret offset.
- */
- private void setCaretPosition( final int offset ) {
- getEditor().moveTo( offset );
- getEditor().requestFollowCaret();
- }
-
- /**
- * Returns the caret's current row and column position.
- *
- * @return The caret's offset into the document.
- */
- public Position getCaretOffset() {
- return getEditor().offsetToPosition( getCaretPosition(), Forward );
- }
-
- /**
- * Allows observers to synchronize caret position changes.
- *
- * @return An observable caret property value.
- */
- public final ObservableValue<Integer> caretPositionProperty() {
- return getEditor().caretPositionProperty();
- }
-
- /**
- * Returns the text area associated with this tab.
- *
- * @return A text editor.
- */
- private StyleClassedTextArea getEditor() {
- return getEditorPane().getEditor();
- }
-
- /**
- * Returns true if the given path exactly matches this tab's path.
- *
- * @param check The path to compare against.
- *
- * @return true The paths are the same.
- */
- public boolean isPath( final Path check ) {
- final Path filePath = getPath();
-
- return filePath == null ? false : filePath.equals( check );
- }
-
- /**
- * Reads the entire file contents from the path associated with this tab.
- */
- private void load() {
- final Path filePath = getPath();
-
- if( filePath != null ) {
- try {
- getEditorPane().setText( asString( Files.readAllBytes( filePath ) ) );
- getEditorPane().scrollToTop();
- } catch( final IOException ex ) {
- getNotifyService().notify( ex );
- }
- }
- }
-
- /**
- * Saves the entire file contents from the path associated with this tab.
- *
- * @return true The file has been saved.
- */
- public boolean save() {
- try {
- final EditorPane editor = getEditorPane();
- Files.write( getPath(), asBytes( editor.getText() ) );
- editor.getUndoManager().mark();
- return true;
- } catch( final IOException ex ) {
- return alert(
- "FileEditor.saveFailed.title", "FileEditor.saveFailed.message", ex
- );
- }
- }
-
- /**
- * Creates an alert dialog and waits for it to close.
- *
- * @param titleKey Resource bundle key for the alert dialog title.
- * @param messageKey Resource bundle key for the alert dialog message.
- * @param e The unexpected happening.
- *
- * @return false
- */
- private boolean alert(
- final String titleKey, final String messageKey, final Exception e ) {
- final Notifier service = getNotifyService();
- final Path filePath = getPath();
-
- final Notification message = service.createNotification(
- Messages.get( titleKey ),
- Messages.get( messageKey ),
- filePath == null ? "" : filePath,
- e.getMessage()
- );
-
- try {
- service.createError( getWindow(), message ).showAndWait();
- } catch( final Exception ex ) {
- getNotifyService().notify( ex );
- }
-
- return false;
- }
-
- private Window getWindow() {
- final Scene scene = getEditorPane().getScene();
-
- if( scene == null ) {
- throw new UnsupportedOperationException( "" );
- }
-
- return scene.getWindow();
- }
-
- /**
- * Returns a best guess at the file encoding. If the encoding could not be
- * detected, this will return the default charset for the JVM.
- *
- * @param bytes The bytes to perform character encoding detection.
- *
- * @return The character encoding.
- */
- private Charset detectEncoding( final byte[] bytes ) {
- final UniversalDetector detector = new UniversalDetector( null );
- detector.handleData( bytes, 0, bytes.length );
- detector.dataEnd();
-
- final String charset = detector.getDetectedCharset();
- final Charset charEncoding = charset == null
- ? Charset.defaultCharset()
- : Charset.forName( charset.toUpperCase( ENGLISH ) );
-
- detector.reset();
-
- return charEncoding;
- }
-
- /**
- * Converts the given string to an array of bytes using the encoding that was
- * originally detected (if any) and associated with this file.
- *
- * @param text The text to convert into the original file encoding.
- *
- * @return A series of bytes ready for writing to a file.
- */
- private byte[] asBytes( final String text ) {
- return text.getBytes( getEncoding() );
- }
-
- /**
- * Converts the given bytes into a Java String. This will call setEncoding
- * with the encoding detected by the CharsetDetector.
- *
- * @param text The text of unknown character encoding.
- *
- * @return The text, in its auto-detected encoding, as a String.
- */
- private String asString( final byte[] text ) {
- setEncoding( detectEncoding( text ) );
- return new String( text, getEncoding() );
- }
-
- public Path getPath() {
- return this.path;
- }
-
- public void setPath( final Path path ) {
- this.path = path;
-
- updateTab();
- }
-
- /**
- * Answers whether this tab has an initialized path reference.
- *
- * @return false This tab has no path.
- */
- public boolean isFileOpen() {
- return this.path != null;
- }
-
- public boolean isModified() {
- return this.modified.get();
- }
-
- ReadOnlyBooleanProperty modifiedProperty() {
- return this.modified.getReadOnlyProperty();
- }
-
- BooleanProperty canUndoProperty() {
- return this.canUndo;
- }
-
- BooleanProperty canRedoProperty() {
- return this.canRedo;
- }
-
- private UndoManager getUndoManager() {
- return getEditorPane().getUndoManager();
- }
-
- /**
- * Forwards the request to the editor pane.
- *
- * @param <T> The type of event listener to add.
- * @param <U> The type of consumer to add.
- * @param event The event that should trigger updates to the listener.
- * @param consumer The listener to receive update events.
- */
- public <T extends Event, U extends T> void addEventListener(
- final EventPattern<? super T, ? extends U> event,
- final Consumer<? super U> consumer ) {
- getEditorPane().addKeyboardListener( event, consumer );
- }
-
- /**
- * Forwards to the editor pane's listeners for keyboard events.
- *
- * @param map The new input map to replace the existing keyboard listener.
- */
- public void addEventListener( final InputMap<InputEvent> map ) {
- getEditorPane().addEventListener( map );
- }
-
- /**
- * Forwards to the editor pane's listeners for keyboard events.
- *
- * @param map The existing input map to remove from the keyboard listeners.
- */
- public void removeEventListener( final InputMap<InputEvent> map ) {
- getEditorPane().removeEventListener( map );
- }
-
- /**
- * Forwards to the editor pane's listeners for text change events.
- *
- * @param listener The listener to notify when the text changes.
- */
- public void addTextChangeListener( final ChangeListener<String> listener ) {
- getEditorPane().addTextChangeListener( listener );
- }
-
- /**
- * Forwards to the editor pane's listeners for caret paragraph change events.
- *
- * @param listener The listener to notify when the caret changes paragraphs.
- */
- public void addCaretParagraphListener( final ChangeListener<Integer> listener ) {
- getEditorPane().addCaretParagraphListener( listener );
- }
-
- /**
- * Forwards the request to the editor pane.
- *
- * @return The text to process.
- */
- public String getEditorText() {
- return getEditorPane().getText();
- }
-
- /**
- * Returns the editor pane, or creates one if it doesn't yet exist.
- *
- * @return The editor pane, never null.
- */
- public synchronized EditorPane getEditorPane() {
- if( this.editorPane == null ) {
- this.editorPane = new MarkdownEditorPane();
- }
-
- return this.editorPane;
- }
-
- private Notifier getNotifyService() {
- return this.alertService;
- }
-
- /**
- * Returns the encoding for the file, defaulting to UTF-8 if it hasn't been
- * determined.
- *
- * @return The file encoding or UTF-8 if unknown.
- */
- private Charset getEncoding() {
- if( this.encoding == null ) {
- this.encoding = UTF_8;
- }
-
+import javafx.application.Platform;
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.Event;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Tab;
+import javafx.scene.control.Tooltip;
+import javafx.scene.input.InputEvent;
+import javafx.scene.text.Text;
+import javafx.stage.Window;
+import org.fxmisc.richtext.StyleClassedTextArea;
+import org.fxmisc.richtext.model.TwoDimensional.Position;
+import org.fxmisc.undo.UndoManager;
+import org.fxmisc.wellbehaved.event.EventPattern;
+import org.fxmisc.wellbehaved.event.InputMap;
+import org.mozilla.universalchardet.UniversalDetector;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.function.Consumer;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Locale.ENGLISH;
+import static org.fxmisc.richtext.model.TwoDimensional.Bias.Forward;
+
+/**
+ * Editor for a single file.
+ *
+ * @author Karl Tauber and White Magic Software, Ltd.
+ */
+public final class FileEditorTab extends Tab {
+
+ /**
+ *
+ */
+ private final Notifier alertService = Services.load( Notifier.class );
+ private EditorPane editorPane;
+
+ /**
+ * Character encoding used by the file (or default encoding if none found).
+ */
+ private Charset encoding;
+
+ private final ReadOnlyBooleanWrapper modified = new ReadOnlyBooleanWrapper();
+ private final BooleanProperty canUndo = new SimpleBooleanProperty();
+ private final BooleanProperty canRedo = new SimpleBooleanProperty();
+
+ private Path path;
+
+ public FileEditorTab( final Path path ) {
+ setPath( path );
+
+ this.modified.addListener( ( observable, oldPath, newPath ) -> updateTab() );
+
+ setOnSelectionChanged( e -> {
+ if( isSelected() ) {
+ Platform.runLater( this::activated );
+ }
+ } );
+ }
+
+ private void updateTab() {
+ setText( getTabTitle() );
+ setGraphic( getModifiedMark() );
+ setTooltip( getTabTooltip() );
+ }
+
+ /**
+ * Returns the base filename (without the directory names).
+ *
+ * @return The untitled text if the path hasn't been set.
+ */
+ private String getTabTitle() {
+ final Path filePath = getPath();
+
+ return (filePath == null)
+ ? Messages.get( "FileEditor.untitled" )
+ : filePath.getFileName().toString();
+ }
+
+ /**
+ * Returns the full filename represented by the path.
+ *
+ * @return The untitled text if the path hasn't been set.
+ */
+ private Tooltip getTabTooltip() {
+ final Path filePath = getPath();
+ return new Tooltip( filePath == null ? "" : filePath.toString() );
+ }
+
+ /**
+ * Returns a marker to indicate whether the file has been modified.
+ *
+ * @return "*" when the file has changed; otherwise null.
+ */
+ private Text getModifiedMark() {
+ return isModified() ? new Text( "*" ) : null;
+ }
+
+ /**
+ * Called when the user switches tab.
+ */
+ private void activated() {
+ // Tab is closed or no longer active.
+ if( getTabPane() == null || !isSelected() ) {
+ return;
+ }
+
+ // Switch to the tab without loading if the contents are already in memory.
+ if( getContent() != null ) {
+ getEditorPane().requestFocus();
+ return;
+ }
+
+ // Load the text and update the preview before the undo manager.
+ load();
+
+ // Track undo requests -- can only be called *after* load.
+ initUndoManager();
+ initLayout();
+ initFocus();
+ }
+
+ private void initLayout() {
+ setContent( getScrollPane() );
+ }
+
+ private Node getScrollPane() {
+ return getEditorPane().getScrollPane();
+ }
+
+ private void initFocus() {
+ getEditorPane().requestFocus();
+ }
+
+ private void initUndoManager() {
+ final UndoManager undoManager = getUndoManager();
+
+ // Clear undo history after first load.
+ undoManager.forgetHistory();
+
+ // Bind the editor undo manager to the properties.
+ modified.bind( Bindings.not( undoManager.atMarkedPositionProperty() ) );
+ canUndo.bind( undoManager.undoAvailableProperty() );
+ canRedo.bind( undoManager.redoAvailableProperty() );
+ }
+
+ /**
+ * Searches from the caret position forward for the given string.
+ *
+ * @param needle The text string to match.
+ */
+ public void searchNext( final String needle ) {
+ final String haystack = getEditorText();
+ int index = haystack.indexOf( needle, getCaretPosition() );
+
+ // Wrap around.
+ if( index == -1 ) {
+ index = haystack.indexOf( needle, 0 );
+ }
+
+ if( index >= 0 ) {
+ setCaretPosition( index );
+ getEditor().selectRange( index, index + needle.length() );
+ }
+ }
+
+ /**
+ * Returns the index into the text where the caret blinks happily away.
+ *
+ * @return A number from 0 to the editor's document text length.
+ */
+ public int getCaretPosition() {
+ return getEditor().getCaretPosition();
+ }
+
+ /**
+ * Moves the caret to a given offset.
+ *
+ * @param offset The new caret offset.
+ */
+ private void setCaretPosition( final int offset ) {
+ getEditor().moveTo( offset );
+ getEditor().requestFollowCaret();
+ }
+
+ /**
+ * Returns the caret's current row and column position.
+ *
+ * @return The caret's offset into the document.
+ */
+ public Position getCaretOffset() {
+ return getEditor().offsetToPosition( getCaretPosition(), Forward );
+ }
+
+ /**
+ * Allows observers to synchronize caret position changes.
+ *
+ * @return An observable caret property value.
+ */
+ public final ObservableValue<Integer> caretPositionProperty() {
+ return getEditor().caretPositionProperty();
+ }
+
+ /**
+ * Returns the text area associated with this tab.
+ *
+ * @return A text editor.
+ */
+ private StyleClassedTextArea getEditor() {
+ return getEditorPane().getEditor();
+ }
+
+ /**
+ * Returns true if the given path exactly matches this tab's path.
+ *
+ * @param check The path to compare against.
+ * @return true The paths are the same.
+ */
+ public boolean isPath( final Path check ) {
+ final Path filePath = getPath();
+
+ return filePath != null && filePath.equals( check );
+ }
+
+ /**
+ * Reads the entire file contents from the path associated with this tab.
+ */
+ private void load() {
+ final Path filePath = getPath();
+
+ if( filePath != null ) {
+ try {
+ getEditorPane().setText( asString( Files.readAllBytes( filePath ) ) );
+ getEditorPane().scrollToTop();
+ } catch( final IOException ex ) {
+ getNotifyService().notify( ex );
+ }
+ }
+ }
+
+ /**
+ * Saves the entire file contents from the path associated with this tab.
+ *
+ * @return true The file has been saved.
+ */
+ public boolean save() {
+ try {
+ final EditorPane editor = getEditorPane();
+ Files.write( getPath(), asBytes( editor.getText() ) );
+ editor.getUndoManager().mark();
+ return true;
+ } catch( final IOException ex ) {
+ return alert(
+ "FileEditor.saveFailed.title", "FileEditor.saveFailed.message", ex
+ );
+ }
+ }
+
+ /**
+ * Creates an alert dialog and waits for it to close.
+ *
+ * @param titleKey Resource bundle key for the alert dialog title.
+ * @param messageKey Resource bundle key for the alert dialog message.
+ * @param e The unexpected happening.
+ * @return false
+ */
+ private boolean alert(
+ final String titleKey, final String messageKey, final Exception e ) {
+ final Notifier service = getNotifyService();
+ final Path filePath = getPath();
+
+ final Notification message = service.createNotification(
+ Messages.get( titleKey ),
+ Messages.get( messageKey ),
+ filePath == null ? "" : filePath,
+ e.getMessage()
+ );
+
+ try {
+ service.createError( getWindow(), message ).showAndWait();
+ } catch( final Exception ex ) {
+ getNotifyService().notify( ex );
+ }
+
+ return false;
+ }
+
+ private Window getWindow() {
+ final Scene scene = getEditorPane().getScene();
+
+ if( scene == null ) {
+ throw new UnsupportedOperationException( "" );
+ }
+
+ return scene.getWindow();
+ }
+
+ /**
+ * Returns a best guess at the file encoding. If the encoding could not be
+ * detected, this will return the default charset for the JVM.
+ *
+ * @param bytes The bytes to perform character encoding detection.
+ * @return The character encoding.
+ */
+ private Charset detectEncoding( final byte[] bytes ) {
+ final UniversalDetector detector = new UniversalDetector( null );
+ detector.handleData( bytes, 0, bytes.length );
+ detector.dataEnd();
+
+ final String charset = detector.getDetectedCharset();
+ final Charset charEncoding = charset == null
+ ? Charset.defaultCharset()
+ : Charset.forName( charset.toUpperCase( ENGLISH ) );
+
+ detector.reset();
+
+ return charEncoding;
+ }
+
+ /**
+ * Converts the given string to an array of bytes using the encoding that was
+ * originally detected (if any) and associated with this file.
+ *
+ * @param text The text to convert into the original file encoding.
+ * @return A series of bytes ready for writing to a file.
+ */
+ private byte[] asBytes( final String text ) {
+ return text.getBytes( getEncoding() );
+ }
+
+ /**
+ * Converts the given bytes into a Java String. This will call setEncoding
+ * with the encoding detected by the CharsetDetector.
+ *
+ * @param text The text of unknown character encoding.
+ * @return The text, in its auto-detected encoding, as a String.
+ */
+ private String asString( final byte[] text ) {
+ setEncoding( detectEncoding( text ) );
+ return new String( text, getEncoding() );
+ }
+
+ public Path getPath() {
+ return this.path;
+ }
+
+ public void setPath( final Path path ) {
+ this.path = path;
+
+ updateTab();
+ }
+
+ /**
+ * Answers whether this tab has an initialized path reference.
+ *
+ * @return false This tab has no path.
+ */
+ public boolean isFileOpen() {
+ return this.path != null;
+ }
+
+ public boolean isModified() {
+ return this.modified.get();
+ }
+
+ ReadOnlyBooleanProperty modifiedProperty() {
+ return this.modified.getReadOnlyProperty();
+ }
+
+ BooleanProperty canUndoProperty() {
+ return this.canUndo;
+ }
+
+ BooleanProperty canRedoProperty() {
+ return this.canRedo;
+ }
+
+ private UndoManager getUndoManager() {
+ return getEditorPane().getUndoManager();
+ }
+
+ /**
+ * Forwards the request to the editor pane.
+ *
+ * @param <T> The type of event listener to add.
+ * @param <U> The type of consumer to add.
+ * @param event The event that should trigger updates to the listener.
+ * @param consumer The listener to receive update events.
+ */
+ public <T extends Event, U extends T> void addEventListener(
+ final EventPattern<? super T, ? extends U> event,
+ final Consumer<? super U> consumer ) {
+ getEditorPane().addKeyboardListener( event, consumer );
+ }
+
+ /**
+ * Forwards to the editor pane's listeners for keyboard events.
+ *
+ * @param map The new input map to replace the existing keyboard listener.
+ */
+ public void addEventListener( final InputMap<InputEvent> map ) {
+ getEditorPane().addEventListener( map );
+ }
+
+ /**
+ * Forwards to the editor pane's listeners for keyboard events.
+ *
+ * @param map The existing input map to remove from the keyboard listeners.
+ */
+ public void removeEventListener( final InputMap<InputEvent> map ) {
+ getEditorPane().removeEventListener( map );
+ }
+
+ /**
+ * Forwards to the editor pane's listeners for text change events.
+ *
+ * @param listener The listener to notify when the text changes.
+ */
+ public void addTextChangeListener( final ChangeListener<String> listener ) {
+ getEditorPane().addTextChangeListener( listener );
+ }
+
+ /**
+ * Forwards to the editor pane's listeners for caret paragraph change events.
+ *
+ * @param listener The listener to notify when the caret changes paragraphs.
+ */
+ public void addCaretParagraphListener(
+ final ChangeListener<Integer> listener ) {
+ getEditorPane().addCaretParagraphListener( listener );
+ }
+
+ /**
+ * Forwards the request to the editor pane.
+ *
+ * @return The text to process.
+ */
+ public String getEditorText() {
+ return getEditorPane().getText();
+ }
+
+ /**
+ * Returns the editor pane, or creates one if it doesn't yet exist.
+ *
+ * @return The editor pane, never null.
+ */
+ public synchronized EditorPane getEditorPane() {
+ if( this.editorPane == null ) {
+ this.editorPane = new MarkdownEditorPane();
+ }
+
+ return this.editorPane;
+ }
+
+ private Notifier getNotifyService() {
+ return this.alertService;
+ }
+
+ /**
+ * Returns the encoding for the file, defaulting to UTF-8 if it hasn't been
+ * determined.
+ *
+ * @return The file encoding or UTF-8 if unknown.
+ */
+ private Charset getEncoding() {
+ if( this.encoding == null ) {
+ this.encoding = UTF_8;
+ }
+
return this.encoding;
}
src/main/java/com/scrivenvar/Main.java
/*
- * Copyright 2016 Karl Tauber and White Magic Software, Ltd.
+ * Copyright 2020 White Magic Software, Ltd.
*
* All rights reserved.
*/
package com.scrivenvar;
-
-import static com.scrivenvar.Constants.*;
-import static com.scrivenvar.Messages.get;
-import com.scrivenvar.preferences.FilePreferencesFactory;
-import com.scrivenvar.service.Options;
-import com.scrivenvar.service.Snitch;
-import com.scrivenvar.service.events.Notifier;
-import com.scrivenvar.util.StageState;
-import java.util.logging.LogManager;
-import javafx.application.Application;
-import javafx.scene.Scene;
-import javafx.scene.image.Image;
-import javafx.stage.Stage;
/**
- * Main application entry point. The application allows users to edit Markdown
- * files and see a real-time preview of the edits.
- *
- * @author Karl Tauber and White Magic Software, Ltd.
+ * Delegates to launching the application using the {@link MainFx} class.
*/
-public final class Main extends Application {
-
- private Options options;
- private Snitch snitch;
- private Thread snitchThread;
-
- private static Application app;
- private final MainWindow mainWindow = new MainWindow();
-
+public class Main {
public static void main( final String[] args ) {
- initLogger();
- initPreferences();
- launch( args );
- }
-
- /**
- * Prevents JavaFX from logging to standard error.
- *
- * @see http://stackoverflow.com/a/41476462/59087
- */
- private static void initLogger() {
- LogManager.getLogManager().reset();
- }
-
- /**
- * Sets the factory used for reading user preferences.
- */
- private static void initPreferences() {
- System.setProperty(
- "java.util.prefs.PreferencesFactory",
- FilePreferencesFactory.class.getName()
- );
- }
-
- /**
- * Application entry point.
- *
- * @param stage The primary application stage.
- *
- * @throws Exception Could not read configuration file.
- */
- @Override
- public void start( final Stage stage ) throws Exception {
- initApplication();
- initNotifyService();
- initState( stage );
- initStage( stage );
- initSnitch();
-
- stage.show();
- }
-
- public static void showDocument( final String uri ) {
- getApplication().getHostServices().showDocument( uri );
- }
-
- private void initApplication() {
- Main.app = this;
- }
-
- /**
- * Constructs the notify service and appends the main window to the list of
- * notification observers.
- */
- private void initNotifyService() {
- final Notifier notifier = Services.load( Notifier.class );
- notifier.addObserver( getMainWindow() );
- }
-
- private StageState initState( final Stage stage ) {
- return new StageState( stage, getOptions().getState() );
- }
-
- private void initStage( final Stage stage ) {
- stage.getIcons().addAll(
- createImage( FILE_LOGO_16 ),
- createImage( FILE_LOGO_32 ),
- createImage( FILE_LOGO_128 ),
- createImage( FILE_LOGO_256 ),
- createImage( FILE_LOGO_512 ) );
- stage.setTitle( getApplicationTitle() );
- stage.setScene( getScene() );
- }
-
- /**
- * Watch for file system changes.
- */
- private void initSnitch() {
- setSnitchThread( new Thread( getSnitch() ) );
- getSnitchThread().start();
- }
-
- /**
- * Stops the snitch service, if its running.
- *
- * @throws InterruptedException Couldn't stop the snitch thread.
- */
- @Override
- public void stop() throws InterruptedException {
- getSnitch().stop();
-
- final Thread thread = getSnitchThread();
-
- if( thread != null ) {
- thread.interrupt();
- thread.join();
- }
- }
-
- private synchronized Snitch getSnitch() {
- if( this.snitch == null ) {
- this.snitch = Services.load( Snitch.class );
- }
-
- return this.snitch;
- }
-
- private Thread getSnitchThread() {
- return this.snitchThread;
- }
-
- private void setSnitchThread( final Thread thread ) {
- this.snitchThread = thread;
- }
-
- private synchronized Options getOptions() {
- if( this.options == null ) {
- this.options = Services.load( Options.class );
- }
-
- return this.options;
- }
-
- private Scene getScene() {
- return getMainWindow().getScene();
- }
-
- private MainWindow getMainWindow() {
- return this.mainWindow;
- }
-
- private String getApplicationTitle() {
- return get( "Main.title" );
- }
-
- private static Application getApplication() {
- return Main.app;
- }
-
- private Image createImage( final String filename ) {
- return new Image( filename );
+ MainFx.main( args );
}
}
src/main/java/com/scrivenvar/MainFx.java
+/*
+ * Copyright 2016 Karl Tauber and White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.scrivenvar;
+
+import com.scrivenvar.preferences.FilePreferencesFactory;
+import com.scrivenvar.service.Options;
+import com.scrivenvar.service.Snitch;
+import com.scrivenvar.service.events.Notifier;
+import com.scrivenvar.util.StageState;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.stage.Stage;
+
+import static com.scrivenvar.Constants.*;
+import static com.scrivenvar.Messages.get;
+
+/**
+ * Application entry point. The application allows users to edit Markdown
+ * files and see a real-time preview of the edits.
+ *
+ * @author Karl Tauber and White Magic Software, Ltd.
+ */
+public final class MainFx extends Application {
+
+ private static Application sApplication;
+
+ private Options mOptions;
+ private Snitch mSnitch;
+ private Thread mSnitchThread;
+ private StageState mStageState;
+
+ private final MainWindow mainWindow = new MainWindow();
+
+ public static void main( final String[] args ) {
+ initLogger();
+ initPreferences();
+ launch( args );
+ }
+
+ /**
+ * Prevent
+ * <a href="http://stackoverflow.com/a/41476462/59087">standard error logging</a>.
+ */
+ private static void initLogger() {
+ //LogManager.getLogManager().reset();
+ }
+
+ /**
+ * Sets the factory used for reading user preferences.
+ */
+ private static void initPreferences() {
+ System.setProperty(
+ "java.util.prefs.PreferencesFactory",
+ FilePreferencesFactory.class.getName()
+ );
+ }
+
+ /**
+ * Application entry point.
+ *
+ * @param stage The primary application stage.
+ */
+ @Override
+ public void start( final Stage stage ) {
+ initApplication();
+ initNotifyService();
+ initState( stage );
+ initStage( stage );
+ initSnitch();
+
+ stage.show();
+ }
+
+ public static void showDocument( final String uri ) {
+ getApplication().getHostServices().showDocument( uri );
+ }
+
+ private void initApplication() {
+ sApplication = this;
+ }
+
+ /**
+ * Constructs the notify service and appends the main window to the list of
+ * notification observers.
+ */
+ private void initNotifyService() {
+ final Notifier notifier = Services.load( Notifier.class );
+ notifier.addObserver( getMainWindow() );
+ }
+
+ private void initState( final Stage stage ) {
+ mStageState = new StageState( stage, getmOptions().getState() );
+ }
+
+ private void initStage( final Stage stage ) {
+ stage.getIcons().addAll(
+ createImage( FILE_LOGO_16 ),
+ createImage( FILE_LOGO_32 ),
+ createImage( FILE_LOGO_128 ),
+ createImage( FILE_LOGO_256 ),
+ createImage( FILE_LOGO_512 ) );
+ stage.setTitle( getApplicationTitle() );
+ stage.setScene( getScene() );
+ }
+
+ /**
+ * Watch for file system changes.
+ */
+ private void initSnitch() {
+ setmSnitchThread( new Thread( getmSnitch() ) );
+ getmSnitchThread().start();
+ }
+
+ /**
+ * Stops the snitch service, if its running.
+ *
+ * @throws InterruptedException Couldn't stop the snitch thread.
+ */
+ @Override
+ public void stop() throws InterruptedException {
+ getmSnitch().stop();
+
+ final Thread thread = getmSnitchThread();
+
+ if( thread != null ) {
+ thread.interrupt();
+ thread.join();
+ }
+ }
+
+ private synchronized Snitch getmSnitch() {
+ if( this.mSnitch == null ) {
+ this.mSnitch = Services.load( Snitch.class );
+ }
+
+ return this.mSnitch;
+ }
+
+ private Thread getmSnitchThread() {
+ return this.mSnitchThread;
+ }
+
+ private void setmSnitchThread( final Thread thread ) {
+ this.mSnitchThread = thread;
+ }
+
+ private synchronized Options getmOptions() {
+ if( this.mOptions == null ) {
+ this.mOptions = Services.load( Options.class );
+ }
+
+ return this.mOptions;
+ }
+
+ private Scene getScene() {
+ return getMainWindow().getScene();
+ }
+
+ private MainWindow getMainWindow() {
+ return this.mainWindow;
+ }
+
+ private String getApplicationTitle() {
+ return get( "Main.title" );
+ }
+
+ private static Application getApplication() {
+ return sApplication;
+ }
+
+ private Image createImage( final String filename ) {
+ return new Image( filename );
+ }
+}
src/main/java/com/scrivenvar/controls/WebHyperlink.java
package com.scrivenvar.controls;
-import com.scrivenvar.Main;
+import com.scrivenvar.MainFx;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
@Override
public void fire() {
- Main.showDocument( getUri() );
+ MainFx.showDocument( getUri() );
}
src/main/java/com/scrivenvar/editors/markdown/MarkdownEditorPane.java
package com.scrivenvar.editors.markdown;
-import static com.scrivenvar.Constants.STYLESHEET_MARKDOWN;
import com.scrivenvar.dialogs.ImageDialog;
import com.scrivenvar.dialogs.LinkDialog;
import com.scrivenvar.editors.EditorPane;
import com.scrivenvar.processors.MarkdownProcessor;
-import static com.scrivenvar.util.Utils.ltrim;
-import static com.scrivenvar.util.Utils.rtrim;
import com.vladsch.flexmark.ast.Link;
-import java.nio.file.Path;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import com.vladsch.flexmark.util.ast.Node;
-import javafx.beans.value.ObservableValue;
import javafx.scene.control.Dialog;
import javafx.scene.control.IndexRange;
-import static javafx.scene.input.KeyCode.ENTER;
import javafx.scene.input.KeyEvent;
import javafx.stage.Window;
import org.fxmisc.richtext.StyleClassedTextArea;
+
+import java.nio.file.Path;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.scrivenvar.Constants.STYLESHEET_MARKDOWN;
+import static com.scrivenvar.util.Utils.ltrim;
+import static com.scrivenvar.util.Utils.rtrim;
+import static javafx.scene.input.KeyCode.ENTER;
import static org.fxmisc.wellbehaved.event.EventPattern.keyPressed;
private static final Pattern AUTO_INDENT_PATTERN = Pattern.compile(
- "(\\s*[*+-]\\s+|\\s*[0-9]+\\.\\s+|\\s+)(.*)" );
+ "(\\s*[*+-]\\s+|\\s*[0-9]+\\.\\s+|\\s+)(.*)" );
public MarkdownEditorPane() {
addKeyboardListener( keyPressed( ENTER ), this::enterPressed );
- }
-
- public ObservableValue<String> markdownProperty() {
- return getEditor().textProperty();
}
private void enterPressed( final KeyEvent e ) {
final StyleClassedTextArea textArea = getEditor();
- final String currentLine = textArea.getText( textArea.getCurrentParagraph() );
+ final String currentLine =
+ textArea.getText( textArea.getCurrentParagraph() );
final Matcher matcher = AUTO_INDENT_PATTERN.matcher( currentLine );
String newText = "\n";
if( matcher.matches() ) {
if( !matcher.group( 2 ).isEmpty() ) {
- // indent new line with same whitespace characters and list markers as current line
+ // indent new line with same whitespace characters and list markers
+ // as current line
newText = newText.concat( matcher.group( 1 ) );
}
else {
// current line contains only whitespace characters and list markers
// --> empty current line
final int caretPosition = textArea.getCaretPosition();
- textArea.selectRange( caretPosition - currentLine.length(), caretPosition );
+ textArea.selectRange( caretPosition - currentLine.length(),
+ caretPosition );
}
}
}
- public void surroundSelection( String leading, String trailing, final String hint ) {
+ public void surroundSelection( String leading, String trailing,
+ final String hint ) {
final StyleClassedTextArea textArea = getEditor();
}
- // remove trailing whitespaces from trailing text if selection ends at text end
+ // remove trailing whitespaces from trailing text if selection ends at
+ // text end
if( end == textArea.getLength() ) {
trailing = rtrim( trailing );
// replace text and update selection
- textArea.replaceText( start, end, leading + trimmedSelectedText + trailing );
+ textArea.replaceText( start,
+ end,
+ leading + trimmedSelectedText + trailing );
textArea.selectRange( selStart, selEnd );
}
/**
* Returns one of: selected text, word under cursor, or parsed hyperlink from
* the markdown AST.
*
- * @return
+ * @return An instance containing the link URL and display text.
*/
private HyperlinkModel getHyperlink() {
}
- final HyperlinkModel model = createHyperlinkModel(
- link, selectedText, "https://website.com"
+ return createHyperlinkModel(
+ link, selectedText, "https://localhost"
);
-
- return model;
}
+ @SuppressWarnings("SameParameterValue")
private HyperlinkModel createHyperlinkModel(
- final Link link, final String selection, final String url ) {
+ final Link link, final String selection, final String url ) {
return link == null
- ? new HyperlinkModel( selection, url )
- : new HyperlinkModel( link );
+ ? new HyperlinkModel( selection, url )
+ : new HyperlinkModel( link );
}
private void insertObject( final Dialog<String> dialog ) {
- dialog.showAndWait().ifPresent( result -> {
- getEditor().replaceSelection( result );
- } );
+ dialog.showAndWait().ifPresent(
+ result -> getEditor().replaceSelection( result )
+ );
}
src/main/java/com/scrivenvar/processors/MarkdownCaretInsertionProcessor.java
import static java.lang.Character.isLetter;
import static java.lang.Math.min;
+
import javafx.beans.value.ObservableValue;
*
* @param processor The next processor in the chain.
- * @param position The caret's current position in the text.
+ * @param position The caret's current position in the text.
*/
public MarkdownCaretInsertionProcessor(
- final Processor<String> processor,
- final ObservableValue<Integer> position ) {
+ final Processor<String> processor,
+ final ObservableValue<Integer> position ) {
super( processor, position );
}
/**
* Changes the text to insert a "caret" at the caret position. This will
* insert the unique key of Constants.MD_CARET_POSITION into the document.
*
* @param t The text document to process.
- *
* @return The text with the caret position token inserted at the caret
* position.
src/main/java/com/scrivenvar/processors/XMLProcessor.java
import com.scrivenvar.Services;
import com.scrivenvar.service.Snitch;
-import java.io.File;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.text.ParseException;
+import net.sf.saxon.TransformerFactoryImpl;
+import net.sf.saxon.trans.XPathException;
+
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.ProcessingInstruction;
import javax.xml.stream.events.XMLEvent;
-import javax.xml.transform.ErrorListener;
-import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
-import net.sf.saxon.TransformerFactoryImpl;
+import java.io.File;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
import static net.sf.saxon.tree.util.ProcInstParser.getPseudoAttribute;
/**
* Transforms an XML document. The XML document must have a stylesheet specified
* as part of its processing instructions, such as:
*
* <code>xml-stylesheet type="text/xsl" href="markdown.xsl"</code>
- *
+ * <p>
* The XSL must transform the XML document into Markdown, or another format
* recognized by the next link on the chain.
*
* @author White Magic Software, Ltd.
*/
public class XMLProcessor extends AbstractProcessor<String>
- implements ErrorListener {
+ implements ErrorListener {
private final Snitch snitch = Services.load( Snitch.class );
*
* @param processor Next link in the processing chain.
- * @param path The path to the XML file content to be processed.
+ * @param path The path to the XML file content to be processed.
*/
public XMLProcessor( final Processor<String> processor, final Path path ) {
*
* @param text The text to transform, can be empty, cannot be null.
- *
* @return The transformed text, or empty if text is empty.
*/
*
* @param text The text to transform.
- *
* @return The transformed text.
*/
private String transform( final String text ) throws Exception {
// Extract the XML stylesheet processing instruction.
final String template = getXsltFilename( text );
final Path xsl = getXslPath( template );
try(
- final StringWriter output = new StringWriter( text.length() );
- final StringReader input = new StringReader( text ) ) {
+ final StringWriter output = new StringWriter( text.length() );
+ final StringReader input = new StringReader( text ) ) {
// Listen for external file modification events.
getSnitch().listen( xsl );
getTransformer( xsl ).transform(
- new StreamSource( input ),
- new StreamResult( output )
+ new StreamSource( input ),
+ new StreamResult( output )
);
*
* @param xsl The path to an XSLT file.
- *
* @return A transformer that will transform XML documents using the given
* XSLT file.
- *
* @throws TransformerConfigurationException Could not instantiate the
- * transformer.
+ * transformer.
*/
private Transformer getTransformer( final Path xsl )
- throws TransformerConfigurationException, IOException {
+ throws TransformerConfigurationException {
if( this.transformer == null ) {
this.transformer = createTransformer( xsl );
*
* @param xsl The stylesheet to use for transforming XML documents.
- *
* @return The edited XML document transformed into another format (usually
* markdown).
- *
* @throws TransformerConfigurationException Could not create the transformer.
*/
protected Transformer createTransformer( final Path xsl )
- throws TransformerConfigurationException {
+ throws TransformerConfigurationException {
final Source xslt = new StreamSource( xsl.toFile() );
*
* @param xml The XML containing an xml-stylesheet processing instruction.
- *
* @return The href pseudo-attribute value.
- *
* @throws XMLStreamException Could not parse the XML file.
- * @throws ParseException Could not find a non-empty HREF attribute value.
*/
private String getXsltFilename( final String xml )
- throws XMLStreamException, ParseException {
+ throws XMLStreamException, XPathException {
String result = "";
if( event.isProcessingInstruction() ) {
- final ProcessingInstruction pi = (ProcessingInstruction)event;
+ final ProcessingInstruction pi = (ProcessingInstruction) event;
final String target = pi.getTarget();
private XMLEventReader createXMLEventReader( final Reader reader )
- throws XMLStreamException {
+ throws XMLStreamException {
return getXMLInputFactory().createXMLEventReader( reader );
}
Delta963 lines added, 782 lines removed, 181-line increase