Hello community, here is the log from the commit of package syncthing-gtk for openSUSE:Factory checked in at 2015-11-08 11:26:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/syncthing-gtk (Old) and /work/SRC/openSUSE:Factory/.syncthing-gtk.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "syncthing-gtk" Changes: -------- --- /work/SRC/openSUSE:Factory/syncthing-gtk/syncthing-gtk.changes 2015-10-14 16:45:10.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.syncthing-gtk.new/syncthing-gtk.changes 2015-11-08 11:26:52.000000000 +0100 @@ -1,0 +2,7 @@ +Thu Nov 5 20:43:01 UTC 2015 - sor.ale...@meowr.ru + +- Update to 0.8: + * Syncthing 0.12 and above API. + * No 'ignore' button on Unknown device message. + +------------------------------------------------------------------- Old: ---- syncthing-gtk-0.7.6.1.tar.gz New: ---- syncthing-gtk-0.8.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ syncthing-gtk.spec ++++++ --- /var/tmp/diff_new_pack.d4lySk/_old 2015-11-08 11:26:53.000000000 +0100 +++ /var/tmp/diff_new_pack.d4lySk/_new 2015-11-08 11:26:53.000000000 +0100 @@ -16,10 +16,10 @@ # -%global __requires_exclude typelib\\(Caja\\)|typelib\\(Nautilus\\)|typelib\\(Nemo\\) +%global __requires_exclude typelib\\((Caja|Nautilus|Nemo)\\) %define _name syncthing_gtk Name: syncthing-gtk -Version: 0.7.6.1 +Version: 0.8 Release: 0 Summary: Syncthing Gtk-based graphical interface License: GPL-2.0+ @@ -36,7 +36,7 @@ Requires: python-dateutil Requires: python-gobject Requires: python-gobject-cairo -Requires: syncthing >= 0.11 +Requires: syncthing >= 0.12 Recommends: libnotify Recommends: librsvg Recommends: python-pyinotify ++++++ syncthing-gtk-0.7.6.1.tar.gz -> syncthing-gtk-0.8.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/README.md new/syncthing-gtk-0.8/README.md --- old/syncthing-gtk-0.7.6.1/README.md 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/README.md 2015-11-05 18:16:32.000000000 +0100 @@ -22,12 +22,11 @@ Dependencies: - python 2.7, GTK 3.8 or newer and [PyGObject](https://live.gnome.org/PyGObject) -- [python-gi-cairo](https://packages.debian.org/sid/python-gi-cairo) on debian based distros (included in PyGObject elsewhere) -- [gir1.2-rsvg-2.0](https://packages.debian.org/sid/gir1.2-rsvg-2.0) on debian based distros (included in PyGObject elsewhere) +- [python-gi-cairo](https://packages.debian.org/sid/python-gi-cairo) and [gir1.2-rsvg-2.0](https://packages.debian.org/sid/gir1.2-rsvg-2.0) on debian based distros (included in PyGObject elsewhere) - [python-dateutil](http://labix.org/python-dateutil) (Python 2 version) - [setuptools](https://pypi.python.org/pypi/setuptools) - [psmisc](http://psmisc.sourceforge.net) (for the `killall` command) -- [Syncthing][syncthing] v0.11 or newer +- [Syncthing][syncthing] v0.12 or newer Optional Dependencies: - [pyinotify](https://github.com/seb-m/pyinotify/wiki) for instant synchronization. @@ -36,8 +35,9 @@ Packages: - Ubuntu (deb-based distros): in [Web Upd8 PPA](https://launchpad.net/~nilarimogard/+archive/ubuntu/webupd8/) (thanks!) or [DEBs](http://ppa.launchpad.net/nilarimogard/webupd8/ubuntu/pool/main/s/syncthing-gtk/) -- SUSE, Fedora (rpm-based distros): in [OpenSUSE Build Service](http://software.opensuse.org/download.html?project=home%3Akozec&package=syncthing-gtk). You can install [Syncthing Package](http://software.opensuse.org/package/syncthing) first. - Arch Linux: In [[community] repository](https://www.archlinux.org/packages/community/any/syncthing-gtk/) +- Fedora: [in decathorpe's copr repository](https://copr.fedoraproject.org/coprs/decathorpe/syncthing/) +- SUSE (and other rpm-based distros): in [OpenSUSE Build Service](http://software.opensuse.org/download.html?project=home%3Akozec&package=syncthing-gtk). - Windows: Get [latest installer from here](https://github.com/kozec/syncthing-gui/releases/latest), or use [the Chocolatey package](https://chocolatey.org/packages/syncthing-gtk). - Or, in worst case scenario, download [latest tarball](https://github.com/kozec/syncthing-gui/releases/latest), extract it and run syncthing-gtk.py. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/app.glade new/syncthing-gtk-0.8/app.glade --- old/syncthing-gtk-0.7.6.1/app.glade 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/app.glade 2015-11-05 18:16:32.000000000 +0100 @@ -594,6 +594,29 @@ </object> </child> <child> + <object class="GtkImageMenuItem" id="menu-popup-pause-device"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">gtk-media-pause</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <property name="always_show_image">True</property> + <signal name="activate" handler="cb_menu_popup_pause_device" swapped="no"/> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="menu-popup-resume-device"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Resume</property> + <property name="image">menu-popup-resume-image</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <property name="always_show_image">True</property> + <signal name="activate" handler="cb_menu_popup_resume_device" swapped="no"/> + </object> + </child> + <child> <object class="GtkImageMenuItem" id="menu-popup-delete-device"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -988,6 +1011,13 @@ <property name="can_focus">False</property> <property name="icon-name">folder-open</property> </object> + +<object class="GtkImage" id="menu-popup-resume-image"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon-name">gtk-media-play</property> +</object> + <!-- /Popup menu icon images --> </interface> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/syncthing_gtk/app.py new/syncthing-gtk-0.8/syncthing_gtk/app.py --- old/syncthing-gtk-0.7.6.1/syncthing_gtk/app.py 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/syncthing_gtk/app.py 2015-11-05 18:16:32.000000000 +0100 @@ -15,9 +15,9 @@ log = logging.getLogger("App") # Internal version used by updater (if enabled) -INTERNAL_VERSION = "v0.7" +INTERNAL_VERSION = "v0.8" # Minimal Syncthing version supported by App -MIN_ST_VERSION = "0.11.0" +MIN_ST_VERSION = "0.12.0" COLOR_DEVICE = "#707070" # Dark-gray COLOR_DEVICE_SYNCING = "#2A89C8" # Blue @@ -37,6 +37,7 @@ RESPONSE_RESTART = 256 RESPONSE_FIX_FOLDER_ID = 257 RESPONSE_FIX_NEW_DEVICE = 258 +RESPONSE_FIX_IGNORE = 259 RESPONSE_QUIT = 260 RESPONSE_START_DAEMON = 271 RESPONSE_SLAIN_DAEMON = 272 @@ -346,6 +347,8 @@ self.daemon.connect("last-seen-changed", self.cb_syncthing_last_seen_changed) self.daemon.connect("device-connected", self.cb_syncthing_device_state_changed, True) self.daemon.connect("device-disconnected", self.cb_syncthing_device_state_changed, False) + self.daemon.connect("device-paused", self.cb_syncthing_device_paused_resumed, True) + self.daemon.connect("device-resumed", self.cb_syncthing_device_paused_resumed, False) self.daemon.connect("device-sync-started", self.cb_syncthing_device_sync_progress) self.daemon.connect("device-sync-progress", self.cb_syncthing_device_sync_progress) self.daemon.connect("device-sync-finished", self.cb_syncthing_device_sync_progress, 1.0) @@ -356,6 +359,7 @@ self.daemon.connect("folder-sync-progress", self.cb_syncthing_folder_state_changed, COLOR_FOLDER_SYNCING, _("Syncing")) self.daemon.connect("folder-sync-finished", self.cb_syncthing_folder_up_to_date) self.daemon.connect("folder-scan-started", self.cb_syncthing_folder_state_changed, 1.0, COLOR_FOLDER_SCANNING, _("Scanning")) + self.daemon.connect("folder-scan-progress", self.cb_syncthing_folder_state_changed, COLOR_FOLDER_SCANNING, _("Scanning")) self.daemon.connect("folder-scan-finished", self.cb_syncthing_folder_up_to_date) self.daemon.connect("folder-stopped", self.cb_syncthing_folder_stopped) self.daemon.connect("system-data-updated", self.cb_syncthing_system_data) @@ -640,7 +644,7 @@ else: self.display_run_daemon_dialog() self.set_status(False) - elif reason == Daemon.OLD_VERSION and self.config["st_autoupdate"] and not self.process and not StDownloader is None: + elif reason == Daemon.OLD_VERSION and self.config["st_autoupdate"] and not self.process is None and not StDownloader is None: # Daemon is too old, but autoupdater is enabled and I have control of deamon. # Try to update. from configuration import LONG_AGO @@ -790,6 +794,7 @@ r = RIBar("", Gtk.MessageType.WARNING,) r.get_label().set_markup(markup) r.add_button(RIBar.build_button(_("_Add")), RESPONSE_FIX_NEW_DEVICE) + r.add_button(RIBar.build_button(_("_Ignore")), RESPONSE_FIX_IGNORE) self.show_error_box(r, {"nid" : nid, "address" : address} ) def cb_syncthing_my_id_changed(self, daemon, device_id): @@ -871,6 +876,19 @@ dtf = dt.strftime("%Y-%m-%d %H:%M") device['last-seen'] = str(dtf) + def cb_syncthing_device_paused_resumed(self, daemon, nid, paused): + if nid in self.devices: # Should be always + device = self.devices[nid] + device.set_status(_("Paused") if paused else _("Disconnected")) + device.set_color_hex(COLOR_DEVICE_OFFLINE) + device["online"] = False + device["connected"] = False + # Update visible values + device.hide_values("sync", "inbps", "outbps", "version") + device.show_values("last-seen") + self.update_folders() + self.set_status(True) + def cb_syncthing_device_state_changed(self, daemon, nid, connected): if nid in self.devices: # Should be always device = self.devices[nid] @@ -1379,9 +1397,11 @@ GLib.timeout_add_seconds(1, self.watcher.start) self.daemon.reload_config(callback) - def change_setting_n_restart(self, setting_name, value, retry_on_error=False): + def change_setting_async(self, setting_name, value, retry_on_error=False, restart=True): """ - Changes one value in daemon configuration and restarts daemon + Asynchronously changes one value in daemon configuration and + optionaly restarts daemon. + This will: - call daemon.read_config() to read configuration from daemon - change value in recieved YAML document @@ -1389,58 +1409,66 @@ - call daemon.restart() Everthing will be done asynchronously and will be repeated until succeed, if retry_on_error is set to True. - Even if retry_on_error is True, error in write_config will + Even if retry_on_error is False, error in write_config will be only logged. It is possible to change nested setting using '/' as separator. That may cause error if parent setting node is not present and this error will not cause retrying process as well. + + If value is callable, it's called instead of setting it. + In such case, callable is called as: + value(config_node_as_dict, setting_name) """ # ^^ Longest comment in entire project # Callbacks def csnr_error(e, trash, setting_name, value, retry_on_error): """ - Error handler for change_setting_n_restart method + Error handler for change_setting_async method """ - log.error("change_setting_n_restart: Failed to read configuration: %s", e) + log.error("change_setting_async: Failed to read configuration: %s", e) if retry_on_error: log.error("Retrying...") - change_setting_n_restart(setting_name, value, True) + change_setting_async(setting_name, value, retry_on_error, restart) else: log.error("Giving up.") def csnr_save_error(e, *a): """ - Another error handler for change_setting_n_restart method. + Another error handler for change_setting_async method. This one just reports failure. """ - log.error("change_setting_n_restart: Failed to store configuration: %s", e) + log.error("change_setting_async: Failed to store configuration: %s", e) log.error("Giving up.") def csnr_config_read(config, setting_name, value, retry_on_error): """ - Handler for change_setting_n_restart + Handler for change_setting_async Modifies recieved config and post it back. """ c, setting = config, setting_name while "/" in setting: key, setting = setting.split("/", 1) c = c[key] - c[setting] = value + if hasattr(value, '__call__'): + value(c, setting) + else: + c[setting] = value self.daemon.write_config(config, csnr_config_saved, csnr_save_error, setting_name, value) def csnr_config_saved(setting_name, value): """ - Handler for change_setting_n_restart + Handler for change_setting_async Reports good stuff and restarts daemon. """ log.verbose("Configuration value '%s' set to '%s'", setting_name, value) - message = "%s %s..." % (_("Syncthing is restarting."), _("Please wait")) - self.display_connect_dialog(message) - self.set_status(False) - self.restart() - GLib.idle_add(self.daemon.restart) + if restart: + message = "%s %s..." % (_("Syncthing is restarting."), _("Please wait")) + self.display_connect_dialog(message) + self.set_status(False) + self.restart() + GLib.idle_add(self.daemon.restart) # Call self.daemon.read_config(csnr_config_read, csnr_error, setting_name, value, retry_on_error) @@ -1584,11 +1612,11 @@ def cb_menu_recvlimit(self, menuitem, speed=0): if menuitem.get_active() and self.recv_limit != speed: - self.change_setting_n_restart("options/maxRecvKbps", speed) + self.change_setting_async("options/maxRecvKbps", speed) def cb_menu_sendlimit(self, menuitem, speed=0): if menuitem.get_active() and self.send_limit != speed: - self.change_setting_n_restart("options/maxSendKbps", speed) + self.change_setting_async("options/maxSendKbps", speed) def cb_menu_recvlimit_other(self, menuitem): return self.cb_menu_limit_other(menuitem, self.recv_limit) @@ -1628,6 +1656,8 @@ b = box["id"] != self.daemon.get_my_id() self["menu-popup-edit-device"].set_visible(b) self["menu-popup-delete-device"].set_visible(b) + self["menu-popup-pause-device"].set_visible(box.get_status() != _("Paused")) + self["menu-popup-resume-device"].set_visible(box.get_status() == _("Paused")) self["popup-menu-device"].popup(None, None, None, None, button, time) def cb_menu_popup(self, source, menu): @@ -1689,9 +1719,16 @@ def cb_menu_popup_delete_device(self, *a): """ Handler for other 'edit' context menu item """ - # Editing device self.check_delete("device", self.rightclick_box["id"], self.rightclick_box.get_title()) + def cb_menu_popup_pause_device(self, *a): + """ Handler for 'resume device' context menu item """ + self.daemon.pause(self.rightclick_box["id"]) + + def cb_menu_popup_resume_device(self, *a): + """ Handler for 'resume device' context menu item """ + self.daemon.resume(self.rightclick_box["id"]) + def check_delete(self, mode, id, name): """ Asks user if he really wants to do what he just asked to do @@ -1831,12 +1868,19 @@ e = DeviceEditorDialog(self, True, additional_data["nid"]) e.load() e.show(self["window"]) + elif response_id == RESPONSE_FIX_IGNORE: + # Ignore unknown device + def add_ignored(target, trash): + if not "ignoredDevices" in target: + target["ignoredDevices"] = [] + target["ignoredDevices"].append(additional_data["nid"]) + self.change_setting_async("ignoredDevices", add_ignored, restart=False) elif response_id == RESPONSE_UR_ALLOW: # Allow Usage reporting - self.change_setting_n_restart("options/urAccepted", 1) + self.change_setting_async("options/urAccepted", 1) elif response_id == RESPONSE_UR_FORBID: # Allow Usage reporting - self.change_setting_n_restart("options/urAccepted", -1) + self.change_setting_async("options/urAccepted", -1) self.cb_infobar_close(bar) def cb_open_closed(self, box): @@ -1880,6 +1924,7 @@ self.quit() def cb_daemon_exit(self, proc, error_code): + print "cb_daemon_exit", proc, self.process if proc == self.process: # Whatever happens, if daemon dies while it shouldn't, # restart it @@ -1917,6 +1962,6 @@ r = d.run() d.destroy() if r == FindDaemonDialog.RESPONSE_SAVED: - self.cb_daemon_exit(None, -1) + self.cb_daemon_exit(self.process, -1) else: self.quit() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/syncthing_gtk/configuration.py new/syncthing-gtk-0.8/syncthing_gtk/configuration.py --- old/syncthing-gtk-0.7.6.1/syncthing_gtk/configuration.py 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/syncthing_gtk/configuration.py 2015-11-05 18:16:32.000000000 +0100 @@ -59,7 +59,7 @@ self.load() except Exception, e: log.warning("Failed to load configuration; Creating new one.") - log.warning("Reason: %s", e) + log.warning("Reason: %s", (e,)) self.create() # Convert objects serialized as string back to object diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/syncthing_gtk/daemon.py new/syncthing-gtk-0.8/syncthing_gtk/daemon.py --- old/syncthing-gtk-0.7.6.1/syncthing_gtk/daemon.py 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/syncthing_gtk/daemon.py 2015-11-05 18:16:32.000000000 +0100 @@ -20,7 +20,7 @@ log = logging.getLogger("Daemon") # Minimal version supported by Daemon class -MIN_VERSION = "0.11" +MIN_VERSION = "0.12" # Random constant used as key when adding headers to returned data in # REST requests; Anything goes, as long as it isn't string @@ -129,6 +129,14 @@ id: id of device last_seen: datetime object or None, if device was never seen + device-paused (id): + Emited when synchronization with device is paused + id: id of folder + + device-resumed (id): + Emited when synchronization with device is resumed + id: id of folder + device-sync-started (id, progress): Emited after device synchronization is started id: id of folder @@ -160,6 +168,11 @@ daemon needs to be restarted id: id of folder + folder-scan-progress (id, progress): + Emited repeatedly while folder is being scanned + id: id of folder + progress: scan progress (0.0 to 1.0) + folder-sync-progress (id, progress): Emited repeatedly while folder is being synchronized id: id of folder @@ -228,18 +241,20 @@ b"device-discovered" : (GObject.SIGNAL_RUN_FIRST, None, (object,object,)), b"device-data-changed" : (GObject.SIGNAL_RUN_FIRST, None, (object, object, object, float, float, object, object)), b"last-seen-changed" : (GObject.SIGNAL_RUN_FIRST, None, (object, object)), + b"device-paused" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), + b"device-resumed" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), b"device-sync-started" : (GObject.SIGNAL_RUN_FIRST, None, (object, float)), b"device-sync-progress" : (GObject.SIGNAL_RUN_FIRST, None, (object, float)), b"device-sync-finished" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), b"folder-added" : (GObject.SIGNAL_RUN_FIRST, None, (object, object)), b"folder-data-changed" : (GObject.SIGNAL_RUN_FIRST, None, (object, object)), b"folder-data-failed" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), - b"folder-sync-started" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), b"folder-sync-finished" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), b"folder-sync-progress" : (GObject.SIGNAL_RUN_FIRST, None, (object, float)), - b"folder-scan-started" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), + b"folder-sync-started" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), b"folder-scan-finished" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), - b"folder-scan-progress" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), + b"folder-scan-started" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), + b"folder-scan-progress" : (GObject.SIGNAL_RUN_FIRST, None, (object, float)), b"folder-stopped" : (GObject.SIGNAL_RUN_FIRST, None, (object,object)), b"item-started" : (GObject.SIGNAL_RUN_FIRST, None, (object,object,object)), b"item-updated" : (GObject.SIGNAL_RUN_FIRST, None, (object,object,object)), @@ -736,11 +751,12 @@ self.timer("event", self._refresh_interval, self._request_events) def _syncthing_cb_errors(self, errors): - for e in errors["errors"]: - t = parsetime(e["time"]) - if t > self._last_error_time: - self.emit("error", e["error"]) - self._last_error_time = t + if errors["errors"] is not None: + for e in errors["errors"]: + t = parsetime(e["time"]) + if t > self._last_error_time: + self.emit("error", e["error"]) + self._last_error_time = t self.timer("errors", self._refresh_interval * 5, self._rest_request, "system/error", self._syncthing_cb_errors) def _syncthing_cb_events_error(self, exception, command): @@ -783,13 +799,20 @@ cons[id]["outbps"] = 0.0 # Store updated device_data for key in cons[id]: - if key != "clientVersion" or cons[id][key] != "": # Happens for 'total' - device_data[key] = cons[id][key] + if not key in ('clientVersion', 'connected'): # Don't want copy those + if cons[id][key] != "": # Happens for 'total' + device_data[key] = cons[id][key] - # Send "device-connected" signal, if device was disconnected until now - if not device_data["connected"] and nid != self._my_id: - device_data["connected"] = True - self.emit("device-connected", nid) + if cons[id]["paused"]: + # Send "device-paused" signal if device needed + device_data["connected"] = False + self.emit("device-paused", nid) + else: + # Send "device-connected" signal, if device was disconnected until now + if cons[id]["connected"]: + if not device_data["connected"] and nid != self._my_id: + device_data["connected"] = True + self.emit("device-connected", nid) # Send "device-data-changed" signal self.emit("device-data-changed", nid, device_data["address"], @@ -907,7 +930,8 @@ if state in ('error', 'stopped'): if not rid in self._stopped_folders: self._stopped_folders.add(rid) - self.emit("folder-stopped", rid, data["invalid"]) + reason = data["invalid"] or data["error"] + self.emit("folder-stopped", rid, reason) self.emit('folder-data-changed', rid, data) p = 0.0 if state == "syncing": @@ -1003,9 +1027,7 @@ self.emit("folder-sync-started", rid) elif state == "scanning": if not rid in self._stopped_folders: - if rid in self._scanning_folders: - self.emit("folder-scan-progress", rid) - else: + if not rid in self._scanning_folders: self._scanning_folders.add(rid) self.emit("folder-scan-started", rid) @@ -1026,13 +1048,19 @@ nid = e["data"]["id"] self.emit("device-connected", nid) elif eType == "DeviceDisconnected": - nid = e["data"]["id"] - self.emit("device-disconnected", nid) - self._request_last_seen() + nid = e["data"]["id"] + self.emit("device-disconnected", nid) elif eType == "DeviceDiscovered": nid = e["data"]["device"] addresses = e["data"]["addrs"] self.emit("device-discovered", nid, addresses) + elif eType == "DevicePaused": + nid = e["data"]["device"] + self.emit("device-paused", nid) + elif eType == "DeviceResumed": + nid = e["data"]["device"] + self.emit("device-resumed", nid) + self._request_last_seen() elif eType == "FolderRejected": nid = e["data"]["device"] rid = e["data"]["folder"] @@ -1041,6 +1069,13 @@ nid = e["data"]["device"] address = e["data"]["address"] self.emit("device-rejected", nid, address) + elif eType == "FolderScanProgress": + rid = e["data"]["folder"] + total = float(e["data"]["total"]) + if total > 0: + # ^^ just in case + status = float(e["data"]["current"]) / total + self.emit("folder-scan-progress", rid, status) elif eType == "ItemStarted": rid = e["data"]["folder"] filename = e["data"]["item"] @@ -1059,11 +1094,8 @@ self.emit("item-updated", rid, filename, mtime) elif eType == "ConfigSaved": self.emit("config-saved") - elif eType == "ItemFinished": - # Not handled (yet?) - pass - elif eType == "DownloadProgress": - # Not handled (yet?) + elif eType in ("ItemFinished", "DownloadProgress", "RelayStateChanged"): + # Not handled pass else: log.warning("Unhandled event type: %s", e) @@ -1231,21 +1263,27 @@ """ Returns True if daemon is known to be alive """ return self._connected - def rescan(self, folder_id, path=None): - """ Asks daemon to rescan entire folder or specified path """ + def pause(self, device_id): + """ Pauses synchronization with specified device """ + self._rest_post("system/pause?device=%s" % (device_id,), {}, lambda *a: a, lambda *a: log.error(a), device_id) + + def resume(self, device_id): + """ Resumes synchronization with specified device """ def on_error(*a): log.error(a) + self._rest_post("system/resume?device=%s" % (device_id,), {}, lambda *a: a, lambda *a: log.error(a), device_id) + + def rescan(self, folder_id, path=None): + """ Asks daemon to rescan entire folder or specified path """ if path is None: - self._rest_post("db/scan?folder=%s" % (folder_id,), {}, lambda *a: a, on_error, folder_id) + self._rest_post("db/scan?folder=%s" % (folder_id,), {}, lambda *a: a, lambda *a: log.error(a), folder_id) else: path_enc = urllib.quote(path.encode('utf-8'), ''.encode('utf-8')) self._rest_post("db/scan?folder=%s&sub=%s" % (folder_id, path_enc), {}, lambda *a: a, on_error, folder_id) def override(self, folder_id): """ Asks daemon to override changes made in specified folder """ - def on_error(*a): - log.error(a) - self._rest_post("model/override?folder=%s" % (folder_id,), {}, lambda *a: a, on_error, folder_id) + self._rest_post("model/override?folder=%s" % (folder_id,), {}, lambda *a: a, lambda *a: log.error(a), folder_id) def request_events(self): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/syncthing_gtk/foldereditor.py new/syncthing-gtk-0.8/syncthing_gtk/foldereditor.py --- old/syncthing-gtk-0.7.6.1/syncthing_gtk/foldereditor.py 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/syncthing_gtk/foldereditor.py 2015-11-05 18:16:32.000000000 +0100 @@ -15,8 +15,6 @@ log = logging.getLogger("FolderEditor") COLOR_NEW = "#A0A0A0" -# Regexp to check if folder id is valid -RE_FOLDER_ID = re.compile("^([a-zA-Z0-9\-\._]{1,64})$") # Regexp to generate folder id from filename RE_GEN_ID = re.compile("([a-zA-Z0-9\-\._]{1,64}).*") VALUES = [ "vid", "vpath", "vreadOnly", "vignorePerms", "vdevices", @@ -254,9 +252,6 @@ if value in self.app.folders: # Duplicate folder id return False - if RE_FOLDER_ID.match(value) is None: - # Invalid string - return False return True def check_path(self, value): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/syncthing_gtk/tools.py new/syncthing-gtk-0.8/syncthing_gtk/tools.py --- old/syncthing-gtk-0.7.6.1/syncthing_gtk/tools.py 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/syncthing_gtk/tools.py 2015-11-05 18:16:32.000000000 +0100 @@ -312,6 +312,12 @@ Returns ~/.config, %APPDATA% or whatever has user set as configuration directory. """ + if IS_WINDOWS: + try: + import windows + return windows.get_unicode_home() + except Exception: + pass confdir = GLib.get_user_config_dir() if confdir is None or IS_XP: if IS_WINDOWS: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syncthing-gtk-0.7.6.1/syncthing_gtk/windows.py new/syncthing-gtk-0.8/syncthing_gtk/windows.py --- old/syncthing-gtk-0.7.6.1/syncthing_gtk/windows.py 2015-10-07 10:04:07.000000000 +0200 +++ new/syncthing-gtk-0.8/syncthing_gtk/windows.py 2015-11-05 18:16:32.000000000 +0100 @@ -11,6 +11,7 @@ from gi.repository import Gio, GLib, GObject, Gtk, Gdk import os, sys, logging, codecs, msvcrt, win32pipe, win32api, _winreg import win32process +from win32com.shell import shell, shellcon log = logging.getLogger("windows.py") SM_SHUTTINGDOWN = 0x2000 @@ -85,6 +86,9 @@ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) +def get_unicode_home(): + return shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, None, 0) + class WinPopenReader: """ Reads from PIPE using GLib timers or idle_add. Emulates part of