/*
* $Id$
*
* Copyright (c) 2000-2013 by Rodney Kinney, Brent Easton
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.chat.peer2peer;
import java.awt.Color;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import net.miginfocom.swing.MigLayout;
import org.litesoft.p2pchat.PeerInfo;
import org.litesoft.p2pchat.PendingPeerManager;
import VASSAL.build.GameModule;
import VASSAL.chat.ui.ChatControlsInitializer;
import VASSAL.chat.ui.ChatServerControls;
import VASSAL.configure.StringArrayConfigurer;
import VASSAL.i18n.Resources;
import VASSAL.preferences.Prefs;
import VASSAL.tools.SequenceEncoder;
import VASSAL.tools.swing.Dialogs;
/**
* Date: Mar 12, 2003
*/
public class DirectPeerPool implements PeerPool, ChatControlsInitializer {
protected static final String ADDRESS_PREF = "PeerAddressBook"; //$NON-NLS-1$
private AcceptPeerThread acceptThread;
private JButton inviteButton;
private JDialog frame;
private int listenPort;
private static StringArrayConfigurer addressConfig;
private JButton invitePeerButton;
private JButton addButton;
private JButton editButton;
private JButton removeButton;
private JList addressList;
private DefaultListModel addressBook;
private Properties params;
private boolean serverMode;
public DirectPeerPool() {
this (new Properties());
}
public DirectPeerPool(Properties param) {
params = new Properties();
params.putAll(param);
serverMode = P2PClientFactory.P2P_SERVER_MODE.equals(params.getProperty(P2PClientFactory.P2P_MODE_KEY));
inviteButton = new JButton(Resources.getString("Peer2Peer.connect")); //$NON-NLS-1$
inviteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.setVisible(true);
}
});
inviteButton.setEnabled(false);
inviteButton.setVisible(P2PClientFactory.P2P_SERVER_MODE.equals(params.getProperty(P2PClientFactory.P2P_MODE_KEY)));
}
public void initialize(P2PPlayer myInfo, PendingPeerManager ppm) throws IOException {
listenPort = 5050;
String port = params.getProperty(P2PClientFactory.P2P_LISTEN_PORT);
if (port != null && port.length() > 0) {
try {
listenPort = Integer.parseInt(port);
}
catch (NumberFormatException e) {
// No error;
}
}
acceptThread = new AcceptPeerThread(listenPort, ppm);
acceptThread.setName("Accept Peer Thread"); //$NON-NLS-1$
myInfo.getInfo().setPort(acceptThread.getPort());
acceptThread.start();
if (frame == null) {
initComponents(myInfo, ppm);
inviteButton.setEnabled(true);
}
}
protected boolean isServerMode() {
return serverMode;
}
public void disconnect() {
if (frame != null) {
frame.dispose();
frame = null;
inviteButton.setEnabled(false);
}
if (acceptThread != null) {
acceptThread.halt();
acceptThread = null;
}
}
public void connectFailed(PeerInfo peerInfo) {
JOptionPane.showMessageDialog(frame, Resources.getString("Peer2Peer.could_not_reach", peerInfo.getAddresses(), String.valueOf(peerInfo.getPort())), //$NON-NLS-1$
Resources.getString("Peer2Peer.invite_failed"), JOptionPane.INFORMATION_MESSAGE); //$NON-NLS-1$
}
public void initComponents(final P2PPlayer me, final PendingPeerManager ppm) {
// Retrieve Address Book from preference
if (addressConfig == null) {
addressConfig = new StringArrayConfigurer(ADDRESS_PREF, null);
Prefs.getGlobalPrefs().addOption(null, addressConfig);
}
String[] encodedEntries = addressConfig.getStringArray();
addressBook = new DefaultListModel();
addressList = new JList(addressBook);
for (String e : encodedEntries) {
addToList(new Entry(e));
}
Frame owner = null;
if (GameModule.getGameModule() != null) {
owner = GameModule.getGameModule().getFrame();
}
frame = new JDialog(owner,Resources.getString("Peer2Peer.direct_connection")); //$NON-NLS-1$
frame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
frame.setLayout(new MigLayout());
frame.add(new WTextArea(Resources.getString("Peer2Peer.other_players_address")), "growx, span 4,wrap"); //$NON-NLS-1$ //$NON-NLS-2$
final JScrollPane scroll = new JScrollPane(addressList);
frame.add(scroll, "span 4, grow, push, w 300, h 400, wrap"); //$NON-NLS-1$
invitePeerButton = new JButton(Resources.getString("Peer2Peer.connect")); //$NON-NLS-1$
invitePeerButton.setToolTipText(Resources.getString("Peer2Peer.invite_button_tooltip")); //$NON-NLS-1$))
invitePeerButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
invite(ppm);
}});
frame.add(invitePeerButton, "growx, push"); //$NON-NLS-1$
addButton = new JButton(Resources.getString("General.add")); //$NON-NLS-1$
addButton.setToolTipText(Resources.getString("Peer2Peer.add_button_tooltip")); //$NON-NLS-1$))
addButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
addEntry();
}});
frame.add(addButton, "growx, push"); //$NON-NLS-1$
editButton = new JButton(Resources.getString("General.edit")); //$NON-NLS-1$
editButton.setToolTipText(Resources.getString("Peer2Peer.edit_button_tooltip")); //$NON-NLS-1$))
editButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
editEntry();
}});
frame.add(editButton, "growx, push"); //$NON-NLS-1$
removeButton = new JButton(Resources.getString("General.remove")); //$NON-NLS-1$
removeButton.setToolTipText(Resources.getString("Peer2Peer.remove_button_tooltip")); //$NON-NLS-1$))
removeButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
removeEntries();
}});
frame.add(removeButton, "growx, push, wrap"); //$NON-NLS-1$
frame.pack();
frame.setLocationRelativeTo(owner);
return;
}
protected void invite(final PendingPeerManager ppm) {
final int selected[] = addressList.getSelectedIndices();
for (int i = 0; i < selected.length; i++) {
final Entry entry = (Entry) addressBook.getElementAt(selected[i]);
final PeerInfo info = PeerInfo.deFormat(entry.getAddress()+":"+entry.getPort()+" "+entry.getDescription()); //$NON-NLS-1$ //$NON-NLS-2$
if (info != null) {
ppm.addNewPeer(info);
GameModule.getGameModule().warn(Resources.getString("Chat.invite_sent", entry.toString())); //$NON-NLS-1$
}
else {
JOptionPane.showMessageDialog(frame, Resources.getString("Peer2Peer.invalid_format")); //$NON-NLS-1$
}
}
}
protected void addEntry() {
final Entry e = new Entry();
if (e.edit()) {
if (! addressBook.contains(e)) {
addToList(e);
saveAddressBook();
}
}
}
protected void addToList(Entry e) {
boolean added = false;
for (int i = 0; i < addressBook.getSize() && !added; i++) {
if (e.compareTo((Entry) (addressBook.getElementAt(i))) < 0) {
addressBook.insertElementAt(e, i);
added = true;
}
}
if (! added) {
addressBook.addElement(e);
}
}
protected void editEntry() {
final int index = addressList.getSelectedIndex();
if (index >= 0) {
final Entry e = (Entry) addressBook.getElementAt(index);
if (e.edit()) {
addressBook.removeElementAt(index);
addToList(e);
saveAddressBook();
}
}
}
protected void removeEntries() {
final int selected[] = addressList.getSelectedIndices();
if (selected.length == 0) {
return;
}
final Entry entries[] = new Entry[selected.length];
for (int i = 0; i < selected.length; i++) {
entries[i] = (Entry) addressBook.getElementAt(selected[i]);
}
final JPanel queryPanel = new JPanel(new MigLayout("", "10[][]10"));
final String mess = (entries.length == 1 ? Resources.getString("Peer2Peer.remove_entry") : Resources.getString("Peer2Peer.remove_entries", entries.length)); //$NON-NLS-1$ //$NON-NLS-2$
queryPanel.add(new JLabel(mess), "align center, wrap");
queryPanel.add(new JLabel(), "wrap");
for (int i = 0; i < entries.length; i++) {
queryPanel.add(new JLabel(entries[i].toString()), "wrap");
}
final Integer result = (Integer) Dialogs.showDialog(null, Resources.getString("Peer2Peer.remove_entry"), //$NON-NLS-1$
queryPanel, JOptionPane.QUESTION_MESSAGE, null, JOptionPane.OK_CANCEL_OPTION,
null, null, null, null);
if (result != null && result.intValue() == 0) {
for (int i = 0; i < entries.length; i++) {
addressBook.removeElement(entries[i]);
}
saveAddressBook();
}
}
protected void saveAddressBook() {
final String[] entries = new String[addressBook.size()];
int i = 0;
for (Enumeration<?> e = addressBook.elements(); e.hasMoreElements(); ) {
entries[i++] = ((Entry) e.nextElement()).encode();
}
addressConfig.setValue(entries);
}
public void initializeControls(ChatServerControls controls) {
controls.getToolbar().add(inviteButton);
}
public void uninitializeControls(ChatServerControls controls) {
controls.getToolbar().remove(inviteButton);
controls.getToolbar().repaint();
}
/**
* A class representing the address of another player's computer.
*
*/
private class Entry implements Comparable<Entry>{
String description;
String address;
String port;
JTextField descriptionField;
JTextField addressField;
JTextField portField;
public Entry () {
this("", "", "5050", "");
}
public Entry (String description, String address, String port, String passwd) {
this.description =description;
this.address = address;
this.port = port;
}
public Entry (String s) {
decode(s);
}
public String getDescription() {
return description;
}
public String getAddress() {
return address;
}
public String getPort() {
return port;
}
// public String getPasswd() {
// return passwd;
// }
public String toString() {
return description + " [" + address + ":" + port; // + (getPasswd().length() == 0 ? "" : "/") + getPasswd() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
private void decode(String s) {
SequenceEncoder.Decoder sd = new SequenceEncoder.Decoder(s, '|');
description = sd.nextToken(""); //$NON-NLS-1$
address = sd.nextToken(""); //$NON-NLS-1$
port = sd.nextToken("5050"); //$NON-NLS-1$
}
public String encode() {
SequenceEncoder se = new SequenceEncoder('|');
se.append(description);
se.append(address);
se.append(port);
return se.getValue();
}
public int compareTo(Entry e) {
return toString().compareTo(e.toString());
}
public boolean edit() {
descriptionField = new JTextField(description);
addressField = new JTextField(address);
portField = new JTextField(port);
final JPanel editPanel = new JPanel(new MigLayout("", "[align right]rel[]", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
editPanel.add(new JLabel(Resources.getString("Editor.description_label"))); //$NON-NLS-1$
editPanel.add(descriptionField, "wrap, grow, push"); //$NON-NLS-1$
editPanel.add(new JLabel(Resources.getString("Chat.ip_address"))); //$NON-NLS-1$
editPanel.add(addressField, "wrap, grow, push"); //$NON-NLS-1$
editPanel.add(new JLabel(Resources.getString("ServerAddressBook.port"))); //$NON-NLS-1$
editPanel.add(portField, "wrap, grow, push"); //$NON-NLS-1$
final Integer result = (Integer) Dialogs.showDialog(null, Resources.getString("Peer2Peer.add_peer_connection"), //$NON-NLS-1$
editPanel, JOptionPane.PLAIN_MESSAGE, null, JOptionPane.OK_CANCEL_OPTION,
null, null, null, null);
if (result != null && result == 0) {
description = descriptionField.getText();
address = addressField.getText();
port = portField.getText();
return true;
}
return false;
}
}
private class WTextArea extends JTextArea {
private static final long serialVersionUID = 1L;
public WTextArea(String s) {
super(s);
setEditable(false);
setLineWrap(true);
setWrapStyleWord(true);
setBackground(UIManager.getColor("OptionPane.background"));
}
}
}