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