package org.limewire.ui.swing.properties;
import static org.limewire.ui.swing.util.I18n.tr;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.application.Resource;
import org.limewire.bittorrent.Torrent;
import org.limewire.bittorrent.TorrentPeer;
import org.limewire.bittorrent.TorrentStatus;
import org.limewire.core.api.FilePropertyKey;
import org.limewire.core.api.download.DownloadItem;
import org.limewire.core.api.library.PropertiableFile;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.ui.swing.components.decorators.TableDecorator;
import org.limewire.ui.swing.properties.FileInfoDialog.FileInfoType;
import org.limewire.ui.swing.table.DefaultLimeTableCellRenderer;
import org.limewire.ui.swing.table.StripedJXTable;
import org.limewire.ui.swing.util.FontUtils;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.ui.swing.util.I18n;
import org.limewire.util.StringUtils;
public class FileInfoTransfersPanel implements FileInfoPanel {
@Resource private Icon lockIcon;
@Resource private Color foreground;
@Resource private Font smallFont;
private final JPanel component;
private DownloadItem download = null;
private DownloadStatusListener downloadStatus;
private final StripedJXTable infoTable;
private final FileInfoTableModel infoModel;
private final Timer refreshTimer;
private final JLabel leechersLabel;
private final JLabel seedersLabel;
private Torrent torrent = null;
public FileInfoTransfersPanel(FileInfoType type, PropertiableFile file, TableDecorator tableDecorator) {
Torrent localTorrent = (Torrent)file.getProperty(FilePropertyKey.TORRENT);
if(localTorrent != null) {
torrent = localTorrent;
}
if(file instanceof DownloadItem) {
download = (DownloadItem) file;
}
GuiUtils.assignResources(this);
component = new JPanel(new MigLayout("fillx, gap 0"));
infoModel = new FileInfoTableModel();
infoTable = new StripedJXTable(infoModel);
tableDecorator.decorate(infoTable);
infoTable.setCellSelectionEnabled(false);
infoTable.setShowGrid(false, false);
infoTable.setEditable(false);
component.add(new JScrollPane(infoTable), "gaptop 10, span, grow, wrap");
String userAgent = file.getPropertyString(FilePropertyKey.USERAGENT);
if(!StringUtils.isEmpty(userAgent) && type == FileInfoType.UPLOADING_FILE) {
component.add(createBoldLabel(I18n.tr("Version")), "split 2, gaptop 10");
component.add(createPlainLabel(userAgent), "wrap");
}
if (download != null) {
component.add(createBoldLabel(I18n.tr("Total Completed:")), "split 2, gaptop 10");
JLabel percentLabel = createPlainLabel("");
component.add(percentLabel, "wrap");
downloadStatus = new DownloadStatusListener(percentLabel);
download.addPropertyChangeListener(downloadStatus);
}
leechersLabel = createPlainLabel("");
seedersLabel = createPlainLabel("");
if(torrent != null) {
component.add(createBoldLabel(I18n.tr("Total Leechers:")), "split 2");
component.add(leechersLabel, "wrap");
component.add(createBoldLabel(I18n.tr("Total Seeders:")), "split 2");
component.add(seedersLabel, "wrap");
}
initTable();
refreshTimer = new Timer(1500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
refresh();
}
});
// Update by polling to avoid putting too much of a burden on the ui
// with an active torrent.
refreshTimer.start();
refresh();
}
public JComponent getComponent() {
return component;
}
@Override
public boolean hasChanged() {
return false;
}
@Override
public void save() {
//component never changes state
}
@Override
public void updatePropertiableFile(PropertiableFile file) {
//do nothing
}
@Override
public void dispose() {
if(refreshTimer != null)
refreshTimer.stop();
if(downloadStatus != null) {
download.removePropertyChangeListener(downloadStatus);
}
}
private void initTable() {
TableColumn column = infoTable.getColumn(FileInfoTableModel.ENCRYPTED);
column.setCellRenderer(new LockRenderer());
column.setMaxWidth(12);
column.setMinWidth(12);
column.setWidth(12);
infoTable.setDefaultRenderer(String.class, new DefaultLimeTableCellRenderer());
infoTable.getColumnExt(FileInfoTableModel.IP).setComparator(IpPort.IP_COMPARATOR);
TableColumn ipColumn = infoTable.getColumn(FileInfoTableModel.IP);
ipColumn.setMinWidth(100);
ipColumn.setCellRenderer(new IPRenderer());
TableColumn uploadColumn = infoTable.getColumn(FileInfoTableModel.UPLOAD_SPEED);
uploadColumn.setCellRenderer(new SpeedRenderer());
TableColumn downloadColumn = infoTable.getColumn(FileInfoTableModel.DOWNLOAD_SPEED);
downloadColumn.setCellRenderer(new SpeedRenderer());
}
private void refresh() {
infoModel.clear();
// Add leecher/seeder info if BT
if (torrent != null) {
infoModel.addAll(torrent.getTorrentPeers());
TorrentStatus status = torrent.getStatus();
seedersLabel.setText((status.getNumComplete() < 0) ? "?" : (""+status.getNumComplete()));
leechersLabel.setText((status.getNumIncomplete() < 0) ? "?" : (""+status.getNumIncomplete()));
}
}
private JLabel createBoldLabel(String text) {
JLabel label = createPlainLabel(text);
FontUtils.bold(label);
return label;
}
private JLabel createPlainLabel(String text) {
JLabel label = new JLabel(text);
label.setFont(smallFont);
label.setForeground(foreground);
return label;
}
/**
* Listens for changes to the download status and updates the dialog.
*/
private class DownloadStatusListener implements PropertyChangeListener {
private final JLabel label;
public DownloadStatusListener(JLabel label) {
this.label = label;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
label.setText(tr("{0}%", download.getPercentComplete()));
}
});
}
}
private class LockRenderer extends DefaultLimeTableCellRenderer {
public LockRenderer() {
setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (value == Boolean.TRUE) {
setIcon(lockIcon);
} else {
setIcon(null);
}
return this;
}
}
private static class FileInfoTableModel extends AbstractTableModel {
public static final int IP = 0;
public static final int ENCRYPTED = 1;
public static final int CLIENT_NAME = 2;
public static final int UPLOAD_SPEED = 3;
public static final int DOWNLOAD_SPEED = 4;
private List<TorrentPeer> sources = new ArrayList<TorrentPeer>();
private String[] columnNames = new String[]{tr("Address"),
"", tr("Client"), tr("Upload"), tr("Download")};
public void clear(){
sources.clear();
}
public void addAll(Collection<TorrentPeer> info){
sources.addAll(info);
fireTableDataChanged();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(int column) {
if(column < columnNames.length){
return columnNames[column];
}
return null;
}
@Override
public int getRowCount() {
return sources.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex >= getRowCount()){
return null;
}
return getColumnValue(sources.get(rowIndex), columnIndex);
}
private Object getColumnValue(TorrentPeer info, int column) {
switch (column){
case IP:
try {
return new IpPortImpl(info.getIPAddress(), 0);
} catch (UnknownHostException e) {
// This only thrown when no IP address can be found for a host or and with global
//IPv6 addresses that have a scope_id.
return null;
}
case ENCRYPTED:
return info.isEncrypted();
case CLIENT_NAME:
return info.getClientName();
case UPLOAD_SPEED:
return Long.valueOf(Math.round(info.getUploadSpeed()));
case DOWNLOAD_SPEED:
return Long.valueOf(Math.round(info.getDownloadSpeed()));
}
return null;
}
}
private static class SpeedRenderer extends DefaultLimeTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (value != null){
value = GuiUtils.formatUnitFromBytesPerSec((Long)value);
}
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
private static class IPRenderer extends DefaultLimeTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (value != null){
value = ((IpPort)value).getAddress();
}
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}