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