summaryrefslogtreecommitdiffstats
path: root/deluge/ui/gtk3
diff options
context:
space:
mode:
Diffstat (limited to 'deluge/ui/gtk3')
-rw-r--r--deluge/ui/gtk3/__init__.py13
-rw-r--r--deluge/ui/gtk3/aboutdialog.py7
-rw-r--r--deluge/ui/gtk3/addtorrentdialog.py17
-rw-r--r--deluge/ui/gtk3/common.py128
-rw-r--r--deluge/ui/gtk3/connectionmanager.py14
-rw-r--r--deluge/ui/gtk3/createtorrentdialog.py5
-rw-r--r--deluge/ui/gtk3/details_tab.py7
-rw-r--r--deluge/ui/gtk3/dialogs.py129
-rw-r--r--deluge/ui/gtk3/edittrackersdialog.py70
-rw-r--r--deluge/ui/gtk3/files_tab.py14
-rw-r--r--deluge/ui/gtk3/filtertreeview.py22
-rw-r--r--deluge/ui/gtk3/glade/add_torrent_dialog.infohash.ui2
-rw-r--r--deluge/ui/gtk3/glade/add_torrent_dialog.ui8
-rw-r--r--deluge/ui/gtk3/glade/add_torrent_dialog.url.ui2
-rw-r--r--deluge/ui/gtk3/glade/connect_peer_dialog.ui2
-rw-r--r--deluge/ui/gtk3/glade/connection_manager.addhost.ui137
-rw-r--r--deluge/ui/gtk3/glade/connection_manager.ui2
-rw-r--r--deluge/ui/gtk3/glade/create_torrent_dialog.remote_path.ui2
-rw-r--r--deluge/ui/gtk3/glade/create_torrent_dialog.remote_save.ui2
-rw-r--r--deluge/ui/gtk3/glade/create_torrent_dialog.ui5
-rw-r--r--deluge/ui/gtk3/glade/edit_trackers.edit.ui2
-rw-r--r--deluge/ui/gtk3/glade/main_window.tabs.ui48
-rw-r--r--deluge/ui/gtk3/glade/main_window.tabs.ui~1507
-rw-r--r--deluge/ui/gtk3/glade/main_window.ui1
-rw-r--r--deluge/ui/gtk3/glade/other_dialog.ui2
-rw-r--r--deluge/ui/gtk3/glade/path_combo_chooser.ui3
-rw-r--r--deluge/ui/gtk3/glade/preferences_dialog.ui158
-rw-r--r--deluge/ui/gtk3/glade/torrent_menu.ui16
-rw-r--r--deluge/ui/gtk3/gtkui.py33
-rw-r--r--deluge/ui/gtk3/ipcinterface.py15
-rw-r--r--deluge/ui/gtk3/listview.py33
-rw-r--r--deluge/ui/gtk3/mainwindow.py43
-rw-r--r--deluge/ui/gtk3/menubar.py36
-rw-r--r--deluge/ui/gtk3/menubar_osx.py3
-rw-r--r--deluge/ui/gtk3/new_release_dialog.py5
-rw-r--r--deluge/ui/gtk3/options_tab.py10
-rw-r--r--deluge/ui/gtk3/path_chooser.py5
-rwxr-xr-xdeluge/ui/gtk3/path_combo_chooser.py48
-rw-r--r--deluge/ui/gtk3/peers_tab.py36
-rw-r--r--deluge/ui/gtk3/piecesbar.py16
-rw-r--r--deluge/ui/gtk3/pluginmanager.py3
-rw-r--r--deluge/ui/gtk3/preferences.py125
-rw-r--r--deluge/ui/gtk3/queuedtorrents.py3
-rw-r--r--deluge/ui/gtk3/removetorrentdialog.py5
-rw-r--r--deluge/ui/gtk3/sidebar.py3
-rw-r--r--deluge/ui/gtk3/status_tab.py7
-rw-r--r--deluge/ui/gtk3/statusbar.py34
-rw-r--r--deluge/ui/gtk3/systemtray.py17
-rw-r--r--deluge/ui/gtk3/tab_data_funcs.py11
-rw-r--r--deluge/ui/gtk3/toolbar.py3
-rw-r--r--deluge/ui/gtk3/torrentdetails.py45
-rw-r--r--deluge/ui/gtk3/torrentview.py20
-rw-r--r--deluge/ui/gtk3/torrentview_data_funcs.py16
-rw-r--r--deluge/ui/gtk3/trackers_tab.py7
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">&lt;b&gt;Languge&lt;/b&gt;</property>
+ <property name="label" translatable="yes">&lt;b&gt;Language&lt;/b&gt;</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',))