[tomboy] Support Windows 7 Jump Lists (bug #587330)



commit 5ff67c1442743d8d55c06fb7cf7437eb64969aec
Author: Stefan Cosma <stefan cosma gmail com>
Date:   Sun Nov 8 17:50:16 2009 +0200

    Support Windows 7 Jump Lists (bug #587330)

 Tomboy.csproj             |   16 +++-
 Tomboy/JumpListManager.cs |  205 ++++++++++++++++++++++++++++++++++++++
 Tomboy/Tomboy.cs          |   21 ++++
 Tomboy/WindowsInterop.cs  |  240 +++++++++++++++++++++++++++++++++++++++++++++
 data/icons/new_note.ico   |  Bin 0 -> 1150 bytes
 data/icons/note.ico       |  Bin 0 -> 1150 bytes
 data/icons/search.ico     |  Bin 0 -> 894 bytes
 7 files changed, 481 insertions(+), 1 deletions(-)
---
diff --git a/Tomboy.csproj b/Tomboy.csproj
index 808ca17..dd588de 100644
--- a/Tomboy.csproj
+++ b/Tomboy.csproj
@@ -109,6 +109,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Tomboy\ActionManager.cs" />
+    <Compile Include="Tomboy\JumpListManager.cs" />
     <Compile Include="Tomboy\Contrast.cs" />
     <Compile Include="Tomboy\Defines.WIN32.cs" />
     <Compile Include="Tomboy\IRemoteControl.cs" />
@@ -136,6 +137,7 @@
     <Compile Include="Tomboy\TagManager.cs" />
     <Compile Include="Tomboy\Tag.cs" />
     <Compile Include="Tomboy\TagButton.cs" />
+    <Compile Include="Tomboy\WindowsInterop.cs" />
     <Compile Include="Tomboy\WindowsFactory.cs" />
     <Compile Include="Tomboy\WindowsKeybinder.cs" />
     <Compile Include="Tomboy\PreferencesDialog.cs" />
@@ -211,6 +213,15 @@
     <None Include="Tomboy\Defines.cs.in" />
   </ItemGroup>
   <ItemGroup>
+    <Content Include="new_note.ico">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="note.ico">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="search.ico">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="tomboy.ico" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
@@ -233,6 +244,9 @@
   </ProjectExtensions>
   <PropertyGroup>
     <PreBuildEvent>copy "$(ProjectDir)data\icons\tomboy.ico" "$(ProjectDir)tomboy.ico"
+copy "$(ProjectDir)data\icons\note.ico" "$(ProjectDir)note.ico"
+copy "$(ProjectDir)data\icons\new_note.ico" "$(ProjectDir)new_note.ico"
+copy "$(ProjectDir)data\icons\search.ico" "$(ProjectDir)search.ico"
 copy "$(ProjectDir)data\icons\hicolor_apps_48x48_tomboy.png" "$(ProjectDir)tomboy.png"
 copy "$(ProjectDir)data\icons\hicolor_places_22x22_note.png" "$(ProjectDir)note.png"
 copy "$(ProjectDir)data\icons\hicolor_actions_16x16_note-new.png" "$(ProjectDir)note-new.png"
@@ -297,4 +311,4 @@ copy "$(ProjectDir)data\icons\hicolor_status_16x16_pin-active.png" "$(ProjectDir
 copy "$(ProjectDir)Tomboy\Tomboy.addin.xml" "$(ProjectDir)"
 copy "$(ProjectDir)data\UIManagerLayout.xml" "$(ProjectDir)"</PostBuildEvent>
   </PropertyGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/Tomboy/JumpListManager.cs b/Tomboy/JumpListManager.cs
new file mode 100644
index 0000000..20a5e63
--- /dev/null
+++ b/Tomboy/JumpListManager.cs
@@ -0,0 +1,205 @@
+//#if WIN32
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Tomboy.Windows.Interop;
+using IShellLink = Tomboy.Windows.Interop.IShellLinkW;
+using Mono.Unix;
+
+namespace Tomboy
+{
+	public class JumpListManager
+	{
+		private static readonly string NoteIcon = "note.ico";
+		private static readonly string NewNoteIcon = "new_note.ico";
+		private static readonly string SearchIcon = "search.ico";
+
+		private static readonly string tomboy_path = System.Reflection.Assembly.GetExecutingAssembly ().Location;
+		private static readonly string icons_path = tomboy_path.Substring (0, tomboy_path.LastIndexOf ('\\') + 1);
+
+		public static void CreateJumpList ()
+		{
+			CreateJumpList (null);
+		}
+
+		public static void CreateJumpList (NoteManager note_manager)
+		{
+			try {
+				ICustomDestinationList custom_destinationd_list =
+				    (ICustomDestinationList) Activator.CreateInstance (Type.GetTypeFromCLSID (CLSID.DestinationList));
+
+				uint slots;
+				Guid riid = CLSID.IObjectArray;
+				IObjectArray removed_objects;
+
+				Logger.Debug ("Windows Taskbar: Begin jump list");
+				custom_destinationd_list.BeginList (out slots, ref riid, out removed_objects);
+
+				AddUserTasks (custom_destinationd_list);
+				AddRecentNotes (custom_destinationd_list, note_manager, slots);
+
+				Logger.Debug ("Windows Taskbar: Commit jump list");
+				custom_destinationd_list.CommitList ();
+
+				Marshal.FinalReleaseComObject (removed_objects);
+				removed_objects = null;
+
+				Marshal.FinalReleaseComObject (custom_destinationd_list);
+				custom_destinationd_list = null;
+			} catch (COMException e) {
+				Logger.Error ("Error creating jump list: {0}\n{1}", e.Message, e.StackTrace);
+			}
+		}
+
+		public static void DeleteJumpList ()
+		{
+			try {
+				ICustomDestinationList custom_destinationd_list =
+				    (ICustomDestinationList) Activator.CreateInstance (Type.GetTypeFromCLSID (CLSID.DestinationList));
+
+				Logger.Debug ("Windows Taskbar: Remove jump list");
+				custom_destinationd_list.DeleteList (null);
+
+				Marshal.FinalReleaseComObject (custom_destinationd_list);
+				custom_destinationd_list = null;
+			} catch (COMException e) {
+				Logger.Error ("Error removing jump list: {0}\n{1}", e.Message, e.StackTrace);
+			}
+		}
+
+		private static void AddUserTasks (ICustomDestinationList custom_destinationd_list)
+		{
+			IObjectCollection object_collection =
+			    (IObjectCollection) Activator.CreateInstance (Type.GetTypeFromCLSID (CLSID.EnumerableObjectCollection));
+
+			IShellLink search_notes = CreateShellLink ("Search All Notes", tomboy_path, "--search",
+			    icons_path + SearchIcon, -1);
+			if (search_notes != null)
+				object_collection.AddObject (search_notes);
+
+			//IShellLink new_notebook = CreateShellLink("New Notebook", topmboy_path, "--new-notebook",
+			//    icons_path, (int)TomboyIcons.NewNotebook);
+			//if (new_notebook != null)
+			//    object_collection.AddObject(new_notebook);
+
+			IShellLink new_note = CreateShellLink ("Create New Note", tomboy_path, "--new-note",
+			    icons_path + NewNoteIcon, -1);
+			if (new_note != null)
+				object_collection.AddObject (new_note);
+
+			custom_destinationd_list.AddUserTasks ((IObjectArray) object_collection);
+
+			Marshal.ReleaseComObject (object_collection);
+			object_collection = null;
+		}
+
+		private static void AddRecentNotes (ICustomDestinationList custom_destinationd_list, NoteManager note_manager, uint slots)
+		{
+			IObjectCollection object_collection =
+			    (IObjectCollection) Activator.CreateInstance (Type.GetTypeFromCLSID (CLSID.EnumerableObjectCollection));
+
+			// Prevent template notes from appearing in the menu
+			Tag template_tag = TagManager.GetOrCreateSystemTag (TagManager.TemplateNoteSystemTag);
+
+			uint index = 0;
+			foreach (Note note in note_manager.Notes) {
+				if (note.IsSpecial)
+					continue;
+
+				// Skip template notes
+				if (note.ContainsTag (template_tag))
+					continue;
+
+				string note_title = note.Title;
+				if (note.IsNew) {
+					note_title += Catalog.GetString (" (new)");
+				}
+
+				IShellLink note_link = CreateShellLink (note_title, tomboy_path, "--open-note " + note.Uri,
+					icons_path + NoteIcon, -1);
+				if (note_link != null)
+					object_collection.AddObject (note_link);
+
+				if (++index == slots - 1)
+					break;
+			}
+
+			// Add Start Here note
+			Note start_note = note_manager.FindByUri (NoteManager.StartNoteUri);
+			IShellLink start_note_link = CreateShellLink (start_note.Title, tomboy_path, "--open-note " +
+				NoteManager.StartNoteUri, icons_path + NoteIcon, -1);
+			if (start_note_link != null)
+				object_collection.AddObject (start_note_link);
+
+			custom_destinationd_list.AppendCategory ("Recent Notes", (IObjectArray) object_collection);
+
+			Marshal.ReleaseComObject (object_collection);
+			object_collection = null;
+		}
+
+		private static IShellLink CreateShellLink (string title, string path)
+		{
+			return CreateShellLink (title, path, string.Empty, string.Empty, 0);
+		}
+
+		private static IShellLink CreateShellLink (string title, string path, string arguments)
+		{
+			return CreateShellLink (title, path, arguments, string.Empty, 0);
+		}
+
+		private static IShellLink CreateShellLink (string title, string path, string arguments, string icon_path, int icon_pos)
+		{
+			try {
+				IShellLink shell_link = (IShellLink) Activator.CreateInstance (Type.GetTypeFromCLSID (CLSID.ShellLink));
+				shell_link.SetPath (path);
+
+				if (!string.IsNullOrEmpty (arguments))
+					shell_link.SetArguments (arguments);
+
+				if (!string.IsNullOrEmpty (icon_path))
+					shell_link.SetIconLocation (icon_path, icon_pos);
+
+				IntPtr pps;
+				Guid ipsiid = CLSID.IPropertyStore;
+
+				Marshal.QueryInterface (Marshal.GetIUnknownForObject (shell_link), ref ipsiid, out pps);
+				IPropertyStore property_store = (IPropertyStore) Marshal.GetTypedObjectForIUnknown (pps, typeof (IPropertyStore));
+
+				PROPVARIANT propvar = new PROPVARIANT ();
+				propvar.SetString (title);
+
+				// PKEY_Title
+				PROPERTYKEY PKEY_Title = new PROPERTYKEY ();
+				PKEY_Title.fmtid = new Guid ("F29F85E0-4FF9-1068-AB91-08002B27B3D9");
+				PKEY_Title.pid = 2;
+
+				property_store.SetValue (ref PKEY_Title, ref propvar);
+				property_store.Commit ();
+
+				IntPtr psl;
+				Guid psliid = CLSID.IShellLinkW;
+
+				Marshal.QueryInterface (Marshal.GetIUnknownForObject (shell_link), ref psliid, out psl);
+				IShellLink link = (IShellLink) Marshal.GetTypedObjectForIUnknown (psl, typeof (IShellLink));
+
+				propvar.Clear ();
+
+				Marshal.ReleaseComObject (property_store);
+				property_store = null;
+
+				Marshal.ReleaseComObject (shell_link);
+				shell_link = null;
+
+				return link;
+			} catch (COMException e) {
+				Logger.Error ("Error createing shell link: {0}\n{1}", e.Message, e.StackTrace);
+			}
+
+			return null;
+		}
+	}
+}
+
+//#endif // WIN32
diff --git a/Tomboy/Tomboy.cs b/Tomboy/Tomboy.cs
index 50f2c2e..319630b 100644
--- a/Tomboy/Tomboy.cs
+++ b/Tomboy/Tomboy.cs
@@ -116,6 +116,27 @@ namespace Tomboy
 			}
 #endif
 
+#if WIN32
+			if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
+				var os_version = Environment.OSVersion.Version;
+				if (( os_version.Major == 6 && os_version.Minor > 0 ) || os_version.Major > 6) {
+					JumpListManager.CreateJumpList (manager);
+
+					manager.NoteAdded += delegate (object sender, Note changed) {
+						JumpListManager.CreateJumpList (manager);
+					};
+
+					manager.NoteRenamed += delegate (Note sender, string old_title) {
+						JumpListManager.CreateJumpList (manager);
+					};
+
+					manager.NoteDeleted += delegate (object sender, Note changed) {
+						JumpListManager.CreateJumpList (manager);
+					};
+				}
+			}
+#endif
+
 			if (is_panel_applet) {
 				tray_icon_showing = true;
 
diff --git a/Tomboy/WindowsInterop.cs b/Tomboy/WindowsInterop.cs
new file mode 100644
index 0000000..30fc43a
--- /dev/null
+++ b/Tomboy/WindowsInterop.cs
@@ -0,0 +1,240 @@
+//#if WIN32
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+
+using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
+
+namespace Tomboy.Windows.Interop
+{
+	public enum eKnownDestCategory
+	{
+		Frequent = 1,
+		Recent
+	}
+
+	[StructLayout (LayoutKind.Sequential)]
+	public struct WIN32_FIND_DATAW
+	{
+		public int FileAttributes;
+		public FILETIME CreationTime;
+		public FILETIME LastAccessTime;
+		public FILETIME LastWriteTime;
+		public int FileSizeHigh;
+		public int FileSizeLow;
+		public int Reserved0;
+		public int Reserved1;
+		[MarshalAs (UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
+		public string FileName;
+		[MarshalAs (UnmanagedType.ByValTStr, SizeConst = 14)]
+		public string AlternateFileName;
+		private const int MAX_PATH = 260;
+	}
+
+	[StructLayout (LayoutKind.Sequential)]
+	public struct PROPERTYKEY
+	{
+		public Guid fmtid;
+		public uint pid;
+	}
+
+	[StructLayout (LayoutKind.Sequential)]
+	public struct PROPVARIANT
+	{
+		ushort vt;
+		ushort wReserved1;
+		ushort wReserved2;
+		ushort wReserved3;
+		IntPtr p;
+		int p2;
+
+		private byte [] GetDataBytes ()
+		{
+			byte [] ret = new byte [IntPtr.Size + sizeof (int)];
+			if (IntPtr.Size == 4)
+				BitConverter.GetBytes (p.ToInt32 ()).CopyTo (ret, 0);
+			else if (IntPtr.Size == 8)
+				BitConverter.GetBytes (p.ToInt64 ()).CopyTo (ret, 0);
+			BitConverter.GetBytes (p2).CopyTo (ret, IntPtr.Size);
+			return ret;
+		}
+
+		[DllImport ("ole32.dll")]
+		private extern static int PropVariantClear (ref PROPVARIANT pvar);
+
+		public void Clear ()
+		{
+			PROPVARIANT var = this;
+			PropVariantClear (ref var);
+
+			vt = (ushort) VarEnum.VT_EMPTY;
+			wReserved1 = wReserved2 = wReserved3 = 0;
+			p = IntPtr.Zero;
+			p2 = 0;
+		}
+
+		public VarEnum Type
+		{
+			get { return (VarEnum) vt; }
+		}
+
+		public object Value
+		{
+			get
+			{
+				switch ((VarEnum) vt) {
+					case VarEnum.VT_LPWSTR:
+						return Marshal.PtrToStringUni (p);
+					case VarEnum.VT_UNKNOWN:
+						return Marshal.GetObjectForIUnknown (p);
+					case VarEnum.VT_DISPATCH:
+						return p;
+					default:
+						throw new NotSupportedException ("The type of this variable is not support ('" + vt.ToString () + "')");
+				}
+			}
+		}
+
+		public void SetString (string value)
+		{
+			vt = (ushort) VarEnum.VT_LPWSTR;
+			p = Marshal.StringToCoTaskMemUni (value);
+		}
+	}
+
+	[ComImport, Guid ("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
+	[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
+	public interface IShellItem
+	{
+		void BindToHandler ([In] IBindCtx bindCtx, [In] ref Guid bhid, [In] ref Guid riid, [Out] out object ppv);
+
+		void GetParent ([Out] IShellItem shellItem);
+
+		void GetDisplayName ([In] uint sigdnName, [Out, MarshalAs (UnmanagedType.LPWStr)] out string name);
+
+		void GetAttributes ([In] uint mask, [Out] out uint attributes);
+
+		void Compare ([In] IShellItem shellItem, [In] uint sichIntf, [Out] out int order);
+	}
+
+	[ComImport, Guid ("000214F9-0000-0000-C000-000000000046")]
+	[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
+	public interface IShellLinkW
+	{
+		void GetPath ([Out, MarshalAs (UnmanagedType.LPWStr)] out string file, [In] int cch, [In, Out] WIN32_FIND_DATAW data, [In] uint flags);
+
+		void GetIDList ([Out] IntPtr idl);
+
+		void SetIDList ([In] IntPtr idl);
+
+		void GetDescription ([Out, MarshalAs (UnmanagedType.LPWStr)] out string name, [In] int cch);
+
+		void SetDescription ([In, MarshalAs (UnmanagedType.LPWStr)] string name);
+
+		void GetWorkingDirectory ([Out, MarshalAs (UnmanagedType.LPWStr)] out string name, [In] int cch);
+
+		void SetWorkingDirectory ([In, MarshalAs (UnmanagedType.LPWStr)] string name);
+
+		void GetArguments ([Out, MarshalAs (UnmanagedType.LPWStr)] out string name, [In] int cch);
+
+		void SetArguments ([In, MarshalAs (UnmanagedType.LPWStr)] string name);
+
+		void GetHotkey ([Out] out ushort hotkey);
+
+		void SetHotkey ([In] ushort hotkey);
+
+		void GetShowCmd ([Out] out int showCmd);
+
+		void SetShowCmd ([In] int showCmd);
+
+		void GetIconLocation ([Out, MarshalAs (UnmanagedType.LPWStr)] out string iconPath, [In] int cch, [Out] int icon);
+
+		void SetIconLocation ([In, MarshalAs (UnmanagedType.LPWStr)] string iconPath, [In] int icon);
+
+		void SetRelativePath ([In, MarshalAs (UnmanagedType.LPWStr)] string pathrel, [In] ushort reserved);
+
+		void Resolve ([In] IntPtr hwnd, [In] ushort flags);
+
+		void SetPath ([In, MarshalAs (UnmanagedType.LPWStr)] string path);
+	}
+
+	[ComImport, Guid ("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")]
+	[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
+	public interface IPropertyStore
+	{
+		void GetCount ([Out] out ushort cProps);
+
+		void GetAt ([In] ushort prop, [Out] out PROPERTYKEY key);
+
+		void GetValue ([In] ref PROPERTYKEY key, [Out] out PROPVARIANT val);
+
+		void SetValue ([In] ref PROPERTYKEY key, [In] ref PROPVARIANT val);
+
+		void Commit ();
+	}
+
+	[ComImport, Guid ("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")]
+	[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
+	public interface IObjectArray
+	{
+		void GetCount ([Out] out uint cObjects);
+
+		void GetAt ([In] uint uiIndex, [In] ref Guid riid, [Out, MarshalAs (UnmanagedType.Interface)] out object ppv);
+	}
+
+	[ComImport, Guid ("5632b1a4-e38a-400a-928a-d4cd63230295")]
+	[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
+	public interface IObjectCollection
+	{
+		[PreserveSig]
+		void GetCount ([Out] out uint cObjects);
+
+		[PreserveSig]
+		void GetAt ([In] uint uiIndex, [In] ref Guid riid, [Out, MarshalAs (UnmanagedType.Interface)] out object ppv);
+
+		void AddObject ([In, MarshalAs (UnmanagedType.Interface)] object pvObject);
+
+		void AddFromArray ([In, MarshalAs (UnmanagedType.Interface)] IObjectArray source);
+
+		void RemoveObjectAt ([In] uint index);
+
+		void Clear ();
+	}
+
+	[ComImport, Guid ("6332debf-87b5-4670-90c0-5e57b408a49e")]
+	[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
+	public interface ICustomDestinationList
+	{
+		void SetAppID ([In, MarshalAs (UnmanagedType.LPWStr)] string appID);
+
+		void BeginList ([Out] out uint cMinSlots, [In] ref Guid riid, [Out, MarshalAs (UnmanagedType.Interface)] out IObjectArray ppv);
+
+		void AppendCategory ([In, MarshalAs (UnmanagedType.LPWStr)] string category, [In] IObjectArray objectArray);
+
+		void AppendKnownCategory ([In] eKnownDestCategory category);
+
+		void AddUserTasks ([In, MarshalAs (UnmanagedType.Interface)] IObjectArray objectArray);
+
+		void CommitList ();
+
+		void GetRemovedDestinations ([In] Guid riid, [Out] out object ppv);
+
+		void DeleteList ([In, MarshalAs (UnmanagedType.LPWStr)] string appID);
+
+		void AbortList ();
+	}
+
+	public struct CLSID
+	{
+		public static readonly Guid IObjectArray = new Guid ("92CA9DCD-5622-4bba-A805-5E9F541BD8C9");
+		public static readonly Guid IPropertyStore = new Guid ("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99");
+		public static readonly Guid IShellLinkW = new Guid ("000214F9-0000-0000-C000-000000000046");
+		public static readonly Guid DestinationList = new Guid ("77f10cf0-3db5-4966-b520-b7c54fd35ed6");
+		public static readonly Guid EnumerableObjectCollection = new Guid ("2d3468c1-36a7-43b6-ac24-d3f02fd9607a");
+		public static readonly Guid ShellItem = new Guid ("9ac9fbe1-e0a2-4ad6-b4ee-e212013ea917");
+		public static readonly Guid ShellLink = new Guid ("00021401-0000-0000-C000-000000000046");
+	}
+}
+
+//#endif // WIN32
diff --git a/data/icons/new_note.ico b/data/icons/new_note.ico
new file mode 100644
index 0000000..833006f
Binary files /dev/null and b/data/icons/new_note.ico differ
diff --git a/data/icons/note.ico b/data/icons/note.ico
new file mode 100644
index 0000000..f6c13a0
Binary files /dev/null and b/data/icons/note.ico differ
diff --git a/data/icons/search.ico b/data/icons/search.ico
new file mode 100644
index 0000000..4d1f827
Binary files /dev/null and b/data/icons/search.ico differ



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