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