1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/lunarsf-Lunar-Markdown-Editor

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
Клонировать/Скачать
TextEditor.cs 45 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
LunarSF Отправлено 8 лет назад 25f1ba7
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// 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.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit
{
/// <summary>
/// The text editor control.
/// Contains a scrollable TextArea.
/// </summary>
[Localizability(LocalizationCategory.Text), ContentProperty("Text")]
public class TextEditor : Control, ITextEditorComponent, IServiceProvider, IWeakEventListener
{
#region Constructors
static TextEditor()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TextEditor),
new FrameworkPropertyMetadata(typeof(TextEditor)));
FocusableProperty.OverrideMetadata(typeof(TextEditor),
new FrameworkPropertyMetadata(Boxes.True));
}
/// <summary>
/// Creates a new TextEditor instance.
/// </summary>
public TextEditor() : this(new TextArea())
{
}
/// <summary>
/// Creates a new TextEditor instance.
/// </summary>
protected TextEditor(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
textArea.TextView.Services.AddService(typeof(TextEditor), this);
textArea.TextEditor = this;
SetCurrentValue(OptionsProperty, textArea.Options);
SetCurrentValue(DocumentProperty, new TextDocument());
}
#if !DOTNET4
void SetCurrentValue(DependencyProperty property, object value)
{
SetValue(property, value);
}
#endif
#endregion
/// <inheritdoc/>
protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
{
return new TextEditorAutomationPeer(this);
}
/// Forward focus to TextArea.
/// <inheritdoc/>
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
if (e.NewFocus == this)
{
Keyboard.Focus(this.TextArea);
e.Handled = true;
}
}
#region Document property
/// <summary>
/// Document property.
/// </summary>
public static readonly DependencyProperty DocumentProperty
= TextView.DocumentProperty.AddOwner(
typeof(TextEditor), new FrameworkPropertyMetadata(OnDocumentChanged));
/// <summary>
/// Gets/Sets the document displayed by the text editor.
/// This is a dependency property.
/// </summary>
public TextDocument Document
{
get { return (TextDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
/// <summary>
/// Occurs when the document property has changed.
/// </summary>
public event EventHandler DocumentChanged;
/// <summary>
/// Raises the <see cref="DocumentChanged"/> event.
/// </summary>
protected virtual void OnDocumentChanged(EventArgs e)
{
if (DocumentChanged != null)
{
DocumentChanged(this, e);
}
}
static void OnDocumentChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
((TextEditor)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue);
}
void OnDocumentChanged(TextDocument oldValue, TextDocument newValue)
{
if (oldValue != null)
{
TextDocumentWeakEventManager.TextChanged.RemoveListener(oldValue, this);
PropertyChangedEventManager.RemoveListener(oldValue.UndoStack, this, "IsOriginalFile");
}
textArea.Document = newValue;
if (newValue != null)
{
TextDocumentWeakEventManager.TextChanged.AddListener(newValue, this);
PropertyChangedEventManager.AddListener(newValue.UndoStack, this, "IsOriginalFile");
}
OnDocumentChanged(EventArgs.Empty);
OnTextChanged(EventArgs.Empty);
}
#endregion
#region Options property
/// <summary>
/// Options property.
/// </summary>
public static readonly DependencyProperty OptionsProperty
= TextView.OptionsProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(OnOptionsChanged));
/// <summary>
/// Gets/Sets the options currently used by the text editor.
/// </summary>
public TextEditorOptions Options
{
get { return (TextEditorOptions)GetValue(OptionsProperty); }
set { SetValue(OptionsProperty, value); }
}
/// <summary>
/// Occurs when a text editor option has changed.
/// </summary>
public event PropertyChangedEventHandler OptionChanged;
/// <summary>
/// Raises the <see cref="OptionChanged"/> event.
/// </summary>
protected virtual void OnOptionChanged(PropertyChangedEventArgs e)
{
if (OptionChanged != null)
{
OptionChanged(this, e);
}
}
static void OnOptionsChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
((TextEditor)dp).OnOptionsChanged((TextEditorOptions)e.OldValue, (TextEditorOptions)e.NewValue);
}
void OnOptionsChanged(TextEditorOptions oldValue, TextEditorOptions newValue)
{
if (oldValue != null)
{
PropertyChangedWeakEventManager.RemoveListener(oldValue, this);
}
textArea.Options = newValue;
if (newValue != null)
{
PropertyChangedWeakEventManager.AddListener(newValue, this);
}
OnOptionChanged(new PropertyChangedEventArgs(null));
}
/// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType == typeof(PropertyChangedWeakEventManager))
{
OnOptionChanged((PropertyChangedEventArgs)e);
return true;
}
else if (managerType == typeof(TextDocumentWeakEventManager.TextChanged))
{
OnTextChanged(e);
return true;
}
else if (managerType == typeof(PropertyChangedEventManager))
{
return HandleIsOriginalChanged((PropertyChangedEventArgs)e);
}
return false;
}
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return ReceiveWeakEvent(managerType, sender, e);
}
#endregion
#region Text property
/// <summary>
/// Gets/Sets the text of the current document.
/// </summary>
[Localizability(LocalizationCategory.Text), DefaultValue("")]
public string Text
{
get
{
TextDocument document = this.Document;
return document != null ? document.Text : string.Empty;
}
set
{
TextDocument document = GetDocument();
document.Text = value ?? string.Empty;
// after replacing the full text, the caret is positioned at the end of the document
// - reset it to the beginning.
this.CaretOffset = 0;
document.UndoStack.ClearAll();
}
}
TextDocument GetDocument()
{
TextDocument document = this.Document;
if (document == null)
throw ThrowUtil.NoDocumentAssigned();
return document;
}
/// <summary>
/// Occurs when the Text property changes.
/// </summary>
public event EventHandler TextChanged;
/// <summary>
/// Raises the <see cref="TextChanged"/> event.
/// </summary>
protected virtual void OnTextChanged(EventArgs e)
{
if (TextChanged != null)
{
TextChanged(this, e);
}
}
#endregion
#region TextArea / ScrollViewer properties
readonly TextArea textArea;
ScrollViewer scrollViewer;
/// <summary>
/// Is called after the template was applied.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
scrollViewer = (ScrollViewer)Template.FindName("PART_ScrollViewer", this);
}
/// <summary>
/// Gets the text area.
/// </summary>
public TextArea TextArea
{
get
{
return textArea;
}
}
/// <summary>
/// Gets the scroll viewer used by the text editor.
/// This property can return null if the template has not been applied / does not contain a scroll viewer.
/// </summary>
internal ScrollViewer ScrollViewer
{
get { return scrollViewer; }
}
bool CanExecute(RoutedUICommand command)
{
TextArea textArea = this.TextArea;
if (textArea == null)
return false;
else
return command.CanExecute(null, textArea);
}
void Execute(RoutedUICommand command)
{
TextArea textArea = this.TextArea;
if (textArea != null)
command.Execute(null, textArea);
}
#endregion
#region Syntax highlighting
/// <summary>
/// The <see cref="SyntaxHighlighting"/> property.
/// </summary>
public static readonly DependencyProperty SyntaxHighlightingProperty =
DependencyProperty.Register("SyntaxHighlighting", typeof(IHighlightingDefinition), typeof(TextEditor),
new FrameworkPropertyMetadata(OnSyntaxHighlightingChanged));
/// <summary>
/// Gets/sets the syntax highlighting definition used to colorize the text.
/// </summary>
public IHighlightingDefinition SyntaxHighlighting
{
get { return (IHighlightingDefinition)GetValue(SyntaxHighlightingProperty); }
set { SetValue(SyntaxHighlightingProperty, value); }
}
IVisualLineTransformer colorizer;
static void OnSyntaxHighlightingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TextEditor)d).OnSyntaxHighlightingChanged(e.NewValue as IHighlightingDefinition);
}
void OnSyntaxHighlightingChanged(IHighlightingDefinition newValue)
{
if (colorizer != null)
{
this.TextArea.TextView.LineTransformers.Remove(colorizer);
colorizer = null;
}
if (newValue != null)
{
colorizer = CreateColorizer(newValue);
if (colorizer != null)
this.TextArea.TextView.LineTransformers.Insert(0, colorizer);
}
}
/// <summary>
/// Creates the highlighting colorizer for the specified highlighting definition.
/// Allows derived classes to provide custom colorizer implementations for special highlighting definitions.
/// </summary>
/// <returns></returns>
protected virtual IVisualLineTransformer CreateColorizer(IHighlightingDefinition highlightingDefinition)
{
if (highlightingDefinition == null)
throw new ArgumentNullException("highlightingDefinition");
return new HighlightingColorizer(highlightingDefinition);
}
#endregion
#region WordWrap
/// <summary>
/// Word wrap dependency property.
/// </summary>
public static readonly DependencyProperty WordWrapProperty =
DependencyProperty.Register("WordWrap", typeof(bool), typeof(TextEditor),
new FrameworkPropertyMetadata(Boxes.False));
/// <summary>
/// Specifies whether the text editor uses word wrapping.
/// </summary>
/// <remarks>
/// Setting WordWrap=true has the same effect as setting HorizontalScrollBarVisibility=Disabled and will override the
/// HorizontalScrollBarVisibility setting.
/// </remarks>
public bool WordWrap
{
get { return (bool)GetValue(WordWrapProperty); }
set { SetValue(WordWrapProperty, Boxes.Box(value)); }
}
#endregion
#region IsReadOnly
/// <summary>
/// IsReadOnly dependency property.
/// </summary>
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(TextEditor),
new FrameworkPropertyMetadata(Boxes.False, OnIsReadOnlyChanged));
/// <summary>
/// Specifies whether the user can change the text editor content.
/// Setting this property will replace the
/// <see cref="Editing.TextArea.ReadOnlySectionProvider">TextArea.ReadOnlySectionProvider</see>.
/// </summary>
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, Boxes.Box(value)); }
}
static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextEditor editor = d as TextEditor;
if (editor != null)
{
if ((bool)e.NewValue)
editor.TextArea.ReadOnlySectionProvider = ReadOnlySectionDocument.Instance;
else
editor.TextArea.ReadOnlySectionProvider = NoReadOnlySections.Instance;
TextEditorAutomationPeer peer = TextEditorAutomationPeer.FromElement(editor) as TextEditorAutomationPeer;
if (peer != null)
{
peer.RaiseIsReadOnlyChanged((bool)e.OldValue, (bool)e.NewValue);
}
}
}
#endregion
#region IsModified
/// <summary>
/// Dependency property for <see cref="IsModified"/>
/// </summary>
public static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(TextEditor),
new FrameworkPropertyMetadata(Boxes.False, OnIsModifiedChanged));
/// <summary>
/// Gets/Sets the 'modified' flag.
/// </summary>
public bool IsModified
{
get { return (bool)GetValue(IsModifiedProperty); }
set { SetValue(IsModifiedProperty, Boxes.Box(value)); }
}
static void OnIsModifiedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextEditor editor = d as TextEditor;
if (editor != null)
{
TextDocument document = editor.Document;
if (document != null)
{
UndoStack undoStack = document.UndoStack;
if ((bool)e.NewValue)
{
if (undoStack.IsOriginalFile)
undoStack.DiscardOriginalFileMarker();
}
else
{
undoStack.MarkAsOriginalFile();
}
}
}
}
bool HandleIsOriginalChanged(PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsOriginalFile")
{
TextDocument document = this.Document;
if (document != null)
{
SetCurrentValue(IsModifiedProperty, Boxes.Box(!document.UndoStack.IsOriginalFile));
}
return true;
}
else
{
return false;
}
}
#endregion
#region ShowLineNumbers
/// <summary>
/// ShowLineNumbers dependency property.
/// </summary>
public static readonly DependencyProperty ShowLineNumbersProperty =
DependencyProperty.Register("ShowLineNumbers", typeof(bool), typeof(TextEditor),
new FrameworkPropertyMetadata(Boxes.False, OnShowLineNumbersChanged));
/// <summary>
/// Specifies whether line numbers are shown on the left to the text view.
/// </summary>
public bool ShowLineNumbers
{
get { return (bool)GetValue(ShowLineNumbersProperty); }
set { SetValue(ShowLineNumbersProperty, Boxes.Box(value)); }
}
static void OnShowLineNumbersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextEditor editor = (TextEditor)d;
var leftMargins = editor.TextArea.LeftMargins;
if ((bool)e.NewValue)
{
LineNumberMargin lineNumbers = new LineNumberMargin();
Line line = (Line)DottedLineMargin.Create();
leftMargins.Insert(0, lineNumbers);
leftMargins.Insert(1, line);
var lineNumbersForeground = new Binding("LineNumbersForeground") { Source = editor };
line.SetBinding(Line.StrokeProperty, lineNumbersForeground);
lineNumbers.SetBinding(Control.ForegroundProperty, lineNumbersForeground);
}
else
{
for (int i = 0; i < leftMargins.Count; i++)
{
if (leftMargins[i] is LineNumberMargin)
{
leftMargins.RemoveAt(i);
if (i < leftMargins.Count && DottedLineMargin.IsDottedLineMargin(leftMargins[i]))
{
leftMargins.RemoveAt(i);
}
break;
}
}
}
}
#endregion
#region LineNumbersForeground
/// <summary>
/// LineNumbersForeground dependency property.
/// </summary>
public static readonly DependencyProperty LineNumbersForegroundProperty =
DependencyProperty.Register("LineNumbersForeground", typeof(Brush), typeof(TextEditor),
new FrameworkPropertyMetadata(Brushes.Gray, OnLineNumbersForegroundChanged));
/// <summary>
/// Gets/sets the Brush used for displaying the foreground color of line numbers.
/// </summary>
public Brush LineNumbersForeground
{
get { return (Brush)GetValue(LineNumbersForegroundProperty); }
set { SetValue(LineNumbersForegroundProperty, value); }
}
static void OnLineNumbersForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextEditor editor = (TextEditor)d;
var lineNumberMargin = editor.TextArea.LeftMargins.FirstOrDefault(margin => margin is LineNumberMargin) as LineNumberMargin; ;
if (lineNumberMargin != null)
{
lineNumberMargin.SetValue(Control.ForegroundProperty, e.NewValue);
}
}
#endregion
#region TextBoxBase-like methods
/// <summary>
/// Appends text to the end of the document.
/// </summary>
public void AppendText(string textData)
{
var document = GetDocument();
document.Insert(document.TextLength, textData);
}
/// <summary>
/// Begins a group of document changes.
/// </summary>
public void BeginChange()
{
GetDocument().BeginUpdate();
}
/// <summary>
/// Copies the current selection to the clipboard.
/// </summary>
public void Copy()
{
Execute(ApplicationCommands.Copy);
}
/// <summary>
/// Removes the current selection and copies it to the clipboard.
/// </summary>
public void Cut()
{
Execute(ApplicationCommands.Cut);
}
/// <summary>
/// Begins a group of document changes and returns an object that ends the group of document
/// changes when it is disposed.
/// </summary>
public IDisposable DeclareChangeBlock()
{
return GetDocument().RunUpdate();
}
/// <summary>
/// Ends the current group of document changes.
/// </summary>
public void EndChange()
{
GetDocument().EndUpdate();
}
/// <summary>
/// Scrolls one line down.
/// </summary>
public void LineDown()
{
if (scrollViewer != null)
scrollViewer.LineDown();
}
/// <summary>
/// Scrolls to the left.
/// </summary>
public void LineLeft()
{
if (scrollViewer != null)
scrollViewer.LineLeft();
}
/// <summary>
/// Scrolls to the right.
/// </summary>
public void LineRight()
{
if (scrollViewer != null)
scrollViewer.LineRight();
}
/// <summary>
/// Scrolls one line up.
/// </summary>
public void LineUp()
{
if (scrollViewer != null)
scrollViewer.LineUp();
}
/// <summary>
/// Scrolls one page down.
/// </summary>
public void PageDown()
{
if (scrollViewer != null)
scrollViewer.PageDown();
}
/// <summary>
/// Scrolls one page up.
/// </summary>
public void PageUp()
{
if (scrollViewer != null)
scrollViewer.PageUp();
}
/// <summary>
/// Scrolls one page left.
/// </summary>
public void PageLeft()
{
if (scrollViewer != null)
scrollViewer.PageLeft();
}
/// <summary>
/// Scrolls one page right.
/// </summary>
public void PageRight()
{
if (scrollViewer != null)
scrollViewer.PageRight();
}
/// <summary>
/// Pastes the clipboard content.
/// </summary>
public void Paste()
{
Execute(ApplicationCommands.Paste);
}
/// <summary>
/// Redoes the most recent undone command.
/// </summary>
/// <returns>True is the redo operation was successful, false is the redo stack is empty.</returns>
public bool Redo()
{
if (CanExecute(ApplicationCommands.Redo))
{
Execute(ApplicationCommands.Redo);
return true;
}
return false;
}
/// <summary>
/// Scrolls to the end of the document.
/// </summary>
public void ScrollToEnd()
{
ApplyTemplate(); // ensure scrollViewer is created
if (scrollViewer != null)
scrollViewer.ScrollToEnd();
}
/// <summary>
/// Scrolls to the start of the document.
/// </summary>
public void ScrollToHome()
{
ApplyTemplate(); // ensure scrollViewer is created
if (scrollViewer != null)
scrollViewer.ScrollToHome();
}
/// <summary>
/// Scrolls to the specified position in the document.
/// </summary>
public void ScrollToHorizontalOffset(double offset)
{
ApplyTemplate(); // ensure scrollViewer is created
if (scrollViewer != null)
scrollViewer.ScrollToHorizontalOffset(offset);
}
/// <summary>
/// Scrolls to the specified position in the document.
/// </summary>
public void ScrollToVerticalOffset(double offset)
{
ApplyTemplate(); // ensure scrollViewer is created
if (scrollViewer != null)
scrollViewer.ScrollToVerticalOffset(offset);
}
/// <summary>
/// Selects the entire text.
/// </summary>
public void SelectAll()
{
Execute(ApplicationCommands.SelectAll);
}
/// <summary>
/// Undoes the most recent command.
/// </summary>
/// <returns>True is the undo operation was successful, false is the undo stack is empty.</returns>
public bool Undo()
{
if (CanExecute(ApplicationCommands.Undo))
{
Execute(ApplicationCommands.Undo);
return true;
}
return false;
}
/// <summary>
/// Gets if the most recent undone command can be redone.
/// </summary>
public bool CanRedo
{
get { return CanExecute(ApplicationCommands.Redo); }
}
/// <summary>
/// Gets if the most recent command can be undone.
/// </summary>
public bool CanUndo
{
get { return CanExecute(ApplicationCommands.Undo); }
}
/// <summary>
/// Gets the vertical size of the document.
/// </summary>
public double ExtentHeight
{
get
{
return scrollViewer != null ? scrollViewer.ExtentHeight : 0;
}
}
/// <summary>
/// Gets the horizontal size of the current document region.
/// </summary>
public double ExtentWidth
{
get
{
return scrollViewer != null ? scrollViewer.ExtentWidth : 0;
}
}
/// <summary>
/// Gets the horizontal size of the viewport.
/// </summary>
public double ViewportHeight
{
get
{
return scrollViewer != null ? scrollViewer.ViewportHeight : 0;
}
}
/// <summary>
/// Gets the horizontal size of the viewport.
/// </summary>
public double ViewportWidth
{
get
{
return scrollViewer != null ? scrollViewer.ViewportWidth : 0;
}
}
/// <summary>
/// Gets the vertical scroll position.
/// </summary>
public double VerticalOffset
{
get
{
return scrollViewer != null ? scrollViewer.VerticalOffset : 0;
}
}
/// <summary>
/// Gets the horizontal scroll position.
/// </summary>
public double HorizontalOffset
{
get
{
return scrollViewer != null ? scrollViewer.HorizontalOffset : 0;
}
}
#endregion
#region TextBox methods
/// <summary>
/// Gets/Sets the selected text.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string SelectedText
{
get
{
TextArea textArea = this.TextArea;
// We'll get the text from the whole surrounding segment.
// This is done to ensure that SelectedText.Length == SelectionLength.
if (textArea != null && textArea.Document != null && !textArea.Selection.IsEmpty)
return textArea.Document.GetText(textArea.Selection.SurroundingSegment);
else
return string.Empty;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
TextArea textArea = this.TextArea;
if (textArea != null && textArea.Document != null)
{
int offset = this.SelectionStart;
int length = this.SelectionLength;
textArea.Document.Replace(offset, length, value);
// keep inserted text selected
textArea.Selection = SimpleSelection.Create(textArea, offset, offset + value.Length);
}
}
}
/// <summary>
/// Gets/sets the caret position.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int CaretOffset
{
get
{
TextArea textArea = this.TextArea;
if (textArea != null)
return textArea.Caret.Offset;
else
return 0;
}
set
{
TextArea textArea = this.TextArea;
if (textArea != null)
textArea.Caret.Offset = value;
}
}
/// <summary>
/// Gets/sets the start position of the selection.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SelectionStart
{
get
{
TextArea textArea = this.TextArea;
if (textArea != null)
{
if (textArea.Selection.IsEmpty)
return textArea.Caret.Offset;
else
return textArea.Selection.SurroundingSegment.Offset;
}
else
{
return 0;
}
}
set
{
Select(value, SelectionLength);
}
}
/// <summary>
/// Gets/sets the length of the selection.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SelectionLength
{
get
{
TextArea textArea = this.TextArea;
if (textArea != null && !textArea.Selection.IsEmpty)
return textArea.Selection.SurroundingSegment.Length;
else
return 0;
}
set
{
Select(SelectionStart, value);
}
}
/// <summary>
/// Selects the specified text section.
/// </summary>
public void Select(int start, int length)
{
int documentLength = Document != null ? Document.TextLength : 0;
if (start < 0 || start > documentLength)
throw new ArgumentOutOfRangeException("start", start, "Value must be between 0 and " + documentLength);
if (length < 0 || start + length > documentLength)
throw new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (documentLength - start));
textArea.Selection = SimpleSelection.Create(textArea, start, start + length);
textArea.Caret.Offset = start + length;
}
/// <summary>
/// Gets the number of lines in the document.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int LineCount
{
get
{
TextDocument document = this.Document;
if (document != null)
return document.LineCount;
else
return 1;
}
}
/// <summary>
/// Clears the text.
/// </summary>
public void Clear()
{
this.Text = string.Empty;
}
#endregion
#region Loading from stream
/// <summary>
/// Loads the text from the stream, auto-detecting the encoding.
/// </summary>
/// <remarks>
/// This method sets <see cref="IsModified"/> to false.
/// </remarks>
public void Load(Stream stream)
{
using (StreamReader reader = FileReader.OpenStream(stream, this.Encoding ?? Encoding.UTF8))
{
this.Text = reader.ReadToEnd();
SetCurrentValue(EncodingProperty, reader.CurrentEncoding); // assign encoding after ReadToEnd() so that the StreamReader can autodetect the encoding
}
SetCurrentValue(IsModifiedProperty, Boxes.False);
}
/// <summary>
/// Loads the text from the stream, auto-detecting the encoding.
/// </summary>
public void Load(string fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Load(fs);
}
}
/// <summary>
/// Encoding dependency property.
/// </summary>
public static readonly DependencyProperty EncodingProperty =
DependencyProperty.Register("Encoding", typeof(Encoding), typeof(TextEditor));
/// <summary>
/// Gets/sets the encoding used when the file is saved.
/// </summary>
/// <remarks>
/// The <see cref="Load(Stream)"/> method autodetects the encoding of the file and sets this property accordingly.
/// The <see cref="Save(Stream)"/> method uses the encoding specified in this property.
/// </remarks>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Encoding Encoding
{
get { return (Encoding)GetValue(EncodingProperty); }
set { SetValue(EncodingProperty, value); }
}
/// <summary>
/// Saves the text to the stream.
/// </summary>
/// <remarks>
/// This method sets <see cref="IsModified"/> to false.
/// </remarks>
public void Save(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
var encoding = this.Encoding;
var document = this.Document;
StreamWriter writer = encoding != null ? new StreamWriter(stream, encoding) : new StreamWriter(stream);
if (document != null)
document.WriteTextTo(writer);
writer.Flush();
// do not close the stream
SetCurrentValue(IsModifiedProperty, Boxes.False);
}
/// <summary>
/// Saves the text to the file.
/// </summary>
public void Save(string fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Save(fs);
}
}
#endregion
#region MouseHover events
/// <summary>
/// The PreviewMouseHover event.
/// </summary>
public static readonly RoutedEvent PreviewMouseHoverEvent =
TextView.PreviewMouseHoverEvent.AddOwner(typeof(TextEditor));
/// <summary>
/// The MouseHover event.
/// </summary>
public static readonly RoutedEvent MouseHoverEvent =
TextView.MouseHoverEvent.AddOwner(typeof(TextEditor));
/// <summary>
/// The PreviewMouseHoverStopped event.
/// </summary>
public static readonly RoutedEvent PreviewMouseHoverStoppedEvent =
TextView.PreviewMouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
/// <summary>
/// The MouseHoverStopped event.
/// </summary>
public static readonly RoutedEvent MouseHoverStoppedEvent =
TextView.MouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
/// <summary>
/// Occurs when the mouse has hovered over a fixed location for some time.
/// </summary>
public event MouseEventHandler PreviewMouseHover
{
add { AddHandler(PreviewMouseHoverEvent, value); }
remove { RemoveHandler(PreviewMouseHoverEvent, value); }
}
/// <summary>
/// Occurs when the mouse has hovered over a fixed location for some time.
/// </summary>
public event MouseEventHandler MouseHover
{
add { AddHandler(MouseHoverEvent, value); }
remove { RemoveHandler(MouseHoverEvent, value); }
}
/// <summary>
/// Occurs when the mouse had previously hovered but now started moving again.
/// </summary>
public event MouseEventHandler PreviewMouseHoverStopped
{
add { AddHandler(PreviewMouseHoverStoppedEvent, value); }
remove { RemoveHandler(PreviewMouseHoverStoppedEvent, value); }
}
/// <summary>
/// Occurs when the mouse had previously hovered but now started moving again.
/// </summary>
public event MouseEventHandler MouseHoverStopped
{
add { AddHandler(MouseHoverStoppedEvent, value); }
remove { RemoveHandler(MouseHoverStoppedEvent, value); }
}
#endregion
#region ScrollBarVisibility
/// <summary>
/// Dependency property for <see cref="HorizontalScrollBarVisibility"/>
/// </summary>
public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = ScrollViewer.HorizontalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
/// <summary>
/// Gets/Sets the horizontal scroll bar visibility.
/// </summary>
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); }
set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
}
/// <summary>
/// Dependency property for <see cref="VerticalScrollBarVisibility"/>
/// </summary>
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
/// <summary>
/// Gets/Sets the vertical scroll bar visibility.
/// </summary>
public ScrollBarVisibility VerticalScrollBarVisibility
{
get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); }
set { SetValue(VerticalScrollBarVisibilityProperty, value); }
}
#endregion
object IServiceProvider.GetService(Type serviceType)
{
return textArea.GetService(serviceType);
}
/// <summary>
/// Gets the text view position from a point inside the editor.
/// </summary>
/// <param name="point">The position, relative to top left
/// corner of TextEditor control</param>
/// <returns>The text view position, or null if the point is outside the document.</returns>
public TextViewPosition? GetPositionFromPoint(Point point)
{
if (this.Document == null)
return null;
TextView textView = this.TextArea.TextView;
return textView.GetPosition(TranslatePoint(point, textView) + textView.ScrollOffset);
}
/// <summary>
/// Scrolls to the specified line.
/// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
/// </summary>
public void ScrollToLine(int line)
{
ScrollTo(line, -1);
}
/// <summary>
/// Scrolls to the specified line/column.
/// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
/// </summary>
public void ScrollTo(int line, int column)
{
const double MinimumScrollPercentage = 0.3;
TextView textView = textArea.TextView;
TextDocument document = textView.Document;
if (scrollViewer != null && document != null)
{
if (line < 1)
line = 1;
if (line > document.LineCount)
line = document.LineCount;
IScrollInfo scrollInfo = textView;
if (!scrollInfo.CanHorizontallyScroll)
{
// Word wrap is enabled. Ensure that we have up-to-date info about line height so that we scroll
// to the correct position.
// This avoids that the user has to repeat the ScrollTo() call several times when there are very long lines.
VisualLine vl = textView.GetOrConstructVisualLine(document.GetLineByNumber(line));
double remainingHeight = scrollViewer.ViewportHeight / 2;
while (remainingHeight > 0)
{
DocumentLine prevLine = vl.FirstDocumentLine.PreviousLine;
if (prevLine == null)
break;
vl = textView.GetOrConstructVisualLine(prevLine);
remainingHeight -= vl.Height;
}
}
Point p = textArea.TextView.GetVisualPosition(new TextViewPosition(line, Math.Max(1, column)), VisualYPosition.LineMiddle);
double verticalPos = p.Y - scrollViewer.ViewportHeight / 2;
if (Math.Abs(verticalPos - scrollViewer.VerticalOffset) > MinimumScrollPercentage * scrollViewer.ViewportHeight)
{
scrollViewer.ScrollToVerticalOffset(Math.Max(0, verticalPos));
}
if (column > 0)
{
if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2)
{
double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2);
if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth)
{
scrollViewer.ScrollToHorizontalOffset(horizontalPos);
}
}
else
{
scrollViewer.ScrollToHorizontalOffset(0);
}
}
}
}
/// <summary>
/// 编辑模式。
/// </summary>
public EditingMode EditingMode { get; set; } = EditingMode.NormalMode;
/// <summary>
/// 移动光标。
/// </summary>
/// <param name="direction"></param>
public void MoveCaret(CaretMovementType direction)
{
double desiredXPos = textArea.Caret.DesiredXPos;
textArea.Caret.Position = CaretNavigationCommandHandler.GetNewCaretPosition(textArea.TextView, textArea.Caret.Position, direction, textArea.Selection.EnableVirtualSpace, ref desiredXPos);
textArea.Caret.DesiredXPos = desiredXPos;
textArea.Caret.BringCaretToView();
}
}
/// <summary>
/// 快捷键模式。在普通模式下,编辑快捷键起作用;在Vim模式下,统一不起作用。
/// </summary>
public enum EditingMode
{
/// <summary>
/// 普通模式,内置快捷键起作用。
/// </summary>
NormalMode,
/// <summary>
/// Vim模式,内置快捷键都不起作用。
/// </summary>
VimMode,
}
}

Комментарий ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/lunarsf-Lunar-Markdown-Editor.git
git@gitlife.ru:oschina-mirror/lunarsf-Lunar-Markdown-Editor.git
oschina-mirror
lunarsf-Lunar-Markdown-Editor
lunarsf-Lunar-Markdown-Editor
v0.4-beta8