| | }; |
| | |
| | - tabs.addListener( (ListChangeListener<Tab>)c -> { |
| | - while( c.next() ) { |
| | - if( c.wasAdded() ) { |
| | - c.getAddedSubList().stream().forEach( (tab) -> { |
| | - ((FileEditorTab)tab.getUserData()).modifiedProperty().addListener( modifiedListener ); |
| | - } ); |
| | - } else if( c.wasRemoved() ) { |
| | - c.getRemoved().stream().forEach( (tab) -> { |
| | - ((FileEditorTab)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(); |
| | - } |
| | - |
| | - public <T extends Event, U extends T> void addEventListener( |
| | - final EventPattern<? super T, ? extends U> event, |
| | - final Consumer<? super U> consumer ) { |
| | - getActiveFileEditor().addEventListener( event, consumer ); |
| | - } |
| | - |
| | - /** |
| | - * Delegates to the active file editor pane, and, ultimately, to its text |
| | - * area. |
| | - * |
| | - * @param map The map of methods to events. |
| | - */ |
| | - public void addEventListener( final InputMap<InputEvent> map ) { |
| | - getActiveFileEditor().addEventListener( map ); |
| | - } |
| | - |
| | - public void removeEventListener( final InputMap<InputEvent> map ) { |
| | - getActiveFileEditor().removeEventListener( map ); |
| | - } |
| | - |
| | - @Override |
| | - public void changed( |
| | - final ObservableValue<? extends Tab> observable, |
| | - final Tab oldTab, |
| | - final Tab newTab ) { |
| | - |
| | - if( newTab != null ) { |
| | - final FileEditorTab tab = (FileEditorTab)newTab; |
| | - |
| | - this.activeFileEditor.set( (FileEditorTab)newTab.getUserData() ); |
| | - } |
| | - } |
| | - |
| | - Node getNode() { |
| | - return this; |
| | - } |
| | - |
| | - /** |
| | - * Allows clients to manipulate the editor content directly. |
| | - * |
| | - * @return The text area for the active file editor. |
| | - */ |
| | - public StyledTextArea getEditor() { |
| | - return getActiveFileEditor().getEditorPane().getEditor(); |
| | - } |
| | - |
| | - public FileEditorTab getActiveFileEditor() { |
| | - return this.activeFileEditor.get(); |
| | - } |
| | - |
| | - ReadOnlyObjectProperty<FileEditorTab> activeFileEditorProperty() { |
| | - return this.activeFileEditor.getReadOnlyProperty(); |
| | - } |
| | - |
| | - ReadOnlyBooleanProperty anyFileEditorModifiedProperty() { |
| | - return this.anyFileEditorModified.getReadOnlyProperty(); |
| | - } |
| | - |
| | - private FileEditorTab createFileEditor( Path path ) { |
| | - final FileEditorTab fileEditor = new FileEditorTab( path ); |
| | - |
| | - fileEditor.setOnCloseRequest( e -> { |
| | - if( !canCloseEditor( fileEditor ) ) { |
| | - e.consume(); |
| | - } |
| | - } ); |
| | - |
| | - return fileEditor; |
| | - } |
| | - |
| | - FileEditorTab newEditor() { |
| | - final FileEditorTab tab = createFileEditor( null ); |
| | - |
| | - getTabs().add( tab ); |
| | - getSelectionModel().select( tab ); |
| | - return tab; |
| | - } |
| | - |
| | - List<FileEditorTab> openFileDialog() { |
| | - final FileChooser dialog |
| | - = createFileChooser( get( "Dialog.file.choose.open.title" ) ); |
| | - final List<File> files = dialog.showOpenMultipleDialog( getWindow() ); |
| | - |
| | - return (files != null && !files.isEmpty()) |
| | - ? openFiles( files ) |
| | - : new ArrayList<>(); |
| | - } |
| | - |
| | - /** |
| | - * Opens the files into new editors, unless one of those files was a |
| | - * definition file. The definition file is loaded into the definition pane, |
| | - * but only the first one selected (multiple definition files will result in a |
| | - * warning). |
| | - * |
| | - * @param files The list of non-definition files that the were requested to |
| | - * open. |
| | - * |
| | - * @return A list of files that can be opened in text editors. |
| | - */ |
| | - private List<FileEditorTab> openFiles( final List<File> files ) { |
| | - final List<FileEditorTab> openedEditors = new ArrayList<>(); |
| | - |
| | - final FileTypePredicate predicate |
| | - = new FileTypePredicate( createExtensionFilter( "definition" ).getExtensions() ); |
| | - |
| | - // The user might have opened muliple definitions files. These will |
| | - // be discarded from the text editable files. |
| | - final List<File> definitions |
| | - = files.stream().filter( predicate ).collect( Collectors.toList() ); |
| | - |
| | - // Create a modifiable list to remove any definition files that were |
| | - // opened. |
| | - final List<File> editors = new ArrayList<>( files ); |
| | - editors.removeAll( definitions ); |
| | - |
| | - // If there are any editor-friendly files opened (e.g,. Markdown, XML), then |
| | - // open them up in new tabs. |
| | - if( editors.size() > 0 ) { |
| | - saveLastDirectory( editors.get( 0 ) ); |
| | - openedEditors.addAll( openEditors( editors, 0 ) ); |
| | - } |
| | - |
| | - if( definitions.size() > 0 ) { |
| | - openDefinition( definitions.get( 0 ) ); |
| | - } |
| | - |
| | - return openedEditors; |
| | - } |
| | - |
| | - private List<FileEditorTab> openEditors( final List<File> files, final int activeIndex ) { |
| | - final List<FileEditorTab> editors = new ArrayList<>(); |
| | - final List<Tab> tabs = getTabs(); |
| | - |
| | - // Close single unmodified "Untitled" tab. |
| | - if( tabs.size() == 1 ) { |
| | - final FileEditorTab fileEditor = (FileEditorTab)(tabs.get( 0 ).getUserData()); |
| | - |
| | - if( fileEditor.getPath() == null && !fileEditor.isModified() ) { |
| | - closeEditor( fileEditor, false ); |
| | - } |
| | - } |
| | - |
| | - for( int i = 0; i < files.size(); i++ ) { |
| | - Path path = files.get( i ).toPath(); |
| | - |
| | - // Check whether file is already opened. |
| | - FileEditorTab fileEditor = findEditor( path ); |
| | - |
| | - if( fileEditor == null ) { |
| | - fileEditor = createFileEditor( path ); |
| | - getTabs().add( fileEditor ); |
| | - editors.add( fileEditor ); |
| | - } |
| | - |
| | - // Select first file. |
| | - if( i == activeIndex ) { |
| | - getSelectionModel().select( fileEditor ); |
| | - } |
| | - } |
| | - |
| | - return editors; |
| | - } |
| | - |
| | - /** |
| | - * Called when the user has opened a definition file (using the file open |
| | - * dialog box). This will replace the current set of definitions for the |
| | - * active tab. |
| | - * |
| | - * @param definition The file to open. |
| | - */ |
| | - private void openDefinition( final File definition ) { |
| | - System.out.println( "open definition file: " + definition.toString() ); |
| | - } |
| | - |
| | - boolean saveEditor( final FileEditorTab fileEditor ) { |
| | - if( fileEditor == null || !fileEditor.isModified() ) { |
| | - return true; |
| | - } |
| | - |
| | - if( fileEditor.getPath() == null ) { |
| | - getSelectionModel().select( fileEditor ); |
| | - |
| | - final FileChooser fileChooser = createFileChooser( Messages.get( "Dialog.file.choose.save.title" ) ); |
| | - final File file = fileChooser.showSaveDialog( getWindow() ); |
| | - if( file == null ) { |
| | - return false; |
| | - } |
| | - |
| | - saveLastDirectory( file ); |
| | - fileEditor.setPath( file.toPath() ); |
| | - } |
| | - |
| | - return fileEditor.save(); |
| | - } |
| | - |
| | - boolean saveAllEditors() { |
| | - FileEditorTab[] allEditors = getAllEditors(); |
| | - |
| | - boolean success = true; |
| | - for( FileEditorTab fileEditor : allEditors ) { |
| | - if( !saveEditor( fileEditor ) ) { |
| | - success = false; |
| | - } |
| | - } |
| | - |
| | - return success; |
| | - } |
| | - |
| | - boolean canCloseEditor( final FileEditorTab fileEditor ) { |
| | - if( !fileEditor.isModified() ) { |
| | - return true; |
| | - } |
| | - |
| | - final AlertMessage message = getAlertService().createAlertMessage( |
| | - Messages.get( "Alert.file.close.title" ), |
| | - Messages.get( "Alert.file.close.text" ), |
| | - fileEditor.getText() |
| | - ); |
| | - |
| | - final Alert alert = getAlertService().createAlertConfirmation( message ); |
| | - final ButtonType response = alert.showAndWait().get(); |
| | - |
| | - return response == YES ? saveEditor( fileEditor ) : response == NO; |
| | - } |
| | - |
| | - private AlertService getAlertService() { |
| | - return this.alertService; |
| | - } |
| | - |
| | - boolean closeEditor( FileEditorTab fileEditor, boolean save ) { |
| | - if( fileEditor == null ) { |
| | - return true; |
| | - } |
| | - |
| | - final Tab tab = fileEditor; |
| | - |
| | - if( save ) { |
| | - Event event = new Event( tab, tab, Tab.TAB_CLOSE_REQUEST_EVENT ); |
| | - Event.fireEvent( tab, event ); |
| | - if( event.isConsumed() ) { |
| | - return false; |
| | - } |
| | - } |
| | - |
| | - getTabs().remove( tab ); |
| | - if( tab.getOnClosed() != null ) { |
| | - Event.fireEvent( tab, new Event( Tab.CLOSED_EVENT ) ); |
| | - } |
| | - |
| | - return true; |
| | - } |
| | - |
| | - boolean closeAllEditors() { |
| | - FileEditorTab[] allEditors = getAllEditors(); |
| | - FileEditorTab 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++ ) { |
| | - FileEditorTab fileEditor = allEditors[ i ]; |
| | - if( fileEditor == activeEditor ) { |
| | - continue; |
| | - } |
| | - |
| | - if( fileEditor.isModified() ) { |
| | - // activate the modified tab to make its modified content visible to the user |
| | - getSelectionModel().select( i ); |
| | - |
| | - if( !canCloseEditor( fileEditor ) ) { |
| | - return false; |
| | - } |
| | - } |
| | - } |
| | - |
| | - // Close all tabs. |
| | - for( final FileEditorTab fileEditor : allEditors ) { |
| | - if( !closeEditor( fileEditor, false ) ) { |
| | - return false; |
| | - } |
| | - } |
| | - |
| | - saveState( allEditors, activeEditor ); |
| | - |
| | - return getTabs().isEmpty(); |
| | - } |
| | - |
| | - private FileEditorTab[] getAllEditors() { |
| | - final ObservableList<Tab> tabs = getTabs(); |
| | - final FileEditorTab[] allEditors = new FileEditorTab[ tabs.size() ]; |
| | - final int length = tabs.size(); |
| | - |
| | - for( int i = 0; i < length; i++ ) { |
| | - allEditors[ i ] = (FileEditorTab)tabs.get( i ).getUserData(); |
| | - } |
| | - |
| | - return allEditors; |
| | - } |
| | - |
| | - private FileEditorTab findEditor( Path path ) { |
| | - for( final Tab tab : getTabs() ) { |
| | - final FileEditorTab fileEditor = (FileEditorTab)tab.getUserData(); |
| | - |
| | - if( path.equals( fileEditor.getPath() ) ) { |
| | - return fileEditor; |
| | - } |
| | - } |
| | - |
| | - return null; |
| | - } |
| | - |
| | - private Settings getSettings() { |
| | - return this.settings; |
| | - } |
| | - |
| | - private FileChooser createFileChooser( String title ) { |
| | - final FileChooser fileChooser = new FileChooser(); |
| | - |
| | - fileChooser.setTitle( title ); |
| | - fileChooser.getExtensionFilters().addAll( |
| | - createExtensionFilters() ); |
| | - |
| | - final String lastDirectory = getState().get( "lastDirectory", null ); |
| | - File file = new File( (lastDirectory != null) ? lastDirectory : "." ); |
| | - |
| | - if( !file.isDirectory() ) { |
| | - file = new File( "." ); |
| | - } |
| | - |
| | - fileChooser.setInitialDirectory( file ); |
| | - return fileChooser; |
| | - } |
| | - |
| | - private List<ExtensionFilter> createExtensionFilters() { |
| | - final List<ExtensionFilter> list = new ArrayList<>(); |
| | - |
| | - // TODO: Return a list of all properties that match the filter prefix. |
| | - // This will allow dynamic filters to be added and removed just by |
| | - // updating the properties file. |
| | - list.add( createExtensionFilter( "markdown" ) ); |
| | - list.add( createExtensionFilter( "definition" ) ); |
| | - list.add( createExtensionFilter( "xml" ) ); |
| | - list.add( createExtensionFilter( "all" ) ); |
| | - return list; |
| | - } |
| | - |
| | - private ExtensionFilter createExtensionFilter( final String filetype ) { |
| | - final String tKey = String.format( "%s.title.%s", FILTER_PREFIX, filetype ); |
| | - final String eKey = String.format( "%s.ext.%s", FILTER_PREFIX, filetype ); |
| | - |
| | - return new ExtensionFilter( Messages.get( tKey ), getExtensions( eKey ) ); |
| | - } |
| | - |
| | - private List<String> getExtensions( final String key ) { |
| | - return getStringSettingList( key ); |
| | - } |
| | - |
| | - private List<String> getStringSettingList( String key ) { |
| | - return getStringSettingList( key, null ); |
| | - } |
| | - |
| | - private List<String> getStringSettingList( String key, List<String> values ) { |
| | - return getSettings().getStringSettingList( key, values ); |
| | - } |
| | - |
| | - private void saveLastDirectory( File file ) { |
| | - getState().put( "lastDirectory", file.getParent() ); |
| | - } |
| | - |
| | - private void restoreState() { |
| | + tabs.addListener( (ListChangeListener<Tab>)change -> { |
| | + while( change.next() ) { |
| | + if( change.wasAdded() ) { |
| | + change.getAddedSubList().stream().forEach( (tab) -> { |
| | + ((FileEditorTab)tab.getUserData()).modifiedProperty().addListener( modifiedListener ); |
| | + } ); |
| | + } else if( change.wasRemoved() ) { |
| | + change.getRemoved().stream().forEach( (tab) -> { |
| | + ((FileEditorTab)tab.getUserData()).modifiedProperty().removeListener( modifiedListener ); |
| | + } ); |
| | + } |
| | + } |
| | + |
| | + // Changes in the tabs may also change anyFileEditorModified property |
| | + // (e.g. closed modified file) |
| | + modifiedListener.changed( null, null, null ); |
| | + } ); |
| | + } |
| | + |
| | + public <T extends Event, U extends T> void addEventListener( |
| | + final EventPattern<? super T, ? extends U> event, |
| | + final Consumer<? super U> consumer ) { |
| | + getActiveFileEditor().addEventListener( event, consumer ); |
| | + } |
| | + |
| | + /** |
| | + * Delegates to the active file editor pane, and, ultimately, to its text |
| | + * area. |
| | + * |
| | + * @param map The map of methods to events. |
| | + */ |
| | + public void addEventListener( final InputMap<InputEvent> map ) { |
| | + getActiveFileEditor().addEventListener( map ); |
| | + } |
| | + |
| | + public void removeEventListener( final InputMap<InputEvent> map ) { |
| | + getActiveFileEditor().removeEventListener( map ); |
| | + } |
| | + |
| | + @Override |
| | + public void changed( |
| | + final ObservableValue<? extends Tab> observable, |
| | + final Tab oldTab, |
| | + final Tab newTab ) { |
| | + |
| | + if( newTab != null ) { |
| | + this.activeFileEditor.set( (FileEditorTab)newTab.getUserData() ); |
| | + } |
| | + } |
| | + |
| | + Node getNode() { |
| | + return this; |
| | + } |
| | + |
| | + /** |
| | + * Allows clients to manipulate the editor content directly. |
| | + * |
| | + * @return The text area for the active file editor. |
| | + */ |
| | + public StyledTextArea getEditor() { |
| | + return getActiveFileEditor().getEditorPane().getEditor(); |
| | + } |
| | + |
| | + public FileEditorTab getActiveFileEditor() { |
| | + return this.activeFileEditor.get(); |
| | + } |
| | + |
| | + ReadOnlyObjectProperty<FileEditorTab> activeFileEditorProperty() { |
| | + return this.activeFileEditor.getReadOnlyProperty(); |
| | + } |
| | + |
| | + ReadOnlyBooleanProperty anyFileEditorModifiedProperty() { |
| | + return this.anyFileEditorModified.getReadOnlyProperty(); |
| | + } |
| | + |
| | + private FileEditorTab createFileEditor( final Path path ) { |
| | + final FileEditorTab tab = new FileEditorTab( path ); |
| | + |
| | + tab.setOnCloseRequest( e -> { |
| | + if( !canCloseEditor( tab ) ) { |
| | + e.consume(); |
| | + } |
| | + } ); |
| | + |
| | + return tab; |
| | + } |
| | + |
| | + /** |
| | + * Called when the user selects New from the File menu. |
| | + * |
| | + * @return The newly added tab. |
| | + */ |
| | + FileEditorTab newEditor() { |
| | + final FileEditorTab tab = createFileEditor( null ); |
| | + |
| | + getTabs().add( tab ); |
| | + getSelectionModel().select( tab ); |
| | + return tab; |
| | + } |
| | + |
| | + List<FileEditorTab> openFileDialog() { |
| | + final FileChooser dialog |
| | + = createFileChooser( get( "Dialog.file.choose.open.title" ) ); |
| | + final List<File> files = dialog.showOpenMultipleDialog( getWindow() ); |
| | + |
| | + return (files != null && !files.isEmpty()) |
| | + ? openFiles( files ) |
| | + : new ArrayList<>(); |
| | + } |
| | + |
| | + /** |
| | + * Opens the files into new editors, unless one of those files was a |
| | + * definition file. The definition file is loaded into the definition pane, |
| | + * but only the first one selected (multiple definition files will result in a |
| | + * warning). |
| | + * |
| | + * @param files The list of non-definition files that the were requested to |
| | + * open. |
| | + * |
| | + * @return A list of files that can be opened in text editors. |
| | + */ |
| | + private List<FileEditorTab> openFiles( final List<File> files ) { |
| | + final List<FileEditorTab> openedEditors = new ArrayList<>(); |
| | + |
| | + final FileTypePredicate predicate |
| | + = new FileTypePredicate( createExtensionFilter( "definition" ).getExtensions() ); |
| | + |
| | + // The user might have opened muliple definitions files. These will |
| | + // be discarded from the text editable files. |
| | + final List<File> definitions |
| | + = files.stream().filter( predicate ).collect( Collectors.toList() ); |
| | + |
| | + // Create a modifiable list to remove any definition files that were |
| | + // opened. |
| | + final List<File> editors = new ArrayList<>( files ); |
| | + editors.removeAll( definitions ); |
| | + |
| | + // If there are any editor-friendly files opened (e.g,. Markdown, XML), then |
| | + // open them up in new tabs. |
| | + if( editors.size() > 0 ) { |
| | + saveLastDirectory( editors.get( 0 ) ); |
| | + openedEditors.addAll( openEditors( editors, 0 ) ); |
| | + } |
| | + |
| | + if( definitions.size() > 0 ) { |
| | + openDefinition( definitions.get( 0 ) ); |
| | + } |
| | + |
| | + return openedEditors; |
| | + } |
| | + |
| | + private List<FileEditorTab> openEditors( final List<File> files, final int activeIndex ) { |
| | + final List<FileEditorTab> editors = new ArrayList<>(); |
| | + final List<Tab> tabs = getTabs(); |
| | + |
| | + // Close single unmodified "Untitled" tab. |
| | + if( tabs.size() == 1 ) { |
| | + final FileEditorTab fileEditor = (FileEditorTab)(tabs.get( 0 ).getUserData()); |
| | + |
| | + if( fileEditor.getPath() == null && !fileEditor.isModified() ) { |
| | + closeEditor( fileEditor, false ); |
| | + } |
| | + } |
| | + |
| | + for( int i = 0; i < files.size(); i++ ) { |
| | + Path path = files.get( i ).toPath(); |
| | + |
| | + // Check whether file is already opened. |
| | + FileEditorTab fileEditor = findEditor( path ); |
| | + |
| | + if( fileEditor == null ) { |
| | + fileEditor = createFileEditor( path ); |
| | + getTabs().add( fileEditor ); |
| | + editors.add( fileEditor ); |
| | + } |
| | + |
| | + // Select first file. |
| | + if( i == activeIndex ) { |
| | + getSelectionModel().select( fileEditor ); |
| | + } |
| | + } |
| | + |
| | + return editors; |
| | + } |
| | + |
| | + /** |
| | + * Called when the user has opened a definition file (using the file open |
| | + * dialog box). This will replace the current set of definitions for the |
| | + * active tab. |
| | + * |
| | + * @param definition The file to open. |
| | + */ |
| | + private void openDefinition( final File definition ) { |
| | + System.out.println( "open definition file: " + definition.toString() ); |
| | + } |
| | + |
| | + boolean saveEditor( final FileEditorTab fileEditor ) { |
| | + if( fileEditor == null || !fileEditor.isModified() ) { |
| | + return true; |
| | + } |
| | + |
| | + if( fileEditor.getPath() == null ) { |
| | + getSelectionModel().select( fileEditor ); |
| | + |
| | + final FileChooser fileChooser = createFileChooser( Messages.get( "Dialog.file.choose.save.title" ) ); |
| | + final File file = fileChooser.showSaveDialog( getWindow() ); |
| | + if( file == null ) { |
| | + return false; |
| | + } |
| | + |
| | + saveLastDirectory( file ); |
| | + fileEditor.setPath( file.toPath() ); |
| | + } |
| | + |
| | + return fileEditor.save(); |
| | + } |
| | + |
| | + boolean saveAllEditors() { |
| | + final FileEditorTab[] allEditors = getAllEditors(); |
| | + |
| | + boolean success = true; |
| | + for( FileEditorTab fileEditor : allEditors ) { |
| | + if( !saveEditor( fileEditor ) ) { |
| | + success = false; |
| | + } |
| | + } |
| | + |
| | + return success; |
| | + } |
| | + |
| | + boolean canCloseEditor( final FileEditorTab tab ) { |
| | + if( !tab.isModified() ) { |
| | + return true; |
| | + } |
| | + |
| | + final AlertMessage message = getAlertService().createAlertMessage( |
| | + Messages.get( "Alert.file.close.title" ), |
| | + Messages.get( "Alert.file.close.text" ), |
| | + tab.getText() |
| | + ); |
| | + |
| | + final Alert alert = getAlertService().createAlertConfirmation( message ); |
| | + final ButtonType response = alert.showAndWait().get(); |
| | + |
| | + return response == YES ? saveEditor( tab ) : response == NO; |
| | + } |
| | + |
| | + private AlertService getAlertService() { |
| | + return this.alertService; |
| | + } |
| | + |
| | + boolean closeEditor( FileEditorTab fileEditor, boolean save ) { |
| | + if( fileEditor == null ) { |
| | + return true; |
| | + } |
| | + |
| | + final Tab tab = fileEditor; |
| | + |
| | + if( save ) { |
| | + Event event = new Event( tab, tab, Tab.TAB_CLOSE_REQUEST_EVENT ); |
| | + Event.fireEvent( tab, event ); |
| | + if( event.isConsumed() ) { |
| | + return false; |
| | + } |
| | + } |
| | + |
| | + getTabs().remove( tab ); |
| | + if( tab.getOnClosed() != null ) { |
| | + Event.fireEvent( tab, new Event( Tab.CLOSED_EVENT ) ); |
| | + } |
| | + |
| | + return true; |
| | + } |
| | + |
| | + boolean closeAllEditors() { |
| | + FileEditorTab[] allEditors = getAllEditors(); |
| | + FileEditorTab 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++ ) { |
| | + FileEditorTab fileEditor = allEditors[ i ]; |
| | + if( fileEditor == activeEditor ) { |
| | + continue; |
| | + } |
| | + |
| | + if( fileEditor.isModified() ) { |
| | + // activate the modified tab to make its modified content visible to the user |
| | + getSelectionModel().select( i ); |
| | + |
| | + if( !canCloseEditor( fileEditor ) ) { |
| | + return false; |
| | + } |
| | + } |
| | + } |
| | + |
| | + // Close all tabs. |
| | + for( final FileEditorTab fileEditor : allEditors ) { |
| | + if( !closeEditor( fileEditor, false ) ) { |
| | + return false; |
| | + } |
| | + } |
| | + |
| | + saveState( allEditors, activeEditor ); |
| | + |
| | + return getTabs().isEmpty(); |
| | + } |
| | + |
| | + private FileEditorTab[] getAllEditors() { |
| | + final ObservableList<Tab> tabs = getTabs(); |
| | + final FileEditorTab[] allEditors = new FileEditorTab[ tabs.size() ]; |
| | + final int length = tabs.size(); |
| | + |
| | + for( int i = 0; i < length; i++ ) { |
| | + allEditors[ i ] = (FileEditorTab)tabs.get( i ).getUserData(); |
| | + } |
| | + |
| | + return allEditors; |
| | + } |
| | + |
| | + private FileEditorTab findEditor( Path path ) { |
| | + for( final Tab tab : getTabs() ) { |
| | + final FileEditorTab fileEditor = (FileEditorTab)tab.getUserData(); |
| | + |
| | + if( path.equals( fileEditor.getPath() ) ) { |
| | + return fileEditor; |
| | + } |
| | + } |
| | + |
| | + return null; |
| | + } |
| | + |
| | + private Settings getSettings() { |
| | + return this.settings; |
| | + } |
| | + |
| | + private FileChooser createFileChooser( String title ) { |
| | + final FileChooser fileChooser = new FileChooser(); |
| | + |
| | + fileChooser.setTitle( title ); |
| | + fileChooser.getExtensionFilters().addAll( |
| | + createExtensionFilters() ); |
| | + |
| | + final String lastDirectory = getState().get( "lastDirectory", null ); |
| | + File file = new File( (lastDirectory != null) ? lastDirectory : "." ); |
| | + |
| | + if( !file.isDirectory() ) { |
| | + file = new File( "." ); |
| | + } |
| | + |
| | + fileChooser.setInitialDirectory( file ); |
| | + return fileChooser; |
| | + } |
| | + |
| | + private List<ExtensionFilter> createExtensionFilters() { |
| | + final List<ExtensionFilter> list = new ArrayList<>(); |
| | + |
| | + // TODO: Return a list of all properties that match the filter prefix. |
| | + // This will allow dynamic filters to be added and removed just by |
| | + // updating the properties file. |
| | + list.add( createExtensionFilter( "markdown" ) ); |
| | + list.add( createExtensionFilter( "definition" ) ); |
| | + list.add( createExtensionFilter( "xml" ) ); |
| | + list.add( createExtensionFilter( "all" ) ); |
| | + return list; |
| | + } |
| | + |
| | + private ExtensionFilter createExtensionFilter( final String filetype ) { |
| | + final String tKey = String.format( "%s.title.%s", FILTER_PREFIX, filetype ); |
| | + final String eKey = String.format( "%s.ext.%s", FILTER_PREFIX, filetype ); |
| | + |
| | + return new ExtensionFilter( Messages.get( tKey ), getExtensions( eKey ) ); |
| | + } |
| | + |
| | + private List<String> getExtensions( final String key ) { |
| | + return getStringSettingList( key ); |
| | + } |
| | + |
| | + private List<String> getStringSettingList( String key ) { |
| | + return getStringSettingList( key, null ); |
| | + } |
| | + |
| | + private List<String> getStringSettingList( String key, List<String> values ) { |
| | + return getSettings().getStringSettingList( key, values ); |
| | + } |
| | + |
| | + private void saveLastDirectory( File file ) { |
| | + getState().put( "lastDirectory", file.getParent() ); |
| | + } |
| | + |
| | + public void restoreState() { |
| | int activeIndex = 0; |
| | |