2 * jSite2 - FileManager.java -
3 * Copyright © 2008 David Roden
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 package net.pterodactylus.jsite.gui;
22 import java.awt.BorderLayout;
23 import java.awt.Color;
24 import java.awt.Component;
25 import java.awt.Dimension;
26 import java.awt.FlowLayout;
28 import java.awt.GridBagConstraints;
29 import java.awt.GridBagLayout;
30 import java.awt.Insets;
31 import java.awt.event.ActionEvent;
32 import java.awt.event.ActionListener;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
39 import javax.swing.BorderFactory;
40 import javax.swing.JButton;
41 import javax.swing.JDialog;
42 import javax.swing.JLabel;
43 import javax.swing.JOptionPane;
44 import javax.swing.JPanel;
45 import javax.swing.JScrollPane;
46 import javax.swing.JTextField;
47 import javax.swing.JTree;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.tree.DefaultTreeCellRenderer;
51 import javax.swing.tree.DefaultTreeModel;
52 import javax.swing.tree.TreeNode;
53 import javax.swing.tree.TreePath;
55 import net.pterodactylus.jsite.i18n.I18n;
56 import net.pterodactylus.jsite.i18n.I18nable;
57 import net.pterodactylus.jsite.i18n.gui.I18nAction;
58 import net.pterodactylus.jsite.i18n.gui.I18nLabel;
59 import net.pterodactylus.jsite.project.Project;
60 import net.pterodactylus.util.logging.Logging;
61 import net.pterodactylus.util.swing.SortableTreeNode;
62 import net.pterodactylus.util.swing.SwingUtils;
65 * Manages physical and virtual files in a project.
67 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
69 public class FileManager extends JDialog implements I18nable, ActionListener, TreeSelectionListener {
72 private static final Logger logger = Logging.getLogger(FileManager.class.getName());
74 /** The Swing interface. */
75 private final SwingInterface swingInterface;
77 /** The project whose files to manage. */
78 private final Project project;
80 /** The root of the file tree. */
81 private final SortableTreeNode fileTreeRoot;
83 /** The tree model for the project files. */
84 private final DefaultTreeModel fileTreeModel;
86 /** Files that are hidden as per {@link File#isHidden()}. */
87 private final List<String> hiddenFiles = new ArrayList<String>();
89 /** The tree cell renderer. */
90 private final FileCellRenderer fileCellRenderer;
92 /** The “rescan” action. */
93 private I18nAction rescanAction;
95 /** The “close” action. */
96 private I18nAction closeAction;
98 /** The “project files” label. */
99 private I18nLabel projectFilesLabel;
101 /** The tree that shows the files. */
102 private JTree fileTree;
104 /** The scroll pane that holds the file tree. */
105 private JScrollPane fileScrollPane;
107 /** The “file properties” label. */
108 private I18nLabel filePropertiesLabel;
110 /** The “file path” label. */
111 private I18nLabel filePathLabel;
113 /** The “file path” textfield. */
114 private JTextField filePathTextField;
116 /** The “file name” label. */
117 private I18nLabel fileNameLabel;
119 /** The “file name” textfield. */
120 private JTextField fileNameTextField;
122 /** The “file size” label. */
123 private I18nLabel fileSizeLabel;
125 /** The “file size” text field. */
126 private JTextField fileSizeTextField;
129 * Creates a new file manager.
131 * @param swingInterface
132 * The Swing interface
134 * The project whose files to manage
136 public FileManager(SwingInterface swingInterface, Project project) {
137 super(swingInterface.getMainWindow(), I18n.get("fileManager.title", project.getName()), true);
138 logger.log(Level.FINEST, "project: " + project);
139 this.swingInterface = swingInterface;
140 this.project = project;
141 fileTreeRoot = new SortableTreeNode(project.getName());
142 fileTreeModel = new DefaultTreeModel(fileTreeRoot);
143 fileCellRenderer = new FileCellRenderer();
147 SwingUtils.center(this);
155 * @see java.awt.Component#setVisible(boolean)
158 public void setVisible(boolean visible) {
162 super.setVisible(visible);
170 * Initializes all actions.
172 private void initActions() {
173 closeAction = new I18nAction("fileManager.button.close") {
178 public void actionPerformed(ActionEvent e) {
182 rescanAction = new I18nAction("fileManager.button.rescan") {
187 @SuppressWarnings("synthetic-access")
188 public void actionPerformed(ActionEvent actionEvent) {
195 * Initializes all components.
197 private void initComponents() {
198 JPanel contentPanel = new JPanel(new BorderLayout(12, 12));
199 contentPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
201 contentPanel.add(createFileManagerPanel(), BorderLayout.CENTER);
202 contentPanel.add(createButtonPanel(), BorderLayout.PAGE_END);
204 setContentPane(contentPanel);
208 * Creates the main panel with the file tree and the file properties.
210 * @return The mail panel
212 private Component createFileManagerPanel() {
213 JPanel fileManagerPanel = new JPanel(new BorderLayout(12, 12));
215 /* file tree panel */
216 JPanel fileTreePanel = new JPanel(new BorderLayout(12, 12));
217 fileManagerPanel.add(fileTreePanel, BorderLayout.LINE_START);
219 fileTree = new JTree(fileTreeModel);
220 fileTree.setShowsRootHandles(false);
221 fileTree.addTreeSelectionListener(this);
222 fileTree.setCellRenderer(fileCellRenderer);
223 fileTreePanel.add(fileScrollPane = new JScrollPane(fileTree), BorderLayout.CENTER);
224 fileScrollPane.setPreferredSize(new Dimension(250, 400));
226 projectFilesLabel = new I18nLabel("fileManager.label.projectFiles", fileTree);
227 JPanel projectFilesLabelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
228 fileTreePanel.add(projectFilesLabelPanel, BorderLayout.NORTH);
229 projectFilesLabelPanel.add(projectFilesLabel);
231 /* the right panel */
232 JPanel rightPanel = new JPanel(new BorderLayout(12, 12));
233 fileManagerPanel.add(rightPanel, BorderLayout.CENTER);
235 /* properties panel */
236 JPanel propertiesPanel = new JPanel(new GridBagLayout());
237 rightPanel.add(propertiesPanel, BorderLayout.CENTER);
238 propertiesPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(12, 12, 12, 12)));
240 filePropertiesLabel = new I18nLabel("fileManager.label.fileProperties");
241 filePropertiesLabel.setFont(filePropertiesLabel.getFont().deriveFont(Font.BOLD));
242 propertiesPanel.add(filePropertiesLabel, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
244 filePathLabel = new I18nLabel("fileManager.label.filePath");
245 filePathTextField = new JTextField();
246 filePathTextField.setEditable(false);
247 propertiesPanel.add(filePathLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 24, 0, 0), 0, 0));
248 propertiesPanel.add(filePathTextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
250 fileNameLabel = new I18nLabel("fileManager.label.fileName");
251 fileNameTextField = new JTextField();
252 fileNameTextField.setEditable(false);
253 propertiesPanel.add(fileNameLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 24, 0, 0), 0, 0));
254 propertiesPanel.add(fileNameTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
256 fileSizeLabel = new I18nLabel("fileManager.label.fileSize");
257 fileSizeTextField = new JTextField();
258 fileSizeTextField.setEditable(false);
259 propertiesPanel.add(fileSizeLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 24, 0, 0), 0, 0));
260 propertiesPanel.add(fileSizeTextField, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
263 propertiesPanel.add(new JPanel(), new GridBagConstraints(0, 3, 2, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
265 /* action button panel */
266 JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12));
267 rightPanel.add(actionButtonPanel, BorderLayout.PAGE_END);
268 actionButtonPanel.setBorder(BorderFactory.createEtchedBorder());
270 JButton rescanButton = new JButton(rescanAction);
271 actionButtonPanel.add(rescanButton);
273 return fileManagerPanel;
277 * Creates the button panel.
279 * @return The button panel
281 private Component createButtonPanel() {
282 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
284 buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
285 JButton closeButton = new JButton(closeAction);
286 buttonPanel.add(closeButton);
288 getRootPane().setDefaultButton(closeButton);
293 * Initiates a file scan.
295 private void initiateFileScan() {
296 swingInterface.getThreadPool().execute(new Runnable() {
299 * @see java.lang.Runnable#run()
301 @SuppressWarnings("synthetic-access")
303 fileTree.setEnabled(false);
304 rescanAction.setEnabled(false);
305 String basePath = project.getBasePath();
306 File basePathDirectory = new File(basePath);
307 fileTreeRoot.removeAll();
309 if (!basePathDirectory.exists() || !basePathDirectory.isDirectory()) {
311 JOptionPane.showMessageDialog(FileManager.this, I18n.get(""), I18n.get(""), JOptionPane.ERROR_MESSAGE);
313 scanDirectory(fileTreeRoot, basePathDirectory, "", hiddenFiles);
315 fileTreeModel.reload();
316 // fileScrollPane.revalidate();
317 rescanAction.setEnabled(true);
318 fileTree.setEnabled(true);
321 private void scanDirectory(SortableTreeNode rootNode, File directory, String currentDirectory, List<String> hiddenFiles) {
322 for (File file : directory.listFiles()) {
323 String fileName = file.getName();
324 SortableTreeNode fileNode = new SortableTreeNode(fileName);
325 rootNode.add(fileNode);
326 if (file.isFile() && file.isHidden()) {
327 hiddenFiles.add((currentDirectory + File.separator + fileName).substring(1));
329 if (file.isDirectory()) {
330 scanDirectory(fileNode, file, currentDirectory + File.separator + fileName, hiddenFiles);
340 * Returns the complete path for the given node.
344 * @return The complete path for the node, or the empty string (“”) for the
347 private String getPathForNode(TreeNode treeNode) {
348 TreeNode[] pathToNode = fileTreeModel.getPathToRoot(treeNode);
349 StringBuilder completePathBuilder = new StringBuilder();
350 boolean first = true;
351 for (TreeNode nodePathNode : pathToNode) {
356 if (!(nodePathNode instanceof SortableTreeNode)) {
357 logger.log(Level.WARNING, "nodePathNode is not a SortableTreeNode!");
360 completePathBuilder.append(File.separatorChar).append(((SortableTreeNode) nodePathNode).getUserObject());
362 String completePath = (completePathBuilder.length() == 0) ? "" : completePathBuilder.substring(1);
367 // INTERFACE I18nable
373 public void updateI18n() {
374 setTitle(I18n.get("fileManager.title", project.getName()));
375 projectFilesLabel.updateI18n();
376 filePropertiesLabel.updateI18n();
377 filePathLabel.updateI18n();
381 // INTERFACE TreeSelectionListener
387 public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
388 TreePath[] selectedPaths = fileTree.getSelectionPaths();
389 if ((selectedPaths != null) && (selectedPaths.length == 1)) {
390 Object lastPathComponent = selectedPaths[0].getLastPathComponent();
391 if (!(lastPathComponent instanceof SortableTreeNode)) {
392 logger.log(Level.WARNING, "lastPathComponent is not a SortableTreeNode!");
395 SortableTreeNode node = (SortableTreeNode) lastPathComponent;
396 String completePath = getPathForNode(node);
397 int lastSeparator = completePath.lastIndexOf(File.separatorChar);
398 String filePath = "";
400 if (lastSeparator == -1) {
401 fileName = completePath;
403 filePath = completePath.substring(0, lastSeparator);
404 fileName = completePath.substring(lastSeparator + 1);
406 filePathTextField.setText(filePath);
407 fileNameTextField.setText(fileName);
410 filePathTextField.setText("");
411 fileNameTextField.setText("");
415 // INTERFACE ActionListener
421 public void actionPerformed(ActionEvent actionEvent) {
426 * Tree cell renderer that takes care of certain display properties for
427 * project-specific stuff.
429 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
431 private class FileCellRenderer extends DefaultTreeCellRenderer {
434 * @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree,
435 * java.lang.Object, boolean, boolean, boolean, int, boolean)
438 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
439 Component superCellRenderer = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
440 if (!(superCellRenderer instanceof JLabel)) {
441 logger.log(Level.WARNING, "superCellRenderer is not a JLabel!");
442 return superCellRenderer;
444 if (!(value instanceof SortableTreeNode)) {
445 logger.log(Level.WARNING, "value is not a SortableTreeNode!");
446 return superCellRenderer;
448 SortableTreeNode node = (SortableTreeNode) value;
449 TreeNode[] pathToRoot = fileTreeModel.getPathToRoot(node);
450 if (pathToRoot.length > 1) {
451 String completePath = getPathForNode(node);
452 if (project.getDefaultFile().equals(completePath)) {
453 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.BOLD));
455 if (hiddenFiles.contains(completePath)) {
456 /* fade hidden files’ font */
457 Color foreground = superCellRenderer.getForeground();
458 Color background = selected ? getBackgroundSelectionColor() : getBackgroundNonSelectionColor();
459 Color averageColor = new Color((foreground.getRed() + background.getRed()) / 2, (foreground.getGreen() + background.getGreen()) / 2, (foreground.getBlue() + background.getBlue()) / 2);
460 superCellRenderer.setForeground(averageColor);
462 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.PLAIN));
466 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.BOLD));
468 return superCellRenderer;