* heavy refactoring of the analyze panel
* added TreeIterator and FilterIterator * refactored file transferable policies * refactored loading overlay
This commit is contained in:
parent
1cf51ae179
commit
2fc8bb7195
|
@ -1,241 +0,0 @@
|
|||
|
||||
package net.sourceforge.filebot.ui;
|
||||
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Font;
|
||||
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.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
|
||||
|
||||
public class FileBotTree extends JTree {
|
||||
|
||||
public FileBotTree() {
|
||||
super(new DefaultTreeModel(new DefaultMutableTreeNode()));
|
||||
getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
||||
setCellRenderer(new FileBotTreeCellRenderer());
|
||||
setShowsRootHandles(true);
|
||||
setRootVisible(false);
|
||||
setRowHeight(22);
|
||||
|
||||
addMouseListener(new ExpandCollapsePopupListener());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DefaultTreeModel getModel() {
|
||||
return (DefaultTreeModel) super.getModel();
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
|
||||
root.removeAllChildren();
|
||||
|
||||
getModel().reload(root);
|
||||
}
|
||||
|
||||
|
||||
public List<File> convertToList() {
|
||||
TreeNode node = (TreeNode) getModel().getRoot();
|
||||
|
||||
return convertToList(node);
|
||||
}
|
||||
|
||||
|
||||
public List<File> convertToList(TreeNode node) {
|
||||
ArrayList<File> list = new ArrayList<File>();
|
||||
|
||||
convertToListImpl((DefaultMutableTreeNode) node, list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private void convertToListImpl(DefaultMutableTreeNode node, List<File> list) {
|
||||
if (node.isLeaf()) {
|
||||
if (node.getUserObject() instanceof File) {
|
||||
File file = (File) node.getUserObject();
|
||||
|
||||
if (file.isFile())
|
||||
list.add(file);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < node.getChildCount(); i++) {
|
||||
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
|
||||
convertToListImpl(child, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
if (value instanceof DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
|
||||
|
||||
Object userObject = node.getUserObject();
|
||||
|
||||
if ((userObject != null) && (userObject instanceof File)) {
|
||||
File file = (File) node.getUserObject();
|
||||
return file.getName();
|
||||
}
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
|
||||
public void expandOrCollapseAll(boolean expand) {
|
||||
TreeNode node = (TreeNode) getModel().getRoot();
|
||||
Enumeration<?> e = node.children();
|
||||
|
||||
while (e.hasMoreElements()) {
|
||||
TreeNode n = (TreeNode) e.nextElement();
|
||||
TreePath path = new TreePath(node).pathByAddingChild(n);
|
||||
expandOrCollapseAllImpl(path, expand);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void expandOrCollapseAllImpl(TreePath parent, boolean expand) {
|
||||
// Traverse children
|
||||
TreeNode node = (TreeNode) parent.getLastPathComponent();
|
||||
if (node.getChildCount() >= 0)
|
||||
for (Enumeration<?> e = node.children(); e.hasMoreElements();) {
|
||||
TreeNode n = (TreeNode) e.nextElement();
|
||||
TreePath path = parent.pathByAddingChild(n);
|
||||
expandOrCollapseAllImpl(path, expand);
|
||||
}
|
||||
|
||||
// Expansion or collapse must be done bottom-up
|
||||
if (expand)
|
||||
expandPath(parent);
|
||||
else
|
||||
collapsePath(parent);
|
||||
}
|
||||
|
||||
|
||||
private class ExpandCollapsePopup extends JPopupMenu {
|
||||
|
||||
public ExpandCollapsePopup() {
|
||||
if (getSelectionCount() >= 1) {
|
||||
Object pathComponent = getLastSelectedPathComponent();
|
||||
|
||||
if (pathComponent instanceof DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) pathComponent;
|
||||
|
||||
Object userObject = node.getUserObject();
|
||||
|
||||
if (userObject instanceof File) {
|
||||
File file = (File) userObject;
|
||||
|
||||
if (file.isFile()) {
|
||||
JMenuItem openItem = new JMenuItem(new OpenAction("Open", file));
|
||||
openItem.setFont(openItem.getFont().deriveFont(Font.BOLD));
|
||||
|
||||
add(openItem);
|
||||
add(new OpenAction("Open Folder", file.getParentFile()));
|
||||
addSeparator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(new ExpandOrCollapseAction(true));
|
||||
add(new ExpandOrCollapseAction(false));
|
||||
}
|
||||
|
||||
|
||||
private class OpenAction extends AbstractAction {
|
||||
|
||||
private File file;
|
||||
|
||||
|
||||
public OpenAction(String text, File file) {
|
||||
super(text);
|
||||
this.file = file;
|
||||
|
||||
if (file == null)
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
try {
|
||||
Desktop.getDesktop().open(file);
|
||||
} catch (Exception e) {
|
||||
MessageManager.showWarning(e.getMessage());
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class ExpandOrCollapseAction extends AbstractAction {
|
||||
|
||||
private boolean expand;
|
||||
|
||||
|
||||
public ExpandOrCollapseAction(boolean expand) {
|
||||
this.expand = expand;
|
||||
|
||||
if (expand) {
|
||||
putValue(SMALL_ICON, ResourceManager.getIcon("tree.expand"));
|
||||
putValue(NAME, "Expand all");
|
||||
} else {
|
||||
putValue(SMALL_ICON, ResourceManager.getIcon("tree.collapse"));
|
||||
putValue(NAME, "Collapse all");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
expandOrCollapseAll(expand);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class ExpandCollapsePopupListener extends MouseAdapter {
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
if (SwingUtilities.isRightMouseButton(e)) {
|
||||
ExpandCollapsePopup popup = new ExpandCollapsePopup();
|
||||
popup.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (SwingUtilities.isRightMouseButton(e)) {
|
||||
TreePath path = getPathForLocation(e.getX(), e.getY());
|
||||
setSelectionPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -4,8 +4,6 @@ package net.sourceforge.filebot.ui.panel.analyze;
|
|||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JTabbedPane;
|
||||
|
@ -15,9 +13,6 @@ import net.miginfocom.swing.MigLayout;
|
|||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.FileBotPanel;
|
||||
import net.sourceforge.filebot.ui.FileTransferableMessageHandler;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.tools.SplitPanel;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.tools.ToolPanel;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.tools.TypePanel;
|
||||
import net.sourceforge.tuned.MessageHandler;
|
||||
|
||||
|
||||
|
@ -26,7 +21,7 @@ public class AnalyzePanel extends FileBotPanel {
|
|||
private final FileTreePanel fileTreePanel = new FileTreePanel();
|
||||
private final JTabbedPane toolsPanel = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
|
||||
private final MessageHandler messageHandler = new FileTransferableMessageHandler(this, fileTreePanel.getFileTree().getTransferablePolicy());
|
||||
private final MessageHandler messageHandler = new FileTransferableMessageHandler(this, fileTreePanel.getTransferablePolicy());
|
||||
|
||||
|
||||
public AnalyzePanel() {
|
||||
|
@ -36,13 +31,18 @@ public class AnalyzePanel extends FileBotPanel {
|
|||
|
||||
setLayout(new MigLayout("insets 0, gapx 50, fill"));
|
||||
|
||||
add(fileTreePanel, "grow");
|
||||
add(toolsPanel, "grow");
|
||||
add(fileTreePanel, "grow, sizegroup column");
|
||||
add(toolsPanel, "grow, sizegroup column");
|
||||
|
||||
addTool(new TypePanel());
|
||||
addTool(new SplitPanel());
|
||||
addTool(new TypeTool());
|
||||
addTool(new SplitTool());
|
||||
|
||||
fileTreePanel.getFileTree().addPropertyChangeListener(FileTree.CONTENT_PROPERTY, fileTreeChangeListener);
|
||||
fileTreePanel.addPropertyChangeListener("filetree", filetreeListener);
|
||||
}
|
||||
|
||||
|
||||
private void addTool(Tool<?> tool) {
|
||||
toolsPanel.addTab(tool.getName(), tool);
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,20 +51,13 @@ public class AnalyzePanel extends FileBotPanel {
|
|||
return messageHandler;
|
||||
}
|
||||
|
||||
|
||||
private void addTool(ToolPanel toolPanel) {
|
||||
toolsPanel.addTab(toolPanel.getToolName(), toolPanel);
|
||||
}
|
||||
|
||||
private PropertyChangeListener fileTreeChangeListener = new PropertyChangeListener() {
|
||||
private final PropertyChangeListener filetreeListener = new PropertyChangeListener() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
List<File> files = (List<File>) evt.getNewValue();
|
||||
|
||||
// stopped loading, refresh tools
|
||||
for (int i = 0; i < toolsPanel.getTabCount(); i++) {
|
||||
ToolPanel toolPanel = (ToolPanel) toolsPanel.getComponentAt(i);
|
||||
toolPanel.update(files);
|
||||
Tool<?> tool = (Tool<?>) toolsPanel.getComponentAt(i);
|
||||
tool.setSourceModel(fileTreePanel.getFileTree().getRoot());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,122 +2,422 @@
|
|||
package net.sourceforge.filebot.ui.panel.analyze;
|
||||
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Font;
|
||||
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.Arrays;
|
||||
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;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
import net.sourceforge.filebot.ui.FileBotTree;
|
||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.MessageManager;
|
||||
import net.sourceforge.tuned.FilterIterator;
|
||||
import net.sourceforge.tuned.TreeIterator;
|
||||
|
||||
|
||||
class FileTree extends FileBotTree {
|
||||
|
||||
public static final String LOADING_PROPERTY = "loading";
|
||||
public static final String CONTENT_PROPERTY = "content";
|
||||
|
||||
private PostProcessor postProcessor;
|
||||
|
||||
private final FileTreeTransferablePolicy transferablePolicy;
|
||||
|
||||
public class FileTree extends JTree {
|
||||
|
||||
public FileTree() {
|
||||
transferablePolicy = new FileTreeTransferablePolicy(this);
|
||||
transferablePolicy.addPropertyChangeListener(LOADING_PROPERTY, new LoadingPropertyChangeListener());
|
||||
super(new DefaultTreeModel(new FolderNode()));
|
||||
getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
||||
setCellRenderer(new FileTreeCellRenderer());
|
||||
setShowsRootHandles(true);
|
||||
setRootVisible(false);
|
||||
|
||||
setTransferHandler(new DefaultTransferHandler(transferablePolicy, null));
|
||||
}
|
||||
|
||||
|
||||
public FileTransferablePolicy getTransferablePolicy() {
|
||||
return transferablePolicy;
|
||||
}
|
||||
|
||||
|
||||
public void removeTreeItems(TreePath paths[]) {
|
||||
List<TreeNode> changedNodes = new ArrayList<TreeNode>(paths.length);
|
||||
setRowHeight(22);
|
||||
setLargeModel(true);
|
||||
|
||||
for (TreePath element : paths) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) (element.getLastPathComponent());
|
||||
|
||||
if (!node.isRoot()) {
|
||||
changedNodes.add(node.getParent());
|
||||
node.removeFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
for (TreeNode treeNode : changedNodes) {
|
||||
getModel().reload(treeNode);
|
||||
}
|
||||
|
||||
contentChanged();
|
||||
addMouseListener(new ExpandCollapsePopupListener());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DefaultTreeModel getModel() {
|
||||
return (DefaultTreeModel) super.getModel();
|
||||
}
|
||||
|
||||
|
||||
public FolderNode getRoot() {
|
||||
return (FolderNode) getModel().getRoot();
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
transferablePolicy.reset();
|
||||
|
||||
super.clear();
|
||||
contentChanged();
|
||||
getRoot().clear();
|
||||
getModel().reload();
|
||||
}
|
||||
|
||||
|
||||
private synchronized void contentChanged() {
|
||||
if (postProcessor != null)
|
||||
postProcessor.cancel(true);
|
||||
public void removeTreeNode(TreePath... paths) {
|
||||
Set<TreeNode> dirtyNodes = new HashSet<TreeNode>();
|
||||
|
||||
postProcessor = new PostProcessor();
|
||||
postProcessor.execute();
|
||||
};
|
||||
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 LoadingPropertyChangeListener implements PropertyChangeListener {
|
||||
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, File... files) {
|
||||
this(text, Arrays.asList(files));
|
||||
}
|
||||
|
||||
|
||||
public OpenAction(String text, Collection<File> files) {
|
||||
super(text);
|
||||
putValue("files", files);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
try {
|
||||
for (File file : (Collection<File>) getValue("files")) {
|
||||
Desktop.getDesktop().open(file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
MessageManager.showWarning(e.getMessage());
|
||||
Logger.getLogger("global").log(Level.SEVERE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 propertyChange(PropertyChangeEvent evt) {
|
||||
Boolean loading = (Boolean) evt.getNewValue();
|
||||
|
||||
firePropertyChange(FileTree.LOADING_PROPERTY, null, loading);
|
||||
|
||||
if (!loading) {
|
||||
getModel().reload();
|
||||
contentChanged();
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class PostProcessor extends SwingWorker<List<File>, Void> {
|
||||
public static class AbstractTreeNode implements TreeNode {
|
||||
|
||||
private TreeNode parent;
|
||||
|
||||
|
||||
@Override
|
||||
protected List<File> doInBackground() throws Exception {
|
||||
return convertToList();
|
||||
public TreeNode getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
public void setParent(TreeNode parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
if (isCancelled())
|
||||
return;
|
||||
|
||||
try {
|
||||
List<File> files = get();
|
||||
FileTree.this.firePropertyChange(CONTENT_PROPERTY, null, files);
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||
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() {
|
||||
// if we have no children, tell the UI we are a leaf,
|
||||
// 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;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a root node (no parent, no title, empty list of children)
|
||||
*/
|
||||
public FolderNode() {
|
||||
this(null, 5);
|
||||
}
|
||||
|
||||
|
||||
public FolderNode(String title, int initialCapacity) {
|
||||
this.title = title;
|
||||
this.children = new ArrayList<AbstractTreeNode>(initialCapacity);
|
||||
}
|
||||
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
public List<AbstractTreeNode> getChildren() {
|
||||
return Collections.unmodifiableList(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);
|
||||
}
|
||||
|
||||
|
||||
@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) {
|
||||
|
||||
@Override
|
||||
protected Iterator<AbstractTreeNode> children(AbstractTreeNode 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()) {
|
||||
|
||||
@Override
|
||||
protected File filter(AbstractTreeNode node) {
|
||||
if (node instanceof FileNode)
|
||||
return ((FileNode) node).getFile();
|
||||
|
||||
// filter out non-file nodes
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
|
||||
package net.sourceforge.filebot.ui;
|
||||
package net.sourceforge.filebot.ui.panel.analyze;
|
||||
|
||||
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreeNode;
|
||||
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.tuned.ui.FancyTreeCellRenderer;
|
||||
import net.sourceforge.tuned.ui.GradientStyle;
|
||||
|
||||
|
||||
public class FileBotTreeCellRenderer extends FancyTreeCellRenderer {
|
||||
public class FileTreeCellRenderer extends FancyTreeCellRenderer {
|
||||
|
||||
public FileBotTreeCellRenderer() {
|
||||
public FileTreeCellRenderer() {
|
||||
super(GradientStyle.TOP_TO_BOTTOM);
|
||||
|
||||
openIcon = ResourceManager.getIcon("tree.open");
|
||||
closedIcon = ResourceManager.getIcon("tree.closed");
|
||||
leafIcon = ResourceManager.getIcon("tree.leaf");
|
||||
|
@ -25,23 +25,22 @@ public class FileBotTreeCellRenderer extends FancyTreeCellRenderer {
|
|||
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||
if (leaf && isFolder(value))
|
||||
super.getTreeCellRendererComponent(tree, value, sel, true, false, row, hasFocus);
|
||||
else
|
||||
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
||||
if (leaf && isFolder(value)) {
|
||||
// make leafs that are empty folders look like expanded nodes
|
||||
expanded = true;
|
||||
leaf = false;
|
||||
}
|
||||
|
||||
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private boolean isFolder(Object value) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
|
||||
Object object = node.getUserObject();
|
||||
|
||||
if (object instanceof File)
|
||||
return ((File) object).isDirectory();
|
||||
if (((TreeNode) value).getAllowsChildren())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.analyze;
|
||||
|
||||
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.TransferHandler;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FileNode;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferable;
|
||||
import net.sourceforge.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) {
|
||||
for (Iterator<File> iterator = ((FolderNode) node).fileIterator(); iterator.hasNext();) {
|
||||
files.add(iterator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!files.isEmpty())
|
||||
return new FileTransferable(files);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exportDone(JComponent source, Transferable data, int action) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) {
|
||||
return TransferHandler.COPY;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,35 +3,43 @@ package net.sourceforge.filebot.ui.panel.analyze;
|
|||
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||
import net.sourceforge.filebot.ui.transfer.LoadAction;
|
||||
import net.sourceforge.tuned.ui.LoadingOverlayPane;
|
||||
import net.sourceforge.tuned.ui.TunedUtil;
|
||||
|
||||
|
||||
class FileTreePanel extends JPanel {
|
||||
class FileTreePanel extends JComponent {
|
||||
|
||||
private FileTree fileTree = new FileTree();
|
||||
|
||||
private FileTreeTransferablePolicy transferablePolicy = new FileTreeTransferablePolicy(fileTree);
|
||||
|
||||
|
||||
public FileTreePanel() {
|
||||
super(new MigLayout("insets 0, nogrid, fill", "align center"));
|
||||
fileTree.setTransferHandler(new DefaultTransferHandler(transferablePolicy, null));
|
||||
|
||||
setBorder(BorderFactory.createTitledBorder("File Tree"));
|
||||
|
||||
add(new LoadingOverlayPane(new JScrollPane(fileTree), ResourceManager.getIcon("loading")), "grow, wrap 1.2mm");
|
||||
setLayout(new MigLayout("insets 0, nogrid, fill", "align center"));
|
||||
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");
|
||||
|
||||
transferablePolicy.addPropertyChangeListener("loading", loadingListener);
|
||||
|
||||
// Shortcut DELETE
|
||||
TunedUtil.putActionForKeystroke(fileTree, KeyStroke.getKeyStroke("pressed DELETE"), removeAction);
|
||||
}
|
||||
|
@ -41,12 +49,19 @@ class FileTreePanel extends JPanel {
|
|||
return fileTree;
|
||||
}
|
||||
|
||||
private final LoadAction loadAction = new LoadAction(fileTree.getTransferablePolicy());
|
||||
|
||||
public FileTreeTransferablePolicy getTransferablePolicy() {
|
||||
return transferablePolicy;
|
||||
}
|
||||
|
||||
private final LoadAction loadAction = new LoadAction(transferablePolicy);
|
||||
|
||||
private final AbstractAction clearAction = new AbstractAction("Clear", ResourceManager.getIcon("action.clear")) {
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
transferablePolicy.reset();
|
||||
fileTree.clear();
|
||||
fireFileTreeChange();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -58,7 +73,7 @@ class FileTreePanel extends JPanel {
|
|||
|
||||
int row = fileTree.getMinSelectionRow();
|
||||
|
||||
fileTree.removeTreeItems(fileTree.getSelectionPaths());
|
||||
fileTree.removeTreeNode(fileTree.getSelectionPaths());
|
||||
|
||||
int maxRow = fileTree.getRowCount() - 1;
|
||||
|
||||
|
@ -66,6 +81,27 @@ class FileTreePanel extends JPanel {
|
|||
row = maxRow;
|
||||
|
||||
fileTree.setSelectionRow(row);
|
||||
|
||||
fireFileTreeChange();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private void fireFileTreeChange() {
|
||||
firePropertyChange("filetree", null, fileTree);
|
||||
}
|
||||
|
||||
private final PropertyChangeListener loadingListener = new PropertyChangeListener() {
|
||||
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
boolean loading = (Boolean) evt.getNewValue();
|
||||
|
||||
// relay loading property changes for loading overlay
|
||||
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
|
||||
|
||||
if (!loading) {
|
||||
fireFileTreeChange();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,14 +5,14 @@ package net.sourceforge.filebot.ui.panel.analyze;
|
|||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtil;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.AbstractTreeNode;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FileNode;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode;
|
||||
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
|
||||
import net.sourceforge.tuned.FileUtil;
|
||||
|
||||
|
||||
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<DefaultMutableTreeNode> {
|
||||
class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<AbstractTreeNode> {
|
||||
|
||||
private final FileTree tree;
|
||||
|
||||
|
@ -22,6 +22,12 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Defaul
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean accept(List<File> files) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
tree.clear();
|
||||
|
@ -29,48 +35,58 @@ class FileTreeTransferablePolicy extends BackgroundFileTransferablePolicy<Defaul
|
|||
|
||||
|
||||
@Override
|
||||
protected void process(List<DefaultMutableTreeNode> chunks) {
|
||||
DefaultTreeModel model = tree.getModel();
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
|
||||
protected void process(List<AbstractTreeNode> chunks) {
|
||||
FolderNode root = tree.getRoot();
|
||||
|
||||
for (DefaultMutableTreeNode node : chunks) {
|
||||
for (AbstractTreeNode node : chunks) {
|
||||
root.add(node);
|
||||
}
|
||||
|
||||
model.reload(root);
|
||||
tree.getModel().reload();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void load(List<File> files) {
|
||||
for (File file : files) {
|
||||
DefaultMutableTreeNode node = getTree(file);
|
||||
|
||||
// operation may be aborted via interrupt
|
||||
if (Thread.currentThread().isInterrupted())
|
||||
return;
|
||||
|
||||
publish(node);
|
||||
try {
|
||||
for (File file : files) {
|
||||
AbstractTreeNode node = getTreeNode(file);
|
||||
|
||||
// publish on EDT
|
||||
publish(node);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// supposed to happen if background execution was aborted
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private DefaultMutableTreeNode getTree(File file) {
|
||||
DefaultMutableTreeNode node = new DefaultMutableTreeNode(file);
|
||||
private AbstractTreeNode getTreeNode(File file) throws InterruptedException {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
|
||||
if (file.isDirectory() && !Thread.currentThread().isInterrupted()) {
|
||||
// run through folders first
|
||||
for (File f : file.listFiles(FileBotUtil.FOLDERS_ONLY)) {
|
||||
node.add(getTree(f));
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
|
||||
FolderNode node = new FolderNode(FileUtil.getFolderName(file), files.length);
|
||||
|
||||
// add folders first
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
node.add(getTreeNode(f));
|
||||
}
|
||||
}
|
||||
|
||||
// then files
|
||||
for (File f : file.listFiles(FileBotUtil.FILES_ONLY)) {
|
||||
node.add(getTree(f));
|
||||
for (File f : files) {
|
||||
if (f.isFile()) {
|
||||
node.add(getTreeNode(f));
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return node;
|
||||
return new FileNode(file);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.analyze;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeModel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode;
|
||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||
import net.sourceforge.tuned.FileUtil;
|
||||
import net.sourceforge.tuned.ui.GradientStyle;
|
||||
import net.sourceforge.tuned.ui.LoadingOverlayPane;
|
||||
import net.sourceforge.tuned.ui.notification.SeparatorBorder;
|
||||
|
||||
|
||||
public 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("Split");
|
||||
|
||||
JScrollPane treeScrollPane = new JScrollPane(tree);
|
||||
treeScrollPane.setBorder(BorderFactory.createEmptyBorder());
|
||||
|
||||
JSpinner spinner = new JSpinner(spinnerModel);
|
||||
spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));
|
||||
|
||||
LoadingOverlayPane loadingOverlayPane = new LoadingOverlayPane(treeScrollPane, this);
|
||||
loadingOverlayPane.setBorder(new SeparatorBorder(2, new Color(0, 0, 0, 90), GradientStyle.TOP_TO_BOTTOM, SeparatorBorder.Position.BOTTOM));
|
||||
|
||||
setLayout(new MigLayout("insets 0, nogrid, fill", "align center"));
|
||||
|
||||
add(loadingOverlayPane, "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);
|
||||
}
|
||||
|
||||
|
||||
private long getSplitSize() {
|
||||
return spinnerModel.getNumber().intValue() * FileUtil.MEGA;
|
||||
}
|
||||
|
||||
private FolderNode sourceModel = null;
|
||||
|
||||
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
if (sourceModel != null)
|
||||
setSourceModel(sourceModel);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TreeModel createModelInBackground(FolderNode sourceModel, Cancellable cancellable) {
|
||||
this.sourceModel = sourceModel;
|
||||
|
||||
FolderNode root = new FolderNode();
|
||||
int nextPart = 1;
|
||||
|
||||
long splitSize = getSplitSize();
|
||||
|
||||
List<File> currentPart = new ArrayList<File>(50);
|
||||
List<File> remainder = new ArrayList<File>(50);
|
||||
long totalSize = 0;
|
||||
|
||||
for (Iterator<File> iterator = sourceModel.fileIterator(); iterator.hasNext() && !cancellable.isCancelled();) {
|
||||
File file = iterator.next();
|
||||
|
||||
long fileSize = file.length();
|
||||
|
||||
if (fileSize > splitSize) {
|
||||
remainder.add(file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (totalSize + fileSize > splitSize) {
|
||||
// part is full, add node and start with next one
|
||||
root.add(createStatisticsNode(String.format("Part %d", nextPart++), currentPart));
|
||||
|
||||
// reset total size and file list
|
||||
totalSize = 0;
|
||||
currentPart.clear();
|
||||
}
|
||||
|
||||
totalSize += fileSize;
|
||||
currentPart.add(file);
|
||||
}
|
||||
|
||||
if (!currentPart.isEmpty()) {
|
||||
// add last part
|
||||
root.add(createStatisticsNode(String.format("Part %d", nextPart++), currentPart));
|
||||
}
|
||||
|
||||
if (!remainder.isEmpty()) {
|
||||
root.add(createStatisticsNode("Remainder", remainder));
|
||||
}
|
||||
|
||||
return new DefaultTreeModel(root);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void setModel(TreeModel model) {
|
||||
tree.setModel(model);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.analyze;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingWorker;
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FileNode;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode;
|
||||
import net.sourceforge.tuned.FileUtil;
|
||||
import net.sourceforge.tuned.ui.LoadingOverlayPane;
|
||||
|
||||
|
||||
abstract class Tool<M> extends JComponent {
|
||||
|
||||
private UpdateModelTask updateTask = null;
|
||||
private Semaphore updateSemaphore = new Semaphore(1);
|
||||
|
||||
|
||||
public Tool(String name) {
|
||||
setName(name);
|
||||
}
|
||||
|
||||
|
||||
public synchronized void setSourceModel(FolderNode sourceModel) {
|
||||
if (updateTask != null) {
|
||||
updateTask.cancel(false);
|
||||
}
|
||||
|
||||
updateTask = new UpdateModelTask(sourceModel);
|
||||
|
||||
updateSemaphore.acquireUninterruptibly();
|
||||
setLoading(true);
|
||||
updateTask.execute();
|
||||
}
|
||||
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
firePropertyChange(LoadingOverlayPane.LOADING_PROPERTY, !loading, loading);
|
||||
}
|
||||
|
||||
|
||||
protected abstract M createModelInBackground(FolderNode sourceModel, Cancellable cancellable);
|
||||
|
||||
|
||||
protected abstract void setModel(M model);
|
||||
|
||||
|
||||
private class UpdateModelTask extends SwingWorker<M, Void> implements Cancellable {
|
||||
|
||||
private final FolderNode sourceModel;
|
||||
|
||||
|
||||
public UpdateModelTask(FolderNode sourceModel) {
|
||||
this.sourceModel = sourceModel;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected M doInBackground() throws Exception {
|
||||
return createModelInBackground(sourceModel, this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
// update task will only be cancelled if a newer update task has been started
|
||||
if (!isCancelled()) {
|
||||
try {
|
||||
setModel(get());
|
||||
} catch (Exception e) {
|
||||
// should not happen
|
||||
Logger.getLogger("global").log(Level.WARNING, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
updateSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static interface Cancellable {
|
||||
|
||||
boolean isCancelled();
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// 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, FileUtil.formatSize(totalSize)));
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.panel.analyze;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeModel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ui.panel.analyze.FileTree.FolderNode;
|
||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||
import net.sourceforge.tuned.FileUtil;
|
||||
import net.sourceforge.tuned.ui.LoadingOverlayPane;
|
||||
|
||||
|
||||
public class TypeTool extends Tool<TreeModel> {
|
||||
|
||||
private FileTree tree = new FileTree();
|
||||
|
||||
|
||||
public TypeTool() {
|
||||
super("Types");
|
||||
|
||||
setLayout(new MigLayout("insets 0, fill"));
|
||||
|
||||
JScrollPane sp = new JScrollPane(tree);
|
||||
sp.setBorder(BorderFactory.createEmptyBorder());
|
||||
add(new LoadingOverlayPane(sp, this), "grow");
|
||||
|
||||
tree.setTransferHandler(new DefaultTransferHandler(null, new FileTreeExportHandler()));
|
||||
tree.setDragEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TreeModel createModelInBackground(FolderNode sourceModel, Cancellable cancellable) {
|
||||
TreeMap<String, List<File>> map = new TreeMap<String, List<File>>();
|
||||
|
||||
for (Iterator<File> iterator = sourceModel.fileIterator(); iterator.hasNext() && !cancellable.isCancelled();) {
|
||||
File file = iterator.next();
|
||||
String extension = FileUtil.getExtension(file);
|
||||
|
||||
List<File> files = map.get(extension);
|
||||
|
||||
if (files == null) {
|
||||
files = new ArrayList<File>(50);
|
||||
map.put(extension, files);
|
||||
}
|
||||
|
||||
files.add(file);
|
||||
}
|
||||
|
||||
FolderNode root = new FolderNode();
|
||||
|
||||
for (Entry<String, List<File>> entry : map.entrySet()) {
|
||||
root.add(createStatisticsNode(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
return new DefaultTreeModel(root);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void setModel(TreeModel model) {
|
||||
tree.setModel(model);
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,12 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean accept(List<File> files) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
list.getModel().clear();
|
||||
|
@ -34,21 +40,22 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
|
|||
|
||||
@Override
|
||||
protected void load(List<File> files) {
|
||||
if (files.size() > 1) {
|
||||
list.setTitle(FileUtil.getFolderName(files.get(0).getParentFile()));
|
||||
}
|
||||
// set title based on parent folder of first file
|
||||
list.setTitle(FileUtil.getFolderName(files.get(0).getParentFile()));
|
||||
|
||||
if (FileBotUtil.containsOnlyFolders(files)) {
|
||||
loadFolderList(files);
|
||||
loadFolders(files);
|
||||
} else if (FileBotUtil.containsOnlyTorrentFiles(files)) {
|
||||
loadTorrentList(files);
|
||||
loadTorrents(files);
|
||||
} else {
|
||||
super.load(files);
|
||||
for (File file : files) {
|
||||
list.getModel().add(FileUtil.getFileName(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loadFolderList(List<File> folders) {
|
||||
private void loadFolders(List<File> folders) {
|
||||
if (folders.size() == 1) {
|
||||
// if only one folder was dropped, use its name as title
|
||||
list.setTitle(FileUtil.getFolderName(folders.get(0)));
|
||||
|
@ -62,7 +69,7 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
|
|||
}
|
||||
|
||||
|
||||
private void loadTorrentList(List<File> torrentFiles) {
|
||||
private void loadTorrents(List<File> torrentFiles) {
|
||||
try {
|
||||
List<Torrent> torrents = new ArrayList<Torrent>(torrentFiles.size());
|
||||
|
||||
|
@ -81,17 +88,11 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
|
|||
}
|
||||
} catch (IOException e) {
|
||||
// should not happen
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||
Logger.getLogger("global").log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void load(File file) {
|
||||
list.getModel().add(FileUtil.getFileName(file));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getFileFilterDescription() {
|
||||
return "files, folders and torrents";
|
||||
|
|
|
@ -22,6 +22,12 @@ class FilesListTransferablePolicy extends FileTransferablePolicy {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean accept(List<File> files) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
model.clear();
|
||||
|
@ -32,17 +38,18 @@ class FilesListTransferablePolicy extends FileTransferablePolicy {
|
|||
protected void load(List<File> files) {
|
||||
if (FileBotUtil.containsOnlyFolders(files)) {
|
||||
for (File folder : files) {
|
||||
super.load(Arrays.asList(folder.listFiles()));
|
||||
loadFiles(Arrays.asList(folder.listFiles()));
|
||||
}
|
||||
} else {
|
||||
super.load(files);
|
||||
loadFiles(files);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void load(File file) {
|
||||
model.add(new FileEntry(file));
|
||||
protected void loadFiles(List<File> files) {
|
||||
for (File file : files) {
|
||||
model.add(new FileEntry(file));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ class NamesListTransferablePolicy extends FilesListTransferablePolicy {
|
|||
|
||||
submit(entries);
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||
Logger.getLogger("global").log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ class NamesListTransferablePolicy extends FilesListTransferablePolicy {
|
|||
|
||||
submit(entries);
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||
Logger.getLogger("global").log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,12 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean accept(List<File> files) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
checksumComputationService.reset();
|
||||
|
@ -49,7 +55,7 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
|||
String line = null;
|
||||
Pattern pattern = Pattern.compile("(.*)\\s+(\\p{XDigit}{8})");
|
||||
|
||||
while (((line = in.readLine()) != null) && !Thread.currentThread().isInterrupted()) {
|
||||
while (((line = in.readLine()) != null) && !Thread.interrupted()) {
|
||||
if (line.startsWith(";"))
|
||||
continue;
|
||||
|
||||
|
@ -74,7 +80,7 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
|||
in.close();
|
||||
} catch (IOException e) {
|
||||
// should not happen
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.SEVERE, e.toString(), e);
|
||||
Logger.getLogger("global").log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,30 +93,34 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
|||
|
||||
@Override
|
||||
protected void load(List<File> files) {
|
||||
if (FileBotUtil.containsOnlySfvFiles(files)) {
|
||||
// one or more sfv files
|
||||
for (File file : files) {
|
||||
loadSfvFile(file);
|
||||
}
|
||||
} else if ((files.size() == 1) && files.get(0).isDirectory()) {
|
||||
// one single folder
|
||||
File file = files.get(0);
|
||||
|
||||
for (File f : file.listFiles()) {
|
||||
load(f, file, "");
|
||||
}
|
||||
} else {
|
||||
// bunch of files
|
||||
for (File f : files) {
|
||||
load(f, f.getParentFile(), "");
|
||||
try {
|
||||
if (FileBotUtil.containsOnlySfvFiles(files)) {
|
||||
// one or more sfv files
|
||||
for (File file : files) {
|
||||
loadSfvFile(file);
|
||||
}
|
||||
} else if ((files.size() == 1) && files.get(0).isDirectory()) {
|
||||
// one single folder
|
||||
File file = files.get(0);
|
||||
|
||||
for (File f : file.listFiles()) {
|
||||
load(f, file, "");
|
||||
}
|
||||
} else {
|
||||
// bunch of files
|
||||
for (File f : files) {
|
||||
load(f, f.getParentFile(), "");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// supposed to happen if background execution was aborted
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void load(File file, File column, String prefix) {
|
||||
if (Thread.currentThread().isInterrupted())
|
||||
return;
|
||||
protected void load(File file, File column, String prefix) throws InterruptedException {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
|
||||
if (file.isDirectory()) {
|
||||
// load all files in the file tree
|
||||
|
@ -122,4 +132,5 @@ class SfvTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumTab
|
|||
publish(new ChecksumTableModel.ChecksumCell(prefix + file.getName(), checksumComputationService.schedule(file, column), column));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,14 +69,14 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
|
|||
*
|
||||
* @param chunks
|
||||
*/
|
||||
protected synchronized final void publish(V... chunks) {
|
||||
protected synchronized void publish(V... chunks) {
|
||||
if (worker != null) {
|
||||
worker.publishChunks(chunks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class BackgroundWorker extends SwingWorker<Void, V> {
|
||||
private class BackgroundWorker extends SwingWorker<Object, V> {
|
||||
|
||||
private final List<File> files;
|
||||
|
||||
|
@ -87,9 +87,8 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
|
|||
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
protected Object doInBackground() {
|
||||
load(files);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -114,13 +113,13 @@ public abstract class BackgroundFileTransferablePolicy<V> extends FileTransferab
|
|||
|
||||
@Override
|
||||
public void started(PropertyChangeEvent evt) {
|
||||
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, null, true);
|
||||
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, false, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void done(PropertyChangeEvent evt) {
|
||||
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, null, false);
|
||||
propertyChangeSupport.firePropertyChange(LOADING_PROPERTY, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public abstract class FileTransferablePolicy extends TransferablePolicy {
|
|||
files.add(file);
|
||||
} catch (Exception e) {
|
||||
// URISyntaxException, IllegalArgumentException, FileNotFoundException
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid file url: " + line);
|
||||
Logger.getLogger("global").log(Level.WARNING, "Invalid file url: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,42 +79,21 @@ public abstract class FileTransferablePolicy extends TransferablePolicy {
|
|||
public void handleTransferable(Transferable tr, TransferAction action) {
|
||||
List<File> files = getFilesFromTransferable(tr);
|
||||
|
||||
if (action != TransferAction.ADD)
|
||||
if (action != TransferAction.ADD) {
|
||||
clear();
|
||||
}
|
||||
|
||||
load(files);
|
||||
}
|
||||
|
||||
|
||||
protected boolean accept(List<File> files) {
|
||||
for (File f : files)
|
||||
if (!accept(f))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
protected abstract boolean accept(List<File> files);
|
||||
|
||||
|
||||
protected void load(List<File> files) {
|
||||
for (File file : files) {
|
||||
load(file);
|
||||
}
|
||||
}
|
||||
protected abstract void load(List<File> files);
|
||||
|
||||
|
||||
protected boolean accept(File file) {
|
||||
return file.isFile() || file.isDirectory();
|
||||
}
|
||||
|
||||
|
||||
protected void clear() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void load(File file) {
|
||||
|
||||
}
|
||||
protected abstract void clear();
|
||||
|
||||
|
||||
public String getFileFilterDescription() {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
package net.sourceforge.tuned;
|
||||
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
public abstract class FilterIterator<S, T> implements Iterator<T> {
|
||||
|
||||
private final Iterator<S> sourceIterator;
|
||||
|
||||
|
||||
public FilterIterator(Iterable<S> source) {
|
||||
this(source.iterator());
|
||||
}
|
||||
|
||||
|
||||
public FilterIterator(Iterator<S> sourceIterator) {
|
||||
this.sourceIterator = sourceIterator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return peekNext(false) != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
try {
|
||||
return peekNext(true);
|
||||
} finally {
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
||||
private T current = null;
|
||||
|
||||
|
||||
private T peekNext(boolean forceNext) {
|
||||
while (current == null && (forceNext || (sourceIterator.hasNext()))) {
|
||||
current = filter(sourceIterator.next());
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
protected abstract T filter(S sourceValue);
|
||||
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
sourceIterator.remove();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
package net.sourceforge.tuned;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
|
||||
public abstract class TreeIterator<T> implements Iterator<T> {
|
||||
|
||||
private final LinkedList<Iterator<T>> recursionStack = new LinkedList<Iterator<T>>();
|
||||
|
||||
|
||||
public TreeIterator(T... root) {
|
||||
recursionStack.push(Arrays.asList(root).iterator());
|
||||
}
|
||||
|
||||
|
||||
protected abstract Iterator<T> children(T node);
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return currentIterator().hasNext();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
T node = currentIterator().next();
|
||||
|
||||
Iterator<T> children = children(node);
|
||||
if (children != null && children.hasNext()) {
|
||||
// step into next recursion level
|
||||
recursionStack.push(children);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
private Iterator<T> currentIterator() {
|
||||
Iterator<T> iterator = recursionStack.peek();
|
||||
|
||||
if (iterator.hasNext() || recursionStack.size() <= 1)
|
||||
return iterator;
|
||||
|
||||
// step back one recursion level
|
||||
recursionStack.pop();
|
||||
|
||||
return currentIterator();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
// can't just use remove() on current iterator, because
|
||||
// we may have stepped into the next recursion level
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -2,20 +2,15 @@
|
|||
package net.sourceforge.tuned.ui;
|
||||
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.OverlayLayout;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
|
||||
public class LoadingOverlayPane extends JPanel {
|
||||
public class LoadingOverlayPane extends JComponent {
|
||||
|
||||
public static final String LOADING_PROPERTY = "loading";
|
||||
|
||||
|
@ -26,37 +21,21 @@ public class LoadingOverlayPane extends JPanel {
|
|||
private int millisToOverlay = 500;
|
||||
|
||||
|
||||
public LoadingOverlayPane(JComponent component, Icon animation) {
|
||||
this(component, new JLabel(""), getView(component));
|
||||
public LoadingOverlayPane(JComponent component, JComponent propertyChangeSource) {
|
||||
this(component, new ProgressIndicator(), propertyChangeSource);
|
||||
}
|
||||
|
||||
|
||||
public LoadingOverlayPane(JComponent component, JComponent animation) {
|
||||
this(component, animation, getView(component));
|
||||
}
|
||||
|
||||
|
||||
public LoadingOverlayPane(JComponent component, JComponent animation, JComponent view) {
|
||||
setLayout(new OverlayLayout(this));
|
||||
public LoadingOverlayPane(JComponent component, JComponent animationComponent, JComponent propertyChangeSource) {
|
||||
setLayout(new MigLayout("fill, insets 0"));
|
||||
this.animationComponent = animationComponent;
|
||||
|
||||
this.animationComponent = animation;
|
||||
add(animationComponent, "pos visual.x2-pref-18px 8px");
|
||||
add(component, "grow");
|
||||
|
||||
component.setAlignmentX(1.0f);
|
||||
component.setAlignmentY(0.0f);
|
||||
animationComponent.setVisible(false);
|
||||
|
||||
animation.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 20));
|
||||
|
||||
animation.setAlignmentX(1.0f);
|
||||
animation.setAlignmentY(0.0f);
|
||||
animationComponent.setPreferredSize(new Dimension(48, 48));
|
||||
animationComponent.setMaximumSize(animationComponent.getPreferredSize());
|
||||
|
||||
add(animation);
|
||||
add(component);
|
||||
|
||||
setOverlayVisible(true);
|
||||
|
||||
view.addPropertyChangeListener(LOADING_PROPERTY, loadingListener);
|
||||
propertyChangeSource.addPropertyChangeListener(LOADING_PROPERTY, loadingListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,16 +45,6 @@ public class LoadingOverlayPane extends JPanel {
|
|||
}
|
||||
|
||||
|
||||
private static JComponent getView(JComponent component) {
|
||||
if (component instanceof JScrollPane) {
|
||||
JScrollPane scrollPane = (JScrollPane) component;
|
||||
return (JComponent) scrollPane.getViewport().getView();
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
|
||||
public void setOverlayVisible(boolean b) {
|
||||
overlayEnabled = b;
|
||||
|
||||
|
|
|
@ -5,73 +5,64 @@ package net.sourceforge.tuned.ui;
|
|||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.geom.Arc2D;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Calendar;
|
||||
|
||||
import javax.swing.BoundedRangeModel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.Timer;
|
||||
|
||||
|
||||
public class ProgressIndicator extends JPanel {
|
||||
public class ProgressIndicator extends JComponent {
|
||||
|
||||
private BoundedRangeModel model = null;
|
||||
private float radius = 4.0f;
|
||||
private int shapeCount = 3;
|
||||
|
||||
private boolean indeterminate = false;
|
||||
private float strokeWidth = 2f;
|
||||
private Stroke stroke = new BasicStroke(strokeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
|
||||
|
||||
private float indeterminateRadius = 4.0f;
|
||||
private int indeterminateShapeCount = 1;
|
||||
|
||||
private float progressStrokeWidth = 4.5f;
|
||||
private float remainingStrokeWidth = 2f;
|
||||
|
||||
private Stroke progressStroke = new BasicStroke(progressStrokeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
|
||||
private Stroke remainingStroke = new BasicStroke(remainingStrokeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
|
||||
|
||||
private Color progressColor = Color.orange;
|
||||
private Color remainingColor = new Color(0f, 0f, 0f, 0.25f);
|
||||
|
||||
private Color textColor = new Color(0x5F5F5F);
|
||||
|
||||
private boolean paintText = true;
|
||||
private boolean paintBackground = false;
|
||||
private Color progressShapeColor = Color.orange;
|
||||
private Color backgroundShapeColor = new Color(0f, 0f, 0f, 0.25f);
|
||||
|
||||
private final Rectangle2D frame = new Rectangle2D.Double();
|
||||
private final Arc2D arc = new Arc2D.Double();
|
||||
private final Ellipse2D circle = new Ellipse2D.Double();
|
||||
|
||||
private final Dimension baseSize = new Dimension(32, 32);
|
||||
|
||||
private Timer updateTimer = new Timer(20, new ActionListener() {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
repaint();
|
||||
}
|
||||
});;
|
||||
|
||||
|
||||
public ProgressIndicator() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
setPreferredSize(baseSize);
|
||||
|
||||
addComponentListener(new ComponentAdapter() {
|
||||
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
stopAnimation();
|
||||
}
|
||||
|
||||
|
||||
public ProgressIndicator(BoundedRangeModel model) {
|
||||
this.model = model;
|
||||
|
||||
indeterminate = (model == null);
|
||||
|
||||
setFont(new Font(Font.DIALOG, Font.BOLD, 8));
|
||||
}
|
||||
|
||||
|
||||
public double getProgress() {
|
||||
if (model == null)
|
||||
return 0;
|
||||
|
||||
double total = model.getMaximum() - model.getMinimum();
|
||||
double current = model.getValue() - model.getMinimum();
|
||||
|
||||
return current / total;
|
||||
@Override
|
||||
public void componentShown(ComponentEvent e) {
|
||||
startAnimation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -85,85 +76,38 @@ public class ProgressIndicator extends JPanel {
|
|||
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
if (paintBackground) {
|
||||
frame.setFrame(0, 0, baseSize.width, baseSize.height);
|
||||
|
||||
g2d.setPaint(getBackground());
|
||||
circle.setFrame(frame);
|
||||
g2d.fill(circle);
|
||||
}
|
||||
frame.setFrame(radius, radius, baseSize.width - radius * 2 - 1, baseSize.height - radius * 2 - 1);
|
||||
|
||||
double inset = Math.max(Math.max(remainingStrokeWidth, progressStrokeWidth), indeterminateRadius);
|
||||
frame.setFrame(inset, inset, baseSize.width - inset * 2 - 1, baseSize.height - inset * 2 - 1);
|
||||
|
||||
if (!indeterminate) {
|
||||
paintProgress(g2d);
|
||||
} else {
|
||||
paintIndeterminate(g2d);
|
||||
}
|
||||
paintShapes(g2d);
|
||||
}
|
||||
|
||||
|
||||
protected void paintProgress(Graphics2D g2d) {
|
||||
|
||||
double progress = getProgress();
|
||||
|
||||
// remaining circle
|
||||
protected void paintShapes(Graphics2D g2d) {
|
||||
circle.setFrame(frame);
|
||||
|
||||
g2d.setStroke(remainingStroke);
|
||||
g2d.setPaint(remainingColor);
|
||||
|
||||
g2d.draw(circle);
|
||||
|
||||
// progress circle
|
||||
arc.setArc(frame, 90, progress * 360 * -1, Arc2D.OPEN);
|
||||
|
||||
g2d.setStroke(progressStroke);
|
||||
g2d.setPaint(progressColor);
|
||||
|
||||
g2d.draw(arc);
|
||||
|
||||
if (paintText) {
|
||||
// text
|
||||
g2d.setFont(getFont());
|
||||
g2d.setPaint(textColor);
|
||||
|
||||
String text = String.format("%d%%", (int) (100 * progress));
|
||||
Rectangle2D textBounds = g2d.getFontMetrics().getStringBounds(text, g2d);
|
||||
|
||||
g2d.drawString(text, (float) (frame.getCenterX() - textBounds.getX() - (textBounds.getWidth() / 2f) + 0.5f), (float) (frame.getCenterY() - textBounds.getY() - (textBounds.getHeight() / 2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void paintIndeterminate(Graphics2D g2d) {
|
||||
circle.setFrame(frame);
|
||||
|
||||
g2d.setStroke(remainingStroke);
|
||||
g2d.setPaint(remainingColor);
|
||||
g2d.setStroke(stroke);
|
||||
g2d.setPaint(backgroundShapeColor);
|
||||
|
||||
g2d.draw(circle);
|
||||
|
||||
Point2D center = new Point2D.Double(frame.getCenterX(), frame.getMinY());
|
||||
|
||||
circle.setFrameFromCenter(center, new Point2D.Double(center.getX() + indeterminateRadius, center.getY() + indeterminateRadius));
|
||||
circle.setFrameFromCenter(center, new Point2D.Double(center.getX() + radius, center.getY() + radius));
|
||||
|
||||
g2d.setStroke(progressStroke);
|
||||
g2d.setPaint(progressColor);
|
||||
g2d.setStroke(stroke);
|
||||
g2d.setPaint(progressShapeColor);
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
||||
double theta = getTheta(now.get(Calendar.MILLISECOND), now.getMaximum(Calendar.MILLISECOND));
|
||||
g2d.rotate(theta, frame.getCenterX(), frame.getCenterY());
|
||||
|
||||
theta = getTheta(1, indeterminateShapeCount);
|
||||
theta = getTheta(1, shapeCount);
|
||||
|
||||
for (int i = 0; i < indeterminateShapeCount; i++) {
|
||||
for (int i = 0; i < shapeCount; i++) {
|
||||
g2d.rotate(theta, frame.getCenterX(), frame.getCenterY());
|
||||
g2d.fill(circle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -172,58 +116,18 @@ public class ProgressIndicator extends JPanel {
|
|||
}
|
||||
|
||||
|
||||
public BoundedRangeModel getModel() {
|
||||
return model;
|
||||
public void startAnimation() {
|
||||
updateTimer.restart();
|
||||
}
|
||||
|
||||
|
||||
public void setModel(BoundedRangeModel model) {
|
||||
this.model = model;
|
||||
public void stopAnimation() {
|
||||
updateTimer.stop();
|
||||
}
|
||||
|
||||
|
||||
public boolean isIndeterminate() {
|
||||
return indeterminate;
|
||||
}
|
||||
|
||||
|
||||
public void setIndeterminate(boolean indeterminate) {
|
||||
this.indeterminate = indeterminate;
|
||||
}
|
||||
|
||||
|
||||
public void setIndeterminateRadius(float indeterminateRadius) {
|
||||
this.indeterminateRadius = indeterminateRadius;
|
||||
}
|
||||
|
||||
|
||||
public void setIndeterminateShapeCount(int indeterminateShapeCount) {
|
||||
this.indeterminateShapeCount = indeterminateShapeCount;
|
||||
}
|
||||
|
||||
|
||||
public void setProgressColor(Color progressColor) {
|
||||
this.progressColor = progressColor;
|
||||
}
|
||||
|
||||
|
||||
public void setRemainingColor(Color remainingColor) {
|
||||
this.remainingColor = remainingColor;
|
||||
}
|
||||
|
||||
|
||||
public void setTextColor(Color textColor) {
|
||||
this.textColor = textColor;
|
||||
}
|
||||
|
||||
|
||||
public void setPaintBackground(boolean paintBackground) {
|
||||
this.paintBackground = paintBackground;
|
||||
}
|
||||
|
||||
|
||||
public void setPaintText(boolean paintString) {
|
||||
this.paintText = paintString;
|
||||
public void setShapeCount(int indeterminateShapeCount) {
|
||||
this.shapeCount = indeterminateShapeCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue