Dave Jarvis' Repositories

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

Added dependency on Apache Commons Configuration. Code formatting. Added interface for application properties. Added default configuration values, such as Rmd as a filename specification for the file dialog.

Authordjarvis <email>
Date2016-10-16 22:48:50 GMT-0700
Commit2e76df97fcb14d51c0704d770c643fb50af08680
Parent5969a1e
Delta936 lines added, 690 lines removed, 246-line increase
src/main/java/org/markdownwriterfx/util/Utils.java
public static String[] getPrefsStrings(Preferences prefs, String key) {
- ArrayList<String> arr = new ArrayList<String>();
+ ArrayList<String> arr = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
String s = prefs.get(key + (i + 1), null);
src/main/java/org/markdownwriterfx/FileEditorTabPane.java
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
-package org.markdownwriterfx;
-
-import java.io.File;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.prefs.Preferences;
-import javafx.beans.property.ReadOnlyBooleanProperty;
-import javafx.beans.property.ReadOnlyBooleanWrapper;
-import javafx.beans.property.ReadOnlyObjectProperty;
-import javafx.beans.property.ReadOnlyObjectWrapper;
-import javafx.beans.value.ChangeListener;
-import javafx.collections.ListChangeListener;
-import javafx.collections.ObservableList;
-import javafx.event.Event;
-import javafx.scene.Node;
-import javafx.scene.control.Alert;
-import javafx.scene.control.Alert.AlertType;
-import javafx.scene.control.ButtonType;
-import javafx.scene.control.Tab;
-import javafx.scene.control.TabPane;
-import javafx.scene.control.TabPane.TabClosingPolicy;
-import javafx.stage.FileChooser;
-import javafx.stage.FileChooser.ExtensionFilter;
-import org.markdownwriterfx.util.Utils;
-
-/**
- * Tab pane for file editors.
- *
- * @author Karl Tauber
- */
-class FileEditorTabPane
-{
- private final MainWindow mainWindow;
- private final TabPane tabPane;
- private final ReadOnlyObjectWrapper<FileEditor> activeFileEditor = new ReadOnlyObjectWrapper<>();
- private final ReadOnlyBooleanWrapper anyFileEditorModified = new ReadOnlyBooleanWrapper();
-
- FileEditorTabPane(MainWindow mainWindow) {
- this.mainWindow = mainWindow;
-
- tabPane = new TabPane();
- tabPane.setFocusTraversable(false);
- tabPane.setTabClosingPolicy(TabClosingPolicy.ALL_TABS);
-
- // update activeFileEditor property
- tabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldTab, newTab) -> {
- activeFileEditor.set((newTab != null) ? (FileEditor) newTab.getUserData() : null);
- });
-
- // update anyFileEditorModified property
- ChangeListener<Boolean> modifiedListener = (observable, oldValue, newValue) -> {
- boolean modified = false;
- for (Tab tab : tabPane.getTabs()) {
- if (((FileEditor)tab.getUserData()).isModified()) {
- modified = true;
- break;
- }
- }
- anyFileEditorModified.set(modified);
- };
- tabPane.getTabs().addListener((ListChangeListener<Tab>) c -> {
- while (c.next()) {
- if (c.wasAdded()) {
- for (Tab tab : c.getAddedSubList())
- ((FileEditor)tab.getUserData()).modifiedProperty().addListener(modifiedListener);
- } else if (c.wasRemoved()) {
- for (Tab tab : c.getRemoved())
- ((FileEditor)tab.getUserData()).modifiedProperty().removeListener(modifiedListener);
- }
- }
-
- // changes in the tabs may also change anyFileEditorModified property
- // (e.g. closed modified file)
- modifiedListener.changed(null, null, null);
- });
-
- // re-open files
- restoreState();
- }
-
- Node getNode() {
- return tabPane;
- }
-
- // 'activeFileEditor' property
- FileEditor getActiveFileEditor() { return activeFileEditor.get(); }
- ReadOnlyObjectProperty<FileEditor> activeFileEditorProperty() {
- return activeFileEditor.getReadOnlyProperty();
- }
-
- // 'anyFileEditorModified' property
- ReadOnlyBooleanProperty anyFileEditorModifiedProperty() {
- return anyFileEditorModified.getReadOnlyProperty();
- }
-
- private FileEditor createFileEditor(Path path) {
- FileEditor fileEditor = new FileEditor(mainWindow, path);
- fileEditor.getTab().setOnCloseRequest(e -> {
- if (!canCloseEditor(fileEditor))
- e.consume();
- });
- return fileEditor;
- }
-
- FileEditor newEditor() {
- FileEditor fileEditor = createFileEditor(null);
- Tab tab = fileEditor.getTab();
- tabPane.getTabs().add(tab);
- tabPane.getSelectionModel().select(tab);
- return fileEditor;
- }
-
- FileEditor[] openEditor() {
- FileChooser fileChooser = createFileChooser(Messages.get("FileEditorTabPane.openChooser.title"));
- List<File> selectedFiles = fileChooser.showOpenMultipleDialog(mainWindow.getScene().getWindow());
- if (selectedFiles == null)
- return null;
-
- saveLastDirectory(selectedFiles.get(0));
- return openEditors(selectedFiles, 0);
- }
-
- FileEditor[] openEditors(List<File> files, int activeIndex) {
- // close single unmodified "Untitled" tab
- if (tabPane.getTabs().size() == 1) {
- FileEditor fileEditor = (FileEditor) tabPane.getTabs().get(0).getUserData();
- if (fileEditor.getPath() == null && !fileEditor.isModified())
- closeEditor(fileEditor, false);
- }
-
- FileEditor[] fileEditors = new FileEditor[files.size()];
- for (int i = 0; i < files.size(); i++) {
- Path path = files.get(i).toPath();
-
- // check whether file is already opened
- FileEditor fileEditor = findEditor(path);
- if (fileEditor == null) {
- fileEditor = createFileEditor(path);
-
- tabPane.getTabs().add(fileEditor.getTab());
- }
-
- // select first file
- if (i == activeIndex)
- tabPane.getSelectionModel().select(fileEditor.getTab());
-
- fileEditors[i] = fileEditor;
- }
- return fileEditors;
- }
-
- boolean saveEditor(FileEditor fileEditor) {
- if (fileEditor == null || !fileEditor.isModified())
- return true;
-
- if (fileEditor.getPath() == null) {
- tabPane.getSelectionModel().select(fileEditor.getTab());
-
- FileChooser fileChooser = createFileChooser(Messages.get("FileEditorTabPane.saveChooser.title"));
- File file = fileChooser.showSaveDialog(mainWindow.getScene().getWindow());
- if (file == null)
- return false;
-
- saveLastDirectory(file);
- fileEditor.setPath(file.toPath());
- }
-
- return fileEditor.save();
- }
-
- boolean saveAllEditors() {
- FileEditor[] allEditors = getAllEditors();
-
- boolean success = true;
- for (FileEditor fileEditor : allEditors) {
- if (!saveEditor(fileEditor))
- success = false;
- }
-
- return success;
- }
-
- boolean canCloseEditor(FileEditor fileEditor) {
- if (!fileEditor.isModified())
- return true;
-
- Alert alert = mainWindow.createAlert(AlertType.CONFIRMATION,
- Messages.get("FileEditorTabPane.closeAlert.title"),
- Messages.get("FileEditorTabPane.closeAlert.message"), fileEditor.getTab().getText());
- alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO, ButtonType.CANCEL);
-
- ButtonType result = alert.showAndWait().get();
- if (result != ButtonType.YES)
- return (result == ButtonType.NO);
-
- return saveEditor(fileEditor);
- }
-
- boolean closeEditor(FileEditor fileEditor, boolean save) {
- if (fileEditor == null)
- return true;
-
- Tab tab = fileEditor.getTab();
-
- if (save) {
- Event event = new Event(tab,tab,Tab.TAB_CLOSE_REQUEST_EVENT);
- Event.fireEvent(tab, event);
- if (event.isConsumed())
- return false;
- }
-
- tabPane.getTabs().remove(tab);
- if (tab.getOnClosed() != null)
- Event.fireEvent(tab, new Event(Tab.CLOSED_EVENT));
-
- return true;
- }
-
- boolean closeAllEditors() {
- FileEditor[] allEditors = getAllEditors();
- FileEditor activeEditor = activeFileEditor.get();
-
- // try to save active tab first because in case the user decides to cancel,
- // then it stays active
- if (activeEditor != null && !canCloseEditor(activeEditor))
- return false;
-
- // save modified tabs
- for (int i = 0; i < allEditors.length; i++) {
- FileEditor fileEditor = allEditors[i];
- if (fileEditor == activeEditor)
- continue;
-
- if (fileEditor.isModified()) {
- // activate the modified tab to make its modified content visible to the user
- tabPane.getSelectionModel().select(i);
-
- if (!canCloseEditor(fileEditor))
- return false;
- }
- }
-
- // close all tabs
- for (FileEditor fileEditor : allEditors) {
- if (!closeEditor(fileEditor, false))
- return false;
- }
-
- saveState(allEditors, activeEditor);
-
- return tabPane.getTabs().isEmpty();
- }
-
- private FileEditor[] getAllEditors() {
- ObservableList<Tab> tabs = tabPane.getTabs();
- FileEditor[] allEditors = new FileEditor[tabs.size()];
- for (int i = 0; i < tabs.size(); i++)
- allEditors[i] = (FileEditor) tabs.get(i).getUserData();
- return allEditors;
- }
-
- private FileEditor findEditor(Path path) {
- for (Tab tab : tabPane.getTabs()) {
- FileEditor fileEditor = (FileEditor) tab.getUserData();
- if (path.equals(fileEditor.getPath()))
- return fileEditor;
- }
- return null;
- }
-
- private FileChooser createFileChooser(String title) {
- FileChooser fileChooser = new FileChooser();
- fileChooser.setTitle(title);
- fileChooser.getExtensionFilters().addAll(
- new ExtensionFilter(Messages.get("FileEditorTabPane.chooser.markdownFilesFilter"), "*.md", "*.markdown", "*.txt"),
- new ExtensionFilter(Messages.get("FileEditorTabPane.chooser.allFilesFilter"), "*.*"));
-
- String lastDirectory = MarkdownWriterFXApp.getState().get("lastDirectory", null);
- File file = new File((lastDirectory != null) ? lastDirectory : ".");
- if (!file.isDirectory())
- file = new File(".");
- fileChooser.setInitialDirectory(file);
- return fileChooser;
- }
-
- private void saveLastDirectory(File file) {
- MarkdownWriterFXApp.getState().put("lastDirectory", file.getParent());
- }
-
- private void restoreState() {
- Preferences state = MarkdownWriterFXApp.getState();
- String[] fileNames = Utils.getPrefsStrings(state, "file");
- String activeFileName = state.get("activeFile", null);
-
- int activeIndex = 0;
- ArrayList<File> files = new ArrayList<>(fileNames.length);
- for (String fileName : fileNames) {
- File file = new File(fileName);
- if (file.exists()) {
- files.add(file);
-
- if (fileName.equals(activeFileName))
- activeIndex = files.size() - 1;
- }
- }
-
- if (files.isEmpty()) {
- newEditor();
- return;
- }
-
- openEditors(files, activeIndex);
- }
-
- private void saveState(FileEditor[] allEditors, FileEditor activeEditor) {
- ArrayList<String> fileNames = new ArrayList<>(allEditors.length);
- for (FileEditor fileEditor : allEditors) {
- if (fileEditor.getPath() != null)
- fileNames.add(fileEditor.getPath().toString());
- }
-
- Preferences state = MarkdownWriterFXApp.getState();
- Utils.putPrefsStrings(state, "file", fileNames.toArray(new String[fileNames.size()]));
- if (activeEditor != null && activeEditor.getPath() != null)
- state.put("activeFile", activeEditor.getPath().toString());
- else
- state.remove("activeFile");
- }
+package org.markdownwriterfx;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
+import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.beans.property.ReadOnlyObjectWrapper;
+import javafx.beans.value.ChangeListener;
+import javafx.collections.ListChangeListener;
+import javafx.collections.ObservableList;
+import javafx.event.Event;
+import javafx.scene.Node;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.TabPane.TabClosingPolicy;
+import javafx.stage.FileChooser;
+import javafx.stage.FileChooser.ExtensionFilter;
+import static org.markdownwriterfx.options.DefaultConfiguration.DEFAULT_EXTENSIONS_ALL;
+import static org.markdownwriterfx.options.DefaultConfiguration.DEFAULT_EXTENSIONS_MARKDOWN;
+import org.markdownwriterfx.util.Utils;
+
+/**
+ * Tab pane for file editors.
+ *
+ * @author Karl Tauber
+ */
+class FileEditorTabPane implements ApplicationProperty {
+
+ private MainWindow mainWindow;
+ private final TabPane tabPane;
+ private final ReadOnlyObjectWrapper<FileEditor> activeFileEditor = new ReadOnlyObjectWrapper<>();
+ private final ReadOnlyBooleanWrapper anyFileEditorModified = new ReadOnlyBooleanWrapper();
+
+ FileEditorTabPane( MainWindow mainWindow ) {
+ setMainWindow( mainWindow );
+
+ tabPane = new TabPane();
+ tabPane.setFocusTraversable( false );
+ tabPane.setTabClosingPolicy( TabClosingPolicy.ALL_TABS );
+
+ // update activeFileEditor property
+ tabPane.getSelectionModel().selectedItemProperty().addListener( (observable, oldTab, newTab) -> {
+ activeFileEditor.set( (newTab != null) ? (FileEditor)newTab.getUserData() : null );
+ } );
+
+ // update anyFileEditorModified property
+ ChangeListener<Boolean> modifiedListener = (observable, oldValue, newValue) -> {
+ boolean modified = false;
+ for( Tab tab : tabPane.getTabs() ) {
+ if( ((FileEditor)tab.getUserData()).isModified() ) {
+ modified = true;
+ break;
+ }
+ }
+ anyFileEditorModified.set( modified );
+ };
+
+ tabPane.getTabs().addListener( (ListChangeListener<Tab>)c -> {
+ while( c.next() ) {
+ if( c.wasAdded() ) {
+ for( Tab tab : c.getAddedSubList() ) {
+ ((FileEditor)tab.getUserData()).modifiedProperty().addListener( modifiedListener );
+ }
+ } else if( c.wasRemoved() ) {
+ for( Tab tab : c.getRemoved() ) {
+ ((FileEditor)tab.getUserData()).modifiedProperty().removeListener( modifiedListener );
+ }
+ }
+ }
+
+ // changes in the tabs may also change anyFileEditorModified property
+ // (e.g. closed modified file)
+ modifiedListener.changed( null, null, null );
+ } );
+
+ // re-open files
+ restoreState();
+ }
+
+ private MainWindow getMainWindow() {
+ return this.mainWindow;
+ }
+
+ private void setMainWindow( MainWindow mainWindow ) {
+ this.mainWindow = mainWindow;
+ }
+
+ Node getNode() {
+ return tabPane;
+ }
+
+ // 'activeFileEditor' property
+ FileEditor getActiveFileEditor() {
+ return activeFileEditor.get();
+ }
+
+ ReadOnlyObjectProperty<FileEditor> activeFileEditorProperty() {
+ return activeFileEditor.getReadOnlyProperty();
+ }
+
+ // 'anyFileEditorModified' property
+ ReadOnlyBooleanProperty anyFileEditorModifiedProperty() {
+ return anyFileEditorModified.getReadOnlyProperty();
+ }
+
+ private FileEditor createFileEditor( Path path ) {
+ FileEditor fileEditor = new FileEditor( getMainWindow(), path );
+ fileEditor.getTab().setOnCloseRequest( e -> {
+ if( !canCloseEditor( fileEditor ) ) {
+ e.consume();
+ }
+ } );
+ return fileEditor;
+ }
+
+ FileEditor newEditor() {
+ FileEditor fileEditor = createFileEditor( null );
+ Tab tab = fileEditor.getTab();
+ tabPane.getTabs().add( tab );
+ tabPane.getSelectionModel().select( tab );
+ return fileEditor;
+ }
+
+ FileEditor[] openEditor() {
+ FileChooser fileChooser = createFileChooser( Messages.get( "FileEditorTabPane.openChooser.title" ) );
+ List<File> selectedFiles = fileChooser.showOpenMultipleDialog( getMainWindow().getScene().getWindow() );
+ if( selectedFiles == null ) {
+ return null;
+ }
+
+ saveLastDirectory( selectedFiles.get( 0 ) );
+ return openEditors( selectedFiles, 0 );
+ }
+
+ FileEditor[] openEditors( List<File> files, int activeIndex ) {
+ // close single unmodified "Untitled" tab
+ if( tabPane.getTabs().size() == 1 ) {
+ FileEditor fileEditor = (FileEditor)tabPane.getTabs().get( 0 ).getUserData();
+ if( fileEditor.getPath() == null && !fileEditor.isModified() ) {
+ closeEditor( fileEditor, false );
+ }
+ }
+
+ FileEditor[] fileEditors = new FileEditor[ files.size() ];
+ for( int i = 0; i < files.size(); i++ ) {
+ Path path = files.get( i ).toPath();
+
+ // check whether file is already opened
+ FileEditor fileEditor = findEditor( path );
+ if( fileEditor == null ) {
+ fileEditor = createFileEditor( path );
+
+ tabPane.getTabs().add( fileEditor.getTab() );
+ }
+
+ // select first file
+ if( i == activeIndex ) {
+ tabPane.getSelectionModel().select( fileEditor.getTab() );
+ }
+
+ fileEditors[ i ] = fileEditor;
+ }
+ return fileEditors;
+ }
+
+ boolean saveEditor( FileEditor fileEditor ) {
+ if( fileEditor == null || !fileEditor.isModified() ) {
+ return true;
+ }
+
+ if( fileEditor.getPath() == null ) {
+ tabPane.getSelectionModel().select( fileEditor.getTab() );
+
+ FileChooser fileChooser = createFileChooser( Messages.get( "FileEditorTabPane.saveChooser.title" ) );
+ File file = fileChooser.showSaveDialog( getMainWindow().getScene().getWindow() );
+ if( file == null ) {
+ return false;
+ }
+
+ saveLastDirectory( file );
+ fileEditor.setPath( file.toPath() );
+ }
+
+ return fileEditor.save();
+ }
+
+ boolean saveAllEditors() {
+ FileEditor[] allEditors = getAllEditors();
+
+ boolean success = true;
+ for( FileEditor fileEditor : allEditors ) {
+ if( !saveEditor( fileEditor ) ) {
+ success = false;
+ }
+ }
+
+ return success;
+ }
+
+ boolean canCloseEditor( FileEditor fileEditor ) {
+ if( !fileEditor.isModified() ) {
+ return true;
+ }
+
+ Alert alert = getMainWindow().createAlert( AlertType.CONFIRMATION,
+ Messages.get( "FileEditorTabPane.closeAlert.title" ),
+ Messages.get( "FileEditorTabPane.closeAlert.message" ), fileEditor.getTab().getText() );
+ alert.getButtonTypes().setAll( ButtonType.YES, ButtonType.NO, ButtonType.CANCEL );
+
+ ButtonType result = alert.showAndWait().get();
+ if( result != ButtonType.YES ) {
+ return (result == ButtonType.NO);
+ }
+
+ return saveEditor( fileEditor );
+ }
+
+ boolean closeEditor( FileEditor fileEditor, boolean save ) {
+ if( fileEditor == null ) {
+ return true;
+ }
+
+ Tab tab = fileEditor.getTab();
+
+ if( save ) {
+ Event event = new Event( tab, tab, Tab.TAB_CLOSE_REQUEST_EVENT );
+ Event.fireEvent( tab, event );
+ if( event.isConsumed() ) {
+ return false;
+ }
+ }
+
+ tabPane.getTabs().remove( tab );
+ if( tab.getOnClosed() != null ) {
+ Event.fireEvent( tab, new Event( Tab.CLOSED_EVENT ) );
+ }
+
+ return true;
+ }
+
+ boolean closeAllEditors() {
+ FileEditor[] allEditors = getAllEditors();
+ FileEditor activeEditor = activeFileEditor.get();
+
+ // try to save active tab first because in case the user decides to cancel,
+ // then it stays active
+ if( activeEditor != null && !canCloseEditor( activeEditor ) ) {
+ return false;
+ }
+
+ // save modified tabs
+ for( int i = 0; i < allEditors.length; i++ ) {
+ FileEditor fileEditor = allEditors[ i ];
+ if( fileEditor == activeEditor ) {
+ continue;
+ }
+
+ if( fileEditor.isModified() ) {
+ // activate the modified tab to make its modified content visible to the user
+ tabPane.getSelectionModel().select( i );
+
+ if( !canCloseEditor( fileEditor ) ) {
+ return false;
+ }
+ }
+ }
+
+ // close all tabs
+ for( FileEditor fileEditor : allEditors ) {
+ if( !closeEditor( fileEditor, false ) ) {
+ return false;
+ }
+ }
+
+ saveState( allEditors, activeEditor );
+
+ return tabPane.getTabs().isEmpty();
+ }
+
+ private FileEditor[] getAllEditors() {
+ ObservableList<Tab> tabs = tabPane.getTabs();
+ FileEditor[] allEditors = new FileEditor[ tabs.size() ];
+ for( int i = 0; i < tabs.size(); i++ ) {
+ allEditors[ i ] = (FileEditor)tabs.get( i ).getUserData();
+ }
+ return allEditors;
+ }
+
+ private FileEditor findEditor( Path path ) {
+ for( Tab tab : tabPane.getTabs() ) {
+ FileEditor fileEditor = (FileEditor)tab.getUserData();
+ if( path.equals( fileEditor.getPath() ) ) {
+ return fileEditor;
+ }
+ }
+ return null;
+ }
+
+ private FileChooser createFileChooser( String title ) {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle( title );
+ fileChooser.getExtensionFilters().addAll(
+ new ExtensionFilter( Messages.get( "FileEditorTabPane.chooser.markdownFilesFilter" ), getMarkdownExtensions() ),
+ new ExtensionFilter( Messages.get( "FileEditorTabPane.chooser.allFilesFilter" ), getAllExtensions() ) );
+
+ String lastDirectory = MarkdownWriterFXApp.getState().get( "lastDirectory", null );
+ File file = new File( (lastDirectory != null) ? lastDirectory : "." );
+ if( !file.isDirectory() ) {
+ file = new File( "." );
+ }
+ fileChooser.setInitialDirectory( file );
+ return fileChooser;
+ }
+
+ private List<String> getMarkdownExtensions() {
+ return coercePropertyList( "application.extensions.markdown", DEFAULT_EXTENSIONS_MARKDOWN );
+ }
+
+ private List<String> getAllExtensions() {
+ return coercePropertyList( "application.extensions.all", DEFAULT_EXTENSIONS_ALL );
+ }
+
+ /**
+ * Convert the generic list of property objects into strings.
+ *
+ * @param property The property value to coerce.
+ * @param defaults The defaults values to use should the property be unset.
+ *
+ * @return The list of properties coerced from objects to strings.
+ */
+ private List<String> coercePropertyList( String property, List<String> defaults ) {
+ final List<Object> properties = getPropertyList( property, defaults );
+
+ return properties.stream()
+ .map( object -> Objects.toString( object, null ) )
+ .collect( Collectors.toList() );
+ }
+
+ @Override
+ public String getProperty( String property, String defaultValue ) {
+ return getMainWindow().getProperty( property, defaultValue );
+ }
+
+ @Override
+ public List<Object> getPropertyList( String property, List<String> defaults ) {
+ return getMainWindow().getPropertyList( property, defaults );
+ }
+
+ private void saveLastDirectory( File file ) {
+ MarkdownWriterFXApp.getState().put( "lastDirectory", file.getParent() );
+ }
+
+ private void restoreState() {
+ Preferences state = MarkdownWriterFXApp.getState();
+ String[] fileNames = Utils.getPrefsStrings( state, "file" );
+ String activeFileName = state.get( "activeFile", null );
+
+ int activeIndex = 0;
+ ArrayList<File> files = new ArrayList<>( fileNames.length );
+ for( String fileName : fileNames ) {
+ File file = new File( fileName );
+ if( file.exists() ) {
+ files.add( file );
+
+ if( fileName.equals( activeFileName ) ) {
+ activeIndex = files.size() - 1;
+ }
+ }
+ }
+
+ if( files.isEmpty() ) {
+ newEditor();
+ return;
+ }
+
+ openEditors( files, activeIndex );
+ }
+
+ private void saveState( FileEditor[] allEditors, FileEditor activeEditor ) {
+ ArrayList<String> fileNames = new ArrayList<>( allEditors.length );
+ for( FileEditor fileEditor : allEditors ) {
+ if( fileEditor.getPath() != null ) {
+ fileNames.add( fileEditor.getPath().toString() );
+ }
+ }
+
+ Preferences state = MarkdownWriterFXApp.getState();
+ Utils.putPrefsStrings( state, "file", fileNames.toArray( new String[ fileNames.size() ] ) );
+ if( activeEditor != null && activeEditor.getPath() != null ) {
+ state.put( "activeFile", activeEditor.getPath().toString() );
+ } else {
+ state.remove( "activeFile" );
+ }
+ }
}
src/main/java/org/markdownwriterfx/MainWindow.java
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
-package org.markdownwriterfx;
-
-import java.text.MessageFormat;
-import java.util.function.Function;
-import javafx.beans.binding.Bindings;
-import javafx.beans.binding.BooleanBinding;
-import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.value.ObservableBooleanValue;
-import javafx.event.Event;
-import javafx.scene.Node;
-import javafx.scene.Scene;
-import javafx.scene.control.Alert;
-import javafx.scene.control.Alert.AlertType;
-import javafx.scene.image.Image;
-import javafx.scene.image.ImageView;
-import javafx.scene.control.Menu;
-import javafx.scene.control.MenuBar;
-import javafx.scene.control.ToolBar;
-import javafx.scene.input.KeyCode;
-import javafx.scene.input.KeyEvent;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.VBox;
-import javafx.stage.Window;
-import javafx.stage.WindowEvent;
-import org.markdownwriterfx.editor.MarkdownEditorPane;
-import org.markdownwriterfx.options.OptionsDialog;
-import org.markdownwriterfx.util.Action;
-import org.markdownwriterfx.util.ActionUtils;
-import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.*;
-
-/**
- * Main window containing a tab pane in the center for file editors.
- *
- * @author Karl Tauber
- */
-class MainWindow
-{
- private final Scene scene;
- private final FileEditorTabPane fileEditorTabPane;
- private MenuBar menuBar;
-
- MainWindow() {
- fileEditorTabPane = new FileEditorTabPane(this);
-
- BorderPane borderPane = new BorderPane();
- borderPane.setPrefSize(800, 800);
- borderPane.setTop(createMenuBarAndToolBar());
- borderPane.setCenter(fileEditorTabPane.getNode());
-
- scene = new Scene(borderPane);
- scene.getStylesheets().add("org/markdownwriterfx/MarkdownWriter.css");
- scene.windowProperty().addListener((observable, oldWindow, newWindow) -> {
- newWindow.setOnCloseRequest(e -> {
- if (!fileEditorTabPane.closeAllEditors())
- e.consume();
- });
-
- // workaround for a bug in JavaFX: unselect menubar if window looses focus
- newWindow.focusedProperty().addListener((obs, oldFocused, newFocused) -> {
- if (!newFocused) {
- // send an ESC key event to the menubar
- menuBar.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED,
- KeyEvent.CHAR_UNDEFINED, "", KeyCode.ESCAPE,
- false, false, false, false));
- }
- });
- });
- }
-
- Scene getScene() {
- return scene;
- }
-
- private Node createMenuBarAndToolBar() {
- BooleanBinding activeFileEditorIsNull = fileEditorTabPane.activeFileEditorProperty().isNull();
-
- // File actions
- Action fileNewAction = new Action(Messages.get("MainWindow.fileNewAction"), "Shortcut+N", FILE_ALT, e -> fileNew());
- Action fileOpenAction = new Action(Messages.get("MainWindow.fileOpenAction"), "Shortcut+O", FOLDER_OPEN_ALT, e -> fileOpen());
- Action fileCloseAction = new Action(Messages.get("MainWindow.fileCloseAction"), "Shortcut+W", null, e -> fileClose(), activeFileEditorIsNull);
- Action fileCloseAllAction = new Action(Messages.get("MainWindow.fileCloseAllAction"), null, null, e -> fileCloseAll(), activeFileEditorIsNull);
- Action fileSaveAction = new Action(Messages.get("MainWindow.fileSaveAction"), "Shortcut+S", FLOPPY_ALT, e -> fileSave(),
- createActiveBooleanProperty(FileEditor::modifiedProperty).not());
- Action fileSaveAllAction = new Action(Messages.get("MainWindow.fileSaveAllAction"), "Shortcut+Shift+S", null, e -> fileSaveAll(),
- Bindings.not(fileEditorTabPane.anyFileEditorModifiedProperty()));
- Action fileExitAction = new Action(Messages.get("MainWindow.fileExitAction"), null, null, e -> fileExit());
-
- // Edit actions
- Action editUndoAction = new Action(Messages.get("MainWindow.editUndoAction"), "Shortcut+Z", UNDO,
- e -> getActiveEditor().undo(),
- createActiveBooleanProperty(FileEditor::canUndoProperty).not());
- Action editRedoAction = new Action(Messages.get("MainWindow.editRedoAction"), "Shortcut+Y", REPEAT,
- e -> getActiveEditor().redo(),
- createActiveBooleanProperty(FileEditor::canRedoProperty).not());
-
- // Insert actions
- Action insertBoldAction = new Action(Messages.get("MainWindow.insertBoldAction"), "Shortcut+B", BOLD,
- e -> getActiveEditor().surroundSelection("**", "**"),
- activeFileEditorIsNull);
- Action insertItalicAction = new Action(Messages.get("MainWindow.insertItalicAction"), "Shortcut+I", ITALIC,
- e -> getActiveEditor().surroundSelection("*", "*"),
- activeFileEditorIsNull);
- Action insertStrikethroughAction = new Action(Messages.get("MainWindow.insertStrikethroughAction"), "Shortcut+T", STRIKETHROUGH,
- e -> getActiveEditor().surroundSelection("~~", "~~"),
- activeFileEditorIsNull);
- Action insertBlockquoteAction = new Action(Messages.get("MainWindow.insertBlockquoteAction"), "Ctrl+Q", QUOTE_LEFT, // not Shortcut+Q because of conflict on Mac
- e -> getActiveEditor().surroundSelection("\n\n> ", ""),
- activeFileEditorIsNull);
- Action insertCodeAction = new Action(Messages.get("MainWindow.insertCodeAction"), "Shortcut+K", CODE,
- e -> getActiveEditor().surroundSelection("`", "`"),
- activeFileEditorIsNull);
- Action insertFencedCodeBlockAction = new Action(Messages.get("MainWindow.insertFencedCodeBlockAction"), "Shortcut+Shift+K", FILE_CODE_ALT,
- e -> getActiveEditor().surroundSelection("\n\n```\n", "\n```\n\n", Messages.get("MainWindow.insertFencedCodeBlockText")),
- activeFileEditorIsNull);
-
- Action insertLinkAction = new Action(Messages.get("MainWindow.insertLinkAction"), "Shortcut+L", LINK,
- e -> getActiveEditor().insertLink(),
- activeFileEditorIsNull);
- Action insertImageAction = new Action(Messages.get("MainWindow.insertImageAction"), "Shortcut+G", PICTURE_ALT,
- e -> getActiveEditor().insertImage(),
- activeFileEditorIsNull);
-
- Action insertHeader1Action = new Action(Messages.get("MainWindow.insertHeader1Action"), "Shortcut+1", HEADER,
- e -> getActiveEditor().surroundSelection("\n\n# ", "", Messages.get("MainWindow.insertHeader1Text")),
- activeFileEditorIsNull);
- Action insertHeader2Action = new Action(Messages.get("MainWindow.insertHeader2Action"), "Shortcut+2", HEADER,
- e -> getActiveEditor().surroundSelection("\n\n## ", "", Messages.get("MainWindow.insertHeader2Text")),
- activeFileEditorIsNull);
- Action insertHeader3Action = new Action(Messages.get("MainWindow.insertHeader3Action"), "Shortcut+3", HEADER,
- e -> getActiveEditor().surroundSelection("\n\n### ", "", Messages.get("MainWindow.insertHeader3Text")),
- activeFileEditorIsNull);
- Action insertHeader4Action = new Action(Messages.get("MainWindow.insertHeader4Action"), "Shortcut+4", HEADER,
- e -> getActiveEditor().surroundSelection("\n\n#### ", "", Messages.get("MainWindow.insertHeader4Text")),
- activeFileEditorIsNull);
- Action insertHeader5Action = new Action(Messages.get("MainWindow.insertHeader5Action"), "Shortcut+5", HEADER,
- e -> getActiveEditor().surroundSelection("\n\n##### ", "", Messages.get("MainWindow.insertHeader5Text")),
- activeFileEditorIsNull);
- Action insertHeader6Action = new Action(Messages.get("MainWindow.insertHeader6Action"), "Shortcut+6", HEADER,
- e -> getActiveEditor().surroundSelection("\n\n###### ", "", Messages.get("MainWindow.insertHeader6Text")),
- activeFileEditorIsNull);
-
- Action insertUnorderedListAction = new Action(Messages.get("MainWindow.insertUnorderedListAction"), "Shortcut+U", LIST_UL,
- e -> getActiveEditor().surroundSelection("\n\n* ", ""),
- activeFileEditorIsNull);
- Action insertOrderedListAction = new Action(Messages.get("MainWindow.insertOrderedListAction"), "Shortcut+Shift+O", LIST_OL,
- e -> getActiveEditor().surroundSelection("\n\n1. ", ""),
- activeFileEditorIsNull);
- Action insertHorizontalRuleAction = new Action(Messages.get("MainWindow.insertHorizontalRuleAction"), "Shortcut+H", null,
- e -> getActiveEditor().surroundSelection("\n\n---\n\n", ""),
- activeFileEditorIsNull);
-
- // Tools actions
- Action toolsOptionsAction = new Action(Messages.get("MainWindow.toolsOptionsAction"), "Shortcut+,", null, e -> toolsOptions());
-
- // Help actions
- Action helpAboutAction = new Action(Messages.get("MainWindow.helpAboutAction"), null, null, e -> helpAbout());
-
-
- //---- MenuBar ----
-
- Menu fileMenu = ActionUtils.createMenu(Messages.get("MainWindow.fileMenu"),
- fileNewAction,
- fileOpenAction,
- null,
- fileCloseAction,
- fileCloseAllAction,
- null,
- fileSaveAction,
- fileSaveAllAction,
- null,
- fileExitAction);
-
- Menu editMenu = ActionUtils.createMenu(Messages.get("MainWindow.editMenu"),
- editUndoAction,
- editRedoAction);
-
- Menu insertMenu = ActionUtils.createMenu(Messages.get("MainWindow.insertMenu"),
- insertBoldAction,
- insertItalicAction,
- insertStrikethroughAction,
- insertBlockquoteAction,
- insertCodeAction,
- insertFencedCodeBlockAction,
- null,
- insertLinkAction,
- insertImageAction,
- null,
- insertHeader1Action,
- insertHeader2Action,
- insertHeader3Action,
- insertHeader4Action,
- insertHeader5Action,
- insertHeader6Action,
- null,
- insertUnorderedListAction,
- insertOrderedListAction,
- insertHorizontalRuleAction);
-
- Menu toolsMenu = ActionUtils.createMenu(Messages.get("MainWindow.toolsMenu"),
- toolsOptionsAction);
-
- Menu helpMenu = ActionUtils.createMenu(Messages.get("MainWindow.helpMenu"),
- helpAboutAction);
-
- menuBar = new MenuBar(fileMenu, editMenu, insertMenu, toolsMenu, helpMenu);
-
-
- //---- ToolBar ----
-
- ToolBar toolBar = ActionUtils.createToolBar(
- fileNewAction,
- fileOpenAction,
- fileSaveAction,
- null,
- editUndoAction,
- editRedoAction,
- null,
- insertBoldAction,
- insertItalicAction,
- insertBlockquoteAction,
- insertCodeAction,
- insertFencedCodeBlockAction,
- null,
- insertLinkAction,
- insertImageAction,
- null,
- insertHeader1Action,
- null,
- insertUnorderedListAction,
- insertOrderedListAction);
-
- return new VBox(menuBar, toolBar);
- }
-
- private MarkdownEditorPane getActiveEditor() {
- return fileEditorTabPane.getActiveFileEditor().getEditor();
- }
-
- /**
- * Creates a boolean property that is bound to another boolean value
- * of the active editor.
- */
- private BooleanProperty createActiveBooleanProperty(Function<FileEditor, ObservableBooleanValue> func) {
- BooleanProperty b = new SimpleBooleanProperty();
- FileEditor fileEditor = fileEditorTabPane.getActiveFileEditor();
- if (fileEditor != null)
- b.bind(func.apply(fileEditor));
- fileEditorTabPane.activeFileEditorProperty().addListener((observable, oldFileEditor, newFileEditor) -> {
- b.unbind();
- if (newFileEditor != null)
- b.bind(func.apply(newFileEditor));
- else
- b.set(false);
- });
- return b;
- }
-
- Alert createAlert(AlertType alertType, String title,
- String contentTextFormat, Object... contentTextArgs)
- {
- Alert alert = new Alert(alertType);
- alert.setTitle(title);
- alert.setHeaderText(null);
- alert.setContentText(MessageFormat.format(contentTextFormat, contentTextArgs));
- alert.initOwner(getScene().getWindow());
- return alert;
- }
-
- //---- File actions -------------------------------------------------------
-
- private void fileNew() {
- fileEditorTabPane.newEditor();
- }
-
- private void fileOpen() {
- fileEditorTabPane.openEditor();
- }
-
- private void fileClose() {
- fileEditorTabPane.closeEditor(fileEditorTabPane.getActiveFileEditor(), true);
- }
-
- private void fileCloseAll() {
- fileEditorTabPane.closeAllEditors();
- }
-
- private void fileSave() {
- fileEditorTabPane.saveEditor(fileEditorTabPane.getActiveFileEditor());
- }
-
- private void fileSaveAll() {
- fileEditorTabPane.saveAllEditors();
- }
-
- private void fileExit() {
- Window window = scene.getWindow();
- Event.fireEvent(window, new WindowEvent(window, WindowEvent.WINDOW_CLOSE_REQUEST));
- }
-
- //---- Tools actions ------------------------------------------------------
-
- private void toolsOptions() {
- OptionsDialog dialog = new OptionsDialog(getScene().getWindow());
- dialog.showAndWait();
- }
-
- //---- Help actions -------------------------------------------------------
-
- private void helpAbout() {
- Alert alert = new Alert(AlertType.INFORMATION);
- alert.setTitle(Messages.get("MainWindow.about.title"));
- alert.setHeaderText(Messages.get("MainWindow.about.headerText"));
- alert.setContentText(Messages.get("MainWindow.about.contentText"));
- alert.setGraphic(new ImageView(new Image("org/markdownwriterfx/markdownwriterfx32.png")));
- alert.initOwner(getScene().getWindow());
-
- alert.showAndWait();
- }
+package org.markdownwriterfx;
+
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.BOLD;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.CODE;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.FILE_ALT;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.FILE_CODE_ALT;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.FLOPPY_ALT;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.FOLDER_OPEN_ALT;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.HEADER;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.ITALIC;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.LINK;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.LIST_OL;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.LIST_UL;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.PICTURE_ALT;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.QUOTE_LEFT;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.REPEAT;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.STRIKETHROUGH;
+import static de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon.UNDO;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.function.Function;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.BooleanBinding;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.value.ObservableBooleanValue;
+import javafx.event.Event;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.Menu;
+import javafx.scene.control.MenuBar;
+import javafx.scene.control.ToolBar;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import static javafx.scene.input.KeyCode.ESCAPE;
+import javafx.scene.input.KeyEvent;
+import static javafx.scene.input.KeyEvent.CHAR_UNDEFINED;
+import static javafx.scene.input.KeyEvent.KEY_PRESSED;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.VBox;
+import javafx.stage.Window;
+import javafx.stage.WindowEvent;
+import org.markdownwriterfx.editor.MarkdownEditorPane;
+import org.markdownwriterfx.options.OptionsDialog;
+import org.markdownwriterfx.util.Action;
+import org.markdownwriterfx.util.ActionUtils;
+
+/**
+ * Main window containing a tab pane in the center for file editors.
+ *
+ * @author Karl Tauber
+ */
+class MainWindow implements ApplicationProperty {
+
+ private final Scene scene;
+ private final FileEditorTabPane fileEditorTabPane;
+ private MenuBar menuBar;
+ private final ApplicationProperty application;
+
+ MainWindow( ApplicationProperty application ) {
+ this.application = application;
+ this.fileEditorTabPane = new FileEditorTabPane( this );
+
+ BorderPane borderPane = new BorderPane();
+ borderPane.setPrefSize( 800, 800 );
+ borderPane.setTop( createMenuBarAndToolBar() );
+ borderPane.setCenter( fileEditorTabPane.getNode() );
+
+ this.scene = new Scene( borderPane );
+ this.scene.getStylesheets().add( "org/markdownwriterfx/MarkdownWriter.css" );
+ this.scene.windowProperty().addListener( (observable, oldWindow, newWindow) -> {
+ newWindow.setOnCloseRequest( e -> {
+ if( !this.fileEditorTabPane.closeAllEditors() ) {
+ e.consume();
+ }
+ } );
+
+ // workaround for a bug in JavaFX: unselect menubar if window looses focus
+ newWindow.focusedProperty().addListener( (obs, oldFocused, newFocused) -> {
+ if( !newFocused ) {
+ // send an ESC key event to the menubar
+ this.menuBar.fireEvent( new KeyEvent( KEY_PRESSED, CHAR_UNDEFINED, "", ESCAPE,
+ false, false, false, false ) );
+ }
+ } );
+ } );
+
+ }
+
+ /**
+ * Delegates resolving the property to the application.
+ *
+ * @param property The property to obtain.
+ * @param defaultValue The default value should no value be set.
+ *
+ * @return The value associated with the given property, or the defaultValue
+ * if the property hasn't been set.
+ */
+ @Override
+ public String getProperty( String property, String defaultValue ) {
+ return getApplicationProperty().getProperty( property, defaultValue );
+ }
+
+ @Override
+ public List<Object> getPropertyList(String property, List<String> defaults ) {
+ return getApplicationProperty().getPropertyList( property, defaults );
+ }
+
+ private ApplicationProperty getApplicationProperty() {
+ return this.application;
+ }
+
+ Scene getScene() {
+ return scene;
+ }
+
+ private Node createMenuBarAndToolBar() {
+ BooleanBinding activeFileEditorIsNull = fileEditorTabPane.activeFileEditorProperty().isNull();
+
+ // File actions
+ Action fileNewAction = new Action( Messages.get( "MainWindow.fileNewAction" ), "Shortcut+N", FILE_ALT, e -> fileNew() );
+ Action fileOpenAction = new Action( Messages.get( "MainWindow.fileOpenAction" ), "Shortcut+O", FOLDER_OPEN_ALT, e -> fileOpen() );
+ Action fileCloseAction = new Action( Messages.get( "MainWindow.fileCloseAction" ), "Shortcut+W", null, e -> fileClose(), activeFileEditorIsNull );
+ Action fileCloseAllAction = new Action( Messages.get( "MainWindow.fileCloseAllAction" ), null, null, e -> fileCloseAll(), activeFileEditorIsNull );
+ Action fileSaveAction = new Action( Messages.get( "MainWindow.fileSaveAction" ), "Shortcut+S", FLOPPY_ALT, e -> fileSave(),
+ createActiveBooleanProperty( FileEditor::modifiedProperty ).not() );
+ Action fileSaveAllAction = new Action( Messages.get( "MainWindow.fileSaveAllAction" ), "Shortcut+Shift+S", null, e -> fileSaveAll(),
+ Bindings.not( fileEditorTabPane.anyFileEditorModifiedProperty() ) );
+ Action fileExitAction = new Action( Messages.get( "MainWindow.fileExitAction" ), null, null, e -> fileExit() );
+
+ // Edit actions
+ Action editUndoAction = new Action( Messages.get( "MainWindow.editUndoAction" ), "Shortcut+Z", UNDO,
+ e -> getActiveEditor().undo(),
+ createActiveBooleanProperty( FileEditor::canUndoProperty ).not() );
+ Action editRedoAction = new Action( Messages.get( "MainWindow.editRedoAction" ), "Shortcut+Y", REPEAT,
+ e -> getActiveEditor().redo(),
+ createActiveBooleanProperty( FileEditor::canRedoProperty ).not() );
+
+ // Insert actions
+ Action insertBoldAction = new Action( Messages.get( "MainWindow.insertBoldAction" ), "Shortcut+B", BOLD,
+ e -> getActiveEditor().surroundSelection( "**", "**" ),
+ activeFileEditorIsNull );
+ Action insertItalicAction = new Action( Messages.get( "MainWindow.insertItalicAction" ), "Shortcut+I", ITALIC,
+ e -> getActiveEditor().surroundSelection( "*", "*" ),
+ activeFileEditorIsNull );
+ Action insertStrikethroughAction = new Action( Messages.get( "MainWindow.insertStrikethroughAction" ), "Shortcut+T", STRIKETHROUGH,
+ e -> getActiveEditor().surroundSelection( "~~", "~~" ),
+ activeFileEditorIsNull );
+ Action insertBlockquoteAction = new Action( Messages.get( "MainWindow.insertBlockquoteAction" ), "Ctrl+Q", QUOTE_LEFT, // not Shortcut+Q because of conflict on Mac
+ e -> getActiveEditor().surroundSelection( "\n\n> ", "" ),
+ activeFileEditorIsNull );
+ Action insertCodeAction = new Action( Messages.get( "MainWindow.insertCodeAction" ), "Shortcut+K", CODE,
+ e -> getActiveEditor().surroundSelection( "`", "`" ),
+ activeFileEditorIsNull );
+ Action insertFencedCodeBlockAction = new Action( Messages.get( "MainWindow.insertFencedCodeBlockAction" ), "Shortcut+Shift+K", FILE_CODE_ALT,
+ e -> getActiveEditor().surroundSelection( "\n\n```\n", "\n```\n\n", Messages.get( "MainWindow.insertFencedCodeBlockText" ) ),
+ activeFileEditorIsNull );
+
+ Action insertLinkAction = new Action( Messages.get( "MainWindow.insertLinkAction" ), "Shortcut+L", LINK,
+ e -> getActiveEditor().insertLink(),
+ activeFileEditorIsNull );
+ Action insertImageAction = new Action( Messages.get( "MainWindow.insertImageAction" ), "Shortcut+G", PICTURE_ALT,
+ e -> getActiveEditor().insertImage(),
+ activeFileEditorIsNull );
+
+ Action insertHeader1Action = new Action( Messages.get( "MainWindow.insertHeader1Action" ), "Shortcut+1", HEADER,
+ e -> getActiveEditor().surroundSelection( "\n\n# ", "", Messages.get( "MainWindow.insertHeader1Text" ) ),
+ activeFileEditorIsNull );
+ Action insertHeader2Action = new Action( Messages.get( "MainWindow.insertHeader2Action" ), "Shortcut+2", HEADER,
+ e -> getActiveEditor().surroundSelection( "\n\n## ", "", Messages.get( "MainWindow.insertHeader2Text" ) ),
+ activeFileEditorIsNull );
+ Action insertHeader3Action = new Action( Messages.get( "MainWindow.insertHeader3Action" ), "Shortcut+3", HEADER,
+ e -> getActiveEditor().surroundSelection( "\n\n### ", "", Messages.get( "MainWindow.insertHeader3Text" ) ),
+ activeFileEditorIsNull );
+ Action insertHeader4Action = new Action( Messages.get( "MainWindow.insertHeader4Action" ), "Shortcut+4", HEADER,
+ e -> getActiveEditor().surroundSelection( "\n\n#### ", "", Messages.get( "MainWindow.insertHeader4Text" ) ),
+ activeFileEditorIsNull );
+ Action insertHeader5Action = new Action( Messages.get( "MainWindow.insertHeader5Action" ), "Shortcut+5", HEADER,
+ e -> getActiveEditor().surroundSelection( "\n\n##### ", "", Messages.get( "MainWindow.insertHeader5Text" ) ),
+ activeFileEditorIsNull );
+ Action insertHeader6Action = new Action( Messages.get( "MainWindow.insertHeader6Action" ), "Shortcut+6", HEADER,
+ e -> getActiveEditor().surroundSelection( "\n\n###### ", "", Messages.get( "MainWindow.insertHeader6Text" ) ),
+ activeFileEditorIsNull );
+
+ Action insertUnorderedListAction = new Action( Messages.get( "MainWindow.insertUnorderedListAction" ), "Shortcut+U", LIST_UL,
+ e -> getActiveEditor().surroundSelection( "\n\n* ", "" ),
+ activeFileEditorIsNull );
+ Action insertOrderedListAction = new Action( Messages.get( "MainWindow.insertOrderedListAction" ), "Shortcut+Shift+O", LIST_OL,
+ e -> getActiveEditor().surroundSelection( "\n\n1. ", "" ),
+ activeFileEditorIsNull );
+ Action insertHorizontalRuleAction = new Action( Messages.get( "MainWindow.insertHorizontalRuleAction" ), "Shortcut+H", null,
+ e -> getActiveEditor().surroundSelection( "\n\n---\n\n", "" ),
+ activeFileEditorIsNull );
+
+ // Tools actions
+ Action toolsOptionsAction = new Action( Messages.get( "MainWindow.toolsOptionsAction" ), "Shortcut+,", null, e -> toolsOptions() );
+
+ // Help actions
+ Action helpAboutAction = new Action( Messages.get( "MainWindow.helpAboutAction" ), null, null, e -> helpAbout() );
+
+ //---- MenuBar ----
+ Menu fileMenu = ActionUtils.createMenu( Messages.get( "MainWindow.fileMenu" ),
+ fileNewAction,
+ fileOpenAction,
+ null,
+ fileCloseAction,
+ fileCloseAllAction,
+ null,
+ fileSaveAction,
+ fileSaveAllAction,
+ null,
+ fileExitAction );
+
+ Menu editMenu = ActionUtils.createMenu( Messages.get( "MainWindow.editMenu" ),
+ editUndoAction,
+ editRedoAction );
+
+ Menu insertMenu = ActionUtils.createMenu( Messages.get( "MainWindow.insertMenu" ),
+ insertBoldAction,
+ insertItalicAction,
+ insertStrikethroughAction,
+ insertBlockquoteAction,
+ insertCodeAction,
+ insertFencedCodeBlockAction,
+ null,
+ insertLinkAction,
+ insertImageAction,
+ null,
+ insertHeader1Action,
+ insertHeader2Action,
+ insertHeader3Action,
+ insertHeader4Action,
+ insertHeader5Action,
+ insertHeader6Action,
+ null,
+ insertUnorderedListAction,
+ insertOrderedListAction,
+ insertHorizontalRuleAction );
+
+ Menu toolsMenu = ActionUtils.createMenu( Messages.get( "MainWindow.toolsMenu" ),
+ toolsOptionsAction );
+
+ Menu helpMenu = ActionUtils.createMenu( Messages.get( "MainWindow.helpMenu" ),
+ helpAboutAction );
+
+ menuBar = new MenuBar( fileMenu, editMenu, insertMenu, toolsMenu, helpMenu );
+
+ //---- ToolBar ----
+ ToolBar toolBar = ActionUtils.createToolBar(
+ fileNewAction,
+ fileOpenAction,
+ fileSaveAction,
+ null,
+ editUndoAction,
+ editRedoAction,
+ null,
+ insertBoldAction,
+ insertItalicAction,
+ insertBlockquoteAction,
+ insertCodeAction,
+ insertFencedCodeBlockAction,
+ null,
+ insertLinkAction,
+ insertImageAction,
+ null,
+ insertHeader1Action,
+ null,
+ insertUnorderedListAction,
+ insertOrderedListAction );
+
+ return new VBox( menuBar, toolBar );
+ }
+
+ private MarkdownEditorPane getActiveEditor() {
+ return fileEditorTabPane.getActiveFileEditor().getEditor();
+ }
+
+ /**
+ * Creates a boolean property that is bound to another boolean value of the
+ * active editor.
+ */
+ private BooleanProperty createActiveBooleanProperty( Function<FileEditor, ObservableBooleanValue> func ) {
+ BooleanProperty b = new SimpleBooleanProperty();
+ FileEditor fileEditor = fileEditorTabPane.getActiveFileEditor();
+ if( fileEditor != null ) {
+ b.bind( func.apply( fileEditor ) );
+ }
+ fileEditorTabPane.activeFileEditorProperty().addListener( (observable, oldFileEditor, newFileEditor) -> {
+ b.unbind();
+ if( newFileEditor != null ) {
+ b.bind( func.apply( newFileEditor ) );
+ } else {
+ b.set( false );
+ }
+ } );
+ return b;
+ }
+
+ Alert createAlert( AlertType alertType, String title,
+ String contentTextFormat, Object... contentTextArgs ) {
+ Alert alert = new Alert( alertType );
+ alert.setTitle( title );
+ alert.setHeaderText( null );
+ alert.setContentText( MessageFormat.format( contentTextFormat, contentTextArgs ) );
+ alert.initOwner( getScene().getWindow() );
+ return alert;
+ }
+
+ //---- File actions -------------------------------------------------------
+ private void fileNew() {
+ fileEditorTabPane.newEditor();
+ }
+
+ private void fileOpen() {
+ fileEditorTabPane.openEditor();
+ }
+
+ private void fileClose() {
+ fileEditorTabPane.closeEditor( fileEditorTabPane.getActiveFileEditor(), true );
+ }
+
+ private void fileCloseAll() {
+ fileEditorTabPane.closeAllEditors();
+ }
+
+ private void fileSave() {
+ fileEditorTabPane.saveEditor( fileEditorTabPane.getActiveFileEditor() );
+ }
+
+ private void fileSaveAll() {
+ fileEditorTabPane.saveAllEditors();
+ }
+
+ private void fileExit() {
+ Window window = scene.getWindow();
+ Event.fireEvent( window, new WindowEvent( window, WindowEvent.WINDOW_CLOSE_REQUEST ) );
+ }
+
+ //---- Tools actions ------------------------------------------------------
+ private void toolsOptions() {
+ OptionsDialog dialog = new OptionsDialog( getScene().getWindow() );
+ dialog.showAndWait();
+ }
+
+ //---- Help actions -------------------------------------------------------
+ private void helpAbout() {
+ Alert alert = new Alert( AlertType.INFORMATION );
+ alert.setTitle( Messages.get( "MainWindow.about.title" ) );
+ alert.setHeaderText( Messages.get( "MainWindow.about.headerText" ) );
+ alert.setContentText( Messages.get( "MainWindow.about.contentText" ) );
+ alert.setGraphic( new ImageView( new Image( "org/markdownwriterfx/markdownwriterfx32.png" ) ) );
+ alert.initOwner( getScene().getWindow() );
+
+ alert.showAndWait();
+ }
}
src/main/java/org/markdownwriterfx/MarkdownWriterFXApp.java
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package org.markdownwriterfx;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import static java.nio.file.Paths.get;
+import java.util.List;
import java.util.prefs.Preferences;
+import static java.util.prefs.Preferences.userRoot;
import javafx.application.Application;
+import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
import org.markdownwriterfx.options.Options;
import org.markdownwriterfx.util.StageState;
/**
* Markdown Writer FX application.
*
* @author Karl Tauber
*/
-public class MarkdownWriterFXApp
- extends Application
-{
- private static Application app;
+public final class MarkdownWriterFXApp extends Application implements ApplicationProperty {
- private MainWindow mainWindow;
- @SuppressWarnings("unused")
- private StageState stageState;
+ private static Application app;
- public static void main(String[] args) {
- launch(args);
- }
+ private MainWindow mainWindow;
+ private StageState stageState;
+ private PropertiesConfiguration configuration;
- @Override
- public void start(Stage primaryStage) throws Exception {
- app = this;
- Options.load(getOptions());
+ public static void main( String[] args ) {
+ launch( args );
+ }
- mainWindow = new MainWindow();
+ /**
+ * Application entry point.
+ *
+ * @param stage The primary application stage.
+ *
+ * @throws Exception Could not read configuration file.
+ */
+ @Override
+ public void start( Stage stage ) throws Exception {
+ initApplication();
+ initConfiguration();
+ initOptions();
+ initWindow();
+ initState( stage );
+ initStage( stage );
- stageState = new StageState(primaryStage, getState());
+ stage.show();
+ }
- primaryStage.getIcons().addAll(
- new Image("org/markdownwriterfx/markdownwriterfx16.png"),
- new Image("org/markdownwriterfx/markdownwriterfx32.png"),
- new Image("org/markdownwriterfx/markdownwriterfx128.png"),
- new Image("org/markdownwriterfx/markdownwriterfx256.png"),
- new Image("org/markdownwriterfx/markdownwriterfx512.png"));
- primaryStage.setTitle("Markdown Writer FX");
- primaryStage.setScene(mainWindow.getScene());
- primaryStage.show();
- }
+ public PropertiesConfiguration getConfiguration() {
+ return this.configuration;
+ }
- public static void showDocument(String uri) {
- app.getHostServices().showDocument(uri);
- }
+ private String getApplicationTitle() {
+ return getProperty( "application.title", "Markdown Writer FX" );
+ }
- static private Preferences getPrefsRoot() {
- return Preferences.userRoot().node("markdownwriterfx");
- }
+ @Override
+ public String getProperty( String property, String defaultValue ) {
+ return getConfiguration().getString( property, defaultValue );
+ }
+
+ @Override
+ public List<Object> getPropertyList(String property, List<String> defaults ) {
+ return getConfiguration().getList( property );
+ }
- static Preferences getOptions() {
- return getPrefsRoot().node("options");
- }
+ private void initApplication() {
+ app = this;
+ }
- public static Preferences getState() {
- return getPrefsRoot().node("state");
- }
+ protected void initConfiguration()
+ throws ConfigurationException, URISyntaxException, IOException {
+ setConfiguration( createPropertiesConfiguration() );
+ }
+
+ private PropertiesConfiguration createPropertiesConfiguration()
+ throws ConfigurationException {
+ final URL url = getConfigurationSource();
+
+ return url == null
+ ? new PropertiesConfiguration()
+ : new PropertiesConfiguration( url );
+ }
+
+ private URL getConfigurationSource() {
+ return getClass().getResource( getConfigurationName() );
+ }
+
+ private String getConfigurationName() {
+ return "settings.properties";
+ }
+
+ protected void initOptions() {
+ Options.load( getOptions() );
+ }
+
+ private void initWindow() {
+ setWindow( new MainWindow( this ) );
+ }
+
+ private void initState( Stage stage ) {
+ stageState = new StageState( stage, getState() );
+ }
+
+ private void initStage( Stage stage ) {
+ stage.getIcons().addAll(
+ new Image( "org/markdownwriterfx/markdownwriterfx16.png" ),
+ new Image( "org/markdownwriterfx/markdownwriterfx32.png" ),
+ new Image( "org/markdownwriterfx/markdownwriterfx128.png" ),
+ new Image( "org/markdownwriterfx/markdownwriterfx256.png" ),
+ new Image( "org/markdownwriterfx/markdownwriterfx512.png" ) );
+ stage.setTitle( getApplicationTitle() );
+ stage.setScene( getScene() );
+ }
+
+ private Scene getScene() {
+ return getMainWindow().getScene();
+ }
+
+ protected MainWindow getMainWindow() {
+ return this.mainWindow;
+ }
+
+ private void setWindow( MainWindow mainWindow ) {
+ this.mainWindow = mainWindow;
+ }
+
+ private StageState getStageState() {
+ return this.stageState;
+ }
+
+ private void setStageState( StageState stageState ) {
+ this.stageState = stageState;
+ }
+
+ private static Application getApplication() {
+ return app;
+ }
+
+ protected void setConfiguration( PropertiesConfiguration configuration ) {
+ this.configuration = configuration;
+ }
+
+ public static void showDocument( String uri ) {
+ getApplication().getHostServices().showDocument( uri );
+ }
+
+ static private Preferences getRootPreferences() {
+ return userRoot().node( "markdownwriterfx" );
+ }
+
+ private static Preferences getOptions() {
+ return getRootPreferences().node( "options" );
+ }
+
+ public static Preferences getState() {
+ return getRootPreferences().node( "state" );
+ }
+
+ /**
+ * Unused.
+ *
+ * @return
+ *
+ * @throws URISyntaxException
+ */
+ private Path getConfigurationPath() throws URISyntaxException {
+ final Path appDir = getApplicationDirectory();
+ return get( appDir.toString(), getConfigurationName() );
+ }
+
+ /**
+ * Unused.
+ *
+ * @return
+ *
+ * @throws URISyntaxException
+ */
+ private Path getApplicationDirectory() throws URISyntaxException {
+ final Path appPath = get( getApplicationPath() );
+ return appPath.getParent();
+ }
+
+ /**
+ * Unused. Returns the path to the application's start-up directory.
+ *
+ * @return A Path where the main class is running.
+ */
+ private String getApplicationPath() throws URISyntaxException {
+ return getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
+ }
+
}