diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-02-19 15:05:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-02-19 15:05:49 +0000 |
commit | d395bd510fa4f4376dc5237ab2f8d190a920d35d (patch) | |
tree | 3e3b16b44064938be801aede14381562bae14f6a /deluge/ui/gtk3 | |
parent | Adding upstream version 2.0.3. (diff) | |
download | deluge-upstream/2.1.1.tar.xz deluge-upstream/2.1.1.zip |
Adding upstream version 2.1.1.upstream/2.1.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'deluge/ui/gtk3')
54 files changed, 2202 insertions, 705 deletions
diff --git a/deluge/ui/gtk3/__init__.py b/deluge/ui/gtk3/__init__.py index 3b9d2b1..8db2773 100644 --- a/deluge/ui/gtk3/__init__.py +++ b/deluge/ui/gtk3/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,13 +6,14 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging +from os import environ from deluge.ui.ui import UI log = logging.getLogger(__name__) +# Hide pygame community banner +environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1' # Keep this class in __init__.py to avoid the console having to import everything in gtkui.py @@ -22,7 +22,7 @@ class Gtk(UI): cmd_description = """GTK-based graphical user interface""" def __init__(self, *args, **kwargs): - super(Gtk, self).__init__( + super().__init__( 'gtk', *args, description='Starts the Deluge GTK+ interface', **kwargs ) @@ -39,10 +39,11 @@ class Gtk(UI): ) def start(self): - super(Gtk, self).start() - from .gtkui import GtkUI + super().start() import deluge.common + from .gtkui import GtkUI + def run(options): try: gtkui = GtkUI(options) diff --git a/deluge/ui/gtk3/aboutdialog.py b/deluge/ui/gtk3/aboutdialog.py index 9974a13..fe3452b 100644 --- a/deluge/ui/gtk3/aboutdialog.py +++ b/deluge/ui/gtk3/aboutdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Marcos Mobley ('markybob') <markybob@gmail.com> # @@ -7,7 +6,7 @@ # See LICENSE for more details. # -from __future__ import unicode_literals +from datetime import date from gi.repository import Gtk @@ -18,7 +17,7 @@ from deluge.ui.client import client from .common import get_deluge_icon, get_pixbuf -class AboutDialog(object): +class AboutDialog: def __init__(self): self.about = Gtk.AboutDialog() self.about.set_transient_for(component.get('MainWindow').window) @@ -38,7 +37,7 @@ class AboutDialog(object): self.about.set_copyright( _('Copyright %(year_start)s-%(year_end)s Deluge Team') - % {'year_start': 2007, 'year_end': 2019} + % {'year_start': 2007, 'year_end': date.today().year} ) self.about.set_comments( _('A peer-to-peer file sharing program\nutilizing the BitTorrent protocol.') diff --git a/deluge/ui/gtk3/addtorrentdialog.py b/deluge/ui/gtk3/addtorrentdialog.py index 9ede710..cf3851d 100644 --- a/deluge/ui/gtk3/addtorrentdialog.py +++ b/deluge/ui/gtk3/addtorrentdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # @@ -7,11 +6,9 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging import os -from base64 import b64encode +from base64 import b64decode, b64encode from xml.sax.saxutils import escape as xml_escape from xml.sax.saxutils import unescape as xml_unescape @@ -19,6 +16,7 @@ from gi.repository import Gtk from gi.repository.GObject import TYPE_INT64, TYPE_UINT64 import deluge.component as component +from deluge.bencode import bdecode from deluge.common import ( create_magnet_uri, decode_bytes, @@ -271,6 +269,7 @@ class AddTorrentDialog(component.Component): return if metadata: + metadata = bdecode(b64decode(metadata)) info = TorrentInfo.from_metadata(metadata, [[t] for t in trackers]) self.files[info_hash] = info.files self.infos[info_hash] = info.filedata @@ -301,7 +300,7 @@ class AddTorrentDialog(component.Component): self.builder.get_object('prefetch_hbox').hide() def add_from_magnets(self, uris): - """Add a list of magnet uris to torrent_liststore.""" + """Add a list of magnet URIs to torrent_liststore.""" already_added = 0 for uri in uris: @@ -775,7 +774,7 @@ class AddTorrentDialog(component.Component): else: ErrorDialog( _('Invalid URL'), - '%s %s' % (url, _('is not a valid URL.')), + '{} {}'.format(url, _('is not a valid URL.')), self.dialog, ).run() @@ -817,7 +816,7 @@ class AddTorrentDialog(component.Component): dialog.destroy() ErrorDialog( _('Download Failed'), - '%s %s' % (_('Failed to download:'), url), + '{} {}'.format(_('Failed to download:'), url), details=result.getErrorMessage(), parent=self.dialog, ).run() @@ -853,11 +852,11 @@ class AddTorrentDialog(component.Component): log.debug('Create torrent tracker lines: %s', trackers_text) trackers = list(trackers_tiers_from_text(trackers_text).keys()) - # Convert the information to a magnet uri, this is just easier to + # Convert the information to a magnet URI, this is just easier to # handle this way. log.debug('trackers: %s', trackers) magnet = create_magnet_uri(infohash, infohash, trackers) - log.debug('magnet uri: %s', magnet) + log.debug('magnet URI: %s', magnet) self.add_from_magnets([magnet]) entry.set_text('') diff --git a/deluge/ui/gtk3/common.py b/deluge/ui/gtk3/common.py index 8359327..42a14b4 100644 --- a/deluge/ui/gtk3/common.py +++ b/deluge/ui/gtk3/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Marcos Mobley ('markybob') <markybob@gmail.com> # @@ -7,16 +6,14 @@ # See LICENSE for more details. # """Common functions for various parts of gtkui to use.""" -from __future__ import unicode_literals - import contextlib import logging import os +import pickle import shutil import sys -import six.moves.cPickle as pickle # noqa: N813 -from gi.repository.Gdk import SELECTION_CLIPBOARD, Display +from gi.repository.Gdk import SELECTION_CLIPBOARD, SELECTION_PRIMARY, Display from gi.repository.GdkPixbuf import Colorspace, Pixbuf from gi.repository.GLib import GError from gi.repository.Gtk import ( @@ -29,7 +26,7 @@ from gi.repository.Gtk import ( SortType, ) -from deluge.common import PY2, get_pixmap, osx_check, windows_check +from deluge.common import get_pixmap, is_ip, osx_check, windows_check log = logging.getLogger(__name__) @@ -42,7 +39,18 @@ def cmp(x, y): and strictly positive if x > y. """ - return (x > y) - (x < y) + try: + return (x > y) - (x < y) + except TypeError: + # Handle NoneType comparison + if x is None: + if y is None: + return 0 + return -1 + elif y is None: + return 1 + else: + raise def create_blank_pixbuf(size=16): @@ -51,12 +59,36 @@ def create_blank_pixbuf(size=16): return pix -def get_pixbuf(filename): +def get_pixbuf(filename: str, size: int = 0) -> Pixbuf: + """Creates a new pixbuf by loading an image from file + + Args: + filename: An image file to load + size: Specify a size constraint (equal aspect ratio) + + Returns: + A newly created pixbuf + + """ + # Skip ico and gif that cause Pixbuf crash on Windows + # https://dev.deluge-torrent.org/ticket/3501 + if windows_check() and filename.endswith(('.ico', '.gif')): + return create_blank_pixbuf(size) + + if not os.path.isabs(filename): + filename = get_pixmap(filename) + + pixbuf = None try: - return Pixbuf.new_from_file(get_pixmap(filename)) + if size: + pixbuf = Pixbuf.new_from_file_at_size(filename, size, size) + else: + pixbuf = Pixbuf.new_from_file(filename) except GError as ex: + # Failed to load the pixbuf (Bad image file), so return a blank pixbuf. log.warning(ex) - return create_blank_pixbuf() + + return pixbuf or create_blank_pixbuf(size or 16) # Status icons.. Create them from file only once to avoid constantly re-creating them. @@ -68,17 +100,6 @@ icon_queued = get_pixbuf('queued16.png') icon_checking = get_pixbuf('checking16.png') -def get_pixbuf_at_size(filename, size): - if not os.path.isabs(filename): - filename = get_pixmap(filename) - try: - return Pixbuf.new_from_file_at_size(filename, size, size) - except GError as ex: - # Failed to load the pixbuf (Bad image file), so return a blank pixbuf. - log.warning(ex) - return create_blank_pixbuf(size) - - def get_logo(size): """A Deluge logo. @@ -91,7 +112,7 @@ def get_logo(size): filename = 'deluge.svg' if windows_check(): filename = 'deluge.png' - return get_pixbuf_at_size(filename, size) + return get_pixbuf(filename, size) def build_menu_radio_list( @@ -221,14 +242,11 @@ def associate_magnet_links(overwrite=False): """ if windows_check(): - try: - import winreg - except ImportError: - import _winreg as winreg # For Python 2. + import winreg try: hkey = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'Magnet') - except WindowsError: + except OSError: overwrite = True else: winreg.CloseKey(hkey) @@ -237,7 +255,7 @@ def associate_magnet_links(overwrite=False): deluge_exe = os.path.join(os.path.dirname(sys.executable), 'deluge.exe') try: magnet_key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT, 'Magnet') - except WindowsError: + except OSError: # Could not create for all users, falling back to current user magnet_key = winreg.CreateKey( winreg.HKEY_CURRENT_USER, 'Software\\Classes\\Magnet' @@ -246,14 +264,12 @@ def associate_magnet_links(overwrite=False): winreg.SetValue(magnet_key, '', winreg.REG_SZ, 'URL:Magnet Protocol') winreg.SetValueEx(magnet_key, 'URL Protocol', 0, winreg.REG_SZ, '') winreg.SetValueEx(magnet_key, 'BrowserFlags', 0, winreg.REG_DWORD, 0x8) - winreg.SetValue( - magnet_key, 'DefaultIcon', winreg.REG_SZ, '{},0'.format(deluge_exe) - ) + winreg.SetValue(magnet_key, 'DefaultIcon', winreg.REG_SZ, f'{deluge_exe},0') winreg.SetValue( magnet_key, r'shell\open\command', winreg.REG_SZ, - '"{}" "%1"'.format(deluge_exe), + f'"{deluge_exe}" "%1"', ) winreg.CloseKey(magnet_key) @@ -267,7 +283,7 @@ def associate_magnet_links(overwrite=False): from gi.repository import GConf except ValueError: log.debug( - 'gconf not available, so will not attempt to register magnet uri handler' + 'gconf not available, so will not attempt to register magnet URI handler' ) return False else: @@ -282,11 +298,11 @@ def associate_magnet_links(overwrite=False): gconf_client.set_bool( '/desktop/gnome/url-handlers/magnet/enabled', True ) - log.info('Deluge registered as default magnet uri handler!') + log.info('Deluge registered as default magnet URI handler!') return True else: log.error( - 'Unable to register Deluge as default magnet uri handler.' + 'Unable to register Deluge as default magnet URI handler.' ) return False return False @@ -309,7 +325,7 @@ def save_pickled_state_file(filename, state): if os.path.isfile(filepath): log.debug('Creating backup of %s at: %s', filename, filepath_bak) shutil.copy2(filepath, filepath_bak) - except IOError as ex: + except OSError as ex: log.error('Unable to backup %s to %s: %s', filepath, filepath_bak, ex) else: log.info('Saving the %s at: %s', filename, filepath) @@ -320,7 +336,7 @@ def save_pickled_state_file(filename, state): _file.flush() os.fsync(_file.fileno()) shutil.move(filepath_tmp, filepath) - except (IOError, EOFError, pickle.PicklingError) as ex: + except (OSError, EOFError, pickle.PicklingError) as ex: log.error('Unable to save %s: %s', filename, ex) if os.path.isfile(filepath_bak): log.info('Restoring backup of %s from: %s', filename, filepath_bak) @@ -345,11 +361,8 @@ def load_pickled_state_file(filename): log.info('Opening %s for load: %s', filename, _filepath) try: with open(_filepath, 'rb') as _file: - if PY2: - state = pickle.load(_file) - else: - state = pickle.load(_file, encoding='utf8') - except (IOError, pickle.UnpicklingError) as ex: + state = pickle.load(_file, encoding='utf8') + except (OSError, pickle.UnpicklingError) as ex: log.warning('Unable to load %s: %s', _filepath, ex) else: log.info('Successfully loaded %s: %s', filename, _filepath) @@ -384,8 +397,8 @@ def listview_replace_treestore(listview): def get_clipboard_text(): text = ( - Clipboard.get(selection=SELECTION_CLIPBOARD).wait_for_text() - or Clipboard.get().wait_for_text() + Clipboard.get(SELECTION_CLIPBOARD).wait_for_text() + or Clipboard.get(SELECTION_PRIMARY).wait_for_text() ) if text: return text.strip() @@ -393,3 +406,30 @@ def get_clipboard_text(): def windowing(like): return like.lower() in str(type(Display.get_default())).lower() + + +def parse_ip_port(text): + """Return an IP and port from text. + + Parses both IPv4 and IPv6. + + Params: + text (str): Text to be parsed for IP and port. + + Returns: + tuple: (ip (str), port (int)) + + """ + if '.' in text: + # ipv4 + ip, __, port = text.rpartition(':') + elif '[' in text: + # ipv6 + ip, __, port = text.partition('[')[2].partition(']:') + else: + return None, None + + if ip and is_ip(ip) and port.isdigit(): + return ip, int(port) + else: + return None, None diff --git a/deluge/ui/gtk3/connectionmanager.py b/deluge/ui/gtk3/connectionmanager.py index d5883c4..b53dd8e 100644 --- a/deluge/ui/gtk3/connectionmanager.py +++ b/deluge/ui/gtk3/connectionmanager.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,11 +6,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os -from socket import gaierror, gethostbyname +from socket import gaierror, getaddrinfo +from urllib.parse import urlparse from gi.repository import Gtk from twisted.internet import defer, reactor @@ -26,12 +24,6 @@ from deluge.ui.hostlist import DEFAULT_PORT, LOCALHOST, HostList from .common import get_clipboard_text from .dialogs import AuthenticationDialog, ErrorDialog -try: - from urllib.parse import urlparse -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - log = logging.getLogger(__name__) HOSTLIST_COL_ID = 0 @@ -230,7 +222,7 @@ class ConnectionManager(component.Component): __, host, port, __, __, status, __, __ = model[row] try: - gethostbyname(host) + getaddrinfo(host, None) except gaierror as ex: log.error( 'Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1] diff --git a/deluge/ui/gtk3/createtorrentdialog.py b/deluge/ui/gtk3/createtorrentdialog.py index 1e5e73c..e9f1690 100644 --- a/deluge/ui/gtk3/createtorrentdialog.py +++ b/deluge/ui/gtk3/createtorrentdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging import os.path from base64 import b64encode @@ -31,7 +28,7 @@ from .torrentview_data_funcs import cell_data_size log = logging.getLogger(__name__) -class CreateTorrentDialog(object): +class CreateTorrentDialog: def __init__(self): pass diff --git a/deluge/ui/gtk3/details_tab.py b/deluge/ui/gtk3/details_tab.py index 2431e08..04a5eab 100644 --- a/deluge/ui/gtk3/details_tab.py +++ b/deluge/ui/gtk3/details_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from xml.sax.saxutils import escape as xml_escape @@ -23,7 +20,7 @@ log = logging.getLogger(__name__) class DetailsTab(Tab): def __init__(self): - super(DetailsTab, self).__init__('Details', 'details_tab', 'details_tab_label') + super().__init__('Details', 'details_tab', 'details_tab_label') self.add_tab_widget('summary_name', None, ('name',)) self.add_tab_widget('summary_total_size', fsize, ('total_size',)) @@ -65,7 +62,7 @@ class DetailsTab(Tab): txt = xml_escape(self.widget_status_as_fstr(widget, status)) if decode_bytes(widget.obj.get_text()) != txt: if 'comment' in widget.status_keys and is_url(txt): - widget.obj.set_markup('<a href="%s">%s</a>' % (txt, txt)) + widget.obj.set_markup(f'<a href="{txt}">{txt}</a>') else: widget.obj.set_markup(txt) diff --git a/deluge/ui/gtk3/dialogs.py b/deluge/ui/gtk3/dialogs.py index 5169ab4..db337d3 100644 --- a/deluge/ui/gtk3/dialogs.py +++ b/deluge/ui/gtk3/dialogs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> # @@ -9,7 +8,7 @@ # pylint: disable=super-on-old-class -from __future__ import unicode_literals +from collections import namedtuple from gi.repository import Gtk from twisted.internet import defer @@ -17,7 +16,9 @@ from twisted.internet import defer import deluge.component as component from deluge.common import windows_check -from .common import get_deluge_icon, get_pixbuf_at_size +from .common import get_deluge_icon, get_pixbuf + +Account = namedtuple('Account', 'username password authlevel') class BaseDialog(Gtk.Dialog): @@ -34,7 +35,7 @@ class BaseDialog(Gtk.Dialog): :param parent: gtkWindow, the parent window, if None it will default to the MainWindow """ - super(BaseDialog, self).__init__( + super().__init__( title=header, parent=parent if parent else component.get('MainWindow').window, flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, @@ -55,7 +56,7 @@ class BaseDialog(Gtk.Dialog): # Hack for Windows since it doesn't support svg if icon.endswith('.svg') and windows_check(): icon = icon.rpartition('.svg')[0] + '16.png' - image.set_from_pixbuf(get_pixbuf_at_size(icon, 24)) + image.set_from_pixbuf(get_pixbuf(icon, 24)) else: image.set_from_icon_name(icon, Gtk.IconSize.LARGE_TOOLBAR) image.set_alignment(0.5, 0.0) @@ -72,12 +73,12 @@ class BaseDialog(Gtk.Dialog): self.vbox.show_all() def _on_delete_event(self, widget, event): - self.deferred.callback(Gtk.ResponseType.DELETE_EVENT) self.destroy() + self.deferred.callback(Gtk.ResponseType.DELETE_EVENT) def _on_response(self, widget, response): - self.deferred.callback(response) self.destroy() + self.deferred.callback(response) def run(self): """ @@ -103,13 +104,15 @@ class YesNoDialog(BaseDialog): :param text: see `:class:BaseDialog` :param parent: see `:class:BaseDialog` """ - super(YesNoDialog, self).__init__( + super().__init__( header, text, 'dialog-question', (_('_No'), Gtk.ResponseType.NO, _('_Yes'), Gtk.ResponseType.YES), parent, ) + # Use the preferred size calculated from the content + self.set_default_size(-1, -1) class InformationDialog(BaseDialog): @@ -125,7 +128,7 @@ class InformationDialog(BaseDialog): :param text: see `:class:BaseDialog` :param parent: see `:class:BaseDialog` """ - super(InformationDialog, self).__init__( + super().__init__( header, text, 'dialog-information', @@ -152,13 +155,13 @@ class ErrorDialog(BaseDialog): :param traceback: show the traceback information in the details area :type traceback: bool """ - super(ErrorDialog, self).__init__( + super().__init__( header, text, 'dialog-error', (_('_Close'), Gtk.ResponseType.CLOSE), parent ) if traceback: - import traceback import sys + import traceback tb = sys.exc_info() tb = traceback.format_exc(tb[2]) @@ -196,7 +199,7 @@ class AuthenticationDialog(BaseDialog): :param err_msg: the error message we got back from the server :type err_msg: string """ - super(AuthenticationDialog, self).__init__( + super().__init__( _('Authenticate'), err_msg, 'dialog-password', @@ -253,7 +256,7 @@ class AccountDialog(BaseDialog): parent=None, ): if username: - super(AccountDialog, self).__init__( + super().__init__( _('Edit Account'), _('Edit existing account'), 'dialog-information', @@ -266,7 +269,7 @@ class AccountDialog(BaseDialog): parent, ) else: - super(AccountDialog, self).__init__( + super().__init__( _('New Account'), _('Create a new account'), 'dialog-information', @@ -274,21 +277,21 @@ class AccountDialog(BaseDialog): parent, ) - self.levels_mapping = levels_mapping + self.account = None table = Gtk.Table(2, 3, False) - self.username_label = Gtk.Label() - self.username_label.set_markup('<b>' + _('Username:') + '</b>') - self.username_label.set_alignment(1.0, 0.5) - self.username_label.set_padding(5, 5) + username_label = Gtk.Label() + username_label.set_markup('<b>' + _('Username:') + '</b>') + username_label.set_alignment(1.0, 0.5) + username_label.set_padding(5, 5) self.username_entry = Gtk.Entry() - table.attach(self.username_label, 0, 1, 0, 1) + table.attach(username_label, 0, 1, 0, 1) table.attach(self.username_entry, 1, 2, 0, 1) - self.authlevel_label = Gtk.Label() - self.authlevel_label.set_markup('<b>' + _('Authentication Level:') + '</b>') - self.authlevel_label.set_alignment(1.0, 0.5) - self.authlevel_label.set_padding(5, 5) + authlevel_label = Gtk.Label() + authlevel_label.set_markup('<b>' + _('Authentication Level:') + '</b>') + authlevel_label.set_alignment(1.0, 0.5) + authlevel_label.set_padding(5, 5) # combo_box_new_text is deprecated but no other pygtk alternative. self.authlevel_combo = Gtk.ComboBoxText() @@ -303,16 +306,16 @@ class AccountDialog(BaseDialog): if active_idx is not None: self.authlevel_combo.set_active(active_idx) - table.attach(self.authlevel_label, 0, 1, 1, 2) + table.attach(authlevel_label, 0, 1, 1, 2) table.attach(self.authlevel_combo, 1, 2, 1, 2) - self.password_label = Gtk.Label() - self.password_label.set_markup('<b>' + _('Password:') + '</b>') - self.password_label.set_alignment(1.0, 0.5) - self.password_label.set_padding(5, 5) + password_label = Gtk.Label() + password_label.set_markup('<b>' + _('Password:') + '</b>') + password_label.set_alignment(1.0, 0.5) + password_label.set_padding(5, 5) self.password_entry = Gtk.Entry() self.password_entry.set_visibility(False) - table.attach(self.password_label, 0, 1, 2, 3) + table.attach(password_label, 0, 1, 2, 3) table.attach(self.password_entry, 1, 2, 2, 3) self.vbox.pack_start(table, False, False, padding=5) @@ -325,18 +328,17 @@ class AccountDialog(BaseDialog): if password: self.password_entry.set_text(username) - self.show_all() - - def get_username(self): - return self.username_entry.get_text() - - def get_password(self): - return self.password_entry.get_text() + self.vbox.show_all() - def get_authlevel(self): - combobox = self.authlevel_combo - level = combobox.get_model()[combobox.get_active()][0] - return level + def _on_response(self, widget, response): + if response == Gtk.ResponseType.OK: + self.account = Account( + self.username_entry.get_text(), + self.password_entry.get_text(), + self.authlevel_combo.get_active_text(), + ) + self.destroy() + self.deferred.callback(response) class OtherDialog(BaseDialog): @@ -357,7 +359,7 @@ class OtherDialog(BaseDialog): if not icon: icon = 'dialog-information' - super(OtherDialog, self).__init__( + super().__init__( header, text, icon, @@ -419,7 +421,7 @@ class PasswordDialog(BaseDialog): :param password_msg: the error message we got back from the server :type password_msg: string """ - super(PasswordDialog, self).__init__( + super().__init__( header=_('Password Protected'), text=password_msg, icon='dialog-password', @@ -453,3 +455,44 @@ class PasswordDialog(BaseDialog): def on_password_activate(self, widget): self.response(Gtk.ResponseType.OK) + + +class CopyMagnetDialog(BaseDialog): + """ + Displays a dialog with a magnet URI + """ + + def __init__(self, torrent_magnet='', parent=None): + super().__init__( + header=_('Copy Magnet URI'), + text='', + icon='magnet_copy.svg', + buttons=(_('_Close'), Gtk.ResponseType.CLOSE), + parent=parent, + ) + self.copied = False + + table = Gtk.Table(1, 2, False) + self.magnet_entry = Gtk.Entry() + self.magnet_entry.set_text(torrent_magnet) + self.magnet_entry.set_editable(False) + self.magnet_entry.connect('copy-clipboard', self.on_copy_emitted) + table.attach(self.magnet_entry, 0, 1, 0, 1) + + copy_button = Gtk.Button.new_with_label(_('Copy')) + copy_button.connect('clicked', self.on_copy_clicked) + table.attach(copy_button, 1, 2, 0, 1) + + self.vbox.pack_start(table, False, False, padding=5) + self.set_focus(self.magnet_entry) + + self.show_all() + + def on_copy_clicked(self, widget): + self.magnet_entry.select_region(0, -1) + self.magnet_entry.copy_clipboard() + self.magnet_entry.set_position(0) + self.copied = True + + def on_copy_emitted(self, widget): + self.copied = True diff --git a/deluge/ui/gtk3/edittrackersdialog.py b/deluge/ui/gtk3/edittrackersdialog.py index 1dfdd2a..861e392 100644 --- a/deluge/ui/gtk3/edittrackersdialog.py +++ b/deluge/ui/gtk3/edittrackersdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,12 +6,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path -from gi.repository import Gtk +from gi.repository import Gdk, Gtk from twisted.internet import defer import deluge.component as component @@ -77,7 +74,7 @@ def trackers_tiers_from_text(text_str=''): return trackers -class EditTrackersDialog(object): +class EditTrackersDialog: def __init__(self, torrent_id, parent=None): self.torrent_id = torrent_id self.builder = Gtk.Builder() @@ -132,6 +129,12 @@ class EditTrackersDialog(object): 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 @@ -191,7 +194,7 @@ class EditTrackersDialog(object): 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.treeview.set_cursor(0) self.dialog.show() def add_tracker(self, tier, url): @@ -207,6 +210,7 @@ class EditTrackersDialog(object): # 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') @@ -215,8 +219,18 @@ class EditTrackersDialog(object): self.liststore.remove(selected) def on_button_edit_clicked(self, widget): - """edits an existing tracker""" + """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) @@ -225,11 +239,27 @@ class EditTrackersDialog(object): self.edit_tracker_entry.grab_focus() self.dialog.set_sensitive(False) - def on_button_edit_cancel_clicked(self, widget): - log.debug('on_button_edit_cancel_clicked') + 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() @@ -290,11 +320,29 @@ class EditTrackersDialog(object): # Clear the entry widget and hide the dialog textview_buf.set_text('') + self.dialog.set_sensitive(True) self.add_tracker_dialog.hide() - def on_button_add_cancel_clicked(self, widget): - log.debug('on_button_add_cancel_clicked') + 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 diff --git a/deluge/ui/gtk3/files_tab.py b/deluge/ui/gtk3/files_tab.py index b3bd5b5..24c1697 100644 --- a/deluge/ui/gtk3/files_tab.py +++ b/deluge/ui/gtk3/files_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,21 +6,26 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import json import logging import os.path +import gi # isort:skip (Required before Gtk import). + +gi.require_version('Gtk', '3.0') + +# isort:imports-thirdparty from gi.repository import Gio, Gtk from gi.repository.Gdk import DragAction, ModifierType, keyval_name from gi.repository.GObject import TYPE_UINT64 +# isort:imports-firstparty import deluge.component as component from deluge.common import open_file, show_file from deluge.ui.client import client from deluge.ui.common import FILE_PRIORITY +# isort:imports-localfolder from .common import ( listview_replace_treestore, load_pickled_state_file, @@ -77,7 +81,7 @@ def cell_progress(column, cell, model, row, data): class FilesTab(Tab): def __init__(self): - super(FilesTab, self).__init__('Files', 'files_tab', 'files_tab_label') + super().__init__('Files', 'files_tab', 'files_tab_label') self.listview = self.main_builder.get_object('files_listview') # filename, size, progress string, progress value, priority, file index, icon id @@ -442,7 +446,7 @@ class FilesTab(Tab): try: value = completed_bytes / self.treestore[parent][1] * 100 except ZeroDivisionError: - # Catch the unusal error found when moving folders around + # Catch the unusual error found when moving folders around value = 0 self.treestore[parent][3] = value self.treestore[parent][2] = '%i%%' % value diff --git a/deluge/ui/gtk3/filtertreeview.py b/deluge/ui/gtk3/filtertreeview.py index bd781e0..40752d7 100644 --- a/deluge/ui/gtk3/filtertreeview.py +++ b/deluge/ui/gtk3/filtertreeview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> # 2008 Andrew Resch <andrewresch@gmail.com> @@ -9,8 +8,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os import warnings @@ -24,7 +21,7 @@ from deluge.common import TORRENT_STATE, decode_bytes, resource_filename from deluge.configmanager import ConfigManager from deluge.ui.client import client -from .common import get_pixbuf, get_pixbuf_at_size +from .common import get_pixbuf log = logging.getLogger(__name__) @@ -89,10 +86,10 @@ class FilterTreeView(component.Component): self.treeview.set_headers_visible(False) self.treeview.set_level_indentation(-21) # Force theme to use expander-size so we don't cut out entries due to indentation hack. - Gtk.rc_parse_string( - """style "treeview-style" {GtkTreeView::expander-size = 7} - class "GtkTreeView" style "treeview-style" """ - ) + provider = Gtk.CssProvider() + provider.load_from_data(b'* {-GtkTreeView-expander-size: 9;}') + context = self.treeview.get_style_context() + context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) self.treeview.set_model(self.treestore) self.treeview.get_selection().connect('changed', self.on_selection_changed) @@ -100,11 +97,6 @@ class FilterTreeView(component.Component): self.treeview.connect('button-press-event', self.on_button_press_event) - # colors using current theme. - style_ctx = component.get('MainWindow').window.get_style_context() - self.colour_background = style_ctx.get_background_color(Gtk.StateFlags.NORMAL) - self.colour_foreground = style_ctx.get_color(Gtk.StateFlags.NORMAL) - # filtertree menu builder = Gtk.Builder() builder.add_from_file( @@ -229,7 +221,7 @@ class FilterTreeView(component.Component): label = decode_bytes(model.get_value(row, 2)) count = model.get_value(row, 3) - # Supress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed + # Suppress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed original_filters = warnings.filters[:] warnings.simplefilter('ignore') try: @@ -261,7 +253,7 @@ class FilterTreeView(component.Component): return get_pixbuf('%s16.png' % pix) def set_row_image(self, cat, value, filename): - pix = get_pixbuf_at_size(filename, 16) + pix = get_pixbuf(filename, size=16) row = self.filters[(cat, value)] self.treestore.set_value(row, 4, pix) return False diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui index a7a8cae..8adbad3 100644 --- a/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui +++ b/deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui @@ -144,8 +144,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.ui index 4d36803..7183272 100644 --- a/deluge/ui/gtk3/glade/add_torrent_dialog.ui +++ b/deluge/ui/gtk3/glade/add_torrent_dialog.ui @@ -727,8 +727,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxup"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment2</property> <property name="update_policy">if-valid</property> </object> @@ -741,8 +739,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxconnections"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment3</property> </object> <packing> @@ -754,8 +750,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxupslots"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment4</property> </object> <packing> @@ -767,8 +761,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_maxdown"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment1</property> </object> <packing> diff --git a/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui b/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui index ecbd0f7..6b75b23 100644 --- a/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui +++ b/deluge/ui/gtk3/glade/add_torrent_dialog.url.ui @@ -143,8 +143,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/connect_peer_dialog.ui b/deluge/ui/gtk3/glade/connect_peer_dialog.ui index f5e9337..4a60751 100644 --- a/deluge/ui/gtk3/glade/connect_peer_dialog.ui +++ b/deluge/ui/gtk3/glade/connect_peer_dialog.ui @@ -128,8 +128,6 @@ <property name="width_chars">39</property> <property name="text" translatable="yes">hostname:port</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/connection_manager.addhost.ui b/deluge/ui/gtk3/glade/connection_manager.addhost.ui index 641a71c..ea5376e 100644 --- a/deluge/ui/gtk3/glade/connection_manager.addhost.ui +++ b/deluge/ui/gtk3/glade/connection_manager.addhost.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.22.1 --> +<!-- Generated with glade 3.22.2 --> <interface> <requires lib="gtk+" version="3.0"/> <object class="GtkAdjustment" id="adjustment_port"> @@ -16,7 +16,7 @@ <property name="window_position">center-on-parent</property> <property name="destroy_with_parent">True</property> <property name="type_hint">dialog</property> - <child> + <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> @@ -24,11 +24,12 @@ <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> - <property name="spacing">2</property> + <property name="spacing">5</property> <child internal-child="action_area"> <object class="GtkButtonBox" id="dialog-action_area5"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="margin_top">15</property> <property name="layout_style">end</property> <child> <object class="GtkButton" id="button_addhost_cancel"> @@ -65,49 +66,41 @@ <property name="expand">False</property> <property name="fill">True</property> <property name="pack_type">end</property> - <property name="position">0</property> + <property name="position">1</property> </packing> </child> <child> - <object class="GtkBox" id="hbox2"> + <object class="GtkGrid"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="spacing">5</property> + <property name="row_spacing">5</property> + <property name="column_spacing">5</property> <child> <object class="GtkLabel" id="label3"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="halign">start</property> <property name="label" translatable="yes">Hostname:</property> + <property name="xalign">0</property> </object> <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> + <property name="left_attach">0</property> + <property name="top_attach">0</property> </packing> </child> <child> - <object class="GtkAlignment" id="alignment4"> + <object class="GtkEntry" id="entry_hostname"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="left_padding">1</property> - <child> - <object class="GtkEntry" id="entry_hostname"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="activates_default">True</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" swapped="no"/> - </object> - </child> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="invisible_char">•</property> + <property name="activates_default">True</property> + <property name="truncate_multiline">True</property> + <signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" swapped="no"/> </object> <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> + <property name="left_attach">1</property> + <property name="top_attach">0</property> </packing> </child> <child> @@ -115,116 +108,90 @@ <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">Port:</property> + <property name="xalign">0</property> </object> <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> + <property name="left_attach">0</property> + <property name="top_attach">1</property> </packing> </child> <child> <object class="GtkSpinButton" id="spinbutton_port"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="halign">start</property> <property name="max_length">5</property> <property name="invisible_char">•</property> <property name="width_chars">5</property> + <property name="max_width_chars">5</property> <property name="progress_pulse_step">1</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_port</property> <property name="climb_rate">1</property> <property name="numeric">True</property> </object> <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">3</property> + <property name="left_attach">1</property> + <property name="top_attach">1</property> </packing> </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkGrid" id="table1"> - <property name="visible">True</property> - <property name="can_focus">False</property> <child> - <object class="GtkAlignment" id="alignment3"> + <object class="GtkLabel" id="label5"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="left_padding">5</property> - <child> - <object class="GtkEntry" id="entry_password"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="visibility">False</property> - <property name="invisible_char">•</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - </object> - </child> + <property name="halign">start</property> + <property name="label" translatable="yes">Username:</property> + <property name="xalign">0</property> </object> <packing> - <property name="left_attach">1</property> - <property name="top_attach">1</property> + <property name="left_attach">0</property> + <property name="top_attach">2</property> </packing> </child> <child> - <object class="GtkAlignment" id="alignment2"> + <object class="GtkEntry" id="entry_username"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="left_padding">5</property> - <child> - <object class="GtkEntry" id="entry_username"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - </object> - </child> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="invisible_char">•</property> + <property name="truncate_multiline">True</property> </object> <packing> <property name="left_attach">1</property> - <property name="top_attach">0</property> + <property name="top_attach">2</property> </packing> </child> <child> - <object class="GtkLabel" id="label5"> + <object class="GtkLabel" id="label6"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="halign">start</property> - <property name="label" translatable="yes">Username:</property> + <property name="label" translatable="yes">Password:</property> + <property name="xalign">0</property> </object> <packing> <property name="left_attach">0</property> - <property name="top_attach">0</property> + <property name="top_attach">3</property> </packing> </child> <child> - <object class="GtkLabel" id="label6"> + <object class="GtkEntry" id="entry_password"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">start</property> - <property name="label" translatable="yes">Password:</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="visibility">False</property> + <property name="invisible_char">•</property> + <property name="truncate_multiline">True</property> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> + <property name="left_attach">1</property> + <property name="top_attach">3</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">0</property> </packing> </child> </object> diff --git a/deluge/ui/gtk3/glade/connection_manager.ui b/deluge/ui/gtk3/glade/connection_manager.ui index 11516aa..44f4b34 100644 --- a/deluge/ui/gtk3/glade/connection_manager.ui +++ b/deluge/ui/gtk3/glade/connection_manager.ui @@ -30,7 +30,7 @@ <property name="modal">True</property> <property name="window_position">center-on-parent</property> <property name="default_width">300</property> - <property name="default_height">250</property> + <property name="default_height">285</property> <property name="destroy_with_parent">True</property> <property name="type_hint">dialog</property> <child> diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui index dc7b7e9..4328330 100644 --- a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui +++ b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui @@ -143,8 +143,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui index a380718..7123054 100644 --- a/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui +++ b/deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui @@ -143,8 +143,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/create_torrent_dialog.ui b/deluge/ui/gtk3/glade/create_torrent_dialog.ui index c27a4b8..0d15940 100644 --- a/deluge/ui/gtk3/glade/create_torrent_dialog.ui +++ b/deluge/ui/gtk3/glade/create_torrent_dialog.ui @@ -356,8 +356,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> @@ -393,8 +391,6 @@ <object class="GtkEntry" id="entry_comments"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> @@ -810,6 +806,7 @@ <property name="receives_default">True</property> <property name="use_underline">True</property> <signal name="clicked" handler="on_button_cancel_clicked" swapped="no"/> + <accelerator key="Escape" signal="clicked"/> </object> <packing> <property name="expand">False</property> diff --git a/deluge/ui/gtk3/glade/edit_trackers.edit.ui b/deluge/ui/gtk3/glade/edit_trackers.edit.ui index 2521e8f..fc3e51b 100644 --- a/deluge/ui/gtk3/glade/edit_trackers.edit.ui +++ b/deluge/ui/gtk3/glade/edit_trackers.edit.ui @@ -144,8 +144,6 @@ <property name="invisible_char">•</property> <property name="activates_default">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">True</property> diff --git a/deluge/ui/gtk3/glade/main_window.tabs.ui b/deluge/ui/gtk3/glade/main_window.tabs.ui index 30bd395..7ecf618 100644 --- a/deluge/ui/gtk3/glade/main_window.tabs.ui +++ b/deluge/ui/gtk3/glade/main_window.tabs.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.22.1 --> +<!-- Generated with glade 3.22.2 --> <interface> <requires lib="gtk+" version="3.0"/> <object class="GtkAdjustment" id="spin_max_connections_adjustment"> @@ -38,14 +38,14 @@ </object> <object class="GtkWindow" id="tabs"> <property name="can_focus">False</property> - <child> + <child type="titlebar"> <placeholder/> </child> <child> <object class="GtkNotebook" id="dummy_nb_see_main_win_torrent_info"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="tab_pos">left</property> + <property name="enable_popup">True</property> <child> <object class="GtkScrolledWindow" id="status_tab"> <property name="visible">True</property> @@ -105,7 +105,7 @@ <property name="row_spacing">5</property> <property name="column_spacing">10</property> <child> - <object class="GtkLabel" id="summary_total_uploaded"> + <object class="GtkLabel" id="summary_download_speed"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="width_chars">20</property> @@ -305,7 +305,7 @@ </packing> </child> <child> - <object class="GtkLabel" id="summary_download_speed"> + <object class="GtkLabel" id="summary_total_uploaded"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="width_chars">15</property> @@ -490,7 +490,6 @@ <object class="GtkLabel" id="status_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Status</property> <property name="use_underline">True</property> </object> @@ -850,12 +849,6 @@ <child> <placeholder/> </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> </object> </child> </object> @@ -871,7 +864,6 @@ <object class="GtkLabel" id="details_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Details</property> <property name="use_underline">True</property> </object> @@ -902,7 +894,6 @@ <object class="GtkLabel" id="files_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">Fi_les</property> <property name="use_underline">True</property> </object> @@ -933,7 +924,6 @@ <object class="GtkLabel" id="peers_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Peers</property> <property name="use_underline">True</property> </object> @@ -1065,23 +1055,20 @@ </packing> </child> <child> - <placeholder/> - </child> - <child> <object class="GtkCheckButton" id="chk_super_seeding"> <property name="label" translatable="yes">Super Seeding</property> - <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> + <property name="halign">start</property> <property name="draw_indicator">True</property> <signal name="toggled" handler="on_chk_toggled" swapped="no"/> </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> - </packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> </child> <child> <object class="GtkCheckButton" id="chk_move_completed"> @@ -1113,6 +1100,9 @@ <property name="position">6</property> </packing> </child> + <child> + <placeholder/> + </child> </object> </child> </object> @@ -1176,8 +1166,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_stop_ratio_adjustment</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -1277,8 +1265,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_connections_adjustment</property> <property name="numeric">True</property> </object> @@ -1292,8 +1278,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_upload_adjustment</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -1308,8 +1292,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_download_adjustment</property> <property name="climb_rate">1</property> <property name="digits">1</property> @@ -1366,8 +1348,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">spin_max_upload_slots_adjustment</property> <property name="numeric">True</property> </object> @@ -1453,7 +1433,6 @@ <object class="GtkLabel" id="options_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Options</property> <property name="use_underline">True</property> </object> @@ -1664,7 +1643,6 @@ <object class="GtkLabel" id="trackers_tab_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="ypad">1</property> <property name="label" translatable="yes">_Trackers</property> <property name="use_underline">True</property> </object> diff --git a/deluge/ui/gtk3/glade/main_window.tabs.ui~ b/deluge/ui/gtk3/glade/main_window.tabs.ui~ new file mode 100644 index 0000000..4362183 --- /dev/null +++ b/deluge/ui/gtk3/glade/main_window.tabs.ui~ @@ -0,0 +1,1507 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.2 --> +<interface> + <requires lib="gtk+" version="3.0"/> + <object class="GtkAdjustment" id="spin_max_connections_adjustment"> + <property name="lower">-1</property> + <property name="upper">999999</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_max_download_adjustment"> + <property name="lower">-1</property> + <property name="upper">2097151</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_max_upload_adjustment"> + <property name="lower">-1</property> + <property name="upper">2097151</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_max_upload_slots_adjustment"> + <property name="lower">-1</property> + <property name="upper">999999</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkAdjustment" id="spin_stop_ratio_adjustment"> + <property name="upper">999999</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + <signal name="value-changed" handler="on_spin_value_changed" swapped="no"/> + </object> + <object class="GtkWindow" id="tabs"> + <property name="can_focus">False</property> + <child type="titlebar"> + <placeholder/> + </child> + <child> + <object class="GtkNotebook" id="dummy_nb_see_main_win_torrent_info"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="enable_popup">True</property> + <child> + <object class="GtkScrolledWindow" id="status_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkViewport" id="viewport1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="resize_mode">queue</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment43"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">5</property> + <property name="bottom_padding">2</property> + <property name="left_padding">10</property> + <property name="right_padding">10</property> + <child> + <object class="GtkBox" id="vbox3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">5</property> + <child> + <object class="GtkBox" id="status_progress_vbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkProgressBar" id="progressbar"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="pulse_step">0.10000000149</property> + <property name="show_text">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="table8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">5</property> + <property name="column_spacing">10</property> + <child> + <object class="GtkLabel" id="summary_download_speed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">20</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_total_downloaded"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_upload_speed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">15</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_seed_rank"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_availability"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap">True</property> + <property name="wrap_mode">word-char</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_share_ratio"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_peers"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">10</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_eta"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_active_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_seed_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_last_transfer"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap">True</property> + <property name="wrap_mode">char</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_last_seen_complete"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap">True</property> + <property name="wrap_mode">char</property> + </object> + <packing> + <property name="left_attach">5</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label42"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Down Speed:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label43"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Up Speed:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label38"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Downloaded:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label39"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Uploaded:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_seeds"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">10</property> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_total_uploaded"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="width_chars">15</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_seeds"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Seeds:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_peers"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Peers:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label41"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Share Ratio:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_availablity"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Availability:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_seed_rank"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Seed Rank:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_eta"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">ETA Time:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_last_transfer"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Last Transfer:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_active_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Active Time:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_last_seen_complete"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Complete Seen:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_seed_time"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Seeding Time:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + <child type="tab"> + <object class="GtkLabel" id="status_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Status</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="details_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkViewport" id="viewport2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="resize_mode">queue</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment54"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">5</property> + <property name="bottom_padding">2</property> + <property name="left_padding">10</property> + <property name="right_padding">15</property> + <child> + <object class="GtkGrid" id="table_details"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">5</property> + <property name="column_spacing">10</property> + <child> + <object class="GtkLabel" id="summary_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap">True</property> + <property name="wrap_mode">word-char</property> + <property name="selectable">True</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_total_size"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_num_files"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_completed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_date_added"> + <property name="width_request">100</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_private"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_pieces"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Pieces:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_pieces"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">4</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_hash"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="width_chars">40</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_torrent_path"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap">True</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + <property name="ellipsize">start</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_comments"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_creator"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Name:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_path"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Download Folder:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_date_added"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Added:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_total_size"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Total Size:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_num_files"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Total Files:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_hash"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Hash:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_creator"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Created By:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_comments"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Comments:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_completed"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Completed:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_private"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Private Torrent:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="left_attach">3</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSeparator" id="vseparator5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + <property name="height">4</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="details_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Details</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">1</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="files_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkTreeView" id="files_listview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeselection1"/> + </child> + </object> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="files_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Fi_les</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">2</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="peers_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkTreeView" id="peers_listview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeselection2"/> + </child> + </object> + </child> + </object> + <packing> + <property name="position">3</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="peers_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Peers</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">3</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="options_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkViewport" id="viewport3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="resize_mode">queue</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">5</property> + <property name="bottom_padding">2</property> + <property name="left_padding">5</property> + <property name="right_padding">15</property> + <child> + <object class="GtkGrid" id="table6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkAlignment" id="alignment8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="top_padding">2</property> + <property name="left_padding">5</property> + <property name="right_padding">5</property> + <child> + <object class="GtkBox" id="vbox1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="hbox1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="label_owner"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Owner:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="summary_owner"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">10</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_shared"> + <property name="label" translatable="yes">Shared</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_prioritize_first_last"> + <property name="label" translatable="yes">Prioritize First/Last</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_sequential_download"> + <property name="label" translatable="yes">Sequential Download</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_super_seeding"> + <property name="label" translatable="yes">Super Seeding</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_move_completed"> + <property name="label" translatable="yes">Move completed:</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_move_completed_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">5</property> + </packing> + </child> + <child> + <object class="GtkBox" id="hbox_move_completed_path_chooser"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">6</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <child> + <object class="GtkBox" id="vbox6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">15</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkCheckButton" id="chk_auto_managed"> + <property name="label" translatable="yes">Auto Managed</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="chk_stop_at_ratio"> + <property name="label" translatable="yes">Stop seed at ratio:</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_stop_at_ratio_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <property name="right_padding">10</property> + <child> + <object class="GtkSpinButton" id="spin_stop_ratio"> + <property name="width_request">50</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_stop_ratio_adjustment</property> + <property name="digits">1</property> + <property name="numeric">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <child> + <object class="GtkCheckButton" id="chk_remove_at_ratio"> + <property name="label" translatable="yes">Remove at ratio</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_chk_toggled" swapped="no"/> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkSeparator" id="hseparator1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="padding">7</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkButton" id="button_apply"> + <property name="label" translatable="yes">_Apply</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <signal name="clicked" handler="on_button_apply_clicked" swapped="no"/> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkGrid" id="table1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="border_width">5</property> + <property name="row_spacing">2</property> + <property name="column_spacing">4</property> + <child> + <object class="GtkSpinButton" id="spin_max_connections"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_connections_adjustment</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spin_max_upload"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_upload_adjustment</property> + <property name="digits">1</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spin_max_download"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_download_adjustment</property> + <property name="climb_rate">1</property> + <property name="digits">1</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label11"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Upload Speed:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label13"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">K/s</property> + <property name="ellipsize">start</property> + <attributes> + <attribute name="scale" value="0.90000000000000002"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label14"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">K/s</property> + <attributes> + <attribute name="scale" value="0.90000000000000002"/> + </attributes> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="spin_max_upload_slots"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="adjustment">spin_max_upload_slots_adjustment</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Download Speed:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label12"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Connections:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label15"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Upload Slots:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Bandwidth Limits</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">4</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="options_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Options</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">4</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="trackers_tab"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkTreeView" id="trackers_listview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeselection3"/> + </child> + </object> + </child> + </object> + <packing> + <property name="position">5</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="trackers_tab_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Trackers</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="position">5</property> + <property name="tab_fill">False</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/deluge/ui/gtk3/glade/main_window.ui b/deluge/ui/gtk3/glade/main_window.ui index 43d8bf0..ecbb8f6 100644 --- a/deluge/ui/gtk3/glade/main_window.ui +++ b/deluge/ui/gtk3/glade/main_window.ui @@ -174,6 +174,7 @@ <property name="use_stock">False</property> <property name="accel_group">accelgroup1</property> <signal name="activate" handler="on_menuitem_quit_activate" swapped="no"/> + <accelerator key="Q" signal="activate" modifiers="GDK_CONTROL_MASK"/> </object> </child> </object> diff --git a/deluge/ui/gtk3/glade/other_dialog.ui b/deluge/ui/gtk3/glade/other_dialog.ui index 26d3d08..01a5597 100644 --- a/deluge/ui/gtk3/glade/other_dialog.ui +++ b/deluge/ui/gtk3/glade/other_dialog.ui @@ -148,8 +148,6 @@ <property name="max_length">6</property> <property name="activates_default">True</property> <property name="width_chars">6</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment1</property> </object> </child> diff --git a/deluge/ui/gtk3/glade/path_combo_chooser.ui b/deluge/ui/gtk3/glade/path_combo_chooser.ui index f79685d..871bac0 100644 --- a/deluge/ui/gtk3/glade/path_combo_chooser.ui +++ b/deluge/ui/gtk3/glade/path_combo_chooser.ui @@ -98,8 +98,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment3</property> <property name="climb_rate">1</property> <property name="numeric">True</property> @@ -571,6 +569,7 @@ <object class="GtkEntry" id="entry_text"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="hexpand">True</property> <property name="invisible_char">•</property> <signal name="changed" handler="on_entry_text_changed" swapped="no"/> <signal name="delete-text" handler="on_entry_text_delete_text" swapped="no"/> diff --git a/deluge/ui/gtk3/glade/preferences_dialog.ui b/deluge/ui/gtk3/glade/preferences_dialog.ui index e1bbc74..aa1531d 100644 --- a/deluge/ui/gtk3/glade/preferences_dialog.ui +++ b/deluge/ui/gtk3/glade/preferences_dialog.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.22.1 --> +<!-- Generated with glade 3.22.2 --> <interface> <requires lib="gtk+" version="3.0"/> <object class="GtkAdjustment" id="adjustment_cache_expiry"> @@ -245,7 +245,7 @@ <property name="type_hint">dialog</property> <signal name="configure-event" handler="on_pref_dialog_configure_event" swapped="no"/> <signal name="delete-event" handler="on_pref_dialog_delete_event" swapped="no"/> - <child> + <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> @@ -479,6 +479,23 @@ </packing> </child> <child> + <object class="GtkCheckButton" id="urldetect_toggle"> + <property name="label" translatable="yes">Detect torrent URLs from clipboard</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="tooltip_text" translatable="yes">Automatically open Add Torrent dialog when clipboard contains a torrent URL</property> + <property name="halign">start</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_urldetect_toggle_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> <object class="GtkCheckButton" id="piecesbar_toggle"> <property name="visible">True</property> <property name="can_focus">True</property> @@ -501,7 +518,7 @@ and daemon (does not apply in Standalone mode).</property> <packing> <property name="expand">True</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">3</property> </packing> </child> <child> @@ -697,7 +714,7 @@ and daemon (does not apply in Standalone mode).</property> <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">3</property> + <property name="position">4</property> </packing> </child> </object> @@ -912,8 +929,6 @@ and daemon (does not apply in Standalone mode).</property> <property name="width_chars">16</property> <property name="text">********</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">False</property> @@ -1078,7 +1093,7 @@ and daemon (does not apply in Standalone mode).</property> <object class="GtkLabel" id="label5"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes"><b>Languge</b></property> + <property name="label" translatable="yes"><b>Language</b></property> <property name="use_markup">True</property> </object> </child> @@ -1133,6 +1148,7 @@ and daemon (does not apply in Standalone mode).</property> <property name="top_padding">2</property> <property name="bottom_padding">2</property> <property name="left_padding">12</property> + <property name="right_padding">12</property> <child> <object class="GtkGrid" id="table9"> <property name="visible">True</property> @@ -1528,8 +1544,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_max_connections_per_second"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_conn_per_sec</property> <property name="numeric">True</property> </object> @@ -1542,8 +1556,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_max_half_open_connections"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_half_open_conn</property> <property name="numeric">True</property> </object> @@ -1607,8 +1619,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum number of connections allowed. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_conn_global</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -1638,8 +1648,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum download speed for all torrents. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_download</property> <property name="climb_rate">1</property> <property name="digits">1</property> @@ -1655,8 +1663,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload speed for all torrents. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload</property> <property name="climb_rate">1</property> <property name="digits">1</property> @@ -1672,8 +1678,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload slots for all torrents. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload_slots_global</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -1832,8 +1836,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload slots per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload_slots_per_torrent</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -1849,8 +1851,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum number of connections per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_conn_per_torrent</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -1913,8 +1913,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum number download speed per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_download_per_torrent</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -1929,8 +1927,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">The maximum upload speed per torrent. Set -1 for unlimited.</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_max_upload_per_torrent</property> <property name="digits">1</property> <property name="numeric">True</property> @@ -2114,8 +2110,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_active"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_active</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -2129,8 +2123,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_seeding"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_seeding</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -2168,8 +2160,6 @@ used sparingly.</property> <object class="GtkSpinButton" id="spin_downloading"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_downloading</property> <property name="snap_to_ticks">True</property> <property name="numeric">True</property> @@ -2204,7 +2194,7 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> - <property name="tooltip_text" translatable="yes">Torrents not transfering any data do not count towards download/seeding active count.</property> + <property name="tooltip_text" translatable="yes">Torrents not transferring any data do not count towards download/seeding active count.</property> <property name="halign">start</property> <property name="draw_indicator">True</property> </object> @@ -2306,8 +2296,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_seed_time_limit</property> </object> <packing> @@ -2320,8 +2308,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_time_ratio_limit</property> <property name="digits">2</property> </object> @@ -2335,8 +2321,6 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_share_ratio_limit</property> <property name="digits">2</property> </object> @@ -2408,8 +2392,6 @@ used sparingly.</property> <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_share_ratio</property> <property name="digits">2</property> <property name="numeric">True</property> @@ -2555,12 +2537,10 @@ used sparingly.</property> <object class="GtkEntry" id="entry_interface"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="tooltip_text" translatable="yes">The IP address of the interface to listen for incoming bittorrent connections on. Leave this empty if you want to use the default.</property> - <property name="max_length">15</property> + <property name="tooltip_text" translatable="yes">IP address or network interface name to listen for incoming BitTorrent connections. Leave empty to use system default.</property> + <property name="max_length">40</property> <property name="width_chars">15</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> </child> </object> @@ -2569,7 +2549,7 @@ used sparingly.</property> <object class="GtkLabel" id="label110"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes">Incoming Address</property> + <property name="label" translatable="yes">Incoming Interface</property> <attributes> <attribute name="weight" value="bold"/> </attributes> @@ -2611,8 +2591,6 @@ used sparingly.</property> <property name="can_focus">True</property> <property name="max_length">5</property> <property name="max_width_chars">6</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_incoming_port</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -2711,6 +2689,24 @@ used sparingly.</property> </packing> </child> <child> + <object class="GtkAlignment" id="alignment31"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">10</property> + <child> + <object class="GtkSpinner" id="port_spinner"> + <property name="visible">False</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> <object class="GtkAlignment" id="alignment48"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -2725,7 +2721,7 @@ used sparingly.</property> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">3</property> </packing> </child> </object> @@ -2776,14 +2772,12 @@ used sparingly.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes"> -The network interface name or IP address for outgoing BitTorrent connections. (Leave empty for default.) + IP address or network interface name for outgoing BitTorrent connections. Leave empty to use system default. </property> - <property name="max_length">15</property> + <property name="max_length">40</property> <property name="invisible_char">●</property> <property name="width_chars">15</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> </child> </object> @@ -2862,8 +2856,7 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">5</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> + <property name="width_chars">7</property> <property name="adjustment">adjustment_spin_outgoing_port_min</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -2894,8 +2887,7 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">5</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> + <property name="width_chars">7</property> <property name="adjustment">adjustment_spin_outgoing_port_max</property> <property name="climb_rate">1</property> <property name="snap_to_ticks">True</property> @@ -3217,8 +3209,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="width_chars">1</property> <property name="text">0x00</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">False</property> @@ -3326,8 +3316,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="can_focus">True</property> <property name="visibility">False</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="left_attach">1</property> @@ -3351,8 +3339,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="visible">True</property> <property name="can_focus">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <signal name="paste-clipboard" handler="on_entry_proxy_host_paste_clipboard" swapped="no"/> </object> <packing> @@ -3380,8 +3366,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <object class="GtkSpinButton" id="spin_proxy_port"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_proxy_port</property> <property name="numeric">True</property> </object> @@ -3397,8 +3381,6 @@ The network interface name or IP address for outgoing BitTorrent connections. (L <property name="visible">True</property> <property name="can_focus">True</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="left_attach">1</property> @@ -3677,8 +3659,6 @@ the proxy instead of using the local DNS service</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">●</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_cache_size</property> <property name="numeric">True</property> <property name="update_policy">if-valid</property> @@ -3694,8 +3674,6 @@ the proxy instead of using the local DNS service</property> <property name="can_focus">True</property> <property name="max_length">5</property> <property name="invisible_char">●</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_cache_expiry</property> </object> <packing> @@ -3878,28 +3856,16 @@ the proxy instead of using the local DNS service</property> </packing> </child> <child> - <object class="GtkLabel" id="label119"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">The number of blocks that were served from cache.</property> - <property name="halign">start</property> - <property name="label" translatable="yes">Blocks Read Hit:</property> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> <object class="GtkLabel" id="label122"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">The cache hit ratio for the read cache.</property> + <property name="halign">start</property> <property name="label" translatable="yes">Read Cache Hit Ratio:</property> </object> <packing> <property name="left_attach">0</property> - <property name="top_attach">3</property> + <property name="top_attach">2</property> </packing> </child> <child> @@ -3913,23 +3879,13 @@ the proxy instead of using the local DNS service</property> </packing> </child> <child> - <object class="GtkLabel" id="label_cache_num_blocks_cache_hits"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> <object class="GtkLabel" id="label_cache_read_hit_ratio"> <property name="visible">True</property> <property name="can_focus">False</property> </object> <packing> <property name="left_attach">1</property> - <property name="top_attach">3</property> + <property name="top_attach">2</property> </packing> </child> <child> @@ -3943,7 +3899,7 @@ the proxy instead of using the local DNS service</property> </object> <packing> <property name="left_attach">0</property> - <property name="top_attach">2</property> + <property name="top_attach">1</property> </packing> </child> <child> @@ -3953,7 +3909,7 @@ the proxy instead of using the local DNS service</property> </object> <packing> <property name="left_attach">1</property> - <property name="top_attach">2</property> + <property name="top_attach">1</property> </packing> </child> </object> @@ -4251,8 +4207,6 @@ the proxy instead of using the local DNS service</property> <property name="tooltip_text" translatable="yes">If Deluge cannot find the database file at this location it will fallback to using DNS to resolve the peer's country.</property> <property name="invisible_char">●</property> <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> </object> <packing> <property name="expand">False</property> @@ -4430,8 +4384,6 @@ the proxy instead of using the local DNS service</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_width_chars">6</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> <property name="adjustment">adjustment_spin_daemon_port</property> </object> <packing> diff --git a/deluge/ui/gtk3/glade/torrent_menu.ui b/deluge/ui/gtk3/glade/torrent_menu.ui index c1b77b4..c9ee289 100644 --- a/deluge/ui/gtk3/glade/torrent_menu.ui +++ b/deluge/ui/gtk3/glade/torrent_menu.ui @@ -31,6 +31,11 @@ <property name="icon_name">media-playback-pause-symbolic</property> <property name="icon_size">1</property> </object> + <object class="GtkImage" id="menu-item-image15"> + <property name="can_focus">False</property> + <property name="icon_name">edit-copy-symbolic</property> + <property name="icon_size">1</property> + </object> <object class="GtkImage" id="menu-item-image19"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -155,6 +160,17 @@ </object> </child> <child> + <object class="GtkImageMenuItem" id="menuitem_copymagnet"> + <property name="label" translatable="yes">_Copy Magnet URI</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="image">menu-item-image15</property> + <property name="use_stock">False</property> + <signal name="activate" handler="on_menuitem_copymagnet_activate" swapped="no"/> + </object> + </child> + <child> <object class="GtkImageMenuItem" id="menuitem_updatetracker"> <property name="label" translatable="yes">_Update Tracker</property> <property name="visible">True</property> diff --git a/deluge/ui/gtk3/gtkui.py b/deluge/ui/gtk3/gtkui.py index d93bd2e..ddb2eb5 100644 --- a/deluge/ui/gtk3/gtkui.py +++ b/deluge/ui/gtk3/gtkui.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -8,8 +7,6 @@ # # pylint: disable=wrong-import-position -from __future__ import division, unicode_literals - import logging import os import signal @@ -18,8 +15,8 @@ import time import gi # isort:skip (Required before Gtk import). -gi.require_version('Gtk', '3.0') # NOQA: E402 -gi.require_version('Gdk', '3.0') # NOQA: E402 +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') # isort:imports-thirdparty from gi.repository.GLib import set_prgname @@ -32,7 +29,7 @@ try: # Install twisted reactor, before any other modules import reactor. reactor = gtk3reactor.install() except ReactorAlreadyInstalledError: - # Running unit tests so trial already installed a rector + # Running unit tests so already installed a rector from twisted.internet import reactor # isort:imports-firstparty @@ -45,7 +42,7 @@ from deluge.common import ( windows_check, ) from deluge.configmanager import ConfigManager, get_config_dir -from deluge.error import DaemonRunningError +from deluge.error import DaemonRunningError, LibtorrentImportError from deluge.i18n import I18N_DOMAIN, set_language, setup_translation from deluge.ui.client import client from deluge.ui.hostlist import LOCALHOST @@ -75,7 +72,7 @@ set_prgname('deluge') log = logging.getLogger(__name__) try: - from setproctitle import setproctitle, getproctitle + from setproctitle import getproctitle, setproctitle except ImportError: def setproctitle(title): @@ -121,6 +118,7 @@ DEFAULT_PREFS = { 'show_toolbar': True, 'show_statusbar': True, 'show_tabsbar': True, + 'tabsbar_tab_pos': 'top', 'tabsbar_position': 235, 'sidebar_show_zero': False, 'sidebar_show_trackers': True, @@ -129,6 +127,7 @@ DEFAULT_PREFS = { 'show_rate_in_title': False, 'createtorrent.trackers': [], 'show_piecesbar': False, + 'detect_urls': True, 'pieces_color_missing': [65535, 0, 0], 'pieces_color_waiting': [4874, 56494, 0], 'pieces_color_downloading': [65535, 55255, 0], @@ -138,7 +137,7 @@ DEFAULT_PREFS = { } -class GtkUI(object): +class GtkUI: def __init__(self, args): # Setup gtkbuilder/glade translation setup_translation() @@ -220,7 +219,7 @@ class GtkUI(object): menubar_osx(self, self.osxapp) self.osxapp.ready() - # Initalize the plugins + # Initialize the plugins self.plugins = PluginManager() # Show the connection manager @@ -313,8 +312,8 @@ class GtkUI(object): 'A Deluge daemon (deluged) is already running.\n' 'To use Standalone mode, stop local daemon and restart Deluge.' ) - except ImportError as ex: - if 'No module named libtorrent' in str(ex): + except LibtorrentImportError as ex: + if 'libtorrent library not found' in str(ex): err_msg = _( 'Only Thin Client mode is available because libtorrent is not installed.\n' 'To use Standalone mode, please install libtorrent package.' @@ -322,9 +321,17 @@ class GtkUI(object): else: log.exception(ex) err_msg = _( - 'Only Thin Client mode is available due to unknown Import Error.\n' + 'Only Thin Client mode is available due to libtorrent import error: %s\n' 'To use Standalone mode, please see logs for error details.' + % (str(ex)) ) + + except ImportError as ex: + log.exception(ex) + err_msg = _( + 'Only Thin Client mode is available due to unknown Import Error.\n' + 'To use Standalone mode, please see logs for error details.' + ) except Exception as ex: log.exception(ex) err_msg = _( diff --git a/deluge/ui/gtk3/ipcinterface.py b/deluge/ui/gtk3/ipcinterface.py index dc51a87..0ef28d8 100644 --- a/deluge/ui/gtk3/ipcinterface.py +++ b/deluge/ui/gtk3/ipcinterface.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,14 +6,14 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os import sys from base64 import b64encode from glob import glob from tempfile import mkstemp +from urllib.parse import urlparse +from urllib.request import url2pathname import rencode import twisted.internet.error @@ -26,14 +25,6 @@ from deluge.common import decode_bytes, is_magnet, is_url, windows_check from deluge.configmanager import ConfigManager, get_config_dir from deluge.ui.client import client -try: - from urllib.parse import urlparse - from urllib.request import url2pathname -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - from urllib import url2pathname # pylint: disable=ungrouped-imports - log = logging.getLogger(__name__) @@ -84,8 +75,8 @@ class IPCInterface(component.Component): if windows_check(): # If we're on windows we need to check the global mutex to see if deluge is # already running. - import win32event import win32api + import win32event import winerror self.mutex = win32event.CreateMutex(None, False, 'deluge') diff --git a/deluge/ui/gtk3/listview.py b/deluge/ui/gtk3/listview.py index 666bb67..e9f6b10 100644 --- a/deluge/ui/gtk3/listview.py +++ b/deluge/ui/gtk3/listview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,20 +6,18 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from gi.repository import GObject, Gtk -from deluge.common import PY2, decode_bytes +from deluge.common import decode_bytes from .common import cmp, load_pickled_state_file, save_pickled_state_file log = logging.getLogger(__name__) -class ListViewColumnState(object): +class ListViewColumnState: """Class used for saving/loading column state.""" def __init__(self, name, position, width, visible, sort, sort_order): @@ -32,13 +29,13 @@ class ListViewColumnState(object): self.sort_order = sort_order -class ListView(object): +class ListView: """ListView is used to make custom GtkTreeViews. It supports the adding and removing of columns, creating a menu for a column toggle list and support for 'status_field's which are used while updating the columns data. """ - class ListViewColumn(object): + class ListViewColumn: """Holds information regarding a column in the ListView""" def __init__(self, name, column_indices): @@ -66,22 +63,20 @@ class ListView(object): self.pixbuf_index = 0 self.data_func = None - class TreeviewColumn(Gtk.TreeViewColumn, object): + class TreeviewColumn(Gtk.TreeViewColumn): """ - TreeViewColumn does not signal right-click events, and we need them - This subclass is equivalent to TreeViewColumn, but it signals these events + TreeViewColumn does not signal right-click events, and we need them + This subclass is equivalent to TreeViewColumn, but it signals these events - Most of the code of this class comes from Quod Libet (http://www.sacredchao.net/quodlibet) + Most of the code of this class comes from Quod Libet (http://www.sacredchao.net/quodlibet) """ __gsignals__ = { - 'button-press-event' - if not PY2 - else b'button-press-event': (GObject.SIGNAL_RUN_LAST, None, (object,)) + 'button-press-event': (GObject.SIGNAL_RUN_LAST, None, (object,)) } def __init__(self, title=None, cell_renderer=None, **args): - """ Constructor, see Gtk.TreeViewColumn """ + """Constructor, see Gtk.TreeViewColumn""" Gtk.TreeViewColumn.__init__(self, title, cell_renderer, **args) label = Gtk.Label(label=title) self.set_widget(label) @@ -112,7 +107,7 @@ class ListView(object): Gtk.TreeViewColumn.set_visible(self, visible) if self.data_func: if not visible: - # Set data function to None to prevent unecessary calls when column is hidden + # Set data function to None to prevent unnecessary calls when column is hidden self.set_cell_data_func(self.cell_renderer, None, func_data=None) else: self.set_cell_data_func( @@ -144,7 +139,6 @@ class ListView(object): self.liststore = None self.model_filter = None - self.treeview.set_rules_hint(True) self.treeview.set_reorderable(False) self.treeview.set_rubber_banding(True) # Enable mouse multi-row selection. self.treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) @@ -281,7 +275,7 @@ class ListView(object): def save_state(self, filename): """Saves the listview state (column positions and visibility) to - filename.""" + filename.""" # A list of ListViewColumnStates state = [] @@ -627,8 +621,7 @@ class ListView(object): unique=False, default_sort=False, ): - """Add a text column to the listview. Only the header name is required. - """ + """Add a text column to the listview. Only the header name is required.""" render = Gtk.CellRendererText() self.add_column( header, diff --git a/deluge/ui/gtk3/mainwindow.py b/deluge/ui/gtk3/mainwindow.py index 5b4240c..d11ff31 100644 --- a/deluge/ui/gtk3/mainwindow.py +++ b/deluge/ui/gtk3/mainwindow.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path from hashlib import sha1 as sha @@ -20,11 +17,11 @@ from twisted.internet import reactor from twisted.internet.error import ReactorNotRunning import deluge.component as component -from deluge.common import decode_bytes, fspeed, resource_filename +from deluge.common import decode_bytes, fspeed, is_magnet, is_url, resource_filename from deluge.configmanager import ConfigManager from deluge.ui.client import client -from .common import get_deluge_icon, windowing +from .common import get_clipboard_text, get_deluge_icon, windowing from .dialogs import PasswordDialog from .ipcinterface import process_args @@ -45,7 +42,7 @@ if windowing('X11'): log = logging.getLogger(__name__) -class _GtkBuilderSignalsHolder(object): +class _GtkBuilderSignalsHolder: def connect_signals(self, mapping_or_class): if isinstance(mapping_or_class, dict): @@ -108,6 +105,7 @@ class MainWindow(component.Component): self.window = self.main_builder.get_object('main_window') self.window.set_icon(get_deluge_icon()) self.tabsbar_pane = self.main_builder.get_object('tabsbar_pane') + self.tabsbar_torrent_info = self.main_builder.get_object('torrent_info') self.sidebar_pane = self.main_builder.get_object('sidebar_pane') # Keep a list of components to pause and resume when changing window state. @@ -131,6 +129,7 @@ class MainWindow(component.Component): self.window.connect('configure-event', self.on_window_configure_event) self.window.connect('delete-event', self.on_window_delete_event) self.window.connect('drag-data-received', self.on_drag_data_received_event) + self.window.connect('notify::is-active', self.on_focus) self.tabsbar_pane.connect( 'notify::position', self.on_tabsbar_pane_position_event ) @@ -147,6 +146,9 @@ class MainWindow(component.Component): 'NewVersionAvailableEvent', self.on_newversionavailable_event ) + self.previous_clipboard_text = '' + self.first_run = True + def connect_signals(self, mapping_or_class): self.gtk_builder_signals_holder.connect_signals(mapping_or_class) @@ -277,12 +279,14 @@ class MainWindow(component.Component): def save_position(self): self.config['window_maximized'] = self.window.props.is_maximized if not self.config['window_maximized'] and self.visible(): - self.config['window_x_pos'], self.config[ - 'window_y_pos' - ] = self.window.get_position() - self.config['window_width'], self.config[ - 'window_height' - ] = self.window.get_size() + ( + self.config['window_x_pos'], + self.config['window_y_pos'], + ) = self.window.get_position() + ( + self.config['window_width'], + self.config['window_height'], + ) = self.window.get_size() def on_window_configure_event(self, widget, event): self.save_position() @@ -327,6 +331,21 @@ class MainWindow(component.Component): def on_expose_event(self, widget, event): component.get('SystemTray').blink(False) + def on_focus(self, window, param): + if window.props.is_active and not self.first_run and self.config['detect_urls']: + text = get_clipboard_text() + if text == self.previous_clipboard_text: + return + self.previous_clipboard_text = text + if text and ( + (is_url(text) and text.endswith('.torrent')) + or is_magnet(text) + and not component.get('MenuBar').magnet_copied() + ): + component.get('AddTorrentDialog').show() + component.get('AddTorrentDialog').on_button_url_clicked(window) + self.first_run = False + def stop(self): self.window.set_title('Deluge') diff --git a/deluge/ui/gtk3/menubar.py b/deluge/ui/gtk3/menubar.py index e09f394..a812a8c 100644 --- a/deluge/ui/gtk3/menubar.py +++ b/deluge/ui/gtk3/menubar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> @@ -9,8 +8,6 @@ # -from __future__ import unicode_literals - import logging import os.path @@ -21,7 +18,7 @@ import deluge.component as component from deluge.configmanager import ConfigManager from deluge.ui.client import client -from .dialogs import ErrorDialog, OtherDialog +from .dialogs import CopyMagnetDialog, ErrorDialog, OtherDialog from .path_chooser import PathChooser log = logging.getLogger(__name__) @@ -34,6 +31,7 @@ class MenuBar(component.Component): self.mainwindow = component.get('MainWindow') self.main_builder = self.mainwindow.get_builder() self.config = ConfigManager('gtk3ui.conf') + self._magnet_copied = False self.builder = Gtk.Builder() # Get the torrent menu from the gtk builder file @@ -142,6 +140,19 @@ class MenuBar(component.Component): self.change_sensitivity = ['menuitem_addtorrent'] + def magnet_copied(self): + """ + lets the caller know whether a magnet was copied internally + + the `mainwindow` checks every time the data in the clipboard, + so it will automatically open the AddTorrentURL dialog in case it + contains a valid link (URL to a torrent or a magnet URI). + + """ + val = self._magnet_copied + self._magnet_copied = False + return val + def start(self): for widget in self.change_sensitivity: self.main_builder.get_object(widget).set_sensitive(True) @@ -282,6 +293,21 @@ class MenuBar(component.Component): component.get('TorrentView').get_selected_torrents() ) + def on_menuitem_copymagnet_activate(self, data=None): + log.debug('on_menuitem_copymagnet_activate') + torrent_ids = component.get('TorrentView').get_selected_torrents() + if torrent_ids: + + def _on_magnet_uri(magnet_uri): + def update_copied(response_id): + if dialog.copied: + self._magnet_copied = True + + dialog = CopyMagnetDialog(magnet_uri) + dialog.run().addCallback(update_copied) + + client.core.get_magnet_uri(torrent_ids[0]).addCallback(_on_magnet_uri) + def on_menuitem_updatetracker_activate(self, data=None): log.debug('on_menuitem_updatetracker_activate') client.core.force_reannounce( @@ -541,7 +567,7 @@ class MenuBar(component.Component): account_to_log = {} for key, value in account.copy().items(): if key == 'password': - value = '*' * len(value) + value = '*' * 10 account_to_log[key] = value known_accounts_to_log.append(account_to_log) log.debug('_on_known_accounts: %s', known_accounts_to_log) diff --git a/deluge/ui/gtk3/menubar_osx.py b/deluge/ui/gtk3/menubar_osx.py index 1df6fab..53150fb 100644 --- a/deluge/ui/gtk3/menubar_osx.py +++ b/deluge/ui/gtk3/menubar_osx.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from gi.repository.Gdk import ModifierType from gi.repository.Gtk import SeparatorMenuItem, accel_groups_from_object from gi.repository.Gtk.AccelFlags import VISIBLE diff --git a/deluge/ui/gtk3/new_release_dialog.py b/deluge/ui/gtk3/new_release_dialog.py index 6aa3282..a635bd2 100644 --- a/deluge/ui/gtk3/new_release_dialog.py +++ b/deluge/ui/gtk3/new_release_dialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from gi.repository.Gtk import IconSize import deluge.common @@ -17,7 +14,7 @@ from deluge.configmanager import ConfigManager from deluge.ui.client import client -class NewReleaseDialog(object): +class NewReleaseDialog: def __init__(self): pass diff --git a/deluge/ui/gtk3/options_tab.py b/deluge/ui/gtk3/options_tab.py index 6a25fd1..b0411a8 100644 --- a/deluge/ui/gtk3/options_tab.py +++ b/deluge/ui/gtk3/options_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # 2017 Calum Lind <calumlind+deluge@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from gi.repository.Gdk import keyval_name import deluge.component as component @@ -21,7 +18,7 @@ from .torrentdetails import Tab class OptionsTab(Tab): def __init__(self): - super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label') + super().__init__('Options', 'options_tab', 'options_tab_label') self.prev_torrent_ids = None self.prev_status = None @@ -191,8 +188,9 @@ class OptionsTab(Tab): ): options[status_key] = widget_value - if options.get('move_completed', False): - options['move_completed_path'] = self.move_completed_path_chooser.get_text() + move_completed_path = self.move_completed_path_chooser.get_text() + if move_completed_path != self.prev_status['move_completed_path']: + options['move_completed_path'] = move_completed_path client.core.set_torrent_options(self.prev_torrent_ids, options) self.button_apply.set_sensitive(False) diff --git a/deluge/ui/gtk3/path_chooser.py b/deluge/ui/gtk3/path_chooser.py index b722841..8058196 100644 --- a/deluge/ui/gtk3/path_chooser.py +++ b/deluge/ui/gtk3/path_chooser.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2013 Bro <bro.development@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -125,7 +122,7 @@ class PathChoosersHandler(component.Component): class PathChooser(PathChooserComboBox): def __init__(self, paths_config_key=None, parent=None): self.paths_config_key = paths_config_key - super(PathChooser, self).__init__(parent=parent) + super().__init__(parent=parent) self.chooser_handler = PathChoosersHandler() self.chooser_handler.register_chooser(self) self.set_auto_completer_func(self.on_completion) diff --git a/deluge/ui/gtk3/path_combo_chooser.py b/deluge/ui/gtk3/path_combo_chooser.py index c26289d..74d9055 100755 --- a/deluge/ui/gtk3/path_combo_chooser.py +++ b/deluge/ui/gtk3/path_combo_chooser.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Copyright (C) 2013 Bro <bro.development@gmail.com> # @@ -8,15 +7,13 @@ # See LICENSE for more details. # -from __future__ import division, print_function, unicode_literals - import os import warnings from gi.repository import Gdk, GObject, Gtk from gi.repository.GObject import SignalFlags -from deluge.common import PY2, resource_filename +from deluge.common import resource_filename from deluge.path_chooser_common import get_completion_paths # Filter the pygobject signal warning: @@ -64,7 +61,7 @@ def path_without_trailing_path_sep(path): return path -class ValueList(object): +class ValueList: paths_without_trailing_path_sep = False @@ -176,7 +173,7 @@ class ValueList(object): """ for i, row in enumerate(self.tree_store): if row[0] == value: - self.treeview.set_cursor((i)) + self.treeview.set_cursor(i) return # The value was not found if select_first: @@ -374,7 +371,7 @@ class StoredValuesList(ValueList): """ # This is left click if event.button != 3: - super(StoredValuesList, self).on_treeview_mouse_button_press_event( + super().on_treeview_mouse_button_press_event( treeview, event, double_click=True ) return False @@ -390,9 +387,9 @@ class StoredValuesList(ValueList): treeview.set_cursor(path, col, 0) self.path_list_popup = Gtk.Menu() - menuitem_edit = Gtk.MenuItem.new_with_label('Edit path') + menuitem_edit = Gtk.MenuItem.new_with_label(_('Edit path')) self.path_list_popup.append(menuitem_edit) - menuitem_remove = Gtk.MenuItem.new_with_label('Remove path') + menuitem_remove = Gtk.MenuItem.new_with_label(_('Remove path')) self.path_list_popup.append(menuitem_remove) def on_edit_clicked(widget, path): @@ -412,9 +409,7 @@ class StoredValuesList(ValueList): PathChooserPopup.popup(self) def on_stored_values_treeview_key_press_event(self, widget, event): - super(StoredValuesList, self).on_value_list_treeview_key_press_event( - widget, event - ) + super().on_value_list_treeview_key_press_event(widget, event) # Prevent the default event handler to move the cursor in the list if key_is_up_or_down(event.keyval): return True @@ -479,9 +474,9 @@ class CompletionList(ValueList): ] = self.on_completion_treeview_motion_notify_event # Add super class signal handler - self.signal_handlers['on_completion_treeview_mouse_button_press_event'] = super( - CompletionList, self - ).on_treeview_mouse_button_press_event + self.signal_handlers[ + 'on_completion_treeview_mouse_button_press_event' + ] = super().on_treeview_mouse_button_press_event def reduce_values(self, prefix): """ @@ -499,9 +494,7 @@ class CompletionList(ValueList): self.add_values(matching_values, clear=True) def on_completion_treeview_key_press_event(self, widget, event): - ret = super(CompletionList, self).on_value_list_treeview_key_press_event( - widget, event - ) + ret = super().on_value_list_treeview_key_press_event(widget, event) if ret: return ret keyval = event.keyval @@ -529,7 +522,7 @@ class CompletionList(ValueList): self.handle_list_scroll(path=path[0], _next=None) -class PathChooserPopup(object): +class PathChooserPopup: """This creates the popop window for the ComboEntry.""" def __init__(self, min_visible_rows, max_visible_rows, popup_alignment_widget): @@ -538,9 +531,8 @@ class PathChooserPopup(object): self.set_max_popup_rows(max_visible_rows) self.popup_window.realize() self.alignment_widget = popup_alignment_widget - self.popup_buttonbox = ( - None - ) # If set, the height of this widget is the minimum height + # If set, the height of this widget is the minimum height + self.popup_buttonbox = None def popup(self): """Make the popup visible.""" @@ -984,7 +976,7 @@ class PathCompletionPopup(CompletionList, PathChooserPopup): return True -class PathAutoCompleter(object): +class PathAutoCompleter: def __init__(self, builder, path_entry, max_visible_rows): self.completion_popup = PathCompletionPopup( builder, path_entry, max_visible_rows @@ -1107,9 +1099,7 @@ class PathAutoCompleter(object): class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject): __gsignals__ = { - signal - if not PY2 - else signal.encode(): (SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,)) + signal: (SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object,)) for signal in [ 'text-changed', 'accelerator-set', @@ -1415,7 +1405,7 @@ class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject): self.set_text(self.get_text()) def _on_entry_combobox_hbox_realize(self, widget): - """ Must do this when the widget is realized """ + """Must do this when the widget is realized""" self.set_filechooser_button_visible(self.filechooser_visible) self.set_path_entry_visible(self.path_entry_visible) @@ -1467,7 +1457,7 @@ class PathChooserComboBox(Gtk.Box, StoredValuesPopup, GObject.GObject): ) return True elif is_ascii_value(keyval, 's'): - super(PathChooserComboBox, self).add_current_value_to_saved_list() + super().add_current_value_to_saved_list() return True elif is_ascii_value(keyval, 'd'): # Set the default value in the text entry @@ -1697,7 +1687,7 @@ if __name__ == '__main__': box1 = Gtk.Box.new(Gtk.Orientation.VERTICAL, spacing=0) def get_resource2(filename): - return '%s/glade/%s' % (os.path.abspath(os.path.dirname(sys.argv[0])), filename) + return f'{os.path.abspath(os.path.dirname(sys.argv[0]))}/glade/{filename}' # Override get_resource which fetches from deluge install # get_resource = get_resource2 diff --git a/deluge/ui/gtk3/peers_tab.py b/deluge/ui/gtk3/peers_tab.py index 33395b9..b458f7a 100644 --- a/deluge/ui/gtk3/peers_tab.py +++ b/deluge/ui/gtk3/peers_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path @@ -32,6 +29,7 @@ from .common import ( icon_downloading, icon_seeding, load_pickled_state_file, + parse_ip_port, save_pickled_state_file, ) from .torrentdetails import Tab @@ -41,18 +39,12 @@ from .torrentview_data_funcs import ( cell_data_speed_up, ) -try: - from future_builtins import zip -except ImportError: - # Ignore on Py3. - pass - log = logging.getLogger(__name__) class PeersTab(Tab): def __init__(self): - super(PeersTab, self).__init__('Peers', 'peers_tab', 'peers_tab_label') + super().__init__('Peers', 'peers_tab', 'peers_tab_label') self.peer_menu = self.main_builder.get_object('menu_peer_tab') component.get('MainWindow').connect_signals(self) @@ -304,15 +296,15 @@ class PeersTab(Tab): peer_ip = peer['ip'] else: # This is an IPv6 address - import socket import binascii + import socket # Split out the :port ip = ':'.join(peer['ip'].split(':')[:-1]) ip_int = int( binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip)), 16 ) - peer_ip = '[%s]:%s' % (ip, peer['ip'].split(':')[-1]) + peer_ip = '[{}]:{}'.format(ip, peer['ip'].split(':')[-1]) if peer['seed']: icon = self.seed_pixbuf @@ -376,19 +368,15 @@ class PeersTab(Tab): peer_dialog = builder.get_object('connect_peer_dialog') txt_ip = builder.get_object('txt_ip') response = peer_dialog.run() + if response: value = txt_ip.get_text() - if value and ':' in value: - if ']' in value: - # ipv6 - ip = value.split(']')[0][1:] - port = value.split(']')[1][1:] - else: - # ipv4 - ip = value.split(':')[0] - port = value.split(':')[1] - if deluge.common.is_ip(ip): - log.debug('adding peer %s to %s', value, self.torrent_id) - client.core.connect_peer(self.torrent_id, ip, port) + ip, port = parse_ip_port(value) + if ip and port: + log.info('Adding peer IP: %s port: %s to %s', ip, port, self.torrent_id) + client.core.connect_peer(self.torrent_id, ip, port) + else: + log.error('Error parsing peer "%s"', value) + peer_dialog.destroy() return True diff --git a/deluge/ui/gtk3/piecesbar.py b/deluge/ui/gtk3/piecesbar.py index ba03e55..8665328 100644 --- a/deluge/ui/gtk3/piecesbar.py +++ b/deluge/ui/gtk3/piecesbar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> # @@ -7,14 +6,13 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - from math import pi import gi # isort:skip (Version check required before import). -gi.require_version('PangoCairo', '1.0') # NOQA: E402 -gi.require_version('cairo', '1.0') # NOQA: E402 +gi.require_version('PangoCairo', '1.0') +gi.require_foreign('cairo') +gi.require_version('cairo', '1.0') # isort:imports-thirdparty import cairo # Backward compat cairo <= 1.15 @@ -23,7 +21,6 @@ from gi.repository.Gtk import DrawingArea, ProgressBar, StateFlags from gi.repository.Pango import SCALE, Weight # isort:imports-firstparty -from deluge.common import PY2 from deluge.configmanager import ConfigManager COLOR_STATES = ['missing', 'waiting', 'downloading', 'completed'] @@ -31,14 +28,15 @@ COLOR_STATES = ['missing', 'waiting', 'downloading', 'completed'] class PiecesBar(DrawingArea): # Draw in response to an draw - __gsignals__ = {'draw': 'override'} if not PY2 else {b'draw': b'override'} + __gsignals__ = {'draw': 'override'} def __init__(self): - super(PiecesBar, self).__init__() + super().__init__() # Get progress bar styles, in order to keep font consistency pb = ProgressBar() pb_style = pb.get_style_context() - self.text_font = pb_style.get_property('font', StateFlags.NORMAL) + # Get a copy of Pango.FontDescription since original needs freed. + self.text_font = pb_style.get_property('font', StateFlags.NORMAL).copy() self.text_font.set_weight(Weight.BOLD) # Done with the ProgressBar styles, don't keep refs of it del pb, pb_style diff --git a/deluge/ui/gtk3/pluginmanager.py b/deluge/ui/gtk3/pluginmanager.py index d60f8d3..63353c0 100644 --- a/deluge/ui/gtk3/pluginmanager.py +++ b/deluge/ui/gtk3/pluginmanager.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component diff --git a/deluge/ui/gtk3/preferences.py b/deluge/ui/gtk3/preferences.py index b196128..a008a95 100644 --- a/deluge/ui/gtk3/preferences.py +++ b/deluge/ui/gtk3/preferences.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me> @@ -8,11 +7,10 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os from hashlib import sha1 as sha +from urllib.parse import urlparse from gi import require_version from gi.repository import Gtk @@ -21,6 +19,7 @@ from gi.repository.Gdk import Color import deluge.common import deluge.component as component from deluge.configmanager import ConfigManager, get_config_dir +from deluge.decorators import maybe_coroutine from deluge.error import AuthManagerError, NotAuthorizedError from deluge.i18n import get_languages from deluge.ui.client import client @@ -31,12 +30,6 @@ from .dialogs import AccountDialog, ErrorDialog, InformationDialog, YesNoDialog from .path_chooser import PathChooser try: - from urllib.parse import urlparse -except ImportError: - # PY2 fallback - from urlparse import urlparse # pylint: disable=ungrouped-imports - -try: require_version('AppIndicator3', '0.1') from gi.repository import AppIndicator3 # noqa: F401 except (ImportError, ValueError): @@ -77,7 +70,7 @@ class Preferences(component.Component): self.load_pref_dialog_state() self.builder.get_object('image_magnet').set_from_file( - deluge.common.get_pixmap('magnet.png') + deluge.common.get_pixmap('magnet16.png') ) # Hide the unused associate magnet button on OSX see: #2420 @@ -255,11 +248,10 @@ class Preferences(component.Component): vbox.pack_start(label, False, True, 0) sep = Gtk.HSeparator() vbox.pack_start(sep, False, True, 0) - align = Gtk.Alignment() - align.set_padding(5, 0, 0, 0) - align.set(0, 0, 1, 1) - align.add(widget) - vbox.pack_start(align, True, True, 0) + widget.set_margin_top(7) + widget.set_vexpand(True) + widget.set_hexpand(True) + vbox.pack_start(widget, True, True, 0) scrolled = Gtk.ScrolledWindow() viewport = Gtk.Viewport() viewport.set_shadow_type(Gtk.ShadowType.NONE) @@ -571,6 +563,9 @@ class Preferences(component.Component): self.builder.get_object('piecesbar_toggle').set_active( self.gtkui_config['show_piecesbar'] ) + self.builder.get_object('urldetect_toggle').set_active( + self.gtkui_config['detect_urls'] + ) self.__set_color('completed', from_config=True) self.__set_color('downloading', from_config=True) self.__set_color('waiting', from_config=True) @@ -677,11 +672,15 @@ class Preferences(component.Component): 'chk_random_outgoing_ports' ).get_active() incoming_address = self.builder.get_object('entry_interface').get_text().strip() - if deluge.common.is_ip(incoming_address) or not incoming_address: + if deluge.common.is_interface(incoming_address) or not incoming_address: new_core_config['listen_interface'] = incoming_address - new_core_config['outgoing_interface'] = ( + outgoing_address = ( self.builder.get_object('entry_outgoing_interface').get_text().strip() ) + if deluge.common.is_interface(outgoing_address) or not outgoing_address: + new_core_config['outgoing_interface'] = ( + self.builder.get_object('entry_outgoing_interface').get_text().strip() + ) new_core_config['peer_tos'] = self.builder.get_object( 'entry_peer_tos' ).get_text() @@ -937,7 +936,7 @@ class Preferences(component.Component): not self.gtkui_config['standalone'] ) - mode = 'Thinclient' if was_standalone else 'Standalone' + mode = _('Thinclient') if was_standalone else _('Standalone') dialog = YesNoDialog( _('Switching Deluge Client Mode...'), _('Do you want to restart to use %s mode?' % mode), @@ -946,6 +945,7 @@ class Preferences(component.Component): def hide(self): self.window_open = False + self.builder.get_object('port_spinner').stop() self.builder.get_object('port_img').hide() self.pref_dialog.hide() @@ -958,7 +958,6 @@ class Preferences(component.Component): 'label_cache_num_blocks_written', 'label_cache_read_hit_ratio', 'label_cache_write_hit_ratio', - 'label_cache_num_blocks_cache_hits', 'label_cache_disk_blocks_in_use', 'label_cache_read_cache_blocks', ) @@ -1091,6 +1090,8 @@ class Preferences(component.Component): log.debug('on_test_port_clicked') def on_get_test(status): + self.builder.get_object('port_spinner').stop() + self.builder.get_object('port_spinner').hide() if status: self.builder.get_object('port_img').set_from_icon_name( 'emblem-ok-symbolic', Gtk.IconSize.MENU @@ -1103,12 +1104,9 @@ class Preferences(component.Component): self.builder.get_object('port_img').show() client.core.test_listen_port().addCallback(on_get_test) - # XXX: Consider using gtk.Spinner() instead of the loading gif - # It requires gtk.ver > 2.12 - self.builder.get_object('port_img').set_from_file( - deluge.common.get_pixmap('loading.gif') - ) - self.builder.get_object('port_img').show() + self.builder.get_object('port_spinner').start() + self.builder.get_object('port_spinner').show() + self.builder.get_object('port_img').hide() client.force_call() def on_plugin_toggled(self, renderer, path): @@ -1180,8 +1178,8 @@ class Preferences(component.Component): chooser.destroy() return - from base64 import b64encode import shutil + from base64 import b64encode filename = os.path.split(filepath)[1] shutil.copyfile(filepath, os.path.join(get_config_dir(), 'plugins', filename)) @@ -1332,58 +1330,46 @@ class Preferences(component.Component): (model, itr) = treeselection.get_selected() if not itr: return - username = model[itr][0] - if username: + level = model[itr][1] + if level: self.builder.get_object('accounts_edit').set_sensitive(True) self.builder.get_object('accounts_delete').set_sensitive(True) else: self.builder.get_object('accounts_edit').set_sensitive(False) self.builder.get_object('accounts_delete').set_sensitive(False) - def on_accounts_add_clicked(self, widget): + @maybe_coroutine + async def on_accounts_add_clicked(self, widget): dialog = AccountDialog( levels_mapping=client.auth_levels_mapping, parent=self.pref_dialog ) + response = await dialog.run() + if response != Gtk.ResponseType.OK: + return - def dialog_finished(response_id): - username = dialog.get_username() - password = dialog.get_password() - authlevel = dialog.get_authlevel() - - def add_ok(rv): - accounts_iter = self.accounts_liststore.append() - self.accounts_liststore.set_value( - accounts_iter, ACCOUNTS_USERNAME, username - ) - self.accounts_liststore.set_value( - accounts_iter, ACCOUNTS_LEVEL, authlevel - ) - self.accounts_liststore.set_value( - accounts_iter, ACCOUNTS_PASSWORD, password - ) - - def add_fail(failure): - if failure.type == AuthManagerError: - ErrorDialog( - _('Error Adding Account'), - _('Authentication failed'), - parent=self.pref_dialog, - details=failure.getErrorMessage(), - ).run() - else: - ErrorDialog( - _('Error Adding Account'), - _('An error occurred while adding account'), - parent=self.pref_dialog, - details=failure.getErrorMessage(), - ).run() - - if response_id == Gtk.ResponseType.OK: - client.core.create_account(username, password, authlevel).addCallback( - add_ok - ).addErrback(add_fail) - - dialog.run().addCallback(dialog_finished) + account = dialog.account + try: + await client.core.create_account(*account) + except AuthManagerError as ex: + return ErrorDialog( + _('Error Adding Account'), + _('Authentication failed'), + parent=self.pref_dialog, + details=ex, + ).run() + except Exception as ex: + return ErrorDialog( + _('Error Adding Account'), + _(f'An error occurred while adding account: {account}'), + parent=self.pref_dialog, + details=ex, + ).run() + + self.accounts_liststore.set( + self.accounts_liststore.append(), + [ACCOUNTS_USERNAME, ACCOUNTS_LEVEL, ACCOUNTS_PASSWORD], + [account.username, account.authlevel, account.password], + ) def on_accounts_edit_clicked(self, widget): (model, itr) = self.accounts_listview.get_selection().get_selected() @@ -1463,6 +1449,9 @@ class Preferences(component.Component): colors_widget = self.builder.get_object('piecebar_colors_expander') colors_widget.set_visible(widget.get_active()) + def on_urldetect_toggle_toggled(self, widget): + self.gtkui_config['detect_urls'] = widget.get_active() + def on_checkbutton_language_toggled(self, widget): self.language_combo.set_visible(not self.language_checkbox.get_active()) diff --git a/deluge/ui/gtk3/queuedtorrents.py b/deluge/ui/gtk3/queuedtorrents.py index 0f08c24..6fdecec 100644 --- a/deluge/ui/gtk3/queuedtorrents.py +++ b/deluge/ui/gtk3/queuedtorrents.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os.path diff --git a/deluge/ui/gtk3/removetorrentdialog.py b/deluge/ui/gtk3/removetorrentdialog.py index 48806a5..06fca77 100644 --- a/deluge/ui/gtk3/removetorrentdialog.py +++ b/deluge/ui/gtk3/removetorrentdialog.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os @@ -21,7 +18,7 @@ from deluge.ui.client import client log = logging.getLogger(__name__) -class RemoveTorrentDialog(object): +class RemoveTorrentDialog: """ This class is used to create and show a Remove Torrent Dialog. diff --git a/deluge/ui/gtk3/sidebar.py b/deluge/ui/gtk3/sidebar.py index 1d75191..5a2b154 100644 --- a/deluge/ui/gtk3/sidebar.py +++ b/deluge/ui/gtk3/sidebar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> @@ -8,8 +7,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from gi.repository.Gtk import Label, PolicyType, ScrolledWindow diff --git a/deluge/ui/gtk3/status_tab.py b/deluge/ui/gtk3/status_tab.py index fab6719..6a9010b 100644 --- a/deluge/ui/gtk3/status_tab.py +++ b/deluge/ui/gtk3/status_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging import deluge.component as component @@ -32,7 +29,7 @@ log = logging.getLogger(__name__) class StatusTab(Tab): def __init__(self): - super(StatusTab, self).__init__('Status', 'status_tab', 'status_tab_label') + super().__init__('Status', 'status_tab', 'status_tab_label') self.config = ConfigManager('gtk3ui.conf') @@ -110,7 +107,7 @@ class StatusTab(Tab): if decode_bytes(widget[0].get_text()) != txt: widget[0].set_text(txt) - # Update progress bar seperately as it's a special case (not a label). + # Update progress bar separately as it's a special case (not a label). fraction = status['progress'] / 100 if self.config['show_piecesbar']: diff --git a/deluge/ui/gtk3/statusbar.py b/deluge/ui/gtk3/statusbar.py index 265e7c8..0a2e800 100644 --- a/deluge/ui/gtk3/statusbar.py +++ b/deluge/ui/gtk3/statusbar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import division, unicode_literals - import logging from gi.repository import Gtk @@ -25,7 +22,7 @@ from .dialogs import OtherDialog log = logging.getLogger(__name__) -class StatusBarItem(object): +class StatusBarItem: def __init__( self, image=None, @@ -142,12 +139,11 @@ class StatusBar(component.Component): self.current_warnings = [] # Add hbox to the statusbar after removing the initial label widget self.hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, spacing=10) - align = Gtk.Alignment() - align.set_padding(2, 0, 3, 0) - align.add(self.hbox) + self.hbox.set_margin_top(2) + self.hbox.set_margin_bottom(3) frame = self.statusbar.get_children()[0] frame.remove(frame.get_children()[0]) - frame.add(align) + frame.add(self.hbox) self.statusbar.show_all() # Create the not connected item self.not_connected_item = StatusBarItem( @@ -321,18 +317,22 @@ class StatusBar(component.Component): def send_status_request(self): # Sends an async request for data from the core keys = [ - 'num_peers', + 'peer.num_peers_connected', 'upload_rate', 'download_rate', 'payload_upload_rate', 'payload_download_rate', + 'net.sent_bytes', + 'net.recv_bytes', + 'net.sent_payload_bytes', + 'net.recv_payload_bytes', ] if self.dht_status: - keys.append('dht_nodes') + keys.append('dht.dht_nodes') if not self.health: - keys.append('has_incoming_connections') + keys.append('net.has_incoming_connections') client.core.get_session_status(keys).addCallback(self._on_get_session_status) client.core.get_free_space().addCallback(self._on_get_free_space) @@ -371,18 +371,18 @@ class StatusBar(component.Component): self.upload_protocol_rate = ( status['upload_rate'] - status['payload_upload_rate'] ) // 1024 - self.num_connections = status['num_peers'] + self.num_connections = status['peer.num_peers_connected'] self.update_download_label() self.update_upload_label() self.update_traffic_label() self.update_connections_label() - if 'dht_nodes' in status: - self.dht_nodes = status['dht_nodes'] + if 'dht.dht_nodes' in status: + self.dht_nodes = status['dht.dht_nodes'] self.update_dht_label() - if 'has_incoming_connections' in status: - self.health = status['has_incoming_connections'] + if 'net.has_incoming_connections' in status: + self.health = status['net.has_incoming_connections'] if self.health: self.remove_item(self.health_item) @@ -413,7 +413,7 @@ class StatusBar(component.Component): if self.max_connections_global < 0: label_string = '%s' % self.num_connections else: - label_string = '%s <small>(%s)</small>' % ( + label_string = '{} <small>({})</small>'.format( self.num_connections, self.max_connections_global, ) diff --git a/deluge/ui/gtk3/systemtray.py b/deluge/ui/gtk3/systemtray.py index f851f32..f65fde5 100644 --- a/deluge/ui/gtk3/systemtray.py +++ b/deluge/ui/gtk3/systemtray.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import os @@ -30,8 +27,12 @@ from .common import build_menu_radio_list, get_logo from .dialogs import OtherDialog try: - require_version('AppIndicator3', '0.1') - from gi.repository import AppIndicator3 + try: + require_version('AyatanaAppIndicator3', '0.1') + from gi.repository import AyatanaAppIndicator3 as AppIndicator3 + except (ValueError, ImportError): + require_version('AppIndicator3', '0.1') + from gi.repository import AppIndicator3 except (ValueError, ImportError): AppIndicator3 = None @@ -234,13 +235,13 @@ class SystemTray(component.Component): if max_download_speed == -1: max_download_speed = _('Unlimited') else: - max_download_speed = '%s %s' % (max_download_speed, _('K/s')) + max_download_speed = '{} {}'.format(max_download_speed, _('K/s')) if max_upload_speed == -1: max_upload_speed = _('Unlimited') else: - max_upload_speed = '%s %s' % (max_upload_speed, _('K/s')) + max_upload_speed = '{} {}'.format(max_upload_speed, _('K/s')) - msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % ( + msg = '{}\n{}: {} ({})\n{}: {} ({})'.format( _('Deluge'), _('Down'), self.download_rate, diff --git a/deluge/ui/gtk3/tab_data_funcs.py b/deluge/ui/gtk3/tab_data_funcs.py index 6fa0ba5..a78994f 100644 --- a/deluge/ui/gtk3/tab_data_funcs.py +++ b/deluge/ui/gtk3/tab_data_funcs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,14 +6,12 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - from deluge.common import fdate, fsize, fspeed, ftime from deluge.ui.common import TRACKER_STATUS_TRANSLATION def ftotal_sized(first, second): - return '%s (%s)' % (fsize(first, shortform=True), fsize(second, shortform=True)) + return f'{fsize(first, shortform=True)} ({fsize(second, shortform=True)})' def fratio(value): @@ -24,7 +21,7 @@ def fratio(value): def fpcnt(value, state, message): state_i18n = _(state) if state not in ('Error', 'Seeding') and value < 100: - percent = '{:.2f}'.format(value).rstrip('0').rstrip('.') + percent = f'{value:.2f}'.rstrip('0').rstrip('.') return _('{state} {percent}%').format(state=state_i18n, percent=percent) elif state == 'Error': return _('{state}: {err_msg}').format(state=state_i18n, err_msg=message) @@ -34,7 +31,7 @@ def fpcnt(value, state, message): def fspeed_max(value, max_value=-1): value = fspeed(value, shortform=True) - return '%s (%s %s)' % (value, max_value, _('K/s')) if max_value > -1 else value + return '{} ({} {})'.format(value, max_value, _('K/s')) if max_value > -1 else value def fdate_or_never(value): @@ -73,7 +70,7 @@ def fseed_rank_or_dash(seed_rank, seeding_time): def fpieces_num_size(num_pieces, piece_size): - return '%s (%s)' % (num_pieces, fsize(piece_size, precision=0)) + return f'{num_pieces} ({fsize(piece_size, precision=0)})' def fcount(value): diff --git a/deluge/ui/gtk3/toolbar.py b/deluge/ui/gtk3/toolbar.py index 7bc029e..1b6952e 100644 --- a/deluge/ui/gtk3/toolbar.py +++ b/deluge/ui/gtk3/toolbar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging from gi.repository.Gtk import SeparatorToolItem, ToolButton diff --git a/deluge/ui/gtk3/torrentdetails.py b/deluge/ui/gtk3/torrentdetails.py index 29e0193..08c37a1 100644 --- a/deluge/ui/gtk3/torrentdetails.py +++ b/deluge/ui/gtk3/torrentdetails.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> # @@ -9,12 +8,17 @@ """The torrent details component shows info about the selected torrent.""" -from __future__ import unicode_literals - import logging from collections import namedtuple -from gi.repository.Gtk import CheckMenuItem, Menu, SeparatorMenuItem +from gi.repository.Gtk import ( + CheckMenuItem, + Menu, + MenuItem, + PositionType, + RadioMenuItem, + SeparatorMenuItem, +) import deluge.component as component from deluge.ui.client import client @@ -26,7 +30,7 @@ log = logging.getLogger(__name__) TabWidget = namedtuple('TabWidget', ('obj', 'func', 'status_keys')) -class Tab(object): +class Tab: def __init__(self, name=None, child_widget=None, tab_label=None): self._name = name self.is_visible = True @@ -100,8 +104,12 @@ class TorrentDetails(component.Component): def __init__(self): component.Component.__init__(self, 'TorrentDetails', interval=2) main_builder = component.get('MainWindow').get_builder() + self.config = component.get('MainWindow').config self.notebook = main_builder.get_object('torrent_info') + self.notebook.set_tab_pos( + getattr(PositionType, self.config['tabsbar_tab_pos'].upper()) + ) # This is the menu item we'll attach the tabs checklist menu to self.menu_tabs = main_builder.get_object('menu_tabs') @@ -112,11 +120,11 @@ class TorrentDetails(component.Component): self.tabs = {} # Add the default tabs - from .status_tab import StatusTab from .details_tab import DetailsTab from .files_tab import FilesTab - from .peers_tab import PeersTab from .options_tab import OptionsTab + from .peers_tab import PeersTab + from .status_tab import StatusTab from .trackers_tab import TrackersTab default_tabs = { @@ -175,7 +183,6 @@ class TorrentDetails(component.Component): # Generate the checklist menu self.generate_menu() - self.config = component.get('MainWindow').config self.visible(self.config['show_tabsbar']) def tab_insert_position(self, weight): @@ -314,9 +321,24 @@ class TorrentDetails(component.Component): self.generate_menu() self.visible(True) + def create_tab_pos_menuitem(self): + """Returns a menu to select which side of the notebook the tabs should be shown""" + tab_pos_menu = Menu() + tab_pos_menuitem = MenuItem.new_with_label(_('Position')) + group = [] + for pos in ('top', 'right', 'bottom', 'left'): + menuitem = RadioMenuItem.new_with_mnemonic(group, _(pos.capitalize())) + group = menuitem.get_group() + menuitem.connect('toggled', self._on_tabs_pos_toggled, pos) + menuitem.set_active(pos == self.notebook.get_tab_pos().value_nick) + tab_pos_menu.append(menuitem) + tab_pos_menuitem.set_submenu(tab_pos_menu) + return tab_pos_menuitem + def generate_menu(self): """Generates the checklist menu for all the tabs and attaches it""" menu = Menu() + # Create 'All' menuitem and a separator menuitem = CheckMenuItem.new_with_mnemonic(self.translate_tabs['All']) menuitem.set_name('All') @@ -347,6 +369,9 @@ class TorrentDetails(component.Component): menuitem.connect('toggled', self._on_menuitem_toggled) menu.append(menuitem) + menu.append(SeparatorMenuItem()) + menu.append(self.create_tab_pos_menuitem()) + self.menu_tabs.set_submenu(menu) self.menu_tabs.show_all() @@ -440,6 +465,10 @@ class TorrentDetails(component.Component): self.set_tab_visible(name, widget.get_active()) + def _on_tabs_pos_toggled(self, widget, position): + self.config['tabsbar_tab_pos'] = position + self.notebook.set_tab_pos(getattr(PositionType, position.upper())) + def save_state(self): """We save the state, which is basically the tab_index list""" # Update the visiblity status of all tabs diff --git a/deluge/ui/gtk3/torrentview.py b/deluge/ui/gtk3/torrentview.py index fcc6edf..16de16e 100644 --- a/deluge/ui/gtk3/torrentview.py +++ b/deluge/ui/gtk3/torrentview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -8,8 +7,6 @@ # """The torrent view component that lists all torrents in the session.""" -from __future__ import unicode_literals - import logging from locale import strcoll @@ -77,13 +74,13 @@ def eta_column_sort(model, iter1, iter2, data): if v1 == v2: return 0 if v1 == 0: - return 1 - if v2 == 0: return -1 - if v1 > v2: + if v2 == 0: return 1 - if v2 > v1: + if v1 > v2: return -1 + if v2 > v1: + return 1 def seed_peer_column_sort(model, iter1, iter2, data): @@ -107,7 +104,7 @@ def progress_sort(model, iter1, iter2, sort_column_id): return cmp(progress1, progress2) -class SearchBox(object): +class SearchBox: def __init__(self, torrentview): self.torrentview = torrentview mainwindow = component.get('MainWindow') @@ -404,6 +401,13 @@ class TorrentView(ListView, component.Component): status_field=['last_seen_complete'], default=False, ) + self.add_func_column( + _('Last Transfer'), + funcs.cell_data_time, + [int], + status_field=['time_since_transfer'], + default=False, + ) self.add_texticon_column( _('Tracker'), function=funcs.cell_data_trackericon, diff --git a/deluge/ui/gtk3/torrentview_data_funcs.py b/deluge/ui/gtk3/torrentview_data_funcs.py index 8bd1f9c..0b2545d 100644 --- a/deluge/ui/gtk3/torrentview_data_funcs.py +++ b/deluge/ui/gtk3/torrentview_data_funcs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import print_function, unicode_literals - import warnings from functools import partial @@ -17,7 +14,7 @@ import deluge.component as component from .common import ( create_blank_pixbuf, - get_pixbuf_at_size, + get_pixbuf, icon_alert, icon_checking, icon_downloading, @@ -42,7 +39,6 @@ ICON_STATE = { # renderer. This is much cheaper than fetch the current value and test if # it's equal. func_last_value = { - 'cell_data_time': None, 'cell_data_ratio_seeds_peers': None, 'cell_data_ratio_ratio': None, 'cell_data_ratio_avail': None, @@ -86,7 +82,7 @@ def set_tracker_icon(tracker_icon, cell): if tracker_icon: pixbuf = tracker_icon.get_cached_icon() if pixbuf is None: - pixbuf = get_pixbuf_at_size(tracker_icon.get_filename(), 16) + pixbuf = get_pixbuf(tracker_icon.get_filename(), 16) tracker_icon.set_cached_icon(pixbuf) else: pixbuf = create_blank_pixbuf() @@ -162,7 +158,7 @@ def cell_data_speed(cell, model, row, data): if speed > 0: speed_str = common.fspeed(speed, shortform=True) cell.set_property( - 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split())) + 'markup', '{} <small>{}</small>'.format(*tuple(speed_str.split())) ) else: cell.set_property('text', '') @@ -189,7 +185,7 @@ def cell_data_speed_limit(cell, model, row, data, cache_key): if speed > 0: speed_str = common.fspeed(speed * 1024, shortform=True) cell.set_property( - 'markup', '{0} <small>{1}</small>'.format(*tuple(speed_str.split())) + 'markup', '{} <small>{}</small>'.format(*tuple(speed_str.split())) ) else: cell.set_property('text', '') @@ -222,10 +218,6 @@ def cell_data_peer(column, cell, model, row, data): def cell_data_time(column, cell, model, row, data): """Display value as time, eg 1m10s""" time = model.get_value(row, data) - if func_last_value['cell_data_time'] == time: - return - func_last_value['cell_data_time'] = time - if time <= 0: time_str = '' else: diff --git a/deluge/ui/gtk3/trackers_tab.py b/deluge/ui/gtk3/trackers_tab.py index d83b995..d671471 100644 --- a/deluge/ui/gtk3/trackers_tab.py +++ b/deluge/ui/gtk3/trackers_tab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com> # @@ -7,8 +6,6 @@ # See LICENSE for more details. # -from __future__ import unicode_literals - import logging import deluge.component as component @@ -22,9 +19,7 @@ log = logging.getLogger(__name__) class TrackersTab(Tab): def __init__(self): - super(TrackersTab, self).__init__( - 'Trackers', 'trackers_tab', 'trackers_tab_label' - ) + super().__init__('Trackers', 'trackers_tab', 'trackers_tab_label') self.add_tab_widget('summary_next_announce', ftime, ('next_announce',)) self.add_tab_widget('summary_tracker', None, ('tracker_host',)) |