package org.limewire.ui.swing.options; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.ArrayList; import java.util.Enumeration; import java.util.EventObject; import java.util.List; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import net.miginfocom.swing.MigLayout; import org.jdesktop.swingx.JXTable; import org.limewire.bittorrent.TorrentManager; import org.limewire.bittorrent.TorrentManagerSettings; import org.limewire.bittorrent.TorrentSettingsAnnotation; import org.limewire.core.settings.ConnectionSettings; import org.limewire.ui.swing.table.AbstractTableFormat; import org.limewire.ui.swing.util.BackgroundExecutorService; import org.limewire.ui.swing.util.I18n; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.swing.DefaultEventTableModel; import com.google.inject.Inject; import com.google.inject.Provider; /** * Network Interface Option View. */ public class NetworkInterfaceOptionPanel extends OptionPanel { private ButtonGroup buttonGroup; private JRadioButton limewireChooseRadioButton; private JRadioButton meChooseRadioButton; private NetworkTable table; private JScrollPane scrollPane; private NetworkItem selectedItem; private EventList<NetworkItem> eventList; private final Provider<TorrentManager> torrentManager; private final TorrentManagerSettings torrentSettings; @Inject public NetworkInterfaceOptionPanel(Provider<TorrentManager> torrentManager, @TorrentSettingsAnnotation TorrentManagerSettings torrentSettings) { this.torrentManager = torrentManager; this.torrentSettings = torrentSettings; setLayout(new MigLayout("insets 15, fillx, wrap")); setOpaque(false); add(getNetworkPanel(), "pushx, growx"); } private JPanel getNetworkPanel() { JPanel p = new JPanel(); p.setBorder(BorderFactory.createTitledBorder("")); p.setLayout(new MigLayout("fillx, gapy 10")); p.setOpaque(false); limewireChooseRadioButton = new JRadioButton(I18n.tr("Let LimeWire choose my network interface (Recommended)")); meChooseRadioButton = new JRadioButton(I18n.tr("Let me choose a specific network interface")); limewireChooseRadioButton.setOpaque(false); meChooseRadioButton.setOpaque(false); buttonGroup = new ButtonGroup(); buttonGroup.add(limewireChooseRadioButton); buttonGroup.add(meChooseRadioButton); eventList = GlazedLists.threadSafeList(new BasicEventList<NetworkItem>()); table = new NetworkTable(new DefaultEventTableModel<NetworkItem>(eventList, new NetworkTableFormat())); scrollPane = new JScrollPane(table); scrollPane.setVisible(false); meChooseRadioButton.addItemListener(new RadioListener(scrollPane, meChooseRadioButton)); p.add(limewireChooseRadioButton, "wrap"); p.add(meChooseRadioButton, "wrap"); p.add(scrollPane, "grow, span"); return p; } @Override ApplyOptionResult applyOptions() { boolean customBefore = ConnectionSettings.CUSTOM_NETWORK_INTERFACE.getValue(); boolean customNow = meChooseRadioButton.isSelected(); ConnectionSettings.CUSTOM_NETWORK_INTERFACE.setValue(customNow); String addressBefore = ConnectionSettings.CUSTOM_INETADRESS.get(); String addressNow = null; for(NetworkItem item : eventList) { if(item.isSelected()) { addressNow = item.getAddress(); ConnectionSettings.CUSTOM_INETADRESS.set(addressNow); break; } } boolean restart = customBefore != customNow || !addressBefore.equals(addressNow); if(torrentManager.get().isInitialized() && torrentManager.get().isValid()) { BackgroundExecutorService.execute(new Runnable() { @Override public void run() { torrentManager.get().setTorrentManagerSettings(torrentSettings); } }); } return new ApplyOptionResult(restart, true); } @Override boolean hasChanged() { if(!ConnectionSettings.CUSTOM_NETWORK_INTERFACE.getValue()) return !limewireChooseRadioButton.isSelected(); String expect = ConnectionSettings.CUSTOM_INETADRESS.get(); for(NetworkItem item : eventList) { if(expect.equals(item.getAddress())); return false; } return true; } @Override public void initOptions() { eventList.clear(); limewireChooseRadioButton.setSelected(!ConnectionSettings.CUSTOM_NETWORK_INTERFACE.getValue()); meChooseRadioButton.setSelected(ConnectionSettings.CUSTOM_NETWORK_INTERFACE.getValue()); try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); // Add the available interfaces / addresses while(interfaces.hasMoreElements()) { NetworkInterface ni = interfaces.nextElement(); Enumeration<InetAddress> addresses = ni.getInetAddresses(); while(addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if(address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress()) continue; NetworkItem networkItem = new NetworkItem(address, ni.getDisplayName()); if(ConnectionSettings.CUSTOM_INETADRESS.get().equals(address.getHostAddress())) { networkItem.setSelected(true); selectedItem = networkItem; } eventList.add(networkItem); } } if(selectedItem == null && eventList.size() > 0) { eventList.get(0).setSelected(true); selectedItem = eventList.get(0); } } catch (SocketException e) { meChooseRadioButton.setEnabled(false); } if(eventList.size() == 0 ) meChooseRadioButton.setEnabled(false); } private static class RadioListener implements ItemListener { private JScrollPane scrollPane; private JRadioButton radioButton; public RadioListener(JScrollPane scrollPane, JRadioButton button) { this.scrollPane = scrollPane; this.radioButton = button; } @Override public void itemStateChanged(ItemEvent e) { scrollPane.setVisible(radioButton.isSelected()); } } private class NetworkTable extends JXTable { public NetworkTable(DefaultEventTableModel<NetworkItem> model) { super(model); setShowGrid(false, false); setColumnSelectionAllowed(false); getColumn(0).setCellRenderer(new CheckBoxRenderer()); getColumn(0).setCellEditor(new CheckBoxRenderer()); getColumn(0).setMaxWidth(30); } @Override public boolean isCellEditable(int row, int col) { if (row >= getRowCount() || col >= getColumnCount() || row < 0 || col < 0) { return false; } return getColumnModel().getColumn(col).getCellEditor() != null; } } private static class NetworkTableFormat extends AbstractTableFormat<NetworkItem> { private static final int CHECK_INDEX = 0; private static final int ADDRESS_INDEX = 1; private static final int NAME_INDEX = 2; public NetworkTableFormat() { super("", I18n.tr("Address"), I18n.tr("Name")); } @Override public Object getColumnValue(NetworkItem baseObject, int column) { switch(column) { case CHECK_INDEX: return baseObject; case ADDRESS_INDEX: return baseObject.getAddress(); case NAME_INDEX: return baseObject.getName(); } throw new IllegalStateException("Unknown column:" + column); } } private static class NetworkItem { private boolean isSelected = false; private InetAddress address; private String displayName; public NetworkItem(InetAddress address, String displayName) { this.address = address; this.displayName = displayName; } public String getAddress() { return address.getHostAddress(); } public String getName() { return displayName; } public boolean isSelected() { return isSelected; } public void setSelected(boolean value) { this.isSelected = value; } } private class CheckBoxRenderer extends JRadioButton implements TableCellRenderer, TableCellEditor { private final List<CellEditorListener> listeners = new ArrayList<CellEditorListener>(); public CheckBoxRenderer() { addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { NetworkItem currentItem = eventList.get(table.getSelectedRow()); if(!currentItem.getAddress().equals(selectedItem.getAddress())) { selectedItem.setSelected(false); selectedItem = currentItem; } currentItem.setSelected(true); cancelCellEditing(); table.repaint(); } }); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if(value instanceof NetworkItem) { NetworkItem item = (NetworkItem) value; this.setSelected(item.isSelected); } else { this.setSelected(false); } if(isSelected) setBackground(table.getSelectionBackground()); else setBackground(table.getBackground()); return this; } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { return getTableCellRendererComponent(table, value, true, true, row, column); } @Override public void cancelCellEditing() { synchronized (listeners) { for (int i=0, N=listeners.size(); i<N; i++) { listeners.get(i).editingCanceled(new ChangeEvent(this)); } } } @Override public Object getCellEditorValue() { return null; } @Override public boolean isCellEditable(EventObject anEvent) { return true; } @Override public void removeCellEditorListener(CellEditorListener lis) { synchronized (listeners) { if (listeners.contains(lis)) listeners.remove(lis); } } @Override public void addCellEditorListener(CellEditorListener lis) { synchronized (listeners) { if (!listeners.contains(lis)) listeners.add(lis); } } @Override public boolean shouldSelectCell(EventObject anEvent) { return true; } @Override public boolean stopCellEditing() { cancelCellEditing(); return true; } } }