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