package com.twasyl.slideshowfx.controllers;
import com.twasyl.slideshowfx.controls.builder.editor.*;
import com.twasyl.slideshowfx.controls.tree.FileTreeCell;
import com.twasyl.slideshowfx.controls.tree.TemplateTreeView;
import com.twasyl.slideshowfx.engine.template.TemplateEngine;
import com.twasyl.slideshowfx.io.SlideshowFXExtensionFilter;
import com.twasyl.slideshowfx.utils.DialogHelper;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TreeItem;
import javafx.scene.input.MouseButton;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Controller class used for the Template Builder.
*
* @author Thierry Wasylczenko
* @version 1.2
* @since SlideshowFX 1.0
*/
public class TemplateBuilderController implements Initializable {
private static final Logger LOGGER = Logger.getLogger(TemplateBuilderController.class.getName());
@FXML
private TemplateTreeView templateContentTreeView;
@FXML
private TabPane openedFiles;
private Stage stage;
private TemplateEngine templateEngine;
/**
* Get the stage where this TemplateBuilder is in.
*
* @return The stage where this TemplateBuilder is in.
*/
public Stage getStage() {
return stage;
}
/**
* Set the stage where this TemplateBuilder will be in.
*
* @param stage The new stage where the TemplateBuilder will be in.
*/
public void setStage(Stage stage) {
this.stage = stage;
}
/**
* This method opens a dialog box only allowing the selection of directories.
*
* @param event The event associated to button clicked to call this method.
*/
@FXML
private void addFolderToTreeView(ActionEvent event) {
final DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle("Add content");
final File directory = chooser.showDialog(null);
if (directory != null) {
this.templateContentTreeView.appendContentToTreeView(directory);
}
}
/**
* This method opens a dialog box only allowing the selection of files.
*
* @param event The event associated to button clicked to call this method.
*/
@FXML
private void addFileToTreeView(ActionEvent event) {
final FileChooser chooser = new FileChooser();
chooser.setTitle("Add content");
final File file = chooser.showOpenDialog(null);
if (file != null) {
this.templateContentTreeView.appendContentToTreeView(file);
}
}
/**
* Build the current template archive. This method checks if the archive has already been saved
* or not. If so, the archive will be overwritten otherwise a dialog asks the user where to save it.
*
* @param event The event associated to button clicked to call this method.
*/
@FXML
private void buildTemplateArchive(ActionEvent event) {
File destination = this.templateEngine.getArchive();
if (destination == null) {
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(SlideshowFXExtensionFilter.TEMPLATE_FILTER);
destination = chooser.showSaveDialog(null);
// Manage if the file name doesn't end with the template extension.
if (!destination.getName().endsWith(TemplateEngine.DEFAULT_DOTTED_ARCHIVE_EXTENSION)) {
destination = new File(destination.getAbsolutePath().concat(TemplateEngine.DEFAULT_DOTTED_ARCHIVE_EXTENSION));
}
}
if (destination != null) {
this.templateEngine.setArchive(destination);
try {
this.templateEngine.saveArchive();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Can not save the template", e);
}
}
}
/**
* Build the current template archive.
*
* @param event The event associated to button clicked to call this method.
*/
@FXML
private void buildAsTemplateArchive(ActionEvent event) {
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(SlideshowFXExtensionFilter.TEMPLATE_FILTER);
File destination = chooser.showSaveDialog(null);
if (destination != null) {
this.templateEngine.setArchive(destination);
try {
this.templateEngine.saveArchive();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Can not save the template", e);
}
}
}
/**
* Save the current opened file.
*
* @param event The event associated to the click
*/
@FXML
private void saveCurrentFile(ActionEvent event) {
IFileEditor currentFile = (IFileEditor) this.openedFiles.getSelectionModel().getSelectedItem();
if (currentFile != null) {
currentFile.saveContent();
}
}
/**
* Save all opened files.
*
* @param event The event associated to the click
*/
@FXML
private void saveAllFiles(ActionEvent event) {
this.openedFiles.getTabs()
.stream()
.filter(tab -> tab instanceof IFileEditor)
.map(tab -> (IFileEditor) tab)
.forEach(editor -> editor.saveContent());
}
/**
* Delete the selection from the TreeView and the filesystem.
*
* @param event The event associated to button clicked to call this method.
*/
@FXML
private void deleteFromTreeView(ActionEvent event) {
ObservableList<TreeItem<File>> selectedItems = this.templateContentTreeView.getSelectionModel().getSelectedItems();
if (!selectedItems.isEmpty()) {
final ButtonType answer = DialogHelper.showConfirmationAlert("Delete selection", "Are you sure you want to delete the selection?");
if (answer == ButtonType.YES) {
selectedItems.filtered(item -> item != this.templateContentTreeView.getRoot())
.forEach(item -> {
try {
this.templateContentTreeView.deleteContentOfTreeView(item);
} catch (IOException e) {
DialogHelper.showError("Error", "Can not delete the content");
}
});
}
}
}
/**
* Allow the user to create a directory in the template. This method asks the user for the given directory name
* (a value with slashes will create multiple directory) and creates the desired directory in the selection of the
* TreeView. If there is no selection, the directory will be created at the root of this template.
*
* @param event The event associated to button clicked to call this method.
*/
@FXML
private void createDirectory(ActionEvent event) {
this.templateContentTreeView.promptUserAndCreateNewDirectory();
}
/**
* Allow the user to create am empty file in the template. This method asks the user for the given file name
* and creates the desired file in the selection of the TreeView. If there is no selection, the file will be created
* at the root of this template.
*
* @param event The event associated to button clicked to call this method.
*/
@FXML
private void createFile(ActionEvent event) {
this.templateContentTreeView.promptUserAndCreateNewFile();
}
/**
* Get the template engine used for the builder.
*
* @return The templated builder used for the builder.
*/
public TemplateEngine getTemplateEngine() {
return templateEngine;
}
/**
* Set the new template engine for this builder. The engine must be fully initialized before calling this method
* because it is used to initialize the view.
*
* @param templateEngine The template engine to be used.
*/
public void setTemplateEngine(TemplateEngine templateEngine) {
this.templateEngine = templateEngine;
if (this.templateEngine != null) {
if (this.templateEngine.getWorkingDirectory() == null) {
this.templateEngine.setWorkingDirectory(this.templateEngine.generateWorkingDirectory());
}
final TreeItem root = new TreeItem(this.templateEngine.getWorkingDirectory());
root.setExpanded(true);
this.templateContentTreeView.setEngine(this.templateEngine);
this.templateContentTreeView.setRoot(root);
final File[] children = this.templateEngine.getWorkingDirectory().listFiles();
if (children != null) {
for (File child : children) {
this.templateContentTreeView.appendContentToTreeView(child, root);
}
}
this.templateContentTreeView.closeItem(root);
root.setExpanded(true);
}
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
// Initialize the tree view
this.templateContentTreeView.setOnItemClick(event -> {
if (event.getClickCount() == 2 && event.getButton().equals(MouseButton.PRIMARY)) {
if (event.getSource() instanceof FileTreeCell) {
File file = ((FileTreeCell) event.getSource()).getItem();
if (file.isFile()) {
/**
* Check if the file is already opened and select it if it is,
otherwise open it.
*/
Optional<IFileEditor> editor = this.openedFiles.getTabs()
.stream()
.filter(tab -> tab instanceof IFileEditor)
.map(tab -> (IFileEditor) tab)
.filter(tab -> tab.getFile().equals(file))
.findFirst();
if (editor.isPresent()) {
this.openedFiles.getSelectionModel().select((Tab) editor.get());
} else {
// The type of editor has to be determined
IFileEditor fileEditor;
// The file is the configuration file
if (file.equals(new File(this.templateEngine.getWorkingDirectory(), this.templateEngine.getConfigurationFilename()))) {
fileEditor = new ConfigurationFileEditor(this.templateEngine.getWorkingDirectory().toPath(), file);
} else {
/**
* Try to determine the best file editor to use
* by checking the MIME type
*/
try {
String mimeType = Files.probeContentType(file.toPath());
if (mimeType != null && mimeType.contains("image"))
fileEditor = new ImageFileEditor();
else fileEditor = new ACEFileEditor();
fileEditor.setWorkingPath(this.templateEngine.getWorkingDirectory().toPath());
fileEditor.setFile(file);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "An error occurred while truing to determine the MIME type of the file to open", e);
fileEditor = new SimpleFileEditor();
}
}
this.openedFiles.getTabs().add((Tab) fileEditor);
this.openedFiles.getSelectionModel().selectLast();
}
}
}
}
});
}
}