~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to src/core/Mono.Texteditor/Mono.TextEditor/Actions/ClipboardActions.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
using Gtk;
36
36
using Mono.TextEditor.Highlighting;
37
37
using Mono.TextEditor.Utils;
 
38
using System.Linq;
38
39
 
39
40
namespace Mono.TextEditor
40
41
{
57
58
                        
58
59
                        Clipboard clipboard = Clipboard.Get (CopyOperation.CLIPBOARD_ATOM);
59
60
                        operation.CopyData (data);
60
 
                        
61
 
                        clipboard.SetWithData ((Gtk.TargetEntry[])CopyOperation.targetList, operation.ClipboardGetFunc,
62
 
                                               operation.ClipboardClearFunc);
 
61
 
 
62
                        clipboard.SetWithData (CopyOperation.TargetEntries, operation.ClipboardGetFunc, operation.ClipboardClearFunc);
63
63
                }
64
64
        
65
65
                public class CopyOperation
66
66
                {
67
67
                        public const int TextType     = 1;
68
 
                        public const int RichTextType = 2;
69
 
                        public const int MonoTextType = 3;
70
 
                        
 
68
                        public const int HTMLTextType = 2;
 
69
                        public const int RichTextType = 3;
 
70
 
 
71
                        public const int MonoTextType = 99;
 
72
 
71
73
                        const int UTF8_FORMAT = 8;
72
74
                        
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);
77
 
                        
 
79
                        public static readonly Gdk.Atom HTML_ATOM;
 
80
 
78
81
                        public CopyOperation () 
79
82
                        {
80
83
                        }
81
 
                        
 
84
 
 
85
                        string GetCopiedPlainText ()
 
86
                        {
 
87
                                var plainText = new StringBuilder ();
 
88
                                bool first = true;
 
89
                                foreach (var line in copiedColoredChunks) {
 
90
                                        if (!first) {
 
91
                                                plainText.AppendLine ();
 
92
                                        } else {
 
93
                                                first = false;
 
94
                                        }
 
95
 
 
96
                                        foreach (var chunk in line) {
 
97
                                                plainText.Append (chunk.Text);
 
98
                                        }
 
99
                                }
 
100
                                return plainText.ToString ();
 
101
                        }
 
102
 
82
103
                        public void SetData (SelectionData selection_data, uint info)
83
104
                        {
84
105
                                if (selection_data == null)
87
108
                                case TextType:
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");
92
 
                                        } else {
93
 
                                                selection_data.Text = copiedDocument.Text;
94
 
                                        }
 
111
 
 
112
 
 
113
                                        selection_data.Text = GetCopiedPlainText ();
95
114
                                        break;
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));
 
118
                                        break;
 
119
                                case HTMLTextType:
 
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));
98
123
                                        break;
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);
103
133
                                        data [0] = 0;
104
134
                                        if (isBlockMode)
105
135
                                                data [0] |= 1;
122
152
                                // NOTHING ?
123
153
                        }
124
154
        
125
 
                        public TextDocument copiedDocument;
126
 
                        public TextDocument monoDocument; // has a slightly different format !!!
 
155
                        internal List<List<ColoredSegment>> copiedColoredChunks;
 
156
                        byte[] copyData;
 
157
 
127
158
                        public Mono.TextEditor.Highlighting.ColorScheme docStyle;
128
159
                        ITextEditorOptions options;
129
 
                        Mono.TextEditor.Highlighting.ISyntaxMode mode;
130
 
 
131
 
                        public static Gtk.TargetList targetList;
132
 
                        
 
160
 
 
161
                        public static readonly TargetEntry[] TargetEntries;
 
162
                        public static readonly TargetList TargetList;
 
163
 
133
164
                        static CopyOperation ()
134
165
                        {
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);
137
173
                                } else {
138
174
                                        RTF_ATOM = Gdk.Atom.Intern ("text/rtf", false);
 
175
                                        HTML_ATOM = Gdk.Atom.Intern ("text/html", false);
139
176
                                }
140
 
                                
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);
145
 
                                
 
177
 
 
178
                                var newTargets = new List<TargetEntry> ();
 
179
 
 
180
                                newTargets.Add (new TargetEntry ("SAVE_TARGETS", TargetFlags.App, TextType));
 
181
 
 
182
                                newTargets.Add (new TargetEntry (HTML_ATOM.Name, TargetFlags.OtherApp, HTMLTextType));
 
183
                                newTargets.Add (new TargetEntry ("UTF8_STRING", TargetFlags.App, TextType));
 
184
 
 
185
                                newTargets.Add (new TargetEntry (RTF_ATOM.Name, TargetFlags.OtherApp, RichTextType));
 
186
                                newTargets.Add (new TargetEntry (MD_ATOM.Name, TargetFlags.App, MonoTextType));
 
187
 
 
188
                                newTargets.Add (new TargetEntry ("text/plain;charset=utf-8", TargetFlags.App, TextType));
 
189
                                newTargets.Add (new TargetEntry ("text/plain", TargetFlags.App, TextType));
 
190
 
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));
151
196
                                }
 
197
                                TargetEntries = newTargets.ToArray ();
 
198
                                TargetList = new TargetList (TargetEntries);
152
199
                        }
153
200
                        
154
201
                        void CopyData (TextEditorData data, Selection selection)
155
202
                        {
156
 
                                copiedDocument = null;
157
 
                                monoDocument = 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);
 
206
                                        copyData = null;
 
207
 
 
208
 
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);
175
217
                                                break;
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));
189
 
                                                        }
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 ());
 
231
                                                        } else {
 
232
                                                                copiedColoredChunks.Add (new List<ColoredSegment> ());
195
233
                                                        }
196
234
                                                }
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;
201
235
                                                break;
202
236
                                        }
203
237
                                } else {
204
 
                                        copiedDocument = null;
 
238
                                        copiedColoredChunks = null;
205
239
                                }
206
240
                        }
207
241
                        
219
253
                                CopyData (data, selection);
220
254
                                
221
255
                                if (Copy != null)
222
 
                                        Copy (copiedDocument != null ? copiedDocument.Text : null);
 
256
                                        Copy (GetCopiedPlainText ());
223
257
                        }
224
258
                
225
259
                        public delegate void CopyDelegate (string text);
244
278
                {
245
279
                        return PasteFrom (clipboard, data, preserveSelection, insertionOffset, false);
246
280
                }
247
 
                
 
281
 
248
282
                static int PasteFrom (Clipboard clipboard, TextEditorData data, bool preserveSelection, int insertionOffset, bool preserveState)
249
283
                {
250
284
                        int result = -1;
254
288
                                clipboard.RequestContents (CopyOperation.MD_ATOM, delegate(Clipboard clp, SelectionData selectionData) {
255
289
                                        if (selectionData.Length > 0) {
256
290
                                                byte[] selBytes = selectionData.Data;
257
 
        
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;
261
 
                                                
262
 
//                                              var clearSelection = data.IsSomethingSelected ? data.MainSelection.SelectionMode != SelectionMode.Block : true;
263
 
                                                using (var undo = data.OpenUndoGroup ()) {
264
 
                                                        if (pasteBlock) {
 
297
                                                if (pasteBlock) {
 
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);
 
304
 
265
305
                                                                data.Caret.PreserveSelection = true;
266
 
                                                        
267
 
                                                                string[] lines = text.Split ('\r');
 
306
                                                                var lines = new List<string> ();
 
307
                                                                int offset = 0;
 
308
                                                                while (true) {
 
309
                                                                        var delimiter = LineSplitter.NextDelimiter (text, offset);
 
310
                                                                        if (delimiter.IsInvalid)
 
311
                                                                                break;
 
312
 
 
313
                                                                        int delimiterEndOffset = delimiter.Offset + delimiter.Length;
 
314
                                                                        lines.Add (text.Substring (offset, delimiter.Offset - offset));
 
315
                                                                        offset = delimiterEndOffset;
 
316
                                                                }
 
317
                                                                if (offset < text.Length)
 
318
                                                                        lines.Add (text.Substring (offset, text.Length - offset));
 
319
 
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;
273
325
                                                                result = 0;
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) {
 
347
                                                        }
 
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 ();
 
353
 
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);
 
357
 
 
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;
303
 
                                                        } else {
304
 
                                                                result = PastePlainText (data, insertionOffset, text);
305
362
                                                        }
 
363
                                                } else {
 
364
                                                        result = PastePlainText (data, insertionOffset, text, preserveSelection, copyData);
306
365
                                                }
307
366
                                        }
308
367
                                });
315
374
                                        if (string.IsNullOrEmpty (text))
316
375
                                                return;
317
376
                                        using (var undo = data.OpenUndoGroup ()) {
318
 
                                                result = PastePlainText (data, insertionOffset, text);
 
377
                                                result = PastePlainText (data, insertionOffset, text, preserveSelection);
319
378
                                        }
320
379
                                });
321
380
                        }
323
382
                        return result;
324
383
                }
325
384
 
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)
327
386
                {
328
 
                        int inserted = data.Insert (offset, text);
329
 
                        data.PasteText (offset, text, inserted);
 
387
                        int inserted = 0;
 
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;
 
399
                                                string textToInsert;
 
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;
 
406
                                                } else {
 
407
                                                        textToInsert = text;
 
408
                                                }
 
409
                                                inserted = data.Insert (lineSegment.Offset + insertOffset, textToInsert);
 
410
                                        }
 
411
                                } else {
 
412
                                        offset = version.MoveOffsetTo (data.Document.Version, offset);
 
413
                                        inserted = data.PasteText (offset, text, copyData);
 
414
                                }
 
415
                        }
330
416
                        return inserted;
331
417
                }
332
418
                
333
419
                public static int PasteFromPrimary (TextEditorData data, int insertionOffset)
334
420
                {
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));
337
423
                        return result;
338
424
                }
341
427
                {
342
428
                        if (!data.CanEditSelection)
343
429
                                return;
344
 
                        using (var undo = data.OpenUndoGroup ()) {
345
 
                                data.DeleteSelectedText (!data.IsSomethingSelected || data.MainSelection.SelectionMode != SelectionMode.Block);
346
 
                                data.EnsureCaretIsNotVirtual ();
347
 
                                PasteFrom (Clipboard.Get (CopyOperation.CLIPBOARD_ATOM), data, true, data.IsSomethingSelected ? data.SelectionRange.Offset : data.Caret.Offset);
348
 
                        }
 
430
                        PasteFrom (Clipboard.Get (CopyOperation.CLIPBOARD_ATOM), data, false, data.IsSomethingSelected ? data.SelectionRange.Offset : data.Caret.Offset);
349
431
                }
350
432
        }
351
433
}