diff --git a/deluge/common.py b/deluge/common.py index 873d3f223..446c77b2a 100644 --- a/deluge/common.py +++ b/deluge/common.py @@ -1,38 +1,11 @@ +# -*- coding: utf-8 -*- # -# common.py +# Copyright (C) 2007,2008 Andrew Resch # -# Copyright (C) 2007, 2008 Andrew Resch +# 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.""" @@ -48,6 +21,14 @@ import gettext import locale import base64 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 @@ -223,20 +204,51 @@ def resource_filename(module, path): ) -def open_file(path): - """ - Opens a file or folder using the system configured program +def open_file(path, timestamp=None): + """Opens a file or folder using the system configured program. - :param path: the path to the file or folder to open - :type path: string + Args: + path (str): The path to the file or folder to open. + timestamp (int, optional): An event request timestamp. """ if windows_check(): - os.startfile(path.decode("utf8")) + os.startfile(path) elif osx_check(): - subprocess.Popen(["open", "%s" % path]) + subprocess.Popen(["open", path]) 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): diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py index b5a4e18e1..41bfd3c3f 100644 --- a/deluge/ui/gtkui/files_tab.py +++ b/deluge/ui/gtkui/files_tab.py @@ -197,6 +197,7 @@ class FilesTab(Tab): self.localhost_widgets = [ builder.get_object("menuitem_open_file"), + builder.get_object("menuitem_show_file"), builder.get_object("menuitem3") ] @@ -215,6 +216,7 @@ class FilesTab(Tab): component.get("MainWindow").connect_signals({ "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_normal_activate": self._on_menuitem_normal_activate, "on_menuitem_high_activate": self._on_menuitem_high_activate, @@ -319,8 +321,7 @@ class FilesTab(Tab): self.torrent_id = None def _on_row_activated(self, tree, path, view_column): - if client.is_localhost: - component.get("SessionProxy").get_torrent_status(self.torrent_id, ["save_path", "files"]).addCallback(self._on_open_file) + self._on_menuitem_open_file_activate() def get_file_path(self, row, path=""): if not row: @@ -339,7 +340,21 @@ class FilesTab(Tab): path = self.get_file_path(select).split("/") filepath = os.path.join(status["save_path"], *path) 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 def prepare_file_store(self, files): @@ -525,7 +540,14 @@ class FilesTab(Tab): return True 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): """Sets the file priorities in the core. It will change the selected diff --git a/deluge/ui/gtkui/glade/main_window.tabs.menu_file.ui b/deluge/ui/gtkui/glade/main_window.tabs.menu_file.ui index 3021f9e69..77edce787 100644 --- a/deluge/ui/gtkui/glade/main_window.tabs.menu_file.ui +++ b/deluge/ui/gtkui/glade/main_window.tabs.menu_file.ui @@ -46,6 +46,17 @@ + + + _Show + True + False + False + True + True + + + True diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index e9fc154d4..ccc29db00 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -326,10 +326,12 @@ class MenuBar(component.Component): log.debug("on_menuitem_open_folder") 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(): 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): log.debug("on_menuitem_move_activate")