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