package org.limewire.ui.swing.properties; import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import net.miginfocom.swing.MigLayout; import org.jdesktop.swingx.table.TableColumnExt; import org.limewire.bittorrent.Torrent; import org.limewire.bittorrent.TorrentTracker; import org.limewire.core.api.FilePropertyKey; import org.limewire.core.api.library.PropertiableFile; import org.limewire.ui.swing.components.TextFieldClipboardControl; import org.limewire.ui.swing.components.decorators.TableDecorator; import org.limewire.ui.swing.library.table.RemoveButton; import org.limewire.ui.swing.table.DefaultLimeTableCellRenderer; import org.limewire.ui.swing.table.StripedJXTable; import org.limewire.ui.swing.table.TableRendererEditor; import org.limewire.ui.swing.util.I18n; public class FileInfoTrackersPanel implements FileInfoPanel { private static final int URL_COLUMN = 0; private static final int TIER_COLUMN = 1; private static final int REMOVE_COLUMN = 2; private final Torrent torrent; private final JPanel component; private List<TorrentTracker> trackerList = null; private StripedJXTable table; public FileInfoTrackersPanel(PropertiableFile propertiableFile, TableDecorator tableDecorator) { component = new JPanel(new MigLayout("fillx, gap 0, insets 7 7 20 7")); torrent = (Torrent)propertiableFile.getProperty(FilePropertyKey.TORRENT); if(torrent == null) { table = null; return; } trackerList = torrent.getTrackers(); createTable(tableDecorator); component.add(new JScrollPane(table), "gaptop 10, gapbottom 6, span, grow, wrap"); if(torrent.isValid() && torrent.isEditable()) { JLabel addTrackerLabel = new JLabel(I18n.tr("Add Tracker:")); final JTextField trackerUrlTextField = new JTextField(100); JLabel tierLabel = new JLabel(I18n.tr("Tier:")); final JSpinner tierSpinner = new JSpinner(new SpinnerNumberModel(0,0,20,1)); final JLabel errorLabel = new JLabel(I18n.tr("Tracker URL Invalid")); errorLabel.setForeground(Color.RED); errorLabel.setVisible(false); JButton addButton = new JButton(new AbstractAction(I18n.tr("Add")) { @Override public void actionPerformed(ActionEvent event) { String url = trackerUrlTextField.getText().trim(); int tier = ((Integer)tierSpinner.getValue()).intValue(); if (addTracker(url, tier)) { refreshTable(); trackerUrlTextField.setText(""); tierSpinner.setValue(0); errorLabel.setVisible(false); } else { errorLabel.setVisible(true); } } }); TextFieldClipboardControl.install(trackerUrlTextField); JFormattedTextField tierSpinnerTextField = ((JSpinner.DefaultEditor)tierSpinner.getEditor()).getTextField(); tierSpinnerTextField.setEditable(false); component.add(addTrackerLabel, "gapright 5"); component.add(trackerUrlTextField, "growx, gapright 10"); component.add(tierLabel, "gapright 5"); component.add(tierSpinner, "gapright 10"); component.add(addButton, "wrap"); component.add(errorLabel, "hidemode 3"); } else { TableColumn removeColumn = table.getColumn(REMOVE_COLUMN); ((TableColumnExt)removeColumn).setVisible(false); } } private void createTable(TableDecorator tableDecorator) { AbstractTableModel model = new AbstractTableModel() { @Override public String getColumnName(int columnIndex) { if (columnIndex == URL_COLUMN) { return I18n.tr("URL"); } else if (columnIndex == TIER_COLUMN) { return I18n.tr("Tier"); } return ""; } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == URL_COLUMN) { return trackerList.get(rowIndex).getURI(); } else if (columnIndex == TIER_COLUMN) { return trackerList.get(rowIndex).getTier(); } else if (columnIndex == REMOVE_COLUMN) { return rowIndex; } throw new IllegalArgumentException("Invalid Column Used"); } @Override public int getRowCount() { return trackerList.size(); } @Override public int getColumnCount() { return 3; } }; table = new StripedJXTable(model) { @Override public boolean isCellEditable(int row, int column) { return column == REMOVE_COLUMN; } }; tableDecorator.decorate(table); table.setCellSelectionEnabled(false); table.setShowGrid(false, false); table.setSortable(false); TableColumn tierColumn = table.getColumn(TIER_COLUMN); tierColumn.setMaxWidth(45); tierColumn.setMinWidth(25); tierColumn.setWidth(40); tierColumn.setPreferredWidth(40); TableColumn removeColumn = table.getColumn(REMOVE_COLUMN); removeColumn.setCellRenderer(new RemoveRendererEditor()); removeColumn.setCellEditor(new RemoveRendererEditor()); removeColumn.setMaxWidth(12); removeColumn.setMinWidth(12); removeColumn.setWidth(12); removeColumn.setPreferredWidth(12); DefaultLimeTableCellRenderer tierRenderer = new DefaultLimeTableCellRenderer(); tierRenderer.setHorizontalAlignment(JLabel.CENTER); tierColumn.setCellRenderer(tierRenderer); } @Override public JComponent getComponent() { return component; } @Override public boolean hasChanged() { return false; } @Override public void save() { } @Override public void updatePropertiableFile(PropertiableFile file) { } @Override public void dispose() { } /** * Pulls new tracker data and refreshes the model. */ private void refreshTable() { trackerList = torrent.getTrackers(); table.repaint(); } /** * Attempts to add a tracker. * * @return true if the add was successful and the tracker was valid. */ private boolean addTracker(String url, int tier) { boolean duplicate = false; for ( TorrentTracker tracker : trackerList ) { if (tracker.getURI().equals(url)) { duplicate = true; break; } } if (!duplicate) { try { new URL(url); torrent.addTracker(url, tier); return true; } catch (MalformedURLException e) { } } return false; } /** * Removes the tracker at the given row from the model. * * @return true if a row was actually removed. */ private boolean removeTracker(int row) { if (row == 0) { return false; } TorrentTracker tracker = trackerList.get(row); if (tracker != null) { torrent.removeTracker(tracker.getURI().toASCIIString(), tracker.getTier()); return true; } return false; } private class RemoveRendererEditor extends TableRendererEditor { private int currentRow = -1; private final RemoveButton button; public RemoveRendererEditor() { button = new RemoveButton(); button.removeActionHandListener(); button.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (removeTracker(currentRow)) { cancelCellEditing(); refreshTable(); } } }); add(button); } @Override protected Component doTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { if (value != null) { currentRow = ((Integer)value).intValue(); button.setVisible(currentRow != 0); } return this; } @Override protected Component doTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value != null) { int currentRow = ((Integer)value).intValue(); button.setVisible(currentRow != 0); } return this; } } }