* rewrite Analyze panel

This commit is contained in:
Reinhard Pointner 2014-07-17 11:53:24 +00:00
parent 7385a8d307
commit 3678e7388d
14 changed files with 441 additions and 450 deletions

View File

@ -42,7 +42,7 @@ public class AnalyzePanel extends JComponent {
// stopped loading, refresh tools
for (int i = 0; i < toolsPanel.getTabCount(); i++) {
Tool<?> tool = (Tool<?>) toolsPanel.getComponentAt(i);
tool.setSourceModel(fileTreePanel.getFileTree().getRoot());
tool.updateRoot(fileTreePanel.getFileTree().getRoot().getFile());
}
}
};

View File

@ -5,7 +5,7 @@ import static net.filebot.MediaTypes.*;
import java.awt.Color;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -16,13 +16,13 @@ import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;
import net.filebot.media.MetaAttributes;
import net.filebot.ui.analyze.FileTree.FolderNode;
import net.filebot.util.FileUtilities;
import net.filebot.util.ui.LoadingOverlayPane;
import net.filebot.web.Episode;
import net.filebot.web.Movie;
import net.filebot.web.SearchResult;
import net.miginfocom.swing.MigLayout;
class AttributeTool extends Tool<TableModel> {
@ -44,12 +44,11 @@ class AttributeTool extends Tool<TableModel> {
}
@Override
protected TableModel createModelInBackground(FolderNode sourceModel) throws InterruptedException {
protected TableModel createModelInBackground(File root) throws InterruptedException {
List<File> files = (root != null) ? FileUtilities.listFiles(root) : Collections.emptyList();
FileAttributesTableModel model = new FileAttributesTableModel();
for (Iterator<File> iterator = sourceModel.fileIterator(); iterator.hasNext();) {
File file = iterator.next();
for (File file : files) {
if (VIDEO_FILES.accept(file)) {
try {
MetaAttributes attributes = new MetaAttributes(file);

View File

@ -12,7 +12,7 @@ import java.beans.PropertyChangeEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@ -31,11 +31,9 @@ import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;
import net.filebot.ResourceManager;
import net.filebot.archive.Archive;
import net.filebot.archive.FileMapper;
import net.filebot.ui.analyze.FileTree.FolderNode;
import net.filebot.util.FileUtilities;
import net.filebot.util.ui.GradientStyle;
import net.filebot.util.ui.LoadingOverlayPane;
@ -44,6 +42,7 @@ import net.filebot.util.ui.ProgressDialog.Cancellable;
import net.filebot.util.ui.SwingWorkerPropertyChangeAdapter;
import net.filebot.util.ui.notification.SeparatorBorder;
import net.filebot.vfs.FileInfo;
import net.miginfocom.swing.MigLayout;
class ExtractTool extends Tool<TableModel> {
@ -73,13 +72,12 @@ class ExtractTool extends Tool<TableModel> {
}
@Override
protected TableModel createModelInBackground(FolderNode sourceModel) throws InterruptedException {
protected TableModel createModelInBackground(File root) throws InterruptedException {
List<File> files = (root != null) ? FileUtilities.listFiles(root) : Collections.emptyList();
List<ArchiveEntry> entries = new ArrayList<ArchiveEntry>();
try {
for (Iterator<File> iterator = sourceModel.fileIterator(); iterator.hasNext();) {
File file = iterator.next();
for (File file : files) {
// ignore non-archives files and trailing multi-volume parts
if (Archive.VOLUME_ONE_FILTER.accept(file)) {
Archive archive = new Archive(file);

View File

@ -1,7 +1,6 @@
package net.filebot.ui.analyze;
import static java.util.Collections.*;
import static net.filebot.ui.NotificationLogging.*;
import java.awt.Desktop;
@ -10,11 +9,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@ -36,117 +32,89 @@ import net.filebot.util.ExceptionUtilities;
import net.filebot.util.FilterIterator;
import net.filebot.util.TreeIterator;
public class FileTree extends JTree {
public FileTree() {
super(new DefaultTreeModel(new FolderNode()));
getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
setCellRenderer(new FileTreeCellRenderer());
setShowsRootHandles(true);
setRootVisible(false);
setRowHeight(22);
setLargeModel(true);
addMouseListener(new ExpandCollapsePopupListener());
}
@Override
public DefaultTreeModel getModel() {
return (DefaultTreeModel) super.getModel();
}
public FolderNode getRoot() {
return (FolderNode) getModel().getRoot();
}
public void clear() {
getRoot().clear();
getModel().setRoot(new FolderNode());
getModel().reload();
}
public void removeTreeNode(TreePath... paths) {
Set<TreeNode> dirtyNodes = new HashSet<TreeNode>();
for (TreePath path : paths) {
AbstractTreeNode node = (AbstractTreeNode) (path.getLastPathComponent());
FolderNode parent = (FolderNode) node.getParent();
if (parent != null) {
parent.remove(node);
dirtyNodes.add(parent);
}
}
for (TreeNode dirtyNode : dirtyNodes) {
getModel().reload(dirtyNode);
}
}
public void expandAll() {
for (int row = 0; row < getRowCount(); row++) {
expandRow(row);
}
}
public void collapseAll() {
for (int row = 0; row < getRowCount(); row++) {
collapseRow(row);
}
}
private class OpenExpandCollapsePopup extends JPopupMenu {
public OpenExpandCollapsePopup() {
TreePath[] selectionPaths = getSelectionPaths();
Set<File> selectionFiles = null;
if (selectionPaths != null) {
selectionFiles = new LinkedHashSet<File>(selectionPaths.length);
for (TreePath treePath : selectionPaths) {
Object node = treePath.getLastPathComponent();
if (node instanceof FileNode) {
selectionFiles.add(((FileNode) node).getFile());
}
}
}
if (selectionFiles != null && !selectionFiles.isEmpty()) {
JMenuItem openItem = new JMenuItem(new OpenAction("Open", selectionFiles));
openItem.setFont(openItem.getFont().deriveFont(Font.BOLD));
add(openItem);
Set<File> selectionParentFolders = new LinkedHashSet<File>(selectionFiles.size());
for (File file : selectionFiles) {
selectionParentFolders.add(file.getParentFile());
}
add(new OpenAction("Open Folder", selectionParentFolders));
addSeparator();
}
add(expandAction);
add(collapseAction);
}
private class OpenAction extends AbstractAction {
public OpenAction(String text, Collection<File> files) {
super(text);
putValue("files", files);
}
public void actionPerformed(ActionEvent event) {
try {
@ -158,104 +126,91 @@ public class FileTree extends JTree {
}
}
}
private final Action expandAction = new AbstractAction("Expand all", ResourceManager.getIcon("tree.expand")) {
@Override
public void actionPerformed(ActionEvent e) {
expandAll();
}
};
private final Action collapseAction = new AbstractAction("Collapse all", ResourceManager.getIcon("tree.collapse")) {
@Override
public void actionPerformed(ActionEvent e) {
collapseAll();
}
};
}
private class ExpandCollapsePopupListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
@Override
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
TreePath path = getPathForLocation(e.getX(), e.getY());
if (!getSelectionModel().isPathSelected(path)) {
// if clicked node is not selected, set selection to this node (and deselect all other currently selected nodes)
setSelectionPath(path);
}
OpenExpandCollapsePopup popup = new OpenExpandCollapsePopup();
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
}
public static class AbstractTreeNode implements TreeNode {
private TreeNode parent;
@Override
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
@Override
public Enumeration<? extends TreeNode> children() {
return null;
}
@Override
public boolean getAllowsChildren() {
return false;
}
@Override
public TreeNode getChildAt(int childIndex) {
return null;
}
@Override
public int getChildCount() {
return 0;
}
@Override
public int getIndex(TreeNode node) {
return -1;
}
@Override
public boolean isLeaf() {
@ -263,155 +218,117 @@ public class FileTree extends JTree {
// so that it won't display any good-for-nothing expand buttons
return getChildCount() == 0;
}
}
public static class FileNode extends AbstractTreeNode {
private final File file;
public FileNode(File file) {
this.file = file;
}
public File getFile() {
return file;
}
@Override
public String toString() {
return file.getName();
}
}
public static class FolderNode extends AbstractTreeNode {
private List<AbstractTreeNode> children;
private String title;
private final File file;
private final String title;
private final List<TreeNode> children;
/**
* Creates a root node (no parent, no title, empty list of children)
*/
public FolderNode() {
this(null, 5);
this(null, "", emptyList());
}
public FolderNode(String title, int initialCapacity) {
this.title = title;
this.children = new ArrayList<AbstractTreeNode>(initialCapacity);
public FolderNode(String title, List<TreeNode> children) {
this(null, title, children);
}
public void setTitle(String title) {
public FolderNode(File file, String title, List<TreeNode> children) {
this.file = file;
this.title = title;
this.children = children;
}
public File getFile() {
return file;
}
@Override
public String toString() {
return title;
}
public List<AbstractTreeNode> getChildren() {
return Collections.unmodifiableList(children);
public List<TreeNode> getChildren() {
return children;
}
public void add(AbstractTreeNode node) {
if (children.add(node)) {
// node added, set parent
node.setParent(this);
}
}
public void remove(AbstractTreeNode node) {
if (children.remove(node)) {
// node removed, reset parent
node.setParent(null);
}
}
public void clear() {
// reset parent of all children
for (AbstractTreeNode node : children) {
node.setParent(null);
}
// clear children
children.clear();
}
@Override
public Enumeration<? extends TreeNode> children() {
return Collections.enumeration(children);
return enumeration(children);
}
@Override
public boolean getAllowsChildren() {
return true;
}
@Override
public TreeNode getChildAt(int childIndex) {
return children.get(childIndex);
}
@Override
public int getChildCount() {
return children.size();
}
@Override
public int getIndex(TreeNode node) {
return children.indexOf(node);
}
public Iterator<AbstractTreeNode> treeIterator() {
return new TreeIterator<AbstractTreeNode>(this) {
public Iterator<TreeNode> treeIterator() {
return new TreeIterator<TreeNode>(this) {
@Override
protected Iterator<AbstractTreeNode> children(AbstractTreeNode node) {
protected Iterator<TreeNode> children(TreeNode node) {
if (node instanceof FolderNode)
return ((FolderNode) node).getChildren().iterator();
// can't step into non-folder nodes
return null;
}
};
}
public Iterator<File> fileIterator() {
return new FilterIterator<AbstractTreeNode, File>(treeIterator()) {
return new FilterIterator<TreeNode, File>(treeIterator()) {
@Override
protected File filter(AbstractTreeNode node) {
protected File filter(TreeNode node) {
if (node instanceof FileNode)
return ((FileNode) node).getFile();
// filter out non-file nodes
return null;
}
};
}
}
}

View File

@ -1,7 +1,5 @@
package net.filebot.ui.analyze;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.util.Iterator;
@ -17,18 +15,16 @@ import net.filebot.ui.analyze.FileTree.FolderNode;
import net.filebot.ui.transfer.FileTransferable;
import net.filebot.ui.transfer.TransferableExportHandler;
class FileTreeExportHandler implements TransferableExportHandler {
@Override
public Transferable createTransferable(JComponent c) {
FileTree tree = (FileTree) c;
LinkedHashSet<File> files = new LinkedHashSet<File>();
for (TreePath path : tree.getSelectionPaths()) {
TreeNode node = (TreeNode) path.getLastPathComponent();
if (node instanceof FileNode) {
files.add(((FileNode) node).getFile());
} else if (node instanceof FolderNode) {
@ -37,23 +33,21 @@ class FileTreeExportHandler implements TransferableExportHandler {
}
}
}
if (!files.isEmpty())
return new FileTransferable(files);
return null;
if (files.isEmpty())
return null;
return new FileTransferable(files);
}
@Override
public void exportDone(JComponent source, Transferable data, int action) {
}
@Override
public int getSourceActions(JComponent c) {
return TransferHandler.COPY;
}
}

View File

@ -1,11 +1,8 @@
package net.filebot.ui.analyze;
import static net.filebot.ui.transfer.BackgroundFileTransferablePolicy.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@ -14,36 +11,32 @@ import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import net.miginfocom.swing.MigLayout;
import net.filebot.ResourceManager;
import net.filebot.ui.transfer.DefaultTransferHandler;
import net.filebot.ui.transfer.LoadAction;
import net.filebot.util.ui.LoadingOverlayPane;
import net.filebot.util.ui.TunedUtilities;
import net.miginfocom.swing.MigLayout;
class FileTreePanel extends JComponent {
private FileTree fileTree = new FileTree();
private FileTreeTransferablePolicy transferablePolicy = new FileTreeTransferablePolicy(fileTree);
public FileTreePanel() {
fileTree.setTransferHandler(new DefaultTransferHandler(transferablePolicy, null));
setBorder(BorderFactory.createTitledBorder("File Tree"));
setLayout(new MigLayout("insets 0, nogrid, fill", "align center", "[fill][pref!]"));
add(new LoadingOverlayPane(new JScrollPane(fileTree), this), "grow, wrap 1.2mm");
add(new JButton(loadAction));
add(new JButton(clearAction), "gap 1.2mm, wrap 1.2mm");
// forward loading events
transferablePolicy.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (LOADING_PROPERTY.equals(evt.getPropertyName())) {
@ -51,10 +44,10 @@ class FileTreePanel extends JComponent {
}
}
});
// update tree when loading is finished
transferablePolicy.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (LOADING_PROPERTY.equals(evt.getPropertyName()) && !(Boolean) evt.getNewValue()) {
@ -62,26 +55,20 @@ class FileTreePanel extends JComponent {
}
}
});
// Shortcut DELETE
TunedUtilities.installAction(fileTree, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), removeAction);
TunedUtilities.installAction(fileTree, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), removeAction);
}
public FileTree getFileTree() {
return fileTree;
}
public FileTreeTransferablePolicy getTransferablePolicy() {
return transferablePolicy;
}
private final LoadAction loadAction = new LoadAction(transferablePolicy);
private final AbstractAction clearAction = new AbstractAction("Clear", ResourceManager.getIcon("action.clear")) {
@Override
public void actionPerformed(ActionEvent e) {
transferablePolicy.reset();
@ -89,32 +76,9 @@ class FileTreePanel extends JComponent {
fireFileTreeChange();
}
};
private final AbstractAction removeAction = new AbstractAction("Remove") {
@Override
public void actionPerformed(ActionEvent e) {
if (fileTree.getSelectionCount() < 1)
return;
int row = fileTree.getMinSelectionRow();
fileTree.removeTreeNode(fileTree.getSelectionPaths());
int maxRow = fileTree.getRowCount() - 1;
if (row > maxRow)
row = maxRow;
fileTree.setSelectionRow(row);
fireFileTreeChange();
}
};
private void fireFileTreeChange() {
firePropertyChange("filetree", null, fileTree);
}
}

View File

@ -4,17 +4,21 @@ import static net.filebot.ui.NotificationLogging.*;
import static net.filebot.util.FileUtilities.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import net.filebot.ui.analyze.FileTree.AbstractTreeNode;
import javax.swing.tree.TreeNode;
import net.filebot.ui.analyze.FileTree.FileNode;
import net.filebot.ui.analyze.FileTree.FolderNode;
import net.filebot.ui.transfer.BackgroundFileTransferablePolicy;
import net.filebot.util.ExceptionUtilities;
import net.filebot.util.FastFile;
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<AbstractTreeNode> {
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<TreeNode> {
private final FileTree tree;
@ -30,18 +34,12 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Abstra
@Override
protected void clear() {
super.clear();
tree.clear();
}
@Override
protected void process(List<AbstractTreeNode> chunks) {
FolderNode root = tree.getRoot();
for (AbstractTreeNode node : chunks) {
root.add(node);
}
protected void process(List<TreeNode> root) {
tree.getModel().setRoot(root.get(0));
tree.getModel().reload();
}
@ -53,40 +51,39 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Abstra
@Override
protected void load(List<File> files) {
try {
for (File file : files) {
// use fast file to minimize system calls like length(), isDirectory(), isFile(), ...
AbstractTreeNode node = getTreeNode(new FastFile(file.getPath()));
// publish on EDT
publish(node);
if (files.size() > 1 || containsOnly(files, FILES)) {
files = Arrays.asList(files.get(0).getParentFile());
}
// use fast file to minimize system calls like length(), isDirectory(), isFile(), ...
FastFile root = FastFile.create(filter(files, FOLDERS)).get(0);
// publish on EDT
publish(getTreeNode(root));
} catch (InterruptedException e) {
// supposed to happen if background execution was aborted
}
}
private AbstractTreeNode getTreeNode(File file) throws InterruptedException {
if (Thread.interrupted())
private TreeNode getTreeNode(File file) throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
if (file.isDirectory()) {
List<File> files = getChildren(file);
FolderNode node = new FolderNode(getFolderName(file), files.size());
LinkedList<TreeNode> children = new LinkedList<TreeNode>();
for (File f : getChildren(file)) {
if (f.isHidden())
continue;
// add folders first
for (File f : files) {
if (f.isDirectory()) {
node.add(getTreeNode(f));
children.addFirst(getTreeNode(f));
} else {
children.addLast(getTreeNode(f));
}
}
for (File f : files) {
if (f.isFile()) {
node.add(getTreeNode(f));
}
}
return node;
return new FolderNode(file, getFolderName(file), new ArrayList<TreeNode>(children));
}
return new FileNode(file);
@ -94,7 +91,7 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Abstra
@Override
public String getFileFilterDescription() {
return "files and folders";
return "folders";
}
}

View File

@ -1,14 +1,10 @@
package net.filebot.ui.analyze;
import java.awt.Color;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
@ -18,125 +14,106 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import net.miginfocom.swing.MigLayout;
import net.filebot.ui.analyze.FileTree.FolderNode;
import net.filebot.ui.transfer.DefaultTransferHandler;
import net.filebot.util.FileUtilities;
import net.filebot.util.ui.GradientStyle;
import net.filebot.util.ui.LoadingOverlayPane;
import net.filebot.util.ui.notification.SeparatorBorder;
import net.miginfocom.swing.MigLayout;
class SplitTool extends Tool<TreeModel> {
class SplitTool extends Tool<TreeModel> implements ChangeListener {
private FileTree tree = new FileTree();
private SpinnerNumberModel spinnerModel = new SpinnerNumberModel(4480, 0, Integer.MAX_VALUE, 100);
public SplitTool() {
super("Disks");
JScrollPane treeScrollPane = new JScrollPane(tree);
treeScrollPane.setBorder(new SeparatorBorder(2, new Color(0, 0, 0, 90), GradientStyle.TOP_TO_BOTTOM, SeparatorBorder.Position.BOTTOM));
JSpinner spinner = new JSpinner(spinnerModel);
spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));
setLayout(new MigLayout("insets 0, nogrid, fill", "align center", "[fill][pref!]"));
add(new LoadingOverlayPane(treeScrollPane, this), "grow, wrap");
add(new JLabel("Split every"));
add(spinner, "wmax 80, gap top rel, gap bottom unrel");
add(new JLabel("MB."));
tree.setTransferHandler(new DefaultTransferHandler(null, new FileTreeExportHandler()));
tree.setDragEnabled(true);
spinnerModel.addChangeListener(this);
spinnerModel.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent evt) {
// update model in foreground, will be much faster than the initial load because length() is cached now
if (getRoot() != null) {
updateRoot(getRoot());
}
}
});
}
private long getSplitSize() {
return spinnerModel.getNumber().intValue() * FileUtilities.MEGA;
}
private FolderNode sourceModel = null;
public void stateChanged(ChangeEvent evt) {
if (sourceModel != null) {
try {
// update model in foreground, will be much faster than the initial load because length() is cached now
setModel(createModelInBackground(sourceModel));
} catch (InterruptedException e) {
// will not happen
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
}
}
}
@Override
protected TreeModel createModelInBackground(FolderNode sourceModel) throws InterruptedException {
this.sourceModel = sourceModel;
FolderNode root = new FolderNode();
protected TreeModel createModelInBackground(File root) throws InterruptedException {
int nextPart = 1;
long splitSize = getSplitSize();
List<File> currentPart = new ArrayList<File>(50);
List<File> remainder = new ArrayList<File>(50);
List<File> files = (root != null) ? FileUtilities.listFiles(root) : Collections.emptyList();
List<TreeNode> rootGroup = new ArrayList<TreeNode>();
List<File> currentPart = new ArrayList<File>();
List<File> remainder = new ArrayList<File>();
long totalSize = 0;
for (Iterator<File> iterator = sourceModel.fileIterator(); iterator.hasNext();) {
File file = iterator.next();
long fileSize = file.length();
for (File f : files) {
long fileSize = f.length();
if (fileSize > splitSize) {
remainder.add(file);
remainder.add(f);
continue;
}
if (totalSize + fileSize > splitSize) {
// part is full, add node and start with next one
root.add(createStatisticsNode(String.format("Disk %d", nextPart++), currentPart));
rootGroup.add(createStatisticsNode(String.format("Disk %d", nextPart++), currentPart));
// reset total size and file list
totalSize = 0;
currentPart.clear();
}
totalSize += fileSize;
currentPart.add(file);
// unwind thread, if we have been cancelled
if (Thread.interrupted()) {
throw new InterruptedException();
}
currentPart.add(f);
}
if (!currentPart.isEmpty()) {
// add last part
root.add(createStatisticsNode(String.format("Disk %d", nextPart++), currentPart));
rootGroup.add(createStatisticsNode(String.format("Disk %d", nextPart++), currentPart));
}
if (!remainder.isEmpty()) {
root.add(createStatisticsNode("Remainder", remainder));
rootGroup.add(createStatisticsNode("Remainder", remainder));
}
return new DefaultTreeModel(root);
return new DefaultTreeModel(new FolderNode("Volumes", rootGroup));
}
@Override
protected void setModel(TreeModel model) {
tree.setModel(model);
}
}

View File

@ -1,8 +1,8 @@
package net.filebot.ui.analyze;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.logging.Level;
@ -10,6 +10,7 @@ import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.SwingWorker;
import javax.swing.tree.TreeNode;
import net.filebot.ui.analyze.FileTree.FileNode;
import net.filebot.ui.analyze.FileTree.FolderNode;
@ -17,63 +18,63 @@ import net.filebot.util.ExceptionUtilities;
import net.filebot.util.FileUtilities;
import net.filebot.util.ui.LoadingOverlayPane;
import org.apache.commons.io.FileUtils;
abstract class Tool<M> extends JComponent {
private UpdateModelTask updateTask = null;
private File root = null;
public Tool(String name) {
setName(name);
}
public void setSourceModel(FolderNode sourceModel) {
public File getRoot() {
return root;
}
public void updateRoot(File root) {
this.root = root;
if (updateTask != null) {
updateTask.cancel(true);
}
Tool.this.firePropertyChange(LoadingOverlayPane.LOADING_PROPERTY, false, true);
updateTask = new UpdateModelTask(sourceModel);
updateTask = new UpdateModelTask(root);
updateTask.execute();
}
protected abstract M createModelInBackground(FolderNode sourceModel) throws InterruptedException;
protected abstract M createModelInBackground(File root) throws InterruptedException;
protected abstract void setModel(M model);
private class UpdateModelTask extends SwingWorker<M, Void> {
private final FolderNode sourceModel;
public UpdateModelTask(FolderNode sourceModel) {
this.sourceModel = sourceModel;
private final File root;
public UpdateModelTask(File root) {
this.root = root;
}
@Override
protected M doInBackground() throws Exception {
return createModelInBackground(sourceModel);
return createModelInBackground(root);
}
@Override
protected void done() {
if (this == updateTask) {
Tool.this.firePropertyChange(LoadingOverlayPane.LOADING_PROPERTY, true, false);
}
// update task will only be cancelled if a newer update task has been started
if (this == updateTask && !isCancelled()) {
try {
setModel(get());
} catch (Exception e) {
Throwable cause = ExceptionUtilities.getRootCause(e);
if (cause instanceof ConcurrentModificationException || cause instanceof InterruptedException) {
// if it happens, it is supposed to
} else {
@ -84,25 +85,28 @@ abstract class Tool<M> extends JComponent {
}
}
}
protected FolderNode createStatisticsNode(String name, List<File> files) {
FolderNode folder = new FolderNode(null, files.size());
long totalSize = 0;
for (File file : files) {
folder.add(new FileNode(file));
totalSize += file.length();
protected List<TreeNode> createFileNodes(Collection<File> files) {
List<TreeNode> nodes = new ArrayList<TreeNode>(files.size());
for (File f : files) {
nodes.add(new FileNode(f));
}
// format the number of files string (e.g. 1 file, 2 files, ...)
String numberOfFiles = String.format("%,d %s", files.size(), files.size() == 1 ? "file" : "files");
// set node text (e.g. txt (1 file, 42 Byte))
folder.setTitle(String.format("%s (%s, %s)", name, numberOfFiles, FileUtilities.formatSize(totalSize)));
return folder;
return nodes;
}
protected FolderNode createStatisticsNode(String name, List<File> files) {
long totalCount = 0;
long totalSize = 0;
for (File f : files) {
totalCount += FileUtilities.listFiles(f).size();
totalSize += FileUtils.sizeOf(f);
}
// set node text (e.g. txt (1 file, 42 Byte))
String title = String.format("%s (%,d %s, %s)", name, totalCount, totalCount == 1 ? "file" : "files", FileUtilities.formatSize(totalSize));
return new FolderNode(null, title, createFileNodes(files));
}
}

View File

@ -1,99 +1,96 @@
package net.filebot.ui.analyze;
import static java.util.Collections.*;
import static net.filebot.util.FileUtilities.*;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JScrollPane;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import net.miginfocom.swing.MigLayout;
import net.filebot.MediaTypes;
import net.filebot.media.MediaDetection;
import net.filebot.ui.analyze.FileTree.FolderNode;
import net.filebot.ui.transfer.DefaultTransferHandler;
import net.filebot.util.FileUtilities;
import net.filebot.util.ui.LoadingOverlayPane;
import net.miginfocom.swing.MigLayout;
class TypeTool extends Tool<TreeModel> {
private FileTree tree = new FileTree();
public TypeTool() {
super("Types");
setLayout(new MigLayout("insets 0, fill"));
JScrollPane treeScrollPane = new JScrollPane(tree);
treeScrollPane.setBorder(BorderFactory.createEmptyBorder());
add(new LoadingOverlayPane(treeScrollPane, this), "grow");
tree.setTransferHandler(new DefaultTransferHandler(null, new FileTreeExportHandler()));
tree.setDragEnabled(true);
}
@Override
protected TreeModel createModelInBackground(FolderNode sourceModel) throws InterruptedException {
Map<String, List<File>> map = new HashMap<String, List<File>>();
for (Iterator<File> iterator = sourceModel.fileIterator(); iterator.hasNext();) {
File file = iterator.next();
String extension = FileUtilities.getExtension(file);
if (extension != null) {
extension = extension.toLowerCase();
protected TreeModel createModelInBackground(File root) throws InterruptedException {
List<File> filesAndFolders = (root != null) ? listFiles(singleton(root), FILE_WALK_MAX_DEPTH, false, true, true) : emptyList();
List<TreeNode> groups = new ArrayList<TreeNode>();
for (Entry<String, FileFilter> it : getMetaTypes().entrySet()) {
List<File> selection = filter(filesAndFolders, it.getValue());
if (selection.size() > 0) {
groups.add(createStatisticsNode(it.getKey(), selection));
}
List<File> files = map.get(extension);
if (files == null) {
files = new ArrayList<File>(50);
map.put(extension, files);
}
files.add(file);
}
List<String> keys = new ArrayList<String>(map.keySet());
// sort strings like always, handle null as empty string
Collections.sort(keys, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return ((s1 != null) ? s1 : "").compareTo((s2 != null) ? s2 : "");
}
});
SortedMap<String, TreeNode> extensionGroups = new TreeMap<String, TreeNode>(String.CASE_INSENSITIVE_ORDER);
for (Entry<String, List<File>> it : mapByExtension(filter(filesAndFolders, FILES)).entrySet()) {
if (it.getKey() == null)
continue;
extensionGroups.put(it.getKey(), createStatisticsNode(it.getKey(), it.getValue()));
}
groups.addAll(extensionGroups.values());
// create tree model
FolderNode root = new FolderNode();
for (String key : keys) {
root.add(createStatisticsNode(key, map.get(key)));
// unwind thread, if we have been cancelled
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
return new DefaultTreeModel(root);
return new DefaultTreeModel(new FolderNode("Types", groups));
}
public Map<String, FileFilter> getMetaTypes() {
Map<String, FileFilter> types = new LinkedHashMap<String, FileFilter>();
types.put("Video", MediaTypes.VIDEO_FILES);
types.put("Disk Folder", MediaDetection.getDiskFolderFilter());
types.put("Subtitle", MediaTypes.SUBTITLE_FILES);
types.put("Audio", MediaTypes.AUDIO_FILES);
types.put("Archive", MediaTypes.ARCHIVE_FILES);
types.put("Verification", MediaTypes.VERIFICATION_FILES);
try {
types.put("Clutter", MediaDetection.getClutterFileFilter());
} catch (IOException e) {
Logger.getLogger(TypeTool.class.getName()).log(Level.WARNING, e.getMessage());
}
return types;
}
@Override
protected void setModel(TreeModel model) {
tree.setModel(model);
}
}

View File

@ -93,7 +93,7 @@ class FilesListTransferablePolicy extends FileTransferablePolicy {
}
}
model.addAll(FastFile.get(entries));
model.addAll(FastFile.create(entries));
}
@Override

View File

@ -96,7 +96,7 @@ class NamesListTransferablePolicy extends FileTransferablePolicy {
loadTorrentFiles(files, values);
} else {
// load all files from the given folders recursively up do a depth of 32
values.addAll(FastFile.get(listFiles(files)));
values.addAll(FastFile.create(listFiles(files)));
}
model.addAll(values);

View File

@ -77,9 +77,10 @@ public class RenameModel extends MatchModel<Object, File> {
for (int i = 0; i < names.size(); i++) {
if (hasComplement(i)) {
File originalFile = files().get(i);
FormattedFuture formattedFuture = names.get(i);
// make sure we're dealing with regular File objects form here on out
File originalFile = new File(files().get(i).getPath());
FormattedFuture formattedFuture = names.get(i);
StringBuilder nameBuilder = new StringBuilder();
// append formatted name, throw exception if not ready

View File

@ -1,6 +1,7 @@
package net.filebot.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -8,8 +9,14 @@ import java.util.List;
public class FastFile extends File {
private Long length;
private Long lastModified;
private Boolean exists;
private Boolean isDirectory;
private Boolean isFile;
private Boolean isHidden;
private String[] list;
private File[] listFiles;
public FastFile(String path) {
super(path);
@ -24,6 +31,11 @@ public class FastFile extends File {
return length != null ? length : (length = super.length());
}
@Override
public boolean exists() {
return exists != null ? exists : (exists = super.exists());
}
@Override
public boolean isDirectory() {
return isDirectory != null ? isDirectory : (isDirectory = super.isDirectory());
@ -35,23 +47,154 @@ public class FastFile extends File {
}
@Override
public File[] listFiles() {
String[] names = list();
if (names == null)
return null;
public boolean isHidden() {
return isHidden != null ? isHidden : (isHidden = super.isHidden());
}
@Override
public long lastModified() {
return lastModified != null ? lastModified : (lastModified = super.lastModified());
}
@Override
public String[] list() {
if (list != null) {
return list;
}
String[] names = super.list();
if (names == null) {
names = new String[0];
}
return (list = names);
}
@Override
public File[] listFiles() {
if (listFiles != null) {
return listFiles;
}
String[] names = list();
File[] files = new File[names.length];
for (int i = 0; i < names.length; i++) {
files[i] = new FastFile(this, names[i]);
}
return files;
return (listFiles = files);
}
public static FastFile get(File file) {
return new FastFile(file.getPath());
@Override
public boolean canRead() {
return true;
}
public static List<FastFile> get(Collection<File> files) {
@Override
public boolean canWrite() {
return false;
}
@Override
public boolean canExecute() {
return false;
}
@Override
public boolean createNewFile() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public boolean delete() {
throw new UnsupportedOperationException();
}
@Override
public void deleteOnExit() {
throw new UnsupportedOperationException();
}
@Override
public boolean mkdir() {
throw new UnsupportedOperationException();
}
@Override
public boolean mkdirs() {
throw new UnsupportedOperationException();
}
@Override
public boolean renameTo(File dest) {
throw new UnsupportedOperationException();
}
@Override
public boolean setLastModified(long time) {
throw new UnsupportedOperationException();
}
@Override
public boolean setReadOnly() {
throw new UnsupportedOperationException();
}
@Override
public boolean setWritable(boolean writable, boolean ownerOnly) {
throw new UnsupportedOperationException();
}
@Override
public boolean setWritable(boolean writable) {
throw new UnsupportedOperationException();
}
@Override
public boolean setReadable(boolean readable, boolean ownerOnly) {
throw new UnsupportedOperationException();
}
@Override
public boolean setReadable(boolean readable) {
throw new UnsupportedOperationException();
}
@Override
public boolean setExecutable(boolean executable, boolean ownerOnly) {
throw new UnsupportedOperationException();
}
@Override
public boolean setExecutable(boolean executable) {
throw new UnsupportedOperationException();
}
@Override
public long getTotalSpace() {
throw new UnsupportedOperationException();
}
@Override
public long getFreeSpace() {
throw new UnsupportedOperationException();
}
@Override
public long getUsableSpace() {
throw new UnsupportedOperationException();
}
public static List<FastFile> create(Collection<File> files) {
List<FastFile> result = new ArrayList<FastFile>(files.size());
for (File file : files) {