/**
* UPnP PortMapper - A tool for managing port forwardings via UPnP
* Copyright (C) 2015 Christoph Pirkl <christoph at users.sourceforge.net>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.chris.portmapper.gui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ActionMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import org.chris.portmapper.PortMapperApp;
import org.chris.portmapper.model.Protocol;
import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.miginfocom.swing.MigLayout;
/**
* This class represents the dialog that can add a range of ports to the edit presets dialog.
*/
public class AddPortRangeDialog extends JDialog {
private static final long serialVersionUID = 1L;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final static String DIALOG_NAME = "add_port_range_dialog";
private final static String ACTION_ADD = DIALOG_NAME + ".add";
private final static String ACTION_CANCEL = DIALOG_NAME + ".cancel";
private final EditPresetDialog editPresetDialog;
private JTextField internalPortFrom;
private JTextField internalPortTo;
private JTextField externalPortFrom;
private JTextField externalPortTo;
private JCheckBox internalEqualsExternalPorts;
private JButton okButton;
private JComboBox<Protocol> protocolComboBox;
private final PortMapperApp app;
public AddPortRangeDialog(final PortMapperApp app, final EditPresetDialog editPresetDialog) {
super(app.getMainFrame(), true);
this.app = app;
this.editPresetDialog = editPresetDialog;
logger.debug("Create settings dialog");
this.setContentPane(this.getDialogPane());
this.getRootPane().setDefaultButton(okButton);
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.setName(DIALOG_NAME);
this.setModal(true);
this.pack();
// Register an action listener that closes the window when the ESC
// button is pressed
final KeyStroke escKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true);
final ActionListener windowCloseActionListener = new ActionListener() {
@Override
public final void actionPerformed(final ActionEvent e) {
cancel();
}
};
getRootPane().registerKeyboardAction(windowCloseActionListener, escKeyStroke,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
}
private static JLabel createLabel(final String postfix) {
final String completeName = DIALOG_NAME + "." + postfix;
final JLabel newLabel = new JLabel(completeName);
newLabel.setName(completeName);
return newLabel;
}
private JPanel getDialogPane() {
final JPanel dialogPane = new JPanel(new MigLayout("", // Layout
// Constraints
"[right]rel[left]", // Column Constraints
"")); // Row Constraints
internalPortFrom = new JTextField(5);
internalPortTo = new JTextField(5);
externalPortFrom = new JTextField(5);
externalPortTo = new JTextField(5);
externalPortFrom.addKeyListener(new KeyListener() {
@Override
public void keyReleased(final KeyEvent e) {
if (internalEqualsExternalPorts.isSelected()) {
internalPortFrom.setText(externalPortFrom.getText());
}
}
@Override
public void keyTyped(final KeyEvent e) {
// ignored
}
@Override
public void keyPressed(final KeyEvent e) {
// ignored
}
});
externalPortTo.addKeyListener(new KeyListener() {
@Override
public void keyReleased(final KeyEvent e) {
if (internalEqualsExternalPorts.isSelected()) {
internalPortTo.setText(externalPortTo.getText());
}
}
@Override
public void keyTyped(final KeyEvent e) {
// ignored
}
@Override
public void keyPressed(final KeyEvent e) {
// ignored
}
});
protocolComboBox = new JComboBox<>();
protocolComboBox.addItem(Protocol.TCP);
protocolComboBox.addItem(Protocol.UDP);
protocolComboBox.setSelectedIndex(0);
internalEqualsExternalPorts = new JCheckBox("add_port_range_dialog.external_equal_internal");
internalEqualsExternalPorts.setName("add_port_range_dialog.external_equal_internal");
internalEqualsExternalPorts.setSelected(true);
internalEqualsExternalPorts.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
logger.debug("Checkbox value changed");
internalPortFrom.setEnabled(!internalEqualsExternalPorts.isSelected());
internalPortTo.setEnabled(!internalEqualsExternalPorts.isSelected());
if (internalEqualsExternalPorts.isSelected()) {
internalPortTo.setText(externalPortTo.getText());
internalPortFrom.setText(externalPortFrom.getText());
}
}
});
internalPortFrom.setEnabled(!internalEqualsExternalPorts.isSelected());
internalPortTo.setEnabled(!internalEqualsExternalPorts.isSelected());
dialogPane.add(createLabel("protocol"));
dialogPane.add(protocolComboBox, "wrap");
dialogPane.add(createLabel("external_ports_from"));
dialogPane.add(externalPortFrom);
dialogPane.add(createLabel("external_ports_to"));
dialogPane.add(externalPortTo, "wrap");
dialogPane.add(internalEqualsExternalPorts, "left, span 4, wrap");
dialogPane.add(createLabel("internal_ports_from"));
dialogPane.add(internalPortFrom);
dialogPane.add(createLabel("internal_ports_to"));
dialogPane.add(internalPortTo, "wrap");
final ActionMap actionMap = app.getContext().getActionMap(this.getClass(), this);
dialogPane.add(new JButton(actionMap.get(ACTION_CANCEL)), "tag cancel, span 2");
okButton = new JButton(actionMap.get(ACTION_ADD));
dialogPane.add(okButton, "tag ok, span 2, wrap");
return dialogPane;
}
/**
* This method is executed when the user clicks the save button. The method saves the entered preset
*/
@Action(name = ACTION_ADD)
public void addPortRange() {
final int newInternalPortFrom;
final int newInternalPortTo;
final int newExternalPortFrom;
final int newExternalPortTo;
try {
newInternalPortFrom = Integer.parseInt(this.internalPortFrom.getText());
newInternalPortTo = Integer.parseInt(this.internalPortTo.getText());
newExternalPortFrom = Integer.parseInt(this.externalPortFrom.getText());
newExternalPortTo = Integer.parseInt(this.externalPortTo.getText());
} catch (final NumberFormatException e) {
showErrorMessage("add_port_range_dialog.invalid_number.title",
"add_port_range_dialog.invalid_number.message");
return;
}
if (newInternalPortFrom >= newInternalPortTo) {
showErrorMessage("add_port_range_dialog.invalid_internal_range.title",
"add_port_range_dialog.invalid_internal_range.message");
return;
}
if (newExternalPortFrom >= newExternalPortTo) {
showErrorMessage("add_port_range_dialog.invalid_external_range.title",
"add_port_range_dialog.invalid_external_range.message");
return;
}
if (newInternalPortTo - newInternalPortFrom != newExternalPortTo - newExternalPortFrom) {
showErrorMessage("add_port_range_dialog.invalid_range_length.title",
"add_port_range_dialog.invalid_range_length.message");
return;
}
for (int i = newInternalPortFrom; i <= newInternalPortTo; i++) {
final int internalPort = i;
final int externalPort = (internalPort - newInternalPortFrom) + newExternalPortFrom;
final Protocol selectedProtocol = (Protocol) protocolComboBox.getSelectedItem();
editPresetDialog.addPort(selectedProtocol, internalPort, externalPort);
}
this.dispose();
}
private void showErrorMessage(final String titleKey, final String messageKey) {
final ResourceMap resourceMap = app.getResourceMap();
JOptionPane.showMessageDialog(this, resourceMap.getString(messageKey), resourceMap.getString(titleKey),
JOptionPane.ERROR_MESSAGE);
}
@Action(name = ACTION_CANCEL)
public void cancel() {
this.dispose();
}
}