add virtual entries
[jSite2.git] / src / net / pterodactylus / jsite / gui / ManageNodesDialog.java
1 /*
2  * jSite2 - ManageNodeDialog.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.BorderLayout;
23 import java.awt.FlowLayout;
24 import java.awt.event.ActionEvent;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.logging.Logger;
31
32 import javax.swing.AbstractListModel;
33 import javax.swing.BorderFactory;
34 import javax.swing.JButton;
35 import javax.swing.JDialog;
36 import javax.swing.JList;
37 import javax.swing.JOptionPane;
38 import javax.swing.JPanel;
39 import javax.swing.JScrollPane;
40 import javax.swing.border.EtchedBorder;
41 import javax.swing.event.ListSelectionEvent;
42 import javax.swing.event.ListSelectionListener;
43
44 import net.pterodactylus.jsite.core.Core;
45 import net.pterodactylus.jsite.core.Node;
46 import net.pterodactylus.jsite.i18n.I18n;
47 import net.pterodactylus.jsite.i18n.I18nable;
48 import net.pterodactylus.jsite.i18n.gui.I18nAction;
49 import net.pterodactylus.jsite.i18n.gui.I18nLabel;
50 import net.pterodactylus.jsite.main.Version;
51 import net.pterodactylus.util.logging.Logging;
52 import net.pterodactylus.util.swing.SwingUtils;
53
54 /**
55  * Dialog that lets the user manage her nodes.
56  * 
57  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
58  * @version $Id$
59  */
60 public class ManageNodesDialog extends JDialog implements ListSelectionListener, I18nable {
61
62         /** The logger.  */
63         @SuppressWarnings("unused")
64         private static final Logger logger = Logging.getLogger(ManageNodesDialog.class.getName());
65
66         /** The core. */
67         private final Core core;
68
69         /** The original list of nodes. */
70         private List<Node> originalNodeList;
71
72         /** The “add node” action. */
73         private I18nAction addNodeAction;
74
75         /** The “edit node” action. */
76         private I18nAction editNodeAction;
77
78         /** The “delete node” action. */
79         private I18nAction deleteNodeAction;
80
81         /** The “okay” action. */
82         private I18nAction okayAction;
83
84         /** The “cancel” action. */
85         private I18nAction cancelAction;
86
87         /** The “edit node” dialog. */
88         private EditNodeDialog editNodeDialog;
89
90         /** The warning label. */
91         private I18nLabel immediatelyEffectiveLabel;
92
93         /** The node list. */
94         private JList nodeList;
95
96         /** The mode for the node list. */
97         private NodeListModel nodeListModel = new NodeListModel();
98
99         /**
100          * Creates a new node manager dialog.
101          * 
102          * @param swingInterface
103          *            The Swing interface
104          */
105         public ManageNodesDialog(SwingInterface swingInterface) {
106                 super(swingInterface.getMainWindow(), I18n.get("manageNodesDialog.title") + " – jSite " + Version.getVersion(), true);
107                 this.core = swingInterface.getCore();
108                 initActions();
109                 initComponents();
110                 initDialogs();
111                 pack();
112                 I18n.registerI18nable(this);
113                 SwingUtils.center(this);
114         }
115
116         //
117         // ACCESSORS
118         //
119
120         /**
121          * Expose the edit node dialog for the simple mode.
122          * 
123          * @return The edit node dialog
124          */
125         EditNodeDialog getEditNodeDialog() {
126                 return editNodeDialog;
127         }
128
129         /**
130          * Returns the list of nodes.
131          * 
132          * @return The list of nodes
133          */
134         public List<Node> getNodeList() {
135                 return originalNodeList;
136         }
137
138         /**
139          * Sets the list of nodes.
140          * 
141          * @param nodeList
142          *            The list of nodes
143          */
144         public void setNodeList(List<Node> nodeList) {
145                 originalNodeList = new ArrayList<Node>(nodeList);
146                 nodeListModel.clear();
147                 for (Node node: nodeList) {
148                         nodeListModel.addNode(node);
149                 }
150         }
151
152         //
153         // PRIVATE METHODS
154         //
155
156         /**
157          * Initializes all actions.
158          */
159         private void initActions() {
160                 okayAction = new I18nAction("general.button.okay") {
161
162                         /**
163                          * {@inheritDoc}
164                          */
165                         @SuppressWarnings("synthetic-access")
166                         public void actionPerformed(ActionEvent e) {
167                                 confirm();
168                         }
169                 };
170                 cancelAction = new I18nAction("general.button.cancel") {
171
172                         /**
173                          * {@inheritDoc}
174                          */
175                         @SuppressWarnings("synthetic-access")
176                         public void actionPerformed(ActionEvent e) {
177                                 cancel();
178                         }
179                 };
180                 addNodeAction = new I18nAction("manageNodesDialog.button.addNode") {
181
182                         /**
183                          * {@inheritDoc}
184                          */
185                         @SuppressWarnings("synthetic-access")
186                         public void actionPerformed(ActionEvent e) {
187                                 addNode();
188                         }
189                 };
190                 editNodeAction = new I18nAction("manageNodesDialog.button.editNode", false) {
191
192                         /**
193                          * {@inheritDoc}
194                          */
195                         @SuppressWarnings("synthetic-access")
196                         public void actionPerformed(ActionEvent e) {
197                                 editNode();
198                         }
199                 };
200                 deleteNodeAction = new I18nAction("manageNodesDialog.button.deleteNode", false) {
201
202                         /**
203                          * {@inheritDoc}
204                          */
205                         @SuppressWarnings("synthetic-access")
206                         public void actionPerformed(ActionEvent e) {
207                                 deleteNodes();
208                         }
209                 };
210         }
211
212         /**
213          * Initializes all components.
214          */
215         private void initComponents() {
216                 JPanel rootPanel = new JPanel(new BorderLayout(12, 12));
217                 rootPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
218
219                 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
220                 rootPanel.add(buttonPanel, BorderLayout.PAGE_END);
221                 buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
222
223                 buttonPanel.add(new JButton(cancelAction));
224                 JButton okayButton = new JButton(okayAction);
225                 getRootPane().setDefaultButton(okayButton);
226                 buttonPanel.add(okayButton);
227
228                 JPanel contentPanel = new JPanel(new BorderLayout(12, 12));
229                 rootPanel.add(contentPanel, BorderLayout.CENTER);
230                 contentPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(12, 12, 12, 12)));
231
232                 JPanel warningPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 12, 12));
233                 warningPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
234                 warningPanel.add(immediatelyEffectiveLabel = new I18nLabel("manageNodesDialog.label.immediatelyEffective"));
235                 contentPanel.add(warningPanel, BorderLayout.PAGE_START);
236
237                 JPanel listButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 12, 12));
238                 contentPanel.add(listButtonPanel, BorderLayout.PAGE_END);
239                 listButtonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
240                 listButtonPanel.add(new JButton(addNodeAction));
241                 listButtonPanel.add(new JButton(editNodeAction));
242                 listButtonPanel.add(new JButton(deleteNodeAction));
243
244                 nodeList = new JList(nodeListModel);
245                 nodeList.addListSelectionListener(this);
246                 contentPanel.add(new JScrollPane(nodeList), BorderLayout.CENTER);
247
248                 setContentPane(rootPanel);
249         }
250
251         /**
252          * Initializes all child dialogs.
253          */
254         private void initDialogs() {
255                 editNodeDialog = new EditNodeDialog(this);
256         }
257
258         //
259         // PRIVATE ACTIONS
260         //
261
262         /**
263          * Adds a new node via {@link #editNodeDialog}.
264          */
265         private void addNode() {
266                 editNodeDialog.setNodeName(I18n.get("general.newNode.name"));
267                 editNodeDialog.setNodeHostname("localhost");
268                 editNodeDialog.setNodePort(9481);
269                 editNodeDialog.setVisible(true);
270                 if (!editNodeDialog.wasCancelled()) {
271                         Node newNode = new Node();
272                         newNode.setName(editNodeDialog.getNodeName());
273                         newNode.setHostname(editNodeDialog.getNodeHostname());
274                         newNode.setPort(editNodeDialog.getNodePort());
275                         try {
276                                 if (!core.addNode(newNode)) {
277                                         JOptionPane.showMessageDialog(this, I18n.get("manageNodesDialog.error.nodeAlreadyKnown.message", newNode.getHostname(), newNode.getPort()), I18n.get("manageNodesDialog.error.nodeAlreadyKnown.title"), JOptionPane.ERROR_MESSAGE);
278                                 }
279                         } catch (UnknownHostException e) {
280                                 /*
281                                  * normally this shouldn't throw because the node editor catches
282                                  * it.
283                                  */
284                                 JOptionPane.showMessageDialog(this, I18n.get("manageNodesDialog.error.nodeUnresolvable.message", newNode.getHostname()), I18n.get("manageNodesDialog.error.nodeUnresolvable.title"), JOptionPane.ERROR_MESSAGE);
285                         }
286                 }
287         }
288
289         /**
290          * Edits a node via {@link #editNodeDialog}.
291          */
292         private void editNode() {
293                 Node selectedNode = (Node) nodeList.getSelectedValue();
294                 editNodeDialog.setNodeName(selectedNode.getName());
295                 editNodeDialog.setNodeHostname(selectedNode.getHostname());
296                 editNodeDialog.setNodePort(selectedNode.getPort());
297                 editNodeDialog.setVisible(true);
298                 if (!editNodeDialog.wasCancelled()) {
299                         selectedNode.setName(editNodeDialog.getNodeName());
300                         selectedNode.setHostname(editNodeDialog.getNodeHostname());
301                         selectedNode.setPort(editNodeDialog.getNodePort());
302                         nodeList.repaint();
303                 }
304         }
305
306         /**
307          * Deletes the selected node.
308          */
309         private void deleteNodes() {
310                 Object[] selectedNodes = nodeList.getSelectedValues();
311                 for (Object node: selectedNodes) {
312                         Node selectedNode = (Node) node;
313                         if (core.isNodeConnected(selectedNode)) {
314                                 int response = JOptionPane.showConfirmDialog(this, I18n.get("manageNodesDialog.error.nodeConnected.message", selectedNode.getName()), I18n.get("manageNodesDialog.error.nodeConnected.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
315                                 if (response == JOptionPane.CANCEL_OPTION) {
316                                         break;
317                                 } else if (response == JOptionPane.NO_OPTION) {
318                                         continue;
319                                 }
320                         }
321                         nodeListModel.removeNode(selectedNode);
322                 }
323                 nodeList.clearSelection();
324         }
325
326         /**
327          * Checks whether the list of nodes is not empty.
328          * 
329          * @return <code>true</code> if there is at least one node defined,
330          *         <code>false</code> otherwise
331          */
332         private boolean verifyNodesExist() {
333                 return nodeListModel.getSize() > 0;
334         }
335
336         /**
337          * This method is called when the “okay” button is pressed. The nodes from
338          * the list are read and the {@link #originalNodeList} member is set so that
339          * the calling code can use {@link #getNodeList()} to get the changed
340          * values.
341          */
342         private void confirm() {
343                 if (!verifyNodesExist()) {
344                         JOptionPane.showMessageDialog(this, I18n.get("manageNodesDialog.error.nodeListEmpty.message"), I18n.get("manageNodesDialog.error.nodeListEmpty.title"), JOptionPane.ERROR_MESSAGE);
345                         return;
346                 }
347                 originalNodeList.clear();
348                 for (Node node: nodeListModel) {
349                         originalNodeList.add(node);
350                 }
351                 setVisible(false);
352         }
353
354         /**
355          * Cancels the dialog.
356          */
357         private void cancel() {
358                 setVisible(false);
359         }
360
361         //
362         // INTERFACE ListSelectionListener
363         //
364
365         /**
366          * {@inheritDoc}
367          */
368         public void valueChanged(ListSelectionEvent listSelectionEvent) {
369                 JList list = (JList) listSelectionEvent.getSource();
370                 int selectCount = list.getSelectedIndices().length;
371                 editNodeAction.setEnabled(selectCount == 1);
372                 deleteNodeAction.setEnabled(selectCount >= 1);
373         }
374
375         //
376         // INTERFACE I18nable
377         //
378
379         /**
380          * {@inheritDoc}
381          */
382         public void updateI18n() {
383                 okayAction.updateI18n();
384                 cancelAction.updateI18n();
385                 addNodeAction.updateI18n();
386                 editNodeAction.updateI18n();
387                 deleteNodeAction.updateI18n();
388                 immediatelyEffectiveLabel.updateI18n();
389                 setTitle(I18n.get("manageNodesDialog.title") + " – jSite " + Version.getVersion());
390                 SwingUtils.repackCentered(this);
391         }
392
393         /**
394          * List model for the {@link ManageNodesDialog#nodeList}.
395          * 
396          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
397          * @version $Id$
398          */
399         private class NodeListModel extends AbstractListModel implements Iterable<Node> {
400
401                 /** The list of nodes. */
402                 @SuppressWarnings("hiding")
403                 private final List<Node> nodeList = new ArrayList<Node>();
404
405                 /**
406                  * Creates a new node list model.
407                  */
408                 public NodeListModel() {
409                         /* do nothing. */
410                 }
411
412                 /**
413                  * Adds the given node to the list model.
414                  * 
415                  * @see Collection#add(Object)
416                  * @param node
417                  *            The node to add
418                  */
419                 public void addNode(Node node) {
420                         nodeList.add(node);
421                         fireIntervalAdded(this, nodeList.size() - 1, nodeList.size() - 1);
422                 }
423
424                 /**
425                  * Removes the given node from the list model.
426                  * 
427                  * @see Collection#remove(Object)
428                  * @param node
429                  *            The node to remove
430                  */
431                 public void removeNode(Node node) {
432                         int nodeIndex = nodeList.indexOf(node);
433                         nodeList.remove(node);
434                         fireIntervalRemoved(this, nodeIndex, nodeIndex);
435                 }
436
437                 /**
438                  * Removes all nodes from the list model.
439                  * 
440                  * @see Collection#clear()
441                  */
442                 public void clear() {
443                         int nodeCount = nodeList.size();
444                         if (nodeCount > 0) {
445                                 nodeList.clear();
446                                 fireIntervalRemoved(this, 0, nodeCount - 1);
447                         }
448                 }
449
450                 /**
451                  * {@inheritDoc}
452                  */
453                 public Iterator<Node> iterator() {
454                         return nodeList.iterator();
455                 }
456
457                 /**
458                  * {@inheritDoc}
459                  */
460                 @SuppressWarnings("synthetic-access")
461                 public Object getElementAt(int index) {
462                         return nodeList.get(index);
463                 }
464
465                 /**
466                  * {@inheritDoc}
467                  */
468                 public int getSize() {
469                         return nodeList.size();
470                 }
471
472         }
473
474 }