From 8c8b64909ad70184080464d940aef7d86e04c408 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 26 May 2008 23:32:43 +0200 Subject: [PATCH] implement first version of file manager --- src/net/pterodactylus/jsite/gui/FileManager.java | 357 +++++++++++++++++++++- src/net/pterodactylus/jsite/gui/ProjectPanel.java | 34 ++- 2 files changed, 385 insertions(+), 6 deletions(-) diff --git a/src/net/pterodactylus/jsite/gui/FileManager.java b/src/net/pterodactylus/jsite/gui/FileManager.java index 78c24a7..faec4ae 100644 --- a/src/net/pterodactylus/jsite/gui/FileManager.java +++ b/src/net/pterodactylus/jsite/gui/FileManager.java @@ -19,12 +19,32 @@ package net.pterodactylus.jsite.gui; +import java.awt.BorderLayout; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; import net.pterodactylus.jsite.i18n.I18n; import net.pterodactylus.jsite.i18n.I18nable; +import net.pterodactylus.jsite.project.Entry; import net.pterodactylus.jsite.project.Project; +import net.pterodactylus.util.data.Node; +import net.pterodactylus.util.data.Tree; +import net.pterodactylus.util.logging.Logging; +import net.pterodactylus.util.swing.SwingUtils; /** * Manages physical and virtual files in a project. @@ -33,9 +53,18 @@ import net.pterodactylus.jsite.project.Project; */ public class FileManager extends JDialog implements I18nable { + /** Logger. */ + private static final Logger logger = Logging.getLogger(FileManager.class.getName()); + /** The project whose files to manage. */ private final Project project; + /** The tree model for the project files. */ + private final FileTreeModel fileTreeModel; + + /** The tree that shows the files. */ + private JTree fileTree; + /** * Creates a new file manager. * @@ -45,9 +74,12 @@ public class FileManager extends JDialog implements I18nable { * The project whose files to manage */ public FileManager(JFrame parent, Project project) { - super(parent, I18n.get("fileManager.title", project.getName())); + super(parent, I18n.get("fileManager.title", project.getName()), true); + logger.log(Level.FINEST, "project: " + project); this.project = project; + fileTreeModel = new FileTreeModel(); initComponents(); + SwingUtils.repackCentered(this); } // @@ -58,7 +90,13 @@ public class FileManager extends JDialog implements I18nable { * Initializes all components. */ private void initComponents() { - /* TODO. */ + JPanel contentPanel = new JPanel(new BorderLayout(12, 12)); + + fileTree = new JTree(fileTreeModel); + fileTree.setShowsRootHandles(false); + contentPanel.add(new JScrollPane(fileTree), BorderLayout.CENTER); + + setContentPane(contentPanel); } // @@ -72,4 +110,319 @@ public class FileManager extends JDialog implements I18nable { setTitle(I18n.get("fileManager.title", project.getName())); } + /** + * Model for the tree of files. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ + private class FileTreeModel implements TreeModel, PropertyChangeListener { + + /** Tree model listeners. */ + private final List treeModelListeners = new ArrayList(); + + /** The tree of files. */ + private final Tree fileTreePathTree = new Tree(); + + /** + * Creates a new file tree model. + */ + FileTreeModel() { + buildTree(); + } + + // + // EVENT MANAGEMENT + // + + /** + * {@inheritDoc} + */ + public void addTreeModelListener(TreeModelListener treeModelListener) { + treeModelListeners.add(treeModelListener); + } + + /** + * {@inheritDoc} + */ + public void removeTreeModelListener(TreeModelListener treeModelListener) { + treeModelListeners.remove(treeModelListener); + } + + // + // ACCESSORS + // + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public Object getChild(Object parent, int index) { + logger.log(Level.FINEST, "getChild(" + parent + ", " + index + ")"); + Node parentNode = findNode(parent); + if (parentNode != null) { + return parentNode.getChild(index).getElement(); + } + return null; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public int getChildCount(Object parent) { + logger.log(Level.FINEST, "getChildCount(" + parent + ")"); + Node parentNode = findNode(parent); + if (parentNode != null) { + logger.log(Level.FINEST, "getChildCount(" + parent + "): " + parentNode.size()); + return parentNode.size(); + } + return -1; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public int getIndexOfChild(Object parent, Object child) { + logger.log(Level.FINEST, "getIndexOfChild(" + parent + ", " + child + ")"); + Node parentNode = findNode(parent); + if (parentNode != null) { + return parentNode.getIndexOfChild((FileTreePath) child); + } + return -1; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public Object getRoot() { + logger.log(Level.FINEST, "getRoot()"); + return fileTreePathTree.getRootNode().getChild(0).getElement(); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public boolean isLeaf(Object node) { + logger.log(Level.FINEST, "isLeaf(" + node + ")"); + Node parentNode = findNode(node); + if (parentNode != null) { + return parentNode.size() == 0; + } + return true; + } + + // + // ACTIONS + // + + /** + * {@inheritDoc} + */ + public void valueForPathChanged(TreePath path, Object newValue) { + /* TODO - implement */ + } + + // + // PRIVATE METHODS + // + + /** + * Finds the node for the given object. This method is quite necessary + * because the element for the root node of the JTree is + * null + * + * @param node + * The element whose node to return + * @return The node, or null if no node could be found + */ + private Node findNode(Object node) { + if (node == null) { + return fileTreePathTree.getRootNode().getChild(0); + } + return fileTreePathTree.getRootNode().getChild(0).findChild((FileTreePath) node); + } + + /** + * Builds the tree from the project’s file entries. + */ + @SuppressWarnings("synthetic-access") + private void buildTree() { + Tree pathTree = new Tree(); + Node pathRootNode = pathTree.getRootNode().addChild(File.separator); + logger.log(Level.FINEST, "project: " + project); + buildTree(pathRootNode, project.getBasePathEntries()); + buildTree(pathRootNode, project.getVirtualEntries()); + /* now convert to a tree suitable for the JTree. */ + Node fileTreePathRootNode = fileTreePathTree.getRootNode(); + fileTreePathRootNode.removeAllChildren(); + convertTree(File.separator, pathRootNode, fileTreePathRootNode.addChild(new FileTreePath(File.separator, project.getName()))); + /* TODO - now add entries to all file tree path tree nodes. */ + } + + /** + * Traverses the tree of path nodes and converts all paths to + * {@link FileTreePath} objects, suitable for the JTree. + * + * @param completePath + * The base path of the current root node + * @param pathRootNode + * The root node of the path tree + * @param fileTreePathRootNode + * The root node of the file tree path tree. + */ + private void convertTree(String completePath, Node pathRootNode, Node fileTreePathRootNode) { + for (Node pathChild: pathRootNode) { + String currentFilePath = completePath + pathChild.getElement(); + Node newNode = fileTreePathRootNode.addChild(new FileTreePath(currentFilePath)); + convertTree(currentFilePath, pathChild, newNode); + } + fileTreePathRootNode.sortChildren(); + } + + /** + * Builds a tree matching the directory structure of the given entries. + * + * @param pathRootNode + * The root node of the tree + * @param entries + * The entries + */ + private void buildTree(Node pathRootNode, List entries) { + for (Entry basePathEntry: entries) { + String entryName = basePathEntry.getName(); + String[] directories = entryName.split("\\" + File.separator); + Node currentPathNode = pathRootNode; + for (String directory: directories) { + if (!currentPathNode.hasChild(directory)) { + currentPathNode = currentPathNode.addChild(directory); + } else { + currentPathNode = currentPathNode.getChild(directory); + } + } + } + } + + // + // INTERFACE PropertyChangeListener + // + + /** + * {@inheritDoc} + */ + public void propertyChange(PropertyChangeEvent propertyChangeEvent) { + if (propertyChangeEvent.getSource() instanceof Project) { + if (propertyChangeEvent.getPropertyName().equals(Project.PROPERTY_BASE_PATH_ENTRIES)) { + buildTree(); + } + } + } + + } + + private static class FileTreePath implements Comparable { + + private final String filePath; + private final String fileName; + private Entry fileEntry; + + public FileTreePath(String filePath) { + this(filePath, null); + } + + public FileTreePath(String filePath, String fileName) { + this.filePath = filePath; + if (fileName == null) { + if (filePath.indexOf(File.separatorChar) != -1) { + this.fileName = filePath.substring(filePath.lastIndexOf(File.separatorChar) + 1); + } else { + this.fileName = filePath; + } + } else { + this.fileName = fileName; + } + } + + /** + * Returns the complete file path. + * + * @return The file path + */ + public String getFilePath() { + return filePath; + } + + /** + * Returns the file name, i.e. everything after the last + * {@link File#separatorChar}. + * + * @return The file name + */ + public String getFileName() { + return fileName; + } + + /** + * Returns the file entry associated with this path, if any. + * + * @return The file entry associated with this path, or + * null if this path denotes a directory + */ + public Entry getFileEntry() { + return fileEntry; + } + + /** + * Sets the entry associated with this path. + * + * @param fileEntry + * The entry + */ + public void setFileEntry(Entry fileEntry) { + this.fileEntry = fileEntry; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object object) { + if ((object == null) || !(object instanceof FileTreePath)) { + return false; + } + FileTreePath fileTreePath = (FileTreePath) object; + return fileTreePath.filePath.equals(filePath); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return filePath.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return fileName; + } + + // + // INTERFACE Comparable + // + + /** + * {@inheritDoc} + */ + public int compareTo(FileTreePath otherFileTreePath) { + return filePath.compareTo(otherFileTreePath.filePath); + } + + } + } diff --git a/src/net/pterodactylus/jsite/gui/ProjectPanel.java b/src/net/pterodactylus/jsite/gui/ProjectPanel.java index 5dac869..3201e55 100644 --- a/src/net/pterodactylus/jsite/gui/ProjectPanel.java +++ b/src/net/pterodactylus/jsite/gui/ProjectPanel.java @@ -26,6 +26,7 @@ import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.util.List; +import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BorderFactory; @@ -67,6 +68,9 @@ public class ProjectPanel extends JPanel implements DocumentListener, I18nable { /** The “change base path” action. */ private I18nAction changeBasePathAction; + /** The “edit files” action. */ + private I18nAction editFilesAction; + /** The “name” label. */ private I18nLabel nameLabel; @@ -98,6 +102,7 @@ public class ProjectPanel extends JPanel implements DocumentListener, I18nable { */ public ProjectPanel(SwingInterface swingInterface, Project project) { super(new BorderLayout(12, 12)); + logger.log(Level.FINEST, "project: " + project); this.swingInterface = swingInterface; this.project = project; initActions(); @@ -135,6 +140,16 @@ public class ProjectPanel extends JPanel implements DocumentListener, I18nable { changeBasePath(); } }; + editFilesAction = new I18nAction("projectPanel.button.editFiles") { + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + editFiles(); + } + }; } /** @@ -162,25 +177,27 @@ public class ProjectPanel extends JPanel implements DocumentListener, I18nable { nameTextField.getDocument().addDocumentListener(this); nameLabel = new I18nLabel("projectPanel.label.name", nameTextField); propertiesPanel.add(nameLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - propertiesPanel.add(nameTextField, new GridBagConstraints(1, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); + propertiesPanel.add(nameTextField, new GridBagConstraints(1, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); descriptionTextField = new JTextField(project.getDescription()); descriptionTextField.getDocument().addDocumentListener(this); descriptionLabel = new I18nLabel("projectPanel.label.description", descriptionTextField); propertiesPanel.add(descriptionLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 0, 0, 0), 0, 0)); - propertiesPanel.add(descriptionTextField, new GridBagConstraints(1, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0)); + propertiesPanel.add(descriptionTextField, new GridBagConstraints(1, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0)); basePathTextField = new JTextField(project.getBasePath()); basePathTextField.setEditable(false); basePathLabel = new I18nLabel("projectPanel.label.basePath"); basePathInformationLabel = new JLabel(I18n.get("projectPanel.basePathInformation.fileCount", project.getBasePathEntries().size())); JButton changeBasePathButton = new JButton(changeBasePathAction); + JButton editFilesButton = new JButton(editFilesAction); propertiesPanel.add(basePathLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 0, 0, 0), 0, 0)); propertiesPanel.add(basePathTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); propertiesPanel.add(changeBasePathButton, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0)); - propertiesPanel.add(basePathInformationLabel, new GridBagConstraints(1, 3, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0)); + propertiesPanel.add(editFilesButton, new GridBagConstraints(3, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0)); + propertiesPanel.add(basePathInformationLabel, new GridBagConstraints(1, 3, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0)); - propertiesPanel.add(new JPanel(), new GridBagConstraints(0, 4, 3, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + propertiesPanel.add(new JPanel(), new GridBagConstraints(0, 4, 4, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); return propertiesPanel; } @@ -248,6 +265,15 @@ public class ProjectPanel extends JPanel implements DocumentListener, I18nable { } } + /** + * Pops up the file manager and lets the user edit the parameters for the + * physical and virtual files. + */ + private void editFiles() { + FileManager fileManager = new FileManager(swingInterface.getMainWindow(), project); + fileManager.setVisible(true); + } + // // INTERFACE I18nable // -- 2.7.4