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

« back to all changes in this revision

Viewing changes to src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextArea.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:
 
1
//
 
2
// TextEditor.cs
 
3
//
 
4
// Author:
 
5
//   Mike KrĆ¼ger <mkrueger@novell.com>
 
6
//
 
7
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining
 
10
// a copy of this software and associated documentation files (the
 
11
// "Software"), to deal in the Software without restriction, including
 
12
// without limitation the rights to use, copy, modify, merge, publish,
 
13
// distribute, sublicense, and/or sell copies of the Software, and to
 
14
// permit persons to whom the Software is furnished to do so, subject to
 
15
// the following conditions:
 
16
// 
 
17
// The above copyright notice and this permission notice shall be
 
18
// included in all copies or substantial portions of the Software.
 
19
// 
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
27
//
 
28
 
 
29
//#define DEBUG_EXPOSE
 
30
 
 
31
using System;
 
32
using System.Linq;
 
33
using System.Diagnostics;
 
34
using System.Collections.Generic;
 
35
using System.Runtime.InteropServices;
 
36
using System.Text;
 
37
using System.Threading;
 
38
using Mono.TextEditor.Highlighting;
 
39
using Mono.TextEditor.PopupWindow;
 
40
using Mono.TextEditor.Theatrics;
 
41
 
 
42
using Gdk;
 
43
using Gtk;
 
44
 
 
45
namespace Mono.TextEditor
 
46
{
 
47
        public class TextArea : Container, ITextEditorDataProvider
 
48
        {
 
49
                TextEditorData textEditorData;
 
50
                
 
51
                protected IconMargin       iconMargin;
 
52
                protected GutterMargin     gutterMargin;
 
53
//              protected DashedLineMargin dashedLineMargin;
 
54
                protected FoldMarkerMargin foldMarkerMargin;
 
55
                protected TextViewMargin   textViewMargin;
 
56
                
 
57
                DocumentLine longestLine      = null;
 
58
                double      longestLineWidth = -1;
 
59
                
 
60
                List<Margin> margins = new List<Margin> ();
 
61
                int oldRequest = -1;
 
62
                
 
63
                bool isDisposed = false;
 
64
                IMMulticontext imContext;
 
65
                Gdk.EventKey lastIMEvent;
 
66
                Gdk.Key lastIMEventMappedKey;
 
67
                uint lastIMEventMappedChar;
 
68
                Gdk.ModifierType lastIMEventMappedModifier;
 
69
                bool sizeHasBeenAllocated;
 
70
                bool imContextNeedsReset;
 
71
                string currentStyleName;
 
72
                
 
73
                double mx, my;
 
74
                
 
75
                public TextDocument Document {
 
76
                        get {
 
77
                                return textEditorData.Document;
 
78
                        }
 
79
                }
 
80
 
 
81
                public bool IsDisposed {
 
82
                        get {
 
83
                                return textEditorData.IsDisposed;
 
84
                        }
 
85
                }
 
86
                
 
87
                /// <summary>
 
88
                /// Gets or sets a value indicating whether this <see cref="Mono.TextEditor.TextEditor"/> converts tabs to spaces.
 
89
                /// It is possible to overwrite the default options value for certain languages (like F#).
 
90
                /// </summary>
 
91
                /// <value>
 
92
                /// <c>true</c> if tabs to spaces should be converted; otherwise, <c>false</c>.
 
93
                /// </value>
 
94
                public bool TabsToSpaces {
 
95
                        get {
 
96
                                return textEditorData.TabsToSpaces;
 
97
                        }
 
98
                        set {
 
99
                                textEditorData.TabsToSpaces = value;
 
100
                        }
 
101
                }
 
102
                
 
103
                public Mono.TextEditor.Caret Caret {
 
104
                        get {
 
105
                                return textEditorData.Caret;
 
106
                        }
 
107
                }
 
108
                
 
109
                protected internal IMMulticontext IMContext {
 
110
                        get { return imContext; }
 
111
                }
 
112
 
 
113
                public MenuItem CreateInputMethodMenuItem (string label)
 
114
                {
 
115
                        if (GtkWorkarounds.GtkMinorVersion >= 16) {
 
116
                                bool showMenu = (bool) GtkWorkarounds.GetProperty (Settings, "gtk-show-input-method-menu").Val;
 
117
                                if (!showMenu)
 
118
                                        return null;
 
119
                        }
 
120
                        MenuItem imContextMenuItem = new MenuItem (label);
 
121
                        Menu imContextMenu = new Menu ();
 
122
                        imContextMenuItem.Submenu = imContextMenu;
 
123
                        IMContext.AppendMenuitems (imContextMenu);
 
124
                        return imContextMenuItem;
 
125
                }
 
126
 
 
127
                [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)]
 
128
                static extern void gtk_im_multicontext_set_context_id (IntPtr context, string context_id);
 
129
 
 
130
                [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)]
 
131
                static extern string gtk_im_multicontext_get_context_id (IntPtr context);
 
132
                
 
133
                [GLib.Property ("im-module")]
 
134
                public string IMModule {
 
135
                        get {
 
136
                                if (GtkWorkarounds.GtkMinorVersion < 16 || imContext == null)
 
137
                                        return null;
 
138
                                return gtk_im_multicontext_get_context_id (imContext.Handle);
 
139
                        }
 
140
                        set {
 
141
                                if (GtkWorkarounds.GtkMinorVersion < 16 || imContext == null)
 
142
                                        return;
 
143
                                gtk_im_multicontext_set_context_id (imContext.Handle, value);
 
144
                        }
 
145
                }
 
146
                
 
147
                public ITextEditorOptions Options {
 
148
                        get {
 
149
                                return textEditorData.Options;
 
150
                        }
 
151
                        set {
 
152
                                if (textEditorData.Options != null)
 
153
                                        textEditorData.Options.Changed -= OptionsChanged;
 
154
                                textEditorData.Options = value;
 
155
                                if (textEditorData.Options != null) {
 
156
                                        textEditorData.Options.Changed += OptionsChanged;
 
157
                                        OptionsChanged (null, null);
 
158
                                }
 
159
                        }
 
160
                }
 
161
                
 
162
                
 
163
                public string FileName {
 
164
                        get {
 
165
                                return Document.FileName;
 
166
                        }
 
167
                }
 
168
                
 
169
                public string MimeType {
 
170
                        get {
 
171
                                return Document.MimeType;
 
172
                        }
 
173
                }
 
174
 
 
175
                void HandleTextEditorDataDocumentMarkerChange (object sender, TextMarkerEvent e)
 
176
                {
 
177
                        if (e.TextMarker is IExtendingTextLineMarker) {
 
178
                                int lineNumber = e.Line.LineNumber;
 
179
                                if (lineNumber <= LineCount) {
 
180
                                        try {
 
181
                                                textEditorData.HeightTree.SetLineHeight (lineNumber, GetLineHeight (e.Line));
 
182
                                        } catch (Exception ex) {
 
183
                                                Console.WriteLine (ex);
 
184
                                        }
 
185
                                }
 
186
                        }
 
187
                }
 
188
                
 
189
                void HAdjustmentValueChanged (object sender, EventArgs args)
 
190
                {
 
191
                        var alloc = this.Allocation;
 
192
                        alloc.X = alloc.Y = 0;
 
193
 
 
194
                        HAdjustmentValueChanged ();
 
195
                }
 
196
                
 
197
                protected virtual void HAdjustmentValueChanged ()
 
198
                {
 
199
                        HideTooltip (false);
 
200
                        double value = this.textEditorData.HAdjustment.Value;
 
201
                        if (value != System.Math.Round (value)) {
 
202
                                this.textEditorData.HAdjustment.Value = System.Math.Round (value);
 
203
                                return;
 
204
                        }
 
205
                        textViewMargin.HideCodeSegmentPreviewWindow ();
 
206
                        QueueDrawArea ((int)this.textViewMargin.XOffset, 0, this.Allocation.Width - (int)this.textViewMargin.XOffset, this.Allocation.Height);
 
207
                        OnHScroll (EventArgs.Empty);
 
208
                        SetChildrenPositions (Allocation);
 
209
                }
 
210
                
 
211
                void VAdjustmentValueChanged (object sender, EventArgs args)
 
212
                {
 
213
                        var alloc = this.Allocation;
 
214
                        alloc.X = alloc.Y = 0;
 
215
 
 
216
                        VAdjustmentValueChanged ();
 
217
                        SetChildrenPositions (alloc);
 
218
                }
 
219
                
 
220
                protected virtual void VAdjustmentValueChanged ()
 
221
                {
 
222
                        HideTooltip (false);
 
223
                        textViewMargin.HideCodeSegmentPreviewWindow ();
 
224
                        double value = this.textEditorData.VAdjustment.Value;
 
225
                        if (value != System.Math.Round (value)) {
 
226
                                this.textEditorData.VAdjustment.Value = System.Math.Round (value);
 
227
                                return;
 
228
                        }
 
229
                        if (isMouseTrapped)
 
230
                                FireMotionEvent (mx + textViewMargin.XOffset, my, lastState);
 
231
                        
 
232
                        double delta = value - this.oldVadjustment;
 
233
                        oldVadjustment = value;
 
234
                        TextViewMargin.caretY -= delta;
 
235
                        
 
236
                        if (System.Math.Abs (delta) >= Allocation.Height - this.LineHeight * 2 || this.TextViewMargin.InSelectionDrag) {
 
237
                                this.QueueDraw ();
 
238
                                OnVScroll (EventArgs.Empty);
 
239
                                return;
 
240
                        }
 
241
                        
 
242
                        if (GdkWindow != null)
 
243
                                GdkWindow.Scroll (0, (int)-delta);
 
244
 
 
245
                        OnVScroll (EventArgs.Empty);
 
246
                }
 
247
                
 
248
                protected virtual void OnVScroll (EventArgs e)
 
249
                {
 
250
                        EventHandler handler = this.VScroll;
 
251
                        if (handler != null)
 
252
                                handler (this, e);
 
253
                }
 
254
 
 
255
                protected virtual void OnHScroll (EventArgs e)
 
256
                {
 
257
                        EventHandler handler = this.HScroll;
 
258
                        if (handler != null)
 
259
                                handler (this, e);
 
260
                }
 
261
                
 
262
                public event EventHandler VScroll;
 
263
                public event EventHandler HScroll;
 
264
 
 
265
                void UnregisterAdjustments ()
 
266
                {
 
267
                        if (textEditorData.HAdjustment != null)
 
268
                                textEditorData.HAdjustment.ValueChanged -= HAdjustmentValueChanged;
 
269
                        if (textEditorData.VAdjustment != null)
 
270
                                textEditorData.VAdjustment.ValueChanged -= VAdjustmentValueChanged;
 
271
                }
 
272
 
 
273
                internal void SetTextEditorScrollAdjustments (Adjustment hAdjustement, Adjustment vAdjustement)
 
274
                {
 
275
                        if (textEditorData == null)
 
276
                                return;
 
277
                        UnregisterAdjustments ();
 
278
                        
 
279
                        if (hAdjustement == null || vAdjustement == null)
 
280
                                return;
 
281
 
 
282
                        this.textEditorData.HAdjustment = hAdjustement;
 
283
                        this.textEditorData.VAdjustment = vAdjustement;
 
284
                        
 
285
                        this.textEditorData.HAdjustment.ValueChanged += HAdjustmentValueChanged;
 
286
                        this.textEditorData.VAdjustment.ValueChanged += VAdjustmentValueChanged;
 
287
                }
 
288
 
 
289
                internal TextArea (TextDocument doc, ITextEditorOptions options, EditMode initialMode)
 
290
                {
 
291
                        GtkWorkarounds.FixContainerLeak (this);
 
292
                        this.Events = EventMask.PointerMotionMask | EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.EnterNotifyMask | EventMask.LeaveNotifyMask | EventMask.VisibilityNotifyMask | EventMask.FocusChangeMask | EventMask.ScrollMask | EventMask.KeyPressMask | EventMask.KeyReleaseMask;
 
293
                        this.DoubleBuffered = true;
 
294
                        base.CanFocus = true;
 
295
 
 
296
                        // This is required to properly handle resizing and rendering of children
 
297
                        ResizeMode = ResizeMode.Queue;
 
298
                }
 
299
 
 
300
                TextEditor editor;
 
301
                internal void Initialize (TextEditor editor, TextDocument doc, ITextEditorOptions options, EditMode initialMode)
 
302
                {
 
303
                        if (doc == null)
 
304
                                throw new ArgumentNullException ("doc");
 
305
                        this.editor = editor;
 
306
                        textEditorData = new TextEditorData (doc);
 
307
                        textEditorData.RecenterEditor += delegate {
 
308
                                CenterToCaret ();
 
309
                                StartCaretPulseAnimation ();
 
310
                        };
 
311
                        textEditorData.Document.TextReplaced += OnDocumentStateChanged;
 
312
                        textEditorData.Document.TextSet += OnTextSet;
 
313
                        textEditorData.Document.LineChanged += UpdateLinesOnTextMarkerHeightChange; 
 
314
                        textEditorData.Document.MarkerAdded += HandleTextEditorDataDocumentMarkerChange;
 
315
                        textEditorData.Document.MarkerRemoved += HandleTextEditorDataDocumentMarkerChange;
 
316
                        
 
317
                        textEditorData.CurrentMode = initialMode;
 
318
                        
 
319
                        this.textEditorData.Options = options ?? TextEditorOptions.DefaultOptions;
 
320
 
 
321
 
 
322
                        textEditorData.Parent = editor;
 
323
 
 
324
                        iconMargin = new IconMargin (editor);
 
325
                        gutterMargin = new GutterMargin (editor);
 
326
//                      dashedLineMargin = new DashedLineMargin (this);
 
327
                        foldMarkerMargin = new FoldMarkerMargin (editor);
 
328
                        textViewMargin = new TextViewMargin (editor);
 
329
 
 
330
                        margins.Add (iconMargin);
 
331
                        margins.Add (gutterMargin);
 
332
                        margins.Add (foldMarkerMargin);
 
333
//                      margins.Add (dashedLineMargin);
 
334
                        
 
335
                        margins.Add (textViewMargin);
 
336
                        this.textEditorData.SelectionChanged += TextEditorDataSelectionChanged;
 
337
                        this.textEditorData.UpdateAdjustmentsRequested += TextEditorDatahandleUpdateAdjustmentsRequested;
 
338
                        Document.DocumentUpdated += DocumentUpdatedHandler;
 
339
                        
 
340
                        this.textEditorData.Options.Changed += OptionsChanged;
 
341
                        
 
342
                        
 
343
                        Gtk.TargetList list = new Gtk.TargetList ();
 
344
                        list.AddTextTargets (ClipboardActions.CopyOperation.TextType);
 
345
                        Gtk.Drag.DestSet (this, DestDefaults.All, (TargetEntry[])list, DragAction.Move | DragAction.Copy);
 
346
                        
 
347
                        imContext = new IMMulticontext ();
 
348
                        imContext.Commit += IMCommit;
 
349
                        
 
350
                        imContext.UsePreedit = true;
 
351
                        imContext.PreeditChanged += PreeditStringChanged;
 
352
                        
 
353
                        imContext.RetrieveSurrounding += delegate (object o, RetrieveSurroundingArgs args) {
 
354
                                //use a single line of context, whole document would be very expensive
 
355
                                //FIXME: UTF16 surrogates handling for caret offset? only matters for astral plane
 
356
                                imContext.SetSurrounding (Document.GetLineText (Caret.Line, false), Caret.Column);
 
357
                                args.RetVal = true;
 
358
                        };
 
359
                        
 
360
                        imContext.SurroundingDeleted += delegate (object o, SurroundingDeletedArgs args) {
 
361
                                //FIXME: UTF16 surrogates handling for offset and NChars? only matters for astral plane
 
362
                                var line = Document.GetLine (Caret.Line);
 
363
                                Document.Remove (line.Offset + args.Offset, args.NChars);
 
364
                                args.RetVal = true;
 
365
                        };
 
366
                        
 
367
                        using (Pixmap inv = new Pixmap (null, 1, 1, 1)) {
 
368
                                invisibleCursor = new Cursor (inv, inv, Gdk.Color.Zero, Gdk.Color.Zero, 0, 0);
 
369
                        }
 
370
                        
 
371
                        InitAnimations ();
 
372
                        this.Document.EndUndo += HandleDocumenthandleEndUndo;
 
373
                        this.textEditorData.HeightTree.LineUpdateFrom += delegate(object sender, HeightTree.HeightChangedEventArgs e) {
 
374
                                //Console.WriteLine ("redraw from :" + e.Line);
 
375
                                RedrawFromLine (e.Line);
 
376
                        };
 
377
                        this.Document.Splitter.LineChanged += delegate(object sender, LineEventArgs e) {
 
378
                                RedrawLine (e.Line.LineNumber);
 
379
                        };
 
380
 
 
381
#if ATK
 
382
                        TextEditorAccessible.Factory.Init (this);
 
383
#endif
 
384
 
 
385
                        if (GtkGestures.IsSupported) {
 
386
                                this.AddGestureMagnifyHandler ((sender, args) => {
 
387
                                        Options.Zoom += Options.Zoom * (args.Magnification / 4d);
 
388
                                });
 
389
                        }
 
390
                        OptionsChanged (this, EventArgs.Empty);
 
391
                }
 
392
 
 
393
                public void RunAction (Action<TextEditorData> action)
 
394
                {
 
395
                        try {
 
396
                                action (GetTextEditorData ());
 
397
                        } catch (Exception e) {
 
398
                                Console.WriteLine ("Error while executing " + action + " :" + e);
 
399
                        }
 
400
                }
 
401
 
 
402
                void HandleDocumenthandleEndUndo (object sender, TextDocument.UndoOperationEventArgs e)
 
403
                {
 
404
                        if (this.Document.HeightChanged) {
 
405
                                this.Document.HeightChanged = false;
 
406
                                SetAdjustments ();
 
407
                        }
 
408
                }
 
409
 
 
410
                void TextEditorDatahandleUpdateAdjustmentsRequested (object sender, EventArgs e)
 
411
                {
 
412
                        SetAdjustments ();
 
413
                }
 
414
                
 
415
                
 
416
                public void ShowListWindow<T> (ListWindow<T> window, DocumentLocation loc)
 
417
                {
 
418
                        var p = LocationToPoint (loc);
 
419
                        int ox = 0, oy = 0;
 
420
                        GdkWindow.GetOrigin (out ox, out oy);
 
421
        
 
422
                        window.Move (ox + p.X - window.TextOffset , oy + p.Y + (int)LineHeight);
 
423
                        window.ShowAll ();
 
424
                }
 
425
                
 
426
                internal int preeditOffset, preeditLine, preeditCursorCharIndex;
 
427
                internal string preeditString;
 
428
                internal Pango.AttrList preeditAttrs;
 
429
                internal bool preeditHeightChange;
 
430
                
 
431
                internal bool ContainsPreedit (int line, int length)
 
432
                {
 
433
                        if (string.IsNullOrEmpty (preeditString))
 
434
                                return false;
 
435
                        
 
436
                        return line <= preeditOffset && preeditOffset <= line + length;
 
437
                }
 
438
 
 
439
                void PreeditStringChanged (object sender, EventArgs e)
 
440
                {
 
441
                        imContext.GetPreeditString (out preeditString, out preeditAttrs, out preeditCursorCharIndex);
 
442
                        if (!string.IsNullOrEmpty (preeditString)) {
 
443
                                if (preeditOffset < 0) {
 
444
                                        preeditOffset = Caret.Offset;
 
445
                                        preeditLine = Caret.Line;
 
446
                                }
 
447
                                using (var preeditLayout = PangoUtil.CreateLayout (this)) {
 
448
                                        preeditLayout.SetText (preeditString);
 
449
                                        preeditLayout.Attributes = preeditAttrs;
 
450
                                        int w, h;
 
451
                                        preeditLayout.GetSize (out w, out h);
 
452
                                        var calcHeight = System.Math.Ceiling (h / Pango.Scale.PangoScale);
 
453
                                        if (LineHeight != calcHeight) {
 
454
                                                textEditorData.HeightTree.SetLineHeight (preeditLine, calcHeight);
 
455
                                                preeditHeightChange = true;
 
456
                                                QueueDraw ();
 
457
                                        }
 
458
                                }
 
459
                        } else {
 
460
                                preeditOffset = -1;
 
461
                                preeditString = null;
 
462
                                preeditAttrs = null;
 
463
                                preeditCursorCharIndex = 0;
 
464
                                if (preeditHeightChange) {
 
465
                                        preeditHeightChange = false;
 
466
                                        textEditorData.HeightTree.Rebuild ();
 
467
                                        QueueDraw ();
 
468
                                }
 
469
                        }
 
470
                        this.textViewMargin.ForceInvalidateLine (preeditLine);
 
471
                        this.textEditorData.Document.CommitLineUpdate (preeditLine);
 
472
                }
 
473
 
 
474
                void CaretPositionChanged (object sender, DocumentLocationEventArgs args) 
 
475
                {
 
476
                        HideTooltip ();
 
477
                        ResetIMContext ();
 
478
                        
 
479
                        if (Caret.AutoScrollToCaret && HasFocus)
 
480
                                ScrollToCaret ();
 
481
                        
 
482
//                      Rectangle rectangle = textViewMargin.GetCaretRectangle (Caret.Mode);
 
483
                        RequestResetCaretBlink ();
 
484
                        
 
485
                        textEditorData.CurrentMode.InternalCaretPositionChanged (textEditorData.Parent, textEditorData);
 
486
                        
 
487
                        if (!IsSomethingSelected) {
 
488
                                if (/*Options.HighlightCaretLine && */args.Location.Line != Caret.Line) 
 
489
                                        RedrawMarginLine (TextViewMargin, args.Location.Line);
 
490
                                RedrawMarginLine (TextViewMargin, Caret.Line);
 
491
                        }
 
492
                }
 
493
                
 
494
                Selection oldSelection = Selection.Empty;
 
495
                void TextEditorDataSelectionChanged (object sender, EventArgs args)
 
496
                {
 
497
                        if (IsSomethingSelected) {
 
498
                                var selectionRange = MainSelection.GetSelectionRange (textEditorData);
 
499
                                if (selectionRange.Offset >= 0 && selectionRange.EndOffset < Document.TextLength) {
 
500
                                        ClipboardActions.CopyToPrimary (this.textEditorData);
 
501
                                } else {
 
502
                                        ClipboardActions.ClearPrimary ();
 
503
                                }
 
504
                        } else {
 
505
                                ClipboardActions.ClearPrimary ();
 
506
                        }
 
507
                        // Handle redraw
 
508
                        Selection selection = MainSelection;
 
509
                        int startLine    = !selection.IsEmpty ? selection.Anchor.Line : -1;
 
510
                        int endLine      = !selection.IsEmpty ? selection.Lead.Line : -1;
 
511
                        int oldStartLine = !oldSelection.IsEmpty ? oldSelection.Anchor.Line : -1;
 
512
                        int oldEndLine   = !oldSelection.IsEmpty ? oldSelection.Lead.Line : -1;
 
513
                        if (SelectionMode == SelectionMode.Block) {
 
514
                                this.RedrawMarginLines (this.textViewMargin, 
 
515
                                                        System.Math.Min (System.Math.Min (oldStartLine, oldEndLine), System.Math.Min (startLine, endLine)),
 
516
                                                        System.Math.Max (System.Math.Max (oldStartLine, oldEndLine), System.Math.Max (startLine, endLine)));
 
517
                        } else {
 
518
                                if (endLine < 0 && startLine >=0)
 
519
                                        endLine = Document.LineCount;
 
520
                                if (oldEndLine < 0 && oldStartLine >=0)
 
521
                                        oldEndLine = Document.LineCount;
 
522
                                int from = oldEndLine, to = endLine;
 
523
                                if (!selection.IsEmpty && !oldSelection.IsEmpty) {
 
524
                                        if (startLine != oldStartLine && endLine != oldEndLine) {
 
525
                                                from = System.Math.Min (startLine, oldStartLine);
 
526
                                                to   = System.Math.Max (endLine, oldEndLine);
 
527
                                        }Ā else if (startLine != oldStartLine) {
 
528
                                                from = startLine;
 
529
                                                to   = oldStartLine;
 
530
                                        } else if (endLine != oldEndLine) {
 
531
                                                from = endLine;
 
532
                                                to   = oldEndLine;
 
533
                                        } else if (startLine == oldStartLine && endLine == oldEndLine)  {
 
534
                                                if (selection.Anchor == oldSelection.Anchor) {
 
535
                                                        this.RedrawMarginLine (this.textViewMargin, endLine);
 
536
                                                } else if (selection.Lead == oldSelection.Lead) {
 
537
                                                        this.RedrawMarginLine (this.textViewMargin, startLine);
 
538
                                                } else { // 3rd case - may happen when changed programmatically
 
539
                                                        this.RedrawMarginLine (this.textViewMargin, endLine);
 
540
                                                        this.RedrawMarginLine (this.textViewMargin, startLine);
 
541
                                                }
 
542
                                                from = to = -1;
 
543
                                        }
 
544
                                } else {
 
545
                                        if (selection.IsEmpty) {
 
546
                                                from = oldStartLine;
 
547
                                                to = oldEndLine;
 
548
                                        } else if (oldSelection.IsEmpty) {
 
549
                                                from = startLine;
 
550
                                                to = endLine;
 
551
                                        } 
 
552
                                }
 
553
                                
 
554
                                if (from >= 0 && to >= 0) {
 
555
                                        this.RedrawMarginLines (this.textViewMargin, 
 
556
                                                                System.Math.Max (0, System.Math.Min (from, to) - 1),
 
557
                                                                System.Math.Max (from, to));
 
558
                                }
 
559
                        }
 
560
                        oldSelection = selection;
 
561
                        OnSelectionChanged (EventArgs.Empty);
 
562
                }
 
563
                
 
564
                internal void ResetIMContext ()
 
565
                {
 
566
                        if (imContextNeedsReset) {
 
567
                                imContext.Reset ();
 
568
                                imContextNeedsReset = false;
 
569
                        }
 
570
                }
 
571
                
 
572
                void IMCommit (object sender, Gtk.CommitArgs ca)
 
573
                {
 
574
                        if (!IsRealized || !IsFocus)
 
575
                                return;
 
576
                        
 
577
                        //this, if anywhere, is where we should handle UCS4 conversions
 
578
                        for (int i = 0; i < ca.Str.Length; i++) {
 
579
                                int utf32Char;
 
580
                                if (char.IsHighSurrogate (ca.Str, i)) {
 
581
                                        utf32Char = char.ConvertToUtf32 (ca.Str, i);
 
582
                                        i++;
 
583
                                } else {
 
584
                                        utf32Char = (int)ca.Str [i];
 
585
                                }
 
586
                                
 
587
                                //include the other pre-IM state *if* the post-IM char matches the pre-IM (key-mapped) one
 
588
                                 if (lastIMEventMappedChar == utf32Char && lastIMEventMappedChar == (uint)lastIMEventMappedKey) {
 
589
                                        editor.OnIMProcessedKeyPressEvent (lastIMEventMappedKey, lastIMEventMappedChar, lastIMEventMappedModifier);
 
590
                                } else {
 
591
                                        editor.OnIMProcessedKeyPressEvent ((Gdk.Key)0, (uint)utf32Char, Gdk.ModifierType.None);
 
592
                                }
 
593
                        }
 
594
                        
 
595
                        //the IME can commit while there's still a pre-edit string
 
596
                        //since we cached the pre-edit offset when it started, need to update it
 
597
                        if (preeditOffset > -1) {
 
598
                                preeditOffset = Caret.Offset;
 
599
                        }
 
600
                }
 
601
                
 
602
                protected override bool OnFocusInEvent (EventFocus evnt)
 
603
                {
 
604
                        var result = base.OnFocusInEvent (evnt);
 
605
                        imContextNeedsReset = true;
 
606
                        IMContext.FocusIn ();
 
607
                        RequestResetCaretBlink ();
 
608
                        Document.CommitLineUpdate (Caret.Line);
 
609
                        return result;
 
610
                }
 
611
                
 
612
                uint focusOutTimerId = 0;
 
613
                void RemoveFocusOutTimerId ()
 
614
                {
 
615
                        if (focusOutTimerId == 0)
 
616
                                return;
 
617
                        GLib.Source.Remove (focusOutTimerId);
 
618
                        focusOutTimerId = 0;
 
619
                }
 
620
                
 
621
                protected override bool OnFocusOutEvent (EventFocus evnt)
 
622
                {
 
623
                        var result = base.OnFocusOutEvent (evnt);
 
624
                        imContextNeedsReset = true;
 
625
                        imContext.FocusOut ();
 
626
                        RemoveFocusOutTimerId ();
 
627
 
 
628
                        if (tipWindow != null && currentTooltipProvider != null) {
 
629
                                if (!currentTooltipProvider.IsInteractive (textEditorData.Parent, tipWindow))
 
630
                                        DelayedHideTooltip ();
 
631
                        } else {
 
632
                                HideTooltip ();
 
633
                        }
 
634
 
 
635
                        TextViewMargin.StopCaretThread ();
 
636
                        Document.CommitLineUpdate (Caret.Line);
 
637
                        return result;
 
638
                }
 
639
 
 
640
                protected override void OnRealized ()
 
641
                {
 
642
                        WidgetFlags |= WidgetFlags.Realized;
 
643
                        WindowAttr attributes = new WindowAttr () {
 
644
                                WindowType = Gdk.WindowType.Child,
 
645
                                X = Allocation.X,
 
646
                                Y = Allocation.Y,
 
647
                                Width = Allocation.Width,
 
648
                                Height = Allocation.Height,
 
649
                                Wclass = WindowClass.InputOutput,
 
650
                                Visual = this.Visual,
 
651
                                Colormap = this.Colormap,
 
652
                                EventMask = (int)(this.Events | Gdk.EventMask.ExposureMask),
 
653
                                Mask = this.Events | Gdk.EventMask.ExposureMask,
 
654
                        };
 
655
                        
 
656
                        WindowAttributesType mask = WindowAttributesType.X | WindowAttributesType.Y | WindowAttributesType.Colormap | WindowAttributesType.Visual;
 
657
                        GdkWindow = new Gdk.Window (ParentWindow, attributes, mask);
 
658
                        GdkWindow.UserData = Raw;
 
659
                        Style = Style.Attach (GdkWindow);
 
660
 
 
661
                        imContext.ClientWindow = this.GdkWindow;
 
662
                        Caret.PositionChanged += CaretPositionChanged;
 
663
                }       
 
664
 
 
665
                protected override void OnUnrealized ()
 
666
                {
 
667
                        imContext.ClientWindow = null;
 
668
                        CancelScheduledHide ();
 
669
                        base.OnUnrealized ();
 
670
                }
 
671
                
 
672
                void DocumentUpdatedHandler (object sender, EventArgs args)
 
673
                {
 
674
                        foreach (DocumentUpdateRequest request in Document.UpdateRequests) {
 
675
                                request.Update (textEditorData.Parent);
 
676
                        }
 
677
                }
 
678
                
 
679
                public event EventHandler EditorOptionsChanged;
 
680
 
 
681
                protected virtual void OptionsChanged (object sender, EventArgs args)
 
682
                {
 
683
                        if (Options == null)
 
684
                                return;
 
685
                        if (currentStyleName != Options.ColorScheme) {
 
686
                                currentStyleName = Options.ColorScheme;
 
687
                                this.textEditorData.ColorStyle = Options.GetColorStyle ();
 
688
                                SetWidgetBgFromStyle ();
 
689
                        }
 
690
                        
 
691
                        iconMargin.IsVisible   = Options.ShowIconMargin;
 
692
                        gutterMargin.IsVisible     = Options.ShowLineNumberMargin;
 
693
                        foldMarkerMargin.IsVisible = Options.ShowFoldMargin || Options.EnableQuickDiff;
 
694
//                      dashedLineMargin.IsVisible = foldMarkerMargin.IsVisible || gutterMargin.IsVisible;
 
695
                        
 
696
                        if (EditorOptionsChanged != null)
 
697
                                EditorOptionsChanged (this, args);
 
698
                        
 
699
                        textViewMargin.OptionsChanged ();
 
700
                        foreach (Margin margin in this.margins) {
 
701
                                if (margin == textViewMargin)
 
702
                                        continue;
 
703
                                margin.OptionsChanged ();
 
704
                        }
 
705
                        SetAdjustments (Allocation);
 
706
                        textEditorData.HeightTree.Rebuild ();
 
707
                        this.QueueResize ();
 
708
                }
 
709
 
 
710
                void SetWidgetBgFromStyle ()
 
711
                {
 
712
                        // This is a hack around a problem with repainting the drag widget.
 
713
                        // When this is not set a white square is drawn when the drag widget is moved
 
714
                        // when the bg color is differs from the color style bg color (e.g. oblivion style)
 
715
                        if (this.textEditorData.ColorStyle != null && GdkWindow != null) {
 
716
                                settingWidgetBg = true; //prevent infinite recusion
 
717
                                
 
718
                                this.ModifyBg (StateType.Normal, (HslColor)this.textEditorData.ColorStyle.PlainText.Background);
 
719
                                settingWidgetBg = false;
 
720
                        }
 
721
                }
 
722
                
 
723
                bool settingWidgetBg = false;
 
724
                protected override void OnStyleSet (Gtk.Style previous_style)
 
725
                {
 
726
                        base.OnStyleSet (previous_style);
 
727
                        if (!settingWidgetBg && textEditorData.ColorStyle != null) {
 
728
//                              textEditorData.ColorStyle.UpdateFromGtkStyle (this.Style);
 
729
                                SetWidgetBgFromStyle ();
 
730
                        }
 
731
                }
 
732
 
 
733
                protected override bool OnVisibilityNotifyEvent (EventVisibility evnt)
 
734
                {
 
735
                        if (evnt.State == VisibilityState.FullyObscured)
 
736
                                HideTooltip ();
 
737
                        return base.OnVisibilityNotifyEvent (evnt);
 
738
                }
 
739
                protected override void OnDestroyed ()
 
740
                {
 
741
                        if (popupWindow != null)
 
742
                                popupWindow.Destroy ();
 
743
 
 
744
                        HideTooltip ();
 
745
                        Document.EndUndo -= HandleDocumenthandleEndUndo;
 
746
                        Document.TextReplaced -= OnDocumentStateChanged;
 
747
                        Document.TextSet -= OnTextSet;
 
748
                        Document.LineChanged -= UpdateLinesOnTextMarkerHeightChange; 
 
749
                        Document.MarkerAdded -= HandleTextEditorDataDocumentMarkerChange;
 
750
                        Document.MarkerRemoved -= HandleTextEditorDataDocumentMarkerChange;
 
751
 
 
752
                        DisposeAnimations ();
 
753
                        
 
754
                        RemoveFocusOutTimerId ();
 
755
                        RemoveScrollWindowTimer ();
 
756
                        if (invisibleCursor != null)
 
757
                                invisibleCursor.Dispose ();
 
758
                        
 
759
                        Caret.PositionChanged -= CaretPositionChanged;
 
760
                        
 
761
                        Document.DocumentUpdated -= DocumentUpdatedHandler;
 
762
                        if (textEditorData.Options != null)
 
763
                                textEditorData.Options.Changed -= OptionsChanged;
 
764
                        
 
765
                        if (imContext != null){
 
766
                                ResetIMContext ();
 
767
                                imContext = imContext.Kill (x => x.Commit -= IMCommit);
 
768
                        }
 
769
 
 
770
                        UnregisterAdjustments ();
 
771
 
 
772
                        foreach (Margin margin in this.margins) {
 
773
                                if (margin is IDisposable)
 
774
                                        ((IDisposable)margin).Dispose ();
 
775
                        }
 
776
                        textEditorData.ClearTooltipProviders ();
 
777
                        
 
778
                        this.textEditorData.SelectionChanged -= TextEditorDataSelectionChanged;
 
779
                        this.textEditorData.Dispose (); 
 
780
 
 
781
                        base.OnDestroyed ();
 
782
                }
 
783
                
 
784
                [Obsolete("This method has been moved to TextEditorData. Will be removed in future versions.")]
 
785
                public void ClearTooltipProviders ()
 
786
                {
 
787
                        textEditorData.ClearTooltipProviders ();
 
788
                }
 
789
                
 
790
                [Obsolete("This method has been moved to TextEditorData. Will be removed in future versions.")]
 
791
                public void AddTooltipProvider (TooltipProvider provider)
 
792
                {
 
793
                        textEditorData.AddTooltipProvider (provider);
 
794
                }
 
795
                
 
796
                [Obsolete("This method has been moved to TextEditorData. Will be removed in future versions.")]
 
797
                public void RemoveTooltipProvider (TooltipProvider provider)
 
798
                {
 
799
                        textEditorData.RemoveTooltipProvider (provider);
 
800
                }
 
801
 
 
802
                internal void RedrawMargin (Margin margin)
 
803
                {
 
804
                        if (isDisposed)
 
805
                                return;
 
806
                        QueueDrawArea ((int)margin.XOffset, 0, GetMarginWidth (margin),  this.Allocation.Height);
 
807
                }
 
808
                
 
809
                public void RedrawMarginLine (Margin margin, int logicalLine)
 
810
                {
 
811
                        if (isDisposed)
 
812
                                return;
 
813
                        
 
814
                        double y = LineToY (logicalLine) - this.textEditorData.VAdjustment.Value;
 
815
                        double h = GetLineHeight (logicalLine);
 
816
                        
 
817
                        if (y + h > 0)
 
818
                                QueueDrawArea ((int)margin.XOffset, (int)y, (int)GetMarginWidth (margin), (int)h);
 
819
                }
 
820
 
 
821
                int GetMarginWidth (Margin margin)
 
822
                {
 
823
                        if (margin.Width < 0)
 
824
                                return Allocation.Width - (int)margin.XOffset;
 
825
                        return (int)margin.Width;
 
826
                }
 
827
                
 
828
                internal void RedrawLine (int logicalLine)
 
829
                {
 
830
                        if (isDisposed || logicalLine > LineCount || logicalLine < DocumentLocation.MinLine)
 
831
                                return;
 
832
                        double y = LineToY (logicalLine) - this.textEditorData.VAdjustment.Value;
 
833
                        double h = GetLineHeight (logicalLine);
 
834
 
 
835
                        if (y + h > 0)
 
836
                                QueueDrawArea (0, (int)y, this.Allocation.Width, (int)h);
 
837
                }
 
838
                
 
839
                public new void QueueDrawArea (int x, int y, int w, int h)
 
840
                {
 
841
                        if (GdkWindow != null) {
 
842
                                GdkWindow.InvalidateRect (new Rectangle (x, y, w, h), false);
 
843
#if DEBUG_EXPOSE
 
844
                                Console.WriteLine ("invalidated {0},{1} {2}x{3}", x, y, w, h);
 
845
#endif
 
846
                        }
 
847
                }
 
848
                
 
849
                public new void QueueDraw ()
 
850
                {
 
851
                        base.QueueDraw ();
 
852
#if DEBUG_EXPOSE
 
853
                                Console.WriteLine ("invalidated entire widget");
 
854
#endif
 
855
                }
 
856
                
 
857
                internal void RedrawPosition (int logicalLine, int logicalColumn)
 
858
                {
 
859
                        if (isDisposed)
 
860
                                return;
 
861
//                              Console.WriteLine ("Redraw position: logicalLine={0}, logicalColumn={1}", logicalLine, logicalColumn);
 
862
                        RedrawLine (logicalLine);
 
863
                }
 
864
                
 
865
                public void RedrawMarginLines (Margin margin, int start, int end)
 
866
                {
 
867
                        if (isDisposed)
 
868
                                return;
 
869
                        if (start < 0)
 
870
                                start = 0;
 
871
                        double visualStart = -this.textEditorData.VAdjustment.Value + LineToY (start);
 
872
                        if (end < 0)
 
873
                                end = Document.LineCount;
 
874
                        double visualEnd   = -this.textEditorData.VAdjustment.Value + LineToY (end) + GetLineHeight (end);
 
875
                        QueueDrawArea ((int)margin.XOffset, (int)visualStart, GetMarginWidth (margin), (int)(visualEnd - visualStart));
 
876
                }
 
877
                        
 
878
                internal void RedrawLines (int start, int end)
 
879
                {
 
880
//                      Console.WriteLine ("redraw lines: start={0}, end={1}", start, end);
 
881
                        if (isDisposed)
 
882
                                return;
 
883
                        if (start < 0)
 
884
                                start = 0;
 
885
                        double visualStart = -this.textEditorData.VAdjustment.Value +  LineToY (start);
 
886
                        if (end < 0)
 
887
                                end = Document.LineCount;
 
888
                        double visualEnd   = -this.textEditorData.VAdjustment.Value + LineToY (end) + GetLineHeight (end);
 
889
                        QueueDrawArea (0, (int)visualStart, this.Allocation.Width, (int)(visualEnd - visualStart));
 
890
                }
 
891
                
 
892
                public void RedrawFromLine (int logicalLine)
 
893
                {
 
894
//                      Console.WriteLine ("Redraw from line: logicalLine={0}", logicalLine);
 
895
                        if (isDisposed)
 
896
                                return;
 
897
                        int y = System.Math.Max (0, (int)(-this.textEditorData.VAdjustment.Value + LineToY (logicalLine)));
 
898
                        QueueDrawArea (0, y,
 
899
                                       this.Allocation.Width, this.Allocation.Height - y);
 
900
                }
 
901
 
 
902
 
 
903
                /// <summary>Handles key input after key mapping and input methods.</summary>
 
904
                /// <param name="key">The mapped keycode.</param>
 
905
                /// <param name="unicodeChar">A UCS4 character. If this is nonzero, it overrides the keycode.</param>
 
906
                /// <param name="modifier">Keyboard modifier, excluding any consumed by key mapping or IM.</param>
 
907
                public void SimulateKeyPress (Gdk.Key key, uint unicodeChar, ModifierType modifier)
 
908
                {
 
909
                        ModifierType filteredModifiers = modifier & (ModifierType.ShiftMask | ModifierType.Mod1Mask
 
910
                                 | ModifierType.ControlMask | ModifierType.MetaMask | ModifierType.SuperMask);
 
911
                        CurrentMode.InternalHandleKeypress (textEditorData.Parent, textEditorData, key, unicodeChar, filteredModifiers);
 
912
                        RequestResetCaretBlink ();
 
913
                }
 
914
                
 
915
                bool IMFilterKeyPress (Gdk.EventKey evt, Gdk.Key mappedKey, uint mappedChar, Gdk.ModifierType mappedModifiers)
 
916
                {
 
917
                        if (lastIMEvent == evt)
 
918
                                return false;
 
919
                        
 
920
                        if (evt.Type == EventType.KeyPress) {
 
921
                                lastIMEvent = evt;
 
922
                                lastIMEventMappedChar = mappedChar;
 
923
                                lastIMEventMappedKey = mappedKey;
 
924
                                lastIMEventMappedModifier = mappedModifiers;
 
925
                        }
 
926
                        
 
927
                        if (imContext.FilterKeypress (evt)) {
 
928
                                imContextNeedsReset = true;
 
929
                                return true;
 
930
                        } else {
 
931
                                return false;
 
932
                        }
 
933
                }
 
934
                
 
935
                Gdk.Cursor invisibleCursor;
 
936
                
 
937
                internal void HideMouseCursor ()
 
938
                {
 
939
                        if (GdkWindow != null)
 
940
                                GdkWindow.Cursor = invisibleCursor;
 
941
                }
 
942
                
 
943
                protected override bool OnKeyPressEvent (Gdk.EventKey evt)
 
944
                {
 
945
                        Gdk.Key key;
 
946
                        Gdk.ModifierType mod;
 
947
                        KeyboardShortcut[] accels;
 
948
                        GtkWorkarounds.MapKeys (evt, out key, out mod, out accels);
 
949
                        //HACK: we never call base.OnKeyPressEvent, so implement the popup key manually
 
950
                        if (key == Gdk.Key.Menu || (key == Gdk.Key.F10 && mod.HasFlag (ModifierType.ShiftMask))) {
 
951
                                OnPopupMenu ();
 
952
                                return true;
 
953
                        }
 
954
                        uint keyVal = (uint)key;
 
955
                        CurrentMode.SelectValidShortcut (accels, out key, out mod);
 
956
                        if (key == Gdk.Key.F1 && (mod & (ModifierType.ControlMask | ModifierType.ShiftMask)) == ModifierType.ControlMask) {
 
957
                                var p = LocationToPoint (Caret.Location);
 
958
                                ShowTooltip (Gdk.ModifierType.None, Caret.Offset, p.X, p.Y);
 
959
                                return true;
 
960
                        }
 
961
                        if (key == Gdk.Key.F2 && textViewMargin.IsCodeSegmentPreviewWindowShown) {
 
962
                                textViewMargin.OpenCodeSegmentEditor ();
 
963
                                return true;
 
964
                        }
 
965
                        
 
966
                        //FIXME: why are we doing this?
 
967
                        if ((key == Gdk.Key.space || key == Gdk.Key.parenleft || key == Gdk.Key.parenright) && (mod & Gdk.ModifierType.ShiftMask) == Gdk.ModifierType.ShiftMask)
 
968
                                mod = Gdk.ModifierType.None;
 
969
                        
 
970
                        uint unicodeChar = Gdk.Keyval.ToUnicode (keyVal);
 
971
                        
 
972
                        if (CurrentMode.WantsToPreemptIM || CurrentMode.PreemptIM (key, unicodeChar, mod)) {
 
973
                                ResetIMContext ();
 
974
                                //FIXME: should call base.OnKeyPressEvent when SimulateKeyPress didn't handle the event
 
975
                                SimulateKeyPress (key, unicodeChar, mod);
 
976
                                return true;
 
977
                        }
 
978
                        bool filter = IMFilterKeyPress (evt, key, unicodeChar, mod);
 
979
                        if (filter)
 
980
                                return true;
 
981
                        
 
982
                        //FIXME: OnIMProcessedKeyPressEvent should return false when it didn't handle the event
 
983
                        if (editor.OnIMProcessedKeyPressEvent (key, unicodeChar, mod))
 
984
                                return true;
 
985
                        
 
986
                        return base.OnKeyPressEvent (evt);
 
987
                }
 
988
                
 
989
 
 
990
                protected override bool OnKeyReleaseEvent (EventKey evnt)
 
991
                {
 
992
                        if (IMFilterKeyPress (evnt, 0, 0, ModifierType.None)) {
 
993
                                imContextNeedsReset = true;
 
994
                        }
 
995
                        return true;
 
996
                }
 
997
                
 
998
                uint mouseButtonPressed = 0;
 
999
                uint lastTime;
 
1000
                double pressPositionX, pressPositionY;
 
1001
                protected override bool OnButtonPressEvent (Gdk.EventButton e)
 
1002
                {
 
1003
                        if (overChildWidget)
 
1004
                                return true;
 
1005
 
 
1006
                        pressPositionX = e.X;
 
1007
                        pressPositionY = e.Y;
 
1008
                        base.IsFocus = true;
 
1009
                        
 
1010
 
 
1011
                        if (lastTime != e.Time) {// filter double clicks
 
1012
                                if (e.Type == EventType.TwoButtonPress) {
 
1013
                                    lastTime = e.Time;
 
1014
                                }Ā else {
 
1015
                                        lastTime = 0;
 
1016
                                }
 
1017
                                mouseButtonPressed = e.Button;
 
1018
                                double startPos;
 
1019
                                Margin margin = GetMarginAtX (e.X, out startPos);
 
1020
                                if (margin == textViewMargin) {
 
1021
                                        //main context menu
 
1022
                                        if (DoPopupMenu != null && e.TriggersContextMenu ()) {
 
1023
                                                DoClickedPopupMenu (e);
 
1024
                                                return true;
 
1025
                                        }
 
1026
                                }
 
1027
                                if (margin != null) 
 
1028
                                        margin.MousePressed (new MarginMouseEventArgs (textEditorData.Parent, e, e.Button, e.X - startPos, e.Y, e.State));
 
1029
                        }
 
1030
                        return base.OnButtonPressEvent (e);
 
1031
                }
 
1032
                
 
1033
                bool DoClickedPopupMenu (Gdk.EventButton e)
 
1034
                {
 
1035
                        double tmOffset = e.X - textViewMargin.XOffset;
 
1036
                        if (tmOffset >= 0) {
 
1037
                                DocumentLocation loc;
 
1038
                                if (textViewMargin.CalculateClickLocation (tmOffset, e.Y, out loc)) {
 
1039
                                        if (!this.IsSomethingSelected || !this.SelectionRange.Contains (Document.LocationToOffset (loc))) {
 
1040
                                                Caret.Location = loc;
 
1041
                                        }
 
1042
                                }
 
1043
                                DoPopupMenu (e);
 
1044
                                this.ResetMouseState ();
 
1045
                                return true;
 
1046
                        }
 
1047
                        return false;
 
1048
                }
 
1049
                
 
1050
                public Action<Gdk.EventButton> DoPopupMenu { get; set; }
 
1051
 
 
1052
                protected override bool OnPopupMenu ()
 
1053
                {
 
1054
                        if (DoPopupMenu != null) {
 
1055
                                DoPopupMenu (null);
 
1056
                                return true;
 
1057
                        }
 
1058
                        return base.OnPopupMenu ();
 
1059
                }
 
1060
                
 
1061
                public Margin LockedMargin {
 
1062
                        get;
 
1063
                        set;
 
1064
                }
 
1065
 
 
1066
                Margin GetMarginAtX (double x, out double startingPos)
 
1067
                {
 
1068
                        double curX = 0;
 
1069
                        foreach (Margin margin in this.margins) {
 
1070
                                if (!margin.IsVisible)
 
1071
                                        continue;
 
1072
                                if (LockedMargin != null) {
 
1073
                                        if (LockedMargin == margin) {
 
1074
                                                startingPos = curX;
 
1075
                                                return margin;
 
1076
                                        }
 
1077
                                } else {
 
1078
                                        if (curX <= x && (x <= curX + margin.Width || margin.Width < 0)) {
 
1079
                                                startingPos = curX;
 
1080
                                                return margin;
 
1081
                                        }
 
1082
                                }
 
1083
                                curX += margin.Width;
 
1084
                        }
 
1085
                        startingPos = -1;
 
1086
                        return null;
 
1087
                }
 
1088
                
 
1089
                protected override bool OnButtonReleaseEvent (EventButton e)
 
1090
                {
 
1091
                        RemoveScrollWindowTimer ();
 
1092
                        
 
1093
                        //main context menu
 
1094
                        if (DoPopupMenu != null && e.IsContextMenuButton ()) {
 
1095
                                return true;
 
1096
                        }
 
1097
                        
 
1098
                        double startPos;
 
1099
                        Margin margin = GetMarginAtX (e.X, out startPos);
 
1100
                        if (margin != null)
 
1101
                                margin.MouseReleased (new MarginMouseEventArgs (textEditorData.Parent, e, e.Button, e.X - startPos, e.Y, e.State));
 
1102
                        ResetMouseState ();
 
1103
                        return base.OnButtonReleaseEvent (e);
 
1104
                }
 
1105
                
 
1106
                protected void ResetMouseState ()
 
1107
                {
 
1108
                        mouseButtonPressed = 0;
 
1109
                        textViewMargin.inDrag = false;
 
1110
                        textViewMargin.InSelectionDrag = false;
 
1111
                }
 
1112
                
 
1113
                bool dragOver = false;
 
1114
                ClipboardActions.CopyOperation dragContents = null;
 
1115
                DocumentLocation defaultCaretPos, dragCaretPos;
 
1116
                Selection selection = Selection.Empty;
 
1117
                
 
1118
                public bool IsInDrag {
 
1119
                        get {
 
1120
                                return dragOver;
 
1121
                        }
 
1122
                }
 
1123
                
 
1124
                public void CaretToDragCaretPosition ()
 
1125
                {
 
1126
                        Caret.Location = defaultCaretPos = dragCaretPos;
 
1127
                }
 
1128
                
 
1129
                protected override void OnDragLeave (DragContext context, uint time_)
 
1130
                {
 
1131
                        if (dragOver) {
 
1132
                                Caret.PreserveSelection = true;
 
1133
                                Caret.Location = defaultCaretPos;
 
1134
                                Caret.PreserveSelection = false;
 
1135
                                ResetMouseState ();
 
1136
                                dragOver = false;
 
1137
                        }
 
1138
                        base.OnDragLeave (context, time_);
 
1139
                }
 
1140
                
 
1141
                protected override void OnDragDataGet (DragContext context, SelectionData selection_data, uint info, uint time_)
 
1142
                {
 
1143
                        if (this.dragContents != null) {
 
1144
                                this.dragContents.SetData (selection_data, info);
 
1145
                                this.dragContents = null;
 
1146
                        }
 
1147
                        base.OnDragDataGet (context, selection_data, info, time_);
 
1148
                }
 
1149
 
 
1150
                protected override void OnDragDataReceived (DragContext context, int x, int y, SelectionData selection_data, uint info, uint time_)
 
1151
                {
 
1152
                        using (var undo = OpenUndoGroup ()) {
 
1153
                                int dragOffset = Document.LocationToOffset (dragCaretPos);
 
1154
                                if (context.Action == DragAction.Move) {
 
1155
                                        if (CanEdit (Caret.Line) && !selection.IsEmpty) {
 
1156
                                                var selectionRange = selection.GetSelectionRange (textEditorData);
 
1157
                                                if (selectionRange.Offset < dragOffset)
 
1158
                                                        dragOffset -= selectionRange.Length;
 
1159
                                                Caret.PreserveSelection = true;
 
1160
                                                textEditorData.DeleteSelection (selection);
 
1161
                                                Caret.PreserveSelection = false;
 
1162
        
 
1163
                                                selection = Selection.Empty;
 
1164
                                        }
 
1165
                                }
 
1166
                                if (selection_data.Length > 0 && selection_data.Format == 8) {
 
1167
                                        Caret.Offset = dragOffset;
 
1168
                                        if (CanEdit (dragCaretPos.Line)) {
 
1169
                                                int offset = Caret.Offset;
 
1170
                                                if (!selection.IsEmpty && selection.GetSelectionRange (textEditorData).Offset >= offset) {
 
1171
                                                        var start = Document.OffsetToLocation (selection.GetSelectionRange (textEditorData).Offset + selection_data.Text.Length);
 
1172
                                                        var end = Document.OffsetToLocation (selection.GetSelectionRange (textEditorData).Offset + selection_data.Text.Length + selection.GetSelectionRange (textEditorData).Length);
 
1173
                                                        selection = new Selection (start, end);
 
1174
                                                }
 
1175
                                                textEditorData.PasteText (offset, selection_data.Text, null);
 
1176
                                                Caret.Offset = offset + selection_data.Text.Length;
 
1177
                                                MainSelection = new Selection (Document.OffsetToLocation (offset), Document.OffsetToLocation (offset + selection_data.Text.Length));
 
1178
                                        }
 
1179
                                        dragOver = false;
 
1180
                                        context = null;
 
1181
                                }
 
1182
                                mouseButtonPressed = 0;
 
1183
                        }
 
1184
                        base.OnDragDataReceived (context, x, y, selection_data, info, time_);
 
1185
                }
 
1186
                
 
1187
                protected override bool OnDragMotion (DragContext context, int x, int y, uint time)
 
1188
                {
 
1189
                        if (!this.HasFocus)
 
1190
                                this.GrabFocus ();
 
1191
                        if (!dragOver) {
 
1192
                                defaultCaretPos = Caret.Location;
 
1193
                        }
 
1194
                        
 
1195
                        DocumentLocation oldLocation = Caret.Location;
 
1196
                        dragOver = true;
 
1197
                        Caret.PreserveSelection = true;
 
1198
                        dragCaretPos = PointToLocation (x - textViewMargin.XOffset, y);
 
1199
                        int offset = Document.LocationToOffset (dragCaretPos);
 
1200
                        if (!selection.IsEmpty && offset >= this.selection.GetSelectionRange (textEditorData).Offset && offset < this.selection.GetSelectionRange (textEditorData).EndOffset) {
 
1201
                                Gdk.Drag.Status (context, DragAction.Default, time);
 
1202
                                Caret.Location = defaultCaretPos;
 
1203
                        } else {
 
1204
                                Gdk.Drag.Status (context, (context.Actions & DragAction.Move) == DragAction.Move ? DragAction.Move : DragAction.Copy, time);
 
1205
                                Caret.Location = dragCaretPos; 
 
1206
                        }
 
1207
                        this.RedrawLine (oldLocation.Line);
 
1208
                        if (oldLocation.Line != Caret.Line)
 
1209
                                this.RedrawLine (Caret.Line);
 
1210
                        Caret.PreserveSelection = false;
 
1211
                        return base.OnDragMotion (context, x, y, time);
 
1212
                }
 
1213
                
 
1214
                Margin oldMargin = null;
 
1215
                bool overChildWidget;
 
1216
 
 
1217
                public event EventHandler BeginHover;
 
1218
 
 
1219
                protected virtual void OnBeginHover (EventArgs e)
 
1220
                {
 
1221
                        var handler = BeginHover;
 
1222
                        if (handler != null)
 
1223
                                handler (this, e);
 
1224
                }
 
1225
 
 
1226
                protected override bool OnMotionNotifyEvent (Gdk.EventMotion e)
 
1227
                {
 
1228
                        OnBeginHover (EventArgs.Empty);
 
1229
                        try {
 
1230
                                // The coordinates have to be properly adjusted to the origin since
 
1231
                                // the event may come from a child widget
 
1232
                                int rx, ry;
 
1233
                                GdkWindow.GetOrigin (out rx, out ry);
 
1234
                                double x = (int) e.XRoot - rx;
 
1235
                                double y = (int) e.YRoot - ry;
 
1236
                                overChildWidget = containerChildren.Any (w => w.Child.Allocation.Contains ((int)x, (int)y));
 
1237
 
 
1238
                                RemoveScrollWindowTimer ();
 
1239
                                Gdk.ModifierType mod = e.State;
 
1240
                                double startPos;
 
1241
                                Margin margin = GetMarginAtX (x, out startPos);
 
1242
                                if (textViewMargin.inDrag && margin == this.textViewMargin && Gtk.Drag.CheckThreshold (this, (int)pressPositionX, (int)pressPositionY, (int)x, (int)y)) {
 
1243
                                        dragContents = new ClipboardActions.CopyOperation ();
 
1244
                                        dragContents.CopyData (textEditorData);
 
1245
                                        DragContext context = Gtk.Drag.Begin (this, ClipboardActions.CopyOperation.TargetList, DragAction.Move | DragAction.Copy, 1, e);
 
1246
                                        if (!Platform.IsMac) {
 
1247
                                                CodeSegmentPreviewWindow window = new CodeSegmentPreviewWindow (textEditorData.Parent, true, textEditorData.SelectionRange, 300, 300);
 
1248
                                                Gtk.Drag.SetIconWidget (context, window, 0, 0);
 
1249
                                        }
 
1250
                                        selection = MainSelection;
 
1251
                                        textViewMargin.inDrag = false;
 
1252
                                } else {
 
1253
                                        FireMotionEvent (x, y, mod);
 
1254
                                        if (mouseButtonPressed != 0) {
 
1255
                                                UpdateScrollWindowTimer (x, y, mod);
 
1256
                                        }
 
1257
                                }
 
1258
                        } catch (Exception ex) {
 
1259
                                GLib.ExceptionManager.RaiseUnhandledException (ex, false);
 
1260
                        }
 
1261
                        return base.OnMotionNotifyEvent (e);
 
1262
                }
 
1263
                
 
1264
                uint   scrollWindowTimer = 0;
 
1265
                double scrollWindowTimer_x;
 
1266
                double scrollWindowTimer_y;
 
1267
                Gdk.ModifierType scrollWindowTimer_mod;
 
1268
                
 
1269
                void UpdateScrollWindowTimer (double x, double y, Gdk.ModifierType mod)
 
1270
                {
 
1271
                        scrollWindowTimer_x = x;
 
1272
                        scrollWindowTimer_y = y;
 
1273
                        scrollWindowTimer_mod = mod;
 
1274
                        if (scrollWindowTimer == 0) {
 
1275
                                scrollWindowTimer = GLib.Timeout.Add (50, delegate {
 
1276
                                        FireMotionEvent (scrollWindowTimer_x, scrollWindowTimer_y, scrollWindowTimer_mod);
 
1277
                                        return true;
 
1278
                                });
 
1279
                        }
 
1280
                }
 
1281
                
 
1282
                void RemoveScrollWindowTimer ()
 
1283
                {
 
1284
                        if (scrollWindowTimer != 0) {
 
1285
                                GLib.Source.Remove (scrollWindowTimer);
 
1286
                                
 
1287
                                scrollWindowTimer = 0;
 
1288
                        }
 
1289
                }
 
1290
                
 
1291
                Gdk.ModifierType lastState = ModifierType.None;
 
1292
 
 
1293
                void FireMotionEvent (double x, double y, Gdk.ModifierType state)
 
1294
                {
 
1295
                        lastState = state;
 
1296
                        mx = x - textViewMargin.XOffset;
 
1297
                        my = y;
 
1298
 
 
1299
                        ShowTooltip (state);
 
1300
 
 
1301
                        double startPos;
 
1302
                        Margin margin;
 
1303
                        if (textViewMargin.InSelectionDrag) {
 
1304
                                margin = textViewMargin;
 
1305
                                startPos = textViewMargin.XOffset;
 
1306
                        } else {
 
1307
                                margin = GetMarginAtX (x, out startPos);
 
1308
                                if (margin != null && GdkWindow != null) {
 
1309
                                        if (!overChildWidget)
 
1310
                                                GdkWindow.Cursor = margin.MarginCursor;
 
1311
                                        else {
 
1312
                                                // Set the default cursor when the mouse is over an embedded widget
 
1313
                                                GdkWindow.Cursor = null;
 
1314
                                        }
 
1315
                                }
 
1316
                        }
 
1317
 
 
1318
                        if (oldMargin != margin && oldMargin != null)
 
1319
                                oldMargin.MouseLeft ();
 
1320
                        
 
1321
                        if (margin != null) 
 
1322
                                margin.MouseHover (new MarginMouseEventArgs (textEditorData.Parent, EventType.MotionNotify,
 
1323
                                        mouseButtonPressed, x - startPos, y, state));
 
1324
                        oldMargin = margin;
 
1325
                }
 
1326
 
 
1327
                #region CustomDrag (for getting dnd data from toolbox items for example)
 
1328
                string     customText;
 
1329
                Gtk.Widget customSource;
 
1330
                public void BeginDrag (string text, Gtk.Widget source, DragContext context)
 
1331
                {
 
1332
                        customText = text;
 
1333
                        customSource = source;
 
1334
                        source.DragDataGet += CustomDragDataGet;
 
1335
                        source.DragEnd     += CustomDragEnd;
 
1336
                }
 
1337
                void CustomDragDataGet (object sender, Gtk.DragDataGetArgs args) 
 
1338
                {
 
1339
                        args.SelectionData.Text = customText;
 
1340
                }
 
1341
                void CustomDragEnd (object sender, Gtk.DragEndArgs args) 
 
1342
                {
 
1343
                        customSource.DragDataGet -= CustomDragDataGet;
 
1344
                        customSource.DragEnd -= CustomDragEnd;
 
1345
                        customSource = null;
 
1346
                        customText = null;
 
1347
                }
 
1348
                #endregion
 
1349
                bool isMouseTrapped = false;
 
1350
                
 
1351
                protected override bool OnEnterNotifyEvent (EventCrossing evnt)
 
1352
                {
 
1353
                        isMouseTrapped = true;
 
1354
                        return base.OnEnterNotifyEvent (evnt);
 
1355
                }
 
1356
                
 
1357
                protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing e)
 
1358
                {
 
1359
                        isMouseTrapped = false;
 
1360
                        if (tipWindow != null && currentTooltipProvider != null) {
 
1361
                                if (!currentTooltipProvider.IsInteractive (textEditorData.Parent, tipWindow))
 
1362
                                        DelayedHideTooltip ();
 
1363
                        } else {
 
1364
                                HideTooltip ();
 
1365
                        }
 
1366
                        textViewMargin.HideCodeSegmentPreviewWindow ();
 
1367
                        
 
1368
                        if (GdkWindow != null)
 
1369
                                GdkWindow.Cursor = null;
 
1370
                        if (oldMargin != null)
 
1371
                                oldMargin.MouseLeft ();
 
1372
                        
 
1373
                        return base.OnLeaveNotifyEvent (e); 
 
1374
                }
 
1375
 
 
1376
                public double LineHeight {
 
1377
                        get {
 
1378
                                return this.textEditorData.LineHeight;
 
1379
                        }
 
1380
                        internal set {
 
1381
                                this.textEditorData.LineHeight = value;
 
1382
                        }
 
1383
                }
 
1384
                
 
1385
                public TextViewMargin TextViewMargin {
 
1386
                        get {
 
1387
                                return textViewMargin;
 
1388
                        }
 
1389
                }
 
1390
                
 
1391
                public Margin IconMargin {
 
1392
                        get { return iconMargin; }
 
1393
                }
 
1394
                
 
1395
                public DocumentLocation LogicalToVisualLocation (DocumentLocation location)
 
1396
                {
 
1397
                        return textEditorData.LogicalToVisualLocation (location);
 
1398
                }
 
1399
 
 
1400
                public DocumentLocation LogicalToVisualLocation (int line, int column)
 
1401
                {
 
1402
                        return textEditorData.LogicalToVisualLocation (line, column);
 
1403
                }
 
1404
                
 
1405
                public void CenterToCaret ()
 
1406
                {
 
1407
                        CenterTo (Caret.Location);
 
1408
                }
 
1409
                
 
1410
                public void CenterTo (int offset)
 
1411
                {
 
1412
                        CenterTo (Document.OffsetToLocation (offset));
 
1413
                }
 
1414
                
 
1415
                public void CenterTo (int line, int column)
 
1416
                {
 
1417
                        CenterTo (new DocumentLocation (line, column));
 
1418
                }
 
1419
                
 
1420
                public void CenterTo (DocumentLocation p)
 
1421
                {
 
1422
                        if (isDisposed || p.Line < 0 || p.Line > Document.LineCount)
 
1423
                                return;
 
1424
                        SetAdjustments (this.Allocation);
 
1425
                        //                      Adjustment adj;
 
1426
                        //adj.Upper
 
1427
                        if (this.textEditorData.VAdjustment.Upper < Allocation.Height) {
 
1428
                                this.textEditorData.VAdjustment.Value = 0;
 
1429
                                return;
 
1430
                        }
 
1431
                        
 
1432
                        //      int yMargin = 1 * this.LineHeight;
 
1433
                        double caretPosition = LineToY (p.Line);
 
1434
                        caretPosition -= this.textEditorData.VAdjustment.PageSize / 2;
 
1435
 
 
1436
                        // Make sure the caret position is inside the bounds. This avoids an unnecessary bump of the scrollview.
 
1437
                        // The adjustment does this check, but does it after assigning the value, so the value may be out of bounds for a while.
 
1438
                        if (caretPosition + this.textEditorData.VAdjustment.PageSize > this.textEditorData.VAdjustment.Upper)
 
1439
                                caretPosition = this.textEditorData.VAdjustment.Upper - this.textEditorData.VAdjustment.PageSize;
 
1440
 
 
1441
                        this.textEditorData.VAdjustment.Value = caretPosition;
 
1442
                        
 
1443
                        if (this.textEditorData.HAdjustment.Upper < Allocation.Width)  {
 
1444
                                this.textEditorData.HAdjustment.Value = 0;
 
1445
                        } else {
 
1446
                                double caretX = ColumnToX (Document.GetLine (p.Line), p.Column);
 
1447
                                double textWith = Allocation.Width - textViewMargin.XOffset;
 
1448
                                if (this.textEditorData.HAdjustment.Value > caretX) {
 
1449
                                        this.textEditorData.HAdjustment.Value = caretX;
 
1450
                                } else if (this.textEditorData.HAdjustment.Value + textWith < caretX + TextViewMargin.CharWidth) {
 
1451
                                        double adjustment = System.Math.Max (0, caretX - textWith + TextViewMargin.CharWidth);
 
1452
                                        this.textEditorData.HAdjustment.Value = adjustment;
 
1453
                                }
 
1454
                        }
 
1455
                }
 
1456
 
 
1457
                public void ScrollTo (int offset)
 
1458
                {
 
1459
                        ScrollTo (Document.OffsetToLocation (offset));
 
1460
                }
 
1461
                
 
1462
                public void ScrollTo (int line, int column)
 
1463
                {
 
1464
                        ScrollTo (new DocumentLocation (line, column));
 
1465
                }
 
1466
 
 
1467
//              class ScrollingActor
 
1468
//              {
 
1469
//                      readonly TextEditor editor;
 
1470
//                      readonly double targetValue;
 
1471
//                      readonly double initValue;
 
1472
//                      
 
1473
//                      public ScrollingActor (Mono.TextEditor.TextEditor editor, double targetValue)
 
1474
//                      {
 
1475
//                              this.editor = editor;
 
1476
//                              this.targetValue = targetValue;
 
1477
//                              this.initValue = editor.VAdjustment.Value;
 
1478
//                      }
 
1479
//
 
1480
//                      public bool Step (Actor<ScrollingActor> actor)
 
1481
//                      {
 
1482
//                              if (actor.Expired) {
 
1483
//                                      editor.VAdjustment.Value = targetValue;
 
1484
//                                      return false;
 
1485
//                              }
 
1486
//                              var newValue = initValue + (targetValue - initValue) / 100   * actor.Percent;
 
1487
//                              editor.VAdjustment.Value = newValue;
 
1488
//                              return true;
 
1489
//                      }
 
1490
//              }
 
1491
 
 
1492
                internal void SmoothScrollTo (double value)
 
1493
                {
 
1494
                        this.textEditorData.VAdjustment.Value = value;
 
1495
/*                      Stage<ScrollingActor> scroll = new Stage<ScrollingActor> (50);
 
1496
                        scroll.UpdateFrequency = 10;
 
1497
                        var scrollingActor = new ScrollingActor (this, value);
 
1498
                        scroll.Add (scrollingActor, 50);
 
1499
 
 
1500
                        scroll.ActorStep += scrollingActor.Step;
 
1501
                        scroll.Play ();*/
 
1502
                }
 
1503
 
 
1504
                public void ScrollTo (DocumentLocation p)
 
1505
                {
 
1506
                        if (isDisposed || p.Line < 0 || p.Line > Document.LineCount || inCaretScroll)
 
1507
                                return;
 
1508
                        inCaretScroll = true;
 
1509
                        try {
 
1510
                                if (this.textEditorData.VAdjustment.Upper < Allocation.Height) {
 
1511
                                        this.textEditorData.VAdjustment.Value = 0;
 
1512
                                } else {
 
1513
                                        double caretPosition = LineToY (p.Line);
 
1514
                                        if (this.textEditorData.VAdjustment.Value > caretPosition) {
 
1515
                                                this.textEditorData.VAdjustment.Value = caretPosition;
 
1516
                                        } else if (this.textEditorData.VAdjustment.Value + this.textEditorData.VAdjustment.PageSize - this.LineHeight < caretPosition) {
 
1517
                                                this.textEditorData.VAdjustment.Value = caretPosition - this.textEditorData.VAdjustment.PageSize + this.LineHeight;
 
1518
                                        }
 
1519
                                }
 
1520
                                
 
1521
                                if (this.textEditorData.HAdjustment.Upper < Allocation.Width)  {
 
1522
                                        this.textEditorData.HAdjustment.Value = 0;
 
1523
                                } else {
 
1524
                                        double caretX = ColumnToX (Document.GetLine (p.Line), p.Column);
 
1525
                                        double textWith = Allocation.Width - textViewMargin.XOffset;
 
1526
                                        if (this.textEditorData.HAdjustment.Value > caretX) {
 
1527
                                                this.textEditorData.HAdjustment.Value = caretX;
 
1528
                                        } else if (this.textEditorData.HAdjustment.Value + textWith < caretX + TextViewMargin.CharWidth) {
 
1529
                                                double adjustment = System.Math.Max (0, caretX - textWith + TextViewMargin.CharWidth);
 
1530
                                                this.textEditorData.HAdjustment.Value = adjustment;
 
1531
                                        }
 
1532
                                }
 
1533
                        } finally {
 
1534
                                inCaretScroll = false;
 
1535
                        }
 
1536
                }
 
1537
 
 
1538
                /// <summary>
 
1539
                /// Scrolls the editor as required for making the specified area visible 
 
1540
                /// </summary>
 
1541
                public void ScrollTo (Gdk.Rectangle rect)
 
1542
                {
 
1543
                        inCaretScroll = true;
 
1544
                        try {
 
1545
                                var vad = this.textEditorData.VAdjustment;
 
1546
                                if (vad.Upper < Allocation.Height) {
 
1547
                                        vad.Value = 0;
 
1548
                                } else {
 
1549
                                        if (vad.Value >= rect.Top) {
 
1550
                                                vad.Value = rect.Top;
 
1551
                                        } else if (vad.Value + vad.PageSize - rect.Height < rect.Top) {
 
1552
                                                vad.Value = rect.Top - vad.PageSize + rect.Height;
 
1553
                                        }
 
1554
                                }
 
1555
 
 
1556
                                var had = this.textEditorData.HAdjustment;
 
1557
                                if (had.Upper < Allocation.Width)  {
 
1558
                                        had.Value = 0;
 
1559
                                } else {
 
1560
                                        if (had.Value >= rect.Left) {
 
1561
                                                had.Value = rect.Left;
 
1562
                                        } else if (had.Value + had.PageSize - rect.Width < rect.Left) {
 
1563
                                                had.Value = rect.Left - had.PageSize + rect.Width;
 
1564
                                        }
 
1565
                                }
 
1566
                        } finally {
 
1567
                                inCaretScroll = false;
 
1568
                        }
 
1569
                }
 
1570
                
 
1571
                bool inCaretScroll = false;
 
1572
                public void ScrollToCaret ()
 
1573
                {
 
1574
                        ScrollTo (Caret.Location);
 
1575
                }
 
1576
 
 
1577
                public void TryToResetHorizontalScrollPosition ()
 
1578
                {
 
1579
                        int caretX = (int)ColumnToX (Document.GetLine (Caret.Line), Caret.Column);
 
1580
                        int textWith = Allocation.Width - (int)textViewMargin.XOffset;
 
1581
                        if (caretX < textWith - TextViewMargin.CharWidth) 
 
1582
                                this.textEditorData.HAdjustment.Value = 0;
 
1583
                }
 
1584
                
 
1585
                protected override void OnSizeAllocated (Gdk.Rectangle allocation)
 
1586
                {
 
1587
                        base.OnSizeAllocated (allocation);
 
1588
                        SetAdjustments (Allocation);
 
1589
                        sizeHasBeenAllocated = true;
 
1590
                        if (Options.WrapLines)
 
1591
                                textViewMargin.PurgeLayoutCache ();
 
1592
                        SetChildrenPositions (allocation);
 
1593
                }
 
1594
 
 
1595
                uint lastScrollTime;
 
1596
                protected override bool OnScrollEvent (EventScroll evnt)
 
1597
                {
 
1598
                        var modifier = !Platform.IsMac? Gdk.ModifierType.ControlMask
 
1599
                                //Mac window manager already uses control-scroll, so use command
 
1600
                                //Command might be either meta or mod1, depending on GTK version
 
1601
                                : (Gdk.ModifierType.MetaMask | Gdk.ModifierType.Mod1Mask);
 
1602
 
 
1603
                        var hasZoomModifier = (evnt.State & modifier) != 0;
 
1604
                        if (hasZoomModifier && lastScrollTime != 0 && (evnt.Time - lastScrollTime) < 100)
 
1605
                                hasZoomModifier = false;
 
1606
 
 
1607
                        if (hasZoomModifier) {
 
1608
                                if (evnt.Direction == ScrollDirection.Up)
 
1609
                                        Options.ZoomIn ();
 
1610
                                else if (evnt.Direction == ScrollDirection.Down)
 
1611
                                        Options.ZoomOut ();
 
1612
                                
 
1613
                                this.QueueDraw ();
 
1614
                                if (isMouseTrapped)
 
1615
                                        FireMotionEvent (mx + textViewMargin.XOffset, my, lastState);
 
1616
                                return true;
 
1617
                        }
 
1618
                        lastScrollTime = evnt.Time;
 
1619
                        return base.OnScrollEvent (evnt); 
 
1620
                }
 
1621
                
 
1622
                void SetHAdjustment ()
 
1623
                {
 
1624
                        textEditorData.HeightTree.Rebuild ();
 
1625
                        
 
1626
                        if (textEditorData.HAdjustment == null || Options == null)
 
1627
                                return;
 
1628
                        textEditorData.HAdjustment.ValueChanged -= HAdjustmentValueChanged;
 
1629
                        if (Options.WrapLines) {
 
1630
                                this.textEditorData.HAdjustment.SetBounds (0, 0, 0, 0, 0);
 
1631
                        } else {
 
1632
                                if (longestLine != null && this.textEditorData.HAdjustment != null) {
 
1633
                                        double maxX = longestLineWidth;
 
1634
                                        if (maxX > Allocation.Width)
 
1635
                                                maxX += 2 * this.textViewMargin.CharWidth;
 
1636
                                        double width = Allocation.Width - this.TextViewMargin.XOffset;
 
1637
                                        var realMaxX = System.Math.Max (maxX, this.textEditorData.HAdjustment.Value + width);
 
1638
 
 
1639
                                        foreach (var containerChild in editor.containerChildren.Concat (containerChildren)) {
 
1640
                                                if (containerChild.Child == this)
 
1641
                                                        continue;
 
1642
                                                realMaxX = System.Math.Max (realMaxX, containerChild.X + containerChild.Child.Allocation.Width);
 
1643
                                        }
 
1644
 
 
1645
                                        this.textEditorData.HAdjustment.SetBounds (
 
1646
                                                0,
 
1647
                                                realMaxX,
 
1648
                                                this.textViewMargin.CharWidth,
 
1649
                                                width,
 
1650
                                                width);
 
1651
                                        if (realMaxX < width)
 
1652
                                                this.textEditorData.HAdjustment.Value = 0;
 
1653
                                }
 
1654
                        }
 
1655
                        textEditorData.HAdjustment.ValueChanged += HAdjustmentValueChanged;
 
1656
                }
 
1657
                
 
1658
                internal void SetAdjustments ()
 
1659
                {
 
1660
                        SetAdjustments (Allocation);
 
1661
                }
 
1662
                
 
1663
                public const int EditorLineThreshold = 0;
 
1664
 
 
1665
                internal void SetAdjustments (Gdk.Rectangle allocation)
 
1666
                {
 
1667
                        SetHAdjustment ();
 
1668
                        
 
1669
                        if (this.textEditorData.VAdjustment != null) {
 
1670
                                double maxY = textEditorData.HeightTree.TotalHeight;
 
1671
                                if (maxY > allocation.Height)
 
1672
                                        maxY += EditorLineThreshold * this.LineHeight;
 
1673
 
 
1674
                                foreach (var containerChild in editor.containerChildren.Concat (containerChildren)) {
 
1675
                                        maxY = System.Math.Max (maxY, containerChild.Y + containerChild.Child.SizeRequest().Height);
 
1676
                                }
 
1677
 
 
1678
                                if (VAdjustment.Value > maxY - allocation.Height) {
 
1679
                                        VAdjustment.Value = System.Math.Max (0, maxY - allocation.Height);
 
1680
                                        QueueDraw ();
 
1681
                                }
 
1682
                                this.textEditorData.VAdjustment.SetBounds (0, 
 
1683
                                                                           System.Math.Max (allocation.Height, maxY), 
 
1684
                                                                           LineHeight,
 
1685
                                                                           allocation.Height,
 
1686
                                                                           allocation.Height);
 
1687
                                if (maxY < allocation.Height)
 
1688
                                        this.textEditorData.VAdjustment.Value = 0;
 
1689
                        }
 
1690
                }
 
1691
                
 
1692
                public int GetWidth (string text)
 
1693
                {
 
1694
                        return this.textViewMargin.GetWidth (text);
 
1695
                }
 
1696
                
 
1697
                void UpdateMarginXOffsets ()
 
1698
                {
 
1699
                        double curX = 0;
 
1700
                        foreach (Margin margin in this.margins) {
 
1701
                                if (!margin.IsVisible)
 
1702
                                        continue;
 
1703
                                margin.XOffset = curX;
 
1704
                                curX += margin.Width;
 
1705
                        }
 
1706
                }
 
1707
                
 
1708
                void RenderMargins (Cairo.Context cr, Cairo.Context textViewCr, Cairo.Rectangle cairoRectangle)
 
1709
                {
 
1710
                        this.TextViewMargin.rulerX = Options.RulerColumn * this.TextViewMargin.CharWidth - this.textEditorData.HAdjustment.Value;
 
1711
                        int startLine = YToLine (cairoRectangle.Y + this.textEditorData.VAdjustment.Value);
 
1712
                        double startY = LineToY (startLine);
 
1713
                        double curY = startY - this.textEditorData.VAdjustment.Value;
 
1714
                        bool setLongestLine = false;
 
1715
                        foreach (var margin in this.margins) {
 
1716
                                if (margin.BackgroundRenderer != null)
 
1717
                                        margin.BackgroundRenderer.Draw (cr, cairoRectangle);
 
1718
                        }
 
1719
 
 
1720
 
 
1721
                        for (int visualLineNumber = textEditorData.LogicalToVisualLine (startLine);; visualLineNumber++) {
 
1722
                                int logicalLineNumber = textEditorData.VisualToLogicalLine (visualLineNumber);
 
1723
                                var line = Document.GetLine (logicalLineNumber);
 
1724
 
 
1725
                                // Ensure that the correct line height is set.
 
1726
                                if (line != null)
 
1727
                                        textViewMargin.GetLayout (line);
 
1728
 
 
1729
                                double lineHeight = GetLineHeight (line);
 
1730
                                foreach (var margin in this.margins) {
 
1731
                                        if (!margin.IsVisible)
 
1732
                                                continue;
 
1733
                                        try {
 
1734
                                                margin.Draw (margin == textViewMargin ? textViewCr : cr, cairoRectangle, line, logicalLineNumber, margin.XOffset, curY, lineHeight);
 
1735
                                        } catch (Exception e) {
 
1736
                                                System.Console.WriteLine (e);
 
1737
                                        }
 
1738
                                }
 
1739
                                // take the line real render width from the text view margin rendering (a line can consist of more than 
 
1740
                                // one line and be longer (foldings!) ex. : someLine1[...]someLine2[...]someLine3)
 
1741
                                double lineWidth = textViewMargin.lastLineRenderWidth + HAdjustment.Value;
 
1742
                                if (longestLine == null || lineWidth > longestLineWidth) {
 
1743
                                        longestLine = line;
 
1744
                                        longestLineWidth = lineWidth;
 
1745
                                        setLongestLine = true;
 
1746
                                }
 
1747
                                curY += lineHeight;
 
1748
                                if (curY > cairoRectangle.Y + cairoRectangle.Height)
 
1749
                                        break;
 
1750
                        }
 
1751
                        
 
1752
                        foreach (var margin in this.margins) {
 
1753
                                if (!margin.IsVisible)
 
1754
                                        continue;
 
1755
                                foreach (var drawer in margin.MarginDrawer)
 
1756
                                        drawer.Draw (cr, cairoRectangle);
 
1757
                        }
 
1758
                        
 
1759
                        if (setLongestLine) 
 
1760
                                SetHAdjustment ();
 
1761
                }
 
1762
                
 
1763
                /*
 
1764
                protected override bool OnWidgetEvent (Event evnt)
 
1765
                {
 
1766
                        System.Console.WriteLine(evnt);
 
1767
                        return base.OnWidgetEvent (evnt);
 
1768
                }*/
 
1769
                
 
1770
                double oldVadjustment = 0;
 
1771
                
 
1772
                void UpdateAdjustments ()
 
1773
                {
 
1774
                        int lastVisibleLine = textEditorData.LogicalToVisualLine (Document.LineCount);
 
1775
                        if (oldRequest != lastVisibleLine) {
 
1776
                                SetAdjustments (this.Allocation);
 
1777
                                oldRequest = lastVisibleLine;
 
1778
                        }
 
1779
                }
 
1780
 
 
1781
#if DEBUG_EXPOSE
 
1782
                DateTime started = DateTime.Now;
 
1783
#endif
 
1784
                protected override bool OnExposeEvent (Gdk.EventExpose e)
 
1785
                {
 
1786
                        if (this.isDisposed)
 
1787
                                return false;
 
1788
                        UpdateAdjustments ();
 
1789
 
 
1790
 
 
1791
                        var area = e.Region.Clipbox;
 
1792
                        var cairoArea = new Cairo.Rectangle (area.X, area.Y, area.Width, area.Height);
 
1793
                        using (Cairo.Context cr = Gdk.CairoHelper.Create (e.Window))
 
1794
                        using (Cairo.Context textViewCr = Gdk.CairoHelper.Create (e.Window)) {
 
1795
                                if (!Options.UseAntiAliasing) {
 
1796
                                        textViewCr.Antialias = Cairo.Antialias.None;
 
1797
                                        cr.Antialias = Cairo.Antialias.None;
 
1798
                                }
 
1799
                                
 
1800
                                UpdateMarginXOffsets ();
 
1801
                                
 
1802
                                cr.LineWidth = Options.Zoom;
 
1803
                                textViewCr.LineWidth = Options.Zoom;
 
1804
                                textViewCr.Rectangle (textViewMargin.XOffset, 0, Allocation.Width - textViewMargin.XOffset, Allocation.Height);
 
1805
                                textViewCr.Clip ();
 
1806
                                
 
1807
                                RenderMargins (cr, textViewCr, cairoArea);
 
1808
                        
 
1809
#if DEBUG_EXPOSE
 
1810
                                Console.WriteLine ("{0} expose {1},{2} {3}x{4}", (long)(DateTime.Now - started).TotalMilliseconds,
 
1811
                                        e.Area.X, e.Area.Y, e.Area.Width, e.Area.Height);
 
1812
#endif
 
1813
                                if (requestResetCaretBlink && HasFocus) {
 
1814
                                        textViewMargin.ResetCaretBlink (200);
 
1815
                                        requestResetCaretBlink = false;
 
1816
                                }
 
1817
                                
 
1818
                                foreach (Animation animation in actors) {
 
1819
                                        animation.Drawer.Draw (cr);
 
1820
                                }
 
1821
                                
 
1822
                                if (HasFocus)
 
1823
                                        textViewMargin.DrawCaret (e.Window, e.Area);
 
1824
                                
 
1825
                                OnPainted (new PaintEventArgs (cr, cairoArea));
 
1826
                        }
 
1827
 
 
1828
                        if (Caret.IsVisible)
 
1829
                                textViewMargin.DrawCaret (e.Window, Allocation);
 
1830
 
 
1831
                        return base.OnExposeEvent (e);
 
1832
                }
 
1833
                
 
1834
                protected virtual void OnPainted (PaintEventArgs e)
 
1835
                {
 
1836
                        EventHandler<PaintEventArgs> handler = this.Painted;
 
1837
                        if (handler != null)
 
1838
                                handler (this, e);
 
1839
                }
 
1840
 
 
1841
                public event EventHandler<PaintEventArgs> Painted;
 
1842
 
 
1843
                #region TextEditorData delegation
 
1844
                public string EolMarker {
 
1845
                        get {
 
1846
                                return textEditorData.EolMarker;
 
1847
                        }
 
1848
                }
 
1849
                
 
1850
                public Mono.TextEditor.Highlighting.ColorScheme ColorStyle {
 
1851
                        get {
 
1852
                                return this.textEditorData.ColorStyle;
 
1853
                        }
 
1854
                }
 
1855
                
 
1856
                public EditMode CurrentMode {
 
1857
                        get {
 
1858
                                return this.textEditorData.CurrentMode;
 
1859
                        }
 
1860
                        set {
 
1861
                                this.textEditorData.CurrentMode = value;
 
1862
                        }
 
1863
                }
 
1864
                
 
1865
                public bool IsSomethingSelected {
 
1866
                        get {
 
1867
                                return this.textEditorData.IsSomethingSelected;
 
1868
                        }
 
1869
                }
 
1870
                
 
1871
                public Selection MainSelection {
 
1872
                        get {
 
1873
                                return textEditorData.MainSelection;
 
1874
                        }
 
1875
                        set {
 
1876
                                textEditorData.MainSelection = value;
 
1877
                        }
 
1878
                }
 
1879
                
 
1880
                public SelectionMode SelectionMode {
 
1881
                        get {
 
1882
                                return textEditorData.SelectionMode;
 
1883
                        }
 
1884
                        set {
 
1885
                                textEditorData.SelectionMode = value;
 
1886
                        }
 
1887
                }
 
1888
 
 
1889
                public TextSegment SelectionRange {
 
1890
                        get {
 
1891
                                return this.textEditorData.SelectionRange;
 
1892
                        }
 
1893
                        set {
 
1894
                                this.textEditorData.SelectionRange = value;
 
1895
                        }
 
1896
                }
 
1897
                                
 
1898
                public string SelectedText {
 
1899
                        get {
 
1900
                                return this.textEditorData.SelectedText;
 
1901
                        }
 
1902
                        set {
 
1903
                                this.textEditorData.SelectedText = value;
 
1904
                        }
 
1905
                }
 
1906
                
 
1907
                public int SelectionAnchor {
 
1908
                        get {
 
1909
                                return this.textEditorData.SelectionAnchor;
 
1910
                        }
 
1911
                        set {
 
1912
                                this.textEditorData.SelectionAnchor = value;
 
1913
                        }
 
1914
                }
 
1915
                
 
1916
                public IEnumerable<DocumentLine> SelectedLines {
 
1917
                        get {
 
1918
                                return this.textEditorData.SelectedLines;
 
1919
                        }
 
1920
                }
 
1921
                
 
1922
                public Adjustment HAdjustment {
 
1923
                        get {
 
1924
                                return this.textEditorData.HAdjustment;
 
1925
                        }
 
1926
                }
 
1927
                
 
1928
                public Adjustment VAdjustment {
 
1929
                        get {
 
1930
                                return this.textEditorData.VAdjustment;
 
1931
                        }
 
1932
                }
 
1933
                
 
1934
                public int Insert (int offset, string value)
 
1935
                {
 
1936
                        return textEditorData.Insert (offset, value);
 
1937
                }
 
1938
                
 
1939
                public void Remove (DocumentRegion region)
 
1940
                {
 
1941
                        textEditorData.Remove (region);
 
1942
                }
 
1943
                
 
1944
                public void Remove (TextSegment removeSegment)
 
1945
                {
 
1946
                        textEditorData.Remove (removeSegment);
 
1947
                }
 
1948
 
 
1949
                public void Remove (int offset, int count)
 
1950
                {
 
1951
                        textEditorData.Remove (offset, count);
 
1952
                }
 
1953
                
 
1954
                public int Replace (int offset, int count, string value)
 
1955
                {
 
1956
                        return textEditorData.Replace (offset, count, value);
 
1957
                }
 
1958
                
 
1959
                public void ClearSelection ()
 
1960
                {
 
1961
                        this.textEditorData.ClearSelection ();
 
1962
                }
 
1963
                
 
1964
                public void DeleteSelectedText ()
 
1965
                {
 
1966
                        this.textEditorData.DeleteSelectedText ();
 
1967
                }
 
1968
                
 
1969
                public void DeleteSelectedText (bool clearSelection)
 
1970
                {
 
1971
                        this.textEditorData.DeleteSelectedText (clearSelection);
 
1972
                }
 
1973
                
 
1974
                public void RunEditAction (Action<TextEditorData> action)
 
1975
                {
 
1976
                        action (this.textEditorData);
 
1977
                }
 
1978
                
 
1979
                public void SetSelection (int anchorOffset, int leadOffset)
 
1980
                {
 
1981
                        this.textEditorData.SetSelection (anchorOffset, leadOffset);
 
1982
                }
 
1983
                
 
1984
                public void SetSelection (DocumentLocation anchor, DocumentLocation lead)
 
1985
                {
 
1986
                        this.textEditorData.SetSelection (anchor, lead);
 
1987
                }
 
1988
                        
 
1989
                public void SetSelection (int anchorLine, int anchorColumn, int leadLine, int leadColumn)
 
1990
                {
 
1991
                        this.textEditorData.SetSelection (anchorLine, anchorColumn, leadLine, leadColumn);
 
1992
                }
 
1993
                
 
1994
                public void ExtendSelectionTo (DocumentLocation location)
 
1995
                {
 
1996
                        this.textEditorData.ExtendSelectionTo (location);
 
1997
                }
 
1998
                public void ExtendSelectionTo (int offset)
 
1999
                {
 
2000
                        this.textEditorData.ExtendSelectionTo (offset);
 
2001
                }
 
2002
                public void SetSelectLines (int from, int to)
 
2003
                {
 
2004
                        this.textEditorData.SetSelectLines (from, to);
 
2005
                }
 
2006
                
 
2007
                public void InsertAtCaret (string text)
 
2008
                {
 
2009
                        textEditorData.InsertAtCaret (text);
 
2010
                }
 
2011
                
 
2012
                public bool CanEdit (int line)
 
2013
                {
 
2014
                        return textEditorData.CanEdit (line);
 
2015
                }
 
2016
                
 
2017
                public string GetLineText (int line)
 
2018
                {
 
2019
                        return textEditorData.GetLineText (line);
 
2020
                }
 
2021
                
 
2022
                public string GetLineText (int line, bool includeDelimiter)
 
2023
                {
 
2024
                        return textEditorData.GetLineText (line, includeDelimiter);
 
2025
                }
 
2026
                
 
2027
                /// <summary>
 
2028
                /// Use with care.
 
2029
                /// </summary>
 
2030
                /// <returns>
 
2031
                /// A <see cref="TextEditorData"/>
 
2032
                /// </returns>
 
2033
                public TextEditorData GetTextEditorData ()
 
2034
                {
 
2035
                        return this.textEditorData;
 
2036
                }
 
2037
                
 
2038
                public event EventHandler SelectionChanged;
 
2039
                protected virtual void OnSelectionChanged (EventArgs args)
 
2040
                {
 
2041
                        CurrentMode.InternalSelectionChanged (editor, textEditorData);
 
2042
                        if (SelectionChanged != null) 
 
2043
                                SelectionChanged (this, args);
 
2044
                }
 
2045
                #endregion
 
2046
                
 
2047
                #region Document delegation
 
2048
                public int Length {
 
2049
                        get {
 
2050
                                return Document.TextLength;
 
2051
                        }
 
2052
                }
 
2053
 
 
2054
                public string Text {
 
2055
                        get {
 
2056
                                return Document.Text;
 
2057
                        }
 
2058
                        set {
 
2059
                                Document.Text = value;
 
2060
                        }
 
2061
                }
 
2062
 
 
2063
                public string GetTextBetween (int startOffset, int endOffset)
 
2064
                {
 
2065
                        return Document.GetTextBetween (startOffset, endOffset);
 
2066
                }
 
2067
                
 
2068
                public string GetTextBetween (DocumentLocation start, DocumentLocation end)
 
2069
                {
 
2070
                        return Document.GetTextBetween (start, end);
 
2071
                }
 
2072
                
 
2073
                public string GetTextBetween (int startLine, int startColumn, int endLine, int endColumn)
 
2074
                {
 
2075
                        return Document.GetTextBetween (startLine, startColumn, endLine, endColumn);
 
2076
                }
 
2077
 
 
2078
                public string GetTextAt (int offset, int count)
 
2079
                {
 
2080
                        return Document.GetTextAt (offset, count);
 
2081
                }
 
2082
 
 
2083
 
 
2084
                public string GetTextAt (TextSegment segment)
 
2085
                {
 
2086
                        return Document.GetTextAt (segment);
 
2087
                }
 
2088
                
 
2089
                public string GetTextAt (DocumentRegion region)
 
2090
                {
 
2091
                        return Document.GetTextAt (region);
 
2092
                }
 
2093
 
 
2094
                public char GetCharAt (int offset)
 
2095
                {
 
2096
                        return Document.GetCharAt (offset);
 
2097
                }
 
2098
                
 
2099
                public IEnumerable<DocumentLine> Lines {
 
2100
                        get {
 
2101
                                return Document.Lines;
 
2102
                        }
 
2103
                }
 
2104
                
 
2105
                public int LineCount {
 
2106
                        get {
 
2107
                                return Document.LineCount;
 
2108
                        }
 
2109
                }
 
2110
                
 
2111
                public int LocationToOffset (int line, int column)
 
2112
                {
 
2113
                        return Document.LocationToOffset (line, column);
 
2114
                }
 
2115
                
 
2116
                public int LocationToOffset (DocumentLocation location)
 
2117
                {
 
2118
                        return Document.LocationToOffset (location);
 
2119
                }
 
2120
                
 
2121
                public DocumentLocation OffsetToLocation (int offset)
 
2122
                {
 
2123
                        return Document.OffsetToLocation (offset);
 
2124
                }
 
2125
 
 
2126
                public string GetLineIndent (int lineNumber)
 
2127
                {
 
2128
                        return Document.GetLineIndent (lineNumber);
 
2129
                }
 
2130
                
 
2131
                public string GetLineIndent (DocumentLine segment)
 
2132
                {
 
2133
                        return Document.GetLineIndent (segment);
 
2134
                }
 
2135
                
 
2136
                public DocumentLine GetLine (int lineNumber)
 
2137
                {
 
2138
                        return Document.GetLine (lineNumber);
 
2139
                }
 
2140
                
 
2141
                public DocumentLine GetLineByOffset (int offset)
 
2142
                {
 
2143
                        return Document.GetLineByOffset (offset);
 
2144
                }
 
2145
                
 
2146
                public int OffsetToLineNumber (int offset)
 
2147
                {
 
2148
                        return Document.OffsetToLineNumber (offset);
 
2149
                }
 
2150
                
 
2151
                public IDisposable OpenUndoGroup()
 
2152
                {
 
2153
                        return Document.OpenUndoGroup ();
 
2154
                }
 
2155
                #endregion
 
2156
                
 
2157
                #region Search & Replace
 
2158
                
 
2159
                bool highlightSearchPattern = false;
 
2160
                
 
2161
                public string SearchPattern {
 
2162
                        get {
 
2163
                                return this.textEditorData.SearchRequest.SearchPattern;
 
2164
                        }
 
2165
                        set {
 
2166
                                if (this.textEditorData.SearchRequest.SearchPattern != value) {
 
2167
                                        this.textEditorData.SearchRequest.SearchPattern = value;
 
2168
                                }
 
2169
                        }
 
2170
                }
 
2171
                
 
2172
                public ISearchEngine SearchEngine {
 
2173
                        get {
 
2174
                                return this.textEditorData.SearchEngine;
 
2175
                        }
 
2176
                        set {
 
2177
                                Debug.Assert (value != null);
 
2178
                                this.textEditorData.SearchEngine = value;
 
2179
                        }
 
2180
                }
 
2181
                
 
2182
                public event EventHandler HighlightSearchPatternChanged;
 
2183
                public bool HighlightSearchPattern {
 
2184
                        get {
 
2185
                                return highlightSearchPattern;
 
2186
                        }
 
2187
                        set {
 
2188
                                if (highlightSearchPattern != value) {
 
2189
                                        this.highlightSearchPattern = value;
 
2190
                                        if (HighlightSearchPatternChanged != null)
 
2191
                                                HighlightSearchPatternChanged (this, EventArgs.Empty);
 
2192
                                        textViewMargin.DisposeLayoutDict ();
 
2193
                                        this.QueueDraw ();
 
2194
                                }
 
2195
                        }
 
2196
                }
 
2197
                
 
2198
                public bool IsCaseSensitive {
 
2199
                        get {
 
2200
                                return this.textEditorData.SearchRequest.CaseSensitive;
 
2201
                        }
 
2202
                        set {
 
2203
                                this.textEditorData.SearchRequest.CaseSensitive = value;
 
2204
                        }
 
2205
                }
 
2206
                
 
2207
                public bool IsWholeWordOnly {
 
2208
                        get {
 
2209
                                return this.textEditorData.SearchRequest.WholeWordOnly;
 
2210
                        }
 
2211
                        
 
2212
                        set {
 
2213
                                this.textEditorData.SearchRequest.WholeWordOnly = value;
 
2214
                        }
 
2215
                }
 
2216
                
 
2217
                public TextSegment SearchRegion {
 
2218
                        get {
 
2219
                                return this.textEditorData.SearchRequest.SearchRegion;
 
2220
                        }
 
2221
                        
 
2222
                        set {
 
2223
                                this.textEditorData.SearchRequest.SearchRegion = value;
 
2224
                        }
 
2225
                }
 
2226
                
 
2227
                public SearchResult SearchForward (int fromOffset)
 
2228
                {
 
2229
                        return textEditorData.SearchForward (fromOffset);
 
2230
                }
 
2231
                
 
2232
                public SearchResult SearchBackward (int fromOffset)
 
2233
                {
 
2234
                        return textEditorData.SearchBackward (fromOffset);
 
2235
                }
 
2236
                
 
2237
                class CaretPulseAnimation : IAnimationDrawer
 
2238
                {
 
2239
                        TextEditor editor;
 
2240
                        
 
2241
                        public double Percent { get; set; }
 
2242
                        
 
2243
                        public Gdk.Rectangle AnimationBounds {
 
2244
                                get {
 
2245
                                        double x = editor.TextViewMargin.caretX;
 
2246
                                        double y = editor.TextViewMargin.caretY;
 
2247
                                        double extend = 100 * 5;
 
2248
                                        int width = (int)(editor.TextViewMargin.charWidth + 2 * extend * editor.Options.Zoom / 2);
 
2249
                                        return new Gdk.Rectangle ((int)(x - extend * editor.Options.Zoom / 2), 
 
2250
                                                                  (int)(y - extend * editor.Options.Zoom),
 
2251
                                                                  width,
 
2252
                                                                  (int)(editor.LineHeight + 2 * extend * editor.Options.Zoom));
 
2253
                                }
 
2254
                        }
 
2255
                        
 
2256
                        public CaretPulseAnimation (TextEditor editor)
 
2257
                        {
 
2258
                                this.editor = editor;
 
2259
                        }
 
2260
                        
 
2261
                        public void Draw (Cairo.Context cr)
 
2262
                        {
 
2263
                                double x = editor.TextViewMargin.caretX;
 
2264
                                double y = editor.TextViewMargin.caretY;
 
2265
                                if (editor.Caret.Mode != CaretMode.Block)
 
2266
                                        x -= editor.TextViewMargin.charWidth / 2;
 
2267
                                cr.Rectangle (editor.TextViewMargin.XOffset, 0, editor.Allocation.Width - editor.TextViewMargin.XOffset, editor.Allocation.Height);
 
2268
                                cr.Clip ();
 
2269
 
 
2270
                                double extend = Percent * 5;
 
2271
                                double width = editor.TextViewMargin.charWidth + 2 * extend * editor.Options.Zoom / 2;
 
2272
                                FoldingScreenbackgroundRenderer.DrawRoundRectangle (cr, true, true, 
 
2273
                                                                                    x - extend * editor.Options.Zoom / 2, 
 
2274
                                                                                    y - extend * editor.Options.Zoom, 
 
2275
                                                                                    System.Math.Min (editor.TextViewMargin.charWidth / 2, width), 
 
2276
                                                                                    width,
 
2277
                                                                                    editor.LineHeight + 2 * extend * editor.Options.Zoom);
 
2278
                                Cairo.Color color = editor.ColorStyle.PlainText.Foreground;
 
2279
                                color.A = 0.8;
 
2280
                                cr.LineWidth = editor.Options.Zoom;
 
2281
                                cr.Color = color;
 
2282
                                cr.Stroke ();
 
2283
                                cr.ResetClip ();
 
2284
                        }
 
2285
                }
 
2286
                
 
2287
                public enum PulseKind {
 
2288
                        In, Out, Bounce
 
2289
                }
 
2290
                
 
2291
                public class RegionPulseAnimation : IAnimationDrawer
 
2292
                {
 
2293
                        TextEditor editor;
 
2294
                        
 
2295
                        public PulseKind Kind { get; set; }
 
2296
                        public double Percent { get; set; }
 
2297
                        
 
2298
                        Gdk.Rectangle region;
 
2299
                        
 
2300
                        public Gdk.Rectangle AnimationBounds {
 
2301
                                get {
 
2302
                                        int x = region.X;
 
2303
                                        int y = region.Y;
 
2304
                                        int animationPosition = (int)(100 * 100);
 
2305
                                        int width = (int)(region.Width + 2 * animationPosition * editor.Options.Zoom / 2);
 
2306
                                        
 
2307
                                        return new Gdk.Rectangle ((int)(x - animationPosition * editor.Options.Zoom / 2), 
 
2308
                                                                  (int)(y - animationPosition * editor.Options.Zoom),
 
2309
                                                                  width,
 
2310
                                                                  (int)(region.Height + 2 * animationPosition * editor.Options.Zoom));
 
2311
                                }
 
2312
                        }
 
2313
                        
 
2314
                        public RegionPulseAnimation (TextEditor editor, Gdk.Point position, Gdk.Size size)
 
2315
                                : this (editor, new Gdk.Rectangle (position, size)) {}
 
2316
                        
 
2317
                        public RegionPulseAnimation (TextEditor editor, Gdk.Rectangle region)
 
2318
                        {
 
2319
                                if (region.X < 0 || region.Y < 0 || region.Width < 0 || region.Height < 0)
 
2320
                                        throw new ArgumentException ("region is invalid");
 
2321
                                
 
2322
                                this.editor = editor;
 
2323
                                this.region = region;
 
2324
                        }
 
2325
                        
 
2326
                        public void Draw (Cairo.Context cr)
 
2327
                        {
 
2328
                                int x = region.X;
 
2329
                                int y = region.Y;
 
2330
                                int animationPosition = (int)(Percent * 100);
 
2331
                                
 
2332
                                cr.Rectangle (editor.TextViewMargin.XOffset, 0, editor.Allocation.Width - editor.TextViewMargin.XOffset, editor.Allocation.Height);
 
2333
                                cr.Clip ();
 
2334
 
 
2335
                                int width = (int)(region.Width + 2 * animationPosition * editor.Options.Zoom / 2);
 
2336
                                FoldingScreenbackgroundRenderer.DrawRoundRectangle (cr, true, true, 
 
2337
                                                                                    (int)(x - animationPosition * editor.Options.Zoom / 2), 
 
2338
                                                                                    (int)(y - animationPosition * editor.Options.Zoom), 
 
2339
                                                                                    System.Math.Min (editor.TextViewMargin.charWidth / 2, width), 
 
2340
                                                                                    width,
 
2341
                                                                                    (int)(region.Height + 2 * animationPosition * editor.Options.Zoom));
 
2342
                                Cairo.Color color = editor.ColorStyle.PlainText.Foreground;
 
2343
                                color.A = 0.8;
 
2344
                                cr.LineWidth = editor.Options.Zoom;
 
2345
                                cr.Color = color;
 
2346
                                cr.Stroke ();
 
2347
                                cr.ResetClip ();
 
2348
                        }
 
2349
                }
 
2350
                
 
2351
                Gdk.Rectangle RangeToRectangle (DocumentLocation start, DocumentLocation end)
 
2352
                {
 
2353
                        if (start.Column < 0 || start.Line < 0 || end.Column < 0 || end.Line < 0)
 
2354
                                return Gdk.Rectangle.Zero;
 
2355
                        
 
2356
                        var startPt = this.LocationToPoint (start);
 
2357
                        var endPt = this.LocationToPoint (end);
 
2358
                        int width = endPt.X - startPt.X;
 
2359
                        
 
2360
                        if (startPt.Y != endPt.Y || startPt.X < 0 || startPt.Y < 0 || width < 0)
 
2361
                                return Gdk.Rectangle.Zero;
 
2362
                        
 
2363
                        return new Gdk.Rectangle (startPt.X, startPt.Y, width, (int)this.LineHeight);
 
2364
                }
 
2365
                
 
2366
                /// <summary>
 
2367
                /// Initiate a pulse at the specified document location
 
2368
                /// </summary>
 
2369
                /// <param name="pulseLocation">
 
2370
                /// A <see cref="DocumentLocation"/>
 
2371
                /// </param>
 
2372
                public void PulseCharacter (DocumentLocation pulseStart)
 
2373
                {
 
2374
                        if (pulseStart.Column < 0 || pulseStart.Line < 0)
 
2375
                                return;
 
2376
                        var rect = RangeToRectangle (pulseStart, new DocumentLocation (pulseStart.Line, pulseStart.Column + 1));
 
2377
                        if (rect.X < 0 || rect.Y < 0 || System.Math.Max (rect.Width, rect.Height) <= 0)
 
2378
                                return;
 
2379
                        StartAnimation (new RegionPulseAnimation (editor, rect) {
 
2380
                                Kind = PulseKind.Bounce
 
2381
                        });
 
2382
                }
 
2383
 
 
2384
                
 
2385
                public SearchResult FindNext (bool setSelection)
 
2386
                {
 
2387
                        SearchResult result = textEditorData.FindNext (setSelection);
 
2388
                        TryToResetHorizontalScrollPosition ();
 
2389
                        AnimateSearchResult (result);
 
2390
                        return result;
 
2391
                }
 
2392
 
 
2393
                public void StartCaretPulseAnimation ()
 
2394
                {
 
2395
                        StartAnimation (new CaretPulseAnimation (editor));
 
2396
                }
 
2397
 
 
2398
                SearchHighlightPopupWindow popupWindow = null;
 
2399
                
 
2400
                public void StopSearchResultAnimation ()
 
2401
                {
 
2402
                        if (popupWindow == null)
 
2403
                                return;
 
2404
                        popupWindow.StopPlaying ();
 
2405
                }
 
2406
                
 
2407
                public void AnimateSearchResult (SearchResult result)
 
2408
                {
 
2409
                        if (!IsComposited || !Options.EnableAnimations || result == null)
 
2410
                                return;
 
2411
                        
 
2412
                        // Don't animate multi line search results
 
2413
                        if (OffsetToLineNumber (result.Segment.Offset) != OffsetToLineNumber (result.Segment.EndOffset))
 
2414
                                return;
 
2415
                        
 
2416
                        TextViewMargin.MainSearchResult = result.Segment;
 
2417
                        if (!TextViewMargin.MainSearchResult.IsInvalid) {
 
2418
                                if (popupWindow != null) {
 
2419
                                        popupWindow.StopPlaying ();
 
2420
                                        popupWindow.Destroy ();
 
2421
                                }
 
2422
                                popupWindow = new SearchHighlightPopupWindow (editor);
 
2423
                                popupWindow.Result = result;
 
2424
                                popupWindow.Popup ();
 
2425
                                popupWindow.Destroyed += delegate {
 
2426
                                        popupWindow = null;
 
2427
                                };
 
2428
                        }
 
2429
                }
 
2430
                
 
2431
                class SearchHighlightPopupWindow : BounceFadePopupWidget
 
2432
                {
 
2433
                        public SearchResult Result {
 
2434
                                get;
 
2435
                                set;
 
2436
                        }
 
2437
                        
 
2438
                        public SearchHighlightPopupWindow (TextEditor editor) : base (editor)
 
2439
                        {
 
2440
                        }
 
2441
                        
 
2442
                        public override void Popup ()
 
2443
                        {
 
2444
                                ExpandWidth = (uint)Editor.LineHeight;
 
2445
                                ExpandHeight = (uint)Editor.LineHeight / 2;
 
2446
                                BounceEasing = Easing.Sine;
 
2447
                                Duration = 150;
 
2448
                                base.Popup ();
 
2449
                        }
 
2450
                        
 
2451
                        protected override void OnAnimationCompleted ()
 
2452
                        {
 
2453
                                base.OnAnimationCompleted ();
 
2454
                                Destroy ();
 
2455
                        }
 
2456
                        
 
2457
                        protected override void OnDestroyed ()
 
2458
                        {
 
2459
                                base.OnDestroyed ();
 
2460
                                if (layout != null)
 
2461
                                        layout.Dispose ();
 
2462
                        }
 
2463
                        
 
2464
                        protected override Cairo.Rectangle CalculateInitialBounds ()
 
2465
                        {
 
2466
                                DocumentLine line = Editor.Document.GetLineByOffset (Result.Offset);
 
2467
                                int lineNr = Editor.Document.OffsetToLineNumber (Result.Offset);
 
2468
                                ISyntaxMode mode = Editor.Document.SyntaxMode != null && Editor.Options.EnableSyntaxHighlighting ? Editor.Document.SyntaxMode : new SyntaxMode (Editor.Document);
 
2469
                                int logicalRulerColumn = line.GetLogicalColumn (Editor.GetTextEditorData (), Editor.Options.RulerColumn);
 
2470
                                var lineLayout = Editor.TextViewMargin.CreateLinePartLayout (mode, line, logicalRulerColumn, line.Offset, line.Length, -1, -1);
 
2471
                                if (lineLayout == null)
 
2472
                                        return new Cairo.Rectangle ();
 
2473
                                
 
2474
                                int l, x1, x2;
 
2475
                                int index = Result.Offset - line.Offset - 1;
 
2476
                                if (index >= 0) {
 
2477
                                        lineLayout.Layout.IndexToLineX (index, true, out l, out x1);
 
2478
                                } else {
 
2479
                                        l = x1 = 0;
 
2480
                                }
 
2481
                                
 
2482
                                index = Result.Offset - line.Offset - 1 + Result.Length;
 
2483
                                if (index >= 0) {
 
2484
                                        lineLayout.Layout.IndexToLineX (index, true, out l, out x2);
 
2485
                                } else {
 
2486
                                        x2 = 0;
 
2487
                                        Console.WriteLine ("Invalid end index :" + index);
 
2488
                                }
 
2489
                                
 
2490
                                double y = Editor.LineToY (lineNr);
 
2491
                                double w = (x2 - x1) / Pango.Scale.PangoScale;
 
2492
                                double x = (x1 / Pango.Scale.PangoScale + Editor.TextViewMargin.XOffset + Editor.TextViewMargin.TextStartPosition);
 
2493
                                var h = Editor.LineHeight;
 
2494
 
 
2495
                                //adjust the width to match TextViewMargin
 
2496
                                w = System.Math.Ceiling (w + 1);
 
2497
 
 
2498
                                //add space for the shadow
 
2499
                                w += shadowOffset;
 
2500
                                h += shadowOffset;
 
2501
 
 
2502
                                return new Cairo.Rectangle (x, y, w, h);
 
2503
                        }
 
2504
 
 
2505
                        const int shadowOffset = 1;
 
2506
 
 
2507
                        Pango.Layout layout = null;
 
2508
 
 
2509
                        protected override void Draw (Cairo.Context cr, Cairo.Rectangle area)
 
2510
                        {
 
2511
                                if (!Editor.Options.UseAntiAliasing)
 
2512
                                        cr.Antialias = Cairo.Antialias.None;
 
2513
                                cr.LineWidth = Editor.Options.Zoom;
 
2514
 
 
2515
                                if (layout == null) {
 
2516
                                        layout = cr.CreateLayout ();
 
2517
                                        layout.FontDescription = Editor.Options.Font;
 
2518
                                        string markup = Editor.GetTextEditorData ().GetMarkup (Result.Offset, Result.Length, true);
 
2519
                                        layout.SetMarkup (markup);
 
2520
                                }
 
2521
 
 
2522
                                // subtract off the shadow again
 
2523
                                var width = area.Width - shadowOffset;
 
2524
                                var height = area.Height - shadowOffset;
 
2525
 
 
2526
                                //from TextViewMargin's actual highlighting
 
2527
                                double corner = System.Math.Min (4, width) * Editor.Options.Zoom;
 
2528
 
 
2529
                                //fill in the highlight rect with solid white to prevent alpha blending artifacts on the corners
 
2530
                                FoldingScreenbackgroundRenderer.DrawRoundRectangle (cr, true, true, 0, 0, corner, width, height);
 
2531
                                cr.Color = new Cairo.Color (1, 1, 1);
 
2532
                                cr.Fill ();
 
2533
 
 
2534
                                //draw the shadow
 
2535
                                FoldingScreenbackgroundRenderer.DrawRoundRectangle (cr, true, true,
 
2536
                                        shadowOffset, shadowOffset, corner, width, height);
 
2537
                                var color = TextViewMargin.DimColor (Editor.ColorStyle.SearchResultMain.Color, 0.3);
 
2538
                                color.A = 0.5 * opacity * opacity;
 
2539
                                cr.Color = color;
 
2540
                                cr.Fill ();
 
2541
 
 
2542
                                //draw the highlight rectangle
 
2543
                                FoldingScreenbackgroundRenderer.DrawRoundRectangle (cr, true, true, 0, 0, corner, width, height);
 
2544
                                using (var gradient = new Cairo.LinearGradient (0, 0, 0, height)) {
 
2545
                                        color = ColorLerp (
 
2546
                                                TextViewMargin.DimColor (Editor.ColorStyle.SearchResultMain.Color, 1.1),
 
2547
                                                Editor.ColorStyle.SearchResultMain.Color,
 
2548
                                                1 - opacity);
 
2549
                                        gradient.AddColorStop (0, color);
 
2550
                                        color = ColorLerp (
 
2551
                                                TextViewMargin.DimColor (Editor.ColorStyle.SearchResultMain.Color, 0.9),
 
2552
                                                Editor.ColorStyle.SearchResultMain.Color,
 
2553
                                                1 - opacity);
 
2554
                                        gradient.AddColorStop (1, color);
 
2555
                                        cr.Pattern = gradient;
 
2556
                                        cr.Fill ();
 
2557
                                }
 
2558
 
 
2559
                                //and finally the text
 
2560
                                cr.Translate (area.X, area.Y);
 
2561
                                cr.Color = new Cairo.Color (0, 0, 0);
 
2562
                                cr.ShowLayout (layout);
 
2563
                        }
 
2564
 
 
2565
                        static Cairo.Color ColorLerp (Cairo.Color from, Cairo.Color to, double scale)
 
2566
                        {
 
2567
                                return new Cairo.Color (
 
2568
                                        Lerp (from.R, to.R, scale),
 
2569
                                        Lerp (from.G, to.G, scale),
 
2570
                                        Lerp (from.B, to.B, scale),
 
2571
                                        Lerp (from.A, to.A, scale)
 
2572
                                );
 
2573
                        }
 
2574
 
 
2575
                        static double Lerp (double from, double to, double scale)
 
2576
                        {
 
2577
                                return from + scale * (to - from);
 
2578
                        }
 
2579
                }
 
2580
                
 
2581
                public SearchResult FindPrevious (bool setSelection)
 
2582
                {
 
2583
                        SearchResult result = textEditorData.FindPrevious (setSelection);
 
2584
                        TryToResetHorizontalScrollPosition ();
 
2585
                        AnimateSearchResult (result);
 
2586
                        return result;
 
2587
                }
 
2588
                
 
2589
                public bool Replace (string withPattern)
 
2590
                {
 
2591
                        return textEditorData.SearchReplace (withPattern, true);
 
2592
                }
 
2593
                
 
2594
                public int ReplaceAll (string withPattern)
 
2595
                {
 
2596
                        return textEditorData.SearchReplaceAll (withPattern);
 
2597
                }
 
2598
                #endregion
 
2599
        
 
2600
                #region Tooltips
 
2601
                [Obsolete("This property has been moved to TextEditorData.  Will be removed in future versions.")]
 
2602
                public IEnumerable<TooltipProvider> TooltipProviders {
 
2603
                        get { return textEditorData.TooltipProviders; }
 
2604
                }
 
2605
 
 
2606
                // Tooltip fields
 
2607
                const int TooltipTimeout = 650;
 
2608
                TooltipItem tipItem;
 
2609
                
 
2610
                int tipX, tipY;
 
2611
                uint tipHideTimeoutId = 0;
 
2612
                uint tipShowTimeoutId = 0;
 
2613
                static Gtk.Window tipWindow;
 
2614
                static TooltipProvider currentTooltipProvider;
 
2615
 
 
2616
                // Data for the next tooltip to be shown
 
2617
                int nextTipOffset = 0;
 
2618
                int nextTipX=0; int nextTipY=0;
 
2619
                Gdk.ModifierType nextTipModifierState = ModifierType.None;
 
2620
                DateTime nextTipScheduledTime; // Time at which we want the tooltip to show
 
2621
                
 
2622
                void ShowTooltip (Gdk.ModifierType modifierState)
 
2623
                {
 
2624
                        if (mx < TextViewMargin.XOffset + TextViewMargin.TextStartPosition) {
 
2625
                                HideTooltip ();
 
2626
                                return;
 
2627
                        }
 
2628
 
 
2629
                        var loc = PointToLocation (mx, my, true);
 
2630
                        if (loc.IsEmpty) {
 
2631
                                HideTooltip ();
 
2632
                                return;
 
2633
                        }
 
2634
 
 
2635
                        // Hide editor tooltips for text marker extended regions (message bubbles)
 
2636
                        double y = LineToY (loc.Line);
 
2637
                        if (y + LineHeight < my) {
 
2638
                                HideTooltip ();
 
2639
                                return;
 
2640
                        }
 
2641
                        
 
2642
                        ShowTooltip (modifierState, 
 
2643
                                     Document.LocationToOffset (loc),
 
2644
                                     (int)mx,
 
2645
                                     (int)my);
 
2646
                }
 
2647
                
 
2648
                void ShowTooltip (Gdk.ModifierType modifierState, int offset, int xloc, int yloc)
 
2649
                {
 
2650
                        CancelScheduledShow ();
 
2651
                        if (textEditorData.SuppressTooltips)
 
2652
                                return;
 
2653
                        if (tipWindow != null && currentTooltipProvider != null && currentTooltipProvider.IsInteractive (editor, tipWindow)) {
 
2654
                                int wx, ww, wh;
 
2655
                                tipWindow.GetSize (out ww, out wh);
 
2656
                                wx = tipX - ww/2;
 
2657
                                if (xloc >= wx && xloc < wx + ww && yloc >= tipY && yloc < tipY + 20 + wh)
 
2658
                                        return;
 
2659
                        }
 
2660
                        if (tipItem != null && !tipItem.ItemSegment.IsInvalid && !tipItem.ItemSegment.Contains (offset)) 
 
2661
                                HideTooltip ();
 
2662
                        nextTipX = xloc;
 
2663
                        nextTipY = yloc;
 
2664
                        nextTipOffset = offset;
 
2665
                        nextTipModifierState = modifierState;
 
2666
                        nextTipScheduledTime = DateTime.Now + TimeSpan.FromMilliseconds (TooltipTimeout);
 
2667
 
 
2668
                        // If a tooltip is already scheduled, there is no need to create a new timer.
 
2669
                        if (tipShowTimeoutId == 0)
 
2670
                                tipShowTimeoutId = GLib.Timeout.Add (TooltipTimeout, TooltipTimer);
 
2671
                }
 
2672
                
 
2673
                bool TooltipTimer ()
 
2674
                {
 
2675
                        // This timer can't be reused, so reset the var now
 
2676
                        tipShowTimeoutId = 0;
 
2677
                        
 
2678
                        // Cancelled?
 
2679
                        if (nextTipOffset == -1)
 
2680
                                return false;
 
2681
                        
 
2682
                        int remainingMs = (int) (nextTipScheduledTime - DateTime.Now).TotalMilliseconds;
 
2683
                        if (remainingMs > 50) {
 
2684
                                // Still some significant time left. Re-schedule the timer
 
2685
                                tipShowTimeoutId = GLib.Timeout.Add ((uint) remainingMs, TooltipTimer);
 
2686
                                return false;
 
2687
                        }
 
2688
                        
 
2689
                        // Find a provider
 
2690
                        TooltipProvider provider = null;
 
2691
                        TooltipItem item = null;
 
2692
                        
 
2693
                        foreach (TooltipProvider tp in textEditorData.tooltipProviders) {
 
2694
                                try {
 
2695
                                        item = tp.GetItem (editor, nextTipOffset);
 
2696
                                } catch (Exception e) {
 
2697
                                        System.Console.WriteLine ("Exception in tooltip provider " + tp + " GetItem:");
 
2698
                                        System.Console.WriteLine (e);
 
2699
                                }
 
2700
                                if (item != null) {
 
2701
                                        provider = tp;
 
2702
                                        break;
 
2703
                                }
 
2704
                        }
 
2705
                        
 
2706
                        if (item != null) {
 
2707
                                // Tip already being shown for this item?
 
2708
                                if (tipWindow != null && tipItem != null && tipItem.Equals (item)) {
 
2709
                                        CancelScheduledHide ();
 
2710
                                        return false;
 
2711
                                }
 
2712
                                
 
2713
                                tipX = nextTipX;
 
2714
                                tipY = nextTipY;
 
2715
                                tipItem = item;
 
2716
                                Gtk.Window tw = null;
 
2717
                                try {
 
2718
                                        tw = provider.ShowTooltipWindow (editor, nextTipOffset, nextTipModifierState, tipX + (int) TextViewMargin.XOffset, tipY, item);
 
2719
                                } catch (Exception e) {
 
2720
                                        Console.WriteLine ("-------- Exception while creating tooltip:");
 
2721
                                        Console.WriteLine (e);
 
2722
                                }
 
2723
                                if (tw == tipWindow)
 
2724
                                        return false;
 
2725
                                HideTooltip ();
 
2726
                                if (tw == null)
 
2727
                                        return false;
 
2728
                                
 
2729
                                CancelScheduledShow ();
 
2730
 
 
2731
                                tipWindow = tw;
 
2732
                                currentTooltipProvider = provider;
 
2733
                                
 
2734
                                tipShowTimeoutId = 0;
 
2735
                        } else
 
2736
                                HideTooltip ();
 
2737
                        return false;
 
2738
                }
 
2739
                
 
2740
                public void HideTooltip (bool checkMouseOver = true)
 
2741
                {
 
2742
                        CancelScheduledHide ();
 
2743
                        CancelScheduledShow ();
 
2744
                        
 
2745
                        if (tipWindow != null) {
 
2746
                                if (checkMouseOver && tipWindow.GdkWindow != null) {
 
2747
                                        // Don't hide the tooltip window if the mouse pointer is inside it.
 
2748
                                        int x, y, w, h;
 
2749
                                        Gdk.ModifierType m;
 
2750
                                        tipWindow.GdkWindow.GetPointer (out x, out y, out m);
 
2751
                                        tipWindow.GdkWindow.GetSize (out w, out h);
 
2752
                                        if (x >= 0 && y >= 0 && x < w && y < h)
 
2753
                                                return;
 
2754
                                }
 
2755
                                tipWindow.Destroy ();
 
2756
                                tipWindow = null;
 
2757
                        }
 
2758
                }
 
2759
                
 
2760
                void DelayedHideTooltip ()
 
2761
                {
 
2762
                        CancelScheduledHide ();
 
2763
                        tipHideTimeoutId = GLib.Timeout.Add (300, delegate {
 
2764
                                HideTooltip ();
 
2765
                                tipHideTimeoutId = 0;
 
2766
                                return false;
 
2767
                        });
 
2768
                }
 
2769
                
 
2770
                void CancelScheduledHide ()
 
2771
                {
 
2772
                        CancelScheduledShow ();
 
2773
                        if (tipHideTimeoutId != 0) {
 
2774
                                GLib.Source.Remove (tipHideTimeoutId);
 
2775
                                tipHideTimeoutId = 0;
 
2776
                        }
 
2777
                }
 
2778
                
 
2779
                void CancelScheduledShow ()
 
2780
                {
 
2781
                        // Don't remove the timeout handler since it may be reused
 
2782
                        nextTipOffset = -1;
 
2783
                }
 
2784
                
 
2785
                void OnDocumentStateChanged (object s, EventArgs a)
 
2786
                {
 
2787
                        HideTooltip ();
 
2788
                }
 
2789
                
 
2790
                void OnTextSet (object sender, EventArgs e)
 
2791
                {
 
2792
                        DocumentLine longest = longestLine;
 
2793
                        foreach (DocumentLine line in Document.Lines) {
 
2794
                                if (longest == null || line.Length > longest.Length)
 
2795
                                        longest = line;
 
2796
                        }
 
2797
                        if (longest != longestLine) {
 
2798
                                int width = (int)(textViewMargin.GetLayout (longest).PangoWidth / Pango.Scale.PangoScale);
 
2799
                                
 
2800
                                if (width > this.longestLineWidth) {
 
2801
                                        this.longestLineWidth = width;
 
2802
                                        this.longestLine = longest;
 
2803
                                }
 
2804
                        }
 
2805
                }
 
2806
                #endregion
 
2807
                
 
2808
                #region Coordinate transformation
 
2809
                public DocumentLocation PointToLocation (double xp, double yp, bool endAtEol = false)
 
2810
                {
 
2811
                        return TextViewMargin.PointToLocation (xp, yp, endAtEol);
 
2812
                }
 
2813
 
 
2814
                public DocumentLocation PointToLocation (Cairo.Point p)
 
2815
                {
 
2816
                        return TextViewMargin.PointToLocation (p);
 
2817
                }
 
2818
                
 
2819
                public DocumentLocation PointToLocation (Cairo.PointD p)
 
2820
                {
 
2821
                        return TextViewMargin.PointToLocation (p);
 
2822
                }
 
2823
 
 
2824
                public Cairo.Point LocationToPoint (DocumentLocation loc)
 
2825
                {
 
2826
                        return TextViewMargin.LocationToPoint (loc);
 
2827
                }
 
2828
 
 
2829
                public Cairo.Point LocationToPoint (int line, int column)
 
2830
                {
 
2831
                        return TextViewMargin.LocationToPoint (line, column);
 
2832
                }
 
2833
                
 
2834
                public Cairo.Point LocationToPoint (int line, int column, bool useAbsoluteCoordinates)
 
2835
                {
 
2836
                        return TextViewMargin.LocationToPoint (line, column, useAbsoluteCoordinates);
 
2837
                }
 
2838
                
 
2839
                public Cairo.Point LocationToPoint (DocumentLocation loc, bool useAbsoluteCoordinates)
 
2840
                {
 
2841
                        return TextViewMargin.LocationToPoint (loc, useAbsoluteCoordinates);
 
2842
                }
 
2843
 
 
2844
                public double ColumnToX (DocumentLine line, int column)
 
2845
                {
 
2846
                        return TextViewMargin.ColumnToX (line, column);
 
2847
                }
 
2848
                
 
2849
                /// <summary>
 
2850
                /// Calculates the line number at line start (in one visual line could be several logical lines be displayed).
 
2851
                /// </summary>
 
2852
                public int YToLine (double yPos)
 
2853
                {
 
2854
                        return TextViewMargin.YToLine (yPos);
 
2855
                }
 
2856
                
 
2857
                public double LineToY (int logicalLine)
 
2858
                {
 
2859
                        return TextViewMargin.LineToY (logicalLine);
 
2860
                }
 
2861
                
 
2862
                public double GetLineHeight (DocumentLine line)
 
2863
                {
 
2864
                        return TextViewMargin.GetLineHeight (line);
 
2865
                }
 
2866
                
 
2867
                public double GetLineHeight (int logicalLineNumber)
 
2868
                {
 
2869
                        return TextViewMargin.GetLineHeight (logicalLineNumber);
 
2870
                }
 
2871
                #endregion
 
2872
                
 
2873
                #region Animation
 
2874
                Stage<Animation> animationStage = new Stage<Animation> ();
 
2875
                List<Animation> actors = new List<Animation> ();
 
2876
                
 
2877
                protected void InitAnimations ()
 
2878
                {
 
2879
                        animationStage.ActorStep += OnAnimationActorStep;
 
2880
                        animationStage.Iteration += OnAnimationIteration;
 
2881
                }
 
2882
                
 
2883
                void DisposeAnimations ()
 
2884
                {
 
2885
                        if (animationStage != null) {
 
2886
                                animationStage.Playing = false;
 
2887
                                animationStage.ActorStep -= OnAnimationActorStep;
 
2888
                                animationStage.Iteration -= OnAnimationIteration;
 
2889
                                animationStage = null;
 
2890
                        }
 
2891
                        
 
2892
                        if (actors != null) {
 
2893
                                foreach (Animation actor in actors) {
 
2894
                                        if (actor is IDisposable)
 
2895
                                                ((IDisposable)actor).Dispose ();
 
2896
                                }
 
2897
                                actors.Clear ();
 
2898
                                actors = null;
 
2899
                        }
 
2900
                }
 
2901
                
 
2902
                Animation StartAnimation (IAnimationDrawer drawer)
 
2903
                {
 
2904
                        return StartAnimation (drawer, 300);
 
2905
                }
 
2906
                
 
2907
                Animation StartAnimation (IAnimationDrawer drawer, uint duration)
 
2908
                {
 
2909
                        return StartAnimation (drawer, duration, Easing.Linear);
 
2910
                }
 
2911
                
 
2912
                Animation StartAnimation (IAnimationDrawer drawer, uint duration, Easing easing)
 
2913
                {
 
2914
                        if (!Options.EnableAnimations)
 
2915
                                return null;
 
2916
                        Animation animation = new Animation (drawer, duration, easing, Blocking.Upstage);
 
2917
                        animationStage.Add (animation, duration);
 
2918
                        actors.Add (animation);
 
2919
                        return animation;
 
2920
                }
 
2921
                
 
2922
                bool OnAnimationActorStep (Actor<Animation> actor)
 
2923
                {
 
2924
                        switch (actor.Target.AnimationState) {
 
2925
                        case AnimationState.Coming:
 
2926
                                actor.Target.Drawer.Percent = actor.Percent;
 
2927
                                if (actor.Expired) {
 
2928
                                        actor.Target.AnimationState = AnimationState.Going;
 
2929
                                        actor.Reset ();
 
2930
                                        return true;
 
2931
                                }
 
2932
                                break;
 
2933
                        case AnimationState.Going:
 
2934
                                if (actor.Expired) {
 
2935
                                        RemoveAnimation (actor.Target);
 
2936
                                        return false;
 
2937
                                }
 
2938
                                actor.Target.Drawer.Percent = 1.0 - actor.Percent;
 
2939
                                break;
 
2940
                        }
 
2941
                        return true;
 
2942
                }
 
2943
                
 
2944
                void RemoveAnimation (Animation animation)
 
2945
                {
 
2946
                        if (animation == null)
 
2947
                                return;
 
2948
                        Rectangle bounds = animation.Drawer.AnimationBounds;
 
2949
                        actors.Remove (animation);
 
2950
                        if (animation is IDisposable)
 
2951
                                ((IDisposable)animation).Dispose ();
 
2952
                        QueueDrawArea (bounds.X, bounds.Y, bounds.Width, bounds.Height);
 
2953
                }
 
2954
                
 
2955
                void OnAnimationIteration (object sender, EventArgs args)
 
2956
                {
 
2957
                        foreach (Animation actor in actors) {
 
2958
                                Rectangle bounds = actor.Drawer.AnimationBounds;
 
2959
                                QueueDrawArea (bounds.X, bounds.Y, bounds.Width, bounds.Height);
 
2960
                        }
 
2961
                }
 
2962
                #endregion
 
2963
                
 
2964
                internal void FireLinkEvent (string link, uint button, ModifierType modifierState)
 
2965
                {
 
2966
                        if (LinkRequest != null)
 
2967
                                LinkRequest (this, new LinkEventArgs (link, button, modifierState));
 
2968
                }
 
2969
                
 
2970
                public event EventHandler<LinkEventArgs> LinkRequest;
 
2971
 
 
2972
                /// <summary>
 
2973
                /// Inserts a margin at the specified list position
 
2974
                /// </summary>
 
2975
                public void InsertMargin (int index, Margin margin)
 
2976
                {
 
2977
                        margins.Insert (index, margin);
 
2978
                        RedrawFromLine (0);
 
2979
                }
 
2980
                
 
2981
                /// <summary>
 
2982
                /// Checks whether the editor has a margin of a given type
 
2983
                /// </summary>
 
2984
                public bool HasMargin (Type marginType)
 
2985
                {
 
2986
                        return margins.Exists((margin) => { return marginType.IsAssignableFrom (margin.GetType ()); });
 
2987
                }
 
2988
                
 
2989
                /// <summary>
 
2990
                /// Gets the first margin of a given type
 
2991
                /// </summary>
 
2992
                public Margin GetMargin (Type marginType)
 
2993
                {
 
2994
                        return margins.Find((margin) => { return marginType.IsAssignableFrom (margin.GetType ()); });
 
2995
                }
 
2996
                bool requestResetCaretBlink = false;
 
2997
                public void RequestResetCaretBlink ()
 
2998
                {
 
2999
                        if (this.IsFocus)
 
3000
                                requestResetCaretBlink = true;
 
3001
                }
 
3002
 
 
3003
                void UpdateLinesOnTextMarkerHeightChange (object sender, LineEventArgs e)
 
3004
                {
 
3005
                        if (!e.Line.Markers.Any (m => m is IExtendingTextLineMarker))
 
3006
                                return;
 
3007
                        var line = e.Line.LineNumber;
 
3008
                        textEditorData.HeightTree.SetLineHeight (line, GetLineHeight (e.Line));
 
3009
                }
 
3010
 
 
3011
                class SetCaret 
 
3012
                {
 
3013
                        TextEditor view;
 
3014
                        int line, column;
 
3015
                        bool highlightCaretLine;
 
3016
                        bool centerCaret;
 
3017
                        
 
3018
                        public SetCaret (TextEditor view, int line, int column, bool highlightCaretLine, bool centerCaret)
 
3019
                        {
 
3020
                                this.view = view;
 
3021
                                this.line = line;
 
3022
                                this.column = column;
 
3023
                                this.highlightCaretLine = highlightCaretLine;
 
3024
                                this.centerCaret = centerCaret;
 
3025
                        }
 
3026
                        
 
3027
                        public void Run (object sender, EventArgs e)
 
3028
                        {
 
3029
                                if (view.IsDisposed)
 
3030
                                        return;
 
3031
                                line = System.Math.Min (line, view.Document.LineCount);
 
3032
                                view.Caret.AutoScrollToCaret = false;
 
3033
                                try {
 
3034
                                        view.Caret.Location = new DocumentLocation (line, column);
 
3035
                                        view.GrabFocus ();
 
3036
                                        if (centerCaret)
 
3037
                                                view.CenterToCaret ();
 
3038
                                        if (view.TextViewMargin.XOffset == 0)
 
3039
                                                view.HAdjustment.Value = 0;
 
3040
                                        view.TextArea.SizeAllocated -= Run;
 
3041
                                } finally {
 
3042
                                        view.Caret.AutoScrollToCaret = true;
 
3043
                                        if (highlightCaretLine) {
 
3044
                                                view.TextViewMargin.HighlightCaretLine = true;
 
3045
                                                view.StartCaretPulseAnimation ();
 
3046
                                        }
 
3047
                                }
 
3048
                        }
 
3049
                }
 
3050
 
 
3051
                public void SetCaretTo (int line, int column)
 
3052
                {
 
3053
                        SetCaretTo (line, column, true);
 
3054
                }
 
3055
                
 
3056
                public void SetCaretTo (int line, int column, bool highlight)
 
3057
                {
 
3058
                        SetCaretTo (line, column, highlight, true);
 
3059
                }
 
3060
 
 
3061
                public void SetCaretTo (int line, int column, bool highlight, bool centerCaret)
 
3062
                {
 
3063
                        if (line < DocumentLocation.MinLine)
 
3064
                                throw new ArgumentException ("line < MinLine");
 
3065
                        if (column < DocumentLocation.MinColumn)
 
3066
                                throw new ArgumentException ("column < MinColumn");
 
3067
                        
 
3068
                        if (!sizeHasBeenAllocated) {
 
3069
                                SetCaret setCaret = new SetCaret (editor, line, column, highlight, centerCaret);
 
3070
                                SizeAllocated += setCaret.Run;
 
3071
                        } else {
 
3072
                                new SetCaret (editor, line, column, highlight, centerCaret).Run (null, null);
 
3073
                        }
 
3074
                }
 
3075
 
 
3076
                #region Container
 
3077
                public override ContainerChild this [Widget w] {
 
3078
                        get {
 
3079
                                return containerChildren.FirstOrDefault (info => info.Child == w || (info.Child is AnimatedWidget && ((AnimatedWidget)info.Child).Widget == w));
 
3080
                        }
 
3081
                }
 
3082
 
 
3083
                public override GLib.GType ChildType ()
 
3084
                {
 
3085
                        return Gtk.Widget.GType;
 
3086
                }
 
3087
                
 
3088
                internal List<TextEditor.EditorContainerChild> containerChildren = new List<TextEditor.EditorContainerChild> ();
 
3089
                
 
3090
                public void AddTopLevelWidget (Gtk.Widget widget, int x, int y)
 
3091
                {
 
3092
                        widget.Parent = this;
 
3093
                        TextEditor.EditorContainerChild info = new TextEditor.EditorContainerChild (this, widget);
 
3094
                        info.X = x;
 
3095
                        info.Y = y;
 
3096
                        containerChildren.Add (info);
 
3097
                        ResizeChild (Allocation, info);
 
3098
                        SetAdjustments ();
 
3099
                }
 
3100
                
 
3101
                public void MoveTopLevelWidget (Gtk.Widget widget, int x, int y)
 
3102
                {
 
3103
                        foreach (var info in containerChildren.ToArray ()) {
 
3104
                                if (info.Child == widget || (info.Child is AnimatedWidget && ((AnimatedWidget)info.Child).Widget == widget)) {
 
3105
                                        if (info.X == x && info.Y == y)
 
3106
                                                break;
 
3107
                                        info.X = x;
 
3108
                                        info.Y = y;
 
3109
                                        if (widget.Visible)
 
3110
                                                ResizeChild (Allocation, info);
 
3111
                                        break;
 
3112
                                }
 
3113
                        }
 
3114
                        SetAdjustments ();
 
3115
                }
 
3116
 
 
3117
                /// <summary>
 
3118
                /// Returns the position of an embedded widget
 
3119
                /// </summary>
 
3120
                public void GetTopLevelWidgetPosition (Gtk.Widget widget, out int x, out int y)
 
3121
                {
 
3122
                        foreach (var info in containerChildren.ToArray ()) {
 
3123
                                if (info.Child == widget || (info.Child is AnimatedWidget && ((AnimatedWidget)info.Child).Widget == widget)) {
 
3124
                                        x = info.X;
 
3125
                                        y = info.Y;
 
3126
                                        return;
 
3127
                                }
 
3128
                        }
 
3129
                        x = y = 0;
 
3130
                }
 
3131
                
 
3132
                public void MoveToTop (Gtk.Widget widget)
 
3133
                {
 
3134
                        var editorContainerChild = containerChildren.FirstOrDefault (c => c.Child == widget);
 
3135
                        if (editorContainerChild == null)
 
3136
                                throw new Exception ("child " + widget + " not found.");
 
3137
                        var newChilds = containerChildren.Where (child => child != editorContainerChild).ToList ();
 
3138
                        newChilds.Add (editorContainerChild);
 
3139
                        this.containerChildren = newChilds;
 
3140
                        widget.GdkWindow.Raise ();
 
3141
                }
 
3142
                
 
3143
                protected override void OnAdded (Widget widget)
 
3144
                {
 
3145
                        AddTopLevelWidget (widget, 0, 0);
 
3146
                }
 
3147
                
 
3148
                protected override void OnRemoved (Widget widget)
 
3149
                {
 
3150
                        foreach (var info in containerChildren.ToArray ()) {
 
3151
                                if (info.Child == widget) {
 
3152
                                        widget.Unparent ();
 
3153
                                        containerChildren.Remove (info);
 
3154
                                        SetAdjustments ();
 
3155
                                        break;
 
3156
                                }
 
3157
                        }
 
3158
                }
 
3159
                
 
3160
                protected override void ForAll (bool include_internals, Gtk.Callback callback)
 
3161
                {
 
3162
                        containerChildren.ForEach (child => callback (child.Child));
 
3163
                }
 
3164
                
 
3165
                protected override void OnMapped ()
 
3166
                {
 
3167
                        WidgetFlags |= WidgetFlags.Mapped;
 
3168
                        // Note: SourceEditorWidget.ShowAutoSaveWarning() might have set TextEditor.Visible to false,
 
3169
                        // in which case we want to not map it (would cause a gtk+ critical error).
 
3170
                        containerChildren.ForEach (child => { if (child.Child.Visible) child.Child.Map (); });
 
3171
                        GdkWindow.Show ();
 
3172
                }
 
3173
                
 
3174
                protected override void OnUnmapped ()
 
3175
                {
 
3176
                        WidgetFlags &= ~WidgetFlags.Mapped;
 
3177
                        
 
3178
                        // We hide the window first so that the user doesn't see widgets disappearing one by one.
 
3179
                        GdkWindow.Hide ();
 
3180
                        
 
3181
                        containerChildren.ForEach (child => child.Child.Unmap ());
 
3182
                }
 
3183
 
 
3184
                void ResizeChild (Rectangle allocation, TextEditor.EditorContainerChild child)
 
3185
                {
 
3186
                        Requisition req = child.Child.SizeRequest ();
 
3187
                        var childRectangle = new Gdk.Rectangle (child.X, child.Y, req.Width, req.Height);
 
3188
                        if (!child.FixedPosition) {
 
3189
//                              double zoom = Options.Zoom;
 
3190
                                childRectangle.X = (int)(child.X /** zoom */- HAdjustment.Value);
 
3191
                                childRectangle.Y = (int)(child.Y /** zoom */- VAdjustment.Value);
 
3192
                        }
 
3193
                        //                      childRectangle.X += allocation.X;
 
3194
                        //                      childRectangle.Y += allocation.Y;
 
3195
                        child.Child.SizeAllocate (childRectangle);
 
3196
                }
 
3197
                
 
3198
                void SetChildrenPositions (Rectangle allocation)
 
3199
                {
 
3200
                        foreach (var child in containerChildren.ToArray ()) {
 
3201
                                ResizeChild (allocation, child);
 
3202
                        }
 
3203
                }
 
3204
                #endregion
 
3205
 
 
3206
        }
 
3207
 
 
3208
        public interface ITextEditorDataProvider
 
3209
        {
 
3210
                TextEditorData GetTextEditorData ();
 
3211
        }
 
3212
        
 
3213
        [Serializable]
 
3214
        public sealed class PaintEventArgs : EventArgs
 
3215
        {
 
3216
                public Cairo.Context Context {
 
3217
                        get;
 
3218
                        set;
 
3219
                }
 
3220
                
 
3221
                public Cairo.Rectangle Area {
 
3222
                        get;
 
3223
                        set;
 
3224
                }
 
3225
                
 
3226
                public PaintEventArgs (Cairo.Context context, Cairo.Rectangle area)
 
3227
                {
 
3228
                        this.Context = context;
 
3229
                        this.Area = area;
 
3230
                }
 
3231
        }
 
3232
}
 
3233
 
 
3234