diff options
Diffstat (limited to 'deluge/ui/gtk3/edittrackersdialog.py')
-rw-r--r-- | deluge/ui/gtk3/edittrackersdialog.py | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/deluge/ui/gtk3/edittrackersdialog.py b/deluge/ui/gtk3/edittrackersdialog.py new file mode 100644 index 0000000..861e392 --- /dev/null +++ b/deluge/ui/gtk3/edittrackersdialog.py @@ -0,0 +1,348 @@ +# +# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> +# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. +# + +import logging +import os.path + +from gi.repository import Gdk, Gtk +from twisted.internet import defer + +import deluge.component as component +from deluge.common import is_url, resource_filename +from deluge.configmanager import ConfigManager +from deluge.ui.client import client + +from .common import get_deluge_icon + +log = logging.getLogger(__name__) + + +def last_tier_trackers_from_liststore(trackers_liststore): + """Create a list of tracker from existing liststore and find last tier number. + + Args: + tracker_liststore (Gtk.ListStore): A Gtk.ListStore with [tier (int), tracker (str)] rows. + + Returns: + tuple(int, list): A tuple containing last tier number and list of trackers. + + """ + + last_tier = 0 + trackers_from_liststore = [] + for tier, tracker in trackers_liststore: + trackers_from_liststore.append(tracker) + if tier >= last_tier: + last_tier = tier + 1 + + return last_tier, trackers_from_liststore + + +def trackers_tiers_from_text(text_str=''): + """Create a list of trackers from text. + + Any duplicate trackers are removed. + + Args: + text_input (str): A block of text with tracker separated by newlines. + + Returns: + list: The list of trackers. + + Notes: + Trackers should be separated by newlines and empty line denotes start of new tier. + + """ + + trackers = {} + tier = 0 + + lines = text_str.strip().split('\n') + for line in lines: + if not line: + tier += 1 + continue + line = line.replace('\\', '/') # Fix any mistyped urls. + if is_url(line) and line not in trackers: + trackers[line] = tier + + return trackers + + +class EditTrackersDialog: + def __init__(self, torrent_id, parent=None): + self.torrent_id = torrent_id + self.builder = Gtk.Builder() + self.gtkui_config = ConfigManager('gtk3ui.conf') + + # Main dialog + self.builder.add_from_file( + resource_filename(__package__, os.path.join('glade', 'edit_trackers.ui')) + ) + # add tracker dialog + self.builder.add_from_file( + resource_filename( + __package__, os.path.join('glade', 'edit_trackers.add.ui') + ) + ) + # edit tracker dialog + self.builder.add_from_file( + resource_filename( + __package__, os.path.join('glade', 'edit_trackers.edit.ui') + ) + ) + + self.dialog = self.builder.get_object('edit_trackers_dialog') + self.treeview = self.builder.get_object('tracker_treeview') + self.add_tracker_dialog = self.builder.get_object('add_tracker_dialog') + self.add_tracker_dialog.set_transient_for(self.dialog) + self.edit_tracker_entry = self.builder.get_object('edit_tracker_entry') + self.edit_tracker_entry.set_transient_for(self.dialog) + self.dialog.set_icon(get_deluge_icon()) + + self.load_edit_trackers_dialog_state() + + if parent is not None: + self.dialog.set_transient_for(parent) + + # Connect the signals + self.builder.connect_signals(self) + + # Create a liststore for tier, url + self.liststore = Gtk.ListStore(int, str) + + # Create the columns + self.treeview.append_column( + Gtk.TreeViewColumn(_('Tier'), Gtk.CellRendererText(), text=0) + ) + self.treeview.append_column( + Gtk.TreeViewColumn(_('Tracker'), Gtk.CellRendererText(), text=1) + ) + + self.treeview.set_model(self.liststore) + self.liststore.set_sort_column_id(0, Gtk.SortType.ASCENDING) + + self.dialog.connect('delete-event', self._on_delete_event) + self.dialog.connect('response', self._on_response) + self.treeview.connect('button_press_event', self.on_button_press_event) + + self.add_tracker_dialog.connect('key-press-event', self.on_key_add_press_event) + self.add_tracker_dialog.connect('delete-event', self.on_delete_event_add) + self.edit_tracker_entry.connect('key-press-event', self.on_key_edit_press_event) + self.edit_tracker_entry.connect('delete-event', self.on_delete_event_edit) + + def run(self): + # Make sure we have a torrent_id.. if not just return + if self.torrent_id is None: + return + + # Get the trackers for this torrent + session = component.get('SessionProxy') + session.get_torrent_status(self.torrent_id, ['trackers']).addCallback( + self._on_get_torrent_status + ) + client.force_call() + + self.deferred = defer.Deferred() + return self.deferred + + def __del__(self): + del self.gtkui_config + + def load_edit_trackers_dialog_state(self): + w = self.gtkui_config['edit_trackers_dialog_width'] + h = self.gtkui_config['edit_trackers_dialog_height'] + if w is not None and h is not None: + self.dialog.resize(w, h) + + def on_edit_trackers_dialog_configure_event(self, widget, event): + self.gtkui_config['edit_trackers_dialog_width'] = event.width + self.gtkui_config['edit_trackers_dialog_height'] = event.height + + def _on_delete_event(self, widget, event): + self.deferred.callback(Gtk.ResponseType.DELETE_EVENT) + self.dialog.destroy() + + def _on_response(self, widget, response): + if response == 1: + self.trackers = [] + + def each(model, path, _iter, data): + tracker = {} + tracker['tier'] = model.get_value(_iter, 0) + tracker['url'] = model.get_value(_iter, 1) + self.trackers.append(tracker) + + self.liststore.foreach(each, None) + if self.old_trackers != self.trackers: + # Set the torrens trackers + client.core.set_torrent_trackers(self.torrent_id, self.trackers) + self.deferred.callback(Gtk.ResponseType.OK) + else: + self.deferred.callback(Gtk.ResponseType.CANCEL) + else: + self.deferred.callback(Gtk.ResponseType.CANCEL) + self.dialog.destroy() + + def _on_get_torrent_status(self, status): + """Display trackers dialog""" + self.old_trackers = list(status['trackers']) + for tracker in self.old_trackers: + self.add_tracker(tracker['tier'], tracker['url']) + self.treeview.set_cursor(0) + self.dialog.show() + + def add_tracker(self, tier, url): + """Adds a tracker to the list""" + self.liststore.append([tier, url]) + + def get_selected(self): + """Returns the selected tracker""" + return self.treeview.get_selection().get_selected()[1] + + def on_button_add_clicked(self, widget): + log.debug('on_button_add_clicked') + # Show the add tracker dialog + self.add_tracker_dialog.show() + self.builder.get_object('textview_trackers').grab_focus() + self.dialog.set_sensitive(False) + + def on_button_remove_clicked(self, widget): + log.debug('on_button_remove_clicked') + selected = self.get_selected() + if selected is not None: + self.liststore.remove(selected) + + def on_button_edit_clicked(self, widget): + """edits an existing tracker on edit button click""" + log.debug('on_button_edit_clicked') + self._edit_tracker() + + def on_button_press_event(self, widget, event): + """edits an existing tracker on double click on tracker name""" + if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS: + log.debug('button_press_event double click') + self._edit_tracker() + + def _edit_tracker(self): + """edits an existing tracker""" + selected = self.get_selected() + if selected: + tracker = self.liststore.get_value(selected, 1) + self.builder.get_object('entry_edit_tracker').set_text(tracker) + self.edit_tracker_entry.show() + self.edit_tracker_entry.grab_focus() + self.dialog.set_sensitive(False) + + def _close_edit_dialog(self): + self.dialog.set_sensitive(True) + self.edit_tracker_entry.hide() + + def on_button_edit_cancel_clicked(self, widget): + """handles the cancel button""" + log.debug('on_button_edit_cancel_clicked') + self._close_edit_dialog() + + def on_key_edit_press_event(self, widget, event): + """handles Escape key press""" + if event.keyval == Gdk.KEY_Escape: + log.debug('on_key_edit_press_event') + self._close_edit_dialog() + + def on_delete_event_edit(self, widget, event): + """handles the Top-Right X button""" + log.debug('on_delete_event_edit') + self._close_edit_dialog() + return True + + def on_button_edit_ok_clicked(self, widget): + log.debug('on_button_edit_ok_clicked') + selected = self.get_selected() + tracker = self.builder.get_object('entry_edit_tracker').get_text() + self.liststore.set_value(selected, 1, tracker) + self.dialog.set_sensitive(True) + self.edit_tracker_entry.hide() + + def on_button_up_clicked(self, widget): + log.debug('on_button_up_clicked') + selected = self.get_selected() + num_rows = self.liststore.iter_n_children(None) + if selected is not None and num_rows > 1: + tier = self.liststore.get_value(selected, 0) + if tier <= 0: + return + new_tier = tier - 1 + # Now change the tier for this tracker + self.liststore.set_value(selected, 0, new_tier) + + def on_button_down_clicked(self, widget): + log.debug('on_button_down_clicked') + selected = self.get_selected() + num_rows = self.liststore.iter_n_children(None) + if selected is not None and num_rows > 1: + tier = self.liststore.get_value(selected, 0) + new_tier = tier + 1 + # Now change the tier for this tracker + self.liststore.set_value(selected, 0, new_tier) + + def on_button_add_ok_clicked(self, widget): + log.debug('on_button_add_ok_clicked') + + # Create a list of trackers from the textview widget + textview_buf = self.builder.get_object('textview_trackers').get_buffer() + trackers_text = textview_buf.get_text( + *textview_buf.get_bounds(), include_hidden_chars=False + ) + + for tracker in trackers_tiers_from_text(trackers_text): + # Figure out what tier number to use.. it's going to be the highest+1 + # Also check for duplicates + # Check if there are any entries + duplicate = False + highest_tier = -1 + for row in self.liststore: + tier = row[0] + if tier > highest_tier: + highest_tier = tier + if tracker == row[1]: + duplicate = True + break + + # If not a duplicate, then add it to the list + if not duplicate: + # Add the tracker to the list + self.add_tracker(highest_tier + 1, tracker) + + # Clear the entry widget and hide the dialog + textview_buf.set_text('') + self.dialog.set_sensitive(True) + self.add_tracker_dialog.hide() + + def _discard_and_close_add_dialog(self): + # Clear the entry widget and hide the dialog + b = Gtk.TextBuffer() + self.builder.get_object('textview_trackers').set_buffer(b) + self.dialog.set_sensitive(True) + self.add_tracker_dialog.hide() + + def on_button_add_cancel_clicked(self, widget): + """handles the cancel button""" + log.debug('on_button_add_cancel_clicked') + self._discard_and_close_add_dialog() + + def on_key_add_press_event(self, widget, event): + """handles Escape key press""" + if event.keyval == Gdk.KEY_Escape: + log.debug('on_key_add_press_event') + self._discard_and_close_add_dialog() + + def on_delete_event_add(self, widget, event): + """handles the Top-Right X button""" + log.debug('on_delete_event_add') + self._discard_and_close_add_dialog() + return True |