de.ks.file.FileViewController.java Source code

Java tutorial

Introduction

Here is the source code for de.ks.file.FileViewController.java

Source

/*
 * Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.ks.file;

import com.google.common.eventbus.Subscribe;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import de.ks.activity.ActivityLoadFinishedEvent;
import de.ks.activity.context.ActivityStore;
import de.ks.activity.initialization.DatasourceCallback;
import de.ks.idnadrev.entity.FileContainer;
import de.ks.idnadrev.entity.FileReference;
import de.ks.persistence.PersistentWork;
import de.ks.text.image.GlobalImageProvider;
import de.ks.text.image.ImageData;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.stage.FileChooser;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FileViewController implements Initializable, DatasourceCallback<FileContainer<?>> {
    private static final Logger log = LoggerFactory.getLogger(FileViewController.class);
    protected final ObservableList<File> files = FXCollections.observableArrayList();
    protected final java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
    protected final ExecutorService executor = Executors.newCachedThreadPool(
            new ThreadFactoryBuilder().setDaemon(true).setNameFormat("awt.Desktop-%d").build());
    @FXML
    protected Button edit;
    @FXML
    protected Button open;
    @FXML
    protected Button openFolder;
    @FXML
    protected Button addNewFile;
    @FXML
    protected Button removeFile;
    @FXML
    protected Label fileNameLabel;
    @FXML
    protected Label folderName;
    @FXML
    protected ListView<File> fileList;

    @Inject
    protected ActivityStore store;
    @Inject
    protected FileStore fileStore;
    @Inject
    protected GlobalImageProvider imageProvider;
    protected final Map<File, CompletableFuture<FileReference>> fileReferences = new HashMap<>();

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        fileList.setItems(files);

        MultipleSelectionModel<File> selectionModel = fileList.getSelectionModel();
        selectionModel.setSelectionMode(SelectionMode.MULTIPLE);

        ReadOnlyObjectProperty<File> selection = selectionModel.selectedItemProperty();
        selection.addListener((p, o, n) -> {
            folderName.setText(n == null ? "" : n.getParentFile().getAbsolutePath());
            fileNameLabel.setText(n == null ? "" : n.getName());
        });

        BooleanBinding isDirectory = Bindings
                .createBooleanBinding(() -> selection.get() != null && selection.get().isDirectory(), selection);
        edit.disableProperty().bind(isDirectory);

        files.addListener((ListChangeListener<File>) change -> {
            files.forEach(file -> {
                if (!fileReferences.containsKey(file) && file.exists() && !file.isDirectory()) {
                    fileReferences.put(file, fileStore.getReference(file));
                }
            });
        });
        BooleanBinding disable = fileList.getSelectionModel().selectedItemProperty().isNull();
        open.disableProperty().bind(disable);
        edit.disableProperty().bind(disable);
        openFolder.disableProperty().bind(disable);
        removeFile.disableProperty().bind(disable);
    }

    public void addFiles(List<File> additionalFiles) {
        additionalFiles.removeAll(files);
        additionalFiles.forEach(this::addPossibleImage);
        log.info("Adding addtional files {}", additionalFiles);
        files.addAll(additionalFiles);

        if (!additionalFiles.isEmpty()) {
            Collections.sort(files);
            Collections.sort(additionalFiles);
            File lastFile = additionalFiles.get(additionalFiles.size() - 1);
            fileList.scrollTo(lastFile);
            fileList.getSelectionModel().clearSelection();
            fileList.getSelectionModel().select(lastFile);
        }
    }

    protected void addPossibleImage(File file) {
        if (file == null) {
            return;
        }
        try {
            String contentType = Files.probeContentType(file.toPath());
            if (contentType != null && contentType.contains("image")) {
                imageProvider.addImage(new ImageData(file.getName(), file.getPath()));
            }
        } catch (IOException e) {
            //
        }
    }

    public ObservableList<File> getFiles() {
        return files;
    }

    public ListView<File> getFileList() {
        return fileList;
    }

    @FXML
    void open(ActionEvent event) {
        ObservableList<File> items = fileList.getSelectionModel().getSelectedItems();
        for (File item : items) {
            executor.submit(() -> {
                try {
                    log.info("Opening {}", item);
                    desktop.open(item);
                } catch (IOException e) {
                    log.error("Could not open {}", item, e);
                }
            });
        }
    }

    @FXML
    void edit(ActionEvent event) {
        ObservableList<File> items = fileList.getSelectionModel().getSelectedItems();
        for (File item : items) {
            executor.submit(() -> {
                try {
                    log.info("Editing {}", item);
                    desktop.edit(item);
                } catch (IOException e) {
                    log.error("Could not open {}", item, e);
                }
            });
        }
    }

    @FXML
    void openFolder(ActionEvent event) {
        TreeSet<File> files = new TreeSet<>();

        ObservableList<File> items = fileList.getSelectionModel().getSelectedItems();
        for (File item : items) {
            if (item.isDirectory()) {
                files.add(item);
            } else {
                files.add(item.getParentFile());
            }
        }
        for (File file : files) {
            executor.submit(() -> {
                try {
                    log.info("Opening {}", file);
                    desktop.open(file);
                } catch (IOException e) {
                    log.error("Could not open {}", file, e);
                }
            });
        }
    }

    @FXML
    public void removeFile(ActionEvent event) {
        ObservableList<File> selectedItems = fileList.getSelectionModel().getSelectedItems();
        log.info("Removing files {}", selectedItems);
        selectedItems.forEach(f -> files.remove(f));
    }

    @FXML
    void addNewFile(ActionEvent event) {
        FileChooser fileChooser = new FileChooser();
        File file = fileChooser.showOpenDialog(edit.getScene().getWindow());
        if (file != null) {
            addFiles(Arrays.asList(file));
        }
    }

    @Subscribe
    public void onRefresh(ActivityLoadFinishedEvent event) {
        log.debug("Clearing files");
        files.clear();
        fileReferences.clear();
        event.<FileContainer<?>>getModel().getFiles().forEach(f -> {
            File file = fileStore.getFile(f);
            fileReferences.put(file, CompletableFuture.completedFuture(f));
            addPossibleImage(file);
            files.add(file);
        });
    }

    @Override
    public void duringLoad(FileContainer<?> model) {
        model.getFiles().forEach(f -> f.getName());
    }

    @Override
    public void duringSave(FileContainer<?> model) {
        fileReferences.keySet().retainAll(files);
        if (this.fileReferences.isEmpty()) {
            log.info("No files to save for {}", model);
        }
        this.fileReferences.entrySet().forEach(entry -> {
            try {
                File file = entry.getKey();
                CompletableFuture<FileReference> cf = entry.getValue();
                FileReference fileReference = cf.get();
                model.getFiles().remove(fileReference);
                model.addFileReference(PersistentWork.reload(fileReference));//ensure it is saved
                if (fileReference.getId() > 0) {
                    return;
                }
                fileStore.scheduleCopy(fileReference, file);

                String search = "file:///" + file.getAbsolutePath();
                String replacement = FileReference.FILESTORE_VAR + fileReference.getMd5Sum() + File.separator
                        + file.getName();
                String newDescription = StringUtils.replace(model.getDescription(), search, replacement);
                model.setDescription(newDescription);
                log.info("Adding file reference {}", fileReference);

                PersistentWork.persist(fileReference);
            } catch (InterruptedException | ExecutionException e) {
                log.error("Could not get fileReference for file {}", entry.getKey());
                throw new RuntimeException(e);
            }
        });
    }
}