package ch.rakudave.jnetmap.view.properties;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.NumberEditor;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import ch.rakudave.jnetmap.controller.Controller;
import ch.rakudave.jnetmap.controller.command.Command;
import ch.rakudave.jnetmap.model.Connection;
import ch.rakudave.jnetmap.model.IF.NetworkIF;
import ch.rakudave.jnetmap.model.IF.PhysicalIF;
import ch.rakudave.jnetmap.model.IF.TransparentIF;
import ch.rakudave.jnetmap.model.device.Device;
import ch.rakudave.jnetmap.net.Subnet;
import ch.rakudave.jnetmap.net.status.IsReachable;
import ch.rakudave.jnetmap.net.status.NativePing;
import ch.rakudave.jnetmap.net.status.OpenSocket;
import ch.rakudave.jnetmap.net.status.PingMethod;
import ch.rakudave.jnetmap.util.Icons;
import ch.rakudave.jnetmap.util.Lang;
import ch.rakudave.jnetmap.util.SwingHelper;
import ch.rakudave.jnetmap.view.components.PortScanner;
import ch.rakudave.jnetmap.view.components.TabPanel;
import ch.rakudave.jnetmap.view.preferences.PreferencePanel;
@SuppressWarnings("serial")
public class InterfaceProperties extends JDialog {
public InterfaceProperties(final Frame owner, final NetworkIF i) {
super(owner, Lang.getNoHTML("interface.properties"), ModalityType.DOCUMENT_MODAL);
setLayout(new BorderLayout(5, 5));
setPreferredSize(new Dimension(350,320));
final PreferencePanel main = getInnerPanel(owner, i);
JPanel bottomRow = new JPanel(new FlowLayout(FlowLayout.TRAILING, 5, 5));
final JDialog _this = this;
JButton cancel = new JButton(Lang.get("action.cancel"), Icons.get("cancel"));
cancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
_this.dispose();
}
});
JButton ok = new JButton(Lang.get("action.ok"), Icons.get("ok"));
ok.setPreferredSize(cancel.getPreferredSize());
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
main.save();
_this.dispose();
if (TabPanel.getCurrentTab() != null) TabPanel.getCurrentTab().repaint();
}
});
bottomRow.add(cancel);
bottomRow.add(ok);
add(main, BorderLayout.CENTER);
add(bottomRow, BorderLayout.SOUTH);
pack();
SwingHelper.centerTo(owner, this);
setVisible(true);
}
public static PreferencePanel getInnerPanel(final Frame owner, final NetworkIF i) {
if (i == null) return null;
final boolean isPhysical = (i instanceof PhysicalIF), isTransparent = (i instanceof TransparentIF);
final String oldAddress = (!isTransparent && i != null && i.getAddress() != null)?i.getAddress().getHostAddress():"127.0.0.1",
oldSubnet = (i == null || i.getSubnet() == null)?"":i.getSubnet().getInfo().getNetmask(),
oldGateway = (i == null || i.getGateway() == null)?"":i.getGateway().getHostAddress();
final JTextField name = new JTextField(i.getName());
final JTextField address = new JTextField((!isTransparent)?oldAddress:"");
address.setEnabled(!isTransparent);
final JTextField subnet = new JTextField(oldSubnet);
subnet.setEnabled(!isTransparent);
final JTextField gateway = new JTextField();
gateway.setEnabled(isPhysical);
address.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
autocomplete(address, subnet, gateway, false);
}
@Override public void focusGained(FocusEvent e) {}
});
address.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
autocomplete(address, subnet, gateway, true);
}
});
final JTextField mac = new JTextField();
mac.setEnabled(isPhysical);
final JPanel portWrapper = new JPanel(new GridLayout(0, 2, 5, 5));
final PingMethod oldMethod = (isPhysical)?((PhysicalIF)i).getPingMethod():null;
int portNr = (isPhysical && oldMethod instanceof OpenSocket)?((OpenSocket)((PhysicalIF)i).getPingMethod()).getPort():0;
final JSpinner port = new JSpinner(new SpinnerNumberModel(portNr, 0, 65535, 1));
port.setEditor(new NumberEditor(port, "#####"));
port.setEnabled(isPhysical);
final JButton portScan = new JButton(new AbstractAction(Lang.get("port.scan")) {
@Override
public void actionPerformed(ActionEvent e) {
PortScanner ps = new PortScanner(owner, oldAddress);
port.setValue(ps.getPort());
}
});
portScan.setEnabled(isPhysical);
portWrapper.add(port);
portWrapper.add(portScan);
final JComboBox method = new JComboBox(new String[] {"Java Ping", "System Ping", "TCP Port"});
method.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
boolean en = method.getSelectedIndex() == 2;
port.setEnabled(en);
portScan.setEnabled(en);
}
});
method.setEnabled(isPhysical);
if (oldMethod != null) method.setSelectedItem(oldMethod.toString());
if (isPhysical) {
gateway.setText(oldGateway);
mac.setText(((PhysicalIF)i).getMacAddress());
}
final JCheckBox addressCheckbox = new JCheckBox(Lang.get("interface.address"));
addressCheckbox.setEnabled(isPhysical || isTransparent);
addressCheckbox.setSelected(isPhysical);
addressCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
boolean s = addressCheckbox.isSelected();
address.setEnabled(s); subnet.setEnabled(s);
gateway.setEnabled(s); mac.setEnabled(s);
method.setEnabled(s); port.setEnabled(s);
portScan.setEnabled(s);
}
});
PreferencePanel p = new PreferencePanel() {
@Override
public void save() {
//TODO skip new command if nothing has changed
Controller.getCurrentMap().getHistory().execute(new Command() {
String oldName = i.getName(), newName = name.getText(),
newAddress = address.getText(),
newSubnet = subnet.getText(), newGateway = gateway.getText(),
oldMac = (isPhysical)?((PhysicalIF)i).getMacAddress():null, newMac = mac.getText();
PingMethod newMethod = nameToMethod((String)method.getSelectedItem(), (Integer) port.getValue());
Connection connection = i.getConnection();
Device parent = i.getDevice();
NetworkIF newIF = null;
@Override
public Object undo() {
if (isPhysical && !addressCheckbox.isSelected()) {
PhysicalIF pif = new PhysicalIF(parent, connection, oldAddress);
pif.setSubnet(oldSubnet);
pif.setGateway(oldGateway);
pif.setMacAddress(oldMac);
pif.setPingMethod(oldMethod);
pif.setName(oldName);
parent.addInterface(pif);
parent.removeInterface(newIF);
} else if (isTransparent && addressCheckbox.isSelected()) {
TransparentIF tif = new TransparentIF(parent, connection, Controller.getCurrentMap().
getOpposite(parent, i.getConnection()).getInterfaceFor(connection));
tif.setName(oldName);
parent.addInterface(tif);
parent.removeInterface(newIF);
} else {
i.setAddress(oldAddress);
i.setSubnet(oldSubnet);
i.setGateway(oldGateway);
i.setName(oldName);
if (isPhysical) {
((PhysicalIF)i).setMacAddress(oldMac);
((PhysicalIF)i).setPingMethod(oldMethod);
}
}
return null;
}
@Override
public Object redo() {
NetworkIF counterpart = Controller.getCurrentMap().
getOpposite(parent, i.getConnection()).getInterfaceFor(connection);
if (isPhysical && !addressCheckbox.isSelected()) {
newIF = new TransparentIF(parent, connection, counterpart);
newIF.setName(newName);
parent.addInterface(newIF);
parent.removeInterface(i);
} else if (isTransparent && addressCheckbox.isSelected()) {
PhysicalIF pif = new PhysicalIF(parent, connection, newAddress);
pif.setSubnet(newSubnet);
pif.setGateway(newGateway);
pif.setMacAddress(newMac);
pif.setPingMethod(newMethod);
pif.setName(newName);
newIF = pif;
parent.addInterface(pif);
parent.removeInterface(i);
if (counterpart instanceof TransparentIF) ((TransparentIF) counterpart).setCounterpart(pif);
} else {
i.setAddress(newAddress);
i.setSubnet(newSubnet);
i.setGateway(newGateway);
i.setName(newName);
if (isPhysical) {
((PhysicalIF)i).setMacAddress(newMac);
((PhysicalIF)i).setPingMethod(newMethod);
}
}
return null;
}
});
}
};
p.setLayout(new GridLayout(0, 2, 5, 5));
p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
p.add(new JLabel(Lang.get("interface.name"))); p.add(name);
p.add(addressCheckbox); p.add(address);
p.add(new JLabel(" "+Lang.getNoHTML("interface.subnet"))); p.add(subnet);
p.add(new JLabel(" "+Lang.getNoHTML("interface.gateway"))); p.add(gateway);
p.add(new JLabel(" "+Lang.getNoHTML("interface.mac"))); p.add(mac);
p.add(new JLabel(" "+Lang.getNoHTML("interface.pingmethod"))); p.add(method);
p.add(new JLabel()); p.add(portWrapper);
if (isPhysical) address.requestFocus();
else name.requestFocus();
return p;
}
private static PingMethod nameToMethod(String name, int port) {
if ("Java Ping".equals(name)) {
return IsReachable.getInstance();
} else if ("System Ping".equals(name)) {
return NativePing.getInstance();
} else {
return new OpenSocket(port);
}
}
private static void autocomplete(JTextField address, JTextField subnet, JTextField gateway, boolean force) {
try {
// attempt auto-completion
String addr = address.getText(), mask = subnet.getText();
mask = (mask.isEmpty())?"255.255.255.0":mask;
Subnet s;
if (addr.contains("/")) s = new Subnet(addr);
else s = new Subnet(addr, mask);
address.setText(s.getInfo().getAddress());
if (subnet.getText().isEmpty() || force) subnet.setText(s.getInfo().getNetmask());
if (gateway.getText().isEmpty() || force) gateway.setText(s.getInfo().getLowAddress());
} catch (Exception ex) {/*just trying, no harm done*/}
}
}