use icons
[jSite2.git] / src / net / pterodactylus / jsite / gui / SwingInterface.java
1 /*
2  * jSite2 - SwingInterface.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.event.ActionEvent;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33 import java.util.Properties;
34 import java.util.concurrent.Executor;
35 import java.util.concurrent.Executors;
36 import java.util.logging.Level;
37 import java.util.logging.LogRecord;
38 import java.util.logging.Logger;
39
40 import javax.swing.AbstractAction;
41 import javax.swing.Action;
42 import javax.swing.JOptionPane;
43 import javax.swing.UIManager;
44 import javax.swing.UnsupportedLookAndFeelException;
45
46 import net.pterodactylus.jsite.core.Core;
47 import net.pterodactylus.jsite.core.CoreListener;
48 import net.pterodactylus.jsite.core.Node;
49 import net.pterodactylus.jsite.core.Project;
50 import net.pterodactylus.jsite.i18n.I18n;
51 import net.pterodactylus.jsite.i18n.gui.I18nAction;
52 import net.pterodactylus.util.image.IconLoader;
53 import net.pterodactylus.util.io.Closer;
54 import net.pterodactylus.util.logging.Logging;
55 import net.pterodactylus.util.logging.LoggingListener;
56
57 /**
58  * The Swing user interface.
59  * 
60  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
61  * @version $Id$
62  */
63 public class SwingInterface implements CoreListener, LoggingListener {
64
65         /** The logger. */
66         private static final Logger logger = Logging.getLogger(SwingInterface.class.getName());
67
68         /** The application core. */
69         private final Core core;
70
71         /** The configuration directory. */
72         private final String configDirectory;
73
74         /** The main window. */
75         private MainWindow mainWindow;
76
77         /** Thread pool. */
78         private Executor threadPool = Executors.newCachedThreadPool();
79
80         /** The logger window. */
81         private LogWindow logWindow;
82
83         /** The “configure” action. */
84         private I18nAction configureAction;
85
86         /** The “import config” action. */
87         private I18nAction importConfigAction;
88
89         /** The “quit” action. */
90         private I18nAction quitAction;
91
92         /** The “manage nodes” action. */
93         private I18nAction manageNodesAction;
94
95         /** The “connect to node” (simple mode) action. */
96         private I18nAction nodeConnectAction;
97
98         /** The “disconnect from node” (simple mode) action. */
99         private I18nAction nodeDisconnectAction;
100
101         /** All node menu items. */
102         private List<Action> nodeConnectActions = Collections.synchronizedList(new ArrayList<Action>());
103
104         /** Mapping from nodes to node connect actions. */
105         private Map<Node, Action> nodeNodeConnectActions = Collections.synchronizedMap(new HashMap<Node, Action>());
106
107         /** Mapping from node connect actions to nodes. */
108         private Map<Action, Node> nodeConnectActionNodes = Collections.synchronizedMap(new HashMap<Action, Node>());
109
110         /** All node disconnect actions. */
111         private List<Action> nodeDisconnectActions = Collections.synchronizedList(new ArrayList<Action>());
112
113         /** Mapping from nodes to node disconnect actions. */
114         private Map<Node, Action> nodeNodeDisconnectActions = Collections.synchronizedMap(new HashMap<Node, Action>());
115
116         /** Mapping from node disconnect actions to nodes. */
117         private Map<Action, Node> nodeDisconnectActionNodes = Collections.synchronizedMap(new HashMap<Action, Node>());
118
119         /** The node manager dialog. */
120         private ManageNodesDialog manageNodesDialog;
121
122         /** All lanugage menu items. */
123         private List<I18nAction> languageActions = new ArrayList<I18nAction>();
124
125         /** The “about” action. */
126         private I18nAction helpAboutAction;
127
128         /** The “add project” action. */
129         private I18nAction addProjectAction;
130
131         /** The “clone project” action. */
132         private I18nAction cloneProjectAction;
133
134         /** The “delete project” action. */
135         private I18nAction deleteProjectAction;
136
137         /** The “about” dialog. */
138         private AboutDialog aboutDialog;
139
140         /** The configuration dialog. */
141         private ConfigurationDialog configurationDialog;
142
143         /** The list of all defined nodes. */
144         private List<Node> nodeList;
145
146         //
147         // CONFIGURATION
148         //
149
150         /** The advanced mode. */
151         private boolean advancedMode;
152
153         /** Whether to antialias the GUI. */
154         private boolean antialias;
155
156         /** The control font. */
157         private String controlFont;
158
159         /** The user font. */
160         private String userFont;
161
162         /** The class name of the look and feel. */
163         private String lookAndFeel;
164
165         /**
166          * Creates a new swing interface.
167          * 
168          * @param core
169          *            The core to operate on
170          * @param configDirectory
171          *            The directory the configuration is stored in
172          */
173         public SwingInterface(Core core, String configDirectory) {
174                 this.core = core;
175                 this.configDirectory = configDirectory;
176                 I18n.setLocale(Locale.ENGLISH);
177                 loadConfig();
178                 if (lookAndFeel != null) {
179                         try {
180                                 UIManager.setLookAndFeel(lookAndFeel);
181                         } catch (ClassNotFoundException cnfe1) {
182                                 logger.log(Level.WARNING, "could not load look and feel", cnfe1);
183                         } catch (InstantiationException ie1) {
184                                 logger.log(Level.WARNING, "could not load look and feel", ie1);
185                         } catch (IllegalAccessException iae1) {
186                                 logger.log(Level.WARNING, "could not load look and feel", iae1);
187                         } catch (UnsupportedLookAndFeelException ulafe1) {
188                                 logger.log(Level.WARNING, "could not load look and feel", ulafe1);
189                         }
190                 }
191                 if (antialias) {
192                         System.setProperty("swing.aatext", "true");
193                 }
194                 if (controlFont != null) {
195                         System.setProperty("swing.plaf.metal.controlFont", controlFont);
196                 }
197                 if (userFont != null) {
198                         System.setProperty("swing.plaf.metal.userFont", userFont);
199                 }
200                 initActions();
201                 initDialogs();
202                 mainWindow = new MainWindow(this);
203                 mainWindow.setAdvancedMode(advancedMode);
204                 logWindow = new LogWindow();
205         }
206
207         //
208         // ACCESSORS
209         //
210
211         /**
212          * Returns the core that is controlled by the Swing interface.
213          * 
214          * @return The core
215          */
216         Core getCore() {
217                 return core;
218         }
219
220         /**
221          * Returns the main window of the Swing interface.
222          * 
223          * @return The main window
224          */
225         MainWindow getMainWindow() {
226                 return mainWindow;
227         }
228
229         /**
230          * Returns whether the advanced mode is activated.
231          * 
232          * @return <code>true</code> if the advanced mode is activated,
233          *         <code>false</code> if the simple mode is activated
234          */
235         boolean isAdvancedMode() {
236                 return advancedMode;
237         }
238
239         /**
240          * Returns the “configure” action.
241          * 
242          * @return The “configure” action
243          */
244         I18nAction getConfigureAction() {
245                 return configureAction;
246         }
247
248         /**
249          * Returns the “import config” action.
250          * 
251          * @return The “import config” action
252          */
253         I18nAction getImportConfigAction() {
254                 return importConfigAction;
255         }
256
257         /**
258          * Returns the “quit” action.
259          * 
260          * @return The “quit” action
261          */
262         I18nAction getQuitAction() {
263                 return quitAction;
264         }
265
266         /**
267          * Returns the “manage nodes” action.
268          * 
269          * @return The “manage nodes” action
270          */
271         I18nAction getManageNodesAction() {
272                 return manageNodesAction;
273         }
274
275         /**
276          * Returns the “connect to node” action.
277          * 
278          * @return The “connect to node” action
279          */
280         I18nAction getNodeConnectAction() {
281                 return nodeConnectAction;
282         }
283
284         /**
285          * Returns all “connect node” actions.
286          * 
287          * @return All “connect node” actions
288          */
289         List<Action> getNodeConnectActions() {
290                 return nodeConnectActions;
291         }
292
293         /**
294          * Returns the “disconnect from node” action.
295          * 
296          * @return The “disconnect from node” action
297          */
298         I18nAction getNodeDisconnectAction() {
299                 return nodeDisconnectAction;
300         }
301
302         /**
303          * Returns all “disconnect node” actions.
304          * 
305          * @return All “disconnect node” action
306          */
307         List<Action> getNodeDisconnectActions() {
308                 return nodeDisconnectActions;
309         }
310
311         /**
312          * Returns all language actions.
313          * 
314          * @return All language actions
315          */
316         List<I18nAction> getLanguageActions() {
317                 return languageActions;
318         }
319
320         /**
321          * Returns the “about” action.
322          * 
323          * @return The “about” action
324          */
325         I18nAction getHelpAboutAction() {
326                 return helpAboutAction;
327         }
328
329         /**
330          * Returns the “add project” action.
331          * 
332          * @return The “add project” action
333          */
334         I18nAction getAddProjectAction() {
335                 return addProjectAction;
336         }
337
338         /**
339          * Returns the “clone project” action.
340          * 
341          * @return The “clone project” action
342          */
343         I18nAction getCloneProjectAction() {
344                 return cloneProjectAction;
345         }
346
347         /**
348          * Returns the “delete project” action.
349          * 
350          * @return The “delete project” action
351          */
352         I18nAction getDeleteProjectAction() {
353                 return deleteProjectAction;
354         }
355
356         //
357         // ACTIONS
358         //
359
360         //
361         // SERVICE METHODS
362         //
363
364         //
365         // PRIVATE METHODS
366         //
367
368         /**
369          * Loads the configuration of the interface.
370          */
371         private void loadConfig() {
372                 /* initialize default stuff. */
373                 antialias = false;
374                 /* now read config. */
375                 File configFile = new File(configDirectory, "swing-interface.properties");
376                 if (!configFile.exists() || !configFile.canRead() || !configFile.isFile()) {
377                         System.err.println("could not find “" + configFile.getAbsolutePath() + "”!");
378                         return;
379                 }
380                 Properties configProperties = new Properties();
381                 FileInputStream configInputStream = null;
382                 try {
383                         configInputStream = new FileInputStream(configFile);
384                         configProperties.load(configInputStream);
385                 } catch (IOException ioe1) {
386                         System.err.println("could not load config, " + ioe1.getMessage());
387                 } finally {
388                         Closer.close(configInputStream);
389                 }
390                 if (configProperties.containsKey("advancedMode")) {
391                         advancedMode = Boolean.valueOf(configProperties.getProperty("advancedMode"));
392                 }
393                 if (configProperties.containsKey("antialias")) {
394                         antialias = Boolean.valueOf(configProperties.getProperty("antialias"));
395                 }
396                 if (configProperties.containsKey("controlFont")) {
397                         controlFont = configProperties.getProperty("controlFont");
398                 }
399                 if (configProperties.containsKey("userFont")) {
400                         userFont = configProperties.getProperty("userFont");
401                 }
402                 if (configProperties.containsKey("lookAndFeel")) {
403                         lookAndFeel = configProperties.getProperty("lookAndFeel");
404                 }
405                 if (configProperties.containsKey("language")) {
406                         I18n.setLocale(new Locale(configProperties.getProperty("language")));
407                 }
408         }
409
410         /**
411          * Saves the configuration.
412          */
413         private void saveConfig() {
414                 File configDirectory = new File(this.configDirectory);
415                 if (!configDirectory.exists()) {
416                         if (!configDirectory.mkdirs()) {
417                                 System.err.println("could not create “" + this.configDirectory + "”!");
418                                 return;
419                         }
420                 }
421                 if (!configDirectory.exists() || !configDirectory.isDirectory() || !configDirectory.canWrite()) {
422                         System.err.println("can not access “" + this.configDirectory + "”!");
423                         return;
424                 }
425                 File configFile = new File(configDirectory, "swing-interface.properties");
426                 Properties configProperties = new Properties();
427                 configProperties.setProperty("advancedMode", String.valueOf(advancedMode));
428                 configProperties.setProperty("antialias", String.valueOf(antialias));
429                 if (controlFont != null) {
430                         configProperties.setProperty("controlFont", controlFont);
431                 }
432                 if (userFont != null) {
433                         configProperties.setProperty("userFont", userFont);
434                 }
435                 if (lookAndFeel != null) {
436                         configProperties.setProperty("lookAndFeel", lookAndFeel);
437                 }
438                 configProperties.setProperty("language", I18n.getLocale().getLanguage());
439                 FileOutputStream configOutputStream = null;
440                 try {
441                         configOutputStream = new FileOutputStream(configFile);
442                         configProperties.store(configOutputStream, "configuration of swing interface");
443                 } catch (IOException ioe1) {
444                         System.err.println("could not save config, " + ioe1.getMessage());
445                 } finally {
446                         Closer.close(configOutputStream);
447                 }
448         }
449
450         /**
451          * Initializes all actions.
452          */
453         private void initActions() {
454                 configureAction = new I18nAction("mainWindow.menu.jSite.configure", IconLoader.loadIcon("/preferences-system.png")) {
455
456                         /**
457                          * {@inheritDoc}
458                          */
459                         @SuppressWarnings("synthetic-access")
460                         public void actionPerformed(ActionEvent actionEvent) {
461                                 configure();
462                         }
463                 };
464                 importConfigAction = new I18nAction("mainWindow.menu.jSite.importConfig") {
465
466                         /**
467                          * {@inheritDoc}
468                          */
469                         @SuppressWarnings("synthetic-access")
470                         public void actionPerformed(ActionEvent actionEvent) {
471                                 importConfig();
472                         }
473                 };
474                 quitAction = new I18nAction("mainWindow.menu.jSite.quit", IconLoader.loadIcon("/system-log-out.png")) {
475
476                         /**
477                          * {@inheritDoc}
478                          */
479                         @SuppressWarnings("synthetic-access")
480                         public void actionPerformed(ActionEvent actionEvent) {
481                                 quit();
482                         }
483                 };
484                 manageNodesAction = new I18nAction("mainWindow.menu.node.item.manageNodes") {
485
486                         /**
487                          * {@inheritDoc}
488                          */
489                         @SuppressWarnings("synthetic-access")
490                         public void actionPerformed(ActionEvent actionEvent) {
491                                 manageNodes();
492                         }
493                 };
494                 nodeConnectAction = new I18nAction("mainWindow.menu.node.item.connect", false) {
495
496                         @SuppressWarnings("synthetic-access")
497                         public void actionPerformed(ActionEvent actionEvent) {
498                                 List<Node> nodes = core.getNodes();
499                                 if (nodes.isEmpty()) {
500                                         return;
501                                 }
502                                 nodeConnect(nodes.get(0));
503                         }
504
505                 };
506                 nodeDisconnectAction = new I18nAction("mainWindow.menu.node.item.disconnect", false) {
507
508                         /**
509                          * {@inheritDoc}
510                          */
511                         @SuppressWarnings("synthetic-access")
512                         public void actionPerformed(ActionEvent e) {
513                                 List<Node> nodes = core.getNodes();
514                                 if (nodes.isEmpty()) {
515                                         return;
516                                 }
517                                 nodeDisconnect(nodes.get(0));
518                         }
519                 };
520                 rebuildNodeActions(core.getNodes());
521                 List<Locale> availableLanguages = I18n.findAvailableLanguages();
522                 for (final Locale locale: availableLanguages) {
523                         I18nAction languageAction = new I18nAction("general.language." + locale.getLanguage()) {
524
525                                 @SuppressWarnings("synthetic-access")
526                                 public void actionPerformed(ActionEvent e) {
527                                         changeLanguage(locale, this);
528                                 }
529
530                         };
531                         if (I18n.getLocale().getLanguage().equals(locale.getLanguage())) {
532                                 languageAction.setEnabled(false);
533                         }
534                         languageActions.add(languageAction);
535                 }
536                 helpAboutAction = new I18nAction("mainWindow.menu.help.item.about") {
537
538                         /**
539                          * {@inheritDoc}
540                          */
541                         @SuppressWarnings("synthetic-access")
542                         public void actionPerformed(ActionEvent actionEvent) {
543                                 helpAbout();
544                         }
545                 };
546                 addProjectAction = new I18nAction("mainWindow.button.addProject") {
547
548                         /**
549                          * {@inheritDoc}
550                          */
551                         @SuppressWarnings("synthetic-access")
552                         public void actionPerformed(ActionEvent actionEvent) {
553                                 addProject();
554                         }
555                 };
556                 cloneProjectAction = new I18nAction("mainWindow.button.cloneProject") {
557
558                         /**
559                          * {@inheritDoc}
560                          */
561                         @SuppressWarnings("synthetic-access")
562                         public void actionPerformed(ActionEvent actionEvent) {
563                                 cloneProject();
564                         }
565                 };
566                 deleteProjectAction = new I18nAction("mainWindow.button.deleteProject") {
567
568                         /**
569                          * {@inheritDoc}
570                          */
571                         @SuppressWarnings("synthetic-access")
572                         public void actionPerformed(ActionEvent actionEvent) {
573                                 deleteProject();
574                         }
575                 };
576         }
577
578         /**
579          * Initializes all child dialogs.
580          */
581         private void initDialogs() {
582                 manageNodesDialog = new ManageNodesDialog(this);
583                 aboutDialog = new AboutDialog(this);
584                 configurationDialog = new ConfigurationDialog(this);
585         }
586
587         //
588         // PRIVATE ACTIONS
589         //
590
591         /**
592          * Shows the configuration dialog.
593          */
594         private void configure() {
595                 configurationDialog.setAdvancedMode(advancedMode);
596                 configurationDialog.setAntialias(antialias);
597                 configurationDialog.setControlFont(controlFont);
598                 configurationDialog.setUserFont(userFont);
599                 configurationDialog.setLookAndFeel(lookAndFeel);
600                 configurationDialog.setVisible(true);
601                 if (!configurationDialog.wasCancelled()) {
602                         advancedMode = configurationDialog.isAdvancedMode();
603                         if (!advancedMode && (nodeList.size() > 1)) {
604                                 JOptionPane.showMessageDialog(mainWindow, I18n.get("mainWindow.warning.multipleNodesNotAdvancedMode.message"), I18n.get("mainWindow.warning.multipleNodesNotAdvancedMode.title"), JOptionPane.WARNING_MESSAGE);
605                         }
606                         mainWindow.setAdvancedMode(advancedMode);
607                         antialias = configurationDialog.isAntialias();
608                         controlFont = configurationDialog.getControlFont();
609                         userFont = configurationDialog.getUserFont();
610                         lookAndFeel = configurationDialog.getLookAndFeel();
611                         saveConfig();
612                 }
613         }
614
615         /**
616          * Imports old jSite configuration.
617          */
618         private void importConfig() {
619                 /* TODO */
620         }
621
622         /**
623          * Quits jSite.
624          */
625         private void quit() {
626                 saveConfig();
627                 System.exit(0);
628         }
629
630         /**
631          * Rebuilds all node connect and disconnect actions.
632          * 
633          * @param nodes
634          *            The list of nodes
635          */
636         private void rebuildNodeActions(List<Node> nodes) {
637                 nodeConnectActions.clear();
638                 nodeNodeConnectActions.clear();
639                 nodeConnectActionNodes.clear();
640                 nodeDisconnectActions.clear();
641                 nodeNodeDisconnectActions.clear();
642                 nodeDisconnectActionNodes.clear();
643                 for (Node node: nodes) {
644                         Action nodeConnectAction = new AbstractAction(node.getName()) {
645
646                                 /**
647                                  * {@inheritDoc}
648                                  */
649                                 @SuppressWarnings("synthetic-access")
650                                 public void actionPerformed(ActionEvent e) {
651                                         Node node = nodeConnectActionNodes.get(this);
652                                         nodeConnect(node);
653                                 }
654                         };
655                         nodeConnectActions.add(nodeConnectAction);
656                         nodeConnectActionNodes.put(nodeConnectAction, node);
657                         nodeNodeConnectActions.put(node, nodeConnectAction);
658                         Action nodeDisconnectAction = new AbstractAction(node.getName()) {
659
660                                 /**
661                                  * {@inheritDoc}
662                                  */
663                                 @SuppressWarnings("synthetic-access")
664                                 public void actionPerformed(ActionEvent e) {
665                                         Node node = nodeDisconnectActionNodes.get(this);
666                                         nodeDisconnect(node);
667                                 }
668                         };
669 // nodeDisconnectActions.add(nodeDisconnectAction);
670                         nodeDisconnectActionNodes.put(nodeDisconnectAction, node);
671                         nodeNodeDisconnectActions.put(node, nodeDisconnectAction);
672                 }
673         }
674
675         /**
676          * Pops up the “manage nodes” dialog.
677          */
678         private void manageNodes() {
679                 if (advancedMode) {
680                         manageNodesDialog.setNodeList(nodeList);
681                         manageNodesDialog.setVisible(true);
682                         nodeList = manageNodesDialog.getNodeList();
683                         rebuildNodeActions(nodeList);
684                         mainWindow.refreshNodeMenuItems();
685                 } else {
686                         if (nodeList.isEmpty()) {
687                                 Node newNode = new Node();
688                                 newNode.setName(I18n.get("general.defaultNode.name"));
689                                 newNode.setHostname("localhost");
690                                 newNode.setPort(9481);
691                                 nodeList.add(newNode);
692                         }
693                         Node firstNode = nodeList.get(0);
694                         EditNodeDialog editNodeDialog = manageNodesDialog.getEditNodeDialog();
695                         editNodeDialog.setNodeName(firstNode.getName());
696                         editNodeDialog.setNodeHostname(firstNode.getHostname());
697                         editNodeDialog.setNodePort(firstNode.getPort());
698                         editNodeDialog.setVisible(true);
699                         if (!editNodeDialog.wasCancelled()) {
700                                 firstNode.setName(editNodeDialog.getNodeName());
701                                 firstNode.setHostname(editNodeDialog.getNodeHostname());
702                                 firstNode.setPort(editNodeDialog.getNodePort());
703                                 /* TODO - give to core. */
704                         }
705                 }
706         }
707
708         /**
709          * Connects to the node.
710          * 
711          * @param node
712          *            The node to connect to
713          */
714         private void nodeConnect(final Node node) {
715                 threadPool.execute(new Runnable() {
716
717                         /**
718                          * {@inheritDoc}
719                          */
720                         @SuppressWarnings("synthetic-access")
721                         public void run() {
722                                 logger.log(Level.INFO, "connecting to node “" + node.getName() + "”…");
723                                 core.connectToNode(node);
724                         }
725                 });
726         }
727
728         /**
729          * Disconnects from the node.
730          * 
731          * @param node
732          *            The node to disconnect from
733          */
734         private void nodeDisconnect(Node node) {
735                 logger.log(Level.INFO, "disconnecting from node “" + node.getName() + "”…");
736                 core.disconnectFromNode(node);
737         }
738
739         /**
740          * Changes the language of the interface. This method also disables the
741          * action for the newly set language and enables all others.
742          * 
743          * @param newLocale
744          *            The new language
745          * @param languageAction
746          *            The action that triggered the change
747          */
748         private void changeLanguage(Locale newLocale, I18nAction languageAction) {
749                 for (I18nAction i18nAction: languageActions) {
750                         i18nAction.setEnabled(i18nAction != languageAction);
751                 }
752                 I18n.setLocale(newLocale);
753         }
754
755         /**
756          * Shows the “about” dialog.
757          */
758         private void helpAbout() {
759                 aboutDialog.setVisible(true);
760         }
761
762         /**
763          * Adds a project.
764          */
765         private void addProject() {
766                 Project project = new Project();
767                 project.setName("New Project");
768                 project.setDescription("");
769         }
770
771         /**
772          * Clones a project.
773          */
774         private void cloneProject() {
775                 /* TODO */
776         }
777
778         /**
779          * Deletes a project.
780          */
781         private void deleteProject() {
782                 /* TODO */
783         }
784
785         //
786         // INTERFACE CoreListener
787         //
788
789         /**
790          * {@inheritDoc}
791          */
792         public void loadingProjectsDone(String directory) {
793                 mainWindow.setStatusBarText(I18n.get("mainWindow.statusBar.projectLoadingDone"));
794         }
795
796         /**
797          * {@inheritDoc}
798          */
799         public void loadingProjectsFailed(String directory, Throwable throwable) {
800                 JOptionPane.showMessageDialog(mainWindow, I18n.get("mainWindow.error.projectLoadingFailed.message", directory), I18n.get("mainWindow.error.projectLoadingFailed.title"), JOptionPane.ERROR_MESSAGE);
801         }
802
803         /**
804          * {@inheritDoc}
805          */
806         public void savingProjectsDone(String directory) {
807                 mainWindow.setStatusBarText(I18n.get("mainWindow.statusBar.projectSavingDone"));
808         }
809
810         /**
811          * {@inheritDoc}
812          */
813         public void savingProjectsFailed(String directory, Throwable throwabled) {
814                 /* TODO */
815         }
816
817         /**
818          * {@inheritDoc}
819          */
820         public void loadingNodesDone(String directory) {
821                 mainWindow.setStatusBarText(I18n.get("mainWindow.statusBar.loadingNodesDone"));
822         }
823
824         /**
825          * {@inheritDoc}
826          */
827         public void loadingNodesFailed(String directory, Throwable throwable) {
828                 /* TODO */
829         }
830
831         /**
832          * {@inheritDoc}
833          */
834         public void savingNodesDone(String directory) {
835                 mainWindow.setStatusBarText(I18n.get("mainWindow.statusBar.savingNodesDone"));
836         }
837
838         /**
839          * {@inheritDoc}
840          */
841         public void savingNodesFailed(String directory, Throwable throwable) {
842                 /* TODO */
843         }
844
845         /**
846          * {@inheritDoc}
847          */
848         public void coreLoaded() {
849                 this.nodeList = core.getNodes();
850                 manageNodesDialog.setNodeList(nodeList);
851                 mainWindow.setVisible(true);
852                 mainWindow.setStatusBarText(I18n.get("mainWindow.statusBar.coreLoaded"));
853         }
854
855         /**
856          * {@inheritDoc}
857          */
858         public void coreStopped() {
859                 mainWindow.setStatusBarText(I18n.get("mainWindow.statusBar.coreStopped"));
860         }
861
862         /**
863          * {@inheritDoc}
864          */
865         public void nodeConnecting(Node node) {
866                 Action nodeConnectAction = nodeNodeConnectActions.get(node);
867                 nodeConnectActions.remove(nodeConnectAction);
868                 mainWindow.setStatusBarText(I18n.get("mainWindow.statusBar.connectingToNode", node.getName(), node.getHostname(), node.getPort()));
869                 mainWindow.refreshNodeMenuItems();
870         }
871
872         /**
873          * {@inheritDoc}
874          */
875         public void nodeConnected(Node node) {
876                 Action nodeDisconnectAction = nodeNodeDisconnectActions.get(node);
877                 nodeDisconnectActions.add(nodeDisconnectAction);
878                 mainWindow.refreshNodeMenuItems();
879         }
880
881         /**
882          * {@inheritDoc}
883          */
884         public void nodeDisconnected(Node node, Throwable throwable) {
885                 Action nodeConnectAction = nodeNodeConnectActions.get(node);
886                 nodeConnectActions.add(nodeConnectAction);
887                 Action nodeDisconnectAction = nodeNodeDisconnectActions.get(node);
888                 nodeDisconnectActions.remove(nodeDisconnectAction);
889                 mainWindow.refreshNodeMenuItems();
890         }
891
892         //
893         // INTERFACE LoggingListener
894         //
895
896         /**
897          * {@inheritDoc}
898          */
899         public void logged(LogRecord logRecord) {
900                 logWindow.logged(logRecord);
901         }
902
903 }