[tasque] Task columns are now Mono.Addins



commit 7e9937cc915f4dea572cb22f14754a3cf969cf9a
Author: Antonius Riha <antoniusriha gmail com>
Date:   Wed Jan 9 17:48:48 2013 +0100

    Task columns are now Mono.Addins
    
    TaskTreeView has been renamed to TaskView and no
    longer inherits from TreeView. It has been reduced to
    loading and managing the interactions of the various
    task columns.
    
    Each task column is now an extension of ITaskColumn,
    an interface that provides extensibility via the
    Mono.Addins framework.
    
    Two Addin projects have been added:
    	* Gtk.Tasque.Columns: provides standard Tasque
    columns
    	* Gtk.Tasque.TimerCompleteColumns: provides
    timed completion of tasks.
    
    The functionality of those addins is the same as in the
    previous version of Tasque. This change is about
    making Gtk.Tasque more extensible and easier to
    maintain.

 .gitignore                                         |    6 +
 Makefile.am                                        |    2 +
 build/build.csproj                                 |    6 +
 po/POTFILES.in                                     |   10 +-
 src/Addins/Gtk.Tasque.Columns/CompleteColumn.cs    |   98 +++
 src/Addins/Gtk.Tasque.Columns/DueDateColumn.cs     |  212 ++++++
 .../Gtk.Tasque.Columns/Gtk.Tasque.Columns.csproj   |   91 +++
 .../Gtk.Tasque.Columns/MyClass.cs}                 |   16 +-
 .../Gtk.Tasque.Columns/NotesColumn.cs}             |   91 +--
 src/Addins/Gtk.Tasque.Columns/PriorityColumn.cs    |  160 +++++
 .../Gtk.Tasque.Columns/Properties/AssemblyInfo.cs} |   19 +-
 .../Gtk.Tasque.Columns/TaskBeingEdited.cs}         |   15 +-
 src/Addins/Gtk.Tasque.Columns/TaskNameColumn.cs    |  179 +++++
 .../CompleteWithTimerColumn.cs                     |  136 ++++
 .../Gtk.Tasque.TimerCompleteColumns.csproj         |   91 +++
 .../Properties/AssemblyInfo.cs}                    |   19 +-
 .../TaskCompleteTimer.cs                           |    0
 .../TaskCompleteTimerState.cs                      |    0
 .../TaskCompleteTimerStoppedEventArgs.cs           |    0
 .../TaskCompleteTimerTickEventArgs.cs              |    0
 .../TimerColumn.cs                                 |   42 +-
 src/Gtk.Tasque/Gtk.Tasque.csproj                   |   14 +-
 src/Gtk.Tasque/GtkApplicationBase.cs               |    5 +
 ...leteTimerStoppedEventArgs.cs => ITaskColumn.cs} |   22 +-
 src/Gtk.Tasque/Properties/AssemblyInfo.cs          |    2 +
 src/Gtk.Tasque/TaskColumnExtensionAttribute.cs     |   65 ++
 src/Gtk.Tasque/TaskGroup.cs                        |   48 +-
 ...ppedEventArgs.cs => TaskRowEditingEventArgs.cs} |   15 +-
 src/Gtk.Tasque/TaskTreeView.cs                     |  715 --------------------
 src/Gtk.Tasque/TaskView.cs                         |  258 +++++++
 src/Gtk.Tasque/TaskWindow.cs                       |   12 +-
 tasque.sln                                         |   26 +
 32 files changed, 1498 insertions(+), 877 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 66812bf..cb66337 100644
--- a/.gitignore
+++ b/.gitignore
@@ -105,6 +105,12 @@ Makefile.in
 /src/tasque/locale/
 /src/tasque/Tasque.exe.Defines.config
 
+# /src/Addins/*/
+/src/Addins/*/*.dll
+/src/Addins/*/*.mdb
+/src/Addins/*/*.pdb
+/src/Addins/*/obj/
+
 # /src/Addins/Backends/*/
 /src/Addins/Backends/*/*.dll
 /src/Addins/Backends/*/*.mdb
diff --git a/Makefile.am b/Makefile.am
index 2007880..fbc2134 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -111,6 +111,8 @@ _BUILDFILES = \
 	src/Addins/Backends/IceCore/IceCoreBackend.csproj \
 	src/Addins/Backends/Rtm/RtmBackend.csproj \
 	src/Addins/Backends/Sqlite/SqliteBackend.csproj \
+	src/Addins/Gtk.Tasque.Columns/Gtk.Tasque.Columns.csproj \
+	src/Addins/Gtk.Tasque.TimerCompleteColumns/Gtk.Tasque.TimerCompleteColumns.csproj \
 	data/data.mdproj \
 	tests/tests.csproj \
 	po/po.mdproj
diff --git a/build/build.csproj b/build/build.csproj
index 32d590a..eff2b39 100644
--- a/build/build.csproj
+++ b/build/build.csproj
@@ -85,6 +85,12 @@
     <Proj Include="X.Common.targets" />
     <Proj Include="X.Substitute.targets" />
     <Proj Include="X.Translate.targets" />
+    <Proj Include="..\src\Addins\Gtk.Tasque.Columns\Gtk.Tasque.Columns.csproj">
+      <Link>Addins\Gtk.Tasque.Columns.csproj</Link>
+    </Proj>
+    <Proj Include="..\src\Addins\Gtk.Tasque.TimerCompleteColumns\Gtk.Tasque.TimerCompleteColumns.csproj">
+      <Link>Addins\Gtk.Tasque.TimerCompleteColumns.csproj</Link>
+    </Proj>
   </ItemGroup>
   <ItemGroup>
     <None Include="build.csproj" />
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 336eae0..0bc5405 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -9,10 +9,16 @@ src/Gtk.Tasque/GtkTray.cs
 src/Gtk.Tasque/NoteDialog.cs
 src/Gtk.Tasque/PreferencesDialog.cs
 src/Gtk.Tasque/RemoteControl.cs
-src/Gtk.Tasque/TaskTreeView.cs
+src/Gtk.Tasque/TaskView.cs
 src/Gtk.Tasque/TaskWindow.cs
-src/Gtk.Tasque/TimerColumn.cs
 src/Gtk.Tasque/Utilities.cs
 src/Addins/Backends/Rtm/Gtk/RtmPreferencesWidget.cs
+src/Addins/Gtk.Tasque.Columns/CompleteColumn.cs
+src/Addins/Gtk.Tasque.Columns/DueDateColumn.cs
+src/Addins/Gtk.Tasque.Columns/NotesColumn.cs
+src/Addins/Gtk.Tasque.Columns/PriorityColumn.cs
+src/Addins/Gtk.Tasque.Columns/TaskNameColumn.cs
+src/Addins/Gtk.Tasque.TimerCompleteColumns/CompleteWithTimerColumn.cs
+src/Addins/Gtk.Tasque.TimerCompleteColumns/TimerColumn.cs
 src/libtasque/AllCategory.cs
 src/libtasque/TaskParser.cs
diff --git a/src/Addins/Gtk.Tasque.Columns/CompleteColumn.cs b/src/Addins/Gtk.Tasque.Columns/CompleteColumn.cs
new file mode 100644
index 0000000..e30a6d1
--- /dev/null
+++ b/src/Addins/Gtk.Tasque.Columns/CompleteColumn.cs
@@ -0,0 +1,98 @@
+//
+// CompleteColumn.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2013 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;
+
+namespace Gtk.Tasque
+{
+	// TODO: Use xml addin description model to provide localized column name
+	[TaskColumnExtension ("Completed")]
+	public class CompleteColumn : ITaskColumn
+	{
+		public CompleteColumn ()
+		{
+			TreeViewColumn = new TreeViewColumn {
+				Title = Catalog.GetString ("Completed"),
+				Sizing = TreeViewColumnSizing.Autosize,
+				Resizable = false,
+				Clickable = true
+			};
+			
+			var renderer = new CellRendererToggle ();
+			renderer.Toggled += OnTaskToggled;
+			TreeViewColumn.PackStart (renderer, false);
+			TreeViewColumn.SetCellDataFunc (renderer, TaskToggleCellDataFunc);
+		}
+
+		public int DefaultPosition { get { return 0; } }
+
+		public TreeViewColumn TreeViewColumn { get; private set; }
+
+		public void Initialize (TreeModel model, TaskView view, IPreferences preferences)
+		{
+			if (model == null)
+				throw new ArgumentNullException ("model");
+			this.model = model;
+		}
+
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
+		
+		void OnTaskToggled (object o, ToggledArgs args)
+		{
+			Logger.Debug ("OnTaskToggled");
+			TreeIter iter;
+			var path = new TreePath (args.Path);
+			if (!model.GetIter (out iter, path))
+				return; // Do nothing
+			
+			var task = model.GetValue (iter, 0) as ITask;
+			if (task == null)
+				return;
+			
+			string statusMsg;
+			if (task.State == TaskState.Active) {
+				task.Complete ();
+				statusMsg = Catalog.GetString ("Task Completed");
+			} else {
+				statusMsg = Catalog.GetString ("Action Canceled");
+				task.Activate ();
+			}
+			TaskWindow.ShowStatus (statusMsg, 5);
+		}
+
+		void TaskToggleCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
+		                             TreeModel treeModel, TreeIter iter)
+		{
+			var crt = cell as CellRendererToggle;
+			var task = model.GetValue (iter, 0) as ITask;
+			crt.Active = task != null && task.State != TaskState.Active;
+		}
+		
+		TreeModel model;
+	}
+}
diff --git a/src/Addins/Gtk.Tasque.Columns/DueDateColumn.cs b/src/Addins/Gtk.Tasque.Columns/DueDateColumn.cs
new file mode 100644
index 0000000..d59f06a
--- /dev/null
+++ b/src/Addins/Gtk.Tasque.Columns/DueDateColumn.cs
@@ -0,0 +1,212 @@
+//
+// DueDateColumn.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2013 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;
+
+namespace Gtk.Tasque
+{
+	// TODO: Use xml addin description model to provide localized column name
+	[TaskColumnExtension ("Due Date")]
+	public class DueDateColumn : ITaskColumn
+	{
+		public DueDateColumn ()
+		{
+			TreeViewColumn = new TreeViewColumn {
+				Title = Catalog.GetString ("Due Date"),
+				Sizing = TreeViewColumnSizing.Fixed,
+				Alignment = 0f,
+				FixedWidth = 90,
+				Resizable = false,
+				Clickable = true
+			};
+			
+			var dueDateStore = new ListStore (typeof (string));
+			var today = DateTime.Now;
+			dueDateStore.AppendValues (
+				today.ToString (Catalog.GetString ("M/d - ")) + Catalog.GetString ("Today"));
+			dueDateStore.AppendValues (
+				today.AddDays (1).ToString (Catalog.GetString ("M/d - ")) + Catalog.GetString ("Tomorrow"));
+			dueDateStore.AppendValues (
+				today.AddDays (2).ToString (Catalog.GetString ("M/d - ddd")));
+			dueDateStore.AppendValues (
+				today.AddDays (3).ToString (Catalog.GetString ("M/d - ddd")));
+			dueDateStore.AppendValues (
+				today.AddDays (4).ToString (Catalog.GetString ("M/d - ddd")));
+			dueDateStore.AppendValues (
+				today.AddDays (5).ToString (Catalog.GetString ("M/d - ddd")));
+			dueDateStore.AppendValues (
+				today.AddDays (6).ToString (Catalog.GetString ("M/d - ddd")));
+			dueDateStore.AppendValues (
+				today.AddDays (7).ToString (Catalog.GetString ("M/d - ")) + Catalog.GetString ("In 1 Week"));			
+			dueDateStore.AppendValues (Catalog.GetString ("No Date"));
+			dueDateStore.AppendValues (Catalog.GetString ("Choose Date..."));
+			
+			var renderer = new CellRendererCombo {
+				Editable = true,
+				HasEntry = false,
+				Model = dueDateStore,
+				TextColumn = 0,
+				Xalign = 0.0f
+			};
+			
+			renderer.EditingStarted += (o, args) => {
+				TreeIter iter;
+				var path = new TreePath (args.Path);
+				if (!model.GetIter (out iter, path))
+					return;
+				
+				var task = model.GetValue (iter, 0) as ITask;
+				if (task == null)
+					return;
+				
+				taskBeingEdited = new TaskBeingEdited (task, iter, path);
+				if (CellEditingStarted != null)
+					CellEditingStarted (this, new TaskRowEditingEventArgs (task, iter, path));
+			};
+			
+			renderer.EditingCanceled += delegate { EndCellEditing (); };
+			
+			renderer.Edited += (o, args) => {
+				TreeIter iter;
+				var path = new TreePath (args.Path);
+				var newText = args.NewText;
+				if (newText != null && model.GetIter (out iter, path)) {
+					
+					//  2/11 - Today
+					//  2/12 - Tomorrow
+					//  2/13 - Wed
+					//  2/14 - Thu
+					//  2/15 - Fri
+					//  2/16 - Sat
+					//  2/17 - Sun
+					// --------------
+					//  2/18 - In 1 Week
+					// --------------
+					//  No Date
+					// ---------------
+					//  Choose Date...
+					
+					var newDate = DateTime.MinValue;
+					var tday = DateTime.Now;
+					var task = model.GetValue (iter, 0) as ITask;			
+					
+					if (newText == tday.ToString (Catalog.GetString ("M/d - ")) + Catalog.GetString ("Today"))
+						newDate = tday;
+					else if (newText == tday.AddDays (1).ToString (Catalog.GetString ("M/d - "))
+					         + Catalog.GetString ("Tomorrow"))
+						newDate = tday.AddDays (1);
+					else if (newText == Catalog.GetString ("No Date"))
+						newDate = DateTime.MinValue;
+					else if (newText == tday.AddDays (7).ToString (Catalog.GetString ("M/d - "))
+					         + Catalog.GetString ("In 1 Week"))
+						newDate = tday.AddDays (7);
+					else if (newText == Catalog.GetString ("Choose Date...")) {
+						var tc = new TaskCalendar (task, view.TreeView.Parent);
+						tc.ShowCalendar ();
+						return;
+					} else {
+						for (int i = 2; i <= 6; i++) {
+							DateTime testDate = tday.AddDays (i);
+							if (testDate.ToString (Catalog.GetString ("M/d - ddd")) == newText) {
+								newDate = testDate;
+								break;
+							}
+						}
+					}
+					
+					Logger.Debug ("task.State {0}", task.State);
+					
+					if (task.State == TaskState.Completed) {
+						// Modify the completion date
+						task.CompletionDate = newDate;
+					} else {
+						// Modify the due date
+						task.DueDate = newDate;
+					}
+				}
+				EndCellEditing ();
+			};
+			
+			TreeViewColumn.PackStart (renderer, true);
+			TreeViewColumn.SetCellDataFunc (renderer, DueDateCellDataFunc);
+		}
+
+		public int DefaultPosition { get { return 3; } }
+
+		public TreeViewColumn TreeViewColumn { get; private set; }
+
+		public void Initialize (TreeModel model, TaskView view, IPreferences preferences)
+		{
+			if (view == null)
+				throw new ArgumentNullException ("view");
+			this.view = view;
+			if (model == null)
+				throw new ArgumentNullException ("model");
+			this.model = model;
+		}
+		
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+		
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
+
+		void DueDateCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
+		                          TreeModel treeModel, TreeIter iter)
+		{
+			var crc = cell as CellRendererCombo;
+			var task = model.GetValue (iter, 0) as ITask;
+			if (task == null)
+				return;
+			
+			var date = task.State == TaskState.Completed ? task.CompletionDate : task.DueDate;
+			if (date == DateTime.MinValue || date == DateTime.MaxValue) {
+				crc.Text = "-";
+				return;
+			}
+			
+			if (date.Year == DateTime.Today.Year)
+				crc.Text = date.ToString (Catalog.GetString ("M/d - ddd"));
+			else
+				crc.Text = date.ToString (Catalog.GetString ("M/d/yy - ddd"));
+		}
+
+		void EndCellEditing ()
+		{
+			if (taskBeingEdited == null)
+				return;
+			
+			if (CellEditingFinished != null)
+				CellEditingFinished (this, new TaskRowEditingEventArgs (taskBeingEdited.Task,
+				                                                        taskBeingEdited.Iter,
+				                                                        taskBeingEdited.Path));
+			taskBeingEdited = null;
+		}
+		
+		TreeModel model;
+		TaskView view;
+		TaskBeingEdited taskBeingEdited;
+	}
+}
diff --git a/src/Addins/Gtk.Tasque.Columns/Gtk.Tasque.Columns.csproj b/src/Addins/Gtk.Tasque.Columns/Gtk.Tasque.Columns.csproj
new file mode 100644
index 0000000..3048a86
--- /dev/null
+++ b/src/Addins/Gtk.Tasque.Columns/Gtk.Tasque.Columns.csproj
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003";>
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">LinuxDebug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>10.0.0</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{C8454FAA-CE15-4202-9B93-4B536A8BC32D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <OutputPath>.</OutputPath>
+    <RootNamespace>Gtk.Tasque.Columns</RootNamespace>
+    <AssemblyName>Gtk.Tasque.Columns</AssemblyName>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ReleaseVersion>0.1.13</ReleaseVersion>
+    <PackageName>tasque</PackageName>
+    <TopBuildDir>..\..\..</TopBuildDir>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'LinuxDebug|AnyCPU' ">
+    <DebugSymbols>True</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>False</Optimize>
+    <DefineConstants>DEBUG;TRACE;LINUX</DefineConstants>
+    <OutputPath>..\..\..\build\bin\lib\tasque</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'LinuxRelease|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>True</Optimize>
+    <DefineConstants>TRACE;LINUX</DefineConstants>
+    <OutputPath>..\..\..\build\bin\lib\tasque</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'WinDebug|AnyCPU' ">
+    <DebugSymbols>True</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>False</Optimize>
+    <DefineConstants>DEBUG;TRACE;WIN</DefineConstants>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'WinRelease|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>True</Optimize>
+    <DefineConstants>TRACE;WIN</DefineConstants>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="Mono.Addins, Version=0.6.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756">
+      <Private>False</Private>
+      <Package>mono-addins</Package>
+    </Reference>
+    <Reference Include="Mono.Posix" />
+    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+      <Private>False</Private>
+      <Package>gtk-sharp-2.0</Package>
+    </Reference>
+    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+      <Private>False</Private>
+      <Package>gtk-sharp-2.0</Package>
+    </Reference>
+    <Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+      <Private>False</Private>
+      <Package>gtk-sharp-2.0</Package>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="..\..\..\build\CommonAssemblyInfo.cs">
+      <Link>Properties\CommonAssemblyInfo.cs</Link>
+    </Compile>
+    <Compile Include="CompleteColumn.cs" />
+    <Compile Include="DueDateColumn.cs" />
+    <Compile Include="NotesColumn.cs" />
+    <Compile Include="PriorityColumn.cs" />
+    <Compile Include="TaskNameColumn.cs" />
+    <Compile Include="TaskBeingEdited.cs" />
+  </ItemGroup>
+  <Import Project="..\..\..\build\X.Common.targets" />
+  <ItemGroup>
+    <Folder Include="Properties\" />
+  </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>
+    </ProjectReference>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/Gtk.Tasque/TaskCompleteTimerState.cs b/src/Addins/Gtk.Tasque.Columns/MyClass.cs
similarity index 90%
copy from src/Gtk.Tasque/TaskCompleteTimerState.cs
copy to src/Addins/Gtk.Tasque.Columns/MyClass.cs
index 438e500..6dc653b 100644
--- a/src/Gtk.Tasque/TaskCompleteTimerState.cs
+++ b/src/Addins/Gtk.Tasque.Columns/MyClass.cs
@@ -1,5 +1,5 @@
 //
-// TaskCompleteTimerState.cs
+// MyClass.cs
 //
 // Author:
 //       Antonius Riha <antoniusriha gmail com>
@@ -23,13 +23,15 @@
 // 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.
-namespace Gtk.Tasque
+using System;
+
+namespace Gtk.Tasque.Columns
 {
-	public enum TaskCompleteTimerState
+	public class MyClass
 	{
-		NotStarted,
-		Running,
-		Paused,
-		Stopped
+		public MyClass ()
+		{
+		}
 	}
 }
+
diff --git a/src/Gtk.Tasque/TimerColumn.cs b/src/Addins/Gtk.Tasque.Columns/NotesColumn.cs
similarity index 50%
copy from src/Gtk.Tasque/TimerColumn.cs
copy to src/Addins/Gtk.Tasque.Columns/NotesColumn.cs
index cea7ffa..e5382ac 100644
--- a/src/Gtk.Tasque/TimerColumn.cs
+++ b/src/Addins/Gtk.Tasque.Columns/NotesColumn.cs
@@ -1,5 +1,5 @@
 //
-// TimerColumn.cs
+// NotesColumn.cs
 //
 // Author:
 //       Antonius Riha <antoniusriha gmail com>
@@ -24,92 +24,63 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 using System;
-using System.Collections.Concurrent;
 using Mono.Unix;
 using Tasque;
+using Gdk;
 
 namespace Gtk.Tasque
 {
-	public class TimerColumn
+	public class NotesColumn : ITaskColumn
 	{
-		public TimerColumn (IPreferences preferences, TreeModel model)
+		static NotesColumn ()
+		{
+			notePixbuf = Utilities.GetIcon ("tasque-note", 12);
+		}
+		
+		static Pixbuf notePixbuf;
+		
+		public NotesColumn ()
 		{
-			if (model == null)
-				throw new ArgumentNullException ("model");
-			this.model = model;
-			if (preferences == null)
-				throw new ArgumentNullException ("preferences");
-			this.preferences = preferences;
-
-			timeoutTargets = new ConcurrentDictionary<ITask, TaskCompleteTimer> ();
-
 			TreeViewColumn = new TreeViewColumn {
-				Title = Catalog.GetString ("Timer"),
+				Title = Catalog.GetString ("Notes"),
 				Sizing = TreeViewColumnSizing.Fixed,
 				FixedWidth = 20,
 				Resizable = false
 			};
 			
-			var renderer = new CellRendererPixbuf {	Xalign = 0.5f };
+			var renderer = new CellRendererPixbuf ();
 			TreeViewColumn.PackStart (renderer, false);
-			TreeViewColumn.SetCellDataFunc (renderer, TaskTimerCellDataFunc);
+			TreeViewColumn.SetCellDataFunc (renderer, TaskNotesCellDataFunc);
 		}
-		
+
+		public int DefaultPosition { get { return 4; } }
+
 		public TreeViewColumn TreeViewColumn { get; private set; }
 		
-		public TaskCompleteTimer CreateTimer (ITask task)
+		public void Initialize (TreeModel model, TaskView view, IPreferences preferences)
 		{
-			if (task == null)
-				throw new ArgumentNullException ("task");
-			
-			var timeout = preferences.GetInt (PreferencesKeys.InactivateTimeoutKey);
-			TreeIter treeIter;
-			var iterFound = false;
-			model.Foreach ((treeModel, treePath, iter) => {
-				if (treeModel.GetValue (iter, 0) == task) {
-					treeIter = iter;
-					iterFound = true;
-					return true;
-				}
-				return false;
-			});
-			
-			var timer = new TaskCompleteTimer (timeout, treeIter, model);
-			if (!iterFound || !timeoutTargets.TryAdd (task, timer))
-				throw new Exception ("Unable to create a timer for the provided task.");
-			
-			timer.TimerStopped += (sender, e) => {
-				TaskCompleteTimer tmr;
-				timeoutTargets.TryRemove (e.Task, out tmr);
-			};
-			
-			return timer;
-		}
-
-		public TaskCompleteTimer GetTimer (ITask task)
-		{
-			TaskCompleteTimer timer;
-			if (timeoutTargets.TryGetValue (task, out timer))
-				return timer;
-			return null;
+			if (model == null)
+				throw new ArgumentNullException ("model");
+			this.model = model;
 		}
+		
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+		
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
 
-		void TaskTimerCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
+		void TaskNotesCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
 		                            TreeModel treeModel, TreeIter iter)
 		{
-			var task = treeModel.GetValue (iter, 0) as ITask;
-			TaskCompleteTimer timer;
 			var crp = cell as CellRendererPixbuf;
-			if (task == null || !timeoutTargets.TryGetValue (task, out timer)) {
+			var task = model.GetValue (iter, 0) as ITask;
+			if (task == null) {
 				crp.Pixbuf = null;
 				return;
 			}
-
-			crp.Pixbuf = timer.CurrentAnimPixbuf;
+			
+			crp.Pixbuf = task.HasNotes ? notePixbuf : null;
 		}
-
-		ConcurrentDictionary<ITask, TaskCompleteTimer> timeoutTargets;
-		IPreferences preferences;
+		
 		TreeModel model;
 	}
 }
diff --git a/src/Addins/Gtk.Tasque.Columns/PriorityColumn.cs b/src/Addins/Gtk.Tasque.Columns/PriorityColumn.cs
new file mode 100644
index 0000000..8d0a685
--- /dev/null
+++ b/src/Addins/Gtk.Tasque.Columns/PriorityColumn.cs
@@ -0,0 +1,160 @@
+//
+// PriorityColumn.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2013 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;
+
+namespace Gtk.Tasque
+{
+	// TODO: Use xml addin description model to provide localized column name
+	[TaskColumnExtension ("Priority")]
+	public class PriorityColumn : ITaskColumn
+	{
+		public PriorityColumn ()
+		{
+			TreeViewColumn = new TreeViewColumn {
+				Title = Catalog.GetString ("Priority"),
+				Sizing = TreeViewColumnSizing.Fixed,
+				Alignment = 0.5f,
+				FixedWidth = 30,
+				Resizable = false,
+				Clickable = true
+			};
+			
+			var priorityStore = new ListStore (typeof (string));
+			priorityStore.AppendValues (Catalog.GetString ("1")); // High
+			priorityStore.AppendValues (Catalog.GetString ("2")); // Medium
+			priorityStore.AppendValues (Catalog.GetString ("3")); // Low
+			priorityStore.AppendValues (Catalog.GetString ("-")); // None
+			
+			var renderer = new CellRendererCombo {
+				Editable = true,
+				HasEntry = false,
+				Model = priorityStore,
+				TextColumn = 0,
+				Xalign = 0.5f
+			};
+			
+			renderer.EditingStarted += (o, args) => {
+				TreeIter iter;
+				var path = new TreePath (args.Path);
+				if (!model.GetIter (out iter, path))
+					return;
+				
+				var task = model.GetValue (iter, 0) as ITask;
+				if (task == null)
+					return;
+				
+				taskBeingEdited = new TaskBeingEdited (task, iter, path);
+				if (CellEditingStarted != null)
+					CellEditingStarted (this, new TaskRowEditingEventArgs (task, iter, path));
+			};
+			
+			renderer.EditingCanceled += delegate { EndCellEditing (); };
+			
+			renderer.Edited += (o, args) => {
+				TreeIter iter;
+				var path = new TreePath (args.Path);
+				if (model.GetIter (out iter, path)) {
+					TaskPriority newPriority;
+					var newText = args.NewText;
+					if (newText == Catalog.GetString ("3"))
+						newPriority = TaskPriority.Low;
+					else if (newText == Catalog.GetString ("2"))
+						newPriority = TaskPriority.Medium;
+					else if (newText == Catalog.GetString ("1"))
+						newPriority = TaskPriority.High;
+					else
+						newPriority = TaskPriority.None;
+				
+					// Update the priority if it's different
+					var task = model.GetValue (iter, 0) as ITask;
+					if (task.Priority != newPriority)
+						task.Priority = newPriority;
+				}
+				EndCellEditing ();
+			};
+
+			TreeViewColumn.PackStart (renderer, true);
+			TreeViewColumn.SetCellDataFunc (renderer, TaskPriorityCellDataFunc);
+		}
+
+		public int DefaultPosition { get { return 1; } }
+
+		public TreeViewColumn TreeViewColumn { get; private set; }
+
+		public void Initialize (TreeModel model, TaskView view,  IPreferences preferences)
+		{
+			if (model == null)
+				throw new ArgumentNullException ("model");
+			this.model = model;
+		}
+
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
+		
+		void EndCellEditing ()
+		{
+			if (taskBeingEdited == null)
+				return;
+			
+			if (CellEditingFinished != null)
+				CellEditingFinished (this, new TaskRowEditingEventArgs (taskBeingEdited.Task,
+				                                                        taskBeingEdited.Iter,
+				                                                        taskBeingEdited.Path));
+			taskBeingEdited = null;
+		}
+		
+		void TaskPriorityCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
+		                               TreeModel treeModel, TreeIter iter)
+		{
+			// TODO: Add bold (for high), light (for None), and also colors to priority?
+			var crc = cell as CellRendererCombo;
+			var task = model.GetValue (iter, 0) as ITask;
+			if (task == null)
+				return;
+			
+			switch (task.Priority) {
+			case TaskPriority.Low:
+				crc.Text = Catalog.GetString ("3");
+				break;
+			case TaskPriority.Medium:
+				crc.Text = Catalog.GetString ("2");
+				break;
+			case TaskPriority.High:
+				crc.Text = Catalog.GetString ("1");
+				break;
+			default:
+				crc.Text = Catalog.GetString ("-");
+				break;
+			}
+		}
+		
+		TreeModel model;
+		TaskBeingEdited taskBeingEdited;
+	}
+}
diff --git a/src/Gtk.Tasque/TaskCompleteTimerState.cs b/src/Addins/Gtk.Tasque.Columns/Properties/AssemblyInfo.cs
similarity index 81%
copy from src/Gtk.Tasque/TaskCompleteTimerState.cs
copy to src/Addins/Gtk.Tasque.Columns/Properties/AssemblyInfo.cs
index 438e500..5437498 100644
--- a/src/Gtk.Tasque/TaskCompleteTimerState.cs
+++ b/src/Addins/Gtk.Tasque.Columns/Properties/AssemblyInfo.cs
@@ -1,5 +1,5 @@
 //
-// TaskCompleteTimerState.cs
+// AssemblyInfo.cs
 //
 // Author:
 //       Antonius Riha <antoniusriha gmail com>
@@ -23,13 +23,10 @@
 // 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.
-namespace Gtk.Tasque
-{
-	public enum TaskCompleteTimerState
-	{
-		NotStarted,
-		Running,
-		Paused,
-		Stopped
-	}
-}
+using System.Reflection;
+using Mono.Addins;
+
+[assembly: AssemblyTitle("Gtk.Tasque.Columns")]
+[assembly: AssemblyDescription("Standard Task Columns for the Gtk# frontend of Tasque")]
+[assembly: Addin]
+[assembly: AddinDependency ("GtkTasque", "0.1.13")]
diff --git a/src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs b/src/Addins/Gtk.Tasque.Columns/TaskBeingEdited.cs
similarity index 81%
copy from src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs
copy to src/Addins/Gtk.Tasque.Columns/TaskBeingEdited.cs
index 7d52791..b512014 100644
--- a/src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs
+++ b/src/Addins/Gtk.Tasque.Columns/TaskBeingEdited.cs
@@ -1,5 +1,5 @@
 //
-// TaskCompleteTimerExpired.cs
+// TaskBeingEdited.cs
 //
 // Author:
 //       Antonius Riha <antoniusriha gmail com>
@@ -28,18 +28,21 @@ using Tasque;
 
 namespace Gtk.Tasque
 {
-	public class TaskCompleteTimerStoppedEventArgs : EventArgs
+	class TaskBeingEdited
 	{
-		public TaskCompleteTimerStoppedEventArgs (ITask task, bool canceled)
+		public TaskBeingEdited (ITask task, TreeIter iter, TreePath path)
 		{
+			if (path == null)
+				throw new ArgumentNullException ("path");
 			if (task == null)
 				throw new ArgumentNullException ("task");
 			Task = task;
-			Canceled = canceled;
+			Iter = iter;
+			Path = path;
 		}
 		
-		public bool Canceled { get; private set; }
-		
 		public ITask Task { get; private set; }
+		public TreeIter Iter { get; private set; }
+		public TreePath Path { get; private set; }
 	}
 }
diff --git a/src/Addins/Gtk.Tasque.Columns/TaskNameColumn.cs b/src/Addins/Gtk.Tasque.Columns/TaskNameColumn.cs
new file mode 100644
index 0000000..3d62ec8
--- /dev/null
+++ b/src/Addins/Gtk.Tasque.Columns/TaskNameColumn.cs
@@ -0,0 +1,179 @@
+//
+// NameColumn.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2013 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;
+using GLib;
+
+namespace Gtk.Tasque
+{
+	// TODO: Use xml addin description model to provide localized column name
+	[TaskColumnExtension ("Task Name")]
+	public class TaskNameColumn : ITaskColumn
+	{
+		public TaskNameColumn ()
+		{
+			TreeViewColumn = new TreeViewColumn {
+				Title = Catalog.GetString ("Task Name"),
+				Sizing = TreeViewColumnSizing.Autosize,
+				Expand = true,
+				Resizable = true
+			};
+			
+			// TODO: Add in code to determine how wide we should make the name
+			// column.
+			// TODO: Add in code to readjust the size of the name column if the
+			// user resizes the Task Window.
+			//column.FixedWidth = 250;
+			
+			var renderer = new CellRendererText { Editable = true };
+
+			renderer.EditingStarted += (o, args) => {
+				TreeIter iter;
+				var path = new TreePath (args.Path);
+				if (!model.GetIter (out iter, path))
+					return;
+				
+				var task = model.GetValue (iter, 0) as ITask;
+				if (task == null)
+					return;
+				
+				taskBeingEdited = new TaskBeingEdited (task, iter, path);
+				if (CellEditingStarted != null)
+					CellEditingStarted (this, new TaskRowEditingEventArgs (task, iter, path));
+			};
+			
+			renderer.EditingCanceled += delegate { EndCellEditing (); };
+			
+			renderer.Edited += (o, args) => {
+				TreeIter iter;
+				var path = new TreePath (args.Path);
+				if (model.GetIter (out iter, path)) {
+					var task = model.GetValue (iter, 0) as ITask;
+					if (task == null)
+						return;
+					
+					var newText = args.NewText;
+					// Attempt to derive due date information from text.
+					if (preferences.GetBool (PreferencesKeys.ParseDateEnabledKey) &&
+					    task.State == TaskState.Active &&
+					    task.DueDate == DateTime.MinValue) {
+						
+						string parsedTaskText;
+						DateTime parsedDueDate;
+						TaskParser.Instance.TryParse (newText, out parsedTaskText, out parsedDueDate);
+						
+						if (parsedDueDate != DateTime.MinValue)
+							task.DueDate = parsedDueDate;
+						newText = parsedTaskText;
+					}
+					
+					task.Name = newText;
+				}
+				EndCellEditing ();
+			};
+			
+			TreeViewColumn.PackStart (renderer, true);
+			TreeViewColumn.SetCellDataFunc (renderer, TaskNameTextCellDataFunc);
+		}
+
+		public int DefaultPosition { get { return 2; } }
+
+		public TreeViewColumn TreeViewColumn { get; private set; }
+		
+		public void Initialize (TreeModel model, TaskView view, IPreferences preferences)
+		{
+			if (preferences == null)
+				throw new ArgumentNullException ("preferences");
+			this.preferences = preferences;
+			if (model == null)
+				throw new ArgumentNullException ("model");
+			this.model = model;
+		}
+		
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+		
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
+
+		void EndCellEditing ()
+		{
+			if (taskBeingEdited == null)
+				return;
+			
+			if (CellEditingFinished != null)
+				CellEditingFinished (this, new TaskRowEditingEventArgs (taskBeingEdited.Task,
+				                                                        taskBeingEdited.Iter,
+				                                                        taskBeingEdited.Path));
+			taskBeingEdited = null;
+		}
+		
+		void TaskNameTextCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
+		                               TreeModel treeModel, TreeIter iter)
+		{
+			var crt = cell as CellRendererText;
+			crt.Ellipsize = Pango.EllipsizeMode.End;
+			var task = model.GetValue (iter, 0) as ITask;
+			if (task == null) {
+				crt.Text = string.Empty;
+				return;
+			}
+			
+			var formatString = "{0}";
+			var todayTasksColor = preferences.Get (PreferencesKeys.TodayTaskTextColor);
+			var overdueTaskColor = preferences.Get (PreferencesKeys.OverdueTaskTextColor);
+			
+			if (!task.IsComplete && task.DueDate.Date == DateTime.Today.Date)
+				crt.Foreground = todayTasksColor;
+			// Overdue and the task has a date assigned to it.
+			else if (!task.IsComplete && task.DueDate < DateTime.Today
+			         && task.DueDate != DateTime.MinValue)
+				crt.Foreground = overdueTaskColor;
+			
+			switch (task.State) {
+			// TODO: Reimplement the feature below
+//			case TaskState.Active:
+//				// Strikeout the text
+//				var timer = timerCol.GetTimer (task);
+//				if (timer != null && timer.State == TaskCompleteTimerState.Running)
+//					formatString = "<span strikethrough=\"true\">{0}</span>";
+//				break;
+			case TaskState.Deleted:
+			case TaskState.Completed:
+				// Gray out the text and add strikeout
+				// TODO: Determine the grayed-out text color appropriate for the current theme
+				formatString = "<span strikethrough=\"true\">{0}</span>";
+				crt.Foreground = "#AAAAAA";
+				break;
+			}
+			
+			crt.Markup = string.Format (formatString, Markup.EscapeText (task.Name));
+		}
+
+		TreeModel model;
+		IPreferences preferences;
+		TaskBeingEdited taskBeingEdited;
+	}
+}
diff --git a/src/Addins/Gtk.Tasque.TimerCompleteColumns/CompleteWithTimerColumn.cs b/src/Addins/Gtk.Tasque.TimerCompleteColumns/CompleteWithTimerColumn.cs
new file mode 100644
index 0000000..5a89fc5
--- /dev/null
+++ b/src/Addins/Gtk.Tasque.TimerCompleteColumns/CompleteWithTimerColumn.cs
@@ -0,0 +1,136 @@
+//
+// CompleteWithTimerColumn.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2013 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;
+
+namespace Gtk.Tasque
+{
+	// TODO: Use xml addin description model to provide localized column name
+	[TaskColumnExtension ("Completed", null, "CompleteColumn", "TimerColumn")]
+	public class CompleteWithTimerColumn : ITaskColumn
+	{
+		public CompleteWithTimerColumn ()
+		{
+			TreeViewColumn = new TreeViewColumn {
+				Title = Catalog.GetString ("Completed"),
+				Sizing = TreeViewColumnSizing.Autosize,
+				Resizable = false,
+				Clickable = true
+			};
+			
+			var renderer = new CellRendererToggle ();
+			renderer.Toggled += OnTaskToggled;
+			TreeViewColumn.PackStart (renderer, false);
+			TreeViewColumn.SetCellDataFunc (renderer, TaskToggleCellDataFunc);
+		}
+		
+		public int DefaultPosition { get { return 0; } }
+
+		public TreeViewColumn TreeViewColumn { get; private set; }
+
+		public void Initialize (TreeModel model, TaskView view, IPreferences preferences)
+		{
+			if (view == null)
+				throw new ArgumentNullException ("view");
+			this.view = view;
+			if (preferences == null)
+				throw new ArgumentNullException ("preferences");
+			this.preferences = preferences;
+			if (model == null)
+				throw new ArgumentNullException ("model");
+			this.model = model;
+		}
+
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
+		
+		void OnTaskToggled (object o, ToggledArgs args)
+		{
+			Logger.Debug ("OnTaskToggled");
+			TreeIter iter;
+			var path = new TreePath (args.Path);
+			if (!model.GetIter (out iter, path))
+				return; // Do nothing
+			
+			var task = model.GetValue (iter, 0) as ITask;
+			if (task == null)
+				return;
+			
+			// remove any timer set up on this task
+			var timerCol = (TimerColumn)view.GetColumn (typeof (TimerColumn));
+			var tmr = timerCol.GetTimer (task);
+			if (tmr != null)
+				tmr.Cancel ();
+			
+			if (task.State == TaskState.Active) {
+				bool showCompletedTasks = preferences.GetBool (PreferencesKeys.ShowCompletedTasksKey);
+				
+				// When showCompletedTasks is true, complete the tasks right
+				// away.  Otherwise, set a timer and show the timer animation
+				// before marking the task completed.
+				if (showCompletedTasks) {
+					task.Complete ();
+					var statusMsg = Catalog.GetString ("Task Completed");
+					TaskWindow.ShowStatus (statusMsg, 5000);
+				} else {
+					var timer = timerCol.CreateTimer (task);
+					timer.TimerStopped += (s, e) => {
+						if (!e.Canceled)
+							e.Task.Complete ();
+					};
+					timer.Tick += (s, e) => {
+						var statusMsg = string.Format (
+							Catalog.GetString ("Completing Task In: {0}"), e.CountdownTick);
+						TaskWindow.ShowStatus (statusMsg, 2000);
+					};
+					timer.Start ();
+				}
+			} else {
+				var statusMsg = Catalog.GetString ("Action Canceled");
+				TaskWindow.ShowStatus (statusMsg, 5000);
+				task.Activate ();
+			}
+		}
+		
+		void TaskToggleCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
+		                             TreeModel treeModel, TreeIter iter)
+		{
+			var crt = cell as CellRendererToggle;
+			var task = model.GetValue (iter, 0) as ITask;
+			if (task == null)
+				crt.Active = false;
+			else {
+				var timerCol = (TimerColumn) view.GetColumn (typeof (TimerColumn));
+				crt.Active = !(task.State == TaskState.Active && timerCol.GetTimer (task) == null);
+			}
+		}
+		
+		TreeModel model;
+		IPreferences preferences;
+		TaskView view;
+	}
+}
diff --git a/src/Addins/Gtk.Tasque.TimerCompleteColumns/Gtk.Tasque.TimerCompleteColumns.csproj b/src/Addins/Gtk.Tasque.TimerCompleteColumns/Gtk.Tasque.TimerCompleteColumns.csproj
new file mode 100644
index 0000000..c755731
--- /dev/null
+++ b/src/Addins/Gtk.Tasque.TimerCompleteColumns/Gtk.Tasque.TimerCompleteColumns.csproj
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003";>
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">LinuxDebug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>10.0.0</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{BFF82E44-DE40-441A-95C8-95269AB9C53D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <OutputPath>.</OutputPath>
+    <RootNamespace>Gtk.Tasque.TimerCompleteColumns</RootNamespace>
+    <AssemblyName>Gtk.Tasque.TimerCompleteColumns</AssemblyName>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ReleaseVersion>0.1.13</ReleaseVersion>
+    <PackageName>tasque</PackageName>
+    <TopBuildDir>..\..\..</TopBuildDir>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'LinuxDebug|AnyCPU' ">
+    <DebugSymbols>True</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>False</Optimize>
+    <DefineConstants>DEBUG;TRACE;LINUX</DefineConstants>
+    <OutputPath>..\..\..\build\bin\lib\tasque</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'LinuxRelease|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>True</Optimize>
+    <DefineConstants>TRACE;LINUX</DefineConstants>
+    <OutputPath>..\..\..\build\bin\lib\tasque</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'WinDebug|AnyCPU' ">
+    <DebugSymbols>True</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>False</Optimize>
+    <DefineConstants>DEBUG;TRACE;WIN</DefineConstants>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'WinRelease|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>True</Optimize>
+    <DefineConstants>TRACE;WIN</DefineConstants>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="Mono.Addins, Version=0.6.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756">
+      <Private>False</Private>
+      <Package>mono-addins</Package>
+    </Reference>
+    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+      <Private>False</Private>
+      <Package>gtk-sharp-2.0</Package>
+    </Reference>
+    <Reference Include="Mono.Posix" />
+    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+      <Private>False</Private>
+      <Package>gtk-sharp-2.0</Package>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="CompleteWithTimerColumn.cs" />
+    <Compile Include="TimerColumn.cs" />
+    <Compile Include="..\..\..\build\CommonAssemblyInfo.cs">
+      <Link>Properties\CommonAssemblyInfo.cs</Link>
+    </Compile>
+    <Compile Include="TaskCompleteTimer.cs" />
+    <Compile Include="TaskCompleteTimerState.cs" />
+    <Compile Include="TaskCompleteTimerStoppedEventArgs.cs" />
+    <Compile Include="TaskCompleteTimerTickEventArgs.cs" />
+  </ItemGroup>
+  <Import Project="..\..\..\build\X.Common.targets" />
+  <ItemGroup>
+    <Folder Include="Properties\" />
+  </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>
+    </ProjectReference>
+    <ProjectReference Include="..\Gtk.Tasque.Columns\Gtk.Tasque.Columns.csproj">
+      <Project>{C8454FAA-CE15-4202-9B93-4B536A8BC32D}</Project>
+      <Name>Gtk.Tasque.Columns</Name>
+    </ProjectReference>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/Gtk.Tasque/TaskCompleteTimerState.cs b/src/Addins/Gtk.Tasque.TimerCompleteColumns/Properties/AssemblyInfo.cs
similarity index 80%
copy from src/Gtk.Tasque/TaskCompleteTimerState.cs
copy to src/Addins/Gtk.Tasque.TimerCompleteColumns/Properties/AssemblyInfo.cs
index 438e500..3182b10 100644
--- a/src/Gtk.Tasque/TaskCompleteTimerState.cs
+++ b/src/Addins/Gtk.Tasque.TimerCompleteColumns/Properties/AssemblyInfo.cs
@@ -1,5 +1,5 @@
 //
-// TaskCompleteTimerState.cs
+// AssemblyInfo.cs
 //
 // Author:
 //       Antonius Riha <antoniusriha gmail com>
@@ -23,13 +23,10 @@
 // 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.
-namespace Gtk.Tasque
-{
-	public enum TaskCompleteTimerState
-	{
-		NotStarted,
-		Running,
-		Paused,
-		Stopped
-	}
-}
+using System.Reflection;
+using Mono.Addins;
+
+[assembly: AssemblyTitle("Gtk.Tasque.TimerCompleteColumns")]
+[assembly: AssemblyDescription("Timed completion columns for the Gtk# frontend of Tasque")]
+[assembly: Addin]
+[assembly: AddinDependency ("GtkTasque", "0.1.13")]
diff --git a/src/Gtk.Tasque/TaskCompleteTimer.cs b/src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimer.cs
similarity index 100%
rename from src/Gtk.Tasque/TaskCompleteTimer.cs
rename to src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimer.cs
diff --git a/src/Gtk.Tasque/TaskCompleteTimerState.cs b/src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimerState.cs
similarity index 100%
rename from src/Gtk.Tasque/TaskCompleteTimerState.cs
rename to src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimerState.cs
diff --git a/src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs b/src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimerStoppedEventArgs.cs
similarity index 100%
copy from src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs
copy to src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimerStoppedEventArgs.cs
diff --git a/src/Gtk.Tasque/TaskCompleteTimerTickEventArgs.cs b/src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimerTickEventArgs.cs
similarity index 100%
rename from src/Gtk.Tasque/TaskCompleteTimerTickEventArgs.cs
rename to src/Addins/Gtk.Tasque.TimerCompleteColumns/TaskCompleteTimerTickEventArgs.cs
diff --git a/src/Gtk.Tasque/TimerColumn.cs b/src/Addins/Gtk.Tasque.TimerCompleteColumns/TimerColumn.cs
similarity index 80%
rename from src/Gtk.Tasque/TimerColumn.cs
rename to src/Addins/Gtk.Tasque.TimerCompleteColumns/TimerColumn.cs
index cea7ffa..9cca6a4 100644
--- a/src/Gtk.Tasque/TimerColumn.cs
+++ b/src/Addins/Gtk.Tasque.TimerCompleteColumns/TimerColumn.cs
@@ -30,17 +30,12 @@ using Tasque;
 
 namespace Gtk.Tasque
 {
-	public class TimerColumn
-	{
-		public TimerColumn (IPreferences preferences, TreeModel model)
+	// TODO: Use xml addin description model to provide localized column name
+	[TaskColumnExtension ("Timer")]
+	public class TimerColumn : ITaskColumn
+	{		
+		public TimerColumn ()
 		{
-			if (model == null)
-				throw new ArgumentNullException ("model");
-			this.model = model;
-			if (preferences == null)
-				throw new ArgumentNullException ("preferences");
-			this.preferences = preferences;
-
 			timeoutTargets = new ConcurrentDictionary<ITask, TaskCompleteTimer> ();
 
 			TreeViewColumn = new TreeViewColumn {
@@ -55,8 +50,32 @@ namespace Gtk.Tasque
 			TreeViewColumn.SetCellDataFunc (renderer, TaskTimerCellDataFunc);
 		}
 		
+		public int DefaultPosition { get { return int.MaxValue; } }
+		
 		public TreeViewColumn TreeViewColumn { get; private set; }
 		
+		public void Initialize (TreeModel model, TaskView view, IPreferences preferences)
+		{
+			if (model == null)
+				throw new ArgumentNullException ("model");
+			this.model = model;
+			if (preferences == null)
+				throw new ArgumentNullException ("preferences");
+			this.preferences = preferences;
+			
+			view.RowEditingStarted += (sender, e) => {
+				var timer = GetTimer (e.Task);
+				if (timer != null && timer.State == TaskCompleteTimerState.Running)
+					timer.Pause ();
+			};
+			
+			view.RowEditingFinished += (sender, e) => {
+				var timer = GetTimer (e.Task);
+				if (timer != null && timer.State == TaskCompleteTimerState.Paused)
+					timer.Resume ();
+			};
+		}
+		
 		public TaskCompleteTimer CreateTimer (ITask task)
 		{
 			if (task == null)
@@ -93,6 +112,9 @@ namespace Gtk.Tasque
 				return timer;
 			return null;
 		}
+		
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+		public event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
 
 		void TaskTimerCellDataFunc (TreeViewColumn treeColumn, CellRenderer cell,
 		                            TreeModel treeModel, TreeIter iter)
diff --git a/src/Gtk.Tasque/Gtk.Tasque.csproj b/src/Gtk.Tasque/Gtk.Tasque.csproj
index 14d6d29..ac89ef7 100644
--- a/src/Gtk.Tasque/Gtk.Tasque.csproj
+++ b/src/Gtk.Tasque/Gtk.Tasque.csproj
@@ -73,6 +73,10 @@
       <Private>False</Private>
     </Reference>
     <Reference Include="System.Core" />
+    <Reference Include="Mono.Addins, Version=0.6.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756">
+      <Private>False</Private>
+      <Package>mono-addins</Package>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\libtasque\libtasque.csproj">
@@ -94,7 +98,6 @@
     <Compile Include="PreferencesDialog.cs" />
     <Compile Include="TaskCalendar.cs" />
     <Compile Include="TaskGroup.cs" />
-    <Compile Include="TaskTreeView.cs" />
     <Compile Include="TaskWindow.cs" />
     <Compile Include="Utilities.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
@@ -105,11 +108,10 @@
     <Compile Include="GtkLinuxApplication.cs" />
     <Compile Include="GtkWinApplication.cs" />
     <Compile Include="TreeModelListAdapter.cs" />
-    <Compile Include="TimerColumn.cs" />
-    <Compile Include="TaskCompleteTimer.cs" />
-    <Compile Include="TaskCompleteTimerTickEventArgs.cs" />
-    <Compile Include="TaskCompleteTimerState.cs" />
-    <Compile Include="TaskCompleteTimerStoppedEventArgs.cs" />
+    <Compile Include="ITaskColumn.cs" />
+    <Compile Include="TaskColumnExtensionAttribute.cs" />
+    <Compile Include="TaskRowEditingEventArgs.cs" />
+    <Compile Include="TaskView.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 879031c..63210f1 100644
--- a/src/Gtk.Tasque/GtkApplicationBase.cs
+++ b/src/Gtk.Tasque/GtkApplicationBase.cs
@@ -27,9 +27,11 @@
 using System;
 using System.Diagnostics;
 using System.IO;
+using Mono.Addins;
 using Mono.Unix;
 using Tasque;
 using Gtk;
+
 #if ENABLE_NOTIFY_SHARP
 using Notifications;
 #endif
@@ -40,6 +42,9 @@ namespace Gtk.Tasque
 	{
 		protected GtkApplicationBase ()
 		{
+			AddinManager.Initialize ();
+			AddinManager.Registry.Update ();
+			
 			confDir = Path.Combine (Environment.GetFolderPath (
 				Environment.SpecialFolder.ApplicationData), "tasque");
 			if (!Directory.Exists (confDir))
diff --git a/src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs b/src/Gtk.Tasque/ITaskColumn.cs
similarity index 73%
copy from src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs
copy to src/Gtk.Tasque/ITaskColumn.cs
index 7d52791..e7a387c 100644
--- a/src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs
+++ b/src/Gtk.Tasque/ITaskColumn.cs
@@ -1,5 +1,5 @@
 //
-// TaskCompleteTimerExpired.cs
+// ITaskColumn.cs
 //
 // Author:
 //       Antonius Riha <antoniusriha gmail com>
@@ -24,22 +24,18 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 using System;
+using Mono.Addins;
 using Tasque;
 
 namespace Gtk.Tasque
 {
-	public class TaskCompleteTimerStoppedEventArgs : EventArgs
+	[TypeExtensionPoint (ExtensionAttributeType = typeof (TaskColumnExtensionAttribute))]
+	public interface ITaskColumn
 	{
-		public TaskCompleteTimerStoppedEventArgs (ITask task, bool canceled)
-		{
-			if (task == null)
-				throw new ArgumentNullException ("task");
-			Task = task;
-			Canceled = canceled;
-		}
-		
-		public bool Canceled { get; private set; }
-		
-		public ITask Task { get; private set; }
+		int DefaultPosition { get; }
+		TreeViewColumn TreeViewColumn { get; }
+		void Initialize (TreeModel model, TaskView view, IPreferences preferences);
+		event EventHandler<TaskRowEditingEventArgs> CellEditingStarted;
+		event EventHandler<TaskRowEditingEventArgs> CellEditingFinished;
 	}
 }
diff --git a/src/Gtk.Tasque/Properties/AssemblyInfo.cs b/src/Gtk.Tasque/Properties/AssemblyInfo.cs
index a664ee1..58b52ed 100644
--- a/src/Gtk.Tasque/Properties/AssemblyInfo.cs
+++ b/src/Gtk.Tasque/Properties/AssemblyInfo.cs
@@ -24,6 +24,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 using System.Reflection;
+using Mono.Addins;
 
 [assembly: AssemblyTitle("Gtk.Tasque")]
 [assembly: AssemblyDescription("Gtk# frontend for Tasque")]
+[assembly: AddinRoot ("GtkTasque", "0.1.13")]
diff --git a/src/Gtk.Tasque/TaskColumnExtensionAttribute.cs b/src/Gtk.Tasque/TaskColumnExtensionAttribute.cs
new file mode 100644
index 0000000..0eac6ff
--- /dev/null
+++ b/src/Gtk.Tasque/TaskColumnExtensionAttribute.cs
@@ -0,0 +1,65 @@
+//
+// TaskColumnExtensionAttribute.cs
+//
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+//
+// Copyright (c) 2013 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.Addins;
+
+namespace Gtk.Tasque
+{
+	public class TaskColumnExtensionAttribute : CustomExtensionAttribute
+	{
+		public TaskColumnExtensionAttribute () : this (null, null, null, null) {}
+		
+		public TaskColumnExtensionAttribute ([NodeAttribute ("Name")] string name)
+		: this (name, null, null, null) {}
+		
+		public TaskColumnExtensionAttribute ([NodeAttribute ("Name")] string name,
+		                                     [NodeAttribute ("Description")] string description)
+		: this (name, description, null, null) {}
+		
+		public TaskColumnExtensionAttribute ([NodeAttribute ("Name")] string name,
+		                                     [NodeAttribute ("Description")] string description,
+		                                     [NodeAttribute ("ReplacedColumnTypeName")] string replacedColumnTypeName,
+		                                     [NodeAttribute ("RequiredColumnTypeName")] string requiredColumnTypeName)
+		{
+			Name = name;
+			Description = description;
+			ReplacedColumnTypeName = replacedColumnTypeName;
+			RequiredColumnTypeName = requiredColumnTypeName;
+		}
+		
+		[NodeAttribute]
+		public string Name { get; set; }
+		
+		[NodeAttribute]
+		public string Description { get; set; }
+		
+		[NodeAttribute]
+		public string ReplacedColumnTypeName { get; set; }
+		
+		[NodeAttribute]
+		public string RequiredColumnTypeName { get; set; }
+	}
+}
diff --git a/src/Gtk.Tasque/TaskGroup.cs b/src/Gtk.Tasque/TaskGroup.cs
index cefe638..d8b4df6 100644
--- a/src/Gtk.Tasque/TaskGroup.cs
+++ b/src/Gtk.Tasque/TaskGroup.cs
@@ -18,7 +18,7 @@ namespace Tasque
 	public class TaskGroup : Gtk.VBox
 	{
 		Gtk.Label header;
-		TaskTreeView treeView;
+		TaskView taskView;
 		TreeModel treeModel;
 		Gtk.HBox extraWidgetHBox;
 		Gtk.Widget extraWidget;
@@ -88,13 +88,13 @@ namespace Tasque
 			//
 			// Group TreeView
 			//
-			treeView = new TaskTreeView (treeModel, application.Preferences);
-			treeView.Show ();
-			PackStart (treeView, true, true, 0);
+			taskView = new TaskView (treeModel, application.Preferences);
+			taskView.TreeView.Show ();
+			PackStart (taskView.TreeView, true, true, 0);
 			
-			treeView.NumberOfTasksChanged += OnNumberOfTasksChanged;
-			treeView.RowActivated += OnRowActivated;
-			treeView.ButtonPressEvent += OnButtonPressed;
+			taskView.NumberOfTasksChanged += OnNumberOfTasksChanged;
+			taskView.TreeView.RowActivated += OnRowActivated;
+			taskView.TreeView.ButtonPressEvent += OnButtonPressed;
 		}
 		#endregion // Constructor
 		
@@ -187,16 +187,16 @@ namespace Tasque
 			}
 		}
 		
-		public Gtk.TreeView TreeView
+		public TaskView TaskView
 		{
-			get { return this.treeView; }
+			get { return this.taskView; }
 		}
 		#endregion // Public Properties
 		
 		#region Public Methods
 		public void Refilter (ICategory selectedCategory)
 		{
-			treeView.Refilter (selectedCategory);
+			taskView.Refilter (selectedCategory);
 		}
 		
 		/// <summary>
@@ -218,7 +218,7 @@ namespace Tasque
 		public bool ContainsTask (ITask task, out Gtk.TreeIter iter)
 		{
 			Gtk.TreeIter tempIter;
-			Gtk.TreeModel model = treeView.Model;
+			Gtk.TreeModel model = taskView.Model;
 			
 			iter = Gtk.TreeIter.Zero;
 			
@@ -239,19 +239,19 @@ namespace Tasque
 		
 		public int GetNChildren(Gtk.TreeIter iter)
 		{
-			return treeView.Model.IterNChildren();
+			return taskView.Model.IterNChildren();
 		}
 			
 		// Find the index within the tree
 		public int GetIterIndex (Gtk.TreeIter iter)
 		{
-			Gtk.TreePath path = treeView.Model.GetPath (iter);
+			Gtk.TreePath path = taskView.Model.GetPath (iter);
 			Gdk.Rectangle rect =
-				treeView.GetBackgroundArea (path, treeView.GetColumn (0));
+				taskView.TreeView.GetBackgroundArea (path, taskView.TreeView.GetColumn (0));
 			
 			int pos = 0;
 			Gtk.TreeIter tempIter;
-			Gtk.TreeModel model = treeView.Model;
+			Gtk.TreeModel model = taskView.Model;
 			ITask task = model.GetValue (iter, 0) as ITask;
 			
 			if (!model.GetIterFirst (out tempIter))
@@ -273,14 +273,14 @@ namespace Tasque
 		// Find the height position within the tree
 		public int GetIterPos (Gtk.TreeIter iter)
 		{
-			Gtk.TreePath path = treeView.Model.GetPath (iter);
+			Gtk.TreePath path = taskView.Model.GetPath (iter);
 			Gdk.Rectangle rect =
-				treeView.GetBackgroundArea (path, treeView.GetColumn (0));
+				taskView.TreeView.GetBackgroundArea (path, taskView.TreeView.GetColumn (0));
 			int height = rect.Height;
 			
 			int pos = 0;
 			Gtk.TreeIter tempIter;
-			Gtk.TreeModel model = treeView.Model;
+			Gtk.TreeModel model = taskView.Model;
 			ITask task = model.GetValue (iter, 0) as ITask;
 			
 			if (!model.GetIterFirst (out tempIter))
@@ -310,14 +310,14 @@ namespace Tasque
 			// Select the iter and go into editing mode on the task name
 			
 			// TODO: Figure out a way to NOT hard-code the column number
-			Gtk.TreeViewColumn nameColumn = treeView.Columns [2];
+			Gtk.TreeViewColumn nameColumn = taskView.TreeView.Columns [2];
 			Gtk.CellRendererText nameCellRendererText =
 				nameColumn.CellRenderers [0] as Gtk.CellRendererText;
-			path = treeView.Model.GetPath (iter);
+			path = taskView.Model.GetPath (iter);
 			
-			treeView.Model.IterNChildren();
+			taskView.Model.IterNChildren();
 				
-			treeView.SetCursorOnCell (path, nameColumn, nameCellRendererText, true);
+			taskView.TreeView.SetCursorOnCell (path, nameColumn, nameCellRendererText, true);
 		}
 		#endregion // Methods
 		
@@ -330,7 +330,7 @@ namespace Tasque
 		{
 			base.OnRealized ();
 			
-			if (treeView.GetNumberOfTasks () == 0
+			if (taskView.GetNumberOfTasks () == 0
 					&& (!Model.ShowCompletedTasks || hideWhenEmpty))
 				Hide ();
 			else
@@ -416,7 +416,7 @@ namespace Tasque
 		{
 			//Logger.Debug ("TaskGroup (\"{0}\").OnNumberOfTasksChanged ()", DisplayName);
 			// Check to see whether this group should be hidden or shown.
-			if (treeView.GetNumberOfTasks () == 0
+			if (taskView.GetNumberOfTasks () == 0
 					&& (!Model.ShowCompletedTasks || hideWhenEmpty))
 				Hide ();
 			else
diff --git a/src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs b/src/Gtk.Tasque/TaskRowEditingEventArgs.cs
similarity index 80%
rename from src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs
rename to src/Gtk.Tasque/TaskRowEditingEventArgs.cs
index 7d52791..bbd12f1 100644
--- a/src/Gtk.Tasque/TaskCompleteTimerStoppedEventArgs.cs
+++ b/src/Gtk.Tasque/TaskRowEditingEventArgs.cs
@@ -1,5 +1,5 @@
 //
-// TaskCompleteTimerExpired.cs
+// RowEditingEventArgs.cs
 //
 // Author:
 //       Antonius Riha <antoniusriha gmail com>
@@ -28,18 +28,21 @@ using Tasque;
 
 namespace Gtk.Tasque
 {
-	public class TaskCompleteTimerStoppedEventArgs : EventArgs
+	public class TaskRowEditingEventArgs : EventArgs
 	{
-		public TaskCompleteTimerStoppedEventArgs (ITask task, bool canceled)
+		public TaskRowEditingEventArgs (ITask task, TreeIter iter, TreePath path)
 		{
 			if (task == null)
 				throw new ArgumentNullException ("task");
 			Task = task;
-			Canceled = canceled;
+			if (path == null)
+				throw new ArgumentNullException ("path");
+			Path = path;
+			Iter = iter;
 		}
 		
-		public bool Canceled { get; private set; }
-		
+		public TreeIter Iter { get; private set; }
+		public TreePath Path { get; private set; }
 		public ITask Task { get; private set; }
 	}
 }
diff --git a/src/Gtk.Tasque/TaskView.cs b/src/Gtk.Tasque/TaskView.cs
new file mode 100644
index 0000000..5a187e8
--- /dev/null
+++ b/src/Gtk.Tasque/TaskView.cs
@@ -0,0 +1,258 @@
+// TaskTreeView.cs created with MonoDevelop
+// User: boyd on 2/9/2008
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using Mono.Addins;
+using Tasque;
+using Gtk;
+
+namespace Gtk.Tasque
+{
+	using TaskColNode = TypeExtensionNode<TaskColumnExtensionAttribute>;
+	
+	/// <summary>
+	/// This is the main TreeView widget that is used to show tasks in Tasque's
+	/// main window.
+	/// </summary>
+	public class TaskView
+	{
+		private Gtk.TreeModelFilter modelFilter;
+		private ICategory filterCategory;	
+		
+		public event EventHandler NumberOfTasksChanged;
+
+		public TaskView (Gtk.TreeModel model, IPreferences preferences)
+		{
+			if (preferences == null)
+				throw new ArgumentNullException ("preferences");
+			
+			TreeView = new TreeView ();
+			
+			#if GTK_2_12
+			// set up the timing for the tooltips
+			TreeView.Settings.SetLongProperty("gtk-tooltip-browse-mode-timeout", 0, "Tasque:TaskTreeView");
+			TreeView.Settings.SetLongProperty("gtk-tooltip-browse-timeout", 750, "Tasque:TaskTreeView");
+			TreeView.Settings.SetLongProperty("gtk-tooltip-timeout", 750, "Tasque:TaskTreeView");
+
+			ConnectEvents();
+			#endif
+			
+			// TODO: Modify the behavior of the TreeView so that it doesn't show
+			// the highlighted row.  Then, also tie in with the mouse hovering
+			// so that as you hover the mouse around, it will automatically
+			// select the row that the mouse is hovered over.  By doing this,
+			// we should be able to not require the user to click on a task
+			// to select it and THEN have to click on the column item they want
+			// to modify.
+			
+			filterCategory = null;
+			
+			modelFilter = new Gtk.TreeModelFilter (model, null);
+			modelFilter.VisibleFunc = FilterFunc;
+			
+			modelFilter.RowInserted += OnRowInsertedHandler;
+			modelFilter.RowDeleted += OnRowDeletedHandler;
+			Refilter ();
+			
+			//Model = modelFilter
+			
+			TreeView.Selection.Mode = Gtk.SelectionMode.Single;
+			TreeView.RulesHint = false;
+			TreeView.HeadersVisible = false;
+			TreeView.HoverSelection = true;
+			
+			// TODO: Figure out how to turn off selection highlight
+			
+			columns = new List<ITaskColumn> ();
+			var nodeList = AddinManager.GetExtensionNodes (typeof(ITaskColumn)).Cast<TaskColNode> ();
+			var nodes = new List<TaskColNode> (nodeList);
+			foreach (var node in nodes)
+				AddColumn (node, nodes);
+			
+			rowEditingDictionary = new ConcurrentDictionary<ITaskColumn, TaskRowEditingEventArgs> ();
+			columns.Sort ((x, y) => x.DefaultPosition.CompareTo (y.DefaultPosition));
+			foreach (var col in columns) {
+				col.Initialize (Model, this, preferences);
+				
+				col.CellEditingStarted += (sender, e) => {
+					if (rowEditingDictionary.IsEmpty)
+						IsTaskBeingEdited = true;
+					
+					if (!rowEditingDictionary.Any (v => v.Value.Task == e.Task)) {
+						if (RowEditingStarted != null)
+							RowEditingStarted (this, e);
+					}
+					rowEditingDictionary.TryAdd ((ITaskColumn)sender, e);
+				};
+				
+				col.CellEditingFinished += (sender, e) => {
+					TaskRowEditingEventArgs args;
+					rowEditingDictionary.TryRemove ((ITaskColumn)sender, out args);
+					if (!rowEditingDictionary.Any (v => v.Value == e.Task)) {
+						if (RowEditingFinished != null)
+							RowEditingFinished (this, e);
+					}
+					
+					if (rowEditingDictionary.IsEmpty)
+						IsTaskBeingEdited = false;
+				};
+				
+				TreeView.AppendColumn (col.TreeViewColumn);
+			}
+		}
+		
+		public bool IsTaskBeingEdited { get; private set; }
+		
+		public TreeModel Model { get { return TreeView.Model; } }
+		
+		public TreeView TreeView { get; private set; }
+
+		#region Public Methods
+		public void Refilter ()
+		{
+			Refilter (filterCategory);
+		}
+		
+		public void Refilter (ICategory selectedCategory)
+		{
+			this.filterCategory = selectedCategory;
+			TreeView.Model = modelFilter;
+			modelFilter.Refilter ();
+		}
+		
+		public int GetNumberOfTasks ()
+		{
+			return modelFilter.IterNChildren ();
+		}
+		
+		public ITaskColumn GetColumn (Type taskColumnType)
+		{
+			return columns.SingleOrDefault (c => c.GetType () == taskColumnType);
+		}
+		#endregion // Public Methods
+		
+		public event EventHandler<TaskRowEditingEventArgs> RowEditingStarted;
+		public event EventHandler<TaskRowEditingEventArgs> RowEditingFinished;
+		
+		#region Private Methods
+		bool AddColumn (TaskColNode colNode, List<TaskColNode> nodes)
+		{
+			// if column is aready in collection, return
+			if (columns.Any (i => i.GetType () == colNode.Type))
+				return false;
+			
+			// if col has a col requirement, add it first recursively if it exists,
+			// otherwise return false (as the requirement cannot be resolved)
+			var reqColTypeName = colNode.Data.RequiredColumnTypeName;
+			if (!string.IsNullOrWhiteSpace (reqColTypeName)) {
+				var reqNode = nodes.SingleOrDefault (n => n.Type.Name == reqColTypeName);
+				if (reqNode == null || !AddColumn (reqNode, nodes))
+					return false;
+			}
+			
+			// if col is replaced by another col, don't add it
+			if (nodes.Any (n => n.Data.ReplacedColumnTypeName == colNode.Type.Name))
+				return false;
+			
+			columns.Add ((ITaskColumn)colNode.CreateInstance ());
+			return true;
+		}
+		
+		#if GTK_2_12
+		private void ConnectEvents()
+		{
+			TreeView.CursorChanged += delegate(object o, EventArgs args) {			
+			int toolTipMaxLength = 250;
+			string snipText = "...";
+			int maxNumNotes = 3;
+			int notesAdded = 0;
+			TreeView.TooltipText = null;
+			TreeView.TriggerTooltipQuery();
+			TreeModel m;
+			TreeIter iter;
+			List<String> list = new List<String>();
+	
+			if(TreeView.Selection.GetSelected(out m, out iter)) {
+				ITask task = Model.GetValue (iter, 0) as ITask;							      
+				if (task != null && task.HasNotes && task.Notes != null) {
+					foreach (INote note in task.Notes) {
+						// for the tooltip, truncate any notes longer than 250 characters.
+						if (note.Text.Length > toolTipMaxLength)
+							list.Add(note.Text.Substring(0, toolTipMaxLength - snipText.Length) + 
+											snipText);
+						else
+							list.Add(note.Text);
+						notesAdded++;
+						// stop iterating once we reach maxNumNotes
+						if (notesAdded >= maxNumNotes) {
+							break;
+						}
+					}
+				}			      		
+		
+				TreeView.HasTooltip = list.Count > 0;
+				if (TreeView.HasTooltip) {
+					// if there are more than maxNumNotes, append a notice to the tooltip
+					if (notesAdded < task.Notes.Count) {
+						int nMoreNotes = task.Notes.Count - notesAdded;
+						if (nMoreNotes > 1)
+							list.Add(String.Format("[{0} more notes]", nMoreNotes));
+						else
+							list.Add("[1 more note]");
+					}
+					TreeView.TooltipText = String.Join("\n\n", list.ToArray());
+					TreeView.TriggerTooltipQuery();
+				}
+			}
+			};
+		}
+		#endif
+		
+		protected virtual bool FilterFunc (Gtk.TreeModel model,
+										   Gtk.TreeIter iter)
+		{
+			// Filter out deleted tasks
+			ITask task = model.GetValue (iter, 0) as ITask;
+
+			if (task == null) {
+				Logger.Error ("FilterFunc: task at iter was null");
+				return false;
+			}
+			
+			if (task.State == TaskState.Deleted) {
+				//Logger.Debug ("TaskTreeView.FilterFunc:\n\t{0}\n\t{1}\n\tReturning false", task.Name, task.State);  
+				return false;
+			}
+			
+			if (filterCategory == null)
+				return true;
+			
+			return filterCategory.ContainsTask (task);
+		}
+		#endregion // Private Methods
+		
+		#region EventHandlers
+		void OnRowInsertedHandler (object sender, Gtk.RowInsertedArgs args)
+		{
+			if (NumberOfTasksChanged == null)
+				return;
+			
+			NumberOfTasksChanged (this, EventArgs.Empty);
+		}
+		
+		void OnRowDeletedHandler (object sender, Gtk.RowDeletedArgs args)
+		{
+			if (NumberOfTasksChanged == null)
+				return;
+			
+			NumberOfTasksChanged (this, EventArgs.Empty);
+		}
+		#endregion // EventHandlers
+		
+		List<ITaskColumn> columns;
+		ConcurrentDictionary<ITaskColumn, TaskRowEditingEventArgs> rowEditingDictionary;
+	}
+}
diff --git a/src/Gtk.Tasque/TaskWindow.cs b/src/Gtk.Tasque/TaskWindow.cs
index 5bbd8c7..b6bd96b 100644
--- a/src/Gtk.Tasque/TaskWindow.cs
+++ b/src/Gtk.Tasque/TaskWindow.cs
@@ -558,7 +558,7 @@ namespace Gtk.Tasque
 
 			foreach (TaskGroup taskGroup in taskGroups) {
 				if (taskGroup.ContainsTask (task, out iter)) {
-					taskGroup.TreeView.Selection.SelectIter (iter);
+					taskGroup.TaskView.TreeView.Selection.SelectIter (iter);
 					break;
 				}
 			}
@@ -601,7 +601,7 @@ namespace Gtk.Tasque
 				//Logger.Debug("taskGroupHeights: {0}", taskGroupHeights);
 				TreePath start;
 				TreePath end;
-				if (taskGroup.TreeView.GetVisibleRange (out start, out end)) {
+				if (taskGroup.TaskView.TreeView.GetVisibleRange (out start, out end)) {
 					Logger.Debug ("TaskGroup '{0}' range: {1} - {2}",
 						taskGroup.DisplayName,
 						start.ToString (),
@@ -634,7 +634,7 @@ namespace Gtk.Tasque
 	
 					//scroll to the new task
 					scrolledWindow.Vadjustment.Value = scrollDistance;
-					taskGroup.TreeView.Selection.SelectIter (iter);
+					taskGroup.TaskView.TreeView.Selection.SelectIter (iter);
 				}
 				if (taskGroup.Visible) {
 					taskGroupHeights += taskGroup.Requisition.Height;
@@ -1144,10 +1144,10 @@ namespace Gtk.Tasque
 				ICategory category =
 					categoryComboBox.Model.GetValue (iter, 0) as ICategory;
 
-				TaskTreeView tree = futureGroup.TreeView as TaskTreeView;
+				TaskView tree = futureGroup.TaskView as TaskView;
 
 				// Don't add a new empty task if we're still editing a task
-				if (tree.TaskBeingEdited != null)
+				if (tree.IsTaskBeingEdited)
 					return;
 
 				ITask task = CreateTask (String.Empty, category);
@@ -1157,7 +1157,7 @@ namespace Gtk.Tasque
 				// Since we added an empty task, it'll always be on top
 				// Looks like a hack
 				Gtk.TreePath path = new Gtk.TreePath ("0");
-				tree.SetCursor (path, tree.GetColumn (2), true);
+				tree.TreeView.SetCursor (path, tree.TreeView.GetColumn (2), true);
 			}
 		}
 
diff --git a/tasque.sln b/tasque.sln
index 289b061..eff0066 100644
--- a/tasque.sln
+++ b/tasque.sln
@@ -27,6 +27,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiveminderBackend", "src\Ad
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdsBackend", "src\Addins\Backends\Eds\EdsBackend.csproj", "{FBDF2B11-0EEE-4463-A282-009AC1F7FC76}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gtk.Tasque.Columns", "src\Addins\Gtk.Tasque.Columns\Gtk.Tasque.Columns.csproj", "{C8454FAA-CE15-4202-9B93-4B536A8BC32D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gtk.Tasque.TimerCompleteColumns", "src\Addins\Gtk.Tasque.TimerCompleteColumns\Gtk.Tasque.TimerCompleteColumns.csproj", "{BFF82E44-DE40-441A-95C8-95269AB9C53D}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tasque", "src\tasque\tasque.csproj", "{A70BD496-A280-4EF5-BBE8-254E0CA89C62}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoMac.Tasque", "src\MonoMac.Tasque\MonoMac.Tasque.csproj", "{67188C62-9332-4402-8702-E8BC1CCA8D2F}"
@@ -165,6 +169,26 @@ Global
 		{BCC1964F-A6E3-4912-B120-6DA22C0EB294}.MonoMacDebug|Any CPU.Build.0 = Debug|Any CPU
 		{BCC1964F-A6E3-4912-B120-6DA22C0EB294}.MonoMacRelease|Any CPU.ActiveCfg = Release|Any CPU
 		{BCC1964F-A6E3-4912-B120-6DA22C0EB294}.MonoMacRelease|Any CPU.Build.0 = Release|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkLinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkLinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkLinuxRelease|Any CPU.ActiveCfg = LinuxRelease|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkLinuxRelease|Any CPU.Build.0 = LinuxRelease|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkWinDebug|Any CPU.ActiveCfg = WinDebug|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkWinDebug|Any CPU.Build.0 = WinDebug|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkWinRelease|Any CPU.ActiveCfg = WinRelease|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.GtkWinRelease|Any CPU.Build.0 = WinRelease|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.MonoMacDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D}.MonoMacRelease|Any CPU.ActiveCfg = LinuxRelease|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkLinuxDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkLinuxDebug|Any CPU.Build.0 = LinuxDebug|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkLinuxRelease|Any CPU.ActiveCfg = LinuxRelease|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkLinuxRelease|Any CPU.Build.0 = LinuxRelease|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkWinDebug|Any CPU.ActiveCfg = WinDebug|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkWinDebug|Any CPU.Build.0 = WinDebug|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkWinRelease|Any CPU.ActiveCfg = WinRelease|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.GtkWinRelease|Any CPU.Build.0 = WinRelease|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.MonoMacDebug|Any CPU.ActiveCfg = LinuxDebug|Any CPU
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D}.MonoMacRelease|Any CPU.ActiveCfg = LinuxRelease|Any CPU
 		{CC8935CB-342C-4FDA-BAF1-24FA3EB53490}.GtkLinuxDebug|Any CPU.ActiveCfg = GtkDebug|Any CPU
 		{CC8935CB-342C-4FDA-BAF1-24FA3EB53490}.GtkLinuxDebug|Any CPU.Build.0 = GtkDebug|Any CPU
 		{CC8935CB-342C-4FDA-BAF1-24FA3EB53490}.GtkLinuxRelease|Any CPU.ActiveCfg = GtkRelease|Any CPU
@@ -198,6 +222,8 @@ Global
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 		{E2E6EE06-C957-4D1E-85CA-90F3773597DE} = {CA8A61DA-519C-444B-8503-801DE3F0E432}
+		{C8454FAA-CE15-4202-9B93-4B536A8BC32D} = {CA8A61DA-519C-444B-8503-801DE3F0E432}
+		{BFF82E44-DE40-441A-95C8-95269AB9C53D} = {CA8A61DA-519C-444B-8503-801DE3F0E432}
 		{0F63E512-FD5A-482C-8389-6A0DBE1301CB} = {E2E6EE06-C957-4D1E-85CA-90F3773597DE}
 		{CC8935CB-342C-4FDA-BAF1-24FA3EB53490} = {E2E6EE06-C957-4D1E-85CA-90F3773597DE}
 		{CCCC10A5-662D-4788-82D3-25689F3D4D4F} = {E2E6EE06-C957-4D1E-85CA-90F3773597DE}



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