[GTKUI] Add new OtherDialog to dialogs
This adds a new OtherDialog to dialogs so that will use Deferred to prevent the dialog loop locking up the mainwindow. Remove old `Other` dialog from common and cleanup up the file. Fixes #2401, context menus for torrents not showing current value. Fixes #2400, add a stop_at_ratio context menu. Change the protocol rate to display as int.
This commit is contained in:
parent
813261df07
commit
ea7ef950a3
|
@ -1,128 +1,93 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# common.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Marcos Pinto ('markybob') <markybob@gmail.com>
|
# Copyright (C) 2008 Marcos Pinto ('markybob') <markybob@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# You must obey the GNU General Public License in all respects for all of
|
|
||||||
# the code used other than OpenSSL. If you modify file(s) with this
|
|
||||||
# exception, you may extend this exception to your version of the file(s),
|
|
||||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
|
||||||
# this exception statement from your version. If you delete this exception
|
|
||||||
# statement from all source files in the program, then also delete it here.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
"""Common functions for various parts of gtkui to use."""
|
"""Common functions for various parts of gtkui to use."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pygtk
|
|
||||||
pygtk.require('2.0')
|
|
||||||
import gtk
|
|
||||||
import logging
|
import logging
|
||||||
import cPickle
|
import cPickle
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
import deluge.component as component
|
import pygtk
|
||||||
|
pygtk.require('2.0')
|
||||||
|
import gtk
|
||||||
|
from gobject import GError
|
||||||
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_logo(size):
|
def get_logo(size):
|
||||||
"""Returns a deluge logo pixbuf based on the size parameter."""
|
"""A Deluge logo.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
size (int): Size of logo in pixels
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
gtk.gdk.Pixbuf: deluge logo
|
||||||
|
"""
|
||||||
|
filename = "deluge.svg"
|
||||||
if deluge.common.windows_check() or deluge.common.osx_check():
|
if deluge.common.windows_check() or deluge.common.osx_check():
|
||||||
return gtk.gdk.pixbuf_new_from_file_at_size(deluge.common.get_pixmap("deluge.png"), \
|
filename = "deluge.png"
|
||||||
size, size)
|
try:
|
||||||
else:
|
return gtk.gdk.pixbuf_new_from_file_at_size(deluge.common.get_pixmap(filename), size, size)
|
||||||
try:
|
except GError as ex:
|
||||||
return gtk.gdk.pixbuf_new_from_file_at_size(deluge.common.get_pixmap("deluge.svg"), \
|
log.warning(ex)
|
||||||
size, size)
|
|
||||||
except Exception, e:
|
|
||||||
log.warning(e)
|
|
||||||
|
|
||||||
def build_menu_radio_list(value_list, callback, pref_value=None,
|
|
||||||
suffix=None, show_notset=False, notset_label=None, notset_lessthan=0,
|
|
||||||
show_other=False, show_activated=False, activated_label=None):
|
|
||||||
# Build a menu with radio menu items from a list and connect them to
|
|
||||||
# the callback. The pref_value is what you would like to test for the
|
|
||||||
# default active radio item.
|
|
||||||
if notset_label is None:
|
|
||||||
notset_label = _("Unlimited")
|
|
||||||
|
|
||||||
if activated_label is None:
|
def build_menu_radio_list(value_list, callback, pref_value=None, suffix=None, show_notset=False,
|
||||||
activated_label = _("Activated")
|
notset_label="∞", notset_lessthan=0, show_other=False):
|
||||||
|
"""Build a menu with radio menu items from a list and connect them to the callback.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
value_list [list]: List of values to build into a menu.
|
||||||
|
callback (function): The function to call when menu item is clicked.
|
||||||
|
pref_value (int): A preferred value to insert into value_list
|
||||||
|
suffix (str): Append a suffix the the menu items in value_list.
|
||||||
|
show_notset (bool): Show the unlimited menu item.
|
||||||
|
notset_label (str): The text for the unlimited menu item.
|
||||||
|
notset_lessthan (int): Activates the unlimited menu item if pref_value is less than this.
|
||||||
|
show_other (bool): Show the `Other` menu item.
|
||||||
|
|
||||||
|
The pref_value is what you would like to test for the default active radio item.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
gtk.Menu: The menu radio
|
||||||
|
"""
|
||||||
menu = gtk.Menu()
|
menu = gtk.Menu()
|
||||||
group = None
|
group = None
|
||||||
if show_activated is False:
|
|
||||||
if pref_value > -1 and pref_value not in value_list:
|
|
||||||
value_list.pop()
|
|
||||||
value_list.append(pref_value)
|
|
||||||
|
|
||||||
for value in sorted(value_list):
|
if pref_value > -1 and pref_value not in value_list:
|
||||||
if suffix != None:
|
value_list.pop()
|
||||||
menuitem = gtk.RadioMenuItem(group, str(value) + " " + \
|
value_list.append(pref_value)
|
||||||
suffix)
|
|
||||||
else:
|
|
||||||
menuitem = gtk.RadioMenuItem(group, str(value))
|
|
||||||
|
|
||||||
group = menuitem
|
for value in sorted(value_list):
|
||||||
|
item_text = str(value)
|
||||||
if value == pref_value and pref_value != None:
|
if suffix:
|
||||||
menuitem.set_active(True)
|
item_text += " " + suffix
|
||||||
|
menuitem = gtk.RadioMenuItem(group, item_text)
|
||||||
if callback != None:
|
group = menuitem
|
||||||
menuitem.connect("toggled", callback)
|
if pref_value and value == pref_value:
|
||||||
|
menuitem.set_active(True)
|
||||||
menu.append(menuitem)
|
if callback:
|
||||||
|
menuitem.connect("toggled", callback)
|
||||||
if show_activated is True:
|
menu.append(menuitem)
|
||||||
for value in sorted(value_list):
|
|
||||||
menuitem = gtk.RadioMenuItem(group, str(activated_label))
|
|
||||||
|
|
||||||
group = menuitem
|
|
||||||
|
|
||||||
if value == pref_value and pref_value != None:
|
|
||||||
menuitem.set_active(True)
|
|
||||||
|
|
||||||
if callback != None:
|
|
||||||
menuitem.connect("toggled", callback)
|
|
||||||
|
|
||||||
menu.append(menuitem)
|
|
||||||
|
|
||||||
if show_notset:
|
if show_notset:
|
||||||
menuitem = gtk.RadioMenuItem(group, notset_label)
|
menuitem = gtk.RadioMenuItem(group, notset_label)
|
||||||
menuitem.set_name("unlimited")
|
menuitem.set_name("unlimited")
|
||||||
if pref_value < notset_lessthan and pref_value != None:
|
if pref_value and pref_value < notset_lessthan:
|
||||||
menuitem.set_active(True)
|
|
||||||
if show_activated and pref_value == 1:
|
|
||||||
menuitem.set_active(True)
|
menuitem.set_active(True)
|
||||||
menuitem.connect("toggled", callback)
|
menuitem.connect("toggled", callback)
|
||||||
menu.append(menuitem)
|
menu.append(menuitem)
|
||||||
|
|
||||||
# Add the Other... menuitem
|
if show_other:
|
||||||
if show_other is True:
|
|
||||||
menuitem = gtk.SeparatorMenuItem()
|
menuitem = gtk.SeparatorMenuItem()
|
||||||
menu.append(menuitem)
|
menu.append(menuitem)
|
||||||
menuitem = gtk.MenuItem(_("Other..."))
|
menuitem = gtk.MenuItem(_("Other..."))
|
||||||
|
@ -132,83 +97,25 @@ def build_menu_radio_list(value_list, callback, pref_value=None,
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
|
|
||||||
def show_other_dialog(header, type_str, image_stockid=None, image_filename=None, default=0):
|
|
||||||
"""
|
|
||||||
Shows a dialog with `header` as the header text and `type_str`
|
|
||||||
as the type text. The type of spinbutton (int or float) is determined by
|
|
||||||
`default` type.
|
|
||||||
|
|
||||||
:param header: str, the header label text
|
|
||||||
:param type_str: str, the type label text, what comes after the spinbutton
|
|
||||||
:param image_stockid: gtkStockId, the stock id of the image in the header
|
|
||||||
:param image_filename: str, filename of icon in pixmaps folder
|
|
||||||
:param default: the default value in the spinbutton
|
|
||||||
|
|
||||||
:returns: None, int or float from spinbutton depending on `default`.
|
|
||||||
None is returned if the user clicks on Cancel.
|
|
||||||
:rtype: None, int or float
|
|
||||||
|
|
||||||
:raises TypeError: if `default` is not of type int or float
|
|
||||||
|
|
||||||
"""
|
|
||||||
if type(default) != int and type(default) != float:
|
|
||||||
raise TypeError("default value needs to be an int or float")
|
|
||||||
|
|
||||||
builder = gtk.Builder()
|
|
||||||
builder.add_from_file(deluge.common.resource_filename(
|
|
||||||
"deluge.ui.gtkui", os.path.join("glade", "other_dialog.ui")
|
|
||||||
))
|
|
||||||
dialog = builder.get_object("other_dialog")
|
|
||||||
dialog.set_transient_for(component.get("MainWindow").window)
|
|
||||||
dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
|
||||||
dialog.set_title("")
|
|
||||||
builder.get_object("label_header").set_markup("<b>" + header + "</b>")
|
|
||||||
builder.get_object("label_type").set_text(type_str)
|
|
||||||
if image_stockid:
|
|
||||||
builder.get_object("image").set_from_stock(image_stockid, gtk.ICON_SIZE_LARGE_TOOLBAR)
|
|
||||||
if image_filename:
|
|
||||||
# Hack for Windows since it doesn't support svg
|
|
||||||
if os.path.splitext(image_filename)[1] == ".svg" and (deluge.common.windows_check() or deluge.common.osx_check()):
|
|
||||||
image_filename = os.path.splitext(image_filename)[0] + "16.png"
|
|
||||||
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(
|
|
||||||
deluge.common.get_pixmap(image_filename), 32, 32)
|
|
||||||
builder.get_object("image").set_from_pixbuf(pixbuf)
|
|
||||||
|
|
||||||
spinbutton = builder.get_object("spinbutton")
|
|
||||||
if type(default) == float:
|
|
||||||
spinbutton.set_digits(1)
|
|
||||||
|
|
||||||
# Set default value and select text
|
|
||||||
spinbutton.set_value(default)
|
|
||||||
spinbutton.select_region(0, -1)
|
|
||||||
|
|
||||||
value = None
|
|
||||||
response = dialog.run()
|
|
||||||
if response == gtk.RESPONSE_OK:
|
|
||||||
if type(default) == int:
|
|
||||||
value = spinbutton.get_value_as_int()
|
|
||||||
else:
|
|
||||||
value = spinbutton.get_value()
|
|
||||||
|
|
||||||
dialog.destroy()
|
|
||||||
return value
|
|
||||||
|
|
||||||
def reparent_iter(treestore, itr, parent, move_siblings=False):
|
def reparent_iter(treestore, itr, parent, move_siblings=False):
|
||||||
"""
|
"""
|
||||||
This effectively moves itr plus it's children to be a child of parent in treestore
|
This effectively moves itr plus it's children to be a child of parent in treestore
|
||||||
|
|
||||||
:param treestore: gtkTreeStore, the treestore
|
Params:
|
||||||
:param itr: gtkTreeIter, the iter to move
|
treestore (gtkTreeStore): the treestore
|
||||||
:param parent: gtkTreeIter, the new parent for itr
|
itr (gtkTreeIter): the iter to move
|
||||||
:param move_siblings: bool. if True, it will move all itr's siblings to parent
|
parent (gtkTreeIter): the new parent for itr
|
||||||
|
move_siblings (bool): if True, it will move all itr's siblings to parent
|
||||||
"""
|
"""
|
||||||
src = itr
|
src = itr
|
||||||
|
|
||||||
def move_children(i, dest):
|
def move_children(i, dest):
|
||||||
while i:
|
while i:
|
||||||
n = treestore.append(dest, treestore.get(i, *xrange(treestore.get_n_columns())))
|
n_cols = treestore.append(dest, treestore.get(i, *xrange(treestore.get_n_columns())))
|
||||||
to_remove = i
|
to_remove = i
|
||||||
if treestore.iter_children(i):
|
if treestore.iter_children(i):
|
||||||
move_children(treestore.iter_children(i), n)
|
move_children(treestore.iter_children(i), n_cols)
|
||||||
if i != src:
|
if i != src:
|
||||||
i = treestore.iter_next(i)
|
i = treestore.iter_next(i)
|
||||||
else:
|
else:
|
||||||
|
@ -219,12 +126,15 @@ def reparent_iter(treestore, itr, parent, move_siblings=False):
|
||||||
|
|
||||||
move_children(itr, parent)
|
move_children(itr, parent)
|
||||||
|
|
||||||
|
|
||||||
def get_deluge_icon():
|
def get_deluge_icon():
|
||||||
"""
|
"""The deluge icon for use in dialogs.
|
||||||
Returns the deluge icon for use in setting a dialogs icon. It will first
|
|
||||||
attempt to get the icon from the theme and will fallback to using an image
|
It will first attempt to get the icon from the theme and will fallback to using an image
|
||||||
that is distributed with the package.
|
that is distributed with the package.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
gtk.gdk.Pixbuf: the deluge icon
|
||||||
"""
|
"""
|
||||||
if deluge.common.windows_check():
|
if deluge.common.windows_check():
|
||||||
return get_logo(32)
|
return get_logo(32)
|
||||||
|
@ -232,18 +142,19 @@ def get_deluge_icon():
|
||||||
try:
|
try:
|
||||||
icon_theme = gtk.icon_theme_get_default()
|
icon_theme = gtk.icon_theme_get_default()
|
||||||
return icon_theme.load_icon("deluge", 64, 0)
|
return icon_theme.load_icon("deluge", 64, 0)
|
||||||
except:
|
except GError:
|
||||||
return get_logo(64)
|
return get_logo(64)
|
||||||
|
|
||||||
|
|
||||||
def associate_magnet_links(overwrite=False):
|
def associate_magnet_links(overwrite=False):
|
||||||
"""
|
"""
|
||||||
Associates magnet links to Deluge.
|
Associates magnet links to Deluge.
|
||||||
|
|
||||||
:param overwrite: if this is True, the current setting will be overwritten
|
Params:
|
||||||
:type overwrite: bool
|
overwrite (bool): if this is True, the current setting will be overwritten
|
||||||
:returns: True if association was set
|
|
||||||
:rtype: bool
|
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if association was set
|
||||||
"""
|
"""
|
||||||
if not deluge.common.windows_check():
|
if not deluge.common.windows_check():
|
||||||
# gconf method is only available in a GNOME environment
|
# gconf method is only available in a GNOME environment
|
||||||
|
@ -267,10 +178,13 @@ def associate_magnet_links(overwrite=False):
|
||||||
return False
|
return False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def save_pickled_state_file(filename, state):
|
def save_pickled_state_file(filename, state):
|
||||||
"""Save a file in the config directory and creates a backup
|
"""Save a file in the config directory and creates a backup
|
||||||
filename: Filename to be saved to config
|
|
||||||
state: The data to be pickled and written to file
|
Params:
|
||||||
|
filename (str): Filename to be saved to config
|
||||||
|
state (state): The data to be pickled and written to file
|
||||||
"""
|
"""
|
||||||
from deluge.configmanager import get_config_dir
|
from deluge.configmanager import get_config_dir
|
||||||
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
|
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
|
||||||
|
@ -298,10 +212,15 @@ def save_pickled_state_file(filename, state):
|
||||||
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
|
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
|
||||||
shutil.move(filepath_bak, filepath)
|
shutil.move(filepath_bak, filepath)
|
||||||
|
|
||||||
|
|
||||||
def load_pickled_state_file(filename):
|
def load_pickled_state_file(filename):
|
||||||
"""Loads a file from the config directory, attempting backup if original fails to load.
|
"""Loads a file from the config directory, attempting backup if original fails to load.
|
||||||
filename: Filename to be loaded from config
|
|
||||||
returns unpickled state
|
Params:
|
||||||
|
filename (str): Filename to be loaded from config
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
state: the unpickled state
|
||||||
"""
|
"""
|
||||||
from deluge.configmanager import get_config_dir
|
from deluge.configmanager import get_config_dir
|
||||||
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
|
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
|
||||||
|
@ -313,7 +232,7 @@ def load_pickled_state_file(filename):
|
||||||
try:
|
try:
|
||||||
with open(_filepath, "rb") as _file:
|
with open(_filepath, "rb") as _file:
|
||||||
state = cPickle.load(_file)
|
state = cPickle.load(_file)
|
||||||
except (IOError, cPickle.UnpicklingError), ex:
|
except (IOError, cPickle.UnpicklingError) as ex:
|
||||||
log.warning("Unable to load %s: %s", _filepath, ex)
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
else:
|
else:
|
||||||
log.info("Successfully loaded %s: %s", filename, _filepath)
|
log.info("Successfully loaded %s: %s", filename, _filepath)
|
||||||
|
|
|
@ -31,13 +31,13 @@
|
||||||
# this exception statement from your version. If you delete this exception
|
# this exception statement from your version. If you delete this exception
|
||||||
# statement from all source files in the program, then also delete it here.
|
# statement from all source files in the program, then also delete it here.
|
||||||
#
|
#
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from deluge.ui.gtkui import common
|
from deluge.ui.gtkui import common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
|
import deluge.common
|
||||||
|
|
||||||
|
|
||||||
class BaseDialog(gtk.Dialog):
|
class BaseDialog(gtk.Dialog):
|
||||||
|
@ -69,7 +69,14 @@ class BaseDialog(gtk.Dialog):
|
||||||
self.set_default_size(200, 100)
|
self.set_default_size(200, 100)
|
||||||
hbox = gtk.HBox(spacing=5)
|
hbox = gtk.HBox(spacing=5)
|
||||||
image = gtk.Image()
|
image = gtk.Image()
|
||||||
image.set_from_stock(icon, gtk.ICON_SIZE_DIALOG)
|
if not gtk.stock_lookup(icon) and (icon.endswith(".svg") or icon.endswith(".png")):
|
||||||
|
# Hack for Windows since it doesn't support svg
|
||||||
|
if icon.endswith(".svg") and (deluge.common.windows_check() or deluge.common.osx_check()):
|
||||||
|
icon = icon.rpartition(".svg")[0] + "16.png"
|
||||||
|
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(deluge.common.get_pixmap(icon), 32, 32)
|
||||||
|
image.set_from_pixbuf(pixbuf)
|
||||||
|
else:
|
||||||
|
image.set_from_stock(icon, gtk.ICON_SIZE_DIALOG)
|
||||||
image.set_alignment(0.5, 0.0)
|
image.set_alignment(0.5, 0.0)
|
||||||
hbox.pack_start(image, False, False)
|
hbox.pack_start(image, False, False)
|
||||||
vbox = gtk.VBox(spacing=5)
|
vbox = gtk.VBox(spacing=5)
|
||||||
|
@ -105,6 +112,7 @@ class BaseDialog(gtk.Dialog):
|
||||||
self.show()
|
self.show()
|
||||||
return self.deferred
|
return self.deferred
|
||||||
|
|
||||||
|
|
||||||
class YesNoDialog(BaseDialog):
|
class YesNoDialog(BaseDialog):
|
||||||
"""
|
"""
|
||||||
Displays a dialog asking the user to select Yes or No to a question.
|
Displays a dialog asking the user to select Yes or No to a question.
|
||||||
|
@ -122,9 +130,10 @@ class YesNoDialog(BaseDialog):
|
||||||
header,
|
header,
|
||||||
text,
|
text,
|
||||||
gtk.STOCK_DIALOG_QUESTION,
|
gtk.STOCK_DIALOG_QUESTION,
|
||||||
(gtk.STOCK_YES, gtk.RESPONSE_YES, gtk.STOCK_NO, gtk.RESPONSE_NO),
|
(gtk.STOCK_NO, gtk.RESPONSE_NO, gtk.STOCK_YES, gtk.RESPONSE_YES),
|
||||||
parent)
|
parent)
|
||||||
|
|
||||||
|
|
||||||
class InformationDialog(BaseDialog):
|
class InformationDialog(BaseDialog):
|
||||||
"""
|
"""
|
||||||
Displays an information dialog.
|
Displays an information dialog.
|
||||||
|
@ -144,6 +153,7 @@ class InformationDialog(BaseDialog):
|
||||||
(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE),
|
(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE),
|
||||||
parent)
|
parent)
|
||||||
|
|
||||||
|
|
||||||
class ErrorDialog(BaseDialog):
|
class ErrorDialog(BaseDialog):
|
||||||
"""
|
"""
|
||||||
Displays an error dialog with optional details text for more information.
|
Displays an error dialog with optional details text for more information.
|
||||||
|
@ -193,6 +203,7 @@ class ErrorDialog(BaseDialog):
|
||||||
self.vbox.pack_start(sw)
|
self.vbox.pack_start(sw)
|
||||||
self.vbox.show_all()
|
self.vbox.show_all()
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationDialog(BaseDialog):
|
class AuthenticationDialog(BaseDialog):
|
||||||
"""
|
"""
|
||||||
Displays a dialog with entry fields asking for username and password.
|
Displays a dialog with entry fields asking for username and password.
|
||||||
|
@ -213,7 +224,7 @@ class AuthenticationDialog(BaseDialog):
|
||||||
|
|
||||||
table = gtk.Table(2, 2, False)
|
table = gtk.Table(2, 2, False)
|
||||||
self.username_label = gtk.Label()
|
self.username_label = gtk.Label()
|
||||||
self.username_label.set_markup(_("<b>Username:</b>"))
|
self.username_label.set_markup("<b>" + _("Username:") + "</b>")
|
||||||
self.username_label.set_alignment(1.0, 0.5)
|
self.username_label.set_alignment(1.0, 0.5)
|
||||||
self.username_label.set_padding(5, 5)
|
self.username_label.set_padding(5, 5)
|
||||||
self.username_entry = gtk.Entry()
|
self.username_entry = gtk.Entry()
|
||||||
|
@ -221,7 +232,7 @@ class AuthenticationDialog(BaseDialog):
|
||||||
table.attach(self.username_entry, 1, 2, 0, 1)
|
table.attach(self.username_entry, 1, 2, 0, 1)
|
||||||
|
|
||||||
self.password_label = gtk.Label()
|
self.password_label = gtk.Label()
|
||||||
self.password_label.set_markup(_("<b>Password:</b>"))
|
self.password_label.set_markup("<b>" + _("Password:") + "</b>")
|
||||||
self.password_label.set_alignment(1.0, 0.5)
|
self.password_label.set_alignment(1.0, 0.5)
|
||||||
self.password_label.set_padding(5, 5)
|
self.password_label.set_padding(5, 5)
|
||||||
self.password_entry = gtk.Entry()
|
self.password_entry = gtk.Entry()
|
||||||
|
@ -249,6 +260,7 @@ class AuthenticationDialog(BaseDialog):
|
||||||
def on_password_activate(self, widget):
|
def on_password_activate(self, widget):
|
||||||
self.response(gtk.RESPONSE_OK)
|
self.response(gtk.RESPONSE_OK)
|
||||||
|
|
||||||
|
|
||||||
class AccountDialog(BaseDialog):
|
class AccountDialog(BaseDialog):
|
||||||
def __init__(self, username=None, password=None, authlevel=None,
|
def __init__(self, username=None, password=None, authlevel=None,
|
||||||
levels_mapping=None, parent=None):
|
levels_mapping=None, parent=None):
|
||||||
|
@ -273,7 +285,7 @@ class AccountDialog(BaseDialog):
|
||||||
|
|
||||||
table = gtk.Table(2, 3, False)
|
table = gtk.Table(2, 3, False)
|
||||||
self.username_label = gtk.Label()
|
self.username_label = gtk.Label()
|
||||||
self.username_label.set_markup(_("<b>Username:</b>"))
|
self.username_label.set_markup("<b>" + _("Username:") + "</b>")
|
||||||
self.username_label.set_alignment(1.0, 0.5)
|
self.username_label.set_alignment(1.0, 0.5)
|
||||||
self.username_label.set_padding(5, 5)
|
self.username_label.set_padding(5, 5)
|
||||||
self.username_entry = gtk.Entry()
|
self.username_entry = gtk.Entry()
|
||||||
|
@ -281,7 +293,7 @@ class AccountDialog(BaseDialog):
|
||||||
table.attach(self.username_entry, 1, 2, 0, 1)
|
table.attach(self.username_entry, 1, 2, 0, 1)
|
||||||
|
|
||||||
self.authlevel_label = gtk.Label()
|
self.authlevel_label = gtk.Label()
|
||||||
self.authlevel_label.set_markup(_("<b>Authentication Level:</b>"))
|
self.authlevel_label.set_markup("<b>" + _("Authentication Level:") + "</b>")
|
||||||
self.authlevel_label.set_alignment(1.0, 0.5)
|
self.authlevel_label.set_alignment(1.0, 0.5)
|
||||||
self.authlevel_label.set_padding(5, 5)
|
self.authlevel_label.set_padding(5, 5)
|
||||||
|
|
||||||
|
@ -289,7 +301,7 @@ class AccountDialog(BaseDialog):
|
||||||
active_idx = None
|
active_idx = None
|
||||||
for idx, level in enumerate(levels_mapping.keys()):
|
for idx, level in enumerate(levels_mapping.keys()):
|
||||||
self.authlevel_combo.append_text(level)
|
self.authlevel_combo.append_text(level)
|
||||||
if authlevel and authlevel==level:
|
if authlevel and authlevel == level:
|
||||||
active_idx = idx
|
active_idx = idx
|
||||||
elif not authlevel and level == 'DEFAULT':
|
elif not authlevel and level == 'DEFAULT':
|
||||||
active_idx = idx
|
active_idx = idx
|
||||||
|
@ -301,7 +313,7 @@ class AccountDialog(BaseDialog):
|
||||||
table.attach(self.authlevel_combo, 1, 2, 1, 2)
|
table.attach(self.authlevel_combo, 1, 2, 1, 2)
|
||||||
|
|
||||||
self.password_label = gtk.Label()
|
self.password_label = gtk.Label()
|
||||||
self.password_label.set_markup(_("<b>Password:</b>"))
|
self.password_label.set_markup("<b>" + _("Password:") + "</b>")
|
||||||
self.password_label.set_alignment(1.0, 0.5)
|
self.password_label.set_alignment(1.0, 0.5)
|
||||||
self.password_label.set_padding(5, 5)
|
self.password_label.set_padding(5, 5)
|
||||||
self.password_entry = gtk.Entry()
|
self.password_entry = gtk.Entry()
|
||||||
|
@ -331,3 +343,63 @@ class AccountDialog(BaseDialog):
|
||||||
combobox = self.authlevel_combo
|
combobox = self.authlevel_combo
|
||||||
level = combobox.get_model()[combobox.get_active()][0]
|
level = combobox.get_model()[combobox.get_active()][0]
|
||||||
return level
|
return level
|
||||||
|
|
||||||
|
|
||||||
|
class OtherDialog(BaseDialog):
|
||||||
|
"""
|
||||||
|
Displays a dialog with a spinner for setting a value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int or float:
|
||||||
|
"""
|
||||||
|
def __init__(self, header, text="", unit_text="", icon=None, default=0, parent=None):
|
||||||
|
self.value_type = type(default)
|
||||||
|
if self.value_type not in (int, float):
|
||||||
|
raise TypeError("default value needs to be an int or float")
|
||||||
|
|
||||||
|
if not icon:
|
||||||
|
icon = gtk.STOCK_DIALOG_INFO
|
||||||
|
|
||||||
|
super(OtherDialog, self).__init__(
|
||||||
|
header,
|
||||||
|
text,
|
||||||
|
icon,
|
||||||
|
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_APPLY, gtk.RESPONSE_OK),
|
||||||
|
parent)
|
||||||
|
|
||||||
|
hbox = gtk.HBox(spacing=5)
|
||||||
|
alignment_spacer = gtk.Alignment()
|
||||||
|
hbox.pack_start(alignment_spacer)
|
||||||
|
alignment_spin = gtk.Alignment(1, 0.5, 1, 1)
|
||||||
|
adjustment_spin = gtk.Adjustment(value=-1, lower=-1, upper=2097151, step_incr=1, page_incr=10)
|
||||||
|
self.spinbutton = gtk.SpinButton(adjustment_spin)
|
||||||
|
self.spinbutton.set_value(default)
|
||||||
|
self.spinbutton.select_region(0, -1)
|
||||||
|
self.spinbutton.set_width_chars(6)
|
||||||
|
self.spinbutton.set_alignment(1)
|
||||||
|
self.spinbutton.set_max_length(6)
|
||||||
|
if self.value_type is float:
|
||||||
|
self.spinbutton.set_digits(1)
|
||||||
|
alignment_spin.add(self.spinbutton)
|
||||||
|
hbox.pack_start(alignment_spin, expand=False)
|
||||||
|
label_type = gtk.Label()
|
||||||
|
label_type.set_text(unit_text)
|
||||||
|
label_type.set_alignment(0.0, 0.5)
|
||||||
|
hbox.pack_start(label_type)
|
||||||
|
|
||||||
|
self.vbox.pack_start(hbox, False, False, padding=5)
|
||||||
|
self.vbox.show_all()
|
||||||
|
|
||||||
|
def _on_delete_event(self, widget, event):
|
||||||
|
self.deferred.callback(None)
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def _on_response(self, widget, response):
|
||||||
|
value = None
|
||||||
|
if response == gtk.RESPONSE_OK:
|
||||||
|
if self.value_type is int:
|
||||||
|
value = self.spinbutton.get_value_as_int()
|
||||||
|
else:
|
||||||
|
value = self.spinbutton.get_value()
|
||||||
|
self.deferred.callback(value)
|
||||||
|
self.destroy()
|
||||||
|
|
|
@ -60,6 +60,15 @@
|
||||||
<property name="use_stock">False</property>
|
<property name="use_stock">False</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem" id="menuitem_stop_seed_at_ratio">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="use_action_appearance">False</property>
|
||||||
|
<property name="label" translatable="yes">Stop seed at _ratio</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="menuitem_auto_managed">
|
<object class="GtkMenuItem" id="menuitem_auto_managed">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
@ -44,13 +44,13 @@ import deluge.error
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import common
|
|
||||||
import dialogs
|
import dialogs
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
from deluge.ui.gtkui.path_chooser import PathChooser
|
from deluge.ui.gtkui.path_chooser import PathChooser
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MenuBar(component.Component):
|
class MenuBar(component.Component):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -85,7 +85,7 @@ class MenuBar(component.Component):
|
||||||
self.builder.get_object("upload-limit-image").set_from_file(deluge.common.get_pixmap("seeding16.png"))
|
self.builder.get_object("upload-limit-image").set_from_file(deluge.common.get_pixmap("seeding16.png"))
|
||||||
|
|
||||||
for menuitem in ("menuitem_down_speed", "menuitem_up_speed",
|
for menuitem in ("menuitem_down_speed", "menuitem_up_speed",
|
||||||
"menuitem_max_connections", "menuitem_upload_slots"):
|
"menuitem_max_connections", "menuitem_upload_slots"):
|
||||||
submenu = gtk.Menu()
|
submenu = gtk.Menu()
|
||||||
item = gtk.MenuItem(_("Set Unlimited"))
|
item = gtk.MenuItem(_("Set Unlimited"))
|
||||||
item.set_name(menuitem)
|
item.set_name(menuitem)
|
||||||
|
@ -108,6 +108,17 @@ class MenuBar(component.Component):
|
||||||
submenu.show_all()
|
submenu.show_all()
|
||||||
self.builder.get_object("menuitem_auto_managed").set_submenu(submenu)
|
self.builder.get_object("menuitem_auto_managed").set_submenu(submenu)
|
||||||
|
|
||||||
|
submenu = gtk.Menu()
|
||||||
|
item = gtk.MenuItem(_("Disable"))
|
||||||
|
item.connect("activate", self.on_menuitem_set_stop_seed_at_ratio_disable)
|
||||||
|
submenu.append(item)
|
||||||
|
item = gtk.MenuItem(_("Enable..."))
|
||||||
|
item.set_name("menuitem_stop_seed_at_ratio")
|
||||||
|
item.connect("activate", self.on_menuitem_set_other)
|
||||||
|
submenu.append(item)
|
||||||
|
submenu.show_all()
|
||||||
|
self.builder.get_object("menuitem_stop_seed_at_ratio").set_submenu(submenu)
|
||||||
|
|
||||||
self.torrentmenu = self.builder.get_object("torrent_menu")
|
self.torrentmenu = self.builder.get_object("torrent_menu")
|
||||||
self.menu_torrent = self.main_builder.get_object("menu_torrent")
|
self.menu_torrent = self.main_builder.get_object("menu_torrent")
|
||||||
|
|
||||||
|
@ -121,7 +132,6 @@ class MenuBar(component.Component):
|
||||||
self.main_builder.get_object("sidebar_show_zero").set_active(self.config["sidebar_show_zero"])
|
self.main_builder.get_object("sidebar_show_zero").set_active(self.config["sidebar_show_zero"])
|
||||||
self.main_builder.get_object("sidebar_show_trackers").set_active(self.config["sidebar_show_trackers"])
|
self.main_builder.get_object("sidebar_show_trackers").set_active(self.config["sidebar_show_trackers"])
|
||||||
|
|
||||||
|
|
||||||
### Connect main window Signals ###
|
### Connect main window Signals ###
|
||||||
component.get("MainWindow").connect_signals({
|
component.get("MainWindow").connect_signals({
|
||||||
## File Menu
|
## File Menu
|
||||||
|
@ -144,8 +154,8 @@ class MenuBar(component.Component):
|
||||||
"on_menuitem_faq_activate": self.on_menuitem_faq_activate,
|
"on_menuitem_faq_activate": self.on_menuitem_faq_activate,
|
||||||
"on_menuitem_community_activate": self.on_menuitem_community_activate,
|
"on_menuitem_community_activate": self.on_menuitem_community_activate,
|
||||||
"on_menuitem_about_activate": self.on_menuitem_about_activate,
|
"on_menuitem_about_activate": self.on_menuitem_about_activate,
|
||||||
"on_menuitem_sidebar_zero_toggled":self.on_menuitem_sidebar_zero_toggled,
|
"on_menuitem_sidebar_zero_toggled": self.on_menuitem_sidebar_zero_toggled,
|
||||||
"on_menuitem_sidebar_trackers_toggled":self.on_menuitem_sidebar_trackers_toggled
|
"on_menuitem_sidebar_trackers_toggled": self.on_menuitem_sidebar_trackers_toggled
|
||||||
})
|
})
|
||||||
|
|
||||||
# Connect menubar signals
|
# Connect menubar signals
|
||||||
|
@ -204,8 +214,7 @@ class MenuBar(component.Component):
|
||||||
if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN:
|
if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN:
|
||||||
# Get known accounts to allow changing ownership
|
# Get known accounts to allow changing ownership
|
||||||
client.core.get_known_accounts().addCallback(
|
client.core.get_known_accounts().addCallback(
|
||||||
self._on_known_accounts).addErrback(self._on_known_accounts_fail
|
self._on_known_accounts).addErrback(self._on_known_accounts_fail)
|
||||||
)
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
log.debug("MenuBar stopping")
|
log.debug("MenuBar stopping")
|
||||||
|
@ -315,6 +324,7 @@ class MenuBar(component.Component):
|
||||||
|
|
||||||
def on_menuitem_open_folder_activate(self, data=None):
|
def on_menuitem_open_folder_activate(self, data=None):
|
||||||
log.debug("on_menuitem_open_folder")
|
log.debug("on_menuitem_open_folder")
|
||||||
|
|
||||||
def _on_torrent_status(status):
|
def _on_torrent_status(status):
|
||||||
deluge.common.open_file(status["save_path"])
|
deluge.common.open_file(status["save_path"])
|
||||||
for torrent_id in component.get("TorrentView").get_selected_torrents():
|
for torrent_id in component.get("TorrentView").get_selected_torrents():
|
||||||
|
@ -422,36 +432,54 @@ class MenuBar(component.Component):
|
||||||
|
|
||||||
def on_menuitem_set_other(self, widget):
|
def on_menuitem_set_other(self, widget):
|
||||||
log.debug("widget.name: %s", widget.name)
|
log.debug("widget.name: %s", widget.name)
|
||||||
funcs = {
|
status_map = {
|
||||||
"menuitem_down_speed": client.core.set_torrent_max_download_speed,
|
"menuitem_down_speed": ["max_download_speed", "max_download_speed"],
|
||||||
"menuitem_up_speed": client.core.set_torrent_max_upload_speed,
|
"menuitem_up_speed": ["max_upload_speed", "max_upload_speed"],
|
||||||
"menuitem_max_connections": client.core.set_torrent_max_connections,
|
"menuitem_max_connections": ["max_connections", "max_connections_global"],
|
||||||
"menuitem_upload_slots": client.core.set_torrent_max_upload_slots
|
"menuitem_upload_slots": ["max_upload_slots", "max_upload_slots_global"],
|
||||||
}
|
"menuitem_stop_seed_at_ratio": ["stop_ratio", "stop_seed_ratio"]
|
||||||
# widget: (header, type_str, image_stockid, image_filename, default)
|
|
||||||
other_dialog_info = {
|
|
||||||
"menuitem_down_speed": (
|
|
||||||
_("Set Maximum Download Speed"),
|
|
||||||
_("KiB/s"), None, "downloading.svg", -1.0
|
|
||||||
),
|
|
||||||
"menuitem_up_speed": (
|
|
||||||
_("Set Maximum Upload Speed"),
|
|
||||||
_("KiB/s"), None, "seeding.svg", -1.0
|
|
||||||
),
|
|
||||||
"menuitem_max_connections": (
|
|
||||||
_("Set Maximum Connections"), "", gtk.STOCK_NETWORK, None, -1
|
|
||||||
),
|
|
||||||
"menuitem_upload_slots": (
|
|
||||||
_("Set Maximum Upload Slots"),
|
|
||||||
"", gtk.STOCK_SORT_ASCENDING, None, -1
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Show the other dialog
|
other_dialog_info = {
|
||||||
value = common.show_other_dialog(*other_dialog_info[widget.name])
|
"menuitem_down_speed": [_("Download Speed Limit"), _("Set the maximum download speed"),
|
||||||
if value and widget.name in funcs:
|
_("KiB/s"), "downloading.svg"],
|
||||||
for torrent in component.get("TorrentView").get_selected_torrents():
|
"menuitem_up_speed": [_("Upload Speed Limit"), _("Set the maximum upload speed"),
|
||||||
funcs[widget.name](torrent, value)
|
_("KiB/s"), "seeding.svg"],
|
||||||
|
"menuitem_max_connections": [_("Incoming Connections"), _("Set the maximum incoming connections"),
|
||||||
|
"", gtk.STOCK_NETWORK],
|
||||||
|
"menuitem_upload_slots": [_("Peer Upload Slots"), _("Set the maximum upload slots"),
|
||||||
|
"", gtk.STOCK_SORT_ASCENDING],
|
||||||
|
"menuitem_stop_seed_at_ratio": [_("Stop Seed At Ratio"), "Stop torrent seeding at ratio", "", None]
|
||||||
|
}
|
||||||
|
|
||||||
|
core_key = status_map[widget.name][0]
|
||||||
|
core_key_global = status_map[widget.name][1]
|
||||||
|
|
||||||
|
def _on_torrent_status(status):
|
||||||
|
other_dialog = other_dialog_info[widget.name]
|
||||||
|
# Add the default using status value
|
||||||
|
if status:
|
||||||
|
other_dialog.append(status[core_key_global])
|
||||||
|
|
||||||
|
def set_value(value):
|
||||||
|
if value is not None:
|
||||||
|
if value == 0:
|
||||||
|
value += -1
|
||||||
|
options = {core_key: value}
|
||||||
|
if core_key == "stop_ratio":
|
||||||
|
options["stop_at_ratio"] = True
|
||||||
|
client.core.set_torrent_options(torrent_ids, options)
|
||||||
|
|
||||||
|
dialog = dialogs.OtherDialog(*other_dialog)
|
||||||
|
dialog.run().addCallback(set_value)
|
||||||
|
|
||||||
|
torrent_ids = component.get("TorrentView").get_selected_torrents()
|
||||||
|
if len(torrent_ids) == 1:
|
||||||
|
core_key_global = core_key
|
||||||
|
d = component.get("SessionProxy").get_torrent_status(torrent_ids[0], {core_key})
|
||||||
|
else:
|
||||||
|
d = client.core.get_config_values([core_key_global])
|
||||||
|
d.addCallback(_on_torrent_status)
|
||||||
|
|
||||||
def on_menuitem_set_automanaged_on(self, widget):
|
def on_menuitem_set_automanaged_on(self, widget):
|
||||||
for torrent in component.get("TorrentView").get_selected_torrents():
|
for torrent in component.get("TorrentView").get_selected_torrents():
|
||||||
|
@ -461,6 +489,10 @@ class MenuBar(component.Component):
|
||||||
for torrent in component.get("TorrentView").get_selected_torrents():
|
for torrent in component.get("TorrentView").get_selected_torrents():
|
||||||
client.core.set_torrent_auto_managed(torrent, False)
|
client.core.set_torrent_auto_managed(torrent, False)
|
||||||
|
|
||||||
|
def on_menuitem_set_stop_seed_at_ratio_disable(self, widget):
|
||||||
|
client.core.set_torrent_options(component.get("TorrentView").get_selected_torrents(),
|
||||||
|
{"stop_at_ratio": False})
|
||||||
|
|
||||||
def on_menuitem_sidebar_zero_toggled(self, widget):
|
def on_menuitem_sidebar_zero_toggled(self, widget):
|
||||||
self.config["sidebar_show_zero"] = widget.get_active()
|
self.config["sidebar_show_zero"] = widget.get_active()
|
||||||
component.get("FilterTreeView").update()
|
component.get("FilterTreeView").update()
|
||||||
|
|
|
@ -41,7 +41,8 @@ import logging
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import common
|
from deluge.ui.gtkui import common
|
||||||
|
from deluge.ui.gtkui import dialogs
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -123,7 +124,7 @@ class StatusBar(component.Component):
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
|
|
||||||
# Status variables that are updated via callback
|
# Status variables that are updated via callback
|
||||||
self.max_connections = -1
|
self.max_connections_global = -1
|
||||||
self.num_connections = 0
|
self.num_connections = 0
|
||||||
self.max_download_speed = -1.0
|
self.max_download_speed = -1.0
|
||||||
self.download_rate = ""
|
self.download_rate = ""
|
||||||
|
@ -292,12 +293,11 @@ class StatusBar(component.Component):
|
||||||
This is called when we receive a ConfigValueChangedEvent from
|
This is called when we receive a ConfigValueChangedEvent from
|
||||||
the core.
|
the core.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if key in self.config_value_changed_dict.keys():
|
if key in self.config_value_changed_dict.keys():
|
||||||
self.config_value_changed_dict[key](value)
|
self.config_value_changed_dict[key](value)
|
||||||
|
|
||||||
def _on_max_connections_global(self, max_connections):
|
def _on_max_connections_global(self, max_connections):
|
||||||
self.max_connections = max_connections
|
self.max_connections_global = max_connections
|
||||||
self.update_connections_label()
|
self.update_connections_label()
|
||||||
|
|
||||||
def _on_dht(self, value):
|
def _on_dht(self, value):
|
||||||
|
@ -345,10 +345,10 @@ class StatusBar(component.Component):
|
||||||
|
|
||||||
def update_connections_label(self):
|
def update_connections_label(self):
|
||||||
# Set the max connections label
|
# Set the max connections label
|
||||||
if self.max_connections < 0:
|
if self.max_connections_global < 0:
|
||||||
label_string = "%s" % self.num_connections
|
label_string = "%s" % self.num_connections
|
||||||
else:
|
else:
|
||||||
label_string = "%s (%s)" % (self.num_connections, self.max_connections)
|
label_string = "%s (%s)" % (self.num_connections, self.max_connections_global)
|
||||||
|
|
||||||
self.connections_item.set_text(label_string)
|
self.connections_item.set_text(label_string)
|
||||||
|
|
||||||
|
@ -377,13 +377,47 @@ class StatusBar(component.Component):
|
||||||
self.upload_item.set_text(label_string)
|
self.upload_item.set_text(label_string)
|
||||||
|
|
||||||
def update_traffic_label(self):
|
def update_traffic_label(self):
|
||||||
label_string = "%.2f/%.2f %s" % (self.download_protocol_rate, self.upload_protocol_rate, _("KiB/s"))
|
label_string = "%i/%i %s" % (self.download_protocol_rate, self.upload_protocol_rate, _("KiB/s"))
|
||||||
self.traffic_item.set_text(label_string)
|
self.traffic_item.set_text(label_string)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
# Send status request
|
# Send status request
|
||||||
self.send_status_request()
|
self.send_status_request()
|
||||||
|
|
||||||
|
def set_limit_value(self, widget, core_key):
|
||||||
|
""" """
|
||||||
|
log.debug("_on_set_unlimit_other %s", core_key)
|
||||||
|
other_dialog_info = {
|
||||||
|
"max_download_speed": (_("Download Speed Limit"), _("Set the maximum download speed"),
|
||||||
|
_("KiB/s"), "downloading.svg", self.max_download_speed),
|
||||||
|
"max_upload_speed": (_("Upload Speed Limit"), _("Set the maximum upload speed"),
|
||||||
|
_("KiB/s"), "seeding.svg", self.max_upload_speed),
|
||||||
|
"max_connections_global": (_("Incoming Connections"), _("Set the maximum incoming connections"),
|
||||||
|
"", gtk.STOCK_NETWORK, self.max_connections_global)
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_value(value):
|
||||||
|
log.debug('value: %s', value)
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
elif value == 0:
|
||||||
|
value = -1
|
||||||
|
# Set the config in the core
|
||||||
|
if value != getattr(self, core_key):
|
||||||
|
client.core.set_config({core_key: value})
|
||||||
|
|
||||||
|
if widget.get_name() == "unlimited":
|
||||||
|
set_value(-1)
|
||||||
|
elif widget.get_name() == "other":
|
||||||
|
def dialog_finished(response_id):
|
||||||
|
if response_id == gtk.RESPONSE_OK:
|
||||||
|
set_value(dialog.get_value())
|
||||||
|
dialog = dialogs.OtherDialog(*other_dialog_info[core_key])
|
||||||
|
dialog.run().addCallback(set_value)
|
||||||
|
else:
|
||||||
|
value = widget.get_children()[0].get_text().split(" ")[0]
|
||||||
|
set_value(value)
|
||||||
|
|
||||||
def _on_download_item_clicked(self, widget, event):
|
def _on_download_item_clicked(self, widget, event):
|
||||||
menu = common.build_menu_radio_list(
|
menu = common.build_menu_radio_list(
|
||||||
self.config["tray_download_speed_list"],
|
self.config["tray_download_speed_list"],
|
||||||
|
@ -395,22 +429,7 @@ class StatusBar(component.Component):
|
||||||
|
|
||||||
def _on_set_download_speed(self, widget):
|
def _on_set_download_speed(self, widget):
|
||||||
log.debug("_on_set_download_speed")
|
log.debug("_on_set_download_speed")
|
||||||
|
self.set_limit_value(widget, "max_download_speed")
|
||||||
if widget.get_name() == "unlimited":
|
|
||||||
value = -1
|
|
||||||
elif widget.get_name() == "other":
|
|
||||||
value = common.show_other_dialog(
|
|
||||||
_("Set Maximum Download Speed"), _("KiB/s"), None, "downloading.svg", self.max_download_speed)
|
|
||||||
if value is None:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
value = float(widget.get_children()[0].get_text().split(" ")[0])
|
|
||||||
|
|
||||||
log.debug("value: %s", value)
|
|
||||||
|
|
||||||
# Set the config in the core
|
|
||||||
if value != self.max_download_speed:
|
|
||||||
client.core.set_config({"max_download_speed": value})
|
|
||||||
|
|
||||||
def _on_upload_item_clicked(self, widget, event):
|
def _on_upload_item_clicked(self, widget, event):
|
||||||
menu = common.build_menu_radio_list(
|
menu = common.build_menu_radio_list(
|
||||||
|
@ -423,49 +442,19 @@ class StatusBar(component.Component):
|
||||||
|
|
||||||
def _on_set_upload_speed(self, widget):
|
def _on_set_upload_speed(self, widget):
|
||||||
log.debug("_on_set_upload_speed")
|
log.debug("_on_set_upload_speed")
|
||||||
|
self.set_limit_value(widget, "max_upload_speed")
|
||||||
if widget.get_name() == "unlimited":
|
|
||||||
value = -1
|
|
||||||
elif widget.get_name() == "other":
|
|
||||||
value = common.show_other_dialog(
|
|
||||||
_("Set Maximum Upload Speed"), _("KiB/s"), None, "seeding.svg", self.max_upload_speed)
|
|
||||||
if value is None:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
value = float(widget.get_children()[0].get_text().split(" ")[0])
|
|
||||||
|
|
||||||
log.debug("value: %s", value)
|
|
||||||
|
|
||||||
# Set the config in the core
|
|
||||||
if value != self.max_upload_speed:
|
|
||||||
client.core.set_config({"max_upload_speed": value})
|
|
||||||
|
|
||||||
def _on_connection_item_clicked(self, widget, event):
|
def _on_connection_item_clicked(self, widget, event):
|
||||||
menu = common.build_menu_radio_list(
|
menu = common.build_menu_radio_list(
|
||||||
self.config["connection_limit_list"],
|
self.config["connection_limit_list"],
|
||||||
self._on_set_connection_limit,
|
self._on_set_connection_limit,
|
||||||
self.max_connections, show_notset=True, show_other=True)
|
self.max_connections_global, show_notset=True, show_other=True)
|
||||||
menu.show_all()
|
menu.show_all()
|
||||||
menu.popup(None, None, None, event.button, event.time)
|
menu.popup(None, None, None, event.button, event.time)
|
||||||
|
|
||||||
def _on_set_connection_limit(self, widget):
|
def _on_set_connection_limit(self, widget):
|
||||||
log.debug("_on_set_connection_limit")
|
log.debug("_on_set_connection_limit")
|
||||||
|
self.set_limit_value(widget, "max_connections_global")
|
||||||
if widget.get_name() == "unlimited":
|
|
||||||
value = -1
|
|
||||||
elif widget.get_name() == "other":
|
|
||||||
value = common.show_other_dialog(
|
|
||||||
_("Set Maximum Connections"), "", gtk.STOCK_NETWORK, None, self.max_connections)
|
|
||||||
if value is None:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
value = int(widget.get_children()[0].get_text().split(" ")[0])
|
|
||||||
|
|
||||||
log.debug("value: %s", value)
|
|
||||||
|
|
||||||
# Set the config in the core
|
|
||||||
if value != self.max_connections:
|
|
||||||
client.core.set_config({"max_connections_global": value})
|
|
||||||
|
|
||||||
def _on_health_icon_clicked(self, widget, event):
|
def _on_health_icon_clicked(self, widget, event):
|
||||||
component.get("Preferences").show("Network")
|
component.get("Preferences").show("Network")
|
||||||
|
|
|
@ -46,10 +46,12 @@ import deluge.component as component
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
import deluge.common
|
import deluge.common
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
|
from deluge.ui.gtkui import dialogs
|
||||||
import common
|
import common
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SystemTray(component.Component):
|
class SystemTray(component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "SystemTray", interval=4)
|
component.Component.__init__(self, "SystemTray", interval=4)
|
||||||
|
@ -102,11 +104,11 @@ class SystemTray(component.Component):
|
||||||
self.tray_menu = self.builder.get_object("tray_menu")
|
self.tray_menu = self.builder.get_object("tray_menu")
|
||||||
|
|
||||||
if appindicator and self.config["enable_appindicator"]:
|
if appindicator and self.config["enable_appindicator"]:
|
||||||
log.debug("Enabling the Application Indicator..")
|
log.debug("Enabling the Application Indicator...")
|
||||||
self.indicator = appindicator.Indicator (
|
self.indicator = appindicator.Indicator("deluge", "deluge",
|
||||||
"deluge", "deluge", appindicator.CATEGORY_APPLICATION_STATUS)
|
appindicator.CATEGORY_APPLICATION_STATUS)
|
||||||
try:
|
try:
|
||||||
self.indicator.set_property ("title", _("Deluge"))
|
self.indicator.set_property("title", _("Deluge"))
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Catch 'title' property error for previous appindicator versions
|
# Catch 'title' property error for previous appindicator versions
|
||||||
pass
|
pass
|
||||||
|
@ -139,9 +141,9 @@ class SystemTray(component.Component):
|
||||||
self.tray.connect("popup-menu", self.on_tray_popup)
|
self.tray.connect("popup-menu", self.on_tray_popup)
|
||||||
|
|
||||||
self.builder.get_object("download-limit-image").set_from_file(
|
self.builder.get_object("download-limit-image").set_from_file(
|
||||||
deluge.common.get_pixmap("downloading16.png"))
|
deluge.common.get_pixmap("downloading16.png"))
|
||||||
self.builder.get_object("upload-limit-image").set_from_file(
|
self.builder.get_object("upload-limit-image").set_from_file(
|
||||||
deluge.common.get_pixmap("seeding16.png"))
|
deluge.common.get_pixmap("seeding16.png"))
|
||||||
|
|
||||||
client.register_event_handler("ConfigValueChangedEvent", self.config_value_changed)
|
client.register_event_handler("ConfigValueChangedEvent", self.config_value_changed)
|
||||||
if client.connected():
|
if client.connected():
|
||||||
|
@ -245,9 +247,10 @@ class SystemTray(component.Component):
|
||||||
else:
|
else:
|
||||||
max_upload_speed = "%s %s" % (max_upload_speed, _("KiB/s"))
|
max_upload_speed = "%s %s" % (max_upload_speed, _("KiB/s"))
|
||||||
|
|
||||||
msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % (\
|
msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % (
|
||||||
_("Deluge"), _("Down"), self.download_rate, \
|
_("Deluge"), _("Down"), self.download_rate,
|
||||||
max_download_speed, _("Up"), self.upload_rate, max_upload_speed)
|
max_download_speed, _("Up"), self.upload_rate, max_upload_speed
|
||||||
|
)
|
||||||
|
|
||||||
# Set the tooltip
|
# Set the tooltip
|
||||||
self.tray.set_tooltip(msg)
|
self.tray.set_tooltip(msg)
|
||||||
|
@ -257,15 +260,17 @@ class SystemTray(component.Component):
|
||||||
def build_tray_bwsetsubmenu(self):
|
def build_tray_bwsetsubmenu(self):
|
||||||
# Create the Download speed list sub-menu
|
# Create the Download speed list sub-menu
|
||||||
submenu_bwdownset = common.build_menu_radio_list(
|
submenu_bwdownset = common.build_menu_radio_list(
|
||||||
self.config["tray_download_speed_list"], self.on_tray_setbwdown,
|
self.config["tray_download_speed_list"], self.on_tray_setbwdown,
|
||||||
self.max_download_speed,
|
self.max_download_speed,
|
||||||
_("KiB/s"), show_notset=True, show_other=True)
|
_("KiB/s"), show_notset=True, show_other=True
|
||||||
|
)
|
||||||
|
|
||||||
# Create the Upload speed list sub-menu
|
# Create the Upload speed list sub-menu
|
||||||
submenu_bwupset = common.build_menu_radio_list(
|
submenu_bwupset = common.build_menu_radio_list(
|
||||||
self.config["tray_upload_speed_list"], self.on_tray_setbwup,
|
self.config["tray_upload_speed_list"], self.on_tray_setbwup,
|
||||||
self.max_upload_speed,
|
self.max_upload_speed,
|
||||||
_("KiB/s"), show_notset=True, show_other=True)
|
_("KiB/s"), show_notset=True, show_other=True
|
||||||
|
)
|
||||||
# Add the sub-menus to the tray menu
|
# Add the sub-menus to the tray menu
|
||||||
self.builder.get_object("menuitem_download_limit").set_submenu(
|
self.builder.get_object("menuitem_download_limit").set_submenu(
|
||||||
submenu_bwdownset)
|
submenu_bwdownset)
|
||||||
|
@ -276,7 +281,7 @@ class SystemTray(component.Component):
|
||||||
submenu_bwdownset.show_all()
|
submenu_bwdownset.show_all()
|
||||||
submenu_bwupset.show_all()
|
submenu_bwupset.show_all()
|
||||||
|
|
||||||
def disable(self,invert_app_ind_conf=False):
|
def disable(self, invert_app_ind_conf=False):
|
||||||
"""Disables the system tray icon or appindicator."""
|
"""Disables the system tray icon or appindicator."""
|
||||||
try:
|
try:
|
||||||
if invert_app_ind_conf:
|
if invert_app_ind_conf:
|
||||||
|
@ -347,8 +352,7 @@ class SystemTray(component.Component):
|
||||||
if deluge.common.windows_check():
|
if deluge.common.windows_check():
|
||||||
popup_function = None
|
popup_function = None
|
||||||
button = 0
|
button = 0
|
||||||
self.tray_menu.popup(None, None, popup_function,
|
self.tray_menu.popup(None, None, popup_function, button, activate_time, status_icon)
|
||||||
button, activate_time, status_icon)
|
|
||||||
|
|
||||||
def on_menuitem_show_deluge_activate(self, menuitem):
|
def on_menuitem_show_deluge_activate(self, menuitem):
|
||||||
log.debug("on_menuitem_show_deluge_activate")
|
log.debug("on_menuitem_show_deluge_activate")
|
||||||
|
@ -391,16 +395,18 @@ class SystemTray(component.Component):
|
||||||
#ignore previous radiomenuitem value
|
#ignore previous radiomenuitem value
|
||||||
if not widget.get_active():
|
if not widget.get_active():
|
||||||
return
|
return
|
||||||
self.setbwlimit(widget, _("Set Maximum Download Speed"), "max_download_speed",
|
self.setbwlimit(widget, _("Download Speed Limit"), _("Set the maximum download speed"),
|
||||||
"tray_download_speed_list", self.max_download_speed, "downloading.svg")
|
"max_download_speed", "tray_download_speed_list", self.max_download_speed,
|
||||||
|
"downloading.svg")
|
||||||
|
|
||||||
def on_tray_setbwup(self, widget, data=None):
|
def on_tray_setbwup(self, widget, data=None):
|
||||||
if isinstance(widget, gtk.RadioMenuItem):
|
if isinstance(widget, gtk.RadioMenuItem):
|
||||||
#ignore previous radiomenuitem value
|
#ignore previous radiomenuitem value
|
||||||
if not widget.get_active():
|
if not widget.get_active():
|
||||||
return
|
return
|
||||||
self.setbwlimit(widget, _("Set Maximum Upload Speed"), "max_upload_speed",
|
self.setbwlimit(widget, _("Upload Speed Limit"), _("Set the maximum upload speed"),
|
||||||
"tray_upload_speed_list", self.max_upload_speed, "seeding.svg")
|
"max_upload_speed", "tray_upload_speed_list", self.max_upload_speed,
|
||||||
|
"seeding.svg")
|
||||||
|
|
||||||
def _on_window_hide(self, widget, data=None):
|
def _on_window_hide(self, widget, data=None):
|
||||||
"""_on_window_hide - update the menuitem's status"""
|
"""_on_window_hide - update the menuitem's status"""
|
||||||
|
@ -412,20 +418,23 @@ class SystemTray(component.Component):
|
||||||
log.debug("_on_window_show")
|
log.debug("_on_window_show")
|
||||||
self.builder.get_object("menuitem_show_deluge").set_active(True)
|
self.builder.get_object("menuitem_show_deluge").set_active(True)
|
||||||
|
|
||||||
def setbwlimit(self, widget, string, core_key, ui_key, default, image):
|
def setbwlimit(self, widget, header, text, core_key, ui_key, default, image):
|
||||||
"""Sets the bandwidth limit based on the user selection."""
|
"""Sets the bandwidth limit based on the user selection."""
|
||||||
value = widget.get_children()[0].get_text().split(" ")[0]
|
def set_value(value):
|
||||||
log.debug('setbwlimit: %s', value)
|
log.debug('setbwlimit: %s', value)
|
||||||
if widget.get_name() == "unlimited":
|
if value is None:
|
||||||
value = -1
|
|
||||||
if widget.get_name() == "other":
|
|
||||||
value = common.show_other_dialog(string, _("KiB/s"), None, image, default)
|
|
||||||
if value == None:
|
|
||||||
return
|
return
|
||||||
elif value == 0:
|
elif value == 0:
|
||||||
value = -1
|
value = -1
|
||||||
# Set the config in the core
|
# Set the config in the core
|
||||||
client.core.set_config({core_key: value})
|
client.core.set_config({core_key: value})
|
||||||
|
if widget.get_name() == "unlimited":
|
||||||
|
set_value(-1)
|
||||||
|
elif widget.get_name() == "other":
|
||||||
|
dialog = dialogs.OtherDialog(header, text, _("KiB/s"), image, default)
|
||||||
|
dialog.run().addCallback(set_value)
|
||||||
|
else:
|
||||||
|
set_value(widget.get_children()[0].get_text().split(" ")[0])
|
||||||
|
|
||||||
def unlock_tray(self, is_showing_dlg=[False]):
|
def unlock_tray(self, is_showing_dlg=[False]):
|
||||||
try:
|
try:
|
||||||
|
@ -445,8 +454,8 @@ class SystemTray(component.Component):
|
||||||
entered_pass.set_visibility(False)
|
entered_pass.set_visibility(False)
|
||||||
|
|
||||||
self.tray_lock = gtk.Dialog(title="", parent=self.window.window,
|
self.tray_lock = gtk.Dialog(title="", parent=self.window.window,
|
||||||
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
|
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||||
gtk.RESPONSE_OK))
|
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||||
self.tray_lock.set_default_response(gtk.RESPONSE_OK)
|
self.tray_lock.set_default_response(gtk.RESPONSE_OK)
|
||||||
self.tray_lock.set_has_separator(False)
|
self.tray_lock.set_has_separator(False)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue