/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.plugin.jabberaccregwizz;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* @author Yana Stamcheva
*/
public class IceConfigPanel
extends TransparentPanel
{
/**
* The check box allowing the user to choose to use ICE.
*/
private final JCheckBox iceBox = new SIPCommCheckBox(
Resources.getString("plugin.jabberaccregwizz.USE_ICE"));
/**
* The check box allowing the user to choose to automatically discover
* STUN servers.
*/
private final JCheckBox autoDiscoverBox = new SIPCommCheckBox(
Resources.getString("plugin.jabberaccregwizz.AUTO_DISCOVER_STUN"));
/**
* The table model for our additional stun servers table.
*/
private final StunServerTableModel tableModel = new StunServerTableModel();
/**
* The stun server table.
*/
private final JTable table = new JTable(tableModel);
/**
* Creates an instance of IceConfigPanel.
*/
public IceConfigPanel()
{
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
iceBox.setAlignmentX(Component.LEFT_ALIGNMENT);
autoDiscoverBox.setAlignmentX(Component.LEFT_ALIGNMENT);
JPanel checkBoxPanel = new TransparentPanel(new GridLayout(0, 1));
checkBoxPanel.add(iceBox);
checkBoxPanel.add(autoDiscoverBox);
add(checkBoxPanel);
add(Box.createVerticalStrut(10));
add(createAdditionalServersComponent());
}
/**
* Creates the list of additional STUN/TURN servers that are added by the
* user.
* @return the created component
*/
private Component createAdditionalServersComponent()
{
table.setPreferredScrollableViewportSize(new Dimension(450, 60));
tableModel.addColumn(
Resources.getString("plugin.jabberaccregwizz.IP_ADDRESS")
+ "/" + Resources.getString("plugin.jabberaccregwizz.PORT"));
tableModel.addColumn(
Resources.getString("plugin.jabberaccregwizz.SUPPORT_TURN"));
table.setDefaultRenderer( StunServerDescriptor.class,
new StunServerCellRenderer());
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
JButton addButton
= new JButton(Resources.getString("service.gui.ADD"));
addButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
StunConfigDialog stunDialog = new StunConfigDialog();
stunDialog.setVisible(true);
}
});
JButton editButton
= new JButton(Resources.getString("service.gui.EDIT"));
editButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
StunServerDescriptor stunServer
= (StunServerDescriptor) tableModel.getValueAt(
table.getSelectedRow(), 0);
if (stunServer != null)
{
StunConfigDialog dialog
= new StunConfigDialog( stunServer.getAddress(),
stunServer.getPort(),
stunServer.isTurnSupported(),
stunServer.getUsername(),
stunServer.getPassword());
dialog.setVisible(true);
}
}
});
JButton deleteButton
= new JButton(Resources.getString("service.gui.DELETE"));
deleteButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
tableModel.removeRow(table.getSelectedRow());
}
});
TransparentPanel buttonsPanel
= new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
buttonsPanel.add(addButton);
buttonsPanel.add(editButton);
buttonsPanel.add(deleteButton);
TransparentPanel mainPanel = new TransparentPanel(new BorderLayout());
mainPanel.setBorder(BorderFactory.createTitledBorder(
Resources.getString(
"plugin.jabberaccregwizz.ADDITIONAL_STUN_SERVERS")));
mainPanel.add(scrollPane);
mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
return mainPanel;
}
/**
* The STUN configuration window.
*/
private class StunConfigDialog extends SIPCommDialog
{
/**
* The main panel
*/
private final JPanel mainPanel
= new TransparentPanel(new BorderLayout());
/**
* The address of the stun server.
*/
private final JTextField addressField = new JTextField();
/**
* The port number field
*/
private final JTextField portField = new JTextField();
/**
* The input verifier that we will be using to validate port numbers.
*/
private final PortVerifier portVerifier = new PortVerifier();
/**
* The check box where user would indicate whether a STUN server is also
* a TURN server.
*/
private final JCheckBox supportTurnCheckBox = new JCheckBox(
Resources.getString("plugin.jabberaccregwizz.SUPPORT_TURN"));
/**
* The user name field
*/
private final JTextField usernameField = new JTextField();
/**
* The password field.
*/
private final JPasswordField passwordField = new JPasswordField();
/**
* The pane where we show errors.
*/
private JEditorPane errorMessagePane;
/**
* Creates a new StunConfigDialog with filled in values.
*
* @param address the IP or FQDN of the server
* @param port the port number
* @param isSupportTurn a boolean indicating whether the server
* is also a TURN relay
* @param username the username we should use with this server
* @param password the password we should use with this server
*/
public StunConfigDialog(String address,
int port,
boolean isSupportTurn,
String username,
char[] password)
{
this();
addressField.setText(address);
portField.setText(Integer.toString( port ));
supportTurnCheckBox.setSelected(isSupportTurn);
usernameField.setText(username);
passwordField.setText(password.toString());
}
/**
* Creates an empty dialog.
*/
public StunConfigDialog()
{
super(false);
setTitle(Resources.getString(
"plugin.jabberaccregwizz.ADD_STUN_SERVER"));
JLabel addressLabel = new JLabel(
Resources.getString("plugin.jabberaccregwizz.IP_ADDRESS"));
JLabel portLabel = new JLabel(
Resources.getString("plugin.jabberaccregwizz.PORT"));
JLabel usernameLabel = new JLabel(
Resources.getString("plugin.jabberaccregwizz.USERNAME"));
JLabel passwordLabel = new JLabel(
Resources.getString("service.gui.PASSWORD"));
TransparentPanel labelsPanel
= new TransparentPanel(new GridLayout(0, 1));
labelsPanel.add(new JLabel());
labelsPanel.add(addressLabel);
labelsPanel.add(portLabel);
labelsPanel.add(usernameLabel);
labelsPanel.add(passwordLabel);
TransparentPanel valuesPanel
= new TransparentPanel(new GridLayout(0, 1));
valuesPanel.add(supportTurnCheckBox);
valuesPanel.add(addressField);
valuesPanel.add(portField);
valuesPanel.add(usernameField);
valuesPanel.add(passwordField);
//register an input verifier so that we would only accept valid
//port numbers.
portField.setInputVerifier(portVerifier);
JButton addButton
= new JButton(Resources.getString("service.gui.ADD"));
JButton cancelButton
= new JButton(Resources.getString("service.gui.CANCEL"));
addButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String address = addressField.getText();
String portStr = portField.getText();
int port = -1;
if(portStr != null && portStr.trim().length() > 0)
port = Integer.parseInt( portField.getText() );
String username = usernameField.getText();
char[] password = passwordField.getPassword();
String errorMessage = null;
if (address == null || address.length() <= 0)
errorMessage = Resources.getString(
"plugin.jabberaccregwizz.NO_STUN_ADDRESS");
if (username == null || username.length() <= 0)
errorMessage = Resources.getString(
"plugin.jabberaccregwizz.NO_STUN_USERNAME");
if (containsStunServer(address, port))
errorMessage = Resources.getString(
"plugin.jabberaccregwizz.STUN_ALREADY_EXIST");
if (errorMessage != null)
{
loadErrorMessage(errorMessage);
return;
}
StunServerDescriptor stunServer = new StunServerDescriptor(
address, port, supportTurnCheckBox.isSelected(),
username, password);
addStunServer(stunServer);
dispose();
}
});
cancelButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dispose();
}
});
TransparentPanel buttonsPanel
= new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
buttonsPanel.add(addButton);
buttonsPanel.add(cancelButton);
mainPanel.add(labelsPanel, BorderLayout.WEST);
mainPanel.add(valuesPanel, BorderLayout.CENTER);
mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
getContentPane().add(mainPanel, BorderLayout.NORTH);
pack();
}
/**
* Loads the given error message in the current dialog, by re-validating
* the content.
*
* @param errorMessage The error message to load.
*/
private void loadErrorMessage(String errorMessage)
{
if (errorMessagePane == null)
{
errorMessagePane = new JEditorPane();
errorMessagePane.setOpaque(false);
errorMessagePane.setForeground(Color.RED);
mainPanel.add(errorMessagePane, BorderLayout.NORTH);
}
errorMessagePane.setText(errorMessage);
mainPanel.revalidate();
mainPanel.repaint();
this.pack();
//WORKAROUND: there's something wrong happening in this pack and
//components get cluttered, partially hiding the password text field.
//I am under the impression that this has something to do with the
//message pane preferred size being ignored (or being 0) which is why
//I am adding it's height to the dialog. It's quite ugly so please fix
//if you have something better in mind.
this.setSize(getWidth(), getHeight()+errorMessagePane.getHeight());
}
/**
* Dummy implementation that we are not using.
*
* @param escaped unused
*/
@Override
protected void close(boolean escaped) {}
}
/**
* A custom cell renderer used in the cell containing the
* StunServer instance.
*/
private static class StunServerCellRenderer
extends DefaultTableCellRenderer
{
// We need a place to store the color the JLabel should be returned
// to after its foreground and background colors have been set
// to the selection background color.
// These vars will be made protected when their names are finalized.
/** the fore ground color to use when not selected */
private Color unselectedForeground;
/** the fore ground color to use when selected */
private Color unselectedBackground;
/**
* Overrides JComponent.setForeground
to assign
* the unselected-foreground color to the specified color.
*
* @param c set the foreground color to this value
*/
public void setForeground(Color c)
{
super.setForeground(c);
unselectedForeground = c;
}
/**
* Overrides JComponent.setBackground
to assign
* the unselected-background color to the specified color.
*
* @param c set the background color to this value
*/
public void setBackground(Color c)
{
super.setBackground(c);
unselectedBackground = c;
}
/**
* Returns a cell renderer for the specified cell.
*
* @param table the {@link JTable} that the cell belongs to.
* @param value the cell value
* @param isSelected indicates whether the cell is selected
* @param hasFocus indicates whether the cell is currently on focus.
* @param row the row index
* @param column the column index
*
* @return the cell renderer
*/
public Component getTableCellRendererComponent( JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
{
if (value instanceof StunServerDescriptor)
{
StunServerDescriptor stunServer = (StunServerDescriptor) value;
this.setText( stunServer.getAddress()
+ "/" + stunServer.getPort());
if (isSelected)
{
super.setForeground(table.getSelectionForeground());
super.setBackground(table.getSelectionBackground());
}
else
{
super.setForeground((unselectedForeground != null)
? unselectedForeground
: table.getForeground());
super.setBackground((unselectedBackground != null)
? unselectedBackground
: table.getBackground());
}
}
else
return super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
return this;
}
}
/**
* A custom table model, with a non editable cells and a custom class column
* objects.
*/
private class StunServerTableModel
extends DefaultTableModel
{
/**
* Returns the class of the objects contained in the column given by
* the index. The class is used to distinguish which renderer should be
* used.
*
* @param columnIndex the column being queried
* @return the class of objects contained in the column
*/
public Class> getColumnClass(int columnIndex)
{
return getValueAt(0, columnIndex).getClass();
}
/**
* Returns false to indicate that none of the columns is
* editable.
*
* @param row the row whose value is to be queried
* @param column the column whose value is to be queried
* @return false
*/
public boolean isCellEditable(int row, int column)
{
return false;
}
}
/**
* Indicates if ice should be used for this account.
* @return true if ICE should be used for this account, otherwise
* returns false
*/
boolean isUseIce()
{
return iceBox.isSelected();
}
/**
* Sets the useIce property.
* @param isUseIce true to indicate that ICE should be used for
* this account, false - otherwise.
*/
void setUseIce(boolean isUseIce)
{
iceBox.setSelected(isUseIce);
}
/**
* Indicates if the stun server should be automatically discovered.
* @return true if the stun server should be automatically
* discovered, otherwise returns false.
*/
boolean isAutoDiscoverStun()
{
return autoDiscoverBox.isSelected();
}
/**
* Sets the autoDiscoverStun property.
* @param isAutoDiscover true to indicate that stun server should
* be auto-discovered, false - otherwise.
*/
void setAutoDiscoverStun(boolean isAutoDiscover)
{
autoDiscoverBox.setSelected(isAutoDiscover);
}
/**
* Returns the list of additional stun servers entered by the user.
*
* @return the list of additional stun servers entered by the user
*/
@SuppressWarnings("unchecked")//getDataVector() is simply not parametrized
List getAdditionalStunServers()
{
LinkedList serversList
= new LinkedList();
Iterator> i
= tableModel.getDataVector().iterator();
while(i.hasNext())
{
Vector row = i.next();
serversList.add(row.elementAt(0));
}
return serversList;
}
/**
* Adds the given stunServer to the list of additional stun
* servers.
* @param stunServer the stun server to add
*/
void addStunServer(StunServerDescriptor stunServer)
{
tableModel.addRow(new Object[]{stunServer,
stunServer.isTurnSupported()});
}
/**
* Indicates if a stun server with the given address and
* port already exists in the additional stun servers table.
*
* @param address the STUN server address to check
* @param port the STUN server port to check
*
* @return true if a STUN server with the given address
* and port already exists in the table, otherwise returns
* false
*/
boolean containsStunServer(String address, int port)
{
for (int i = 0; i < tableModel.getRowCount(); i++)
{
StunServerDescriptor stunServer
= (StunServerDescriptor) tableModel.getValueAt(i, 0);
if (stunServer.getAddress().equalsIgnoreCase(address)
&& stunServer.getPort() == port)
return true;
}
return false;
}
/**
* The input verifier that we use to verify port numbers.
*/
private static class PortVerifier extends InputVerifier
{
/**
* Checks whether the JComponent's input is a valid port number. This
* has no side effects. It returns a boolean indicating
* the status of the argument's input.
*
* @param input the JComponent to verify
* @return true when valid, false when invalid or when
* input is not a {@link JTextField} instance.
*/
public boolean verify(JComponent input)
{
if ( !( input instanceof JTextField ) )
return false;
JTextField portField = (JTextField)input;
String portStr = portField.getText();
int port = -1;
//we accept empty strings as that would mean the default port.
if( portStr== null || portStr.trim().length() == 0)
return true;
try
{
port = Integer.parseInt(portStr);
}
catch(Throwable t)
{
//something's wrong and whatever it is - we don't care we
//simply return false.
return false;
}
return NetworkUtils.isValidPortNumber(port);
}
}
}