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