Index: ChangeLog
===================================================================
--- ChangeLog	(revision 73958)
+++ ChangeLog	(working copy)
@@ -1,3 +1,10 @@
+2007-03-08  George Giolfan  <georgegiolfan@yahoo.com>
+
+	* Help.cs: Implement.
+	* XplatUI.cs: Add HtmlHelp method.
+	* XplatUIDriver.cs: Add HtmlHelp method.
+	* XplatUIWin32.cs: Add HtmlHelp method and related Win32 stuff.
+
 2007-03-07  Pedro Martínez Juliá  <pedromj@gmail.com>
 
 	* DataGridView.cs: Remove event handler from DataView when a
Index: Help.cs
===================================================================
--- Help.cs	(revision 73958)
+++ Help.cs	(working copy)
@@ -21,13 +21,14 @@
 //
 // Authors:
 //	Peter Bartok	(pbartok@novell.com)
+//	George Giolfan	(georgegiolfan@yahoo.com)
 //
 //
 
-// NOT COMPLETE
-
 using System;
+using System.Diagnostics;
 using System.Drawing;
+using System.IO;
 
 namespace System.Windows.Forms {
 	public class Help {
@@ -45,12 +46,38 @@
 			ShowHelp(parent, url, navigator, null);
 		}
 
-		[MonoTODO("Create glue code to tie into libCHM")]
+#if NET_2_0
+		public static void ShowHelp (Control parent, string url, HelpNavigator command, object parameter) {
+#else
 		public static void ShowHelp(Control parent, string url, HelpNavigator command, object param) {
-			MessageBox.Show(parent, (string)Locale.GetText("Help (" + command +
-					(param != null ? "(" + param + ")" : String.Empty) +
-					") not yet implemented"), (string)Locale.GetText("Popup Help"),
-					MessageBoxButtons.OK, MessageBoxIcon.Stop);
+			object parameter = param;
+#endif
+			const string HelpUrlIsNotValid = "Help URL '{0}' is not valid.";
+			if (url == null || url.Length == 0)
+				throw new ArgumentException(string.Format(HelpUrlIsNotValid, null), "url");
+			string scheme;
+			string local_path;
+			try {
+				Uri uri = new Uri(url);
+				if (uri.Scheme == "file") {
+					scheme = "file";
+					local_path = uri.LocalPath;
+				} else
+					return;
+			} catch (UriFormatException) {
+				scheme = null;
+				local_path = url;
+			}
+			if (string.Equals(new FileInfo(local_path).Extension, ".chm", StringComparison.InvariantCultureIgnoreCase))
+				if (parameter is int)
+					throw new ArgumentException("InvalidArgument=Value of 'Integer' is not valid for 'param'.", "param");
+				else if (parameter == null || parameter is string)
+					XplatUI.HtmlHelp(parent, scheme + (File.Exists(local_path) ? new FileInfo(local_path).FullName : local_path), command,(string)parameter);
+			else
+				if (new FileInfo(local_path).Exists)
+					Process.Start(url);
+				else
+					throw new ArgumentException(string.Format(HelpUrlIsNotValid, url), "url");
 		}
 
 		public static void ShowHelp(Control parent, string url, string keyword) {
@@ -64,10 +91,69 @@
 			ShowHelp(parent, url, HelpNavigator.Index, null);
 		}
 
-		[MonoTODO("Create glue code to tie into libCHM")]
-		public static void ShowPopup(Control parent, string caption, Point location) {
-			MessageBox.Show(parent, (string)Locale.GetText("Popup Help not yet implemented"), (string)Locale.GetText("Popup Help"), MessageBoxButtons.OK, MessageBoxIcon.Stop);
+		public static void ShowPopup (Control parent, string caption, Point location) {
+			//FIXME: How is parent used?
+			PopupForm.Show (caption, location);
 		}
 		#endregion	// Public Static Methods
+
+		#region Private Classes
+		class PopupForm : Form {
+			string caption;
+			Rectangle black_border_rectangle;
+			Rectangle gray_border_rectangle;
+			Rectangle background_rectagle;
+			Point caption_location;
+
+			PopupForm (string caption, Point location)
+			{
+				ShowInTaskbar = false;
+				this.caption = caption;
+				FormBorderStyle = FormBorderStyle.None;
+				StartPosition = FormStartPosition.Manual;
+				Graphics g = CreateGraphics ();
+				SizeF text_size = g.MeasureString (caption, Font, Screen.FromControl (this).Bounds.Width);
+				g.Dispose ();
+				const int margin_width = 3;
+				const int caption_distance = margin_width + 2;
+				Rectangle caption_rectagle = new Rectangle (caption_distance, caption_distance, (int)text_size.Width, (int)text_size.Height);
+				const int caption_extra_space = 4 + 2 * margin_width;
+				Size = new Size (caption_rectagle.Width + caption_extra_space, caption_rectagle.Height + caption_extra_space);
+				const int margin_size = 2 * margin_width;
+				background_rectagle = new Rectangle (2, 2, caption_rectagle.Width + margin_size, caption_rectagle.Height + margin_size);
+				black_border_rectangle = new Rectangle (0, 0, background_rectagle.Width + 3, background_rectagle.Height + 3);
+				gray_border_rectangle = new Rectangle (1, 1, background_rectagle.Width + 1, background_rectagle.Height + 1);
+				caption_location = caption_rectagle.Location;
+				DesktopLocation = new Point (location.X - Width / 2, location.Y - Height / 4);
+			}
+
+			protected override void OnPaint (PaintEventArgs e)
+			{
+				base.OnPaint (e);
+				Graphics g = e.Graphics;
+				g.DrawRectangle (Pens.Black, black_border_rectangle);
+				g.DrawRectangle (Pens.LightGray, gray_border_rectangle);
+				g.FillRectangle (Brushes.White, background_rectagle);
+				g.DrawString (caption, Font, Brushes.Black, caption_location);
+			}
+
+			protected override void OnMouseDown (MouseEventArgs e)
+			{
+				base.OnMouseDown (e);
+				Close ();
+			}
+
+			protected override void OnDeactivate (EventArgs e)
+			{
+				base.OnDeactivate (e);
+				Close ();
+			}
+
+			public static void Show (string caption, Point location)
+			{
+				new PopupForm (caption, location).Show ();
+			}
+		}
+		#endregion	// Private Classes
 	}
 }
Index: XplatUI.cs
===================================================================
--- XplatUI.cs	(revision 73958)
+++ XplatUI.cs	(working copy)
@@ -603,6 +603,11 @@
 			driver.HandleException(e);
 		}
 
+		internal static void HtmlHelp (Control parent, string url, HelpNavigator command, string parameter)
+		{
+			driver.HtmlHelp (parent, url, command, parameter);
+		}
+
 		internal static void Invalidate(IntPtr handle, Rectangle rc, bool clear) {
 			#if DriverDebug
 				Console.WriteLine("Invalidate({0}, {1}, {2}): Called", Window(handle), rc, clear);
Index: XplatUIDriver.cs
===================================================================
--- XplatUIDriver.cs	(revision 73958)
+++ XplatUIDriver.cs	(working copy)
@@ -322,6 +322,16 @@
 			dest_dc.DrawImage ((Bitmap)offscreen_drawable, r, r, GraphicsUnit.Pixel);
 		}
 
+		[MonoTODO ("Create glue code to tie into libCHM")]
+		internal virtual void HtmlHelp (Control parent, string url, HelpNavigator command, string parameter) 
+		{
+			// Assumes the CHM Reader extension.
+			try {
+				System.Diagnostics.Process.Start ("firefox", "\"chm:" + (url.StartsWith ("file:") ? url : "file:" + url) + (parameter == null ? null : "!" + (parameter.StartsWith ("/") ? parameter : "/" + parameter)) + "\"");
+			} catch {
+				MessageBox.Show (parent, "The help file could not be displayed.", "Help", MessageBoxButtons.OK, MessageBoxIcon.Error);
+			}
+		}
 #endregion	// XplatUI Driver Methods
 	}
 
Index: XplatUIWin32.cs
===================================================================
--- XplatUIWin32.cs	(revision 73958)
+++ XplatUIWin32.cs	(working copy)
@@ -748,7 +748,47 @@
 			LWA_ALPHA			= 0x2,
 		}
 
-		
+		enum HtmlHelpCommand {
+			HH_DISPLAY_TOPIC	= 0x0000,
+			HH_DISPLAY_TOC		= 0x0001,
+			HH_DISPLAY_INDEX	= 0x0002,
+			HH_DISPLAY_SEARCH	= 0x0003,
+			HH_KEYWORD_LOOKUP	= 0x000D,
+#if NET_2_0
+			HH_HELP_CONTEXT		= 0x000F,
+			HH_ALINK_LOOKUP		= 0x0013,
+#endif
+		}
+
+		const int FALSE = 0;
+		const int TRUE = 1;
+
+		[StructLayout (LayoutKind.Sequential, CharSet=CharSet.Unicode)]
+		class HH_AKLINK {
+			public HH_AKLINK (string keyword) {
+				pszKeywords = keyword;
+			}
+			int cbStruct = Marshal.SizeOf (typeof (HH_AKLINK));
+			int fReserved;
+			string pszKeywords;
+			string pszUrl;
+			string pszMsgText;
+			string pszMsgTitle;
+			string pszWindow;
+			int fIndexOnFail = TRUE;
+		}
+
+		[StructLayout (LayoutKind.Sequential)]
+		class HH_FTS_QUERY {
+			int cbStruct = Marshal.SizeOf (typeof (HH_FTS_QUERY));
+			int fUniCodeStrings;
+			string pszSearchQuery;
+			int iProximity;
+			int fStemmedSearch;
+			int fTitleOnly;
+			int fExecute;
+			string pszWindow;
+		}
 		#endregion
 
 		#region Constructor & Destructor
@@ -2758,6 +2798,48 @@
 		}
 
 		internal override event EventHandler Idle;
+
+		internal override void HtmlHelp (Control parent, string url, HelpNavigator command, string parameter)
+		{
+			IntPtr parent_handle = parent == null ? IntPtr.Zero : parent.Handle;
+			switch (command) {
+			case HelpNavigator.Topic:
+				Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_TOPIC, parameter);
+				break;
+			case HelpNavigator.TableOfContents:
+				Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_TOC, IntPtr.Zero);
+				break;
+			//LAMESPEC: In the example on the HelpNavigator documentation, whatever is in the text box is passed as parameter (and in the case of HelpNavigator.Index, it does something).
+			case HelpNavigator.Index:
+				Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_INDEX, parameter);
+				break;
+			case HelpNavigator.Find:
+				Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_SEARCH, new HH_FTS_QUERY ());
+				break;
+			case HelpNavigator.KeywordIndex:
+				Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_TOPIC, (string)null);
+				//FIXME: The text is not passed correctly.
+				Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_KEYWORD_LOOKUP, new HH_AKLINK (parameter));
+				break;
+#if NET_2_0
+			case HelpNavigator.TopicId:
+				int topic_id;
+				if (int.TryParse (parameter, out topic_id))
+					Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_HELP_CONTEXT, topic_id);
+				else
+					Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_INDEX, parameter);
+				break;
+			case HelpNavigator.AssociateIndex:
+				if (string.IsNullOrEmpty (parameter))
+					Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_INDEX, IntPtr.Zero);
+				else {
+					Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_DISPLAY_TOPIC, (string)null);
+					Win32HtmlHelp (parent_handle, url, HtmlHelpCommand.HH_ALINK_LOOKUP, new HH_AKLINK (parameter.Substring (0, 1)));
+				}
+				break;
+#endif
+			}
+		}
 		#endregion	// Public Static Methods
 
 		#region Win32 Imports
@@ -3130,6 +3212,21 @@
 
 		[DllImport ("gdi32.dll", EntryPoint="CreateCompatibleBitmap", CallingConvention=CallingConvention.StdCall)]
 		internal static extern IntPtr Win32CreateCompatibleBitmap (IntPtr hdc, int nWidth, int nHeight);
+
+		[DllImport ("hhctrl.ocx", EntryPoint="HtmlHelpW", CharSet=CharSet.Unicode)]
+		static extern IntPtr Win32HtmlHelp (IntPtr hwndCaller, string pszFile, HtmlHelpCommand uCommand, IntPtr dwData);
+
+		[DllImport ("hhctrl.ocx", EntryPoint="HtmlHelpW", CharSet=CharSet.Unicode)]
+		static extern IntPtr Win32HtmlHelp (IntPtr hwndCaller, string pszFile, HtmlHelpCommand uCommand, string dwData);
+
+		[DllImport ("hhctrl.ocx", EntryPoint="HtmlHelpW", CharSet=CharSet.Unicode)]
+		static extern IntPtr Win32HtmlHelp (IntPtr hwndCaller, string pszFile, HtmlHelpCommand uCommand, HH_AKLINK dwData);
+
+		[DllImport ("hhctrl.ocx", EntryPoint="HtmlHelpW", CharSet=CharSet.Unicode)]
+		static extern IntPtr Win32HtmlHelp (IntPtr hwndCaller, string pszFile, HtmlHelpCommand uCommand, int dwData);
+
+		[DllImport ("hhctrl.ocx", EntryPoint="HtmlHelpW", CharSet=CharSet.Unicode)]
+		static extern IntPtr Win32HtmlHelp (IntPtr hwndCaller, string pszFile, HtmlHelpCommand uCommand, HH_FTS_QUERY dwData);
 		#endregion
 	}
 }
