diff --git a/source/net/filebot/ui/analyze/AnalyzePanel.java b/source/net/filebot/ui/analyze/AnalyzePanel.java index b729d20d..aa8e7aaf 100644 --- a/source/net/filebot/ui/analyze/AnalyzePanel.java +++ b/source/net/filebot/ui/analyze/AnalyzePanel.java @@ -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()); } } }; diff --git a/source/net/filebot/ui/analyze/AttributeTool.java b/source/net/filebot/ui/analyze/AttributeTool.java index 2006d7b0..d8021c05 100644 --- a/source/net/filebot/ui/analyze/AttributeTool.java +++ b/source/net/filebot/ui/analyze/AttributeTool.java @@ -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 { @@ -44,12 +44,11 @@ class AttributeTool extends Tool { } @Override - protected TableModel createModelInBackground(FolderNode sourceModel) throws InterruptedException { + protected TableModel createModelInBackground(File root) throws InterruptedException { + List files = (root != null) ? FileUtilities.listFiles(root) : Collections.emptyList(); + FileAttributesTableModel model = new FileAttributesTableModel(); - - for (Iterator iterator = sourceModel.fileIterator(); iterator.hasNext();) { - File file = iterator.next(); - + for (File file : files) { if (VIDEO_FILES.accept(file)) { try { MetaAttributes attributes = new MetaAttributes(file); diff --git a/source/net/filebot/ui/analyze/ExtractTool.java b/source/net/filebot/ui/analyze/ExtractTool.java index 09865230..cbef89ae 100644 --- a/source/net/filebot/ui/analyze/ExtractTool.java +++ b/source/net/filebot/ui/analyze/ExtractTool.java @@ -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 { @@ -73,13 +72,12 @@ class ExtractTool extends Tool { } @Override - protected TableModel createModelInBackground(FolderNode sourceModel) throws InterruptedException { + protected TableModel createModelInBackground(File root) throws InterruptedException { + List files = (root != null) ? FileUtilities.listFiles(root) : Collections.emptyList(); + List entries = new ArrayList(); - try { - for (Iterator 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); diff --git a/source/net/filebot/ui/analyze/FileTree.java b/source/net/filebot/ui/analyze/FileTree.java index 4f678c2d..7a2f2749 100644 --- a/source/net/filebot/ui/analyze/FileTree.java +++ b/source/net/filebot/ui/analyze/FileTree.java @@ -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 dirtyNodes = new HashSet(); - - 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 selectionFiles = null; - + if (selectionPaths != null) { selectionFiles = new LinkedHashSet(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 selectionParentFolders = new LinkedHashSet(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 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 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 children; - private String title; - + + private final File file; + + private final String title; + private final List 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(initialCapacity); + public FolderNode(String title, List children) { + this(null, title, children); } - - public void setTitle(String title) { + public FolderNode(File file, String title, List children) { + this.file = file; this.title = title; + this.children = children; + } + + public File getFile() { + return file; } - @Override public String toString() { return title; } - - public List getChildren() { - return Collections.unmodifiableList(children); + public List 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 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 treeIterator() { - return new TreeIterator(this) { - + public Iterator treeIterator() { + return new TreeIterator(this) { + @Override - protected Iterator children(AbstractTreeNode node) { + protected Iterator children(TreeNode node) { if (node instanceof FolderNode) return ((FolderNode) node).getChildren().iterator(); - + // can't step into non-folder nodes return null; } - + }; } - public Iterator fileIterator() { - return new FilterIterator(treeIterator()) { - + return new FilterIterator(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; } }; } } - + } diff --git a/source/net/filebot/ui/analyze/FileTreeExportHandler.java b/source/net/filebot/ui/analyze/FileTreeExportHandler.java index 1a0d16e5..7f826d67 100644 --- a/source/net/filebot/ui/analyze/FileTreeExportHandler.java +++ b/source/net/filebot/ui/analyze/FileTreeExportHandler.java @@ -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 files = new LinkedHashSet(); - + 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; } - + } diff --git a/source/net/filebot/ui/analyze/FileTreePanel.java b/source/net/filebot/ui/analyze/FileTreePanel.java index 37743ac7..1292bddd 100644 --- a/source/net/filebot/ui/analyze/FileTreePanel.java +++ b/source/net/filebot/ui/analyze/FileTreePanel.java @@ -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); } - + } diff --git a/source/net/filebot/ui/analyze/FileTreeTransferablePolicy.java b/source/net/filebot/ui/analyze/FileTreeTransferablePolicy.java index 85022323..70e80490 100644 --- a/source/net/filebot/ui/analyze/FileTreeTransferablePolicy.java +++ b/source/net/filebot/ui/analyze/FileTreeTransferablePolicy.java @@ -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 { +class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy { private final FileTree tree; @@ -30,18 +34,12 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy chunks) { - FolderNode root = tree.getRoot(); - - for (AbstractTreeNode node : chunks) { - root.add(node); - } - + protected void process(List root) { + tree.getModel().setRoot(root.get(0)); tree.getModel().reload(); } @@ -53,40 +51,39 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy 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 files = getChildren(file); - FolderNode node = new FolderNode(getFolderName(file), files.size()); + LinkedList children = new LinkedList(); + 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(children)); } return new FileNode(file); @@ -94,7 +91,7 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy { -class SplitTool extends Tool 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 currentPart = new ArrayList(50); - List remainder = new ArrayList(50); + + List files = (root != null) ? FileUtilities.listFiles(root) : Collections.emptyList(); + + List rootGroup = new ArrayList(); + List currentPart = new ArrayList(); + List remainder = new ArrayList(); long totalSize = 0; - - for (Iterator 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); } - + } diff --git a/source/net/filebot/ui/analyze/Tool.java b/source/net/filebot/ui/analyze/Tool.java index 6baefdc8..d554069b 100644 --- a/source/net/filebot/ui/analyze/Tool.java +++ b/source/net/filebot/ui/analyze/Tool.java @@ -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 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 { - - 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 extends JComponent { } } } - - - protected FolderNode createStatisticsNode(String name, List 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 createFileNodes(Collection files) { + List nodes = new ArrayList(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 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)); + } + } diff --git a/source/net/filebot/ui/analyze/TypeTool.java b/source/net/filebot/ui/analyze/TypeTool.java index 7703d02d..2fadc88b 100644 --- a/source/net/filebot/ui/analyze/TypeTool.java +++ b/source/net/filebot/ui/analyze/TypeTool.java @@ -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 { - + 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> map = new HashMap>(); - - for (Iterator 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 filesAndFolders = (root != null) ? listFiles(singleton(root), FILE_WALK_MAX_DEPTH, false, true, true) : emptyList(); + List groups = new ArrayList(); + + for (Entry it : getMetaTypes().entrySet()) { + List selection = filter(filesAndFolders, it.getValue()); + if (selection.size() > 0) { + groups.add(createStatisticsNode(it.getKey(), selection)); } - - List files = map.get(extension); - if (files == null) { - files = new ArrayList(50); - map.put(extension, files); - } - - files.add(file); } - - List keys = new ArrayList(map.keySet()); - - // sort strings like always, handle null as empty string - Collections.sort(keys, new Comparator() { - - @Override - public int compare(String s1, String s2) { - return ((s1 != null) ? s1 : "").compareTo((s2 != null) ? s2 : ""); - } - }); - + + SortedMap extensionGroups = new TreeMap(String.CASE_INSENSITIVE_ORDER); + for (Entry> 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 getMetaTypes() { + Map types = new LinkedHashMap(); + 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); } - + } diff --git a/source/net/filebot/ui/rename/FilesListTransferablePolicy.java b/source/net/filebot/ui/rename/FilesListTransferablePolicy.java index 7e6d1c63..1a0493f6 100644 --- a/source/net/filebot/ui/rename/FilesListTransferablePolicy.java +++ b/source/net/filebot/ui/rename/FilesListTransferablePolicy.java @@ -93,7 +93,7 @@ class FilesListTransferablePolicy extends FileTransferablePolicy { } } - model.addAll(FastFile.get(entries)); + model.addAll(FastFile.create(entries)); } @Override diff --git a/source/net/filebot/ui/rename/NamesListTransferablePolicy.java b/source/net/filebot/ui/rename/NamesListTransferablePolicy.java index d4be2e8e..e00f27ad 100644 --- a/source/net/filebot/ui/rename/NamesListTransferablePolicy.java +++ b/source/net/filebot/ui/rename/NamesListTransferablePolicy.java @@ -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); diff --git a/source/net/filebot/ui/rename/RenameModel.java b/source/net/filebot/ui/rename/RenameModel.java index 21dacb1a..109f0626 100644 --- a/source/net/filebot/ui/rename/RenameModel.java +++ b/source/net/filebot/ui/rename/RenameModel.java @@ -77,9 +77,10 @@ public class RenameModel extends MatchModel { 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 diff --git a/source/net/filebot/util/FastFile.java b/source/net/filebot/util/FastFile.java index 6ca8d484..7c32a934 100644 --- a/source/net/filebot/util/FastFile.java +++ b/source/net/filebot/util/FastFile.java @@ -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 get(Collection 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 create(Collection files) { List result = new ArrayList(files.size()); for (File file : files) {