[tasque] Make libtasque Gtk# agnostic.



commit 8d085917a13c3dd68e9d876d9785bb2b3606616d
Author: Antonius Riha <antoniusriha gmail com>
Date:   Sun Dec 2 23:41:46 2012 +0100

    Make libtasque Gtk# agnostic.

 src/Addins/Backends/Dummy/DummyBackend.cs          |  201 ++---
 src/Addins/Backends/Dummy/DummyBackend.csproj      |    5 +-
 src/Addins/Backends/Dummy/DummyTask.cs             |   27 +-
 .../Backends/Rtm/Gtk/RtmPreferencesWidget.cs       |   33 +-
 src/Addins/Backends/Rtm/RtmBackend.cs              |  825 ++++++++------------
 src/Addins/Backends/Rtm/RtmBackend.csproj          |    5 +-
 src/Addins/Backends/Rtm/RtmCategory.cs             |   11 +-
 src/Addins/Backends/Rtm/RtmNote.cs                 |    2 +-
 src/Addins/Backends/Rtm/RtmTask.cs                 |   36 +-
 src/Addins/Backends/Sqlite/SqliteBackend.cs        |  193 ++---
 src/Addins/Backends/Sqlite/SqliteBackend.csproj    |    5 +-
 src/Addins/Backends/Sqlite/SqliteTask.cs           |   29 +-
 src/Gtk.Tasque/Application.cs                      |  527 +++++++++++++
 src/Gtk.Tasque/CompletedTaskGroup.cs               |   84 ++-
 src/Gtk.Tasque/Gtk.Tasque.csproj                   |    4 +-
 src/Gtk.Tasque/GtkApplicationBase.cs               |   16 +-
 src/Gtk.Tasque/GtkTray.cs                          |    7 +-
 src/Gtk.Tasque/PreferencesDialog.cs                |   31 +-
 src/Gtk.Tasque/RemoteControl.cs                    |   90 +--
 src/Gtk.Tasque/TaskGroup.cs                        |   69 +-
 src/Gtk.Tasque/TaskWindow.cs                       |   69 +-
 src/Gtk.Tasque/TreeModelListAdapter.cs             |  105 +++
 src/{Gtk.Tasque => libtasque}/AbstractTask.cs      |   17 +
 src/{Gtk.Tasque => libtasque}/AllCategory.cs       |    1 +
 src/libtasque/CategoryComparer.cs                  |   45 ++
 src/libtasque/CompletedTaskGroupModel.cs           |   10 +-
 src/libtasque/IBackend.cs                          |    5 +-
 src/libtasque/ITask.cs                             |    3 +-
 src/libtasque/TaskComparer.cs                      |   40 +
 src/libtasque/TaskGroupModel.cs                    |  305 +++++++-
 src/libtasque/TaskGroupModelFactory.cs             |   15 +-
 src/libtasque/libtasque.csproj                     |   11 +-
 32 files changed, 1751 insertions(+), 1075 deletions(-)
---
diff --git a/src/Addins/Backends/Dummy/DummyBackend.cs b/src/Addins/Backends/Dummy/DummyBackend.cs
index 8c83477..222aa1f 100644
--- a/src/Addins/Backends/Dummy/DummyBackend.cs
+++ b/src/Addins/Backends/Dummy/DummyBackend.cs
@@ -3,31 +3,29 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
 using Mono.Unix;
 using Tasque.Backends;
 using Gtk.Tasque.Backends.Dummy;
+using System.ComponentModel;
 
 namespace Tasque.Backends.Dummy
 {
 	public class DummyBackend : IBackend
 	{
-		/// <summary>
-		/// Keep track of the Gtk.TreeIters for the tasks so that they can
-		/// be referenced later.
-		///
-		/// Key   = Task ID
-		/// Value = Gtk.TreeIter in taskStore
-		/// </summary>
-		private Dictionary<int, Gtk.TreeIter> taskIters;
 		private int newTaskId;
-		private Gtk.TreeStore taskStore;
-		private Gtk.TreeModelSort sortedTasksModel;
 		private bool initialized;
 		private bool configured = true;
 		
-		private Gtk.ListStore categoryListStore;
-		private Gtk.TreeModelSort sortedCategoriesModel;
-
+		ObservableCollection<ITask> taskStore;
+		ObservableCollection<ICategory> categoryListStore;
+		ReadOnlyObservableCollection<ITask> readOnlyTaskStore;
+		ReadOnlyObservableCollection<ICategory> readOnlyCategoryStore;
+		
+		TaskComparer taskComparer;
+		CategoryComparer categoryComparer;
+		
 		public event BackendInitializedHandler BackendInitialized;
 		public event BackendSyncStartedHandler BackendSyncStarted;
 		public event BackendSyncFinishedHandler BackendSyncFinished;
@@ -40,18 +38,13 @@ namespace Tasque.Backends.Dummy
 		{
 			initialized = false;
 			newTaskId = 0;
-			taskIters = new Dictionary<int, Gtk.TreeIter> (); 
-			taskStore = new Gtk.TreeStore (typeof (ITask));
-			
-			sortedTasksModel = new Gtk.TreeModelSort (taskStore);
-			sortedTasksModel.SetSortFunc (0, new Gtk.TreeIterCompareFunc (CompareTasksSortFunc));
-			sortedTasksModel.SetSortColumnId (0, Gtk.SortType.Ascending);
-			
-			categoryListStore = new Gtk.ListStore (typeof (ICategory));
-			
-			sortedCategoriesModel = new Gtk.TreeModelSort (categoryListStore);
-			sortedCategoriesModel.SetSortFunc (0, new Gtk.TreeIterCompareFunc (CompareCategorySortFunc));
-			sortedCategoriesModel.SetSortColumnId (0, Gtk.SortType.Ascending);
+			taskStore = new ObservableCollection<ITask> ();
+			categoryListStore = new ObservableCollection<ICategory> ();
+			readOnlyTaskStore = new ReadOnlyObservableCollection<ITask> (taskStore);
+			readOnlyCategoryStore
+				= new ReadOnlyObservableCollection<ICategory> (categoryListStore);
+			taskComparer = new TaskComparer ();
+			categoryComparer = new CategoryComparer ();
 		}
 		
 		#region Public Properties
@@ -63,17 +56,17 @@ namespace Tasque.Backends.Dummy
 		/// <value>
 		/// All the tasks including ITaskDivider items.
 		/// </value>
-		public Gtk.TreeModel Tasks
+		public ICollection<ITask> Tasks
 		{
-			get { return sortedTasksModel; }
+			get { return readOnlyTaskStore; }
 		}
 		
 		/// <value>
 		/// This returns all the task lists (categories) that exist.
 		/// </value>
-		public Gtk.TreeModel Categories
+		public ICollection<ICategory> Categories
 		{
-			get { return sortedCategoriesModel; }
+			get { return readOnlyCategoryStore; }
 		}
 		
 		/// <value>
@@ -93,7 +86,7 @@ namespace Tasque.Backends.Dummy
 		}		
 		#endregion // Public Properties
 		
-		#region Public Methods
+		#region Public Methodsopen source contributors badges
 		public ITask CreateTask (string taskName, ICategory category)		
 		{
 			// not sure what to do here with the category
@@ -105,11 +98,11 @@ namespace Tasque.Backends.Dummy
 			else
 				task.Category = category;
 			
-			Gtk.TreeIter iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
 			newTaskId++;
 			
+			task.PropertyChanged += HandlePropertyChanged;
+			
 			return task;
 		}
 		
@@ -118,35 +111,28 @@ namespace Tasque.Backends.Dummy
 		
 		public void Refresh()
 		{}
-		
+
 		public void Initialize (Preferences preferences)
 		{
 			if (preferences == null)
 				throw new ArgumentNullException ("preferences");
 
-			Gtk.TreeIter iter;
-			
 			//
 			// Add in the "All" Category
 			//
-			AllCategory allCategory = new Tasque.AllCategory (preferences);
-			iter = categoryListStore.Append ();
-			categoryListStore.SetValue (iter, 0, allCategory);
+			AddCategory (new AllCategory (preferences));
 			
 			//
 			// Add in some fake categories
 			//
 			homeCategory = new DummyCategory ("Home");
-			iter = categoryListStore.Append ();
-			categoryListStore.SetValue (iter, 0, homeCategory);
+			AddCategory (homeCategory);
 			
 			workCategory = new DummyCategory ("Work");
-			iter = categoryListStore.Append ();
-			categoryListStore.SetValue (iter, 0, workCategory);
+			AddCategory (workCategory);
 			
 			projectsCategory = new DummyCategory ("Projects");
-			iter = categoryListStore.Append ();
-			categoryListStore.SetValue (iter, 0, projectsCategory);
+			AddCategory (projectsCategory);
 			
 			//
 			// Add in some fake tasks
@@ -156,9 +142,8 @@ namespace Tasque.Backends.Dummy
 			task.Category = projectsCategory;
 			task.DueDate = DateTime.Now.AddDays (1);
 			task.Priority = TaskPriority.Medium;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Call Roger");
@@ -166,79 +151,70 @@ namespace Tasque.Backends.Dummy
 			task.DueDate = DateTime.Now.AddDays (-1);
 			task.Complete ();
 			task.CompletionDate = task.DueDate;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Replace burnt out lightbulb");
 			task.Category = homeCategory;
 			task.DueDate = DateTime.Now;
 			task.Priority = TaskPriority.Low;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "File taxes");
 			task.Category = homeCategory;
 			task.DueDate = new DateTime (2008, 4, 1);
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Purchase lumber");
 			task.Category = projectsCategory;
 			task.DueDate = DateTime.Now.AddDays (1);
 			task.Priority = TaskPriority.High;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 						
 			task = new DummyTask (this, newTaskId, "Estimate drywall requirements");
 			task.Category = projectsCategory;
 			task.DueDate = DateTime.Now.AddDays (1);
 			task.Priority = TaskPriority.Low;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Borrow framing nailer from Ben");
 			task.Category = projectsCategory;
 			task.DueDate = DateTime.Now.AddDays (1);
 			task.Priority = TaskPriority.High;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Call for an insulation estimate");
 			task.Category = projectsCategory;
 			task.DueDate = DateTime.Now.AddDays (1);
 			task.Priority = TaskPriority.Medium;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Pay storage rental fee");
 			task.Category = homeCategory;
 			task.DueDate = DateTime.Now.AddDays (1);
 			task.Priority = TaskPriority.None;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Place carpet order");
 			task.Category = projectsCategory;
 			task.Priority = TaskPriority.None;
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			task = new DummyTask (this, newTaskId, "Test task overdue");
@@ -246,9 +222,8 @@ namespace Tasque.Backends.Dummy
 			task.DueDate = DateTime.Now.AddDays (-89);
 			task.Priority = TaskPriority.None;
 			task.Complete ();
-			iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [newTaskId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			newTaskId++;
 			
 			initialized = true;
@@ -271,59 +246,39 @@ namespace Tasque.Backends.Dummy
 		#endregion // Public Methods
 		
 		#region Private Methods
-		static int CompareTasksSortFunc (Gtk.TreeModel model,
-										 Gtk.TreeIter a,
-										 Gtk.TreeIter b)
+		internal void DeleteTask (DummyTask task)
 		{
-			ITask taskA = model.GetValue (a, 0) as ITask;
-			ITask taskB = model.GetValue (b, 0) as ITask;
-			
-			if (taskA == null || taskB == null)
-				return 0;
-			
-			return (taskA.CompareTo (taskB));
+			if (taskStore.Remove (task))
+				task.PropertyChanged -= HandlePropertyChanged;
 		}
 		
-		static int CompareCategorySortFunc (Gtk.TreeModel model,
-											Gtk.TreeIter a,
-											Gtk.TreeIter b)
+		void AddCategory (ICategory category)
 		{
-			ICategory categoryA = model.GetValue (a, 0) as ICategory;
-			ICategory categoryB = model.GetValue (b, 0) as ICategory;
-			
-			if (categoryA == null || categoryB == null)
-				return 0;
-			
-			if (categoryA is Tasque.AllCategory)
-				return -1;
-			else if (categoryB is Tasque.AllCategory)
-				return 1;
-			
-			return (categoryA.Name.CompareTo (categoryB.Name));
+			var index = categoryListStore.Count;
+			var valIdx = categoryListStore.Select ((val, idx) => new { val, idx })
+				.FirstOrDefault (x => categoryComparer.Compare (x.val, category) > 0);
+			if (valIdx != null)
+				index = valIdx.idx;
+			categoryListStore.Insert (index, category);
 		}
 		
-		public void UpdateTask (DummyTask task)
+		void AddTask (DummyTask task)
 		{
-			// Set the task in the store so the model will update the UI.
-			Gtk.TreeIter iter;
+			var index = taskStore.Count;
+			var valIdx = taskStore.Select ((val, idx) => new { val, idx })
+				.FirstOrDefault (t => taskComparer.Compare (t.val, task) > 0);
+			if (valIdx != null)
+				index = valIdx.idx;
 			
-			if (!taskIters.ContainsKey (task.DummyId))
-				return;
-				
-			iter = taskIters [task.DummyId];
-			
-			if (task.State == TaskState.Deleted) {
-				taskIters.Remove (task.DummyId);
-				if (!taskStore.Remove (ref iter)) {
-					Logger.Debug ("Successfully deleted from taskStore: {0}",
-						task.Name);
-				} else {
-					Logger.Debug ("Problem removing from taskStore: {0}",
-						task.Name);
-				}
-			} else {
-				taskStore.SetValue (iter, 0, task);
-			}
+			taskStore.Insert (index, task);
+		}
+		
+		void HandlePropertyChanged (object sender, PropertyChangedEventArgs e)
+		{
+			// when a property changes (any property atm), "reorder" tasks
+			var task = (DummyTask)sender;
+			if (taskStore.Remove (task))
+				AddTask (task);
 		}
 		#endregion // Private Methods
 		
diff --git a/src/Addins/Backends/Dummy/DummyBackend.csproj b/src/Addins/Backends/Dummy/DummyBackend.csproj
index 5000d1e..ee106b1 100644
--- a/src/Addins/Backends/Dummy/DummyBackend.csproj
+++ b/src/Addins/Backends/Dummy/DummyBackend.csproj
@@ -43,6 +43,7 @@
     <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
       <Private>False</Private>
     </Reference>
+    <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="DummyTask.cs" />
@@ -56,10 +57,6 @@
     <Compile Include="Gtk\DummyPreferences.cs" />
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\..\..\Gtk.Tasque\Gtk.Tasque.csproj">
-      <Project>{B19B9840-669D-4984-9772-E1F55193A67F}</Project>
-      <Name>Gtk.Tasque</Name>
-    </ProjectReference>
     <ProjectReference Include="..\..\..\libtasque\libtasque.csproj">
       <Project>{784C9AA8-2B28-400B-8CC4-DCDC48CA37F0}</Project>
       <Name>libtasque</Name>
diff --git a/src/Addins/Backends/Dummy/DummyTask.cs b/src/Addins/Backends/Dummy/DummyTask.cs
index e8e86e3..5d51199 100644
--- a/src/Addins/Backends/Dummy/DummyTask.cs
+++ b/src/Addins/Backends/Dummy/DummyTask.cs
@@ -48,12 +48,12 @@ namespace Tasque.Backends.Dummy
 			get { return name; }
 			set {
 Logger.Debug ("Setting new task name");
+				OnPropertyChanging ("Name");
 				if (value == null)
 					name = string.Empty;
 				
 				name = value.Trim ();
-				
-				backend.UpdateTask (this);
+				OnPropertyChanged ("Name");
 			}
 		}
 		
@@ -62,9 +62,9 @@ Logger.Debug ("Setting new task name");
 			get { return dueDate; }
 			set {
 Logger.Debug ("Setting new task due date");
+				OnPropertyChanging ("DueDate");
 				dueDate = value;
-				
-				backend.UpdateTask (this);
+				OnPropertyChanged ("DueDate");
 			}
 		}
 		
@@ -73,9 +73,9 @@ Logger.Debug ("Setting new task due date");
 			get { return completionDate; }
 			set {
 Logger.Debug ("Setting new task completion date");
+				OnPropertyChanging ("CompletionDate");
 				completionDate = value;
-				
-				backend.UpdateTask (this);
+				OnPropertyChanged ("CompletionDate");
 			}
 		}
 		
@@ -89,9 +89,9 @@ Logger.Debug ("Setting new task completion date");
 			get { return priority; }
 			set {
 Logger.Debug ("Setting new task priority");
+				OnPropertyChanging ("Priority");
 				priority = value;
-				
-				backend.UpdateTask (this);
+				OnPropertyChanged ("Priority");
 			}
 		}
 
@@ -129,32 +129,29 @@ Logger.Debug ("Setting new task priority");
 		public override void Activate ()
 		{
 Logger.Debug ("DummyTask.Activate ()");
-			completionDate = DateTime.MinValue;
 			state = TaskState.Active;
-			backend.UpdateTask (this);
+			CompletionDate = DateTime.MinValue;
 		}
 		
 		public override void Inactivate ()
 		{
 Logger.Debug ("DummyTask.Inactivate ()");
-			completionDate = DateTime.Now;
 			state = TaskState.Inactive;
-			backend.UpdateTask (this);
+			CompletionDate = DateTime.Now;
 		}
 		
 		public override void Complete ()
 		{
 			Logger.Debug ("DummyTask.Complete ()");
-			CompletionDate = DateTime.Now;
 			state = TaskState.Completed;
-			backend.UpdateTask (this);
+			CompletionDate = DateTime.Now;
 		}
 		
 		public override void Delete ()
 		{
 Logger.Debug ("DummyTask.Delete ()");
 			state = TaskState.Deleted;
-			backend.UpdateTask (this);
+			backend.DeleteTask (this);
 		}
 		
 		public override INote CreateNote(string text)
diff --git a/src/Addins/Backends/Rtm/Gtk/RtmPreferencesWidget.cs b/src/Addins/Backends/Rtm/Gtk/RtmPreferencesWidget.cs
index da9912c..528dc72 100644
--- a/src/Addins/Backends/Rtm/Gtk/RtmPreferencesWidget.cs
+++ b/src/Addins/Backends/Rtm/Gtk/RtmPreferencesWidget.cs
@@ -1,12 +1,14 @@
 // RtmPreferencesWidget.cs created with MonoDevelop
 // User: boyd at 11:29 PMÂ2/18/2008
-
 using System;
 using System.Diagnostics;
-using Gtk;
 using Mono.Unix;
+using Gdk;
+using Gtk;
+using Tasque;
+using Tasque.Backends;
 
-namespace Tasque.Backends.RtmBackend
+namespace Tasque.Backends.Rtm
 {
 	public class RtmPreferencesWidget : Gtk.EventBox, IBackendPreferences
 	{
@@ -24,7 +26,7 @@ namespace Tasque.Backends.RtmBackend
 		
 		static RtmPreferencesWidget ()
 		{
-			normalPixbuf = Utilities.GetIcon ("tasque-rtm-logo", 128);
+			normalPixbuf = GetIcon ("tasque-rtm-logo", 128);
 		}
 		
 		public RtmPreferencesWidget (RtmBackend backend, Preferences preferences) : base ()
@@ -156,5 +158,28 @@ namespace Tasque.Backends.RtmBackend
 						"\n" + userName.Trim ();
 			}
 		}
+
+		static Pixbuf GetIcon (string iconName, int size)
+		{
+			try {
+				return IconTheme.Default.LoadIcon (iconName, size, 0);
+			} catch (GLib.GException) {}
+			
+			try {
+				var ret = new Pixbuf (null, iconName + ".png");
+				return ret.ScaleSimple (size, size, InterpType.Bilinear);
+			} catch (ArgumentException) {}
+			
+			// TODO: This is a temporary fix to allow installing all icons as assembly
+			//       resources. The proper thing to do is to ship the actual icons,
+			//       and append to the default gtk+ icon theme path. See Tomboy.
+			try {
+				var ret = new Pixbuf (null, string.Format ("{0}-{1}.png", iconName, size));
+				return ret.ScaleSimple (size, size, InterpType.Bilinear);
+			} catch (ArgumentException) {}
+			
+			Logger.Debug ("Unable to load icon '{0}'.", iconName);
+			return null;
+		}
 	}
 }
diff --git a/src/Addins/Backends/Rtm/RtmBackend.cs b/src/Addins/Backends/Rtm/RtmBackend.cs
index 380a7c8..79bd449 100644
--- a/src/Addins/Backends/Rtm/RtmBackend.cs
+++ b/src/Addins/Backends/Rtm/RtmBackend.cs
@@ -1,197 +1,123 @@
-// RtmBackend.cs created with MonoDevelop
-// User: boyd at 7:10 AMÂ2/11/2008
 //
-// To change standard headers go to Edit->Preferences->Coding->Standard Headers
+// RtmBackend.cs
 //
-
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2012 Antonius Riha
+//
+// 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.
 using System;
-using Mono.Unix;
-using Tasque.Backends;
-using RtmNet;
-using System.Threading;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
 
-namespace Tasque.Backends.RtmBackend
+namespace Tasque.Backends.Rtm
 {
 	public class RtmBackend : IBackend
 	{
-		Preferences preferences;
-
-		private const string apiKey = "b29f7517b6584035d07df3170b80c430";
-		private const string sharedSecret = "93eb5f83628b2066";
-		private Gtk.TreeStore taskStore;
-		private Gtk.TreeModelSort sortedTasksModel;
-
-		private Gtk.ListStore categoryListStore;
-		private Gtk.TreeModelSort sortedCategoriesModel;
-		
-		private Thread refreshThread;
-		private bool runningRefreshThread;
-		private AutoResetEvent runRefreshEvent;
-
-		private Rtm rtm;
-		private string frob;
-		private Auth rtmAuth;
-		private string timeline;
-		
-		private Dictionary<string, Gtk.TreeIter> taskIters;
-		private object taskLock;
-
-		private Dictionary<string, RtmCategory> categories;
-		private object catLock;
-		private bool initialized;
-		private bool configured;
-
-		public event BackendInitializedHandler BackendInitialized;
-		public event BackendSyncStartedHandler BackendSyncStarted;
-		public event BackendSyncFinishedHandler BackendSyncFinished;
-		
 		public RtmBackend ()
 		{
-			initialized = false;
-			configured = false;
-
-			taskIters = new Dictionary<string, Gtk.TreeIter> ();
-			taskLock = new Object();
-			
-			categories = new Dictionary<string, RtmCategory> ();
-			catLock = new Object();
-
-			// *************************************
-			// Data Model Set up
-			// *************************************
-			taskStore = new Gtk.TreeStore (typeof (ITask));
-
-			sortedTasksModel = new Gtk.TreeModelSort (taskStore);
-			sortedTasksModel.SetSortFunc (0, new Gtk.TreeIterCompareFunc (CompareTasksSortFunc));
-			sortedTasksModel.SetSortColumnId (0, Gtk.SortType.Ascending);
+			taskComparer = new TaskComparer ();
+			categoryComparer = new CategoryComparer ();
 
-			categoryListStore = new Gtk.ListStore (typeof (ICategory));
+			tasks = new ObservableCollection<ITask> ();
+			categories = new ObservableCollection<ICategory> ();
 
-			sortedCategoriesModel = new Gtk.TreeModelSort (categoryListStore);
-			sortedCategoriesModel.SetSortFunc (0, new Gtk.TreeIterCompareFunc (CompareCategorySortFunc));
-			sortedCategoriesModel.SetSortColumnId (0, Gtk.SortType.Ascending);
+			Tasks = new ReadOnlyObservableCollection<ITask> (tasks);
+			Categories = new ReadOnlyObservableCollection<ICategory> (categories);
 		}
 
-		#region Public Properties
-		public string Name
-		{
-			get { return "Remember the Milk"; }
-		}
+		public string Name { get { return "Remember the Milk"; } }
 		
-		/// <value>
-		/// All the tasks including ITaskDivider items.
-		/// </value>
-		public Gtk.TreeModel Tasks
-		{
-			get { return sortedTasksModel; }
-		}
-
-		/// <value>
-		/// This returns all the task lists (categories) that exist.
-		/// </value>
-		public Gtk.TreeModel Categories
-		{
-			get { return sortedCategoriesModel; }
-		}
-
-		public string RtmUserName
-		{
-			get {
-				if( (rtmAuth != null) && (rtmAuth.User != null) ) {
-					return rtmAuth.User.Username;
-				} else
-					return null;
-			}
-		}
+		public ICollection<ITask> Tasks { get; private set; }
 		
-		/// <value>
-		/// Indication that the rtm backend is configured
-		/// </value>
-		public bool Configured
-		{
-			get { return configured; }
-		}
+		public ICollection<ICategory> Categories { get; private set; }
 		
-		/// <value>
-		/// Inidication that the backend is initialized
-		/// </value>
-		public bool Initialized
-		{
-			get { return initialized; }
-		}
+		public bool Configured { get; private set; }
 		
-		public IBackendPreferences Preferences { get { return new RtmPreferencesWidget (this, preferences); } }
+		public bool Initialized { get; private set; }
 		
-#endregion // Public Properties
+		public IBackendPreferences Preferences {
+			get { return new RtmPreferencesWidget (this, preferences); }
+		}
 
-#region Public Methods
 		public ITask CreateTask (string taskName, ICategory category)
 		{
-			string categoryID;
 			RtmTask rtmTask = null;
-			
-			if(category is Tasque.AllCategory)
-				categoryID = null;
-			else
-				categoryID = (category as RtmCategory).ID;	
 
-			if(rtm != null) {
+			string categoryID = null;
+			if (!(category is AllCategory))
+				categoryID = (category as RtmCategory).ID;
+			
+			if (rtm != null) {
 				try {
-					List list;
-					
-					if(categoryID == null)
-						list = rtm.TasksAdd(timeline, taskName);
+					RtmNet.List list;
+					if (categoryID == null)
+						list = rtm.TasksAdd (timeline, taskName);
 					else
-						list = rtm.TasksAdd(timeline, taskName, categoryID);
-
-					rtmTask = UpdateTaskFromResult(list);
-				} catch(Exception e) {
-					Logger.Debug("Unable to set create task: " + taskName);
-					Logger.Debug(e.ToString());
+						list = rtm.TasksAdd (timeline, taskName, categoryID);
+					rtmTask = UpdateTaskFromResult (list);
+				} catch (Exception e) {
+					Logger.Debug ("Unable to set create task: " + taskName);
+					Logger.Debug (e.ToString ());
 				}
-			}
-			else
-				throw new Exception("Unable to communicate with Remember The Milk");
-				
+			} else
+				throw new Exception ("Unable to communicate with Remember The Milk");
+			
 			return rtmTask;
 		}
-		
-		public void DeleteTask(ITask task)
+
+		public void DeleteTask (ITask task)
 		{
-			RtmTask rtmTask = task as RtmTask;
-			if(rtm != null) {
+			var rtmTask = task as RtmTask;
+			if (rtm != null) {
 				try {
-					rtm.TasksDelete(timeline, rtmTask.ListID, rtmTask.SeriesTaskID, rtmTask.TaskTaskID);
-
-					lock(taskLock)
-					{
-						Gtk.Application.Invoke ( delegate {
-							if(taskIters.ContainsKey(rtmTask.ID)) {
-								Gtk.TreeIter iter = taskIters[rtmTask.ID];
-								taskStore.Remove(ref iter);
-								taskIters.Remove(rtmTask.ID);
-							}
-						});
-					}
-				} catch(Exception e) {
-					Logger.Debug("Unable to delete task: " + task.Name);
-					Logger.Debug(e.ToString());
+					rtm.TasksDelete (timeline, rtmTask.ListID,
+					                 rtmTask.SeriesTaskID, rtmTask.TaskTaskID);
+					rtmTask.Delete ();
+				} catch (Exception e) {
+					Logger.Debug ("Unable to delete task: " + task.Name);
+					Logger.Debug (e.ToString ());
 				}
-			}
-			else
-				throw new Exception("Unable to communicate with Remember The Milk");
+			} else
+				throw new Exception ("Unable to communicate with Remember The Milk");
 		}
-		
-		public void Refresh()
+
+		public void Refresh ()
 		{
+			if (rtmAuth == null)
+				return;
+
 			Logger.Debug("Refreshing data...");
 
-			if (!runningRefreshThread)
-				StartThread();
+			if (BackendSyncStarted != null)
+				BackendSyncStarted ();
+
+			var lists = rtm.ListsGetList ();
+			UpdateCategories (lists);
+			UpdateTasks (lists);
+
+			if (BackendSyncFinished != null)
+				BackendSyncFinished ();
 
-			runRefreshEvent.Set();
-			
 			Logger.Debug("Done refreshing data!");
 		}
 
@@ -200,99 +126,80 @@ namespace Tasque.Backends.RtmBackend
 			if (preferences == null)
 				throw new ArgumentNullException ("preferences");
 			this.preferences = preferences;
-
-			// make sure we have the all Category in our list
-			Gtk.Application.Invoke ( delegate {
-				AllCategory allCategory = new Tasque.AllCategory (preferences);
-				Gtk.TreeIter iter = categoryListStore.Append ();
-				categoryListStore.SetValue (iter, 0, allCategory);
-			});
 			
-			runRefreshEvent = new AutoResetEvent(false);
+			// make sure we have the all Category in our list
+			AllCategory allCategory = new AllCategory (preferences);
+			AddCategory (allCategory);
 			
-			runningRefreshThread = false;
-			refreshThread  = new Thread(RefreshThreadLoop);
-
 			// *************************************
 			// AUTHENTICATION to Remember The Milk
 			// *************************************
 			string authToken = preferences.Get (Tasque.Preferences.AuthTokenKey);
-			if (authToken != null ) {
-				Logger.Debug("Found AuthToken, checking credentials...");
+			if (authToken != null) {
+				Logger.Debug ("Found AuthToken, checking credentials...");
 				try {
-					rtm = new Rtm(apiKey, sharedSecret, authToken);
-					rtmAuth = rtm.AuthCheckToken(authToken);
-					timeline = rtm.TimelineCreate();
-					Logger.Debug("RTM Auth Token is valid!");
-					Logger.Debug("Setting configured status to true");
-					configured = true;
+					rtm = new RtmNet.Rtm (apiKey, sharedSecret, authToken);
+					rtmAuth = rtm.AuthCheckToken (authToken);
+					timeline = rtm.TimelineCreate ();
+					Logger.Debug ("RTM Auth Token is valid!");
+					Logger.Debug ("Setting configured status to true");
+					Configured = true;
 				} catch (RtmNet.RtmApiException e) {
-					
 					preferences.Set (Tasque.Preferences.AuthTokenKey, null);
 					preferences.Set (Tasque.Preferences.UserIdKey, null);
 					preferences.Set (Tasque.Preferences.UserNameKey, null);
 					rtm = null;
 					rtmAuth = null;
-					Logger.Error("Exception authenticating, reverting" + e.Message);
-				} catch (ResponseXmlException e) {
+					Logger.Error ("Exception authenticating, reverting" + e.Message);
+				} catch (RtmNet.ResponseXmlException e) {
 					rtm = null;
 					rtmAuth = null;
 					Logger.Error ("Cannot parse RTM response. Maybe the service is down."
-					              + e.Message);
+						+ e.Message);
 				} catch (RtmNet.RtmWebException e) {
 					rtm = null;
 					rtmAuth = null;
-					Logger.Error("Not connected to RTM, maybe proxy: #{0}", e.Message);
- 				}
-				catch (System.Net.WebException e) {
+					Logger.Error ("Not connected to RTM, maybe proxy: #{0}", e.Message);
+				} catch (System.Net.WebException e) {
 					rtm = null;
 					rtmAuth = null;
-					Logger.Error("Problem connecting to internet: #{0}", e.Message);
+					Logger.Error ("Problem connecting to internet: #{0}", e.Message);
 				}
 			}
+			
+			if (rtm == null)
+				rtm = new RtmNet.Rtm (apiKey, sharedSecret);
 
-			if(rtm == null)
-				rtm = new Rtm(apiKey, sharedSecret);
-	
-			StartThread();	
-		}
+			Refresh ();
 
-		public void StartThread()
-		{
-			if (!configured) {
-				Logger.Debug("Backend not configured, not starting thread");
-				return;
-			}
-			runningRefreshThread = true;
-			Logger.Debug("ThreadState: " + refreshThread.ThreadState);
-			if (refreshThread.ThreadState == ThreadState.Running) {
-				Logger.Debug ("RtmBackend refreshThread already running");
-			} else {
-				if (!refreshThread.IsAlive) {
-					refreshThread  = new Thread(RefreshThreadLoop);
-				}
-				refreshThread.Start();
-			}
-			runRefreshEvent.Set();
+			Initialized = true;
+			if (BackendInitialized != null)
+				BackendInitialized ();
 		}
 
-		public void Cleanup()
+		public void Cleanup ()
 		{
-			runningRefreshThread = false;
-			runRefreshEvent.Set();
-			refreshThread.Abort ();
+			tasks.Clear ();
+			categories.Clear ();
+
+			rtm = null;
+			Initialized = false;
 		}
 
-		public string GetAuthUrl()
+		public event BackendInitializedHandler BackendInitialized;
+		public event BackendSyncStartedHandler BackendSyncStarted;
+		public event BackendSyncFinishedHandler BackendSyncFinished;
+
+		#region Internals
+		internal void DeleteTask (RtmTask task)
 		{
-			frob = rtm.AuthGetFrob();
-			string url = rtm.AuthCalcUrl(frob, AuthLevel.Delete);
-			return url;
+			if (tasks.Remove (task))
+				task.PropertyChanged -= HandlePropertyChanged;
 		}
 
-		public void FinishedAuth()
+		internal void FinishedAuth ()
 		{
-			rtmAuth = rtm.AuthGetToken(frob);
+			rtmAuth = rtm.AuthGetToken (frob);
 			if (rtmAuth != null) {
 				preferences.Set (Tasque.Preferences.AuthTokenKey, rtmAuth.Token);
 				if (rtmAuth.User != null) {
@@ -301,412 +208,300 @@ namespace Tasque.Backends.RtmBackend
 				}
 			}
 			
-			string authToken = preferences.Get (Tasque.Preferences.AuthTokenKey);
-			if (authToken != null ) {
-				Logger.Debug("Found AuthToken, checking credentials...");
+			var authToken = preferences.Get (Tasque.Preferences.AuthTokenKey);
+			if (authToken != null) {
+				Logger.Debug ("Found AuthToken, checking credentials...");
 				try {
-					rtm = new Rtm(apiKey, sharedSecret, authToken);
-					rtmAuth = rtm.AuthCheckToken(authToken);
-					timeline = rtm.TimelineCreate();
-					Logger.Debug("RTM Auth Token is valid!");
-					Logger.Debug("Setting configured status to true");
-					configured = true;
-					Refresh();
+					rtm = new RtmNet.Rtm (apiKey, sharedSecret, authToken);
+					rtmAuth = rtm.AuthCheckToken (authToken);
+					timeline = rtm.TimelineCreate ();
+					Logger.Debug ("RTM Auth Token is valid!");
+					Logger.Debug ("Setting configured status to true");
+					Configured = true;
+					Refresh ();
 				} catch (Exception e) {
 					rtm = null;
 					rtmAuth = null;				
-					Logger.Error("Exception authenticating, reverting" + e.Message);
+					Logger.Error ("Exception authenticating, reverting" + e.Message);
 				}	
 			}
 		}
 
-		public void UpdateTaskName(RtmTask task)
+		internal string GetAuthUrl ()
 		{
-			if(rtm != null) {
-				try {
-					List list = rtm.TasksSetName(timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID, task.Name);		
-					UpdateTaskFromResult(list);
-				} catch(Exception e) {
-					Logger.Debug("Unable to set name on task: " + task.Name);
-					Logger.Debug(e.ToString());
-				}
+			frob = rtm.AuthGetFrob ();
+			string url = rtm.AuthCalcUrl (frob, RtmNet.AuthLevel.Delete);
+			return url;
+		}
+
+		internal RtmCategory GetCategory (string id)
+		{
+			foreach (var item in categories) {
+				var category = item as RtmCategory;
+				if (category != null && category.ID == id)
+					return category;
 			}
+			return null;
 		}
-		
-		public void UpdateTaskDueDate(RtmTask task)
+
+		internal void UpdateTaskName (RtmTask task)
 		{
-			if(rtm != null) {
+			if (rtm != null) {
 				try {
-					List list;
-					if(task.DueDate == DateTime.MinValue)
-						list = rtm.TasksSetDueDate(timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID);
-					else	
-						list = rtm.TasksSetDueDate(timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID, task.DueDateString);
-					UpdateTaskFromResult(list);
-				} catch(Exception e) {
-					Logger.Debug("Unable to set due date on task: " + task.Name);
-					Logger.Debug(e.ToString());
+					RtmNet.List list = rtm.TasksSetName (timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID, task.Name);		
+					UpdateTaskFromResult (list);
+				} catch (Exception e) {
+					Logger.Debug ("Unable to set name on task: " + task.Name);
+					Logger.Debug (e.ToString ());
 				}
 			}
 		}
 		
-		public void UpdateTaskCompleteDate(RtmTask task)
+		internal void UpdateTaskDueDate (RtmTask task)
 		{
-			UpdateTask(task);
+			if (rtm != null) {
+				try {
+					if (task.DueDate == DateTime.MinValue)
+						rtm.TasksSetDueDate (timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID);
+					else
+						rtm.TasksSetDueDate (timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID, task.DueDateString);
+				} catch (Exception e) {
+					Logger.Debug ("Unable to set due date on task: " + task.Name);
+					Logger.Debug (e.ToString ());
+				}
+			}
 		}
 		
-		public void UpdateTaskPriority(RtmTask task)
+		internal void UpdateTaskPriority (RtmTask task)
 		{
-			if(rtm != null) {
+			if (rtm != null) {
 				try {
-					List list = rtm.TasksSetPriority(timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID, task.PriorityString);
-					UpdateTaskFromResult(list);
-				} catch(Exception e) {
-					Logger.Debug("Unable to set priority on task: " + task.Name);
-					Logger.Debug(e.ToString());
+					RtmNet.List list = rtm.TasksSetPriority (timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID, task.PriorityString);
+					UpdateTaskFromResult (list);
+				} catch (Exception e) {
+					Logger.Debug ("Unable to set priority on task: " + task.Name);
+					Logger.Debug (e.ToString ());
 				}
 			}
 		}
 		
-		public void UpdateTaskActive(RtmTask task)
+		internal void UpdateTaskActive (RtmTask task)
 		{
-			if(task.State == TaskState.Completed)
-			{
-				if(rtm != null) {
+			if (task.State == TaskState.Completed) {
+				if (rtm != null) {
 					try {
-						List list = rtm.TasksUncomplete(timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID);
-						UpdateTaskFromResult(list);
-					} catch(Exception e) {
-						Logger.Debug("Unable to set Task as completed: " + task.Name);
-						Logger.Debug(e.ToString());
+						rtm.TasksUncomplete (timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID);
+					} catch (Exception e) {
+						Logger.Debug ("Unable to set Task as completed: " + task.Name);
+						Logger.Debug (e.ToString ());
 					}
 				}
 			}
-			else
-				UpdateTask(task);
 		}
 		
-		public void UpdateTaskInactive(RtmTask task)
+		internal void UpdateTaskCompleted (RtmTask task)
 		{
-			UpdateTask(task);
-		}	
-
-		public void UpdateTaskCompleted(RtmTask task)
-		{
-			if(rtm != null) {
+			if (rtm != null) {
 				try {
-					List list = rtm.TasksComplete(timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID);
-					UpdateTaskFromResult(list);
-				} catch(Exception e) {
-					Logger.Debug("Unable to set Task as completed: " + task.Name);
-					Logger.Debug(e.ToString());
+					rtm.TasksComplete (timeline, task.ListID, task.SeriesTaskID, task.TaskTaskID);
+				} catch (Exception e) {
+					Logger.Debug ("Unable to set Task as completed: " + task.Name);
+					Logger.Debug (e.ToString ());
 				}
 			}
-		}	
-
-		public void UpdateTaskDeleted(RtmTask task)
-		{
-			UpdateTask(task);
 		}
 		
-
-		public void MoveTaskCategory(RtmTask task, string id)
+		internal void MoveTaskCategory (RtmTask task, string id)
 		{
-			if(rtm != null) {
+			if (rtm != null) {
 				try {
-					List list = rtm.TasksMoveTo(timeline, task.ListID, id, task.SeriesTaskID, task.TaskTaskID);
-					UpdateTaskFromResult(list);
-				} catch(Exception e) {
-					Logger.Debug("Unable to set Task as completed: " + task.Name);
-					Logger.Debug(e.ToString());
+					rtm.TasksMoveTo (timeline, task.ListID, id, task.SeriesTaskID, task.TaskTaskID);
+				} catch (Exception e) {
+					Logger.Debug ("Unable to set Task as completed: " + task.Name);
+					Logger.Debug (e.ToString ());
 				}
 			}					
 		}
-		
-		
-		public void UpdateTask(RtmTask task)
-		{
-			lock(taskLock)
-			{
-				Gtk.TreeIter iter;
-				
-				Gtk.Application.Invoke ( delegate {
-					if(taskIters.ContainsKey(task.ID)) {
-						iter = taskIters[task.ID];
-						taskStore.SetValue (iter, 0, task);
-					}
-				});
-			}		
-		}
-		
-		public RtmTask UpdateTaskFromResult(List list)
-		{
-			TaskSeries ts = list.TaskSeriesCollection[0];
-			if(ts != null) {
-				RtmTask rtmTask = null;
-				foreach(Task task in ts.TaskCollection)
-				{
-					rtmTask = new RtmTask(ts, task, this, list.ID);
-					lock(taskLock)
-					{
-						Gtk.Application.Invoke ( delegate {
-							if(taskIters.ContainsKey(rtmTask.ID)) {
-								Gtk.TreeIter iter = taskIters[rtmTask.ID];
-								taskStore.SetValue (iter, 0, rtmTask);
-							} else {
-								Gtk.TreeIter iter = taskStore.AppendNode();
-								taskIters.Add(rtmTask.ID, iter);
-								taskStore.SetValue (iter, 0, rtmTask);
-							}
-						});
-					}
-				}
-				/* Always return the last task received */
-				return rtmTask;
-			}
-			return null;
-		}
-		
-		public RtmCategory GetCategory(string id)
-		{
-			if(categories.ContainsKey(id))
-				return categories[id];
-			else
-				return null;
-		}
-		
-		public RtmNote CreateNote (RtmTask rtmTask, string text)
+
+		internal RtmNote CreateNote (RtmTask rtmTask, string text)
 		{
 			RtmNet.Note note = null;
 			RtmNote rtmNote = null;
 			
-			if(rtm != null) {
+			if (rtm != null) {
 				try {
-					note = rtm.NotesAdd(timeline, rtmTask.ListID, rtmTask.SeriesTaskID, rtmTask.TaskTaskID, String.Empty, text);
-					rtmNote = new RtmNote(note);
-				} catch(Exception e) {
-					Logger.Debug("RtmBackend.CreateNote: Unable to create a new note");
-					Logger.Debug(e.ToString());
+					note = rtm.NotesAdd (timeline, rtmTask.ListID, rtmTask.SeriesTaskID, rtmTask.TaskTaskID, String.Empty, text);
+					rtmNote = new RtmNote (note);
+				} catch (Exception e) {
+					Logger.Debug ("RtmBackend.CreateNote: Unable to create a new note");
+					Logger.Debug (e.ToString ());
 				}
-			}
-			else
-				throw new Exception("Unable to communicate with Remember The Milk");
-				
+			} else
+				throw new Exception ("Unable to communicate with Remember The Milk");
+			
 			return rtmNote;
 		}
 
-
-		public void DeleteNote (RtmTask rtmTask, RtmNote note)
+		internal void DeleteNote (RtmTask rtmTask, RtmNote note)
 		{
-			if(rtm != null) {
+			if (rtm != null) {
 				try {
-					rtm.NotesDelete(timeline, note.ID);
-				} catch(Exception e) {
-					Logger.Debug("RtmBackend.DeleteNote: Unable to delete note");
-					Logger.Debug(e.ToString());
+					rtm.NotesDelete (timeline, note.ID);
+				} catch (Exception e) {
+					Logger.Debug ("RtmBackend.DeleteNote: Unable to delete note");
+					Logger.Debug (e.ToString ());
 				}
-			}
-			else
-				throw new Exception("Unable to communicate with Remember The Milk");
+			} else
+				throw new Exception ("Unable to communicate with Remember The Milk");
 		}
-
-		public void SaveNote (RtmTask rtmTask, RtmNote note)
+		
+		internal void SaveNote (RtmTask rtmTask, RtmNote note)
 		{
-			if(rtm != null) {
+			if (rtm != null) {
 				try {
-					rtm.NotesEdit(timeline, note.ID, String.Empty, note.Text);
-				} catch(Exception e) {
-					Logger.Debug("RtmBackend.SaveNote: Unable to save note");
-					Logger.Debug(e.ToString());
+					rtm.NotesEdit (timeline, note.ID, String.Empty, note.Text);
+				} catch (Exception e) {
+					Logger.Debug ("RtmBackend.SaveNote: Unable to save note");
+					Logger.Debug (e.ToString ());
 				}
-			}
-			else
-				throw new Exception("Unable to communicate with Remember The Milk");
-		}
-
-#endregion // Public Methods
-
-#region Private Methods
-		static int CompareTasksSortFunc (Gtk.TreeModel model,
-				Gtk.TreeIter a,
-				Gtk.TreeIter b)
-		{
-			ITask taskA = model.GetValue (a, 0) as ITask;
-			ITask taskB = model.GetValue (b, 0) as ITask;
-
-			if (taskA == null || taskB == null)
-				return 0;
-
-			return (taskA.CompareTo (taskB));
-		}
-
-		static int CompareCategorySortFunc (Gtk.TreeModel model,
-											Gtk.TreeIter a,
-											Gtk.TreeIter b)
-		{
-			ICategory categoryA = model.GetValue (a, 0) as ICategory;
-			ICategory categoryB = model.GetValue (b, 0) as ICategory;
-			
-			if (categoryA == null || categoryB == null)
-				return 0;
-			
-			if (categoryA is Tasque.AllCategory)
-				return -1;
-			else if (categoryB is Tasque.AllCategory)
-				return 1;
-			
-			return (categoryA.Name.CompareTo (categoryB.Name));
+			} else
+				throw new Exception ("Unable to communicate with Remember The Milk");
 		}
+		#endregion
 
+		#region My privates
 		/// <summary>
 		/// Update the model to match what is in RTM
 		/// FIXME: This is a lame implementation and needs to be optimized
 		/// </summary>		
-		private void UpdateCategories(Lists lists)
+		void UpdateCategories (RtmNet.Lists lists)
 		{
-			Logger.Debug("RtmBackend.UpdateCategories was called");
-			
+			Logger.Debug ("RtmBackend.UpdateCategories was called");
 			try {
-				foreach(List list in lists.listCollection)
-				{
+				foreach (var list in lists.listCollection) {
 					if (list.Smart == 1) {
 						Logger.Warn ("Smart list \"{0}\" omitted", list.Name);
 						continue;
 					}
 					
-					RtmCategory rtmCategory = new RtmCategory(list);
-
-					lock(catLock)
-					{
-						Gtk.TreeIter iter;
-						
-						Gtk.Application.Invoke ( delegate {
-
-							if(categories.ContainsKey(rtmCategory.ID)) {
-								iter = categories[rtmCategory.ID].Iter;
-								categoryListStore.SetValue (iter, 0, rtmCategory);
-							} else {
-								iter = categoryListStore.Append();
-								categoryListStore.SetValue (iter, 0, rtmCategory);
-								rtmCategory.Iter = iter;
-								categories.Add(rtmCategory.ID, rtmCategory);
-							}
-						});
-					}
+					var rtmCategory = new RtmCategory (list);
+					if (categories.Any (c => c.Name == rtmCategory.Name))
+						continue;
+					
+					AddCategory (rtmCategory);
 				}
 			} catch (Exception e) {
-				Logger.Debug("Exception in fetch " + e.Message);
+				Logger.Debug ("Exception in fetch " + e.Message);
 			}
-			Logger.Debug("RtmBackend.UpdateCategories is done");			
+			Logger.Debug ("RtmBackend.UpdateCategories is done");			
 		}
-
+		
 		/// <summary>
 		/// Update the model to match what is in RTM
 		/// FIXME: This is a lame implementation and needs to be optimized
 		/// </summary>		
-		private void UpdateTasks(Lists lists)
+		void UpdateTasks (RtmNet.Lists lists)
 		{
-			Logger.Debug("RtmBackend.UpdateTasks was called");
-			
+			Logger.Debug ("RtmBackend.UpdateTasks was called");
 			try {
-				foreach(List list in lists.listCollection)
-				{
+				foreach (var list in lists.listCollection) {
 					// smart lists are based on criteria and therefore
 					// can contain tasks that actually belong to another list.
 					// Hence skip smart lists in task list population.
 					if (list.Smart == 1)
 						continue;
 					
-					Tasks tasks = null;
+					RtmNet.Tasks rtmTasks = null;
 					try {
-						tasks = rtm.TasksGetList(list.ID);
+						rtmTasks = rtm.TasksGetList (list.ID);
 					} catch (Exception tglex) {
-						Logger.Debug("Exception calling TasksGetList(list.ListID) " + tglex.Message);
+						Logger.Debug ("Exception calling TasksGetList (list.ListID) "
+							+ tglex.Message);
 					}
-
-					if(tasks != null) {
-						foreach(List tList in tasks.ListCollection)
-						{
+					
+					if (rtmTasks != null) {
+						foreach (var tList in rtmTasks.ListCollection) {
 							if (tList.TaskSeriesCollection == null)
 								continue;
-							foreach(TaskSeries ts in tList.TaskSeriesCollection)
-							{
-								foreach(Task task in ts.TaskCollection)
-								{
-									RtmTask rtmTask = new RtmTask(ts, task, this, tList.ID);
-
-									lock(taskLock)
-									{
-										Gtk.TreeIter iter;
-
-										Gtk.Application.Invoke ( delegate {
-
-											if(taskIters.ContainsKey(rtmTask.ID)) {
-												iter = taskIters[rtmTask.ID];
-											} else {
-												iter = taskStore.AppendNode ();
-												taskIters.Add(rtmTask.ID, iter);
-											}
-
-											taskStore.SetValue (iter, 0, rtmTask);
-										});
-									}
-								}
-							}
+
+							foreach (var ts in tList.TaskSeriesCollection)
+								UpdateTaskCore (ts, tList.ID);
 						}
 					}
 				}
 			} catch (Exception e) {
-				Logger.Debug("Exception in fetch " + e.Message);
-				Logger.Debug(e.ToString());
+				Logger.Debug ("Exception in fetch " + e.Message);
+				Logger.Debug (e.ToString ());
+			}
+			Logger.Debug ("RtmBackend.UpdateTasks is done");			
+		}
+
+		RtmTask UpdateTaskFromResult (RtmNet.List list)
+		{
+			var ts = list.TaskSeriesCollection [0];
+			if (ts != null)
+				return UpdateTaskCore (ts, list.ID);
+			return null;
+		}
+
+		RtmTask UpdateTaskCore (RtmNet.TaskSeries taskSeries, string listId)
+		{
+			RtmTask rtmTask = null;
+			foreach (var task in taskSeries.TaskCollection) {
+				rtmTask = new RtmTask (taskSeries, task, this, listId);
+				if (tasks.Any (t => t.Id == rtmTask.Id))
+					continue;
+				
+				rtmTask.PropertyChanged += HandlePropertyChanged;
+				AddTask (rtmTask);
 			}
-			Logger.Debug("RtmBackend.UpdateTasks is done");			
+			/* Always return the last task received */
+			return rtmTask;
 		}
 		
+		void AddCategory (ICategory category)
+		{
+			var index = categories.Count;
+			var valIdx = categories.Select ((val, idx) => new { val, idx })
+				.FirstOrDefault (x => categoryComparer.Compare (x.val, category) > 0);
+			if (valIdx != null)
+				index = valIdx.idx;
+			categories.Insert (index, category);
+		}
 		
+		void AddTask (RtmTask task)
+		{
+			var index = tasks.Count;
+			var valIdx = tasks.Select ((val, idx) => new { val, idx })
+				.FirstOrDefault (t => taskComparer.Compare (t.val, task) > 0);
+			if (valIdx != null)
+				index = valIdx.idx;
+			
+			tasks.Insert (index, task);
+		}
 		
-		private void RefreshThreadLoop()
+		void HandlePropertyChanged (object sender, PropertyChangedEventArgs e)
 		{
-			while(runningRefreshThread) {
-				runRefreshEvent.WaitOne();
-
-				if(!runningRefreshThread)
-					return;
-
-				// Fire the event on the main thread
-				Gtk.Application.Invoke ( delegate {
-					if(BackendSyncStarted != null)
-						BackendSyncStarted();
-				});
+			// when a property changes (any property atm), "reorder" tasks
+			var task = (RtmTask)sender;
+			if (tasks.Remove (task))
+				AddTask (task);
+		}
 
-				runRefreshEvent.Reset();
+		ObservableCollection<ITask> tasks;
+		ObservableCollection<ICategory> categories;
+		TaskComparer taskComparer;
+		CategoryComparer categoryComparer;
 
-				if(rtmAuth != null) {
-					Lists lists = rtm.ListsGetList();
-					UpdateCategories(lists);
-					UpdateTasks(lists);
-				}
-				if(!initialized) {
-					initialized = true;
-
-					// Fire the event on the main thread
-					Gtk.Application.Invoke ( delegate {
-						if(BackendInitialized != null)
-							BackendInitialized();
-					});
-				}
+		Preferences preferences;
 
-				// Fire the event on the main thread
-				Gtk.Application.Invoke ( delegate {
-					if(BackendSyncFinished != null)
-						BackendSyncFinished();
-				});
-			}
-		}
-		
-#endregion // Private Methods
+		const string apiKey = "b29f7517b6584035d07df3170b80c430";
+		const string sharedSecret = "93eb5f83628b2066";
 
-#region Event Handlers
-#endregion // Event Handlers
+		RtmNet.Rtm rtm;
+		string frob;
+		RtmNet.Auth rtmAuth;
+		string timeline;
+		#endregion
 	}
 }
diff --git a/src/Addins/Backends/Rtm/RtmBackend.csproj b/src/Addins/Backends/Rtm/RtmBackend.csproj
index 3ba0aa3..8373fad 100644
--- a/src/Addins/Backends/Rtm/RtmBackend.csproj
+++ b/src/Addins/Backends/Rtm/RtmBackend.csproj
@@ -58,6 +58,7 @@
     <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
       <Private>False</Private>
     </Reference>
+    <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\..\libtasque\libtasque.csproj">
@@ -68,10 +69,6 @@
       <Project>{0AA1B96E-03DE-4D26-B4FD-507E988FD9B7}</Project>
       <Name>RtmNet</Name>
     </ProjectReference>
-    <ProjectReference Include="..\..\..\Gtk.Tasque\Gtk.Tasque.csproj">
-      <Project>{B19B9840-669D-4984-9772-E1F55193A67F}</Project>
-      <Name>Gtk.Tasque</Name>
-    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="RtmTask.cs" />
diff --git a/src/Addins/Backends/Rtm/RtmCategory.cs b/src/Addins/Backends/Rtm/RtmCategory.cs
index 87595c3..b8cac95 100644
--- a/src/Addins/Backends/Rtm/RtmCategory.cs
+++ b/src/Addins/Backends/Rtm/RtmCategory.cs
@@ -3,17 +3,14 @@
 //
 // To change standard headers go to Edit->Preferences->Coding->Standard Headers
 //
-
-using System;
 using Tasque;
 using RtmNet;
 
-namespace Tasque.Backends.RtmBackend
+namespace Tasque.Backends.Rtm
 {
 	public class RtmCategory : ICategory
 	{
 		private List list;
-		private Gtk.TreeIter iter;
 
 		public RtmCategory(List list)
 		{
@@ -54,12 +51,6 @@ namespace Tasque.Backends.RtmBackend
 		{
 			get { return list.Smart; }
 		}
-		
-		public Gtk.TreeIter Iter
-		{
-			get { return iter; }
-			set { iter = value; }
-		}
 
 		public bool ContainsTask(ITask task)
 		{
diff --git a/src/Addins/Backends/Rtm/RtmNote.cs b/src/Addins/Backends/Rtm/RtmNote.cs
index 72b0689..2e605b5 100644
--- a/src/Addins/Backends/Rtm/RtmNote.cs
+++ b/src/Addins/Backends/Rtm/RtmNote.cs
@@ -8,7 +8,7 @@ using System;
 using Tasque;
 using RtmNet;
 
-namespace Tasque.Backends.RtmBackend
+namespace Tasque.Backends.Rtm
 {
 	public class RtmNote : INote
 	{
diff --git a/src/Addins/Backends/Rtm/RtmTask.cs b/src/Addins/Backends/Rtm/RtmTask.cs
index d742660..d6bb74f 100644
--- a/src/Addins/Backends/Rtm/RtmTask.cs
+++ b/src/Addins/Backends/Rtm/RtmTask.cs
@@ -5,7 +5,7 @@ using System;
 using RtmNet;
 using System.Collections.Generic;
 
-namespace Tasque.Backends.RtmBackend
+namespace Tasque.Backends.Rtm
 {
 	public class RtmTask : AbstractTask
 	{
@@ -61,8 +61,10 @@ namespace Tasque.Backends.RtmBackend
 			get { return taskSeries.Name; }
 			set {
 				if (value != null) {
+					OnPropertyChanging ("Name");
 					taskSeries.Name = value.Trim ();
 					rtmBackend.UpdateTaskName(this);
+					OnPropertyChanged ("CompletionDate");
 				}
 			}
 		}
@@ -73,9 +75,11 @@ namespace Tasque.Backends.RtmBackend
 		public override DateTime DueDate
 		{
 			get { return task.Due; }
-			set { 
+			set {
+				OnPropertyChanging ("DueDate");
 				task.Due = value;
-				rtmBackend.UpdateTaskDueDate(this);			
+				rtmBackend.UpdateTaskDueDate(this);
+				OnPropertyChanged ("CompletionDate");
 			}
 		}
 		
@@ -100,9 +104,10 @@ namespace Tasque.Backends.RtmBackend
 		public override DateTime CompletionDate
 		{
 			get { return task.Completed; }
-			set { 
+			set {
+				OnPropertyChanging ("CompletionDate");
 				task.Completed = value;
-				rtmBackend.UpdateTaskCompleteDate(this);
+				OnPropertyChanged ("CompletionDate");
 			}
 		}
 		
@@ -133,6 +138,7 @@ namespace Tasque.Backends.RtmBackend
 				}
 			}
 			set {
+				OnPropertyChanging ("Priority");
 				switch (value) {
 					default:
 					case TaskPriority.None:
@@ -148,7 +154,8 @@ namespace Tasque.Backends.RtmBackend
 						task.Priority = "3";
 						break;
 				}
-				rtmBackend.UpdateTaskPriority(this);				
+				rtmBackend.UpdateTaskPriority(this);
+				OnPropertyChanged ("CompletionDate");
 			}
 		}
 		
@@ -189,8 +196,10 @@ namespace Tasque.Backends.RtmBackend
 		{
 			get { return category; } 
 			set {
+				OnPropertyChanging ("Category");
 				RtmCategory rtmCategory = value as RtmCategory;
-				rtmBackend.MoveTaskCategory(this, rtmCategory.ID);				
+				rtmBackend.MoveTaskCategory(this, rtmCategory.ID);
+				OnPropertyChanged ("CompletionDate");
 			}
 		}
 		
@@ -239,8 +248,7 @@ namespace Tasque.Backends.RtmBackend
 		{
 			Logger.Debug("Activating Task: " + Name);
 			state = TaskState.Active;
-			task.Completed = DateTime.MinValue;
-			rtmBackend.UpdateTaskActive(this);
+			CompletionDate = DateTime.MinValue;
 		}
 		
 		/// <summary>
@@ -250,8 +258,7 @@ namespace Tasque.Backends.RtmBackend
 		{
 			Logger.Debug("Inactivating Task: " + Name);		
 			state = TaskState.Inactive;
-			task.Completed = DateTime.Now;
-			rtmBackend.UpdateTaskInactive(this);
+			CompletionDate = DateTime.Now;
 		}
 		
 		/// <summary>
@@ -261,9 +268,8 @@ namespace Tasque.Backends.RtmBackend
 		{
 			Logger.Debug("Completing Task: " + Name);			
 			state = TaskState.Completed;
-			if(task.Completed == DateTime.MinValue)
-				task.Completed = DateTime.Now;
-			rtmBackend.UpdateTaskCompleted(this);
+			if(CompletionDate == DateTime.MinValue)
+				CompletionDate = DateTime.Now;
 		}
 		
 		/// <summary>
@@ -272,7 +278,7 @@ namespace Tasque.Backends.RtmBackend
 		public override void Delete ()
 		{
 			state = TaskState.Deleted;
-			rtmBackend.UpdateTaskDeleted(this);
+			rtmBackend.DeleteTask (this);
 		}
 		
 		/// <summary>
diff --git a/src/Addins/Backends/Sqlite/SqliteBackend.cs b/src/Addins/Backends/Sqlite/SqliteBackend.cs
index 6c6d50d..49201e5 100644
--- a/src/Addins/Backends/Sqlite/SqliteBackend.cs
+++ b/src/Addins/Backends/Sqlite/SqliteBackend.cs
@@ -1,27 +1,30 @@
 // SqliteBackend.cs created with MonoDevelop
 // User: boyd at 7:10 AMÂ2/11/2008
-
 using System;
 using System.Collections.Generic;
-using Mono.Unix;
-using Tasque.Backends;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
 using Mono.Data.Sqlite;
+using Tasque.Backends;
 using Gtk.Tasque.Backends.Sqlite;
 
 namespace Tasque.Backends.Sqlite
 {
 	public class SqliteBackend : IBackend
 	{
-		private Dictionary<int, Gtk.TreeIter> taskIters;
-		private Gtk.TreeStore taskStore;
-		private Gtk.TreeModelSort sortedTasksModel;
 		private bool initialized;
 		private bool configured = true;
+
+		ObservableCollection<ITask> taskStore;
+		ObservableCollection<ICategory> categoryListStore;
+		ReadOnlyObservableCollection<ITask> readOnlyTaskStore;
+		ReadOnlyObservableCollection<ICategory> readOnlyCategoryStore;
 		
-		private Database db;
+		TaskComparer taskComparer;
+		CategoryComparer categoryComparer;
 		
-		private Gtk.ListStore categoryListStore;
-		private Gtk.TreeModelSort sortedCategoriesModel;
+		private Database db;
 
 		public event BackendInitializedHandler BackendInitialized;
 		public event BackendSyncStartedHandler BackendSyncStarted;
@@ -34,18 +37,13 @@ namespace Tasque.Backends.Sqlite
 		public SqliteBackend ()
 		{
 			initialized = false;
-			taskIters = new Dictionary<int, Gtk.TreeIter> (); 
-			taskStore = new Gtk.TreeStore (typeof (ITask));
-			
-			sortedTasksModel = new Gtk.TreeModelSort (taskStore);
-			sortedTasksModel.SetSortFunc (0, new Gtk.TreeIterCompareFunc (CompareTasksSortFunc));
-			sortedTasksModel.SetSortColumnId (0, Gtk.SortType.Ascending);
-			
-			categoryListStore = new Gtk.ListStore (typeof (ICategory));
-			
-			sortedCategoriesModel = new Gtk.TreeModelSort (categoryListStore);
-			sortedCategoriesModel.SetSortFunc (0, new Gtk.TreeIterCompareFunc (CompareCategorySortFunc));
-			sortedCategoriesModel.SetSortColumnId (0, Gtk.SortType.Ascending);
+			taskStore = new ObservableCollection<ITask> ();
+			categoryListStore = new ObservableCollection<ICategory> ();
+			readOnlyTaskStore = new ReadOnlyObservableCollection<ITask> (taskStore);
+			readOnlyCategoryStore
+				= new ReadOnlyObservableCollection<ICategory> (categoryListStore);
+			taskComparer = new TaskComparer ();
+			categoryComparer = new CategoryComparer ();
 		}
 		
 		#region Public Properties
@@ -57,17 +55,17 @@ namespace Tasque.Backends.Sqlite
 		/// <value>
 		/// All the tasks including ITaskDivider items.
 		/// </value>
-		public Gtk.TreeModel Tasks
+		public ICollection<ITask> Tasks
 		{
-			get { return sortedTasksModel; }
+			get { return readOnlyTaskStore; }
 		}
 		
 		/// <value>
 		/// This returns all the task lists (categories) that exist.
 		/// </value>
-		public Gtk.TreeModel Categories
+		public ICollection<ICategory> Categories
 		{
-			get { return sortedCategoriesModel; }
+			get { return readOnlyCategoryStore; }
 		}
 		
 		/// <value>
@@ -113,9 +111,8 @@ namespace Tasque.Backends.Sqlite
 			else
 				task.Category = category;
 			
-			Gtk.TreeIter iter = taskStore.AppendNode ();
-			taskStore.SetValue (iter, 0, task);
-			taskIters [task.SqliteId] = iter;
+			AddTask (task);
+			task.PropertyChanged += HandlePropertyChanged;
 			
 			return task;
 		}
@@ -145,14 +142,11 @@ namespace Tasque.Backends.Sqlite
 			// Add in the "All" Category
 			//
 			AllCategory allCategory = new Tasque.AllCategory (preferences);
-			Gtk.TreeIter iter = categoryListStore.Append ();
-			categoryListStore.SetValue (iter, 0, allCategory);
-			
-			
+			AddCategory (allCategory);
+
 			RefreshCategories();
 			RefreshTasks();		
 
-		
 			initialized = true;
 			if(BackendInitialized != null) {
 				BackendInitialized();
@@ -163,7 +157,6 @@ namespace Tasque.Backends.Sqlite
 		{
 			this.categoryListStore.Clear();
 			this.taskStore.Clear();
-			this.taskIters.Clear();
 
 			if (db != null)
 				db.Close();
@@ -182,68 +175,8 @@ namespace Tasque.Backends.Sqlite
 		}
 		
 		#endregion // Public Methods
-		
-		#region Private Methods
-		static int CompareTasksSortFunc (Gtk.TreeModel model,
-										 Gtk.TreeIter a,
-										 Gtk.TreeIter b)
-		{
-			ITask taskA = model.GetValue (a, 0) as ITask;
-			ITask taskB = model.GetValue (b, 0) as ITask;
-			
-			if (taskA == null || taskB == null)
-				return 0;
-			
-			return (taskA.CompareTo (taskB));
-		}
-		
-		static int CompareCategorySortFunc (Gtk.TreeModel model,
-											Gtk.TreeIter a,
-											Gtk.TreeIter b)
-		{
-			ICategory categoryA = model.GetValue (a, 0) as ICategory;
-			ICategory categoryB = model.GetValue (b, 0) as ICategory;
-			
-			if (categoryA == null || categoryB == null)
-				return 0;
-			
-			if (categoryA is Tasque.AllCategory)
-				return -1;
-			else if (categoryB is Tasque.AllCategory)
-				return 1;
-			
-			return (categoryA.Name.CompareTo (categoryB.Name));
-		}
-		
-		public void UpdateTask (SqliteTask task)
-		{
-			// Set the task in the store so the model will update the UI.
-			Gtk.TreeIter iter;
-			
-			if (!taskIters.ContainsKey (task.SqliteId))
-				return;
-				
-			iter = taskIters [task.SqliteId];
-			
-			if (task.State == TaskState.Deleted) {
-				taskIters.Remove (task.SqliteId);
-				if (!taskStore.Remove (ref iter)) {
-					Logger.Debug ("Successfully deleted from taskStore: {0}",
-						task.Name);
-				} else {
-					Logger.Debug ("Problem removing from taskStore: {0}",
-						task.Name);
-				}
-			} else {
-				taskStore.SetValue (iter, 0, task);
-			}
-		}
-		
-		
-		
 		public void RefreshCategories()
 		{
-			Gtk.TreeIter iter;
 			SqliteCategory newCategory;
 			bool hasValues = false;
 			
@@ -258,8 +191,7 @@ namespace Tasque.Backends.Sqlite
 				newCategory = new SqliteCategory (this, id);
 				if( (defaultCategory == null) || (newCategory.Name.CompareTo("Work") == 0) )
 					defaultCategory = newCategory;
-				iter = categoryListStore.Append ();
-				categoryListStore.SetValue (iter, 0, newCategory);				
+				AddCategory (newCategory);
 			}
 			
 			dataReader.Close();
@@ -268,30 +200,24 @@ namespace Tasque.Backends.Sqlite
 			if(!hasValues)
 			{
 				defaultCategory = newCategory = new SqliteCategory (this, "Work");
-				iter = categoryListStore.Append ();
-				categoryListStore.SetValue (iter, 0, newCategory);
+				AddCategory (defaultCategory);
 
 				newCategory = new SqliteCategory (this, "Personal");
-				iter = categoryListStore.Append ();
-				categoryListStore.SetValue (iter, 0, newCategory);
+				AddCategory (newCategory);
 				
 				newCategory = new SqliteCategory (this, "Family");
-				iter = categoryListStore.Append ();
-				categoryListStore.SetValue (iter, 0, newCategory);		
+				AddCategory (newCategory);
 
 				newCategory = new SqliteCategory (this, "Project");
-				iter = categoryListStore.Append ();
-				categoryListStore.SetValue (iter, 0, newCategory);		
+				AddCategory (newCategory);
 			}
 		}
-		
 
 		public void RefreshTasks()
 		{
-			Gtk.TreeIter iter;
 			SqliteTask newTask;
 			bool hasValues = false;
-
+			
 			string command = "SELECT id,Category,Name,DueDate,CompletionDate,Priority, State FROM Tasks";
 			SqliteCommand cmd = db.Connection.CreateCommand();
 			cmd.CommandText = command;
@@ -304,32 +230,65 @@ namespace Tasque.Backends.Sqlite
 				long completionDate = dataReader.GetInt64(4);
 				int priority = dataReader.GetInt32(5);
 				int state = dataReader.GetInt32(6);
-
+				
 				hasValues = true;
-
+				
 				newTask = new SqliteTask(this, id, category,
 				                         name, dueDate, completionDate,
 				                         priority, state);
-				iter = taskStore.AppendNode();
-				taskStore.SetValue (iter, 0, newTask);
-				taskIters [newTask.SqliteId] = iter;
+				AddTask (newTask);
+				newTask.PropertyChanged += HandlePropertyChanged;
 			}
-
+			
 			dataReader.Close();
 			cmd.Dispose();
-
+			
 			if(!hasValues)
 			{
 				newTask = new SqliteTask (this, "Create some tasks");
 				newTask.Category = defaultCategory;
 				newTask.DueDate = DateTime.Now;
 				newTask.Priority = TaskPriority.Medium;
-				iter = taskStore.AppendNode ();
-				taskStore.SetValue (iter, 0, newTask);	
-				taskIters [newTask.SqliteId] = iter;
+				AddTask (newTask);
+				newTask.PropertyChanged += HandlePropertyChanged;
 			}
 		}
 
+		#region Private Methods
+		internal void DeleteTask (SqliteTask task)
+		{
+			if (taskStore.Remove (task))
+				task.PropertyChanged -= HandlePropertyChanged;
+		}
+
+		void AddCategory (ICategory category)
+		{
+			var index = categoryListStore.Count;
+			var valIdx = categoryListStore.Select ((val, idx) => new { val, idx })
+				.FirstOrDefault (x => categoryComparer.Compare (x.val, category) > 0);
+			if (valIdx != null)
+				index = valIdx.idx;
+			categoryListStore.Insert (index, category);
+		}
+		
+		void AddTask (SqliteTask task)
+		{
+			var index = taskStore.Count;
+			var valIdx = taskStore.Select ((val, idx) => new { val, idx })
+				.FirstOrDefault (t => taskComparer.Compare (t.val, task) > 0);
+			if (valIdx != null)
+				index = valIdx.idx;
+			
+			taskStore.Insert (index, task);
+		}
+
+		void HandlePropertyChanged (object sender, PropertyChangedEventArgs e)
+		{
+			// when a property changes (any property atm), "reorder" tasks
+			var task = (SqliteTask)sender;
+			if (taskStore.Remove (task))
+				AddTask (task);
+		}
 		#endregion // Private Methods
 		
 		#region Event Handlers
diff --git a/src/Addins/Backends/Sqlite/SqliteBackend.csproj b/src/Addins/Backends/Sqlite/SqliteBackend.csproj
index ebee3af..c762a43 100644
--- a/src/Addins/Backends/Sqlite/SqliteBackend.csproj
+++ b/src/Addins/Backends/Sqlite/SqliteBackend.csproj
@@ -77,16 +77,13 @@
       <Private>False</Private>
       <Package>gtk-sharp-2.0</Package>
     </Reference>
+    <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\..\libtasque\libtasque.csproj">
       <Project>{784C9AA8-2B28-400B-8CC4-DCDC48CA37F0}</Project>
       <Name>libtasque</Name>
     </ProjectReference>
-    <ProjectReference Include="..\..\..\Gtk.Tasque\Gtk.Tasque.csproj">
-      <Project>{B19B9840-669D-4984-9772-E1F55193A67F}</Project>
-      <Name>Gtk.Tasque</Name>
-    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="SqliteTask.cs" />
diff --git a/src/Addins/Backends/Sqlite/SqliteTask.cs b/src/Addins/Backends/Sqlite/SqliteTask.cs
index 9ede1b1..d262fd7 100644
--- a/src/Addins/Backends/Sqlite/SqliteTask.cs
+++ b/src/Addins/Backends/Sqlite/SqliteTask.cs
@@ -65,11 +65,12 @@ namespace Tasque.Backends.Sqlite
 		{
 			get { return this.name; }
 			set {
+				OnPropertyChanging ("Name");
 				string name = backend.SanitizeText (value);
 				this.name = name;
 				string command = String.Format("UPDATE Tasks set Name='{0}' where ID='{1}'", name, id);
 				backend.Database.ExecuteScalar(command);
-				backend.UpdateTask(this);
+				OnPropertyChanged ("Name");
 			}
 		}
 		
@@ -77,10 +78,11 @@ namespace Tasque.Backends.Sqlite
 		{
 			get { return Database.ToDateTime(this.dueDate); }
 			set {
+				OnPropertyChanging ("DueDate");
 			        this.dueDate = Database.FromDateTime(value);
 				string command = String.Format("UPDATE Tasks set DueDate='{0}' where ID='{1}'", this.dueDate, id);
 				backend.Database.ExecuteScalar(command);
-				backend.UpdateTask(this);				
+				OnPropertyChanged ("DueDate");
 			}
 		}
 		
@@ -89,12 +91,13 @@ namespace Tasque.Backends.Sqlite
 		{
 			get { return Database.ToDateTime(this.completionDate); }
 			set {
+				OnPropertyChanging ("CompletionDate");
 				this.completionDate = Database.FromDateTime(value);
 				string command = String.Format("UPDATE Tasks set CompletionDate='{0}' where ID='{1}'", this.completionDate, id);
 				backend.Database.ExecuteScalar(command);
-				backend.UpdateTask(this);
+				OnPropertyChanged ("CompletionDate");
 			}
-		}		
+		}
 		
 		
 		public override bool IsComplete
@@ -111,10 +114,11 @@ namespace Tasque.Backends.Sqlite
 		{
 			get { return (TaskPriority) this.priority; }
 			set {
+				OnPropertyChanging ("Priority");
 				this.priority = (int) value;
 				string command = String.Format("UPDATE Tasks set Priority='{0}' where ID='{1}'", this.priority, id);
 				backend.Database.ExecuteScalar(command);
-				backend.UpdateTask(this);
+				OnPropertyChanged ("Priority");
 			}
 		}
 
@@ -143,7 +147,6 @@ namespace Tasque.Backends.Sqlite
 				this.state = (int) value;
 				string command = String.Format("UPDATE Tasks set State='{0}' where ID='{1}'", this.state, id);
 				backend.Database.ExecuteScalar(command);
-				backend.UpdateTask(this);
 			}
 		}
 
@@ -151,10 +154,11 @@ namespace Tasque.Backends.Sqlite
 		{
 			get { return new SqliteCategory(backend, this.category); }
 			set {
+				OnPropertyChanging ("Category");
 				this.category = (int)(value as SqliteCategory).ID;
  				string command = String.Format("UPDATE Tasks set Category='{0}' where ID='{1}'", category, id);
 				backend.Database.ExecuteScalar(command);
-				backend.UpdateTask(this);
+				OnPropertyChanged ("Category");
 			}
 		}
 		
@@ -183,32 +187,29 @@ namespace Tasque.Backends.Sqlite
 		public override void Activate ()
 		{
 			// Logger.Debug ("SqliteTask.Activate ()");
-			CompletionDate = DateTime.MinValue;
 			LocalState = TaskState.Active;
-			backend.UpdateTask (this);
+			CompletionDate = DateTime.MinValue;
 		}
 		
 		public override void Inactivate ()
 		{
 			// Logger.Debug ("SqliteTask.Inactivate ()");
-			CompletionDate = DateTime.Now;
 			LocalState = TaskState.Inactive;
-			backend.UpdateTask (this);
+			CompletionDate = DateTime.Now;
 		}
 		
 		public override void Complete ()
 		{
 			//Logger.Debug ("SqliteTask.Complete ()");
-			CompletionDate = DateTime.Now;
 			LocalState = TaskState.Completed;
-			backend.UpdateTask (this);
+			CompletionDate = DateTime.Now;
 		}
 		
 		public override void Delete ()
 		{
 			//Logger.Debug ("SqliteTask.Delete ()");
 			LocalState = TaskState.Deleted;
-			backend.UpdateTask (this);
+			backend.DeleteTask (this);
 		}
 		
 		public override INote CreateNote(string text)
diff --git a/src/Gtk.Tasque/Application.cs b/src/Gtk.Tasque/Application.cs
new file mode 100644
index 0000000..d145622
--- /dev/null
+++ b/src/Gtk.Tasque/Application.cs
@@ -0,0 +1,527 @@
+/***************************************************************************
+ *  Application.cs
+ *
+ *  Copyright (C) 2008 Novell, Inc.
+ *  Written by Calvin Gaisford <calvinrg gmail com>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  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.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Xml;
+using System.Net.Sockets;
+
+using Gtk;
+using Gdk;
+using Mono.Unix;
+using Mono.Unix.Native;
+#if ENABLE_NOTIFY_SHARP
+using Notifications;
+#endif
+using Tasque.Backends;
+
+namespace Tasque
+{
+	public class Application
+	{
+		private static Tasque.Application application = null;
+		private static System.Object locker = new System.Object();
+		private bool initialized;
+		private INativeApplication nativeApp;
+#if !WIN && !OSX
+		private RemoteControl remoteControl;
+#endif
+		private Gdk.Pixbuf normalPixBuf;
+		private Gtk.Image trayImage;
+		private GtkTray trayIcon;
+		private Preferences preferences;
+		private EventBox eb;
+		private IBackend backend;
+		private PreferencesDialog preferencesDialog;
+		private bool quietStart = false;
+		
+		private DateTime currentDay = DateTime.Today;
+		
+		/// <value>
+		/// Keep track of the available backends.  The key is the Type name of
+		/// the backend.
+		/// </value>
+		private Dictionary<string, IBackend> availableBackends;
+		
+		private IBackend customBackend;
+
+		public static IBackend Backend
+		{ 
+			get { return Application.Instance.backend; }
+			set { Application.Instance.SetBackend (value); }
+		}
+		
+		public static List<IBackend> AvailableBackends
+		{
+			get {
+				return new List<IBackend> (Application.Instance.availableBackends.Values);
+			}
+//			get { return Application.Instance.availableBackends; }
+		}
+		
+		public static Application Instance
+		{
+			get {
+				lock(locker) {
+					return application;
+				}
+			}
+		}
+
+		public INativeApplication NativeApplication
+		{
+			get
+			{
+				return nativeApp;
+			}
+		}
+
+		public event EventHandler BackendChanged;
+
+		void OnBackendChanged ()
+		{
+			if (BackendChanged != null)
+				BackendChanged (this, EventArgs.Empty);
+		}
+		
+		public TaskGroupModel OverdueTasks { get; private set; }
+		
+		public TaskGroupModel TodayTasks { get; private set; }
+		
+		public TaskGroupModel TomorrowTasks { get; private set; }
+		
+		public static Preferences Preferences
+		{
+			get { return Application.Instance.preferences; }
+		}
+
+		public Application (INativeApplication nativeApp)
+		{
+			if (nativeApp == null)
+				throw new ArgumentNullException ("nativeApp");
+			this.nativeApp = nativeApp;
+			application = this;
+		}
+
+		public void Init (string[] args)
+		{
+			lock (locker) {
+				if (initialized)
+					return;
+				initialized = true;
+			}
+
+			nativeApp.Initialize (
+				Defines.LocaleDir,
+				"Tasque",
+				"Tasque",
+				args);
+
+			preferences = new Preferences (nativeApp.ConfDir);
+			
+#if !WIN && !OSX
+			// Register Tasque RemoteControl
+			try {
+				remoteControl = RemoteControlProxy.Register ();
+				if (remoteControl != null) {
+					Logger.Debug ("Tasque remote control active.");
+				} else {
+					// If Tasque is already running, open the tasks window
+					// so the user gets some sort of feedback when they
+					// attempt to run Tasque again.
+					RemoteControl remote = null;
+					try {
+						remote = RemoteControlProxy.GetInstance ();
+						remote.ShowTasks ();
+					} catch {}
+
+					Logger.Debug ("Tasque is already running.  Exiting...");
+					System.Environment.Exit (0);
+				}
+			} catch (Exception e) {
+				Logger.Debug ("Tasque remote control disabled (DBus exception): {0}",
+				            e.Message);
+			}
+#endif
+			
+			string potentialBackendClassName = null;
+			
+			for (int i = 0; i < args.Length; i++) {
+				switch (args [i]) {
+					
+				case "--quiet":
+					quietStart = true;
+					Logger.Debug ("Starting quietly");
+					break;
+					
+				case "--backend":
+					if ( (i + 1 < args.Length) &&
+					    !string.IsNullOrEmpty (args [i + 1]) &&
+					    args [i + 1] [0] != '-') {
+						potentialBackendClassName = args [++i];
+					} // TODO: Else, print usage
+					break;
+					
+				default:
+					// Support old argument behavior
+					if (!string.IsNullOrEmpty (args [i]))
+						potentialBackendClassName = args [i];
+					break;
+				}
+			}
+			
+			// See if a specific backend is specified
+			if (potentialBackendClassName != null) {
+				Logger.Debug ("Backend specified: " +
+				              potentialBackendClassName);
+				
+				customBackend = null;
+				Assembly asm = Assembly.GetCallingAssembly ();
+				try {
+					customBackend = (IBackend)
+						asm.CreateInstance (potentialBackendClassName);
+				} catch (Exception e) {
+					Logger.Warn ("Backend specified on args not found: {0}\n\t{1}",
+						potentialBackendClassName, e.Message);
+				}
+			}
+			
+			// Discover all available backends
+			LoadAvailableBackends ();
+
+			GLib.Idle.Add(InitializeIdle);
+			GLib.Timeout.Add (60000, CheckForDaySwitch);
+		}
+		
+		/// <summary>
+		/// Load all the available backends that Tasque can find.  First look in
+		/// Tasque.exe and then for other DLLs in the same directory Tasque.ex
+		/// resides.
+		/// </summary>
+		private void LoadAvailableBackends ()
+		{
+			availableBackends = new Dictionary<string,IBackend> ();
+			
+			List<IBackend> backends = new List<IBackend> ();
+			
+			Assembly tasqueAssembly = Assembly.GetCallingAssembly ();
+			
+			// Look for other backends in Tasque.exe
+			backends.AddRange (GetBackendsFromAssembly (tasqueAssembly));
+			
+			// Look through the assemblies located in the same directory as
+			// Tasque.exe.
+			Logger.Debug ("Tasque.exe location:  {0}", tasqueAssembly.Location);
+			
+			DirectoryInfo loadPathInfo =
+				Directory.GetParent (tasqueAssembly.Location);
+			Logger.Info ("Searching for Backend DLLs in: {0}", loadPathInfo.FullName);
+			
+			foreach (FileInfo fileInfo in loadPathInfo.GetFiles ("*.dll")) {
+				Logger.Info ("\tReading {0}", fileInfo.FullName);
+				Assembly asm = null;
+				try {
+					asm = Assembly.LoadFile (fileInfo.FullName);
+				} catch (Exception e) {
+					Logger.Debug ("Exception loading {0}: {1}",
+								  fileInfo.FullName,
+								  e.Message);
+					continue;
+				}
+				
+				backends.AddRange (GetBackendsFromAssembly (asm));
+			}
+			
+			foreach (IBackend backend in backends) {
+				string typeId = backend.GetType ().ToString ();
+				if (availableBackends.ContainsKey (typeId))
+					continue;
+				
+				Logger.Debug ("Storing '{0}' = '{1}'", typeId, backend.Name);
+				availableBackends [typeId] = backend;
+			}
+		}
+		
+		private List<IBackend> GetBackendsFromAssembly (Assembly asm)
+		{
+			List<IBackend> backends = new List<IBackend> ();
+			
+			Type[] types = null;
+			
+			try {
+				types = asm.GetTypes ();
+			} catch (Exception e) {
+				Logger.Warn ("Exception reading types from assembly '{0}': {1}",
+					asm.ToString (), e.Message);
+				return backends;
+			}
+			foreach (Type type in types) {
+				if (!type.IsClass) {
+					continue; // Skip non-class types
+				}
+				if (type.GetInterface ("Tasque.Backends.IBackend") == null) {
+					continue;
+				}
+				Logger.Debug ("Found Available Backend: {0}", type.ToString ());
+				
+				IBackend availableBackend = null;
+				try {
+					availableBackend = (IBackend)
+						asm.CreateInstance (type.ToString ());
+				} catch (Exception e) {
+					Logger.Warn ("Could not instantiate {0}: {1}",
+								 type.ToString (),
+								 e.Message);
+					continue;
+				}
+				
+				if (availableBackend != null) {
+					backends.Add (availableBackend);
+				}
+			}
+			
+			return backends;
+		}
+
+		private void SetBackend (IBackend value)
+		{
+			bool changingBackend = false;
+			if (this.backend != null) {
+				UnhookFromTooltipTaskGroupModels ();
+				changingBackend = true;
+				// Cleanup the old backend
+				try {
+					Logger.Debug ("Cleaning up backend: {0}",
+					              this.backend.Name);
+					this.backend.Cleanup ();
+				} catch (Exception e) {
+					Logger.Warn ("Exception cleaning up '{0}': {1}",
+					             this.backend.Name,
+					             e);
+				}
+			}
+				
+			// Initialize the new backend
+			var oldBackend = backend;
+			this.backend = value;
+			if (this.backend == null) {
+				if (trayIcon != null)
+					trayIcon.RefreshTrayIconTooltip ();
+				return;
+			}
+				
+			Logger.Info ("Using backend: {0} ({1})",
+			             this.backend.Name,
+			             this.backend.GetType ().ToString ());
+			this.backend.Initialize (preferences);
+			
+			if (!changingBackend) {
+				TaskWindow.Reinitialize (!this.quietStart);
+			} else {
+				TaskWindow.Reinitialize (true);
+			}
+
+			RebuildTooltipTaskGroupModels ();
+			if (trayIcon != null)
+				trayIcon.RefreshTrayIconTooltip ();
+			
+			Logger.Debug("Configuration status: {0}",
+			             this.backend.Configured.ToString());
+
+			if (backend != oldBackend)
+				OnBackendChanged ();
+		}
+
+		private bool InitializeIdle()
+		{
+			if (customBackend != null) {
+				Application.Backend = customBackend;
+			} else {
+				// Check to see if the user has a preference of which backend
+				// to use.  If so, use it, otherwise, pop open the preferences
+				// dialog so they can choose one.
+				string backendTypeString = Preferences.Get (Preferences.CurrentBackend);
+				Logger.Debug ("CurrentBackend specified in Preferences: {0}", backendTypeString);
+				if (backendTypeString != null
+						&& availableBackends.ContainsKey (backendTypeString)) {
+					Application.Backend = availableBackends [backendTypeString];
+				}
+			}
+			
+			trayIcon = GtkTray.CreateTray ();
+			
+			if (backend == null) {
+				// Pop open the preferences dialog so the user can choose a
+				// backend service to use.
+				Application.ShowPreferences ();
+			} else if (!quietStart) {
+				TaskWindow.ShowWindow ();
+			}
+			if (backend == null || !backend.Configured){
+				GLib.Timeout.Add(1000, new GLib.TimeoutHandler(RetryBackend));
+			}
+
+			nativeApp.InitializeIdle ();
+			
+			return false;
+		}
+		private bool RetryBackend(){
+			try {
+				if (backend != null && !backend.Configured) {
+					backend.Cleanup();
+					backend.Initialize (preferences);
+				}
+			} catch (Exception e) {
+				Logger.Error("{0}", e.Message);
+			}
+			if (backend == null || !backend.Configured) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+		
+		private bool CheckForDaySwitch ()
+		{
+			if (DateTime.Today != currentDay) {
+				Logger.Debug ("Day has changed, reloading tasks");
+				currentDay = DateTime.Today;
+				// Reinitialize window according to new date
+				if (TaskWindow.IsOpen)
+					TaskWindow.Reinitialize (true);
+				
+				UnhookFromTooltipTaskGroupModels ();
+				RebuildTooltipTaskGroupModels ();
+				if (trayIcon != null)
+					trayIcon.RefreshTrayIconTooltip ();
+			}
+			
+			return true;
+		}
+
+		private void UnhookFromTooltipTaskGroupModels ()
+		{
+			foreach (TaskGroupModel model in new TaskGroupModel[] { OverdueTasks, TodayTasks, TomorrowTasks })
+			{
+				if (model == null) {
+					continue;
+				}
+				
+				model.CollectionChanged -= OnTooltipModelChanged;
+			}
+		}
+
+		private void OnTooltipModelChanged (object o, EventArgs args)
+		{
+			if (trayIcon != null)
+				trayIcon.RefreshTrayIconTooltip ();
+		}
+
+		private void RebuildTooltipTaskGroupModels ()
+		{
+			if (backend == null || backend.Tasks == null) {
+				OverdueTasks = null;
+				TodayTasks = null;
+				TomorrowTasks = null;
+				
+				return;
+			}
+
+			OverdueTasks = TaskGroupModelFactory.CreateOverdueModel (backend.Tasks);
+			TodayTasks = TaskGroupModelFactory.CreateTodayModel (backend.Tasks);
+			TomorrowTasks = TaskGroupModelFactory.CreateTomorrowModel (backend.Tasks);
+
+			foreach (TaskGroupModel model in new TaskGroupModel[] { OverdueTasks, TodayTasks, TomorrowTasks })
+			{
+				if (model == null) {
+					continue;
+				}
+				
+				model.CollectionChanged += OnTooltipModelChanged;
+			}
+		}
+		
+		private void OnPreferencesDialogHidden (object sender, EventArgs args)
+		{
+			preferencesDialog.Destroy ();
+			preferencesDialog = null;
+		}
+		
+		public static void ShowPreferences ()
+		{
+			Logger.Info ("ShowPreferences called");
+			var app = application;
+			if (app.preferencesDialog == null) {
+				app.preferencesDialog = new PreferencesDialog ();
+				app.preferencesDialog.Hidden += app.OnPreferencesDialogHidden;
+			}
+			
+			app.preferencesDialog.Present ();
+		}
+
+#if ENABLE_NOTIFY_SHARP
+		public static void ShowAppNotification(Notification notification)
+		{
+			// TODO: Use this API for newer versions of notify-sharp
+			//notification.AttachToStatusIcon(
+			//		Tasque.Application.Instance.trayIcon);
+			notification.Show();
+		}
+#endif
+
+		public void StartMainLoop ()
+		{
+			nativeApp.StartMainLoop ();
+		}
+
+		public void Quit ()
+		{
+			Logger.Info ("Quit called - terminating application");
+			if (backend != null) {
+				UnhookFromTooltipTaskGroupModels ();
+				backend.Cleanup ();
+			}
+			TaskWindow.SavePosition ();
+
+			nativeApp.QuitMainLoop ();
+		}
+
+		public void Exit (int exitCode)
+		{
+			if (nativeApp != null)
+				nativeApp.Exit (exitCode);
+			else
+				Environment.Exit (exitCode);
+		}
+	}
+}
diff --git a/src/Gtk.Tasque/CompletedTaskGroup.cs b/src/Gtk.Tasque/CompletedTaskGroup.cs
index 4bcd71d..7213e28 100644
--- a/src/Gtk.Tasque/CompletedTaskGroup.cs
+++ b/src/Gtk.Tasque/CompletedTaskGroup.cs
@@ -5,8 +5,13 @@
 //
 
 using System;
-using Gtk;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
 using Mono.Unix;
+using Gtk;
+using Gtk.Tasque;
 
 namespace Tasque
 {
@@ -26,9 +31,8 @@ namespace Tasque
 		ShowCompletedRange currentRange;
 		
 		public CompletedTaskGroup (string groupName, DateTime rangeStart,
-								   DateTime rangeEnd, Gtk.TreeModel tasks, INativeApplication application)
-			: base (groupName, rangeStart, rangeEnd,
-					new CompletedTasksSortModel(tasks), application)
+		                           DateTime rangeEnd, ICollection<ITask> tasks, INativeApplication application)
+			: base (groupName, rangeStart, rangeEnd, new CompletedTasksSortModel(tasks), application)
 		{
 			// Don't hide this group when it's empty because then the range
 			// slider won't appear and the user won't be able to customize the
@@ -75,11 +79,12 @@ namespace Tasque
 			this.ExtraWidget = rangeSlider;
 		}
 
-		protected override TaskGroupModel CreateModel (DateTime rangeStart,
-		                                               DateTime rangeEnd,
-		                                               TreeModel tasks)
+		protected override TreeModel CreateModel (DateTime rangeStart, DateTime rangeEnd,
+		                                          ICollection<ITask> tasks)
 		{
-			return new CompletedTaskGroupModel (rangeStart, rangeEnd, tasks);
+			Model = new CompletedTaskGroupModel (rangeStart, rangeEnd,
+			                                     tasks, Application.Preferences);
+			return new TreeModelListAdapter<ITask> (Model);
 		}
 		
 		protected void OnSelectedCategorySettingChanged (
@@ -100,18 +105,8 @@ namespace Tasque
 			string cat = Application.Preferences.Get (
 							Preferences.SelectedCategoryKey);
 			if (cat != null) {
-				TreeIter iter;
-				TreeModel model = Application.Backend.Categories;
-				
-				if (model.GetIterFirst (out iter)) {
-					do {
-						ICategory category = model.GetValue (iter, 0) as ICategory;
-						if (category.Name.CompareTo (cat) == 0) {
-							foundCategory = category;
-							break;
-						}
-					} while (model.IterNext (ref iter));
-				}
+				var model = Application.Backend.Categories;
+				foundCategory = model.FirstOrDefault (c => c.Name == cat);
 			}
 			
 			return foundCategory;
@@ -211,29 +206,52 @@ namespace Tasque
 	/// completed tasks in reverse order (i.e., most recently completed tasks
 	/// at the top of the list).
 	/// </summary>
-	class CompletedTasksSortModel : Gtk.TreeModelSort
+	class CompletedTasksSortModel : ObservableCollection<ITask>
 	{
-		public CompletedTasksSortModel (Gtk.TreeModel childModel)
-			: base (childModel)
+		public CompletedTasksSortModel (ICollection<ITask> childModel)
 		{
-			SetSortFunc (0, new Gtk.TreeIterCompareFunc (CompareTasksSortFunc));
-			SetSortColumnId (0, Gtk.SortType.Descending);
+			originalTasks = childModel;
+			((INotifyCollectionChanged)childModel).CollectionChanged += HandleCollectionChanged;
+			foreach (var item in originalTasks)
+				AddTask (item);
 		}
 		
 		#region Private Methods
-		static int CompareTasksSortFunc (Gtk.TreeModel model,
-										 Gtk.TreeIter a,
-										 Gtk.TreeIter b)
+		void AddTask (ITask task)
 		{
-			ITask taskA = model.GetValue (a, 0) as ITask;
-			ITask taskB = model.GetValue (b, 0) as ITask;
+			var index = Count;
+			var valIdx = this.Select ((val, idx) => new { val, idx })
+				.FirstOrDefault (t => CompareTasksSortFunc (t.val, task) > 0);
+			if (valIdx != null)
+				index = valIdx.idx;
 			
-			if (taskA == null || taskB == null)
+			Insert (index, task);
+		}
+		
+		void HandleCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
+		{
+			//FIXME: Only add and remove actions are expected
+			switch (e.Action) {
+			case NotifyCollectionChangedAction.Add:
+				AddTask ((ITask)e.NewItems [0]);
+				break;
+			case NotifyCollectionChangedAction.Remove:
+				var task = ((ITask)e.OldItems [0]);
+				Remove (task);
+				break;
+			}
+		}
+		
+		int CompareTasksSortFunc (ITask x, ITask y)
+		{
+			if (x == null || y == null)
 				return 0;
 			
-			// Reverse the logic with the '!' so it's in re
-			return (taskA.CompareToByCompletionDate (taskB));
+			// Reverse the logic with the '-' so it's in reverse
+			return -(x.CompareToByCompletionDate (y));
 		}
 		#endregion // Private Methods
+		
+		ICollection<ITask> originalTasks;
 	}
 }
diff --git a/src/Gtk.Tasque/Gtk.Tasque.csproj b/src/Gtk.Tasque/Gtk.Tasque.csproj
index 2430b6f..01fdb69 100644
--- a/src/Gtk.Tasque/Gtk.Tasque.csproj
+++ b/src/Gtk.Tasque/Gtk.Tasque.csproj
@@ -72,6 +72,7 @@
     <Reference Include="dbus-sharp-glib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5675b0c3093115b5">
       <Private>False</Private>
     </Reference>
+    <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\libtasque\libtasque.csproj">
@@ -84,8 +85,6 @@
     <Compile Include="GtkTray.cs" />
     <Compile Include="StatusIconTray.cs" />
     <Compile Condition=" '$(AppIndicator)' != '' " Include="AppIndicatorTray.cs" />
-    <Compile Include="AbstractTask.cs" />
-    <Compile Include="AllCategory.cs" />
     <Compile Include="CellRendererDate.cs" />
     <Compile Include="CompletedTaskGroup.cs" />
     <Compile Include="DateButton.cs" />
@@ -105,6 +104,7 @@
     <Compile Include="GtkApplicationBase.cs" />
     <Compile Include="GtkLinuxApplication.cs" />
     <Compile Include="GtkWinApplication.cs" />
+    <Compile Include="TreeModelListAdapter.cs" />
   </ItemGroup>
   <ItemGroup Condition=" '$(Configuration)' == 'LinuxDebug' Or '$(Configuration)' == 'LinuxRelease' ">
     <Compile Include="RemoteControl.cs" />
diff --git a/src/Gtk.Tasque/GtkApplicationBase.cs b/src/Gtk.Tasque/GtkApplicationBase.cs
index af01121..d0511f8 100644
--- a/src/Gtk.Tasque/GtkApplicationBase.cs
+++ b/src/Gtk.Tasque/GtkApplicationBase.cs
@@ -202,19 +202,17 @@ namespace Tasque
 				return;
 			}
 			
-			OverdueTasks = TaskGroupModelFactory.CreateOverdueModel (Backend.Tasks);
-			TodayTasks = TaskGroupModelFactory.CreateTodayModel (Backend.Tasks);
-			TomorrowTasks = TaskGroupModelFactory.CreateTomorrowModel (Backend.Tasks);
+			OverdueTasks = TaskGroupModelFactory.CreateOverdueModel (Backend.Tasks, Preferences);
+			TodayTasks = TaskGroupModelFactory.CreateTodayModel (Backend.Tasks, Preferences);
+			TomorrowTasks = TaskGroupModelFactory.CreateTomorrowModel (Backend.Tasks, Preferences);
 			
 			foreach (TaskGroupModel model in new TaskGroupModel[] { OverdueTasks, TodayTasks, TomorrowTasks })
 			{
 				if (model == null) {
 					continue;
 				}
-				
-				model.RowInserted += OnTooltipModelChanged;
-				model.RowChanged += OnTooltipModelChanged;
-				model.RowDeleted += OnTooltipModelChanged;
+
+				model.CollectionChanged += OnTooltipModelChanged;
 			}
 		}
 
@@ -226,9 +224,7 @@ namespace Tasque
 					continue;
 				}
 				
-				model.RowInserted -= OnTooltipModelChanged;
-				model.RowChanged -= OnTooltipModelChanged;
-				model.RowDeleted -= OnTooltipModelChanged;
+				model.CollectionChanged -= OnTooltipModelChanged;
 			}
 		}
 		
diff --git a/src/Gtk.Tasque/GtkTray.cs b/src/Gtk.Tasque/GtkTray.cs
index 3801bea..a563628 100644
--- a/src/Gtk.Tasque/GtkTray.cs
+++ b/src/Gtk.Tasque/GtkTray.cs
@@ -24,6 +24,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 using System;
+using System.Linq;
 using System.Text;
 using Mono.Unix;
 using Gtk;
@@ -81,7 +82,7 @@ namespace Tasque
 			
 			var overdueTasks = application.OverdueTasks;
 			if (overdueTasks != null) {
-				int count = overdueTasks.IterNChildren ();
+				int count = overdueTasks.Count ();
 
 				if (count > 0) {
 					sb.Append (String.Format (Catalog.GetPluralString ("{0} task is Overdue\n", "{0} tasks are Overdue\n", count), count));
@@ -90,7 +91,7 @@ namespace Tasque
 			
 			var todayTasks = application.TodayTasks;
 			if (todayTasks != null) {
-				int count = todayTasks.IterNChildren ();
+				int count = todayTasks.Count ();
 
 				if (count > 0) {
 					sb.Append (String.Format (Catalog.GetPluralString ("{0} task for Today\n", "{0} tasks for Today\n", count), count));
@@ -99,7 +100,7 @@ namespace Tasque
 			
 			var tomorrowTasks = application.TomorrowTasks;
 			if (tomorrowTasks != null) {
-				int count = tomorrowTasks.IterNChildren ();
+				int count = tomorrowTasks.Count ();
 
 				if (count > 0) {
 					sb.Append (String.Format (Catalog.GetPluralString ("{0} task for Tomorrow\n", "{0} tasks for Tomorrow\n", count), count));
diff --git a/src/Gtk.Tasque/PreferencesDialog.cs b/src/Gtk.Tasque/PreferencesDialog.cs
index 36ac198..341245c 100644
--- a/src/Gtk.Tasque/PreferencesDialog.cs
+++ b/src/Gtk.Tasque/PreferencesDialog.cs
@@ -51,7 +51,7 @@ namespace Tasque
 		Dictionary<int, IBackend> backendComboMap; // track backends
 		int 					selectedBackend;
 		Gtk.CheckButton			showCompletedTasksCheckButton;
-		Gtk.TreeModelFilter		filteredCategories;
+		Gtk.ListStore   		filteredCategories;
 		List<string>			categoriesToHide;
 		Gtk.TreeView			categoriesTree;
 
@@ -598,8 +598,11 @@ namespace Tasque
 			}
 			
 			IBackend backend = backendComboMap [selectedBackend];
-			filteredCategories = new TreeModelFilter (backend.Categories, null);
-			filteredCategories.VisibleFunc = FilterFunc;
+			filteredCategories = new ListStore (typeof (ICategory));
+			foreach (var item in backend.Categories) {
+				if (!(item == null || item is AllCategory))
+					filteredCategories.AppendValues (item);
+			}
 			categoriesTree.Model = filteredCategories;
 		}
 		
@@ -607,27 +610,5 @@ namespace Tasque
 		{
 			RebuildCategoryTree ();
 		}
-		
-		/// <summary>
-		/// Filter out the AllCategory
-		/// </summary>
-		/// <param name="model">
-		/// A <see cref="Gtk.TreeModel"/>
-		/// </param>
-		/// <param name="iter">
-		/// A <see cref="Gtk.TreeIter"/>
-		/// </param>
-		/// <returns>
-		/// A <see cref="System.Boolean"/>
-		/// </returns>
-		protected bool FilterFunc (Gtk.TreeModel model,
-										   Gtk.TreeIter iter)
-		{
-			ICategory category = model.GetValue (iter, 0) as ICategory;
-			if (category == null || category is AllCategory)
-				return false;
-			
-			return true;
-		}
 	}
 }
diff --git a/src/Gtk.Tasque/RemoteControl.cs b/src/Gtk.Tasque/RemoteControl.cs
index 5e92b2c..2c310d2 100644
--- a/src/Gtk.Tasque/RemoteControl.cs
+++ b/src/Gtk.Tasque/RemoteControl.cs
@@ -6,6 +6,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
 
 using Mono.Unix; // for Catalog.GetString ()
 
@@ -115,8 +116,7 @@ namespace Tasque
 		public string CreateTask (string categoryName, string taskName,
 						bool enterEditMode, bool parseDate)
 		{
-			Gtk.TreeIter iter;
-			Gtk.TreeModel model = application.Backend.Categories;
+			var model = application.Backend.Categories;
 			
 			//
 			// Validate the input parameters.  Don't allow null or empty strings
@@ -130,18 +130,11 @@ namespace Tasque
 			//
 			// Look for the specified category
 			//
-			if (!model.GetIterFirst (out iter)) {
+			if (model.Count == 0) {
 				return string.Empty;
 			}
 			
-			ICategory category = null;
-			do {
-				ICategory tempCategory = model.GetValue (iter, 0) as ICategory;
-				if (tempCategory.Name.ToLower ().CompareTo (categoryName.ToLower ()) == 0) {
-					// Found a match
-					category = tempCategory;
-				}
-			} while (model.IterNext (ref iter));
+			ICategory category = model.FirstOrDefault (c => c.Name.ToLower () == categoryName.ToLower ());
 			
 			if (category == null) {
 				return string.Empty;
@@ -181,7 +174,7 @@ namespace Tasque
 					Catalog.GetString ("New task created."), // summary
 					Catalog.GetString (taskName), // body
 					Utilities.GetIcon ("tasque", 48));
-			Application.ShowAppNotification (notify);
+			application.ShowAppNotification (notify);
 			#endif
 			
 			
@@ -198,19 +191,16 @@ namespace Tasque
 		{
 			List<string> categories = new List<string> ();
 			string[] emptyArray = categories.ToArray ();
+
+			var model = application.Backend.Categories;
 			
-			Gtk.TreeIter iter;
-			Gtk.TreeModel model = application.Backend.Categories;
-			
-			if (!model.GetIterFirst (out iter))
+			if (model.Count == 0)
 				return emptyArray;
 			
-			do {
-				ICategory category = model.GetValue (iter, 0) as ICategory;
-				if (category is AllCategory)
-					continue; // Prevent the AllCategory from being returned
-				categories.Add (category.Name);
-			} while (model.IterNext (ref iter));
+			foreach (var item in model) {
+				if (!(item is AllCategory))
+					categories.Add (item.Name);
+			}
 			
 			return categories.ToArray ();
 		}
@@ -229,22 +219,14 @@ namespace Tasque
 		/// </returns>
 		public string[] GetTaskIds ()
 		{
-			Gtk.TreeIter iter;
-			Gtk.TreeModel model;
-			
-			ITask task;
-			List<string> ids;
-			
-			ids = new List<string> ();
-			model = application.Backend.Tasks;
-			
-			if (!model.GetIterFirst (out iter))
+			var ids = new List<string> ();
+			var model = application.Backend.Tasks;
+
+			if (model.Count == 0)
 				return new string[0];
-				
-			do {
-				task = model.GetValue (iter, 0) as ITask;
-				ids.Add (task.Id);
-			} while (model.IterNext (ref iter));
+			
+			foreach (var item in model)
+				ids.Add (item.Id);
 			
 			return ids.ToArray ();
 		}
@@ -324,20 +306,18 @@ namespace Tasque
 			{
 				return false;
 			}
-			Gtk.TreeIter iter;
-			Gtk.TreeModel model = application.Backend.Categories;
+
+			var model = application.Backend.Categories;
 			
-			if (!model.GetIterFirst (out iter))
+			if (model.Count == 0)
 				return false;
 			
-			do {
-				ICategory category = model.GetValue (iter, 0) as ICategory;
-				if (string.Compare(category.Name,categoryName)==0)
-				{
-					task.Category = category;
+			foreach (var item in model) {
+				if (item.Name == categoryName) {
+					task.Category = item;
 					return true;
 				}
-			} while (model.IterNext (ref iter));
+			}
 			return false;
 		}
 		
@@ -513,22 +493,8 @@ namespace Tasque
 		/// </returns>
 		private ITask GetTaskById (string id)
 		{
-			Gtk.TreeIter  iter;
-			Gtk.TreeModel model;
-			
-			ITask task = null;
-			model = application.Backend.Tasks;
-			
-			if (model.GetIterFirst (out iter)) {
-				do {
-					task = model.GetValue (iter, 0) as ITask;
-					if (task.Id.Equals (id)) {
-						return task;
-					}
-				} while (model.IterNext (ref iter));
-			}			
-			
-			return task;
+			var model = application.Backend.Tasks;
+			return model.FirstOrDefault (t => t.Id == id);
 		}
 
 		INativeApplication application;
diff --git a/src/Gtk.Tasque/TaskGroup.cs b/src/Gtk.Tasque/TaskGroup.cs
index 122ea72..711f137 100644
--- a/src/Gtk.Tasque/TaskGroup.cs
+++ b/src/Gtk.Tasque/TaskGroup.cs
@@ -2,8 +2,11 @@
 // User: boyd at 7:50 PMÂ2/11/2008
 
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using Gdk;
 using Gtk;
+using Gtk.Tasque;
 
 namespace Tasque
 {
@@ -16,7 +19,7 @@ namespace Tasque
 	{
 		Gtk.Label header;
 		TaskTreeView treeView;
-		TaskGroupModel filteredTasks;
+		TreeModel treeModel;
 		Gtk.HBox extraWidgetHBox;
 		Gtk.Widget extraWidget;
 		
@@ -24,7 +27,7 @@ namespace Tasque
 		
 		#region Constructor
 		public TaskGroup (string groupName, DateTime rangeStart,
-						  DateTime rangeEnd, Gtk.TreeModel tasks, INativeApplication application)
+		                  DateTime rangeEnd, ICollection<ITask> tasks, INativeApplication application)
 		{
 			if (application == null)
 				throw new ArgumentNullException ("application");
@@ -38,11 +41,8 @@ namespace Tasque
 			// groups in the main TaskWindow.  Reference Tomboy's NoteOfTheDay
 			// add-in for code that reacts on day changes.
 
-			filteredTasks = CreateModel (rangeStart, rangeEnd, tasks);
-
-			filteredTasks.ShowCompletedTasks = 
-				Application.Preferences.GetBool (
-					Preferences.ShowCompletedTasksKey);
+			treeModel = CreateModel (rangeStart, rangeEnd, tasks);
+			
 			Application.Preferences.SettingChanged += OnSettingChanged;
 			
 			// TODO: Add something to watch events so that the group will
@@ -88,7 +88,7 @@ namespace Tasque
 			//
 			// Group TreeView
 			//
-			treeView = new TaskTreeView (filteredTasks, application.Preferences);
+			treeView = new TaskTreeView (treeModel, application.Preferences);
 			treeView.Show ();
 			PackStart (treeView, true, true, 0);
 			
@@ -162,12 +162,12 @@ namespace Tasque
 		/// </value>
 		public DateTime TimeRangeStart
 		{
-			get { return filteredTasks.TimeRangeStart; }
+			get { return Model.TimeRangeStart; }
 			set {
-				if (value == filteredTasks.TimeRangeStart)
+				if (value == Model.TimeRangeStart)
 					return;
 				
-				filteredTasks.SetRange (value, filteredTasks.TimeRangeEnd);
+				Model.SetRange (value, Model.TimeRangeEnd);
 				Refilter ();
 			}
 		}
@@ -177,12 +177,12 @@ namespace Tasque
 		/// </value>
 		public DateTime TimeRangeEnd
 		{
-			get { return filteredTasks.TimeRangeEnd; }
+			get { return Model.TimeRangeEnd; }
 			set {
-				if (value == filteredTasks.TimeRangeEnd)
+				if (value == Model.TimeRangeEnd)
 					return;
 				
-				filteredTasks.SetRange (filteredTasks.TimeRangeStart, value);
+				Model.SetRange (Model.TimeRangeStart, value);
 				Refilter ();
 			}
 		}
@@ -196,7 +196,6 @@ namespace Tasque
 		#region Public Methods
 		public void Refilter (ICategory selectedCategory)
 		{
-			filteredTasks.Refilter ();
 			treeView.Refilter (selectedCategory);
 		}
 		
@@ -325,12 +324,14 @@ namespace Tasque
 		#region Private Methods
 		protected INativeApplication Application { get; private set; }
 
+		protected TaskGroupModel Model { get; set; }
+
 		protected override void OnRealized ()
 		{
 			base.OnRealized ();
 			
 			if (treeView.GetNumberOfTasks () == 0
-					&& (!filteredTasks.ShowCompletedTasks || hideWhenEmpty))
+					&& (!Model.ShowCompletedTasks || hideWhenEmpty))
 				Hide ();
 			else
 				Show ();
@@ -342,11 +343,13 @@ namespace Tasque
 			header.Markup = GetHeaderMarkup (DisplayName);
 		}
 
-		protected virtual  TaskGroupModel CreateModel (DateTime rangeStart,
-		                                               DateTime rangeEnd,
-		                                               TreeModel tasks)
+		protected virtual TreeModel CreateModel (DateTime rangeStart,
+		                                         DateTime rangeEnd,
+		                                         ICollection<ITask> tasks)
 		{
-			return new TaskGroupModel (rangeStart, rangeEnd, tasks);
+			Model = new TaskGroupModel (rangeStart, rangeEnd,
+			                            tasks, Application.Preferences);
+			return new TreeModelListAdapter<ITask> (Model);
 		}
 		
 		/// <summary>
@@ -374,25 +377,13 @@ namespace Tasque
 			string selectedCategoryName =
 				Application.Preferences.Get (Preferences.SelectedCategoryKey);
 			
+			ICategory category = null;
 			if (selectedCategoryName != null) {
-				Gtk.TreeIter iter;
-				Gtk.TreeModel model = Application.Backend.Categories;
-
-				// Iterate through (yeah, I know this is gross!) and find the
-				// matching category
-				if (model.GetIterFirst (out iter)) {
-					do {
-						ICategory cat = model.GetValue (iter, 0) as ICategory;
-						if (cat == null)
-							continue; // Needed for some reason to prevent crashes from some backends
-						if (cat.Name.CompareTo (selectedCategoryName) == 0) {
-							return cat;
-						}
-					} while (model.IterNext (ref iter));
-				}
+				var model = Application.Backend.Categories;
+				category = model.FirstOrDefault (c => c != null && c.Name == selectedCategoryName);
 			}
 			
-			return null;
+			return category;
 		}
 		
 		/// <summary>
@@ -426,7 +417,7 @@ namespace Tasque
 			//Logger.Debug ("TaskGroup (\"{0}\").OnNumberOfTasksChanged ()", DisplayName);
 			// Check to see whether this group should be hidden or shown.
 			if (treeView.GetNumberOfTasks () == 0
-					&& (!filteredTasks.ShowCompletedTasks || hideWhenEmpty))
+					&& (!Model.ShowCompletedTasks || hideWhenEmpty))
 				Hide ();
 			else
 				Show ();
@@ -455,10 +446,10 @@ namespace Tasque
 			
 			bool newValue =
 				preferences.GetBool (Preferences.ShowCompletedTasksKey);
-			if (filteredTasks.ShowCompletedTasks == newValue)
+			if (Model.ShowCompletedTasks == newValue)
 				return; // don't do anything if nothing has changed
 			
-			filteredTasks.ShowCompletedTasks = newValue;
+			Model.ShowCompletedTasks = newValue;
 			
 			ICategory cat = GetSelectedCategory ();
 			if (cat != null)
diff --git a/src/Gtk.Tasque/TaskWindow.cs b/src/Gtk.Tasque/TaskWindow.cs
index 8411656..09f984c 100644
--- a/src/Gtk.Tasque/TaskWindow.cs
+++ b/src/Gtk.Tasque/TaskWindow.cs
@@ -30,6 +30,8 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
 using Gdk;
 using Gtk;
 using Mono.Unix;
@@ -363,7 +365,12 @@ namespace Tasque
 			
 			// Set up the combo box (after the above to set the current filter)
 
-			categoryComboBox.Model = application.Backend.Categories;		
+			var categoryComboStore = new ListStore (typeof(ICategory));
+			foreach (var item in application.Backend.Categories) {
+				categoryComboStore.AppendValues (item);
+			}
+			
+			categoryComboBox.Model = categoryComboStore;
 
 			// Read preferences for the last-selected category and select it
 			string selectedCategoryName =
@@ -373,7 +380,6 @@ namespace Tasque
 			
 			SelectCategory (selectedCategoryName);
 		}
-
 		
 		#region Public Methods
 		/// <summary>
@@ -662,25 +668,10 @@ namespace Tasque
 		{
 			// This is disgustingly inefficient, but, oh well
 			int count = 0;
-			
-			Gtk.TreeIter iter;
-			Gtk.TreeModel model = application.Backend.Tasks;
-			
-			if (!model.GetIterFirst (out iter))
-				return 0;
-			
-			do {
-				ITask task = model.GetValue (iter, 0) as ITask;
-				if (task == null)
-					continue;
-				if (task.State != TaskState.Active
-						&& task.State != TaskState.Inactive)
-					continue;
-				
-				if (category.ContainsTask (task))
-					count++;
-			} while (model.IterNext (ref iter));
-			
+			var model = application.Backend.Tasks;
+			count = model.Count (t => t != null &&
+			                     (t.State == TaskState.Active || t.State == TaskState.Inactive) &&
+			                     category.ContainsTask (t));
 			return count;
 		}
 		
@@ -723,24 +714,17 @@ namespace Tasque
 			}
 		}
 		
-		private void RebuildAddTaskMenu (Gtk.TreeModel categoriesModel)
+		private void RebuildAddTaskMenu (ICollection<ICategory> categoriesModel)
 		{
 			Gtk.Menu menu = new Menu ();
 			
-			Gtk.TreeIter iter;
-			if (categoriesModel.GetIterFirst (out iter)) {
-				do {
-					ICategory category =
-						categoriesModel.GetValue (iter, 0) as ICategory;
-					
-					if (category is AllCategory)
-						continue; // Skip this one
-					
-					CategoryMenuItem item = new CategoryMenuItem (category);
-					item.Activated += OnNewTaskByCategory;
-					item.ShowAll ();
-					menu.Add (item);
-				} while (categoriesModel.IterNext (ref iter));
+			foreach (var cat in categoriesModel) {
+				if (cat is AllCategory)
+					continue;
+				var item = new CategoryMenuItem (cat);
+				item.Activated += OnNewTaskByCategory;
+				item.ShowAll ();
+				menu.Add (item);
 			}
 			
 			addTaskButton.Menu = menu;
@@ -1115,13 +1099,12 @@ namespace Tasque
 					 * here in order to enable changing categories. The list of available categories
 					 * is pre-filtered as to not contain the current category and the AllCategory.
 					 */
-					TreeModelFilter filteredCategories = new TreeModelFilter(application.Backend.Categories, null);
-					filteredCategories.VisibleFunc = delegate(TreeModel t, TreeIter i) {
-						ICategory category = t.GetValue (i, 0) as ICategory;
-						if (category == null || category is AllCategory || category.Equals(clickedTask.Category))
-							return false;
-						return true;
-					};
+
+				    var filteredCategories = new ListStore (typeof (ICategory));
+				    foreach (var cat in application.Backend.Categories) {
+					    if (cat != null && !(cat is AllCategory) && !cat.Equals (clickedTask.Category))
+						    filteredCategories.AppendValues (cat);
+		        	}
 
 					// The categories submenu is only created in case we actually provide at least one category.
 					if (filteredCategories.GetIterFirst(out iter))
diff --git a/src/Gtk.Tasque/TreeModelListAdapter.cs b/src/Gtk.Tasque/TreeModelListAdapter.cs
new file mode 100644
index 0000000..6a44756
--- /dev/null
+++ b/src/Gtk.Tasque/TreeModelListAdapter.cs
@@ -0,0 +1,105 @@
+//
+// TreeModelListAdapter.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2012 Antonius Riha
+//
+// 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.
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.InteropServices;
+using GLib;
+
+namespace Gtk.Tasque
+{
+	public class TreeModelListAdapter<T> : ListStore, IDisposable where T : INotifyPropertyChanged
+	{
+		public TreeModelListAdapter (IEnumerable<T> source) : base (typeof (T))
+		{
+			if (source == null)
+				throw new ArgumentNullException ("source");
+			var observableSource = source as INotifyCollectionChanged;
+			if (observableSource == null)
+				throw new ArgumentException ("source must be InotifyCollectionChanged");
+			this.source = source;
+			observableSource.CollectionChanged += HandleCollectionChanged;
+			
+			// fill ListStore and register change events from all items in source,
+			// since we need to update the TreeView on a by-row basis
+			foreach (var item in source) {
+				AppendValues (item);
+				item.PropertyChanged += HandlePropertyChanged;
+			}
+		}
+		
+		public void DisposeLocal ()
+		{
+			if (disposed)
+				return;
+			
+			foreach (var item in source)
+				item.PropertyChanged -= HandlePropertyChanged;
+			
+			disposed = true;
+		}
+
+		void HandlePropertyChanged (object sender, PropertyChangedEventArgs e)
+		{
+			// get index of changed item
+			var item = (T)sender;
+			var index = 0;
+			foreach (var element in source) {
+				if (item.Equals (element))
+					break;
+				index++;
+			}
+			TreeIter iter;
+			GetIter (out iter, new TreePath (new int [] { index }));
+			SetValue (iter, 0, sender);
+		}
+
+		void HandleCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
+		{
+			TreeIter iter;
+			//FIXME: Only handles add and remove actions
+			switch (e.Action) {
+			case NotifyCollectionChangedAction.Add:
+				var newItem = (T)e.NewItems [0];
+				iter = Insert (e.NewStartingIndex);
+				SetValues (iter, newItem);
+				newItem.PropertyChanged += HandlePropertyChanged;
+				break;
+			case NotifyCollectionChangedAction.Remove: 
+				var oldItem = (T)e.OldItems [0];
+				oldItem.PropertyChanged -= HandlePropertyChanged;
+				GetIter (out iter, new TreePath (new int [] { e.OldStartingIndex }));
+				Remove (ref iter);
+				break;
+			}
+		}
+		
+		bool disposed;
+		IEnumerable<T> source;
+	}
+}
diff --git a/src/Gtk.Tasque/AbstractTask.cs b/src/libtasque/AbstractTask.cs
similarity index 88%
rename from src/Gtk.Tasque/AbstractTask.cs
rename to src/libtasque/AbstractTask.cs
index 3aa5a2d..74d2b09 100644
--- a/src/Gtk.Tasque/AbstractTask.cs
+++ b/src/libtasque/AbstractTask.cs
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 
 namespace Tasque
 {
@@ -181,5 +182,21 @@ namespace Tasque
 			return Name.CompareTo (task.Name);
 		}
 		#endregion // Private Methods
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		protected virtual void OnPropertyChanged (string propertyName)
+		{
+			if (PropertyChanged != null)
+				PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
+		}
+		
+		protected virtual void OnPropertyChanging (string propertyName)
+		{
+			if (PropertyChanging != null)
+				PropertyChanging (this, new PropertyChangingEventArgs (propertyName));
+		}
 	}
 }
diff --git a/src/Gtk.Tasque/AllCategory.cs b/src/libtasque/AllCategory.cs
similarity index 95%
rename from src/Gtk.Tasque/AllCategory.cs
rename to src/libtasque/AllCategory.cs
index 1197f77..e080c25 100644
--- a/src/Gtk.Tasque/AllCategory.cs
+++ b/src/libtasque/AllCategory.cs
@@ -24,6 +24,7 @@ namespace Tasque
 			this.preferences = preferences;
 			categoriesToHide =
 				preferences.GetStringList (Preferences.HideInAllCategory);
+			categoriesToHide = preferences.GetStringList (Preferences.HideInAllCategory);
 			preferences.SettingChanged += OnSettingChanged;
 		}
 		
diff --git a/src/libtasque/CategoryComparer.cs b/src/libtasque/CategoryComparer.cs
new file mode 100644
index 0000000..b81fcfa
--- /dev/null
+++ b/src/libtasque/CategoryComparer.cs
@@ -0,0 +1,45 @@
+//
+// CategoryComparer.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2012 Antonius Riha
+//
+// 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.
+using System.Collections.Generic;
+
+namespace Tasque
+{
+	public class CategoryComparer : Comparer<ICategory>
+	{
+		public override int Compare (ICategory x, ICategory y)
+		{
+			if (x == null || y == null)
+				return 0;
+			
+			if (x is AllCategory)
+				return -1;
+			else if (y is AllCategory)
+				return 1;
+			
+			return (x.Name.CompareTo (y.Name));
+		}
+	}
+}
diff --git a/src/libtasque/CompletedTaskGroupModel.cs b/src/libtasque/CompletedTaskGroupModel.cs
index 0db7cbe..446d0e6 100644
--- a/src/libtasque/CompletedTaskGroupModel.cs
+++ b/src/libtasque/CompletedTaskGroupModel.cs
@@ -1,13 +1,14 @@
 
 using System;
-using Gtk;
+using System.Collections.Generic;
 
 namespace Tasque
 {
 	public class CompletedTaskGroupModel : TaskGroupModel
 	{
-		public CompletedTaskGroupModel (DateTime rangeStart, DateTime rangeEnd, TreeModel tasks)
-			: base (rangeStart, rangeEnd, tasks)
+		public CompletedTaskGroupModel (DateTime rangeStart, DateTime rangeEnd,
+		                                ICollection<ITask> tasks, Preferences preferences)
+			: base (rangeStart, rangeEnd, tasks, preferences)
 		{
 		}
 
@@ -24,13 +25,12 @@ namespace Tasque
 		/// <returns>
 		/// A <see cref="System.Boolean"/>
 		/// </returns>
-		protected override bool FilterTasks (TreeModel model, TreeIter iter)
+		protected override bool FilterTasks (ITask task)
 		{
 			// Don't show any task here if showCompletedTasks is false
 			if (!showCompletedTasks)
 				return false;
 			
-			ITask task = model.GetValue (iter, 0) as ITask;
 			if (task == null || task.State != TaskState.Completed)
 				return false;
 			
diff --git a/src/libtasque/IBackend.cs b/src/libtasque/IBackend.cs
index 81fc8ee..d7c628d 100644
--- a/src/libtasque/IBackend.cs
+++ b/src/libtasque/IBackend.cs
@@ -2,6 +2,7 @@
 // User: boyd at 7:02 AMÂ2/11/2008
 
 using System;
+using System.Collections.Generic;
 
 namespace Tasque.Backends
 {
@@ -33,7 +34,7 @@ namespace Tasque.Backends
 		/// <value>
 		/// All the tasks provided by the backend.
 		/// </value>
-		Gtk.TreeModel Tasks
+		ICollection<ITask> Tasks
 		{
 			get;
 		}
@@ -41,7 +42,7 @@ namespace Tasque.Backends
 		/// <value>
 		/// This returns all the ICategory items from the backend.
 		/// </value>
-		Gtk.TreeModel Categories
+		ICollection<ICategory> Categories
 		{
 			get;
 		}
diff --git a/src/libtasque/ITask.cs b/src/libtasque/ITask.cs
index aeef9c1..f5a4446 100644
--- a/src/libtasque/ITask.cs
+++ b/src/libtasque/ITask.cs
@@ -3,10 +3,11 @@
 
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 
 namespace Tasque
 {
-	public interface ITask
+	public interface ITask : INotifyPropertyChanged, INotifyPropertyChanging
 	{
 		#region Properties
 		/// <value>
diff --git a/src/libtasque/TaskComparer.cs b/src/libtasque/TaskComparer.cs
new file mode 100644
index 0000000..1285fe1
--- /dev/null
+++ b/src/libtasque/TaskComparer.cs
@@ -0,0 +1,40 @@
+//
+// TaskComparer.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2012 Antonius Riha
+//
+// 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.
+using System.Collections.Generic;
+
+namespace Tasque
+{
+	public class TaskComparer : Comparer<ITask>
+	{
+		public override int Compare (ITask x, ITask y)
+		{
+			if (x == null || y == null)
+				return 0;
+			
+			return (x.CompareTo (y));
+		}
+	}
+}
diff --git a/src/libtasque/TaskGroupModel.cs b/src/libtasque/TaskGroupModel.cs
index 0e8cdb1..7235258 100644
--- a/src/libtasque/TaskGroupModel.cs
+++ b/src/libtasque/TaskGroupModel.cs
@@ -1,18 +1,25 @@
 
 using System;
-
-using Gtk;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
 
 namespace Tasque
 {
-	public class TaskGroupModel : TreeModelFilter
+	/// <summary>
+	/// Task group model. Filters tasks.
+	/// </summary>
+	public class TaskGroupModel
+		: IEnumerable<ITask>, INotifyCollectionChanged, INotifyPropertyChanged, IDisposable
 	{
 		public bool ShowCompletedTasks
 		{
 			get { return showCompletedTasks; }
 			set {
 				showCompletedTasks = value;
-				base.Refilter ();
+				OnPropertyChanged ("ShowCompletedTasks");
 			}
 		}
 
@@ -27,27 +34,184 @@ namespace Tasque
 		}
 		
 		public TaskGroupModel (DateTime rangeStart, DateTime rangeEnd,
-		                       Gtk.TreeModel tasks) : base (tasks, null)
+		                       ICollection<ITask> tasks, Preferences preferences)
 		{
+			if (preferences == null)
+				throw new ArgumentNullException ("preferences");
+			if (tasks == null)
+				throw new ArgumentNullException ("tasks");
+			
 			this.timeRangeStart = rangeStart;
 			this.timeRangeEnd = rangeEnd;
+			
+			taskChangeLog = new List<MementoTask> ();
+			
+			showCompletedTasks = preferences.GetBool (
+				Preferences.ShowCompletedTasksKey);
+			
+			originalTasks = tasks;
+			((INotifyCollectionChanged)tasks).CollectionChanged += HandleCollectionChanged;
+			
+			// register change events for each task
+			foreach (var item in tasks) {
+				item.PropertyChanging += HandlePropertyChanging;
+				item.PropertyChanged += HandlePropertyChanged;
+			}
+		}
 
-			base.VisibleFunc = FilterTasks;
+		void HandlePropertyChanging (object sender, PropertyChangingEventArgs e)
+		{
+			var task = (ITask)sender;
+			var index = 0;
+			foreach (var item in this) {
+				if (item == task)
+					break;
+				index++;
+			}
+			var mementoTask = new MementoTask (task, index);
+			taskChangeLog.Add (mementoTask);
 		}
 
+		void HandlePropertyChanged (object sender, PropertyChangedEventArgs e)
+		{
+			var task = (ITask)sender;
+			var mementoTask = taskChangeLog.First (m => m.OriginalTaskRef == task);
+			taskChangeLog.Remove (mementoTask);
+			
+			if (FilterTasks (mementoTask)) {
+				if (!FilterTasks (task)) {
+					var eArgs = new NotifyCollectionChangedEventArgs (
+						NotifyCollectionChangedAction.Remove, task, mementoTask.OriginalIndex);
+					OnCollectionChanged (eArgs);
+				}
+			} else {
+				if (FilterTasks (task)) {
+					var index = 0;
+					foreach (var item in this) {
+						if (item == task)
+							break;
+						index++;
+					}
+					var eArgs = new NotifyCollectionChangedEventArgs (
+						NotifyCollectionChangedAction.Add, task, index);
+					OnCollectionChanged (eArgs);
+				}
+			}
+		}
+
+		void HandleCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
+		{
+			ITask changedItem = null;
+			var index = 0;
+			//FIXME: Only accounts for add and remove actions
+			switch (e.Action) {
+			case NotifyCollectionChangedAction.Add:
+				changedItem = (ITask)e.NewItems [0];
+				
+				// register change event on task
+				changedItem.PropertyChanged += HandlePropertyChanged;
+				changedItem.PropertyChanging += HandlePropertyChanging;
+				
+				if (!FilterTasks (changedItem))
+					return;
+				
+				// calculate index without filtered out items
+				foreach (var item in this) {
+					if (item == changedItem)
+						break;
+					index++;
+				}
+				
+				break;
+			case NotifyCollectionChangedAction.Remove:
+				changedItem = (ITask)e.OldItems [0];
+				
+				// unregister change event on task
+				changedItem.PropertyChanged -= HandlePropertyChanged;
+				changedItem.PropertyChanging -= HandlePropertyChanging;
+				
+				if (!FilterTasks (changedItem))
+					return;
+					
+				var i = 0;
+				var enmrtr = originalTasks.GetEnumerator ();
+				bool enmrtrStatus;
+				while (enmrtrStatus = enmrtr.MoveNext ()) {
+					// move enumerator to right position
+					if (i++ < e.OldStartingIndex)
+						continue;
+				
+					// right position: i == oldStartingIndex
+					if (FilterTasks (enmrtr.Current))
+						break;
+				}
+				
+				if (enmrtrStatus) {
+					foreach (var task in this) {
+						if (task == enmrtr.Current)
+							break;
+						index++;
+					}
+				} else
+					index = this.Count ();
+				
+				break;
+			}
+			
+			var eArgs = new NotifyCollectionChangedEventArgs (e.Action, changedItem, index);
+			OnCollectionChanged (eArgs);
+		}
+		
+		public IEnumerator<ITask> GetEnumerator ()
+		{
+			foreach (var item in originalTasks) {
+				if (FilterTasks (item))
+					yield return item;
+			}
+		}
+		
 		public void SetRange (DateTime rangeStart, DateTime rangeEnd)
 		{
 			this.timeRangeStart = rangeStart;
 			this.timeRangeEnd = rangeEnd;
-			base.Refilter ();
+			OnPropertyChanged ("TimeRangeStart");
+			OnPropertyChanged ("TimeRangeEnd");
+		}
+		
+		public void Dispose ()
+		{
+			if (disposed)
+				return;
+			
+			foreach (var item in originalTasks) {
+				item.PropertyChanged -= HandlePropertyChanged;
+				item.PropertyChanging -= HandlePropertyChanging;
+			}
+			
+			disposed = true;
 		}
 
+		public event NotifyCollectionChangedEventHandler CollectionChanged;
+		
+		protected virtual void OnCollectionChanged (NotifyCollectionChangedEventArgs e)
+		{
+			if (CollectionChanged != null)
+				CollectionChanged (this, e);
+		}
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void OnPropertyChanged (string propertyName)
+		{
+			if (PropertyChanged != null)
+				PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
+		}
+		
 		/// <summary>
 	        /// Filter out tasks that don't fit within the group's date range
 	        /// </summary>
-		protected virtual bool FilterTasks (Gtk.TreeModel model, Gtk.TreeIter iter)
+		protected virtual bool FilterTasks (ITask task)
 		{
-			ITask task = model.GetValue (iter, 0) as ITask;
 			if (task == null || task.State == TaskState.Deleted)
 				return false;
 			
@@ -105,5 +269,128 @@ namespace Tasque
 			
 			return true;
 		}
+		
+		#region Explicit content
+		IEnumerator IEnumerable.GetEnumerator ()
+		{
+			return GetEnumerator ();
+		}
+		#endregion
+		
+		ICollection<ITask> originalTasks;
+		List<MementoTask> taskChangeLog;
+		bool disposed;
+		
+		class MementoTask : ITask
+		{
+			public MementoTask (ITask originalTask, int index)
+			{
+				OriginalTaskRef = originalTask;
+				Id = originalTask.Id;
+				Name = originalTask.Name;
+				DueDate = originalTask.DueDate;
+				CompletionDate = originalTask.CompletionDate;
+				IsComplete = originalTask.IsComplete;
+				Priority = originalTask.Priority;
+				HasNotes = originalTask.HasNotes;
+				SupportsMultipleNotes = originalTask.SupportsMultipleNotes;
+				State = originalTask.State;
+				Category = originalTask.Category;
+				TimerID = originalTask.TimerID;
+			}
+			
+			public int OriginalIndex { get; private set; }
+			
+			public ITask OriginalTaskRef { get; private set; }
+			
+			public string Id { get; private set; }
+			
+			public string Name { get; set; }
+			
+			public DateTime DueDate { get; set; }
+			
+			public DateTime CompletionDate { get; set; }
+			
+			public bool IsComplete { get; private set; }
+			
+			public TaskPriority Priority { get; set; }
+			
+			public bool HasNotes { get; private set; }
+			
+			public bool SupportsMultipleNotes { get; private set; }
+			
+			public TaskState State { get; private set; }
+			
+			public ICategory Category { get; set; }
+			
+			public uint TimerID { get; set; }
+			
+			#region Explicit content
+			List<INote> ITask.Notes {
+				get { throw new NotSupportedException (); }
+			}
+			
+			void ITask.Activate ()
+			{
+				throw new NotSupportedException ();
+			}
+			
+			void ITask.Inactivate ()
+			{
+				throw new NotSupportedException ();
+			}
+			
+			void ITask.Complete ()
+			{
+				throw new NotSupportedException ();
+			}
+			
+			void ITask.Delete ()
+			{
+				throw new NotSupportedException ();
+			}
+			
+			INote ITask.CreateNote (string text)
+			{
+				throw new NotSupportedException ();
+			}
+			
+			void ITask.DeleteNote (INote note)
+			{
+				throw new NotSupportedException ();
+			}
+			
+			void ITask.SaveNote (INote note)
+			{
+				throw new NotSupportedException ();
+			}
+			
+			int ITask.CompareTo (ITask task)
+			{
+				throw new NotSupportedException ();
+			}
+			
+			int ITask.CompareToByCompletionDate (ITask task)
+			{
+				throw new NotSupportedException ();
+			}
+			
+			event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged {
+				add {
+					throw new NotSupportedException ();
+				} remove {
+					throw new NotSupportedException ();
+				}
+			}
+			
+			event PropertyChangingEventHandler INotifyPropertyChanging.PropertyChanging {
+				add {
+					throw new NotSupportedException ();
+				} remove {
+					throw new NotSupportedException ();
+				}
+			}
+			#endregion
+		}
 	}
 }
diff --git a/src/libtasque/TaskGroupModelFactory.cs b/src/libtasque/TaskGroupModelFactory.cs
index 61e5819..a4e276c 100644
--- a/src/libtasque/TaskGroupModelFactory.cs
+++ b/src/libtasque/TaskGroupModelFactory.cs
@@ -1,13 +1,12 @@
 
 using System;
-
-using Gtk;
+using System.Collections.Generic;
 
 namespace Tasque
 {
 	public static class TaskGroupModelFactory
 	{
-		public static TaskGroupModel CreateTodayModel (TreeModel tasks)
+		public static TaskGroupModel CreateTodayModel (ICollection<ITask> tasks, Preferences preferences)
 		{
 			DateTime rangeStart = DateTime.Now;
 			rangeStart = new DateTime (rangeStart.Year, rangeStart.Month,
@@ -15,20 +14,20 @@ namespace Tasque
 			DateTime rangeEnd = DateTime.Now;
 			rangeEnd = new DateTime (rangeEnd.Year, rangeEnd.Month,
 									 rangeEnd.Day, 23, 59, 59);
-			return new TaskGroupModel (rangeStart, rangeEnd, tasks);
+			return new TaskGroupModel (rangeStart, rangeEnd, tasks, preferences);
 		}
 
-		public static TaskGroupModel CreateOverdueModel (TreeModel tasks)
+		public static TaskGroupModel CreateOverdueModel (ICollection<ITask> tasks, Preferences preferences)
 		{
 			DateTime rangeStart = DateTime.MinValue;
 			DateTime rangeEnd = DateTime.Now.AddDays (-1);
 			rangeEnd = new DateTime (rangeEnd.Year, rangeEnd.Month, rangeEnd.Day,
 									 23, 59, 59);
 
-			return new TaskGroupModel (rangeStart, rangeEnd, tasks);
+			return new TaskGroupModel (rangeStart, rangeEnd, tasks, preferences);
 		}
 
-		public static TaskGroupModel CreateTomorrowModel (TreeModel tasks)
+		public static TaskGroupModel CreateTomorrowModel (ICollection<ITask> tasks, Preferences preferences)
 		{
 			DateTime rangeStart = DateTime.Now.AddDays (1);
 			rangeStart = new DateTime (rangeStart.Year, rangeStart.Month,
@@ -37,7 +36,7 @@ namespace Tasque
 			rangeEnd = new DateTime (rangeEnd.Year, rangeEnd.Month,
 									 rangeEnd.Day, 23, 59, 59);
 
-			return new TaskGroupModel (rangeStart, rangeEnd, tasks);
+			return new TaskGroupModel (rangeStart, rangeEnd, tasks, preferences);
 		}
 	}
 }
diff --git a/src/libtasque/libtasque.csproj b/src/libtasque/libtasque.csproj
index 3957724..b1df76e 100644
--- a/src/libtasque/libtasque.csproj
+++ b/src/libtasque/libtasque.csproj
@@ -30,16 +30,9 @@
     <OutputPath>..\..\build\bin\lib\tasque</OutputPath>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="Mono.Posix" />
-    <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
-      <Private>False</Private>
-    </Reference>
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
@@ -81,6 +74,10 @@
     <Compile Include="TaskGroupModel.cs" />
     <Compile Include="TaskGroupModelFactory.cs" />
     <Compile Include="CompletedTaskGroupModel.cs" />
+    <Compile Include="AbstractTask.cs" />
+    <Compile Include="CategoryComparer.cs" />
+    <Compile Include="TaskComparer.cs" />
+    <Compile Include="AllCategory.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="DateFormatters\" />



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]