From ab465d06ef04d8a015d387d4887d9c00dfaae2d7 Mon Sep 17 00:00:00 2001 From: kozec Date: Tue, 27 Jan 2015 21:34:43 +0100 Subject: [PATCH] Added UI for enabling/disabling filemanager integration --- setup.py | 1 + syncthing_gtk/editordialog.py | 2 +- syncthing_gtk/uisettingsdialog.py | 125 +++++++++++++++++++++- ui-settings.glade | 170 +++++++++++++++++++++++------- 4 files changed, 257 insertions(+), 41 deletions(-) diff --git a/setup.py b/setup.py index 3f690637..d3076c08 100755 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ def get_version(): packages = ['syncthing_gtk'], data_files = [ ('share/syncthing-gtk', glob.glob("*.glade") ), + ('share/syncthing-gtk', glob.glob("scripts/syncthing-plugin-*.py") ), ('share/syncthing-gtk/icons', glob.glob("icons/*") ), ('share/pixmaps', glob.glob("icons/emblem-*.png") ), ('share/applications', ['syncthing-gtk.desktop'] ), diff --git a/syncthing_gtk/editordialog.py b/syncthing_gtk/editordialog.py index 657ee126..4cff99f8 100644 --- a/syncthing_gtk/editordialog.py +++ b/syncthing_gtk/editordialog.py @@ -88,7 +88,7 @@ def show(self, parent=None): def present(self, values=[]): self["editor"].present() for v in values: - if self[v].get_sensitive(): + if not self[v] is None and self[v].get_sensitive(): self[v].grab_focus() return diff --git a/syncthing_gtk/uisettingsdialog.py b/syncthing_gtk/uisettingsdialog.py index 6a4787fa..9e28af70 100644 --- a/syncthing_gtk/uisettingsdialog.py +++ b/syncthing_gtk/uisettingsdialog.py @@ -10,9 +10,10 @@ from syncthing_gtk import Notifications, HAS_DESKTOP_NOTIFY from syncthing_gtk.tools import * from syncthing_gtk.configuration import LONG_AGO -import os +import os, logging _ = lambda (a) : a +log = logging.getLogger("UISettingsDialog") VALUES = [ "vautostart_daemon", "vautokill_daemon", "vminimize_on_start", "vautostart", "vuse_old_header", "vicons_in_menu", @@ -20,6 +21,31 @@ "vnotification_for_error", "vst_autoupdate", "vsyncthing_binary", ] +# Values for filemanager integration. Key is ID of checkbox widget +FM_DATA = { + "fmcb_nemo" : ( + "nemo/extensions-3.0/libnemo-python.so", # python plugin location, relative to /usr/lib + "Nemo python bindings", # name or description of required package + "syncthing-plugin-nemo", # plugin script filename, without extension + "nemo-python/extensions", # script folder, relative to XDG_DATA_HOME + "Nemo" # name + ), + "fmcb_nautilus" : ( + "nemo/extensions-3.0/libnemo-python.so", + "Nautilus python bindings", + "syncthing-plugin-nautilus", + "nautilus-python/extensions", + "Nautilus" + ), + "fmcb_caja" : ( + "caja/extensions-2.0/libcaja-python.so", + "Caja python bindings", + "syncthing-plugin-caja", + "caja-python/extensions", + "Caja" + ) +} + class UISettingsDialog(EditorDialog): def __init__(self, app): EditorDialog.__init__(self, app, "ui-settings.glade", @@ -55,6 +81,23 @@ def load_data(self): self["rbOnExitLeave"].set_sensitive(False) self["rbOnExitAsk"].set_sensitive(False) self["rbOnExitTerminate"].set_active(True) + # Check for filemanager python bindings current state of plugins + status = [] + for widget_id in FM_DATA: + so_file, package, plugin, location, name = FM_DATA[widget_id] + if not get_fm_source_path(plugin) is None: + if library_exists(so_file): + self[widget_id].set_sensitive(True) + self[widget_id].set_active( + os.path.exists(get_fm_target_path(plugin, location)) + ) + else: + log.warning("Cannot find %s required to support %s", so_file, name) + status.append(_("Install %s package to enable %s support") % (package, name)) + else: + log.warning("Cannot find %s.py required to support %s", plugin, name) + self["fmLblIntegrationStatus"].set_text("\n".join(status)) + self.cb_data_loaded(copy) self.cb_check_value() @@ -129,6 +172,34 @@ def on_save_reuqested(self): # Save data to configuration file for k in self.values: self.app.config[k] = self.values[k] + # Create / delete fm integration scripts + for widget_id in FM_DATA: + so_file, package, plugin, location, name = FM_DATA[widget_id] + if self[widget_id].get_sensitive() and self[widget_id].get_active(): + # Should be enabled. Check if script is in place and create it if not + source = get_fm_source_path(plugin) + target = get_fm_target_path(plugin, location) + if not source is None and not os.path.exists(target): + try: + if is_file_or_symlink(target): + os.unlink(target) + os.symlink(source, target) + log.info("Created symlink '%s' -> '%s'", source, target) + except Exception, e: + log.error("Failed to symlink '%s' -> '%s'", source, target) + log.error(e) + else: + # Should be disabled. Remove redundant scripts + for extension in ("py", "pyc", "pyo"): + target = get_fm_target_path(plugin, location, extension) + if is_file_or_symlink(target): + try: + os.unlink(target) + log.info("Removed '%s'", target) + except Exception, e: + log.error("Failed to remove '%s'", target) + log.error(e) + # Report work done self.syncthing_cb_post_config() @@ -145,6 +216,58 @@ def on_saved(self): # Restart or cancel updatecheck self.app.check_for_upgrade() +def library_exists(name): + """ + Checks if there is specified so file installed in one of known prefixes + """ + PREFIXES = [ "/usr/lib", "/usr/local/lib/" ] # Anything else? + for prefix in PREFIXES: + if os.path.exists(os.path.join(prefix, name)): + return True + return False + +def get_fm_target_path(plugin, location, extension="py"): + """ + Returns full path to plugin file in filemanager plugins directory + """ + datahome = os.path.expanduser("~/.local/share") + if "XDG_DATA_HOME" in os.environ: + datahome = os.environ["XDG_DATA_HOME"] + return os.path.join(datahome, location, "%s.%s" % (plugin, extension)) + +def get_fm_source_path(plugin): + """ + Returns path to location where plugin file is installed + """ + filename = "%s.py" % (plugin,) + paths = ( + # Relative path used while developing or when running + # ST-GTK without installation + "./scripts/", + # Default installation path + "/usr/share/syncthing-gtk", + # Not-so default installation path + "/usr/local/share/syncthing-gtk", + ) + for path in paths: + fn = os.path.abspath(os.path.join(path, filename)) + if os.path.exists(fn): + return fn + return None + +def is_file_or_symlink(path): + """ + Returns True if specified file exists, even as broken symlink. + (os.path.exists() returns False for broken symlinks) + """ + if os.path.exists(path): return True + try: + os.readlink(path) + return True + except: + pass + return False + def browse_for_binary(parent_window, settings_dialog, value): """ Display file browser dialog to browse for syncthing binary. diff --git a/ui-settings.glade b/ui-settings.glade index 28697ee7..5060853d 100644 --- a/ui-settings.glade +++ b/ui-settings.glade @@ -69,18 +69,17 @@ - + True True - + 400 True False 5 5 5 - 5 True @@ -93,7 +92,6 @@ 0 8 3 - 1 @@ -111,7 +109,6 @@ 1 9 2 - 1 @@ -128,8 +125,6 @@ 1 10 - 1 - 1 @@ -146,8 +141,6 @@ 1 11 - 1 - 1 @@ -162,7 +155,6 @@ 0 12 3 - 1 @@ -179,8 +171,6 @@ 1 13 - 1 - 1 @@ -197,8 +187,6 @@ 1 14 - 1 - 1 @@ -215,8 +203,6 @@ 1 15 - 1 - 1 @@ -234,7 +220,6 @@ 0 2 3 - 1 @@ -249,7 +234,6 @@ 0 4 3 - 1 @@ -265,8 +249,6 @@ 1 5 - 1 - 1 @@ -282,8 +264,6 @@ 1 7 - 1 - 1 @@ -299,8 +279,6 @@ 1 6 - 1 - 1 @@ -317,7 +295,6 @@ 0 0 3 - 1 @@ -334,7 +311,6 @@ 0 1 3 - 1 @@ -352,7 +328,6 @@ 0 3 3 - 1 @@ -415,7 +390,7 @@ True False - _Basic + _Interface True @@ -423,7 +398,132 @@ - + + True + False + 5 + 3 + 5 + + + True + False + 0 + Integrate Syncthing with filemanagers + True + end + + + + + + 0 + 0 + + + + + Nemo (Cinamon Desktop) + True + False + True + False + 10 + 5 + 0 + True + + + 0 + 1 + + + + + Nautilus (Ubuntu/GNOME) + True + False + True + False + 10 + 0 + True + + + 0 + 2 + + + + + Caja (MATE Desktop) + True + False + True + False + 10 + 0 + True + + + 0 + 3 + + + + + True + False + False + 7 + 0 + Install blahblahblah to enable Nemo support +Install blahblahblah to enable Nautilus support +Install blahblahblah to enable Caja support + + + + + + 0 + 5 + + + + + True + False + 8 + 0 + 3 + <b>Note:</b> You may need to restart your filemanager after enabling or disabling integration. + True + True + + + 0 + 4 + + + + + 1 + + + + + True + False + _Integration + True + + + 1 + False + + + + True False 5 @@ -444,7 +544,6 @@ 0 0 2 - 1 @@ -457,8 +556,6 @@ 0 1 - 1 - 1 @@ -475,8 +572,6 @@ 1 1 - 1 - 1 @@ -493,7 +588,6 @@ 0 2 2 - 1 @@ -520,7 +614,6 @@ 0 3 2 - 1 @@ -537,12 +630,11 @@ 0 4 2 - 1 - 1 + 2 True @@ -554,7 +646,7 @@ True - 1 + 2 True False