diff --git a/deluge/core/filtermanager.py b/deluge/core/filtermanager.py index c52fec29b..a4fd54d8e 100644 --- a/deluge/core/filtermanager.py +++ b/deluge/core/filtermanager.py @@ -80,8 +80,23 @@ def filter_one_keyword(torrent_ids, keyword): def filter_by_name(torrent_ids, search_string): all_torrents = component.get("TorrentManager").torrents + try: + search_string, match_case = search_string[0].split('::match') + except ValueError: + search_string = search_string[0] + match_case = False + + if match_case is False: + search_string = search_string.lower() + for torrent_id in torrent_ids: - if search_string[0].lower() in all_torrents[torrent_id].filename.lower(): + torrent_name = all_torrents[torrent_id].get_name() + if match_case is False: + torrent_name = all_torrents[torrent_id].get_name().lower() + else: + torrent_name = all_torrents[torrent_id].get_name() + + if search_string in torrent_name: yield torrent_id def tracker_error_filter(torrent_ids, values): diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index db3952142..3c0d4ce24 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -225,6 +225,30 @@ class Torrent(object): def get_options(self): return self.options + def get_name(self): + if self.handle.has_metadata(): + name = self.torrent_info.file_at(0).path.split("/", 1)[0] + if not name: + name = self.torrent_info.name() + try: + return name.decode("utf8", "ignore") + except UnicodeDecodeError: + return name + elif self.magnet: + try: + keys = dict([k.split('=') for k in self.magnet.split('?')[-1].split('&')]) + name = keys.get('dn') + if not name: + return self.torrent_id + name = unquote(name).replace('+', ' ') + try: + return name.decode("utf8", "ignore") + except UnicodeDecodeError: + return name + except: + pass + return self.torrent_id + def set_owner(self, account): self.owner = account @@ -685,32 +709,6 @@ class Torrent(object): return self.torrent_info.comment() return "" - def ti_name(): - if self.handle.has_metadata(): - name = self.torrent_info.file_at(0).path.split("/", 1)[0] - if not name: - name = self.torrent_info.name() - try: - return name.decode("utf8", "ignore") - except UnicodeDecodeError: - return name - - elif self.magnet: - try: - keys = dict([k.split('=') for k in self.magnet.split('?')[-1].split('&')]) - name = keys.get('dn') - if not name: - return self.torrent_id - name = unquote(name).replace('+', ' ') - try: - return name.decode("utf8", "ignore") - except UnicodeDecodeError: - return name - except: - pass - - return self.torrent_id - def ti_priv(): if self.handle.has_metadata(): return self.torrent_info.priv() @@ -742,7 +740,7 @@ class Torrent(object): "file_progress": self.get_file_progress, "files": self.get_files, "is_seed": self.handle.is_seed, - "name": ti_name, + "name": self.get_name, "num_files": ti_num_files, "num_pieces": ti_num_pieces, "pieces": ti_pieces_info, diff --git a/deluge/ui/gtkui/glade/main_window.glade b/deluge/ui/gtkui/glade/main_window.glade index cb0db23b8..c82aaf8d3 100644 --- a/deluge/ui/gtkui/glade/main_window.glade +++ b/deluge/ui/gtkui/glade/main_window.glade @@ -38,14 +38,6 @@ False - - - True - False - gtk-add - 1 - - @@ -58,14 +50,6 @@ False - - - True - False - gtk-new - 1 - - @@ -83,14 +67,6 @@ False - - - True - False - gtk-quit - 1 - - @@ -148,14 +124,6 @@ False - - - True - False - gtk-network - 1 - - @@ -239,6 +207,18 @@ True + + + True + False + False + False + _Find ... + True + + + + True @@ -302,14 +282,6 @@ True False - - - True - False - gtk-home - 1 - - @@ -324,14 +296,6 @@ False - - - True - False - gtk-dialog-question - 1 - - @@ -344,15 +308,6 @@ True False - - - True - False - 0.4699999988079071 - gtk-info - 1 - - @@ -384,217 +339,201 @@ - + True False - + True + False False - - - True - False - False - True - Add torrent - False - Add Torrent - True - gtk-add - - - - False - True - - - - - True - False - False - True - Remove torrent - False - Remove Torrent - gtk-remove - - - - False - True - - - - - True - False - - - False - - - - - True - False - False - True - Pause the selected torrents - False - Pause - True - gtk-media-pause - - - - False - True - - - - - True - False - False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - Resume the selected torrents - False - Resume - gtk-media-play - - - - False - True - - - - - True - False - - - False - - - - - True - False - False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - Queue Torrent Up - False - Queue Up - gtk-go-up - - - - False - True - - - - - True - False - False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - Queue Torrent Down - False - Queue Down - gtk-go-down - - - - False - True - - - - - True - False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - False - - - - - True - False - True - Preferences - False - Preferences - True - gtk-preferences - - - - False - True - - - - - True - False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - Connection Manager - False - Connection Manager - gtk-network - - - - False - True - - - - - True - True - 0 - - - - - True - True - Search torrents by name - - True - False - gtk-clear - False - True - False - True - Clear the search - - + True + Add torrent + False + Add Torrent + True + gtk-add + False - False - 5 - 1 + True + + + + + True + False + False + True + Remove torrent + False + Remove Torrent + gtk-remove + + + + False + True + + + + + True + False + False + Filter torrents by name. +This will filter torrents for the current selection on the sidebar. + False + _Filter + True + gtk-find + + + + + False + True + + + + + True + False + + + False + + + + + True + False + False + True + Pause the selected torrents + False + Pause + True + gtk-media-pause + + + + False + True + + + + + True + False + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + Resume the selected torrents + False + Resume + gtk-media-play + + + + False + True + + + + + True + False + + + False + + + + + True + False + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + Queue Torrent Up + False + Queue Up + gtk-go-up + + + + False + True + + + + + True + False + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + Queue Torrent Down + False + Queue Down + gtk-go-down + + + + False + True + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + False + + + + + True + False + True + Preferences + False + Preferences + True + gtk-preferences + + + + False + True + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + Connection Manager + False + Connection Manager + gtk-network + + + + False + True False - False + True 1 @@ -624,24 +563,130 @@ - + True False - automatic - automatic - out - + + False + + + True + True + True + True + Close + False + none + + + + True + False + gtk-close + 2 + + + + + False + False + 0 + + + + + True + False + Filter: + + + False + False + 1 + 1 + + + + + 350 + True + True + True + Filter torrents by name. +This will filter torrents for the current selection on the sidebar. + + True + True + False + gtk-clear + True + True + False + True + Clear the search + Clear the search + + + + + False + False + 1 + 2 + + + + + _Match Case + True + True + False + False + True + True + + + + True + True + 1 + 3 + + + + + False + True + 0 + + + + True True - True - False + automatic + automatic + out + + + True + True + True + False + + + + True + True + 1 + True - False + True @@ -684,152 +729,6 @@ - - True - False - - - gtk-open - True - False - False - True - True - - - - - - True - False - - - - - _Expand All - True - False - False - True - False - - - - True - False - gtk-zoom-fit - 1 - - - - - - - True - False - - - - - _Do Not Download - True - False - False - True - False - - - - True - False - gtk-no - 1 - - - - - - - _Normal Priority - True - False - False - True - False - - - - True - False - gtk-yes - 1 - - - - - - - _High Priority - True - False - False - True - False - - - - True - False - gtk-go-up - 1 - - - - - - - Hi_ghest Priority - True - False - False - True - False - - - - True - False - gtk-goto-top - 1 - - - - - - - True - False - - - _Add Peer - True - False - Add a peer by its IP - False - True - False - - - - True - False - gtk-add - 1 - - - - - False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index 5099fd54d..858b7e86e 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -74,7 +74,9 @@ class ToolBar(component.Component): "toolbutton_pause", "toolbutton_resume", "toolbutton_queue_up", - "toolbutton_queue_down" + "toolbutton_queue_down", + "toolbutton_filter", + "find_menuitem" ] self.config.register_set_function("classic_mode", self._on_classic_mode, True) diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index 9221d3bf7..1e90db432 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -184,6 +184,86 @@ def seed_peer_column_sort(model, iter1, iter2, data): return queue_peer_seed_sort_function(v2, v4) return queue_peer_seed_sort_function(v1, v3) +class SearchBox(object): + def __init__(self, torrentview): + self.torrentview = torrentview + self.window = torrentview.window + + self.visible = False + self.search_pending = None + + self.search_box = self.window.main_glade.get_widget("search_box") + self.search_torrents_entry = self.window.main_glade.get_widget("search_torrents_entry") + self.close_search_button = self.window.main_glade.get_widget("close_search_button") + self.match_search_button = self.window.main_glade.get_widget("search_torrents_match") + self.window.main_glade.signal_autoconnect(self) + + def show(self): + self.visible = True + self.search_box.show_all() + + def hide(self): + self.visible = False + self.clear_search() + self.search_box.hide_all() + + def clear_search(self): + if self.search_pending and self.search_pending.active(): + self.search_pending.cancel() + + self.search_torrents_entry.set_text("") + if self.torrentview.filter and 'name' in self.torrentview.filter: + self.torrentview.filter.pop('name', None) + self.search_pending = reactor.callLater(0.5, self.torrentview.update) + + def set_search_filter(self): + if self.search_pending and self.search_pending.active(): + self.search_pending.cancel() + + if self.torrentview.filter and 'name' in self.torrentview.filter: + self.torrentview.filter.pop('name', None) + + elif self.torrentview.filter is None: + self.torrentview.filter = {} + + search_string = self.search_torrents_entry.get_text() + if not search_string: + self.clear_search() + else: + if self.match_search_button.get_active(): + search_string += '::match' + self.torrentview.filter['name'] = search_string + + def on_close_search_button_clicked(self, widget): + self.hide() + + def on_find_menuitem_activate(self, widget): + if self.visible: + self.hide() + else: + self.show() + + def on_toolbutton_filter_clicked(self, widget): + if self.visible: + self.hide() + else: + self.show() + + def on_search_torrents_match_toggled(self, widget): + if self.search_torrents_entry.get_text(): + self.set_search_filter() + self.search_pending = reactor.callLater(0.5, self.torrentview.update) + + def on_search_torrents_entry_icon_press(self, entry, icon, event): + if icon != gtk.ENTRY_ICON_SECONDARY: + return + self.clear_search() + + def on_search_torrents_entry_changed(self, widget): + self.set_search_filter() + self.search_pending = reactor.callLater(0.7, self.torrentview.update) + + class TorrentView(listview.ListView, component.Component): """TorrentView handles the listing of torrents.""" def __init__(self): @@ -272,7 +352,6 @@ class TorrentView(listview.ListView, component.Component): # Set filter to None for now self.filter = None - self.search_pending = None ### Connect Signals ### # Connect to the 'button-press-event' to know when to bring up the @@ -290,15 +369,6 @@ class TorrentView(listview.ListView, component.Component): self.treeview.connect("key-press-event", self.on_key_press_event) self.treeview.connect("columns-changed", self.on_columns_changed_event) - self.search_torrents_entry = self.window.main_glade.get_widget("search_torrents_entry") - self.search_torrents_entry.connect( - "icon-press", self.on_search_torrents_entry_icon_press - ) - self.search_torrents_entry.connect( - "changed", self.on_search_torrents_entry_changed - ) - - client.register_event_handler("TorrentStateChangedEvent", self.on_torrentstatechanged_event) client.register_event_handler("TorrentAddedEvent", self.on_torrentadded_event) client.register_event_handler("TorrentRemovedEvent", self.on_torrentremoved_event) @@ -306,6 +376,8 @@ class TorrentView(listview.ListView, component.Component): client.register_event_handler("SessionResumedEvent", self.on_sessionresumed_event) client.register_event_handler("TorrentQueueChangedEvent", self.on_torrentqueuechanged_event) + self.search_box = SearchBox(self) + def start(self): """Start the torrentview""" # We need to get the core session state to know which torrents are in @@ -332,7 +404,7 @@ class TorrentView(listview.ListView, component.Component): self.liststore.clear() self.prev_status = {} self.filter = None - self.search_torrents_entry.set_text("") + self.search_box.hide() def shutdown(self): """Called when GtkUi is exiting""" @@ -396,7 +468,7 @@ class TorrentView(listview.ListView, component.Component): def update(self): if self.got_state: - if self.search_pending is not None and self.search_pending.active(): + if self.search_box.search_pending is not None and self.search_box.search_pending.active(): # An update request is scheduled, let's wait for that one return # Send a status request @@ -620,33 +692,3 @@ class TorrentView(listview.ListView, component.Component): torrentmenu = component.get("MenuBar").torrentmenu torrentmenu.popup(None, None, None, 3, event.time) return True - - def on_search_torrents_entry_icon_press(self, entry, icon, event): - if icon != gtk.ENTRY_ICON_SECONDARY: - return - - if self.search_pending and self.search_pending.active(): - self.search_pending.cancel() - - entry.set_text("") - if self.filter and 'name' in self.filter: - self.filter.pop('name', None) - self.search_pending = reactor.callLater(0.7, self.update) - - def on_search_torrents_entry_changed(self, widget): - search_string = widget.get_text().lower() - - if self.search_pending and self.search_pending.active(): - self.search_pending.cancel() - - if not search_string: - if self.filter and 'name' in self.filter: - self.filter.pop('name', None) - self.search_pending = reactor.callLater(0.7, self.update) - return - - if self.filter is None: - self.filter = {} - - self.filter['name'] = search_string - self.search_pending = reactor.callLater(0.7, self.update)