/**
* Created on May 12, 2010
*
* Copyright 2008 Vuze, Inc. 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; version 2 of the License only.
*
* 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.
*
* 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
*/
package org.gudy.azureus2.ui.swt.views;
import java.io.File;
import java.util.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.FileUtil;
import org.gudy.azureus2.plugins.ui.UIInputReceiver;
import org.gudy.azureus2.plugins.ui.UIInputReceiverListener;
import org.gudy.azureus2.ui.swt.Messages;
import org.gudy.azureus2.ui.swt.SimpleTextEntryWindow;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.shells.MessageBoxShell;
import org.gudy.azureus2.ui.swt.views.utils.ManagerUtils;
import com.aelitis.azureus.core.AzureusCoreOperation;
import com.aelitis.azureus.core.AzureusCoreOperationTask;
import com.aelitis.azureus.ui.common.table.TableRowCore;
import com.aelitis.azureus.ui.common.table.TableView;
/**
* @author TuxPaper
* @created May 12, 2010
*
*/
public class FilesViewMenuUtil
{
public static final Object PRIORITY_HIGH = Integer.valueOf(0);
public static final Object PRIORITY_NORMAL = Integer.valueOf(1);
public static final Object PRIORITY_NUMERIC = Integer.valueOf(99);
public static final Object PRIORITY_SKIPPED = Integer.valueOf(2);
public static final Object PRIORITY_DELETE = Integer.valueOf(3);
public static void fillMenu(final TableView tv,
final Menu menu, final DownloadManager manager,
final Object[] data_sources) {
Shell shell = menu.getShell();
boolean hasSelection = (data_sources.length > 0);
final MenuItem itemOpen = new MenuItem(menu, SWT.PUSH);
Messages.setLanguageText(itemOpen, "FilesView.menu.open");
Utils.setMenuItemImage(itemOpen, "run");
// Invoke open on enter, double click
menu.setDefaultItem(itemOpen);
// Explore (Copied from MyTorrentsView)
final boolean use_open_containing_folder = COConfigurationManager.getBooleanParameter("MyTorrentsView.menu.show_parent_folder_enabled");
final MenuItem itemExplore = new MenuItem(menu, SWT.PUSH);
Messages.setLanguageText(itemExplore, "MyTorrentsView.menu."
+ (use_open_containing_folder ? "open_parent_folder" : "explore"));
itemExplore.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
for (int i = data_sources.length - 1; i >= 0; i--) {
DiskManagerFileInfo info = (DiskManagerFileInfo) data_sources[i];
if (info != null) {
ManagerUtils.open(info, use_open_containing_folder);
}
}
}
});
itemExplore.setEnabled(hasSelection);
MenuItem itemRenameOrRetarget = null, itemRename = null, itemRetarget = null;
itemRenameOrRetarget = new MenuItem(menu, SWT.PUSH);
Messages.setLanguageText(itemRenameOrRetarget, "FilesView.menu.rename");
itemRenameOrRetarget.setData("rename", Boolean.valueOf(true));
itemRenameOrRetarget.setData("retarget", Boolean.valueOf(true));
itemRename = new MenuItem(menu, SWT.PUSH);
itemRetarget = new MenuItem(menu, SWT.PUSH);
Messages.setLanguageText(itemRename, "FilesView.menu.rename_only");
Messages.setLanguageText(itemRetarget, "FilesView.menu.retarget");
itemRename.setData("rename", Boolean.valueOf(true));
itemRename.setData("retarget", Boolean.valueOf(false));
itemRetarget.setData("rename", Boolean.valueOf(false));
itemRetarget.setData("retarget", Boolean.valueOf(true));
final MenuItem itemPriority = new MenuItem(menu, SWT.CASCADE);
Messages.setLanguageText(itemPriority, "FilesView.menu.setpriority");
final Menu menuPriority = new Menu(shell, SWT.DROP_DOWN);
itemPriority.setMenu(menuPriority);
final MenuItem itemHigh = new MenuItem(menuPriority, SWT.CASCADE);
itemHigh.setData("Priority", PRIORITY_HIGH);
Messages.setLanguageText(itemHigh, "FilesView.menu.setpriority.high");
final MenuItem itemLow = new MenuItem(menuPriority, SWT.CASCADE);
itemLow.setData("Priority", PRIORITY_NORMAL);
Messages.setLanguageText(itemLow, "FilesView.menu.setpriority.normal");
final MenuItem itemNumeric = new MenuItem(menuPriority, SWT.CASCADE);
itemNumeric.setData("Priority", PRIORITY_NUMERIC);
Messages.setLanguageText(itemNumeric, "FilesView.menu.setpriority.numeric");
final MenuItem itemSkipped = new MenuItem(menuPriority, SWT.CASCADE);
itemSkipped.setData("Priority", PRIORITY_SKIPPED);
Messages.setLanguageText(itemSkipped, "FilesView.menu.setpriority.skipped");
final MenuItem itemDelete = new MenuItem(menuPriority, SWT.CASCADE);
itemDelete.setData("Priority", PRIORITY_DELETE);
Messages.setLanguageText(itemDelete, "wizard.multitracker.delete"); // lazy but we're near release
new MenuItem(menu, SWT.SEPARATOR);
if (!hasSelection) {
itemOpen.setEnabled(false);
itemPriority.setEnabled(false);
itemRenameOrRetarget.setEnabled(false);
itemRename.setEnabled(false);
itemRetarget.setEnabled(false);
return;
}
boolean open = true;
boolean all_compact = true;
boolean all_skipped = true;
boolean all_priority = true;
boolean all_not_priority = true;
DiskManagerFileInfo[] dmi_array = new DiskManagerFileInfo[data_sources.length];
System.arraycopy(data_sources, 0, dmi_array, 0, data_sources.length);
int[] storage_types = manager.getStorageType(dmi_array);
for (int i = 0; i < dmi_array.length; i++) {
DiskManagerFileInfo file_info = dmi_array[i];
if (open && file_info.getAccessMode() != DiskManagerFileInfo.READ) {
open = false;
}
if (all_compact && storage_types[i] != DiskManagerFileInfo.ST_COMPACT && storage_types[i] != DiskManagerFileInfo.ST_REORDER_COMPACT ) {
all_compact = false;
}
if (all_skipped || all_priority || all_not_priority) {
if (file_info.isSkipped()) {
all_priority = false;
all_not_priority = false;
} else {
all_skipped = false;
// Only do this check if we need to.
if (all_not_priority || all_priority) {
if (file_info.getPriority() > 0 ) {
all_not_priority = false;
} else {
all_priority = false;
}
}
}
}
}
// we can only open files if they are read-only
itemOpen.setEnabled(open);
// can't rename files for non-persistent downloads (e.g. shares) as these
// are managed "externally"
itemRenameOrRetarget.setEnabled(manager.isPersistent());
itemRename.setEnabled(manager.isPersistent());
itemRetarget.setEnabled(manager.isPersistent());
itemSkipped.setEnabled(!all_skipped);
itemHigh.setEnabled(!all_priority);
itemLow.setEnabled(!all_not_priority);
itemDelete.setEnabled(!all_compact);
itemOpen.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
for (int i = 0; i < data_sources.length; i++) {
DiskManagerFileInfo info = (DiskManagerFileInfo) data_sources[i];
if (info != null && info.getAccessMode() == DiskManagerFileInfo.READ) {
Utils.launch(info);
}
}
}
});
Listener rename_listener = new Listener() {
public void handleEvent(Event event) {
final boolean rename_it = ((Boolean) event.widget.getData("rename")).booleanValue();
final boolean retarget_it = ((Boolean) event.widget.getData("retarget")).booleanValue();
rename(tv, manager, data_sources, rename_it, retarget_it);
}
};
itemRenameOrRetarget.addListener(SWT.Selection, rename_listener);
itemRename.addListener(SWT.Selection, rename_listener);
itemRetarget.addListener(SWT.Selection, rename_listener);
Listener priorityListener = new Listener() {
public void handleEvent(Event event) {
final int priority = ((Integer) event.widget.getData("Priority")).intValue();
Utils.getOffOfSWTThread(new AERunnable() {
public void runSupport() {
changePriority(priority, data_sources);
}
});
}
};
itemNumeric.addListener(SWT.Selection, priorityListener);
itemHigh.addListener(SWT.Selection, priorityListener);
itemLow.addListener(SWT.Selection, priorityListener);
itemSkipped.addListener(SWT.Selection, priorityListener);
itemDelete.addListener(SWT.Selection, priorityListener);
}
public static void rename(final TableView tv, final DownloadManager manager,
final Object[] datasources, boolean rename_it, boolean retarget_it) {
if (manager == null) {
return;
}
if (datasources.length == 0) {
return;
}
String save_dir = null;
if (!rename_it && retarget_it) {
save_dir = askForSaveDirectory((DiskManagerFileInfo) datasources[0]);
if (save_dir == null) {
return;
}
}
boolean paused = false;
try {
for (int i = 0; i < datasources.length; i++) {
final DiskManagerFileInfo fileInfo = (DiskManagerFileInfo) datasources[i];
File existing_file = fileInfo.getFile(true);
File f_target = null;
if (rename_it && retarget_it) {
String s_target = askForRetargetedFilename(fileInfo);
if (s_target != null)
f_target = new File(s_target);
} else if (rename_it) {
String s_target = askForRenameFilename(fileInfo);
if (s_target != null)
f_target = new File(existing_file.getParentFile(), s_target);
} else {
// Parent directory has changed.
f_target = new File(save_dir, existing_file.getName());
}
// So are we doing a rename?
// If the user has decided against it - abort the op.
if (f_target == null) {
return;
}
if (!paused) {
paused = manager.pause();
}
if (f_target.exists()) {
// Nothing to do.
if (f_target.equals(existing_file))
continue;
// A rewrite will occur, so we need to ask the user's permission.
else if (existing_file.exists() && !askCanOverwrite(existing_file))
continue;
// If we reach here, then it means we are doing a real move, but there is
// no existing file.
}
final File ff_target = f_target;
final TableRowCore row = tv == null ? null : tv.getRow(datasources[i]);
Utils.getOffOfSWTThread(new AERunnable() {
public void runSupport() {
moveFile(manager, fileInfo, ff_target);
if (row != null) {
row.invalidate();
}
}
});
}
} finally {
if (paused) {
manager.resume();
}
}
}
public static void changePriority(Object type, final Object[] datasources) {
if (datasources == null || datasources.length == 0) {
return;
}
if (type == PRIORITY_NUMERIC) {
changePriorityManual(datasources);
return;
}
Map<DownloadManager, ArrayList<DiskManagerFileInfo>> mapDMtoDMFI = new HashMap<DownloadManager, ArrayList<DiskManagerFileInfo>>();
DiskManagerFileInfo[] file_infos = new DiskManagerFileInfo[datasources.length];
for (int i = 0; i < datasources.length; i++) {
file_infos[i] = (DiskManagerFileInfo) datasources[i];
DownloadManager dm = file_infos[i].getDownloadManager();
ArrayList<DiskManagerFileInfo> listFileInfos = mapDMtoDMFI.get(dm);
if (listFileInfos == null) {
listFileInfos = new ArrayList<DiskManagerFileInfo>(1);
mapDMtoDMFI.put(dm, listFileInfos);
}
listFileInfos.add(file_infos[i]);
if (type == PRIORITY_NORMAL || type == PRIORITY_HIGH) {
file_infos[i].setPriority(type == PRIORITY_HIGH ? 1 : 0);
}
}
boolean skipped = (type == PRIORITY_SKIPPED || type == PRIORITY_DELETE);
boolean delete_action = (type == PRIORITY_DELETE);
for (DownloadManager dm : mapDMtoDMFI.keySet()) {
ArrayList<DiskManagerFileInfo> list = mapDMtoDMFI.get(dm);
DiskManagerFileInfo[] fileInfos = list.toArray(new DiskManagerFileInfo[0]);
boolean paused = setSkipped(dm, fileInfos, skipped, delete_action);
if (paused) {
dm.resume();
}
}
}
private static void changePriorityManual(final Object[] datasources) {
SimpleTextEntryWindow entryWindow = new SimpleTextEntryWindow(
"FilesView.dialog.priority.title",
"FilesView.dialog.priority.text");
entryWindow.prompt(new UIInputReceiverListener() {
public void UIInputReceiverClosed(UIInputReceiver entryWindow) {
if (!entryWindow.hasSubmittedInput()) {
return;
}
String sReturn = entryWindow.getSubmittedInput();
if (sReturn == null)
return;
int priority = -1;
try {
priority = Integer.valueOf(sReturn).intValue();
} catch (NumberFormatException er) {
// Ignore
}
if ( priority >= 0 ){
Map<DownloadManager, ArrayList<DiskManagerFileInfo>> mapDMtoDMFI = new HashMap<DownloadManager, ArrayList<DiskManagerFileInfo>>();
DiskManagerFileInfo[] file_infos = new DiskManagerFileInfo[datasources.length];
for (int i = 0; i < datasources.length; i++) {
file_infos[i] = (DiskManagerFileInfo) datasources[i];
DownloadManager dm = file_infos[i].getDownloadManager();
ArrayList<DiskManagerFileInfo> listFileInfos = mapDMtoDMFI.get(dm);
if (listFileInfos == null) {
listFileInfos = new ArrayList<DiskManagerFileInfo>(1);
mapDMtoDMFI.put(dm, listFileInfos);
}
listFileInfos.add(file_infos[i]);
file_infos[i].setPriority(priority);
}
for (DownloadManager dm : mapDMtoDMFI.keySet()) {
ArrayList<DiskManagerFileInfo> list = mapDMtoDMFI.get(dm);
DiskManagerFileInfo[] fileInfos = list.toArray(new DiskManagerFileInfo[0]);
boolean paused = setSkipped(dm, fileInfos, false, false);
if (paused) {
dm.resume();
}
}
}
}
});
}
private static String askForRenameFilename(DiskManagerFileInfo fileInfo) {
SimpleTextEntryWindow dialog = new SimpleTextEntryWindow(
"FilesView.rename.filename.title", "FilesView.rename.filename.text");
dialog.setPreenteredText(fileInfo.getFile(true).getName(), false); // false -> it's not "suggested", it's a previous value
dialog.allowEmptyInput(false);
dialog.prompt();
if (!dialog.hasSubmittedInput()) {
return null;
}
return dialog.getSubmittedInput();
}
private static String askForRetargetedFilename(DiskManagerFileInfo fileInfo) {
FileDialog fDialog = new FileDialog(Utils.findAnyShell(), SWT.SYSTEM_MODAL
| SWT.SAVE);
File existing_file = fileInfo.getFile(true);
fDialog.setFilterPath(existing_file.getParent());
fDialog.setFileName(existing_file.getName());
fDialog.setText(MessageText.getString("FilesView.rename.choose.path"));
return fDialog.open();
}
private static String askForSaveDirectory(DiskManagerFileInfo fileInfo) {
DirectoryDialog dDialog = new DirectoryDialog(Utils.findAnyShell(),
SWT.SYSTEM_MODAL | SWT.SAVE);
File current_dir = fileInfo.getFile(true).getParentFile();
dDialog.setFilterPath(current_dir.getPath());
dDialog.setText(MessageText.getString("FilesView.rename.choose.path.dir"));
return dDialog.open();
}
private static boolean askCanOverwrite(File file) {
MessageBoxShell mb = new MessageBoxShell(SWT.OK | SWT.CANCEL,
MessageText.getString("FilesView.rename.confirm.delete.title"),
MessageText.getString("FilesView.rename.confirm.delete.text",
new String[] {
file.toString()
}));
mb.setDefaultButtonUsingStyle(SWT.OK);
mb.setRememberOnlyIfButton(0);
mb.setRemember("FilesView.messagebox.rename.id", true, null);
mb.setLeftImage(SWT.ICON_WARNING);
mb.open(null);
return mb.waitUntilClosed() == SWT.OK;
}
// same code is used in tableitems.files.NameItem
private static void moveFile(final DownloadManager manager,
final DiskManagerFileInfo fileInfo, final File target) {
// this behaviour should be put further down in the core but I'd rather not
// do so close to release :(
manager.setUserData("is_changing_links", true);
try {
final boolean[] result = {
false
};
FileUtil.runAsTask(new AzureusCoreOperationTask() {
public void run(AzureusCoreOperation operation) {
result[0] = fileInfo.setLink(target);
manager.setUserData("is_changing_links", false);
if (!result[0]) {
new MessageBoxShell(SWT.ICON_ERROR | SWT.OK,
MessageText.getString("FilesView.rename.failed.title"),
MessageText.getString("FilesView.rename.failed.text")).open(null);
}
}
});
} catch (Exception e) {
manager.setUserData("is_changing_links", false);
}
}
// Returns true if it was paused here.
private static boolean setSkipped(DownloadManager manager,
DiskManagerFileInfo[] infos, boolean skipped, boolean delete_action) {
// if we're not managing the download then don't do anything other than
// change the file's priority
if (!manager.isPersistent()) {
for (int i = 0; i < infos.length; i++) {
infos[i].setSkipped(skipped);
}
return false;
}
int[] existing_storage_types = manager.getStorageType(infos);
int nbFiles = manager.getDiskManagerFileInfoSet().nbFiles();
boolean[] setLinear = new boolean[nbFiles];
boolean[] setCompact = new boolean[nbFiles];
boolean[] setReorder = new boolean[nbFiles];
boolean[] setReorderCompact = new boolean[nbFiles];
int compactCount = 0;
int linearCount = 0;
int reorderCount = 0;
int reorderCompactCount = 0;
if (infos.length > 1) {
}
// This should hopefully reduce the number of "exists" checks.
File save_location = manager.getAbsoluteSaveLocation();
boolean root_exists = save_location.isDirectory()
|| (infos.length <= 1 && save_location.exists());
boolean type_has_been_changed = false;
boolean requires_pausing = false;
for (int i = 0; i < infos.length; i++) {
int existing_storage_type = existing_storage_types[i];
int compact_target;
int non_compact_target;
if ( existing_storage_type == DiskManagerFileInfo.ST_COMPACT || existing_storage_type == DiskManagerFileInfo.ST_LINEAR ){
compact_target = DiskManagerFileInfo.ST_COMPACT;
non_compact_target = DiskManagerFileInfo.ST_LINEAR;
}else{
compact_target = DiskManagerFileInfo.ST_REORDER_COMPACT;
non_compact_target = DiskManagerFileInfo.ST_REORDER;
}
int new_storage_type;
if (skipped) {
// Check to see if the file exists, but try to avoid doing an
// actual disk check if possible.
File existing_file = infos[i].getFile(true);
// Avoid performing existing_file.exists if we know that it is meant
// to reside in the default save location and that location does not
// exist.
boolean perform_check;
if (root_exists) {
perform_check = true;
} else if (FileUtil.isAncestorOf(save_location, existing_file)) {
perform_check = false;
} else {
perform_check = true;
}
if (perform_check && existing_file.exists()) {
if (delete_action) {
MessageBoxShell mb = new MessageBoxShell(SWT.OK | SWT.CANCEL,
MessageText.getString("FilesView.rename.confirm.delete.title"),
MessageText.getString("FilesView.rename.confirm.delete.text",
new String[] {
existing_file.toString()
}));
mb.setDefaultButtonUsingStyle(SWT.OK);
mb.setRememberOnlyIfButton(0);
mb.setRemember("FilesView.messagebox.delete.id", false, null);
mb.setLeftImage(SWT.ICON_WARNING);
mb.open(null);
boolean wants_to_delete = mb.waitUntilClosed() == SWT.OK;
if ( wants_to_delete ){
new_storage_type = compact_target;
}else{
new_storage_type = non_compact_target;
}
}else{
new_storage_type = non_compact_target;
}
}
// File does not exist.
else {
new_storage_type = compact_target;
}
}else{
new_storage_type = non_compact_target;
}
boolean has_changed = existing_storage_type != new_storage_type;
type_has_been_changed |= has_changed;
requires_pausing |= (has_changed && ( new_storage_type == DiskManagerFileInfo.ST_COMPACT || new_storage_type == DiskManagerFileInfo.ST_REORDER_COMPACT ));
type_has_been_changed = existing_storage_type != new_storage_type;
if (new_storage_type == DiskManagerFileInfo.ST_COMPACT) {
setCompact[infos[i].getIndex()] = true;
compactCount++;
} else if (new_storage_type == DiskManagerFileInfo.ST_LINEAR) {
setLinear[infos[i].getIndex()] = true;
linearCount++;
} else if (new_storage_type == DiskManagerFileInfo.ST_REORDER) {
setReorder[infos[i].getIndex()] = true;
reorderCount++;
} else if (new_storage_type == DiskManagerFileInfo.ST_REORDER_COMPACT) {
setReorderCompact[infos[i].getIndex()] = true;
reorderCompactCount++;
}
}
boolean ok = true;
boolean paused = false;
if (type_has_been_changed) {
if (requires_pausing)
paused = manager.pause();
if (linearCount > 0)
ok &= Arrays.equals(
setLinear,
manager.getDiskManagerFileInfoSet().setStorageTypes(setLinear,
DiskManagerFileInfo.ST_LINEAR));
if (compactCount > 0)
ok &= Arrays.equals(
setCompact,
manager.getDiskManagerFileInfoSet().setStorageTypes(setCompact,
DiskManagerFileInfo.ST_COMPACT));
if (reorderCount > 0)
ok &= Arrays.equals(
setReorder,
manager.getDiskManagerFileInfoSet().setStorageTypes(setReorder,
DiskManagerFileInfo.ST_REORDER));
if (reorderCompactCount > 0)
ok &= Arrays.equals(
setReorderCompact,
manager.getDiskManagerFileInfoSet().setStorageTypes(setReorderCompact,
DiskManagerFileInfo.ST_REORDER_COMPACT));
}
if (ok) {
for (int i = 0; i < infos.length; i++) {
infos[i].setSkipped(skipped);
}
}
return paused;
}
}