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