58
59
Clipboard clipboard = Clipboard.Get (CopyOperation.CLIPBOARD_ATOM);
59
60
operation.CopyData (data);
61
clipboard.SetWithData ((Gtk.TargetEntry[])CopyOperation.targetList, operation.ClipboardGetFunc,
62
operation.ClipboardClearFunc);
62
clipboard.SetWithData (CopyOperation.TargetEntries, operation.ClipboardGetFunc, operation.ClipboardClearFunc);
65
65
public class CopyOperation
67
67
public const int TextType = 1;
68
public const int RichTextType = 2;
69
public const int MonoTextType = 3;
68
public const int HTMLTextType = 2;
69
public const int RichTextType = 3;
71
public const int MonoTextType = 99;
71
73
const int UTF8_FORMAT = 8;
73
75
public static readonly Gdk.Atom CLIPBOARD_ATOM = Gdk.Atom.Intern ("CLIPBOARD", false);
74
76
public static readonly Gdk.Atom PRIMARYCLIPBOARD_ATOM = Gdk.Atom.Intern ("PRIMARY", false);
75
77
public static readonly Gdk.Atom RTF_ATOM;
76
78
public static readonly Gdk.Atom MD_ATOM = Gdk.Atom.Intern ("text/monotext", false);
79
public static readonly Gdk.Atom HTML_ATOM;
78
81
public CopyOperation ()
85
string GetCopiedPlainText ()
87
var plainText = new StringBuilder ();
89
foreach (var line in copiedColoredChunks) {
91
plainText.AppendLine ();
96
foreach (var chunk in line) {
97
plainText.Append (chunk.Text);
100
return plainText.ToString ();
82
103
public void SetData (SelectionData selection_data, uint info)
84
105
if (selection_data == null)
88
109
// Windows specific hack to work around bug: Bug 661973 - copy operation in TextEditor braks text lines with duplicate line endings when the file has CRLF
89
110
// Remove when https://bugzilla.gnome.org/show_bug.cgi?id=640439 is fixed.
90
if (Platform.IsWindows) {
91
selection_data.Text = copiedDocument.Text.Replace ("\r\n", "\n");
93
selection_data.Text = copiedDocument.Text;
113
selection_data.Text = GetCopiedPlainText ();
96
115
case RichTextType:
97
selection_data.Set (RTF_ATOM, UTF8_FORMAT, System.Text.Encoding.UTF8.GetBytes (RtfWriter.GenerateRtf (copiedDocument, mode, docStyle, options)));
116
var rtf = RtfWriter.GenerateRtf (copiedColoredChunks, docStyle, options);
117
selection_data.Set (RTF_ATOM, UTF8_FORMAT, Encoding.UTF8.GetBytes (rtf));
120
var html = HtmlWriter.GenerateHtml (copiedColoredChunks, docStyle, options);
121
// Console.WriteLine ("html:" + html);
122
selection_data.Set (HTML_ATOM, UTF8_FORMAT, Encoding.UTF8.GetBytes (html));
99
124
case MonoTextType:
100
byte[] rawText = System.Text.Encoding.UTF8.GetBytes (monoDocument.Text);
101
byte[] data = new byte [rawText.Length + 1];
102
rawText.CopyTo (data, 1);
125
byte[] rawText = Encoding.UTF8.GetBytes (GetCopiedPlainText ());
126
var copyDataLength = (byte)(copyData != null ? copyData.Length : 0);
127
var dataOffset = 1 + 1 + copyDataLength;
128
byte[] data = new byte [rawText.Length + dataOffset];
129
data [1] = copyDataLength;
130
if (copyDataLength > 0)
131
copyData.CopyTo (data, 2);
132
rawText.CopyTo (data, dataOffset);
125
public TextDocument copiedDocument;
126
public TextDocument monoDocument; // has a slightly different format !!!
155
internal List<List<ColoredSegment>> copiedColoredChunks;
127
158
public Mono.TextEditor.Highlighting.ColorScheme docStyle;
128
159
ITextEditorOptions options;
129
Mono.TextEditor.Highlighting.ISyntaxMode mode;
131
public static Gtk.TargetList targetList;
161
public static readonly TargetEntry[] TargetEntries;
162
public static readonly TargetList TargetList;
133
164
static CopyOperation ()
135
166
if (Platform.IsMac) {
136
167
RTF_ATOM = Gdk.Atom.Intern ("NSRTFPboardType", false); //TODO: use public.rtf when dep on MacOS 10.6
168
const string NSHTMLPboardType = "Apple HTML pasteboard type";
169
HTML_ATOM = Gdk.Atom.Intern (NSHTMLPboardType, false);
170
} else if (Platform.IsWindows) {
171
RTF_ATOM = Gdk.Atom.Intern ("Rich Text Format", false);
172
HTML_ATOM = Gdk.Atom.Intern ("HTML Format", false);
138
174
RTF_ATOM = Gdk.Atom.Intern ("text/rtf", false);
175
HTML_ATOM = Gdk.Atom.Intern ("text/html", false);
141
targetList = new Gtk.TargetList ();
142
targetList.Add (RTF_ATOM, /* FLAGS */0, RichTextType);
143
targetList.Add (MD_ATOM, /* FLAGS */0, MonoTextType);
144
targetList.AddTextTargets (TextType);
178
var newTargets = new List<TargetEntry> ();
180
newTargets.Add (new TargetEntry ("SAVE_TARGETS", TargetFlags.App, TextType));
182
newTargets.Add (new TargetEntry (HTML_ATOM.Name, TargetFlags.OtherApp, HTMLTextType));
183
newTargets.Add (new TargetEntry ("UTF8_STRING", TargetFlags.App, TextType));
185
newTargets.Add (new TargetEntry (RTF_ATOM.Name, TargetFlags.OtherApp, RichTextType));
186
newTargets.Add (new TargetEntry (MD_ATOM.Name, TargetFlags.App, MonoTextType));
188
newTargets.Add (new TargetEntry ("text/plain;charset=utf-8", TargetFlags.App, TextType));
189
newTargets.Add (new TargetEntry ("text/plain", TargetFlags.App, TextType));
146
191
//HACK: work around gtk_selection_data_set_text causing crashes on Mac w/ QuickSilver, Clipbard History etc.
147
if (Platform.IsMac) {
148
targetList.Remove ("COMPOUND_TEXT");
149
targetList.Remove ("TEXT");
150
targetList.Remove ("STRING");
192
if (!Platform.IsMac) {
193
newTargets.Add (new TargetEntry ("COMPOUND_TEXT", TargetFlags.App, TextType));
194
newTargets.Add (new TargetEntry ("STRING", TargetFlags.App, TextType));
195
newTargets.Add (new TargetEntry ("TEXT", TargetFlags.App, TextType));
197
TargetEntries = newTargets.ToArray ();
198
TargetList = new TargetList (TargetEntries);
154
201
void CopyData (TextEditorData data, Selection selection)
156
copiedDocument = null;
158
if (selection != null && data != null && data.Document != null) {
159
copiedDocument = new TextDocument ();
160
monoDocument = new TextDocument ();
203
if (!selection.IsEmpty && data != null && data.Document != null) {
161
204
this.docStyle = data.ColorStyle;
162
205
this.options = data.Options;
163
this.mode = SyntaxModeService.GetSyntaxMode (monoDocument, data.MimeType);
164
209
switch (selection.SelectionMode) {
165
210
case SelectionMode.Normal:
166
211
isBlockMode = false;
167
212
var segment = selection.GetSelectionRange (data);
168
var text = data.GetTextAt (segment);
169
copiedDocument.Text = text;
170
monoDocument.Text = text;
171
var line = data.Document.GetLineByOffset (segment.Offset);
172
var spanStack = line.StartSpan.Clone ();
173
SyntaxModeService.ScanSpans (data.Document, this.mode as SyntaxMode, this.mode as SyntaxMode, spanStack, line.Offset, segment.Offset);
174
this.copiedDocument.GetLine (DocumentLocation.MinLine).StartSpan = spanStack;
213
copiedColoredChunks = ColoredSegment.GetChunks (data, segment);
214
var pasteHandler = data.TextPasteHandler;
215
if (pasteHandler != null)
216
copyData = pasteHandler.GetCopyData (segment);
176
218
case SelectionMode.Block:
177
219
isBlockMode = true;
179
221
DocumentLocation visEnd = data.LogicalToVisualLocation (selection.Lead);
180
222
int startCol = System.Math.Min (visStart.Column, visEnd.Column);
181
223
int endCol = System.Math.Max (visStart.Column, visEnd.Column);
224
copiedColoredChunks = new List<List<ColoredSegment>> ();
182
225
for (int lineNr = selection.MinLine; lineNr <= selection.MaxLine; lineNr++) {
183
226
DocumentLine curLine = data.Document.GetLine (lineNr);
184
227
int col1 = curLine.GetLogicalColumn (data, startCol) - 1;
185
228
int col2 = System.Math.Min (curLine.GetLogicalColumn (data, endCol) - 1, curLine.Length);
186
229
if (col1 < col2) {
187
copiedDocument.Insert (copiedDocument.TextLength, data.Document.GetTextAt (curLine.Offset + col1, col2 - col1));
188
monoDocument.Insert (monoDocument.TextLength, data.Document.GetTextAt (curLine.Offset + col1, col2 - col1));
190
if (lineNr < selection.MaxLine) {
191
// Clipboard line end needs to be system dependend and not the document one.
192
copiedDocument.Insert (copiedDocument.TextLength, Environment.NewLine);
193
// \r in mono document stands for block selection line end.
194
monoDocument.Insert (monoDocument.TextLength, "\r");
230
copiedColoredChunks.Add (ColoredSegment.GetChunks (data, new TextSegment (curLine.Offset + col1, col2 - col1)).First ());
232
copiedColoredChunks.Add (new List<ColoredSegment> ());
197
line = data.Document.GetLine (selection.MinLine);
198
spanStack = line.StartSpan.Clone ();
199
SyntaxModeService.ScanSpans (data.Document, this.mode as SyntaxMode, this.mode as SyntaxMode, spanStack, line.Offset, line.Offset + startCol);
200
this.copiedDocument.GetLine (DocumentLocation.MinLine).StartSpan = spanStack;
204
copiedDocument = null;
238
copiedColoredChunks = null;
254
288
clipboard.RequestContents (CopyOperation.MD_ATOM, delegate(Clipboard clp, SelectionData selectionData) {
255
289
if (selectionData.Length > 0) {
256
290
byte[] selBytes = selectionData.Data;
258
string text = System.Text.Encoding.UTF8.GetString (selBytes, 1, selBytes.Length - 1);
291
byte[] copyData = new byte[selBytes[1]];
292
Array.Copy (selBytes, 2, copyData, 0, copyData.Length);
293
var rawTextOffset = 1 + 1 + copyData.Length;
294
string text = System.Text.Encoding.UTF8.GetString (selBytes, rawTextOffset, selBytes.Length - rawTextOffset);
259
295
bool pasteBlock = (selBytes [0] & 1) == 1;
260
296
bool pasteLine = (selBytes [0] & 2) == 2;
262
// var clearSelection = data.IsSomethingSelected ? data.MainSelection.SelectionMode != SelectionMode.Block : true;
263
using (var undo = data.OpenUndoGroup ()) {
298
using (var undo = data.OpenUndoGroup ()) {
299
var version = data.Document.Version;
300
if (!preserveSelection)
301
data.DeleteSelectedText (!data.IsSomethingSelected || data.MainSelection.SelectionMode != SelectionMode.Block);
302
data.EnsureCaretIsNotVirtual ();
303
insertionOffset = version.MoveOffsetTo (data.Document.Version, insertionOffset);
265
305
data.Caret.PreserveSelection = true;
267
string[] lines = text.Split ('\r');
306
var lines = new List<string> ();
309
var delimiter = LineSplitter.NextDelimiter (text, offset);
310
if (delimiter.IsInvalid)
313
int delimiterEndOffset = delimiter.Offset + delimiter.Length;
314
lines.Add (text.Substring (offset, delimiter.Offset - offset));
315
offset = delimiterEndOffset;
317
if (offset < text.Length)
318
lines.Add (text.Substring (offset, text.Length - offset));
268
320
int lineNr = data.Document.OffsetToLineNumber (insertionOffset);
269
321
int col = insertionOffset - data.Document.GetLine (lineNr).Offset;
270
322
int visCol = data.Document.GetLine (lineNr).GetVisualColumn (data, col);
271
323
DocumentLine curLine;
272
324
int lineCol = col;
274
for (int i = 0; i < lines.Length; i++) {
326
for (int i = 0; i < lines.Count; i++) {
275
327
while (data.Document.LineCount <= lineNr + i) {
276
328
data.Insert (data.Document.TextLength, Environment.NewLine);
277
329
result += Environment.NewLine.Length;
292
344
if (!preserveState)
293
345
data.ClearSelection ();
294
346
data.Caret.PreserveSelection = false;
295
} else if (pasteLine) {
348
} else if (pasteLine) {
349
using (var undo = data.OpenUndoGroup ()) {
350
if (!preserveSelection)
351
data.DeleteSelectedText (!data.IsSomethingSelected || data.MainSelection.SelectionMode != SelectionMode.Block);
352
data.EnsureCaretIsNotVirtual ();
296
354
data.Caret.PreserveSelection = true;
297
355
result = text.Length;
298
356
DocumentLine curLine = data.Document.GetLine (data.Caret.Line);
299
data.Insert (curLine.Offset, text + data.EolMarker);
358
result = PastePlainText (data, curLine.Offset, text + data.EolMarker, preserveSelection, copyData);
300
359
if (!preserveState)
301
360
data.ClearSelection ();
302
361
data.Caret.PreserveSelection = false;
304
result = PastePlainText (data, insertionOffset, text);
364
result = PastePlainText (data, insertionOffset, text, preserveSelection, copyData);
326
static int PastePlainText (TextEditorData data, int offset, string text)
385
static int PastePlainText (TextEditorData data, int offset, string text, bool preserveSelection = false, byte[] copyData = null)
328
int inserted = data.Insert (offset, text);
329
data.PasteText (offset, text, inserted);
388
using (var undo = data.OpenUndoGroup ()) {
389
var version = data.Document.Version;
390
if (!preserveSelection)
391
data.DeleteSelectedText (!data.IsSomethingSelected || data.MainSelection.SelectionMode != SelectionMode.Block);
392
data.EnsureCaretIsNotVirtual ();
393
if (data.IsSomethingSelected && data.MainSelection.SelectionMode == SelectionMode.Block) {
394
var selection = data.MainSelection;
395
var visualInsertLocation = data.LogicalToVisualLocation (selection.Anchor);
396
for (int lineNumber = selection.MinLine; lineNumber <= selection.MaxLine; lineNumber++) {
397
var lineSegment = data.GetLine (lineNumber);
398
int insertOffset = lineSegment.GetLogicalColumn (data, visualInsertLocation.Column) - 1;
400
if (lineSegment.Length < insertOffset) {
401
int visualLastColumn = lineSegment.GetVisualColumn (data, lineSegment.Length + 1);
402
int charsToInsert = visualInsertLocation.Column - visualLastColumn;
403
int spaceCount = charsToInsert % data.Options.TabSize;
404
textToInsert = new string ('\t', (charsToInsert - spaceCount) / data.Options.TabSize) + new string (' ', spaceCount) + text;
405
insertOffset = lineSegment.Length;
409
inserted = data.Insert (lineSegment.Offset + insertOffset, textToInsert);
412
offset = version.MoveOffsetTo (data.Document.Version, offset);
413
inserted = data.PasteText (offset, text, copyData);
333
419
public static int PasteFromPrimary (TextEditorData data, int insertionOffset)
335
var result = PasteFrom (Clipboard.Get (CopyOperation.PRIMARYCLIPBOARD_ATOM), data, false, insertionOffset, true);
421
var result = PasteFrom (Clipboard.Get (CopyOperation.PRIMARYCLIPBOARD_ATOM), data, true, insertionOffset, true);
336
422
data.Document.CommitLineUpdate (data.GetLineByOffset (insertionOffset));