Added feature select files when adding torrent. Patch is from eternalswd,

thanks :).
This commit is contained in:
Alex Dedul 2007-07-15 19:24:37 +00:00
parent b06922dcd4
commit c93a5c57b7
6 changed files with 349 additions and 114 deletions

93
glade/files_dialog.glade Normal file
View File

@ -0,0 +1,93 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkDialog" id="file_dialog">
<property name="border_width">5</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="title" translatable="yes">Deluge File Selection</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="modal">False</property>
<property name="default_height">550</property>
<property name="default_width">550</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">True</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox3">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">1</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area3">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="button2">
<property name="visible">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">0</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">1</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="file_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">2</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -158,35 +158,55 @@
<property name="bottom_padding">2</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkHBox" id="hbox11">
<widget class="GtkVBox" id="newvbox1">
<property name="visible">True</property>
<property name="spacing">10</property>
<child>
<widget class="GtkLabel" id="label56">
<widget class="GtkHBox" id="hbox11">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">The number of active torrents that Deluge will run. Set to -1 for unlimited.</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Maximum simultaneous active torrents:</property>
<property name="spacing">10</property>
<child>
<widget class="GtkLabel" id="label56">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">The number of active torrents that Deluge will run. Set to -1 for unlimited.</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Maximum simultaneous active torrents:</property>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkSpinButton" id="spin_torrents">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="tooltip" translatable="yes">The number of active torrents that Deluge will run. Set to -1 for unlimited.</property>
<property name="xalign">1</property>
<property name="adjustment">-1 -1 1000 1 10 10</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
<property name="update_policy">GTK_UPDATE_IF_VALID</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">2</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkSpinButton" id="spin_torrents">
<widget class="GtkCheckButton" id="chk_enable_files_dialog">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="tooltip" translatable="yes">The number of active torrents that Deluge will run. Set to -1 for unlimited.</property>
<property name="xalign">1</property>
<property name="adjustment">-1 -1 1000 1 10 10</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
<property name="update_policy">GTK_UPDATE_IF_VALID</property>
<property name="tooltip" translatable="yes">Enable selecting files for torrents before loading</property>
<property name="label" translatable="yes">Enable selecting files for torrents before loading</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">2</property>
<property name="position">1</property>
</packing>
</child>

View File

@ -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')

166
src/files.py Normal file
View File

@ -0,0 +1,166 @@
#!/usr/bin/env python
#
# files.py
#
# Copyright (C) Zach Tibbitts 2006 <zach@collegegeek.org>
#
# 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)

View File

@ -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')

View File

@ -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("~/"),