d4c83972c0c5674d3a088bf9fd8aac3318fa1835
[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 original list of nodes. */
57         private List<Node> originalNodeList;
58
59         /** The current list of wrapped nodes. */
60         private List<NodeWrapper> wrappedNodeList = new ArrayList<NodeWrapper>();
61
62         /** The current default node. */
63         private Node defaultNode;
64
65         /** The “add node” action. */
66         private Action addNodeAction;
67
68         /** The “edit node” action. */
69         private Action editNodeAction;
70
71         /** The “delete node” action. */
72         private Action deleteNodeAction;
73
74         /** The “set default node” action. */
75         private Action setDefaultNodeAction;
76
77         /** The “okay” action. */
78         private Action okayAction;
79
80         /** The “cancel” action. */
81         private Action cancelAction;
82
83         /** The “edit node” dialog. */
84         private EditNodeDialog editNodeDialog;
85
86         /** The node list. */
87         private JList nodeList;
88
89         /** The mode for the node list. */
90         private NodeListModel nodeListModel = new NodeListModel();
91
92         /**
93          * Creates a new node manager dialog.
94          * 
95          * @param parentFrame
96          *            The parent frame of the dialog
97          */
98         public ManageNodesDialog(JFrame parentFrame) {
99                 super(parentFrame, I18n.get("manageNodesDialog.title") + " – jSite " + Version.getVersion(), true);
100                 initActions();
101                 initComponents();
102                 initDialogs();
103                 pack();
104                 SwingUtils.center(this);
105         }
106
107         //
108         // ACCESSORS
109         //
110
111         /**
112          * Returns the default node.
113          * 
114          * @return The default node, or <code>null</code> if no default node has
115          *         been set
116          */
117         public Node getDefaultNode() {
118                 return defaultNode;
119         }
120
121         /**
122          * Sets the default node.
123          * 
124          * @param defaultNode
125          *            The default node, or <code>null</code> if no default node
126          *            has been set
127          */
128         public void setDefaultNode(Node defaultNode) {
129                 this.defaultNode = defaultNode;
130         }
131
132         /**
133          * Returns the list of nodes.
134          * 
135          * @return The list of nodes
136          */
137         public List<Node> getNodeList() {
138                 return originalNodeList;
139         }
140
141         /**
142          * Sets the list of nodes.
143          * 
144          * @param nodeList
145          *            The list of nodes
146          */
147         public void setNodeList(List<Node> nodeList) {
148                 originalNodeList = nodeList;
149                 wrappedNodeList.clear();
150                 for (Node node: nodeList) {
151                         wrappedNodeList.add(new NodeWrapper(node));
152                 }
153                 nodeListModel.setNodeList(wrappedNodeList);
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 (wrappedNodeList.isEmpty()) {
288                                 defaultNode = newNode;
289                                 newNodeWrapper.setDefaultNode(true);
290                         }
291                         wrappedNodeList.add(new NodeWrapper(newNode));
292                         nodeListModel.setNodeList(wrappedNodeList);
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                 wrappedNodeList.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: wrappedNodeList) {
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 #wrappedNodeList} and
340          * {@link #defaultNode} members are set so that the calling code can use
341          * {@link #getNodeList()} and {@link #getDefaultNode()} to get the changed
342          * values.
343          */
344         private void confirm() {
345                 originalNodeList.clear();
346                 for (NodeWrapper nodeWrapper: wrappedNodeList) {
347                         originalNodeList.add(nodeWrapper.getWrappedNode());
348                         if (nodeWrapper.isDefaultNode()) {
349                                 defaultNode = nodeWrapper.getWrappedNode();
350                         }
351                 }
352                 setVisible(false);
353         }
354
355         /**
356          * Cancels the dialog.
357          */
358         private void cancel() {
359                 setVisible(false);
360         }
361
362         //
363         // INTERFACE ListSelectionListener
364         //
365
366         /**
367          * {@inheritDoc}
368          */
369         public void valueChanged(ListSelectionEvent listSelectionEvent) {
370                 JList list = (JList) listSelectionEvent.getSource();
371                 int selectCount = list.getSelectedIndices().length;
372                 editNodeAction.setEnabled(selectCount == 1);
373                 deleteNodeAction.setEnabled(selectCount >= 1);
374                 setDefaultNodeAction.setEnabled(selectCount == 1);
375         }
376
377         /**
378          * Wrapper around a {@link Node} to store whether a node is the default
379          * node. This frees us from having to write a {@link ListCellRenderer} for
380          * {@link ManageNodesDialog#nodeList}.
381          * 
382          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
383          * @version $Id$
384          */
385         private static class NodeWrapper {
386
387                 /** The wrapped node. */
388                 private final Node wrappedNode;
389
390                 /** The default node. */
391                 private boolean defaultNode;
392
393                 /**
394                  * Creates a new node wrapper around the given node.
395                  * 
396                  * @param wrappedNode
397                  *            The node to wrap
398                  */
399                 public NodeWrapper(Node wrappedNode) {
400                         this.wrappedNode = wrappedNode;
401                 }
402
403                 /**
404                  * Returns the wrapped node.
405                  * 
406                  * @return The wrapped node
407                  */
408                 public Node getWrappedNode() {
409                         return wrappedNode;
410                 }
411
412                 /**
413                  * Returns whether the wrapped node is the default node.
414                  * 
415                  * @return <code>true</code> if the wrapped node is the default node,
416                  *         <code>false</code> otherwise
417                  */
418                 public boolean isDefaultNode() {
419                         return defaultNode;
420                 }
421
422                 /**
423                  * Sets whether the wrapped node is the default node.
424                  * 
425                  * @param defaultNode
426                  *            <code>true</code> if the wrapped node is the default
427                  *            node, <code>false</code> otherwise
428                  */
429                 public void setDefaultNode(boolean defaultNode) {
430                         this.defaultNode = defaultNode;
431                 }
432
433                 /**
434                  * {@inheritDoc}
435                  */
436                 @Override
437                 public String toString() {
438                         return wrappedNode.toString() + (defaultNode ? " (default)" : "");
439                 }
440
441         }
442
443         /**
444          * List model for the {@link ManageNodesDialog#nodeList}. TODO
445          * 
446          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
447          * @version $Id$
448          */
449         private class NodeListModel extends AbstractListModel {
450
451                 /** The list of wrapped nodes. */
452                 @SuppressWarnings("hiding")
453                 private List<NodeWrapper> nodeList = Collections.emptyList();
454
455                 /**
456                  * Creates a new node list model.
457                  */
458                 public NodeListModel() {
459                 }
460
461                 /**
462                  * Sets the new node list.
463                  * 
464                  * @param nodeList
465                  *            The list of nodes to display
466                  */
467                 public void setNodeList(List<NodeWrapper> nodeList) {
468                         this.nodeList = nodeList;
469                         fireContentsChanged(this, 0, nodeList.size() - 1);
470                 }
471
472                 /**
473                  * {@inheritDoc}
474                  */
475                 @SuppressWarnings("synthetic-access")
476                 public Object getElementAt(int index) {
477                         NodeWrapper currentNodeWrapper = nodeList.get(index);
478                         return currentNodeWrapper;
479                 }
480
481                 /**
482                  * {@inheritDoc}
483                  */
484                 public int getSize() {
485                         return nodeList.size();
486                 }
487
488         }
489
490 }