Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert find unlinked files dialog to JavaFX #4375

Merged
merged 6 commits into from
Oct 17, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,226 changes: 0 additions & 1,226 deletions src/main/java/org/jabref/gui/FindUnlinkedFilesDialog.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
import org.jabref.gui.actions.DatabasePropertiesAction;
import org.jabref.gui.actions.EditExternalFileTypesAction;
import org.jabref.gui.actions.ErrorConsoleAction;
import org.jabref.gui.actions.FindUnlinkedFilesAction;
import org.jabref.gui.actions.IntegrityCheckAction;
import org.jabref.gui.actions.LookupIdentifierAction;
import org.jabref.gui.actions.ManageCustomExportsAction;
Expand All @@ -89,6 +88,7 @@
import org.jabref.gui.exporter.ExportToClipboardAction;
import org.jabref.gui.exporter.SaveAllAction;
import org.jabref.gui.exporter.SaveDatabaseAction;
import org.jabref.gui.externalfiles.FindUnlinkedFilesAction;
import org.jabref.gui.externalfiletype.ExternalFileTypes;
import org.jabref.gui.help.AboutAction;
import org.jabref.gui.help.HelpAction;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.jabref.gui.actions;
package org.jabref.gui.externalfiles;

import org.jabref.gui.FindUnlinkedFilesDialog;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.actions.SimpleCommand;

public class FindUnlinkedFilesAction extends SimpleCommand {

Expand All @@ -14,7 +14,7 @@ public FindUnlinkedFilesAction(JabRefFrame jabRefFrame) {
@Override
public void execute() {
FindUnlinkedFilesDialog dlg = new FindUnlinkedFilesDialog(jabRefFrame);
dlg.setVisible(true);
dlg.showAndWait();
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

import java.io.File;
import java.io.FileFilter;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.undo.CompoundEdit;

import org.jabref.gui.BasePanel;
Expand Down Expand Up @@ -93,43 +91,32 @@ public EntryFromFileCreator getEntryCreator(File file) {
* @param entryType
* @return List of unexpected import event messages including failures.
*/
public List<String> addEntrysFromFiles(List<File> files,
BibDatabase database, EntryType entryType,
boolean generateKeywordsFromPathToFile) {
List<String> importGUIMessages = new LinkedList<>();
addEntriesFromFiles(files, database, null, entryType,
generateKeywordsFromPathToFile, null, importGUIMessages);
return importGUIMessages;
public List<String> addEntrysFromFiles(List<Path> files,
BibDatabase database, EntryType entryType,
boolean generateKeywordsFromPathToFile) {
return addEntriesFromFiles(files, database, null, entryType, generateKeywordsFromPathToFile);
}

/**
* Tries to add a entry for each file in the List.
*
* @param files
* @param database
* @param panel
* @param entryType
* @param generateKeywordsFromPathToFile
* @param changeListener
* @param importGUIMessages list of unexpected import event - Messages including
* failures
* @return Returns The number of entries added
* @return Returns a list of unexpected failures while importing
*/
public int addEntriesFromFiles(List<File> files,
BibDatabase database, BasePanel panel, EntryType entryType,
boolean generateKeywordsFromPathToFile,
ChangeListener changeListener, List<String> importGUIMessages) {
public List<String> addEntriesFromFiles(List<Path> files,
BibDatabase database, BasePanel panel, EntryType entryType,
boolean generateKeywordsFromPathToFile) {

List<String> importGUIMessages = new ArrayList<>();
int count = 0;
CompoundEdit ce = new CompoundEdit();
for (File f : files) {
EntryFromFileCreator creator = getEntryCreator(f);
for (Path f : files) {
EntryFromFileCreator creator = getEntryCreator(f.toFile());
if (creator == null) {
importGUIMessages.add("Problem importing " + f.getPath() + ": Unknown filetype.");
importGUIMessages.add("Problem importing " + f.toAbsolutePath() + ": Unknown filetype.");
} else {
Optional<BibEntry> entry = creator.createEntry(f, generateKeywordsFromPathToFile);
Optional<BibEntry> entry = creator.createEntry(f.toFile(), generateKeywordsFromPathToFile);
if (!entry.isPresent()) {
importGUIMessages.add("Problem importing " + f.getPath() + ": Entry could not be created.");
importGUIMessages.add("Problem importing " + f.toAbsolutePath() + ": Entry could not be created.");
continue;
}
if (entryType != null) {
Expand All @@ -147,7 +134,7 @@ public int addEntriesFromFiles(List<File> files,
// Work around SIDE EFFECT of creator.createEntry. The EntryFromPDFCreator also creates the entry in the table
// Therefore, we only insert the entry if it is not already present
if (database.insertEntry(entry.get())) {
importGUIMessages.add("Problem importing " + f.getPath() + ": Insert into BibDatabase failed.");
importGUIMessages.add("Problem importing " + f.toAbsolutePath() + ": Insert into BibDatabase failed.");
} else {
count++;
if (panel != null) {
Expand All @@ -156,17 +143,13 @@ public int addEntriesFromFiles(List<File> files,
}
}
}

if (changeListener != null) {
changeListener.stateChanged(new ChangeEvent(this));
}
}

if ((count > 0) && (panel != null)) {
ce.end();
panel.getUndoManager().addEdit(ce);
}
return count;
return importGUIMessages;

}

Expand Down Expand Up @@ -211,12 +194,9 @@ public String toString() {
* @return A List of all known possible file filters.
*/
public List<FileFilter> getFileFilterList() {

List<FileFilter> filters = new ArrayList<>();
filters.add(getFileFilter());
for (FileFilter creator : entryCreators) {
filters.add(creator);
}
filters.addAll(entryCreators);
return filters;
}
}
68 changes: 36 additions & 32 deletions src/main/java/org/jabref/gui/importer/UnlinkedFilesCrawler.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,39 @@

import java.io.File;
import java.io.FileFilter;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javafx.scene.control.CheckBoxTreeItem;

import org.jabref.gui.FindUnlinkedFilesDialog.CheckableTreeNode;
import org.jabref.gui.FindUnlinkedFilesDialog.FileNodeWrapper;
import org.jabref.gui.externalfiles.FindUnlinkedFilesDialog.FileNodeWrapper;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;

/**
* Util class for searching files on the file system which are not linked to a provided {@link BibDatabase}.
*/
public class UnlinkedFilesCrawler {
/**
* File filter, that accepts directories only.
*/
private static final FileFilter DIRECTORY_FILTER = pathname -> (pathname != null) && pathname.isDirectory();
public class UnlinkedFilesCrawler extends BackgroundTask<CheckBoxTreeItem<FileNodeWrapper>> {

private final Path directory;
private final FileFilter fileFilter;
private int counter;
private final BibDatabaseContext databaseContext;


public UnlinkedFilesCrawler(BibDatabaseContext databaseContext) {
public UnlinkedFilesCrawler(Path directory, FileFilter fileFilter, BibDatabaseContext databaseContext) {
this.directory = directory;
this.fileFilter = fileFilter;
this.databaseContext = databaseContext;
}

public CheckableTreeNode searchDirectory(File directory, FileFilter filter) {
UnlinkedPDFFileFilter ff = new UnlinkedPDFFileFilter(filter, databaseContext);
return searchDirectory(directory, ff, new AtomicBoolean(true), null);
@Override
protected CheckBoxTreeItem<FileNodeWrapper> call() throws Exception {
UnlinkedPDFFileFilter unlinkedPDFFileFilter = new UnlinkedPDFFileFilter(fileFilter, databaseContext);
return searchDirectory(directory.toFile(), unlinkedPDFFileFilter);
}

/**
Expand All @@ -43,7 +44,7 @@ public CheckableTreeNode searchDirectory(File directory, FileFilter filter) {
* {@link EntryFromFileCreatorManager}, are taken into the resulting tree. <br>
* <br>
* The result will be a tree structure of nodes of the type
* {@link CheckableTreeNode}. <br>
* {@link CheckBoxTreeItem}. <br>
* <br>
* The user objects that are attached to the nodes is the
* {@link FileNodeWrapper}, which wraps the {@link File}-Object. <br>
Expand All @@ -53,11 +54,7 @@ public CheckableTreeNode searchDirectory(File directory, FileFilter filter) {
* the recursion running. When the states value changes, the method will
* resolve its recursion and return what it has saved so far.
*/
public CheckableTreeNode searchDirectory(File directory, UnlinkedPDFFileFilter ff, AtomicBoolean state, ChangeListener changeListener) {
/* Cancellation of the search from outside! */
if ((state == null) || !state.get()) {
return null;
}
private CheckBoxTreeItem<FileNodeWrapper> searchDirectory(File directory, UnlinkedPDFFileFilter ff) {
// Return null if the directory is not valid.
if ((directory == null) || !directory.exists() || !directory.isDirectory()) {
return null;
Expand All @@ -70,35 +67,42 @@ public CheckableTreeNode searchDirectory(File directory, UnlinkedPDFFileFilter f
} else {
files = Arrays.asList(filesArray);
}
CheckableTreeNode root = new CheckableTreeNode(null);
CheckBoxTreeItem<FileNodeWrapper> root = new CheckBoxTreeItem<>(new FileNodeWrapper(directory.toPath(), 0));

int filesCount = 0;

filesArray = directory.listFiles(DIRECTORY_FILTER);
filesArray = directory.listFiles(pathname -> (pathname != null) && pathname.isDirectory());
List<File> subDirectories;
if (filesArray == null) {
subDirectories = Collections.emptyList();
} else {
subDirectories = Arrays.asList(filesArray);
}
for (File subDirectory : subDirectories) {
CheckableTreeNode subRoot = searchDirectory(subDirectory, ff, state, changeListener);
if ((subRoot != null) && (subRoot.getChildCount() > 0)) {
filesCount += ((FileNodeWrapper) subRoot.getUserObject()).fileCount;
root.add(subRoot);
if (isCanceled()) {
return root;
}

CheckBoxTreeItem<FileNodeWrapper> subRoot = searchDirectory(subDirectory, ff);
if ((subRoot != null) && (!subRoot.getChildren().isEmpty())) {
filesCount += subRoot.getValue().fileCount;
root.getChildren().add(subRoot);
}
}

root.setUserObject(new FileNodeWrapper(directory, files.size() + filesCount));
root.setValue(new FileNodeWrapper(directory.toPath(), files.size() + filesCount));

for (File file : files) {
root.add(new CheckableTreeNode(new FileNodeWrapper(file)));
if (changeListener != null) {
changeListener.stateChanged(new ChangeEvent(this));
root.getChildren().add(new CheckBoxTreeItem<>(new FileNodeWrapper(file.toPath())));

counter++;
if (counter == 1) {
updateMessage(Localization.lang("One file found"));
} else {
updateMessage(Localization.lang("%0 files found", Integer.toString(counter)));
}
}

return root;
}

}
56 changes: 41 additions & 15 deletions src/main/java/org/jabref/gui/util/BackgroundTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
import java.util.function.Consumer;
import java.util.function.Function;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;

import org.fxmisc.easybind.EasyBind;
Expand All @@ -26,7 +30,9 @@ public abstract class BackgroundTask<V> {
private Consumer<V> onSuccess;
private Consumer<Exception> onException;
private Runnable onFinished;
private BooleanProperty isCanceled = new SimpleBooleanProperty(false);
private ObjectProperty<BackgroundProgress> progress = new SimpleObjectProperty<>(new BackgroundProgress(0, 0));
private StringProperty message = new SimpleStringProperty("");
private DoubleProperty workDonePercentage = new SimpleDoubleProperty(0);

public BackgroundTask() {
Expand All @@ -52,6 +58,37 @@ protected Void call() throws Exception {
};
}

private static <T> Consumer<T> chain(Runnable first, Consumer<T> second) {
if (first != null) {
if (second != null) {
return result -> {
first.run();
second.accept(result);
};
} else {
return result -> first.run();
}
} else {
return second;
}
}

public boolean isCanceled() {
return isCanceled.get();
}

public void cancel() {
this.isCanceled.set(true);
}

public BooleanProperty isCanceledProperty() {
return isCanceled;
}

public StringProperty messageProperty() {
return message;
}

public double getWorkDonePercentage() {
return workDonePercentage.get();
}
Expand All @@ -68,21 +105,6 @@ public ObjectProperty<BackgroundProgress> progressProperty() {
return progress;
}

private static <T> Consumer<T> chain(Runnable first, Consumer<T> second) {
if (first != null) {
if (second != null) {
return result -> {
first.run();
second.accept(result);
};
} else {
return result -> first.run();
}
} else {
return second;
}
}

/**
* Sets the {@link Runnable} that is invoked after the task is started.
*/
Expand Down Expand Up @@ -197,6 +219,10 @@ protected void updateProgress(double workDone, double max) {
updateProgress(new BackgroundProgress(workDone, max));
}

protected void updateMessage(String newMessage) {
message.setValue(newMessage);
}

class BackgroundProgress {

private final double workDone;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ private <V> Task<V> getJavaFXTask(BackgroundTask<V> task) {

{
EasyBind.subscribe(task.progressProperty(), progress -> updateProgress(progress.getWorkDone(), progress.getMax()));
EasyBind.subscribe(task.messageProperty(), this::updateMessage);
EasyBind.subscribe(task.isCanceledProperty(), cancelled -> {
if (cancelled) {
cancel();
}
});
}

@Override
Expand Down
Loading