/*
* OpenTorrentWindow.java
*
* Created on February 23, 2004, 4:09 PM
*
* Copyright (C) 2004, 2005, 2006 Aelitis SAS, All rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* AELITIS, SAS au capital de 46,603.30 euros,
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*/
package org.gudy.azureus2.ui.swt;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.*;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.StringIterator;
import org.gudy.azureus2.core3.config.StringList;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.download.DownloadManagerInitialisationAdapter;
import org.gudy.azureus2.core3.global.GlobalManager;
import org.gudy.azureus2.core3.internat.LocaleTorrentUtil;
import org.gudy.azureus2.core3.internat.LocaleUtilDecoder;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.logging.LogAlert;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.torrent.TOTorrentFile;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloader;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloaderCallBackInterface;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.ui.swt.components.shell.ShellFactory;
import org.gudy.azureus2.ui.swt.mainwindow.Colors;
import org.gudy.azureus2.ui.swt.mainwindow.TorrentOpener;
import org.gudy.azureus2.ui.swt.shells.MessageBoxShell;
import org.gudy.azureus2.ui.swt.shells.MessageSlideShell;
import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.vuzefile.VuzeFile;
import com.aelitis.azureus.core.vuzefile.VuzeFileComponent;
import com.aelitis.azureus.core.vuzefile.VuzeFileHandler;
import com.aelitis.azureus.ui.common.updater.UIUpdatable;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.imageloader.ImageLoader;
import com.aelitis.azureus.ui.swt.uiupdater.UIUpdaterSWT;
/**
* Torrent Opener Window.
*
* @author TuxPaper
*
* TODO Category Option
*/
public class OpenTorrentWindow
implements TorrentDownloaderCallBackInterface, UIUpdatable
{
/**
* We prevent users from unselecting small files to prevent them missing
* out "signature" files from trackers (.nfo file, readme file etc)
*
* We define two constants to control this - one defines what a small file
* is, and the other defines whether we believe a torrent has signature
* files or not - we do this by seeing how many small files the torrent has.
*
* If it has several small files, then it would be silly for us to assume
* that the torrent consists of multiple signature files.
*
* Note: I (amc1) have disabled this now, because it can force users who may want
* to only download one file to download those small files, which may not be in
* an overlapping piece. Since I've now seen comments from people who've complained
* about this, I'm disabling it.
*/
//private final static int MIN_NODOWNLOAD_SIZE = 64 * 1024;
//private final static int MAX_NODOWNLOAD_COUNT = 3;
private final static int MIN_BUTTON_HEIGHT = -1;
private final static String PARAM_DEFSAVEPATH = "Default save path";
private final static String PARAM_MOVEWHENDONE = "Move Completed When Done";
private static final String PARAM_VIEWMODE = "OpenTorrentWindow.viewMode";
private final static String MSG_ALREADY_EXISTS = "OpenTorrentWindow.mb.alreadyExists";
private final static String MSG_ALREADY_EXISTS_NAME = MSG_ALREADY_EXISTS
+ ".default.name";
private final static int STARTMODE_QUEUED = 0;
private final static int STARTMODE_STOPPED = 1;
private final static int STARTMODE_FORCESTARTED = 2;
private final static int STARTMODE_SEEDING = 3;
private final static int QUEUELOCATION_TOP = 0;
private final static int QUEUELOCATION_BOTTOM = 1;
private final static String[] startModes = {
"queued",
"stopped",
"forceStarted",
"seeding"
};
private final static String[] queueLocations = {
"first",
"last"
};
/** Only one window, since it can handle multiple torrents */
private static OpenTorrentWindow stOpenTorrentWindow = null;
// SWT Stuff
private Shell shell;
private Table dataFileTable;
private TableEditor dataFileTableEditor;
private Table torrentTable;
private Button ok;
private Combo cmbDataDir;
private Composite cSaveTo;
private Combo cmbStartMode = null;
private Combo cmbQueueLocation = null;
// Link to the outside
private GlobalManager gm;
// Internal Stuff
/** TorrentFileInfo list. All dataFiles currently in table, same order */
private ArrayList dataFiles = new ArrayList();
/** TorrentInfo list. All torrents to open, same order as table */
private ArrayList torrentList = new ArrayList();
/** List of torrents being downloaded. Stored so we don't close window
* until they are done/aborted.
*/
private ArrayList<TorrentDownloader> downloaders = new ArrayList<TorrentDownloader>();
private boolean bOverrideStartModeToStopped = false;
private boolean bDefaultForSeeding;
/** Things to be disposed of when window closes */
private ArrayList disposeList = new ArrayList();
private boolean bClosed = false;
/** Shell to use to open children (FileDialog, etc) */
private Shell shellForChildren;
private String sDestDir;
protected boolean bSkipDataDirModify = false;
private StringList dirList;
private Label dataFileTableLabel;
private Composite diskspaceComp;
/**
* A counter to track torrent file downloads that are still active;
* this is purely used to enable/disable the OK button
*/
private int activeTorrentCount = 0;
/**
*
* @param parent
* @param gm
* @param sPathOfFilesToOpen
* @param sFilesToOpen
* @param bDefaultStopped
* @param bForSeeding
* @param bPopupOpenURL
*/
public synchronized static final void invoke(Shell parent, GlobalManager gm,
String sPathOfFilesToOpen, String[] sFilesToOpen,
boolean bDefaultStopped, boolean bForSeeding, boolean bPopupOpenURL) {
String saveSilentlyDir = null;
if (stOpenTorrentWindow == null) {
boolean bMustOpen = (sPathOfFilesToOpen == null && sFilesToOpen == null)
|| bForSeeding;
if (!bMustOpen) {
saveSilentlyDir = getSaveSilentlyDir();
bMustOpen = saveSilentlyDir == null;
}
stOpenTorrentWindow = new OpenTorrentWindow(parent, gm, bMustOpen);
} else {
if (stOpenTorrentWindow.shell != null)
stOpenTorrentWindow.shell.forceActive();
}
if (stOpenTorrentWindow != null) {
// local var because static may get set o null
OpenTorrentWindow openTorrentWindow = stOpenTorrentWindow;
openTorrentWindow.bOverrideStartModeToStopped = bDefaultStopped;
openTorrentWindow.bDefaultForSeeding = bForSeeding;
if (sFilesToOpen != null || sPathOfFilesToOpen != null) {
// If none of the files sent to us were valid files, don't open the
// window
if (!bPopupOpenURL
&& openTorrentWindow.addTorrents(sPathOfFilesToOpen, sFilesToOpen) == 0
&& openTorrentWindow.torrentList.size() == 0
&& openTorrentWindow.downloaders.size() == 0) {
openTorrentWindow.close(true, true);
return;
}
}
if (bPopupOpenURL)
openTorrentWindow.browseURL();
if (saveSilentlyDir != null) {
openTorrentWindow.sDestDir = saveSilentlyDir;
for (int i = 0; i < openTorrentWindow.torrentList.size(); i++) {
final TorrentInfo info = (TorrentInfo) openTorrentWindow.torrentList.get(i);
info.renameDuplicates();
}
openTorrentWindow.openTorrents();
openTorrentWindow.close(true, false);
}
}
}
/**
*
* @param parent
* @param gm
*/
public synchronized static final void invoke(final Shell parent,
GlobalManager gm) {
invoke(parent, gm, null, null, false, false, false);
}
public synchronized static final void invokeURLPopup(final Shell parent,
GlobalManager gm) {
invoke(parent, gm, null, null, false, false, true);
}
/**
*
* @param parent
* @param gm
* @param bOpenWindow
*/
private OpenTorrentWindow(final Shell parent, GlobalManager gm,
boolean bOpenWindow) {
this.gm = gm;
sDestDir = COConfigurationManager.getStringParameter(PARAM_DEFSAVEPATH);
if (bOpenWindow)
openWindow(parent);
else
shellForChildren = parent;
}
private void openWindow(Shell parent) {
boolean bTorrentInClipboard = false;
GridData gridData;
Label label;
Composite cArea;
shell = ShellFactory.createShell(parent, SWT.RESIZE | SWT.DIALOG_TRIM);
shellForChildren = shell;
shell.setText(MessageText.getString("OpenTorrentWindow.title"));
Utils.setShellIcon(shell);
GridLayout layout = FixupLayout(new GridLayout(), false);
shell.setLayout(layout);
shell.addListener(SWT.Resize, new Listener() {
public void handleEvent(Event e) {
resizeTables(3);
}
});
Clipboard clipboard = new Clipboard(shell.getDisplay());
String sClipText = (String) clipboard.getContents(TextTransfer.getInstance());
if (sClipText != null)
bTorrentInClipboard = addTorrentsFromTextList(sClipText, true) > 0;
// label = new Label(shell, SWT.BORDER | SWT.WRAP);
// Messages.setLanguageText(label, "OpenTorrentWindow.message");
// gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
// label.setLayoutData(gridData);
// Torrents
// ========
Composite cButtons = new Composite(shell, SWT.NONE);
RowLayout rLayout = new RowLayout(SWT.HORIZONTAL);
rLayout.marginBottom = 0;
rLayout.marginLeft = 0;
rLayout.marginRight = 0;
rLayout.marginTop = 0;
cButtons.setLayout(rLayout);
// Buttons for tableTorrents
Button browseTorrent = new Button(cButtons, SWT.PUSH);
Messages.setLanguageText(browseTorrent, "OpenTorrentWindow.addFiles");
browseTorrent.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
FileDialog fDialog = new FileDialog(shell, SWT.OPEN | SWT.MULTI);
fDialog.setFilterExtensions(new String[] {
"*.torrent",
"*.tor",
Constants.FILE_WILDCARD
});
fDialog.setFilterNames(new String[] {
"*.torrent",
"*.tor",
Constants.FILE_WILDCARD
});
fDialog.setFilterPath(TorrentOpener.getFilterPathTorrent());
fDialog.setText(MessageText.getString("MainWindow.dialog.choose.file"));
String fileName = TorrentOpener.setFilterPathTorrent(fDialog.open());
if (fileName != null) {
addTorrents(fDialog.getFilterPath(), fDialog.getFileNames());
}
}
});
Utils.setGridData(cButtons, GridData.FILL_HORIZONTAL, browseTorrent,
MIN_BUTTON_HEIGHT);
Button browseURL = new Button(cButtons, SWT.PUSH);
Messages.setLanguageText(browseURL, "OpenTorrentWindow.addFiles.URL");
browseURL.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
browseURL();
}
});
Button browseFolder = new Button(cButtons, SWT.PUSH);
Messages.setLanguageText(browseFolder, "OpenTorrentWindow.addFiles.Folder");
browseFolder.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
DirectoryDialog fDialog = new DirectoryDialog(shell, SWT.NULL);
fDialog.setFilterPath(TorrentOpener.getFilterPathTorrent());
fDialog.setMessage(MessageText.getString("MainWindow.dialog.choose.folder"));
String path = TorrentOpener.setFilterPathTorrent(fDialog.open());
if (path != null) {
addTorrents(path, null);
}
}
});
if (bTorrentInClipboard) {
Button pasteOpen = new Button(cButtons, SWT.PUSH);
Messages.setLanguageText(pasteOpen,
"OpenTorrentWindow.addFiles.Clipboard");
pasteOpen.setToolTipText(sClipText);
pasteOpen.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
Clipboard clipboard = new Clipboard(shell.getDisplay());
String sClipText = (String) clipboard.getContents(TextTransfer.getInstance());
if (sClipText != null) {
addTorrentsFromTextList(sClipText.trim(), false);
}
}
});
}
Group gTorrentsArea = new Group(shell, SWT.NONE);
gridData = new GridData(GridData.FILL_HORIZONTAL);
gTorrentsArea.setLayoutData(gridData);
layout = FixupLayout(new GridLayout(), true);
gTorrentsArea.setLayout(layout);
Messages.setLanguageText(gTorrentsArea, "OpenTorrentWindow.torrentLocation");
Composite cTorrentList = new Composite(gTorrentsArea, SWT.NONE);
gridData = new GridData(GridData.FILL_HORIZONTAL);
cTorrentList.setLayoutData(gridData);
createTorrentListArea(cTorrentList);
Composite cTorrentOptions = new Composite(gTorrentsArea, SWT.NONE);
gridData = new GridData(GridData.FILL_HORIZONTAL);
cTorrentOptions.setLayoutData(gridData);
layout = FixupLayout(new GridLayout(), true);
layout.marginHeight = 0;
layout.marginWidth = 0;
cTorrentOptions.setLayout(layout);
label = new Label(cTorrentOptions, SWT.NONE);
gridData = new GridData(GridData.FILL_HORIZONTAL);
label.setLayoutData(gridData);
Messages.setLanguageText(label, "OpenTorrentWindow.torrent.options");
int userMode = COConfigurationManager.getIntParameter("User Mode");
if (userMode > 0) {
Composite cTorrentModes = new Composite(cTorrentOptions, SWT.NONE);
gridData = new GridData(GridData.FILL_HORIZONTAL);
cTorrentModes.setLayoutData(gridData);
layout = new GridLayout();
layout.numColumns = 4;
layout.marginWidth = 0;
layout.marginHeight = 0;
cTorrentModes.setLayout(layout);
label = new Label(cTorrentModes, SWT.NONE);
gridData = new GridData(GridData.VERTICAL_ALIGN_CENTER);
label.setLayoutData(gridData);
Messages.setLanguageText(label, "OpenTorrentWindow.startMode");
cmbStartMode = new Combo(cTorrentModes, SWT.BORDER | SWT.READ_ONLY);
gridData = new GridData(GridData.FILL_HORIZONTAL);
cmbStartMode.setLayoutData(gridData);
updateStartModeCombo();
cmbStartMode.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
setSelectedStartMode(cmbStartMode.getSelectionIndex());
}
});
label = new Label(cTorrentModes, SWT.NONE);
gridData = new GridData(GridData.VERTICAL_ALIGN_CENTER);
label.setLayoutData(gridData);
Messages.setLanguageText(label, "OpenTorrentWindow.addPosition");
cmbQueueLocation = new Combo(cTorrentModes, SWT.BORDER | SWT.READ_ONLY);
gridData = new GridData(GridData.FILL_HORIZONTAL);
cmbQueueLocation.setLayoutData(gridData);
updateQueueLocationCombo();
cmbQueueLocation.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
setSelectedQueueLocation(cmbQueueLocation.getSelectionIndex());
}
});
}
// Save To..
// =========
cSaveTo = new Composite(cTorrentOptions, SWT.NONE);
layout = FixupLayout(new GridLayout(), false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
layout.numColumns = 2;
cSaveTo.setLayout(layout);
Label lblDataDir = new Label(cSaveTo, SWT.NONE);
gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gridData.horizontalSpan = 2;
lblDataDir.setLayoutData(gridData);
Messages.setLanguageText(lblDataDir, "OpenTorrentWindow.dataLocation");
cmbDataDir = new Combo(cSaveTo, SWT.BORDER);
gridData = new GridData(GridData.FILL_HORIZONTAL);
cmbDataDir.setLayoutData(gridData);
cmbDataDir.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
cmbDataDirChanged();
}
});
cmbDataDir.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
cmbDataDirChanged();
}
});
updateDataDirCombo();
dirList = COConfigurationManager.getStringListParameter("saveTo_list");
StringIterator iter = dirList.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (!s.equals(sDestDir)) {
cmbDataDir.add(s);
}
}
Button browseData = new Button(cSaveTo, SWT.PUSH);
Messages.setLanguageText(browseData, "ConfigView.button.browse");
browseData.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
String sSavePath;
String sDefPath = cmbDataDir.getText();
File f = new File(sDefPath);
if (sDefPath.length() > 0) {
while (!f.exists()) {
f = f.getParentFile();
if (f == null) {
f = new File(sDefPath);
break;
}
}
}
DirectoryDialog dDialog = new DirectoryDialog(shell, SWT.SYSTEM_MODAL);
dDialog.setFilterPath(f.getAbsolutePath());
dDialog.setMessage(MessageText.getString("MainWindow.dialog.choose.savepath_forallfiles"));
sSavePath = dDialog.open();
if (sSavePath != null) {
cmbDataDir.setText(sSavePath);
}
}
});
gridData = new GridData(GridData.FILL_HORIZONTAL);
cSaveTo.setLayoutData(gridData);
// File List
// =========
Group gFilesArea = new Group(shell, SWT.NONE);
gridData = new GridData(GridData.FILL_BOTH);
gFilesArea.setLayoutData(gridData);
layout = FixupLayout(new GridLayout(), true);
gFilesArea.setLayout(layout);
Messages.setLanguageText(gFilesArea, "OpenTorrentWindow.fileList");
createTableDataFiles(gFilesArea);
// Ok, cancel
cArea = new Composite(shell, SWT.NULL);
layout = new GridLayout();
layout.marginHeight = 0;
layout.numColumns = 2;
cArea.setLayout(layout);
ok = new Button(cArea, SWT.PUSH);
Messages.setLanguageText(ok, "Button.ok");
gridData = new GridData(GridData.HORIZONTAL_ALIGN_END);
gridData.widthHint = 70;
ok.setLayoutData(gridData);
shell.setDefaultButton(ok);
ok.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
okPressed();
}
});
checkSeedingMode();
Button cancel = new Button(cArea, SWT.PUSH);
Messages.setLanguageText(cancel, "Button.cancel");
gridData = new GridData();
gridData.widthHint = 70;
cancel.setLayoutData(gridData);
cancel.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
close(true, true);
}
});
Utils.setGridData(cArea, GridData.HORIZONTAL_ALIGN_END, ok,
MIN_BUTTON_HEIGHT);
shell.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if (!bClosed)
close(false, true);
}
});
shell.addListener(SWT.Traverse, new Listener() {
public void handleEvent(Event e) {
if (e.detail == SWT.TRAVERSE_ESCAPE) {
close(true, true);
}
}
});
KeyListener pasteKeyListener = new org.eclipse.swt.events.KeyAdapter() {
public void keyPressed(KeyEvent e) {
int key = e.character;
if ((e.stateMask & SWT.MOD1) != 0 && e.character <= 26
&& e.character > 0)
key += 'a' - 1;
if ((key == 'v' && (e.stateMask & SWT.MOD1) > 0)
|| (e.keyCode == SWT.INSERT && (e.stateMask & SWT.SHIFT) > 0)) {
e.doit = false;
// Paste
Clipboard clipboard = new Clipboard(shell.getDisplay());
String sClipText = (String) clipboard.getContents(TextTransfer.getInstance());
if (sClipText != null) {
addTorrentsFromTextList(sClipText, false);
}
}
}
};
setPasteKeyListener(shell, pasteKeyListener);
Utils.createTorrentDropTarget(shell, false);
shell.pack();
if (!Utils.linkShellMetricsToConfig(shell, "OpenTorrentWindow")) {
Utils.centreWindow(shell);
}
resizeTables(3);
shell.open();
if (cSaveTo != null && !cSaveTo.isDisposed()) {
cSaveTo.setFocus();
}
try {
UIUpdaterSWT.getInstance().addUpdater(this);
} catch (Exception e) {
Debug.out(e);
}
}
protected void cmbDataDirChanged() {
if (bSkipDataDirModify) {
return;
}
sDestDir = cmbDataDir.getText();
int[] indexes = torrentTable.getSelectionIndices();
for (int i = 0; i < indexes.length; i++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indexes[i]);
//if (!info.allFilesMoving())
info.sDestDir = sDestDir;
}
torrentTable.clearAll();
checkSeedingMode();
if (!Utils.isCocoa || SWT.getVersion() > 3600) { // See Eclipse Bug 292449
File file = new File(sDestDir);
if (!file.isDirectory()) {
cmbDataDir.setBackground(Colors.colorErrorBG);
} else {
cmbDataDir.setBackground(null);
}
cmbDataDir.redraw();
cmbDataDir.update();
}
diskFreeInfoRefreshPending = true;
}
protected void okPressed() {
if (bClosed) {
return;
}
if ((torrentList.size() == 0 && downloaders.size() == 0)) {
close(true, false);
return;
}
File file = new File(cmbDataDir.getText());
File fileDefSavePath = new File(
COConfigurationManager.getStringParameter(PARAM_DEFSAVEPATH));
if (file.equals(fileDefSavePath) && !fileDefSavePath.isDirectory()) {
FileUtil.mkdirs(fileDefSavePath);
}
boolean isPathInvalid = cmbDataDir.getText().length() == 0 || file.isFile();
if (!isPathInvalid && !file.isDirectory()) {
MessageBoxShell mb = new MessageBoxShell(SWT.YES | SWT.NO
| SWT.ICON_QUESTION, "OpenTorrentWindow.mb.askCreateDir",
new String[] {
file.toString()
});
mb.open(null);
int doCreate = mb.waitUntilClosed();
if (doCreate == SWT.YES)
isPathInvalid = !FileUtil.mkdirs(file);
else {
cmbDataDir.setFocus();
return;
}
}
if (isPathInvalid) {
MessageBoxShell mb = new MessageBoxShell(SWT.OK | SWT.ICON_ERROR,
"OpenTorrentWindow.mb.noGlobalDestDir", new String[] {
file.toString()
});
mb.open(null);
cmbDataDir.setFocus();
return;
}
String sExistingFiles = "";
int iNumExistingFiles = 0;
for (int i = 0; i < torrentList.size(); i++) {
TorrentInfo info = (TorrentInfo) torrentList.get(i);
file = new File(info.getDataDir());
// Need to make directory now, or single file torrent will take the
// "dest dir" as their filename. ie:
// 1) Add single file torrent with named "hi.exe"
// 2) type a non-existant directory c:\test\moo
// 3) unselect the torrent
// 4) change the global def directory to a real one
// 5) click ok. "hi.exe" will be written as moo in c:\test
if (!file.isDirectory() && !FileUtil.mkdirs(file)) {
MessageBoxShell mb = new MessageBoxShell(SWT.OK | SWT.ICON_ERROR,
"OpenTorrentWindow.mb.noDestDir", new String[] {
file.toString(),
info.getTorrentName()
});
mb.open(null);
return;
}
if (!info.isValid) {
MessageBoxShell mb = new MessageBoxShell(SWT.OK | SWT.ICON_ERROR,
"OpenTorrentWindow.mb.notValid", new String[] {
info.getTorrentName()
});
mb.open(null);
return;
}
TorrentFileInfo[] files = info.getFiles();
for (int j = 0; j < files.length; j++) {
TorrentFileInfo fileInfo = files[j];
if (fileInfo.getDestFileFullName().exists()) {
sExistingFiles += fileInfo.orgFullName + " - " + info.getTorrentName()
+ "\n";
iNumExistingFiles++;
if (iNumExistingFiles > 5) {
// this has the potential effect of adding 5 files from the first
// torrent and then 1 file from each of the remaining torrents
break;
}
}
}
}
if (sExistingFiles.length() > 0) {
if (iNumExistingFiles > 5) {
sExistingFiles += MessageText.getString(
"OpenTorrentWindow.mb.existingFiles.partialList", new String[] {
"" + iNumExistingFiles
})
+ "\n";
}
MessageBoxShell mb = new MessageBoxShell(SWT.OK | SWT.CANCEL
| SWT.ICON_WARNING, "OpenTorrentWindow.mb.existingFiles",
new String[] {
sExistingFiles
});
mb.open(null);
if (mb.waitUntilClosed() != SWT.OK) {
return;
}
}
String sDefaultPath = COConfigurationManager.getStringParameter(PARAM_DEFSAVEPATH);
if (!sDestDir.equals(sDefaultPath)) {
// Move sDestDir to top of list
// First, check to see if sDestDir is already in the list
File fDestDir = new File(sDestDir);
int iDirPos = -1;
for (int i = 0; i < dirList.size(); i++) {
String sDirName = dirList.get(i);
File dir = new File(sDirName);
if (dir.equals(fDestDir)) {
iDirPos = i;
break;
}
}
// If already in list, remove it
if (iDirPos > 0 && iDirPos < dirList.size())
dirList.remove(iDirPos);
// and add it to the top
dirList.add(0, sDestDir);
// Limit
if (dirList.size() > 15)
dirList.remove(dirList.size() - 1);
// Temporary list cleanup
try {
for (int j = 0; j < dirList.size(); j++) {
File dirJ = new File(dirList.get(j));
for (int i = 0; i < dirList.size(); i++) {
try {
if (i == j)
continue;
File dirI = new File(dirList.get(i));
if (dirI.equals(dirJ)) {
dirList.remove(i);
// dirList shifted up, fix indexes
if (j > i)
j--;
i--;
}
} catch (Exception e) {
// Ignore
}
}
}
} catch (Exception e) {
// Ignore
}
COConfigurationManager.setParameter("saveTo_list", dirList);
COConfigurationManager.save();
}
if (COConfigurationManager.getBooleanParameter("DefaultDir.AutoUpdate")
&& !COConfigurationManager.getBooleanParameter("Use default data dir"))
COConfigurationManager.setParameter(PARAM_DEFSAVEPATH, sDestDir);
openTorrents();
close(true, false);
}
/**
* @param layout
* @return
*/
private GridLayout FixupLayout(GridLayout layout, boolean bFixMargin) {
if (Constants.isOSX) {
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
if (bFixMargin) {
layout.marginHeight = 0;
layout.marginWidth = 0;
}
}
return layout;
}
private void updateDataDirCombo() {
if (cmbDataDir == null) {
return;
}
try {
bSkipDataDirModify = true;
int[] indexes = torrentTable.getSelectionIndices();
if (indexes.length == 0) {
if (cmbDataDir.getItemCount() == 0) {
cmbDataDir.add(sDestDir);
}
cmbDataDir.setText(sDestDir);
return;
}
boolean allSame = true;
String lastDir = null;
for (int i = 0; i < indexes.length; i++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indexes[i]);
if (lastDir != null && !info.sDestDir.equals(lastDir)) {
allSame = false;
break;
}
lastDir = info.sDestDir;
}
if (allSame && lastDir != null) {
cmbDataDir.setText(lastDir);
sDestDir = lastDir;
} else {
cmbDataDir.setText("");
}
} finally {
bSkipDataDirModify = false;
}
}
private void updateStartModeCombo() {
if (cmbStartMode == null)
return;
int[] indexes = torrentTable.getSelectionIndices();
String[] sItemsText = new String[startModes.length];
int iMaxMatches = 0;
int iIndexToSelect = getDefaultStartMode();
for (int i = 0; i < startModes.length; i++) {
int iMatches = 0;
for (int j = 0; j < indexes.length; j++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indexes[j]);
if (info.iStartID == i)
iMatches++;
}
if (iMatches > iMaxMatches) {
iMaxMatches = iMatches;
iIndexToSelect = i;
}
String sText = MessageText.getString("OpenTorrentWindow.startMode."
+ startModes[i]);
if (iMatches > 0)
sText += " "
+ MessageText.getString("OpenTorrentWindow.xOfTotal", new String[] {
Integer.toString(iMatches),
Integer.toString(indexes.length)
});
sItemsText[i] = sText;
}
cmbStartMode.setItems(sItemsText);
cmbStartMode.select(iIndexToSelect);
cmbStartMode.layout(true);
}
private void updateQueueLocationCombo() {
if (cmbQueueLocation == null)
return;
int[] indexes = torrentTable.getSelectionIndices();
String[] sItemsText = new String[queueLocations.length];
int iMaxMatches = 0;
int iIndexToSelect = QUEUELOCATION_BOTTOM;
for (int i = 0; i < queueLocations.length; i++) {
int iMatches = 0;
for (int j = 0; j < indexes.length; j++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indexes[j]);
if (info.iQueueLocation == i)
iMatches++;
}
if (iMatches > iMaxMatches) {
iMaxMatches = iMatches;
iIndexToSelect = i;
}
String sText = MessageText.getString("OpenTorrentWindow.addPosition."
+ queueLocations[i]);
if (iMatches > 0)
sText += " "
+ MessageText.getString("OpenTorrentWindow.xOfTotal", new String[] {
Integer.toString(iMatches),
Integer.toString(indexes.length)
});
sItemsText[i] = sText;
}
cmbQueueLocation.setItems(sItemsText);
cmbQueueLocation.select(iIndexToSelect);
}
/**
* @param c
* @param keyListener
*/
private void setPasteKeyListener(Control c, KeyListener keyListener) {
if (!(c instanceof Text) && !(c instanceof Combo)
&& !(c instanceof Composite) || (c instanceof Table)) {
c.addKeyListener(keyListener);
}
if (c instanceof Composite) {
Control[] controls = ((Composite) c).getChildren();
for (int i = 0; i < controls.length; i++) {
setPasteKeyListener(controls[i], keyListener);
}
}
}
private void browseURL() {
new OpenUrlWindow(shellForChildren, null, null,
OpenTorrentWindow.this);
}
private void close(boolean dispose, boolean bCancel) {
stOpenTorrentWindow = null;
// Can't rely on (stOpenTorrentWindow == null) to check if we are closed
// since another thread may create another OpenTorrentWindow while
// we are closing this one.
bClosed = true;
try {
UIUpdaterSWT.getInstance().removeUpdater(this);
} catch (Exception e) {
Debug.out(e);
}
if (dispose && shell != null && !shell.isDisposed()) {
// We won't be recalled by disposal hook because we set bClosed
shell.dispose();
}
Utils.disposeSWTObjects(disposeList);
if (bCancel) {
List<TorrentDownloader> to_cancel;
synchronized( downloaders ){
to_cancel = new ArrayList<TorrentDownloader>( downloaders );
downloaders.clear();
}
if (to_cancel.size() > 0){
for (Iterator iter = to_cancel.iterator(); iter.hasNext();) {
TorrentDownloader element = (TorrentDownloader) iter.next();
element.cancel();
}
}
for (Iterator iter = torrentList.iterator(); iter.hasNext();) {
TorrentInfo info = (TorrentInfo) iter.next();
if (info.bDeleteFileOnCancel) {
File file = new File(info.sFileName);
if (file.exists())
file.delete();
}
}
torrentList.clear();
}
}
private void createTorrentListArea(Composite cArea) {
GridData gridData;
TableColumn tc;
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.numColumns = 2;
cArea.setLayout(layout);
torrentTable = new Table(cArea, SWT.MULTI | SWT.BORDER
| SWT.FULL_SELECTION | SWT.VIRTUAL);
gridData = new GridData(GridData.FILL_HORIZONTAL
| GridData.VERTICAL_ALIGN_FILL);
gridData.heightHint = 50;
gridData.widthHint = 450;
torrentTable.setLayoutData(gridData);
tc = new TableColumn(torrentTable, SWT.NULL);
Messages.setLanguageText(tc, "OpenTorrentWindow.torrentTable.name");
tc.setWidth(150);
tc = new TableColumn(torrentTable, SWT.NULL);
Messages.setLanguageText(tc, "OpenTorrentWindow.torrentTable.saveLocation");
tc.setWidth(150);
tc = new TableColumn(torrentTable, SWT.NULL);
Messages.setLanguageText(tc, "OpenTorrentWindow.startMode");
tc.setWidth(70);
tc = new TableColumn(torrentTable, SWT.NULL);
Messages.setLanguageText(tc, "OpenTorrentWindow.addPosition");
tc.setWidth(80);
if (Utils.LAST_TABLECOLUMN_EXPANDS)
tc.setData("Width", new Long(80));
torrentTable.addListener(SWT.SetData, new Listener() {
public void handleEvent(Event event) {
if (bClosed)
return;
TableItem item = (TableItem) event.item;
int index = torrentTable.indexOf(item);
if (index < 0)
return;
TorrentInfo info = (TorrentInfo) torrentList.get(index);
item.setText(new String[] {
info.getTorrentName(),
info.getDataDir(),
MessageText.getString("OpenTorrentWindow.startMode."
+ startModes[info.iStartID]),
MessageText.getString("OpenTorrentWindow.addPosition."
+ queueLocations[info.iQueueLocation])
});
if (!info.isValid) {
item.setForeground(Colors.red);
Font font = item.getFont();
FontData[] fd = font.getFontData();
for (int i = 0; i < fd.length; i++) {
fd[i].setStyle(SWT.ITALIC);
}
font = new Font(item.getDisplay(), fd);
disposeList.add(font);
item.setFont(font);
}
Utils.alternateRowBackground(item);
}
});
torrentTable.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
dataFiles.clear();
int[] indexes = torrentTable.getSelectionIndices();
for (int i = 0; i < indexes.length; i++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indexes[i]);
TorrentFileInfo[] files = info.getFiles();
dataFiles.addAll(Arrays.asList(files));
}
updateDataDirCombo();
updateStartModeCombo();
updateQueueLocationCombo();
dataFileTable.setItemCount(dataFiles.size());
dataFileTable.clearAll();
editCell(-1);
updateSize();
resizeTables(2);
}
});
torrentTable.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.character == SWT.DEL) {
deleteSelected(torrentTable, torrentList);
e.doit = false;
}
}
});
torrentTable.setHeaderVisible(true);
// Menu for tableTorrents
String sTitle;
Menu menu = new Menu(torrentTable.getShell());
MenuItem item;
sTitle = MessageText.getString("OpenTorrentWindow.startMode");
int userMode = COConfigurationManager.getIntParameter("User Mode");
for (int i = 0; i < startModes.length; i++) {
if (i == STARTMODE_FORCESTARTED && userMode == 0)
continue;
item = new MenuItem(menu, SWT.PUSH);
item.setData("Value", new Long(i));
item.setText(sTitle
+ ": "
+ MessageText.getString("OpenTorrentWindow.startMode."
+ startModes[i]));
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Long l = (Long) e.widget.getData("Value");
if (l != null) {
setSelectedStartMode(l.intValue());
checkSeedingMode();
}
}
});
}
item = new MenuItem(menu, SWT.SEPARATOR);
sTitle = MessageText.getString("OpenTorrentWindow.addPosition");
for (int i = 0; i < queueLocations.length; i++) {
item = new MenuItem(menu, SWT.PUSH);
item.setData("Value", new Long(i));
item.setText(sTitle
+ ": "
+ MessageText.getString("OpenTorrentWindow.addPosition."
+ queueLocations[i]));
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Long l = (Long) e.widget.getData("Value");
if (l != null) {
setSelectedQueueLocation(l.intValue());
}
}
});
}
item = new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.PUSH);
// steal text
Messages.setLanguageText(item, "MyTorrentsView.menu.remove");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
deleteSelected(torrentTable, torrentList);
}
});
item = new MenuItem(menu, SWT.PUSH);
Messages.setLanguageText(item,
"OpenTorrentWindow.fileList.changeDestination");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
int[] indexes = torrentTable.getSelectionIndices();
String sDefPath = sDestDir;
for (int i = 0; i < indexes.length; i++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indexes[i]);
TorrentFileInfo[] files = info.getFiles();
if (files.length == 1 && info.torrent.isSimpleTorrent()) {
changeFileDestination(new int[] {
0
});
} else {
DirectoryDialog dDialog = new DirectoryDialog(shellForChildren,
SWT.SYSTEM_MODAL);
dDialog.setFilterPath(sDefPath);
dDialog.setMessage(MessageText.getString("MainWindow.dialog.choose.savepath")
+ " (" + info.getTorrentName() + ")");
String sNewDir = dDialog.open();
if (sNewDir == null)
return;
File newDir = new File(sNewDir).getAbsoluteFile();
if(newDir.isDirectory())
sDefPath = sNewDir;
info.sDestDir = newDir.getParent();
if (info.sDestDir == null)
info.sDestDir = newDir.getPath();
info.sDestSubDir = newDir.getName();
for (int j = 0; j < files.length; j++) {
TorrentFileInfo fileInfo = files[j];
fileInfo.setDestFileName(null);
}
}
} // for i
checkSeedingMode();
updateDataDirCombo();
diskFreeInfoRefreshPending = true;
}
});
torrentTable.setMenu(menu);
Composite cTorrentListRight = new Composite(cArea, SWT.NONE);
gridData = new GridData();
cTorrentListRight.setLayoutData(gridData);
RowLayout rLayout = new RowLayout(SWT.VERTICAL);
rLayout.marginBottom = 0;
rLayout.marginLeft = 0;
rLayout.marginRight = 0;
rLayout.marginTop = 0;
if (!Constants.isOSX)
rLayout.spacing = 0;
rLayout.fill = true;
cTorrentListRight.setLayout(rLayout);
ImageLoader imageLoader = ImageLoader.getInstance();
Button torMoveUp = new Button(cTorrentListRight, SWT.PUSH);
imageLoader.setButtonImage(torMoveUp, "up");
torMoveUp.setToolTipText(MessageText.getString("Button.moveUp"));
torMoveUp.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
int[] indices = torrentTable.getSelectionIndices();
if (indices.length == 0)
return;
Arrays.sort(indices);
if (indices[0] == 0)
return;
for (int i = 0; i < indices.length; i++) {
int pos = indices[i];
Object save = torrentList.get(pos - 1);
torrentList.set(pos - 1, torrentList.get(pos));
torrentList.set(pos, save);
indices[i]--;
}
torrentTable.setSelection(indices);
torrentTable.clearAll();
}
});
Button torMoveDown = new Button(cTorrentListRight, SWT.PUSH);
imageLoader.setButtonImage(torMoveDown, "down");
torMoveDown.setToolTipText(MessageText.getString("Button.moveDown"));
torMoveDown.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
int[] indices = torrentTable.getSelectionIndices();
if (indices.length == 0)
return;
Arrays.sort(indices);
int max = indices.length - 1;
if (indices[max] == torrentList.size() - 1)
return;
for (int i = max; i >= 0; i--) {
int pos = indices[i];
Object save = torrentList.get(pos + 1);
torrentList.set(pos + 1, torrentList.get(pos));
torrentList.set(pos, save);
indices[i]++;
}
torrentTable.setSelection(indices);
torrentTable.clearAll();
}
});
Button torMoveRemove = new Button(cTorrentListRight, SWT.PUSH);
torMoveRemove.setToolTipText(MessageText.getString("OpenTorrentWindow.torrent.remove"));
imageLoader.setButtonImage(torMoveRemove, "delete");
torMoveRemove.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
deleteSelected(torrentTable, torrentList);
}
});
}
/**
* @param iLocation
*/
protected void setSelectedQueueLocation(int iLocation) {
int[] indices = torrentTable.getSelectionIndices();
for (int i = 0; i < indices.length; i++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indices[i]);
info.iQueueLocation = iLocation;
}
updateQueueLocationCombo();
torrentTable.clear(indices);
}
/**
* @param iStartID
*/
protected void setSelectedStartMode(int iStartID) {
int[] indices = torrentTable.getSelectionIndices();
for (int i = 0; i < indices.length; i++) {
TorrentInfo info = (TorrentInfo) torrentList.get(indices[i]);
info.iStartID = iStartID;
}
checkSeedingMode();
updateStartModeCombo();
torrentTable.clear(indices);
}
private void checkSeedingMode() {
// Check for seeding
for (int i = 0; i < torrentList.size(); i++) {
boolean bTorrentValid = true;
TorrentInfo info = (TorrentInfo) torrentList.get(i);
if (info.iStartID == STARTMODE_SEEDING) {
// check if all selected files exist
TorrentFileInfo[] files = info.getFiles();
for (int j = 0; j < files.length; j++) {
TorrentFileInfo fileInfo = files[j];
if (!fileInfo.bDownload)
continue;
File file = fileInfo.getDestFileFullName();
if (!file.exists()) {
fileInfo.isValid = false;
bTorrentValid = false;
} else if (!fileInfo.isValid) {
fileInfo.isValid = true;
}
}
}
info.isValid = bTorrentValid;
}
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
if (torrentTable != null && !torrentTable.isDisposed()) {
torrentTable.clearAll();
}
if (dataFileTable != null && !dataFileTable.isDisposed()) {
dataFileTable.clearAll();
editCell(-1);
}
}
});
}
private void deleteSelected(Table table, ArrayList list) {
int[] indexes = table.getSelectionIndices();
Arrays.sort(indexes);
for (int i = indexes.length - 1; i >= 0; i--) {
if (list.get(indexes[i]) instanceof TorrentInfo) {
TorrentInfo info = (TorrentInfo) list.get(indexes[i]);
if (info.bDeleteFileOnCancel) {
File file = new File(info.sFileName);
if (file.exists())
file.delete();
}
}
list.remove(indexes[i]);
}
table.setItemCount(list.size());
table.clearAll();
table.notifyListeners(SWT.Selection, new Event());
}
private void editCell(final int row)
{
Text oldEditor = (Text)dataFileTableEditor.getEditor();
if(row < 0 || row >= dataFileTable.getItemCount())
{
if(oldEditor != null && !oldEditor.isDisposed())
oldEditor.dispose();
return;
}
final Text newEditor = oldEditor == null || oldEditor.isDisposed() ? new Text(dataFileTable,SWT.BORDER) : oldEditor;
final TorrentFileInfo file = (TorrentFileInfo) dataFiles.get(row);
final String uneditedName = file.getDestFileName();
TableItem item = dataFileTable.getItem(row);
TableColumn column = dataFileTable.getColumn(EDIT_COLUMN_INDEX);
newEditor.setText(uneditedName);
newEditor.selectAll();
newEditor.forceFocus();
Rectangle leftAlignedBounds = item.getBounds(EDIT_COLUMN_INDEX);
leftAlignedBounds.width = dataFileTableEditor.minimumWidth = newEditor.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
if(leftAlignedBounds.intersection(dataFileTable.getClientArea()).equals(leftAlignedBounds))
dataFileTableEditor.horizontalAlignment = SWT.LEFT;
else
dataFileTableEditor.horizontalAlignment = SWT.RIGHT;
dataFileTable.deselectAll();
dataFileTable.select(row);
dataFileTable.showItem(item);
dataFileTable.showColumn(column);
class QuickEditListener implements ModifyListener, SelectionListener, KeyListener, TraverseListener {
public void modifyText(ModifyEvent e) {
file.setDestFileName(newEditor.getText());
try
{
file.getDestFileFullName().getCanonicalFile();
newEditor.setBackground(null);
} catch (IOException e1)
{
newEditor.setBackground(Colors.colorErrorBG);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
try
{
file.getDestFileFullName().getCanonicalFile();
} catch (IOException e1)
{
file.setDestFileName(uneditedName);
}
move(row,1,(Text)e.widget);
}
public void widgetSelected(SelectionEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
if(e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.ARROW_UP)
{
e.doit = false;
move(row,e.keyCode == SWT.ARROW_DOWN ? 1 : -1,(Text)e.widget);
}
}
public void keyTraversed(TraverseEvent e) {
if(e.detail == SWT.TRAVERSE_ESCAPE || e.detail == SWT.TRAVERSE_RETURN)
e.doit = false;
if(e.detail == SWT.TRAVERSE_ESCAPE)
editCell(-1);
}
private void move(int oldRow, int offset, Text current)
{
current.removeModifyListener(QuickEditListener.this);
current.removeSelectionListener(QuickEditListener.this);
current.removeKeyListener(QuickEditListener.this);
current.removeTraverseListener(QuickEditListener.this);
editCell(oldRow+offset);
dataFileTable.clear(oldRow);
}
}
QuickEditListener listener = new QuickEditListener();
newEditor.addModifyListener(listener);
newEditor.addSelectionListener(listener);
newEditor.addKeyListener(listener);
newEditor.addTraverseListener(listener);
dataFileTableEditor.setEditor(newEditor, dataFileTable.getItem(row), EDIT_COLUMN_INDEX);
}
private static final int EDIT_COLUMN_INDEX = 1;
private void createTableDataFiles(Composite cArea) {
GridData gridData;
TableColumn tc;
dataFileTable = new Table(cArea, SWT.BORDER | SWT.CHECK
| SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.MULTI);
dataFileTableEditor = new TableEditor(dataFileTable);
dataFileTableEditor.grabHorizontal = true;
dataFileTableEditor.minimumWidth = 50;
gridData = new GridData(GridData.FILL_BOTH);
gridData.heightHint = 80;
gridData.widthHint = 100;
dataFileTable.setLayoutData(gridData);
tc = new TableColumn(dataFileTable, SWT.NULL);
Messages.setLanguageText(tc, "OpenTorrentWindow.fileTable.fileName");
tc.setWidth(150);
tc = new TableColumn(dataFileTable, SWT.NULL);
Messages.setLanguageText(tc, "OpenTorrentWindow.fileTable.destinationName");
tc.setWidth(140);
tc = new TableColumn(dataFileTable, SWT.NULL);
Messages.setLanguageText(tc, "OpenTorrentWindow.fileTable.size");
tc.setAlignment(SWT.TRAIL);
tc.setWidth(90);
if (Utils.LAST_TABLECOLUMN_EXPANDS)
tc.setData("Width", new Long(90));
dataFileTable.addListener(SWT.SetData, new Listener() {
public void handleEvent(Event event) {
if (bClosed)
return;
final TableItem item = (TableItem) event.item;
int index = dataFileTable.indexOf(item);
final TorrentFileInfo file = (TorrentFileInfo) dataFiles.get(index);
item.setText(new String[] {
file.orgFullName,
file.isLinked() ? file.getDestFileFullName().toString() : file.getDestFileName(),
DisplayFormatters.formatByteCountToKiBEtc(file.lSize)
});
if (!file.isValid) {
item.setForeground(Colors.red);
Font font = item.getFont();
FontData[] fd = font.getFontData();
for (int i = 0; i < fd.length; i++) {
fd[i].setStyle(SWT.ITALIC);
}
font = new Font(item.getDisplay(), fd);
disposeList.add(font);
item.setFont(font);
}
Utils.alternateRowBackground(item);
Utils.setCheckedInSetData(item, file.bDownload);
item.setGrayed(!file.okToDisable());
}
});
dataFileTable.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
if (event.detail == SWT.CHECK) {
TableItem item = (TableItem) event.item;
int index = dataFileTable.indexOf(item);
TorrentFileInfo file = (TorrentFileInfo) dataFiles.get(index);
// don't allow disabling of small files
// XXX Maybe warning prompt instead?
if (!item.getChecked() && !file.okToDisable())
item.setChecked(true);
else
file.bDownload = item.getChecked();
updateSize();
}
}
});
dataFileTable.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
editCell(-1); // cleanup
if(e.button != 1)
return;
TableItem[] items = dataFileTable.getItems();
boolean found = false;
int i;
outer: for (i = 0; i < items.length; i++)
{
TableItem item = items[i];
Rectangle rect = item.getBounds();
if (e.y < rect.y || (rect.y + rect.height) < e.y)
continue;
for (int j = 0; j < dataFileTable.getColumnCount(); j++)
{
if (!item.getBounds(j).contains(e.x, e.y))
continue;
found = j == EDIT_COLUMN_INDEX;
break outer;
}
}
if(found)
editCell(i);
}
});
dataFileTable.setHeaderVisible(true);
Menu menu = new Menu(dataFileTable);
dataFileTable.setMenu(menu);
MenuItem item;
item = new MenuItem(menu, SWT.PUSH);
Messages.setLanguageText(item,
"OpenTorrentWindow.fileList.changeDestination");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
int[] indexes = dataFileTable.getSelectionIndices();
changeFileDestination(indexes);
}
});
Composite cBottomArea = new Composite(cArea, SWT.NONE);
GridLayout gLayout = new GridLayout();
gLayout.marginHeight = 0;
gLayout.marginWidth = 0;
gLayout.numColumns = 2;
gLayout.verticalSpacing = 0;
cBottomArea.setLayout(gLayout);
gridData = new GridData(GridData.FILL_HORIZONTAL);
cBottomArea.setLayoutData(gridData);
Composite cButtons = new Composite(cBottomArea, SWT.NONE);
RowLayout rLayout = new RowLayout(SWT.HORIZONTAL);
rLayout.wrap = false;
rLayout.marginBottom = 0;
rLayout.marginLeft = 0;
rLayout.marginRight = 0;
rLayout.marginTop = 0;
cButtons.setLayout(rLayout);
gridData = new GridData(SWT.END, SWT.BEGINNING, false, false);
gridData.verticalSpan = 2;
cButtons.setLayoutData(gridData);
Button btnSelectAll = new Button(cButtons, SWT.PUSH);
Messages.setLanguageText(btnSelectAll, "Button.selectAll");
btnSelectAll.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
dataFileTable.selectAll();
}
});
Button btnMarkSelected = new Button(cButtons, SWT.PUSH);
Messages.setLanguageText(btnMarkSelected, "Button.markSelected");
btnMarkSelected.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
int[] indexes = dataFileTable.getSelectionIndices();
for (int i = 0; i < indexes.length; i++) {
TorrentFileInfo file = (TorrentFileInfo) dataFiles.get(indexes[i]);
file.bDownload = true;
}
dataFileTable.clearAll();
updateSize();
}
});
Button btnUnmarkSelected = new Button(cButtons, SWT.PUSH);
Messages.setLanguageText(btnUnmarkSelected, "Button.unmarkSelected");
btnUnmarkSelected.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
int[] indexes = dataFileTable.getSelectionIndices();
for (int i = 0; i < indexes.length; i++) {
TorrentFileInfo file = (TorrentFileInfo) dataFiles.get(indexes[i]);
if (file.okToDisable())
file.bDownload = false;
}
dataFileTable.clearAll();
updateSize();
}
});
dataFileTableLabel = new Label(cBottomArea, SWT.WRAP);
dataFileTableLabel.setAlignment(SWT.RIGHT);
gridData = new GridData(SWT.END, SWT.BEGINNING, true, false);
dataFileTableLabel.setLayoutData(gridData);
diskspaceComp = new Composite(cBottomArea, SWT.NONE);
gLayout = new GridLayout(2, false);
gLayout.marginHeight = gLayout.marginWidth = 1;
gLayout.verticalSpacing = 0;
gLayout.horizontalSpacing = 15;
diskspaceComp.setLayout(gLayout);
gridData = new GridData(SWT.END, SWT.BEGINNING, true, false);
diskspaceComp.setLayoutData(gridData);
}
/**
* @param indexes
*/
protected void changeFileDestination(int[] indexes) {
for (int i = 0; i < indexes.length; i++) {
TorrentFileInfo fileInfo = (TorrentFileInfo) dataFiles.get(indexes[i]);
int style = (fileInfo.parent.iStartID == STARTMODE_SEEDING) ? SWT.OPEN
: SWT.SAVE;
FileDialog fDialog = new FileDialog(shellForChildren, SWT.SYSTEM_MODAL
| style);
String sFilterPath = fileInfo.getDestPathName();
String sFileName = fileInfo.orgFileName;
File f = new File(sFilterPath);
if (!f.isDirectory()) {
// Move up the tree until we have an existing path
while (sFilterPath != null) {
String parentPath = f.getParent();
if (parentPath == null)
break;
sFilterPath = parentPath;
f = new File(sFilterPath);
if (f.isDirectory())
break;
}
}
if (sFilterPath != null)
fDialog.setFilterPath(sFilterPath);
fDialog.setFileName(sFileName);
fDialog.setText(MessageText.getString("MainWindow.dialog.choose.savepath")
+ " (" + fileInfo.orgFullName + ")");
String sNewName = fDialog.open();
if (sNewName == null)
return;
if (fileInfo.parent.iStartID == STARTMODE_SEEDING) {
File file = new File(sNewName);
if (file.length() == fileInfo.lSize)
fileInfo.setFullDestName(sNewName);
else {
MessageBoxShell mb = new MessageBoxShell(SWT.OK,
"OpenTorrentWindow.mb.badSize", new String[] {
file.getName(),
fileInfo.orgFullName
});
mb.open(null);
}
} else
fileInfo.setFullDestName(sNewName);
} // for i
checkSeedingMode();
updateDataDirCombo();
diskFreeInfoRefreshPending = true;
}
/**
* Add Torrent(s) to Window using a text list of files/urls/torrents
*
* @param sClipText Text to parse
* @param bVerifyOnly Only check if there's potential torrents in the text,
* do not try to add the torrents.
*
* @return Number of torrents added or found. When bVerifyOnly, this number
* may not be exact.
*/
private int addTorrentsFromTextList(String sClipText, boolean bVerifyOnly) {
String[] lines = null;
int iNumFound = 0;
// # of consecutive non torrent lines
int iNoTorrentLines = 0;
// no use checking the whole clipboard (which may be megabytes)
final int MAX_CONSECUTIVE_NONTORRENT_LINES = 100;
final String[] splitters = {
"\r\n",
"\n",
"\r",
"\t"
};
for (int i = 0; i < splitters.length; i++)
if (sClipText.indexOf(splitters[i]) >= 0) {
lines = sClipText.split(splitters[i]);
break;
}
if (lines == null)
lines = new String[] {
sClipText
};
// Check if URL, 20 byte hash, Dir, or file
for (int i = 0; i < lines.length; i++) {
String line = lines[i].trim();
if (line.startsWith("\"") && line.endsWith("\"")) {
if (line.length() < 3) {
line = "";
} else {
line = line.substring(1, line.length() - 2);
}
}
boolean ok;
if (line == "") {
ok = false;
} else if (UrlUtils.isURL(line)) {
ok = true;
} else {
File file = new File(line);
if (!file.exists()) {
ok = false;
} else if (file.isDirectory()) {
if (bVerifyOnly) {
// XXX Could do a file count here, but the number found is not
// expected to be an exact number anyway, since we aren't
// event verifying if they are torrents.
ok = true;
} else {
iNumFound += addTorrents(lines[i], null);
ok = false;
}
} else {
ok = true;
}
}
if (!ok) {
iNoTorrentLines++;
lines[i] = null;
if (iNoTorrentLines > MAX_CONSECUTIVE_NONTORRENT_LINES)
break;
} else {
iNumFound++;
iNoTorrentLines = 0;
}
}
if (bVerifyOnly) {
return iNumFound;
}
return addTorrents(null, lines);
}
/**
* Add Torrent(s) to window
*
* @param sTorrentFilePath
* @param sTorrentFilenames
* @return # torrents actually added to list (or downloading)
*/
private int addTorrents(String sTorrentFilePath, String[] sTorrentFilenames) {
sTorrentFilePath = ensureTrailingSeparator(sTorrentFilePath);
// Process Directory
if (sTorrentFilePath != null && sTorrentFilenames == null) {
File dir = new File(sTorrentFilePath);
if (!dir.isDirectory())
return 0;
final File[] files = dir.listFiles(new FileFilter() {
public boolean accept(File arg0) {
if (FileUtil.getCanonicalFileName(arg0.getName()).endsWith(".torrent"))
return true;
if (FileUtil.getCanonicalFileName(arg0.getName()).endsWith(".tor"))
return true;
return false;
}
});
if (files.length == 0)
return 0;
sTorrentFilenames = new String[files.length];
for (int i = 0; i < files.length; i++)
sTorrentFilenames[i] = files[i].getName();
}
int numAdded = 0;
for (int i = 0; i < sTorrentFilenames.length; i++) {
if (sTorrentFilenames[i] == null || sTorrentFilenames[i] == "")
continue;
// Process File
String sFileName = ((sTorrentFilePath == null) ? "" : sTorrentFilePath)
+ sTorrentFilenames[i];
if (!new File(sFileName).exists()) {
// Process URL
String sURL = UrlUtils.parseTextForURL(sTorrentFilenames[i], true);
if (sURL != null) {
if (COConfigurationManager.getBooleanParameter("Add URL Silently")) {
new FileDownloadWindow(shellForChildren, sURL, null, null, this);
} else {
new OpenUrlWindow(shellForChildren, sURL, null, this);
}
numAdded++;
continue;
}
}
if (addTorrent(sFileName, sFileName) != null)
numAdded++;
}
if (numAdded > 0 && shell != null && torrentTable != null
&& !shell.isDisposed()) {
int iTotal = torrentList.size();
torrentTable.setItemCount(iTotal);
// select the ones we just added
torrentTable.select(iTotal - numAdded, iTotal - 1);
torrentTable.clearAll();
// select doesn't notify listeners? Do it manually.
torrentTable.notifyListeners(SWT.Selection, new Event());
resizeTables(1);
checkSeedingMode();
}
return numAdded;
}
private TorrentInfo addTorrent(String sFileName,
final String sOriginatingLocation) {
TorrentInfo info = null;
TOTorrent torrent = null;
File torrentFile;
boolean bDeleteFileOnCancel = false;
// Make a copy if user wants that. We'll delete it when we cancel, if we
// actually made a copy.
try {
if (sFileName.startsWith("file://localhost/")) {
sFileName = UrlUtils.decode(sFileName.substring(16));
}
final File fOriginal = new File(sFileName);
if (!fOriginal.isFile() || !fOriginal.exists()) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
if (shell == null)
new MessageSlideShell(Display.getCurrent(), SWT.ICON_ERROR,
"OpenTorrentWindow.mb.openError", fOriginal.toString(), new String[] {
UrlUtils.decode(sOriginatingLocation),
"Not a File"
}, -1 );
else {
MessageBoxShell mb = new MessageBoxShell(SWT.OK,
"OpenTorrentWindow.mb.openError", new String[] {
sOriginatingLocation,
"Not a File"
});
mb.open(null);
}
}
});
return null;
}
if (fOriginal.length() > 20*1024*1024) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
if (shell == null)
new MessageSlideShell(Display.getCurrent(), SWT.ICON_ERROR,
"OpenTorrentWindow.mb.openError", fOriginal.toString(), new String[] {
UrlUtils.decode(sOriginatingLocation),
"Too large to be a torrent"
}, -1 );
else {
MessageBoxShell mb = new MessageBoxShell(SWT.OK,
"OpenTorrentWindow.mb.openError", new String[] {
sOriginatingLocation,
"Too large to be a torrent"
});
mb.open(null);
}
}
});
return null;
}
torrentFile = TorrentUtils.copyTorrentFileToSaveDir(fOriginal, true);
bDeleteFileOnCancel = !fOriginal.equals(torrentFile);
// TODO if the files are still equal, and it isn't in the save
// dir, we should copy it to a temp file in case something
// re-writes it. No need to copy a torrent coming from the
// downloader though..
} catch (IOException e1) {
// Use torrent in wherever it is and hope for the best
// XXX Should error instead?
torrentFile = new File(sFileName);
}
VuzeFileHandler vfh = VuzeFileHandler.getSingleton();
VuzeFile vf = vfh.loadVuzeFile( torrentFile );
if ( vf != null ){
vfh.handleFiles( new VuzeFile[]{ vf }, VuzeFileComponent.COMP_TYPE_NONE );
return null;
}
// Do a quick check to see if it's a torrent
if (!TorrentUtil.isFileTorrent(torrentFile, shellForChildren,
torrentFile.getName())) {
if (bDeleteFileOnCancel) {
torrentFile.delete();
}
return null;
}
// Load up the torrent, see it it's real
try {
torrent = TorrentUtils.readFromFile(torrentFile, false);
} catch (final TOTorrentException e) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
if (shell == null)
new MessageSlideShell(Display.getCurrent(), SWT.ICON_ERROR,
"OpenTorrentWindow.mb.openError", Debug.getStackTrace(e),
new String[] {
sOriginatingLocation,
e.getMessage()
}, -1 );
else {
MessageBoxShell mb = new MessageBoxShell(SWT.OK,
"OpenTorrentWindow.mb.openError", new String[] {
sOriginatingLocation,
e.getMessage()
});
mb.open(null);
}
}
});
if (bDeleteFileOnCancel)
torrentFile.delete();
return null;
}
String sExistingName = null;
try {
HashWrapper hash = torrent.getHashWrapper();
if (hash != null) {
for (int i = 0; i < torrentList.size(); i++) {
try {
TorrentInfo existing = (TorrentInfo) torrentList.get(i);
if (existing.torrent.getHashWrapper().equals(hash)) {
//sExistingName = existing.sOriginatingLocation;
// Exit without warning when it already exists in list
if (bDeleteFileOnCancel)
torrentFile.delete();
return null;
}
} catch (Exception e) {
}
}
}
} catch (Exception e) {
}
DownloadManager existingDownload = null;
if (sExistingName == null) {
// Check if torrent already exists in gm, and add if not
existingDownload = (gm == null) ? null : gm.getDownloadManager(torrent);
if (existingDownload != null) {
sExistingName = existingDownload.getDisplayName();
}
}
if (sExistingName == null) {
info = new TorrentInfo(torrentFile.getAbsolutePath(), torrent,
bDeleteFileOnCancel);
info.sOriginatingLocation = sOriginatingLocation;
torrentList.add(info);
} else {
final String sfExistingName = sExistingName;
final DownloadManager fExistingDownload = existingDownload;
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
Shell mainShell = UIFunctionsManagerSWT.getUIFunctionsSWT().getMainShell();
if (Display.getDefault().getActiveShell() == null || !mainShell.isVisible() || mainShell.getMinimized() ) {
new MessageSlideShell(Display.getCurrent(), SWT.ICON_INFORMATION,
MSG_ALREADY_EXISTS, null, new String[] {
":" + sOriginatingLocation,
sfExistingName,
MessageText.getString(MSG_ALREADY_EXISTS_NAME),
}, new Object[] {
fExistingDownload
}, -1 );
} else {
MessageBoxShell mb = new MessageBoxShell(SWT.OK, MSG_ALREADY_EXISTS,
new String[] {
":" + sOriginatingLocation,
sfExistingName,
MessageText.getString(MSG_ALREADY_EXISTS_NAME),
});
mb.open(null);
}
}
});
if (bDeleteFileOnCancel)
torrentFile.delete();
}
return info;
}
/**
* Resize the columns of the tables to fit without horizontal scrollbar
*
* @param which bitwise field of which table to recalc
* Bit 0: torrents table
* Bit 1: Data Files Table
*/
private void resizeTables(int which) {
try {
TableColumn[] tcs;
if ((which & 1) > 0 && torrentTable != null
&& !torrentTable.isDisposed()) {
tcs = torrentTable.getColumns();
int newSize = torrentTable.getClientArea().width - 20;
int iLength = tcs.length;
if (Utils.LAST_TABLECOLUMN_EXPANDS) {
iLength--;
newSize -= ((Long) tcs[iLength].getData("Width")).intValue();
}
final int columnToExpand = 1;
for (int i = 0; i < iLength; i++)
if (i != columnToExpand)
newSize -= tcs[i].getWidth();
if (newSize > 10)
tcs[columnToExpand].setWidth(newSize);
}
// Adjust only first column
if ((which & 2) > 0 && dataFileTable != null
&& !dataFileTable.isDisposed()) {
tcs = dataFileTable.getColumns();
int newSize = dataFileTable.getClientArea().width - 20;
int iLength = tcs.length;
if (Utils.LAST_TABLECOLUMN_EXPANDS) {
iLength--;
newSize -= ((Long) tcs[iLength].getData("Width")).intValue();
}
final int columnToExpand = 0;
for (int i = 0; i < iLength; i++)
if (i != columnToExpand)
newSize -= tcs[i].getWidth();
if (newSize > 10)
tcs[columnToExpand].setWidth(newSize);
}
} catch (Exception e) {
// ignore
e.printStackTrace();
}
}
/**
* Open the torrents already added based on user choices
*
* @param sDataDir
*/
private void openTorrents() {
Utils.getOffOfSWTThread(new AERunnable() {
public void runSupport() {
_openTorrents();
}
});
}
private void _openTorrents() {
ArrayList addedTorrentsTop = new ArrayList();
for (int i = 0; i < torrentList.size(); i++) {
final TorrentInfo info = (TorrentInfo) torrentList.get(i);
try {
if (info.torrent == null)
continue;
int iStartState = (info.iStartID == STARTMODE_STOPPED)
? DownloadManager.STATE_STOPPED : DownloadManager.STATE_QUEUED;
final TorrentFileInfo[] files = info.getFiles();
byte[] hash = null;
try {
hash = info.torrent.getHash();
} catch (TOTorrentException e1) {
}
DownloadManager dm = gm.addDownloadManager(info.sFileName, hash, info.sDestDir, info.sDestSubDir, iStartState, true, info.iStartID == STARTMODE_SEEDING, new DownloadManagerInitialisationAdapter()
{
public void initialised(DownloadManager dm) {
DiskManagerFileInfo[] fileInfos = dm.getDiskManagerFileInfo();
boolean reorder_mode = COConfigurationManager.getBooleanParameter( "Enable reorder storage mode" );
int reorder_mode_min_mb = COConfigurationManager.getIntParameter( "Reorder storage mode min MB" );
try
{
dm.getDownloadState().suppressStateSave(true);
boolean[] toSkip = new boolean[fileInfos.length];
boolean[] toCompact = new boolean[fileInfos.length];
boolean[] toReorderCompact = new boolean[fileInfos.length];
int comp_num = 0;
int reorder_comp_num = 0;
for (int iIndex = 0; iIndex < fileInfos.length; iIndex++)
{
DiskManagerFileInfo fileInfo = fileInfos[iIndex];
if (iIndex >= 0 && iIndex < files.length && files[iIndex].lSize == fileInfo.getLength())
{
// Always pull destination file from fileInfo and not from
// TorrentFileInfo because the destination may have changed
// by magic code elsewhere
File fDest = fileInfo.getFile(true);
if (files[iIndex].isLinked()){
fDest = files[iIndex].getDestFileFullName();
// Can't use fileInfo.setLink(fDest) as it renames
// the existing file if there is one
dm.getDownloadState().setFileLink( fileInfo.getFile(false), fDest);
}
if (!files[iIndex].bDownload){
toSkip[iIndex] = true;
if (!fDest.exists()){
if ( reorder_mode && ( fileInfo.getLength()/(1024*1024)) >= reorder_mode_min_mb ){
toReorderCompact[iIndex] = true;
reorder_comp_num++;
}else{
toCompact[iIndex] = true;
comp_num++;
}
}
}
}
}
if ( comp_num > 0 ){
dm.getDiskManagerFileInfoSet().setStorageTypes(toCompact, DiskManagerFileInfo.ST_COMPACT);
}
if ( reorder_comp_num > 0 ){
dm.getDiskManagerFileInfoSet().setStorageTypes(toReorderCompact, DiskManagerFileInfo.ST_REORDER_COMPACT );
}
dm.getDiskManagerFileInfoSet().setSkipped(toSkip, true);
}finally{
dm.getDownloadState().suppressStateSave(false);
}
}
});
// If dm is null, most likely there was an error printed.. let's hope
// the user was notified and skip the error quietly.
// We don't have to worry about deleting the file (info.bDelete..)
// since gm.addDown.. will handle it.
if (dm == null)
continue;
if (info.iQueueLocation == QUEUELOCATION_TOP)
addedTorrentsTop.add(dm);
if (info.iStartID == STARTMODE_FORCESTARTED) {
dm.setForceStart(true);
}
} catch (Exception e) {
if (shell == null)
new MessageSlideShell(Display.getCurrent(), SWT.ICON_ERROR,
"OpenTorrentWindow.mb.openError", Debug.getStackTrace(e),
new String[] {
info.sOriginatingLocation,
e.getMessage()
}, -1 );
else {
MessageBoxShell mb = new MessageBoxShell(SWT.OK, "OpenTorrentWindow.mb.openError",
new String[] {
info.sOriginatingLocation,
e.getMessage()
});
mb.open(null);
}
}
}
if (addedTorrentsTop.size() > 0) {
DownloadManager[] dms = (DownloadManager[]) addedTorrentsTop.toArray(new DownloadManager[0]);
gm.moveTop(dms);
}
torrentList.clear();
}
private int getDefaultStartMode() {
if (bDefaultForSeeding)
return STARTMODE_SEEDING;
return (bOverrideStartModeToStopped || COConfigurationManager.getBooleanParameter("Default Start Torrents Stopped"))
? STARTMODE_STOPPED : STARTMODE_QUEUED;
}
// TorrentDownloaderCallBackInterface
public void TorrentDownloaderEvent(int state, final TorrentDownloader inf) {
// This method is run even if the window is closed.
// The default is to delete file on cancel
// We set this flag to false if we detected the file was not a torrent
if (!inf.getDeleteFileOnCancel()
&& (state == TorrentDownloader.STATE_CANCELLED
|| state == TorrentDownloader.STATE_ERROR
|| state == TorrentDownloader.STATE_DUPLICATE || state == TorrentDownloader.STATE_FINISHED)) {
activeTorrentCount--;
enableControl(ok, activeTorrentCount < 1);
// PARG - yes, this code sucks, added some sync here to prvent some errors but obviously
// it needs a complete rewrite
synchronized( downloaders ){
if (!downloaders.contains(inf))
return;
downloaders.remove(inf);
}
File file = inf.getFile();
// we already know it isn't a torrent.. we are just using the call
// to popup the message
TorrentUtil.isFileTorrent(file, shellForChildren, inf.getURL());
if (file.exists()) {
file.delete();
}
return;
}
if (state == TorrentDownloader.STATE_INIT) {
activeTorrentCount++;
enableControl(ok, activeTorrentCount < 1);
synchronized( downloaders ){
downloaders.add(inf);
}
} else if (state == TorrentDownloader.STATE_FINISHED) {
activeTorrentCount--;
enableControl(ok, activeTorrentCount < 1);
// This can be called more than once for each inf..
synchronized( downloaders ){
if (!downloaders.contains(inf))
return;
downloaders.remove(inf);
}
File file = inf.getFile();
if (addTorrent(file.getAbsolutePath(), inf.getURL()) == null) {
// addTorrent may not delete it on error if the downloader saved it
// to the place where user wants to store torrents (which is most
// likely)
if (file.exists())
file.delete();
} else {
if (shell != null && !shell.isDisposed()) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
torrentTable.setItemCount(torrentList.size());
torrentTable.clearAll();
// select the one we just added
torrentTable.select(torrentList.size() - 1);
// select doesn't notify listeners? Do it manually.
torrentTable.notifyListeners(SWT.Selection, new Event());
resizeTables(1);
}
});
} else {
String saveSilentlyDir = getSaveSilentlyDir();
if (saveSilentlyDir != null) {
sDestDir = saveSilentlyDir;
for (int i = 0; i < torrentList.size(); i++) {
final TorrentInfo info = (TorrentInfo) torrentList.get(i);
info.renameDuplicates();
}
openTorrents();
}
}
}
checkSeedingMode();
} else if (state == TorrentDownloader.STATE_CANCELLED
|| state == TorrentDownloader.STATE_ERROR
|| state == TorrentDownloader.STATE_DUPLICATE) {
activeTorrentCount--;
enableControl(ok, activeTorrentCount < 1);
synchronized( downloaders ){
downloaders.remove(inf);
}
} else if (state == TorrentDownloader.STATE_DOWNLOADING) {
int count = inf.getLastReadCount();
int numRead = inf.getTotalRead();
if (!inf.getDeleteFileOnCancel() && numRead >= 16384) {
inf.cancel();
} else if (numRead == count && count > 0) {
final byte[] bytes = inf.getLastReadBytes();
if (bytes[0] != 'd') {
inf.setDeleteFileOnCancel(false);
}
}
} else {
return;
}
}
/**
* Class to store one Torrent file's info. Used to populate table and store
* user's choices.
*/
private class TorrentInfo
{
/** Where the torrent came from. Could be a file, URL, or some other text */
String sOriginatingLocation;
/** Filename the .torrent is saved to */
String sFileName;
String sDestDir;
/** for multifiletorrents and change location */
String sDestSubDir;
TOTorrent torrent;
int iStartID;
int iQueueLocation;
boolean isValid;
boolean bDeleteFileOnCancel;
private TorrentFileInfo[] files = null;
/**
* Init
*
* @param sFileName
* @param torrent
* @param bDeleteFileOnCancel
*/
public TorrentInfo(String sFileName, TOTorrent torrent,
boolean bDeleteFileOnCancel) {
this.bDeleteFileOnCancel = bDeleteFileOnCancel;
this.sFileName = sFileName;
this.sOriginatingLocation = sFileName;
this.torrent = torrent;
this.sDestDir = OpenTorrentWindow.this.sDestDir;
iStartID = getDefaultStartMode();
iQueueLocation = QUEUELOCATION_BOTTOM;
isValid = true;
// Force a check on the encoding, will prompt user if we dunno
try {
LocaleTorrentUtil.getTorrentEncoding(TorrentInfo.this.torrent);
} catch (Exception e) {
e.printStackTrace();
}
if (getSaveSilentlyDir() == null
&& COConfigurationManager.getBooleanParameter("DefaultDir.BestGuess")
&& !COConfigurationManager.getBooleanParameter(PARAM_MOVEWHENDONE)) {
this.sDestDir = getSmartDestDir();
}
}
public String getParentDir() {
return sDestDir;
}
public void setParentDir(String parentDir)
{
sDestDir = parentDir;
}
public String getDataDir() {
if (torrent.isSimpleTorrent())
return sDestDir;
return new File(sDestDir, sDestSubDir == null ? FileUtil.convertOSSpecificChars(getTorrentName(), true)
: sDestSubDir).getPath();
}
public String getSmartDestDir() {
String sSmartDir = sDestDir;
try {
String name = getTorrentName();
String torrentFileName = new File(sFileName).getName().replaceFirst(
"\\.torrent$", "");
int totalSegmentsLengths = 0;
String[][] segments = {
name.split("[^a-zA-Z]+"),
torrentFileName.split("[^a-zA-Z]+")
};
List downloadManagers = gm.getDownloadManagers();
for (int x = 0; x < segments.length; x++) {
String[] segmentArray = segments[x];
for (int i = 0; i < segmentArray.length; i++) {
int l = segmentArray[i].length();
if (l <= 1) {
continue;
}
segmentArray[i] = segmentArray[i].toLowerCase();
totalSegmentsLengths += l;
}
}
int maxMatches = 0;
DownloadManager match = null;
for (Iterator iter = downloadManagers.iterator(); iter.hasNext();) {
DownloadManager dm = (DownloadManager) iter.next();
if (dm.getState() == DownloadManager.STATE_ERROR) {
continue;
}
int numMatches = 0;
String dmName = dm.getDisplayName().toLowerCase();
for (int x = 0; x < segments.length; x++) {
String[] segmentArray = segments[x];
for (int i = 0; i < segmentArray.length; i++) {
int l = segmentArray[i].length();
if (l <= 1) {
continue;
}
String segment = segmentArray[i];
if (dmName.indexOf(segment) >= 0) {
numMatches += l;
}
}
}
if (numMatches > maxMatches) {
maxMatches = numMatches;
match = dm;
}
}
if (match != null) {
//System.out.println(match + ": " + (maxMatches * 100 / totalSegmentsLengths) + "%\n");
int iMatchLevel = (maxMatches * 100 / totalSegmentsLengths);
if (iMatchLevel >= 30) {
File f = match.getSaveLocation();
if (!f.isDirectory() || match.getDiskManagerFileInfo().length > 1) {
// don't place data within another torrent's data dir
f = f.getParentFile();
}
if (f != null && f.isDirectory()) {
sSmartDir = f.getAbsolutePath();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return sSmartDir;
}
public TorrentFileInfo[] getFiles() {
if (files == null && torrent != null) {
TOTorrentFile[] tfiles = torrent.getFiles();
files = new TorrentFileInfo[tfiles.length];
for (int i = 0; i < files.length; i++) {
files[i] = new TorrentFileInfo(this, tfiles[i], i);
}
}
return files;
}
public String getTorrentName() {
return TorrentUtils.getLocalisedName(torrent);
}
public boolean allFilesMoving() {
TorrentFileInfo[] files = getFiles();
for (int j = 0; j < files.length; j++) {
if (files[j].isLinked()) {
return false;
}
}
return true;
}
public boolean allFilesExist() {
// check if all selected files exist
TorrentFileInfo[] files = getFiles();
for (int i = 0; i < files.length; i++) {
TorrentFileInfo fileInfo = files[i];
if (!fileInfo.bDownload)
continue;
File file = fileInfo.getDestFileFullName();
if (!file.exists() || file.length() != fileInfo.lSize) {
return false;
}
}
return true;
}
public void renameDuplicates() {
if (iStartID == STARTMODE_SEEDING
|| !COConfigurationManager.getBooleanParameter("DefaultDir.AutoSave.AutoRename")
|| allFilesExist()) {
return;
}
if (!torrent.isSimpleTorrent()) {
if (new File(getDataDir()).isDirectory()) {
File f;
int idx = 0;
do {
idx++;
f = new File(getDataDir() + "-" + idx);
} while (f.isDirectory());
sDestSubDir = f.getName();
}
} else {
// should only be one file
TorrentFileInfo[] fileInfos = getFiles();
for (int i = 0; i < fileInfos.length; i++) {
TorrentFileInfo info = fileInfos[i];
File file = info.getDestFileFullName();
int idx = 0;
while (file.exists()) {
idx++;
file = new File(info.getDestPathName(), idx + "-" + info.getDestFileName());
}
info.setDestFileName(file.getName());
}
}
}
/*
private Boolean has_multiple_small_files = null;
private boolean hasMultipleSmallFiles() {
TorrentFileInfo[] tfi_files = getFiles();
if (tfi_files.length <= MAX_NODOWNLOAD_COUNT)
return false;
int small_files_counted = 0;
for (int i=0; i<tfi_files.length; i++) {
if (tfi_files[i].lSize < MIN_NODOWNLOAD_SIZE) {
small_files_counted++;
if (small_files_counted > MAX_NODOWNLOAD_COUNT) {
return true;
}
}
}
return false;
}
*/
// Indicates whether all files in this torrent can be deselected
// (if not, then it occurs on a per-file basis).
public boolean okToDisableAll() {
return true;
/*
if (iStartID == STARTMODE_SEEDING)
return true;
// Do we have multiple small files? We'll allow all of them to
// be disabled if we do.
if (has_multiple_small_files == null) {
has_multiple_small_files = new Boolean(hasMultipleSmallFiles());
}
// You can disable all files if there are lots of small files.
return has_multiple_small_files.booleanValue();
*/
}
}
/**
* Class to store the file list of a Torrent. Used to populate table and
* store user's choices
*/
private class TorrentFileInfo
{
/** relative path + full file name as specified by the torrent */
final String orgFullName;
final String orgFileName;
long lSize;
boolean bDownload;
private String destFileName;
private String destPathName;
long iIndex;
boolean isValid;
final TorrentInfo parent;
/**
* Init
*
* @param parent
* @param torrentFile
* @param iIndex
*/
public TorrentFileInfo(TorrentInfo parent, TOTorrentFile torrentFile,
int iIndex) {
this.parent = parent;
lSize = torrentFile.getLength();
this.iIndex = iIndex;
bDownload = true;
isValid = true;
orgFullName = torrentFile.getRelativePath(); // translated to locale
orgFileName = new File(orgFullName).getName();
}
public void setFullDestName(String newFullName)
{
if(newFullName == null)
{
setDestPathName(null);
setDestFileName(null);
return;
}
File newPath = new File(newFullName);
setDestPathName(newPath.getParent());
setDestFileName(newPath.getName());
}
public void setDestPathName(String newPath)
{
if(parent.torrent.isSimpleTorrent())
parent.setParentDir(newPath);
else
destPathName = newPath;
}
public void setDestFileName (String newFileName)
{
if(orgFileName.equals(newFileName))
destFileName = null;
else
destFileName = newFileName;
}
public String getDestPathName() {
if (destPathName != null)
return destPathName;
if (parent.torrent.isSimpleTorrent())
return parent.getParentDir();
return new File(parent.getDataDir(), orgFullName).getParent();
}
public String getDestFileName() {
return destFileName == null ? orgFileName : destFileName;
}
public File getDestFileFullName() {
String path = getDestPathName();
String file = getDestFileName();
return new File(path,file);
}
public boolean okToDisable() {
return /* lSize >= MIN_NODOWNLOAD_SIZE || */parent.okToDisableAll();
}
public boolean isLinked()
{
return destFileName != null || destPathName != null;
}
}
private String ensureTrailingSeparator(String sPath) {
if (sPath == null || sPath.length() == 0 || sPath.endsWith(File.separator))
return sPath;
return sPath + File.separator;
}
/**
*
* @return Null if user doesn't want to save silently, or if no path set
*/
private static String getSaveSilentlyDir() {
boolean bUseDefault = COConfigurationManager.getBooleanParameter("Use default data dir");
if (!bUseDefault)
return null;
String sDefDir = "";
try {
sDefDir = COConfigurationManager.getDirectoryParameter(PARAM_DEFSAVEPATH);
} catch (IOException e) {
return null;
}
return (sDefDir == "") ? null : sDefDir;
}
private final static class Partition
{
public Partition(File root) {
this.root = root;
}
long bytesToConsume = 0;
long freeSpace = 0;
final File root;
}
private final static class FileStatsCacheItem
{
public FileStatsCacheItem(final File f) {
exists = f.exists();
if (exists)
freeSpace = FileUtil.getUsableSpace(f);
else
freeSpace = -1;
}
boolean exists;
long freeSpace;
}
private long getCachedDirFreeSpace(File directory) {
FileStatsCacheItem item = (FileStatsCacheItem) fileStatCache.get(directory);
if (item == null)
fileStatCache.put(directory, item = new FileStatsCacheItem(directory));
return item.freeSpace;
}
private boolean getCachedExistsStat(File directory) {
FileStatsCacheItem item = (FileStatsCacheItem) fileStatCache.get(directory);
if (item == null)
fileStatCache.put(directory, item = new FileStatsCacheItem(directory));
return item.exists;
}
private final Map fileStatCache = new WeakHashMap(20);
private final Map parentToRootCache = new WeakHashMap(20);
private volatile boolean diskFreeInfoRefreshPending = false;
private volatile boolean diskFreeInfoRefreshRunning = false;
// @see com.aelitis.azureus.ui.swt.uiupdater.UIUpdatable#getUpdateUIName()
public String getUpdateUIName() {
return "OpenTorrentWindow";
}
// @see com.aelitis.azureus.ui.swt.uiupdater.UIUpdatable#updateUI()
public void updateUI() {
if (bClosed) {
try {
UIUpdaterSWT.getInstance().removeUpdater(this);
} catch (Exception e) {
Debug.out(e);
}
return;
}
if (diskFreeInfoRefreshPending && !diskFreeInfoRefreshRunning
&& FileUtil.getUsableSpaceSupported()) {
diskFreeInfoRefreshRunning = true;
diskFreeInfoRefreshPending = false;
final HashSet FSroots = new HashSet(Arrays.asList(File.listRoots()));
final HashMap partitions = new HashMap();
for (int i = 0; i < torrentList.size(); i++) {
TorrentInfo tor = (TorrentInfo) torrentList.get(i);
TorrentFileInfo[] files = tor.getFiles();
for (int j = 0; j < files.length; j++) {
TorrentFileInfo file = files[j];
if (!file.bDownload)
continue;
// reduce each file to its partition root
File root = file.getDestFileFullName().getAbsoluteFile();
Partition part = (Partition) partitions.get((File) parentToRootCache.get(root.getParentFile()));
if (part == null) {
File next;
while (true) {
root = root.getParentFile();
next = root.getParentFile();
if (next == null)
break;
// bubble up until we hit an existing directory
if (!getCachedExistsStat(root) || !root.isDirectory())
continue;
// check for mount points (different free space) or simple loops in the directory structure
if (FSroots.contains(root) || root.equals(next)
|| getCachedDirFreeSpace(next) != getCachedDirFreeSpace(root))
break;
}
parentToRootCache.put(
file.getDestFileFullName().getAbsoluteFile().getParentFile(), root);
part = (Partition) partitions.get(root);
if (part == null) {
part = new Partition(root);
part.freeSpace = getCachedDirFreeSpace(root);
partitions.put(root, part);
}
}
part.bytesToConsume += file.lSize;
}
}
// clear child objects
Control[] labels = diskspaceComp.getChildren();
for (int i = 0; i < labels.length; i++)
labels[i].dispose();
// build labels
Iterator it = partitions.values().iterator();
while (it.hasNext()) {
Partition part = (Partition) it.next();
boolean filesTooBig = part.bytesToConsume > part.freeSpace;
Label l;
l = new Label(diskspaceComp, SWT.NONE);
l.setForeground(filesTooBig ? Colors.colorError : null);
l.setText(part.root.getPath());
l.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));
l = new Label(diskspaceComp, SWT.NONE);
l.setForeground(filesTooBig ? Colors.colorError : null);
l.setText(MessageText.getString("OpenTorrentWindow.diskUsage",
new String[] {
DisplayFormatters.formatByteCountToKiBEtc(part.bytesToConsume),
DisplayFormatters.formatByteCountToKiBEtc(part.freeSpace)
}));
l.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));
}
diskspaceComp.update();
diskspaceComp.getParent().getParent().getParent().layout(true, true);
diskFreeInfoRefreshRunning = false;
}
}
private void updateSize() {
/*
* determine info for selected torrents only
*/
long totalSize = 0;
long checkedSize = 0;
for (int i = 0; i < dataFiles.size(); i++) {
TorrentFileInfo file = (TorrentFileInfo) dataFiles.get(i);
totalSize += file.lSize;
if (file.bDownload) {
checkedSize += file.lSize;
}
}
// build string and set label
if (totalSize == 0) {
dataFileTableLabel.setText("");
} else {
dataFileTableLabel.setText(MessageText.getString(
"OpenTorrentWindow.filesInfo", new String[] {
DisplayFormatters.formatByteCountToKiBEtc(checkedSize),
DisplayFormatters.formatByteCountToKiBEtc(totalSize)
}));
}
dataFileTableLabel.update();
dataFileTableLabel.getParent().getParent().layout(true, true);
diskFreeInfoRefreshPending = true;
}
/**
* Convenience method for setting the enabled state of a control
* <P>This method may be called from any thread</p>
* @param control
* @param enabledState
*/
private void enableControl(final Control control, final boolean enabledState) {
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
if (control != null && false == control.isDisposed()) {
control.setEnabled(enabledState);
}
}
});
}
public static void main(String[] args) {
AzureusCore core = AzureusCoreFactory.create();
core.start();
Display display = Display.getDefault();
Colors.getInstance();
invoke(null, core.getGlobalManager());
//OpenTorrentWindow window = new OpenTorrentWindow(null, null, true);
while (stOpenTorrentWindow != null && !stOpenTorrentWindow.bClosed) {
if (!display.readAndDispatch())
display.sleep();
}
core.stop();
}
}