PageRenderTime 30ms CodeModel.GetById 15ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs

http://github.com/icsharpcode/ILSpy
C# | 159 lines | 82 code | 18 blank | 59 comment | 10 complexity | 32109ef2bbb09f1061f24456333ad371 MD5 | raw file
  1// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
  2// 
  3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4// software and associated documentation files (the "Software"), to deal in the Software
  5// without restriction, including without limitation the rights to use, copy, modify, merge,
  6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7// to whom the Software is furnished to do so, subject to the following conditions:
  8// 
  9// The above copyright notice and this permission notice shall be included in all copies or
 10// substantial portions of the Software.
 11// 
 12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 17// DEALINGS IN THE SOFTWARE.
 18
 19using System;
 20using System.Text.RegularExpressions;
 21using ICSharpCode.AvalonEdit.Utils;
 22
 23namespace ICSharpCode.AvalonEdit.Rendering
 24{
 25	// This class is public because it can be used as a base class for custom links.
 26	
 27	/// <summary>
 28	/// Detects hyperlinks and makes them clickable.
 29	/// </summary>
 30	/// <remarks>
 31	/// This element generator can be easily enabled and configured using the
 32	/// <see cref="TextEditorOptions"/>.
 33	/// </remarks>
 34	public class LinkElementGenerator : VisualLineElementGenerator, IBuiltinElementGenerator
 35	{
 36		// a link starts with a protocol (or just with www), followed by 0 or more 'link characters', followed by a link end character
 37		// (this allows accepting punctuation inside links but not at the end)
 38		internal readonly static Regex defaultLinkRegex = new Regex(@"\b(https?://|ftp://|www\.)[\w\d\._/\-~%@()+:?&=#!]*[\w\d/]");
 39		
 40		// try to detect email addresses
 41		internal readonly static Regex defaultMailRegex = new Regex(@"\b[\w\d\.\-]+\@[\w\d\.\-]+\.[a-z]{2,6}\b");
 42		
 43		readonly Regex linkRegex;
 44		
 45		/// <summary>
 46		/// Gets/Sets whether the user needs to press Control to click the link.
 47		/// The default value is true.
 48		/// </summary>
 49		public bool RequireControlModifierForClick { get; set; }
 50		
 51		/// <summary>
 52		/// Creates a new LinkElementGenerator.
 53		/// </summary>
 54		public LinkElementGenerator()
 55		{
 56			this.linkRegex = defaultLinkRegex;
 57			this.RequireControlModifierForClick = true;
 58		}
 59		
 60		/// <summary>
 61		/// Creates a new LinkElementGenerator using the specified regex.
 62		/// </summary>
 63		protected LinkElementGenerator(Regex regex) : this()
 64		{
 65			if (regex == null)
 66				throw new ArgumentNullException("regex");
 67			this.linkRegex = regex;
 68		}
 69		
 70		void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options)
 71		{
 72			this.RequireControlModifierForClick = options.RequireControlModifierForHyperlinkClick;
 73		}
 74		
 75		Match GetMatch(int startOffset, out int matchOffset)
 76		{
 77			int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
 78			StringSegment relevantText = CurrentContext.GetText(startOffset, endOffset - startOffset);
 79			Match m = linkRegex.Match(relevantText.Text, relevantText.Offset, relevantText.Count);
 80			matchOffset = m.Success ? m.Index - relevantText.Offset + startOffset : -1;
 81			return m;
 82		}
 83		
 84		/// <inheritdoc/>
 85		public override int GetFirstInterestedOffset(int startOffset)
 86		{
 87			int matchOffset;
 88			GetMatch(startOffset, out matchOffset);
 89			return matchOffset;
 90		}
 91		
 92		/// <inheritdoc/>
 93		public override VisualLineElement ConstructElement(int offset)
 94		{
 95			int matchOffset;
 96			Match m = GetMatch(offset, out matchOffset);
 97			if (m.Success && matchOffset == offset) {
 98				return ConstructElementFromMatch(m);
 99			} else {
100				return null;
101			}
102		}
103		
104		/// <summary>
105		/// Constructs a VisualLineElement that replaces the matched text.
106		/// The default implementation will create a <see cref="VisualLineLinkText"/>
107		/// based on the URI provided by <see cref="GetUriFromMatch"/>.
108		/// </summary>
109		protected virtual VisualLineElement ConstructElementFromMatch(Match m)
110		{
111			Uri uri = GetUriFromMatch(m);
112			if (uri == null)
113				return null;
114			VisualLineLinkText linkText = new VisualLineLinkText(CurrentContext.VisualLine, m.Length);
115			linkText.NavigateUri = uri;
116			linkText.RequireControlModifierForClick = this.RequireControlModifierForClick;
117			return linkText;
118		}
119		
120		/// <summary>
121		/// Fetches the URI from the regex match. Returns null if the URI format is invalid.
122		/// </summary>
123		protected virtual Uri GetUriFromMatch(Match match)
124		{
125			string targetUrl = match.Value;
126			if (targetUrl.StartsWith("www.", StringComparison.Ordinal))
127				targetUrl = "http://" + targetUrl;
128			if (Uri.IsWellFormedUriString(targetUrl, UriKind.Absolute))
129				return new Uri(targetUrl);
130			
131			return null;
132		}
133	}
134	
135	// This class is internal because it does not need to be accessed by the user - it can be configured using TextEditorOptions.
136	
137	/// <summary>
138	/// Detects e-mail addresses and makes them clickable.
139	/// </summary>
140	/// <remarks>
141	/// This element generator can be easily enabled and configured using the
142	/// <see cref="TextEditorOptions"/>.
143	/// </remarks>
144	sealed class MailLinkElementGenerator : LinkElementGenerator
145	{
146		/// <summary>
147		/// Creates a new MailLinkElementGenerator.
148		/// </summary>
149		public MailLinkElementGenerator()
150			: base(defaultMailRegex)
151		{
152		}
153		
154		protected override Uri GetUriFromMatch(Match match)
155		{
156			return new Uri("mailto:" + match.Value);
157		}
158	}
159}