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.JCheckBox;
42 import javax.swing.JComboBox;
43 import javax.swing.JDialog;
44 import javax.swing.JLabel;
45 import javax.swing.JOptionPane;
46 import javax.swing.JPanel;
47 import javax.swing.JScrollPane;
48 import javax.swing.JTree;
49 import javax.swing.event.TreeSelectionEvent;
50 import javax.swing.event.TreeSelectionListener;
51 import javax.swing.tree.DefaultTreeCellRenderer;
52 import javax.swing.tree.DefaultTreeModel;
53 import javax.swing.tree.TreeNode;
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.io.MimeTypes;
61 import net.pterodactylus.util.logging.Logging;
62 import net.pterodactylus.util.swing.SortableTreeNode;
63 import net.pterodactylus.util.swing.SwingUtils;
66 * Manages physical and virtual files in a project.
68 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
70 public class FileManager extends JDialog implements I18nable, ActionListener, TreeSelectionListener {
73 private static final Logger logger = Logging.getLogger(FileManager.class.getName());
75 /** The Swing interface. */
76 private final SwingInterface swingInterface;
78 /** The project whose files to manage. */
79 private final Project project;
81 /** The root of the file tree. */
82 private final SortableTreeNode fileTreeRoot;
84 /** The tree model for the project files. */
85 private final DefaultTreeModel fileTreeModel;
87 /** Files that are hidden as per {@link File#isHidden()}. */
88 private final List<String> hiddenFiles = new ArrayList<String>();
90 /** The tree cell renderer. */
91 private final FileCellRenderer fileCellRenderer;
93 /** The “rescan” action. */
94 private I18nAction rescanAction;
96 /** The “close” action. */
97 private I18nAction closeAction;
99 /** The “project files” label. */
100 private I18nLabel projectFilesLabel;
102 /** The tree that shows the files. */
103 private JTree fileTree;
105 /** The scroll pane that holds the file tree. */
106 private JScrollPane fileScrollPane;
108 /** The “insert” action. */
109 private I18nAction insertAction;
111 /** The “insert” checkbox. */
112 private JCheckBox insertCheckBox;
114 /** The “use custom mime type” action. */
115 private I18nAction useCustomMimeTypeAction;
117 /** The “use custom mime type” checkbox. */
118 private JCheckBox useCustomMimeTypeCheckBox;
120 /** The “mime type” combo box. */
121 private JComboBox mimeTypeComboBox;
124 * Creates a new file manager.
126 * @param swingInterface
127 * The Swing interface
129 * The project whose files to manage
131 public FileManager(SwingInterface swingInterface, Project project) {
132 super(swingInterface.getMainWindow(), I18n.get("fileManager.title", project.getName()), true);
133 logger.log(Level.FINEST, "project: " + project);
134 this.swingInterface = swingInterface;
135 this.project = project;
136 fileTreeRoot = new SortableTreeNode(project.getName());
137 fileTreeModel = new DefaultTreeModel(fileTreeRoot);
138 fileCellRenderer = new FileCellRenderer();
142 SwingUtils.center(this);
150 * @see java.awt.Component#setVisible(boolean)
153 public void setVisible(boolean visible) {
157 super.setVisible(visible);
165 * Initializes all actions.
167 private void initActions() {
168 closeAction = new I18nAction("fileManager.button.close") {
173 public void actionPerformed(ActionEvent e) {
177 rescanAction = new I18nAction("fileManager.button.rescan") {
182 @SuppressWarnings("synthetic-access")
183 public void actionPerformed(ActionEvent actionEvent) {
187 insertAction = new I18nAction("fileManager.checkbox.insertFile") {
192 @SuppressWarnings("synthetic-access")
193 public void actionPerformed(ActionEvent actionEvent) {
197 insertAction.setEnabled(false);
198 useCustomMimeTypeAction = new I18nAction("fileManager.checkbox.useCustomMimeType") {
203 @SuppressWarnings("synthetic-access")
204 public void actionPerformed(ActionEvent actionEvent) {
208 useCustomMimeTypeAction.setEnabled(false);
212 * Initializes all components.
214 private void initComponents() {
215 JPanel contentPanel = new JPanel(new BorderLayout(12, 12));
216 contentPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
218 contentPanel.add(createFileManagerPanel(), BorderLayout.CENTER);
219 contentPanel.add(createButtonPanel(), BorderLayout.PAGE_END);
221 setContentPane(contentPanel);
225 * Creates the main panel with the file tree and the file properties.
227 * @return The mail panel
229 private Component createFileManagerPanel() {
230 JPanel fileManagerPanel = new JPanel(new BorderLayout(12, 12));
232 /* file tree panel */
233 JPanel fileTreePanel = new JPanel(new BorderLayout(12, 12));
234 fileManagerPanel.add(fileTreePanel, BorderLayout.LINE_START);
236 fileTree = new JTree(fileTreeModel);
237 fileTree.setShowsRootHandles(false);
238 fileTree.addTreeSelectionListener(this);
239 fileTree.setCellRenderer(fileCellRenderer);
240 fileTreePanel.add(fileScrollPane = new JScrollPane(fileTree), BorderLayout.CENTER);
241 fileScrollPane.setPreferredSize(new Dimension(250, 400));
243 projectFilesLabel = new I18nLabel("fileManager.label.projectFiles", fileTree);
244 JPanel projectFilesLabelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
245 fileTreePanel.add(projectFilesLabelPanel, BorderLayout.NORTH);
246 projectFilesLabelPanel.add(projectFilesLabel);
248 /* the right panel */
249 JPanel rightPanel = new JPanel(new BorderLayout(12, 12));
250 fileManagerPanel.add(rightPanel, BorderLayout.CENTER);
252 /* properties panel */
253 JPanel propertiesPanel = new JPanel(new GridBagLayout());
254 rightPanel.add(propertiesPanel, BorderLayout.CENTER);
255 propertiesPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(12, 12, 12, 12)));
257 insertCheckBox = new JCheckBox(insertAction);
258 propertiesPanel.add(insertCheckBox, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
260 useCustomMimeTypeCheckBox = new JCheckBox(useCustomMimeTypeAction);
261 List<String> allMimeTypes = MimeTypes.getAllMimeTypes();
262 mimeTypeComboBox = new JComboBox(allMimeTypes.toArray(new String[0]));
263 mimeTypeComboBox.setEnabled(false);
264 mimeTypeComboBox.addActionListener(this);
265 propertiesPanel.add(useCustomMimeTypeCheckBox, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 0, 0, 0), 0, 0));
266 propertiesPanel.add(mimeTypeComboBox, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0));
268 propertiesPanel.add(new JPanel(), new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
270 /* action button panel */
271 JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12));
272 rightPanel.add(actionButtonPanel, BorderLayout.PAGE_END);
273 actionButtonPanel.setBorder(BorderFactory.createEtchedBorder());
275 JButton rescanButton = new JButton(rescanAction);
276 actionButtonPanel.add(rescanButton);
278 return fileManagerPanel;
282 * Creates the button panel.
284 * @return The button panel
286 private Component createButtonPanel() {
287 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
289 buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
290 JButton closeButton = new JButton(closeAction);
291 buttonPanel.add(closeButton);
293 getRootPane().setDefaultButton(closeButton);
298 * Initiates a file scan.
300 private void initiateFileScan() {
301 swingInterface.getThreadPool().execute(new Runnable() {
304 * @see java.lang.Runnable#run()
306 @SuppressWarnings("synthetic-access")
308 fileTree.setEnabled(false);
309 rescanAction.setEnabled(false);
310 String basePath = project.getBasePath();
311 File basePathDirectory = new File(basePath);
312 fileTreeRoot.removeAll();
314 if (!basePathDirectory.exists() || !basePathDirectory.isDirectory()) {
316 JOptionPane.showMessageDialog(FileManager.this, I18n.get(""), I18n.get(""), JOptionPane.ERROR_MESSAGE);
318 scanDirectory(fileTreeRoot, basePathDirectory, "", hiddenFiles);
320 fileTreeModel.reload();
321 // fileScrollPane.revalidate();
322 rescanAction.setEnabled(true);
323 fileTree.setEnabled(true);
326 private void scanDirectory(SortableTreeNode rootNode, File directory, String currentDirectory, List<String> hiddenFiles) {
327 for (File file : directory.listFiles()) {
328 String fileName = file.getName();
329 SortableTreeNode fileNode = new SortableTreeNode(fileName);
330 rootNode.add(fileNode);
331 if (file.isFile() && file.isHidden()) {
332 hiddenFiles.add((currentDirectory + File.separator + fileName).substring(1));
334 if (file.isDirectory()) {
335 scanDirectory(fileNode, file, currentDirectory + File.separator + fileName, hiddenFiles);
345 // INTERFACE I18nable
351 public void updateI18n() {
352 setTitle(I18n.get("fileManager.title", project.getName()));
356 // INTERFACE TreeSelectionListener
362 public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
367 // INTERFACE ActionListener
373 public void actionPerformed(ActionEvent actionEvent) {
378 * Tree cell renderer that takes care of certain display properties for
379 * project-specific stuff.
381 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
383 private class FileCellRenderer extends DefaultTreeCellRenderer {
386 * @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree,
387 * java.lang.Object, boolean, boolean, boolean, int, boolean)
390 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
391 Component superCellRenderer = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
392 if (!(superCellRenderer instanceof JLabel)) {
393 logger.log(Level.WARNING, "superCellRenderer is not a JLabel!");
394 return superCellRenderer;
396 if (!(value instanceof SortableTreeNode)) {
397 logger.log(Level.WARNING, "value is not a SortableTreeNode!");
398 return superCellRenderer;
400 SortableTreeNode node = (SortableTreeNode) value;
401 TreeNode[] pathToRoot = fileTreeModel.getPathToRoot(node);
402 if (pathToRoot.length > 1) {
403 StringBuilder completePathBuilder = new StringBuilder();
404 boolean first = true;
405 for (TreeNode rootPathNode : pathToRoot) {
410 if (!(rootPathNode instanceof SortableTreeNode)) {
411 logger.log(Level.WARNING, "rootPathNode is not a SortableTreeNode!");
414 completePathBuilder.append(File.separatorChar).append(((SortableTreeNode) rootPathNode).getUserObject());
416 String completePath = completePathBuilder.substring(1);
417 if (project.getDefaultFile().equals(completePath)) {
418 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.BOLD));
420 if (hiddenFiles.contains(completePath)) {
421 /* fade hidden files’ font */
422 Color foreground = superCellRenderer.getForeground();
423 Color background = selected ? getBackgroundSelectionColor() : getBackgroundNonSelectionColor();
424 Color averageColor = new Color((foreground.getRed() + background.getRed()) / 2, (foreground.getGreen() + background.getGreen()) / 2, (foreground.getBlue() + background.getBlue()) / 2);
425 superCellRenderer.setForeground(averageColor);
427 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.PLAIN));
431 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.BOLD));
433 return superCellRenderer;