// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media.TextFormatting; using System.Windows.Threading; using Tango.Scripting.Editors.Document; using Tango.Scripting.Editors.Rendering; using Tango.Scripting.Editors.Utils; namespace Tango.Scripting.Editors.Editing { /// /// Handles selection of text using the mouse. /// sealed class SelectionMouseHandler : ITextAreaInputHandler { #region enum SelectionMode enum SelectionMode { /// /// no selection (no mouse button down) /// None, /// /// left mouse button down on selection, might be normal click /// or might be drag'n'drop /// PossibleDragStart, /// /// dragging text /// Drag, /// /// normal selection (click+drag) /// Normal, /// /// whole-word selection (double click+drag or ctrl+click+drag) /// WholeWord, /// /// whole-line selection (triple click+drag) /// WholeLine, /// /// rectangular selection (alt+click+drag) /// Rectangular } #endregion readonly TextArea textArea; SelectionMode mode; AnchorSegment startWord; Point possibleDragStartMousePos; #region Constructor + Attach + Detach public SelectionMouseHandler(TextArea textArea) { if (textArea == null) throw new ArgumentNullException("textArea"); this.textArea = textArea; } public TextArea TextArea { get { return textArea; } } public void Attach() { textArea.MouseLeftButtonDown += textArea_MouseLeftButtonDown; textArea.MouseMove += textArea_MouseMove; textArea.MouseLeftButtonUp += textArea_MouseLeftButtonUp; textArea.QueryCursor += textArea_QueryCursor; textArea.OptionChanged += textArea_OptionChanged; enableTextDragDrop = textArea.Options.EnableTextDragDrop; if (enableTextDragDrop) { AttachDragDrop(); } } public void Detach() { mode = SelectionMode.None; textArea.MouseLeftButtonDown -= textArea_MouseLeftButtonDown; textArea.MouseMove -= textArea_MouseMove; textArea.MouseLeftButtonUp -= textArea_MouseLeftButtonUp; textArea.QueryCursor -= textArea_QueryCursor; textArea.OptionChanged -= textArea_OptionChanged; if (enableTextDragDrop) { DetachDragDrop(); } } void AttachDragDrop() { textArea.AllowDrop = true; textArea.GiveFeedback += textArea_GiveFeedback; textArea.QueryContinueDrag += textArea_QueryContinueDrag; textArea.DragEnter += textArea_DragEnter; textArea.DragOver += textArea_DragOver; textArea.DragLeave += textArea_DragLeave; textArea.Drop += textArea_Drop; } void DetachDragDrop() { textArea.AllowDrop = false; textArea.GiveFeedback -= textArea_GiveFeedback; textArea.QueryContinueDrag -= textArea_QueryContinueDrag; textArea.DragEnter -= textArea_DragEnter; textArea.DragOver -= textArea_DragOver; textArea.DragLeave -= textArea_DragLeave; textArea.Drop -= textArea_Drop; } bool enableTextDragDrop; void textArea_OptionChanged(object sender, PropertyChangedEventArgs e) { bool newEnableTextDragDrop = textArea.Options.EnableTextDragDrop; if (newEnableTextDragDrop != enableTextDragDrop) { enableTextDragDrop = newEnableTextDragDrop; if (newEnableTextDragDrop) AttachDragDrop(); else DetachDragDrop(); } } #endregion #region Dropping text [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] void textArea_DragEnter(object sender, DragEventArgs e) { try { e.Effects = GetEffect(e); textArea.Caret.Show(); } catch (Exception ex) { OnDragException(ex); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] void textArea_DragOver(object sender, DragEventArgs e) { try { e.Effects = GetEffect(e); } catch (Exception ex) { OnDragException(ex); } } DragDropEffects GetEffect(DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.UnicodeText, true)) { e.Handled = true; int visualColumn; int offset = GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn); if (offset >= 0) { textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn); textArea.Caret.DesiredXPos = double.NaN; if (textArea.ReadOnlySectionProvider.CanInsert(offset)) { if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move && (e.KeyStates & DragDropKeyStates.ControlKey) != DragDropKeyStates.ControlKey) { return DragDropEffects.Move; } else { return e.AllowedEffects & DragDropEffects.Copy; } } } } return DragDropEffects.None; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] void textArea_DragLeave(object sender, DragEventArgs e) { try { e.Handled = true; if (!textArea.IsKeyboardFocusWithin) textArea.Caret.Hide(); } catch (Exception ex) { OnDragException(ex); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] void textArea_Drop(object sender, DragEventArgs e) { try { DragDropEffects effect = GetEffect(e); e.Effects = effect; if (effect != DragDropEffects.None) { string text = e.Data.GetData(DataFormats.UnicodeText, true) as string; if (text != null) { int start = textArea.Caret.Offset; if (mode == SelectionMode.Drag && textArea.Selection.Contains(start)) { Debug.WriteLine("Drop: did not drop: drop target is inside selection"); e.Effects = DragDropEffects.None; } else { Debug.WriteLine("Drop: insert at " + start); bool rectangular = e.Data.GetDataPresent(RectangleSelection.RectangularSelectionDataType); string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line); text = TextUtilities.NormalizeNewLines(text, newLine); // Mark the undo group with the currentDragDescriptor, if the drag // is originating from the same control. This allows combining // the undo groups when text is moved. textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor); try { if (rectangular && RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, true)) { } else { textArea.Document.Insert(start, text); textArea.Selection = Selection.Create(textArea, start, start + text.Length); } } finally { textArea.Document.UndoStack.EndUndoGroup(); } } e.Handled = true; } } } catch (Exception ex) { OnDragException(ex); } } void OnDragException(Exception ex) { // WPF swallows exceptions during drag'n'drop or reports them incorrectly, so // we re-throw them later to allow the application's unhandled exception handler // to catch them textArea.Dispatcher.BeginInvoke( DispatcherPriority.Normal, new Action(delegate { throw new DragDropException("Exception during drag'n'drop", ex); })); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] void textArea_GiveFeedback(object sender, GiveFeedbackEventArgs e) { try { e.UseDefaultCursors = true; e.Handled = true; } catch (Exception ex) { OnDragException(ex); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] void textArea_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) { try { if (e.EscapePressed) { e.Action = DragAction.Cancel; } else if ((e.KeyStates & DragDropKeyStates.LeftMouseButton) != DragDropKeyStates.LeftMouseButton) { e.Action = DragAction.Drop; } else { e.Action = DragAction.Continue; } e.Handled = true; } catch (Exception ex) { OnDragException(ex); } } #endregion #region Start Drag object currentDragDescriptor; void StartDrag() { // prevent nested StartDrag calls mode = SelectionMode.Drag; // mouse capture and Drag'n'Drop doesn't mix textArea.ReleaseMouseCapture(); DataObject dataObject = textArea.Selection.CreateDataO
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)

using System;
using Tango.Scripting.Editors.Utils;

namespace Tango.Scripting.Editors.Document
{
	/// <summary>
	/// Contains weak event managers for the TextDocument events.
	/// </summary>
	public static class TextDocumentWeakEventManager
	{
		/// <summary>
		/// Weak event manager for the <see cref="TextDocument.UpdateStarted"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		public sealed class UpdateStarted : WeakEventManagerBase<UpdateStarted, TextDocument>
		{
			/// <inheritdoc/>
			protected override void StartListening(TextDocument source)
			{
				source.UpdateStarted += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(TextDocument source)
			{
				source.UpdateStarted -= DeliverEvent;
			}
		}
		
		/// <summary>
		/// Weak event manager for the <see cref="TextDocument.UpdateFinished"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		public sealed class UpdateFinished : WeakEventManagerBase<UpdateFinished, TextDocument>
		{
			/// <inheritdoc/>
			protected override void StartListening(TextDocument source)
			{
				source.UpdateFinished += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(TextDocument source)
			{
				source.UpdateFinished -= DeliverEvent;
			}
		}
		
		/// <summary>
		/// Weak event manager for the <see cref="TextDocument.Changing"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		public sealed class Changing : WeakEventManagerBase<Changing, TextDocument>
		{
			/// <inheritdoc/>
			protected override void StartListening(TextDocument source)
			{
				source.Changing += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(TextDocument source)
			{
				source.Changing -= DeliverEvent;
			}
		}
		
		/// <summary>
		/// Weak event manager for the <see cref="TextDocument.Changed"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		public sealed class Changed : WeakEventManagerBase<Changed, TextDocument>
		{
			/// <inheritdoc/>
			protected override void StartListening(TextDocument source)
			{
				source.Changed += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(TextDocument source)
			{
				source.Changed -= DeliverEvent;
			}
		}
		
		/// <summary>
		/// Weak event manager for the <see cref="TextDocument.LineCountChanged"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		[Obsolete("The TextDocument.LineCountChanged event will be removed in a future version. Use PropertyChangedEventManager instead.")]
		public sealed class LineCountChanged : WeakEventManagerBase<LineCountChanged, TextDocument>
		{
			/// <inheritdoc/>
			protected override void StartListening(TextDocument source)
			{
				source.LineCountChanged += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(TextDocument source)
			{
				source.LineCountChanged -= DeliverEvent;
			}
		}
		
		/// <summary>
		/// Weak event manager for the <see cref="TextDocument.TextLengthChanged"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		[Obsolete("The TextDocument.TextLengthChanged event will be removed in a future version. Use PropertyChangedEventManager instead.")]
		public sealed class TextLengthChanged : WeakEventManagerBase<TextLengthChanged, TextDocument>
		{
			/// <inheritdoc/>
			protected override void StartListening(TextDocument source)
			{
				source.TextLengthChanged += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(TextDocument source)
			{
				source.TextLengthChanged -= DeliverEvent;
			}
		}
		
		/// <summary>
		/// Weak event manager for the <see cref="TextDocument.TextChanged"/> event.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
		public sealed class TextChanged : WeakEventManagerBase<TextChanged, TextDocument>
		{
			/// <inheritdoc/>
			protected override void StartListening(TextDocument source)
			{
				source.TextChanged += DeliverEvent;
			}
			
			/// <inheritdoc/>
			protected override void StopListening(TextDocument source)
			{
				source.TextChanged -= DeliverEvent;
			}
		}
	}
}