--- /dev/null
+/*
+ * jSite2 - ManageNodeDialog.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.jsite.gui;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.AbstractListModel;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ListCellRenderer;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.pterodactylus.jsite.core.Node;
+import net.pterodactylus.jsite.i18n.I18n;
+import net.pterodactylus.jsite.main.Version;
+import net.pterodactylus.util.swing.SwingUtils;
+
+/**
+ * Dialog that lets the user manage her nodes.
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public class ManageNodesDialog extends JDialog implements ListSelectionListener {
+
+ /** The current list of nodes. */
+ private List<NodeWrapper> nodes = new ArrayList<NodeWrapper>();
+
+ /** The current default node. */
+ private Node defaultNode;
+
+ /** The “add node” action. */
+ private Action addNodeAction;
+
+ /** The “edit node” action. */
+ private Action editNodeAction;
+
+ /** The “delete node” action. */
+ private Action deleteNodeAction;
+
+ /** The “set default node” action. */
+ private Action setDefaultNodeAction;
+
+ /** The “okay” action. */
+ private Action okayAction;
+
+ /** The “cancel” action. */
+ private Action cancelAction;
+
+ /** The “edit node” dialog. */
+ private EditNodeDialog editNodeDialog;
+
+ /** The node list. */
+ private JList nodeList;
+
+ /** The mode for the node list. */
+ private NodeListModel nodeListModel = new NodeListModel();
+
+ /**
+ * Creates a new node manager dialog.
+ *
+ * @param parentFrame
+ * The parent frame of the dialog
+ */
+ public ManageNodesDialog(JFrame parentFrame) {
+ super(parentFrame, I18n.get("manageNodesDialog.title") + " – jSite " + Version.getVersion(), true);
+ initActions();
+ initComponents();
+ initDialogs();
+ pack();
+ SwingUtils.center(this);
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the default node.
+ *
+ * @return The default node, or <code>null</code> if no default node has
+ * been set
+ */
+ public Node getDefaultNode() {
+ return defaultNode;
+ }
+
+ /**
+ * Sets the default node.
+ *
+ * @param defaultNode
+ * The default node, or <code>null</code> if no default node
+ * has been set
+ */
+ public void setDefaultNode(Node defaultNode) {
+ this.defaultNode = defaultNode;
+ }
+
+ /**
+ * Returns the list of nodes.
+ *
+ * @return The list of nodes
+ */
+ public List<Node> getNodeList() {
+ List<Node> nodes = new ArrayList<Node>();
+ for (NodeWrapper nodeWrapper: this.nodes) {
+ nodes.add(nodeWrapper.getWrappedNode());
+ }
+ return nodes;
+ }
+
+ /**
+ * Sets the list of nodes.
+ *
+ * @param nodes
+ * The list of nodes
+ */
+ public void setNodeList(List<Node> nodes) {
+ this.nodes.clear();
+ for (Node node: nodes) {
+ this.nodes.add(new NodeWrapper(node));
+ }
+ nodeListModel.setNodeList(this.nodes);
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Initializes all actions.
+ */
+ private void initActions() {
+ okayAction = new I18nAction("general.button.okay") {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent e) {
+ confirm();
+ }
+ };
+ cancelAction = new I18nAction("general.button.cancel") {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent e) {
+ cancel();
+ }
+ };
+ addNodeAction = new I18nAction("manageNodesDialog.button.addNode") {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent e) {
+ addNode();
+ }
+ };
+ editNodeAction = new I18nAction("manageNodesDialog.button.editNode", false) {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent e) {
+ editNode();
+ }
+ };
+ deleteNodeAction = new I18nAction("manageNodesDialog.button.deleteNode", false) {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent e) {
+ deleteNode();
+ }
+ };
+ setDefaultNodeAction = new I18nAction("manageNodesDialog.button.setDefaultNode", false) {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent e) {
+ setDefaultNode();
+ }
+ };
+ }
+
+ /**
+ * Initializes all components.
+ */
+ private void initComponents() {
+ JPanel rootPanel = new JPanel(new BorderLayout(12, 12));
+ rootPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
+
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
+ rootPanel.add(buttonPanel, BorderLayout.PAGE_END);
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
+
+ buttonPanel.add(new JButton(cancelAction));
+ JButton okayButton = new JButton(okayAction);
+ getRootPane().setDefaultButton(okayButton);
+ buttonPanel.add(okayButton);
+
+ JPanel contentPanel = new JPanel(new BorderLayout(12, 12));
+ rootPanel.add(contentPanel, BorderLayout.CENTER);
+ contentPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(12, 12, 12, 12)));
+
+ JPanel listButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 12, 12));
+ contentPanel.add(listButtonPanel, BorderLayout.PAGE_END);
+ listButtonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
+ listButtonPanel.add(new JButton(addNodeAction));
+ listButtonPanel.add(new JButton(editNodeAction));
+ listButtonPanel.add(new JButton(deleteNodeAction));
+ listButtonPanel.add(new JButton(setDefaultNodeAction));
+
+ nodeList = new JList(nodeListModel);
+ nodeList.addListSelectionListener(this);
+ contentPanel.add(new JScrollPane(nodeList), BorderLayout.CENTER);
+
+ setContentPane(rootPanel);
+ }
+
+ /**
+ * Initializes all child dialogs.
+ */
+ private void initDialogs() {
+ editNodeDialog = new EditNodeDialog(this);
+ }
+
+ //
+ // PRIVATE ACTIONS
+ //
+
+ /**
+ * Adds a new node via {@link #editNodeDialog}.
+ */
+ private void addNode() {
+ editNodeDialog.setNodeName("New Node");
+ editNodeDialog.setNodeHostname("localhost");
+ editNodeDialog.setNodePort(9481);
+ editNodeDialog.setNodeOnSameMachine(true);
+ editNodeDialog.setVisible(true);
+ if (!editNodeDialog.wasCancelled()) {
+ Node newNode = new Node();
+ newNode.setName(editNodeDialog.getNodeName());
+ newNode.setHostname(editNodeDialog.getNodeHostname());
+ newNode.setPort(editNodeDialog.getNodePort());
+ NodeWrapper newNodeWrapper = new NodeWrapper(newNode);
+ if (nodes.isEmpty()) {
+ defaultNode = newNode;
+ newNodeWrapper.setDefaultNode(true);
+ }
+ nodes.add(new NodeWrapper(newNode));
+ nodeListModel.setNodeList(nodes);
+ }
+ }
+
+ /**
+ * Edits a node via {@link #editNodeDialog}.
+ */
+ private void editNode() {
+ NodeWrapper selectedNodeWrapper = (NodeWrapper) nodeList.getSelectedValue();
+ Node selectedNode = selectedNodeWrapper.getWrappedNode();
+ editNodeDialog.setNodeName(selectedNode.getName());
+ editNodeDialog.setNodeHostname(selectedNode.getHostname());
+ editNodeDialog.setNodePort(selectedNode.getPort());
+ editNodeDialog.setNodeOnSameMachine(selectedNode.isSameMachine());
+ editNodeDialog.setVisible(true);
+ if (!editNodeDialog.wasCancelled()) {
+ selectedNode.setName(editNodeDialog.getNodeName());
+ selectedNode.setHostname(editNodeDialog.getNodeHostname());
+ selectedNode.setPort(editNodeDialog.getNodePort());
+ selectedNode.setSameMachine(editNodeDialog.isNodeOnSameMachine());
+ nodeList.repaint();
+ }
+ }
+
+ /**
+ * Deletes the selected node.
+ */
+ private void deleteNode() {
+ NodeWrapper selectedNode = (NodeWrapper) nodeList.getSelectedValue();
+ nodes.remove(selectedNode);
+ nodeList.clearSelection();
+ nodeList.repaint();
+ }
+
+ /**
+ * Sets the default node to the selected node.
+ */
+ private void setDefaultNode() {
+ NodeWrapper selectedNode = (NodeWrapper) nodeList.getSelectedValue();
+ for (NodeWrapper nodeWrapper: nodes) {
+ nodeWrapper.setDefaultNode(nodeWrapper == selectedNode);
+ }
+ nodeList.repaint();
+ }
+
+ /**
+ * This method is called when the “okay” button is pressed. The nodes from
+ * the list are read and the {@link #nodes} and {@link #defaultNode} members
+ * are set so that the calling code can use {@link #getNodeList()} and
+ * {@link #getDefaultNode()} to get the changed values.
+ */
+ private void confirm() {
+ setVisible(false);
+ }
+
+ /**
+ * Cancels the dialog.
+ */
+ private void cancel() {
+ setVisible(false);
+ }
+
+ //
+ // INTERFACE ListSelectionListener
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ public void valueChanged(ListSelectionEvent listSelectionEvent) {
+ JList list = (JList) listSelectionEvent.getSource();
+ int selectCount = list.getSelectedIndices().length;
+ editNodeAction.setEnabled(selectCount == 1);
+ deleteNodeAction.setEnabled(selectCount >= 1);
+ setDefaultNodeAction.setEnabled(selectCount == 1);
+ }
+
+ /**
+ * Wrapper around a {@link Node} to store whether a node is the default
+ * node. This frees us from having to write a {@link ListCellRenderer} for
+ * {@link ManageNodesDialog#nodeList}.
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+ private static class NodeWrapper {
+
+ /** The wrapped node. */
+ private final Node wrappedNode;
+
+ /** The default node. */
+ private boolean defaultNode;
+
+ /**
+ * Creates a new node wrapper around the given node.
+ *
+ * @param wrappedNode
+ * The node to wrap
+ */
+ public NodeWrapper(Node wrappedNode) {
+ this.wrappedNode = wrappedNode;
+ }
+
+ /**
+ * Returns the wrapped node.
+ *
+ * @return The wrapped node
+ */
+ public Node getWrappedNode() {
+ return wrappedNode;
+ }
+
+ /**
+ * Returns whether the wrapped node is the default node.
+ *
+ * @return <code>true</code> if the wrapped node is the default node,
+ * <code>false</code> otherwise
+ */
+ public boolean isDefaultNode() {
+ return defaultNode;
+ }
+
+ /**
+ * Sets whether the wrapped node is the default node.
+ *
+ * @param defaultNode
+ * <code>true</code> if the wrapped node is the default
+ * node, <code>false</code> otherwise
+ */
+ public void setDefaultNode(boolean defaultNode) {
+ this.defaultNode = defaultNode;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return wrappedNode.toString() + (defaultNode ? " (default)" : "");
+ }
+
+ }
+
+ /**
+ * List model for the {@link ManageNodesDialog#nodeList}. TODO
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+ private class NodeListModel extends AbstractListModel {
+
+ /** The list of wrapped nodes. */
+ @SuppressWarnings("hiding")
+ private List<NodeWrapper> nodeList = Collections.emptyList();
+
+ /**
+ * Creates a new node list model.
+ */
+ public NodeListModel() {
+ }
+
+ /**
+ * Sets the new node list.
+ *
+ * @param nodeList
+ * The list of nodes to display
+ */
+ public void setNodeList(List<NodeWrapper> nodeList) {
+ this.nodeList = nodeList;
+ fireContentsChanged(this, 0, nodeList.size() - 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public Object getElementAt(int index) {
+ NodeWrapper currentNodeWrapper = nodeList.get(index);
+ return currentNodeWrapper;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getSize() {
+ return nodeList.size();
+ }
+
+ }
+
+}