As part of the Summer of Code I will be adding some enhancements to monodoc, The first of those it's Gecko support for rendering. The Patch sent actually support both rendering engines, gecko and gtkhtml. The idea is to keep compatibility in the proccess of getting gecko (that's what Miguel said ;-).
The rendering is moved to an Interface, called IHtmlRender, which has two implementations, one with Gecko and the other with GtkHtml. The last one is a mere copy/paste of the old code. The implementation with Gecko has some limitations at this point:
* The scrollbars drawed are mozilla-themed and not gtk. Don't know why yet.
* Copy/Paste/Select All, does not work with gecko. I hope that gets solved once Michael (also from the Summer of Code) finish his work with Mozilla
* The default fonts are so big for me
* The inner anchors (<a name="") dont work. This is a problem due to the way we send the html code to GtkMozEmbed. As there is no real URL (no file written in the hard disk or in Internet), GtkMozEmbed does not know how to resolve inner links.
Apart from those, everything works ;-). The contreversy with the images loading in gecko some months before in the list was solved writting those images in a temporary directory and puting the URL in the html code generated. The first approach was to use the technique of Beagle, encoding the images in Base64 (as in the mail) and puting that in the src attribute of the images. The problem was that Gecko freezed if you gave him html code bigger than 64 KB (see Beagle bug #158767). For some pages of the Mono-Handbook, sticking the images there grew the code so much that the limit was reached and monodoc freezed. So, I changed the way the images were loaded to the cache based (It's not so nice but it works).
Well, that's all I think. Ah! for test the gecko support, call monodoc with --gecko option (if not the GtkHtml render is used).
Mario.
Changelog
Description: Binary data
Index: configure.in =================================================================== --- configure.in (revision 46818) +++ configure.in (working copy) @@ -42,6 +42,9 @@ #fi AC_SUBST(GTK_SHARP_LIBS) +PKG_CHECK_MODULES(GECKO_SHARP, gecko-sharp=0.6) +AC_SUBST(GECKO_SHARP_LIBS) + dnl Intl GETTEXT_PACKAGE=mono-tools AC_SUBST(GETTEXT_PACKAGE) Index: docbrowser/browser.cs =================================================================== --- docbrowser/browser.cs (revision 46818) +++ docbrowser/browser.cs (working copy) @@ -24,6 +24,7 @@ static int Main (string [] args) { string topic = null; + bool useGecko = false; for (int i = 0; i < args.Length; i++){ switch (args [i]){ @@ -80,6 +81,9 @@ i++; break; + case "--gecko": + useGecko = true; + break; default: topic = args [i]; break; @@ -91,7 +95,7 @@ Settings.RunningGUI = true; Application.Init (); - Browser browser = new Browser (); + Browser browser = new Browser (useGecko); if (topic != null) browser.LoadUrl (topic); @@ -119,6 +123,7 @@ public Notebook tabs_nb; public Tab CurrentTab; bool HoldCtrl; + public bool UseGecko; [Glade.Widget] MenuItem bookmarksMenu; @@ -165,8 +170,9 @@ public ArrayList bookList; - public Browser () + public Browser (bool UseGecko) { + this.UseGecko = UseGecko; ui = new Glade.XML (null, "browser.glade", "window1", null); ui.Autoconnect (this); @@ -274,7 +280,7 @@ bar_style.SetBackgroundGC (StateType.Normal, MainWindow.Style.BackgroundGCs[1]); } - Stream GetResourceImage (string name) + public Stream GetResourceImage (string name) { Assembly assembly = System.Reflection.Assembly.GetCallingAssembly (); System.IO.Stream s = assembly.GetManifestResourceStream (name); @@ -317,12 +323,12 @@ } } - public void LinkClicked (object o, LinkClickedArgs args) + public void LinkClicked (object o, EventArgs args) { if (HoldCtrl) AddTab(); - LoadUrl (args.Url); + LoadUrl (CurrentTab.html.Url); } private System.Xml.XmlNode edit_node; @@ -376,12 +382,7 @@ { CurrentUrl = url; - Gtk.HTMLStream stream = CurrentTab.html.Begin ("text/html"); - - stream.Write ("<html><body>"); - stream.Write (text); - stream.Write ("</body></html>"); - CurrentTab.html.End (stream, HTMLStreamStatus.Ok); + CurrentTab.html.Render("<html><body>" + text + "</body></html>"); if (matched_node != null) { if (tree_browser.SelectedNode != matched_node) tree_browser.ShowNode (matched_node); @@ -431,9 +432,9 @@ // Invoked when the mouse is over a link // string last_url = ""; - public void OnUrlMouseOver (object o, OnUrlArgs args) + public void OnUrlMouseOver (object o, EventArgs args) { - string new_url = args.Url; + string new_url = CurrentTab.html.Url; if (new_url == null) new_url = ""; @@ -1808,10 +1809,10 @@ class Tab : Notebook { // Our HTML preview during editing. - public HTML html_preview; + public IHtmlRender html_preview; // Where we render the contents - public HTML html; + public IHtmlRender html; public TextView text_editor; public Mode Tab_mode; @@ -1843,16 +1844,18 @@ // First Page // ScrolledWindow html_container = new ScrolledWindow(); + html_container.Show(); // // Setup the HTML rendering area // - html = new HTML (); - html.Show (); - html_container.Add (html); - html.LinkClicked += new LinkClickedHandler (browser.LinkClicked); - html.OnUrl += new OnUrlHandler (browser.OnUrlMouseOver); - html.UrlRequested += new UrlRequestedHandler (browser.UrlRequested); + if (browser.UseGecko) + html = new GeckoHtmlRender (browser); + else + html = new GtkHtmlHtmlRender (browser); + html_container.Add (html.HtmlPanel); + html.UrlClicked += new EventHandler (browser.LinkClicked); + html.OnUrl += new EventHandler (browser.OnUrlMouseOver); browser.context_id = browser.statusbar.GetContextId (""); AppendPage(html_container, new Label("Html")); @@ -1902,10 +1905,11 @@ // // code preview panel // - html_preview = new HTML (); - html_preview.Show (); - html_preview_container.Add (html_preview); - + if (browser.UseGecko) + html_preview = new GeckoHtmlRender (browser); + else + html_preview = new GtkHtmlHtmlRender (browser); + html_preview_container.Add (html_preview.HtmlPanel); html_preview_frame.Add(html_preview_container); MainPart.PackStart(sw); @@ -2066,9 +2070,7 @@ } browser.statusbar.Pop (browser.context_id); browser.statusbar.Push (browser.context_id, "XML OK"); - Gtk.HTMLStream s = html_preview.Begin ("text/html"); - s.Write (sw.ToString ()); - html_preview.End (s, HTMLStreamStatus.Ok); + html_preview.Render(sw.ToString()); } void OnTabClose (object sender, EventArgs a) { Index: docbrowser/monodoc.in =================================================================== --- docbrowser/monodoc.in (revision 46818) +++ docbrowser/monodoc.in (working copy) @@ -46,6 +46,7 @@ echo " --help Print this message" echo " --html TOPIC Print the HTML contents of TOPIC" echo " --make-index Create the documentation index" + echo " --gecko Use Mozilla to render the contents" echo echo "The following options are available for authoring documentation:" echo " --edit path Edit (unassembled) documentation at path" Index: docbrowser/HtmlRender.cs =================================================================== --- docbrowser/HtmlRender.cs (revision 0) +++ docbrowser/HtmlRender.cs (revision 0) @@ -0,0 +1,248 @@ +using System; +using Gecko; +using Gtk; +using System.Text; +using System.IO; +using System.Collections; + +namespace Monodoc { +interface IHtmlRender { + // Jump to an anchor of the form <a name="tttt"> + void JumpToAnchor (string anchor_name); + + //Copy to the clipboard the selcted text + void Copy (); + + //Select all the text + void SelectAll (); + + //Render the HTML code given + void Render (string HtmlCode); + + //Event fired when the use is over an Url + event EventHandler OnUrl; + + //Event fired when the user clicks on a Link + event EventHandler UrlClicked; + + // Variable that handles the info encessary for the events + // As every implementation of HtmlRender will have differents events + // we try to homogenize them with the variabel + string Url { get; } + + Widget HtmlPanel { get; } +} + + +class GeckoHtmlRender : IHtmlRender { + + Hashtable cache_imgs; + string tmpPath; + WebControl _HtmlPanel; + Viewport panel; + public Widget HtmlPanel { + get { return (Widget) panel; } + } + + string _url; + public string Url { + get { return _url; } + } + Browser browser; + + public event EventHandler OnUrl; + public event EventHandler UrlClicked; + + public GeckoHtmlRender (Browser browser) + { + this.browser = browser; + _HtmlPanel = new WebControl("/tmp/monodoc", "MonodocGecko"); //FIXME + _HtmlPanel.Show(); //due to Gecko bug + _HtmlPanel.OpenUri += OnOpenUri; + _HtmlPanel.LinkMsg += OnLinkMsg; + panel = new Viewport(); + panel.Add (_HtmlPanel); + cache_imgs = new Hashtable(); + tmpPath = Path.Combine (Path.GetTempPath(), "monodoc"); + } + protected void OnOpenUri (object o, OpenUriArgs args) + { + _url = args.AURI; + if (UrlClicked != null) + UrlClicked (this, new EventArgs()); + args.RetVal = true; //this prevents Gecko to continue processing + } + protected void OnLinkMsg (object o, EventArgs args) + { + _url = _HtmlPanel.LinkMessage; + if (OnUrl != null) + OnUrl (this, args); + } + + /* NOT ALREADY IMPLEMENTED */ + public void JumpToAnchor (string anchor_name) + { + } + + /* NOT ALREADY IMPLEMENTED */ + public void Copy() {} + + /* NOT ALREADY IMPLEMENTED */ + public void SelectAll() {} + + public void Render (string HtmlCode) + { + string r = ProcessImages(HtmlCode); + _HtmlPanel.OpenStream("file:///", "text/html"); + _HtmlPanel.AppendData(r); + _HtmlPanel.CloseStream(); + } + + // Substitute the src of the images with the appropriate path + string ProcessImages(string HtmlCode) + { + //If there are no Images return fast + int pos = HtmlCode.IndexOf ("<img", 0, HtmlCode.Length); + if (pos == -1) + return HtmlCode; + + StringBuilder html = new StringBuilder (); + html.Append (HtmlCode.Substring (0, pos)); + int srcIni, srcEnd; + string Img; + Stream s; + string path, img_name; + + while (pos != -1) { + + //look for the src of the img + srcIni = HtmlCode.IndexOf ("src=\"", pos); + srcEnd = HtmlCode.IndexOf ("\"", srcIni+6); + Img = HtmlCode.Substring (srcIni+5, srcEnd-srcIni-5); + + path = "NO_IMG"; + //is the img cached? + if (cache_imgs.Contains(Img)) { + path = (string) cache_imgs[Img]; + } else { + //obtain the stream from the compressed sources + s = browser.help_tree.GetImage (Img); + if (s != null) { + //write the file to a tmp directory + img_name = Img.Substring (Img.LastIndexOf (":")+1); + path = Path.Combine (tmpPath, img_name); + Directory.CreateDirectory (Path.GetDirectoryName (path)); + FileStream file = new FileStream (path, FileMode.Create); + byte[] buffer = new byte [8192]; + int n; + + while ((n = s.Read (buffer, 0, 8192)) != 0) + file.Write (buffer, 0, n); + file.Flush(); + file.Close(); + System.Console.WriteLine("Cache: {0}", path); + //Add the image to the cache + cache_imgs[Img] = path; + } + } + //Add the html code from <img until src=" + html.Append (HtmlCode.Substring (pos, srcIni + 5 - pos)); + //Add the Image path + html.Append (path); + //Look for the next image + pos = HtmlCode.IndexOf ("<img", srcIni); + + if (pos == -1) + //Add the rest of the file + html.Append (HtmlCode.Substring (srcEnd)); + else + //Add from " to the next <img + html.Append (HtmlCode.Substring (srcEnd, pos - srcEnd)); //check this + } + return html.ToString(); + } + +} + + + +class GtkHtmlHtmlRender : IHtmlRender { + + HTML _HtmlPanel; + public Widget HtmlPanel { + get { + return (Widget) _HtmlPanel; } + } + + string _url; + public string Url { + get { return _url; } + } + Browser browser; + + public event EventHandler OnUrl; + public event EventHandler UrlClicked; + + + public GtkHtmlHtmlRender (Browser browser) + { + _HtmlPanel = new HTML(); + _HtmlPanel.Show(); + _HtmlPanel.LinkClicked += new LinkClickedHandler (LinkClicked); + _HtmlPanel.OnUrl += new OnUrlHandler (OnUrlMouseOver); + _HtmlPanel.UrlRequested += new UrlRequestedHandler (UrlRequested); + this.browser = browser; + } + protected void LinkClicked (object o, LinkClickedArgs args) + { + _url = args.Url; + if (UrlClicked != null) + UrlClicked (this, new EventArgs()); + } + protected void OnUrlMouseOver (object o, OnUrlArgs args) + { + _url = args.Url; + if (OnUrl != null) + OnUrl (this, args); + } + public void JumpToAnchor (string anchor) + { + _HtmlPanel.JumpToAnchor(anchor); + } + + public void Copy () + { + _HtmlPanel.Copy(); + } + + public void SelectAll () + { + _HtmlPanel.SelectAll(); + } + + public void Render (string HtmlCode) + { + + Gtk.HTMLStream stream = _HtmlPanel.Begin ("text/html"); + stream.Write(HtmlCode); + _HtmlPanel.End (stream, HTMLStreamStatus.Ok); + } + + protected void UrlRequested (object sender, UrlRequestedArgs args) + { + Stream s = browser.help_tree.GetImage (args.Url); + + if (s == null) + s = browser.GetResourceImage ("monodoc.png"); + byte [] buffer = new byte [8192]; + int n, m; + m=0; + while ((n = s.Read (buffer, 0, 8192)) != 0) { + args.Handle.Write (buffer, n); + m += n; + } + args.Handle.Close (HTMLStreamStatus.Ok); + } + +} +} Index: docbrowser/Makefile.am =================================================================== --- docbrowser/Makefile.am (revision 46818) +++ docbrowser/Makefile.am (working copy) @@ -13,13 +13,14 @@ $(srcdir)/elabel.cs \ $(srcdir)/history.cs \ $(srcdir)/Contributions.cs \ - $(srcdir)/XmlNodeWriter.cs + $(srcdir)/XmlNodeWriter.cs \ + $(srcdir)/HtmlRender.cs admin_sources = \ $(srcdir)/admin.cs \ $(srcdir)/Contributions.cs -browser_assemblies = $(GTK_SHARP_LIBS) -pkg:monodoc -r:System.Web.Services +browser_assemblies = $(GTK_SHARP_LIBS) $(GECKO_SHARP_LIBS) -pkg:monodoc -r:System.Web.Services EXTRA_DIST = \ $(browser_sources) \
_______________________________________________ Mono-docs-list maillist - Mono-docs-list@lists.ximian.com http://lists.ximian.com/mailman/listinfo/mono-docs-list