complete insert override stuff
[jSite2.git] / src / net / pterodactylus / jsite / gui / FileManager.java
1 /*
2  * jSite2 - FileManager.java -
3  * Copyright © 2008 David Roden
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 package net.pterodactylus.jsite.gui;
21
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;
27 import java.awt.Font;
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;
33 import java.awt.event.MouseEvent;
34 import java.awt.event.MouseListener;
35 import java.beans.PropertyChangeEvent;
36 import java.beans.PropertyChangeListener;
37 import java.io.File;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.logging.Level;
44 import java.util.logging.Logger;
45
46 import javax.swing.AbstractAction;
47 import javax.swing.AbstractButton;
48 import javax.swing.Action;
49 import javax.swing.BorderFactory;
50 import javax.swing.JButton;
51 import javax.swing.JCheckBox;
52 import javax.swing.JComboBox;
53 import javax.swing.JDialog;
54 import javax.swing.JLabel;
55 import javax.swing.JOptionPane;
56 import javax.swing.JPanel;
57 import javax.swing.JPopupMenu;
58 import javax.swing.JScrollPane;
59 import javax.swing.JTextField;
60 import javax.swing.JTree;
61 import javax.swing.event.TreeModelEvent;
62 import javax.swing.event.TreeModelListener;
63 import javax.swing.event.TreeSelectionEvent;
64 import javax.swing.event.TreeSelectionListener;
65 import javax.swing.tree.DefaultTreeCellRenderer;
66 import javax.swing.tree.TreeModel;
67 import javax.swing.tree.TreePath;
68
69 import net.pterodactylus.jsite.i18n.I18n;
70 import net.pterodactylus.jsite.i18n.I18nable;
71 import net.pterodactylus.jsite.i18n.gui.I18nAction;
72 import net.pterodactylus.jsite.i18n.gui.I18nLabel;
73 import net.pterodactylus.jsite.i18n.gui.I18nMenu;
74 import net.pterodactylus.jsite.project.FileOverride;
75 import net.pterodactylus.jsite.project.Project;
76 import net.pterodactylus.jsite.project.ProjectFile;
77 import net.pterodactylus.util.io.MimeTypes;
78 import net.pterodactylus.util.logging.Logging;
79 import net.pterodactylus.util.swing.SwingUtils;
80
81 /**
82  * Manages physical and virtual files in a project.
83  * 
84  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
85  */
86 public class FileManager extends JDialog implements I18nable, ActionListener, TreeSelectionListener, MouseListener {
87
88         /** Logger. */
89         private static final Logger logger = Logging.getLogger(FileManager.class.getName());
90
91         /** The Swing interface. */
92         private final SwingInterface swingInterface;
93
94         /** The project whose files to manage. */
95         private final Project project;
96
97         /** The tree model for the project files. */
98         private final ProjectFileTreeModel fileTreeModel;
99
100         /** The tree cell renderer. */
101         private final FileCellRenderer fileCellRenderer;
102
103         /** The “rescan” action. */
104         private I18nAction rescanAction;
105
106         /** The “close” action. */
107         private I18nAction closeAction;
108
109         /** The “set default file” action. */
110         private I18nAction setDefaultFileAction;
111
112         /** The “project files” label. */
113         private I18nLabel projectFilesLabel;
114
115         /** The tree that shows the files. */
116         private JTree fileTree;
117
118         /** The scroll pane that holds the file tree. */
119         private JScrollPane fileScrollPane;
120
121         /** The “file properties” label. */
122         private I18nLabel filePropertiesLabel;
123
124         /** The “file path” label. */
125         private I18nLabel filePathLabel;
126
127         /** The “file path” textfield. */
128         private JTextField filePathTextField;
129
130         /** The “file name” label. */
131         private I18nLabel fileNameLabel;
132
133         /** The “file name” textfield. */
134         private JTextField fileNameTextField;
135
136         /** The “file size” label. */
137         private I18nLabel fileSizeLabel;
138
139         /** The “file size” text field. */
140         private JTextField fileSizeTextField;
141
142         /** The “override” label. */
143         private I18nLabel fileSettingsLabel;
144
145         /** The “insert default” label. */
146         private I18nLabel insertDefaultLabel;
147
148         /** The “insert default” checkbox. */
149         private JCheckBox insertDefaultCheckBox;
150
151         /** The “override insert default” action. */
152         private I18nAction overrideInsertDefaultAction;
153
154         /** The “override insert default” checkbox. */
155         private JCheckBox overrideInsertDefaultCheckBox;
156
157         /** The “override insert” action. */
158         private Action insertOverrideAction;
159
160         /** The “override insert” checkbox. */
161         private JCheckBox insertOverrideCheckBox;
162
163         /** The context menu for the tree. */
164         private JPopupMenu treeContextMenu;
165
166         /** The “apply override” menu. */
167         private I18nMenu overrideMenu;
168
169         /** The “apply insert override” action. */
170         private I18nAction applyInsertOverrideAction;
171
172         /** The “apply mime type override” action. */
173         private I18nAction applyMimeTypeOverrideAction;
174
175         /** The “remove override” action. */
176         private I18nAction removeOverrideAction;
177
178         /**
179          * Creates a new file manager.
180          * 
181          * @param swingInterface
182          *            The Swing interface
183          * @param project
184          *            The project whose files to manage
185          */
186         public FileManager(SwingInterface swingInterface, Project project) {
187                 super(swingInterface.getMainWindow(), I18n.get("fileManager.title", project.getName()), true);
188                 logger.log(Level.FINEST, "project: " + project);
189                 this.swingInterface = swingInterface;
190                 this.project = project;
191                 fileTreeModel = new ProjectFileTreeModel();
192                 project.addPropertyChangeListener(fileTreeModel);
193                 fileCellRenderer = new FileCellRenderer();
194                 initActions();
195                 initComponents();
196                 pack();
197                 SwingUtils.center(this);
198         }
199
200         //
201         // ACTIONS
202         //
203
204         /**
205          * @see java.awt.Component#setVisible(boolean)
206          */
207         @Override
208         public void setVisible(boolean visible) {
209                 if (visible) {
210                         initiateFileScan();
211                 }
212                 super.setVisible(visible);
213         }
214
215         //
216         // PRIVATE METHODS
217         //
218
219         /**
220          * Initializes all actions.
221          */
222         private void initActions() {
223                 closeAction = new I18nAction("fileManager.button.close") {
224
225                         /**
226                          * {@inheritDoc}
227                          */
228                         @SuppressWarnings("synthetic-access")
229                         public void actionPerformed(ActionEvent e) {
230                                 String defaultFile = project.getDefaultFile();
231                                 if ((defaultFile == null) || (defaultFile.length() == 0)) {
232                                         /* TODO - i18n */
233                                         JOptionPane.showMessageDialog(FileManager.this, I18n.get(""), I18n.get(""), JOptionPane.ERROR_MESSAGE);
234                                         return;
235                                 }
236                                 ProjectFile projectFile = project.getFile(defaultFile);
237                                 if (projectFile == null) {
238                                         JOptionPane.showMessageDialog(FileManager.this, I18n.get(""), I18n.get(""), JOptionPane.ERROR_MESSAGE);
239                                         return;
240                                 }
241                                 setVisible(false);
242                         }
243                 };
244                 rescanAction = new I18nAction("fileManager.button.rescan") {
245
246                         /**
247                          * {@inheritDoc}
248                          */
249                         @SuppressWarnings("synthetic-access")
250                         public void actionPerformed(ActionEvent actionEvent) {
251                                 initiateFileScan();
252                         }
253                 };
254                 setDefaultFileAction = new I18nAction("fileManager.menu.item.setDefaultFile") {
255
256                         /**
257                          * {@inheritDoc}
258                          */
259                         @SuppressWarnings("synthetic-access")
260                         public void actionPerformed(ActionEvent actionEvent) {
261                                 TreePath selectedPath = fileTree.getSelectionPath();
262                                 if (selectedPath == null) {
263                                         logger.log(Level.WARNING, "nothing selected!");
264                                         return;
265                                 }
266                                 ProjectFileWrapper projectFileWrapper = (ProjectFileWrapper) selectedPath.getLastPathComponent();
267                                 if (isHidden(projectFileWrapper)) {
268                                         /* TODO - i18n */
269                                         JOptionPane.showMessageDialog(FileManager.this, I18n.get(""), I18n.get(""), JOptionPane.ERROR_MESSAGE);
270                                         return;
271                                 }
272                                 if (projectFileWrapper.getProjectFile().isDirectory()) {
273                                         /* TODO - i18n */
274                                         JOptionPane.showMessageDialog(FileManager.this, I18n.get(""), I18n.get(""), JOptionPane.ERROR_MESSAGE);
275                                         return;
276                                 }
277                                 String completePath = projectFileWrapper.getProjectFile().getCompletePath();
278                                 project.setDefaultFile(completePath);
279                         }
280                 };
281                 applyInsertOverrideAction = new I18nAction("fileManager.menu.item.applyInsertOverride") {
282
283                         /**
284                          * {@inheritDoc}
285                          */
286                         @SuppressWarnings("synthetic-access")
287                         public void actionPerformed(ActionEvent actionEvent) {
288                                 applyInsertOverride();
289                         }
290                 };
291                 applyMimeTypeOverrideAction = new I18nAction("fileManager.menu.item.applyMimeTypeOverride") {
292
293                         /**
294                          * {@inheritDoc}
295                          */
296                         @SuppressWarnings("synthetic-access")
297                         public void actionPerformed(ActionEvent actionEvent) {
298                                 applyMimeTypeOverride();
299                         }
300                 };
301                 removeOverrideAction = new I18nAction("fileManager.menu.item.removeOverride") {
302
303                         /**
304                          * {@inheritDoc}
305                          */
306                         @SuppressWarnings("synthetic-access")
307                         public void actionPerformed(ActionEvent actionEvent) {
308                                 removeOverride();
309                         }
310                 };
311                 overrideInsertDefaultAction = new I18nAction("fileManager.checkbox.overrideInsertDefault") {
312
313                         /**
314                          * {@inheritDoc}
315                          */
316                         @SuppressWarnings("synthetic-access")
317                         public void actionPerformed(ActionEvent actionEvent) {
318                                 boolean overrideInsert = overrideInsertDefaultCheckBox.isSelected();
319                                 insertOverrideAction.setEnabled(overrideInsert);
320                                 List<ProjectFileWrapper> selectedProjectFileWrappers = getSelectedProjectFileWrappers(true);
321                                 ProjectFileWrapper projectFileWrapper = selectedProjectFileWrappers.get(0);
322                                 ProjectFile projectFile = projectFileWrapper.getProjectFile();
323                                 FileOverride fileOverride = project.getFileOverride(projectFile);
324                                 if (overrideInsert) {
325                                         if (fileOverride == null) {
326                                                 fileOverride = new FileOverride();
327                                                 project.addFileOverride(projectFile, fileOverride);
328                                                 fileOverride.setInsert(!projectFile.isHidden());
329                                         }
330                                 } else {
331                                         fileOverride.setInsert(null);
332                                         if (fileOverride.isEmpty()) {
333                                                 project.removeFileOverride(projectFile);
334                                         }
335                                 }
336                         }
337                 };
338                 overrideInsertDefaultAction.setEnabled(false);
339                 insertOverrideAction = new AbstractAction() {
340
341                         /**
342                          * {@inheritDoc}
343                          */
344                         public void actionPerformed(ActionEvent actionEvent) {
345                                 boolean insertOverride = insertOverrideCheckBox.isSelected();
346                                 List<ProjectFileWrapper> selectedProjectFileWrappers = getSelectedProjectFileWrappers(true);
347                                 ProjectFileWrapper projectFileWrapper = selectedProjectFileWrappers.get(0);
348                                 ProjectFile projectFile = projectFileWrapper.getProjectFile();
349                                 FileOverride fileOverride = project.getFileOverride(projectFile);
350                                 fileOverride.setInsert(insertOverride);
351                                 fileTree.setShowsRootHandles(false);
352                                 fileTree.repaint();
353                         }
354                 };
355                 insertOverrideAction.setEnabled(false);
356         }
357
358         /**
359          * Initializes all components.
360          */
361         private void initComponents() {
362                 treeContextMenu = new JPopupMenu();
363                 treeContextMenu.add(setDefaultFileAction);
364
365                 overrideMenu = new I18nMenu("fileManager.menu.override");
366                 treeContextMenu.add(overrideMenu);
367
368                 overrideMenu.add(applyInsertOverrideAction);
369                 overrideMenu.add(applyMimeTypeOverrideAction);
370                 overrideMenu.addSeparator();
371                 overrideMenu.add(removeOverrideAction);
372
373                 JPanel contentPanel = new JPanel(new BorderLayout(12, 12));
374                 contentPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
375
376                 contentPanel.add(createFileManagerPanel(), BorderLayout.CENTER);
377                 contentPanel.add(createButtonPanel(), BorderLayout.PAGE_END);
378
379                 setContentPane(contentPanel);
380         }
381
382         /**
383          * Creates the main panel with the file tree and the file properties.
384          * 
385          * @return The mail panel
386          */
387         private Component createFileManagerPanel() {
388                 JPanel fileManagerPanel = new JPanel(new BorderLayout(12, 12));
389
390                 /* file tree panel */
391                 JPanel fileTreePanel = new JPanel(new BorderLayout(12, 12));
392                 fileManagerPanel.add(fileTreePanel, BorderLayout.LINE_START);
393
394                 fileTree = new JTree(fileTreeModel);
395                 fileTree.setShowsRootHandles(false);
396                 fileTree.addTreeSelectionListener(this);
397                 fileTree.addMouseListener(this);
398                 fileTree.setCellRenderer(fileCellRenderer);
399                 fileTreePanel.add(fileScrollPane = new JScrollPane(fileTree), BorderLayout.CENTER);
400                 fileScrollPane.setPreferredSize(new Dimension(200, 350));
401
402                 projectFilesLabel = new I18nLabel("fileManager.label.projectFiles", fileTree);
403                 JPanel projectFilesLabelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
404                 fileTreePanel.add(projectFilesLabelPanel, BorderLayout.NORTH);
405                 projectFilesLabelPanel.add(projectFilesLabel);
406
407                 /* the right panel */
408                 JPanel rightPanel = new JPanel(new BorderLayout(12, 12));
409                 fileManagerPanel.add(rightPanel, BorderLayout.CENTER);
410
411                 /* properties panel */
412                 JPanel propertiesPanel = new JPanel(new GridBagLayout());
413                 rightPanel.add(propertiesPanel, BorderLayout.CENTER);
414                 propertiesPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(12, 12, 12, 12)));
415                 propertiesPanel.setPreferredSize(new Dimension(400, 350));
416
417                 filePropertiesLabel = new I18nLabel("fileManager.label.fileProperties");
418                 filePropertiesLabel.setFont(filePropertiesLabel.getFont().deriveFont(Font.BOLD));
419                 propertiesPanel.add(filePropertiesLabel, new GridBagConstraints(0, 0, 4, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
420
421                 filePathLabel = new I18nLabel("fileManager.label.filePath");
422                 filePathTextField = new JTextField();
423                 filePathTextField.setEditable(false);
424                 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));
425                 propertiesPanel.add(filePathTextField, new GridBagConstraints(1, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
426
427                 fileNameLabel = new I18nLabel("fileManager.label.fileName");
428                 fileNameTextField = new JTextField();
429                 fileNameTextField.setEditable(false);
430                 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));
431                 propertiesPanel.add(fileNameTextField, new GridBagConstraints(1, 2, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
432
433                 fileSizeLabel = new I18nLabel("fileManager.label.fileSize");
434                 fileSizeTextField = new JTextField();
435                 fileSizeTextField.setEditable(false);
436                 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));
437                 propertiesPanel.add(fileSizeTextField, new GridBagConstraints(1, 3, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
438
439                 /* file settings. */
440                 fileSettingsLabel = new I18nLabel("fileManager.label.fileSettings");
441                 fileSettingsLabel.setFont(fileSettingsLabel.getFont().deriveFont(Font.BOLD));
442                 propertiesPanel.add(fileSettingsLabel, new GridBagConstraints(0, 4, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(24, 0, 0, 0), 0, 0));
443
444                 insertDefaultLabel = new I18nLabel("fileManager.label.insertDefault");
445                 propertiesPanel.add(insertDefaultLabel, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 24, 0, 0), 0, 0));
446                 insertDefaultCheckBox = new JCheckBox();
447                 insertDefaultCheckBox.setEnabled(false);
448                 propertiesPanel.add(insertDefaultCheckBox, new GridBagConstraints(1, 5, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
449                 overrideInsertDefaultCheckBox = new JCheckBox(overrideInsertDefaultAction);
450                 propertiesPanel.add(overrideInsertDefaultCheckBox, new GridBagConstraints(2, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
451                 insertOverrideCheckBox = new JCheckBox(insertOverrideAction);
452                 propertiesPanel.add(insertOverrideCheckBox, new GridBagConstraints(3, 5, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 12, 0, 0), 0, 0));
453
454                 /* glue panel. */
455                 propertiesPanel.add(new JPanel(), new GridBagConstraints(0, 6, 4, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
456
457                 /* action button panel */
458                 JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12));
459                 rightPanel.add(actionButtonPanel, BorderLayout.PAGE_END);
460                 actionButtonPanel.setBorder(BorderFactory.createEtchedBorder());
461
462                 JButton rescanButton = new JButton(rescanAction);
463                 actionButtonPanel.add(rescanButton);
464
465                 return fileManagerPanel;
466         }
467
468         /**
469          * Creates the button panel.
470          * 
471          * @return The button panel
472          */
473         private Component createButtonPanel() {
474                 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
475
476                 buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
477                 JButton closeButton = new JButton(closeAction);
478                 buttonPanel.add(closeButton);
479
480                 getRootPane().setDefaultButton(closeButton);
481                 return buttonPanel;
482         }
483
484         /**
485          * Initiates a file scan.
486          */
487         private void initiateFileScan() {
488                 swingInterface.getThreadPool().execute(new Runnable() {
489
490                         /**
491                          * @see java.lang.Runnable#run()
492                          */
493                         @SuppressWarnings("synthetic-access")
494                         public void run() {
495                                 fileTree.setEnabled(false);
496                                 rescanAction.setEnabled(false);
497                                 ProjectFile baseProjectFile = project.getBaseFile();
498                                 if (baseProjectFile != null) {
499                                         fileTreeModel.setBaseProjectFile(baseProjectFile);
500                                 }
501                                 // fileScrollPane.revalidate();
502                                 rescanAction.setEnabled(true);
503                                 fileTree.setEnabled(true);
504                         }
505
506                 });
507         }
508
509         /**
510          * Checks whether the given mouse event is a popup trigger and occured over
511          * a file. If so, the context menu is shown.
512          * 
513          * @param mouseEvent
514          *            The mouse event to check
515          */
516         private void maybeShowContextMenu(MouseEvent mouseEvent) {
517                 if (!mouseEvent.isPopupTrigger()) {
518                         return;
519                 }
520                 List<ProjectFileWrapper> selectedProjectFileWrappers = getSelectedProjectFileWrappers(false);
521                 TreePath clickedPath = fileTree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
522                 ProjectFileWrapper clickedProjectFileWrapper = (ProjectFileWrapper) clickedPath.getLastPathComponent();
523                 if (!selectedProjectFileWrappers.contains(clickedProjectFileWrapper)) {
524                         fileTree.setSelectionPath(clickedPath);
525                         selectedProjectFileWrappers = getSelectedProjectFileWrappers(false);
526                 }
527                 logger.log(Level.FINEST, "selectedProjectFileWrappers.size(): " + selectedProjectFileWrappers.size());
528                 if (selectedProjectFileWrappers.size() == 1) {
529                         ProjectFileWrapper projectFileWrapper = selectedProjectFileWrappers.get(0);
530                         ProjectFile projectFile = projectFileWrapper.getProjectFile();
531                         setDefaultFileAction.setEnabled(!isHidden(projectFileWrapper) && projectFile.isFile() && !projectFile.getCompletePath().equals(project.getDefaultFile()));
532                 } else {
533                         setDefaultFileAction.setEnabled(false);
534                 }
535                 overrideMenu.setVisible(selectedProjectFileWrappers.size() > 1);
536                 treeContextMenu.show(fileTree, mouseEvent.getX(), mouseEvent.getY());
537         }
538
539         /**
540          * Finds whether the {@link ProjectFile} given by
541          * <code>projectFileWrapper</code> is hidden.
542          * 
543          * @param projectFileWrapper
544          *            The wrapped project file
545          * @return <code>true</code> if the file is hidden and should not be
546          *         inserted, <code>false</code> otherwise
547          */
548         private boolean isHidden(ProjectFileWrapper projectFileWrapper) {
549                 ProjectFile projectFile = projectFileWrapper.getProjectFile();
550                 FileOverride fileOverride = project.getFileOverrides().get(projectFile.getCompletePath());
551                 logger.log(Level.FINEST, "fileOverride: " + fileOverride);
552                 return ((fileOverride == null) && projectFile.isHidden()) || ((fileOverride != null) && (fileOverride.isInsert() == null) && projectFile.isHidden()) || ((fileOverride != null) && (fileOverride.isInsert() != null) && (Boolean.FALSE.equals(fileOverride.isInsert())));
553         }
554
555         /**
556          * Opens the “apply insert override” dialog and lets the user apply an
557          * override for the “insert” setting for multiple files.
558          */
559         private void applyInsertOverride() {
560                 JCheckBox insertCheckBox = new JCheckBox(I18n.get("fileManager.menu.item.insert.name"));
561                 String okString = I18n.get("general.button.okay.name");
562                 String cancelString = I18n.get("general.button.cancel.name");
563                 int choice = JOptionPane.showOptionDialog(this, new Object[] { I18n.get("fileManager.dialog.insertOverride.message"), insertCheckBox }, I18n.get("fileManager.dialog.insertOverride.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] { okString, cancelString }, okString);
564                 logger.log(Level.FINEST, "choice: " + choice);
565                 if ((choice == JOptionPane.CLOSED_OPTION) || (choice == 1)) {
566                         return;
567                 }
568                 logger.log(Level.INFO, "selected insert override: " + insertCheckBox.isSelected());
569                 List<ProjectFileWrapper> selectedProjectFileWrappers = getSelectedProjectFileWrappers(true);
570                 for (ProjectFileWrapper selectedProjectFileWrapper : selectedProjectFileWrappers) {
571                         ProjectFile projectFile = selectedProjectFileWrapper.getProjectFile();
572                         FileOverride fileOverride = project.getFileOverride(projectFile);
573                         if (fileOverride == null) {
574                                 fileOverride = new FileOverride();
575                                 project.addFileOverride(projectFile, fileOverride);
576                         }
577                         fileOverride.setInsert(insertCheckBox.isSelected());
578                 }
579                 /* HACK - Swing sucks a bit */
580                 fileTree.setShowsRootHandles(false);
581         }
582
583         /**
584          * Opens the “apply mime type override” dialog and lets the user apply an
585          * override for the “mime type” setting for multiple files.
586          */
587         private void applyMimeTypeOverride() {
588                 List<String> allMimeTypes = MimeTypes.getAllMimeTypes();
589                 allMimeTypes.add(0, null);
590                 JComboBox mimeTypeComboBox = new JComboBox(allMimeTypes.toArray());
591                 String okString = I18n.get("general.button.okay.name");
592                 String cancelString = I18n.get("general.button.cancel.name");
593                 int choice = JOptionPane.showOptionDialog(this, new Object[] { I18n.get("fileManager.dialog.mimeTypeOverride.message"), mimeTypeComboBox }, I18n.get("fileManager.dialog.mimeTypeOverride.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] { okString, cancelString }, okString);
594                 if ((choice == JOptionPane.CLOSED_OPTION) || (choice == 1)) {
595                         return;
596                 }
597                 logger.log(Level.FINE, "selected mime type: " + mimeTypeComboBox.getSelectedItem());
598                 List<ProjectFileWrapper> selectedProjectFileWrappers = getSelectedProjectFileWrappers(true);
599                 for (ProjectFileWrapper selectedProjectFileWrapper : selectedProjectFileWrappers) {
600                         ProjectFile projectFile = selectedProjectFileWrapper.getProjectFile();
601                         FileOverride fileOverride = project.getFileOverride(projectFile);
602                         if (fileOverride == null) {
603                                 fileOverride = new FileOverride();
604                                 project.addFileOverride(projectFile, fileOverride);
605                         }
606                         fileOverride.setContentType((String) mimeTypeComboBox.getSelectedItem());
607                 }
608         }
609
610         /**
611          * Removes the overrides of all selected files.
612          */
613         private void removeOverride() {
614                 String continueString = I18n.get("general.button.continue.name");
615                 String cancelString = I18n.get("general.button.cancel.name");
616                 List<ProjectFileWrapper> selectedProjectFileWrappers = getSelectedProjectFileWrappers(true);
617                 int choice = JOptionPane.showOptionDialog(this, I18n.get("fileManager.dialog.removeOverride.message", selectedProjectFileWrappers.size()), I18n.get("fileManager.dialog.removeOverride.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, new Object[] { continueString, cancelString }, continueString);
618                 if ((choice == JOptionPane.CLOSED_OPTION) || (choice == 1)) {
619                         return;
620                 }
621                 for (ProjectFileWrapper selectedProjectFileWrapper : selectedProjectFileWrappers) {
622                         project.removeFileOverride(selectedProjectFileWrapper.getProjectFile());
623                 }
624         }
625
626         /**
627          * Returns all currently selected {@link ProjectFileWrapper}s.
628          * 
629          * @param filesOnly
630          *            <code>true</code> to return only selected files,
631          *            <code>false</code> to include directories
632          * @return All selected project file wrappers
633          */
634         private List<ProjectFileWrapper> getSelectedProjectFileWrappers(boolean filesOnly) {
635                 List<ProjectFileWrapper> selectedProjectFileWrappers = new ArrayList<ProjectFileWrapper>();
636                 TreePath[] selectedPaths = fileTree.getSelectionPaths();
637                 if (selectedPaths != null) {
638                         for (TreePath selectedPath : selectedPaths) {
639                                 ProjectFileWrapper projectFileWrapper = (ProjectFileWrapper) selectedPath.getLastPathComponent();
640                                 if (filesOnly && !projectFileWrapper.getProjectFile().isFile()) {
641                                         continue;
642                                 }
643                                 selectedProjectFileWrappers.add(projectFileWrapper);
644                         }
645                 }
646                 return selectedProjectFileWrappers;
647         }
648
649         //
650         // INTERFACE I18nable
651         //
652
653         /**
654          * {@inheritDoc}
655          */
656         public void updateI18n() {
657                 setTitle(I18n.get("fileManager.title", project.getName()));
658                 projectFilesLabel.updateI18n();
659                 filePropertiesLabel.updateI18n();
660                 filePathLabel.updateI18n();
661         }
662
663         //
664         // INTERFACE TreeSelectionListener
665         //
666
667         /**
668          * {@inheritDoc}
669          */
670         public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
671                 List<ProjectFileWrapper> selectedProjectFileWrappers = getSelectedProjectFileWrappers(true);
672                 String filePathText = "";
673                 String fileNameText = "";
674                 String fileSizeText = "";
675                 boolean insertDefaultSelected = false;
676                 boolean overrideInsertEnabled = false;
677                 boolean overrideInsertSelected = false;
678                 boolean insertOverrideEnabled = false;
679                 boolean insertOverrideSelected = false;
680                 if (selectedProjectFileWrappers.size() == 1) {
681                         ProjectFileWrapper projectFileWrapper = selectedProjectFileWrappers.get(0);
682                         ProjectFile projectFile = projectFileWrapper.getProjectFile();
683                         if (projectFile.isFile()) {
684                                 String completePath = projectFile.getCompletePath();
685                                 int lastSeparator = completePath.lastIndexOf(File.separatorChar);
686                                 if (lastSeparator != -1) {
687                                         filePathText = completePath.substring(0, lastSeparator);
688                                 }
689                                 fileNameText = projectFile.getName();
690                                 fileSizeText = String.valueOf(projectFile.getSize());
691                                 insertDefaultSelected = !projectFile.isHidden();
692                                 overrideInsertEnabled = true;
693                                 FileOverride fileOverride = project.getFileOverride(projectFile);
694                                 if (fileOverride != null) {
695                                         Boolean overrideInsert = fileOverride.isInsert();
696                                         overrideInsertSelected = overrideInsert != null;
697                                         insertOverrideEnabled = overrideInsertSelected;
698                                         insertOverrideSelected = overrideInsertSelected ? overrideInsert : !projectFile.isHidden();
699                                 }
700                         }
701                 } else if (selectedProjectFileWrappers.size() > 1) {
702                         /* TODO */
703                 }
704                 filePathTextField.setText(filePathText);
705                 fileNameTextField.setText(fileNameText);
706                 fileSizeTextField.setText(fileSizeText);
707                 setSelected(insertDefaultCheckBox, insertDefaultSelected);
708                 setEnabled(overrideInsertDefaultAction, overrideInsertEnabled);
709                 setSelected(overrideInsertDefaultCheckBox, overrideInsertSelected);
710                 setEnabled(insertOverrideAction, insertOverrideEnabled);
711                 setSelected(insertOverrideCheckBox, insertOverrideSelected);
712         }
713
714         /**
715          * Sets the given action’s enabled state to the given enabled state if the
716          * action’s current enabled state is not the given enabled state.
717          * 
718          * @param action
719          *            The action to set the enabled state on
720          * @param enabled
721          *            The new enabled state of the action
722          */
723         private void setEnabled(Action action, boolean enabled) {
724                 if (action.isEnabled() != enabled) {
725                         action.setEnabled(enabled);
726                 }
727         }
728
729         /**
730          * Sets the given button’s selected state to the given selected state if the
731          * button’s current selected state is not the given selected state.
732          * 
733          * @param button
734          *            The button to set the selected state on
735          * @param selected
736          *            The new selected state of the button
737          */
738         private void setSelected(AbstractButton button, boolean selected) {
739                 if (button.isSelected() != selected) {
740                         button.setSelected(selected);
741                 }
742         }
743
744         //
745         // INTERFACE ActionListener
746         //
747
748         /**
749          * {@inheritDoc}
750          */
751         public void actionPerformed(ActionEvent actionEvent) {
752                 /* TODO */
753         }
754
755         //
756         // INTERFACE MouseListener
757         //
758
759         /**
760          * {@inheritDoc}
761          */
762         public void mouseClicked(MouseEvent mouseEvent) {
763                 maybeShowContextMenu(mouseEvent);
764         }
765
766         /**
767          * {@inheritDoc}
768          */
769         public void mouseEntered(MouseEvent mouseEvent) {
770                 /* ignore. */
771         }
772
773         /**
774          * {@inheritDoc}
775          */
776         public void mouseExited(MouseEvent mouseEvent) {
777                 /* ignore. */
778         }
779
780         /**
781          * {@inheritDoc}
782          */
783         public void mousePressed(MouseEvent mouseEvent) {
784                 maybeShowContextMenu(mouseEvent);
785         }
786
787         /**
788          * {@inheritDoc}
789          */
790         public void mouseReleased(MouseEvent mouseEvent) {
791                 maybeShowContextMenu(mouseEvent);
792         }
793
794         /**
795          * Tree cell renderer that takes care of certain display properties for
796          * project-specific stuff.
797          * 
798          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
799          */
800         private class FileCellRenderer extends DefaultTreeCellRenderer {
801
802                 /**
803                  * Empty constructor.
804                  */
805                 FileCellRenderer() {
806                         /* do nothing. */
807                 }
808
809                 /**
810                  * @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree,
811                  *      java.lang.Object, boolean, boolean, boolean, int, boolean)
812                  */
813                 @SuppressWarnings("synthetic-access")
814                 @Override
815                 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
816                         Component superCellRenderer = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
817                         if (!(superCellRenderer instanceof JLabel)) {
818                                 logger.log(Level.SEVERE, "superCellRenderer is not a JLabel!");
819                                 return superCellRenderer;
820                         }
821                         if (!(value instanceof ProjectFileWrapper)) {
822                                 logger.log(Level.SEVERE, "value is not a ProjectFileWrapper!");
823                                 return superCellRenderer;
824                         }
825                         ProjectFileWrapper projectFileWrapper = (ProjectFileWrapper) value;
826                         ProjectFile projectFile = projectFileWrapper.getProjectFile();
827                         FileOverride fileOverride = project.getFileOverride(projectFile);
828                         String completePath = projectFile.getCompletePath();
829                         boolean paintBold = false;
830                         boolean paintHalfColor = false;
831                         boolean showFile = !projectFile.isHidden();
832                         if (fileOverride != null) {
833                                 Boolean insertOverride = fileOverride.isInsert();
834                                 if (insertOverride != null) {
835                                         showFile = insertOverride;
836                                 }
837                         }
838                         if (projectFile.isFile() && !showFile) {
839                                 paintHalfColor = true;
840                         } else if (completePath.equals(project.getDefaultFile())) {
841                                 paintBold = true;
842                         } else if (projectFile.getParents().size() == 1) {
843                                 paintBold = true;
844                         }
845                         if (paintHalfColor) {
846                                 /* TODO - cache colors */
847                                 Color foreground = superCellRenderer.getForeground();
848                                 Color background = selected ? getBackgroundSelectionColor() : getBackgroundNonSelectionColor();
849                                 Color averageColor = new Color((foreground.getRed() + background.getRed()) / 2, (foreground.getGreen() + background.getGreen()) / 2, (foreground.getBlue() + background.getBlue()) / 2);
850                                 superCellRenderer.setForeground(averageColor);
851                         } else {
852                                 superCellRenderer.setForeground(selected ? getTextSelectionColor() : getTextNonSelectionColor());
853                         }
854                         if (paintBold) {
855                                 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.BOLD));
856                         } else {
857                                 superCellRenderer.setFont(superCellRenderer.getFont().deriveFont(Font.PLAIN));
858                         }
859                         return superCellRenderer;
860                 }
861
862         }
863
864         /**
865          * TreeModel that is based on {@link Project#getBaseFile()}.
866          * 
867          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
868          */
869         private class ProjectFileTreeModel implements TreeModel, PropertyChangeListener {
870
871                 /** Tree model listeners. */
872                 private final List<TreeModelListener> treeModelListeners = Collections.synchronizedList(new ArrayList<TreeModelListener>());
873
874                 /** The base project file. */
875                 private ProjectFile baseProjectFile;
876
877                 /** Maps path names to project files. */
878                 private final Map<String, ProjectFile> pathProjectFiles = Collections.synchronizedMap(new HashMap<String, ProjectFile>());
879
880                 /** Maps project files to wrappers. */
881                 private final Map<ProjectFile, ProjectFileWrapper> projectFileWrappers = Collections.synchronizedMap(new HashMap<ProjectFile, ProjectFileWrapper>());
882
883                 /**
884                  * Empty constructor.
885                  */
886                 ProjectFileTreeModel() {
887                         /* do nothing. */
888                 }
889
890                 //
891                 // EVENT MANAGEMENT
892                 //
893
894                 /**
895                  * {@inheritDoc}
896                  */
897                 public void addTreeModelListener(TreeModelListener treeModelListener) {
898                         treeModelListeners.add(treeModelListener);
899                 }
900
901                 /**
902                  * {@inheritDoc}
903                  */
904                 public void removeTreeModelListener(TreeModelListener treeModelListener) {
905                         treeModelListeners.remove(treeModelListener);
906                 }
907
908                 /**
909                  * Notifies all listeners that a node has changed.
910                  * 
911                  * @param changedProjectFileWrapper
912                  *            The wrapper around the changed project file
913                  */
914                 protected void fireTreeNodesChanged(ProjectFileWrapper changedProjectFileWrapper) {
915                         ProjectFile changedProjectFile = changedProjectFileWrapper.getProjectFile();
916                         ProjectFile changedProjectFileParent = changedProjectFile.getParent();
917                         ProjectFile currentProjectFileParent = changedProjectFile;
918                         List<ProjectFileWrapper> parentProjectFileWrappers = new ArrayList<ProjectFileWrapper>();
919                         do {
920                                 parentProjectFileWrappers.add(0, projectFileWrappers.get(currentProjectFileParent));
921                                 currentProjectFileParent = currentProjectFileParent.getParent();
922                         } while (currentProjectFileParent != null);
923                         TreeModelEvent treeModelEvent = new TreeModelEvent(this, parentProjectFileWrappers.toArray(), new int[] { getIndexOfChild(projectFileWrappers.get(changedProjectFileParent), changedProjectFileWrapper) }, new Object[] { changedProjectFileWrapper });
924                         for (TreeModelListener treeModelListener : treeModelListeners) {
925                                 treeModelListener.treeNodesChanged(treeModelEvent);
926                         }
927                 }
928
929                 /**
930                  * Notifies all listeners that the tree structure has changed
931                  * significantly.
932                  * 
933                  * @see TreeModelListener#treeStructureChanged(TreeModelEvent)
934                  * @param newRootNode
935                  */
936                 protected void fireTreeStructureChanged(ProjectFileWrapper newRootNode) {
937                         for (TreeModelListener treeModelListener : treeModelListeners) {
938                                 treeModelListener.treeStructureChanged(new TreeModelEvent(this, new Object[] { newRootNode }));
939                         }
940                 }
941
942                 //
943                 // ACCESSORS
944                 //
945
946                 /**
947                  * Sets the new base project file. This causes the model to reload.
948                  * 
949                  * @param baseProjectFile
950                  *            The new base project file
951                  */
952                 @SuppressWarnings("synthetic-access")
953                 public synchronized void setBaseProjectFile(ProjectFile baseProjectFile) {
954                         this.baseProjectFile = baseProjectFile;
955                         projectFileWrappers.clear();
956                         pathProjectFiles.clear();
957                         createWrappers(baseProjectFile);
958                         projectFileWrappers.get(baseProjectFile).setNameOverride(project.getName());
959                         fireTreeStructureChanged(projectFileWrappers.get(baseProjectFile));
960                 }
961
962                 //
963                 // PRIVATE METHODS
964                 //
965
966                 /**
967                  * Creates {@link ProjectFileWrapper}s for all files below the given
968                  * project file.
969                  * 
970                  * @param projectFile
971                  *            The base project file for all project files to create
972                  *            wrappers for
973                  */
974                 private void createWrappers(ProjectFile projectFile) {
975                         projectFileWrappers.put(projectFile, new ProjectFileWrapper(projectFile));
976                         pathProjectFiles.put(projectFile.getCompletePath(), projectFile);
977                         for (ProjectFile projectFileChild : projectFile.getFiles()) {
978                                 if (projectFileChild.isDirectory()) {
979                                         createWrappers(projectFileChild);
980                                 }
981                                 projectFileWrappers.put(projectFileChild, new ProjectFileWrapper(projectFileChild));
982                                 pathProjectFiles.put(projectFileChild.getCompletePath(), projectFileChild);
983                         }
984                 }
985
986                 //
987                 // INTERFACE TreeModel
988                 //
989
990                 /**
991                  * {@inheritDoc}
992                  */
993                 public Object getRoot() {
994                         return projectFileWrappers.get(baseProjectFile);
995                 }
996
997                 /**
998                  * {@inheritDoc}
999                  */
1000                 @SuppressWarnings("synthetic-access")
1001                 public Object getChild(Object parent, int index) {
1002                         if (!(parent instanceof ProjectFileWrapper)) {
1003                                 logger.log(Level.SEVERE, "parent is not a ProjectFileWrapper!");
1004                                 return null;
1005                         }
1006                         ProjectFileWrapper projectFileWrapper = (ProjectFileWrapper) parent;
1007                         ProjectFile projectFile = projectFileWrapper.getProjectFile();
1008                         return projectFileWrappers.get(projectFile.getFiles().get(index));
1009                 }
1010
1011                 /**
1012                  * {@inheritDoc}
1013                  */
1014                 @SuppressWarnings("synthetic-access")
1015                 public int getChildCount(Object parent) {
1016                         if (!(parent instanceof ProjectFileWrapper)) {
1017                                 logger.log(Level.SEVERE, "parent is not a ProjectFileWrapper!");
1018                                 return -1;
1019                         }
1020                         ProjectFileWrapper projectFileWrapper = (ProjectFileWrapper) parent;
1021                         ProjectFile projectFile = projectFileWrapper.getProjectFile();
1022                         return projectFile.getFiles().size();
1023                 }
1024
1025                 /**
1026                  * {@inheritDoc}
1027                  */
1028                 @SuppressWarnings("synthetic-access")
1029                 public int getIndexOfChild(Object parent, Object child) {
1030                         if (!(parent instanceof ProjectFileWrapper)) {
1031                                 logger.log(Level.SEVERE, "parent is not a ProjectFileWrapper!");
1032                                 return -1;
1033                         }
1034                         if (!(child instanceof ProjectFileWrapper)) {
1035                                 logger.log(Level.SEVERE, "child is not a ProjectFileWrapper!");
1036                                 return -1;
1037                         }
1038                         ProjectFileWrapper projectFileWrapper = (ProjectFileWrapper) parent;
1039                         ProjectFile projectFile = projectFileWrapper.getProjectFile();
1040                         return projectFile.getFiles().indexOf(((ProjectFileWrapper) child).getProjectFile());
1041                 }
1042
1043                 /**
1044                  * {@inheritDoc}
1045                  */
1046                 @SuppressWarnings("synthetic-access")
1047                 public boolean isLeaf(Object node) {
1048                         if (!(node instanceof ProjectFileWrapper)) {
1049                                 logger.log(Level.SEVERE, "node is not a ProjectFileWrapper!");
1050                                 return true;
1051                         }
1052                         ProjectFileWrapper projectFileWrapper = (ProjectFileWrapper) node;
1053                         return projectFileWrapper.getProjectFile().isFile();
1054                 }
1055
1056                 /**
1057                  * {@inheritDoc}
1058                  */
1059                 public void valueForPathChanged(TreePath path, Object newValue) {
1060                         /* ignore, items will not be modified in tree. */
1061                 }
1062
1063                 //
1064                 // INTERFACE PropertyChangeListener
1065                 //
1066
1067                 /**
1068                  * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
1069                  */
1070                 @SuppressWarnings("synthetic-access")
1071                 public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
1072                         if (Project.PROPERTY_DEFAULT_FILE.equals(propertyChangeEvent.getPropertyName())) {
1073                                 if (propertyChangeEvent.getOldValue() != null) {
1074                                         String oldCompletePath = (String) propertyChangeEvent.getOldValue();
1075                                         logger.log(Level.FINEST, "oldCompletePath: " + oldCompletePath);
1076                                         ProjectFile oldProjectFile = pathProjectFiles.get(oldCompletePath);
1077                                         logger.log(Level.FINEST, "oldProjectFile: " + oldProjectFile);
1078                                         ProjectFileWrapper oldProjectFileWrapper = projectFileWrappers.get(oldProjectFile);
1079                                         logger.log(Level.FINEST, "oldProjectFileWrapper: " + oldProjectFileWrapper);
1080                                         fireTreeNodesChanged(oldProjectFileWrapper);
1081                                 }
1082                                 String newCompletePath = (String) propertyChangeEvent.getNewValue();
1083                                 ProjectFile newProjectFile = pathProjectFiles.get(newCompletePath);
1084                                 ProjectFileWrapper newProjectFileWrapper = projectFileWrappers.get(newProjectFile);
1085                                 System.out.println("newProjectFileWrapper: " + newProjectFileWrapper);
1086                                 fireTreeNodesChanged(newProjectFileWrapper);
1087                                 /* HACK - swing sucks a bit. */
1088                                 fileTree.setShowsRootHandles(false);
1089                         }
1090                 }
1091
1092         }
1093
1094         /**
1095          * Wrapper around a {@link ProjectFile} that overwrites
1096          * {@link Object#toString()} to return the project file’s name.
1097          * 
1098          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
1099          */
1100         private static class ProjectFileWrapper {
1101
1102                 /** The wrapped project file. */
1103                 private final ProjectFile projectFile;
1104
1105                 /** The override name. */
1106                 private String nameOverride;
1107
1108                 /**
1109                  * Creates a new wrapper around a project file.
1110                  * 
1111                  * @param projectFile
1112                  *            The project file to wrap
1113                  */
1114                 public ProjectFileWrapper(ProjectFile projectFile) {
1115                         this.projectFile = projectFile;
1116                 }
1117
1118                 /**
1119                  * Returns the wrapped project file.
1120                  * 
1121                  * @return The wrapped project file
1122                  */
1123                 public ProjectFile getProjectFile() {
1124                         return projectFile;
1125                 }
1126
1127                 /**
1128                  * Sets the name override. If the name override is not <code>null</code>
1129                  * it will be shown insted of the project file’s name.
1130                  * 
1131                  * @param nameOverride
1132                  *            The name override
1133                  */
1134                 void setNameOverride(String nameOverride) {
1135                         this.nameOverride = nameOverride;
1136                 }
1137
1138                 /**
1139                  * {@inheritDoc}
1140                  */
1141                 @Override
1142                 public String toString() {
1143                         return (nameOverride != null) ? nameOverride : projectFile.getName();
1144                 }
1145
1146         }
1147
1148 }