package org.limewire.ui.swing.properties;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.application.Resource;
import org.limewire.bittorrent.Torrent;
import org.limewire.bittorrent.TorrentFileEntry;
import org.limewire.ui.swing.table.AbstractTableFormat;
import org.limewire.ui.swing.table.MouseableTable;
import org.limewire.ui.swing.util.FontUtils;
import org.limewire.ui.swing.util.GuiUtils;
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;
public class FileInfoBittorrentPanel implements FileInfoPanel {
private static final int DONT_DOWNLOAD = 0;
private static final int LOWEST_PRIORITY = 1;
private static final int NORMAL_PRIORITY = 2;
private static final int HIGHEST_PRIORITY = 3;
private final Torrent torrent;
private final JPanel component;
private BitTorrentTable table;
private EventList<TorrentFileEntryWrapper> eventList;
public FileInfoBittorrentPanel(Torrent torrent) {
this.torrent = torrent;
component = new JPanel(new MigLayout("fill"));
init();
}
@Override
public JComponent getComponent() {
return component;
}
private void init() {
component.setOpaque(false);
eventList = GlazedLists.threadSafeList(new BasicEventList<TorrentFileEntryWrapper>());
List<TorrentFileEntry> fileEntries = torrent.getTorrentFileEntries();
for(TorrentFileEntry entry : fileEntries) {
eventList.add(new TorrentFileEntryWrapper(entry));
}
table = new BitTorrentTable(new DefaultEventTableModel<TorrentFileEntryWrapper>(eventList, new BitTorrentTableFormat()));
component.add(new JScrollPane(table), "grow");
}
@Override
public boolean hasChanged() {
boolean hasChanged = false;
for(TorrentFileEntryWrapper wrapper : eventList) {
if(wrapper.getPriority() != wrapper.getTorrentFileEntry().getPriority()) {
hasChanged = true;
break;
}
}
return hasChanged;
}
@Override
public void save() {
if(hasChanged()) {
for(TorrentFileEntryWrapper wrapper : eventList) {
torrent.setTorrenFileEntryPriority(wrapper.getTorrentFileEntry(), wrapper.getPriority());
}
}
}
@Override
public void unregisterListeners() {
//no listeners registered
}
/**
* We need this because we don't want to update the real entries
* if the user hits the cancel button
*/
private class TorrentFileEntryWrapper {
private final TorrentFileEntry entry;
private int priority;
public TorrentFileEntryWrapper(TorrentFileEntry entry) {
this.entry = entry;
this.priority = entry.getPriority();
}
public String getPath() {
return entry.getPath();
}
public float getProgress() {
return entry.getProgress();
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public TorrentFileEntry getTorrentFileEntry() {
return entry;
}
}
private class BitTorrentTable extends MouseableTable {
public BitTorrentTable(DefaultEventTableModel<TorrentFileEntryWrapper> model) {
super(model);
setShowHorizontalLines(false);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setColumnSelectionAllowed(false);
final CheckBoxRendererEditor checkBoxEditor = new CheckBoxRendererEditor();
checkBoxEditor.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
if(checkBoxEditor.getCellEditorValue() != null) {
checkBoxEditor.getCellEditorValue().setPriority(checkBoxEditor.isSelected() ? 1 : 0);
checkBoxEditor.cancelCellEditing();
}
BitTorrentTable.this.repaint();
}
});
getColumn(BitTorrentTableFormat.DOWNLOAD_INDEX).setCellRenderer(new CheckBoxRendererEditor());
getColumn(BitTorrentTableFormat.DOWNLOAD_INDEX).setCellEditor(checkBoxEditor);
getColumn(BitTorrentTableFormat.PERCENT_INDEX).setCellRenderer(new PercentRenderer());
final PriorityRendererEditor editor = new PriorityRendererEditor();
editor.getButton().addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
int oldPriority = editor.getCellEditorValue().getPriority();
if(oldPriority != DONT_DOWNLOAD) {
editor.getCellEditorValue().setPriority(oldPriority + 1 > HIGHEST_PRIORITY ? LOWEST_PRIORITY : (oldPriority + 1));
editor.cancelCellEditing();
BitTorrentTable.this.repaint();
}
}
});
getColumn(BitTorrentTableFormat.PRIORITY_INDEX).setCellRenderer(new PriorityRendererEditor());
getColumn(BitTorrentTableFormat.PRIORITY_INDEX).setCellEditor(editor);
getColumnExt(BitTorrentTableFormat.DOWNLOAD_INDEX).setMaxWidth(30);
getColumnExt(BitTorrentTableFormat.DOWNLOAD_INDEX).setMinWidth(30);
getColumnExt(BitTorrentTableFormat.PERCENT_INDEX).setMaxWidth(50);
getColumnExt(BitTorrentTableFormat.PERCENT_INDEX).setMinWidth(50);
getColumnExt(BitTorrentTableFormat.PRIORITY_INDEX).setMaxWidth(60);
getColumnExt(BitTorrentTableFormat.PRIORITY_INDEX).setMinWidth(60);
}
}
private class BitTorrentTableFormat extends AbstractTableFormat<TorrentFileEntryWrapper> {
private static final int DOWNLOAD_INDEX = 0;
private static final int NAME_INDEX = 1;
private static final int PERCENT_INDEX = 2;
private static final int PRIORITY_INDEX = 3;
public BitTorrentTableFormat() {
super(I18n.tr("DL"),
I18n.tr("Name"),
I18n.tr("%"),
I18n.tr("Priority"));
}
@Override
public Object getColumnValue(TorrentFileEntryWrapper baseObject, int column) {
switch(column) {
case DOWNLOAD_INDEX: return baseObject;
case NAME_INDEX: return baseObject.getPath();
case PERCENT_INDEX: return baseObject;
case PRIORITY_INDEX: return baseObject;
}
throw new IllegalStateException("Unknown column:" + column);
}
}
private class CheckBoxRendererEditor extends JCheckBox implements TableCellRenderer, TableCellEditor {
/** List of cell editor listeners. */
private final List<CellEditorListener> listenerList = new ArrayList<CellEditorListener>();
private TorrentFileEntryWrapper currentWrapper;
public CheckBoxRendererEditor() {
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
return getTableCellComponent(table, value, isSelected, column, column);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
return getTableCellComponent(table, value, isSelected, column, column);
}
private Component getTableCellComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
if(value instanceof TorrentFileEntryWrapper) {
currentWrapper = (TorrentFileEntryWrapper) value;
if(((TorrentFileEntryWrapper)value).getProgress() == 1.0f) {
setSelected(true);
setEnabled(false);
} else {
setSelected(((TorrentFileEntryWrapper)value).getPriority() != DONT_DOWNLOAD);
setEnabled(!torrent.isFinished());
}
} else {
currentWrapper = null;
setSelected(false);
}
return this;
}
@Override
public void addCellEditorListener(CellEditorListener l) {
if (!listenerList.contains(l)) {
listenerList.add(l);
}
}
@Override
public void cancelCellEditing() {
ChangeEvent event = new ChangeEvent(this);
for (int i = 0, size = listenerList.size(); i < size; i++) {
listenerList.get(i).editingCanceled(event);
}
}
@Override
public TorrentFileEntryWrapper getCellEditorValue() {
return currentWrapper;
}
@Override
public boolean isCellEditable(EventObject anEvent) {
return true;
}
@Override
public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(l);
}
@Override
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
@Override
public boolean stopCellEditing() {
cancelCellEditing();
return true;
}
}
private class PercentRenderer extends DefaultTableCellRenderer {
public PercentRenderer() {
setBorder(BorderFactory.createEmptyBorder(0,5,0,5));
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
if(value instanceof TorrentFileEntryWrapper) {
float percent = ((TorrentFileEntryWrapper)value).getProgress();
if( percent == 1.0f) {
setText(I18n.tr("Done"));
} else if(((TorrentFileEntryWrapper)value).getPriority() == DONT_DOWNLOAD){
setText("");
} else {
setText((int)(percent * 100) + "%");
}
} else {
setText("");
}
return this;
}
}
private class PriorityRendererEditor extends JPanel implements TableCellRenderer, TableCellEditor {
/** List of cell editor listeners. */
private final List<CellEditorListener> listenerList = new ArrayList<CellEditorListener>();
@Resource private Icon lowestPriorityIcon;
@Resource private Icon normalPriorityIcon;
@Resource private Icon highestPriorityIcon;
@Resource private Font textFont;
@Resource private Color fontColor;
private final JButton button;
private TorrentFileEntryWrapper currentWrapper;
public PriorityRendererEditor() {
super(new MigLayout("align 50%"));
GuiUtils.assignResources(this);
button = new JButton();
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.setFont(textFont);
button.setFocusPainted(false);
button.setForeground(fontColor);
FontUtils.underline(button);
add(button);
}
public JButton getButton() {
return button;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
return getTableCellComponent(table, value, isSelected, row, column);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
return getTableCellComponent(table, value, isSelected, row, column);
}
private Component getTableCellComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
if(value instanceof TorrentFileEntryWrapper) {
currentWrapper = (TorrentFileEntryWrapper) value;
int priority = ((TorrentFileEntryWrapper)value).getPriority();
if(((TorrentFileEntryWrapper)value).getProgress() == 1.0f) {
button.setIcon(null);
button.setText("");
// button.setText(I18n.tr("delete"));
} else if(priority == DONT_DOWNLOAD) {
button.setIcon(null);
button.setText("");
} else if(priority == LOWEST_PRIORITY) {
button.setIcon(lowestPriorityIcon);
button.setText("");
} else if(priority == NORMAL_PRIORITY) {
button.setIcon(normalPriorityIcon);
button.setText("");
} else if(priority == HIGHEST_PRIORITY) {
button.setIcon(highestPriorityIcon);
button.setText("");
}
} else {
currentWrapper = null;
button.setIcon(null);
button.setText("");
}
return this;
}
@Override
public void addCellEditorListener(CellEditorListener l) {
if (!listenerList.contains(l)) {
listenerList.add(l);
}
}
@Override
public void cancelCellEditing() {
ChangeEvent event = new ChangeEvent(this);
for (int i = 0, size = listenerList.size(); i < size; i++) {
listenerList.get(i).editingCanceled(event);
}
}
@Override
public TorrentFileEntryWrapper getCellEditorValue() {
return currentWrapper;
}
@Override
public boolean isCellEditable(EventObject anEvent) {
return true;
}
@Override
public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(l);
}
@Override
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
@Override
public boolean stopCellEditing() {
cancelCellEditing();
return true;
}
}
}