mirror of
https://git.deluge-torrent.org/deluge
synced 2025-09-25 19:58:32 +00:00
Remove trailing whitspace from *.py
This commit is contained in:
parent
7813bd4098
commit
a0bf6d8fe1
62 changed files with 1138 additions and 1131 deletions
|
@ -88,7 +88,7 @@ def encode_bool(x, r):
|
||||||
encode_int(1, r)
|
encode_int(1, r)
|
||||||
else:
|
else:
|
||||||
encode_int(0, r)
|
encode_int(0, r)
|
||||||
|
|
||||||
def encode_string(x, r):
|
def encode_string(x, r):
|
||||||
r.extend((str(len(x)), ':', x))
|
r.extend((str(len(x)), ':', x))
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# component.py
|
# component.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -47,18 +47,18 @@ class Component:
|
||||||
self._interval = interval
|
self._interval = interval
|
||||||
self._timer = None
|
self._timer = None
|
||||||
self._state = COMPONENT_STATE.index("Stopped")
|
self._state = COMPONENT_STATE.index("Stopped")
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _start(self):
|
def _start(self):
|
||||||
self._state = COMPONENT_STATE.index("Started")
|
self._state = COMPONENT_STATE.index("Started")
|
||||||
if self._update():
|
if self._update():
|
||||||
self._timer = gobject.timeout_add(self._interval, self._update)
|
self._timer = gobject.timeout_add(self._interval, self._update)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -68,20 +68,20 @@ class Component:
|
||||||
gobject.source_remove(self._timer)
|
gobject.source_remove(self._timer)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _pause(self):
|
def _pause(self):
|
||||||
self._state = COMPONENT_STATE.index("Paused")
|
self._state = COMPONENT_STATE.index("Paused")
|
||||||
try:
|
try:
|
||||||
gobject.source_remove(self._timer)
|
gobject.source_remove(self._timer)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _resume(self):
|
def _resume(self):
|
||||||
self._start()
|
self._start()
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
try:
|
try:
|
||||||
self.update()
|
self.update()
|
||||||
|
@ -90,29 +90,29 @@ class Component:
|
||||||
# update method.
|
# update method.
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ComponentRegistry:
|
class ComponentRegistry:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.components = {}
|
self.components = {}
|
||||||
self.depend = {}
|
self.depend = {}
|
||||||
|
|
||||||
def register(self, name, obj, depend):
|
def register(self, name, obj, depend):
|
||||||
"""Registers a component.. depend must be list or None"""
|
"""Registers a component.. depend must be list or None"""
|
||||||
log.debug("Registered %s with ComponentRegistry..", name)
|
log.debug("Registered %s with ComponentRegistry..", name)
|
||||||
self.components[name] = obj
|
self.components[name] = obj
|
||||||
if depend != None:
|
if depend != None:
|
||||||
self.depend[name] = depend
|
self.depend[name] = depend
|
||||||
|
|
||||||
def get(self, name):
|
def get(self, name):
|
||||||
"""Returns a reference to the component 'name'"""
|
"""Returns a reference to the component 'name'"""
|
||||||
return self.components[name]
|
return self.components[name]
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Starts all components"""
|
"""Starts all components"""
|
||||||
for component in self.components.keys():
|
for component in self.components.keys():
|
||||||
self.start_component(component)
|
self.start_component(component)
|
||||||
|
|
||||||
def start_component(self, name):
|
def start_component(self, name):
|
||||||
"""Starts a component"""
|
"""Starts a component"""
|
||||||
# Check to see if this component has any dependencies
|
# Check to see if this component has any dependencies
|
||||||
|
@ -125,24 +125,24 @@ class ComponentRegistry:
|
||||||
log.debug("Starting component %s..", name)
|
log.debug("Starting component %s..", name)
|
||||||
self.components[name].start()
|
self.components[name].start()
|
||||||
self.components[name]._start()
|
self.components[name]._start()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stops all components"""
|
"""Stops all components"""
|
||||||
for component in self.components.keys():
|
for component in self.components.keys():
|
||||||
self.stop_component(component)
|
self.stop_component(component)
|
||||||
|
|
||||||
def stop_component(self, component):
|
def stop_component(self, component):
|
||||||
if self.components[component].get_state() != \
|
if self.components[component].get_state() != \
|
||||||
COMPONENT_STATE.index("Stopped"):
|
COMPONENT_STATE.index("Stopped"):
|
||||||
log.debug("Stopping component %s..", component)
|
log.debug("Stopping component %s..", component)
|
||||||
self.components[component].stop()
|
self.components[component].stop()
|
||||||
self.components[component]._stop()
|
self.components[component]._stop()
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
"""Pauses all components. Stops calling update()"""
|
"""Pauses all components. Stops calling update()"""
|
||||||
for component in self.components.keys():
|
for component in self.components.keys():
|
||||||
self.pause_component(component)
|
self.pause_component(component)
|
||||||
|
|
||||||
def pause_component(self, component):
|
def pause_component(self, component):
|
||||||
if self.components[component].get_state() not in \
|
if self.components[component].get_state() not in \
|
||||||
[COMPONENT_STATE.index("Paused"), COMPONENT_STATE.index("Stopped")]:
|
[COMPONENT_STATE.index("Paused"), COMPONENT_STATE.index("Stopped")]:
|
||||||
|
@ -153,11 +153,11 @@ class ComponentRegistry:
|
||||||
"""Resumes all components. Starts calling update()"""
|
"""Resumes all components. Starts calling update()"""
|
||||||
for component in self.components.keys():
|
for component in self.components.keys():
|
||||||
self.resume_component(component)
|
self.resume_component(component)
|
||||||
|
|
||||||
def resume_component(self, component):
|
def resume_component(self, component):
|
||||||
if self.components[component].get_state() == COMPONENT_STATE.index("Paused"):
|
if self.components[component].get_state() == COMPONENT_STATE.index("Paused"):
|
||||||
log.debug("Resuming component %s..", component)
|
log.debug("Resuming component %s..", component)
|
||||||
self.components[component]._resume()
|
self.components[component]._resume()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Updates all components"""
|
"""Updates all components"""
|
||||||
|
@ -166,9 +166,9 @@ class ComponentRegistry:
|
||||||
if self.components[component].get_state() == \
|
if self.components[component].get_state() == \
|
||||||
COMPONENT_STATE.index("Started"):
|
COMPONENT_STATE.index("Started"):
|
||||||
self.components[component].update()
|
self.components[component].update()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""Shuts down all components. This should be called when the program
|
"""Shuts down all components. This should be called when the program
|
||||||
exits so that components can do any necessary clean-up."""
|
exits so that components can do any necessary clean-up."""
|
||||||
|
@ -180,8 +180,8 @@ class ComponentRegistry:
|
||||||
self.components[component].shutdown()
|
self.components[component].shutdown()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("Unable to call shutdown(): %s", e)
|
log.debug("Unable to call shutdown(): %s", e)
|
||||||
|
|
||||||
|
|
||||||
_ComponentRegistry = ComponentRegistry()
|
_ComponentRegistry = ComponentRegistry()
|
||||||
|
|
||||||
def register(name, obj, depend=None):
|
def register(name, obj, depend=None):
|
||||||
|
@ -215,7 +215,7 @@ def resume(component=None):
|
||||||
_ComponentRegistry.resume()
|
_ComponentRegistry.resume()
|
||||||
else:
|
else:
|
||||||
_ComponentRegistry.resume_component(component)
|
_ComponentRegistry.resume_component(component)
|
||||||
|
|
||||||
def update():
|
def update():
|
||||||
"""Updates all components"""
|
"""Updates all components"""
|
||||||
_ComponentRegistry.update()
|
_ComponentRegistry.update()
|
||||||
|
@ -223,7 +223,7 @@ def update():
|
||||||
def shutdown():
|
def shutdown():
|
||||||
"""Shutdowns all components"""
|
"""Shutdowns all components"""
|
||||||
_ComponentRegistry.shutdown()
|
_ComponentRegistry.shutdown()
|
||||||
|
|
||||||
def get(component):
|
def get(component):
|
||||||
"""Return a reference to the component"""
|
"""Return a reference to the component"""
|
||||||
return _ComponentRegistry.get(component)
|
return _ComponentRegistry.get(component)
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# configmanager.py
|
# configmanager.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -62,36 +62,36 @@ class _ConfigManager:
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.warning("Unable to make config directory: %s", e)
|
log.warning("Unable to make config directory: %s", e)
|
||||||
|
|
||||||
self.config_directory = directory
|
self.config_directory = directory
|
||||||
|
|
||||||
def get_config_dir(self):
|
def get_config_dir(self):
|
||||||
log.debug("get_config_dir: %s", self.config_directory)
|
log.debug("get_config_dir: %s", self.config_directory)
|
||||||
return self.config_directory
|
return self.config_directory
|
||||||
|
|
||||||
def close(self, config):
|
def close(self, config):
|
||||||
"""Closes a config file."""
|
"""Closes a config file."""
|
||||||
try:
|
try:
|
||||||
del self.config_files[config]
|
del self.config_files[config]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Saves all the configs to disk."""
|
"""Saves all the configs to disk."""
|
||||||
for key in self.config_files.keys():
|
for key in self.config_files.keys():
|
||||||
self.config_files[key].save()
|
self.config_files[key].save()
|
||||||
# We need to return True to keep the timer active
|
# We need to return True to keep the timer active
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_config(self, config_file, defaults=None):
|
def get_config(self, config_file, defaults=None):
|
||||||
"""Get a reference to the Config object for this filename"""
|
"""Get a reference to the Config object for this filename"""
|
||||||
log.debug("Getting config '%s'", config_file)
|
log.debug("Getting config '%s'", config_file)
|
||||||
# Create the config object if not already created
|
# Create the config object if not already created
|
||||||
if config_file not in self.config_files.keys():
|
if config_file not in self.config_files.keys():
|
||||||
self.config_files[config_file] = Config(config_file, defaults, self.config_directory)
|
self.config_files[config_file] = Config(config_file, defaults, self.config_directory)
|
||||||
|
|
||||||
return self.config_files[config_file]
|
return self.config_files[config_file]
|
||||||
|
|
||||||
# Singleton functions
|
# Singleton functions
|
||||||
_configmanager = _ConfigManager()
|
_configmanager = _ConfigManager()
|
||||||
|
|
||||||
|
@ -107,6 +107,6 @@ def get_config_dir(filename=None):
|
||||||
return os.path.join(_configmanager.get_config_dir(), filename)
|
return os.path.join(_configmanager.get_config_dir(), filename)
|
||||||
else:
|
else:
|
||||||
return _configmanager.get_config_dir()
|
return _configmanager.get_config_dir()
|
||||||
|
|
||||||
def close(config):
|
def close(config):
|
||||||
return _configmanager.close(config)
|
return _configmanager.close(config)
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# alertmanager.py
|
# alertmanager.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -51,17 +51,17 @@ class AlertManager(component.Component):
|
||||||
lt.alert.category_t.tracker_notification |
|
lt.alert.category_t.tracker_notification |
|
||||||
lt.alert.category_t.status_notification |
|
lt.alert.category_t.status_notification |
|
||||||
lt.alert.category_t.ip_block_notification)
|
lt.alert.category_t.ip_block_notification)
|
||||||
|
|
||||||
# handlers is a dictionary of lists {"alert_type": [handler1,h2,..]}
|
# handlers is a dictionary of lists {"alert_type": [handler1,h2,..]}
|
||||||
self.handlers = {}
|
self.handlers = {}
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.handle_alerts()
|
self.handle_alerts()
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
del self.session
|
del self.session
|
||||||
del self.handlers
|
del self.handlers
|
||||||
|
|
||||||
def register_handler(self, alert_type, handler):
|
def register_handler(self, alert_type, handler):
|
||||||
"""Registers a function that will be called when 'alert_type' is pop'd
|
"""Registers a function that will be called when 'alert_type' is pop'd
|
||||||
in handle_alerts. The handler function should look like:
|
in handle_alerts. The handler function should look like:
|
||||||
|
@ -72,11 +72,11 @@ class AlertManager(component.Component):
|
||||||
# There is no entry for this alert type yet, so lets make it with an
|
# There is no entry for this alert type yet, so lets make it with an
|
||||||
# empty list.
|
# empty list.
|
||||||
self.handlers[alert_type] = []
|
self.handlers[alert_type] = []
|
||||||
|
|
||||||
# Append the handler to the list in the handlers dictionary
|
# Append the handler to the list in the handlers dictionary
|
||||||
self.handlers[alert_type].append(handler)
|
self.handlers[alert_type].append(handler)
|
||||||
log.debug("Registered handler for alert %s", alert_type)
|
log.debug("Registered handler for alert %s", alert_type)
|
||||||
|
|
||||||
def deregister_handler(self, handler):
|
def deregister_handler(self, handler):
|
||||||
"""De-registers the 'handler' function from all alert types."""
|
"""De-registers the 'handler' function from all alert types."""
|
||||||
# Iterate through all handlers and remove 'handler' where found
|
# Iterate through all handlers and remove 'handler' where found
|
||||||
|
@ -84,7 +84,7 @@ class AlertManager(component.Component):
|
||||||
if handler in value:
|
if handler in value:
|
||||||
# Handler is in this alert type list
|
# Handler is in this alert type list
|
||||||
value.remove(handler)
|
value.remove(handler)
|
||||||
|
|
||||||
def handle_alerts(self, wait=False):
|
def handle_alerts(self, wait=False):
|
||||||
"""Pops all libtorrent alerts in the session queue and handles them
|
"""Pops all libtorrent alerts in the session queue and handles them
|
||||||
appropriately."""
|
appropriately."""
|
||||||
|
@ -102,7 +102,7 @@ class AlertManager(component.Component):
|
||||||
gobject.idle_add(handler, alert)
|
gobject.idle_add(handler, alert)
|
||||||
else:
|
else:
|
||||||
handler(alert)
|
handler(alert)
|
||||||
|
|
||||||
alert = self.session.pop_alert()
|
alert = self.session.pop_alert()
|
||||||
|
|
||||||
# Return True so that the timer continues
|
# Return True so that the timer continues
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# autoadd.py
|
# autoadd.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -48,7 +48,7 @@ class AutoAdd(component.Component):
|
||||||
component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=5000)
|
component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=5000)
|
||||||
# Get the core config
|
# Get the core config
|
||||||
self.config = ConfigManager("core.conf")
|
self.config = ConfigManager("core.conf")
|
||||||
|
|
||||||
# A list of filenames
|
# A list of filenames
|
||||||
self.invalid_torrents = []
|
self.invalid_torrents = []
|
||||||
# Filename:Attempts
|
# Filename:Attempts
|
||||||
|
@ -59,19 +59,19 @@ class AutoAdd(component.Component):
|
||||||
self._on_autoadd_enable, apply_now=True)
|
self._on_autoadd_enable, apply_now=True)
|
||||||
self.config.register_set_function("autoadd_location",
|
self.config.register_set_function("autoadd_location",
|
||||||
self._on_autoadd_location)
|
self._on_autoadd_location)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
if not self.config["autoadd_enable"]:
|
if not self.config["autoadd_enable"]:
|
||||||
# We shouldn't be updating because autoadd is not enabled
|
# We shouldn't be updating because autoadd is not enabled
|
||||||
component.pause("AutoAdd")
|
component.pause("AutoAdd")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check the auto add folder for new torrents to add
|
# Check the auto add folder for new torrents to add
|
||||||
if not os.path.exists(self.config["autoadd_location"]):
|
if not os.path.exists(self.config["autoadd_location"]):
|
||||||
log.warning("Invalid AutoAdd folder: %s", self.config["autoadd_location"])
|
log.warning("Invalid AutoAdd folder: %s", self.config["autoadd_location"])
|
||||||
component.pause("AutoAdd")
|
component.pause("AutoAdd")
|
||||||
return
|
return
|
||||||
|
|
||||||
for filename in os.listdir(self.config["autoadd_location"]):
|
for filename in os.listdir(self.config["autoadd_location"]):
|
||||||
if filename.split(".")[-1] == "torrent":
|
if filename.split(".")[-1] == "torrent":
|
||||||
filepath = os.path.join(self.config["autoadd_location"], filename)
|
filepath = os.path.join(self.config["autoadd_location"], filename)
|
||||||
|
@ -88,16 +88,16 @@ class AutoAdd(component.Component):
|
||||||
os.rename(filepath, filepath + ".invalid")
|
os.rename(filepath, filepath + ".invalid")
|
||||||
del self.attempts[filename]
|
del self.attempts[filename]
|
||||||
self.invalid_torrents.remove(filename)
|
self.invalid_torrents.remove(filename)
|
||||||
else:
|
else:
|
||||||
self.invalid_torrents.append(filename)
|
self.invalid_torrents.append(filename)
|
||||||
self.attempts[filename] = 1
|
self.attempts[filename] = 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# The torrent looks good, so lets add it to the session
|
# The torrent looks good, so lets add it to the session
|
||||||
component.get("TorrentManager").add(filedump=filedump, filename=filename)
|
component.get("TorrentManager").add(filedump=filedump, filename=filename)
|
||||||
|
|
||||||
os.remove(filepath)
|
os.remove(filepath)
|
||||||
|
|
||||||
def load_torrent(self, filename):
|
def load_torrent(self, filename):
|
||||||
try:
|
try:
|
||||||
log.debug("Attempting to open %s for add.", filename)
|
log.debug("Attempting to open %s for add.", filename)
|
||||||
|
@ -109,10 +109,10 @@ class AutoAdd(component.Component):
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
log.warning("Unable to open %s: %s", filename, e)
|
log.warning("Unable to open %s: %s", filename, e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
# Get the info to see if any exceptions are raised
|
# Get the info to see if any exceptions are raised
|
||||||
info = lt.torrent_info(lt.bdecode(filedump))
|
info = lt.torrent_info(lt.bdecode(filedump))
|
||||||
|
|
||||||
return filedump
|
return filedump
|
||||||
|
|
||||||
def _on_autoadd_enable(self, key, value):
|
def _on_autoadd_enable(self, key, value):
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# daemon.py
|
# daemon.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -39,8 +39,8 @@ class Daemon:
|
||||||
def __init__(self, options, args):
|
def __init__(self, options, args):
|
||||||
version = deluge.common.get_version()
|
version = deluge.common.get_version()
|
||||||
if deluge.common.get_revision() != "":
|
if deluge.common.get_revision() != "":
|
||||||
version = version + "r" + deluge.common.get_revision()
|
version = version + "r" + deluge.common.get_revision()
|
||||||
|
|
||||||
log.info("Deluge daemon %s", version)
|
log.info("Deluge daemon %s", version)
|
||||||
log.debug("options: %s", options)
|
log.debug("options: %s", options)
|
||||||
log.debug("args: %s", args)
|
log.debug("args: %s", args)
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# oldstateupgrader.py
|
# oldstateupgrader.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -73,14 +73,14 @@ class OldStateUpgrader:
|
||||||
if os.path.exists(self.state05_location) and not os.path.exists(self.state10_location):
|
if os.path.exists(self.state05_location) and not os.path.exists(self.state10_location):
|
||||||
# If the 0.5 state file exists and the 1.0 doesn't, then let's upgrade it
|
# If the 0.5 state file exists and the 1.0 doesn't, then let's upgrade it
|
||||||
self.upgrade05()
|
self.upgrade05()
|
||||||
|
|
||||||
def upgrade05(self):
|
def upgrade05(self):
|
||||||
try:
|
try:
|
||||||
state = PickleUpgrader(open(self.state05_location, "rb")).load()
|
state = PickleUpgrader(open(self.state05_location, "rb")).load()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("Unable to open 0.5 state file: %s", e)
|
log.debug("Unable to open 0.5 state file: %s", e)
|
||||||
return
|
return
|
||||||
|
|
||||||
new_state = deluge.core.torrentmanager.TorrentManagerState()
|
new_state = deluge.core.torrentmanager.TorrentManagerState()
|
||||||
for ti, uid in state.torrents.items():
|
for ti, uid in state.torrents.items():
|
||||||
torrent_path = os.path.join(self.config["config_location"], "torrentfiles", ti.filename)
|
torrent_path = os.path.join(self.config["config_location"], "torrentfiles", ti.filename)
|
||||||
|
@ -92,7 +92,7 @@ class OldStateUpgrader:
|
||||||
_file.close()
|
_file.close()
|
||||||
except (IOError, RuntimeError), e:
|
except (IOError, RuntimeError), e:
|
||||||
log.warning("Unable to open %s: %s", filepath, e)
|
log.warning("Unable to open %s: %s", filepath, e)
|
||||||
|
|
||||||
# Copy the torrent file to the new location
|
# Copy the torrent file to the new location
|
||||||
import shutil
|
import shutil
|
||||||
shutil.copyfile(torrent_path, os.path.join(self.config["state_location"], str(torrent_info.info_hash()) + ".torrent"))
|
shutil.copyfile(torrent_path, os.path.join(self.config["state_location"], str(torrent_info.info_hash()) + ".torrent"))
|
||||||
|
@ -116,7 +116,7 @@ class OldStateUpgrader:
|
||||||
)
|
)
|
||||||
# Append the object to the state list
|
# Append the object to the state list
|
||||||
new_state.torrents.append(new_torrent)
|
new_state.torrents.append(new_torrent)
|
||||||
|
|
||||||
# Now we need to write out the new state file
|
# Now we need to write out the new state file
|
||||||
try:
|
try:
|
||||||
log.debug("Saving torrent state file.")
|
log.debug("Saving torrent state file.")
|
||||||
|
@ -127,7 +127,7 @@ class OldStateUpgrader:
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
log.warning("Unable to save state file: %s", e)
|
log.warning("Unable to save state file: %s", e)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Rename the persistent.state file
|
# Rename the persistent.state file
|
||||||
try:
|
try:
|
||||||
os.rename(self.state05_location, self.state05_location + ".old")
|
os.rename(self.state05_location, self.state05_location + ".old")
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# pluginmanager.py
|
# pluginmanager.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -39,11 +39,11 @@ import deluge.pluginmanagerbase
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
|
|
||||||
class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
component.Component):
|
component.Component):
|
||||||
"""PluginManager handles the loading of plugins and provides plugins with
|
"""PluginManager handles the loading of plugins and provides plugins with
|
||||||
functions to access parts of the core."""
|
functions to access parts of the core."""
|
||||||
|
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
component.Component.__init__(self, "PluginManager")
|
component.Component.__init__(self, "PluginManager")
|
||||||
self.core = core
|
self.core = core
|
||||||
|
@ -53,7 +53,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
"post_torrent_remove": [],
|
"post_torrent_remove": [],
|
||||||
"post_session_load": []
|
"post_session_load": []
|
||||||
}
|
}
|
||||||
|
|
||||||
self.status_fields = {}
|
self.status_fields = {}
|
||||||
|
|
||||||
# Call the PluginManagerBase constructor
|
# Call the PluginManagerBase constructor
|
||||||
|
@ -63,19 +63,19 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
def start(self):
|
def start(self):
|
||||||
# Enable plugins that are enabled in the config
|
# Enable plugins that are enabled in the config
|
||||||
self.enable_plugins()
|
self.enable_plugins()
|
||||||
|
|
||||||
# Set update timer to call update() in plugins every second
|
# Set update timer to call update() in plugins every second
|
||||||
self.update_timer = gobject.timeout_add(1000, self.update_plugins)
|
self.update_timer = gobject.timeout_add(1000, self.update_plugins)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
# Disable all enabled plugins
|
# Disable all enabled plugins
|
||||||
self.disable_plugins()
|
self.disable_plugins()
|
||||||
# Stop the update timer
|
# Stop the update timer
|
||||||
gobject.source_remove(self.update_timer)
|
gobject.source_remove(self.update_timer)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def update_plugins(self):
|
def update_plugins(self):
|
||||||
for plugin in self.plugins.keys():
|
for plugin in self.plugins.keys():
|
||||||
try:
|
try:
|
||||||
|
@ -87,13 +87,13 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
def get_core(self):
|
def get_core(self):
|
||||||
"""Returns a reference to the core"""
|
"""Returns a reference to the core"""
|
||||||
return self.core
|
return self.core
|
||||||
|
|
||||||
def register_status_field(self, field, function):
|
def register_status_field(self, field, function):
|
||||||
"""Register a new status field. This can be used in the same way the
|
"""Register a new status field. This can be used in the same way the
|
||||||
client requests other status information from core."""
|
client requests other status information from core."""
|
||||||
log.debug("Registering status field %s with PluginManager", field)
|
log.debug("Registering status field %s with PluginManager", field)
|
||||||
self.status_fields[field] = function
|
self.status_fields[field] = function
|
||||||
|
|
||||||
def deregister_status_field(self, field):
|
def deregister_status_field(self, field):
|
||||||
"""Deregisters a status field"""
|
"""Deregisters a status field"""
|
||||||
log.debug("Deregistering status field %s with PluginManager", field)
|
log.debug("Deregistering status field %s with PluginManager", field)
|
||||||
|
@ -101,7 +101,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
del self.status_fields[field]
|
del self.status_fields[field]
|
||||||
except:
|
except:
|
||||||
log.warning("Unable to deregister status field %s", field)
|
log.warning("Unable to deregister status field %s", field)
|
||||||
|
|
||||||
def get_status(self, torrent_id, fields):
|
def get_status(self, torrent_id, fields):
|
||||||
"""Return the value of status fields for the selected torrent_id."""
|
"""Return the value of status fields for the selected torrent_id."""
|
||||||
status = {}
|
status = {}
|
||||||
|
@ -112,27 +112,27 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
log.warning("Status field %s is not registered with the\
|
log.warning("Status field %s is not registered with the\
|
||||||
PluginManager.", field)
|
PluginManager.", field)
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def register_hook(self, hook, function):
|
def register_hook(self, hook, function):
|
||||||
"""Register a hook function with the plugin manager"""
|
"""Register a hook function with the plugin manager"""
|
||||||
try:
|
try:
|
||||||
self.hooks[hook].append(function)
|
self.hooks[hook].append(function)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning("Plugin attempting to register invalid hook.")
|
log.warning("Plugin attempting to register invalid hook.")
|
||||||
|
|
||||||
def deregister_hook(self, hook, function):
|
def deregister_hook(self, hook, function):
|
||||||
"""Deregisters a hook function"""
|
"""Deregisters a hook function"""
|
||||||
try:
|
try:
|
||||||
self.hooks[hook].remove(function)
|
self.hooks[hook].remove(function)
|
||||||
except:
|
except:
|
||||||
log.warning("Unable to deregister hook %s", hook)
|
log.warning("Unable to deregister hook %s", hook)
|
||||||
|
|
||||||
def run_post_torrent_add(self, torrent_id):
|
def run_post_torrent_add(self, torrent_id):
|
||||||
"""This hook is run after a torrent has been added to the session."""
|
"""This hook is run after a torrent has been added to the session."""
|
||||||
log.debug("run_post_torrent_add")
|
log.debug("run_post_torrent_add")
|
||||||
for function in self.hooks["post_torrent_add"]:
|
for function in self.hooks["post_torrent_add"]:
|
||||||
function(torrent_id)
|
function(torrent_id)
|
||||||
|
|
||||||
def run_post_torrent_remove(self, torrent_id):
|
def run_post_torrent_remove(self, torrent_id):
|
||||||
"""This hook is run after a torrent has been removed from the session.
|
"""This hook is run after a torrent has been removed from the session.
|
||||||
"""
|
"""
|
||||||
|
@ -147,16 +147,16 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
log.debug("run_post_session_load")
|
log.debug("run_post_session_load")
|
||||||
for function in self.hooks["post_session_load"]:
|
for function in self.hooks["post_session_load"]:
|
||||||
function()
|
function()
|
||||||
|
|
||||||
def get_torrent_list(self):
|
def get_torrent_list(self):
|
||||||
"""Returns a list of torrent_id's in the current session."""
|
"""Returns a list of torrent_id's in the current session."""
|
||||||
return component.get("TorrentManager").get_torrent_list()
|
return component.get("TorrentManager").get_torrent_list()
|
||||||
|
|
||||||
def block_ip_range(self, range):
|
def block_ip_range(self, range):
|
||||||
"""Blocks the ip range in the core"""
|
"""Blocks the ip range in the core"""
|
||||||
return self.core.export_block_ip_range(range)
|
return self.core.export_block_ip_range(range)
|
||||||
|
|
||||||
def reset_ip_filter(self):
|
def reset_ip_filter(self):
|
||||||
"""Resets the ip filter"""
|
"""Resets the ip filter"""
|
||||||
return self.core.export_reset_ip_filter()
|
return self.core.export_reset_ip_filter()
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ DEFAULT_PREFS = {
|
||||||
"max_upload_speed": -1.0,
|
"max_upload_speed": -1.0,
|
||||||
"max_download_speed": -1.0,
|
"max_download_speed": -1.0,
|
||||||
"max_upload_slots_global": 4,
|
"max_upload_slots_global": 4,
|
||||||
"max_half_open_connections": (lambda: deluge.common.windows_check() and
|
"max_half_open_connections": (lambda: deluge.common.windows_check() and
|
||||||
(lambda: deluge.common.vista_check() and 4 or 8)() or -1)(),
|
(lambda: deluge.common.vista_check() and 4 or 8)() or -1)(),
|
||||||
"max_connections_per_second": 20,
|
"max_connections_per_second": 20,
|
||||||
"ignore_limits_on_local_network": True,
|
"ignore_limits_on_local_network": True,
|
||||||
|
@ -114,13 +114,13 @@ class PreferencesManager(component.Component):
|
||||||
component.Component.__init__(self, "PreferencesManager")
|
component.Component.__init__(self, "PreferencesManager")
|
||||||
|
|
||||||
self.config = deluge.configmanager.ConfigManager("core.conf", DEFAULT_PREFS)
|
self.config = deluge.configmanager.ConfigManager("core.conf", DEFAULT_PREFS)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.core = component.get("Core")
|
self.core = component.get("Core")
|
||||||
self.session = component.get("Core").session
|
self.session = component.get("Core").session
|
||||||
self.settings = component.get("Core").settings
|
self.settings = component.get("Core").settings
|
||||||
self.signals = component.get("SignalManager")
|
self.signals = component.get("SignalManager")
|
||||||
|
|
||||||
# Register set functions in the Config
|
# Register set functions in the Config
|
||||||
self.config.register_set_function("torrentfiles_location",
|
self.config.register_set_function("torrentfiles_location",
|
||||||
self._on_set_torrentfiles_location)
|
self._on_set_torrentfiles_location)
|
||||||
|
@ -194,7 +194,7 @@ class PreferencesManager(component.Component):
|
||||||
self._on_new_release_check)
|
self._on_new_release_check)
|
||||||
|
|
||||||
self.config.register_change_callback(self._on_config_value_change)
|
self.config.register_change_callback(self._on_config_value_change)
|
||||||
|
|
||||||
# Config set functions
|
# Config set functions
|
||||||
def _on_config_value_change(self, key, value):
|
def _on_config_value_change(self, key, value):
|
||||||
self.signals.emit("config_value_changed", key, value)
|
self.signals.emit("config_value_changed", key, value)
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# rpcserver.py
|
# rpcserver.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -43,14 +43,14 @@ import deluge.configmanager
|
||||||
def export(func):
|
def export(func):
|
||||||
func._rpcserver_export = True
|
func._rpcserver_export = True
|
||||||
return func
|
return func
|
||||||
|
|
||||||
class RPCServer(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer, component.Component):
|
class RPCServer(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer, component.Component):
|
||||||
def __init__(self, port):
|
def __init__(self, port):
|
||||||
component.Component.__init__(self, "RPCServer")
|
component.Component.__init__(self, "RPCServer")
|
||||||
|
|
||||||
# Get config
|
# Get config
|
||||||
self.config = deluge.configmanager.ConfigManager("core.conf")
|
self.config = deluge.configmanager.ConfigManager("core.conf")
|
||||||
|
|
||||||
if port == None:
|
if port == None:
|
||||||
port = self.config["daemon_port"]
|
port = self.config["daemon_port"]
|
||||||
|
|
||||||
|
@ -67,31 +67,31 @@ class RPCServer(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer, component
|
||||||
except:
|
except:
|
||||||
log.info("Daemon already running or port not available..")
|
log.info("Daemon already running or port not available..")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
self.register_multicall_functions()
|
self.register_multicall_functions()
|
||||||
self.register_introspection_functions()
|
self.register_introspection_functions()
|
||||||
|
|
||||||
self.socket.setblocking(False)
|
self.socket.setblocking(False)
|
||||||
|
|
||||||
gobject.io_add_watch(self.socket.fileno(), gobject.IO_IN | gobject.IO_OUT | gobject.IO_PRI | gobject.IO_ERR | gobject.IO_HUP, self._on_socket_activity)
|
gobject.io_add_watch(self.socket.fileno(), gobject.IO_IN | gobject.IO_OUT | gobject.IO_PRI | gobject.IO_ERR | gobject.IO_HUP, self._on_socket_activity)
|
||||||
|
|
||||||
def _on_socket_activity(self, source, condition):
|
def _on_socket_activity(self, source, condition):
|
||||||
"""This gets called when there is activity on the socket, ie, data to read
|
"""This gets called when there is activity on the socket, ie, data to read
|
||||||
or to write."""
|
or to write."""
|
||||||
self.handle_request()
|
self.handle_request()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def register_object(self, obj, name=None):
|
def register_object(self, obj, name=None):
|
||||||
if not name:
|
if not name:
|
||||||
name = obj.__class__.__name__
|
name = obj.__class__.__name__
|
||||||
|
|
||||||
for d in dir(obj):
|
for d in dir(obj):
|
||||||
if d[0] == "_":
|
if d[0] == "_":
|
||||||
continue
|
continue
|
||||||
if getattr(getattr(obj, d), '_rpcserver_export', False):
|
if getattr(getattr(obj, d), '_rpcserver_export', False):
|
||||||
log.debug("Registering method: %s", name + "." + d)
|
log.debug("Registering method: %s", name + "." + d)
|
||||||
self.register_function(getattr(obj, d), name + "." + d)
|
self.register_function(getattr(obj, d), name + "." + d)
|
||||||
|
|
||||||
def get_request(self):
|
def get_request(self):
|
||||||
"""Get the request and client address from the socket.
|
"""Get the request and client address from the socket.
|
||||||
We override this so that we can get the ip address of the client.
|
We override this so that we can get the ip address of the client.
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# signalmanager.py
|
# signalmanager.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -53,7 +53,7 @@ class SignalManager(component.Component):
|
||||||
"""Registers a handler for signals"""
|
"""Registers a handler for signals"""
|
||||||
if signal not in self.handler.keys():
|
if signal not in self.handler.keys():
|
||||||
self.handler[signal] = []
|
self.handler[signal] = []
|
||||||
|
|
||||||
self.handler[signal].append(handler)
|
self.handler[signal].append(handler)
|
||||||
log.debug("Registered signal handler for %s", signal)
|
log.debug("Registered signal handler for %s", signal)
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class SignalManager(component.Component):
|
||||||
for (key, value) in self.handlers:
|
for (key, value) in self.handlers:
|
||||||
if handler in value:
|
if handler in value:
|
||||||
value.remove(handler)
|
value.remove(handler)
|
||||||
|
|
||||||
def deregister_client(self, address):
|
def deregister_client(self, address):
|
||||||
"""Deregisters a client"""
|
"""Deregisters a client"""
|
||||||
log.debug("Deregistering %s as a signal reciever..", address)
|
log.debug("Deregistering %s as a signal reciever..", address)
|
||||||
|
@ -83,7 +83,7 @@ class SignalManager(component.Component):
|
||||||
if signal in self.handlers.keys():
|
if signal in self.handlers.keys():
|
||||||
for handler in self.handlers[signal]:
|
for handler in self.handlers[signal]:
|
||||||
handler(*data)
|
handler(*data)
|
||||||
|
|
||||||
for uri in self.clients:
|
for uri in self.clients:
|
||||||
gobject.idle_add(self._emit, uri, signal, 1, *data)
|
gobject.idle_add(self._emit, uri, signal, 1, *data)
|
||||||
|
|
||||||
|
|
|
@ -397,6 +397,14 @@ class Torrent:
|
||||||
else:
|
else:
|
||||||
status = self.status
|
status = self.status
|
||||||
|
|
||||||
|
if self.is_finished and (self.options["stop_at_ratio"] or self.config["stop_seed_at_ratio"]):
|
||||||
|
# We're a seed, so calculate the time to the 'stop_share_ratio'
|
||||||
|
if not status.upload_payload_rate:
|
||||||
|
return 0
|
||||||
|
stop_ratio = self.config["stop_seed_ratio"] if self.config["stop_at_seed_ratio"] else self.options["stop_seed_ratio"]
|
||||||
|
|
||||||
|
return ((status.all_time_download * stop_ratio) - status.all_time_upload) / status.upload_payload_rate
|
||||||
|
|
||||||
left = status.total_wanted - status.total_done
|
left = status.total_wanted - status.total_done
|
||||||
|
|
||||||
if left <= 0 or status.download_payload_rate == 0:
|
if left <= 0 or status.download_payload_rate == 0:
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# error.py
|
# error.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -36,7 +36,7 @@ class DelugeError(Exception):
|
||||||
self.value = value
|
self.value = value
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.value)
|
return repr(self.value)
|
||||||
|
|
||||||
class NoCoreError(DelugeError):
|
class NoCoreError(DelugeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# log.py
|
# log.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
|
|
@ -71,7 +71,7 @@ def decode_from_filesystem(path):
|
||||||
|
|
||||||
def dummy(v):
|
def dummy(v):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def make_meta_file(path, url, piece_length, progress=dummy,
|
def make_meta_file(path, url, piece_length, progress=dummy,
|
||||||
title=None, comment=None, safe=None, content_type=None,
|
title=None, comment=None, safe=None, content_type=None,
|
||||||
target=None, url_list=None, name=None, private=False,
|
target=None, url_list=None, name=None, private=False,
|
||||||
|
@ -105,7 +105,7 @@ def make_meta_file(path, url, piece_length, progress=dummy,
|
||||||
data['created by'] = created_by
|
data['created by'] = created_by
|
||||||
if httpseeds:
|
if httpseeds:
|
||||||
data['httpseeds'] = httpseeds
|
data['httpseeds'] = httpseeds
|
||||||
|
|
||||||
h.write(bencode(data))
|
h.write(bencode(data))
|
||||||
h.close()
|
h.close()
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ def makeinfo(path, piece_length, progress, name = None,
|
||||||
num_pieces = totalsize / piece_length
|
num_pieces = totalsize / piece_length
|
||||||
else:
|
else:
|
||||||
num_pieces = 1
|
num_pieces = 1
|
||||||
|
|
||||||
for p, f in subs:
|
for p, f in subs:
|
||||||
pos = 0
|
pos = 0
|
||||||
size = os.path.getsize(f)
|
size = os.path.getsize(f)
|
||||||
|
@ -197,7 +197,7 @@ def makeinfo(path, piece_length, progress, name = None,
|
||||||
num_pieces = size / piece_length
|
num_pieces = size / piece_length
|
||||||
else:
|
else:
|
||||||
num_pieces = 1
|
num_pieces = 1
|
||||||
|
|
||||||
pieces = []
|
pieces = []
|
||||||
p = 0
|
p = 0
|
||||||
h = file(path, 'rb')
|
h = file(path, 'rb')
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# gtkui.py
|
# gtkui.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -44,59 +44,59 @@ from core import FORMATS
|
||||||
class GtkUI(ui.UI):
|
class GtkUI(ui.UI):
|
||||||
def enable(self):
|
def enable(self):
|
||||||
log.debug("Blocklist GtkUI enable..")
|
log.debug("Blocklist GtkUI enable..")
|
||||||
|
|
||||||
self.load_preferences_page()
|
self.load_preferences_page()
|
||||||
|
|
||||||
self.status_item = component.get("StatusBar").add_item(
|
self.status_item = component.get("StatusBar").add_item(
|
||||||
image=self.get_resource("blocklist16.png"),
|
image=self.get_resource("blocklist16.png"),
|
||||||
text="",
|
text="",
|
||||||
callback=self._on_status_item_clicked,
|
callback=self._on_status_item_clicked,
|
||||||
tooltip="Blocked IP Ranges")
|
tooltip="Blocked IP Ranges")
|
||||||
|
|
||||||
# Register some hooks
|
# Register some hooks
|
||||||
self.plugin.register_hook("on_apply_prefs", self._on_apply_prefs)
|
self.plugin.register_hook("on_apply_prefs", self._on_apply_prefs)
|
||||||
self.plugin.register_hook("on_show_prefs", self._on_show_prefs)
|
self.plugin.register_hook("on_show_prefs", self._on_show_prefs)
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
log.debug("Blocklist GtkUI disable..")
|
log.debug("Blocklist GtkUI disable..")
|
||||||
|
|
||||||
# Remove the preferences page
|
# Remove the preferences page
|
||||||
self.plugin.remove_preferences_page("Blocklist")
|
self.plugin.remove_preferences_page("Blocklist")
|
||||||
|
|
||||||
# Remove status item
|
# Remove status item
|
||||||
component.get("StatusBar").remove_item(self.status_item)
|
component.get("StatusBar").remove_item(self.status_item)
|
||||||
del self.status_item
|
del self.status_item
|
||||||
|
|
||||||
# Deregister the hooks
|
# Deregister the hooks
|
||||||
self.plugin.deregister_hook("on_apply_prefs", self._on_apply_prefs)
|
self.plugin.deregister_hook("on_apply_prefs", self._on_apply_prefs)
|
||||||
self.plugin.deregister_hook("on_show_prefs", self._on_show_prefs)
|
self.plugin.deregister_hook("on_show_prefs", self._on_show_prefs)
|
||||||
|
|
||||||
del self.glade
|
del self.glade
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
def _on_get_status(status):
|
def _on_get_status(status):
|
||||||
if status["state"] == "Downloading":
|
if status["state"] == "Downloading":
|
||||||
self.table_info.hide()
|
self.table_info.hide()
|
||||||
self.glade.get_widget("button_check_download").set_sensitive(False)
|
self.glade.get_widget("button_check_download").set_sensitive(False)
|
||||||
self.glade.get_widget("button_force_download").set_sensitive(False)
|
self.glade.get_widget("button_force_download").set_sensitive(False)
|
||||||
|
|
||||||
self.status_item.set_text(
|
self.status_item.set_text(
|
||||||
"Downloading %.2f%%" % (status["file_progress"] * 100))
|
"Downloading %.2f%%" % (status["file_progress"] * 100))
|
||||||
self.progress_bar.set_text("Downloading %.2f%%" % (status["file_progress"] * 100))
|
self.progress_bar.set_text("Downloading %.2f%%" % (status["file_progress"] * 100))
|
||||||
self.progress_bar.set_fraction(status["file_progress"])
|
self.progress_bar.set_fraction(status["file_progress"])
|
||||||
self.progress_bar.show()
|
self.progress_bar.show()
|
||||||
|
|
||||||
elif status["state"] == "Importing":
|
elif status["state"] == "Importing":
|
||||||
self.table_info.hide()
|
self.table_info.hide()
|
||||||
self.glade.get_widget("button_check_download").set_sensitive(False)
|
self.glade.get_widget("button_check_download").set_sensitive(False)
|
||||||
self.glade.get_widget("button_force_download").set_sensitive(False)
|
self.glade.get_widget("button_force_download").set_sensitive(False)
|
||||||
|
|
||||||
self.status_item.set_text(
|
self.status_item.set_text(
|
||||||
"Importing " + str(status["num_blocked"]))
|
"Importing " + str(status["num_blocked"]))
|
||||||
self.progress_bar.set_text("Importing %s" % (status["num_blocked"]))
|
self.progress_bar.set_text("Importing %s" % (status["num_blocked"]))
|
||||||
self.progress_bar.pulse()
|
self.progress_bar.pulse()
|
||||||
self.progress_bar.show()
|
self.progress_bar.show()
|
||||||
|
|
||||||
elif status["state"] == "Idle":
|
elif status["state"] == "Idle":
|
||||||
self.progress_bar.hide()
|
self.progress_bar.hide()
|
||||||
self.glade.get_widget("button_check_download").set_sensitive(True)
|
self.glade.get_widget("button_check_download").set_sensitive(True)
|
||||||
|
@ -113,30 +113,30 @@ class GtkUI(ui.UI):
|
||||||
FORMATS[status["file_type"]][0])
|
FORMATS[status["file_type"]][0])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.glade.get_widget("label_type").set_text("")
|
self.glade.get_widget("label_type").set_text("")
|
||||||
|
|
||||||
self.glade.get_widget("label_url").set_text(
|
self.glade.get_widget("label_url").set_text(
|
||||||
status["file_url"])
|
status["file_url"])
|
||||||
|
|
||||||
client.blocklist_get_status(_on_get_status)
|
client.blocklist_get_status(_on_get_status)
|
||||||
|
|
||||||
def _on_show_prefs(self):
|
def _on_show_prefs(self):
|
||||||
def _on_get_config(config):
|
def _on_get_config(config):
|
||||||
# Update the combo box. It's ugly, get over it.
|
# Update the combo box. It's ugly, get over it.
|
||||||
self.glade.get_widget("combobox_types").set_active_iter(
|
self.glade.get_widget("combobox_types").set_active_iter(
|
||||||
self.glade.get_widget("combobox_types").get_model().\
|
self.glade.get_widget("combobox_types").get_model().\
|
||||||
get_iter(FORMATS[config["listtype"]][1]))
|
get_iter(FORMATS[config["listtype"]][1]))
|
||||||
|
|
||||||
self.glade.get_widget("entry_url").set_text(
|
self.glade.get_widget("entry_url").set_text(
|
||||||
config["url"])
|
config["url"])
|
||||||
|
|
||||||
self.glade.get_widget("spin_check_days").set_value(
|
self.glade.get_widget("spin_check_days").set_value(
|
||||||
config["check_after_days"])
|
config["check_after_days"])
|
||||||
|
|
||||||
self.glade.get_widget("chk_import_on_start").set_active(
|
self.glade.get_widget("chk_import_on_start").set_active(
|
||||||
config["load_on_start"])
|
config["load_on_start"])
|
||||||
|
|
||||||
client.blocklist_get_config(_on_get_config)
|
client.blocklist_get_config(_on_get_config)
|
||||||
|
|
||||||
def _on_apply_prefs(self):
|
def _on_apply_prefs(self):
|
||||||
config = {}
|
config = {}
|
||||||
config["listtype"] = self.glade.get_widget("combobox_types").\
|
config["listtype"] = self.glade.get_widget("combobox_types").\
|
||||||
|
@ -145,33 +145,33 @@ class GtkUI(ui.UI):
|
||||||
config["check_after_days"] = self.glade.get_widget("spin_check_days").get_value_as_int()
|
config["check_after_days"] = self.glade.get_widget("spin_check_days").get_value_as_int()
|
||||||
config["load_on_start"] = self.glade.get_widget("chk_import_on_start").get_active()
|
config["load_on_start"] = self.glade.get_widget("chk_import_on_start").get_active()
|
||||||
client.blocklist_set_config(None, config)
|
client.blocklist_set_config(None, config)
|
||||||
|
|
||||||
def _on_button_check_download_clicked(self, widget):
|
def _on_button_check_download_clicked(self, widget):
|
||||||
client.blocklist_import(None, True, False)
|
client.blocklist_import(None, True, False)
|
||||||
|
|
||||||
def _on_button_force_download_clicked(self, widget):
|
def _on_button_force_download_clicked(self, widget):
|
||||||
client.blocklist_import(None, True, True)
|
client.blocklist_import(None, True, True)
|
||||||
|
|
||||||
def _on_status_item_clicked(self, widget, event):
|
def _on_status_item_clicked(self, widget, event):
|
||||||
component.get("Preferences").show("Blocklist")
|
component.get("Preferences").show("Blocklist")
|
||||||
|
|
||||||
def load_preferences_page(self):
|
def load_preferences_page(self):
|
||||||
"""Initializes the preferences page and adds it to the preferences dialog"""
|
"""Initializes the preferences page and adds it to the preferences dialog"""
|
||||||
# Load the preferences page
|
# Load the preferences page
|
||||||
self.glade = gtk.glade.XML(self.get_resource("blocklist_pref.glade"))
|
self.glade = gtk.glade.XML(self.get_resource("blocklist_pref.glade"))
|
||||||
|
|
||||||
self.progress_bar = self.glade.get_widget("progressbar")
|
self.progress_bar = self.glade.get_widget("progressbar")
|
||||||
self.table_info = self.glade.get_widget("table_info")
|
self.table_info = self.glade.get_widget("table_info")
|
||||||
|
|
||||||
# Hide the progress bar initially
|
# Hide the progress bar initially
|
||||||
self.progress_bar.hide()
|
self.progress_bar.hide()
|
||||||
self.table_info.show()
|
self.table_info.show()
|
||||||
|
|
||||||
self.glade.signal_autoconnect({
|
self.glade.signal_autoconnect({
|
||||||
"on_button_check_download_clicked": self._on_button_check_download_clicked,
|
"on_button_check_download_clicked": self._on_button_check_download_clicked,
|
||||||
"on_button_force_download_clicked": self._on_button_force_download_clicked
|
"on_button_force_download_clicked": self._on_button_force_download_clicked
|
||||||
})
|
})
|
||||||
|
|
||||||
# Setup types combobox
|
# Setup types combobox
|
||||||
combo = self.glade.get_widget("combobox_types")
|
combo = self.glade.get_widget("combobox_types")
|
||||||
combo_list = gtk.ListStore(str, str)
|
combo_list = gtk.ListStore(str, str)
|
||||||
|
@ -182,21 +182,21 @@ class GtkUI(ui.UI):
|
||||||
|
|
||||||
for k in FORMATS.keys():
|
for k in FORMATS.keys():
|
||||||
i = combo_list.append([FORMATS[k][0], k])
|
i = combo_list.append([FORMATS[k][0], k])
|
||||||
FORMATS[k][1] = combo_list.get_path(i)
|
FORMATS[k][1] = combo_list.get_path(i)
|
||||||
|
|
||||||
combo.set_active(0)
|
combo.set_active(0)
|
||||||
|
|
||||||
# Set button icons
|
# Set button icons
|
||||||
self.glade.get_widget("image_download").set_from_file(
|
self.glade.get_widget("image_download").set_from_file(
|
||||||
self.get_resource("blocklist_download24.png"))
|
self.get_resource("blocklist_download24.png"))
|
||||||
|
|
||||||
self.glade.get_widget("image_import").set_from_file(
|
self.glade.get_widget("image_import").set_from_file(
|
||||||
self.get_resource("blocklist_import24.png"))
|
self.get_resource("blocklist_import24.png"))
|
||||||
|
|
||||||
# Update the preferences page with config values from the core
|
# Update the preferences page with config values from the core
|
||||||
self._on_show_prefs()
|
self._on_show_prefs()
|
||||||
|
|
||||||
# Add the page to the preferences dialog
|
# Add the page to the preferences dialog
|
||||||
self.plugin.add_preferences_page(
|
self.plugin.add_preferences_page(
|
||||||
"Blocklist",
|
"Blocklist",
|
||||||
self.glade.get_widget("blocklist_prefs_box"))
|
self.glade.get_widget("blocklist_prefs_box"))
|
||||||
|
|
|
@ -19,7 +19,7 @@ class FormatException(TextException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class TextBase:
|
class TextBase:
|
||||||
|
|
||||||
def __init__(self, fd, regexp):
|
def __init__(self, fd, regexp):
|
||||||
log.debug("TextBase loading")
|
log.debug("TextBase loading")
|
||||||
self.count = 0
|
self.count = 0
|
||||||
|
@ -28,7 +28,7 @@ class TextBase:
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
self.count += 1
|
self.count += 1
|
||||||
|
|
||||||
txt = self.fd.readline()
|
txt = self.fd.readline()
|
||||||
if txt == "":
|
if txt == "":
|
||||||
return False
|
return False
|
||||||
|
@ -89,7 +89,7 @@ class GZMuleReader(MuleReader):
|
||||||
log.debug("Wrong file type or corrupted blocklist file.")
|
log.debug("Wrong file type or corrupted blocklist file.")
|
||||||
|
|
||||||
|
|
||||||
# Reads zip files from SafePeer style files
|
# Reads zip files from SafePeer style files
|
||||||
class PGZip(TextBase):
|
class PGZip(TextBase):
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
|
@ -125,7 +125,7 @@ class PGZip(TextBase):
|
||||||
# End of zip
|
# End of zip
|
||||||
return False
|
return False
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
except FormatException, e:
|
except FormatException, e:
|
||||||
log.debug("Blocklist: PGZip: Got format exception for zipfile")
|
log.debug("Blocklist: PGZip: Got format exception for zipfile")
|
||||||
# Just skip
|
# Just skip
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# ui.py
|
# ui.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -40,9 +40,9 @@ class UI:
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_resource(self, filename):
|
def get_resource(self, filename):
|
||||||
return pkg_resources.resource_filename("blocklist", os.path.join("data", filename))
|
return pkg_resources.resource_filename("blocklist", os.path.join("data", filename))
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# core.py
|
# core.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -39,7 +39,7 @@ class CorePluginBase:
|
||||||
# Register all export_* functions
|
# Register all export_* functions
|
||||||
for func in dir(self):
|
for func in dir(self):
|
||||||
if func.startswith("export_"):
|
if func.startswith("export_"):
|
||||||
log.debug("Registering export function %s as %s", func,
|
log.debug("Registering export function %s as %s", func,
|
||||||
plugin_name.lower() + "_" + func[7:])
|
plugin_name.lower() + "_" + func[7:])
|
||||||
self.plugin.get_core().register_function(
|
self.plugin.get_core().register_function(
|
||||||
getattr(self, "%s" % func), plugin_name.lower()\
|
getattr(self, "%s" % func), plugin_name.lower()\
|
||||||
|
|
|
@ -4,19 +4,19 @@
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
# Copyright (C) 2008 Mark Stahler ('kramed') <markstahler@gmail.com>
|
# Copyright (C) 2008 Mark Stahler ('kramed') <markstahler@gmail.com>
|
||||||
|
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -46,8 +46,7 @@ class UI:
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,10 +77,10 @@ class Graph:
|
||||||
self.interval = 2000 # 2 secs
|
self.interval = 2000 # 2 secs
|
||||||
self.text_bg = (255, 255 , 255, 128) # prototyping
|
self.text_bg = (255, 255 , 255, 128) # prototyping
|
||||||
self.set_left_axis()
|
self.set_left_axis()
|
||||||
|
|
||||||
def set_left_axis(self, **kargs):
|
def set_left_axis(self, **kargs):
|
||||||
self.left_axis = kargs
|
self.left_axis = kargs
|
||||||
|
|
||||||
def add_stat(self, stat, label='', axis='left', line=True, fill=True, color=None):
|
def add_stat(self, stat, label='', axis='left', line=True, fill=True, color=None):
|
||||||
self.stat_info[stat] = {
|
self.stat_info[stat] = {
|
||||||
'axis': axis,
|
'axis': axis,
|
||||||
|
@ -89,18 +89,18 @@ class Graph:
|
||||||
'fill': fill,
|
'fill': fill,
|
||||||
'color': color
|
'color': color
|
||||||
}
|
}
|
||||||
|
|
||||||
def async_request(self):
|
def async_request(self):
|
||||||
aclient.stats_get_stats(self.set_stats, self.stat_info.keys())
|
aclient.stats_get_stats(self.set_stats, self.stat_info.keys())
|
||||||
aclient.stats_get_config(self.set_config)
|
aclient.stats_get_config(self.set_config)
|
||||||
|
|
||||||
def set_stats(self, stats):
|
def set_stats(self, stats):
|
||||||
self.stats = stats
|
self.stats = stats
|
||||||
|
|
||||||
def set_config(self, config):
|
def set_config(self, config):
|
||||||
self.length = config["length"]
|
self.length = config["length"]
|
||||||
self.interval = config["update_interval"]
|
self.interval = config["update_interval"]
|
||||||
|
|
||||||
def draw_to_context(self, context, width, height):
|
def draw_to_context(self, context, width, height):
|
||||||
self.ctx = context
|
self.ctx = context
|
||||||
self.width, self.height = width, height
|
self.width, self.height = width, height
|
||||||
|
@ -111,11 +111,11 @@ class Graph:
|
||||||
if self.legend_selected:
|
if self.legend_selected:
|
||||||
self.draw_legend()
|
self.draw_legend()
|
||||||
return self.ctx
|
return self.ctx
|
||||||
|
|
||||||
def draw(self, width, height):
|
def draw(self, width, height):
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
|
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
|
||||||
self.ctx = cairo.Context(self.surface)
|
self.ctx = cairo.Context(self.surface)
|
||||||
self.draw_rect(white, 0, 0, self.width, self.height)
|
self.draw_rect(white, 0, 0, self.width, self.height)
|
||||||
|
@ -125,24 +125,24 @@ class Graph:
|
||||||
if self.legend_selected:
|
if self.legend_selected:
|
||||||
self.draw_legend()
|
self.draw_legend()
|
||||||
return self.surface
|
return self.surface
|
||||||
|
|
||||||
def draw_x_axis(self):
|
def draw_x_axis(self):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
duration = self.length * (self.interval / 1000.0)
|
duration = self.length * (self.interval / 1000.0)
|
||||||
start = now - duration
|
start = now - duration
|
||||||
ratio = (self.width - 40) / duration
|
ratio = (self.width - 40) / duration
|
||||||
seconds_to_minute = 60 - time.localtime(start)[5]
|
seconds_to_minute = 60 - time.localtime(start)[5]
|
||||||
|
|
||||||
for i in xrange(0, 5):
|
for i in xrange(0, 5):
|
||||||
text = time.strftime('%H:%M', time.localtime(start + seconds_to_minute + (60*i)))
|
text = time.strftime('%H:%M', time.localtime(start + seconds_to_minute + (60*i)))
|
||||||
x = int(ratio * (seconds_to_minute + (60*i)))
|
x = int(ratio * (seconds_to_minute + (60*i)))
|
||||||
self.draw_text(text, x + 46, self.height - 20)
|
self.draw_text(text, x + 46, self.height - 20)
|
||||||
x = x + 59.5
|
x = x + 59.5
|
||||||
self.draw_dotted_line(gray, x, 20, x, self.height - 20)
|
self.draw_dotted_line(gray, x, 20, x, self.height - 20)
|
||||||
|
|
||||||
y = self.height - 22.5
|
y = self.height - 22.5
|
||||||
self.draw_dotted_line(gray, 60, y, int(self.width), y)
|
self.draw_dotted_line(gray, 60, y, int(self.width), y)
|
||||||
|
|
||||||
def draw_left_axis(self):
|
def draw_left_axis(self):
|
||||||
stats = {}
|
stats = {}
|
||||||
max_values = []
|
max_values = []
|
||||||
|
@ -158,15 +158,15 @@ class Graph:
|
||||||
max_value = max(*max_values)
|
max_value = max(*max_values)
|
||||||
else:
|
else:
|
||||||
max_value = max_values[0]
|
max_value = max_values[0]
|
||||||
|
|
||||||
if max_value < self.left_axis['min']:
|
if max_value < self.left_axis['min']:
|
||||||
max_value = self.left_axis['min']
|
max_value = self.left_axis['min']
|
||||||
|
|
||||||
height = self.height - self.line_size - 22
|
height = self.height - self.line_size - 22
|
||||||
#max_value = float(round(max_value, len(str(max_value)) * -1))
|
#max_value = float(round(max_value, len(str(max_value)) * -1))
|
||||||
max_value = float(max_value)
|
max_value = float(max_value)
|
||||||
ratio = height / max_value
|
ratio = height / max_value
|
||||||
|
|
||||||
for i in xrange(1, 6):
|
for i in xrange(1, 6):
|
||||||
y = int(ratio * ((max_value / 5) * i)) - 0.5
|
y = int(ratio * ((max_value / 5) * i)) - 0.5
|
||||||
if i < 5:
|
if i < 5:
|
||||||
|
@ -174,19 +174,19 @@ class Graph:
|
||||||
text = self.left_axis['formatter']((max_value / 5) * (5 - i))
|
text = self.left_axis['formatter']((max_value / 5) * (5 - i))
|
||||||
self.draw_text(text, 0, y - 6)
|
self.draw_text(text, 0, y - 6)
|
||||||
self.draw_dotted_line(gray, 60.5, 20, 60.5, self.height - 20)
|
self.draw_dotted_line(gray, 60.5, 20, 60.5, self.height - 20)
|
||||||
|
|
||||||
for stat, info in stats.iteritems():
|
for stat, info in stats.iteritems():
|
||||||
self.draw_value_poly(info['values'], info['color'], max_value)
|
self.draw_value_poly(info['values'], info['color'], max_value)
|
||||||
self.draw_value_poly(info['values'], info['fill_color'], max_value, info['fill'])
|
self.draw_value_poly(info['values'], info['fill_color'], max_value, info['fill'])
|
||||||
|
|
||||||
def draw_legend(self):
|
def draw_legend(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def trace_path(self, values, max_value):
|
def trace_path(self, values, max_value):
|
||||||
height = self.height - 24
|
height = self.height - 24
|
||||||
width = self.width
|
width = self.width
|
||||||
line_width = self.line_size
|
line_width = self.line_size
|
||||||
|
|
||||||
self.ctx.set_line_width(line_width)
|
self.ctx.set_line_width(line_width)
|
||||||
self.ctx.move_to(width, height)
|
self.ctx.move_to(width, height)
|
||||||
|
|
||||||
|
@ -198,43 +198,43 @@ class Graph:
|
||||||
for i, value in enumerate(values):
|
for i, value in enumerate(values):
|
||||||
if i == self.length - 1:
|
if i == self.length - 1:
|
||||||
x = 62
|
x = 62
|
||||||
self.ctx.line_to(x,
|
self.ctx.line_to(x,
|
||||||
int(height - 1 - ((height - 28) * value / max_value))
|
int(height - 1 - ((height - 28) * value / max_value))
|
||||||
)
|
)
|
||||||
x -= step
|
x -= step
|
||||||
|
|
||||||
self.ctx.line_to(
|
self.ctx.line_to(
|
||||||
int(width + 62 - (((len(values) - 1) * width) / (self.length - 1))),
|
int(width + 62 - (((len(values) - 1) * width) / (self.length - 1))),
|
||||||
height)
|
height)
|
||||||
self.ctx.close_path()
|
self.ctx.close_path()
|
||||||
|
|
||||||
def draw_value_poly(self, values, color, max_value, fill=False):
|
def draw_value_poly(self, values, color, max_value, fill=False):
|
||||||
self.trace_path(values, max_value)
|
self.trace_path(values, max_value)
|
||||||
self.ctx.set_source_rgba(*color)
|
self.ctx.set_source_rgba(*color)
|
||||||
|
|
||||||
if fill:
|
if fill:
|
||||||
self.ctx.fill()
|
self.ctx.fill()
|
||||||
else:
|
else:
|
||||||
self.ctx.stroke()
|
self.ctx.stroke()
|
||||||
|
|
||||||
def draw_text(self, text, x, y):
|
def draw_text(self, text, x, y):
|
||||||
self.ctx.set_font_size(9)
|
self.ctx.set_font_size(9)
|
||||||
self.ctx.move_to(x, y + 9)
|
self.ctx.move_to(x, y + 9)
|
||||||
self.ctx.set_source_rgba(*self.black)
|
self.ctx.set_source_rgba(*self.black)
|
||||||
self.ctx.show_text(text)
|
self.ctx.show_text(text)
|
||||||
|
|
||||||
def draw_rect(self, color, x, y, height, width):
|
def draw_rect(self, color, x, y, height, width):
|
||||||
self.ctx.set_source_rgba(*color)
|
self.ctx.set_source_rgba(*color)
|
||||||
self.ctx.rectangle(x, y, height, width)
|
self.ctx.rectangle(x, y, height, width)
|
||||||
self.ctx.fill()
|
self.ctx.fill()
|
||||||
|
|
||||||
def draw_line(self, color, x1, y1, x2, y2):
|
def draw_line(self, color, x1, y1, x2, y2):
|
||||||
self.ctx.set_source_rgba(*color)
|
self.ctx.set_source_rgba(*color)
|
||||||
self.ctx.set_line_width(1)
|
self.ctx.set_line_width(1)
|
||||||
self.ctx.move_to(x1, y1)
|
self.ctx.move_to(x1, y1)
|
||||||
self.ctx.line_to(x2, y2)
|
self.ctx.line_to(x2, y2)
|
||||||
self.ctx.stroke()
|
self.ctx.stroke()
|
||||||
|
|
||||||
def draw_dotted_line(self, color, x1, y1, x2, y2):
|
def draw_dotted_line(self, color, x1, y1, x2, y2):
|
||||||
self.ctx.set_source_rgba(*color)
|
self.ctx.set_source_rgba(*color)
|
||||||
self.ctx.set_line_width(1)
|
self.ctx.set_line_width(1)
|
||||||
|
|
|
@ -57,7 +57,7 @@ class GraphsTab(Tab):
|
||||||
self.bandwidth_graph.connect('expose_event', self.bandwidth_expose)
|
self.bandwidth_graph.connect('expose_event', self.bandwidth_expose)
|
||||||
self.window.unparent()
|
self.window.unparent()
|
||||||
self.label.unparent()
|
self.label.unparent()
|
||||||
|
|
||||||
def bandwidth_expose(self, widget, event):
|
def bandwidth_expose(self, widget, event):
|
||||||
self.graph_widget = self.bandwidth_graph
|
self.graph_widget = self.bandwidth_graph
|
||||||
self.graph = graph.Graph()
|
self.graph = graph.Graph()
|
||||||
|
@ -66,10 +66,10 @@ class GraphsTab(Tab):
|
||||||
self.graph.set_left_axis(formatter=fspeed, min=10240)
|
self.graph.set_left_axis(formatter=fspeed, min=10240)
|
||||||
self.update_timer = gobject.timeout_add(2000, self.update_graph)
|
self.update_timer = gobject.timeout_add(2000, self.update_graph)
|
||||||
self.update_graph()
|
self.update_graph()
|
||||||
|
|
||||||
def update_graph(self):
|
def update_graph(self):
|
||||||
width, height = self.graph_widget.allocation.width, self.graph_widget.allocation.height
|
width, height = self.graph_widget.allocation.width, self.graph_widget.allocation.height
|
||||||
context = self.graph_widget.window.cairo_create()
|
context = self.graph_widget.window.cairo_create()
|
||||||
self.graph.async_request()
|
self.graph.async_request()
|
||||||
aclient.force_call(True)
|
aclient.force_call(True)
|
||||||
self.graph.draw_to_context(context, width, height)
|
self.graph.draw_to_context(context, width, height)
|
||||||
|
@ -86,7 +86,7 @@ class GtkUI(object):
|
||||||
self.plugin.register_hook("on_apply_prefs", self.on_apply_prefs)
|
self.plugin.register_hook("on_apply_prefs", self.on_apply_prefs)
|
||||||
self.plugin.register_hook("on_show_prefs", self.on_show_prefs)
|
self.plugin.register_hook("on_show_prefs", self.on_show_prefs)
|
||||||
self.on_show_prefs()
|
self.on_show_prefs()
|
||||||
|
|
||||||
self.graphs_tab = GraphsTab(XML(self.get_resource("tabs.glade")))
|
self.graphs_tab = GraphsTab(XML(self.get_resource("tabs.glade")))
|
||||||
self.torrent_details = component.get('TorrentDetails')
|
self.torrent_details = component.get('TorrentDetails')
|
||||||
self.torrent_details.notebook.append_page(self.graphs_tab.window, self.graphs_tab.label)
|
self.torrent_details.notebook.append_page(self.graphs_tab.window, self.graphs_tab.label)
|
||||||
|
|
|
@ -159,7 +159,7 @@ class BaseClient(object):
|
||||||
"""
|
"""
|
||||||
no_callback_list = ["add_torrent_url", "pause_all_torrents",
|
no_callback_list = ["add_torrent_url", "pause_all_torrents",
|
||||||
"resume_all_torrents", "set_config", "enable_plugin",
|
"resume_all_torrents", "set_config", "enable_plugin",
|
||||||
"disable_plugin", "set_torrent_trackers", "connect_peer",
|
"disable_plugin", "set_torrent_trackers", "connect_peer",
|
||||||
"set_torrent_max_connections", "set_torrent_max_upload_slots",
|
"set_torrent_max_connections", "set_torrent_max_upload_slots",
|
||||||
"set_torrent_max_upload_speed", "set_torrent_max_download_speed",
|
"set_torrent_max_upload_speed", "set_torrent_max_download_speed",
|
||||||
"set_torrent_private_flag", "set_torrent_file_priorities",
|
"set_torrent_private_flag", "set_torrent_file_priorities",
|
||||||
|
@ -169,7 +169,7 @@ class BaseClient(object):
|
||||||
"set_torrent_prioritize_first_last", "set_torrent_auto_managed",
|
"set_torrent_prioritize_first_last", "set_torrent_auto_managed",
|
||||||
"set_torrent_stop_ratio", "set_torrent_stop_at_ratio",
|
"set_torrent_stop_ratio", "set_torrent_stop_at_ratio",
|
||||||
"set_torrent_remove_at_ratio", "set_torrent_move_on_completed",
|
"set_torrent_remove_at_ratio", "set_torrent_move_on_completed",
|
||||||
"set_torrent_move_on_completed_path", "add_torrent_magnets",
|
"set_torrent_move_on_completed_path", "add_torrent_magnets",
|
||||||
"create_torrent", "upload_plugin", "rescan_plugins", "rename_files",
|
"create_torrent", "upload_plugin", "rescan_plugins", "rename_files",
|
||||||
"rename_folder"]
|
"rename_folder"]
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ default_style = {
|
||||||
'magenta' : make_style(fg='magenta'),
|
'magenta' : make_style(fg='magenta'),
|
||||||
'cyan' : make_style(fg='cyan'),
|
'cyan' : make_style(fg='cyan'),
|
||||||
'white' : make_style(fg='white'),
|
'white' : make_style(fg='white'),
|
||||||
|
|
||||||
'bold_black' : make_style(fg='black', attrs='bright'),
|
'bold_black' : make_style(fg='black', attrs='bright'),
|
||||||
'bold_red' : make_style(fg='red', attrs='bright'),
|
'bold_red' : make_style(fg='red', attrs='bright'),
|
||||||
'bold_green' : make_style(fg='green', attrs='bright'),
|
'bold_green' : make_style(fg='green', attrs='bright'),
|
||||||
|
|
|
@ -9,7 +9,7 @@ import re
|
||||||
import cStringIO, tokenize
|
import cStringIO, tokenize
|
||||||
|
|
||||||
def atom(next, token):
|
def atom(next, token):
|
||||||
"""taken with slight modifications from http://effbot.org/zone/simple-iterator-parser.htm"""
|
"""taken with slight modifications from http://effbot.org/zone/simple-iterator-parser.htm"""
|
||||||
if token[1] == "(":
|
if token[1] == "(":
|
||||||
out = []
|
out = []
|
||||||
token = next()
|
token = next()
|
||||||
|
@ -34,7 +34,7 @@ def atom(next, token):
|
||||||
|
|
||||||
def simple_eval(source):
|
def simple_eval(source):
|
||||||
""" evaluates the 'source' string into a combination of primitive python objects
|
""" evaluates the 'source' string into a combination of primitive python objects
|
||||||
taken from http://effbot.org/zone/simple-iterator-parser.htm"""
|
taken from http://effbot.org/zone/simple-iterator-parser.htm"""
|
||||||
src = cStringIO.StringIO(source).readline
|
src = cStringIO.StringIO(source).readline
|
||||||
src = tokenize.generate_tokens(src)
|
src = tokenize.generate_tokens(src)
|
||||||
src = (token for token in src if token[0] is not tokenize.NL)
|
src = (token for token in src if token[0] is not tokenize.NL)
|
||||||
|
@ -46,11 +46,11 @@ def simple_eval(source):
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Show and set configuration values"""
|
"""Show and set configuration values"""
|
||||||
|
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('-s', '--set', action='store_true', default=False, dest='set',
|
make_option('-s', '--set', action='store_true', default=False, dest='set',
|
||||||
help='set value for key'),
|
help='set value for key'),
|
||||||
)
|
)
|
||||||
usage = "Usage: config [key1 [key2 ...]]\n"\
|
usage = "Usage: config [key1 [key2 ...]]\n"\
|
||||||
" config --set key value"
|
" config --set key value"
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ class Command(BaseCommand):
|
||||||
color = 'cyan'
|
color = 'cyan'
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
color = 'magenta'
|
color = 'magenta'
|
||||||
|
|
||||||
print templates.config_display(key, style[color](str(value)))
|
print templates.config_display(key, style[color](str(value)))
|
||||||
client.get_config(_on_get_config)
|
client.get_config(_on_get_config)
|
||||||
|
|
||||||
|
@ -113,4 +113,4 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def split(self, text):
|
def split(self, text):
|
||||||
return str.split(text)
|
return str.split(text)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Command(BaseCommand):
|
||||||
# get a list of commands, exclude 'help' so we won't run into a recursive loop.
|
# get a list of commands, exclude 'help' so we won't run into a recursive loop.
|
||||||
self._commands = load_commands(os.path.join(UI_PATH,'commands'), exclude=['help'])
|
self._commands = load_commands(os.path.join(UI_PATH,'commands'), exclude=['help'])
|
||||||
self._commands['help'] = self
|
self._commands['help'] = self
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
if args:
|
if args:
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
|
@ -24,7 +24,7 @@ class Command(BaseCommand):
|
||||||
cmd = self._commands[args[0]]
|
cmd = self._commands[args[0]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print templates.ERROR('unknown command %r' % args[0])
|
print templates.ERROR('unknown command %r' % args[0])
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
parser = cmd.create_parser()
|
parser = cmd.create_parser()
|
||||||
print parser.format_help()
|
print parser.format_help()
|
||||||
|
@ -34,8 +34,8 @@ class Command(BaseCommand):
|
||||||
max_length = max( len(k) for k in self._commands)
|
max_length = max( len(k) for k in self._commands)
|
||||||
for cmd in sorted(self._commands):
|
for cmd in sorted(self._commands):
|
||||||
print templates.help(max_length, cmd, self._commands[cmd].__doc__ or '')
|
print templates.help(max_length, cmd, self._commands[cmd].__doc__ or '')
|
||||||
print
|
print
|
||||||
print 'for help on a specific command, use "<command> --help"'
|
print 'for help on a specific command, use "<command> --help"'
|
||||||
|
|
||||||
def complete(self, text, *args):
|
def complete(self, text, *args):
|
||||||
return [ x for x in self._commands.keys() if x.startswith(text) ]
|
return [ x for x in self._commands.keys() if x.startswith(text) ]
|
||||||
|
|
|
@ -47,18 +47,18 @@ class Command(BaseCommand):
|
||||||
usage = "Usage: info [<torrent-id> [<torrent-id> ...]]\n"\
|
usage = "Usage: info [<torrent-id> [<torrent-id> ...]]\n"\
|
||||||
" You can give the first few characters of a torrent-id to identify the torrent."
|
" You can give the first few characters of a torrent-id to identify the torrent."
|
||||||
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
args = mapping.to_ids(args)
|
args = mapping.to_ids(args)
|
||||||
self.torrents = match_torrents(args)
|
self.torrents = match_torrents(args)
|
||||||
for tor in self.torrents:
|
for tor in self.torrents:
|
||||||
self.show_info(tor, options.get('verbose'))
|
self.show_info(tor, options.get('verbose'))
|
||||||
|
|
||||||
def complete(self, text, *args):
|
def complete(self, text, *args):
|
||||||
torrents = match_torrents()
|
torrents = match_torrents()
|
||||||
names = mapping.get_names(torrents)
|
names = mapping.get_names(torrents)
|
||||||
return [ x[1] for x in names if x[1].startswith(text) ]
|
return [ x[1] for x in names if x[1].startswith(text) ]
|
||||||
|
|
||||||
def show_info(self, torrent, verbose):
|
def show_info(self, torrent, verbose):
|
||||||
def _got_torrent_status(state):
|
def _got_torrent_status(state):
|
||||||
print templates.info_general('ID', torrent)
|
print templates.info_general('ID', torrent)
|
||||||
|
@ -98,7 +98,7 @@ class Command(BaseCommand):
|
||||||
for peer in state['peers']:
|
for peer in state['peers']:
|
||||||
client_str = unicode(peer['client'])
|
client_str = unicode(peer['client'])
|
||||||
client_str += unicode(peer['seed']) if peer['seed'] else ''
|
client_str += unicode(peer['seed']) if peer['seed'] else ''
|
||||||
print templates.info_peers(str(peer['ip']), unicode(client_str),
|
print templates.info_peers(str(peer['ip']), unicode(client_str),
|
||||||
str(common.fspeed(peer['up_speed'])), str(common.fspeed(peer['down_speed'])))
|
str(common.fspeed(peer['up_speed'])), str(common.fspeed(peer['down_speed'])))
|
||||||
print ""
|
print ""
|
||||||
client.get_torrent_status(_got_torrent_status, torrent, status_keys)
|
client.get_torrent_status(_got_torrent_status, torrent, status_keys)
|
||||||
|
|
|
@ -26,4 +26,4 @@ class Command(BaseCommand):
|
||||||
torrents = match_torrents()
|
torrents = match_torrents()
|
||||||
names = mapping.get_names(torrents)
|
names = mapping.get_names(torrents)
|
||||||
return [ x[1] for x in names if x[1].startswith(text) ]
|
return [ x[1] for x in names if x[1].startswith(text) ]
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Command(BaseCommand):
|
||||||
"""Remove a torrent"""
|
"""Remove a torrent"""
|
||||||
usage = "Usage: rm <torrent-id>"
|
usage = "Usage: rm <torrent-id>"
|
||||||
aliases = ['del']
|
aliases = ['del']
|
||||||
|
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('--remove_torrent', action='store_true', default=False,
|
make_option('--remove_torrent', action='store_true', default=False,
|
||||||
help="remove the torrent's file"),
|
help="remove the torrent's file"),
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,19 +2,19 @@
|
||||||
# coreconfig.py
|
# coreconfig.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -42,22 +42,22 @@ class CoreConfig(component.Component):
|
||||||
self.config = {}
|
self.config = {}
|
||||||
component.get("Signals").connect_to_signal("config_value_changed",
|
component.get("Signals").connect_to_signal("config_value_changed",
|
||||||
self._on_config_value_changed)
|
self._on_config_value_changed)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
client.get_config(self._on_get_config)
|
client.get_config(self._on_get_config)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.config = {}
|
self.config = {}
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return self.config[key]
|
return self.config[key]
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
client.set_config({key: value})
|
client.set_config({key: value})
|
||||||
|
|
||||||
def _on_get_config(self, config):
|
def _on_get_config(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def _on_config_value_changed(self, key, value):
|
def _on_config_value_changed(self, key, value):
|
||||||
self.config[key] = value
|
self.config[key] = value
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# createtorrentdialog.py
|
# createtorrentdialog.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -47,11 +47,11 @@ class CreateTorrentDialog:
|
||||||
def show(self):
|
def show(self):
|
||||||
self.glade = gtk.glade.XML(
|
self.glade = gtk.glade.XML(
|
||||||
pkg_resources.resource_filename(
|
pkg_resources.resource_filename(
|
||||||
"deluge.ui.gtkui",
|
"deluge.ui.gtkui",
|
||||||
"glade/create_torrent_dialog.glade"))
|
"glade/create_torrent_dialog.glade"))
|
||||||
|
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
|
|
||||||
self.dialog = self.glade.get_widget("create_torrent_dialog")
|
self.dialog = self.glade.get_widget("create_torrent_dialog")
|
||||||
self.dialog.set_transient_for(component.get("MainWindow").window)
|
self.dialog.set_transient_for(component.get("MainWindow").window)
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class CreateTorrentDialog:
|
||||||
"on_button_remove_clicked": self._on_button_remove_clicked,
|
"on_button_remove_clicked": self._on_button_remove_clicked,
|
||||||
"on_button_down_clicked": self._on_button_down_clicked
|
"on_button_down_clicked": self._on_button_down_clicked
|
||||||
})
|
})
|
||||||
|
|
||||||
# path, icon, size
|
# path, icon, size
|
||||||
self.files_treestore = gtk.TreeStore(str, str, gobject.TYPE_UINT64)
|
self.files_treestore = gtk.TreeStore(str, str, gobject.TYPE_UINT64)
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ class CreateTorrentDialog:
|
||||||
column.pack_start(render)
|
column.pack_start(render)
|
||||||
column.set_cell_data_func(render, listview.cell_data_size, 2)
|
column.set_cell_data_func(render, listview.cell_data_size, 2)
|
||||||
self.glade.get_widget("treeview_files").append_column(column)
|
self.glade.get_widget("treeview_files").append_column(column)
|
||||||
|
|
||||||
self.glade.get_widget("treeview_files").set_model(self.files_treestore)
|
self.glade.get_widget("treeview_files").set_model(self.files_treestore)
|
||||||
self.glade.get_widget("treeview_files").set_show_expanders(False)
|
self.glade.get_widget("treeview_files").set_show_expanders(False)
|
||||||
|
|
||||||
|
@ -99,12 +99,12 @@ class CreateTorrentDialog:
|
||||||
|
|
||||||
self.glade.get_widget("tracker_treeview").set_model(self.trackers_liststore)
|
self.glade.get_widget("tracker_treeview").set_model(self.trackers_liststore)
|
||||||
self.trackers_liststore.set_sort_column_id(0, gtk.SORT_ASCENDING)
|
self.trackers_liststore.set_sort_column_id(0, gtk.SORT_ASCENDING)
|
||||||
|
|
||||||
if not client.is_localhost() and client.connected():
|
if not client.is_localhost() and client.connected():
|
||||||
self.glade.get_widget("button_remote_path").show()
|
self.glade.get_widget("button_remote_path").show()
|
||||||
else:
|
else:
|
||||||
self.glade.get_widget("button_remote_path").hide()
|
self.glade.get_widget("button_remote_path").hide()
|
||||||
|
|
||||||
self.dialog.show()
|
self.dialog.show()
|
||||||
|
|
||||||
def adjust_piece_size(self):
|
def adjust_piece_size(self):
|
||||||
|
@ -118,7 +118,7 @@ class CreateTorrentDialog:
|
||||||
if pieces < 2048 or (index + 1) == len(model):
|
if pieces < 2048 or (index + 1) == len(model):
|
||||||
self.glade.get_widget("combo_piece_size").set_active(index)
|
self.glade.get_widget("combo_piece_size").set_active(index)
|
||||||
break
|
break
|
||||||
|
|
||||||
def _on_button_file_clicked(self, widget):
|
def _on_button_file_clicked(self, widget):
|
||||||
log.debug("_on_button_file_clicked")
|
log.debug("_on_button_file_clicked")
|
||||||
# Setup the filechooserdialog
|
# Setup the filechooserdialog
|
||||||
|
@ -145,7 +145,7 @@ class CreateTorrentDialog:
|
||||||
self.files_treestore.append(None, [result, gtk.STOCK_FILE, deluge.common.get_path_size(result)])
|
self.files_treestore.append(None, [result, gtk.STOCK_FILE, deluge.common.get_path_size(result)])
|
||||||
self.adjust_piece_size()
|
self.adjust_piece_size()
|
||||||
chooser.destroy()
|
chooser.destroy()
|
||||||
|
|
||||||
def _on_button_folder_clicked(self, widget):
|
def _on_button_folder_clicked(self, widget):
|
||||||
log.debug("_on_button_folder_clicked")
|
log.debug("_on_button_folder_clicked")
|
||||||
# Setup the filechooserdialog
|
# Setup the filechooserdialog
|
||||||
|
@ -168,11 +168,11 @@ class CreateTorrentDialog:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.files_treestore.clear()
|
self.files_treestore.clear()
|
||||||
|
|
||||||
self.files_treestore.append(None, [result, gtk.STOCK_OPEN, deluge.common.get_path_size(result)])
|
self.files_treestore.append(None, [result, gtk.STOCK_OPEN, deluge.common.get_path_size(result)])
|
||||||
self.adjust_piece_size()
|
self.adjust_piece_size()
|
||||||
chooser.destroy()
|
chooser.destroy()
|
||||||
|
|
||||||
def _on_button_remote_path_clicked(self, widget):
|
def _on_button_remote_path_clicked(self, widget):
|
||||||
log.debug("_on_button_remote_path_clicked")
|
log.debug("_on_button_remote_path_clicked")
|
||||||
dialog = self.glade.get_widget("remote_path_dialog")
|
dialog = self.glade.get_widget("remote_path_dialog")
|
||||||
|
@ -181,7 +181,7 @@ class CreateTorrentDialog:
|
||||||
entry.set_text("/")
|
entry.set_text("/")
|
||||||
entry.grab_focus()
|
entry.grab_focus()
|
||||||
response = dialog.run()
|
response = dialog.run()
|
||||||
|
|
||||||
if response == gtk.RESPONSE_OK:
|
if response == gtk.RESPONSE_OK:
|
||||||
result = entry.get_text()
|
result = entry.get_text()
|
||||||
def _on_get_path_size(size):
|
def _on_get_path_size(size):
|
||||||
|
@ -189,16 +189,16 @@ class CreateTorrentDialog:
|
||||||
if size > 0:
|
if size > 0:
|
||||||
self.files_treestore.clear()
|
self.files_treestore.clear()
|
||||||
self.files_treestore.append(None, [result, gtk.STOCK_NETWORK, size])
|
self.files_treestore.append(None, [result, gtk.STOCK_NETWORK, size])
|
||||||
self.adjust_piece_size()
|
self.adjust_piece_size()
|
||||||
client.get_path_size(_on_get_path_size, result)
|
client.get_path_size(_on_get_path_size, result)
|
||||||
client.force_call(True)
|
client.force_call(True)
|
||||||
|
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
def _on_button_cancel_clicked(self, widget):
|
def _on_button_cancel_clicked(self, widget):
|
||||||
log.debug("_on_button_cancel_clicked")
|
log.debug("_on_button_cancel_clicked")
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
def _on_button_save_clicked(self, widget):
|
def _on_button_save_clicked(self, widget):
|
||||||
log.debug("_on_button_save_clicked")
|
log.debug("_on_button_save_clicked")
|
||||||
if len(self.files_treestore) == 0:
|
if len(self.files_treestore) == 0:
|
||||||
|
@ -223,7 +223,7 @@ class CreateTorrentDialog:
|
||||||
chooser.set_transient_for(self.dialog)
|
chooser.set_transient_for(self.dialog)
|
||||||
chooser.set_select_multiple(False)
|
chooser.set_select_multiple(False)
|
||||||
chooser.set_property("skip-taskbar-hint", True)
|
chooser.set_property("skip-taskbar-hint", True)
|
||||||
|
|
||||||
# Add .torrent and * file filters
|
# Add .torrent and * file filters
|
||||||
file_filter = gtk.FileFilter()
|
file_filter = gtk.FileFilter()
|
||||||
file_filter.set_name(_("Torrent files"))
|
file_filter.set_name(_("Torrent files"))
|
||||||
|
@ -233,7 +233,7 @@ class CreateTorrentDialog:
|
||||||
file_filter.set_name(_("All files"))
|
file_filter.set_name(_("All files"))
|
||||||
file_filter.add_pattern("*")
|
file_filter.add_pattern("*")
|
||||||
chooser.add_filter(file_filter)
|
chooser.add_filter(file_filter)
|
||||||
|
|
||||||
chooser.set_current_name(os.path.split(self.files_treestore[0][0])[-1] + ".torrent")
|
chooser.set_current_name(os.path.split(self.files_treestore[0][0])[-1] + ".torrent")
|
||||||
# Run the dialog
|
# Run the dialog
|
||||||
response = chooser.run()
|
response = chooser.run()
|
||||||
|
@ -271,7 +271,7 @@ class CreateTorrentDialog:
|
||||||
combo = self.glade.get_widget("combo_piece_size")
|
combo = self.glade.get_widget("combo_piece_size")
|
||||||
piece_length = int(combo.get_model()[combo.get_active()][0].split()[0]) * 1024
|
piece_length = int(combo.get_model()[combo.get_active()][0].split()[0]) * 1024
|
||||||
num_pieces = self.files_treestore[0][2] / piece_length
|
num_pieces = self.files_treestore[0][2] / piece_length
|
||||||
|
|
||||||
author = self.glade.get_widget("entry_author").get_text()
|
author = self.glade.get_widget("entry_author").get_text()
|
||||||
comment = self.glade.get_widget("entry_comments").get_text()
|
comment = self.glade.get_widget("entry_comments").get_text()
|
||||||
private = self.glade.get_widget("chk_private_flag").get_active()
|
private = self.glade.get_widget("chk_private_flag").get_active()
|
||||||
|
@ -298,7 +298,7 @@ class CreateTorrentDialog:
|
||||||
import threading
|
import threading
|
||||||
threading.Thread(target=self.create_torrent,
|
threading.Thread(target=self.create_torrent,
|
||||||
args=(
|
args=(
|
||||||
path,
|
path,
|
||||||
tracker,
|
tracker,
|
||||||
piece_length,
|
piece_length,
|
||||||
self._on_create_torrent_progress,
|
self._on_create_torrent_progress,
|
||||||
|
@ -309,7 +309,7 @@ class CreateTorrentDialog:
|
||||||
author,
|
author,
|
||||||
webseeds,
|
webseeds,
|
||||||
add_to_session)).start()
|
add_to_session)).start()
|
||||||
|
|
||||||
chooser.destroy()
|
chooser.destroy()
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
|
@ -330,14 +330,14 @@ class CreateTorrentDialog:
|
||||||
self.glade.get_widget("progress_dialog").hide_all()
|
self.glade.get_widget("progress_dialog").hide_all()
|
||||||
if add_to_session:
|
if add_to_session:
|
||||||
client.add_torrent_file([target])
|
client.add_torrent_file([target])
|
||||||
|
|
||||||
def _on_create_torrent_progress(self, value, num_pieces):
|
def _on_create_torrent_progress(self, value, num_pieces):
|
||||||
percent = float(value)/float(num_pieces)
|
percent = float(value)/float(num_pieces)
|
||||||
pbar = self.glade.get_widget("progressbar")
|
pbar = self.glade.get_widget("progressbar")
|
||||||
pbar.set_text(_("%.2f%%") % (percent*100))
|
pbar.set_text(_("%.2f%%") % (percent*100))
|
||||||
if percent >= 0 and percent <= 1.0:
|
if percent >= 0 and percent <= 1.0:
|
||||||
pbar.set_fraction(percent)
|
pbar.set_fraction(percent)
|
||||||
|
|
||||||
def _on_button_up_clicked(self, widget):
|
def _on_button_up_clicked(self, widget):
|
||||||
log.debug("_on_button_up_clicked")
|
log.debug("_on_button_up_clicked")
|
||||||
row = self.glade.get_widget("tracker_treeview").get_selection().get_selected()[1]
|
row = self.glade.get_widget("tracker_treeview").get_selection().get_selected()[1]
|
||||||
|
@ -350,12 +350,12 @@ class CreateTorrentDialog:
|
||||||
log.debug("_on_button_down_clicked")
|
log.debug("_on_button_down_clicked")
|
||||||
row = self.glade.get_widget("tracker_treeview").get_selection().get_selected()[1]
|
row = self.glade.get_widget("tracker_treeview").get_selection().get_selected()[1]
|
||||||
self.trackers_liststore[row][0] += 1
|
self.trackers_liststore[row][0] += 1
|
||||||
|
|
||||||
def _on_button_add_clicked(self, widget):
|
def _on_button_add_clicked(self, widget):
|
||||||
log.debug("_on_button_add_clicked")
|
log.debug("_on_button_add_clicked")
|
||||||
glade = gtk.glade.XML(
|
glade = gtk.glade.XML(
|
||||||
pkg_resources.resource_filename(
|
pkg_resources.resource_filename(
|
||||||
"deluge.ui.gtkui",
|
"deluge.ui.gtkui",
|
||||||
"glade/edit_trackers.glade"))
|
"glade/edit_trackers.glade"))
|
||||||
dialog = glade.get_widget("add_tracker_dialog")
|
dialog = glade.get_widget("add_tracker_dialog")
|
||||||
dialog.set_transient_for(self.dialog)
|
dialog.set_transient_for(self.dialog)
|
||||||
|
@ -372,7 +372,7 @@ class CreateTorrentDialog:
|
||||||
for l in lines:
|
for l in lines:
|
||||||
if deluge.common.is_url(l):
|
if deluge.common.is_url(l):
|
||||||
trackers.append(l)
|
trackers.append(l)
|
||||||
|
|
||||||
# We are going to add these trackers to the heighest tier + 1
|
# We are going to add these trackers to the heighest tier + 1
|
||||||
tier = 0
|
tier = 0
|
||||||
for row in self.trackers_liststore:
|
for row in self.trackers_liststore:
|
||||||
|
@ -383,10 +383,10 @@ class CreateTorrentDialog:
|
||||||
self.trackers_liststore.append([tier, tracker])
|
self.trackers_liststore.append([tier, tracker])
|
||||||
|
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
def _on_button_remove_clicked(self, widget):
|
def _on_button_remove_clicked(self, widget):
|
||||||
log.debug("_on_button_remove_clicked")
|
log.debug("_on_button_remove_clicked")
|
||||||
row = self.glade.get_widget("tracker_treeview").get_selection().get_selected()[1]
|
row = self.glade.get_widget("tracker_treeview").get_selection().get_selected()[1]
|
||||||
self.trackers_liststore.remove(row)
|
self.trackers_liststore.remove(row)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# dbusinterface.py
|
# dbusinterface.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -46,7 +46,7 @@ elif dbus.version >= (0,80,0):
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.common
|
import deluge.common
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
|
|
||||||
class DbusInterface(dbus.service.Object, component.Component):
|
class DbusInterface(dbus.service.Object, component.Component):
|
||||||
def __init__(self, args, path="/org/deluge_torrent/Deluge"):
|
def __init__(self, args, path="/org/deluge_torrent/Deluge"):
|
||||||
component.Component.__init__(self, "DbusInterface")
|
component.Component.__init__(self, "DbusInterface")
|
||||||
|
@ -66,11 +66,11 @@ class DbusInterface(dbus.service.Object, component.Component):
|
||||||
else:
|
else:
|
||||||
new_args.append(arg)
|
new_args.append(arg)
|
||||||
args = new_args
|
args = new_args
|
||||||
|
|
||||||
# Send the args to the running session
|
# Send the args to the running session
|
||||||
if args != [] and args != None:
|
if args != [] and args != None:
|
||||||
bus = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
proxy = bus.get_object("org.deluge_torrent.Deluge",
|
proxy = bus.get_object("org.deluge_torrent.Deluge",
|
||||||
"/org/deluge_torrent/Deluge")
|
"/org/deluge_torrent/Deluge")
|
||||||
ui = dbus.Interface(proxy, "org.deluge_torrent.Deluge")
|
ui = dbus.Interface(proxy, "org.deluge_torrent.Deluge")
|
||||||
ui.process_args(args)
|
ui.process_args(args)
|
||||||
|
@ -82,7 +82,7 @@ class DbusInterface(dbus.service.Object, component.Component):
|
||||||
self.process_args(args)
|
self.process_args(args)
|
||||||
# Register Deluge with Dbus
|
# Register Deluge with Dbus
|
||||||
log.info("Registering with DBUS..")
|
log.info("Registering with DBUS..")
|
||||||
bus_name = dbus.service.BusName("org.deluge_torrent.Deluge",
|
bus_name = dbus.service.BusName("org.deluge_torrent.Deluge",
|
||||||
bus=dbus.SessionBus())
|
bus=dbus.SessionBus())
|
||||||
dbus.service.Object.__init__(self, bus_name, path)
|
dbus.service.Object.__init__(self, bus_name, path)
|
||||||
|
|
||||||
|
@ -91,4 +91,4 @@ class DbusInterface(dbus.service.Object, component.Component):
|
||||||
"""Process arguments sent to already running Deluge"""
|
"""Process arguments sent to already running Deluge"""
|
||||||
from ipcinterface import process_args
|
from ipcinterface import process_args
|
||||||
process_args(args)
|
process_args(args)
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# details_tab.py
|
# details_tab.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -46,7 +46,7 @@ class DetailsTab(Tab):
|
||||||
# Get the labels we need to update.
|
# Get the labels we need to update.
|
||||||
# widgetname, modifier function, status keys
|
# widgetname, modifier function, status keys
|
||||||
glade = component.get("MainWindow").main_glade
|
glade = component.get("MainWindow").main_glade
|
||||||
|
|
||||||
self._name = "Details"
|
self._name = "Details"
|
||||||
self._child_widget = glade.get_widget("details_tab")
|
self._child_widget = glade.get_widget("details_tab")
|
||||||
self._tab_label = glade.get_widget("details_tab_label")
|
self._tab_label = glade.get_widget("details_tab_label")
|
||||||
|
@ -60,11 +60,11 @@ class DetailsTab(Tab):
|
||||||
(glade.get_widget("summary_message"), str, ("message",)),
|
(glade.get_widget("summary_message"), str, ("message",)),
|
||||||
(glade.get_widget("summary_hash"), str, ("hash",))
|
(glade.get_widget("summary_hash"), str, ("hash",))
|
||||||
]
|
]
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
# Get the first selected torrent
|
# Get the first selected torrent
|
||||||
selected = component.get("TorrentView").get_selected_torrents()
|
selected = component.get("TorrentView").get_selected_torrents()
|
||||||
|
|
||||||
# Only use the first torrent in the list or return if None selected
|
# Only use the first torrent in the list or return if None selected
|
||||||
if len(selected) != 0:
|
if len(selected) != 0:
|
||||||
selected = selected[0]
|
selected = selected[0]
|
||||||
|
@ -72,20 +72,20 @@ class DetailsTab(Tab):
|
||||||
# No torrent is selected in the torrentview
|
# No torrent is selected in the torrentview
|
||||||
self.clear()
|
self.clear()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get the torrent status
|
# Get the torrent status
|
||||||
status_keys = ["name", "total_size", "num_files",
|
status_keys = ["name", "total_size", "num_files",
|
||||||
"tracker", "save_path", "message", "hash"]
|
"tracker", "save_path", "message", "hash"]
|
||||||
|
|
||||||
client.get_torrent_status(
|
client.get_torrent_status(
|
||||||
self._on_get_torrent_status, selected, status_keys)
|
self._on_get_torrent_status, selected, status_keys)
|
||||||
|
|
||||||
def _on_get_torrent_status(self, status):
|
def _on_get_torrent_status(self, status):
|
||||||
# Check to see if we got valid data from the core
|
# Check to see if we got valid data from the core
|
||||||
if status is None:
|
if status is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Update all the label widgets
|
# Update all the label widgets
|
||||||
for widget in self.label_widgets:
|
for widget in self.label_widgets:
|
||||||
if widget[1] != None:
|
if widget[1] != None:
|
||||||
args = []
|
args = []
|
||||||
|
@ -95,14 +95,14 @@ class DetailsTab(Tab):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("Unable to get status value: %s", e)
|
log.debug("Unable to get status value: %s", e)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
txt = widget[1](*args)
|
txt = widget[1](*args)
|
||||||
else:
|
else:
|
||||||
txt = status[widget[2][0]]
|
txt = status[widget[2][0]]
|
||||||
|
|
||||||
if widget[0].get_text() != txt:
|
if widget[0].get_text() != txt:
|
||||||
widget[0].set_text(txt)
|
widget[0].set_text(txt)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
for widget in self.label_widgets:
|
for widget in self.label_widgets:
|
||||||
widget[0].set_text("")
|
widget[0].set_text("")
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# edittrackersdialog.py
|
# edittrackersdialog.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -44,18 +44,18 @@ class EditTrackersDialog:
|
||||||
def __init__(self, torrent_id, parent=None):
|
def __init__(self, torrent_id, parent=None):
|
||||||
self.torrent_id = torrent_id
|
self.torrent_id = torrent_id
|
||||||
self.glade = gtk.glade.XML(
|
self.glade = gtk.glade.XML(
|
||||||
pkg_resources.resource_filename("deluge.ui.gtkui",
|
pkg_resources.resource_filename("deluge.ui.gtkui",
|
||||||
"glade/edit_trackers.glade"))
|
"glade/edit_trackers.glade"))
|
||||||
|
|
||||||
self.dialog = self.glade.get_widget("edit_trackers_dialog")
|
self.dialog = self.glade.get_widget("edit_trackers_dialog")
|
||||||
self.treeview = self.glade.get_widget("tracker_treeview")
|
self.treeview = self.glade.get_widget("tracker_treeview")
|
||||||
self.add_tracker_dialog = self.glade.get_widget("add_tracker_dialog")
|
self.add_tracker_dialog = self.glade.get_widget("add_tracker_dialog")
|
||||||
self.add_tracker_dialog.set_transient_for(self.dialog)
|
self.add_tracker_dialog.set_transient_for(self.dialog)
|
||||||
self.edit_tracker_entry = self.glade.get_widget("edit_tracker_entry")
|
self.edit_tracker_entry = self.glade.get_widget("edit_tracker_entry")
|
||||||
self.edit_tracker_entry.set_transient_for(self.dialog)
|
self.edit_tracker_entry.set_transient_for(self.dialog)
|
||||||
|
|
||||||
self.dialog.set_icon(common.get_logo(32))
|
self.dialog.set_icon(common.get_logo(32))
|
||||||
|
|
||||||
if parent != None:
|
if parent != None:
|
||||||
self.dialog.set_transient_for(parent)
|
self.dialog.set_transient_for(parent)
|
||||||
|
|
||||||
|
@ -73,24 +73,24 @@ class EditTrackersDialog:
|
||||||
"on_button_add_ok_clicked": self.on_button_add_ok_clicked,
|
"on_button_add_ok_clicked": self.on_button_add_ok_clicked,
|
||||||
"on_button_add_cancel_clicked": self.on_button_add_cancel_clicked
|
"on_button_add_cancel_clicked": self.on_button_add_cancel_clicked
|
||||||
})
|
})
|
||||||
|
|
||||||
# Create a liststore for tier, url
|
# Create a liststore for tier, url
|
||||||
self.liststore = gtk.ListStore(int, str)
|
self.liststore = gtk.ListStore(int, str)
|
||||||
|
|
||||||
# Create the columns
|
# Create the columns
|
||||||
self.treeview.append_column(
|
self.treeview.append_column(
|
||||||
gtk.TreeViewColumn(_("Tier"), gtk.CellRendererText(), text=0))
|
gtk.TreeViewColumn(_("Tier"), gtk.CellRendererText(), text=0))
|
||||||
self.treeview.append_column(
|
self.treeview.append_column(
|
||||||
gtk.TreeViewColumn(_("Tracker"), gtk.CellRendererText(), text=1))
|
gtk.TreeViewColumn(_("Tracker"), gtk.CellRendererText(), text=1))
|
||||||
|
|
||||||
self.treeview.set_model(self.liststore)
|
self.treeview.set_model(self.liststore)
|
||||||
self.liststore.set_sort_column_id(0, gtk.SORT_ASCENDING)
|
self.liststore.set_sort_column_id(0, gtk.SORT_ASCENDING)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# Make sure we have a torrent_id.. if not just return
|
# Make sure we have a torrent_id.. if not just return
|
||||||
if self.torrent_id == None:
|
if self.torrent_id == None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get the trackers for this torrent
|
# Get the trackers for this torrent
|
||||||
|
|
||||||
client.get_torrent_status(
|
client.get_torrent_status(
|
||||||
|
@ -101,17 +101,17 @@ class EditTrackersDialog:
|
||||||
"""Display trackers dialog"""
|
"""Display trackers dialog"""
|
||||||
for tracker in status["trackers"]:
|
for tracker in status["trackers"]:
|
||||||
self.add_tracker(tracker["tier"], tracker["url"])
|
self.add_tracker(tracker["tier"], tracker["url"])
|
||||||
|
|
||||||
self.dialog.show()
|
self.dialog.show()
|
||||||
|
|
||||||
def add_tracker(self, tier, url):
|
def add_tracker(self, tier, url):
|
||||||
"""Adds a tracker to the list"""
|
"""Adds a tracker to the list"""
|
||||||
self.liststore.append([tier, url])
|
self.liststore.append([tier, url])
|
||||||
|
|
||||||
def get_selected(self):
|
def get_selected(self):
|
||||||
"""Returns the selected tracker"""
|
"""Returns the selected tracker"""
|
||||||
return self.treeview.get_selection().get_selected()[1]
|
return self.treeview.get_selection().get_selected()[1]
|
||||||
|
|
||||||
def on_button_up_clicked(self, widget):
|
def on_button_up_clicked(self, widget):
|
||||||
log.debug("on_button_up_clicked")
|
log.debug("on_button_up_clicked")
|
||||||
selected = self.get_selected()
|
selected = self.get_selected()
|
||||||
|
@ -121,13 +121,13 @@ class EditTrackersDialog:
|
||||||
new_tier = tier + 1
|
new_tier = tier + 1
|
||||||
# Now change the tier for this tracker
|
# Now change the tier for this tracker
|
||||||
self.liststore.set_value(selected, 0, new_tier)
|
self.liststore.set_value(selected, 0, new_tier)
|
||||||
|
|
||||||
def on_button_add_clicked(self, widget):
|
def on_button_add_clicked(self, widget):
|
||||||
log.debug("on_button_add_clicked")
|
log.debug("on_button_add_clicked")
|
||||||
# Show the add tracker dialog
|
# Show the add tracker dialog
|
||||||
self.add_tracker_dialog.show()
|
self.add_tracker_dialog.show()
|
||||||
self.glade.get_widget("textview_trackers").grab_focus()
|
self.glade.get_widget("textview_trackers").grab_focus()
|
||||||
|
|
||||||
def on_button_remove_clicked(self, widget):
|
def on_button_remove_clicked(self, widget):
|
||||||
log.debug("on_button_remove_clicked")
|
log.debug("on_button_remove_clicked")
|
||||||
selected = self.get_selected()
|
selected = self.get_selected()
|
||||||
|
@ -166,7 +166,7 @@ class EditTrackersDialog:
|
||||||
new_tier = tier - 1
|
new_tier = tier - 1
|
||||||
# Now change the tier for this tracker
|
# Now change the tier for this tracker
|
||||||
self.liststore.set_value(selected, 0, new_tier)
|
self.liststore.set_value(selected, 0, new_tier)
|
||||||
|
|
||||||
def on_button_ok_clicked(self, widget):
|
def on_button_ok_clicked(self, widget):
|
||||||
log.debug("on_button_ok_clicked")
|
log.debug("on_button_ok_clicked")
|
||||||
self.trackers = []
|
self.trackers = []
|
||||||
|
@ -179,11 +179,11 @@ class EditTrackersDialog:
|
||||||
# Set the torrens trackers
|
# Set the torrens trackers
|
||||||
client.set_torrent_trackers(self.torrent_id, self.trackers)
|
client.set_torrent_trackers(self.torrent_id, self.trackers)
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
def on_button_cancel_clicked(self, widget):
|
def on_button_cancel_clicked(self, widget):
|
||||||
log.debug("on_button_cancel_clicked")
|
log.debug("on_button_cancel_clicked")
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
def on_button_add_ok_clicked(self, widget):
|
def on_button_add_ok_clicked(self, widget):
|
||||||
log.debug("on_button_add_ok_clicked")
|
log.debug("on_button_add_ok_clicked")
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ class EditTrackersDialog:
|
||||||
self.liststore.foreach(tier_count, [tracker, highest_tier, duplicate])
|
self.liststore.foreach(tier_count, [tracker, highest_tier, duplicate])
|
||||||
else:
|
else:
|
||||||
highest_tier = -1
|
highest_tier = -1
|
||||||
|
|
||||||
# If not a duplicate, then add it to the list
|
# If not a duplicate, then add it to the list
|
||||||
if not duplicate:
|
if not duplicate:
|
||||||
# Add the tracker to the list
|
# Add the tracker to the list
|
||||||
|
@ -224,9 +224,9 @@ class EditTrackersDialog:
|
||||||
# Clear the entry widget and hide the dialog
|
# Clear the entry widget and hide the dialog
|
||||||
textview.get_buffer().set_text("")
|
textview.get_buffer().set_text("")
|
||||||
self.add_tracker_dialog.hide()
|
self.add_tracker_dialog.hide()
|
||||||
|
|
||||||
def on_button_add_cancel_clicked(self, widget):
|
def on_button_add_cancel_clicked(self, widget):
|
||||||
log.debug("on_button_add_cancel_clicked")
|
log.debug("on_button_add_cancel_clicked")
|
||||||
# Clear the entry widget and hide the dialog
|
# Clear the entry widget and hide the dialog
|
||||||
self.glade.get_widget("entry_tracker").set_text("")
|
self.glade.get_widget("entry_tracker").set_text("")
|
||||||
self.add_tracker_dialog.hide()
|
self.add_tracker_dialog.hide()
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# files_tab.py
|
# files_tab.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -104,12 +104,12 @@ class FilesTab(Tab):
|
||||||
self.listview = glade.get_widget("files_listview")
|
self.listview = glade.get_widget("files_listview")
|
||||||
# filename, size, progress string, progress value, priority, file index, icon id
|
# filename, size, progress string, progress value, priority, file index, icon id
|
||||||
self.treestore = gtk.TreeStore(str, gobject.TYPE_UINT64, str, int, int, int, str)
|
self.treestore = gtk.TreeStore(str, gobject.TYPE_UINT64, str, int, int, int, str)
|
||||||
|
|
||||||
# We need to store the row that's being edited to prevent updating it until
|
# We need to store the row that's being edited to prevent updating it until
|
||||||
# it's been done editing
|
# it's been done editing
|
||||||
self._editing_index = None
|
self._editing_index = None
|
||||||
|
|
||||||
# Filename column
|
# Filename column
|
||||||
column = gtk.TreeViewColumn(_("Filename"))
|
column = gtk.TreeViewColumn(_("Filename"))
|
||||||
render = gtk.CellRendererPixbuf()
|
render = gtk.CellRendererPixbuf()
|
||||||
column.pack_start(render, False)
|
column.pack_start(render, False)
|
||||||
|
@ -129,7 +129,7 @@ class FilesTab(Tab):
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
# Size column
|
# Size column
|
||||||
column = gtk.TreeViewColumn(_("Size"))
|
column = gtk.TreeViewColumn(_("Size"))
|
||||||
render = gtk.CellRendererText()
|
render = gtk.CellRendererText()
|
||||||
column.pack_start(render, False)
|
column.pack_start(render, False)
|
||||||
|
@ -142,7 +142,7 @@ class FilesTab(Tab):
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
# Progress column
|
# Progress column
|
||||||
column = gtk.TreeViewColumn(_("Progress"))
|
column = gtk.TreeViewColumn(_("Progress"))
|
||||||
render = gtk.CellRendererProgress()
|
render = gtk.CellRendererProgress()
|
||||||
column.pack_start(render)
|
column.pack_start(render)
|
||||||
|
@ -154,8 +154,8 @@ class FilesTab(Tab):
|
||||||
column.set_min_width(10)
|
column.set_min_width(10)
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
# Priority column
|
# Priority column
|
||||||
column = gtk.TreeViewColumn(_("Priority"))
|
column = gtk.TreeViewColumn(_("Priority"))
|
||||||
render = gtk.CellRendererPixbuf()
|
render = gtk.CellRendererPixbuf()
|
||||||
column.pack_start(render, False)
|
column.pack_start(render, False)
|
||||||
|
@ -172,9 +172,9 @@ class FilesTab(Tab):
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
self.listview.set_model(self.treestore)
|
self.listview.set_model(self.treestore)
|
||||||
|
|
||||||
self.listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
|
self.listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
|
||||||
|
|
||||||
self.file_menu = glade.get_widget("menu_file_tab")
|
self.file_menu = glade.get_widget("menu_file_tab")
|
||||||
self.listview.connect("row-activated", self._on_row_activated)
|
self.listview.connect("row-activated", self._on_row_activated)
|
||||||
self.listview.connect("button-press-event", self._on_button_press_event)
|
self.listview.connect("button-press-event", self._on_button_press_event)
|
||||||
|
@ -187,7 +187,7 @@ class FilesTab(Tab):
|
||||||
|
|
||||||
self.listview.connect("drag_data_get", self._on_drag_data_get_data)
|
self.listview.connect("drag_data_get", self._on_drag_data_get_data)
|
||||||
self.listview.connect("drag_data_received", self._on_drag_data_received_data)
|
self.listview.connect("drag_data_received", self._on_drag_data_received_data)
|
||||||
|
|
||||||
glade.signal_autoconnect({
|
glade.signal_autoconnect({
|
||||||
"on_menuitem_open_file_activate": self._on_menuitem_open_file_activate,
|
"on_menuitem_open_file_activate": self._on_menuitem_open_file_activate,
|
||||||
"on_menuitem_donotdownload_activate": self._on_menuitem_donotdownload_activate,
|
"on_menuitem_donotdownload_activate": self._on_menuitem_donotdownload_activate,
|
||||||
|
@ -200,22 +200,22 @@ class FilesTab(Tab):
|
||||||
# Connect to the 'torrent_file_renamed' signal
|
# Connect to the 'torrent_file_renamed' signal
|
||||||
component.get("Signals").connect_to_signal("torrent_file_renamed", self._on_torrent_file_renamed_signal)
|
component.get("Signals").connect_to_signal("torrent_file_renamed", self._on_torrent_file_renamed_signal)
|
||||||
component.get("Signals").connect_to_signal("torrent_folder_renamed", self._on_torrent_folder_renamed_signal)
|
component.get("Signals").connect_to_signal("torrent_folder_renamed", self._on_torrent_folder_renamed_signal)
|
||||||
|
|
||||||
# Attempt to load state
|
# Attempt to load state
|
||||||
self.load_state()
|
self.load_state()
|
||||||
|
|
||||||
# torrent_id: (filepath, size)
|
# torrent_id: (filepath, size)
|
||||||
self.files_list = {}
|
self.files_list = {}
|
||||||
|
|
||||||
self.torrent_id = None
|
self.torrent_id = None
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
filename = "files_tab.state"
|
filename = "files_tab.state"
|
||||||
state = []
|
state = []
|
||||||
for index, column in enumerate(self.listview.get_columns()):
|
for index, column in enumerate(self.listview.get_columns()):
|
||||||
state.append(ColumnState(column.get_title(), index, column.get_width(),
|
state.append(ColumnState(column.get_title(), index, column.get_width(),
|
||||||
column.get_sort_indicator(), int(column.get_sort_order())))
|
column.get_sort_indicator(), int(column.get_sort_order())))
|
||||||
|
|
||||||
# Get the config location for saving the state file
|
# Get the config location for saving the state file
|
||||||
config_location = ConfigManager("gtkui.conf")["config_location"]
|
config_location = ConfigManager("gtkui.conf")["config_location"]
|
||||||
|
|
||||||
|
@ -226,13 +226,13 @@ class FilesTab(Tab):
|
||||||
state_file.close()
|
state_file.close()
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
log.warning("Unable to save state file: %s", e)
|
log.warning("Unable to save state file: %s", e)
|
||||||
|
|
||||||
def load_state(self):
|
def load_state(self):
|
||||||
filename = "files_tab.state"
|
filename = "files_tab.state"
|
||||||
# Get the config location for loading the state file
|
# Get the config location for loading the state file
|
||||||
config_location = ConfigManager("gtkui.conf")["config_location"]
|
config_location = ConfigManager("gtkui.conf")["config_location"]
|
||||||
state = None
|
state = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.debug("Loading FilesTab state file: %s", filename)
|
log.debug("Loading FilesTab state file: %s", filename)
|
||||||
state_file = open(os.path.join(config_location, filename), "rb")
|
state_file = open(os.path.join(config_location, filename), "rb")
|
||||||
|
@ -240,10 +240,10 @@ class FilesTab(Tab):
|
||||||
state_file.close()
|
state_file.close()
|
||||||
except (EOFError, IOError), e:
|
except (EOFError, IOError), e:
|
||||||
log.warning("Unable to load state file: %s", e)
|
log.warning("Unable to load state file: %s", e)
|
||||||
|
|
||||||
if state == None:
|
if state == None:
|
||||||
return
|
return
|
||||||
|
|
||||||
for column_state in state:
|
for column_state in state:
|
||||||
# Find matching columns in the listview
|
# Find matching columns in the listview
|
||||||
for (index, column) in enumerate(self.listview.get_columns()):
|
for (index, column) in enumerate(self.listview.get_columns()):
|
||||||
|
@ -272,18 +272,18 @@ class FilesTab(Tab):
|
||||||
# No torrent is selected in the torrentview
|
# No torrent is selected in the torrentview
|
||||||
self.clear()
|
self.clear()
|
||||||
return
|
return
|
||||||
|
|
||||||
if torrent_id != self.torrent_id:
|
if torrent_id != self.torrent_id:
|
||||||
# We only want to do this if the torrent_id has changed
|
# We only want to do this if the torrent_id has changed
|
||||||
self.treestore.clear()
|
self.treestore.clear()
|
||||||
self.torrent_id = torrent_id
|
self.torrent_id = torrent_id
|
||||||
|
|
||||||
if self.torrent_id not in self.files_list.keys():
|
if self.torrent_id not in self.files_list.keys():
|
||||||
# We need to get the files list
|
# We need to get the files list
|
||||||
log.debug("Getting file list from core..")
|
log.debug("Getting file list from core..")
|
||||||
client.get_torrent_status(
|
client.get_torrent_status(
|
||||||
self._on_get_torrent_files,
|
self._on_get_torrent_files,
|
||||||
self.torrent_id,
|
self.torrent_id,
|
||||||
["files", "file_progress", "file_priorities"])
|
["files", "file_progress", "file_priorities"])
|
||||||
client.force_call(block=True)
|
client.force_call(block=True)
|
||||||
else:
|
else:
|
||||||
|
@ -306,23 +306,23 @@ class FilesTab(Tab):
|
||||||
def get_file_path(self, row, path=""):
|
def get_file_path(self, row, path=""):
|
||||||
if not row:
|
if not row:
|
||||||
return path
|
return path
|
||||||
|
|
||||||
path = self.treestore.get_value(row, 0) + path
|
path = self.treestore.get_value(row, 0) + path
|
||||||
return self.get_file_path(self.treestore.iter_parent(row), path)
|
return self.get_file_path(self.treestore.iter_parent(row), path)
|
||||||
|
|
||||||
def _on_open_file(self, status):
|
def _on_open_file(self, status):
|
||||||
paths = self.listview.get_selection().get_selected_rows()[1]
|
paths = self.listview.get_selection().get_selected_rows()[1]
|
||||||
selected = []
|
selected = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
selected.append(self.treestore.get_iter(path))
|
selected.append(self.treestore.get_iter(path))
|
||||||
|
|
||||||
for select in selected:
|
for select in selected:
|
||||||
path = self.get_file_path(select).split("/")
|
path = self.get_file_path(select).split("/")
|
||||||
filepath = os.path.join(status["save_path"], *path)
|
filepath = os.path.join(status["save_path"], *path)
|
||||||
log.debug("Open file '%s'", filepath)
|
log.debug("Open file '%s'", filepath)
|
||||||
deluge.common.open_file(filepath)
|
deluge.common.open_file(filepath)
|
||||||
|
|
||||||
## The following 3 methods create the folder/file view in the treeview
|
## The following 3 methods create the folder/file view in the treeview
|
||||||
def prepare_file_store(self, files):
|
def prepare_file_store(self, files):
|
||||||
split_files = { }
|
split_files = { }
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -341,7 +341,7 @@ class FilesTab(Tab):
|
||||||
files_storage[file_name_chunk] = { }
|
files_storage[file_name_chunk] = { }
|
||||||
self.prepare_file(file, file_name[first_slash_index+1:],
|
self.prepare_file(file, file_name[first_slash_index+1:],
|
||||||
file_num, files_storage[file_name_chunk])
|
file_num, files_storage[file_name_chunk])
|
||||||
|
|
||||||
def add_files(self, parent_iter, split_files):
|
def add_files(self, parent_iter, split_files):
|
||||||
ret = 0
|
ret = 0
|
||||||
for key,value in split_files.iteritems():
|
for key,value in split_files.iteritems():
|
||||||
|
@ -356,13 +356,13 @@ class FilesTab(Tab):
|
||||||
value[1]["size"], "", 0, 0, value[0], gtk.STOCK_FILE])
|
value[1]["size"], "", 0, 0, value[0], gtk.STOCK_FILE])
|
||||||
ret += value[1]["size"]
|
ret += value[1]["size"]
|
||||||
return ret
|
return ret
|
||||||
###
|
###
|
||||||
|
|
||||||
def update_files(self):
|
def update_files(self):
|
||||||
self.treestore.clear()
|
self.treestore.clear()
|
||||||
self.prepare_file_store(self.files_list[self.torrent_id])
|
self.prepare_file_store(self.files_list[self.torrent_id])
|
||||||
self.listview.expand_row("0", False)
|
self.listview.expand_row("0", False)
|
||||||
|
|
||||||
def get_selected_files(self):
|
def get_selected_files(self):
|
||||||
"""Returns a list of file indexes that are selected"""
|
"""Returns a list of file indexes that are selected"""
|
||||||
def get_iter_children(itr, selected):
|
def get_iter_children(itr, selected):
|
||||||
|
@ -372,7 +372,7 @@ class FilesTab(Tab):
|
||||||
if self.treestore.iter_has_child(i):
|
if self.treestore.iter_has_child(i):
|
||||||
get_iter_children(i, selected)
|
get_iter_children(i, selected)
|
||||||
i = self.treestore.iter_next(i)
|
i = self.treestore.iter_next(i)
|
||||||
|
|
||||||
selected = []
|
selected = []
|
||||||
paths = self.listview.get_selection().get_selected_rows()[1]
|
paths = self.listview.get_selection().get_selected_rows()[1]
|
||||||
for path in paths:
|
for path in paths:
|
||||||
|
@ -380,24 +380,24 @@ class FilesTab(Tab):
|
||||||
selected.append(self.treestore[i][5])
|
selected.append(self.treestore[i][5])
|
||||||
if self.treestore.iter_has_child(i):
|
if self.treestore.iter_has_child(i):
|
||||||
get_iter_children(i, selected)
|
get_iter_children(i, selected)
|
||||||
|
|
||||||
return selected
|
return selected
|
||||||
|
|
||||||
def _on_get_torrent_files(self, status):
|
def _on_get_torrent_files(self, status):
|
||||||
self.files_list[self.torrent_id] = status["files"]
|
self.files_list[self.torrent_id] = status["files"]
|
||||||
self.update_files()
|
self.update_files()
|
||||||
self._on_get_torrent_status(status)
|
self._on_get_torrent_status(status)
|
||||||
|
|
||||||
def get_files_from_tree(self, rows, files_list, indent):
|
def get_files_from_tree(self, rows, files_list, indent):
|
||||||
if not rows:
|
if not rows:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if row[5] > -1:
|
if row[5] > -1:
|
||||||
files_list.append((row[5], row))
|
files_list.append((row[5], row))
|
||||||
self.get_files_from_tree(row.iterchildren(), files_list, indent+1)
|
self.get_files_from_tree(row.iterchildren(), files_list, indent+1)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _on_get_torrent_status(self, status):
|
def _on_get_torrent_status(self, status):
|
||||||
# (index, iter)
|
# (index, iter)
|
||||||
files_list = []
|
files_list = []
|
||||||
|
@ -418,7 +418,7 @@ class FilesTab(Tab):
|
||||||
file_priority = status["file_priorities"][index]
|
file_priority = status["file_priorities"][index]
|
||||||
if row[4] != file_priority:
|
if row[4] != file_priority:
|
||||||
row[4] = file_priority
|
row[4] = file_priority
|
||||||
|
|
||||||
def _on_button_press_event(self, widget, event):
|
def _on_button_press_event(self, widget, event):
|
||||||
"""This is a callback for showing the right-click context menu."""
|
"""This is a callback for showing the right-click context menu."""
|
||||||
log.debug("on_button_press_event")
|
log.debug("on_button_press_event")
|
||||||
|
@ -436,10 +436,10 @@ class FilesTab(Tab):
|
||||||
self.listview.get_selection().select_iter(row)
|
self.listview.get_selection().select_iter(row)
|
||||||
else:
|
else:
|
||||||
self.listview.get_selection().select_iter(row)
|
self.listview.get_selection().select_iter(row)
|
||||||
|
|
||||||
self.file_menu.popup(None, None, None, event.button, event.time)
|
self.file_menu.popup(None, None, None, event.button, event.time)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _on_menuitem_open_file_activate(self, menuitem):
|
def _on_menuitem_open_file_activate(self, menuitem):
|
||||||
self._on_row_activated(None, None, None)
|
self._on_row_activated(None, None, None)
|
||||||
|
|
||||||
|
@ -458,32 +458,32 @@ class FilesTab(Tab):
|
||||||
file_priorities.sort()
|
file_priorities.sort()
|
||||||
priorities = [p[1] for p in file_priorities]
|
priorities = [p[1] for p in file_priorities]
|
||||||
log.debug("priorities: %s", priorities)
|
log.debug("priorities: %s", priorities)
|
||||||
|
|
||||||
client.set_torrent_file_priorities(self.torrent_id, priorities)
|
client.set_torrent_file_priorities(self.torrent_id, priorities)
|
||||||
|
|
||||||
def _on_menuitem_donotdownload_activate(self, menuitem):
|
def _on_menuitem_donotdownload_activate(self, menuitem):
|
||||||
self._set_file_priorities_on_user_change(
|
self._set_file_priorities_on_user_change(
|
||||||
self.get_selected_files(),
|
self.get_selected_files(),
|
||||||
deluge.common.FILE_PRIORITY["Do Not Download"])
|
deluge.common.FILE_PRIORITY["Do Not Download"])
|
||||||
|
|
||||||
def _on_menuitem_normal_activate(self, menuitem):
|
def _on_menuitem_normal_activate(self, menuitem):
|
||||||
self._set_file_priorities_on_user_change(
|
self._set_file_priorities_on_user_change(
|
||||||
self.get_selected_files(),
|
self.get_selected_files(),
|
||||||
deluge.common.FILE_PRIORITY["Normal Priority"])
|
deluge.common.FILE_PRIORITY["Normal Priority"])
|
||||||
|
|
||||||
def _on_menuitem_high_activate(self, menuitem):
|
def _on_menuitem_high_activate(self, menuitem):
|
||||||
self._set_file_priorities_on_user_change(
|
self._set_file_priorities_on_user_change(
|
||||||
self.get_selected_files(),
|
self.get_selected_files(),
|
||||||
deluge.common.FILE_PRIORITY["High Priority"])
|
deluge.common.FILE_PRIORITY["High Priority"])
|
||||||
|
|
||||||
def _on_menuitem_highest_activate(self, menuitem):
|
def _on_menuitem_highest_activate(self, menuitem):
|
||||||
self._set_file_priorities_on_user_change(
|
self._set_file_priorities_on_user_change(
|
||||||
self.get_selected_files(),
|
self.get_selected_files(),
|
||||||
deluge.common.FILE_PRIORITY["Highest Priority"])
|
deluge.common.FILE_PRIORITY["Highest Priority"])
|
||||||
|
|
||||||
def _on_menuitem_expand_all_activate(self, menuitem):
|
def _on_menuitem_expand_all_activate(self, menuitem):
|
||||||
self.listview.expand_all()
|
self.listview.expand_all()
|
||||||
|
|
||||||
def _on_filename_edited(self, renderer, path, new_text):
|
def _on_filename_edited(self, renderer, path, new_text):
|
||||||
index = self.treestore[path][5]
|
index = self.treestore[path][5]
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ class FilesTab(Tab):
|
||||||
fp += self.treestore[i][0]
|
fp += self.treestore[i][0]
|
||||||
return fp
|
return fp
|
||||||
return fp
|
return fp
|
||||||
|
|
||||||
# Only recurse if file is in a folder..
|
# Only recurse if file is in a folder..
|
||||||
if self.treestore.iter_parent(itr):
|
if self.treestore.iter_parent(itr):
|
||||||
filepath = get_filepath(itr, str()) + new_text
|
filepath = get_filepath(itr, str()) + new_text
|
||||||
|
@ -511,35 +511,35 @@ class FilesTab(Tab):
|
||||||
filepath = new_text
|
filepath = new_text
|
||||||
|
|
||||||
log.debug("filepath: %s", filepath)
|
log.debug("filepath: %s", filepath)
|
||||||
|
|
||||||
client.rename_files(self.torrent_id, [(index, filepath)])
|
client.rename_files(self.torrent_id, [(index, filepath)])
|
||||||
else:
|
else:
|
||||||
# We are renaming a folder
|
# We are renaming a folder
|
||||||
folder = self.treestore[path][0]
|
folder = self.treestore[path][0]
|
||||||
|
|
||||||
parent_path = ""
|
parent_path = ""
|
||||||
itr = self.treestore.iter_parent(self.treestore.get_iter(path))
|
itr = self.treestore.iter_parent(self.treestore.get_iter(path))
|
||||||
while itr:
|
while itr:
|
||||||
parent_path = self.treestore[itr][0] + parent_path
|
parent_path = self.treestore[itr][0] + parent_path
|
||||||
itr = self.treestore.iter_parent(itr)
|
itr = self.treestore.iter_parent(itr)
|
||||||
|
|
||||||
client.rename_folder(self.torrent_id, parent_path + folder, parent_path + new_text)
|
client.rename_folder(self.torrent_id, parent_path + folder, parent_path + new_text)
|
||||||
|
|
||||||
self._editing_index = None
|
self._editing_index = None
|
||||||
|
|
||||||
def _on_filename_editing_start(self, renderer, editable, path):
|
def _on_filename_editing_start(self, renderer, editable, path):
|
||||||
self._editing_index = self.treestore[path][5]
|
self._editing_index = self.treestore[path][5]
|
||||||
|
|
||||||
def _on_filename_editing_canceled(self, renderer):
|
def _on_filename_editing_canceled(self, renderer):
|
||||||
self._editing_index = None
|
self._editing_index = None
|
||||||
|
|
||||||
def _on_torrent_file_renamed_signal(self, torrent_id, index, name):
|
def _on_torrent_file_renamed_signal(self, torrent_id, index, name):
|
||||||
log.debug("index: %s name: %s", index, name)
|
log.debug("index: %s name: %s", index, name)
|
||||||
old_name = self.files_list[torrent_id][index]["path"]
|
old_name = self.files_list[torrent_id][index]["path"]
|
||||||
self.files_list[torrent_id][index]["path"] = name
|
self.files_list[torrent_id][index]["path"] = name
|
||||||
|
|
||||||
# We need to update the filename displayed if we're currently viewing
|
# We need to update the filename displayed if we're currently viewing
|
||||||
# this torrents files.
|
# this torrents files.
|
||||||
if torrent_id == self.torrent_id:
|
if torrent_id == self.torrent_id:
|
||||||
old_name_len = len(old_name.split("/"))
|
old_name_len = len(old_name.split("/"))
|
||||||
name_len = len(name.split("/"))
|
name_len = len(name.split("/"))
|
||||||
|
@ -572,18 +572,18 @@ class FilesTab(Tab):
|
||||||
if create:
|
if create:
|
||||||
parent_iter = self.treestore.append(parent_iter,
|
parent_iter = self.treestore.append(parent_iter,
|
||||||
[tc + "/", 0, "", 0, 0, -1, gtk.STOCK_DIRECTORY])
|
[tc + "/", 0, "", 0, 0, -1, gtk.STOCK_DIRECTORY])
|
||||||
|
|
||||||
# Find the iter for the file that needs to be moved
|
# Find the iter for the file that needs to be moved
|
||||||
def get_file_iter(model, path, itr, user_data):
|
def get_file_iter(model, path, itr, user_data):
|
||||||
if model[itr][5] == index:
|
if model[itr][5] == index:
|
||||||
model[itr][0] = name.split("/")[-1]
|
model[itr][0] = name.split("/")[-1]
|
||||||
t = self.treestore.append(
|
t = self.treestore.append(
|
||||||
parent_iter,
|
parent_iter,
|
||||||
self.treestore.get(itr,
|
self.treestore.get(itr,
|
||||||
*xrange(self.treestore.get_n_columns())))
|
*xrange(self.treestore.get_n_columns())))
|
||||||
self.treestore.remove(itr)
|
self.treestore.remove(itr)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self.treestore.foreach(get_file_iter, None)
|
self.treestore.foreach(get_file_iter, None)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
@ -602,30 +602,30 @@ class FilesTab(Tab):
|
||||||
|
|
||||||
def _on_torrent_folder_renamed_signal(self, torrent_id, old_folder, new_folder):
|
def _on_torrent_folder_renamed_signal(self, torrent_id, old_folder, new_folder):
|
||||||
log.debug("old_folder: %s new_folder: %s", old_folder, new_folder)
|
log.debug("old_folder: %s new_folder: %s", old_folder, new_folder)
|
||||||
|
|
||||||
if old_folder[-1] != "/":
|
if old_folder[-1] != "/":
|
||||||
old_folder += "/"
|
old_folder += "/"
|
||||||
if new_folder[-1] != "/":
|
if new_folder[-1] != "/":
|
||||||
new_folder += "/"
|
new_folder += "/"
|
||||||
|
|
||||||
for fd in self.files_list[torrent_id]:
|
for fd in self.files_list[torrent_id]:
|
||||||
if fd["path"].startswith(old_folder):
|
if fd["path"].startswith(old_folder):
|
||||||
fd["path"] = fd["path"].replace(old_folder, new_folder, 1)
|
fd["path"] = fd["path"].replace(old_folder, new_folder, 1)
|
||||||
|
|
||||||
if torrent_id == self.torrent_id:
|
if torrent_id == self.torrent_id:
|
||||||
|
|
||||||
old_split = old_folder.split("/")
|
old_split = old_folder.split("/")
|
||||||
try:
|
try:
|
||||||
old_split.remove("")
|
old_split.remove("")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
new_split = new_folder.split("/")
|
new_split = new_folder.split("/")
|
||||||
try:
|
try:
|
||||||
new_split.remove("")
|
new_split.remove("")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
itr = None
|
itr = None
|
||||||
for i, old in enumerate(old_split):
|
for i, old in enumerate(old_split):
|
||||||
itr = self.treestore.iter_children(itr)
|
itr = self.treestore.iter_children(itr)
|
||||||
|
@ -636,11 +636,11 @@ class FilesTab(Tab):
|
||||||
self.treestore[itr][0] = new_split[i] + "/"
|
self.treestore[itr][0] = new_split[i] + "/"
|
||||||
break
|
break
|
||||||
itr = self.treestore.iter_next(itr)
|
itr = self.treestore.iter_next(itr)
|
||||||
|
|
||||||
def _on_drag_data_get_data(self, treeview, context, selection, target_id, etime):
|
def _on_drag_data_get_data(self, treeview, context, selection, target_id, etime):
|
||||||
indexes = self.get_selected_files()
|
indexes = self.get_selected_files()
|
||||||
selection.set_text(",".join([str(x) for x in indexes]))
|
selection.set_text(",".join([str(x) for x in indexes]))
|
||||||
|
|
||||||
def _on_drag_data_received_data(self, treeview, context, x, y, selection, info, etime):
|
def _on_drag_data_received_data(self, treeview, context, x, y, selection, info, etime):
|
||||||
log.debug("selection.data: %s", selection.data)
|
log.debug("selection.data: %s", selection.data)
|
||||||
drop_info = treeview.get_dest_row_at_pos(x, y)
|
drop_info = treeview.get_dest_row_at_pos(x, y)
|
||||||
|
@ -651,11 +651,11 @@ class FilesTab(Tab):
|
||||||
parent_path = ""
|
parent_path = ""
|
||||||
if model[itr][5] == -1:
|
if model[itr][5] == -1:
|
||||||
parent_path += model[itr][0]
|
parent_path += model[itr][0]
|
||||||
|
|
||||||
while parent_iter:
|
while parent_iter:
|
||||||
parent_path = model[parent_iter][0] + parent_path
|
parent_path = model[parent_iter][0] + parent_path
|
||||||
parent_iter = model.iter_parent(parent_iter)
|
parent_iter = model.iter_parent(parent_iter)
|
||||||
|
|
||||||
#[(index, filepath), ...]
|
#[(index, filepath), ...]
|
||||||
to_rename = []
|
to_rename = []
|
||||||
|
|
||||||
|
@ -665,8 +665,8 @@ class FilesTab(Tab):
|
||||||
to_rename.append((model[itr][5], parent_path + model[itr][0]))
|
to_rename.append((model[itr][5], parent_path + model[itr][0]))
|
||||||
if len(to_rename) == len(selected):
|
if len(to_rename) == len(selected):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
model.foreach(find_file, None)
|
model.foreach(find_file, None)
|
||||||
log.debug("to_rename: %s", to_rename)
|
log.debug("to_rename: %s", to_rename)
|
||||||
client.rename_files(self.torrent_id, to_rename)
|
client.rename_files(self.torrent_id, to_rename)
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# new_release_dialog.py
|
# new_release_dialog.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -38,7 +38,7 @@ from deluge.configmanager import ConfigManager
|
||||||
class NewReleaseDialog:
|
class NewReleaseDialog:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def show(self, available_version):
|
def show(self, available_version):
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
glade = component.get("MainWindow").main_glade
|
glade = component.get("MainWindow").main_glade
|
||||||
|
@ -52,14 +52,14 @@ class NewReleaseDialog:
|
||||||
"clicked", self._on_button_goto_downloads)
|
"clicked", self._on_button_goto_downloads)
|
||||||
glade.get_widget("button_close_new_release").connect(
|
glade.get_widget("button_close_new_release").connect(
|
||||||
"clicked", self._on_button_close_new_release)
|
"clicked", self._on_button_close_new_release)
|
||||||
|
|
||||||
self.dialog.show_all()
|
self.dialog.show_all()
|
||||||
|
|
||||||
def _on_button_goto_downloads(self, widget):
|
def _on_button_goto_downloads(self, widget):
|
||||||
deluge.common.open_url_in_browser("http://deluge-torrent.org")
|
deluge.common.open_url_in_browser("http://deluge-torrent.org")
|
||||||
self.config["show_new_releases"] = not self.chk_not_show_dialog.get_active()
|
self.config["show_new_releases"] = not self.chk_not_show_dialog.get_active()
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
def _on_button_close_new_release(self, widget):
|
def _on_button_close_new_release(self, widget):
|
||||||
self.config["show_new_releases"] = not self.chk_not_show_dialog.get_active()
|
self.config["show_new_releases"] = not self.chk_not_show_dialog.get_active()
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# notification.py
|
# notification.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Marcos Pinto ('markybob') <markybob@gmail.com>
|
# Copyright (C) 2008 Marcos Pinto ('markybob') <markybob@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -72,7 +72,7 @@ class Notification:
|
||||||
log.warning("pynotify is not installed")
|
log.warning("pynotify is not installed")
|
||||||
else:
|
else:
|
||||||
if pynotify.init("Deluge"):
|
if pynotify.init("Deluge"):
|
||||||
self.note = pynotify.Notification(_("Torrent complete"),
|
self.note = pynotify.Notification(_("Torrent complete"),
|
||||||
status["name"] + "\n" + _("Including %i files" % status["num_files"]))
|
status["name"] + "\n" + _("Including %i files" % status["num_files"]))
|
||||||
self.note.set_icon_from_pixbuf(common.get_logo(48))
|
self.note.set_icon_from_pixbuf(common.get_logo(48))
|
||||||
if not self.note.show():
|
if not self.note.show():
|
||||||
|
@ -99,7 +99,7 @@ class Notification:
|
||||||
"""sends email notification of finished torrent"""
|
"""sends email notification of finished torrent"""
|
||||||
import smtplib
|
import smtplib
|
||||||
headers = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % (
|
headers = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % (
|
||||||
self.config["ntf_email_add"], self.config["ntf_email_add"],
|
self.config["ntf_email_add"], self.config["ntf_email_add"],
|
||||||
"Finished torrent %s" % (status["name"]))
|
"Finished torrent %s" % (status["name"]))
|
||||||
text = _("This email is to inform you that Deluge has finished downloading %s , \
|
text = _("This email is to inform you that Deluge has finished downloading %s , \
|
||||||
which includes %i files.\nTo stop receiving these alerts, simply turn off \
|
which includes %i files.\nTo stop receiving these alerts, simply turn off \
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# peers_tab.py
|
# peers_tab.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -51,7 +51,7 @@ def cell_data_progress(column, cell, model, row, data):
|
||||||
value = model.get_value(row, data)
|
value = model.get_value(row, data)
|
||||||
cell.set_property("value", value * 100)
|
cell.set_property("value", value * 100)
|
||||||
cell.set_property("text", "%.2f%%" % (value * 100))
|
cell.set_property("text", "%.2f%%" % (value * 100))
|
||||||
|
|
||||||
class ColumnState:
|
class ColumnState:
|
||||||
def __init__(self, name, position, width, sort, sort_order):
|
def __init__(self, name, position, width, sort, sort_order):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -59,7 +59,7 @@ class ColumnState:
|
||||||
self.width = width
|
self.width = width
|
||||||
self.sort = sort
|
self.sort = sort
|
||||||
self.sort_order = sort_order
|
self.sort_order = sort_order
|
||||||
|
|
||||||
class PeersTab(Tab):
|
class PeersTab(Tab):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Tab.__init__(self)
|
Tab.__init__(self)
|
||||||
|
@ -78,14 +78,14 @@ class PeersTab(Tab):
|
||||||
# country pixbuf, ip, client, downspeed, upspeed, country code, int_ip, seed/peer icon, progress
|
# country pixbuf, ip, client, downspeed, upspeed, country code, int_ip, seed/peer icon, progress
|
||||||
self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, str, int, int, str, gobject.TYPE_UINT, gtk.gdk.Pixbuf, float)
|
self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, str, int, int, str, gobject.TYPE_UINT, gtk.gdk.Pixbuf, float)
|
||||||
self.cached_flag_pixbufs = {}
|
self.cached_flag_pixbufs = {}
|
||||||
|
|
||||||
self.seed_pixbuf = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("seeding16.png"))
|
self.seed_pixbuf = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("seeding16.png"))
|
||||||
self.peer_pixbuf = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("downloading16.png"))
|
self.peer_pixbuf = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("downloading16.png"))
|
||||||
|
|
||||||
# key is ip address, item is row iter
|
# key is ip address, item is row iter
|
||||||
self.peers = {}
|
self.peers = {}
|
||||||
|
|
||||||
# Country column
|
# Country column
|
||||||
column = gtk.TreeViewColumn()
|
column = gtk.TreeViewColumn()
|
||||||
render = gtk.CellRendererPixbuf()
|
render = gtk.CellRendererPixbuf()
|
||||||
column.pack_start(render, False)
|
column.pack_start(render, False)
|
||||||
|
@ -97,8 +97,8 @@ class PeersTab(Tab):
|
||||||
column.set_min_width(10)
|
column.set_min_width(10)
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
# Address column
|
# Address column
|
||||||
column = gtk.TreeViewColumn(_("Address"))
|
column = gtk.TreeViewColumn(_("Address"))
|
||||||
render = gtk.CellRendererPixbuf()
|
render = gtk.CellRendererPixbuf()
|
||||||
column.pack_start(render, False)
|
column.pack_start(render, False)
|
||||||
|
@ -114,7 +114,7 @@ class PeersTab(Tab):
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
# Client column
|
# Client column
|
||||||
column = gtk.TreeViewColumn(_("Client"))
|
column = gtk.TreeViewColumn(_("Client"))
|
||||||
render = gtk.CellRendererText()
|
render = gtk.CellRendererText()
|
||||||
column.pack_start(render, False)
|
column.pack_start(render, False)
|
||||||
|
@ -139,7 +139,7 @@ class PeersTab(Tab):
|
||||||
column.set_min_width(10)
|
column.set_min_width(10)
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
# Down Speed column
|
# Down Speed column
|
||||||
column = gtk.TreeViewColumn(_("Down Speed"))
|
column = gtk.TreeViewColumn(_("Down Speed"))
|
||||||
render = gtk.CellRendererText()
|
render = gtk.CellRendererText()
|
||||||
|
@ -151,8 +151,8 @@ class PeersTab(Tab):
|
||||||
column.set_expand(False)
|
column.set_expand(False)
|
||||||
column.set_min_width(10)
|
column.set_min_width(10)
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
# Up Speed column
|
# Up Speed column
|
||||||
column = gtk.TreeViewColumn(_("Up Speed"))
|
column = gtk.TreeViewColumn(_("Up Speed"))
|
||||||
render = gtk.CellRendererText()
|
render = gtk.CellRendererText()
|
||||||
|
@ -164,21 +164,21 @@ class PeersTab(Tab):
|
||||||
column.set_expand(False)
|
column.set_expand(False)
|
||||||
column.set_min_width(10)
|
column.set_min_width(10)
|
||||||
column.set_reorderable(True)
|
column.set_reorderable(True)
|
||||||
self.listview.append_column(column)
|
self.listview.append_column(column)
|
||||||
|
|
||||||
self.listview.set_model(self.liststore)
|
self.listview.set_model(self.liststore)
|
||||||
|
|
||||||
self.load_state()
|
self.load_state()
|
||||||
|
|
||||||
self.torrent_id = None
|
self.torrent_id = None
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
filename = "peers_tab.state"
|
filename = "peers_tab.state"
|
||||||
state = []
|
state = []
|
||||||
for index, column in enumerate(self.listview.get_columns()):
|
for index, column in enumerate(self.listview.get_columns()):
|
||||||
state.append(ColumnState(column.get_title(), index, column.get_width(),
|
state.append(ColumnState(column.get_title(), index, column.get_width(),
|
||||||
column.get_sort_indicator(), int(column.get_sort_order())))
|
column.get_sort_indicator(), int(column.get_sort_order())))
|
||||||
|
|
||||||
# Get the config location for saving the state file
|
# Get the config location for saving the state file
|
||||||
config_location = ConfigManager("gtkui.conf")["config_location"]
|
config_location = ConfigManager("gtkui.conf")["config_location"]
|
||||||
|
|
||||||
|
@ -189,13 +189,13 @@ class PeersTab(Tab):
|
||||||
state_file.close()
|
state_file.close()
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
log.warning("Unable to save state file: %s", e)
|
log.warning("Unable to save state file: %s", e)
|
||||||
|
|
||||||
def load_state(self):
|
def load_state(self):
|
||||||
filename = "peers_tab.state"
|
filename = "peers_tab.state"
|
||||||
# Get the config location for loading the state file
|
# Get the config location for loading the state file
|
||||||
config_location = ConfigManager("gtkui.conf")["config_location"]
|
config_location = ConfigManager("gtkui.conf")["config_location"]
|
||||||
state = None
|
state = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.debug("Loading PeersTab state file: %s", filename)
|
log.debug("Loading PeersTab state file: %s", filename)
|
||||||
state_file = open(os.path.join(config_location, filename), "rb")
|
state_file = open(os.path.join(config_location, filename), "rb")
|
||||||
|
@ -203,14 +203,14 @@ class PeersTab(Tab):
|
||||||
state_file.close()
|
state_file.close()
|
||||||
except (EOFError, IOError), e:
|
except (EOFError, IOError), e:
|
||||||
log.warning("Unable to load state file: %s", e)
|
log.warning("Unable to load state file: %s", e)
|
||||||
|
|
||||||
if state == None:
|
if state == None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(state) != len(self.listview.get_columns()):
|
if len(state) != len(self.listview.get_columns()):
|
||||||
log.warning("peers_tab.state is not compatible! rejecting..")
|
log.warning("peers_tab.state is not compatible! rejecting..")
|
||||||
return
|
return
|
||||||
|
|
||||||
for column_state in state:
|
for column_state in state:
|
||||||
# Find matching columns in the listview
|
# Find matching columns in the listview
|
||||||
for (index, column) in enumerate(self.listview.get_columns()):
|
for (index, column) in enumerate(self.listview.get_columns()):
|
||||||
|
@ -227,7 +227,7 @@ class PeersTab(Tab):
|
||||||
self.listview.move_column_after(column, None)
|
self.listview.move_column_after(column, None)
|
||||||
else:
|
else:
|
||||||
self.listview.move_column_after(column, self.listview.get_columns()[column_state.position - 1])
|
self.listview.move_column_after(column, self.listview.get_columns()[column_state.position - 1])
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
# Get the first selected torrent
|
# Get the first selected torrent
|
||||||
torrent_id = component.get("TorrentView").get_selected_torrents()
|
torrent_id = component.get("TorrentView").get_selected_torrents()
|
||||||
|
@ -239,7 +239,7 @@ class PeersTab(Tab):
|
||||||
# No torrent is selected in the torrentview
|
# No torrent is selected in the torrentview
|
||||||
self.liststore.clear()
|
self.liststore.clear()
|
||||||
return
|
return
|
||||||
|
|
||||||
if torrent_id != self.torrent_id:
|
if torrent_id != self.torrent_id:
|
||||||
# We only want to do this if the torrent_id has changed
|
# We only want to do this if the torrent_id has changed
|
||||||
self.liststore.clear()
|
self.liststore.clear()
|
||||||
|
@ -251,20 +251,20 @@ class PeersTab(Tab):
|
||||||
def get_flag_pixbuf(self, country):
|
def get_flag_pixbuf(self, country):
|
||||||
if country == " ":
|
if country == " ":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not self.cached_flag_pixbufs.has_key(country):
|
if not self.cached_flag_pixbufs.has_key(country):
|
||||||
# We haven't created a pixbuf for this country yet
|
# We haven't created a pixbuf for this country yet
|
||||||
try:
|
try:
|
||||||
self.cached_flag_pixbufs[country] = gtk.gdk.pixbuf_new_from_file(
|
self.cached_flag_pixbufs[country] = gtk.gdk.pixbuf_new_from_file(
|
||||||
pkg_resources.resource_filename(
|
pkg_resources.resource_filename(
|
||||||
"deluge",
|
"deluge",
|
||||||
os.path.join("data", "pixmaps", "flags", country.lower() + ".png")))
|
os.path.join("data", "pixmaps", "flags", country.lower() + ".png")))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("Unable to load flag: %s", e)
|
log.debug("Unable to load flag: %s", e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.cached_flag_pixbufs[country]
|
return self.cached_flag_pixbufs[country]
|
||||||
|
|
||||||
def _on_get_torrent_status(self, status):
|
def _on_get_torrent_status(self, status):
|
||||||
new_ips = set()
|
new_ips = set()
|
||||||
for peer in status["peers"]:
|
for peer in status["peers"]:
|
||||||
|
@ -291,12 +291,12 @@ class PeersTab(Tab):
|
||||||
|
|
||||||
if icon != values[3]:
|
if icon != values[3]:
|
||||||
self.liststore.set_value(row, 7, icon)
|
self.liststore.set_value(row, 7, icon)
|
||||||
|
|
||||||
if peer["progress"] != values[4]:
|
if peer["progress"] != values[4]:
|
||||||
self.liststore.set_value(row, 8, peer["progress"])
|
self.liststore.set_value(row, 8, peer["progress"])
|
||||||
else:
|
else:
|
||||||
# Peer is not in list so we need to add it
|
# Peer is not in list so we need to add it
|
||||||
|
|
||||||
# Create an int IP address for sorting purposes
|
# Create an int IP address for sorting purposes
|
||||||
ip_int = sum([int(byte) << shift
|
ip_int = sum([int(byte) << shift
|
||||||
for byte, shift in izip(peer["ip"].split(":")[0].split("."), (24, 16, 8, 0))])
|
for byte, shift in izip(peer["ip"].split(":")[0].split("."), (24, 16, 8, 0))])
|
||||||
|
@ -305,20 +305,20 @@ class PeersTab(Tab):
|
||||||
icon = self.seed_pixbuf
|
icon = self.seed_pixbuf
|
||||||
else:
|
else:
|
||||||
icon = self.peer_pixbuf
|
icon = self.peer_pixbuf
|
||||||
|
|
||||||
row = self.liststore.append([
|
row = self.liststore.append([
|
||||||
self.get_flag_pixbuf(peer["country"]),
|
self.get_flag_pixbuf(peer["country"]),
|
||||||
peer["ip"],
|
peer["ip"],
|
||||||
peer["client"],
|
peer["client"],
|
||||||
peer["down_speed"],
|
peer["down_speed"],
|
||||||
peer["up_speed"],
|
peer["up_speed"],
|
||||||
peer["country"],
|
peer["country"],
|
||||||
ip_int,
|
ip_int,
|
||||||
icon,
|
icon,
|
||||||
peer["progress"]])
|
peer["progress"]])
|
||||||
|
|
||||||
self.peers[peer["ip"]] = row
|
self.peers[peer["ip"]] = row
|
||||||
|
|
||||||
# Now we need to remove any ips that were not in status["peers"] list
|
# Now we need to remove any ips that were not in status["peers"] list
|
||||||
for ip in set(self.peers.keys()).difference(new_ips):
|
for ip in set(self.peers.keys()).difference(new_ips):
|
||||||
self.liststore.remove(self.peers[ip])
|
self.liststore.remove(self.peers[ip])
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# pluginmanager.py
|
# pluginmanager.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -37,7 +37,7 @@ from deluge.ui.client import aclient as client
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
|
|
||||||
class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
component.Component):
|
component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "PluginManager", depend=["Signals"])
|
component.Component.__init__(self, "PluginManager", depend=["Signals"])
|
||||||
|
@ -56,14 +56,14 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
self.hooks[hook].append(function)
|
self.hooks[hook].append(function)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning("Plugin attempting to register invalid hook.")
|
log.warning("Plugin attempting to register invalid hook.")
|
||||||
|
|
||||||
def deregister_hook(self, hook, function):
|
def deregister_hook(self, hook, function):
|
||||||
"""Deregisters a hook function"""
|
"""Deregisters a hook function"""
|
||||||
try:
|
try:
|
||||||
self.hooks[hook].remove(function)
|
self.hooks[hook].remove(function)
|
||||||
except:
|
except:
|
||||||
log.warning("Unable to deregister hook %s", hook)
|
log.warning("Unable to deregister hook %s", hook)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start the plugin manager"""
|
"""Start the plugin manager"""
|
||||||
# Update the enabled_plugins from the core
|
# Update the enabled_plugins from the core
|
||||||
|
@ -72,13 +72,13 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
def stop(self):
|
def stop(self):
|
||||||
# Disable the plugins
|
# Disable the plugins
|
||||||
self.disable_plugins()
|
self.disable_plugins()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
# We call the plugins' update() method every second
|
# We call the plugins' update() method every second
|
||||||
for plugin in self.plugins.values():
|
for plugin in self.plugins.values():
|
||||||
if hasattr(plugin, "update"):
|
if hasattr(plugin, "update"):
|
||||||
plugin.update()
|
plugin.update()
|
||||||
|
|
||||||
def _on_get_enabled_plugins(self, enabled_plugins):
|
def _on_get_enabled_plugins(self, enabled_plugins):
|
||||||
log.debug("Core has these plugins enabled: %s", enabled_plugins)
|
log.debug("Core has these plugins enabled: %s", enabled_plugins)
|
||||||
for plugin in enabled_plugins:
|
for plugin in enabled_plugins:
|
||||||
|
@ -92,7 +92,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
log.debug("run_on_show_prefs")
|
log.debug("run_on_show_prefs")
|
||||||
for function in self.hooks["on_show_prefs"]:
|
for function in self.hooks["on_show_prefs"]:
|
||||||
function()
|
function()
|
||||||
|
|
||||||
def run_on_apply_prefs(self):
|
def run_on_apply_prefs(self):
|
||||||
"""This hook is run after the user clicks Apply or OK in the preferences
|
"""This hook is run after the user clicks Apply or OK in the preferences
|
||||||
dialog.
|
dialog.
|
||||||
|
@ -100,39 +100,39 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||||
log.debug("run_on_apply_prefs")
|
log.debug("run_on_apply_prefs")
|
||||||
for function in self.hooks["on_apply_prefs"]:
|
for function in self.hooks["on_apply_prefs"]:
|
||||||
function()
|
function()
|
||||||
|
|
||||||
## Plugin functions.. will likely move to own class..
|
## Plugin functions.. will likely move to own class..
|
||||||
|
|
||||||
def add_torrentview_text_column(self, *args, **kwargs):
|
def add_torrentview_text_column(self, *args, **kwargs):
|
||||||
return component.get("TorrentView").add_text_column(*args, **kwargs)
|
return component.get("TorrentView").add_text_column(*args, **kwargs)
|
||||||
|
|
||||||
def remove_torrentview_column(self, *args):
|
def remove_torrentview_column(self, *args):
|
||||||
return component.get("TorrentView").remove_column(*args)
|
return component.get("TorrentView").remove_column(*args)
|
||||||
|
|
||||||
def add_toolbar_separator(self):
|
def add_toolbar_separator(self):
|
||||||
return component.get("ToolBar").add_separator()
|
return component.get("ToolBar").add_separator()
|
||||||
|
|
||||||
def add_toolbar_button(self, *args, **kwargs):
|
def add_toolbar_button(self, *args, **kwargs):
|
||||||
return component.get("ToolBar").add_toolbutton(*args, **kwargs)
|
return component.get("ToolBar").add_toolbutton(*args, **kwargs)
|
||||||
|
|
||||||
def remove_toolbar_button(self, *args):
|
def remove_toolbar_button(self, *args):
|
||||||
return component.get("ToolBar").remove(*args)
|
return component.get("ToolBar").remove(*args)
|
||||||
|
|
||||||
def add_torrentmenu_menu(self, *args):
|
def add_torrentmenu_menu(self, *args):
|
||||||
return component.get("MenuBar").torrentmenu.append(*args)
|
return component.get("MenuBar").torrentmenu.append(*args)
|
||||||
|
|
||||||
def add_torrentmenu_separator(self):
|
def add_torrentmenu_separator(self):
|
||||||
return component.get("MenuBar").add_torrentmenu_separator()
|
return component.get("MenuBar").add_torrentmenu_separator()
|
||||||
|
|
||||||
def remove_torrentmenu_item(self, *args):
|
def remove_torrentmenu_item(self, *args):
|
||||||
return component.get("MenuBar").torrentmenu.remove(*args)
|
return component.get("MenuBar").torrentmenu.remove(*args)
|
||||||
|
|
||||||
def add_preferences_page(self, *args):
|
def add_preferences_page(self, *args):
|
||||||
return component.get("Preferences").add_page(*args)
|
return component.get("Preferences").add_page(*args)
|
||||||
|
|
||||||
def remove_preferences_page(self, *args):
|
def remove_preferences_page(self, *args):
|
||||||
return component.get("Preferences").remove_page(*args)
|
return component.get("Preferences").remove_page(*args)
|
||||||
|
|
||||||
def update_torrent_view(self, *args):
|
def update_torrent_view(self, *args):
|
||||||
return component.get("TorrentView").update(*args)
|
return component.get("TorrentView").update(*args)
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# queuedtorrents.py
|
# queuedtorrents.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -49,16 +49,16 @@ class QueuedTorrents(component.Component):
|
||||||
component.Component.__init__(self, "QueuedTorrents", depend=["StatusBar"])
|
component.Component.__init__(self, "QueuedTorrents", depend=["StatusBar"])
|
||||||
self.queue = []
|
self.queue = []
|
||||||
self.status_item = None
|
self.status_item = None
|
||||||
|
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
self.glade = gtk.glade.XML(
|
self.glade = gtk.glade.XML(
|
||||||
pkg_resources.resource_filename("deluge.ui.gtkui",
|
pkg_resources.resource_filename("deluge.ui.gtkui",
|
||||||
"glade/queuedtorrents.glade"))
|
"glade/queuedtorrents.glade"))
|
||||||
self.glade.get_widget("chk_autoadd").set_active(
|
self.glade.get_widget("chk_autoadd").set_active(
|
||||||
self.config["autoadd_queued"])
|
self.config["autoadd_queued"])
|
||||||
self.dialog = self.glade.get_widget("queued_torrents_dialog")
|
self.dialog = self.glade.get_widget("queued_torrents_dialog")
|
||||||
self.dialog.set_icon(common.get_logo(32))
|
self.dialog.set_icon(common.get_logo(32))
|
||||||
|
|
||||||
self.glade.signal_autoconnect({
|
self.glade.signal_autoconnect({
|
||||||
"on_button_remove_clicked": self.on_button_remove_clicked,
|
"on_button_remove_clicked": self.on_button_remove_clicked,
|
||||||
"on_button_clear_clicked": self.on_button_clear_clicked,
|
"on_button_clear_clicked": self.on_button_clear_clicked,
|
||||||
|
@ -66,14 +66,14 @@ class QueuedTorrents(component.Component):
|
||||||
"on_button_add_clicked": self.on_button_add_clicked,
|
"on_button_add_clicked": self.on_button_add_clicked,
|
||||||
"on_chk_autoadd_toggled": self.on_chk_autoadd_toggled
|
"on_chk_autoadd_toggled": self.on_chk_autoadd_toggled
|
||||||
})
|
})
|
||||||
|
|
||||||
self.treeview = self.glade.get_widget("treeview")
|
self.treeview = self.glade.get_widget("treeview")
|
||||||
self.treeview.append_column(
|
self.treeview.append_column(
|
||||||
gtk.TreeViewColumn(_("Torrent"), gtk.CellRendererText(), text=0))
|
gtk.TreeViewColumn(_("Torrent"), gtk.CellRendererText(), text=0))
|
||||||
|
|
||||||
self.liststore = gtk.ListStore(str, str)
|
self.liststore = gtk.ListStore(str, str)
|
||||||
self.treeview.set_model(self.liststore)
|
self.treeview.set_model(self.liststore)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.dialog.set_transient_for(component.get("MainWindow").window)
|
self.dialog.set_transient_for(component.get("MainWindow").window)
|
||||||
self.dialog.show()
|
self.dialog.show()
|
||||||
|
@ -91,12 +91,12 @@ class QueuedTorrents(component.Component):
|
||||||
# We only want the add button sensitive if we're connected to a host
|
# We only want the add button sensitive if we're connected to a host
|
||||||
self.glade.get_widget("button_add").set_sensitive(True)
|
self.glade.get_widget("button_add").set_sensitive(True)
|
||||||
self.run()
|
self.run()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
# We only want the add button sensitive if we're connected to a host
|
# We only want the add button sensitive if we're connected to a host
|
||||||
self.glade.get_widget("button_add").set_sensitive(False)
|
self.glade.get_widget("button_add").set_sensitive(False)
|
||||||
self.update_status_bar()
|
self.update_status_bar()
|
||||||
|
|
||||||
def add_to_queue(self, torrents):
|
def add_to_queue(self, torrents):
|
||||||
"""Adds the list of torrents to the queue"""
|
"""Adds the list of torrents to the queue"""
|
||||||
# Add to the queue while removing duplicates
|
# Add to the queue while removing duplicates
|
||||||
|
@ -106,10 +106,10 @@ class QueuedTorrents(component.Component):
|
||||||
self.liststore.clear()
|
self.liststore.clear()
|
||||||
for torrent in self.queue:
|
for torrent in self.queue:
|
||||||
self.liststore.append([os.path.split(torrent)[1], torrent])
|
self.liststore.append([os.path.split(torrent)[1], torrent])
|
||||||
|
|
||||||
# Update the status bar
|
# Update the status bar
|
||||||
self.update_status_bar()
|
self.update_status_bar()
|
||||||
|
|
||||||
def update_status_bar(self):
|
def update_status_bar(self):
|
||||||
"""Attempts to update status bar"""
|
"""Attempts to update status bar"""
|
||||||
# If there are no queued torrents.. remove statusbar widgets and return
|
# If there are no queued torrents.. remove statusbar widgets and return
|
||||||
|
@ -118,7 +118,7 @@ class QueuedTorrents(component.Component):
|
||||||
component.get("StatusBar").remove_item(self.status_item)
|
component.get("StatusBar").remove_item(self.status_item)
|
||||||
self.status_item = None
|
self.status_item = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
statusbar = component.get("StatusBar")
|
statusbar = component.get("StatusBar")
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
@ -126,18 +126,18 @@ class QueuedTorrents(component.Component):
|
||||||
# update it later.
|
# update it later.
|
||||||
gobject.timeout_add(100, self.update_status_bar)
|
gobject.timeout_add(100, self.update_status_bar)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Set the label text for statusbar
|
# Set the label text for statusbar
|
||||||
if len(self.queue) > 1:
|
if len(self.queue) > 1:
|
||||||
label = str(len(self.queue)) + _(" Torrents Queued")
|
label = str(len(self.queue)) + _(" Torrents Queued")
|
||||||
else:
|
else:
|
||||||
label = str(len(self.queue)) + _(" Torrent Queued")
|
label = str(len(self.queue)) + _(" Torrent Queued")
|
||||||
|
|
||||||
# Add the statusbar items if needed, or just modify the label if they
|
# Add the statusbar items if needed, or just modify the label if they
|
||||||
# have already been added.
|
# have already been added.
|
||||||
if self.status_item == None:
|
if self.status_item == None:
|
||||||
self.status_item = component.get("StatusBar").add_item(
|
self.status_item = component.get("StatusBar").add_item(
|
||||||
stock=gtk.STOCK_SORT_DESCENDING,
|
stock=gtk.STOCK_SORT_DESCENDING,
|
||||||
text=label,
|
text=label,
|
||||||
callback=self.on_statusbar_click)
|
callback=self.on_statusbar_click)
|
||||||
else:
|
else:
|
||||||
|
@ -145,11 +145,11 @@ class QueuedTorrents(component.Component):
|
||||||
|
|
||||||
# We return False so the timer stops
|
# We return False so the timer stops
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def on_statusbar_click(self, widget, event):
|
def on_statusbar_click(self, widget, event):
|
||||||
log.debug("on_statusbar_click")
|
log.debug("on_statusbar_click")
|
||||||
self.run()
|
self.run()
|
||||||
|
|
||||||
def on_button_remove_clicked(self, widget):
|
def on_button_remove_clicked(self, widget):
|
||||||
selected = self.treeview.get_selection().get_selected()[1]
|
selected = self.treeview.get_selection().get_selected()[1]
|
||||||
if selected != None:
|
if selected != None:
|
||||||
|
@ -187,13 +187,13 @@ class QueuedTorrents(component.Component):
|
||||||
component.get("AddTorrentDialog").show(self.config["focus_add_dialog"])
|
component.get("AddTorrentDialog").show(self.config["focus_add_dialog"])
|
||||||
else:
|
else:
|
||||||
client.add_torrent_file([torrent_path])
|
client.add_torrent_file([torrent_path])
|
||||||
|
|
||||||
self.liststore.foreach(add_torrent, None)
|
self.liststore.foreach(add_torrent, None)
|
||||||
del self.queue[:]
|
del self.queue[:]
|
||||||
self.dialog.hide()
|
self.dialog.hide()
|
||||||
self.update_status_bar()
|
self.update_status_bar()
|
||||||
|
|
||||||
def on_chk_autoadd_toggled(self, widget):
|
def on_chk_autoadd_toggled(self, widget):
|
||||||
self.config["autoadd_queued"] = widget.get_active()
|
self.config["autoadd_queued"] = widget.get_active()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# removetorrentdialog.py
|
# removetorrentdialog.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -45,27 +45,27 @@ class RemoveTorrentDialog:
|
||||||
self.torrent_ids = torrent_ids
|
self.torrent_ids = torrent_ids
|
||||||
self.remove_torrentfile = remove_torrentfile
|
self.remove_torrentfile = remove_torrentfile
|
||||||
self.remove_data = remove_data
|
self.remove_data = remove_data
|
||||||
|
|
||||||
self.glade = gtk.glade.XML(
|
self.glade = gtk.glade.XML(
|
||||||
pkg_resources.resource_filename("deluge.ui.gtkui",
|
pkg_resources.resource_filename("deluge.ui.gtkui",
|
||||||
"glade/remove_torrent_dialog.glade"))
|
"glade/remove_torrent_dialog.glade"))
|
||||||
|
|
||||||
self.dialog = self.glade.get_widget("remove_torrent_dialog")
|
self.dialog = self.glade.get_widget("remove_torrent_dialog")
|
||||||
self.dialog.set_icon(common.get_logo(32))
|
self.dialog.set_icon(common.get_logo(32))
|
||||||
self.dialog.set_transient_for(component.get("MainWindow").window)
|
self.dialog.set_transient_for(component.get("MainWindow").window)
|
||||||
|
|
||||||
self.glade.signal_autoconnect({
|
self.glade.signal_autoconnect({
|
||||||
"on_button_ok_clicked": self.on_button_ok_clicked,
|
"on_button_ok_clicked": self.on_button_ok_clicked,
|
||||||
"on_button_cancel_clicked": self.on_button_cancel_clicked
|
"on_button_cancel_clicked": self.on_button_cancel_clicked
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(self.torrent_ids) > 1:
|
if len(self.torrent_ids) > 1:
|
||||||
# We need to pluralize the dialog
|
# We need to pluralize the dialog
|
||||||
self.dialog.set_title("Remove Torrents?")
|
self.dialog.set_title("Remove Torrents?")
|
||||||
self.glade.get_widget("label_title").set_markup(
|
self.glade.get_widget("label_title").set_markup(
|
||||||
_("<big><b>Are you sure you want to remove the selected torrents?</b></big>"))
|
_("<big><b>Are you sure you want to remove the selected torrents?</b></big>"))
|
||||||
self.glade.get_widget("button_ok").set_label(_("Remove Selected Torrents"))
|
self.glade.get_widget("button_ok").set_label(_("Remove Selected Torrents"))
|
||||||
|
|
||||||
if self.remove_torrentfile or self.remove_data:
|
if self.remove_torrentfile or self.remove_data:
|
||||||
self.glade.get_widget("hseparator1").show()
|
self.glade.get_widget("hseparator1").show()
|
||||||
if self.remove_torrentfile:
|
if self.remove_torrentfile:
|
||||||
|
@ -78,14 +78,14 @@ class RemoveTorrentDialog:
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
return
|
return
|
||||||
self.dialog.show()
|
self.dialog.show()
|
||||||
|
|
||||||
def on_button_ok_clicked(self, widget):
|
def on_button_ok_clicked(self, widget):
|
||||||
client.remove_torrent(
|
client.remove_torrent(
|
||||||
self.torrent_ids, self.remove_torrentfile, self.remove_data)
|
self.torrent_ids, self.remove_torrentfile, self.remove_data)
|
||||||
# Unselect all to avoid issues with the selection changed event
|
# Unselect all to avoid issues with the selection changed event
|
||||||
component.get("TorrentView").treeview.get_selection().unselect_all()
|
component.get("TorrentView").treeview.get_selection().unselect_all()
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
def on_button_cancel_clicked(self, widget):
|
def on_button_cancel_clicked(self, widget):
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
|
|
|
@ -53,17 +53,17 @@ class SideBar(component.Component):
|
||||||
self.hpaned = glade.get_widget("hpaned")
|
self.hpaned = glade.get_widget("hpaned")
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
#self.hpaned_position = self.hpaned.get_position()
|
#self.hpaned_position = self.hpaned.get_position()
|
||||||
|
|
||||||
# Tabs holds references to the Tab widgets by their name
|
# Tabs holds references to the Tab widgets by their name
|
||||||
self.tabs = {}
|
self.tabs = {}
|
||||||
|
|
||||||
# Hide if necessary
|
# Hide if necessary
|
||||||
self.visible(self.config["show_sidebar"])
|
self.visible(self.config["show_sidebar"])
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
log.debug("hpaned.position: %s", self.hpaned.get_position())
|
log.debug("hpaned.position: %s", self.hpaned.get_position())
|
||||||
self.config["sidebar_position"] = self.hpaned.get_position()
|
self.config["sidebar_position"] = self.hpaned.get_position()
|
||||||
|
|
||||||
def visible(self, visible):
|
def visible(self, visible):
|
||||||
if visible:
|
if visible:
|
||||||
if self.config["sidebar_position"]:
|
if self.config["sidebar_position"]:
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# signals.py
|
# signals.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -46,21 +46,21 @@ class Signals(component.Component):
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
self.config["signal_port"] = self.receiver.get_port()
|
self.config["signal_port"] = self.receiver.get_port()
|
||||||
self.config.save()
|
self.config.save()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.receiver.set_remote(not client.is_localhost())
|
self.receiver.set_remote(not client.is_localhost())
|
||||||
self.receiver.run()
|
self.receiver.run()
|
||||||
|
|
||||||
self.receiver.connect_to_signal("torrent_added",
|
self.receiver.connect_to_signal("torrent_added",
|
||||||
self.torrent_added_signal)
|
self.torrent_added_signal)
|
||||||
self.receiver.connect_to_signal("torrent_removed",
|
self.receiver.connect_to_signal("torrent_removed",
|
||||||
self.torrent_removed_signal)
|
self.torrent_removed_signal)
|
||||||
self.receiver.connect_to_signal("torrent_paused", self.torrent_paused)
|
self.receiver.connect_to_signal("torrent_paused", self.torrent_paused)
|
||||||
self.receiver.connect_to_signal("torrent_resumed",
|
self.receiver.connect_to_signal("torrent_resumed",
|
||||||
self.torrent_resumed)
|
self.torrent_resumed)
|
||||||
self.receiver.connect_to_signal("torrent_all_paused",
|
self.receiver.connect_to_signal("torrent_all_paused",
|
||||||
self.torrent_all_paused)
|
self.torrent_all_paused)
|
||||||
self.receiver.connect_to_signal("torrent_all_resumed",
|
self.receiver.connect_to_signal("torrent_all_resumed",
|
||||||
self.torrent_all_resumed)
|
self.torrent_all_resumed)
|
||||||
self.receiver.connect_to_signal("config_value_changed",
|
self.receiver.connect_to_signal("config_value_changed",
|
||||||
self.config_value_changed)
|
self.config_value_changed)
|
||||||
|
@ -76,30 +76,30 @@ class Signals(component.Component):
|
||||||
self.torrent_state_changed)
|
self.torrent_state_changed)
|
||||||
self.receiver.connect_to_signal("torrent_finished",
|
self.receiver.connect_to_signal("torrent_finished",
|
||||||
self.torrent_finished)
|
self.torrent_finished)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
try:
|
try:
|
||||||
self.receiver.shutdown()
|
self.receiver.shutdown()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def connect_to_signal(self, signal, callback):
|
def connect_to_signal(self, signal, callback):
|
||||||
"""Connects a callback to a signal"""
|
"""Connects a callback to a signal"""
|
||||||
self.receiver.connect_to_signal(signal, callback)
|
self.receiver.connect_to_signal(signal, callback)
|
||||||
|
|
||||||
def torrent_finished(self, torrent_id):
|
def torrent_finished(self, torrent_id):
|
||||||
log.debug("torrent_finished signal received..")
|
log.debug("torrent_finished signal received..")
|
||||||
log.debug("torrent id: %s", torrent_id)
|
log.debug("torrent id: %s", torrent_id)
|
||||||
from deluge.ui.gtkui.notification import Notification
|
from deluge.ui.gtkui.notification import Notification
|
||||||
Notification().notify(torrent_id)
|
Notification().notify(torrent_id)
|
||||||
|
|
||||||
def torrent_added_signal(self, torrent_id):
|
def torrent_added_signal(self, torrent_id):
|
||||||
log.debug("torrent_added signal received..")
|
log.debug("torrent_added signal received..")
|
||||||
log.debug("torrent id: %s", torrent_id)
|
log.debug("torrent id: %s", torrent_id)
|
||||||
# Add the torrent to the treeview
|
# Add the torrent to the treeview
|
||||||
component.get("TorrentView").add_row(torrent_id)
|
component.get("TorrentView").add_row(torrent_id)
|
||||||
component.get("TorrentView").mark_dirty(torrent_id)
|
component.get("TorrentView").mark_dirty(torrent_id)
|
||||||
|
|
||||||
def torrent_removed_signal(self, torrent_id):
|
def torrent_removed_signal(self, torrent_id):
|
||||||
log.debug("torrent_remove signal received..")
|
log.debug("torrent_remove signal received..")
|
||||||
log.debug("torrent id: %s", torrent_id)
|
log.debug("torrent id: %s", torrent_id)
|
||||||
|
@ -112,43 +112,43 @@ class Signals(component.Component):
|
||||||
component.get("TorrentView").update()
|
component.get("TorrentView").update()
|
||||||
component.get("ToolBar").update_buttons()
|
component.get("ToolBar").update_buttons()
|
||||||
component.get("MenuBar").update_menu()
|
component.get("MenuBar").update_menu()
|
||||||
|
|
||||||
def torrent_resumed(self, torrent_id):
|
def torrent_resumed(self, torrent_id):
|
||||||
log.debug("torrent_resumed signal received..")
|
log.debug("torrent_resumed signal received..")
|
||||||
component.get("TorrentView").mark_dirty(torrent_id)
|
component.get("TorrentView").mark_dirty(torrent_id)
|
||||||
component.get("TorrentView").update()
|
component.get("TorrentView").update()
|
||||||
component.get("ToolBar").update_buttons()
|
component.get("ToolBar").update_buttons()
|
||||||
component.get("MenuBar").update_menu()
|
component.get("MenuBar").update_menu()
|
||||||
|
|
||||||
def torrent_all_paused(self):
|
def torrent_all_paused(self):
|
||||||
log.debug("torrent_all_paused signal received..")
|
log.debug("torrent_all_paused signal received..")
|
||||||
component.get("TorrentView").mark_dirty()
|
component.get("TorrentView").mark_dirty()
|
||||||
component.get("TorrentView").update()
|
component.get("TorrentView").update()
|
||||||
component.get("ToolBar").update_buttons()
|
component.get("ToolBar").update_buttons()
|
||||||
component.get("MenuBar").update_menu()
|
component.get("MenuBar").update_menu()
|
||||||
|
|
||||||
def torrent_all_resumed(self):
|
def torrent_all_resumed(self):
|
||||||
log.debug("torrent_all_resumed signal received..")
|
log.debug("torrent_all_resumed signal received..")
|
||||||
component.get("TorrentView").mark_dirty()
|
component.get("TorrentView").mark_dirty()
|
||||||
component.get("TorrentView").update()
|
component.get("TorrentView").update()
|
||||||
component.get("ToolBar").update_buttons()
|
component.get("ToolBar").update_buttons()
|
||||||
component.get("MenuBar").update_menu()
|
component.get("MenuBar").update_menu()
|
||||||
|
|
||||||
def config_value_changed(self, key, value):
|
def config_value_changed(self, key, value):
|
||||||
log.debug("config_value_changed signal received..")
|
log.debug("config_value_changed signal received..")
|
||||||
component.get("StatusBar").config_value_changed(key, value)
|
component.get("StatusBar").config_value_changed(key, value)
|
||||||
component.get("SystemTray").config_value_changed(key, value)
|
component.get("SystemTray").config_value_changed(key, value)
|
||||||
|
|
||||||
def torrent_queue_changed(self):
|
def torrent_queue_changed(self):
|
||||||
log.debug("torrent_queue_changed signal received..")
|
log.debug("torrent_queue_changed signal received..")
|
||||||
component.get("TorrentView").mark_dirty()
|
component.get("TorrentView").mark_dirty()
|
||||||
component.get("TorrentView").update()
|
component.get("TorrentView").update()
|
||||||
|
|
||||||
def torrent_resume_at_stop_ratio(self):
|
def torrent_resume_at_stop_ratio(self):
|
||||||
log.debug("torrent_resume_at_stop_ratio")
|
log.debug("torrent_resume_at_stop_ratio")
|
||||||
component.get("StatusBar").display_warning(
|
component.get("StatusBar").display_warning(
|
||||||
text=_("Torrent is past stop ratio."))
|
text=_("Torrent is past stop ratio."))
|
||||||
|
|
||||||
def new_version_available(self, value):
|
def new_version_available(self, value):
|
||||||
log.debug("new_version_available: %s", value)
|
log.debug("new_version_available: %s", value)
|
||||||
if self.config["show_new_releases"]:
|
if self.config["show_new_releases"]:
|
||||||
|
@ -159,7 +159,7 @@ class Signals(component.Component):
|
||||||
log.debug("args_from_external: %s", value)
|
log.debug("args_from_external: %s", value)
|
||||||
import ipcinterface
|
import ipcinterface
|
||||||
ipcinterface.process_args(value)
|
ipcinterface.process_args(value)
|
||||||
|
|
||||||
def torrent_state_changed(self, value):
|
def torrent_state_changed(self, value):
|
||||||
log.debug("torrent_state_changed: %s", value)
|
log.debug("torrent_state_changed: %s", value)
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# statusbar.py
|
# statusbar.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -63,40 +63,40 @@ class StatusBarItem:
|
||||||
# Add text
|
# Add text
|
||||||
if text != None:
|
if text != None:
|
||||||
self.set_text(text)
|
self.set_text(text)
|
||||||
|
|
||||||
if callback != None:
|
if callback != None:
|
||||||
self.set_callback(callback)
|
self.set_callback(callback)
|
||||||
|
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def set_callback(self, callback):
|
def set_callback(self, callback):
|
||||||
self._ebox.connect("button-press-event", callback)
|
self._ebox.connect("button-press-event", callback)
|
||||||
|
|
||||||
def show_all(self):
|
def show_all(self):
|
||||||
self._ebox.show()
|
self._ebox.show()
|
||||||
self._hbox.show()
|
self._hbox.show()
|
||||||
self._image.show()
|
self._image.show()
|
||||||
self._label.show()
|
self._label.show()
|
||||||
|
|
||||||
def set_image_from_file(self, image):
|
def set_image_from_file(self, image):
|
||||||
self._image.set_from_file(image)
|
self._image.set_from_file(image)
|
||||||
|
|
||||||
def set_image_from_stock(self, stock):
|
def set_image_from_stock(self, stock):
|
||||||
self._image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
|
self._image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
|
||||||
|
|
||||||
def set_text(self, text):
|
def set_text(self, text):
|
||||||
if self._label.get_text() != text:
|
if self._label.get_text() != text:
|
||||||
self._label.set_text(text)
|
self._label.set_text(text)
|
||||||
|
|
||||||
def get_widgets(self):
|
def get_widgets(self):
|
||||||
return self._widgets()
|
return self._widgets()
|
||||||
|
|
||||||
def get_eventbox(self):
|
def get_eventbox(self):
|
||||||
return self._ebox
|
return self._ebox
|
||||||
|
|
||||||
def get_text(self):
|
def get_text(self):
|
||||||
return self._label.get_text()
|
return self._label.get_text()
|
||||||
|
|
||||||
class StatusBar(component.Component):
|
class StatusBar(component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "StatusBar", interval=3000)
|
component.Component.__init__(self, "StatusBar", interval=3000)
|
||||||
|
@ -104,7 +104,7 @@ class StatusBar(component.Component):
|
||||||
self.statusbar = self.window.main_glade.get_widget("statusbar")
|
self.statusbar = self.window.main_glade.get_widget("statusbar")
|
||||||
self.tooltips = gtk.Tooltips()
|
self.tooltips = gtk.Tooltips()
|
||||||
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 = -1
|
||||||
self.num_connections = 0
|
self.num_connections = 0
|
||||||
|
@ -115,7 +115,7 @@ class StatusBar(component.Component):
|
||||||
self.dht_nodes = 0
|
self.dht_nodes = 0
|
||||||
self.dht_status = False
|
self.dht_status = False
|
||||||
self.health = False
|
self.health = False
|
||||||
|
|
||||||
self.config_value_changed_dict = {
|
self.config_value_changed_dict = {
|
||||||
"max_connections_global": self._on_max_connections_global,
|
"max_connections_global": self._on_max_connections_global,
|
||||||
"max_download_speed": self._on_max_download_speed,
|
"max_download_speed": self._on_max_download_speed,
|
||||||
|
@ -132,11 +132,11 @@ class StatusBar(component.Component):
|
||||||
self.statusbar.show_all()
|
self.statusbar.show_all()
|
||||||
# Create the not connected item
|
# Create the not connected item
|
||||||
self.not_connected_item = StatusBarItem(
|
self.not_connected_item = StatusBarItem(
|
||||||
stock=gtk.STOCK_STOP, text=_("Not Connected"),
|
stock=gtk.STOCK_STOP, text=_("Not Connected"),
|
||||||
callback=self._on_notconnected_item_clicked)
|
callback=self._on_notconnected_item_clicked)
|
||||||
# Show the not connected status bar
|
# Show the not connected status bar
|
||||||
self.show_not_connected()
|
self.show_not_connected()
|
||||||
|
|
||||||
# Hide if necessary
|
# Hide if necessary
|
||||||
self.visible(self.config["show_statusbar"])
|
self.visible(self.config["show_statusbar"])
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ class StatusBar(component.Component):
|
||||||
callback=self._on_health_icon_clicked)
|
callback=self._on_health_icon_clicked)
|
||||||
|
|
||||||
self.health = False
|
self.health = False
|
||||||
|
|
||||||
# Get some config values
|
# Get some config values
|
||||||
client.get_config_value(
|
client.get_config_value(
|
||||||
self._on_max_connections_global, "max_connections_global")
|
self._on_max_connections_global, "max_connections_global")
|
||||||
|
@ -180,9 +180,9 @@ class StatusBar(component.Component):
|
||||||
client.get_config_value(
|
client.get_config_value(
|
||||||
self._on_dht, "dht")
|
self._on_dht, "dht")
|
||||||
client.get_health(self._on_get_health)
|
client.get_health(self._on_get_health)
|
||||||
|
|
||||||
self.send_status_request()
|
self.send_status_request()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
# When stopped, we just show the not connected thingy
|
# When stopped, we just show the not connected thingy
|
||||||
try:
|
try:
|
||||||
|
@ -194,20 +194,20 @@ class StatusBar(component.Component):
|
||||||
self.remove_item(self.health_item)
|
self.remove_item(self.health_item)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("Unable to remove StatusBar item: %s", e)
|
log.debug("Unable to remove StatusBar item: %s", e)
|
||||||
self.show_not_connected()
|
self.show_not_connected()
|
||||||
|
|
||||||
def visible(self, visible):
|
def visible(self, visible):
|
||||||
if visible:
|
if visible:
|
||||||
self.statusbar.show()
|
self.statusbar.show()
|
||||||
else:
|
else:
|
||||||
self.statusbar.hide()
|
self.statusbar.hide()
|
||||||
|
|
||||||
self.config["show_statusbar"] = visible
|
self.config["show_statusbar"] = visible
|
||||||
|
|
||||||
def show_not_connected(self):
|
def show_not_connected(self):
|
||||||
self.hbox.pack_start(
|
self.hbox.pack_start(
|
||||||
self.not_connected_item.get_eventbox(), expand=False, fill=False)
|
self.not_connected_item.get_eventbox(), expand=False, fill=False)
|
||||||
|
|
||||||
def add_item(self, image=None, stock=None, text=None, callback=None, tooltip=None):
|
def add_item(self, image=None, stock=None, text=None, callback=None, tooltip=None):
|
||||||
"""Adds an item to the status bar"""
|
"""Adds an item to the status bar"""
|
||||||
# The return tuple.. we return whatever widgets we add
|
# The return tuple.. we return whatever widgets we add
|
||||||
|
@ -216,7 +216,7 @@ class StatusBar(component.Component):
|
||||||
if tooltip:
|
if tooltip:
|
||||||
self.tooltips.set_tip(item.get_eventbox(), tooltip)
|
self.tooltips.set_tip(item.get_eventbox(), tooltip)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def remove_item(self, item):
|
def remove_item(self, item):
|
||||||
"""Removes an item from the statusbar"""
|
"""Removes an item from the statusbar"""
|
||||||
if item.get_eventbox() in self.hbox.get_children():
|
if item.get_eventbox() in self.hbox.get_children():
|
||||||
|
@ -230,7 +230,7 @@ class StatusBar(component.Component):
|
||||||
item = self.add_item(image, stock, text, callback)
|
item = self.add_item(image, stock, text, callback)
|
||||||
# Start a timer to remove this item in seconds
|
# Start a timer to remove this item in seconds
|
||||||
gobject.timeout_add(seconds * 1000, self.remove_item, item)
|
gobject.timeout_add(seconds * 1000, self.remove_item, item)
|
||||||
|
|
||||||
def display_warning(self, text, callback=None):
|
def display_warning(self, text, callback=None):
|
||||||
"""Displays a warning to the user in the status bar"""
|
"""Displays a warning to the user in the status bar"""
|
||||||
if text not in self.current_warnings:
|
if text not in self.current_warnings:
|
||||||
|
@ -238,16 +238,16 @@ class StatusBar(component.Component):
|
||||||
stock=gtk.STOCK_DIALOG_WARNING, text=text, callback=callback)
|
stock=gtk.STOCK_DIALOG_WARNING, text=text, callback=callback)
|
||||||
self.current_warnings.append(text)
|
self.current_warnings.append(text)
|
||||||
gobject.timeout_add(3000, self.remove_warning, item)
|
gobject.timeout_add(3000, self.remove_warning, item)
|
||||||
|
|
||||||
def remove_warning(self, item):
|
def remove_warning(self, item):
|
||||||
self.current_warnings.remove(item.get_text())
|
self.current_warnings.remove(item.get_text())
|
||||||
self.remove_item(item)
|
self.remove_item(item)
|
||||||
|
|
||||||
def clear_statusbar(self):
|
def clear_statusbar(self):
|
||||||
def remove(child):
|
def remove(child):
|
||||||
self.hbox.remove(child)
|
self.hbox.remove(child)
|
||||||
self.hbox.foreach(remove)
|
self.hbox.foreach(remove)
|
||||||
|
|
||||||
def send_status_request(self):
|
def send_status_request(self):
|
||||||
# Sends an async request for data from the core
|
# Sends an async request for data from the core
|
||||||
client.get_num_connections(self._on_get_num_connections)
|
client.get_num_connections(self._on_get_num_connections)
|
||||||
|
@ -262,22 +262,22 @@ class StatusBar(component.Component):
|
||||||
def config_value_changed(self, key, value):
|
def config_value_changed(self, key, value):
|
||||||
"""This is called when we received a config_value_changed signal from
|
"""This is called when we received a config_value_changed signal 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 = max_connections
|
||||||
self.update_connections_label()
|
self.update_connections_label()
|
||||||
|
|
||||||
def _on_get_num_connections(self, num_connections):
|
def _on_get_num_connections(self, num_connections):
|
||||||
self.num_connections = num_connections
|
self.num_connections = num_connections
|
||||||
self.update_connections_label()
|
self.update_connections_label()
|
||||||
|
|
||||||
def _on_get_dht_nodes(self, dht_nodes):
|
def _on_get_dht_nodes(self, dht_nodes):
|
||||||
self.dht_nodes = dht_nodes
|
self.dht_nodes = dht_nodes
|
||||||
self.update_dht_label()
|
self.update_dht_label()
|
||||||
|
|
||||||
def _on_dht(self, value):
|
def _on_dht(self, value):
|
||||||
self.dht_status = value
|
self.dht_status = value
|
||||||
if value:
|
if value:
|
||||||
|
@ -290,7 +290,7 @@ class StatusBar(component.Component):
|
||||||
def _on_max_download_speed(self, max_download_speed):
|
def _on_max_download_speed(self, max_download_speed):
|
||||||
self.max_download_speed = max_download_speed
|
self.max_download_speed = max_download_speed
|
||||||
self.update_download_label()
|
self.update_download_label()
|
||||||
|
|
||||||
def _on_get_download_rate(self, download_rate):
|
def _on_get_download_rate(self, download_rate):
|
||||||
self.download_rate = deluge.common.fsize(download_rate)
|
self.download_rate = deluge.common.fsize(download_rate)
|
||||||
self.update_download_label()
|
self.update_download_label()
|
||||||
|
@ -298,16 +298,16 @@ class StatusBar(component.Component):
|
||||||
def _on_max_upload_speed(self, max_upload_speed):
|
def _on_max_upload_speed(self, max_upload_speed):
|
||||||
self.max_upload_speed = max_upload_speed
|
self.max_upload_speed = max_upload_speed
|
||||||
self.update_upload_label()
|
self.update_upload_label()
|
||||||
|
|
||||||
def _on_get_upload_rate(self, upload_rate):
|
def _on_get_upload_rate(self, upload_rate):
|
||||||
self.upload_rate = deluge.common.fsize(upload_rate)
|
self.upload_rate = deluge.common.fsize(upload_rate)
|
||||||
self.update_upload_label()
|
self.update_upload_label()
|
||||||
|
|
||||||
def _on_get_health(self, value):
|
def _on_get_health(self, value):
|
||||||
self.health = value
|
self.health = value
|
||||||
if self.health:
|
if self.health:
|
||||||
self.remove_item(self.health_item)
|
self.remove_item(self.health_item)
|
||||||
|
|
||||||
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 < 0:
|
||||||
|
@ -316,11 +316,11 @@ class StatusBar(component.Component):
|
||||||
label_string = "%s (%s)" % (self.num_connections, self.max_connections)
|
label_string = "%s (%s)" % (self.num_connections, self.max_connections)
|
||||||
|
|
||||||
self.connections_item.set_text(label_string)
|
self.connections_item.set_text(label_string)
|
||||||
|
|
||||||
def update_dht_label(self):
|
def update_dht_label(self):
|
||||||
# Set the max connections label
|
# Set the max connections label
|
||||||
self.dht_item.set_text("%s" % (self.dht_nodes))
|
self.dht_item.set_text("%s" % (self.dht_nodes))
|
||||||
|
|
||||||
def update_download_label(self):
|
def update_download_label(self):
|
||||||
# Set the download speed label
|
# Set the download speed label
|
||||||
if self.max_download_speed < 0:
|
if self.max_download_speed < 0:
|
||||||
|
@ -330,7 +330,7 @@ class StatusBar(component.Component):
|
||||||
self.download_rate, self.max_download_speed, _("KiB/s"))
|
self.download_rate, self.max_download_speed, _("KiB/s"))
|
||||||
|
|
||||||
self.download_item.set_text(label_string)
|
self.download_item.set_text(label_string)
|
||||||
|
|
||||||
def update_upload_label(self):
|
def update_upload_label(self):
|
||||||
# Set the upload speed label
|
# Set the upload speed label
|
||||||
if self.max_upload_speed < 0:
|
if self.max_upload_speed < 0:
|
||||||
|
@ -338,16 +338,16 @@ class StatusBar(component.Component):
|
||||||
else:
|
else:
|
||||||
label_string = "%s/s (%s %s)" % (
|
label_string = "%s/s (%s %s)" % (
|
||||||
self.upload_rate, self.max_upload_speed, _("KiB/s"))
|
self.upload_rate, self.max_upload_speed, _("KiB/s"))
|
||||||
|
|
||||||
self.upload_item.set_text(label_string)
|
self.upload_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 _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"],
|
||||||
self._on_set_download_speed,
|
self._on_set_download_speed,
|
||||||
self.max_download_speed,
|
self.max_download_speed,
|
||||||
_("KiB/s"), show_notset=True, show_other=True)
|
_("KiB/s"), show_notset=True, show_other=True)
|
||||||
|
@ -368,14 +368,14 @@ class StatusBar(component.Component):
|
||||||
value = float(widget.get_children()[0].get_text().split(" ")[0])
|
value = float(widget.get_children()[0].get_text().split(" ")[0])
|
||||||
|
|
||||||
log.debug("value: %s", value)
|
log.debug("value: %s", value)
|
||||||
|
|
||||||
# Set the config in the core
|
# Set the config in the core
|
||||||
if value != self.max_download_speed:
|
if value != self.max_download_speed:
|
||||||
client.set_config({"max_download_speed": value})
|
client.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(
|
||||||
self.config["tray_upload_speed_list"],
|
self.config["tray_upload_speed_list"],
|
||||||
self._on_set_upload_speed,
|
self._on_set_upload_speed,
|
||||||
self.max_upload_speed,
|
self.max_upload_speed,
|
||||||
_("KiB/s"), show_notset=True, show_other=True)
|
_("KiB/s"), show_notset=True, show_other=True)
|
||||||
|
@ -396,22 +396,22 @@ class StatusBar(component.Component):
|
||||||
value = float(widget.get_children()[0].get_text().split(" ")[0])
|
value = float(widget.get_children()[0].get_text().split(" ")[0])
|
||||||
|
|
||||||
log.debug("value: %s", value)
|
log.debug("value: %s", value)
|
||||||
|
|
||||||
# Set the config in the core
|
# Set the config in the core
|
||||||
if value != self.max_upload_speed:
|
if value != self.max_upload_speed:
|
||||||
client.set_config({"max_upload_speed": value})
|
client.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, 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")
|
||||||
|
|
||||||
if widget.get_name() == _("Unlimited"):
|
if widget.get_name() == _("Unlimited"):
|
||||||
value = -1
|
value = -1
|
||||||
elif widget.get_name() == _("Other..."):
|
elif widget.get_name() == _("Other..."):
|
||||||
|
@ -423,11 +423,11 @@ class StatusBar(component.Component):
|
||||||
value = int(widget.get_children()[0].get_text().split(" ")[0])
|
value = int(widget.get_children()[0].get_text().split(" ")[0])
|
||||||
|
|
||||||
log.debug("value: %s", value)
|
log.debug("value: %s", value)
|
||||||
|
|
||||||
# Set the config in the core
|
# Set the config in the core
|
||||||
if value != self.max_connections:
|
if value != self.max_connections:
|
||||||
client.set_config({"max_connections_global": value})
|
client.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")
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# systemtray.py
|
# systemtray.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -59,7 +59,7 @@ class SystemTray(component.Component):
|
||||||
"separatormenuitem3",
|
"separatormenuitem3",
|
||||||
"separatormenuitem4"
|
"separatormenuitem4"
|
||||||
]
|
]
|
||||||
self.config.register_set_function("enable_system_tray",
|
self.config.register_set_function("enable_system_tray",
|
||||||
self.on_enable_system_tray_set)
|
self.on_enable_system_tray_set)
|
||||||
|
|
||||||
self.max_download_speed = -1.0
|
self.max_download_speed = -1.0
|
||||||
|
@ -71,12 +71,12 @@ class SystemTray(component.Component):
|
||||||
"max_download_speed": self._on_max_download_speed,
|
"max_download_speed": self._on_max_download_speed,
|
||||||
"max_upload_speed": self._on_max_upload_speed
|
"max_upload_speed": self._on_max_upload_speed
|
||||||
}
|
}
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
"""Enables the system tray icon."""
|
"""Enables the system tray icon."""
|
||||||
log.debug("Enabling the system tray icon..")
|
log.debug("Enabling the system tray icon..")
|
||||||
self.tray_glade = gtk.glade.XML(
|
self.tray_glade = gtk.glade.XML(
|
||||||
pkg_resources.resource_filename("deluge.ui.gtkui",
|
pkg_resources.resource_filename("deluge.ui.gtkui",
|
||||||
"glade/tray_menu.glade"))
|
"glade/tray_menu.glade"))
|
||||||
|
|
||||||
if deluge.common.windows_check():
|
if deluge.common.windows_check():
|
||||||
|
@ -88,11 +88,11 @@ class SystemTray(component.Component):
|
||||||
except:
|
except:
|
||||||
log.warning("Update PyGTK to 2.10 or greater for SystemTray..")
|
log.warning("Update PyGTK to 2.10 or greater for SystemTray..")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.tray.connect("activate", self.on_tray_clicked)
|
self.tray.connect("activate", self.on_tray_clicked)
|
||||||
self.tray.connect("popup-menu", self.on_tray_popup)
|
self.tray.connect("popup-menu", self.on_tray_popup)
|
||||||
|
|
||||||
|
|
||||||
self.tray_glade.signal_autoconnect({
|
self.tray_glade.signal_autoconnect({
|
||||||
"on_menuitem_show_deluge_activate": \
|
"on_menuitem_show_deluge_activate": \
|
||||||
self.on_menuitem_show_deluge_activate,
|
self.on_menuitem_show_deluge_activate,
|
||||||
|
@ -106,7 +106,7 @@ class SystemTray(component.Component):
|
||||||
"on_menuitem_quitdaemon_activate": \
|
"on_menuitem_quitdaemon_activate": \
|
||||||
self.on_menuitem_quitdaemon_activate
|
self.on_menuitem_quitdaemon_activate
|
||||||
})
|
})
|
||||||
|
|
||||||
self.tray_menu = self.tray_glade.get_widget("tray_menu")
|
self.tray_menu = self.tray_glade.get_widget("tray_menu")
|
||||||
|
|
||||||
self.tray_glade.get_widget("download-limit-image").set_from_file(
|
self.tray_glade.get_widget("download-limit-image").set_from_file(
|
||||||
|
@ -119,12 +119,12 @@ class SystemTray(component.Component):
|
||||||
self.hide_widget_list.remove("separatormenuitem4")
|
self.hide_widget_list.remove("separatormenuitem4")
|
||||||
self.tray_glade.get_widget("menuitem_quitdaemon").hide()
|
self.tray_glade.get_widget("menuitem_quitdaemon").hide()
|
||||||
self.tray_glade.get_widget("separatormenuitem4").hide()
|
self.tray_glade.get_widget("separatormenuitem4").hide()
|
||||||
|
|
||||||
if client.get_core_uri() == None:
|
if client.get_core_uri() == None:
|
||||||
# Hide menu widgets because we're not connected to a host.
|
# Hide menu widgets because we're not connected to a host.
|
||||||
for widget in self.hide_widget_list:
|
for widget in self.hide_widget_list:
|
||||||
self.tray_glade.get_widget(widget).hide()
|
self.tray_glade.get_widget(widget).hide()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if self.config["enable_system_tray"]:
|
if self.config["enable_system_tray"]:
|
||||||
# Show widgets in the hide list because we've connected to a host
|
# Show widgets in the hide list because we've connected to a host
|
||||||
|
@ -133,7 +133,7 @@ class SystemTray(component.Component):
|
||||||
|
|
||||||
# Build the bandwidth speed limit menus
|
# Build the bandwidth speed limit menus
|
||||||
self.build_tray_bwsetsubmenu()
|
self.build_tray_bwsetsubmenu()
|
||||||
|
|
||||||
# Get some config values
|
# Get some config values
|
||||||
client.get_config_value(
|
client.get_config_value(
|
||||||
self._on_max_download_speed, "max_download_speed")
|
self._on_max_download_speed, "max_download_speed")
|
||||||
|
@ -148,10 +148,10 @@ class SystemTray(component.Component):
|
||||||
self.tray_glade.get_widget(widget).hide()
|
self.tray_glade.get_widget(widget).hide()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("Unable to hide system tray menu widgets: %s", e)
|
log.debug("Unable to hide system tray menu widgets: %s", e)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
self.tray.set_visible(False)
|
self.tray.set_visible(False)
|
||||||
|
|
||||||
def send_status_request(self):
|
def send_status_request(self):
|
||||||
client.get_download_rate(self._on_get_download_rate)
|
client.get_download_rate(self._on_get_download_rate)
|
||||||
client.get_upload_rate(self._on_get_upload_rate)
|
client.get_upload_rate(self._on_get_upload_rate)
|
||||||
|
@ -159,31 +159,31 @@ class SystemTray(component.Component):
|
||||||
def config_value_changed(self, key, value):
|
def config_value_changed(self, key, value):
|
||||||
"""This is called when we received a config_value_changed signal from
|
"""This is called when we received a config_value_changed signal 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_download_speed(self, max_download_speed):
|
def _on_max_download_speed(self, max_download_speed):
|
||||||
if self.max_download_speed != max_download_speed:
|
if self.max_download_speed != max_download_speed:
|
||||||
self.max_download_speed = max_download_speed
|
self.max_download_speed = max_download_speed
|
||||||
self.build_tray_bwsetsubmenu()
|
self.build_tray_bwsetsubmenu()
|
||||||
|
|
||||||
def _on_get_download_rate(self, download_rate):
|
def _on_get_download_rate(self, download_rate):
|
||||||
self.download_rate = deluge.common.fsize(download_rate)
|
self.download_rate = deluge.common.fsize(download_rate)
|
||||||
|
|
||||||
def _on_max_upload_speed(self, max_upload_speed):
|
def _on_max_upload_speed(self, max_upload_speed):
|
||||||
if self.max_upload_speed != max_upload_speed:
|
if self.max_upload_speed != max_upload_speed:
|
||||||
self.max_upload_speed = max_upload_speed
|
self.max_upload_speed = max_upload_speed
|
||||||
self.build_tray_bwsetsubmenu()
|
self.build_tray_bwsetsubmenu()
|
||||||
|
|
||||||
def _on_get_upload_rate(self, upload_rate):
|
def _on_get_upload_rate(self, upload_rate):
|
||||||
self.upload_rate = deluge.common.fsize(upload_rate)
|
self.upload_rate = deluge.common.fsize(upload_rate)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
# Set the tool tip text
|
# Set the tool tip text
|
||||||
max_download_speed = self.max_download_speed
|
max_download_speed = self.max_download_speed
|
||||||
max_upload_speed = self.max_upload_speed
|
max_upload_speed = self.max_upload_speed
|
||||||
|
|
||||||
if max_download_speed == -1:
|
if max_download_speed == -1:
|
||||||
max_download_speed = _("Unlimited")
|
max_download_speed = _("Unlimited")
|
||||||
else:
|
else:
|
||||||
|
@ -192,39 +192,39 @@ class SystemTray(component.Component):
|
||||||
max_upload_speed = _("Unlimited")
|
max_upload_speed = _("Unlimited")
|
||||||
else:
|
else:
|
||||||
max_upload_speed = "%s KiB/s" % (max_upload_speed)
|
max_upload_speed = "%s KiB/s" % (max_upload_speed)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
self.send_status_request()
|
self.send_status_request()
|
||||||
|
|
||||||
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.tray_setbwdown,
|
self.config["tray_download_speed_list"], self.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.tray_setbwup,
|
self.config["tray_upload_speed_list"], self.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.tray_glade.get_widget("menuitem_download_limit").set_submenu(
|
self.tray_glade.get_widget("menuitem_download_limit").set_submenu(
|
||||||
submenu_bwdownset)
|
submenu_bwdownset)
|
||||||
self.tray_glade.get_widget("menuitem_upload_limit").set_submenu(
|
self.tray_glade.get_widget("menuitem_upload_limit").set_submenu(
|
||||||
submenu_bwupset)
|
submenu_bwupset)
|
||||||
|
|
||||||
# Show the sub-menus for all to see
|
# Show the sub-menus for all to see
|
||||||
submenu_bwdownset.show_all()
|
submenu_bwdownset.show_all()
|
||||||
submenu_bwupset.show_all()
|
submenu_bwupset.show_all()
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
"""Disables the system tray icon."""
|
"""Disables the system tray icon."""
|
||||||
log.debug("Disabling the system tray icon..")
|
log.debug("Disabling the system tray icon..")
|
||||||
|
@ -243,18 +243,18 @@ class SystemTray(component.Component):
|
||||||
# If self.tray is not defined then ignore. This happens when the
|
# If self.tray is not defined then ignore. This happens when the
|
||||||
# tray icon is not being used.
|
# tray icon is not being used.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_enable_system_tray_set(self, key, value):
|
def on_enable_system_tray_set(self, key, value):
|
||||||
"""Called whenever the 'enable_system_tray' config key is modified"""
|
"""Called whenever the 'enable_system_tray' config key is modified"""
|
||||||
if value:
|
if value:
|
||||||
self.enable()
|
self.enable()
|
||||||
else:
|
else:
|
||||||
self.disable()
|
self.disable()
|
||||||
|
|
||||||
def on_tray_clicked(self, icon):
|
def on_tray_clicked(self, icon):
|
||||||
"""Called when the tray icon is left clicked."""
|
"""Called when the tray icon is left clicked."""
|
||||||
self.blink(False)
|
self.blink(False)
|
||||||
|
|
||||||
if self.window.active():
|
if self.window.active():
|
||||||
self.window.hide()
|
self.window.hide()
|
||||||
else:
|
else:
|
||||||
|
@ -262,20 +262,20 @@ class SystemTray(component.Component):
|
||||||
if not self.unlock_tray():
|
if not self.unlock_tray():
|
||||||
return
|
return
|
||||||
self.window.present()
|
self.window.present()
|
||||||
|
|
||||||
def on_tray_popup(self, status_icon, button, activate_time):
|
def on_tray_popup(self, status_icon, button, activate_time):
|
||||||
"""Called when the tray icon is right clicked."""
|
"""Called when the tray icon is right clicked."""
|
||||||
self.blink(False)
|
self.blink(False)
|
||||||
|
|
||||||
if self.window.visible():
|
if self.window.visible():
|
||||||
self.tray_glade.get_widget("menuitem_show_deluge").set_active(True)
|
self.tray_glade.get_widget("menuitem_show_deluge").set_active(True)
|
||||||
else:
|
else:
|
||||||
self.tray_glade.get_widget("menuitem_show_deluge").set_active(False)
|
self.tray_glade.get_widget("menuitem_show_deluge").set_active(False)
|
||||||
|
|
||||||
popup_function = gtk.status_icon_position_menu
|
popup_function = gtk.status_icon_position_menu
|
||||||
if deluge.common.windows_check():
|
if deluge.common.windows_check():
|
||||||
popup_function = None
|
popup_function = None
|
||||||
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):
|
||||||
|
@ -287,20 +287,20 @@ class SystemTray(component.Component):
|
||||||
self.window.present()
|
self.window.present()
|
||||||
elif not menuitem.get_active() and self.window.visible():
|
elif not menuitem.get_active() and self.window.visible():
|
||||||
self.window.hide()
|
self.window.hide()
|
||||||
|
|
||||||
def on_menuitem_add_torrent_activate(self, menuitem):
|
def on_menuitem_add_torrent_activate(self, menuitem):
|
||||||
log.debug("on_menuitem_add_torrent_activate")
|
log.debug("on_menuitem_add_torrent_activate")
|
||||||
from addtorrentdialog import AddTorrentDialog
|
from addtorrentdialog import AddTorrentDialog
|
||||||
client.add_torrent_file(AddTorrentDialog().show())
|
client.add_torrent_file(AddTorrentDialog().show())
|
||||||
|
|
||||||
def on_menuitem_pause_all_activate(self, menuitem):
|
def on_menuitem_pause_all_activate(self, menuitem):
|
||||||
log.debug("on_menuitem_pause_all_activate")
|
log.debug("on_menuitem_pause_all_activate")
|
||||||
client.pause_all_torrents()
|
client.pause_all_torrents()
|
||||||
|
|
||||||
def on_menuitem_resume_all_activate(self, menuitem):
|
def on_menuitem_resume_all_activate(self, menuitem):
|
||||||
log.debug("on_menuitem_resume_all_activate")
|
log.debug("on_menuitem_resume_all_activate")
|
||||||
client.resume_all_torrents()
|
client.resume_all_torrents()
|
||||||
|
|
||||||
def on_menuitem_quit_activate(self, menuitem):
|
def on_menuitem_quit_activate(self, menuitem):
|
||||||
log.debug("on_menuitem_quit_activate")
|
log.debug("on_menuitem_quit_activate")
|
||||||
if self.config["lock_tray"]:
|
if self.config["lock_tray"]:
|
||||||
|
@ -311,7 +311,7 @@ class SystemTray(component.Component):
|
||||||
client.shutdown()
|
client.shutdown()
|
||||||
|
|
||||||
self.window.quit()
|
self.window.quit()
|
||||||
|
|
||||||
def on_menuitem_quitdaemon_activate(self, menuitem):
|
def on_menuitem_quitdaemon_activate(self, menuitem):
|
||||||
log.debug("on_menuitem_quitdaemon_activate")
|
log.debug("on_menuitem_quitdaemon_activate")
|
||||||
if self.config["lock_tray"]:
|
if self.config["lock_tray"]:
|
||||||
|
@ -320,54 +320,54 @@ class SystemTray(component.Component):
|
||||||
|
|
||||||
client.shutdown()
|
client.shutdown()
|
||||||
self.window.quit()
|
self.window.quit()
|
||||||
|
|
||||||
def tray_setbwdown(self, widget, data=None):
|
def tray_setbwdown(self, widget, data=None):
|
||||||
self.setbwlimit(widget, _("Download"), "max_download_speed",
|
self.setbwlimit(widget, _("Download"), "max_download_speed",
|
||||||
"tray_download_speed_list", self.max_download_speed)
|
"tray_download_speed_list", self.max_download_speed)
|
||||||
|
|
||||||
def tray_setbwup(self, widget, data=None):
|
def tray_setbwup(self, widget, data=None):
|
||||||
self.setbwlimit(widget, _("Upload"), "max_upload_speed",
|
self.setbwlimit(widget, _("Upload"), "max_upload_speed",
|
||||||
"tray_upload_speed_list", self.max_upload_speed)
|
"tray_upload_speed_list", self.max_upload_speed)
|
||||||
|
|
||||||
def setbwlimit(self, widget, string, core_key, ui_key, default):
|
def setbwlimit(self, widget, string, core_key, ui_key, default):
|
||||||
"""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().rstrip(" " + _("KiB/s"))
|
value = widget.get_children()[0].get_text().rstrip(" " + _("KiB/s"))
|
||||||
if value == _("Unlimited"):
|
if value == _("Unlimited"):
|
||||||
value = -1
|
value = -1
|
||||||
|
|
||||||
if value == _("Other..."):
|
if value == _("Other..."):
|
||||||
value = common.show_other_dialog(
|
value = common.show_other_dialog(
|
||||||
string + " Speed (KiB/s):", default)
|
string + " Speed (KiB/s):", default)
|
||||||
if value == None:
|
if value == None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set the config in the core
|
# Set the config in the core
|
||||||
value = float(value)
|
value = float(value)
|
||||||
config_to_set = {core_key: value}
|
config_to_set = {core_key: value}
|
||||||
client.set_config(config_to_set)
|
client.set_config(config_to_set)
|
||||||
|
|
||||||
self.build_tray_bwsetsubmenu()
|
self.build_tray_bwsetsubmenu()
|
||||||
|
|
||||||
def unlock_tray(self, is_showing_dlg=[False]):
|
def unlock_tray(self, is_showing_dlg=[False]):
|
||||||
try:
|
try:
|
||||||
from hashlib import sha1 as sha_hash
|
from hashlib import sha1 as sha_hash
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from sha import new as sha_hash
|
from sha import new as sha_hash
|
||||||
|
|
||||||
log.debug("Show tray lock dialog")
|
log.debug("Show tray lock dialog")
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
if is_showing_dlg[0]:
|
if is_showing_dlg[0]:
|
||||||
return
|
return
|
||||||
is_showing_dlg[0] = True
|
is_showing_dlg[0] = True
|
||||||
|
|
||||||
entered_pass = gtk.Entry(25)
|
entered_pass = gtk.Entry(25)
|
||||||
entered_pass.set_activates_default(True)
|
entered_pass.set_activates_default(True)
|
||||||
entered_pass.set_width_chars(25)
|
entered_pass.set_width_chars(25)
|
||||||
entered_pass.set_visibility(False)
|
entered_pass.set_visibility(False)
|
||||||
entered_pass.show()
|
entered_pass.show()
|
||||||
tray_lock = gtk.Dialog(title=_("Deluge is locked"), parent=None,
|
tray_lock = gtk.Dialog(title=_("Deluge is locked"), parent=None,
|
||||||
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK,
|
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK,
|
||||||
gtk.RESPONSE_ACCEPT))
|
gtk.RESPONSE_ACCEPT))
|
||||||
label = gtk.Label(_("Deluge is password protected.\nTo show the Deluge \
|
label = gtk.Label(_("Deluge is password protected.\nTo show the Deluge \
|
||||||
window, please enter your password"))
|
window, please enter your password"))
|
||||||
|
@ -385,6 +385,6 @@ window, please enter your password"))
|
||||||
result = True
|
result = True
|
||||||
tray_lock.destroy()
|
tray_lock.destroy()
|
||||||
is_showing_dlg[0] = False
|
is_showing_dlg[0] = False
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# toolbar.py
|
# toolbar.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -70,7 +70,7 @@ class ToolBar(component.Component):
|
||||||
"toolbutton_queue_up",
|
"toolbutton_queue_up",
|
||||||
"toolbutton_queue_down"
|
"toolbutton_queue_down"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Set the Remove Torrent toolbuttons drop-down menu
|
# Set the Remove Torrent toolbuttons drop-down menu
|
||||||
tb_remove = self.window.main_glade.get_widget("toolbutton_remove")
|
tb_remove = self.window.main_glade.get_widget("toolbutton_remove")
|
||||||
tb_remove.set_menu(
|
tb_remove.set_menu(
|
||||||
|
@ -78,7 +78,7 @@ class ToolBar(component.Component):
|
||||||
|
|
||||||
if self.config["classic_mode"]:
|
if self.config["classic_mode"]:
|
||||||
self.window.main_glade.get_widget("toolbutton_connectionmanager").hide()
|
self.window.main_glade.get_widget("toolbutton_connectionmanager").hide()
|
||||||
|
|
||||||
# Hide if necessary
|
# Hide if necessary
|
||||||
self.visible(self.config["show_toolbar"])
|
self.visible(self.config["show_toolbar"])
|
||||||
|
|
||||||
|
@ -86,19 +86,19 @@ class ToolBar(component.Component):
|
||||||
for widget in self.change_sensitivity:
|
for widget in self.change_sensitivity:
|
||||||
self.window.main_glade.get_widget(widget).set_sensitive(True)
|
self.window.main_glade.get_widget(widget).set_sensitive(True)
|
||||||
self.update_buttons()
|
self.update_buttons()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
for widget in self.change_sensitivity:
|
for widget in self.change_sensitivity:
|
||||||
self.window.main_glade.get_widget(widget).set_sensitive(False)
|
self.window.main_glade.get_widget(widget).set_sensitive(False)
|
||||||
|
|
||||||
def visible(self, visible):
|
def visible(self, visible):
|
||||||
if visible:
|
if visible:
|
||||||
self.toolbar.show()
|
self.toolbar.show()
|
||||||
else:
|
else:
|
||||||
self.toolbar.hide()
|
self.toolbar.hide()
|
||||||
|
|
||||||
self.config["show_toolbar"] = visible
|
self.config["show_toolbar"] = visible
|
||||||
|
|
||||||
def add_toolbutton(self, callback, label=None, image=None, stock=None,
|
def add_toolbutton(self, callback, label=None, image=None, stock=None,
|
||||||
tooltip=None):
|
tooltip=None):
|
||||||
"""Adds a toolbutton to the toolbar"""
|
"""Adds a toolbutton to the toolbar"""
|
||||||
|
@ -115,15 +115,15 @@ class ToolBar(component.Component):
|
||||||
toolbutton.set_tooltip_text(tooltip)
|
toolbutton.set_tooltip_text(tooltip)
|
||||||
# Connect the 'clicked' event callback
|
# Connect the 'clicked' event callback
|
||||||
toolbutton.connect("clicked", callback)
|
toolbutton.connect("clicked", callback)
|
||||||
|
|
||||||
# Append the button to the toolbar
|
# Append the button to the toolbar
|
||||||
self.toolbar.insert(toolbutton, -1)
|
self.toolbar.insert(toolbutton, -1)
|
||||||
|
|
||||||
# Show the new toolbutton
|
# Show the new toolbutton
|
||||||
toolbutton.show_all()
|
toolbutton.show_all()
|
||||||
|
|
||||||
return toolbutton
|
return toolbutton
|
||||||
|
|
||||||
def add_separator(self, position=None):
|
def add_separator(self, position=None):
|
||||||
"""Adds a separator toolitem"""
|
"""Adds a separator toolitem"""
|
||||||
sep = gtk.SeparatorToolItem()
|
sep = gtk.SeparatorToolItem()
|
||||||
|
@ -134,13 +134,13 @@ class ToolBar(component.Component):
|
||||||
self.toolbar.insert(sep, -1)
|
self.toolbar.insert(sep, -1)
|
||||||
|
|
||||||
sep.show()
|
sep.show()
|
||||||
|
|
||||||
return sep
|
return sep
|
||||||
|
|
||||||
def remove(self, widget):
|
def remove(self, widget):
|
||||||
"""Removes a widget from the toolbar"""
|
"""Removes a widget from the toolbar"""
|
||||||
self.toolbar.remove(widget)
|
self.toolbar.remove(widget)
|
||||||
|
|
||||||
### Callbacks ###
|
### Callbacks ###
|
||||||
def on_toolbutton_add_clicked(self, data):
|
def on_toolbutton_add_clicked(self, data):
|
||||||
log.debug("on_toolbutton_add_clicked")
|
log.debug("on_toolbutton_add_clicked")
|
||||||
|
@ -156,7 +156,7 @@ class ToolBar(component.Component):
|
||||||
log.debug("on_toolbutton_pause_clicked")
|
log.debug("on_toolbutton_pause_clicked")
|
||||||
# Use the menubar's callbacks
|
# Use the menubar's callbacks
|
||||||
component.get("MenuBar").on_menuitem_pause_activate(data)
|
component.get("MenuBar").on_menuitem_pause_activate(data)
|
||||||
|
|
||||||
def on_toolbutton_resume_clicked(self, data):
|
def on_toolbutton_resume_clicked(self, data):
|
||||||
log.debug("on_toolbutton_resume_clicked")
|
log.debug("on_toolbutton_resume_clicked")
|
||||||
# Use the menubar's calbacks
|
# Use the menubar's calbacks
|
||||||
|
@ -171,18 +171,18 @@ class ToolBar(component.Component):
|
||||||
log.debug("on_toolbutton_connectionmanager_clicked")
|
log.debug("on_toolbutton_connectionmanager_clicked")
|
||||||
# Use the menubar's callbacks
|
# Use the menubar's callbacks
|
||||||
component.get("MenuBar").on_menuitem_connectionmanager_activate(data)
|
component.get("MenuBar").on_menuitem_connectionmanager_activate(data)
|
||||||
|
|
||||||
def on_toolbutton_queue_up_clicked(self, data):
|
def on_toolbutton_queue_up_clicked(self, data):
|
||||||
log.debug("on_toolbutton_queue_up_clicked")
|
log.debug("on_toolbutton_queue_up_clicked")
|
||||||
component.get("MenuBar").on_menuitem_queue_up_activate(data)
|
component.get("MenuBar").on_menuitem_queue_up_activate(data)
|
||||||
|
|
||||||
def on_toolbutton_queue_down_clicked(self, data):
|
def on_toolbutton_queue_down_clicked(self, data):
|
||||||
log.debug("on_toolbutton_queue_down_clicked")
|
log.debug("on_toolbutton_queue_down_clicked")
|
||||||
component.get("MenuBar").on_menuitem_queue_down_activate(data)
|
component.get("MenuBar").on_menuitem_queue_down_activate(data)
|
||||||
|
|
||||||
def update_buttons(self, action=None, torrent_id=None):
|
def update_buttons(self, action=None, torrent_id=None):
|
||||||
if action == None:
|
if action == None:
|
||||||
# If all the selected torrents are paused, then disable the 'Pause'
|
# If all the selected torrents are paused, then disable the 'Pause'
|
||||||
# button.
|
# button.
|
||||||
# The same goes for the 'Resume' button.
|
# The same goes for the 'Resume' button.
|
||||||
pause = False
|
pause = False
|
||||||
|
@ -205,18 +205,18 @@ class ToolBar(component.Component):
|
||||||
if pause and resume:
|
if pause and resume:
|
||||||
break
|
break
|
||||||
|
|
||||||
# Enable the 'Remove Torrent' button only if there's some selected
|
# Enable the 'Remove Torrent' button only if there's some selected
|
||||||
# torrent.
|
# torrent.
|
||||||
remove = (len(selected) > 0)
|
remove = (len(selected) > 0)
|
||||||
|
|
||||||
for name, sensitive in (("toolbutton_pause", pause),
|
for name, sensitive in (("toolbutton_pause", pause),
|
||||||
("toolbutton_resume", resume),
|
("toolbutton_resume", resume),
|
||||||
("toolbutton_remove", remove)):
|
("toolbutton_remove", remove)):
|
||||||
self.window.main_glade.get_widget(name).set_sensitive(sensitive)
|
self.window.main_glade.get_widget(name).set_sensitive(sensitive)
|
||||||
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pause = False
|
pause = False
|
||||||
resume = False
|
resume = False
|
||||||
if action == "paused":
|
if action == "paused":
|
||||||
|
@ -231,5 +231,5 @@ class ToolBar(component.Component):
|
||||||
self.window.main_glade.get_widget("toolbutton_resume").set_sensitive(resume)
|
self.window.main_glade.get_widget("toolbutton_resume").set_sensitive(resume)
|
||||||
else:
|
else:
|
||||||
self.update_buttons()
|
self.update_buttons()
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# torrentdetails.py
|
# torrentdetails.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -49,38 +49,38 @@ class Tab:
|
||||||
self.is_visible = True
|
self.is_visible = True
|
||||||
self.position = -1
|
self.position = -1
|
||||||
self.weight = -1
|
self.weight = -1
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
def get_child_widget(self):
|
def get_child_widget(self):
|
||||||
parent = self._child_widget.get_parent()
|
parent = self._child_widget.get_parent()
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
parent.remove(self._child_widget)
|
parent.remove(self._child_widget)
|
||||||
|
|
||||||
return self._child_widget
|
return self._child_widget
|
||||||
|
|
||||||
def get_tab_label(self):
|
def get_tab_label(self):
|
||||||
parent = self._tab_label.get_parent()
|
parent = self._tab_label.get_parent()
|
||||||
log.debug("parent: %s", parent)
|
log.debug("parent: %s", parent)
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
parent.remove(self._tab_label)
|
parent.remove(self._tab_label)
|
||||||
|
|
||||||
return self._tab_label
|
return self._tab_label
|
||||||
|
|
||||||
class TorrentDetails(component.Component):
|
class TorrentDetails(component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "TorrentDetails", interval=2000)
|
component.Component.__init__(self, "TorrentDetails", interval=2000)
|
||||||
self.window = component.get("MainWindow")
|
self.window = component.get("MainWindow")
|
||||||
glade = self.window.main_glade
|
glade = self.window.main_glade
|
||||||
|
|
||||||
self.notebook = glade.get_widget("torrent_info")
|
self.notebook = glade.get_widget("torrent_info")
|
||||||
|
|
||||||
# This is the menu item we'll attach the tabs checklist menu to
|
# This is the menu item we'll attach the tabs checklist menu to
|
||||||
self.menu_tabs = glade.get_widget("menu_tabs")
|
self.menu_tabs = glade.get_widget("menu_tabs")
|
||||||
|
|
||||||
self.notebook.connect("switch-page", self._on_switch_page)
|
self.notebook.connect("switch-page", self._on_switch_page)
|
||||||
|
|
||||||
# Tabs holds references to the Tab objects by their name
|
# Tabs holds references to the Tab objects by their name
|
||||||
self.tabs = {}
|
self.tabs = {}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ class TorrentDetails(component.Component):
|
||||||
"Peers": PeersTab,
|
"Peers": PeersTab,
|
||||||
"Options": OptionsTab
|
"Options": OptionsTab
|
||||||
}
|
}
|
||||||
|
|
||||||
# tab_name, visible
|
# tab_name, visible
|
||||||
default_order = [
|
default_order = [
|
||||||
("Statistics", True),
|
("Statistics", True),
|
||||||
|
@ -107,7 +107,7 @@ class TorrentDetails(component.Component):
|
||||||
("Peers", True),
|
("Peers", True),
|
||||||
("Options", True)
|
("Options", True)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Get the state from saved file
|
# Get the state from saved file
|
||||||
state = self.load_state()
|
state = self.load_state()
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ class TorrentDetails(component.Component):
|
||||||
log.debug("Old tabs.state, using default..")
|
log.debug("Old tabs.state, using default..")
|
||||||
state = None
|
state = None
|
||||||
break
|
break
|
||||||
|
|
||||||
# The state is a list of tab_names in the order they should appear
|
# The state is a list of tab_names in the order they should appear
|
||||||
if state == None:
|
if state == None:
|
||||||
# Set the default order
|
# Set the default order
|
||||||
|
@ -126,15 +126,15 @@ class TorrentDetails(component.Component):
|
||||||
# Add the tabs in the order from the state
|
# Add the tabs in the order from the state
|
||||||
for tab_name, visible in state:
|
for tab_name, visible in state:
|
||||||
self.add_tab(default_tabs[tab_name]())
|
self.add_tab(default_tabs[tab_name]())
|
||||||
|
|
||||||
# Hide any of the non-visible ones
|
# Hide any of the non-visible ones
|
||||||
for tab_name, visible in state:
|
for tab_name, visible in state:
|
||||||
if not visible:
|
if not visible:
|
||||||
self.hide_tab(tab_name)
|
self.hide_tab(tab_name)
|
||||||
|
|
||||||
# Generate the checklist menu
|
# Generate the checklist menu
|
||||||
self.generate_menu()
|
self.generate_menu()
|
||||||
|
|
||||||
def add_tab(self, tab_object, position=-1, generate_menu=True):
|
def add_tab(self, tab_object, position=-1, generate_menu=True):
|
||||||
"""Adds a tab object to the notebook."""
|
"""Adds a tab object to the notebook."""
|
||||||
self.tabs[tab_object.get_name()] = tab_object
|
self.tabs[tab_object.get_name()] = tab_object
|
||||||
|
@ -145,18 +145,18 @@ class TorrentDetails(component.Component):
|
||||||
|
|
||||||
tab_object.position = pos
|
tab_object.position = pos
|
||||||
tab_object.weight = pos
|
tab_object.weight = pos
|
||||||
|
|
||||||
# Regenerate positions if an insert occured
|
# Regenerate positions if an insert occured
|
||||||
if position > -1:
|
if position > -1:
|
||||||
self.regenerate_positions()
|
self.regenerate_positions()
|
||||||
|
|
||||||
if generate_menu:
|
if generate_menu:
|
||||||
self.generate_menu()
|
self.generate_menu()
|
||||||
|
|
||||||
if not self.notebook.get_property("visible"):
|
if not self.notebook.get_property("visible"):
|
||||||
# If the notebook isn't visible, show it
|
# If the notebook isn't visible, show it
|
||||||
self.visible(True)
|
self.visible(True)
|
||||||
|
|
||||||
def regenerate_positions(self):
|
def regenerate_positions(self):
|
||||||
"""This will sync up the positions in the tab, with the position stored
|
"""This will sync up the positions in the tab, with the position stored
|
||||||
in the tab object"""
|
in the tab object"""
|
||||||
|
@ -171,7 +171,7 @@ class TorrentDetails(component.Component):
|
||||||
del self.tabs[tab_name]
|
del self.tabs[tab_name]
|
||||||
self.regenerate_positions()
|
self.regenerate_positions()
|
||||||
self.generate_menu()
|
self.generate_menu()
|
||||||
|
|
||||||
# If there are no tabs visible, then do not show the notebook
|
# If there are no tabs visible, then do not show the notebook
|
||||||
if len(self.tabs) == 0:
|
if len(self.tabs) == 0:
|
||||||
self.visible(False)
|
self.visible(False)
|
||||||
|
@ -181,13 +181,13 @@ class TorrentDetails(component.Component):
|
||||||
log.debug("n_pages: %s", self.notebook.get_n_pages())
|
log.debug("n_pages: %s", self.notebook.get_n_pages())
|
||||||
for n in xrange(self.notebook.get_n_pages() - 1, -1, -1):
|
for n in xrange(self.notebook.get_n_pages() - 1, -1, -1):
|
||||||
self.notebook.remove_page(n)
|
self.notebook.remove_page(n)
|
||||||
|
|
||||||
for tab in self.tabs:
|
for tab in self.tabs:
|
||||||
self.tabs[tab].is_visible = False
|
self.tabs[tab].is_visible = False
|
||||||
log.debug("n_pages: %s", self.notebook.get_n_pages())
|
log.debug("n_pages: %s", self.notebook.get_n_pages())
|
||||||
self.generate_menu()
|
self.generate_menu()
|
||||||
self.visible(False)
|
self.visible(False)
|
||||||
|
|
||||||
def show_all_tabs(self):
|
def show_all_tabs(self):
|
||||||
"""Shows all tabs"""
|
"""Shows all tabs"""
|
||||||
for tab in self.tabs:
|
for tab in self.tabs:
|
||||||
|
@ -201,7 +201,7 @@ class TorrentDetails(component.Component):
|
||||||
if not self.notebook.get_property("visible"):
|
if not self.notebook.get_property("visible"):
|
||||||
# If the notebook isn't visible, show it
|
# If the notebook isn't visible, show it
|
||||||
self.visible(True)
|
self.visible(True)
|
||||||
|
|
||||||
def hide_tab(self, tab_name):
|
def hide_tab(self, tab_name):
|
||||||
"""Hides tab by name"""
|
"""Hides tab by name"""
|
||||||
self.notebook.remove_page(self.tabs[tab_name].position)
|
self.notebook.remove_page(self.tabs[tab_name].position)
|
||||||
|
@ -217,11 +217,11 @@ class TorrentDetails(component.Component):
|
||||||
# Determine insert position based on weight
|
# Determine insert position based on weight
|
||||||
# weights is a list of visible tab names in weight order
|
# weights is a list of visible tab names in weight order
|
||||||
weights = []
|
weights = []
|
||||||
|
|
||||||
for tab in self.tabs:
|
for tab in self.tabs:
|
||||||
if self.tabs[tab].is_visible:
|
if self.tabs[tab].is_visible:
|
||||||
weights.append((self.tabs[tab].weight, self.tabs[tab].get_name()))
|
weights.append((self.tabs[tab].weight, self.tabs[tab].get_name()))
|
||||||
|
|
||||||
weights.sort()
|
weights.sort()
|
||||||
log.debug("weights: %s", weights)
|
log.debug("weights: %s", weights)
|
||||||
position = self.tabs[tab_name].position
|
position = self.tabs[tab_name].position
|
||||||
|
@ -230,7 +230,7 @@ class TorrentDetails(component.Component):
|
||||||
if w[0] >= self.tabs[tab_name].weight:
|
if w[0] >= self.tabs[tab_name].weight:
|
||||||
position = self.tabs[w[1]].position
|
position = self.tabs[w[1]].position
|
||||||
break
|
break
|
||||||
|
|
||||||
log.debug("position: %s", position)
|
log.debug("position: %s", position)
|
||||||
self.notebook.insert_page(
|
self.notebook.insert_page(
|
||||||
self.tabs[tab_name].get_child_widget(),
|
self.tabs[tab_name].get_child_widget(),
|
||||||
|
@ -239,7 +239,7 @@ class TorrentDetails(component.Component):
|
||||||
self.tabs[tab_name].is_visible = True
|
self.tabs[tab_name].is_visible = True
|
||||||
self.regenerate_positions()
|
self.regenerate_positions()
|
||||||
self.generate_menu()
|
self.generate_menu()
|
||||||
|
|
||||||
def generate_menu(self):
|
def generate_menu(self):
|
||||||
"""Generates the checklist menu for all the tabs and attaches it"""
|
"""Generates the checklist menu for all the tabs and attaches it"""
|
||||||
menu = gtk.Menu()
|
menu = gtk.Menu()
|
||||||
|
@ -252,35 +252,35 @@ class TorrentDetails(component.Component):
|
||||||
all_tabs = False
|
all_tabs = False
|
||||||
break
|
break
|
||||||
menuitem.set_active(all_tabs)
|
menuitem.set_active(all_tabs)
|
||||||
menuitem.connect("toggled", self._on_menuitem_toggled)
|
menuitem.connect("toggled", self._on_menuitem_toggled)
|
||||||
|
|
||||||
menu.append(menuitem)
|
menu.append(menuitem)
|
||||||
|
|
||||||
menuitem = gtk.SeparatorMenuItem()
|
menuitem = gtk.SeparatorMenuItem()
|
||||||
menu.append(menuitem)
|
menu.append(menuitem)
|
||||||
|
|
||||||
# Create a list in order of tabs to create menu
|
# Create a list in order of tabs to create menu
|
||||||
menuitem_list = []
|
menuitem_list = []
|
||||||
for tab_name in self.tabs:
|
for tab_name in self.tabs:
|
||||||
menuitem_list.append((self.tabs[tab_name].weight, tab_name))
|
menuitem_list.append((self.tabs[tab_name].weight, tab_name))
|
||||||
menuitem_list.sort()
|
menuitem_list.sort()
|
||||||
|
|
||||||
for pos, name in menuitem_list:
|
for pos, name in menuitem_list:
|
||||||
menuitem = gtk.CheckMenuItem(name)
|
menuitem = gtk.CheckMenuItem(name)
|
||||||
menuitem.set_active(self.tabs[name].is_visible)
|
menuitem.set_active(self.tabs[name].is_visible)
|
||||||
menuitem.connect("toggled", self._on_menuitem_toggled)
|
menuitem.connect("toggled", self._on_menuitem_toggled)
|
||||||
menu.append(menuitem)
|
menu.append(menuitem)
|
||||||
|
|
||||||
self.menu_tabs.set_submenu(menu)
|
self.menu_tabs.set_submenu(menu)
|
||||||
self.menu_tabs.show_all()
|
self.menu_tabs.show_all()
|
||||||
|
|
||||||
def visible(self, visible):
|
def visible(self, visible):
|
||||||
if visible:
|
if visible:
|
||||||
self.notebook.show()
|
self.notebook.show()
|
||||||
else:
|
else:
|
||||||
self.notebook.hide()
|
self.notebook.hide()
|
||||||
self.window.vpaned.set_position(-1)
|
self.window.vpaned.set_position(-1)
|
||||||
|
|
||||||
def set_tab_visible(self, tab_name, visible):
|
def set_tab_visible(self, tab_name, visible):
|
||||||
"""Sets the tab to visible"""
|
"""Sets the tab to visible"""
|
||||||
log.debug("set_tab_visible name: %s visible: %s", tab_name, visible)
|
log.debug("set_tab_visible name: %s visible: %s", tab_name, visible)
|
||||||
|
@ -288,14 +288,14 @@ class TorrentDetails(component.Component):
|
||||||
self.show_tab(tab_name)
|
self.show_tab(tab_name)
|
||||||
elif not visible and self.tabs[tab_name].is_visible:
|
elif not visible and self.tabs[tab_name].is_visible:
|
||||||
self.hide_tab(tab_name)
|
self.hide_tab(tab_name)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
for tab in self.tabs.values():
|
for tab in self.tabs.values():
|
||||||
try:
|
try:
|
||||||
tab.start()
|
tab.start()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.clear()
|
self.clear()
|
||||||
for tab in self.tabs.values():
|
for tab in self.tabs.values():
|
||||||
|
@ -304,7 +304,7 @@ class TorrentDetails(component.Component):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
# Save the state of the tabs
|
# Save the state of the tabs
|
||||||
for tab in self.tabs:
|
for tab in self.tabs:
|
||||||
|
@ -312,7 +312,7 @@ class TorrentDetails(component.Component):
|
||||||
self.tabs[tab].save_state()
|
self.tabs[tab].save_state()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Save tabs state
|
# Save tabs state
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
|
||||||
|
@ -320,7 +320,7 @@ class TorrentDetails(component.Component):
|
||||||
if len(component.get("TorrentView").get_selected_torrents()) == 0:
|
if len(component.get("TorrentView").get_selected_torrents()) == 0:
|
||||||
# No torrents selected, so just clear
|
# No torrents selected, so just clear
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
if self.notebook.get_property("visible"):
|
if self.notebook.get_property("visible"):
|
||||||
if page_num == None:
|
if page_num == None:
|
||||||
page_num = self.notebook.get_current_page()
|
page_num = self.notebook.get_current_page()
|
||||||
|
@ -335,7 +335,7 @@ class TorrentDetails(component.Component):
|
||||||
# Update the tab that is in view
|
# Update the tab that is in view
|
||||||
if name:
|
if name:
|
||||||
self.tabs[name].update()
|
self.tabs[name].update()
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
# Get the tab name
|
# Get the tab name
|
||||||
try:
|
try:
|
||||||
|
@ -362,9 +362,9 @@ class TorrentDetails(component.Component):
|
||||||
else:
|
else:
|
||||||
self.hide_all_tabs()
|
self.hide_all_tabs()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.set_tab_visible(name, widget.get_active())
|
self.set_tab_visible(name, widget.get_active())
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
"""We save the state, which is basically the tab_index list"""
|
"""We save the state, which is basically the tab_index list"""
|
||||||
filename = "tabs.state"
|
filename = "tabs.state"
|
||||||
|
@ -375,7 +375,7 @@ class TorrentDetails(component.Component):
|
||||||
# Sort by weight
|
# Sort by weight
|
||||||
state.sort()
|
state.sort()
|
||||||
state = [(n, v) for w, n, v in state]
|
state = [(n, v) for w, n, v in state]
|
||||||
|
|
||||||
# Get the config location for saving the state file
|
# Get the config location for saving the state file
|
||||||
config_location = ConfigManager("gtkui.conf")["config_location"]
|
config_location = ConfigManager("gtkui.conf")["config_location"]
|
||||||
|
|
||||||
|
@ -392,7 +392,7 @@ class TorrentDetails(component.Component):
|
||||||
# Get the config location for loading the state file
|
# Get the config location for loading the state file
|
||||||
config_location = ConfigManager("gtkui.conf")["config_location"]
|
config_location = ConfigManager("gtkui.conf")["config_location"]
|
||||||
state = None
|
state = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.debug("Loading TorrentDetails state file: %s", filename)
|
log.debug("Loading TorrentDetails state file: %s", filename)
|
||||||
state_file = open(os.path.join(config_location, filename), "rb")
|
state_file = open(os.path.join(config_location, filename), "rb")
|
||||||
|
@ -400,5 +400,5 @@ class TorrentDetails(component.Component):
|
||||||
state_file.close()
|
state_file.close()
|
||||||
except (EOFError, IOError), e:
|
except (EOFError, IOError), e:
|
||||||
log.warning("Unable to load state file: %s", e)
|
log.warning("Unable to load state file: %s", e)
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
|
@ -201,7 +201,7 @@ class CommandConfig(Command):
|
||||||
color = CYAN
|
color = CYAN
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
color = MAGENTA
|
color = MAGENTA
|
||||||
|
|
||||||
print ("* " + BLUE_B + "%s:" + color + " %s" + NORMAL) % (key, value)
|
print ("* " + BLUE_B + "%s:" + color + " %s" + NORMAL) % (key, value)
|
||||||
client.get_config(_on_get_config)
|
client.get_config(_on_get_config)
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
# signalreceiver.py
|
# signalreceiver.py
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
# Copyright (C) 2007, 2008 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# Deluge is free software.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
# GNU General Public License, as published by the Free Software
|
# GNU General Public License, as published by the Free Software
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
# any later version.
|
# any later version.
|
||||||
#
|
#
|
||||||
# deluge is distributed in the hope that it will be useful,
|
# deluge is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
# See the GNU General Public License for more details.
|
# See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with deluge. If not, write to:
|
# along with deluge. If not, write to:
|
||||||
# The Free Software Foundation, Inc.,
|
# The Free Software Foundation, Inc.,
|
||||||
|
@ -47,24 +47,24 @@ import socket
|
||||||
from deluge.log import LOG as log
|
from deluge.log import LOG as log
|
||||||
|
|
||||||
class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
log.debug("SignalReceiver init..")
|
log.debug("SignalReceiver init..")
|
||||||
# Set to true so that the receiver thread will exit
|
# Set to true so that the receiver thread will exit
|
||||||
|
|
||||||
self.signals = {}
|
self.signals = {}
|
||||||
self.emitted_signals = []
|
self.emitted_signals = []
|
||||||
|
|
||||||
self.remote = False
|
self.remote = False
|
||||||
|
|
||||||
self.start_server()
|
self.start_server()
|
||||||
|
|
||||||
def start_server(self, port=None):
|
def start_server(self, port=None):
|
||||||
# Setup the xmlrpc server
|
# Setup the xmlrpc server
|
||||||
host = "127.0.0.1"
|
host = "127.0.0.1"
|
||||||
if self.remote:
|
if self.remote:
|
||||||
host = ""
|
host = ""
|
||||||
|
|
||||||
server_ready = False
|
server_ready = False
|
||||||
while not server_ready:
|
while not server_ready:
|
||||||
if port:
|
if port:
|
||||||
|
@ -82,10 +82,10 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||||
else:
|
else:
|
||||||
self.port = _port
|
self.port = _port
|
||||||
server_ready = True
|
server_ready = True
|
||||||
|
|
||||||
# Register the emit_signal function
|
# Register the emit_signal function
|
||||||
self.register_function(self.emit_signal)
|
self.register_function(self.emit_signal)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""Shutdowns receiver thread"""
|
"""Shutdowns receiver thread"""
|
||||||
log.debug("Shutting down signalreceiver")
|
log.debug("Shutting down signalreceiver")
|
||||||
|
@ -101,26 +101,26 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||||
log.debug("Joining listening thread..")
|
log.debug("Joining listening thread..")
|
||||||
self.listening_thread.join(1.0)
|
self.listening_thread.join(1.0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def set_remote(self, remote):
|
def set_remote(self, remote):
|
||||||
self.remote = remote
|
self.remote = remote
|
||||||
self.start_server(self.port)
|
self.start_server(self.port)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""This gets called when we start the thread"""
|
"""This gets called when we start the thread"""
|
||||||
# Register the signal receiver with the core
|
# Register the signal receiver with the core
|
||||||
self._shutdown = False
|
self._shutdown = False
|
||||||
client.register_client(str(self.port))
|
client.register_client(str(self.port))
|
||||||
|
|
||||||
self.listening_thread = threading.Thread(target=self.handle_thread)
|
self.listening_thread = threading.Thread(target=self.handle_thread)
|
||||||
|
|
||||||
gobject.timeout_add(50, self.handle_signals)
|
gobject.timeout_add(50, self.handle_signals)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.listening_thread.start()
|
self.listening_thread.start()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("Thread: %s", e)
|
log.debug("Thread: %s", e)
|
||||||
|
|
||||||
def handle_thread(self):
|
def handle_thread(self):
|
||||||
try:
|
try:
|
||||||
while not self._shutdown:
|
while not self._shutdown:
|
||||||
|
@ -128,11 +128,11 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||||
self._shutdown = False
|
self._shutdown = False
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug("handle_thread: %s", e)
|
log.debug("handle_thread: %s", e)
|
||||||
|
|
||||||
def get_port(self):
|
def get_port(self):
|
||||||
"""Get the port that the SignalReceiver is listening on"""
|
"""Get the port that the SignalReceiver is listening on"""
|
||||||
return self.port
|
return self.port
|
||||||
|
|
||||||
def emit_signal(self, signal, *data):
|
def emit_signal(self, signal, *data):
|
||||||
"""Exported method used by the core to emit a signal to the client"""
|
"""Exported method used by the core to emit a signal to the client"""
|
||||||
self.emitted_signals.append((signal, data))
|
self.emitted_signals.append((signal, data))
|
||||||
|
@ -143,13 +143,13 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||||
try:
|
try:
|
||||||
for callback in self.signals[signal]:
|
for callback in self.signals[signal]:
|
||||||
gobject.idle_add(callback, *data)
|
gobject.idle_add(callback, *data)
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.warning("Unable to call callback for signal %s: %s", signal, e)
|
log.warning("Unable to call callback for signal %s: %s", signal, e)
|
||||||
|
|
||||||
self.emitted_signals = []
|
self.emitted_signals = []
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def connect_to_signal(self, signal, callback):
|
def connect_to_signal(self, signal, callback):
|
||||||
"""Connect to a signal"""
|
"""Connect to a signal"""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -241,7 +241,7 @@ class json_rpc:
|
||||||
class json_upload:
|
class json_upload:
|
||||||
def GET(self):
|
def GET(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@deco.check_session
|
@deco.check_session
|
||||||
def POST(self, name=None):
|
def POST(self, name=None):
|
||||||
import os
|
import os
|
||||||
|
@ -254,7 +254,7 @@ class json_upload:
|
||||||
shutil.copyfileobj(vars.torrentFile.file, tmp_file)
|
shutil.copyfileobj(vars.torrentFile.file, tmp_file)
|
||||||
tmp_file.close()
|
tmp_file.close()
|
||||||
print path
|
print path
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
component.get("PageManager").register_page("/json/rpc",json_rpc)
|
component.get("PageManager").register_page("/json/rpc",json_rpc)
|
||||||
|
|
|
@ -305,7 +305,7 @@ class ModelChoiceField(ChoiceField):
|
||||||
help_text=None, *args, **kwargs):
|
help_text=None, *args, **kwargs):
|
||||||
self.empty_label = empty_label
|
self.empty_label = empty_label
|
||||||
self.cache_choices = cache_choices
|
self.cache_choices = cache_choices
|
||||||
|
|
||||||
# Call Field instead of ChoiceField __init__() because we don't need
|
# Call Field instead of ChoiceField __init__() because we don't need
|
||||||
# ChoiceField.__init__().
|
# ChoiceField.__init__().
|
||||||
Field.__init__(self, required, widget, label, initial, help_text,
|
Field.__init__(self, required, widget, label, initial, help_text,
|
||||||
|
@ -321,8 +321,8 @@ class ModelChoiceField(ChoiceField):
|
||||||
|
|
||||||
queryset = property(_get_queryset, _set_queryset)
|
queryset = property(_get_queryset, _set_queryset)
|
||||||
|
|
||||||
# this method will be used to create object labels by the QuerySetIterator.
|
# this method will be used to create object labels by the QuerySetIterator.
|
||||||
# Override it to customize the label.
|
# Override it to customize the label.
|
||||||
def label_from_instance(self, obj):
|
def label_from_instance(self, obj):
|
||||||
"""
|
"""
|
||||||
This method is used to convert objects into strings; it's used to
|
This method is used to convert objects into strings; it's used to
|
||||||
|
@ -330,7 +330,7 @@ class ModelChoiceField(ChoiceField):
|
||||||
can override this method to customize the display of the choices.
|
can override this method to customize the display of the choices.
|
||||||
"""
|
"""
|
||||||
return smart_unicode(obj)
|
return smart_unicode(obj)
|
||||||
|
|
||||||
def _get_choices(self):
|
def _get_choices(self):
|
||||||
# If self._choices is set, then somebody must have manually set
|
# If self._choices is set, then somebody must have manually set
|
||||||
# the property self.choices. In this case, just return self._choices.
|
# the property self.choices. In this case, just return self._choices.
|
||||||
|
|
|
@ -32,7 +32,7 @@ except ImportError:
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
import doctest
|
import doctest
|
||||||
|
|
||||||
doctest.testmod(utils)
|
doctest.testmod(utils)
|
||||||
doctest.testmod(db)
|
doctest.testmod(db)
|
||||||
doctest.testmod(net)
|
doctest.testmod(net)
|
||||||
|
@ -40,21 +40,21 @@ def main():
|
||||||
doctest.testmod(http)
|
doctest.testmod(http)
|
||||||
doctest.testmod(webapi)
|
doctest.testmod(webapi)
|
||||||
doctest.testmod(request)
|
doctest.testmod(request)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
doctest.testmod(cheetah)
|
doctest.testmod(cheetah)
|
||||||
except NameError:
|
except NameError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
template.test()
|
template.test()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
urls = ('/web.py', 'source')
|
urls = ('/web.py', 'source')
|
||||||
class source:
|
class source:
|
||||||
def GET(self):
|
def GET(self):
|
||||||
header('Content-Type', 'text/python')
|
header('Content-Type', 'text/python')
|
||||||
print open(sys.argv[0]).read()
|
print open(sys.argv[0]).read()
|
||||||
|
|
||||||
if listget(sys.argv, 1) != 'test':
|
if listget(sys.argv, 1) != 'test':
|
||||||
run(urls, locals())
|
run(urls, locals())
|
||||||
|
|
||||||
|
|
|
@ -20,22 +20,22 @@ def upvars(level=2):
|
||||||
|
|
||||||
r_include = re_compile(r'(?!\\)#include \"(.*?)\"($|#)', re.M)
|
r_include = re_compile(r'(?!\\)#include \"(.*?)\"($|#)', re.M)
|
||||||
def __compiletemplate(template, base=None, isString=False):
|
def __compiletemplate(template, base=None, isString=False):
|
||||||
if isString:
|
if isString:
|
||||||
text = template
|
text = template
|
||||||
else:
|
else:
|
||||||
text = open('templates/'+template).read()
|
text = open('templates/'+template).read()
|
||||||
# implement #include at compile-time
|
# implement #include at compile-time
|
||||||
def do_include(match):
|
def do_include(match):
|
||||||
text = open('templates/'+match.groups()[0]).read()
|
text = open('templates/'+match.groups()[0]).read()
|
||||||
return text
|
return text
|
||||||
while r_include.findall(text):
|
while r_include.findall(text):
|
||||||
text = r_include.sub(do_include, text)
|
text = r_include.sub(do_include, text)
|
||||||
|
|
||||||
execspace = _compiletemplate.bases.copy()
|
execspace = _compiletemplate.bases.copy()
|
||||||
tmpl_compiler = Compiler(source=text, mainClassName='GenTemplate')
|
tmpl_compiler = Compiler(source=text, mainClassName='GenTemplate')
|
||||||
tmpl_compiler.addImportedVarNames(execspace.keys())
|
tmpl_compiler.addImportedVarNames(execspace.keys())
|
||||||
exec str(tmpl_compiler) in execspace
|
exec str(tmpl_compiler) in execspace
|
||||||
if base:
|
if base:
|
||||||
_compiletemplate.bases[base] = execspace['GenTemplate']
|
_compiletemplate.bases[base] = execspace['GenTemplate']
|
||||||
|
|
||||||
return execspace['GenTemplate']
|
return execspace['GenTemplate']
|
||||||
|
@ -43,17 +43,17 @@ def __compiletemplate(template, base=None, isString=False):
|
||||||
_compiletemplate = memoize(__compiletemplate)
|
_compiletemplate = memoize(__compiletemplate)
|
||||||
_compiletemplate.bases = {}
|
_compiletemplate.bases = {}
|
||||||
|
|
||||||
def render(template, terms=None, asTemplate=False, base=None,
|
def render(template, terms=None, asTemplate=False, base=None,
|
||||||
isString=False):
|
isString=False):
|
||||||
"""
|
"""
|
||||||
Renders a template, caching where it can.
|
Renders a template, caching where it can.
|
||||||
|
|
||||||
`template` is the name of a file containing the a template in
|
`template` is the name of a file containing the a template in
|
||||||
the `templates/` folder, unless `isString`, in which case it's the
|
the `templates/` folder, unless `isString`, in which case it's the
|
||||||
template itself.
|
template itself.
|
||||||
|
|
||||||
`terms` is a dictionary used to fill the template. If it's None, then
|
`terms` is a dictionary used to fill the template. If it's None, then
|
||||||
the caller's local variables are used instead, plus context, if it's not
|
the caller's local variables are used instead, plus context, if it's not
|
||||||
already set, is set to `context`.
|
already set, is set to `context`.
|
||||||
|
|
||||||
If asTemplate is False, it `output`s the template directly. Otherwise,
|
If asTemplate is False, it `output`s the template directly. Otherwise,
|
||||||
|
@ -69,7 +69,7 @@ def render(template, terms=None, asTemplate=False, base=None,
|
||||||
if isinstance(terms, list):
|
if isinstance(terms, list):
|
||||||
new = {}
|
new = {}
|
||||||
old = upvars()
|
old = upvars()
|
||||||
for k in terms:
|
for k in terms:
|
||||||
new[k] = old[k]
|
new[k] = old[k]
|
||||||
terms = new
|
terms = new
|
||||||
# default: grab all locals
|
# default: grab all locals
|
||||||
|
@ -77,22 +77,22 @@ def render(template, terms=None, asTemplate=False, base=None,
|
||||||
terms = {'context': ctx, 'ctx':ctx}
|
terms = {'context': ctx, 'ctx':ctx}
|
||||||
terms.update(sys._getframe(1).f_locals)
|
terms.update(sys._getframe(1).f_locals)
|
||||||
# terms=d means use d as the searchList
|
# terms=d means use d as the searchList
|
||||||
if not isinstance(terms, tuple):
|
if not isinstance(terms, tuple):
|
||||||
terms = (terms,)
|
terms = (terms,)
|
||||||
|
|
||||||
if 'headers' in ctx and not isString and template.endswith('.html'):
|
if 'headers' in ctx and not isString and template.endswith('.html'):
|
||||||
header('Content-Type','text/html; charset=utf-8', unique=True)
|
header('Content-Type','text/html; charset=utf-8', unique=True)
|
||||||
|
|
||||||
if loadhooks.has_key('reloader'):
|
if loadhooks.has_key('reloader'):
|
||||||
compiled_tmpl = __compiletemplate(template, base=base, isString=isString)
|
compiled_tmpl = __compiletemplate(template, base=base, isString=isString)
|
||||||
else:
|
else:
|
||||||
compiled_tmpl = _compiletemplate(template, base=base, isString=isString)
|
compiled_tmpl = _compiletemplate(template, base=base, isString=isString)
|
||||||
compiled_tmpl = compiled_tmpl(searchList=terms, filter=WebSafe)
|
compiled_tmpl = compiled_tmpl(searchList=terms, filter=WebSafe)
|
||||||
if asTemplate:
|
if asTemplate:
|
||||||
return compiled_tmpl
|
return compiled_tmpl
|
||||||
else:
|
else:
|
||||||
return output(str(compiled_tmpl))
|
return output(str(compiled_tmpl))
|
||||||
|
|
||||||
class WebSafe(Filter):
|
class WebSafe(Filter):
|
||||||
def filter(self, val, **keywords):
|
def filter(self, val, **keywords):
|
||||||
return websafe(val)
|
return websafe(val)
|
||||||
|
|
|
@ -12,7 +12,7 @@ __all__ = [
|
||||||
"sqllist", "sqlors", "aparam", "reparam",
|
"sqllist", "sqlors", "aparam", "reparam",
|
||||||
"SQLQuery", "sqlquote",
|
"SQLQuery", "sqlquote",
|
||||||
"SQLLiteral", "sqlliteral",
|
"SQLLiteral", "sqlliteral",
|
||||||
"connect",
|
"connect",
|
||||||
"TransactionError", "transaction", "transact", "commit", "rollback",
|
"TransactionError", "transaction", "transact", "commit", "rollback",
|
||||||
"query",
|
"query",
|
||||||
"select", "insert", "update", "delete"
|
"select", "insert", "update", "delete"
|
||||||
|
@ -45,17 +45,17 @@ def _interpolate(format):
|
||||||
Takes a format string and returns a list of 2-tuples of the form
|
Takes a format string and returns a list of 2-tuples of the form
|
||||||
(boolean, string) where boolean says whether string should be evaled
|
(boolean, string) where boolean says whether string should be evaled
|
||||||
or not.
|
or not.
|
||||||
|
|
||||||
from <http://lfw.org/python/Itpl.py> (public domain, Ka-Ping Yee)
|
from <http://lfw.org/python/Itpl.py> (public domain, Ka-Ping Yee)
|
||||||
"""
|
"""
|
||||||
from tokenize import tokenprog
|
from tokenize import tokenprog
|
||||||
|
|
||||||
def matchorfail(text, pos):
|
def matchorfail(text, pos):
|
||||||
match = tokenprog.match(text, pos)
|
match = tokenprog.match(text, pos)
|
||||||
if match is None:
|
if match is None:
|
||||||
raise _ItplError(text, pos)
|
raise _ItplError(text, pos)
|
||||||
return match, match.end()
|
return match, match.end()
|
||||||
|
|
||||||
namechars = "abcdefghijklmnopqrstuvwxyz" \
|
namechars = "abcdefghijklmnopqrstuvwxyz" \
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||||
chunks = []
|
chunks = []
|
||||||
|
@ -63,7 +63,7 @@ def _interpolate(format):
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
dollar = format.find("$", pos)
|
dollar = format.find("$", pos)
|
||||||
if dollar < 0:
|
if dollar < 0:
|
||||||
break
|
break
|
||||||
nextchar = format[dollar + 1]
|
nextchar = format[dollar + 1]
|
||||||
|
|
||||||
|
@ -74,9 +74,9 @@ def _interpolate(format):
|
||||||
match, pos = matchorfail(format, pos)
|
match, pos = matchorfail(format, pos)
|
||||||
tstart, tend = match.regs[3]
|
tstart, tend = match.regs[3]
|
||||||
token = format[tstart:tend]
|
token = format[tstart:tend]
|
||||||
if token == "{":
|
if token == "{":
|
||||||
level = level + 1
|
level = level + 1
|
||||||
elif token == "}":
|
elif token == "}":
|
||||||
level = level - 1
|
level = level - 1
|
||||||
chunks.append((1, format[dollar + 2:pos - 1]))
|
chunks.append((1, format[dollar + 2:pos - 1]))
|
||||||
|
|
||||||
|
@ -93,11 +93,11 @@ def _interpolate(format):
|
||||||
match, pos = matchorfail(format, pos)
|
match, pos = matchorfail(format, pos)
|
||||||
tstart, tend = match.regs[3]
|
tstart, tend = match.regs[3]
|
||||||
token = format[tstart:tend]
|
token = format[tstart:tend]
|
||||||
if token[0] in "([":
|
if token[0] in "([":
|
||||||
level = level + 1
|
level = level + 1
|
||||||
elif token[0] in ")]":
|
elif token[0] in ")]":
|
||||||
level = level - 1
|
level = level - 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
chunks.append((1, format[dollar + 1:pos]))
|
chunks.append((1, format[dollar + 1:pos]))
|
||||||
|
|
||||||
|
@ -105,14 +105,14 @@ def _interpolate(format):
|
||||||
chunks.append((0, format[pos:dollar + 1]))
|
chunks.append((0, format[pos:dollar + 1]))
|
||||||
pos = dollar + 1 + (nextchar == "$")
|
pos = dollar + 1 + (nextchar == "$")
|
||||||
|
|
||||||
if pos < len(format):
|
if pos < len(format):
|
||||||
chunks.append((0, format[pos:]))
|
chunks.append((0, format[pos:]))
|
||||||
return chunks
|
return chunks
|
||||||
|
|
||||||
class UnknownParamstyle(Exception):
|
class UnknownParamstyle(Exception):
|
||||||
"""
|
"""
|
||||||
raised for unsupported db paramstyles
|
raised for unsupported db paramstyles
|
||||||
|
|
||||||
(currently supported: qmark, numeric, format, pyformat)
|
(currently supported: qmark, numeric, format, pyformat)
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
@ -122,7 +122,7 @@ def aparam():
|
||||||
Returns the appropriate string to be used to interpolate
|
Returns the appropriate string to be used to interpolate
|
||||||
a value with the current `web.ctx.db_module` or simply %s
|
a value with the current `web.ctx.db_module` or simply %s
|
||||||
if there isn't one.
|
if there isn't one.
|
||||||
|
|
||||||
>>> aparam()
|
>>> aparam()
|
||||||
'%s'
|
'%s'
|
||||||
"""
|
"""
|
||||||
|
@ -130,12 +130,12 @@ def aparam():
|
||||||
style = web.ctx.db_module.paramstyle
|
style = web.ctx.db_module.paramstyle
|
||||||
else:
|
else:
|
||||||
style = 'pyformat'
|
style = 'pyformat'
|
||||||
|
|
||||||
if style == 'qmark':
|
if style == 'qmark':
|
||||||
return '?'
|
return '?'
|
||||||
elif style == 'numeric':
|
elif style == 'numeric':
|
||||||
return ':1'
|
return ':1'
|
||||||
elif style in ['format', 'pyformat']:
|
elif style in ['format', 'pyformat']:
|
||||||
return '%s'
|
return '%s'
|
||||||
raise UnknownParamstyle, style
|
raise UnknownParamstyle, style
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ def reparam(string_, dictionary):
|
||||||
"""
|
"""
|
||||||
Takes a string and a dictionary and interpolates the string
|
Takes a string and a dictionary and interpolates the string
|
||||||
using values from the dictionary. Returns an `SQLQuery` for the result.
|
using values from the dictionary. Returns an `SQLQuery` for the result.
|
||||||
|
|
||||||
>>> reparam("s = $s", dict(s=True))
|
>>> reparam("s = $s", dict(s=True))
|
||||||
<sql: "s = 't'">
|
<sql: "s = 't'">
|
||||||
"""
|
"""
|
||||||
|
@ -159,7 +159,7 @@ def reparam(string_, dictionary):
|
||||||
def sqlify(obj):
|
def sqlify(obj):
|
||||||
"""
|
"""
|
||||||
converts `obj` to its proper SQL version
|
converts `obj` to its proper SQL version
|
||||||
|
|
||||||
>>> sqlify(None)
|
>>> sqlify(None)
|
||||||
'NULL'
|
'NULL'
|
||||||
>>> sqlify(True)
|
>>> sqlify(True)
|
||||||
|
@ -167,10 +167,10 @@ def sqlify(obj):
|
||||||
>>> sqlify(3)
|
>>> sqlify(3)
|
||||||
'3'
|
'3'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# because `1 == True and hash(1) == hash(True)`
|
# because `1 == True and hash(1) == hash(True)`
|
||||||
# we have to do this the hard way...
|
# we have to do this the hard way...
|
||||||
|
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return 'NULL'
|
return 'NULL'
|
||||||
elif obj is True:
|
elif obj is True:
|
||||||
|
@ -191,7 +191,7 @@ class SQLQuery:
|
||||||
# tested in sqlquote's docstring
|
# tested in sqlquote's docstring
|
||||||
def __init__(self, s='', v=()):
|
def __init__(self, s='', v=()):
|
||||||
self.s, self.v = str(s), tuple(v)
|
self.s, self.v = str(s), tuple(v)
|
||||||
|
|
||||||
def __getitem__(self, key): # for backwards-compatibility
|
def __getitem__(self, key): # for backwards-compatibility
|
||||||
return [self.s, self.v][key]
|
return [self.s, self.v][key]
|
||||||
|
|
||||||
|
@ -209,13 +209,13 @@ class SQLQuery:
|
||||||
return self
|
return self
|
||||||
else:
|
else:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
try:
|
try:
|
||||||
return self.s % tuple([sqlify(x) for x in self.v])
|
return self.s % tuple([sqlify(x) for x in self.v])
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return self.s
|
return self.s
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<sql: %s>' % repr(str(self))
|
return '<sql: %s>' % repr(str(self))
|
||||||
|
|
||||||
|
@ -226,10 +226,10 @@ class SQLLiteral:
|
||||||
>>> insert('foo', time=SQLLiteral('NOW()'), _test=True)
|
>>> insert('foo', time=SQLLiteral('NOW()'), _test=True)
|
||||||
<sql: 'INSERT INTO foo (time) VALUES (NOW())'>
|
<sql: 'INSERT INTO foo (time) VALUES (NOW())'>
|
||||||
"""
|
"""
|
||||||
def __init__(self, v):
|
def __init__(self, v):
|
||||||
self.v = v
|
self.v = v
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.v
|
return self.v
|
||||||
|
|
||||||
sqlliteral = SQLLiteral
|
sqlliteral = SQLLiteral
|
||||||
|
@ -237,7 +237,7 @@ sqlliteral = SQLLiteral
|
||||||
def sqlquote(a):
|
def sqlquote(a):
|
||||||
"""
|
"""
|
||||||
Ensures `a` is quoted properly for use in a SQL query.
|
Ensures `a` is quoted properly for use in a SQL query.
|
||||||
|
|
||||||
>>> 'WHERE x = ' + sqlquote(True) + ' AND y = ' + sqlquote(3)
|
>>> 'WHERE x = ' + sqlquote(True) + ' AND y = ' + sqlquote(3)
|
||||||
<sql: "WHERE x = 't' AND y = 3">
|
<sql: "WHERE x = 't' AND y = 3">
|
||||||
"""
|
"""
|
||||||
|
@ -249,19 +249,19 @@ class UnknownDB(Exception):
|
||||||
|
|
||||||
def connect(dbn, **keywords):
|
def connect(dbn, **keywords):
|
||||||
"""
|
"""
|
||||||
Connects to the specified database.
|
Connects to the specified database.
|
||||||
|
|
||||||
`dbn` currently must be "postgres", "mysql", or "sqlite".
|
`dbn` currently must be "postgres", "mysql", or "sqlite".
|
||||||
|
|
||||||
If DBUtils is installed, connection pooling will be used.
|
If DBUtils is installed, connection pooling will be used.
|
||||||
"""
|
"""
|
||||||
if dbn == "postgres":
|
if dbn == "postgres":
|
||||||
try:
|
try:
|
||||||
import psycopg2 as db
|
import psycopg2 as db
|
||||||
except ImportError:
|
except ImportError:
|
||||||
try:
|
try:
|
||||||
import psycopg as db
|
import psycopg as db
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import pgdb as db
|
import pgdb as db
|
||||||
if 'pw' in keywords:
|
if 'pw' in keywords:
|
||||||
keywords['password'] = keywords['pw']
|
keywords['password'] = keywords['pw']
|
||||||
|
@ -289,7 +289,7 @@ def connect(dbn, **keywords):
|
||||||
web.config._hasPooling = False
|
web.config._hasPooling = False
|
||||||
keywords['database'] = keywords['db']
|
keywords['database'] = keywords['db']
|
||||||
del keywords['db']
|
del keywords['db']
|
||||||
|
|
||||||
elif dbn == "firebird":
|
elif dbn == "firebird":
|
||||||
import kinterbasdb as db
|
import kinterbasdb as db
|
||||||
if 'pw' in keywords:
|
if 'pw' in keywords:
|
||||||
|
@ -298,7 +298,7 @@ def connect(dbn, **keywords):
|
||||||
keywords['database'] = keywords['db']
|
keywords['database'] = keywords['db']
|
||||||
del keywords['db']
|
del keywords['db']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise UnknownDB, dbn
|
raise UnknownDB, dbn
|
||||||
|
|
||||||
web.ctx.db_name = dbn
|
web.ctx.db_name = dbn
|
||||||
|
@ -313,12 +313,12 @@ def connect(dbn, **keywords):
|
||||||
return PooledDB.PooledDB(dbapi=db, **keywords)
|
return PooledDB.PooledDB(dbapi=db, **keywords)
|
||||||
else:
|
else:
|
||||||
return PooledDB.PooledDB(creator=db, **keywords)
|
return PooledDB.PooledDB(creator=db, **keywords)
|
||||||
|
|
||||||
def db_cursor():
|
def db_cursor():
|
||||||
if isinstance(web.ctx.db, dict):
|
if isinstance(web.ctx.db, dict):
|
||||||
keywords = web.ctx.db
|
keywords = web.ctx.db
|
||||||
if web.config._hasPooling:
|
if web.config._hasPooling:
|
||||||
if 'db' not in globals():
|
if 'db' not in globals():
|
||||||
globals()['db'] = _PooledDB(db, keywords)
|
globals()['db'] = _PooledDB(db, keywords)
|
||||||
web.ctx.db = globals()['db'].connection()
|
web.ctx.db = globals()['db'].connection()
|
||||||
else:
|
else:
|
||||||
|
@ -327,12 +327,12 @@ def connect(dbn, **keywords):
|
||||||
web.ctx.db_cursor = db_cursor
|
web.ctx.db_cursor = db_cursor
|
||||||
|
|
||||||
web.ctx.dbq_count = 0
|
web.ctx.dbq_count = 0
|
||||||
|
|
||||||
def db_execute(cur, sql_query, dorollback=True):
|
def db_execute(cur, sql_query, dorollback=True):
|
||||||
"""executes an sql query"""
|
"""executes an sql query"""
|
||||||
|
|
||||||
web.ctx.dbq_count += 1
|
web.ctx.dbq_count += 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
a = time.time()
|
a = time.time()
|
||||||
out = cur.execute(sql_query.s, sql_query.v)
|
out = cur.execute(sql_query.s, sql_query.v)
|
||||||
|
@ -372,31 +372,31 @@ def transact():
|
||||||
"""Start a transaction."""
|
"""Start a transaction."""
|
||||||
if not web.ctx.db_transaction:
|
if not web.ctx.db_transaction:
|
||||||
# commit everything up to now, so we don't rollback it later
|
# commit everything up to now, so we don't rollback it later
|
||||||
if hasattr(web.ctx.db, 'commit'):
|
if hasattr(web.ctx.db, 'commit'):
|
||||||
web.ctx.db.commit()
|
web.ctx.db.commit()
|
||||||
else:
|
else:
|
||||||
db_cursor = web.ctx.db_cursor()
|
db_cursor = web.ctx.db_cursor()
|
||||||
web.ctx.db_execute(db_cursor,
|
web.ctx.db_execute(db_cursor,
|
||||||
SQLQuery("SAVEPOINT webpy_sp_%s" % web.ctx.db_transaction))
|
SQLQuery("SAVEPOINT webpy_sp_%s" % web.ctx.db_transaction))
|
||||||
web.ctx.db_transaction += 1
|
web.ctx.db_transaction += 1
|
||||||
|
|
||||||
def commit():
|
def commit():
|
||||||
"""Commits a transaction."""
|
"""Commits a transaction."""
|
||||||
web.ctx.db_transaction -= 1
|
web.ctx.db_transaction -= 1
|
||||||
if web.ctx.db_transaction < 0:
|
if web.ctx.db_transaction < 0:
|
||||||
raise TransactionError, "not in a transaction"
|
raise TransactionError, "not in a transaction"
|
||||||
|
|
||||||
if not web.ctx.db_transaction:
|
if not web.ctx.db_transaction:
|
||||||
if hasattr(web.ctx.db, 'commit'):
|
if hasattr(web.ctx.db, 'commit'):
|
||||||
web.ctx.db.commit()
|
web.ctx.db.commit()
|
||||||
else:
|
else:
|
||||||
db_cursor = web.ctx.db_cursor()
|
db_cursor = web.ctx.db_cursor()
|
||||||
web.ctx.db_execute(db_cursor,
|
web.ctx.db_execute(db_cursor,
|
||||||
SQLQuery("RELEASE SAVEPOINT webpy_sp_%s" % web.ctx.db_transaction))
|
SQLQuery("RELEASE SAVEPOINT webpy_sp_%s" % web.ctx.db_transaction))
|
||||||
|
|
||||||
def rollback(care=True):
|
def rollback(care=True):
|
||||||
"""Rolls back a transaction."""
|
"""Rolls back a transaction."""
|
||||||
web.ctx.db_transaction -= 1
|
web.ctx.db_transaction -= 1
|
||||||
if web.ctx.db_transaction < 0:
|
if web.ctx.db_transaction < 0:
|
||||||
web.db_transaction = 0
|
web.db_transaction = 0
|
||||||
if care:
|
if care:
|
||||||
|
@ -405,7 +405,7 @@ def rollback(care=True):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not web.ctx.db_transaction:
|
if not web.ctx.db_transaction:
|
||||||
if hasattr(web.ctx.db, 'rollback'):
|
if hasattr(web.ctx.db, 'rollback'):
|
||||||
web.ctx.db.rollback()
|
web.ctx.db.rollback()
|
||||||
else:
|
else:
|
||||||
db_cursor = web.ctx.db_cursor()
|
db_cursor = web.ctx.db_cursor()
|
||||||
|
@ -416,9 +416,9 @@ def rollback(care=True):
|
||||||
def query(sql_query, vars=None, processed=False, _test=False):
|
def query(sql_query, vars=None, processed=False, _test=False):
|
||||||
"""
|
"""
|
||||||
Execute SQL query `sql_query` using dictionary `vars` to interpolate it.
|
Execute SQL query `sql_query` using dictionary `vars` to interpolate it.
|
||||||
If `processed=True`, `vars` is a `reparam`-style list to use
|
If `processed=True`, `vars` is a `reparam`-style list to use
|
||||||
instead of interpolating.
|
instead of interpolating.
|
||||||
|
|
||||||
>>> query("SELECT * FROM foo", _test=True)
|
>>> query("SELECT * FROM foo", _test=True)
|
||||||
<sql: 'SELECT * FROM foo'>
|
<sql: 'SELECT * FROM foo'>
|
||||||
>>> query("SELECT * FROM foo WHERE x = $x", vars=dict(x='f'), _test=True)
|
>>> query("SELECT * FROM foo WHERE x = $x", vars=dict(x='f'), _test=True)
|
||||||
|
@ -427,15 +427,15 @@ def query(sql_query, vars=None, processed=False, _test=False):
|
||||||
<sql: "SELECT * FROM foo WHERE x = 'f'">
|
<sql: "SELECT * FROM foo WHERE x = 'f'">
|
||||||
"""
|
"""
|
||||||
if vars is None: vars = {}
|
if vars is None: vars = {}
|
||||||
|
|
||||||
if not processed and not isinstance(sql_query, SQLQuery):
|
if not processed and not isinstance(sql_query, SQLQuery):
|
||||||
sql_query = reparam(sql_query, vars)
|
sql_query = reparam(sql_query, vars)
|
||||||
|
|
||||||
if _test: return sql_query
|
if _test: return sql_query
|
||||||
|
|
||||||
db_cursor = web.ctx.db_cursor()
|
db_cursor = web.ctx.db_cursor()
|
||||||
web.ctx.db_execute(db_cursor, sql_query)
|
web.ctx.db_execute(db_cursor, sql_query)
|
||||||
|
|
||||||
if db_cursor.description:
|
if db_cursor.description:
|
||||||
names = [x[0] for x in db_cursor.description]
|
names = [x[0] for x in db_cursor.description]
|
||||||
def iterwrapper():
|
def iterwrapper():
|
||||||
|
@ -450,28 +450,28 @@ def query(sql_query, vars=None, processed=False, _test=False):
|
||||||
for x in db_cursor.fetchall()]
|
for x in db_cursor.fetchall()]
|
||||||
else:
|
else:
|
||||||
out = db_cursor.rowcount
|
out = db_cursor.rowcount
|
||||||
|
|
||||||
if not web.ctx.db_transaction: web.ctx.db.commit()
|
if not web.ctx.db_transaction: web.ctx.db.commit()
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def sqllist(lst):
|
def sqllist(lst):
|
||||||
"""
|
"""
|
||||||
Converts the arguments for use in something like a WHERE clause.
|
Converts the arguments for use in something like a WHERE clause.
|
||||||
|
|
||||||
>>> sqllist(['a', 'b'])
|
>>> sqllist(['a', 'b'])
|
||||||
'a, b'
|
'a, b'
|
||||||
>>> sqllist('a')
|
>>> sqllist('a')
|
||||||
'a'
|
'a'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(lst, str):
|
if isinstance(lst, str):
|
||||||
return lst
|
return lst
|
||||||
else:
|
else:
|
||||||
return ', '.join(lst)
|
return ', '.join(lst)
|
||||||
|
|
||||||
def sqlors(left, lst):
|
def sqlors(left, lst):
|
||||||
"""
|
"""
|
||||||
`left is a SQL clause like `tablename.arg = `
|
`left is a SQL clause like `tablename.arg = `
|
||||||
and `lst` is a list of values. Returns a reparam-style
|
and `lst` is a list of values. Returns a reparam-style
|
||||||
pair featuring the SQL that ORs together the clause
|
pair featuring the SQL that ORs together the clause
|
||||||
for each item in the lst.
|
for each item in the lst.
|
||||||
|
@ -490,11 +490,11 @@ def sqlors(left, lst):
|
||||||
ln = len(lst)
|
ln = len(lst)
|
||||||
if ln == 0:
|
if ln == 0:
|
||||||
return SQLQuery("2+2=5", [])
|
return SQLQuery("2+2=5", [])
|
||||||
if ln == 1:
|
if ln == 1:
|
||||||
lst = lst[0]
|
lst = lst[0]
|
||||||
|
|
||||||
if isinstance(lst, iters):
|
if isinstance(lst, iters):
|
||||||
return SQLQuery('(' + left +
|
return SQLQuery('(' + left +
|
||||||
(' OR ' + left).join([aparam() for param in lst]) + ")", lst)
|
(' OR ' + left).join([aparam() for param in lst]) + ")", lst)
|
||||||
else:
|
else:
|
||||||
return SQLQuery(left + aparam(), [lst])
|
return SQLQuery(left + aparam(), [lst])
|
||||||
|
@ -502,24 +502,24 @@ def sqlors(left, lst):
|
||||||
def sqlwhere(dictionary, grouping=' AND '):
|
def sqlwhere(dictionary, grouping=' AND '):
|
||||||
"""
|
"""
|
||||||
Converts a `dictionary` to an SQL WHERE clause `SQLQuery`.
|
Converts a `dictionary` to an SQL WHERE clause `SQLQuery`.
|
||||||
|
|
||||||
>>> sqlwhere({'cust_id': 2, 'order_id':3})
|
>>> sqlwhere({'cust_id': 2, 'order_id':3})
|
||||||
<sql: 'order_id = 3 AND cust_id = 2'>
|
<sql: 'order_id = 3 AND cust_id = 2'>
|
||||||
>>> sqlwhere({'cust_id': 2, 'order_id':3}, grouping=', ')
|
>>> sqlwhere({'cust_id': 2, 'order_id':3}, grouping=', ')
|
||||||
<sql: 'order_id = 3, cust_id = 2'>
|
<sql: 'order_id = 3, cust_id = 2'>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return SQLQuery(grouping.join([
|
return SQLQuery(grouping.join([
|
||||||
'%s = %s' % (k, aparam()) for k in dictionary.keys()
|
'%s = %s' % (k, aparam()) for k in dictionary.keys()
|
||||||
]), dictionary.values())
|
]), dictionary.values())
|
||||||
|
|
||||||
def select(tables, vars=None, what='*', where=None, order=None, group=None,
|
def select(tables, vars=None, what='*', where=None, order=None, group=None,
|
||||||
limit=None, offset=None, _test=False):
|
limit=None, offset=None, _test=False):
|
||||||
"""
|
"""
|
||||||
Selects `what` from `tables` with clauses `where`, `order`,
|
Selects `what` from `tables` with clauses `where`, `order`,
|
||||||
`group`, `limit`, and `offset`. Uses vars to interpolate.
|
`group`, `limit`, and `offset`. Uses vars to interpolate.
|
||||||
Otherwise, each clause can be a SQLQuery.
|
Otherwise, each clause can be a SQLQuery.
|
||||||
|
|
||||||
>>> select('foo', _test=True)
|
>>> select('foo', _test=True)
|
||||||
<sql: 'SELECT * FROM foo'>
|
<sql: 'SELECT * FROM foo'>
|
||||||
>>> select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True)
|
>>> select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True)
|
||||||
|
@ -527,7 +527,7 @@ def select(tables, vars=None, what='*', where=None, order=None, group=None,
|
||||||
"""
|
"""
|
||||||
if vars is None: vars = {}
|
if vars is None: vars = {}
|
||||||
qout = ""
|
qout = ""
|
||||||
|
|
||||||
def gen_clause(sql, val):
|
def gen_clause(sql, val):
|
||||||
if isinstance(val, (int, long)):
|
if isinstance(val, (int, long)):
|
||||||
if sql == 'WHERE':
|
if sql == 'WHERE':
|
||||||
|
@ -540,14 +540,14 @@ def select(tables, vars=None, what='*', where=None, order=None, group=None,
|
||||||
nout = val
|
nout = val
|
||||||
elif val:
|
elif val:
|
||||||
nout = reparam(val, vars)
|
nout = reparam(val, vars)
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
out = ""
|
out = ""
|
||||||
if qout: out += " "
|
if qout: out += " "
|
||||||
out += sql + " " + nout
|
out += sql + " " + nout
|
||||||
return out
|
return out
|
||||||
|
|
||||||
if web.ctx.get('db_name') == "firebird":
|
if web.ctx.get('db_name') == "firebird":
|
||||||
for (sql, val) in (
|
for (sql, val) in (
|
||||||
('FIRST', limit),
|
('FIRST', limit),
|
||||||
|
@ -588,11 +588,11 @@ def insert(tablename, seqname=None, _test=False, **values):
|
||||||
Inserts `values` into `tablename`. Returns current sequence ID.
|
Inserts `values` into `tablename`. Returns current sequence ID.
|
||||||
Set `seqname` to the ID if it's not the default, or to `False`
|
Set `seqname` to the ID if it's not the default, or to `False`
|
||||||
if there isn't one.
|
if there isn't one.
|
||||||
|
|
||||||
>>> insert('foo', joe='bob', a=2, _test=True)
|
>>> insert('foo', joe='bob', a=2, _test=True)
|
||||||
<sql: "INSERT INTO foo (a, joe) VALUES (2, 'bob')">
|
<sql: "INSERT INTO foo (a, joe) VALUES (2, 'bob')">
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if values:
|
if values:
|
||||||
sql_query = SQLQuery("INSERT INTO %s (%s) VALUES (%s)" % (
|
sql_query = SQLQuery("INSERT INTO %s (%s) VALUES (%s)" % (
|
||||||
tablename,
|
tablename,
|
||||||
|
@ -603,12 +603,12 @@ def insert(tablename, seqname=None, _test=False, **values):
|
||||||
sql_query = SQLQuery("INSERT INTO %s DEFAULT VALUES" % tablename)
|
sql_query = SQLQuery("INSERT INTO %s DEFAULT VALUES" % tablename)
|
||||||
|
|
||||||
if _test: return sql_query
|
if _test: return sql_query
|
||||||
|
|
||||||
db_cursor = web.ctx.db_cursor()
|
db_cursor = web.ctx.db_cursor()
|
||||||
if seqname is False:
|
if seqname is False:
|
||||||
pass
|
pass
|
||||||
elif web.ctx.db_name == "postgres":
|
elif web.ctx.db_name == "postgres":
|
||||||
if seqname is None:
|
if seqname is None:
|
||||||
seqname = tablename + "_id_seq"
|
seqname = tablename + "_id_seq"
|
||||||
sql_query += "; SELECT currval('%s')" % seqname
|
sql_query += "; SELECT currval('%s')" % seqname
|
||||||
elif web.ctx.db_name == "mysql":
|
elif web.ctx.db_name == "mysql":
|
||||||
|
@ -618,13 +618,13 @@ def insert(tablename, seqname=None, _test=False, **values):
|
||||||
web.ctx.db_execute(db_cursor, sql_query)
|
web.ctx.db_execute(db_cursor, sql_query)
|
||||||
# not really the same...
|
# not really the same...
|
||||||
sql_query = SQLQuery("SELECT last_insert_rowid()")
|
sql_query = SQLQuery("SELECT last_insert_rowid()")
|
||||||
|
|
||||||
web.ctx.db_execute(db_cursor, sql_query)
|
web.ctx.db_execute(db_cursor, sql_query)
|
||||||
try:
|
try:
|
||||||
out = db_cursor.fetchone()[0]
|
out = db_cursor.fetchone()[0]
|
||||||
except Exception:
|
except Exception:
|
||||||
out = None
|
out = None
|
||||||
|
|
||||||
if not web.ctx.db_transaction: web.ctx.db.commit()
|
if not web.ctx.db_transaction: web.ctx.db.commit()
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
@ -633,14 +633,14 @@ def update(tables, where, vars=None, _test=False, **values):
|
||||||
"""
|
"""
|
||||||
Update `tables` with clause `where` (interpolated using `vars`)
|
Update `tables` with clause `where` (interpolated using `vars`)
|
||||||
and setting `values`.
|
and setting `values`.
|
||||||
|
|
||||||
>>> joe = 'Joseph'
|
>>> joe = 'Joseph'
|
||||||
>>> update('foo', where='name = $joe', name='bob', age=5,
|
>>> update('foo', where='name = $joe', name='bob', age=5,
|
||||||
... vars=locals(), _test=True)
|
... vars=locals(), _test=True)
|
||||||
<sql: "UPDATE foo SET age = 5, name = 'bob' WHERE name = 'Joseph'">
|
<sql: "UPDATE foo SET age = 5, name = 'bob' WHERE name = 'Joseph'">
|
||||||
"""
|
"""
|
||||||
if vars is None: vars = {}
|
if vars is None: vars = {}
|
||||||
|
|
||||||
if isinstance(where, (int, long)):
|
if isinstance(where, (int, long)):
|
||||||
where = "id = " + sqlquote(where)
|
where = "id = " + sqlquote(where)
|
||||||
elif isinstance(where, (list, tuple)) and len(where) == 2:
|
elif isinstance(where, (list, tuple)) and len(where) == 2:
|
||||||
|
@ -649,24 +649,24 @@ def update(tables, where, vars=None, _test=False, **values):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
where = reparam(where, vars)
|
where = reparam(where, vars)
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
"UPDATE " + sqllist(tables) +
|
"UPDATE " + sqllist(tables) +
|
||||||
" SET " + sqlwhere(values, ', ') +
|
" SET " + sqlwhere(values, ', ') +
|
||||||
" WHERE " + where)
|
" WHERE " + where)
|
||||||
|
|
||||||
if _test: return query
|
if _test: return query
|
||||||
|
|
||||||
db_cursor = web.ctx.db_cursor()
|
db_cursor = web.ctx.db_cursor()
|
||||||
web.ctx.db_execute(db_cursor, query)
|
web.ctx.db_execute(db_cursor, query)
|
||||||
|
|
||||||
if not web.ctx.db_transaction: web.ctx.db.commit()
|
if not web.ctx.db_transaction: web.ctx.db.commit()
|
||||||
return db_cursor.rowcount
|
return db_cursor.rowcount
|
||||||
|
|
||||||
def delete(table, where=None, using=None, vars=None, _test=False):
|
def delete(table, where=None, using=None, vars=None, _test=False):
|
||||||
"""
|
"""
|
||||||
Deletes from `table` with clauses `where` and `using`.
|
Deletes from `table` with clauses `where` and `using`.
|
||||||
|
|
||||||
>>> name = 'Joe'
|
>>> name = 'Joe'
|
||||||
>>> delete('foo', where='name = $name', vars=locals(), _test=True)
|
>>> delete('foo', where='name = $name', vars=locals(), _test=True)
|
||||||
<sql: "DELETE FROM foo WHERE name = 'Joe'">
|
<sql: "DELETE FROM foo WHERE name = 'Joe'">
|
||||||
|
@ -689,9 +689,9 @@ def delete(table, where=None, using=None, vars=None, _test=False):
|
||||||
q += ' WHERE ' + where
|
q += ' WHERE ' + where
|
||||||
if using and web.ctx.get('db_name') != "firebird":
|
if using and web.ctx.get('db_name') != "firebird":
|
||||||
q += ' USING ' + sqllist(using)
|
q += ' USING ' + sqllist(using)
|
||||||
|
|
||||||
if _test: return q
|
if _test: return q
|
||||||
|
|
||||||
db_cursor = web.ctx.db_cursor()
|
db_cursor = web.ctx.db_cursor()
|
||||||
web.ctx.db_execute(db_cursor, q)
|
web.ctx.db_execute(db_cursor, q)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
pretty debug errors
|
pretty debug errors
|
||||||
(part of web.py)
|
(part of web.py)
|
||||||
|
|
||||||
adapted from Django <djangoproject.com>
|
adapted from Django <djangoproject.com>
|
||||||
Copyright (c) 2005, the Lawrence Journal-World
|
Copyright (c) 2005, the Lawrence Journal-World
|
||||||
Used under the modified BSD license:
|
Used under the modified BSD license:
|
||||||
http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
||||||
|
@ -37,11 +37,11 @@ $def with (exception_type, exception_value, frames)
|
||||||
h2 span { font-size:80%; color:#666; font-weight:normal; }
|
h2 span { font-size:80%; color:#666; font-weight:normal; }
|
||||||
h3 { margin:1em 0 .5em 0; }
|
h3 { margin:1em 0 .5em 0; }
|
||||||
h4 { margin:0 0 .5em 0; font-weight: normal; }
|
h4 { margin:0 0 .5em 0; font-weight: normal; }
|
||||||
table {
|
table {
|
||||||
border:1px solid #ccc; border-collapse: collapse; background:white; }
|
border:1px solid #ccc; border-collapse: collapse; background:white; }
|
||||||
tbody td, tbody th { vertical-align:top; padding:2px 3px; }
|
tbody td, tbody th { vertical-align:top; padding:2px 3px; }
|
||||||
thead th {
|
thead th {
|
||||||
padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
|
padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
|
||||||
font-weight:normal; font-size:11px; border:1px solid #ddd; }
|
font-weight:normal; font-size:11px; border:1px solid #ddd; }
|
||||||
tbody th { text-align:right; color:#666; padding-right:.5em; }
|
tbody th { text-align:right; color:#666; padding-right:.5em; }
|
||||||
table.vars { margin:5px 0 2px 40px; }
|
table.vars { margin:5px 0 2px 40px; }
|
||||||
|
@ -49,14 +49,14 @@ $def with (exception_type, exception_value, frames)
|
||||||
table td.code { width:100%;}
|
table td.code { width:100%;}
|
||||||
table td.code div { overflow:hidden; }
|
table td.code div { overflow:hidden; }
|
||||||
table.source th { color:#666; }
|
table.source th { color:#666; }
|
||||||
table.source td {
|
table.source td {
|
||||||
font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
|
font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
|
||||||
ul.traceback { list-style-type:none; }
|
ul.traceback { list-style-type:none; }
|
||||||
ul.traceback li.frame { margin-bottom:1em; }
|
ul.traceback li.frame { margin-bottom:1em; }
|
||||||
div.context { margin: 10px 0; }
|
div.context { margin: 10px 0; }
|
||||||
div.context ol {
|
div.context ol {
|
||||||
padding-left:30px; margin:0 10px; list-style-position: inside; }
|
padding-left:30px; margin:0 10px; list-style-position: inside; }
|
||||||
div.context ol li {
|
div.context ol li {
|
||||||
font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
|
font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
|
||||||
div.context ol.context-line li { color:black; background-color:#ccc; }
|
div.context ol.context-line li { color:black; background-color:#ccc; }
|
||||||
div.context ol.context-line li span { float: right; }
|
div.context ol.context-line li span { float: right; }
|
||||||
|
@ -78,7 +78,7 @@ $def with (exception_type, exception_value, frames)
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//<!--
|
//<!--
|
||||||
function getElementsByClassName(oElm, strTagName, strClassName){
|
function getElementsByClassName(oElm, strTagName, strClassName){
|
||||||
// Written by Jonathan Snook, http://www.snook.ca/jon;
|
// Written by Jonathan Snook, http://www.snook.ca/jon;
|
||||||
// Add-ons by Robert Nyman, http://www.robertnyman.com
|
// Add-ons by Robert Nyman, http://www.robertnyman.com
|
||||||
var arrElements = (strTagName == "*" && document.all)? document.all :
|
var arrElements = (strTagName == "*" && document.all)? document.all :
|
||||||
oElm.getElementsByTagName(strTagName);
|
oElm.getElementsByTagName(strTagName);
|
||||||
|
@ -157,7 +157,7 @@ $for frame in frames:
|
||||||
<li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
|
<li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
$if frame.vars:
|
$if frame.vars:
|
||||||
<div class="commands">
|
<div class="commands">
|
||||||
<a href='#' onclick="return varToggle(this, '$frame.id')"><span>▶</span> Local vars</a>
|
<a href='#' onclick="return varToggle(this, '$frame.id')"><span>▶</span> Local vars</a>
|
||||||
|
@ -183,7 +183,7 @@ $if ctx.output or ctx.headers:
|
||||||
<p class="req" style="padding-bottom: 2em"><code>
|
<p class="req" style="padding-bottom: 2em"><code>
|
||||||
$ctx.output
|
$ctx.output
|
||||||
</code></p>
|
</code></p>
|
||||||
|
|
||||||
<h2>Request information</h2>
|
<h2>Request information</h2>
|
||||||
|
|
||||||
<h3>INPUT</h3>
|
<h3>INPUT</h3>
|
||||||
|
@ -253,8 +253,8 @@ def djangoerror():
|
||||||
|
|
||||||
return lower_bound, pre_context, context_line, post_context
|
return lower_bound, pre_context, context_line, post_context
|
||||||
except (OSError, IOError):
|
except (OSError, IOError):
|
||||||
return None, [], None, []
|
return None, [], None, []
|
||||||
|
|
||||||
exception_type, exception_value, tback = sys.exc_info()
|
exception_type, exception_value, tback = sys.exc_info()
|
||||||
frames = []
|
frames = []
|
||||||
while tback is not None:
|
while tback is not None:
|
||||||
|
@ -279,9 +279,9 @@ def djangoerror():
|
||||||
frames.reverse()
|
frames.reverse()
|
||||||
urljoin = urlparse.urljoin
|
urljoin = urlparse.urljoin
|
||||||
def prettify(x):
|
def prettify(x):
|
||||||
try:
|
try:
|
||||||
out = pprint.pformat(x)
|
out = pprint.pformat(x)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
out = '[could not display: <' + e.__class__.__name__ + \
|
out = '[could not display: <' + e.__class__.__name__ + \
|
||||||
': '+str(e)+'>]'
|
': '+str(e)+'>]'
|
||||||
return out
|
return out
|
||||||
|
@ -296,10 +296,10 @@ def debugerror():
|
||||||
A replacement for `internalerror` that presents a nice page with lots
|
A replacement for `internalerror` that presents a nice page with lots
|
||||||
of debug information for the programmer.
|
of debug information for the programmer.
|
||||||
|
|
||||||
(Based on the beautiful 500 page from [Django](http://djangoproject.com/),
|
(Based on the beautiful 500 page from [Django](http://djangoproject.com/),
|
||||||
designed by [Wilson Miner](http://wilsonminer.com/).)
|
designed by [Wilson Miner](http://wilsonminer.com/).)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
web.ctx.headers = [('Content-Type', 'text/html')]
|
web.ctx.headers = [('Content-Type', 'text/html')]
|
||||||
web.ctx.output = djangoerror()
|
web.ctx.output = djangoerror()
|
||||||
|
|
||||||
|
@ -307,10 +307,10 @@ if __name__ == "__main__":
|
||||||
urls = (
|
urls = (
|
||||||
'/', 'index'
|
'/', 'index'
|
||||||
)
|
)
|
||||||
|
|
||||||
class index:
|
class index:
|
||||||
def GET(self):
|
def GET(self):
|
||||||
thisdoesnotexist
|
thisdoesnotexist
|
||||||
|
|
||||||
web.internalerror = web.debugerror
|
web.internalerror = web.debugerror
|
||||||
web.run(urls)
|
web.run(urls)
|
|
@ -23,7 +23,7 @@ class Form:
|
||||||
o = copy.deepcopy(self)
|
o = copy.deepcopy(self)
|
||||||
if x: o.validates(x)
|
if x: o.validates(x)
|
||||||
return o
|
return o
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
out = ''
|
out = ''
|
||||||
out += self.rendernote(self.note)
|
out += self.rendernote(self.note)
|
||||||
|
@ -34,7 +34,7 @@ class Form:
|
||||||
out += '<td id="note_%s">%s</td></tr>\n' % (i.id, self.rendernote(i.note))
|
out += '<td id="note_%s">%s</td></tr>\n' % (i.id, self.rendernote(i.note))
|
||||||
out += "</table>"
|
out += "</table>"
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def rendernote(self, note):
|
def rendernote(self, note):
|
||||||
if note: return '<strong class="wrong">%s</strong>' % note
|
if note: return '<strong class="wrong">%s</strong>' % note
|
||||||
else: return ""
|
else: return ""
|
||||||
|
@ -63,12 +63,12 @@ class Form:
|
||||||
|
|
||||||
def fill(self, source=None, **kw):
|
def fill(self, source=None, **kw):
|
||||||
return self.validates(source, _validate=False, **kw)
|
return self.validates(source, _validate=False, **kw)
|
||||||
|
|
||||||
def __getitem__(self, i):
|
def __getitem__(self, i):
|
||||||
for x in self.inputs:
|
for x in self.inputs:
|
||||||
if x.name == i: return x
|
if x.name == i: return x
|
||||||
raise KeyError, i
|
raise KeyError, i
|
||||||
|
|
||||||
def _get_d(self): #@@ should really be form.attr, no?
|
def _get_d(self): #@@ should really be form.attr, no?
|
||||||
return utils.storage([(i.name, i.value) for i in self.inputs])
|
return utils.storage([(i.name, i.value) for i in self.inputs])
|
||||||
d = property(_get_d)
|
d = property(_get_d)
|
||||||
|
@ -100,7 +100,7 @@ class Input(object):
|
||||||
for (n, v) in self.attrs.items():
|
for (n, v) in self.attrs.items():
|
||||||
str += ' %s="%s"' % (n, net.websafe(v))
|
str += ' %s="%s"' % (n, net.websafe(v))
|
||||||
return str
|
return str
|
||||||
|
|
||||||
#@@ quoting
|
#@@ quoting
|
||||||
|
|
||||||
class Textbox(Input):
|
class Textbox(Input):
|
||||||
|
@ -139,7 +139,7 @@ class Dropdown(Input):
|
||||||
if type(arg) == tuple:
|
if type(arg) == tuple:
|
||||||
value, desc= arg
|
value, desc= arg
|
||||||
else:
|
else:
|
||||||
value, desc = arg, arg
|
value, desc = arg, arg
|
||||||
|
|
||||||
if self.value == value: select_p = ' selected="selected"'
|
if self.value == value: select_p = ' selected="selected"'
|
||||||
else: select_p = ''
|
else: select_p = ''
|
||||||
|
@ -196,11 +196,11 @@ class File(Input):
|
||||||
x += self.addatts()
|
x += self.addatts()
|
||||||
x += ' />'
|
x += ' />'
|
||||||
return x
|
return x
|
||||||
|
|
||||||
class Validator:
|
class Validator:
|
||||||
def __deepcopy__(self, memo): return copy.copy(self)
|
def __deepcopy__(self, memo): return copy.copy(self)
|
||||||
def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
|
def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
|
||||||
def valid(self, value):
|
def valid(self, value):
|
||||||
try: return self.test(value)
|
try: return self.test(value)
|
||||||
except: return False
|
except: return False
|
||||||
|
|
||||||
|
@ -210,6 +210,6 @@ class regexp(Validator):
|
||||||
def __init__(self, rexp, msg):
|
def __init__(self, rexp, msg):
|
||||||
self.rexp = re.compile(rexp)
|
self.rexp = re.compile(rexp)
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
def valid(self, value):
|
def valid(self, value):
|
||||||
return bool(self.rexp.match(value))
|
return bool(self.rexp.match(value))
|
||||||
|
|
|
@ -4,9 +4,9 @@ Network Utilities
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"validipaddr", "validipport", "validip", "validaddr",
|
"validipaddr", "validipport", "validip", "validaddr",
|
||||||
"urlquote",
|
"urlquote",
|
||||||
"httpdate", "parsehttpdate",
|
"httpdate", "parsehttpdate",
|
||||||
"htmlquote", "websafe",
|
"htmlquote", "websafe",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ def validip(ip, defaultaddr="0.0.0.0", defaultport=8080):
|
||||||
"""returns `(ip_address, port)` from string `ip_addr_port`"""
|
"""returns `(ip_address, port)` from string `ip_addr_port`"""
|
||||||
addr = defaultaddr
|
addr = defaultaddr
|
||||||
port = defaultport
|
port = defaultport
|
||||||
|
|
||||||
ip = ip.split(":", 1)
|
ip = ip.split(":", 1)
|
||||||
if len(ip) == 1:
|
if len(ip) == 1:
|
||||||
if not ip[0]:
|
if not ip[0]:
|
||||||
|
@ -60,7 +60,7 @@ def validip(ip, defaultaddr="0.0.0.0", defaultport=8080):
|
||||||
def validaddr(string_):
|
def validaddr(string_):
|
||||||
"""
|
"""
|
||||||
returns either (ip_address, port) or "/path/to/socket" from string_
|
returns either (ip_address, port) or "/path/to/socket" from string_
|
||||||
|
|
||||||
>>> validaddr('/path/to/socket')
|
>>> validaddr('/path/to/socket')
|
||||||
'/path/to/socket'
|
'/path/to/socket'
|
||||||
>>> validaddr('8000')
|
>>> validaddr('8000')
|
||||||
|
@ -82,7 +82,7 @@ def validaddr(string_):
|
||||||
def urlquote(val):
|
def urlquote(val):
|
||||||
"""
|
"""
|
||||||
Quotes a string for use in a URL.
|
Quotes a string for use in a URL.
|
||||||
|
|
||||||
>>> urlquote('://?f=1&j=1')
|
>>> urlquote('://?f=1&j=1')
|
||||||
'%3A//%3Ff%3D1%26j%3D1'
|
'%3A//%3Ff%3D1%26j%3D1'
|
||||||
>>> urlquote(None)
|
>>> urlquote(None)
|
||||||
|
@ -98,7 +98,7 @@ def urlquote(val):
|
||||||
def httpdate(date_obj):
|
def httpdate(date_obj):
|
||||||
"""
|
"""
|
||||||
Formats a datetime object for use in HTTP headers.
|
Formats a datetime object for use in HTTP headers.
|
||||||
|
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
>>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1))
|
>>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1))
|
||||||
'Thu, 01 Jan 1970 01:01:01 GMT'
|
'Thu, 01 Jan 1970 01:01:01 GMT'
|
||||||
|
@ -121,7 +121,7 @@ def parsehttpdate(string_):
|
||||||
def htmlquote(text):
|
def htmlquote(text):
|
||||||
"""
|
"""
|
||||||
Encodes `text` for raw use in HTML.
|
Encodes `text` for raw use in HTML.
|
||||||
|
|
||||||
>>> htmlquote("<'&\\">")
|
>>> htmlquote("<'&\\">")
|
||||||
'<'&">'
|
'<'&">'
|
||||||
"""
|
"""
|
||||||
|
@ -135,7 +135,7 @@ def htmlquote(text):
|
||||||
def websafe(val):
|
def websafe(val):
|
||||||
"""
|
"""
|
||||||
Converts `val` so that it's safe for use in UTF-8 HTML.
|
Converts `val` so that it's safe for use in UTF-8 HTML.
|
||||||
|
|
||||||
>>> websafe("<'&\\">")
|
>>> websafe("<'&\\">")
|
||||||
'<'&">'
|
'<'&">'
|
||||||
>>> websafe(None)
|
>>> websafe(None)
|
||||||
|
|
|
@ -20,9 +20,9 @@ def handle(mapping, fvars=None):
|
||||||
substitutions. `handle` will import modules as necessary.
|
substitutions. `handle` will import modules as necessary.
|
||||||
"""
|
"""
|
||||||
for url, ofno in utils.group(mapping, 2):
|
for url, ofno in utils.group(mapping, 2):
|
||||||
if isinstance(ofno, tuple):
|
if isinstance(ofno, tuple):
|
||||||
ofn, fna = ofno[0], list(ofno[1:])
|
ofn, fna = ofno[0], list(ofno[1:])
|
||||||
else:
|
else:
|
||||||
ofn, fna = ofno, []
|
ofn, fna = ofno, []
|
||||||
fn, result = utils.re_subm('^' + url + '$', ofn, web.ctx.path)
|
fn, result = utils.re_subm('^' + url + '$', ofn, web.ctx.path)
|
||||||
if result: # it's a match
|
if result: # it's a match
|
||||||
|
@ -30,10 +30,10 @@ def handle(mapping, fvars=None):
|
||||||
url = fn.split(' ', 1)[1]
|
url = fn.split(' ', 1)[1]
|
||||||
if web.ctx.method == "GET":
|
if web.ctx.method == "GET":
|
||||||
x = web.ctx.env.get('QUERY_STRING', '')
|
x = web.ctx.env.get('QUERY_STRING', '')
|
||||||
if x:
|
if x:
|
||||||
url += '?' + x
|
url += '?' + x
|
||||||
return http.redirect(url)
|
return http.redirect(url)
|
||||||
elif '.' in fn:
|
elif '.' in fn:
|
||||||
x = fn.split('.')
|
x = fn.split('.')
|
||||||
mod, cls = '.'.join(x[:-1]), x[-1]
|
mod, cls = '.'.join(x[:-1]), x[-1]
|
||||||
mod = __import__(mod, globals(), locals(), [""])
|
mod = __import__(mod, globals(), locals(), [""])
|
||||||
|
@ -41,18 +41,18 @@ def handle(mapping, fvars=None):
|
||||||
else:
|
else:
|
||||||
cls = fn
|
cls = fn
|
||||||
mod = fvars
|
mod = fvars
|
||||||
if isinstance(mod, types.ModuleType):
|
if isinstance(mod, types.ModuleType):
|
||||||
mod = vars(mod)
|
mod = vars(mod)
|
||||||
try:
|
try:
|
||||||
cls = mod[cls]
|
cls = mod[cls]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return web.notfound()
|
return web.notfound()
|
||||||
|
|
||||||
meth = web.ctx.method
|
meth = web.ctx.method
|
||||||
if meth == "HEAD":
|
if meth == "HEAD":
|
||||||
if not hasattr(cls, meth):
|
if not hasattr(cls, meth):
|
||||||
meth = "GET"
|
meth = "GET"
|
||||||
if not hasattr(cls, meth):
|
if not hasattr(cls, meth):
|
||||||
return nomethod(cls)
|
return nomethod(cls)
|
||||||
tocall = getattr(cls(), meth)
|
tocall = getattr(cls(), meth)
|
||||||
args = list(result.groups())
|
args = list(result.groups())
|
||||||
|
@ -86,9 +86,9 @@ def autodelegate(prefix=''):
|
||||||
def GET_password(self): pass
|
def GET_password(self): pass
|
||||||
def GET_privacy(self): pass
|
def GET_privacy(self): pass
|
||||||
|
|
||||||
`GET_password` would get called for `/prefs/password` while `GET_privacy` for
|
`GET_password` would get called for `/prefs/password` while `GET_privacy` for
|
||||||
`GET_privacy` gets called for `/prefs/privacy`.
|
`GET_privacy` gets called for `/prefs/privacy`.
|
||||||
|
|
||||||
If a user visits `/prefs/password/change` then `GET_password(self, '/change')`
|
If a user visits `/prefs/password/change` then `GET_password(self, '/change')`
|
||||||
is called.
|
is called.
|
||||||
"""
|
"""
|
||||||
|
@ -100,7 +100,7 @@ def autodelegate(prefix=''):
|
||||||
else:
|
else:
|
||||||
func = prefix + arg
|
func = prefix + arg
|
||||||
args = []
|
args = []
|
||||||
|
|
||||||
if hasattr(self, func):
|
if hasattr(self, func):
|
||||||
try:
|
try:
|
||||||
return getattr(self, func)(*args)
|
return getattr(self, func)(*args)
|
||||||
|
@ -118,11 +118,11 @@ def webpyfunc(inp, fvars, autoreload=False):
|
||||||
"""find name of the module name from fvars."""
|
"""find name of the module name from fvars."""
|
||||||
file, name = fvars['__file__'], fvars['__name__']
|
file, name = fvars['__file__'], fvars['__name__']
|
||||||
if name == '__main__':
|
if name == '__main__':
|
||||||
# Since the __main__ module can't be reloaded, the module has
|
# Since the __main__ module can't be reloaded, the module has
|
||||||
# to be imported using its file name.
|
# to be imported using its file name.
|
||||||
name = os.path.splitext(os.path.basename(file))[0]
|
name = os.path.splitext(os.path.basename(file))[0]
|
||||||
return name
|
return name
|
||||||
|
|
||||||
mod = __import__(modname(), None, None, [""])
|
mod = __import__(modname(), None, None, [""])
|
||||||
#@@probably should replace this with some inspect magic
|
#@@probably should replace this with some inspect magic
|
||||||
name = utils.dictfind(fvars, inp)
|
name = utils.dictfind(fvars, inp)
|
||||||
|
|
|
@ -56,20 +56,20 @@ class Parser:
|
||||||
self.p = 0
|
self.p = 0
|
||||||
self._lock = [False]
|
self._lock = [False]
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
self._lock[-1] = True
|
self._lock[-1] = True
|
||||||
|
|
||||||
def curline(self):
|
def curline(self):
|
||||||
return self.t[:self.p].count('\n')+1
|
return self.t[:self.p].count('\n')+1
|
||||||
|
|
||||||
def csome(self):
|
def csome(self):
|
||||||
return repr(self.t[self.p:self.p+5]+'...')
|
return repr(self.t[self.p:self.p+5]+'...')
|
||||||
|
|
||||||
def Error(self, x, y=None):
|
def Error(self, x, y=None):
|
||||||
if y is None: y = self.csome()
|
if y is None: y = self.csome()
|
||||||
raise ParseError, "%s: expected %s, got %s (line %s)" % (self.name, x, y, self.curline())
|
raise ParseError, "%s: expected %s, got %s (line %s)" % (self.name, x, y, self.curline())
|
||||||
|
|
||||||
def q(self, f):
|
def q(self, f):
|
||||||
def internal(*a, **kw):
|
def internal(*a, **kw):
|
||||||
checkp = self.p
|
checkp = self.p
|
||||||
|
@ -85,29 +85,29 @@ class Parser:
|
||||||
self._lock.pop()
|
self._lock.pop()
|
||||||
return q or True
|
return q or True
|
||||||
return internal
|
return internal
|
||||||
|
|
||||||
def tokr(self, t):
|
def tokr(self, t):
|
||||||
text = self.c(len(t))
|
text = self.c(len(t))
|
||||||
if text != t:
|
if text != t:
|
||||||
self.Error(repr(t), repr(text))
|
self.Error(repr(t), repr(text))
|
||||||
return t
|
return t
|
||||||
|
|
||||||
def ltokr(self, *l):
|
def ltokr(self, *l):
|
||||||
for x in l:
|
for x in l:
|
||||||
o = self.tokq(x)
|
o = self.tokq(x)
|
||||||
if o: return o
|
if o: return o
|
||||||
self.Error('one of '+repr(l))
|
self.Error('one of '+repr(l))
|
||||||
|
|
||||||
def rer(self, r):
|
def rer(self, r):
|
||||||
x = re.match(r, self.t[self.p:]) #@@re_compile
|
x = re.match(r, self.t[self.p:]) #@@re_compile
|
||||||
if not x:
|
if not x:
|
||||||
self.Error('r'+repr(r))
|
self.Error('r'+repr(r))
|
||||||
return self.tokr(x.group())
|
return self.tokr(x.group())
|
||||||
|
|
||||||
def endr(self):
|
def endr(self):
|
||||||
if self.p != len(self.t):
|
if self.p != len(self.t):
|
||||||
self.Error('EOF')
|
self.Error('EOF')
|
||||||
|
|
||||||
def c(self, n=1):
|
def c(self, n=1):
|
||||||
out = self.t[self.p:self.p+n]
|
out = self.t[self.p:self.p+n]
|
||||||
if out == '' and n != 0:
|
if out == '' and n != 0:
|
||||||
|
@ -117,7 +117,7 @@ class Parser:
|
||||||
|
|
||||||
def lookbehind(self, t):
|
def lookbehind(self, t):
|
||||||
return self.t[self.p-len(t):self.p] == t
|
return self.t[self.p-len(t):self.p] == t
|
||||||
|
|
||||||
def __getattr__(self, a):
|
def __getattr__(self, a):
|
||||||
if a.endswith('q'):
|
if a.endswith('q'):
|
||||||
return self.q(getattr(self, a[:-1]+'r'))
|
return self.q(getattr(self, a[:-1]+'r'))
|
||||||
|
@ -128,25 +128,25 @@ class TemplateParser(Parser):
|
||||||
Parser.__init__(self, *a, **kw)
|
Parser.__init__(self, *a, **kw)
|
||||||
self.curws = ''
|
self.curws = ''
|
||||||
self.curind = ''
|
self.curind = ''
|
||||||
|
|
||||||
def o(self, *a):
|
def o(self, *a):
|
||||||
return a+('lineno', self.curline())
|
return a+('lineno', self.curline())
|
||||||
|
|
||||||
def go(self):
|
def go(self):
|
||||||
# maybe try to do some traceback parsing/hacking
|
# maybe try to do some traceback parsing/hacking
|
||||||
return self.gor()
|
return self.gor()
|
||||||
|
|
||||||
def gor(self):
|
def gor(self):
|
||||||
header = self.defwithq()
|
header = self.defwithq()
|
||||||
results = self.lines(start=True)
|
results = self.lines(start=True)
|
||||||
self.endr()
|
self.endr()
|
||||||
return header, results
|
return header, results
|
||||||
|
|
||||||
def ws(self):
|
def ws(self):
|
||||||
n = 0
|
n = 0
|
||||||
while self.tokq(" "): n += 1
|
while self.tokq(" "): n += 1
|
||||||
return " " * n
|
return " " * n
|
||||||
|
|
||||||
def defwithr(self):
|
def defwithr(self):
|
||||||
self.tokr('$def with ')
|
self.tokr('$def with ')
|
||||||
self.lock()
|
self.lock()
|
||||||
|
@ -158,12 +158,12 @@ class TemplateParser(Parser):
|
||||||
if self.tokq('='):
|
if self.tokq('='):
|
||||||
v = self.exprr()
|
v = self.exprr()
|
||||||
kw.append((x, v))
|
kw.append((x, v))
|
||||||
else:
|
else:
|
||||||
args.append(x)
|
args.append(x)
|
||||||
x = self.tokq(', ') and self.req(r_var)
|
x = self.tokq(', ') and self.req(r_var)
|
||||||
self.tokr(')\n')
|
self.tokr(')\n')
|
||||||
return self.o('defwith', 'null', None, 'args', args, 'kwargs', kw)
|
return self.o('defwith', 'null', None, 'args', args, 'kwargs', kw)
|
||||||
|
|
||||||
def literalr(self):
|
def literalr(self):
|
||||||
o = (
|
o = (
|
||||||
self.req('"[^"]*"') or #@@ no support for escapes
|
self.req('"[^"]*"') or #@@ no support for escapes
|
||||||
|
@ -177,7 +177,7 @@ class TemplateParser(Parser):
|
||||||
|
|
||||||
if o is False: self.Error('literal')
|
if o is False: self.Error('literal')
|
||||||
return self.o('literal', 'thing', o)
|
return self.o('literal', 'thing', o)
|
||||||
|
|
||||||
def listr(self):
|
def listr(self):
|
||||||
self.tokr('[')
|
self.tokr('[')
|
||||||
self.lock()
|
self.lock()
|
||||||
|
@ -189,7 +189,7 @@ class TemplateParser(Parser):
|
||||||
if not self.tokq(', '): break
|
if not self.tokq(', '): break
|
||||||
self.tokr(']')
|
self.tokr(']')
|
||||||
return self.o('list', 'thing', x)
|
return self.o('list', 'thing', x)
|
||||||
|
|
||||||
def dictr(self):
|
def dictr(self):
|
||||||
self.tokr('{')
|
self.tokr('{')
|
||||||
self.lock()
|
self.lock()
|
||||||
|
@ -210,11 +210,11 @@ class TemplateParser(Parser):
|
||||||
o = self.exprr() # todo: allow list
|
o = self.exprr() # todo: allow list
|
||||||
self.tokr(')')
|
self.tokr(')')
|
||||||
return self.o('paren', 'thing', o)
|
return self.o('paren', 'thing', o)
|
||||||
|
|
||||||
def atomr(self):
|
def atomr(self):
|
||||||
"""returns var, literal, paren, dict, or list"""
|
"""returns var, literal, paren, dict, or list"""
|
||||||
o = (
|
o = (
|
||||||
self.varq() or
|
self.varq() or
|
||||||
self.parenq() or
|
self.parenq() or
|
||||||
self.dictq() or
|
self.dictq() or
|
||||||
self.listq() or
|
self.listq() or
|
||||||
|
@ -222,9 +222,9 @@ class TemplateParser(Parser):
|
||||||
)
|
)
|
||||||
if o is False: self.Error('atom')
|
if o is False: self.Error('atom')
|
||||||
return o
|
return o
|
||||||
|
|
||||||
def primaryr(self):
|
def primaryr(self):
|
||||||
"""returns getattr, call, or getitem"""
|
"""returns getattr, call, or getitem"""
|
||||||
n = self.atomr()
|
n = self.atomr()
|
||||||
while 1:
|
while 1:
|
||||||
if self.tokq('.'):
|
if self.tokq('.'):
|
||||||
|
@ -237,7 +237,7 @@ class TemplateParser(Parser):
|
||||||
elif self.tokq('('):
|
elif self.tokq('('):
|
||||||
args = []
|
args = []
|
||||||
kw = []
|
kw = []
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
# need to see if we're doing a keyword argument
|
# need to see if we're doing a keyword argument
|
||||||
checkp = self.p
|
checkp = self.p
|
||||||
|
@ -252,7 +252,7 @@ class TemplateParser(Parser):
|
||||||
args.append(x)
|
args.append(x)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
if not self.tokq(', '): break
|
if not self.tokq(', '): break
|
||||||
self.tokr(')')
|
self.tokr(')')
|
||||||
n = self.o('call', 'thing', n, 'args', args, 'kwargs', kw)
|
n = self.o('call', 'thing', n, 'args', args, 'kwargs', kw)
|
||||||
|
@ -262,9 +262,9 @@ class TemplateParser(Parser):
|
||||||
n = self.o('getitem', 'thing', n, 'item', v)
|
n = self.o('getitem', 'thing', n, 'item', v)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def exprr(self):
|
def exprr(self):
|
||||||
negate = self.tokq('not ')
|
negate = self.tokq('not ')
|
||||||
x = self.primaryr()
|
x = self.primaryr()
|
||||||
|
@ -273,12 +273,12 @@ class TemplateParser(Parser):
|
||||||
self.tokr(' ')
|
self.tokr(' ')
|
||||||
y = self.exprr()
|
y = self.exprr()
|
||||||
x = self.o('test', 'x', x, 'op', operator, 'y', y)
|
x = self.o('test', 'x', x, 'op', operator, 'y', y)
|
||||||
|
|
||||||
return self.o('expr', 'thing', x, 'negate', negate)
|
return self.o('expr', 'thing', x, 'negate', negate)
|
||||||
|
|
||||||
def varr(self):
|
def varr(self):
|
||||||
return self.o('var', 'name', self.rer(r_var))
|
return self.o('var', 'name', self.rer(r_var))
|
||||||
|
|
||||||
def liner(self):
|
def liner(self):
|
||||||
out = []
|
out = []
|
||||||
o = self.curws
|
o = self.curws
|
||||||
|
@ -293,7 +293,7 @@ class TemplateParser(Parser):
|
||||||
o = o[:-1] + c
|
o = o[:-1] + c
|
||||||
else:
|
else:
|
||||||
filter = not bool(self.tokq(':'))
|
filter = not bool(self.tokq(':'))
|
||||||
|
|
||||||
if self.tokq('{'):
|
if self.tokq('{'):
|
||||||
out.append(o)
|
out.append(o)
|
||||||
out.append(self.o('itpl', 'name', self.exprr(), 'filter', filter))
|
out.append(self.o('itpl', 'name', self.exprr(), 'filter', filter))
|
||||||
|
@ -301,7 +301,7 @@ class TemplateParser(Parser):
|
||||||
o = ''
|
o = ''
|
||||||
else:
|
else:
|
||||||
g = self.primaryq()
|
g = self.primaryq()
|
||||||
if g:
|
if g:
|
||||||
out.append(o)
|
out.append(o)
|
||||||
out.append(self.o('itpl', 'name', g, 'filter', filter))
|
out.append(self.o('itpl', 'name', g, 'filter', filter))
|
||||||
o = ''
|
o = ''
|
||||||
|
@ -316,7 +316,7 @@ class TemplateParser(Parser):
|
||||||
o = o[:-1]
|
o = o[:-1]
|
||||||
out.append(o)
|
out.append(o)
|
||||||
return self.o('line', 'thing', out)
|
return self.o('line', 'thing', out)
|
||||||
|
|
||||||
def varsetr(self):
|
def varsetr(self):
|
||||||
self.tokr('$var ')
|
self.tokr('$var ')
|
||||||
self.lock()
|
self.lock()
|
||||||
|
@ -331,21 +331,21 @@ class TemplateParser(Parser):
|
||||||
expr = self.exprr()
|
expr = self.exprr()
|
||||||
self.tokr(":")
|
self.tokr(":")
|
||||||
ifc = self.lines()
|
ifc = self.lines()
|
||||||
|
|
||||||
elifs = []
|
elifs = []
|
||||||
while self.tokq(self.curws + self.curind + '$elif '):
|
while self.tokq(self.curws + self.curind + '$elif '):
|
||||||
v = self.exprr()
|
v = self.exprr()
|
||||||
self.tokr(':')
|
self.tokr(':')
|
||||||
c = self.lines()
|
c = self.lines()
|
||||||
elifs.append(self.o('elif', 'clause', v, 'body', c))
|
elifs.append(self.o('elif', 'clause', v, 'body', c))
|
||||||
|
|
||||||
if self.tokq(self.curws + self.curind + "$else:"):
|
if self.tokq(self.curws + self.curind + "$else:"):
|
||||||
elsec = self.lines()
|
elsec = self.lines()
|
||||||
else:
|
else:
|
||||||
elsec = None
|
elsec = None
|
||||||
|
|
||||||
return self.o('if', 'clause', expr, 'then', ifc, 'elif', elifs, 'else', elsec)
|
return self.o('if', 'clause', expr, 'then', ifc, 'elif', elifs, 'else', elsec)
|
||||||
|
|
||||||
def forr(self):
|
def forr(self):
|
||||||
self.tokr("$for ")
|
self.tokr("$for ")
|
||||||
self.lock()
|
self.lock()
|
||||||
|
@ -359,44 +359,44 @@ class TemplateParser(Parser):
|
||||||
elsec = self.lines()
|
elsec = self.lines()
|
||||||
else:
|
else:
|
||||||
elsec = None
|
elsec = None
|
||||||
|
|
||||||
return self.o('for', 'name', v, 'body', l, 'in', g, 'else', elsec)
|
return self.o('for', 'name', v, 'body', l, 'in', g, 'else', elsec)
|
||||||
|
|
||||||
def whiler(self):
|
def whiler(self):
|
||||||
self.tokr('$while ')
|
self.tokr('$while ')
|
||||||
self.lock()
|
self.lock()
|
||||||
v = self.exprr()
|
v = self.exprr()
|
||||||
self.tokr(":")
|
self.tokr(":")
|
||||||
l = self.lines()
|
l = self.lines()
|
||||||
|
|
||||||
if self.tokq(self.curws + self.curind + '$else:'):
|
if self.tokq(self.curws + self.curind + '$else:'):
|
||||||
elsec = self.lines()
|
elsec = self.lines()
|
||||||
else:
|
else:
|
||||||
elsec = None
|
elsec = None
|
||||||
|
|
||||||
return self.o('while', 'clause', v, 'body', l, 'null', None, 'else', elsec)
|
return self.o('while', 'clause', v, 'body', l, 'null', None, 'else', elsec)
|
||||||
|
|
||||||
def assignr(self):
|
def assignr(self):
|
||||||
self.tokr('$ ')
|
self.tokr('$ ')
|
||||||
assign = self.rer(r_var) # NOTE: setable
|
assign = self.rer(r_var) # NOTE: setable
|
||||||
self.tokr(' = ')
|
self.tokr(' = ')
|
||||||
expr = self.exprr()
|
expr = self.exprr()
|
||||||
self.tokr('\n')
|
self.tokr('\n')
|
||||||
|
|
||||||
return self.o('assign', 'name', assign, 'expr', expr)
|
return self.o('assign', 'name', assign, 'expr', expr)
|
||||||
|
|
||||||
def commentr(self):
|
def commentr(self):
|
||||||
self.tokr('$#')
|
self.tokr('$#')
|
||||||
self.lock()
|
self.lock()
|
||||||
while self.c() != '\n': pass
|
while self.c() != '\n': pass
|
||||||
return self.o('comment')
|
return self.o('comment')
|
||||||
|
|
||||||
def setabler(self):
|
def setabler(self):
|
||||||
out = [self.varr()] #@@ not quite right
|
out = [self.varr()] #@@ not quite right
|
||||||
while self.tokq(', '):
|
while self.tokq(', '):
|
||||||
out.append(self.varr())
|
out.append(self.varr())
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def lines(self, start=False):
|
def lines(self, start=False):
|
||||||
"""
|
"""
|
||||||
This function gets called from two places:
|
This function gets called from two places:
|
||||||
|
@ -417,13 +417,13 @@ class TemplateParser(Parser):
|
||||||
oldws = self.curws
|
oldws = self.curws
|
||||||
t = self.tokq(oldws + self.curind)
|
t = self.tokq(oldws + self.curind)
|
||||||
if not t: break
|
if not t: break
|
||||||
|
|
||||||
self.curws += self.ws()
|
self.curws += self.ws()
|
||||||
x = t and (
|
x = t and (
|
||||||
self.varsetq() or
|
self.varsetq() or
|
||||||
self.ifq() or
|
self.ifq() or
|
||||||
self.forq() or
|
self.forq() or
|
||||||
self.whileq() or
|
self.whileq() or
|
||||||
self.assignq() or
|
self.assignq() or
|
||||||
self.commentq() or
|
self.commentq() or
|
||||||
self.lineq())
|
self.lineq())
|
||||||
|
@ -437,7 +437,7 @@ class TemplateParser(Parser):
|
||||||
|
|
||||||
if not start: self.curind = oldind
|
if not start: self.curind = oldind
|
||||||
return o
|
return o
|
||||||
|
|
||||||
class Stowage(storage):
|
class Stowage(storage):
|
||||||
def __str__(self): return self.get('_str')
|
def __str__(self): return self.get('_str')
|
||||||
#@@ edits in place
|
#@@ edits in place
|
||||||
|
@ -453,7 +453,7 @@ class Stowage(storage):
|
||||||
return self
|
return self
|
||||||
else:
|
else:
|
||||||
raise TypeError, 'cannot add'
|
raise TypeError, 'cannot add'
|
||||||
|
|
||||||
class WTF(AssertionError): pass
|
class WTF(AssertionError): pass
|
||||||
class SecurityError(Exception):
|
class SecurityError(Exception):
|
||||||
"""The template seems to be trying to do something naughty."""
|
"""The template seems to be trying to do something naughty."""
|
||||||
|
@ -469,7 +469,7 @@ class Template:
|
||||||
'.html' : 'text/html; charset=utf-8',
|
'.html' : 'text/html; charset=utf-8',
|
||||||
'.txt' : 'text/plain',
|
'.txt' : 'text/plain',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, text, filter=None, filename=""):
|
def __init__(self, text, filter=None, filename=""):
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
@ -482,7 +482,7 @@ class Template:
|
||||||
self.h_defwith(header)
|
self.h_defwith(header)
|
||||||
else:
|
else:
|
||||||
self.args, self.kwargs = (), {}
|
self.args, self.kwargs = (), {}
|
||||||
|
|
||||||
def __call__(self, *a, **kw):
|
def __call__(self, *a, **kw):
|
||||||
d = self.globals.copy()
|
d = self.globals.copy()
|
||||||
d.update(self._parseargs(a, kw))
|
d.update(self._parseargs(a, kw))
|
||||||
|
@ -494,14 +494,14 @@ class Template:
|
||||||
content_type = self.find_content_type()
|
content_type = self.find_content_type()
|
||||||
if content_type:
|
if content_type:
|
||||||
web.header('Content-Type', content_type, unique=True)
|
web.header('Content-Type', content_type, unique=True)
|
||||||
|
|
||||||
return f.go()
|
return f.go()
|
||||||
|
|
||||||
def find_content_type(self):
|
def find_content_type(self):
|
||||||
for ext, content_type in self.content_types.iteritems():
|
for ext, content_type in self.content_types.iteritems():
|
||||||
if self.filename.endswith(ext):
|
if self.filename.endswith(ext):
|
||||||
return content_type
|
return content_type
|
||||||
|
|
||||||
def _parseargs(self, inargs, inkwargs):
|
def _parseargs(self, inargs, inkwargs):
|
||||||
# difference from Python:
|
# difference from Python:
|
||||||
# no error on setting a keyword arg twice
|
# no error on setting a keyword arg twice
|
||||||
|
@ -526,10 +526,10 @@ class Template:
|
||||||
if v is Required:
|
if v is Required:
|
||||||
unset.append(k)
|
unset.append(k)
|
||||||
if unset:
|
if unset:
|
||||||
raise TypeError, 'values for %s are required' % unset
|
raise TypeError, 'values for %s are required' % unset
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def h_defwith(self, header):
|
def h_defwith(self, header):
|
||||||
assert header[WHAT] == 'defwith'
|
assert header[WHAT] == 'defwith'
|
||||||
f = Fill(self.tree, d={})
|
f = Fill(self.tree, d={})
|
||||||
|
@ -546,18 +546,18 @@ class Handle:
|
||||||
def __init__(self, parsetree, **kw):
|
def __init__(self, parsetree, **kw):
|
||||||
self._funccache = {}
|
self._funccache = {}
|
||||||
self.parsetree = parsetree
|
self.parsetree = parsetree
|
||||||
for (k, v) in kw.iteritems(): setattr(self, k, v)
|
for (k, v) in kw.iteritems(): setattr(self, k, v)
|
||||||
|
|
||||||
def h(self, item):
|
def h(self, item):
|
||||||
return getattr(self, 'h_' + item[WHAT])(item)
|
return getattr(self, 'h_' + item[WHAT])(item)
|
||||||
|
|
||||||
class Fill(Handle):
|
class Fill(Handle):
|
||||||
builtins = global_globals
|
builtins = global_globals
|
||||||
def filter(self, text):
|
def filter(self, text):
|
||||||
if text is None: return ''
|
if text is None: return ''
|
||||||
else: return utf8(text)
|
else: return utf8(text)
|
||||||
# often replaced with stuff like net.websafe
|
# often replaced with stuff like net.websafe
|
||||||
|
|
||||||
def h_literal(self, i):
|
def h_literal(self, i):
|
||||||
item = i[THING]
|
item = i[THING]
|
||||||
if isinstance(item, (unicode, str)) and item[0] in ['"', "'"]:
|
if isinstance(item, (unicode, str)) and item[0] in ['"', "'"]:
|
||||||
|
@ -565,27 +565,27 @@ class Fill(Handle):
|
||||||
elif isinstance(item, (float, int)):
|
elif isinstance(item, (float, int)):
|
||||||
pass
|
pass
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def h_list(self, i):
|
def h_list(self, i):
|
||||||
x = i[THING]
|
x = i[THING]
|
||||||
out = []
|
out = []
|
||||||
for item in x:
|
for item in x:
|
||||||
out.append(self.h(item))
|
out.append(self.h(item))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def h_dict(self, i):
|
def h_dict(self, i):
|
||||||
x = i[THING]
|
x = i[THING]
|
||||||
out = {}
|
out = {}
|
||||||
for k, v in x.iteritems():
|
for k, v in x.iteritems():
|
||||||
out[self.h(k)] = self.h(v)
|
out[self.h(k)] = self.h(v)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def h_paren(self, i):
|
def h_paren(self, i):
|
||||||
item = i[THING]
|
item = i[THING]
|
||||||
if isinstance(item, list):
|
if isinstance(item, list):
|
||||||
raise NotImplementedError, 'tuples'
|
raise NotImplementedError, 'tuples'
|
||||||
return self.h(item)
|
return self.h(item)
|
||||||
|
|
||||||
def h_getattr(self, i):
|
def h_getattr(self, i):
|
||||||
thing, attr = i[THING], i[ATTR]
|
thing, attr = i[THING], i[ATTR]
|
||||||
thing = self.h(thing)
|
thing = self.h(thing)
|
||||||
|
@ -603,25 +603,25 @@ class Fill(Handle):
|
||||||
return lambda s: s.join(thing)
|
return lambda s: s.join(thing)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def h_call(self, i):
|
def h_call(self, i):
|
||||||
call = self.h(i[THING])
|
call = self.h(i[THING])
|
||||||
args = [self.h(x) for x in i[ARGS]]
|
args = [self.h(x) for x in i[ARGS]]
|
||||||
kw = dict([(x, self.h(y)) for (x, y) in i[KWARGS]])
|
kw = dict([(x, self.h(y)) for (x, y) in i[KWARGS]])
|
||||||
return call(*args, **kw)
|
return call(*args, **kw)
|
||||||
|
|
||||||
def h_getitem(self, i):
|
def h_getitem(self, i):
|
||||||
thing, item = i[THING], i[ITEM]
|
thing, item = i[THING], i[ITEM]
|
||||||
thing = self.h(thing)
|
thing = self.h(thing)
|
||||||
item = self.h(item)
|
item = self.h(item)
|
||||||
return thing[item]
|
return thing[item]
|
||||||
|
|
||||||
def h_expr(self, i):
|
def h_expr(self, i):
|
||||||
item = self.h(i[THING])
|
item = self.h(i[THING])
|
||||||
if i[NEGATE]:
|
if i[NEGATE]:
|
||||||
item = not item
|
item = not item
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def h_test(self, item):
|
def h_test(self, item):
|
||||||
ox, op, oy = item[X], item[OP], item[Y]
|
ox, op, oy = item[X], item[OP], item[Y]
|
||||||
# for short-circuiting to work, we can't eval these here
|
# for short-circuiting to work, we can't eval these here
|
||||||
|
@ -662,7 +662,7 @@ class Fill(Handle):
|
||||||
return e(ox) % e(oy)
|
return e(ox) % e(oy)
|
||||||
else:
|
else:
|
||||||
raise WTF, 'op ' + op
|
raise WTF, 'op ' + op
|
||||||
|
|
||||||
def h_var(self, i):
|
def h_var(self, i):
|
||||||
v = i[NAME]
|
v = i[NAME]
|
||||||
if v in self.d:
|
if v in self.d:
|
||||||
|
@ -673,7 +673,7 @@ class Fill(Handle):
|
||||||
return self.output
|
return self.output
|
||||||
else:
|
else:
|
||||||
raise NameError, 'could not find %s (line %s)' % (repr(i[NAME]), i[LINENO])
|
raise NameError, 'could not find %s (line %s)' % (repr(i[NAME]), i[LINENO])
|
||||||
|
|
||||||
def h_line(self, i):
|
def h_line(self, i):
|
||||||
out = []
|
out = []
|
||||||
for x in i[THING]:
|
for x in i[THING]:
|
||||||
|
@ -684,13 +684,13 @@ class Fill(Handle):
|
||||||
o = self.h(x[NAME])
|
o = self.h(x[NAME])
|
||||||
if x[FILTER]:
|
if x[FILTER]:
|
||||||
o = self.filter(o)
|
o = self.filter(o)
|
||||||
else:
|
else:
|
||||||
o = (o is not None and utf8(o)) or ""
|
o = (o is not None and utf8(o)) or ""
|
||||||
out.append(o)
|
out.append(o)
|
||||||
else:
|
else:
|
||||||
raise WTF, x
|
raise WTF, x
|
||||||
return ''.join(out)
|
return ''.join(out)
|
||||||
|
|
||||||
def h_varset(self, i):
|
def h_varset(self, i):
|
||||||
self.output[i[NAME]] = ''.join(self.h_lines(i[BODY]))
|
self.output[i[NAME]] = ''.join(self.h_lines(i[BODY]))
|
||||||
return ''
|
return ''
|
||||||
|
@ -708,7 +708,7 @@ class Fill(Handle):
|
||||||
else:
|
else:
|
||||||
do = i[ELSE]
|
do = i[ELSE]
|
||||||
return ''.join(self.h_lines(do))
|
return ''.join(self.h_lines(do))
|
||||||
|
|
||||||
def h_for(self, i):
|
def h_for(self, i):
|
||||||
out = []
|
out = []
|
||||||
assert i[IN][WHAT] == 'expr'
|
assert i[IN][WHAT] == 'expr'
|
||||||
|
@ -724,13 +724,13 @@ class Fill(Handle):
|
||||||
for x, y in zip(forvar, nv):
|
for x, y in zip(forvar, nv):
|
||||||
assert x[WHAT] == 'var'
|
assert x[WHAT] == 'var'
|
||||||
self.d[x[NAME]] = y
|
self.d[x[NAME]] = y
|
||||||
|
|
||||||
out.extend(self.h_lines(i[BODY]))
|
out.extend(self.h_lines(i[BODY]))
|
||||||
else:
|
else:
|
||||||
if i[ELSE]:
|
if i[ELSE]:
|
||||||
out.extend(self.h_lines(i[ELSE]))
|
out.extend(self.h_lines(i[ELSE]))
|
||||||
return ''.join(out)
|
return ''.join(out)
|
||||||
|
|
||||||
def h_while(self, i):
|
def h_while(self, i):
|
||||||
out = []
|
out = []
|
||||||
expr = self.h(i[CLAUSE])
|
expr = self.h(i[CLAUSE])
|
||||||
|
@ -750,11 +750,11 @@ class Fill(Handle):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def h_comment(self, i): pass
|
def h_comment(self, i): pass
|
||||||
|
|
||||||
def h_lines(self, lines):
|
def h_lines(self, lines):
|
||||||
if lines is None: return []
|
if lines is None: return []
|
||||||
return map(self.h, lines)
|
return map(self.h, lines)
|
||||||
|
|
||||||
def go(self):
|
def go(self):
|
||||||
self.output = Stowage()
|
self.output = Stowage()
|
||||||
self.output._str = ''.join(map(self.h, self.parsetree))
|
self.output._str = ''.join(map(self.h, self.parsetree))
|
||||||
|
@ -769,11 +769,11 @@ class render:
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
else:
|
else:
|
||||||
self.cache = False
|
self.cache = False
|
||||||
|
|
||||||
def _do(self, name, filter=None):
|
def _do(self, name, filter=None):
|
||||||
if self.cache is False or name not in self.cache:
|
if self.cache is False or name not in self.cache:
|
||||||
|
|
||||||
tmplpath = os.path.join(self.loc, name)
|
tmplpath = os.path.join(self.loc, name)
|
||||||
p = [f for f in glob.glob(tmplpath + '.*') if not f.endswith('~')] # skip backup files
|
p = [f for f in glob.glob(tmplpath + '.*') if not f.endswith('~')] # skip backup files
|
||||||
if not p and os.path.isdir(tmplpath):
|
if not p and os.path.isdir(tmplpath):
|
||||||
return render(tmplpath, cache=self.cache)
|
return render(tmplpath, cache=self.cache)
|
||||||
|
@ -783,7 +783,7 @@ class render:
|
||||||
p = p[0]
|
p = p[0]
|
||||||
c = Template(open(p).read(), filename=p)
|
c = Template(open(p).read(), filename=p)
|
||||||
if self.cache is not False: self.cache[name] = (p, c)
|
if self.cache is not False: self.cache[name] = (p, c)
|
||||||
|
|
||||||
if self.cache is not False: p, c = self.cache[name]
|
if self.cache is not False: p, c = self.cache[name]
|
||||||
|
|
||||||
if p.endswith('.html') or p.endswith('.xml'):
|
if p.endswith('.html') or p.endswith('.xml'):
|
||||||
|
@ -806,7 +806,7 @@ def test():
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
else:
|
else:
|
||||||
assert a == b, "\nexpected: %s\ngot: %s" % (repr(b), repr(a))
|
assert a == b, "\nexpected: %s\ngot: %s" % (repr(b), repr(a))
|
||||||
|
|
||||||
from utils import storage, group
|
from utils import storage, group
|
||||||
|
|
||||||
class t:
|
class t:
|
||||||
|
@ -828,7 +828,7 @@ def test():
|
||||||
else:
|
else:
|
||||||
print >> sys.stderr, 'FAIL:', repr(self.source), 'expected', repr(other), ', got', repr(self.value)
|
print >> sys.stderr, 'FAIL:', repr(self.source), 'expected', repr(other), ', got', repr(self.value)
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
t('1')() == '1\n'
|
t('1')() == '1\n'
|
||||||
t('$def with ()\n1')() == '1\n'
|
t('$def with ()\n1')() == '1\n'
|
||||||
t('$def with (a)\n$a')(1) == '1\n'
|
t('$def with (a)\n$a')(1) == '1\n'
|
||||||
|
@ -872,7 +872,7 @@ def test():
|
||||||
assertEqual(str(j), '')
|
assertEqual(str(j), '')
|
||||||
assertEqual(j.foo, 'bar\n')
|
assertEqual(j.foo, 'bar\n')
|
||||||
if verbose: sys.stderr.write('\n')
|
if verbose: sys.stderr.write('\n')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test()
|
test()
|
||||||
|
|
|
@ -4,8 +4,8 @@ General Utilities
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Storage", "storage", "storify",
|
"Storage", "storage", "storify",
|
||||||
"iters",
|
"iters",
|
||||||
"rstrips", "lstrips", "strips", "utf8",
|
"rstrips", "lstrips", "strips", "utf8",
|
||||||
"TimeoutError", "timelimit",
|
"TimeoutError", "timelimit",
|
||||||
"Memoize", "memoize",
|
"Memoize", "memoize",
|
||||||
|
@ -31,7 +31,7 @@ class Storage(dict):
|
||||||
"""
|
"""
|
||||||
A Storage object is like a dictionary except `obj.foo` can be used
|
A Storage object is like a dictionary except `obj.foo` can be used
|
||||||
in addition to `obj['foo']`.
|
in addition to `obj['foo']`.
|
||||||
|
|
||||||
>>> o = storage(a=1)
|
>>> o = storage(a=1)
|
||||||
>>> o.a
|
>>> o.a
|
||||||
1
|
1
|
||||||
|
@ -45,15 +45,15 @@ class Storage(dict):
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'a'
|
AttributeError: 'a'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
try:
|
try:
|
||||||
return self[key]
|
return self[key]
|
||||||
except KeyError, k:
|
except KeyError, k:
|
||||||
raise AttributeError, k
|
raise AttributeError, k
|
||||||
|
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
self[key] = value
|
self[key] = value
|
||||||
|
|
||||||
def __delattr__(self, key):
|
def __delattr__(self, key):
|
||||||
|
@ -62,7 +62,7 @@ class Storage(dict):
|
||||||
except KeyError, k:
|
except KeyError, k:
|
||||||
raise AttributeError, k
|
raise AttributeError, k
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Storage ' + dict.__repr__(self) + '>'
|
return '<Storage ' + dict.__repr__(self) + '>'
|
||||||
|
|
||||||
storage = Storage
|
storage = Storage
|
||||||
|
@ -70,16 +70,16 @@ storage = Storage
|
||||||
def storify(mapping, *requireds, **defaults):
|
def storify(mapping, *requireds, **defaults):
|
||||||
"""
|
"""
|
||||||
Creates a `storage` object from dictionary `mapping`, raising `KeyError` if
|
Creates a `storage` object from dictionary `mapping`, raising `KeyError` if
|
||||||
d doesn't have all of the keys in `requireds` and using the default
|
d doesn't have all of the keys in `requireds` and using the default
|
||||||
values for keys found in `defaults`.
|
values for keys found in `defaults`.
|
||||||
|
|
||||||
For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of
|
For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of
|
||||||
`storage({'a':1, 'b':2, 'c':3})`.
|
`storage({'a':1, 'b':2, 'c':3})`.
|
||||||
|
|
||||||
If a `storify` value is a list (e.g. multiple values in a form submission),
|
If a `storify` value is a list (e.g. multiple values in a form submission),
|
||||||
`storify` returns the last element of the list, unless the key appears in
|
`storify` returns the last element of the list, unless the key appears in
|
||||||
`defaults` as a list. Thus:
|
`defaults` as a list. Thus:
|
||||||
|
|
||||||
>>> storify({'a':[1, 2]}).a
|
>>> storify({'a':[1, 2]}).a
|
||||||
2
|
2
|
||||||
>>> storify({'a':[1, 2]}, a=[]).a
|
>>> storify({'a':[1, 2]}, a=[]).a
|
||||||
|
@ -88,24 +88,24 @@ def storify(mapping, *requireds, **defaults):
|
||||||
[1]
|
[1]
|
||||||
>>> storify({}, a=[]).a
|
>>> storify({}, a=[]).a
|
||||||
[]
|
[]
|
||||||
|
|
||||||
Similarly, if the value has a `value` attribute, `storify will return _its_
|
Similarly, if the value has a `value` attribute, `storify will return _its_
|
||||||
value, unless the key appears in `defaults` as a dictionary.
|
value, unless the key appears in `defaults` as a dictionary.
|
||||||
|
|
||||||
>>> storify({'a':storage(value=1)}).a
|
>>> storify({'a':storage(value=1)}).a
|
||||||
1
|
1
|
||||||
>>> storify({'a':storage(value=1)}, a={}).a
|
>>> storify({'a':storage(value=1)}, a={}).a
|
||||||
<Storage {'value': 1}>
|
<Storage {'value': 1}>
|
||||||
>>> storify({}, a={}).a
|
>>> storify({}, a={}).a
|
||||||
{}
|
{}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def getvalue(x):
|
def getvalue(x):
|
||||||
if hasattr(x, 'value'):
|
if hasattr(x, 'value'):
|
||||||
return x.value
|
return x.value
|
||||||
else:
|
else:
|
||||||
return x
|
return x
|
||||||
|
|
||||||
stor = Storage()
|
stor = Storage()
|
||||||
for key in requireds + tuple(mapping.keys()):
|
for key in requireds + tuple(mapping.keys()):
|
||||||
value = mapping[key]
|
value = mapping[key]
|
||||||
|
@ -122,22 +122,22 @@ def storify(mapping, *requireds, **defaults):
|
||||||
|
|
||||||
for (key, value) in defaults.iteritems():
|
for (key, value) in defaults.iteritems():
|
||||||
result = value
|
result = value
|
||||||
if hasattr(stor, key):
|
if hasattr(stor, key):
|
||||||
result = stor[key]
|
result = stor[key]
|
||||||
if value == () and not isinstance(result, tuple):
|
if value == () and not isinstance(result, tuple):
|
||||||
result = (result,)
|
result = (result,)
|
||||||
setattr(stor, key, result)
|
setattr(stor, key, result)
|
||||||
|
|
||||||
return stor
|
return stor
|
||||||
|
|
||||||
iters = [list, tuple]
|
iters = [list, tuple]
|
||||||
import __builtin__
|
import __builtin__
|
||||||
if hasattr(__builtin__, 'set'):
|
if hasattr(__builtin__, 'set'):
|
||||||
iters.append(set)
|
iters.append(set)
|
||||||
try:
|
try:
|
||||||
from sets import Set
|
from sets import Set
|
||||||
iters.append(Set)
|
iters.append(Set)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class _hack(tuple): pass
|
class _hack(tuple): pass
|
||||||
|
@ -148,13 +148,13 @@ of lists, tuples, sets, and Sets are available in this version of Python.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _strips(direction, text, remove):
|
def _strips(direction, text, remove):
|
||||||
if direction == 'l':
|
if direction == 'l':
|
||||||
if text.startswith(remove):
|
if text.startswith(remove):
|
||||||
return text[len(remove):]
|
return text[len(remove):]
|
||||||
elif direction == 'r':
|
elif direction == 'r':
|
||||||
if text.endswith(remove):
|
if text.endswith(remove):
|
||||||
return text[:-len(remove)]
|
return text[:-len(remove)]
|
||||||
else:
|
else:
|
||||||
raise ValueError, "Direction needs to be r or l."
|
raise ValueError, "Direction needs to be r or l."
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -164,17 +164,17 @@ def rstrips(text, remove):
|
||||||
|
|
||||||
>>> rstrips("foobar", "bar")
|
>>> rstrips("foobar", "bar")
|
||||||
'foo'
|
'foo'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return _strips('r', text, remove)
|
return _strips('r', text, remove)
|
||||||
|
|
||||||
def lstrips(text, remove):
|
def lstrips(text, remove):
|
||||||
"""
|
"""
|
||||||
removes the string `remove` from the left of `text`
|
removes the string `remove` from the left of `text`
|
||||||
|
|
||||||
>>> lstrips("foobar", "foo")
|
>>> lstrips("foobar", "foo")
|
||||||
'bar'
|
'bar'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return _strips('l', text, remove)
|
return _strips('l', text, remove)
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ def strips(text, remove):
|
||||||
|
|
||||||
>>> strips("foobarfoo", "foo")
|
>>> strips("foobarfoo", "foo")
|
||||||
'bar'
|
'bar'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return rstrips(lstrips(text, remove), remove)
|
return rstrips(lstrips(text, remove), remove)
|
||||||
|
|
||||||
|
@ -201,12 +201,12 @@ def timelimit(timeout):
|
||||||
"""
|
"""
|
||||||
A decorator to limit a function to `timeout` seconds, raising `TimeoutError`
|
A decorator to limit a function to `timeout` seconds, raising `TimeoutError`
|
||||||
if it takes longer.
|
if it takes longer.
|
||||||
|
|
||||||
>>> import time
|
>>> import time
|
||||||
>>> def meaningoflife():
|
>>> def meaningoflife():
|
||||||
... time.sleep(.2)
|
... time.sleep(.2)
|
||||||
... return 42
|
... return 42
|
||||||
>>>
|
>>>
|
||||||
>>> timelimit(.1)(meaningoflife)()
|
>>> timelimit(.1)(meaningoflife)()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
|
@ -214,7 +214,7 @@ def timelimit(timeout):
|
||||||
>>> timelimit(1)(meaningoflife)()
|
>>> timelimit(1)(meaningoflife)()
|
||||||
42
|
42
|
||||||
|
|
||||||
_Caveat:_ The function isn't stopped after `timeout` seconds but continues
|
_Caveat:_ The function isn't stopped after `timeout` seconds but continues
|
||||||
executing in a separate thread. (There seems to be no way to kill a thread.)
|
executing in a separate thread. (There seems to be no way to kill a thread.)
|
||||||
|
|
||||||
inspired by <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878>
|
inspired by <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878>
|
||||||
|
@ -249,7 +249,7 @@ def timelimit(timeout):
|
||||||
class Memoize:
|
class Memoize:
|
||||||
"""
|
"""
|
||||||
'Memoizes' a function, caching its return values for each input.
|
'Memoizes' a function, caching its return values for each input.
|
||||||
|
|
||||||
>>> import time
|
>>> import time
|
||||||
>>> def meaningoflife():
|
>>> def meaningoflife():
|
||||||
... time.sleep(.2)
|
... time.sleep(.2)
|
||||||
|
@ -265,14 +265,14 @@ class Memoize:
|
||||||
42
|
42
|
||||||
>>> timelimit(.1)(fastlife)()
|
>>> timelimit(.1)(fastlife)()
|
||||||
42
|
42
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
self.func = func
|
self.func = func
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
def __call__(self, *args, **keywords):
|
def __call__(self, *args, **keywords):
|
||||||
key = (args, tuple(keywords.items()))
|
key = (args, tuple(keywords.items()))
|
||||||
if key not in self.cache:
|
if key not in self.cache:
|
||||||
self.cache[key] = self.func(*args, **keywords)
|
self.cache[key] = self.func(*args, **keywords)
|
||||||
return self.cache[key]
|
return self.cache[key]
|
||||||
|
|
||||||
|
@ -284,16 +284,16 @@ A memoized version of re.compile.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class _re_subm_proxy:
|
class _re_subm_proxy:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.match = None
|
self.match = None
|
||||||
def __call__(self, match):
|
def __call__(self, match):
|
||||||
self.match = match
|
self.match = match
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def re_subm(pat, repl, string):
|
def re_subm(pat, repl, string):
|
||||||
"""
|
"""
|
||||||
Like re.sub, but returns the replacement _and_ the match object.
|
Like re.sub, but returns the replacement _and_ the match object.
|
||||||
|
|
||||||
>>> t, m = re_subm('g(oo+)fball', r'f\\1lish', 'goooooofball')
|
>>> t, m = re_subm('g(oo+)fball', r'f\\1lish', 'goooooofball')
|
||||||
>>> t
|
>>> t
|
||||||
'foooooolish'
|
'foooooolish'
|
||||||
|
@ -305,25 +305,25 @@ def re_subm(pat, repl, string):
|
||||||
compiled_pat.sub(proxy.__call__, string)
|
compiled_pat.sub(proxy.__call__, string)
|
||||||
return compiled_pat.sub(repl, string), proxy.match
|
return compiled_pat.sub(repl, string), proxy.match
|
||||||
|
|
||||||
def group(seq, size):
|
def group(seq, size):
|
||||||
"""
|
"""
|
||||||
Returns an iterator over a series of lists of length size from iterable.
|
Returns an iterator over a series of lists of length size from iterable.
|
||||||
|
|
||||||
>>> list(group([1,2,3,4], 2))
|
>>> list(group([1,2,3,4], 2))
|
||||||
[[1, 2], [3, 4]]
|
[[1, 2], [3, 4]]
|
||||||
"""
|
"""
|
||||||
if not hasattr(seq, 'next'):
|
if not hasattr(seq, 'next'):
|
||||||
seq = iter(seq)
|
seq = iter(seq)
|
||||||
while True:
|
while True:
|
||||||
yield [seq.next() for i in xrange(size)]
|
yield [seq.next() for i in xrange(size)]
|
||||||
|
|
||||||
class IterBetter:
|
class IterBetter:
|
||||||
"""
|
"""
|
||||||
Returns an object that can be used as an iterator
|
Returns an object that can be used as an iterator
|
||||||
but can also be used via __getitem__ (although it
|
but can also be used via __getitem__ (although it
|
||||||
cannot go backwards -- that is, you cannot request
|
cannot go backwards -- that is, you cannot request
|
||||||
`iterbetter[0]` after requesting `iterbetter[1]`).
|
`iterbetter[0]` after requesting `iterbetter[1]`).
|
||||||
|
|
||||||
>>> import itertools
|
>>> import itertools
|
||||||
>>> c = iterbetter(itertools.count())
|
>>> c = iterbetter(itertools.count())
|
||||||
>>> c[1]
|
>>> c[1]
|
||||||
|
@ -335,24 +335,24 @@ class IterBetter:
|
||||||
...
|
...
|
||||||
IndexError: already passed 3
|
IndexError: already passed 3
|
||||||
"""
|
"""
|
||||||
def __init__(self, iterator):
|
def __init__(self, iterator):
|
||||||
self.i, self.c = iterator, 0
|
self.i, self.c = iterator, 0
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
while 1:
|
while 1:
|
||||||
yield self.i.next()
|
yield self.i.next()
|
||||||
self.c += 1
|
self.c += 1
|
||||||
def __getitem__(self, i):
|
def __getitem__(self, i):
|
||||||
#todo: slices
|
#todo: slices
|
||||||
if i < self.c:
|
if i < self.c:
|
||||||
raise IndexError, "already passed "+str(i)
|
raise IndexError, "already passed "+str(i)
|
||||||
try:
|
try:
|
||||||
while i > self.c:
|
while i > self.c:
|
||||||
self.i.next()
|
self.i.next()
|
||||||
self.c += 1
|
self.c += 1
|
||||||
# now self.c == i
|
# now self.c == i
|
||||||
self.c += 1
|
self.c += 1
|
||||||
return self.i.next()
|
return self.i.next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise IndexError, str(i)
|
raise IndexError, str(i)
|
||||||
iterbetter = IterBetter
|
iterbetter = IterBetter
|
||||||
|
|
||||||
|
@ -365,23 +365,23 @@ def dictreverse(mapping):
|
||||||
|
|
||||||
def dictfind(dictionary, element):
|
def dictfind(dictionary, element):
|
||||||
"""
|
"""
|
||||||
Returns a key whose value in `dictionary` is `element`
|
Returns a key whose value in `dictionary` is `element`
|
||||||
or, if none exists, None.
|
or, if none exists, None.
|
||||||
|
|
||||||
>>> d = {1:2, 3:4}
|
>>> d = {1:2, 3:4}
|
||||||
>>> dictfind(d, 4)
|
>>> dictfind(d, 4)
|
||||||
3
|
3
|
||||||
>>> dictfind(d, 5)
|
>>> dictfind(d, 5)
|
||||||
"""
|
"""
|
||||||
for (key, value) in dictionary.iteritems():
|
for (key, value) in dictionary.iteritems():
|
||||||
if element is value:
|
if element is value:
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def dictfindall(dictionary, element):
|
def dictfindall(dictionary, element):
|
||||||
"""
|
"""
|
||||||
Returns the keys whose values in `dictionary` are `element`
|
Returns the keys whose values in `dictionary` are `element`
|
||||||
or, if none exists, [].
|
or, if none exists, [].
|
||||||
|
|
||||||
>>> d = {1:4, 3:4}
|
>>> d = {1:4, 3:4}
|
||||||
>>> dictfindall(d, 4)
|
>>> dictfindall(d, 4)
|
||||||
[1, 3]
|
[1, 3]
|
||||||
|
@ -396,9 +396,9 @@ def dictfindall(dictionary, element):
|
||||||
|
|
||||||
def dictincr(dictionary, element):
|
def dictincr(dictionary, element):
|
||||||
"""
|
"""
|
||||||
Increments `element` in `dictionary`,
|
Increments `element` in `dictionary`,
|
||||||
setting it to one if it doesn't exist.
|
setting it to one if it doesn't exist.
|
||||||
|
|
||||||
>>> d = {1:2, 3:4}
|
>>> d = {1:2, 3:4}
|
||||||
>>> dictincr(d, 1)
|
>>> dictincr(d, 1)
|
||||||
3
|
3
|
||||||
|
@ -417,7 +417,7 @@ def dictadd(*dicts):
|
||||||
"""
|
"""
|
||||||
Returns a dictionary consisting of the keys in the argument dictionaries.
|
Returns a dictionary consisting of the keys in the argument dictionaries.
|
||||||
If they share a key, the value from the last argument is used.
|
If they share a key, the value from the last argument is used.
|
||||||
|
|
||||||
>>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1})
|
>>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1})
|
||||||
{1: 0, 2: 1, 3: 1}
|
{1: 0, 2: 1, 3: 1}
|
||||||
"""
|
"""
|
||||||
|
@ -429,21 +429,21 @@ def dictadd(*dicts):
|
||||||
def listget(lst, ind, default=None):
|
def listget(lst, ind, default=None):
|
||||||
"""
|
"""
|
||||||
Returns `lst[ind]` if it exists, `default` otherwise.
|
Returns `lst[ind]` if it exists, `default` otherwise.
|
||||||
|
|
||||||
>>> listget(['a'], 0)
|
>>> listget(['a'], 0)
|
||||||
'a'
|
'a'
|
||||||
>>> listget(['a'], 1)
|
>>> listget(['a'], 1)
|
||||||
>>> listget(['a'], 1, 'b')
|
>>> listget(['a'], 1, 'b')
|
||||||
'b'
|
'b'
|
||||||
"""
|
"""
|
||||||
if len(lst)-1 < ind:
|
if len(lst)-1 < ind:
|
||||||
return default
|
return default
|
||||||
return lst[ind]
|
return lst[ind]
|
||||||
|
|
||||||
def intget(integer, default=None):
|
def intget(integer, default=None):
|
||||||
"""
|
"""
|
||||||
Returns `integer` as an int or `default` if it can't.
|
Returns `integer` as an int or `default` if it can't.
|
||||||
|
|
||||||
>>> intget('3')
|
>>> intget('3')
|
||||||
3
|
3
|
||||||
>>> intget('3a')
|
>>> intget('3a')
|
||||||
|
@ -458,7 +458,7 @@ def intget(integer, default=None):
|
||||||
def datestr(then, now=None):
|
def datestr(then, now=None):
|
||||||
"""
|
"""
|
||||||
Converts a (UTC) datetime object to a nice string representation.
|
Converts a (UTC) datetime object to a nice string representation.
|
||||||
|
|
||||||
>>> from datetime import datetime, timedelta
|
>>> from datetime import datetime, timedelta
|
||||||
>>> d = datetime(1970, 5, 1)
|
>>> d = datetime(1970, 5, 1)
|
||||||
>>> datestr(d, now=d)
|
>>> datestr(d, now=d)
|
||||||
|
@ -533,12 +533,12 @@ def datestr(then, now=None):
|
||||||
def numify(string):
|
def numify(string):
|
||||||
"""
|
"""
|
||||||
Removes all non-digit characters from `string`.
|
Removes all non-digit characters from `string`.
|
||||||
|
|
||||||
>>> numify('800-555-1212')
|
>>> numify('800-555-1212')
|
||||||
'8005551212'
|
'8005551212'
|
||||||
>>> numify('800.555.1212')
|
>>> numify('800.555.1212')
|
||||||
'8005551212'
|
'8005551212'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return ''.join([c for c in str(string) if c.isdigit()])
|
return ''.join([c for c in str(string) if c.isdigit()])
|
||||||
|
|
||||||
|
@ -546,10 +546,10 @@ def denumify(string, pattern):
|
||||||
"""
|
"""
|
||||||
Formats `string` according to `pattern`, where the letter X gets replaced
|
Formats `string` according to `pattern`, where the letter X gets replaced
|
||||||
by characters from `string`.
|
by characters from `string`.
|
||||||
|
|
||||||
>>> denumify("8005551212", "(XXX) XXX-XXXX")
|
>>> denumify("8005551212", "(XXX) XXX-XXXX")
|
||||||
'(800) 555-1212'
|
'(800) 555-1212'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
out = []
|
out = []
|
||||||
for c in pattern:
|
for c in pattern:
|
||||||
|
@ -569,15 +569,15 @@ def dateify(datestring):
|
||||||
class CaptureStdout:
|
class CaptureStdout:
|
||||||
"""
|
"""
|
||||||
Captures everything `func` prints to stdout and returns it instead.
|
Captures everything `func` prints to stdout and returns it instead.
|
||||||
|
|
||||||
>>> def idiot():
|
>>> def idiot():
|
||||||
... print "foo"
|
... print "foo"
|
||||||
>>> capturestdout(idiot)()
|
>>> capturestdout(idiot)()
|
||||||
'foo\\n'
|
'foo\\n'
|
||||||
|
|
||||||
**WARNING:** Not threadsafe!
|
**WARNING:** Not threadsafe!
|
||||||
"""
|
"""
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
self.func = func
|
self.func = func
|
||||||
def __call__(self, *args, **keywords):
|
def __call__(self, *args, **keywords):
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
@ -585,9 +585,9 @@ class CaptureStdout:
|
||||||
out = StringIO()
|
out = StringIO()
|
||||||
oldstdout = sys.stdout
|
oldstdout = sys.stdout
|
||||||
sys.stdout = out
|
sys.stdout = out
|
||||||
try:
|
try:
|
||||||
self.func(*args, **keywords)
|
self.func(*args, **keywords)
|
||||||
finally:
|
finally:
|
||||||
sys.stdout = oldstdout
|
sys.stdout = oldstdout
|
||||||
return out.getvalue()
|
return out.getvalue()
|
||||||
|
|
||||||
|
@ -597,14 +597,14 @@ class Profile:
|
||||||
"""
|
"""
|
||||||
Profiles `func` and returns a tuple containing its output
|
Profiles `func` and returns a tuple containing its output
|
||||||
and a string with human-readable profiling information.
|
and a string with human-readable profiling information.
|
||||||
|
|
||||||
>>> import time
|
>>> import time
|
||||||
>>> out, inf = profile(time.sleep)(.001)
|
>>> out, inf = profile(time.sleep)(.001)
|
||||||
>>> out
|
>>> out
|
||||||
>>> inf[:10].strip()
|
>>> inf[:10].strip()
|
||||||
'took 0.0'
|
'took 0.0'
|
||||||
"""
|
"""
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
self.func = func
|
self.func = func
|
||||||
def __call__(self, *args): ##, **kw): kw unused
|
def __call__(self, *args): ##, **kw): kw unused
|
||||||
import hotshot, hotshot.stats, tempfile ##, time already imported
|
import hotshot, hotshot.stats, tempfile ##, time already imported
|
||||||
|
@ -639,31 +639,31 @@ if not hasattr(traceback, 'format_exc'):
|
||||||
|
|
||||||
def tryall(context, prefix=None):
|
def tryall(context, prefix=None):
|
||||||
"""
|
"""
|
||||||
Tries a series of functions and prints their results.
|
Tries a series of functions and prints their results.
|
||||||
`context` is a dictionary mapping names to values;
|
`context` is a dictionary mapping names to values;
|
||||||
the value will only be tried if it's callable.
|
the value will only be tried if it's callable.
|
||||||
|
|
||||||
>>> tryall(dict(j=lambda: True))
|
>>> tryall(dict(j=lambda: True))
|
||||||
j: True
|
j: True
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
results:
|
results:
|
||||||
True: 1
|
True: 1
|
||||||
|
|
||||||
For example, you might have a file `test/stuff.py`
|
For example, you might have a file `test/stuff.py`
|
||||||
with a series of functions testing various things in it.
|
with a series of functions testing various things in it.
|
||||||
At the bottom, have a line:
|
At the bottom, have a line:
|
||||||
|
|
||||||
if __name__ == "__main__": tryall(globals())
|
if __name__ == "__main__": tryall(globals())
|
||||||
|
|
||||||
Then you can run `python test/stuff.py` and get the results of
|
Then you can run `python test/stuff.py` and get the results of
|
||||||
all the tests.
|
all the tests.
|
||||||
"""
|
"""
|
||||||
context = context.copy() # vars() would update
|
context = context.copy() # vars() would update
|
||||||
results = {}
|
results = {}
|
||||||
for (key, value) in context.iteritems():
|
for (key, value) in context.iteritems():
|
||||||
if not hasattr(value, '__call__'):
|
if not hasattr(value, '__call__'):
|
||||||
continue
|
continue
|
||||||
if prefix and not key.startswith(prefix):
|
if prefix and not key.startswith(prefix):
|
||||||
continue
|
continue
|
||||||
print key + ':',
|
print key + ':',
|
||||||
try:
|
try:
|
||||||
|
@ -674,7 +674,7 @@ def tryall(context, prefix=None):
|
||||||
print 'ERROR'
|
print 'ERROR'
|
||||||
dictincr(results, 'ERROR')
|
dictincr(results, 'ERROR')
|
||||||
print ' ' + '\n '.join(traceback.format_exc().split('\n'))
|
print ' ' + '\n '.join(traceback.format_exc().split('\n'))
|
||||||
|
|
||||||
print '-'*40
|
print '-'*40
|
||||||
print 'results:'
|
print 'results:'
|
||||||
for (key, value) in results.iteritems():
|
for (key, value) in results.iteritems():
|
||||||
|
@ -682,18 +682,18 @@ def tryall(context, prefix=None):
|
||||||
|
|
||||||
class ThreadedDict:
|
class ThreadedDict:
|
||||||
"""
|
"""
|
||||||
Takes a dictionary that maps threads to objects.
|
Takes a dictionary that maps threads to objects.
|
||||||
When a thread tries to get or set an attribute or item
|
When a thread tries to get or set an attribute or item
|
||||||
of the threadeddict, it passes it on to the object
|
of the threadeddict, it passes it on to the object
|
||||||
for that thread in dictionary.
|
for that thread in dictionary.
|
||||||
"""
|
"""
|
||||||
def __init__(self, dictionary):
|
def __init__(self, dictionary):
|
||||||
self.__dict__['_ThreadedDict__d'] = dictionary
|
self.__dict__['_ThreadedDict__d'] = dictionary
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
return getattr(self.__d[threading.currentThread()], attr)
|
return getattr(self.__d[threading.currentThread()], attr)
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return self.__d[threading.currentThread()][item]
|
return self.__d[threading.currentThread()][item]
|
||||||
|
|
||||||
def __setattr__(self, attr, value):
|
def __setattr__(self, attr, value):
|
||||||
|
@ -711,10 +711,10 @@ class ThreadedDict:
|
||||||
def __delitem__(self, item):
|
def __delitem__(self, item):
|
||||||
del self.__d[threading.currentThread()][item]
|
del self.__d[threading.currentThread()][item]
|
||||||
|
|
||||||
def __setitem__(self, item, value):
|
def __setitem__(self, item, value):
|
||||||
self.__d[threading.currentThread()][item] = value
|
self.__d[threading.currentThread()][item] = value
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.__d[threading.currentThread()])
|
return hash(self.__d[threading.currentThread()])
|
||||||
|
|
||||||
threadeddict = ThreadedDict
|
threadeddict = ThreadedDict
|
||||||
|
@ -722,25 +722,25 @@ threadeddict = ThreadedDict
|
||||||
def autoassign(self, locals):
|
def autoassign(self, locals):
|
||||||
"""
|
"""
|
||||||
Automatically assigns local variables to `self`.
|
Automatically assigns local variables to `self`.
|
||||||
|
|
||||||
>>> self = storage()
|
>>> self = storage()
|
||||||
>>> autoassign(self, dict(a=1, b=2))
|
>>> autoassign(self, dict(a=1, b=2))
|
||||||
>>> self
|
>>> self
|
||||||
<Storage {'a': 1, 'b': 2}>
|
<Storage {'a': 1, 'b': 2}>
|
||||||
|
|
||||||
Generally used in `__init__` methods, as in:
|
Generally used in `__init__` methods, as in:
|
||||||
|
|
||||||
def __init__(self, foo, bar, baz=1): autoassign(self, locals())
|
def __init__(self, foo, bar, baz=1): autoassign(self, locals())
|
||||||
"""
|
"""
|
||||||
for (key, value) in locals.iteritems():
|
for (key, value) in locals.iteritems():
|
||||||
if key == 'self':
|
if key == 'self':
|
||||||
continue
|
continue
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def to36(q):
|
def to36(q):
|
||||||
"""
|
"""
|
||||||
Converts an integer to base 36 (a useful scheme for human-sayable IDs).
|
Converts an integer to base 36 (a useful scheme for human-sayable IDs).
|
||||||
|
|
||||||
>>> to36(35)
|
>>> to36(35)
|
||||||
'z'
|
'z'
|
||||||
>>> to36(119292)
|
>>> to36(119292)
|
||||||
|
@ -751,9 +751,9 @@ def to36(q):
|
||||||
'0'
|
'0'
|
||||||
>>> to36(-393)
|
>>> to36(-393)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValueError: must supply a positive integer
|
ValueError: must supply a positive integer
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if q < 0: raise ValueError, "must supply a positive integer"
|
if q < 0: raise ValueError, "must supply a positive integer"
|
||||||
letters = "0123456789abcdefghijklmnopqrstuvwxyz"
|
letters = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
|
@ -991,7 +991,7 @@ class MultiCall:
|
||||||
|
|
||||||
def get_call_list(self):
|
def get_call_list(self):
|
||||||
return self.__call_list
|
return self.__call_list
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# convenience functions
|
# convenience functions
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue