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