[tomboy] [sync] Implement automatic background synchronization (bug #562097)
- From: Sanford Armstrong <sharm src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [tomboy] [sync] Implement automatic background synchronization (bug #562097)
- Date: Mon, 8 Feb 2010 20:06:29 +0000 (UTC)
commit fd70ff2d1db8392484a7e6fab6f29eafb1fd20e2
Author: Sandy Armstrong <sanfordarmstrong gmail com>
Date: Mon Feb 8 11:54:50 2010 -0800
[sync] Implement automatic background synchronization (bug #562097)
The preference UI to enable this feature is desensitized for now, because
this feature is still experimental. It can be enabled by changing the
/apps/tomboy/sync/autosync_timeout preference to a value greater than 0.
Tomboy.mdp | 1 +
Tomboy/Preferences.cs | 4 ++
Tomboy/PreferencesDialog.cs | 48 ++++++++++++++++
Tomboy/Synchronization/SilentUI.cs | 96 +++++++++++++++++++++++++++++++++
Tomboy/Synchronization/SyncManager.cs | 55 +++++++++++++++++++
data/tomboy.schemas.in | 17 ++++++
6 files changed, 221 insertions(+), 0 deletions(-)
---
diff --git a/Tomboy.mdp b/Tomboy.mdp
index d28779e..e1e2134 100644
--- a/Tomboy.mdp
+++ b/Tomboy.mdp
@@ -17,6 +17,7 @@
<File subtype="Code" buildaction="Compile" name="Tomboy/IRemoteControl.cs" />
<File subtype="Code" buildaction="Compile" name="Tomboy/RemoteControlWrapper.cs" />
<File subtype="Code" buildaction="Compile" name="Tomboy/Synchronization/ISyncUI.cs" />
+ <File subtype="Code" buildaction="Compile" name="Tomboy/Synchronization/SilentUI.cs" />
<File name="Tomboy/ActionManager.cs" subtype="Code" buildaction="Compile" />
<File name="Tomboy/Applet.cs" subtype="Code" buildaction="Compile" />
<File name="Tomboy/Logger.cs" subtype="Code" buildaction="Compile" />
diff --git a/Tomboy/Preferences.cs b/Tomboy/Preferences.cs
index 6268015..ab1996c 100644
--- a/Tomboy/Preferences.cs
+++ b/Tomboy/Preferences.cs
@@ -38,6 +38,7 @@ namespace Tomboy
public const string SYNC_LOCAL_PATH = "/apps/tomboy/sync/sync_local_path";
public const string SYNC_SELECTED_SERVICE_ADDIN = "/apps/tomboy/sync/sync_selected_service_addin";
public const string SYNC_CONFIGURED_CONFLICT_BEHAVIOR = "/apps/tomboy/sync/sync_conflict_behavior";
+ public const string SYNC_AUTOSYNC_TIMEOUT = "/apps/tomboy/sync/autosync_timeout";
public const string NOTE_RENAME_BEHAVIOR = "/apps/tomboy/note_rename_behavior";
@@ -135,6 +136,9 @@ namespace Tomboy
case SYNC_CONFIGURED_CONFLICT_BEHAVIOR:
return 0;
+ case SYNC_AUTOSYNC_TIMEOUT:
+ return -1;
+
case NOTE_RENAME_BEHAVIOR:
return 0;
diff --git a/Tomboy/PreferencesDialog.cs b/Tomboy/PreferencesDialog.cs
index 94a8806..4d34c42 100644
--- a/Tomboy/PreferencesDialog.cs
+++ b/Tomboy/PreferencesDialog.cs
@@ -18,6 +18,8 @@ namespace Tomboy
Gtk.Widget syncAddinPrefsWidget;
Gtk.Button resetSyncAddinButton;
Gtk.Button saveSyncAddinButton;
+ Gtk.CheckButton autosyncCheck;
+ Gtk.SpinButton autosyncSpinner;
Gtk.ComboBox rename_behavior_combo;
readonly AddinManager addin_manager;
@@ -135,6 +137,17 @@ namespace Tomboy
}
if (rename_behavior_combo.Active != rename_behavior)
rename_behavior_combo.Active = rename_behavior;
+ } else if (args.Key == Preferences.SYNC_AUTOSYNC_TIMEOUT) {
+ int timeout = (int) args.Value;
+ if (timeout <= 0 && autosyncCheck.Active)
+ autosyncCheck.Active = false;
+ else if (timeout > 0) {
+ timeout = (timeout >= 5 && timeout < 1000) ? timeout : 5;
+ if (!autosyncCheck.Active)
+ autosyncCheck.Active = true;
+ if ((int) autosyncSpinner.Value != timeout)
+ autosyncSpinner.Value = timeout;
+ }
}
}
@@ -485,6 +498,41 @@ namespace Tomboy
syncAddinPrefsContainer.Show ();
vbox.PackStart (syncAddinPrefsContainer, true, true, 10);
+ // Autosync preference
+ int timeout = (int) Preferences.Get (Preferences.SYNC_AUTOSYNC_TIMEOUT);
+ if (timeout > 0 && timeout < 5) {
+ timeout = 5;
+ Preferences.Set (Preferences.SYNC_AUTOSYNC_TIMEOUT, 5);
+ }
+ Gtk.HBox autosyncBox = new Gtk.HBox (false, 5);
+ // Translators: This is and the next string go together.
+ // Together they look like "Automatically Sync in Background Every [_] Minutes",
+ // where "[_]" is a GtkSpinButton.
+ autosyncCheck =
+ new Gtk.CheckButton (Catalog.GetString ("Automaticall_y Sync in Background Every"));
+ autosyncSpinner = new Gtk.SpinButton (5, 1000, 1);
+ autosyncSpinner.Value = timeout >= 5 ? timeout : 10;
+ Gtk.Label autosyncExtraText =
+ // Translators: See above comment for details on
+ // this string.
+ new Gtk.Label (Catalog.GetString ("Minutes"));
+ autosyncCheck.Active = autosyncSpinner.Sensitive = timeout >= 5;
+ EventHandler updateTimeoutPref = (o, e) => {
+ Preferences.Set (Preferences.SYNC_AUTOSYNC_TIMEOUT,
+ autosyncCheck.Active ? (int) autosyncSpinner.Value : -1);
+ };
+ autosyncCheck.Toggled += (o, e) => {
+ autosyncSpinner.Sensitive = autosyncCheck.Active;
+ updateTimeoutPref (o, e);
+ };
+ autosyncSpinner.ValueChanged += updateTimeoutPref;
+
+ autosyncBox.PackStart (autosyncCheck);
+ autosyncBox.PackStart (autosyncSpinner);
+ autosyncBox.PackStart (autosyncExtraText);
+ autosyncBox.Sensitive = false; // TODO: Remove when this feature is stable
+ vbox.PackStart (autosyncBox, false, true, 0);
+
Gtk.HButtonBox bbox = new Gtk.HButtonBox ();
bbox.Spacing = 4;
bbox.LayoutStyle = Gtk.ButtonBoxStyle.End;
diff --git a/Tomboy/Synchronization/SilentUI.cs b/Tomboy/Synchronization/SilentUI.cs
new file mode 100644
index 0000000..ef0b7fd
--- /dev/null
+++ b/Tomboy/Synchronization/SilentUI.cs
@@ -0,0 +1,96 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Sandy Armstrong <sanfordarmstrong gmail com>
+//
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Tomboy.Sync
+{
+ public class SilentUI : ISyncUI
+ {
+ private bool uiDisabled = false;
+ private NoteManager manager;
+
+ public SilentUI (NoteManager manager)
+ {
+ this.manager = manager;
+ }
+
+ #region ISyncUI implementation
+ public void SyncStateChanged (SyncState state)
+ {
+ // TODO: Update tray/applet icon
+ // D-Bus event?
+ // libnotify bubbles when appropriate
+ Logger.Debug ("SilentUI: SyncStateChanged: {0}", state);
+ AutoResetEvent evt;
+ switch (state) {
+ case SyncState.Connecting:
+ uiDisabled = true;
+ // TODO: Disable all kinds of note editing
+ // -New notes from server should be disabled, too
+ // -Anyway we could skip this when uploading changes?
+ // -Should store original Enabled state
+ GuiUtils.GtkInvokeAndWait (() => {
+ manager.ReadOnly = true;
+ foreach (Note note in new List<Note> (manager.Notes)) {
+ note.Enabled = false;
+ }
+ });
+ break;
+ case SyncState.Idle:
+ if (uiDisabled) {
+ GuiUtils.GtkInvokeAndWait (() => {
+ manager.ReadOnly = false;
+ foreach (Note note in new List<Note> (manager.Notes)) {
+ note.Enabled = true;
+ }
+ });
+ uiDisabled = false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void NoteSynchronized (string noteTitle, NoteSyncType type)
+ {
+ Logger.Debug ("SilentUI: NoteSynchronized, Title: {0}, Type: {1}", noteTitle, type);
+ }
+
+ public void NoteConflictDetected (NoteManager manager, Note localConflictNote, NoteUpdate remoteNote, IList<string> noteUpdateTitles)
+ {
+ Logger.Debug ("SilentUI: NoteConflictDetected, overwriting without a care");
+ // TODO: At least respect conflict prefs
+ // TODO: Implement more useful conflict handling
+ if (localConflictNote.Id != remoteNote.UUID)
+ manager.Delete (localConflictNote);
+ SyncManager.ResolveConflict (SyncTitleConflictResolution.OverwriteExisting);
+ }
+ #endregion
+ }
+}
diff --git a/Tomboy/Synchronization/SyncManager.cs b/Tomboy/Synchronization/SyncManager.cs
index 8c55a9f..efc3d32 100644
--- a/Tomboy/Synchronization/SyncManager.cs
+++ b/Tomboy/Synchronization/SyncManager.cs
@@ -205,10 +205,64 @@ namespace Tomboy.Sync
UpdateSyncAction ();
}
+ private static Timer autosyncTimer;
+ private static int autosyncTimeout = -1;
+
static void UpdateSyncAction ()
{
string sync_addin_id = Preferences.Get (Preferences.SYNC_SELECTED_SERVICE_ADDIN) as string;
Tomboy.ActionManager["SyncNotesAction"].Sensitive = !string.IsNullOrEmpty (sync_addin_id);
+
+ int timeoutPref = (int) Preferences.Get (Preferences.SYNC_AUTOSYNC_TIMEOUT);
+ if (timeoutPref != autosyncTimeout) {
+ autosyncTimeout = timeoutPref;
+ if (autosyncTimer != null) {
+ autosyncTimer.Dispose ();
+ autosyncTimer = null;
+ }
+ if (autosyncTimeout > 0) {
+ autosyncTimeout = autosyncTimeout >= 5 ? autosyncTimeout : 5;
+ autosyncTimer = new Timer ((o) => BackgroundSyncChecker (),
+ null,
+ 60000, // Perform a sync one minute after setting change
+ autosyncTimeout * 60000);
+ }
+ }
+ }
+
+ static void BackgroundSyncChecker ()
+ {
+ if (syncThread != null)
+ return;
+ var addin = GetConfiguredSyncService ();
+ if (addin != null) {
+ // TODO: block sync while checking
+ var server = addin.CreateSyncServer ();
+ bool clientHasUpdates = client.DeletedNoteTitles.Count > 0;
+ bool serverHasUpdates = false;
+ if (!clientHasUpdates) {
+ foreach (Note note in new List<Note> (NoteMgr.Notes)) {
+ if (client.GetRevision (note) == -1 ||
+ note.MetadataChangeDate > client.LastSyncDate) {
+ clientHasUpdates = true;
+ break;
+ }
+ }
+ }
+ // Wasteful to check when we'll sync anyway
+ // TODO: Unless we want to show a bubble when server has updates for users that don't autosync
+ if (!clientHasUpdates) {
+ Logger.Debug ("BackgroundSyncChecker: No client updates; checking with server");
+ serverHasUpdates = server.UpdatesAvailableSince (client.LastSynchronizedRevision);
+ }
+ addin.PostSyncCleanup (); // Let FUSE unmount, etc
+
+ if (clientHasUpdates || serverHasUpdates) {
+ Logger.Debug ("BackgroundSyncChecker: Detected that sync would be a good idea now");
+ // TODO: Check that it's safe to sync, block other sync UIs
+ PerformSynchronization (new SilentUI (NoteMgr));
+ }
+ }
}
public static void ResetClient ()
@@ -506,6 +560,7 @@ namespace Tomboy.Sync
}
}
+
/// <summary>
/// The GUI should call this after having the user resolve a conflict
/// so the synchronization thread can continue.
diff --git a/data/tomboy.schemas.in b/data/tomboy.schemas.in
index d403fe3..2692923 100644
--- a/data/tomboy.schemas.in
+++ b/data/tomboy.schemas.in
@@ -549,6 +549,23 @@
</schema>
<schema>
+ <key>/schemas/apps/tomboy/sync/autosync_timeout</key>
+ <applyto>/apps/tomboy/sync/autosync_timeout</applyto>
+ <owner>tomboy</owner>
+ <type>int</type>
+ <default>-1</default>
+ <locale name="C">
+ <short>Automatic Background Synchronization Timeout</short>
+ <long>
+ Integer value indicating how frequently to perform a background sync
+ of your notes (when sync is configured). Any value less than 1
+ indicates that autosync is disabled. The lowest acceptable positive
+ value is 5. Value is in minutes.
+ </long>
+ </locale>
+ </schema>
+
+ <schema>
<key>/schemas/apps/tomboy/note_rename_behavior</key>
<applyto>/apps/tomboy/note_rename_behavior</applyto>
<owner>tomboy</owner>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]