package org.limewire.ui.swing.properties;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JToggleButton;
import javax.swing.SpinnerNumberModel;
import net.miginfocom.swing.MigLayout;
import org.limewire.bittorrent.LimeWireTorrentProperties;
import org.limewire.bittorrent.Torrent;
import org.limewire.bittorrent.TorrentManagerSettings;
import org.limewire.core.api.library.PropertiableFile;
import org.limewire.core.settings.BittorrentSettings;
import org.limewire.core.settings.DownloadSettings;
import org.limewire.core.settings.UploadSettings;
import org.limewire.ui.swing.util.I18n;
/**
* A Panel that allows the user to management seeding and bandwidth
* on a given Torrent.
*/
public class TorrentManagementPanel implements FileInfoPanel {
private final Torrent torrent;
private final TorrentManagerSettings torrentSettings;
private final JPanel component;
private JRadioButton defaultRadioButton;
private JRadioButton uploadForeverRadioButton;
private JRadioButton uploadControlRadioButton;
private JSpinner seedRatioSpinner;
private JSpinner seedDaysSpinner;
private JSpinner seedHoursSpinner;
private JCheckBox limitDownloadBandWidthCheckBox;
private JCheckBox limitUploadBandwidthCheckBox;
private JSpinner maxUploadSpeedSpinner;
private JSpinner maxDownloadSpeedSpinner;
public TorrentManagementPanel(Torrent torrent, TorrentManagerSettings torrentSettings) {
this.torrent = torrent;
this.torrentSettings = torrentSettings;
component = new JPanel(new MigLayout("insets 0"));
init();
initValues();
}
@Override
public JComponent getComponent() {
return component;
}
@Override
public boolean hasChanged() {
if (!torrent.isValid()) {
return false;
}
float ratio = torrent.getProperty(LimeWireTorrentProperties.MAX_SEED_RATIO_LIMIT, -1f);
int time = torrent.getProperty(LimeWireTorrentProperties.MAX_SEED_TIME_RATIO_LIMIT, -1);
if(defaultRadioButton.isSelected()) {
if(ratio != -1f || time != -1)
return true;
} else if(uploadForeverRadioButton.isSelected()) {
if(ratio != Float.MAX_VALUE || time != Integer.MAX_VALUE)
return true;
} else {
//check this
if(ratio != ((SpinnerNumberModel)seedRatioSpinner.getModel()).getNumber().floatValue())
return true;
if(time != getSeconds(((SpinnerNumberModel)seedDaysSpinner.getModel()).getNumber().intValue(), ((SpinnerNumberModel)seedHoursSpinner.getModel()).getNumber().intValue()))
return true;
}
int uploadRate = torrent.getMaxUploadBandwidth();
int downloadRate = torrent.getMaxDownloadBandwidth();
if(limitDownloadBandWidthCheckBox.isSelected()) {
if(downloadRate != (((SpinnerNumberModel)maxDownloadSpeedSpinner.getModel()).getNumber().intValue() * 1024))
return true;
} else {
if(downloadRate != -1)
return true;
}
if(limitUploadBandwidthCheckBox.isSelected()) {
if(uploadRate != (((SpinnerNumberModel)maxUploadSpeedSpinner.getModel()).getNumber().intValue() * 1024))
return true;
} else {
if(uploadRate != -1)
return true;
}
return false;
}
@Override
public void save() {
if (!torrent.isValid()) {
return;
}
if(defaultRadioButton.isSelected()) {
torrent.setProperty(LimeWireTorrentProperties.MAX_SEED_RATIO_LIMIT, null);
torrent.setProperty(LimeWireTorrentProperties.MAX_SEED_TIME_RATIO_LIMIT, null);
} else if(uploadForeverRadioButton.isSelected()) {
torrent.setProperty(LimeWireTorrentProperties.MAX_SEED_RATIO_LIMIT, Float.MAX_VALUE);
torrent.setProperty(LimeWireTorrentProperties.MAX_SEED_TIME_RATIO_LIMIT, Integer.MAX_VALUE);
} else {
torrent.setProperty(LimeWireTorrentProperties.MAX_SEED_RATIO_LIMIT, ((SpinnerNumberModel)seedRatioSpinner.getModel()).getNumber().floatValue());
torrent.setProperty(LimeWireTorrentProperties.MAX_SEED_TIME_RATIO_LIMIT,
(getSeconds(((SpinnerNumberModel)seedDaysSpinner.getModel()).getNumber().intValue(),
((SpinnerNumberModel)seedHoursSpinner.getModel()).getNumber().intValue())));
}
if(limitDownloadBandWidthCheckBox.isSelected()) {
torrent.setMaxDownloadBandwidth(((SpinnerNumberModel)maxDownloadSpeedSpinner.getModel()).getNumber().intValue() * 1024);
} else {
torrent.setMaxDownloadBandwidth(0);
}
if(limitUploadBandwidthCheckBox.isSelected()) {
torrent.setMaxUploadBandwidth(((SpinnerNumberModel)maxUploadSpeedSpinner.getModel()).getNumber().intValue() * 1024);
} else {
torrent.setMaxUploadBandwidth(0);
}
}
@Override
public void updatePropertiableFile(PropertiableFile file) {
}
@Override
public void dispose() {
}
private void initValues() {
if(torrent == null || !torrent.isEditable()) {
defaultRadioButton.setSelected(true);
limitDownloadBandWidthCheckBox.setSelected(false);
limitUploadBandwidthCheckBox.setSelected(false);
} else {
float ratio = torrent.getProperty(LimeWireTorrentProperties.MAX_SEED_RATIO_LIMIT, -1f);
int time = torrent.getProperty(LimeWireTorrentProperties.MAX_SEED_TIME_RATIO_LIMIT, -1);
int uploadRate = -1;
int downloadRate = -1;
if (torrent.isValid()) {
uploadRate = torrent.getMaxUploadBandwidth()/1024;
downloadRate = torrent.getMaxDownloadBandwidth()/1024;
}
// seed forever
if(ratio == Float.MAX_VALUE && time == Integer.MAX_VALUE) {
uploadForeverRadioButton.setSelected(true);
initTimeSpinners(BittorrentSettings.LIBTORRENT_SEED_TIME_LIMIT.get());
} else if((ratio >= 0 && ratio != torrentSettings.getSeedRatioLimit()) || (time >= 0 && time != torrentSettings.getSeedTimeLimit())) { // user set values
uploadControlRadioButton.setSelected(true);
seedRatioSpinner.setValue(ratio);
initTimeSpinners(getTime(torrent));
} else { // default values
defaultRadioButton.setSelected(true);
initTimeSpinners(getTime(torrent));
}
limitUploadBandwidthCheckBox.setSelected(uploadRate >= (Integer)((SpinnerNumberModel)maxUploadSpeedSpinner.getModel()).getMinimum() && uploadRate != torrentSettings.getMaxUploadBandwidth()/1024);
if(limitUploadBandwidthCheckBox.isSelected())
maxUploadSpeedSpinner.setValue(uploadRate);
limitDownloadBandWidthCheckBox.setSelected(downloadRate >= (Integer) ((SpinnerNumberModel)maxDownloadSpeedSpinner.getModel()).getMinimum() && downloadRate != torrentSettings.getMaxDownloadBandwidth()/1024);
if(limitDownloadBandWidthCheckBox.isSelected()) {
maxDownloadSpeedSpinner.setValue(downloadRate);
}
}
}
private void initTimeSpinners(int time) {
int wholeDays = getWholeDays(time);
seedDaysSpinner.setValue(wholeDays);
seedHoursSpinner.setValue(getRemainderHours(time, wholeDays));
}
/**
* Returns the number of seconds to seed this Torrent. If no value has
* been set on the Torrent, returns the default seed time within TorrentSettings.
*/
private int getTime(Torrent torrent) {
int time = torrent.getProperty(LimeWireTorrentProperties.MAX_SEED_TIME_RATIO_LIMIT, -1);
float defaultTime = BittorrentSettings.LIBTORRENT_SEED_TIME_LIMIT.get();
if(time == -1)
return (int)defaultTime;
else
return time;
}
private void init() {
ButtonGroup buttonGroup = new ButtonGroup();
defaultRadioButton = new JRadioButton(I18n.tr("Use default torrent options"));
uploadForeverRadioButton = new JRadioButton(I18n.tr("Upload this torrent forever"));
uploadControlRadioButton = new JRadioButton(I18n.tr("Upload this torrent until either of the following:"));
uploadControlRadioButton.setSelected(true);
//add the radio buttons to the component
addRadioButton(buttonGroup, defaultRadioButton);
addRadioButton(buttonGroup, uploadForeverRadioButton);
addRadioButton(buttonGroup, uploadControlRadioButton);
seedRatioSpinner = createSeedRatioSpinner();
seedDaysSpinner = createDaySpinner();
seedHoursSpinner = createHourSpinner();
// add the spinners to the component
component.add(new JLabel(I18n.tr("Ratio:")), "split 2, gapleft 20");
component.add(seedRatioSpinner, "span, wrap");
component.add(new JLabel(I18n.tr("Maximum days:")), "gapleft 20, split 4");
component.add(seedDaysSpinner, "");
component.add(new JLabel("Hours:"), "gapleft 20");
component.add(seedHoursSpinner, "wrap");
uploadControlRadioButton.addItemListener(new EnableSetterListener(seedRatioSpinner, seedDaysSpinner, seedHoursSpinner));
// add separator
component.add(new JSeparator(), "growx, span, gaptop 10, gapbottom 10, wrap");
limitDownloadBandWidthCheckBox = createCheckBox(I18n.tr("Limit this torrent's download bandwidth"));
maxDownloadSpeedSpinner = createDownloadSpinner();
JLabel maxDownloadLabel = new JLabel("KB/s");
limitDownloadBandWidthCheckBox.addItemListener(new VisibilitySetterListener(maxDownloadSpeedSpinner, maxDownloadLabel));
// add download bandwidth limits
component.add(limitDownloadBandWidthCheckBox);
component.add(maxDownloadSpeedSpinner);
component.add(maxDownloadLabel, "wrap");
limitUploadBandwidthCheckBox = createCheckBox(I18n.tr("Limit this torrent's upload bandwidth"));
maxUploadSpeedSpinner = createUploadSpinner();
JLabel maxUploadLabel = new JLabel("KB/s");
limitUploadBandwidthCheckBox.addItemListener(new VisibilitySetterListener(maxUploadSpeedSpinner, maxUploadLabel));
// add upload bandwidth limits
component.add(limitUploadBandwidthCheckBox);
component.add(maxUploadSpeedSpinner);
component.add(maxUploadLabel, "wrap");
}
private void addRadioButton(ButtonGroup buttonGroup, JRadioButton button) {
buttonGroup.add(button);
button.setOpaque(false);
component.add(button, "wrap");
}
private JSpinner createSpinner(SpinnerNumberModel model) {
JSpinner spinner = new JSpinner(model);
spinner.setPreferredSize(new Dimension(50, 20));
spinner.setMaximumSize(new Dimension(60, 20));
return spinner;
}
private JCheckBox createCheckBox(String string) {
JCheckBox checkBox = new JCheckBox(string);
checkBox.setContentAreaFilled(false);
checkBox.setSelected(true);
return checkBox;
}
/**
* Creates a Spinner to set the Max Download Bandwidth for a Torrent.
*/
private JSpinner createDownloadSpinner() {
SpinnerNumberModel maxSpinnerModel = new SpinnerNumberModel(
DownloadSettings.MAX_DOWNLOAD_SPEED.getValue()/1024,
DownloadSettings.MAX_DOWNLOAD_SPEED.getMinValue().intValue()/1024,
DownloadSettings.LIMIT_MAX_DOWNLOAD_SPEED.get() ?
(DownloadSettings.MAX_DOWNLOAD_SPEED.getValue()/1024):
(DownloadSettings.MAX_DOWNLOAD_SPEED.getMaxValue().intValue()/1024), 1);
return createSpinner(maxSpinnerModel);
}
/**
* Creates a Spinner to set the Max Upload Bandwidth for a Torrent.
*/
private JSpinner createUploadSpinner() {
SpinnerNumberModel maxUploadSpinnerModel = new SpinnerNumberModel(
UploadSettings.MAX_UPLOAD_SPEED.getValue()/1024,
UploadSettings.MAX_UPLOAD_SPEED.getMinValue().intValue()/1024,
UploadSettings.LIMIT_MAX_UPLOAD_SPEED.get() ?
(UploadSettings.MAX_UPLOAD_SPEED.getValue()/1024) :
(UploadSettings.MAX_UPLOAD_SPEED.getMaxValue().intValue()/1024), 1);
return createSpinner(maxUploadSpinnerModel);
}
/**
* Creates a Spinner to set the number of days to seed a Torrent.
*/
private JSpinner createDaySpinner() {
int wholeDays = getWholeDays(BittorrentSettings.LIBTORRENT_SEED_TIME_LIMIT.get());
SpinnerNumberModel seedDaysModel = new SpinnerNumberModel(
wholeDays,
getWholeDays(BittorrentSettings.LIBTORRENT_SEED_TIME_LIMIT.getMinValue()),
getWholeDays(BittorrentSettings.LIBTORRENT_SEED_TIME_LIMIT.getMaxValue()), 1);
return createSpinner(seedDaysModel);
}
/**
* Creates a Spinner to set the number of hours to seed a Torrent. The max is 24 hours, the
* minimum is 0 and the default is 0.
*/
private JSpinner createHourSpinner() {
int wholeDays = getWholeDays(BittorrentSettings.LIBTORRENT_SEED_TIME_LIMIT.get());
SpinnerNumberModel seedHoursModel = new SpinnerNumberModel(
getRemainderHours(BittorrentSettings.LIBTORRENT_SEED_TIME_LIMIT.get(), wholeDays),
0,
24, 1);
return createSpinner(seedHoursModel);
}
/**
* Creates a Spinner to set the Seed Ratio for a Torrent. The max value is Float.MAX_VALUE, the min
* is 0f and the default is 2.0f.
*/
private JSpinner createSeedRatioSpinner() {
SpinnerNumberModel seedRatioModel = new SpinnerNumberModel(BittorrentSettings.LIBTORRENT_SEED_RATIO_LIMIT
.get().floatValue(), BittorrentSettings.LIBTORRENT_SEED_RATIO_LIMIT.getMinValue()
.floatValue(), BittorrentSettings.LIBTORRENT_SEED_RATIO_LIMIT.getMaxValue()
.floatValue(), .05);
return createSpinner(seedRatioModel);
}
/**
* Returns the number of days given a number of seconds.
*/
private static int getWholeDays(Integer integer) {
return (int) Math.floor(integer.doubleValue() / (60 * 60 * 24));
}
/**
* Returns the number of hours from the number of seconds.
*/
private static int getRemainderHours(Integer totalSeconds, int days) {
return (int)Math.round(totalSeconds.doubleValue() / (60 *60) - days*24);
}
/**
* Given a number of days and hours, returns the time in seconds.
*/
private static int getSeconds(int days, int hours) {
return days*24*60*60 + hours*60*60;
}
/**
* Listens to a JToggleButton being selected and updates the isEnabled
* method of a JComponent
*/
private static class EnableSetterListener implements ItemListener {
private final JComponent[] components;
public EnableSetterListener(JComponent... component) {
this.components = component;
}
@Override
public void itemStateChanged(ItemEvent e) {
for(JComponent component : components) {
component.setEnabled(((JToggleButton)e.getSource()).isSelected());
}
}
}
/**
* Listens to a JToggleButton being selected and updates the visibility
* of a set of JComponents.
*/
private static class VisibilitySetterListener implements ItemListener {
private final JComponent[] components;
public VisibilitySetterListener(JComponent... component) {
this.components = component;
}
@Override
public void itemStateChanged(ItemEvent e) {
for(JComponent component : components) {
component.setVisible(((JToggleButton)e.getSource()).isSelected());
}
}
}
}