package com.limegroup.gnutella.gui.connection;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import com.limegroup.gnutella.ManagedConnection;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.gui.AutoCompleteTextField;
import com.limegroup.gnutella.gui.ClearableAutoCompleteTextField;
import com.limegroup.gnutella.gui.GUIMediator;
import com.limegroup.gnutella.gui.GUIUtils;
import com.limegroup.gnutella.gui.PaddedPanel;
import com.limegroup.gnutella.gui.WholeNumberField;
import com.limegroup.gnutella.gui.tables.AbstractTableMediator;
import com.limegroup.gnutella.gui.tables.LimeJTable;
import com.limegroup.gnutella.gui.tables.TableSettings;
import com.limegroup.gnutella.gui.themes.ThemeMediator;
import com.limegroup.gnutella.util.NetworkUtils;
/**
* This class acts as a mediator between all of the components of the
* connection window.
*/
public final class ConnectionMediator extends AbstractTableMediator {
public static ConnectionMediator instance() { return INSTANCE; }
/**
* Listeners so buttons and possibly future right-click menu share.
*/
ActionListener ADD_LISTENER;
/**
* Listeners so buttons and possibly future right-click menu share.
*/
ActionListener BROWSE_HOST_LISTENER;
private static final String IS_ULTRAPEER =
GUIMediator.getStringResource("CV_STRING_ULTRAPEER");
private static final String IS_LEAF =
GUIMediator.getStringResource("CV_STRING_CLIENT");
private static final String CONNECTING =
GUIMediator.getStringResource("CV_TABLE_STRING_CONNECTINGS");
private static final String LEAVES =
GUIMediator.getStringResource("CV_TABLE_STRING_LEAVES");
private static final String ULTRAPEERS =
GUIMediator.getStringResource("CV_TABLE_STRING_ULTRAPEERS");
private static final String PEERS =
GUIMediator.getStringResource("CV_TABLE_STRING_PEERS");
private static final String STANDARD =
GUIMediator.getStringResource("CV_TABLE_STRING_STANDARDS");
/**
* Instance of singleton access
*/
private static final ConnectionMediator INSTANCE =
new ConnectionMediator();
/**
* Extra component constants
*/
private JLabel SERVENT_STATUS;
/**
* The label displaying the number of ultrapeers, peers & leaves.
*/
private JLabel NEIGHBORS;
/**
* Build the listeners
*/
protected void buildListeners() {
super.buildListeners();
ADD_LISTENER = new AddListener();
BROWSE_HOST_LISTENER = new ActionListener() {
public void actionPerformed(ActionEvent e) {
doBrowseHost();
}
};
}
/**
* Overriden to have different default values for tooltips.
*/
protected void buildSettings() {
SETTINGS = new TableSettings(ID) {
public boolean getDefaultTooltips() {
return false;
}
};
}
/**
* Add the listeners
*/
protected void addListeners() {
super.addListeners();
}
/**
* Set up the necessary constants.
*/
protected void setupConstants() {
// Create padded panel without bottom padding so that button
// rows for all the tabs line up.
MAIN_PANEL = new PaddedPanel();
DATA_MODEL = new ConnectionModel();
TABLE = new LimeJTable(DATA_MODEL);
BUTTON_ROW = (new ConnectionButtons(this)).getComponent();
SERVENT_STATUS = new JLabel("");
NEIGHBORS = new JLabel("");
}
/**
* Overridden to set the size.
*/
protected JComponent getScrolledTablePane() {
JComponent pane = super.getScrolledTablePane();
SCROLL_PANE.setPreferredSize(new Dimension(3000, 5000));
return pane;
}
/**
* Update the splash screen
*/
protected void updateSplashScreen() {
GUIMediator.setSplashScreenString(
GUIMediator.getStringResource("SPLASH_STATUS_CONNECTION_WINDOW"));
}
/**
* Override the default main panel setup
*/
protected void setupMainPanel() {
if (MAIN_PANEL == null)
return;
super.setupMainPanel();
JPanel status = new JPanel();
status.setLayout(new BorderLayout());
status.add(SERVENT_STATUS, BorderLayout.WEST);
status.add(NEIGHBORS, BorderLayout.EAST);
MAIN_PANEL.add(status, 0);
}
/**
* Constructor -- private for Singleton access
*/
private ConnectionMediator() {
super("CONNECTION_TABLE");
GUIMediator.addRefreshListener(this);
ThemeMediator.addThemeObserver(this);
doRefresh();
}
/**
* Removes all selected rows from Router,
* which will in turn remove it from the list.
* Overrides default removeSelection
*/
public void removeSelection() {
int[] sel = TABLE.getSelectedRows();
Arrays.sort(sel);
ManagedConnection c;
for( int counter = sel.length - 1; counter >= 0; counter--) {
int i = sel[counter];
c = (ManagedConnection)DATA_MODEL.get(i).getInitializeObject();
RouterService.removeConnection(c);
}
clearSelection();
}
/**
* Returns the JPopupMenu for the connection table
*/
protected JPopupMenu createPopupMenu() {
JPopupMenu jpm = new JPopupMenu();
// add
JMenuItem jmi = new JMenuItem(GUIMediator
.getStringResource("CV_BUTTON_ADD"));
jmi.addActionListener(ADD_LISTENER);
jpm.add(jmi);
jpm.addSeparator();
// remove
jmi = new JMenuItem(GUIMediator.getStringResource("CV_BUTTON_REMOVE"));
jmi.addActionListener(REMOVE_LISTENER);
jpm.add(jmi);
jpm.addSeparator();
// browse host
jmi = new JMenuItem(GUIMediator
.getStringResource("GENERAL_BROWSE_HOST_LABEL"));
jmi.addActionListener(BROWSE_HOST_LISTENER);
jpm.add(jmi);
return jpm;
}
/**
* Handles the selection of the specified row in the connection window,
* enabling or disabling buttons
*
* @param row the selected row
*/
public void handleSelection(int row) {
setButtonEnabled( ConnectionButtons.REMOVE_BUTTON, true );
setButtonEnabled( ConnectionButtons.BROWSE_HOST_BUTTON, true );
}
/**
* Handles the deselection of all rows in the download table,
* disabling all necessary buttons and menu items.
*/
public void handleNoSelection() {
setButtonEnabled( ConnectionButtons.REMOVE_BUTTON, false );
setButtonEnabled( ConnectionButtons.BROWSE_HOST_BUTTON, false );
}
public void handleActionKey() {
doBrowseHost();
}
/**
* get the first selected row and trigger a browse host
*/
private void doBrowseHost() {
int[] rows = TABLE.getSelectedRows();
if(rows.length > 0) {
ManagedConnection c =
(ManagedConnection)DATA_MODEL.get(rows[0]).getInitializeObject();
GUIMediator.instance().doBrowseHost(c.getAddress(), c.getPort());
}
}
/**
* Override the default doRefresh so we can update the servent status label
* (Uses doRefresh instead of refresh so this will only get called
* when the table is showing. Small optimization.)
*/
public void doRefresh() {
super.doRefresh();
SERVENT_STATUS.setText(GUIMediator.getStringResource("CV_STRING_STATUS") + " " +
( RouterService.isSupernode() ?
IS_ULTRAPEER : IS_LEAF ) + " "
);
int[] counts = ((ConnectionModel)DATA_MODEL).getConnectionInfo();
NEIGHBORS.setText("(" +
counts[1] + " " + ULTRAPEERS + ", " +
counts[2] + " " + PEERS + ", " +
counts[3] + " " + LEAVES + ", " +
counts[0] + " " + CONNECTING + ", " +
counts[4] + " " + STANDARD + ")");
}
/**
* Determines the number of connections that are in connecting state.
*/
public int getConnectingCount() {
return ((ConnectionModel)DATA_MODEL).getConnectingCount();
}
private void tryConnection(final String hostname, final int portnum) {
GUIMediator.instance().schedule(new Runnable() {
public void run() {
RouterService.connectToHostAsynchronously(hostname, portnum);
}
});
}
/**
* Clear the connections visually
*/
public void clearConnections() {
DATA_MODEL.clear();
}
/**
* Adds the host & port to the dictionary of the HOST_INPUT
*/
void addKnownHost( String host, int port ) {
//HOST_INPUT.addToDictionary( host + ":" + port );
}
/**
* First attempts to parse out the ':' from the host.
* If one exists, it replaces the text in PORT_INPUT.
* Otherwise, it uses the text in port input.
*/
private final class AddListener implements ActionListener {
private JDialog dialog = null;
private AutoCompleteTextField HOST_INPUT = new ClearableAutoCompleteTextField(20);
private JTextField PORT_INPUT = new WholeNumberField(6346, 4);
private JButton OK_BUTTON = new JButton(GUIMediator.getStringResource("GENERAL_OK_BUTTON_LABEL"));
private JButton CANCEL_BUTTON = new JButton(GUIMediator.getStringResource("GENERAL_CANCEL_BUTTON_LABEL"));
private void createDialog() {
if(dialog != null) return;
// 1. create modal dialog
// Host: [ ]
// Port: [ ]
// [ OK ] [ Cancel ]
dialog = new JDialog(GUIMediator.getAppFrame(),
GUIMediator.getStringResource("CV_ADD_DIALOG_TITLE"),
true);
JPanel jp = (JPanel)dialog.getContentPane();
GUIUtils.addHideAction(jp);
jp.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// space between title bar and Host line
gbc.gridwidth = 5;
gbc.gridheight = 1;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 0;
jp.add(getHorizontalSpacer(), gbc);
// lefthand side space
gbc.gridwidth = 1;
gbc.gridheight = 5;
gbc.weightx = 0;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.gridx = 0;
gbc.gridy = 1;
jp.add(getVerticalSpacer(), gbc);
// righthand side space
gbc.gridwidth = 1;
gbc.gridheight = 5;
gbc.weightx = 0;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.gridx = 4;
gbc.gridy = 1;
jp.add(getVerticalSpacer(), gbc);
// bottom spacer between buttons and window border
gbc.gridwidth = 5;
gbc.gridheight = 1;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 6;
jp.add(getHorizontalSpacer(), gbc);
// host label
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridx = 1;
gbc.gridy = 1;
jp.add(new JLabel(GUIMediator.getStringResource("CV_ADD_HOST_LABEL")), gbc);
// host label <-> host input field spacer
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridx = 2;
gbc.gridy = 1;
jp.add(getVerticalSpacer(), gbc);
// host input field
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 3;
gbc.gridy = 1;
jp.add(HOST_INPUT, gbc);
// host <-> port spacer
gbc.gridwidth = 3;
gbc.gridheight = 1;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 1;
gbc.gridy = 2;
jp.add(getHorizontalSpacer(), gbc);
// port label
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridx = 1;
gbc.gridy = 3;
jp.add(new JLabel(GUIMediator.getStringResource("CV_ADD_PORT_LABEL")), gbc);
// port label <-> port input field spacer
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridx = 2;
gbc.gridy = 3;
jp.add(getVerticalSpacer(), gbc);
// port input field
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 3;
gbc.gridy = 3;
jp.add(PORT_INPUT, gbc);
// port <-> buttons spacer
gbc.gridwidth = 3;
gbc.gridheight = 1;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 1;
gbc.gridy = 4;
jp.add(getHorizontalSpacer(), gbc);
// buttons
JPanel buttons = new JPanel();
OK_BUTTON.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
String hostnamestr = HOST_INPUT.getText();
String portstr = PORT_INPUT.getText();
// look for the port in the host
int idx = hostnamestr.lastIndexOf(':');
// if it exists, rewrite the host & port
if ( idx != -1 ) {
PORT_INPUT.setText( hostnamestr.substring(idx+1) );
portstr = PORT_INPUT.getText();
HOST_INPUT.setText( hostnamestr.substring(0, idx) );
hostnamestr = HOST_INPUT.getText();
}
int portnum = -1;
try {
portnum = Integer.parseInt(portstr);
} catch (NumberFormatException ee) {
portnum = 6346;
}
if(!NetworkUtils.isValidPort(portnum))
portnum = 6346;
PORT_INPUT.setText("" + portnum);
if ( !hostnamestr.equals("") ) {
tryConnection(hostnamestr, portnum);
dialog.setVisible(false);
dialog.dispose();
} else {
HOST_INPUT.requestFocus();
}
}
});
CANCEL_BUTTON.addActionListener(GUIUtils.getDisposeAction());
buttons.add(OK_BUTTON);
buttons.add(CANCEL_BUTTON);
gbc.gridwidth = 3;
gbc.gridheight = 1;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.EAST;
gbc.gridx = 1;
gbc.gridy = 5;
jp.add(buttons, gbc);
}
public void actionPerformed(ActionEvent e) {
if(dialog == null) createDialog();
// 2. display dialog centered (and modal)
dialog.getRootPane().setDefaultButton(OK_BUTTON);
dialog.pack();
dialog.setLocation(GUIMediator.getScreenCenterPoint(dialog));
dialog.setVisible(true);
}
/** Returns a vertical separator */
private Component getVerticalSpacer() {
return Box.createRigidArea(new Dimension(6,0));
}
/** Returns a horizontal separator */
private Component getHorizontalSpacer() {
return Box.createRigidArea(new Dimension(0,6));
}
}
}