From c2f98712d0abf25c107bd421273270e1a200ce35 Mon Sep 17 00:00:00 2001 From: Alex Corrado Date: Tue, 18 Sep 2012 17:23:44 -0400 Subject: [PATCH] Refactor MarkdownView into a more generalized RichTextView API --- Xwt.Gtk/Xwt.Gtk.csproj | 2 +- Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs | 2 +- Xwt.Gtk/Xwt.GtkBackend/MarkdownViewBackend.cs | 236 --------------- Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs | 270 ++++++++++++++++++ Xwt.WPF/Xwt.WPF.csproj | 2 +- Xwt.WPF/Xwt.WPFBackend/MarkdownViewBackend.cs | 220 -------------- Xwt.WPF/Xwt.WPFBackend/RichTextViewBackend.cs | 238 +++++++++++++++ Xwt.WPF/Xwt.WPFBackend/WPFEngine.cs | 2 +- Xwt/Xwt.Backends/IMarkdownViewBackend.cs | 67 ----- Xwt/Xwt.Backends/IRichTextViewBackend.cs | 70 +++++ .../MarkdownTextFormat.cs} | 127 +++----- Xwt/Xwt.Formats/TextFormat.cs | 42 +++ Xwt/Xwt.csproj | 15 +- Xwt/Xwt/RichTextView.cs | 128 +++++++++ 14 files changed, 797 insertions(+), 624 deletions(-) delete mode 100644 Xwt.Gtk/Xwt.GtkBackend/MarkdownViewBackend.cs create mode 100755 Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs mode change 100644 => 100755 Xwt.WPF/Xwt.WPF.csproj delete mode 100644 Xwt.WPF/Xwt.WPFBackend/MarkdownViewBackend.cs create mode 100755 Xwt.WPF/Xwt.WPFBackend/RichTextViewBackend.cs mode change 100644 => 100755 Xwt.WPF/Xwt.WPFBackend/WPFEngine.cs delete mode 100644 Xwt/Xwt.Backends/IMarkdownViewBackend.cs create mode 100644 Xwt/Xwt.Backends/IRichTextViewBackend.cs rename Xwt/{Xwt/MarkdownView.cs => Xwt.Formats/MarkdownTextFormat.cs} (67%) create mode 100644 Xwt/Xwt.Formats/TextFormat.cs create mode 100644 Xwt/Xwt/RichTextView.cs diff --git a/Xwt.Gtk/Xwt.Gtk.csproj b/Xwt.Gtk/Xwt.Gtk.csproj index 4c00c8ab..9c30cbbf 100644 --- a/Xwt.Gtk/Xwt.Gtk.csproj +++ b/Xwt.Gtk/Xwt.Gtk.csproj @@ -113,7 +113,7 @@ - + diff --git a/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs b/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs index 12be464b..ee6dd772 100755 --- a/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs +++ b/Xwt.Gtk/Xwt.GtkBackend/GtkEngine.cs @@ -99,7 +99,7 @@ namespace Xwt.GtkBackend WidgetRegistry.RegisterBackend (typeof(Xwt.LinkLabel), typeof (LinkLabelBackend)); WidgetRegistry.RegisterBackend (typeof(Xwt.Placement), typeof (BoxBackend)); WidgetRegistry.RegisterBackend (typeof(Xwt.Spinner), typeof (SpinnerBackend)); - WidgetRegistry.RegisterBackend (typeof(Xwt.MarkdownView), typeof (MarkdownViewBackend)); + WidgetRegistry.RegisterBackend (typeof(Xwt.RichTextView), typeof (RichTextViewBackend)); WidgetRegistry.RegisterBackend (typeof(Xwt.Expander), typeof (ExpanderBackend)); } diff --git a/Xwt.Gtk/Xwt.GtkBackend/MarkdownViewBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/MarkdownViewBackend.cs deleted file mode 100644 index cbd1ecc7..00000000 --- a/Xwt.Gtk/Xwt.GtkBackend/MarkdownViewBackend.cs +++ /dev/null @@ -1,236 +0,0 @@ -// -// MarkdownViewBackend.cs -// -// Author: -// Jérémie Laval -// -// Copyright (c) 2012 Xamarin, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; -using System.Collections.Generic; - -using Xwt; -using Xwt.Backends; -using Xwt.Engine; - -namespace Xwt.GtkBackend -{ - public class MarkdownViewBackend : WidgetBackend, IMarkdownViewBackend - { - const string NewLine = "\n"; - Gtk.TextTagTable table; - List> links = new List> (); - - bool NavigateToUrlEnabled { - get; set; - } - - public MarkdownViewBackend () - { - Widget = new Gtk.TextView (); - Widget.Show (); - Widget.Editable = false; - Widget.WrapMode = Gtk.WrapMode.Word; - InitTagTable (); - } - - void InitTagTable () - { - table = new Gtk.TextTagTable (); - table.Add (new Gtk.TextTag ("bold") { - Weight = Pango.Weight.Bold - }); - table.Add (new Gtk.TextTag ("italic") { - Style = Pango.Style.Italic - }); - table.Add (new Gtk.TextTag ("tt") { - Family = "Monospace" - }); - table.Add (new Gtk.TextTag ("li") { - LeftMargin = 14 - }); - table.Add (new Gtk.TextTag ("h1") { - Weight = Pango.Weight.Bold, - Scale = Pango.Scale.XXLarge - }); - table.Add (new Gtk.TextTag ("h2") { - Weight = Pango.Weight.Bold, - Scale = Pango.Scale.XLarge - }); - table.Add (new Gtk.TextTag ("h3") { - Weight = Pango.Weight.Bold, - Scale = Pango.Scale.Large - }); - table.Add (new Gtk.TextTag ("h4") { - Scale = Pango.Scale.Large - }); - table.Add (new Gtk.TextTag ("pre") { - Family = "Monospace", - Indent = 14 - }); - } - - protected new Gtk.TextView Widget { - get { - return (Gtk.TextView)base.Widget; - } - set { - base.Widget = value; - } - } - - public override void EnableEvent (object eventId) - { - base.EnableEvent (eventId); - if (eventId is MarkdownViewEvent) { - switch ((MarkdownViewEvent) eventId) { - case MarkdownViewEvent.NavigateToUrl: - NavigateToUrlEnabled = true; - break; - } - } - } - - public override void DisableEvent (object eventId) - { - base.DisableEvent (eventId); - if (eventId is MarkdownViewEvent) { - switch ((MarkdownViewEvent) eventId) { - case MarkdownViewEvent.NavigateToUrl: - NavigateToUrlEnabled = false; - break; - } - } - } - - public object CreateBuffer () - { - return new Gtk.TextBuffer (table); - } - - public void EmitText (object buffer, string text) - { - var b = ((Gtk.TextBuffer)buffer); - var iter = b.EndIter; - b.Insert (ref iter, text); - } - - public void EmitStyledText (object buffer, string text, MarkdownInlineStyle style) - { - var b = ((Gtk.TextBuffer)buffer); - var iter = b.EndIter; - var tagName = string.Empty; - switch (style) { - case MarkdownInlineStyle.Bold: - tagName = "bold"; - break; - case MarkdownInlineStyle.Italic: - tagName = "italic"; - break; - case MarkdownInlineStyle.Monospace: - tagName = "tt"; - break; - } - if (string.IsNullOrEmpty (tagName)) - EmitText (buffer, text); - else - b.InsertWithTagsByName (ref iter, text, tagName); - } - - public void EmitHeader (object buffer, string title, int level) - { - var b = ((Gtk.TextBuffer)buffer); - var iter = b.EndIter; - b.Insert (ref iter, NewLine); - b.InsertWithTagsByName (ref iter, title, "h" + level); - b.Insert (ref iter, NewLine); - } - - public void EmitStartParagraph (object buffer) - { - EmitText (buffer, NewLine); - } - - public void EmitEndParagraph (object buffer) - { - EmitText (buffer, NewLine); - } - - public void EmitOpenList (object buffer) - { - EmitText (buffer, NewLine); - } - - public void EmitOpenBullet (object buffer) - { - var b = (Gtk.TextBuffer)buffer; - var iter = b.EndIter; - b.InsertWithTagsByName (ref iter, "• ", "li"); - } - - public void EmitCloseBullet (object buffer) - { - EmitText (buffer, NewLine); - } - - public void EmitCloseList (object buffer) - { - - } - - public void EmitLink (object buffer, string href, string text) - { - var b = (Gtk.TextBuffer)buffer; - var iter = b.EndIter; - var anchor = b.CreateChildAnchor (ref iter); - var link = new LinkLabel (text) { Uri = new Uri (href, UriKind.RelativeOrAbsolute) }; - link.NavigateToUrl += (sender, e) => { - if (NavigateToUrlEnabled) { - ((IMarkdownViewEventSink) EventSink).OnNavigateToUrl (e.Uri); - e.SetHandled (); - } - }; - var gtkWidget = (Gtk.Widget) WidgetRegistry.GetNativeWidget (link); - links.Add (new KeyValuePair (anchor, gtkWidget)); - } - - public void EmitCodeBlock (object buffer, string code) - { - var b = ((Gtk.TextBuffer)buffer); - var iter = b.EndIter; - b.Insert (ref iter, NewLine); - b.InsertWithTagsByName (ref iter, code, "pre"); - } - - public void EmitHorizontalRuler (object buffer) - { - - } - - public void SetBuffer (object buffer) - { - Widget.Buffer = (Gtk.TextBuffer)buffer; - foreach (var l in links) - Widget.AddChildAtAnchor (l.Value, l.Key); - links.Clear (); - } - } -} - diff --git a/Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs new file mode 100755 index 00000000..9c43917a --- /dev/null +++ b/Xwt.Gtk/Xwt.GtkBackend/RichTextViewBackend.cs @@ -0,0 +1,270 @@ +// +// RichTextViewBackend.cs +// +// Author: +// Jérémie Laval +// +// Copyright (c) 2012 Xamarin, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; + +using Xwt; +using Xwt.Backends; +using Xwt.Engine; + +namespace Xwt.GtkBackend +{ + public class RichTextViewBackend : WidgetBackend, IRichTextViewBackend + { + Gtk.TextTagTable table; + LinkLabel [] links; + + bool NavigateToUrlEnabled { + get; set; + } + + public RichTextViewBackend () + { + Widget = new Gtk.TextView (); + Widget.Show (); + Widget.Editable = false; + Widget.WrapMode = Gtk.WrapMode.Word; + InitTagTable (); + } + + void InitTagTable () + { + table = new Gtk.TextTagTable (); + table.Add (new Gtk.TextTag ("bold") { + Weight = Pango.Weight.Bold + }); + table.Add (new Gtk.TextTag ("italic") { + Style = Pango.Style.Italic + }); + table.Add (new Gtk.TextTag ("tt") { + Family = "Monospace" + }); + table.Add (new Gtk.TextTag ("li") { + LeftMargin = 14 + }); + table.Add (new Gtk.TextTag ("h1") { + Weight = Pango.Weight.Bold, + Scale = Pango.Scale.XXLarge + }); + table.Add (new Gtk.TextTag ("h2") { + Weight = Pango.Weight.Bold, + Scale = Pango.Scale.XLarge + }); + table.Add (new Gtk.TextTag ("h3") { + Weight = Pango.Weight.Bold, + Scale = Pango.Scale.Large + }); + table.Add (new Gtk.TextTag ("h4") { + Scale = Pango.Scale.Large + }); + table.Add (new Gtk.TextTag ("pre") { + Family = "Monospace", + Indent = 14 + }); + } + + protected new Gtk.TextView Widget { + get { + return (Gtk.TextView)base.Widget; + } + set { + base.Widget = value; + } + } + + public override void EnableEvent (object eventId) + { + base.EnableEvent (eventId); + if (eventId is RichTextViewEvent) { + switch ((RichTextViewEvent) eventId) { + case RichTextViewEvent.NavigateToUrl: + NavigateToUrlEnabled = true; + break; + } + } + } + + public override void DisableEvent (object eventId) + { + base.DisableEvent (eventId); + if (eventId is RichTextViewEvent) { + switch ((RichTextViewEvent) eventId) { + case RichTextViewEvent.NavigateToUrl: + NavigateToUrlEnabled = false; + break; + } + } + } + + public IRichTextBuffer CreateBuffer () + { + return new RichTextBuffer (table); + } + + public void SetBuffer (IRichTextBuffer buffer) + { + var buf = buffer as RichTextBuffer; + if (buf == null) + throw new ArgumentException ("Passed buffer is of incorrect type", "buffer"); + + if (links != null) { + foreach (var link in links) + link.NavigateToUrl -= HandleNavigateToUrl; + } + + Widget.Buffer = buf; + links = new LinkLabel [buf.Links.Count]; + for (var i = 0; i < links.Length; i++) { + var link = buf.Links [i]; + var label = new LinkLabel (link.Text) + { + Uri = link.Href + }; + label.NavigateToUrl += HandleNavigateToUrl; + Widget.AddChildAtAnchor ((Gtk.Widget) WidgetRegistry.GetNativeWidget (label), link.Anchor); + links [i] = label; + } + } + + void HandleNavigateToUrl (object sender, NavigateToUrlEventArgs e) + { + if (NavigateToUrlEnabled) { + ((IRichTextViewEventSink) EventSink).OnNavigateToUrl (e.Uri); + e.SetHandled (); + } + } + + struct Link { + public string Text; + public Uri Href; + public Gtk.TextChildAnchor Anchor; + } + + class RichTextBuffer : Gtk.TextBuffer, IRichTextBuffer + { + const string NewLine = "\n"; + + public List Links { + get; private set; + } + + public RichTextBuffer (Gtk.TextTagTable table) : base (table) + { + Links = new List (); + } + + public void EmitText (string text) + { + var iter = EndIter; + Insert (ref iter, text); + } + + public void EmitStyledText (string text, RichTextInlineStyle style) + { + var iter = EndIter; + var tagName = string.Empty; + switch (style) { + case RichTextInlineStyle.Bold: + tagName = "bold"; + break; + case RichTextInlineStyle.Italic: + tagName = "italic"; + break; + case RichTextInlineStyle.Monospace: + tagName = "tt"; + break; + } + if (string.IsNullOrEmpty (tagName)) + EmitText (text); + else + InsertWithTagsByName (ref iter, text, tagName); + } + + public void EmitHeader (string title, int level) + { + var iter = EndIter; + Insert (ref iter, NewLine); + InsertWithTagsByName (ref iter, title, "h" + level); + Insert (ref iter, NewLine); + } + + public void EmitStartParagraph () + { + EmitText (NewLine); + } + + public void EmitEndParagraph () + { + EmitText (NewLine); + } + + public void EmitOpenList () + { + EmitText (NewLine); + } + + public void EmitOpenBullet () + { + var iter = EndIter; + InsertWithTagsByName (ref iter, "• ", "li"); + } + + public void EmitCloseBullet () + { + EmitText (NewLine); + } + + public void EmitCloseList () + { + } + + public void EmitLink (string href, string text) + { + var iter = EndIter; + var anchor = CreateChildAnchor (ref iter); + Links.Add (new Link () + { + Text = text, + Href = new Uri (href, UriKind.RelativeOrAbsolute), + Anchor = anchor + }); + } + + public void EmitCodeBlock (string code) + { + var iter = EndIter; + Insert (ref iter, NewLine); + InsertWithTagsByName (ref iter, code, "pre"); + } + + public void EmitHorizontalRuler () + { + //FIXME + } + } + } +} + diff --git a/Xwt.WPF/Xwt.WPF.csproj b/Xwt.WPF/Xwt.WPF.csproj old mode 100644 new mode 100755 index 59b50516..bec2acd3 --- a/Xwt.WPF/Xwt.WPF.csproj +++ b/Xwt.WPF/Xwt.WPF.csproj @@ -65,7 +65,6 @@ - @@ -133,6 +132,7 @@ + diff --git a/Xwt.WPF/Xwt.WPFBackend/MarkdownViewBackend.cs b/Xwt.WPF/Xwt.WPFBackend/MarkdownViewBackend.cs deleted file mode 100644 index 71b0b329..00000000 --- a/Xwt.WPF/Xwt.WPFBackend/MarkdownViewBackend.cs +++ /dev/null @@ -1,220 +0,0 @@ -// -// MarkdownViewBackend.cs -// -// Author: -// Alan McGovern -// -// Copyright (c) 2012 Xamarin, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Markup; -using Xwt.Backends; - -using Xwt.WPFBackend.Utilities; -using System.Windows.Data; -using System.Windows.Navigation; - -namespace Xwt.WPFBackend -{ - class MarkdownViewBackend - : WidgetBackend, IMarkdownViewBackend - { - int FontSize = 16; - int HeaderIncrement = 8; - - StringBuilder Builder { - get; set; - } - - public new IMarkdownViewEventSink EventSink { - get { return (IMarkdownViewEventSink) base.EventSink; } - } - - public new ExRichTextBox Widget - { - get { return (ExRichTextBox) base.Widget; } - set { base.Widget = value; } - } - - XmlWriter Writer { - get; set; - } - - public MarkdownViewBackend () - { - Widget = new ExRichTextBox (); - } - - public override void EnableEvent (object eventId) - { - base.EnableEvent (eventId); - if (eventId is MarkdownViewEvent) { - switch ((MarkdownViewEvent) eventId) { - case MarkdownViewEvent.NavigateToUrl: - Widget.AddHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated), true); - break; - } - } - } - - public override void DisableEvent (object eventId) - { - base.DisableEvent (eventId); - if (eventId is MarkdownViewEvent) { - switch ((MarkdownViewEvent) eventId) { - case MarkdownViewEvent.NavigateToUrl: - Widget.RemoveHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated)); - break; - } - } - } - - void HyperlinkNavigated (object sender, System.Windows.Navigation.RequestNavigateEventArgs e) - { - Xwt.Engine.Toolkit.Invoke (() => { - EventSink.OnNavigateToUrl (e.Uri); - e.Handled = true; - }); - } - - object IMarkdownViewBackend.CreateBuffer () - { - Builder = new StringBuilder (); - Writer = XmlWriter.Create (Builder, new XmlWriterSettings () { OmitXmlDeclaration = true, NewLineOnAttributes = true, Indent = true, IndentChars = "\t" }); - Writer.WriteStartElement ("FlowDocument", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"); - return Writer; - } - - void IMarkdownViewBackend.EmitText (object buffer, string text) - { - if (!string.IsNullOrEmpty (text)) - Writer.WriteElementString ("Run", text); - } - - void IMarkdownViewBackend.EmitHeader (object buffer, string title, int level) - { - ((IMarkdownViewBackend) this).EmitStartParagraph (buffer); - Writer.WriteStartElement ("Run"); - Writer.WriteAttributeString ("FontSize", (FontSize + HeaderIncrement * level).ToString ()); - Writer.WriteString (title); - Writer.WriteEndElement (); - ((IMarkdownViewBackend) this).EmitEndParagraph (buffer); - } - - void IMarkdownViewBackend.EmitOpenList (object buffer) - { - Writer.WriteStartElement ("List"); - } - - void IMarkdownViewBackend.EmitOpenBullet (object buffer) - { - Writer.WriteStartElement ("ListItem"); - ((IMarkdownViewBackend) this).EmitStartParagraph (buffer); - } - - void IMarkdownViewBackend.EmitCloseBullet (object buffer) - { - Writer.WriteEndElement (); - Writer.WriteEndElement (); - } - - void IMarkdownViewBackend.EmitCloseList (object buffer) - { - // Close the list - Writer.WriteEndElement (); - } - - void IMarkdownViewBackend.EmitLink (object buffer, string href, string text) - { - Writer.WriteStartElement ("Hyperlink"); - Writer.WriteAttributeString ("NavigateUri", href); - ((IMarkdownViewBackend) this).EmitText (buffer, text); - Writer.WriteEndElement (); - } - - void IMarkdownViewBackend.EmitCodeBlock (object buffer, string code) - { - ((IMarkdownViewBackend) this).EmitStartParagraph (buffer); - Writer.WriteAttributeString ("xml", "space", null, "preserve"); - Writer.WriteAttributeString ("TextIndent", "0"); - Writer.WriteAttributeString ("Margin", "50,0,0,0"); - Writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont"); - Writer.WriteString (code); - ((IMarkdownViewBackend) this).EmitEndParagraph (buffer); - } - - void IMarkdownViewBackend.SetBuffer (object buffer) - { - Writer.WriteEndElement (); - Writer.Flush (); - - if (Widget.Document != null) - Widget.Document.ClearValue (FlowDocument.PageWidthProperty); - Widget.Document = (FlowDocument) XamlReader.Parse (Builder.ToString ()); - Widget.Document.SetBinding (FlowDocument.PageWidthProperty, new Binding ("ActualWidth") { Source = Widget }); - Widget.IsDocumentEnabled = true; - Widget.Document.IsEnabled = true; - Widget.IsReadOnly = true; - } - - void IMarkdownViewBackend.EmitStyledText (object buffer, string text, MarkdownInlineStyle style) - { - switch (style) { - case MarkdownInlineStyle.Bold: - case MarkdownInlineStyle.Italic: - Writer.WriteStartElement (style.ToString ()); - ((IMarkdownViewBackend) this).EmitText (buffer, text); - break; - - case MarkdownInlineStyle.Monospace: - Writer.WriteStartElement ("Run"); - Writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont"); - Writer.WriteString (text); - break; - } - Writer.WriteEndElement (); - } - - void IMarkdownViewBackend.EmitStartParagraph (object buffer) - { - Writer.WriteStartElement ("Paragraph"); - } - - void IMarkdownViewBackend.EmitEndParagraph (object buffer) - { - Writer.WriteEndElement (); - } - - void IMarkdownViewBackend.EmitHorizontalRuler (object buffer) - { - Writer.WriteStartElement("BlockUIContainer"); - Writer.WriteAttributeString ("Margin", "0,0,0,0"); - Writer.WriteElementString("Separator", ""); - Writer.WriteEndElement(); - } - } -} diff --git a/Xwt.WPF/Xwt.WPFBackend/RichTextViewBackend.cs b/Xwt.WPF/Xwt.WPFBackend/RichTextViewBackend.cs new file mode 100755 index 00000000..6371d08c --- /dev/null +++ b/Xwt.WPF/Xwt.WPFBackend/RichTextViewBackend.cs @@ -0,0 +1,238 @@ +// +// RichTextViewBackend.cs +// +// Author: +// Alan McGovern +// +// Copyright (c) 2012 Xamarin, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Markup; +using Xwt.Backends; + +using Xwt.WPFBackend.Utilities; +using System.Windows.Data; +using System.Windows.Navigation; + +namespace Xwt.WPFBackend +{ + class RichTextViewBackend + : WidgetBackend, IRichTextViewBackend + { + public new IRichTextViewEventSink EventSink { + get { return (IRichTextViewEventSink) base.EventSink; } + } + + public new ExRichTextBox Widget + { + get { return (ExRichTextBox) base.Widget; } + set { base.Widget = value; } + } + + public RichTextViewBackend () + { + Widget = new ExRichTextBox (); + } + + public override void EnableEvent (object eventId) + { + base.EnableEvent (eventId); + if (eventId is RichTextViewEvent) { + switch ((RichTextViewEvent) eventId) { + case RichTextViewEvent.NavigateToUrl: + Widget.AddHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated), true); + break; + } + } + } + + public override void DisableEvent (object eventId) + { + base.DisableEvent (eventId); + if (eventId is RichTextViewEvent) { + switch ((RichTextViewEvent) eventId) { + case RichTextViewEvent.NavigateToUrl: + Widget.RemoveHandler (Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler (HyperlinkNavigated)); + break; + } + } + } + + void HyperlinkNavigated (object sender, System.Windows.Navigation.RequestNavigateEventArgs e) + { + Xwt.Engine.Toolkit.Invoke (() => { + EventSink.OnNavigateToUrl (e.Uri); + e.Handled = true; + }); + } + + public IRichTextBuffer CreateBuffer () + { + return new RichTextBuffer (); + } + + public void SetBuffer (IRichTextBuffer buffer) + { + var buf = buffer as RichTextBuffer; + if (buf == null) + throw new ArgumentException ("Passed buffer is of incorrect type", "buffer"); + + if (Widget.Document != null) + Widget.Document.ClearValue (FlowDocument.PageWidthProperty); + Widget.Document = buf.ToFlowDocument (); + Widget.Document.SetBinding (FlowDocument.PageWidthProperty, new Binding ("ActualWidth") { Source = Widget }); + Widget.IsDocumentEnabled = true; + Widget.Document.IsEnabled = true; + Widget.IsReadOnly = true; + } + + class RichTextBuffer : IRichTextBuffer + { + const int FontSize = 16; + const int HeaderIncrement = 8; + + StringBuilder builder; + XmlWriter writer; + FlowDocument doc; + + public RichTextBuffer () + { + builder = new StringBuilder (); + writer = XmlWriter.Create (builder, new XmlWriterSettings () { OmitXmlDeclaration = true, NewLineOnAttributes = true, Indent = true, IndentChars = "\t" }); + writer.WriteStartElement ("FlowDocument", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"); + } + + public void EmitText (string text) + { + if (!string.IsNullOrEmpty (text)) + writer.WriteElementString ("Run", text); + } + + public void EmitHeader (string title, int level) + { + EmitStartParagraph (); + writer.WriteStartElement ("Run"); + writer.WriteAttributeString ("FontSize", (FontSize + HeaderIncrement * level).ToString ()); + writer.WriteString (title); + writer.WriteEndElement (); + EmitEndParagraph (); + } + + public void EmitOpenList () + { + writer.WriteStartElement ("List"); + } + + public void EmitOpenBullet () + { + writer.WriteStartElement ("ListItem"); + EmitStartParagraph (); + } + + public void EmitCloseBullet () + { + writer.WriteEndElement (); + writer.WriteEndElement (); + } + + public void EmitCloseList () + { + // Close the list + writer.WriteEndElement (); + } + + public void EmitLink (string href, string text) + { + writer.WriteStartElement ("Hyperlink"); + writer.WriteAttributeString ("NavigateUri", href); + EmitText (text); + writer.WriteEndElement (); + } + + public void EmitCodeBlock (string code) + { + EmitStartParagraph (); + writer.WriteAttributeString ("xml", "space", null, "preserve"); + writer.WriteAttributeString ("TextIndent", "0"); + writer.WriteAttributeString ("Margin", "50,0,0,0"); + writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont"); + writer.WriteString (code); + EmitEndParagraph (); + } + + public void EmitStyledText (string text, RichTextInlineStyle style) + { + switch (style) { + case RichTextInlineStyle.Bold: + case RichTextInlineStyle.Italic: + writer.WriteStartElement (style.ToString ()); + EmitText (text); + break; + + case RichTextInlineStyle.Monospace: + writer.WriteStartElement ("Run"); + writer.WriteAttributeString ("FontFamily", "GlobalMonospace.CompositeFont"); + writer.WriteString (text); + break; + } + writer.WriteEndElement (); + } + + public void EmitStartParagraph () + { + writer.WriteStartElement ("Paragraph"); + } + + public void EmitEndParagraph () + { + writer.WriteEndElement (); + } + + public void EmitHorizontalRuler () + { + writer.WriteStartElement("BlockUIContainer"); + writer.WriteAttributeString ("Margin", "0,0,0,0"); + writer.WriteElementString("Separator", ""); + writer.WriteEndElement(); + } + + public FlowDocument ToFlowDocument () + { + if (doc == null) { + writer.WriteEndElement (); + writer.Flush (); + doc = (FlowDocument) XamlReader.Parse (builder.ToString ()); + builder = null; + writer = null; + } + return doc; + } + } + + + } +} diff --git a/Xwt.WPF/Xwt.WPFBackend/WPFEngine.cs b/Xwt.WPF/Xwt.WPFBackend/WPFEngine.cs old mode 100644 new mode 100755 index ecbf42ab..449eb3b5 --- a/Xwt.WPF/Xwt.WPFBackend/WPFEngine.cs +++ b/Xwt.WPF/Xwt.WPFBackend/WPFEngine.cs @@ -94,7 +94,7 @@ namespace Xwt.WPFBackend WidgetRegistry.RegisterBackend (typeof (Placement), typeof (BoxBackend)); WidgetRegistry.RegisterBackend (typeof (Popover), typeof (PopoverBackend)); WidgetRegistry.RegisterBackend (typeof (ProgressBar), typeof (ProgressBarBackend)); - WidgetRegistry.RegisterBackend (typeof (MarkdownView), typeof (MarkdownViewBackend)); + WidgetRegistry.RegisterBackend (typeof (RichTextView), typeof (RichTextViewBackend)); WidgetRegistry.RegisterBackend (typeof (LinkLabel), typeof (LinkLabelBackend)); WidgetRegistry.RegisterBackend (typeof (Spinner), typeof (SpinnerBackend)); } diff --git a/Xwt/Xwt.Backends/IMarkdownViewBackend.cs b/Xwt/Xwt.Backends/IMarkdownViewBackend.cs deleted file mode 100644 index 603feed4..00000000 --- a/Xwt/Xwt.Backends/IMarkdownViewBackend.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// IMarkdownViewBackend.cs -// -// Author: -// Jérémie Laval -// -// Copyright (c) 2012 Xamarin, Inc. -using System; - -namespace Xwt.Backends -{ - [Flags] - public enum MarkdownInlineStyle - { - Italic, - Bold, - Monospace - } - - public interface IMarkdownViewBackend : IWidgetBackend - { - object CreateBuffer (); - - // Emit unstyled text - void EmitText (object buffer, string text); - - // Emit text using combination of the MarkdownInlineStyle - void EmitStyledText (object buffer, string text, MarkdownInlineStyle style); - - // Emit a header (h1, h2, ...) - void EmitHeader (object buffer, string title, int level); - - // What's outputed afterwards will be a in new paragrapgh - void EmitStartParagraph (object buffer); - void EmitEndParagraph (object buffer); - - // Emit a list - // Chain is: - // open-list, open-bullet, , close-bullet, close-list - void EmitOpenList (object buffer); - void EmitOpenBullet (object buffer); - void EmitCloseBullet (object buffer); - void EmitCloseList (object buffer); - - // Emit a link displaying text and opening the href URL - void EmitLink (object buffer, string href, string text); - - // Emit code in a preformated blockquote - void EmitCodeBlock (object buffer, string code); - - // Emit an horizontal ruler - void EmitHorizontalRuler (object buffer); - - // Display the passed buffer - void SetBuffer (object buffer); - } - - public interface IMarkdownViewEventSink : IWidgetEventSink - { - void OnNavigateToUrl (Uri uri); - } - - public enum MarkdownViewEvent - { - NavigateToUrl = 1 - } -} diff --git a/Xwt/Xwt.Backends/IRichTextViewBackend.cs b/Xwt/Xwt.Backends/IRichTextViewBackend.cs new file mode 100644 index 00000000..88fc284b --- /dev/null +++ b/Xwt/Xwt.Backends/IRichTextViewBackend.cs @@ -0,0 +1,70 @@ +// +// IMarkdownViewBackend.cs +// +// Author: +// Jérémie Laval +// +// Copyright (c) 2012 Xamarin, Inc. +using System; + +namespace Xwt.Backends +{ + [Flags] + public enum RichTextInlineStyle + { + Italic, + Bold, + Monospace + } + + public interface IRichTextViewBackend : IWidgetBackend + { + IRichTextBuffer CreateBuffer (); + + // Display the passed buffer + void SetBuffer (IRichTextBuffer buffer); + } + + public interface IRichTextBuffer + { + // Emit unstyled text + void EmitText (string text); + + // Emit text using combination of the MarkdownInlineStyle + void EmitStyledText (string text, RichTextInlineStyle style); + + // Emit a header (h1, h2, ...) + void EmitHeader (string title, int level); + + // What's outputed afterwards will be a in new paragrapgh + void EmitStartParagraph (); + void EmitEndParagraph (); + + // Emit a list + // Chain is: + // open-list, open-bullet, , close-bullet, close-list + void EmitOpenList (); + void EmitOpenBullet (); + void EmitCloseBullet (); + void EmitCloseList (); + + // Emit a link displaying text and opening the href URL + void EmitLink (string href, string text); + + // Emit code in a preformated blockquote + void EmitCodeBlock (string code); + + // Emit an horizontal ruler + void EmitHorizontalRuler (); + } + + public interface IRichTextViewEventSink : IWidgetEventSink + { + void OnNavigateToUrl (Uri uri); + } + + public enum RichTextViewEvent + { + NavigateToUrl = 1 + } +} diff --git a/Xwt/Xwt/MarkdownView.cs b/Xwt/Xwt.Formats/MarkdownTextFormat.cs similarity index 67% rename from Xwt/Xwt/MarkdownView.cs rename to Xwt/Xwt.Formats/MarkdownTextFormat.cs index dcbef45f..a269d5b4 100644 --- a/Xwt/Xwt/MarkdownView.cs +++ b/Xwt/Xwt.Formats/MarkdownTextFormat.cs @@ -1,10 +1,10 @@ // -// MarkdownView.cs +// MarkdownTextFormat.cs // // Author: -// Jérémie Laval +// Alex Corrado // -// Copyright (c) 2012 Xamarin, Inc. +// Copyright (c) 2012 Xamarin Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -23,79 +23,25 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. + using System; using System.Linq; +using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Collections.Generic; using Xwt.Backends; -namespace Xwt +namespace Xwt.Formats { - public class MarkdownView : Widget + public class MarkdownTextFormat : TextFormat { - protected new class WidgetBackendHost : Widget.WidgetBackendHost, IMarkdownViewEventSink + + public override void Parse (Stream input, IRichTextBuffer buffer) { - public void OnNavigateToUrl (Uri uri) - { - ((MarkdownView) Parent).OnNavigateToUrl (new NavigateToUrlEventArgs (uri)); - } - } - - string markdown; - - IMarkdownViewBackend Backend { - get { return (IMarkdownViewBackend) BackendHost.Backend; } - } - - public string Markdown - { - get - { - return markdown; - } - set - { - markdown = value; - object buffer = ParseMarkdown (value); - Backend.SetBuffer (buffer); - } - } - - EventHandler navigateToUrl; - public event EventHandler NavigateToUrl - { - add - { - BackendHost.OnBeforeEventAdd (MarkdownViewEvent.NavigateToUrl, navigateToUrl); - navigateToUrl += value; - } - remove - { - navigateToUrl -= value; - BackendHost.OnAfterEventRemove (MarkdownViewEvent.NavigateToUrl, navigateToUrl); - } - } - - public MarkdownView () - { - NavigateToUrl += delegate { }; // ensure the virtual method is always called - Markdown = string.Empty; - } - - protected override BackendHost CreateBackendHost () - { - return new WidgetBackendHost (); - } - - protected virtual void OnNavigateToUrl (NavigateToUrlEventArgs e) - { - if (navigateToUrl != null) - navigateToUrl (this, e); - - if (!e.Handled) - Application.EngineBackend.ShowWebBrowser (e); + using (var reader = new StreamReader (input)) + ParseMarkdown (reader.ReadToEnd (), buffer); } /* The subset we support: @@ -108,10 +54,9 @@ namespace Xwt * - Inline code is wrapped between the '`' character * - horizontal ruler, a line with at least 3 hyphens */ - object ParseMarkdown (string markdown) + void ParseMarkdown (string markdown, IRichTextBuffer buffer) { var lines = markdown.Replace ("\r\n", "\n").Split (new[] { '\n' }); - var buffer = Backend.CreateBuffer (); var wasParagraph = false; for (int i = 0; i < lines.Length; i++) { @@ -119,7 +64,7 @@ namespace Xwt // New paragraph if (string.IsNullOrWhiteSpace (line)) { if (wasParagraph) { - Backend.EmitEndParagraph (buffer); + buffer.EmitEndParagraph (); wasParagraph = false; } } @@ -127,7 +72,7 @@ namespace Xwt // Title else if (line.StartsWith ("#")) { var level = line.TakeWhile (c => c == '#').Count (); - Backend.EmitHeader (buffer, line.Trim (' ', '#'), level); + buffer.EmitHeader (line.Trim (' ', '#'), level); } // Title (setex-style) @@ -142,15 +87,15 @@ namespace Xwt // must close the paragraph containing 'FooBarBaz' first. Or we should disallow this construct if (wasParagraph) { wasParagraph = false; - Backend.EmitEndParagraph (buffer); + buffer.EmitEndParagraph (); } - Backend.EmitHeader (buffer, line, level); + buffer.EmitHeader (line, level); i++; } // Ruler else if (line.All (c => c == '-') && line.Length >= 3) { - Backend.EmitHorizontalRuler (buffer); + buffer.EmitHorizontalRuler (); } // Code blocks @@ -164,45 +109,43 @@ namespace Xwt } i--; if (wasParagraph) { - Backend.EmitEndParagraph (buffer); + buffer.EmitEndParagraph (); wasParagraph = false; } - Backend.EmitCodeBlock (buffer, codeblock.Replace ("\n", Environment.NewLine).ToString ()); + buffer.EmitCodeBlock (codeblock.Replace ("\n", Environment.NewLine).ToString ()); } // List else if (new[] { '+', '-', '*' }.Contains (line.TrimStart()[0])) { - Backend.EmitOpenList (buffer); + buffer.EmitOpenList (); var bullet = line[0].ToString (); for (; i < lines.Length; i++) { line = lines[i]; if (!line.StartsWith (bullet)) break; - Backend.EmitOpenBullet (buffer); + buffer.EmitOpenBullet (); ParseText (buffer, line.TrimStart (' ', '-')); - Backend.EmitCloseBullet (buffer); + buffer.EmitCloseBullet (); } i--; - Backend.EmitCloseList (buffer); + buffer.EmitCloseList (); } // Normal paragraph else { if (!wasParagraph) - Backend.EmitStartParagraph (buffer); + buffer.EmitStartParagraph (); ParseText (buffer, line); wasParagraph = true; } } - + // If we don't end in a newline we need to end the open paragrah if (wasParagraph) - Backend.EmitEndParagraph (buffer); - - return buffer; + buffer.EmitEndParagraph (); } - void ParseText (object buffer, string line) + void ParseText (IRichTextBuffer buffer, string line) { // First transform any embedded URL into a proper format line = autoUrl.Replace (line, m => string.Format ("[{0}]({1})", m.Value, m.Value)); @@ -213,34 +156,34 @@ namespace Xwt while (match.Success) { var text = line.Substring (currentIndex, match.Index - currentIndex); if (!string.IsNullOrEmpty (text)) - Backend.EmitText (buffer, text); + buffer.EmitText (text); // Emphasis if (match.Groups["char"].Success) { - MarkdownInlineStyle style = 0; + RichTextInlineStyle style = 0; switch (match.Groups["char"].Value[0]) { case '*': - style |= MarkdownInlineStyle.Bold; + style |= RichTextInlineStyle.Bold; break; case '_': - style |= MarkdownInlineStyle.Italic; + style |= RichTextInlineStyle.Italic; break; case '`': - style |= MarkdownInlineStyle.Monospace; + style |= RichTextInlineStyle.Monospace; break; } - Backend.EmitStyledText (buffer, match.Groups["emph"].Value, style); + buffer.EmitStyledText (match.Groups["emph"].Value, style); } // Link else { var url = match.Groups["url"].Value; var name = match.Groups["name"].Value; - Backend.EmitLink (buffer, url, name); + buffer.EmitLink (url, name); } currentIndex = match.Index + match.Length; match = match.NextMatch (); } // Add remaining text - Backend.EmitText (buffer, line.Substring (currentIndex)); + buffer.EmitText (line.Substring (currentIndex)); } static Regex richText = new Regex (@"\[(?.+)\]\((?.+)\) diff --git a/Xwt/Xwt.Formats/TextFormat.cs b/Xwt/Xwt.Formats/TextFormat.cs new file mode 100644 index 00000000..592a8433 --- /dev/null +++ b/Xwt/Xwt.Formats/TextFormat.cs @@ -0,0 +1,42 @@ +// +// TextFormat.cs +// +// Author: +// Alex Corrado +// +// Copyright (c) 2012 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; + +using Xwt.Backends; + +namespace Xwt.Formats +{ + public abstract class TextFormat + { + public static readonly TextFormat Markdown = new MarkdownTextFormat (); + + // Parses the given input stream into the given buffer + public abstract void Parse (Stream input, IRichTextBuffer buffer); + } +} + diff --git a/Xwt/Xwt.csproj b/Xwt/Xwt.csproj index 1aff665b..d00f7c25 100644 --- a/Xwt/Xwt.csproj +++ b/Xwt/Xwt.csproj @@ -351,15 +351,17 @@ Component - - - Component - + + Component + + + + @@ -372,4 +374,7 @@ - \ No newline at end of file + + + + diff --git a/Xwt/Xwt/RichTextView.cs b/Xwt/Xwt/RichTextView.cs new file mode 100644 index 00000000..2a9fb924 --- /dev/null +++ b/Xwt/Xwt/RichTextView.cs @@ -0,0 +1,128 @@ +// +// RichTextView.cs +// +// Author: +// Jérémie Laval +// Alex Corrado +// +// Copyright (c) 2012 Xamarin, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; +using System.Linq; +using System.Text; + +using Xwt.Backends; +using Xwt.Formats; + +namespace Xwt +{ + public class RichTextView : Widget + { + protected new class WidgetBackendHost : Widget.WidgetBackendHost, IRichTextViewEventSink + { + public void OnNavigateToUrl (Uri uri) + { + ((RichTextView) Parent).OnNavigateToUrl (new NavigateToUrlEventArgs (uri)); + } + } + + IRichTextViewBackend Backend { + get { return (IRichTextViewBackend) BackendHost.Backend; } + } + + EventHandler navigateToUrl; + public event EventHandler NavigateToUrl + { + add + { + BackendHost.OnBeforeEventAdd (RichTextViewEvent.NavigateToUrl, navigateToUrl); + navigateToUrl += value; + } + remove + { + navigateToUrl -= value; + BackendHost.OnAfterEventRemove (RichTextViewEvent.NavigateToUrl, navigateToUrl); + } + } + + public RichTextView () + { + NavigateToUrl += delegate { }; // ensure the virtual method is always called + } + + public void LoadFile (string fileName, TextFormat format) + { + using (var stream = new FileStream (fileName, FileMode.Open, FileAccess.Read)) + LoadStream (stream, format); + } + + public void LoadText (string text, TextFormat format) + { + using (var stream = new MemoryStream (Encoding.UTF8.GetBytes (text), false)) + LoadStream (stream, format); + } + + public virtual void LoadStream (Stream input, TextFormat format) + { + var buffer = Backend.CreateBuffer (); + format.Parse (input, buffer); + Backend.SetBuffer (buffer); + } + + protected override BackendHost CreateBackendHost () + { + return new WidgetBackendHost (); + } + + protected virtual void OnNavigateToUrl (NavigateToUrlEventArgs e) + { + if (navigateToUrl != null) + navigateToUrl (this, e); + + if (!e.Handled) + Application.EngineBackend.ShowWebBrowser (e); + } + } + + public class MarkdownView : RichTextView + { + string markdown; + public string Markdown + { + get + { + return markdown; + } + set + { + markdown = value; + LoadText (value, TextFormat.Markdown); + } + } + + public MarkdownView () + { + Markdown = string.Empty; + } + } +} +