diff --git a/syncthing_gtk/app.py b/syncthing_gtk/app.py index 7de029dd..2b01c185 100644 --- a/syncthing_gtk/app.py +++ b/syncthing_gtk/app.py @@ -22,6 +22,7 @@ COLOR_REPO_SYNCING = "#2A89C8" COLOR_REPO_SCANNING = "#2A89C8" COLOR_REPO_IDLE = "#2AAB61" +COLOR_REPO_STOPPED = "#87000B" SI_FRAMES = 4 # Number of animation frames for status icon # Infobar position @@ -185,6 +186,7 @@ def setup_connection(self): self.daemon.connect("repo-sync-finished", self.cb_syncthing_repo_state_changed, 1.0, COLOR_REPO_IDLE, _("Idle")) self.daemon.connect("repo-scan-started", self.cb_syncthing_repo_state_changed, 1.0, COLOR_REPO_SCANNING, _("Scanning")) self.daemon.connect("repo-scan-finished", self.cb_syncthing_repo_state_changed, 1.0, COLOR_REPO_IDLE, _("Idle")) + self.daemon.connect("repo-stopped", self.cb_syncthing_repo_stopped) self.daemon.connect("system-data-updated", self.cb_syncthing_system_data) def start_deamon(self): @@ -276,8 +278,11 @@ def cb_syncthing_error(self, daemon, message): if "Unexpected repository ID" in message: # Handled by event, don't display twice return + severity = Gtk.MessageType.WARNING + if "Stopping repo" in message: + severity = Gtk.MessageType.ERROR self.error_messages.add(message) - self.show_error_box(RIBar(message, Gtk.MessageType.WARNING)) + self.show_error_box(RIBar(message, severity)) def cb_syncthing_repo_rejected(self, daemon, nid, rid): if (nid, rid) in self.error_messages: @@ -418,6 +423,17 @@ def cb_syncthing_repo_state_changed(self, daemon, rid, percentage, color, text): repo.set_status(text, percentage) self.set_status(True) + def cb_syncthing_repo_stopped(self, daemon, rid, message): + if rid in self.repos: # Should be always + repo = self.repos[rid] + repo.set_color_hex(COLOR_REPO_STOPPED) + repo.set_status(_("Stopped"), 0) + # Color, theme-based icon is used here. It's intentional and + # supposed to draw attention + repo.add_value("error", "dialog-error", _("Error"), message) + self["repolist"].show_all() + + def set_status(self, is_connected): """ Sets icon and text on first line of popup menu """ if is_connected: diff --git a/syncthing_gtk/daemon.py b/syncthing_gtk/daemon.py index 8c8c73f5..6e5b4cce 100644 --- a/syncthing_gtk/daemon.py +++ b/syncthing_gtk/daemon.py @@ -143,6 +143,14 @@ class Daemon(GObject.GObject, TimerManager): Emited after repository scan is finished id: id of repo + repo-stopped (id, message): + Emited when repository enters 'stopped' state. + No 'repo-sync', 'repo-sync-progress' and 'repo-scan-started' + events are emitted after repo enters this state, until + reconnect() is called. + id: id of repo + message: error message + item-started (repo_id, filename, time): Emited when synchronization of file starts repo_id: id of repo that contains file @@ -191,6 +199,7 @@ class Daemon(GObject.GObject, TimerManager): b"repo-scan-started" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), b"repo-scan-finished" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), b"repo-scan-progress" : (GObject.SIGNAL_RUN_FIRST, None, (object,)), + b"repo-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)), b"system-data-updated" : (GObject.SIGNAL_RUN_FIRST, None, (int, float, int)), @@ -221,6 +230,10 @@ def __init__(self): self._refresh_interval = 1 # seconds # syncing_repos holds set of repos that are being synchronized self._syncing_repos = set() + # stopped_repos holds set of repos in 'stopped' state + # No 'repo-sync', 'repo-sync-progress' and 'repo-scan-started' + # events are emitted after repo enters this state + self._stopped_repos = set() # syncing_nodes does same thing, only for nodes self._syncing_nodes = set() # and once again, for repos in 'Scanning' state @@ -705,6 +718,10 @@ def _syncthing_cb_version(self, data): def _syncthing_cb_repo_data(self, data, rid): state = data['state'] + if len(data['invalid'].strip()) > 0: + if not rid in self._stopped_repos: + self._stopped_repos.add(rid) + self.emit("repo-stopped", rid, data["invalid"]) self.emit('repo-data-changed', rid, data) if state == "syncing": p = 0.0 @@ -794,24 +811,28 @@ def _repo_state_changed(self, rid, state, progress): recheck = False if state != "syncing" and rid in self._syncing_repos: self._syncing_repos.discard(rid) - self.emit("repo-sync-finished", rid) + if not rid in self._stopped_repos: + self.emit("repo-sync-finished", rid) if state != "scanning" and rid in self._scanning_repos: self._scanning_repos.discard(rid) - self.emit("repo-scan-finished", rid) + if not rid in self._stopped_repos: + self.emit("repo-scan-finished", rid) if state == "syncing": - if rid in self._syncing_repos: - self.emit("repo-sync-progress", rid, progress) - else: - self._syncing_repos.add(rid) - self.emit("repo-sync-started", rid) - recheck = True + if not rid in self._stopped_repos: + if rid in self._syncing_repos: + self.emit("repo-sync-progress", rid, progress) + else: + self._syncing_repos.add(rid) + self.emit("repo-sync-started", rid) + recheck = True elif state == "scanning": - if rid in self._scanning_repos: - self.emit("repo-scan-progress", rid) - else: - self._scanning_repos.add(rid) - self.emit("repo-scan-started", rid) - recheck = True + if not rid in self._stopped_repos: + if rid in self._scanning_repos: + self.emit("repo-scan-progress", rid) + else: + self._scanning_repos.add(rid) + self.emit("repo-scan-started", rid) + recheck = True return recheck def _on_event(self, e): @@ -872,6 +893,7 @@ def reconnect(self): self._my_id = None self._connected = False self._syncing_repos = set() + self._stopped_repos = set() self._syncing_nodes = set() self._scanning_repos = set() self._needs_update = set() diff --git a/syncthing_gtk/infobox.py b/syncthing_gtk/infobox.py index 73cc9f29..ced6502f 100644 --- a/syncthing_gtk/infobox.py +++ b/syncthing_gtk/infobox.py @@ -311,7 +311,12 @@ def is_open(self): def add_value(self, key, icon, title, value): """ Adds new line with provided properties """ - wIcon = Gtk.Image.new_from_file(os.path.join(self.app.iconpath, icon)) + if "." in icon: + # Icon is filename + wIcon = Gtk.Image.new_from_file(os.path.join(self.app.iconpath, icon)) + else: + # Icon is theme icon name + wIcon = Gtk.Image.new_from_icon_name(icon, 1) wTitle, wValue = Gtk.Label(), Gtk.Label() self.value_widgets[key] = wValue self.set_value(key, value)