mirror of
https://git.deluge-torrent.org/deluge
synced 2025-08-02 22:48:40 +00:00
[#2098] Add function to highlight the torrent folder/file
* Will show/highlight a file path in system file manager. *nix uses dbus with an xdg_open fallback. * [GTKUI] Open Folder still opens the download location but now shows the torrent data file/folder * [GTKUI] Files_tab now has a second menu item 'Show' to show a file's location * The open_file and show_file functions now use timestamps on *nix so that windows open in front, this fixes recent desktop changes that prevent windows randomly stealing focus. * Removed utf8 decode for Windows. All paths should be unicode string, any resulting errors should be traced to source and corrected.
This commit is contained in:
parent
670cd21685
commit
c5f7eeaacb
4 changed files with 93 additions and 46 deletions
|
@ -1,38 +1,11 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# common.py
|
# Copyright (C) 2007,2008 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
|
||||||
#
|
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# You must obey the GNU General Public License in all respects for all of
|
|
||||||
# the code used other than OpenSSL. If you modify file(s) with this
|
|
||||||
# exception, you may extend this exception to your version of the file(s),
|
|
||||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
|
||||||
# this exception statement from your version. If you delete this exception
|
|
||||||
# statement from all source files in the program, then also delete it here.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
"""Common functions for various parts of Deluge to use."""
|
"""Common functions for various parts of Deluge to use."""
|
||||||
|
|
||||||
|
@ -48,6 +21,14 @@ import gettext
|
||||||
import locale
|
import locale
|
||||||
import base64
|
import base64
|
||||||
import urllib
|
import urllib
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
try:
|
||||||
|
import dbus
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
dbus_fileman = bus.get_object("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1")
|
||||||
|
except:
|
||||||
|
dbus_fileman = None
|
||||||
|
|
||||||
from deluge.error import InvalidPathError
|
from deluge.error import InvalidPathError
|
||||||
|
|
||||||
|
@ -223,20 +204,51 @@ def resource_filename(module, path):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def open_file(path):
|
def open_file(path, timestamp=None):
|
||||||
"""
|
"""Opens a file or folder using the system configured program.
|
||||||
Opens a file or folder using the system configured program
|
|
||||||
|
|
||||||
:param path: the path to the file or folder to open
|
Args:
|
||||||
:type path: string
|
path (str): The path to the file or folder to open.
|
||||||
|
timestamp (int, optional): An event request timestamp.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if windows_check():
|
if windows_check():
|
||||||
os.startfile(path.decode("utf8"))
|
os.startfile(path)
|
||||||
elif osx_check():
|
elif osx_check():
|
||||||
subprocess.Popen(["open", "%s" % path])
|
subprocess.Popen(["open", path])
|
||||||
else:
|
else:
|
||||||
subprocess.Popen(["xdg-open", "%s" % path])
|
if timestamp is None:
|
||||||
|
timestamp = int(time.time())
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["DESKTOP_STARTUP_ID"] = "%s-%u-%s-xdg_open_TIME%d" % \
|
||||||
|
(os.path.basename(sys.argv[0]), os.getpid(), os.uname()[1], timestamp)
|
||||||
|
subprocess.Popen(["xdg-open", "%s" % path], env=env)
|
||||||
|
|
||||||
|
|
||||||
|
def show_file(path, timestamp=None):
|
||||||
|
"""Shows (highlights) a file or folder using the system configured file manager.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (str): The path to the file or folder to show.
|
||||||
|
timestamp (int, optional): An event request timestamp.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if windows_check():
|
||||||
|
subprocess.Popen(["explorer", "/select,", path])
|
||||||
|
elif osx_check():
|
||||||
|
subprocess.Popen(["open", "-R", path])
|
||||||
|
else:
|
||||||
|
if timestamp is None:
|
||||||
|
timestamp = int(time.time())
|
||||||
|
startup_id = "%s_%u_%s-dbus_TIME%d" % (os.path.basename(sys.argv[0]), os.getpid(), os.uname()[1], timestamp)
|
||||||
|
if dbus_fileman:
|
||||||
|
paths = [urlparse.urljoin("file:", urllib.pathname2url(utf8_encoded(path)))]
|
||||||
|
dbus_fileman.ShowItems(paths, startup_id, dbus_interface="org.freedesktop.FileManager1")
|
||||||
|
else:
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["DESKTOP_STARTUP_ID"] = startup_id.replace("dbus", "xdg-open")
|
||||||
|
# No option in xdg to highlight a file so just open parent folder.
|
||||||
|
subprocess.Popen(["xdg-open", os.path.dirname(path.rstrip("/"))], env=env)
|
||||||
|
|
||||||
|
|
||||||
def open_url_in_browser(url):
|
def open_url_in_browser(url):
|
||||||
|
|
|
@ -197,6 +197,7 @@ class FilesTab(Tab):
|
||||||
|
|
||||||
self.localhost_widgets = [
|
self.localhost_widgets = [
|
||||||
builder.get_object("menuitem_open_file"),
|
builder.get_object("menuitem_open_file"),
|
||||||
|
builder.get_object("menuitem_show_file"),
|
||||||
builder.get_object("menuitem3")
|
builder.get_object("menuitem3")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -215,6 +216,7 @@ class FilesTab(Tab):
|
||||||
|
|
||||||
component.get("MainWindow").connect_signals({
|
component.get("MainWindow").connect_signals({
|
||||||
"on_menuitem_open_file_activate": self._on_menuitem_open_file_activate,
|
"on_menuitem_open_file_activate": self._on_menuitem_open_file_activate,
|
||||||
|
"on_menuitem_show_file_activate": self._on_menuitem_show_file_activate,
|
||||||
"on_menuitem_donotdownload_activate": self._on_menuitem_donotdownload_activate,
|
"on_menuitem_donotdownload_activate": self._on_menuitem_donotdownload_activate,
|
||||||
"on_menuitem_normal_activate": self._on_menuitem_normal_activate,
|
"on_menuitem_normal_activate": self._on_menuitem_normal_activate,
|
||||||
"on_menuitem_high_activate": self._on_menuitem_high_activate,
|
"on_menuitem_high_activate": self._on_menuitem_high_activate,
|
||||||
|
@ -319,8 +321,7 @@ class FilesTab(Tab):
|
||||||
self.torrent_id = None
|
self.torrent_id = None
|
||||||
|
|
||||||
def _on_row_activated(self, tree, path, view_column):
|
def _on_row_activated(self, tree, path, view_column):
|
||||||
if client.is_localhost:
|
self._on_menuitem_open_file_activate()
|
||||||
component.get("SessionProxy").get_torrent_status(self.torrent_id, ["save_path", "files"]).addCallback(self._on_open_file)
|
|
||||||
|
|
||||||
def get_file_path(self, row, path=""):
|
def get_file_path(self, row, path=""):
|
||||||
if not row:
|
if not row:
|
||||||
|
@ -339,7 +340,21 @@ class FilesTab(Tab):
|
||||||
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)
|
timestamp = gtk.get_current_event_time()
|
||||||
|
deluge.common.open_file(filepath, timestamp=timestamp)
|
||||||
|
|
||||||
|
def _on_show_file(self, status):
|
||||||
|
paths = self.listview.get_selection().get_selected_rows()[1]
|
||||||
|
selected = []
|
||||||
|
for path in paths:
|
||||||
|
selected.append(self.treestore.get_iter(path))
|
||||||
|
|
||||||
|
for select in selected:
|
||||||
|
path = self.get_file_path(select).split("/")
|
||||||
|
filepath = os.path.join(status["save_path"], *path)
|
||||||
|
log.debug("Show file '%s'", filepath)
|
||||||
|
timestamp = gtk.get_current_event_time()
|
||||||
|
deluge.common.show_file(filepath, timestamp=timestamp)
|
||||||
|
|
||||||
## 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):
|
||||||
|
@ -525,7 +540,14 @@ class FilesTab(Tab):
|
||||||
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)
|
if client.is_localhost:
|
||||||
|
component.get("SessionProxy").get_torrent_status(
|
||||||
|
self.torrent_id, ["save_path", "files"]).addCallback(self._on_open_file)
|
||||||
|
|
||||||
|
def _on_menuitem_show_file_activate(self, menuitem):
|
||||||
|
if client.is_localhost:
|
||||||
|
component.get("SessionProxy").get_torrent_status(
|
||||||
|
self.torrent_id, ["save_path", "files"]).addCallback(self._on_show_file)
|
||||||
|
|
||||||
def _set_file_priorities_on_user_change(self, selected, priority):
|
def _set_file_priorities_on_user_change(self, selected, priority):
|
||||||
"""Sets the file priorities in the core. It will change the selected
|
"""Sets the file priorities in the core. It will change the selected
|
||||||
|
|
|
@ -46,6 +46,17 @@
|
||||||
<signal name="activate" handler="on_menuitem_open_file_activate" swapped="no"/>
|
<signal name="activate" handler="on_menuitem_open_file_activate" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImageMenuItem" id="menuitem_show_file">
|
||||||
|
<property name="label" translatable="yes">_Show</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="use_action_appearance">False</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="activate" handler="on_menuitem_show_file_activate" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorMenuItem" id="menuitem3">
|
<object class="GtkSeparatorMenuItem" id="menuitem3">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
@ -326,10 +326,12 @@ class MenuBar(component.Component):
|
||||||
log.debug("on_menuitem_open_folder")
|
log.debug("on_menuitem_open_folder")
|
||||||
|
|
||||||
def _on_torrent_status(status):
|
def _on_torrent_status(status):
|
||||||
deluge.common.open_file(status["save_path"])
|
timestamp = gtk.get_current_event_time()
|
||||||
|
path = os.path.join(status["save_path"], status["files"][0]["path"].split('/')[0])
|
||||||
|
deluge.common.show_file(path, timestamp=timestamp)
|
||||||
for torrent_id in component.get("TorrentView").get_selected_torrents():
|
for torrent_id in component.get("TorrentView").get_selected_torrents():
|
||||||
component.get("SessionProxy").get_torrent_status(
|
component.get("SessionProxy").get_torrent_status(
|
||||||
torrent_id, ["save_path"]).addCallback(_on_torrent_status)
|
torrent_id, ["save_path", "files"]).addCallback(_on_torrent_status)
|
||||||
|
|
||||||
def on_menuitem_move_activate(self, data=None):
|
def on_menuitem_move_activate(self, data=None):
|
||||||
log.debug("on_menuitem_move_activate")
|
log.debug("on_menuitem_move_activate")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue