Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4908 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
12 changed files with 375 additions and 48 deletions
@ -0,0 +1,138 @@
@@ -0,0 +1,138 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Diagnostics; |
||||
using System.Text.RegularExpressions; |
||||
using System.Windows; |
||||
using System.Windows.Documents; |
||||
using System.Windows.Input; |
||||
using System.Windows.Media; |
||||
using System.Windows.Media.TextFormatting; |
||||
|
||||
using ICSharpCode.AvalonEdit.Document; |
||||
using System.Windows.Navigation; |
||||
|
||||
namespace ICSharpCode.AvalonEdit.Rendering |
||||
{ |
||||
// This class is public because it can be used as a base class for custom links.
|
||||
|
||||
/// <summary>
|
||||
/// Detects hyperlinks and makes them clickable.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This element generator can be easily enabled and configured using the
|
||||
/// <see cref="TextEditorOptions"/>.
|
||||
/// </remarks>
|
||||
public class LinkElementGenerator : VisualLineElementGenerator, IBuiltinElementGenerator |
||||
{ |
||||
// a link starts with a protocol (or just with www), followed by 0 or more 'link characters', followed by a link end character
|
||||
// (this allows accepting punctuation inside links but not at the end)
|
||||
internal readonly static Regex defaultLinkRegex = new Regex(@"\b(https?://|ftp://|www\.)[\w\d\._/\-~%()+:?&=]*[\w\d/]"); |
||||
|
||||
// try to detect email addresses
|
||||
internal readonly static Regex defaultMailRegex = new Regex(@"\b[\w\d\.\-]+\@[\w\d\.\-]+\.[a-z]{2,6}\b"); |
||||
|
||||
readonly Regex linkRegex; |
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets whether the user needs to press Control to click the link.
|
||||
/// The default value is true.
|
||||
/// </summary>
|
||||
public bool RequireControlModifierForClick { get; set; } |
||||
|
||||
/// <summary>
|
||||
/// Creates a new LinkElementGenerator.
|
||||
/// </summary>
|
||||
public LinkElementGenerator() |
||||
{ |
||||
this.linkRegex = defaultLinkRegex; |
||||
this.RequireControlModifierForClick = true; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates a new LinkElementGenerator using the specified regex.
|
||||
/// </summary>
|
||||
protected LinkElementGenerator(Regex regex) : this() |
||||
{ |
||||
if (regex == null) |
||||
throw new ArgumentNullException("regex"); |
||||
this.linkRegex = regex; |
||||
} |
||||
|
||||
void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options) |
||||
{ |
||||
this.RequireControlModifierForClick = options.RequireControlModifierForHyperlinkClick; |
||||
} |
||||
|
||||
Match GetMatch(int startOffset) |
||||
{ |
||||
DocumentLine endLine = CurrentContext.VisualLine.LastDocumentLine; |
||||
int endOffset = endLine.Offset + endLine.Length; |
||||
string relevantText = CurrentContext.Document.GetText(startOffset, endOffset - startOffset); |
||||
return linkRegex.Match(relevantText); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetFirstInterestedOffset(int startOffset) |
||||
{ |
||||
Match m = GetMatch(startOffset); |
||||
return m.Success ? startOffset + m.Index : -1; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public override VisualLineElement ConstructElement(int offset) |
||||
{ |
||||
Match m = GetMatch(offset); |
||||
if (m.Success && m.Index == 0) { |
||||
VisualLineLinkText linkText = new VisualLineLinkText(CurrentContext.VisualLine, m.Length); |
||||
linkText.NavigateUri = GetUriFromMatch(m); |
||||
linkText.RequireControlModifierForClick = this.RequireControlModifierForClick; |
||||
return linkText; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Fetches the URI from the regex match.
|
||||
/// </summary>
|
||||
protected virtual Uri GetUriFromMatch(Match match) |
||||
{ |
||||
string targetUrl = match.Value; |
||||
if (targetUrl.StartsWith("www.", StringComparison.Ordinal)) |
||||
targetUrl = "http://" + targetUrl; |
||||
return new Uri(targetUrl); |
||||
} |
||||
} |
||||
|
||||
// This class is internal because it does not need to be accessed by the user - it can be configured using TextEditorOptions.
|
||||
|
||||
/// <summary>
|
||||
/// Detects e-mail addresses and makes them clickable.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This element generator can be easily enabled and configured using the
|
||||
/// <see cref="TextEditorOptions"/>.
|
||||
/// </remarks>
|
||||
sealed class MailLinkElementGenerator : LinkElementGenerator |
||||
{ |
||||
/// <summary>
|
||||
/// Creates a new MailLinkElementGenerator.
|
||||
/// </summary>
|
||||
public MailLinkElementGenerator() |
||||
: base(defaultMailRegex) |
||||
{ |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Uri GetUriFromMatch(Match match) |
||||
{ |
||||
return new Uri("mailto:" + match.Value); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Diagnostics; |
||||
using System.Windows; |
||||
using System.Windows.Documents; |
||||
using System.Windows.Input; |
||||
using System.Windows.Media; |
||||
using System.Windows.Media.TextFormatting; |
||||
using System.Windows.Navigation; |
||||
|
||||
namespace ICSharpCode.AvalonEdit.Rendering |
||||
{ |
||||
/// <summary>
|
||||
/// VisualLineElement that represents a piece of text and is a clickable link.
|
||||
/// </summary>
|
||||
public class VisualLineLinkText : VisualLineText |
||||
{ |
||||
/// <summary>
|
||||
/// Gets/Sets the URL that is navigated to when the link is clicked.
|
||||
/// </summary>
|
||||
public Uri NavigateUri { get; set; } |
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the window name where the URL will be opened.
|
||||
/// </summary>
|
||||
public string TargetName { get; set; } |
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets whether the user needs to press Control to click the link.
|
||||
/// The default value is true.
|
||||
/// </summary>
|
||||
public bool RequireControlModifierForClick { get; set; } |
||||
|
||||
/// <summary>
|
||||
/// Creates a visual line text element with the specified length.
|
||||
/// It uses the <see cref="ITextRunConstructionContext.VisualLine"/> and its
|
||||
/// <see cref="VisualLineElement.RelativeTextOffset"/> to find the actual text string.
|
||||
/// </summary>
|
||||
public VisualLineLinkText(VisualLine parentVisualLine, int length) : base(parentVisualLine, length) |
||||
{ |
||||
this.RequireControlModifierForClick = true; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context) |
||||
{ |
||||
this.TextRunProperties.SetForegroundBrush(Brushes.Blue); |
||||
this.TextRunProperties.SetTextDecorations(TextDecorations.Underline); |
||||
return base.CreateTextRun(startVisualColumn, context); |
||||
} |
||||
|
||||
bool LinkIsClickable() |
||||
{ |
||||
if (NavigateUri == null) |
||||
return false; |
||||
if (RequireControlModifierForClick) |
||||
return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control; |
||||
else |
||||
return true; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
protected internal override void OnQueryCursor(QueryCursorEventArgs e) |
||||
{ |
||||
if (LinkIsClickable()) { |
||||
e.Handled = true; |
||||
e.Cursor = Cursors.Hand; |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", |
||||
Justification = "I've seen Process.Start throw undocumented exceptions when the mail client / web browser is installed incorrectly")] |
||||
protected internal override void OnMouseDown(MouseButtonEventArgs e) |
||||
{ |
||||
if (e.ChangedButton == MouseButton.Left && !e.Handled && LinkIsClickable()) { |
||||
RequestNavigateEventArgs args = new RequestNavigateEventArgs(this.NavigateUri, this.TargetName); |
||||
args.RoutedEvent = Hyperlink.RequestNavigateEvent; |
||||
FrameworkElement element = e.Source as FrameworkElement; |
||||
if (element != null) { |
||||
// allow user code to handle the navigation request
|
||||
element.RaiseEvent(args); |
||||
} |
||||
if (!args.Handled) { |
||||
try { |
||||
Process.Start(this.NavigateUri.ToString()); |
||||
} catch { |
||||
// ignore all kinds of errors during web browser start
|
||||
} |
||||
} |
||||
e.Handled = true; |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
protected override VisualLineText CreateInstance(int length) |
||||
{ |
||||
return new VisualLineLinkText(ParentVisualLine, length) { |
||||
NavigateUri = this.NavigateUri, |
||||
TargetName = this.TargetName, |
||||
RequireControlModifierForClick = this.RequireControlModifierForClick |
||||
}; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue