diff --git a/glade/files_dialog.glade b/glade/files_dialog.glade
new file mode 100644
index 000000000..9869cac6d
--- /dev/null
+++ b/glade/files_dialog.glade
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+ 5
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Deluge File Selection
+ GTK_WINDOW_TOPLEVEL
+ GTK_WIN_POS_CENTER_ON_PARENT
+ False
+ 550
+ 550
+ True
+ True
+ True
+ True
+ True
+ GDK_WINDOW_TYPE_HINT_DIALOG
+ GDK_GRAVITY_NORTH_WEST
+ True
+ False
+ False
+
+
+
+ True
+ False
+ 1
+
+
+
+ True
+ GTK_BUTTONBOX_END
+
+
+
+ True
+ gtk-cancel
+ True
+ GTK_RELIEF_NORMAL
+ True
+ 0
+
+
+
+
+
+ True
+ gtk-ok
+ True
+ GTK_RELIEF_NORMAL
+ True
+ 1
+
+
+
+
+ 0
+ False
+ True
+ GTK_PACK_END
+
+
+
+
+
+ True
+ True
+ GTK_POLICY_ALWAYS
+ GTK_POLICY_ALWAYS
+ GTK_SHADOW_NONE
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+
+
+
+
+ 2
+ True
+ True
+
+
+
+
+
+
+
diff --git a/glade/preferences_dialog.glade b/glade/preferences_dialog.glade
index 145523f18..b7cc5dabd 100644
--- a/glade/preferences_dialog.glade
+++ b/glade/preferences_dialog.glade
@@ -158,35 +158,55 @@
2
12
-
+
True
- 10
-
+
True
- The number of active torrents that Deluge will run. Set to -1 for unlimited.
- 0
- Maximum simultaneous active torrents:
+ 10
+
+
+ True
+ The number of active torrents that Deluge will run. Set to -1 for unlimited.
+ 0
+ Maximum simultaneous active torrents:
+
+
+ False
+
+
+
+
+ True
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ The number of active torrents that Deluge will run. Set to -1 for unlimited.
+ 1
+ -1 -1 1000 1 10 10
+ 1
+ True
+ GTK_UPDATE_IF_VALID
+
+
+ False
+ 2
+ 1
+
+
-
- False
-
-
+
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- The number of active torrents that Deluge will run. Set to -1 for unlimited.
- 1
- -1 -1 1000 1 10 10
- 1
- True
- GTK_UPDATE_IF_VALID
+ Enable selecting files for torrents before loading
+ Enable selecting files for torrents before loading
+ 0
+ True
False
- 2
1
diff --git a/src/dialogs.py b/src/dialogs.py
index e722184b8..160f67ebd 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -35,6 +35,7 @@ import gtk
import gtk.glade
import os
import os.path
+import files
PREFS_FILENAME = "prefs.state"
@@ -85,6 +86,7 @@ class PreferencesDlg:
self.glade.get_widget("finished_path_button").set_sensitive(False)
self.glade.get_widget("finished_path_button").set_filename(self.preferences.get("default_finished_path"))
self.glade.get_widget("download_path_button").set_filename(self.preferences.get("default_download_path"))
+ self.glade.get_widget("chk_enable_files_dialog").set_active(self.preferences.get("enable_files_dialog"))
self.glade.get_widget("chk_compact").set_active(self.preferences.get("use_compact_storage"))
self.glade.get_widget("active_port_label").set_text(str(self.parent.manager.get_state()['port']))
self.glade.get_widget("spin_port_min").set_value(self.preferences.get("listen_on")[0])
@@ -131,6 +133,7 @@ class PreferencesDlg:
self.preferences.set("default_download_path", self.glade.get_widget("download_path_button").get_filename())
self.preferences.set("enable_move_completed", self.glade.get_widget("chk_move_completed").get_active())
self.preferences.set("default_finished_path", self.glade.get_widget("finished_path_button").get_filename())
+ self.preferences.set("enable_files_dialog", self.glade.get_widget("chk_enable_files_dialog").get_active())
self.preferences.set("auto_end_seeding", self.glade.get_widget("chk_autoseed").get_active())
self.preferences.set("auto_seed_ratio", self.glade.get_widget("ratio_spinner").get_value())
self.preferences.set("use_compact_storage", self.glade.get_widget("chk_compact").get_active())
@@ -168,6 +171,28 @@ class PreferencesDlg:
self.glade.get_widget("chk_move_completed").set_sensitive(True)
self.glade.get_widget("finished_path_button").set_sensitive(True)
+class FilesDlg:
+ def __init__(self, parent, files_for_dialog):
+ self.files_for_dialog = files_for_dialog
+ self.parent = parent
+ self.glade = gtk.glade.XML(common.get_glade_file("files_dialog.glade"), domain='deluge')
+ self.dialog = self.glade.get_widget("file_dialog")
+ self.dialog.set_icon_from_file(common.get_pixmap("deluge32.png"))
+ self.file_view = self.glade.get_widget("file_view")
+
+ def show(self, manager, unique_id):
+ self.manager = manager
+ self.files_for_dialog.clear_file_store()
+ self.files_for_dialog.use_unique_id(unique_id)
+ self.files_for_dialog.file_view_actions(self.file_view)
+ self.files_for_dialog.prepare_store()
+ self.dialog.show()
+ r = self.dialog.run()
+ self.dialog.hide()
+ self.files_for_dialog.remove_columns()
+ self.files_for_dialog.clear_file_store()
+ return r
+
class PluginDlg:
def __init__(self, parent, plugins):
self.glade = gtk.glade.XML(common.get_glade_file("plugin_dialog.glade"), domain='deluge')
diff --git a/src/files.py b/src/files.py
new file mode 100644
index 000000000..858c2f083
--- /dev/null
+++ b/src/files.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+#
+# files.py
+#
+# Copyright (C) Zach Tibbitts 2006
+#
+# 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, or (at your option)
+# any later version.
+#
+# 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.,
+# 51 Franklin Street, Fifth Floor
+# Boston, MA 02110-1301, USA.
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the OpenSSL
+# library.
+# You must obey the GNU General Public License in all respects for all of
+# the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the file(s),
+# but you are not obligated to do so. If you do not wish to do so, delete
+# this exception statement from your version. If you delete this exception
+# statement from all source files in the program, then also delete it here.
+
+import os
+import sys
+import imp
+import gtk
+import dgtk
+import common
+from itertools import izip
+import gobject
+
+class FilesManager:
+ def __init__(self, manager, is_file_tab):
+ self.manager = manager
+ self.file_glade = gtk.glade.XML(common.get_glade_file("file_tab_menu.glade"), domain='deluge')
+ self.file_menu = self.file_glade.get_widget("file_tab_menu")
+ self.file_glade.signal_autoconnect({
+ "select_all": self.file_select_all,
+ "unselect_all": self.file_unselect_all,
+ "check_selected": self.file_check_selected,
+ "uncheck_selected": self.file_uncheck_selected,
+ })
+ self.file_unique_id = -1
+ # Stores file path -> gtk.TreeIter's iter mapping for quick look up
+ # in self.update_torrent_info_widget
+ self.file_store_dict = {}
+ self.file_store = gtk.ListStore(bool, str, gobject.TYPE_UINT64)
+ self.file_store_sorted = gtk.TreeModelSort(self.file_store)
+ self.is_file_tab = is_file_tab
+ if self.is_file_tab:
+ self.file_store = gtk.ListStore(bool, str, gobject.TYPE_UINT64, float)
+ self.file_store_sorted = gtk.TreeModelSort(self.file_store)
+
+ def use_unique_id(self, unique_id):
+ self.file_unique_id = unique_id
+
+ def file_view_actions(self, file_view):
+ self.file_view = file_view
+ def percent(column, cell, model, iter, data):
+ percent = float(model.get_value(iter, data))
+ percent_str = "%.2f%%"%percent
+ cell.set_property("text", percent_str)
+ self.file_selected = []
+ self.toggle_column = dgtk.add_toggle_column(self.file_view, _("Download"), 0, toggled_signal=self.file_toggled)
+ self.filename_column = dgtk.add_text_column(self.file_view, _("Filename"), 1)
+ self.filename_column.set_expand(True)
+ self.size_column = dgtk.add_func_column(self.file_view, _("Size"), dgtk.cell_data_size, 2)
+ if self.is_file_tab:
+ dgtk.add_func_column(self.file_view, _("Progress"), percent, 3)
+ self.file_view.set_model(self.file_store_sorted)
+ self.file_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+ self.file_view.get_selection().set_select_function(self.file_clicked)
+ self.file_view.connect("button-press-event", self.file_view_clicked)
+
+ def remove_columns(self):
+ self.file_view.remove_column(self.size_column)
+ self.file_view.remove_column(self.filename_column)
+ self.file_view.remove_column(self.toggle_column)
+
+ def clear_file_store(self):
+ self.file_store.clear()
+ self.file_store_dict = {}
+
+ def prepare_store(self):
+ if not self.file_store_dict:
+ all_files = self.manager.get_torrent_file_info(self.file_unique_id)
+ file_filter = self.manager.get_file_filter(self.file_unique_id)
+ if file_filter is None:
+ file_filter = [False] * len(all_files)
+ if self.is_file_tab:
+ for file, filt in izip(all_files, file_filter):
+ iter = self.file_store.append([not filt, file['path'],
+ file['size'],
+ round(file['progress'], 2)])
+ self.file_store_dict[file['path']] = iter
+ else:
+ for file, filt in izip(all_files, file_filter):
+ iter = self.file_store.append([not filt, file['path'],
+ file['size']])
+ self.file_store_dict[file['path']] = iter
+
+
+ def update_store(self):
+ new_file_info = self.manager.get_torrent_file_info(self.file_unique_id)
+ for file in new_file_info:
+ iter = self.file_store_dict[file['path']]
+ if self.file_store.get_value(iter, 3) != round(file['progress'], 2):
+ self.file_store.set(iter, 3, file['progress'])
+
+ def file_select_all(self, widget):
+ self.file_view.get_selection().select_all()
+
+ def file_unselect_all(self, widget):
+ self.file_view.get_selection().unselect_all()
+
+ def file_check_selected(self, widget):
+ self.file_view.get_selection().selected_foreach(self.file_toggle_selected, True)
+ self.file_toggled_update_filter()
+
+ def file_uncheck_selected(self, widget):
+ self.file_view.get_selection().selected_foreach(self.file_toggle_selected, False)
+ self.file_toggled_update_filter()
+
+ def file_clicked(self, path):
+ return not self.file_selected
+
+ def file_view_clicked(self, widget, event):
+ if event.button == 3:
+ self.file_menu.popup(None, None, None, event.button, event.time)
+ return True
+ else:
+ self.file_selected = False
+ return False
+
+ def file_toggle_selected(self, treemodel, path, selected_iter, value):
+ child_iter = self.file_store_sorted.convert_iter_to_child_iter(None,
+ selected_iter)
+ self.file_store_sorted.get_model().set_value(child_iter, 0, value)
+
+ def file_toggled(self, renderer, path):
+ self.file_selected = True
+ file_iter = self.file_store_sorted.get_iter_from_string(path)
+ value = not renderer.get_active()
+ selection = self.file_view.get_selection()
+ if selection.iter_is_selected(file_iter):
+ selection.selected_foreach(self.file_toggle_selected, value)
+ else:
+ child_iter = self.file_store_sorted.convert_iter_to_child_iter(
+ None, file_iter)
+ self.file_store_sorted.get_model().set_value(child_iter, 0, value)
+
+ self.file_toggled_update_filter()
+
+ def file_toggled_update_filter(self):
+ file_filter = [not x[0] for x in self.file_store]
+ self.manager.set_file_filter(self.file_unique_id, file_filter)
diff --git a/src/interface.py b/src/interface.py
index 058631eb8..d124b5f28 100644
--- a/src/interface.py
+++ b/src/interface.py
@@ -47,6 +47,7 @@ import common
import dialogs
import dgtk
import ipc_manager
+import files
import plugins
class DelugeGTK:
@@ -66,6 +67,8 @@ class DelugeGTK:
#Start the Deluge Manager:
self.manager = core.Manager(common.CLIENT_CODE, common.CLIENT_VERSION,
'%s %s'%(common.PROGRAM_NAME, common.PROGRAM_VERSION), common.CONFIG_DIR)
+ self.files_for_tab = files.FilesManager(self.manager, True)
+ self.files_for_dialog = files.FilesManager(self.manager, False)
self.plugins = plugins.PluginManager(self.manager, self)
self.plugins.add_plugin_dir(common.PLUGIN_DIR)
if os.path.isdir(os.path.join(common.CONFIG_DIR , 'plugins')):
@@ -102,6 +105,7 @@ class DelugeGTK:
self.preferences_dialog = dialogs.PreferencesDlg(self, self.config)
self.plugin_dialog = dialogs.PluginDlg(self, self.plugins)
+ self.files_dialog = dialogs.FilesDlg(self, self.files_for_dialog)
self.build_torrent_table()
self.build_summary_tab()
self.build_file_tab()
@@ -658,88 +662,13 @@ class DelugeGTK:
self.peer_store_dict = {}
def build_file_tab(self):
- def percent(column, cell, model, iter, data):
- percent = float(model.get_value(iter, data))
- percent_str = "%.2f%%"%percent
- cell.set_property("text", percent_str)
-
-
+ self.files_for_tab.clear_file_store()
+ self.files_for_tab.use_unique_id(self.get_selected_torrent())
self.file_view = self.wtree.get_widget("file_view")
- self.file_glade = gtk.glade.XML(common.get_glade_file("file_tab_menu.glade"), domain='deluge')
- self.file_menu = self.file_glade.get_widget("file_tab_menu")
- self.file_glade.signal_autoconnect({
- "select_all": self.file_select_all,
- "unselect_all": self.file_unselect_all,
- "check_selected": self.file_check_selected,
- "uncheck_selected": self.file_uncheck_selected,
- })
- self.file_store = gtk.ListStore(bool, str, gobject.TYPE_UINT64, float)
- self.file_store_sorted = gtk.TreeModelSort(self.file_store)
- # Stores file path -> gtk.TreeIter's iter mapping for quick look up
- # in self.update_torrent_info_widget
- self.file_store_dict = {}
- self.file_view.set_model(self.file_store_sorted)
- self.file_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
- self.file_view.get_selection().set_select_function(self.file_clicked)
- self.file_selected = []
- self.file_view.connect("button-press-event", self.file_view_clicked)
-
- dgtk.add_toggle_column(self.file_view, _("Download"), 0, toggled_signal=self.file_toggled)
- dgtk.add_text_column(self.file_view, _("Filename"), 1).set_expand(True)
- dgtk.add_func_column(self.file_view, _("Size"), dgtk.cell_data_size, 2)
- dgtk.add_func_column(self.file_view, _("Progress"), percent, 3)
+ self.files_for_tab.file_view_actions(self.file_view)
def clear_file_store(self):
- self.file_store.clear()
- self.file_store_dict = {}
-
- def file_select_all(self, widget):
- self.file_view.get_selection().select_all()
-
- def file_unselect_all(self, widget):
- self.file_view.get_selection().unselect_all()
-
- def file_check_selected(self, widget):
- self.file_view.get_selection().selected_foreach(self.file_toggle_selected, True)
- self.file_toggled_update_filter()
-
- def file_uncheck_selected(self, widget):
- self.file_view.get_selection().selected_foreach(self.file_toggle_selected, False)
- self.file_toggled_update_filter()
-
- def file_clicked(self, path):
- return not self.file_selected
-
- def file_view_clicked(self, widget, event):
- if event.button == 3:
- self.file_menu.popup(None, None, None, event.button, event.time)
- return True
- else:
- self.file_selected = False
- return False
-
- def file_toggle_selected(self, treemodel, path, selected_iter, value):
- child_iter = self.file_store_sorted.convert_iter_to_child_iter(None,
- selected_iter)
- self.file_store_sorted.get_model().set_value(child_iter, 0, value)
-
- def file_toggled(self, renderer, path):
- self.file_selected = True
- file_iter = self.file_store_sorted.get_iter_from_string(path)
- value = not renderer.get_active()
- selection = self.file_view.get_selection()
- if selection.iter_is_selected(file_iter):
- selection.selected_foreach(self.file_toggle_selected, value)
- else:
- child_iter = self.file_store_sorted.convert_iter_to_child_iter(
- None, file_iter)
- self.file_store_sorted.get_model().set_value(child_iter, 0, value)
-
- self.file_toggled_update_filter()
-
- def file_toggled_update_filter(self):
- file_filter = [not x[0] for x in self.file_store]
- self.manager.set_file_filter(self.get_selected_torrent(), file_filter)
+ self.files_for_tab.clear_file_store()
def show_about_dialog(self, arg=None):
dialogs.show_about_dialog()
@@ -1108,23 +1037,9 @@ class DelugeGTK:
elif page_num == 2: # Files
# Fill self.file_store with files only once and only when we click to
# Files tab or it's already open
- if not self.file_store_dict:
- all_files = self.manager.get_torrent_file_info(unique_id)
- file_filter = self.manager.get_file_filter(unique_id)
- if file_filter is None:
- file_filter = [False] * len(all_files)
- for file, filt in izip(all_files, file_filter):
- iter = self.file_store.append([not filt, file['path'],
- file['size'],
- round(file['progress'], 2)])
- self.file_store_dict[file['path']] = iter
-
- new_file_info = self.manager.get_torrent_file_info(unique_id)
-
- for file in new_file_info:
- iter = self.file_store_dict[file['path']]
- if self.file_store.get_value(iter, 3) != round(file['progress'], 2):
- self.file_store.set(iter, 3, file['progress'])
+ self.files_for_tab.use_unique_id(unique_id)
+ self.files_for_tab.prepare_store()
+ self.files_for_tab.update_store()
def calc_share_ratio(self, unique_id, torrent_state):
@@ -1178,6 +1093,13 @@ class DelugeGTK:
return
try:
unique_id = self.manager.add_torrent(torrent, path, self.config.get('use_compact_storage'))
+ if not append and self.config.get('enable_files_dialog'):
+ self.manager.set_user_pause(unique_id, True)
+ if self.files_dialog.show(self.manager, unique_id) == 1:
+ self.manager.set_user_pause(unique_id, False)
+ else:
+ self.manager.remove_torrent(unique_id, True, True)
+
except core.InvalidEncodingError, e:
print "InvalidEncodingError", e
dialogs.show_popup_warning(self.window, _("An error occured while trying to add the torrent. It's possible your .torrent file is corrupted."))
@@ -1191,7 +1113,15 @@ class DelugeGTK:
_("Available Space:") + " " + nice_free)
else:
if append:
- self.torrent_model_append(unique_id)
+ if self.config.get('enable_files_dialog'):
+ self.manager.set_user_pause(unique_id, True)
+ if self.files_dialog.show(self.manager, unique_id) == 1:
+ self.manager.set_user_pause(unique_id, False)
+ self.torrent_model_append(unique_id)
+ else:
+ self.manager.remove_torrent(unique_id, True, True)
+ else:
+ self.torrent_model_append(unique_id)
def launchpad(self, obj=None):
common.open_url_in_browser('self', 'https://translations.launchpad.net/deluge/trunk/+pots/deluge')
diff --git a/src/pref.py b/src/pref.py
index 3d41d1712..eace2c707 100644
--- a/src/pref.py
+++ b/src/pref.py
@@ -41,6 +41,7 @@ DEFAULT_PREFS = {
"auto_end_seeding" : False,
"auto_seed_ratio" : 0,
"close_to_tray" : False,
+ "enable_files_dialog" : False,
"default_download_path" : os.path.expanduser("~/"),
"default_load_path" : os.path.expanduser("~/"),
"default_finished_path" : os.path.expanduser("~/"),