Browse Source

Implemented Copy/Paste for rectangular selections.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4663 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 16 years ago
parent
commit
c78ef52b7a
  1. 16
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
  2. 141
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs
  3. 18
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs
  4. 20
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs
  5. 7
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/HtmlClipboard.cs

16
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs

@ -291,13 +291,7 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -291,13 +291,7 @@ namespace ICSharpCode.AvalonEdit.Editing
static void CopySelectedText(TextArea textArea)
{
string text = textArea.Selection.GetText(textArea.Document);
// Ensure we use the appropriate newline sequence for the OS
DataObject data = new DataObject(NewLineFinder.NormalizeNewLines(text, Environment.NewLine));
// Also copy text in HTML format to clipboard - good for pasting text into Word
// or to the SharpDevelop forums.
HtmlClipboard.SetHtml(data, HtmlClipboard.CreateHtmlFragmentForSelection(textArea, new HtmlOptions(textArea.Options)));
Clipboard.SetDataObject(data, true);
Clipboard.SetDataObject(textArea.Selection.CreateDataObject(textArea), true);
}
const string LineSelectedType = "MSDEVLineSelect"; // This is the type VS 2003 and 2005 use for flagging a whole line copy
@ -343,9 +337,15 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -343,9 +337,15 @@ namespace ICSharpCode.AvalonEdit.Editing
if (!string.IsNullOrEmpty(text)) {
bool fullLine = textArea.Options.CutCopyWholeLine && Clipboard.ContainsData(LineSelectedType);
bool rectangular = Clipboard.ContainsData(RectangleSelection.RectangularSelectionDataType);
if (fullLine) {
DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
textArea.Document.Insert(currentLine.Offset, text);
if (textArea.ReadOnlySectionProvider.CanInsert(currentLine.Offset)) {
textArea.Document.Insert(currentLine.Offset, text);
}
} else if (rectangular && textArea.Selection.IsEmpty) {
if (!RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Offset, text, false))
textArea.ReplaceSelectionWithText(text);
} else {
textArea.ReplaceSelectionWithText(text);
}

141
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs

@ -7,7 +7,9 @@ @@ -7,7 +7,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
@ -55,7 +57,7 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -55,7 +57,7 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <inheritdoc/>
public override bool Contains(int offset)
{
if (StartOffset <= offset && offset <= EndOffset) {
if (Math.Min(StartOffset, EndOffset) <= offset && offset <= Math.Max(StartOffset, EndOffset)) {
foreach (ISegment s in this.Segments) {
if (s.Contains(offset))
return true;
@ -64,6 +66,18 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -64,6 +66,18 @@ namespace ICSharpCode.AvalonEdit.Editing
return false;
}
/// <inheritdoc/>
public override string GetText(TextDocument document)
{
StringBuilder b = new StringBuilder();
foreach (ISegment s in this.Segments) {
if (b.Length > 0)
b.AppendLine();
b.Append(document.GetText(s));
}
return b.ToString();
}
/// <inheritdoc/>
public override Selection StartSelectionOrSetEndpoint(int startOffset, int newEndOffset)
{
@ -125,7 +139,9 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -125,7 +139,9 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <inheritdoc/>
public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
{
return Selection.Empty;
return new RectangleSelection(document,
e.GetNewOffset(StartOffset, AnchorMovementType.AfterInsertion),
e.GetNewOffset(EndOffset, AnchorMovementType.BeforeInsertion));
}
/// <inheritdoc/>
@ -139,33 +155,120 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -139,33 +155,120 @@ namespace ICSharpCode.AvalonEdit.Editing
TextLocation start = document.GetLocation(StartOffset);
TextLocation end = document.GetLocation(EndOffset);
int editColumn = Math.Min(start.Column, end.Column);
foreach (ISegment lineSegment in this.Segments.Reverse()) {
if (lineSegment.Length == 0) {
if (newText.Length > 0 && textArea.ReadOnlySectionProvider.CanInsert(lineSegment.Offset)) {
textArea.Document.Insert(lineSegment.Offset, newText);
}
} else {
var segmentsToDelete = textArea.ReadOnlySectionProvider.GetDeletableSegments(lineSegment).ToList();
for (int i = segmentsToDelete.Count - 1; i >= 0; i--) {
if (i == segmentsToDelete.Count - 1) {
textArea.Document.Replace(segmentsToDelete[i], newText);
} else {
textArea.Document.Remove(segmentsToDelete[i]);
}
}
}
}
if (NewLineFinder.NextNewLine(newText, 0) == SimpleSegment.Invalid) {
// insert same text into every line
foreach (ISegment lineSegment in this.Segments.Reverse()) {
ReplaceSingleLineText(textArea, lineSegment, newText);
}
TextLocation newStart = new TextLocation(start.Line, editColumn + newText.Length);
TextLocation newEnd = new TextLocation(end.Line, editColumn + newText.Length);
textArea.Caret.Location = newEnd;
textArea.Selection = new RectangleSelection(document, document.GetOffset(newStart), document.GetOffset(newEnd));
} else {
textArea.Selection = Selection.Empty;
// convert all segment start/ends to anchors
var segments = this.Segments.Select(s => new AnchorSegment(this.document, s)).ToList();
SimpleSegment ds = NewLineFinder.NextNewLine(newText, 0);
// we'll check whether all lines have the same length. If so, we can continue using a rectangular selection.
int commonLength = -1;
// now insert lines into rectangular selection
int lastDelimiterEnd = 0;
bool isAtEnd = false;
int i;
for (i = 0; i < segments.Count; i++) {
string lineText;
if (ds == SimpleSegment.Invalid || (i == segments.Count - 1)) {
lineText = newText.Substring(lastDelimiterEnd);
isAtEnd = true;
// if we have more lines to insert than this selection is long, we cannot continue using a rectangular selection
if (ds != SimpleSegment.Invalid)
commonLength = -1;
} else {
lineText = newText.Substring(lastDelimiterEnd, ds.Offset - lastDelimiterEnd);
}
if (i == 0) {
commonLength = lineText.Length;
} else if (commonLength != lineText.Length) {
commonLength = -1;
}
ReplaceSingleLineText(textArea, segments[i], lineText);
if (isAtEnd)
break;
lastDelimiterEnd = ds.EndOffset;
ds = NewLineFinder.NextNewLine(newText, lastDelimiterEnd);
}
if (commonLength >= 0) {
TextLocation newStart = new TextLocation(start.Line, editColumn + commonLength);
TextLocation newEnd = new TextLocation(start.Line + i, editColumn + commonLength);
textArea.Selection = new RectangleSelection(document, document.GetOffset(newStart), document.GetOffset(newEnd));
} else {
textArea.Selection = Selection.Empty;
}
}
}
}
void ReplaceSingleLineText(TextArea textArea, ISegment lineSegment, string newText)
{
if (lineSegment.Length == 0) {
if (newText.Length > 0 && textArea.ReadOnlySectionProvider.CanInsert(lineSegment.Offset)) {
textArea.Document.Insert(lineSegment.Offset, newText);
}
} else {
var segmentsToDelete = textArea.ReadOnlySectionProvider.GetDeletableSegments(lineSegment).ToList();
for (int i = segmentsToDelete.Count - 1; i >= 0; i--) {
if (i == segmentsToDelete.Count - 1) {
textArea.Document.Replace(segmentsToDelete[i], newText);
} else {
textArea.Document.Remove(segmentsToDelete[i]);
}
}
}
}
/// <summary>
/// Performs a rectangular paste operation.
/// </summary>
public static bool PerformRectangularPaste(TextArea textArea, int startOffset, string text, bool selectInsertedText)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
if (text == null)
throw new ArgumentNullException("text");
int newLineCount = text.Count(c => c == '\n');
TextLocation startLocation = textArea.Document.GetLocation(startOffset);
TextLocation endLocation = new TextLocation(startLocation.Line + newLineCount, startLocation.Column);
if (endLocation.Line <= textArea.Document.LineCount) {
int endOffset = textArea.Document.GetOffset(endLocation);
if (textArea.Document.GetLocation(endOffset) == endLocation) {
RectangleSelection rsel = new RectangleSelection(textArea.Document, startOffset, endOffset);
rsel.ReplaceSelectionWithText(textArea, text);
if (selectInsertedText && textArea.Selection is RectangleSelection) {
RectangleSelection sel = (RectangleSelection)textArea.Selection;
textArea.Selection = new RectangleSelection(textArea.Document, startOffset, sel.EndOffset);
}
return true;
}
}
return false;
}
/// <summary>
/// Gets the name of the entry in the DataObject that signals rectangle selections.
/// </summary>
public const string RectangularSelectionDataType = "AvalonEditRectangularSelection";
/// <inheritdoc/>
public override System.Windows.DataObject CreateDataObject(TextArea textArea)
{
var data = base.CreateDataObject(textArea);
MemoryStream isRectangle = new MemoryStream(1);
isRectangle.WriteByte(1);
data.SetData(RectangularSelectionDataType, isRectangle, false);
return data;
}
/// <inheritdoc/>
public override string ToString()
{

18
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs

@ -11,6 +11,7 @@ using System.Text; @@ -11,6 +11,7 @@ using System.Text;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
using System.Windows;
namespace ICSharpCode.AvalonEdit.Editing
{
@ -141,5 +142,22 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -141,5 +142,22 @@ namespace ICSharpCode.AvalonEdit.Editing
}
return false;
}
/// <summary>
/// Creates a data object containing the selection's text.
/// </summary>
public virtual DataObject CreateDataObject(TextArea textArea)
{
string text = GetText(textArea.Document);
// Ensure we use the appropriate newline sequence for the OS
DataObject data = new DataObject(NewLineFinder.NormalizeNewLines(text, Environment.NewLine));
// we cannot use DataObject.SetText - then we cannot drag to SciTe
// (but dragging to Word works in both cases)
// Also copy text in HTML format to clipboard - good for pasting text into Word
// or to the SharpDevelop forums.
HtmlClipboard.SetHtml(data, HtmlClipboard.CreateHtmlFragmentForSelection(textArea, new HtmlOptions(textArea.Options)));
return data;
}
}
}

20
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs

@ -180,6 +180,8 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -180,6 +180,8 @@ namespace ICSharpCode.AvalonEdit.Editing
} else {
Debug.WriteLine("Drop: insert at " + start);
bool rectangular = e.Data.GetDataPresent(RectangleSelection.RectangularSelectionDataType);
string newLine = NewLineFinder.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
text = NewLineFinder.NormalizeNewLines(text, newLine);
@ -188,8 +190,12 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -188,8 +190,12 @@ namespace ICSharpCode.AvalonEdit.Editing
// the undo groups when text is moved.
textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor);
try {
textArea.Document.Insert(start, text);
textArea.Selection = new SimpleSelection(start, start + text.Length);
if (rectangular && RectangleSelection.PerformRectangularPaste(textArea, start, text, true)) {
} else {
textArea.Document.Insert(start, text);
textArea.Selection = new SimpleSelection(start, start + text.Length);
}
} finally {
textArea.Document.UndoStack.EndUndoGroup();
}
@ -243,15 +249,7 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -243,15 +249,7 @@ namespace ICSharpCode.AvalonEdit.Editing
// mouse capture and Drag'n'Drop doesn't mix
textArea.ReleaseMouseCapture();
string text = textArea.Selection.GetText(textArea.Document);
text = NewLineFinder.NormalizeNewLines(text, Environment.NewLine);
DataObject dataObject = new DataObject(text);
// we cannot use DataObject.SetText - then we cannot drag to SciTe
// (but dragging to Word works in both cases)
// also copy as HTML - adds syntax highlighting when dragging to Word
string htmlFragment = HtmlClipboard.CreateHtmlFragmentForSelection(textArea, new HtmlOptions(textArea.Options));
HtmlClipboard.SetHtml(dataObject, htmlFragment);
DataObject dataObject = textArea.Selection.CreateDataObject(textArea);
DragDropEffects allowedEffects = DragDropEffects.All;
var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();

7
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/HtmlClipboard.cs

@ -109,8 +109,13 @@ namespace ICSharpCode.AvalonEdit.Utils @@ -109,8 +109,13 @@ namespace ICSharpCode.AvalonEdit.Utils
throw new ArgumentNullException("options");
DocumentHighlighter highlighter = textArea.GetService(typeof(DocumentHighlighter)) as DocumentHighlighter;
StringBuilder html = new StringBuilder();
bool first = true;
foreach (ISegment selectedSegment in textArea.Selection.Segments) {
html.AppendLine(CreateHtmlFragment(textArea.Document, highlighter, selectedSegment, options));
if (first)
first = false;
else
html.AppendLine("<br>");
html.Append(CreateHtmlFragment(textArea.Document, highlighter, selectedSegment, options));
}
return html.ToString();
}

Loading…
Cancel
Save