// Copyright (c) 2016-2017 Nicolas Musset. All rights reserved. // This file is licensed under the MIT license. // See the LICENSE.md file in the project root for more information. using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using Markdig.Agg; using Markdig.Syntax.Inlines; using MatterHackers.Agg; using MatterHackers.Agg.Image; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; using MatterHackers.MatterControl; using MatterHackers.VectorMath; namespace Markdig.Renderers.Agg.Inlines { public class TextLinkX : FlowLayoutWidget { private LinkInline linkInline; private string url; private AggRenderer aggRenderer; public TextLinkX(AggRenderer renderer, string url, LinkInline linkInline) { this.HAnchor = HAnchor.Fit; this.VAnchor = VAnchor.Fit; this.Cursor = Cursors.Hand; this.linkInline = linkInline; this.url = url; this.aggRenderer = renderer; } protected override void OnClick(MouseEventArgs mouseEvent) { if (linkInline.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { ApplicationController.Instance.LaunchBrowser(linkInline.Url); } else { if (aggRenderer.RootWidget.Parents().FirstOrDefault() is MarkdownWidget markdownWidget) { markdownWidget.LoadUri(new Uri(url)); } } base.OnClick(mouseEvent); } public override GuiWidget AddChild(GuiWidget childToAdd, int indexInChildrenList = -1) { if (childToAdd is TextWidget textWidget) { // Underline TextWidget children of TextLink nodes textWidget.Underline = true; } // Allow link parent to own mouse events childToAdd.Selectable = false; return base.AddChild(childToAdd, indexInChildrenList); } } public class ImageLinkSimpleX : FlowLayoutWidget { private static ImageBuffer icon = AggContext.StaticData.LoadIcon("internet.png", 16, 16); public ImageLinkSimpleX(AggRenderer renderer, string imageUrl, string linkUrl = null) { this.HAnchor = HAnchor.Stretch; this.VAnchor = VAnchor.Fit; this.Selectable = false; this.ImageUrl = imageUrl; this.LinkUrl = linkUrl; this.aggRenderer = renderer; if (linkUrl != null) { this.Selectable = true; } sequenceWidget = new ResponsiveImageSequenceWidget(new ImageSequence(icon)) { Cursor = Cursors.Hand, }; sequenceWidget.MaximumSizeChanged += (s, e) => { this.MinStretchOrFitHorizontal(20 * GuiWidget.DeviceScale, sequenceWidget.MaximumSize.X); if (aggRenderer.RootWidget.Parents().FirstOrDefault() is MarkdownWidget markdownWidget) { markdownWidget.Width += 1; } }; sequenceWidget.Click += SequenceWidget_Click; this.AddChild(sequenceWidget); } private void SequenceWidget_Click(object sender, MouseEventArgs e) { if (this.LinkUrl != null) { if (LinkUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { ApplicationController.Instance.LaunchBrowser(LinkUrl); } else { try { if (aggRenderer.RootWidget.Parents().FirstOrDefault() is MarkdownWidget markdownWidget) { markdownWidget.LoadUri(new Uri(LinkUrl)); } } catch { } } } } public override void OnDraw(Graphics2D graphics2D) { if (!hasBeenLoaded) { if (ImageUrl.StartsWith("http")) { WebCache.RetrieveImageSquenceAsync(sequenceWidget.ImageSequence, ImageUrl); } hasBeenLoaded = true; } base.OnDraw(graphics2D); } /// /// Sets this control to Stretch and all direct parent FlowLayoutWidgets to Stretch, it then ensures /// this and all direct parent FlowLayouts have a max width of the contents of this. /// /// The minimum size will be set to the larger of the existing minimum size or this value. /// The maximum size will be set to this value. private void MinStretchOrFitHorizontal(double absoluteMinWidth, double absoluteMaxWidth) { this.HAnchor = HAnchor.Stretch; MinimumSize = new Vector2(Math.Max(absoluteMinWidth, MinimumSize.X), MinimumSize.Y); MaximumSize = new Vector2(absoluteMaxWidth, MaximumSize.Y); } public string ImageUrl { get; } private string LinkUrl { get; } private AggRenderer aggRenderer; private bool hasBeenLoaded; private ResponsiveImageSequenceWidget sequenceWidget; } public class ImageLinkAdvancedX : FlowLayoutWidget { private static HttpClient client = new HttpClient(); private static ImageBuffer icon = AggContext.StaticData.LoadIcon("internet.png", 16, 16); public string Url { get; } public ImageLinkAdvancedX(string url) { HAnchor = HAnchor.Fit; VAnchor = VAnchor.Fit; this.Url = url; var imageBuffer = new ImageBuffer(icon); var imageWidget = new ImageWidget(imageBuffer); this.AddChild(imageWidget); try { if (url.StartsWith("http")) { client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).ContinueWith(task => { var response = task.Result; if (response.IsSuccessStatusCode) { response.Content.ReadAsStreamAsync().ContinueWith(streamTask => { // response.Headers.TryGetValues("", s[""] == "" || if (string.Equals(Path.GetExtension(url), ".svg", StringComparison.OrdinalIgnoreCase)) { // Load svg into SvgWidget, swap for ImageWidget try { var svgWidget = new SvgWidget(streamTask.Result, 1) { Border = 1, BorderColor = Color.YellowGreen }; this.ReplaceChild(imageWidget, svgWidget); } catch (Exception svgEx) { Debug.WriteLine("Error loading svg: {0} :: {1}", url, svgEx.Message); } } else { // Load img if (!AggContext.ImageIO.LoadImageData(streamTask.Result, imageBuffer)) { Debug.WriteLine("Error loading image: " + url); } } }); } }); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } } /// /// A Agg renderer for a . /// /// public class AggLinkInlineRenderer : AggObjectRenderer { /// protected override void Write(AggRenderer renderer, LinkInline link) { var url = link.GetDynamicUrl != null ? link.GetDynamicUrl() ?? link.Url : link.Url; if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) { url = "#"; } if (!url.StartsWith("http")) { var pageID = url; url = new Uri(renderer.BaseUri, url).AbsoluteUri; renderer.ChildLinks.Add(new MarkdownDocumentLink() { Uri = new Uri(url), LinkInline = link, PageID = pageID }); } if (link.IsImage) { if (link.Parent is LinkInline linkInLine) { renderer.WriteInline(new ImageLinkSimpleX(renderer, url, linkInLine.Url)); } else { renderer.WriteInline(new ImageLinkSimpleX(renderer, url)); } } else { if (link.FirstChild is LinkInline linkInLine && linkInLine.IsImage) { renderer.WriteChildren(link); } else { renderer.Push(new TextLinkX(renderer, url, link)); renderer.WriteChildren(link); renderer.Pop(); } } } } }