5
// Mike KrĆ¼ger <mkrueger@novell.com>
7
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
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:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
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.
29
//#define DEBUG_EXPOSE
33
using System.Diagnostics;
34
using System.Collections.Generic;
35
using System.Runtime.InteropServices;
37
using System.Threading;
38
using Mono.TextEditor.Highlighting;
39
using Mono.TextEditor.PopupWindow;
40
using Mono.TextEditor.Theatrics;
45
namespace Mono.TextEditor
47
public class TextArea : Container, ITextEditorDataProvider
49
TextEditorData textEditorData;
51
protected IconMargin iconMargin;
52
protected GutterMargin gutterMargin;
53
// protected DashedLineMargin dashedLineMargin;
54
protected FoldMarkerMargin foldMarkerMargin;
55
protected TextViewMargin textViewMargin;
57
DocumentLine longestLine = null;
58
double longestLineWidth = -1;
60
List<Margin> margins = new List<Margin> ();
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;
75
public TextDocument Document {
77
return textEditorData.Document;
81
public bool IsDisposed {
83
return textEditorData.IsDisposed;
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#).
92
/// <c>true</c> if tabs to spaces should be converted; otherwise, <c>false</c>.
94
public bool TabsToSpaces {
96
return textEditorData.TabsToSpaces;
99
textEditorData.TabsToSpaces = value;
103
public Mono.TextEditor.Caret Caret {
105
return textEditorData.Caret;
109
protected internal IMMulticontext IMContext {
110
get { return imContext; }
113
public MenuItem CreateInputMethodMenuItem (string label)
115
if (GtkWorkarounds.GtkMinorVersion >= 16) {
116
bool showMenu = (bool) GtkWorkarounds.GetProperty (Settings, "gtk-show-input-method-menu").Val;
120
MenuItem imContextMenuItem = new MenuItem (label);
121
Menu imContextMenu = new Menu ();
122
imContextMenuItem.Submenu = imContextMenu;
123
IMContext.AppendMenuitems (imContextMenu);
124
return imContextMenuItem;
127
[DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)]
128
static extern void gtk_im_multicontext_set_context_id (IntPtr context, string context_id);
130
[DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)]
131
static extern string gtk_im_multicontext_get_context_id (IntPtr context);
133
[GLib.Property ("im-module")]
134
public string IMModule {
136
if (GtkWorkarounds.GtkMinorVersion < 16 || imContext == null)
138
return gtk_im_multicontext_get_context_id (imContext.Handle);
141
if (GtkWorkarounds.GtkMinorVersion < 16 || imContext == null)
143
gtk_im_multicontext_set_context_id (imContext.Handle, value);
147
public ITextEditorOptions Options {
149
return textEditorData.Options;
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);
163
public string FileName {
165
return Document.FileName;
169
public string MimeType {
171
return Document.MimeType;
175
void HandleTextEditorDataDocumentMarkerChange (object sender, TextMarkerEvent e)
177
if (e.TextMarker is IExtendingTextLineMarker) {
178
int lineNumber = e.Line.LineNumber;
179
if (lineNumber <= LineCount) {
181
textEditorData.HeightTree.SetLineHeight (lineNumber, GetLineHeight (e.Line));
182
} catch (Exception ex) {
183
Console.WriteLine (ex);
189
void HAdjustmentValueChanged (object sender, EventArgs args)
191
var alloc = this.Allocation;
192
alloc.X = alloc.Y = 0;
194
HAdjustmentValueChanged ();
197
protected virtual void HAdjustmentValueChanged ()
200
double value = this.textEditorData.HAdjustment.Value;
201
if (value != System.Math.Round (value)) {
202
this.textEditorData.HAdjustment.Value = System.Math.Round (value);
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);
211
void VAdjustmentValueChanged (object sender, EventArgs args)
213
var alloc = this.Allocation;
214
alloc.X = alloc.Y = 0;
216
VAdjustmentValueChanged ();
217
SetChildrenPositions (alloc);
220
protected virtual void VAdjustmentValueChanged ()
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);
230
FireMotionEvent (mx + textViewMargin.XOffset, my, lastState);
232
double delta = value - this.oldVadjustment;
233
oldVadjustment = value;
234
TextViewMargin.caretY -= delta;
236
if (System.Math.Abs (delta) >= Allocation.Height - this.LineHeight * 2 || this.TextViewMargin.InSelectionDrag) {
238
OnVScroll (EventArgs.Empty);
242
if (GdkWindow != null)
243
GdkWindow.Scroll (0, (int)-delta);
245
OnVScroll (EventArgs.Empty);
248
protected virtual void OnVScroll (EventArgs e)
250
EventHandler handler = this.VScroll;
255
protected virtual void OnHScroll (EventArgs e)
257
EventHandler handler = this.HScroll;
262
public event EventHandler VScroll;
263
public event EventHandler HScroll;
265
void UnregisterAdjustments ()
267
if (textEditorData.HAdjustment != null)
268
textEditorData.HAdjustment.ValueChanged -= HAdjustmentValueChanged;
269
if (textEditorData.VAdjustment != null)
270
textEditorData.VAdjustment.ValueChanged -= VAdjustmentValueChanged;
273
internal void SetTextEditorScrollAdjustments (Adjustment hAdjustement, Adjustment vAdjustement)
275
if (textEditorData == null)
277
UnregisterAdjustments ();
279
if (hAdjustement == null || vAdjustement == null)
282
this.textEditorData.HAdjustment = hAdjustement;
283
this.textEditorData.VAdjustment = vAdjustement;
285
this.textEditorData.HAdjustment.ValueChanged += HAdjustmentValueChanged;
286
this.textEditorData.VAdjustment.ValueChanged += VAdjustmentValueChanged;
289
internal TextArea (TextDocument doc, ITextEditorOptions options, EditMode initialMode)
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;
296
// This is required to properly handle resizing and rendering of children
297
ResizeMode = ResizeMode.Queue;
301
internal void Initialize (TextEditor editor, TextDocument doc, ITextEditorOptions options, EditMode initialMode)
304
throw new ArgumentNullException ("doc");
305
this.editor = editor;
306
textEditorData = new TextEditorData (doc);
307
textEditorData.RecenterEditor += delegate {
309
StartCaretPulseAnimation ();
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;
317
textEditorData.CurrentMode = initialMode;
319
this.textEditorData.Options = options ?? TextEditorOptions.DefaultOptions;
322
textEditorData.Parent = editor;
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);
330
margins.Add (iconMargin);
331
margins.Add (gutterMargin);
332
margins.Add (foldMarkerMargin);
333
// margins.Add (dashedLineMargin);
335
margins.Add (textViewMargin);
336
this.textEditorData.SelectionChanged += TextEditorDataSelectionChanged;
337
this.textEditorData.UpdateAdjustmentsRequested += TextEditorDatahandleUpdateAdjustmentsRequested;
338
Document.DocumentUpdated += DocumentUpdatedHandler;
340
this.textEditorData.Options.Changed += OptionsChanged;
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);
347
imContext = new IMMulticontext ();
348
imContext.Commit += IMCommit;
350
imContext.UsePreedit = true;
351
imContext.PreeditChanged += PreeditStringChanged;
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);
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);
367
using (Pixmap inv = new Pixmap (null, 1, 1, 1)) {
368
invisibleCursor = new Cursor (inv, inv, Gdk.Color.Zero, Gdk.Color.Zero, 0, 0);
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);
377
this.Document.Splitter.LineChanged += delegate(object sender, LineEventArgs e) {
378
RedrawLine (e.Line.LineNumber);
382
TextEditorAccessible.Factory.Init (this);
385
if (GtkGestures.IsSupported) {
386
this.AddGestureMagnifyHandler ((sender, args) => {
387
Options.Zoom += Options.Zoom * (args.Magnification / 4d);
390
OptionsChanged (this, EventArgs.Empty);
393
public void RunAction (Action<TextEditorData> action)
396
action (GetTextEditorData ());
397
} catch (Exception e) {
398
Console.WriteLine ("Error while executing " + action + " :" + e);
402
void HandleDocumenthandleEndUndo (object sender, TextDocument.UndoOperationEventArgs e)
404
if (this.Document.HeightChanged) {
405
this.Document.HeightChanged = false;
410
void TextEditorDatahandleUpdateAdjustmentsRequested (object sender, EventArgs e)
416
public void ShowListWindow<T> (ListWindow<T> window, DocumentLocation loc)
418
var p = LocationToPoint (loc);
420
GdkWindow.GetOrigin (out ox, out oy);
422
window.Move (ox + p.X - window.TextOffset , oy + p.Y + (int)LineHeight);
426
internal int preeditOffset, preeditLine, preeditCursorCharIndex;
427
internal string preeditString;
428
internal Pango.AttrList preeditAttrs;
429
internal bool preeditHeightChange;
431
internal bool ContainsPreedit (int line, int length)
433
if (string.IsNullOrEmpty (preeditString))
436
return line <= preeditOffset && preeditOffset <= line + length;
439
void PreeditStringChanged (object sender, EventArgs e)
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;
447
using (var preeditLayout = PangoUtil.CreateLayout (this)) {
448
preeditLayout.SetText (preeditString);
449
preeditLayout.Attributes = preeditAttrs;
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;
461
preeditString = null;
463
preeditCursorCharIndex = 0;
464
if (preeditHeightChange) {
465
preeditHeightChange = false;
466
textEditorData.HeightTree.Rebuild ();
470
this.textViewMargin.ForceInvalidateLine (preeditLine);
471
this.textEditorData.Document.CommitLineUpdate (preeditLine);
474
void CaretPositionChanged (object sender, DocumentLocationEventArgs args)
479
if (Caret.AutoScrollToCaret && HasFocus)
482
// Rectangle rectangle = textViewMargin.GetCaretRectangle (Caret.Mode);
483
RequestResetCaretBlink ();
485
textEditorData.CurrentMode.InternalCaretPositionChanged (textEditorData.Parent, textEditorData);
487
if (!IsSomethingSelected) {
488
if (/*Options.HighlightCaretLine && */args.Location.Line != Caret.Line)
489
RedrawMarginLine (TextViewMargin, args.Location.Line);
490
RedrawMarginLine (TextViewMargin, Caret.Line);
494
Selection oldSelection = Selection.Empty;
495
void TextEditorDataSelectionChanged (object sender, EventArgs args)
497
if (IsSomethingSelected) {
498
var selectionRange = MainSelection.GetSelectionRange (textEditorData);
499
if (selectionRange.Offset >= 0 && selectionRange.EndOffset < Document.TextLength) {
500
ClipboardActions.CopyToPrimary (this.textEditorData);
502
ClipboardActions.ClearPrimary ();
505
ClipboardActions.ClearPrimary ();
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)));
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) {
530
} else if (endLine != 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);
545
if (selection.IsEmpty) {
548
} else if (oldSelection.IsEmpty) {
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));
560
oldSelection = selection;
561
OnSelectionChanged (EventArgs.Empty);
564
internal void ResetIMContext ()
566
if (imContextNeedsReset) {
568
imContextNeedsReset = false;
572
void IMCommit (object sender, Gtk.CommitArgs ca)
574
if (!IsRealized || !IsFocus)
577
//this, if anywhere, is where we should handle UCS4 conversions
578
for (int i = 0; i < ca.Str.Length; i++) {
580
if (char.IsHighSurrogate (ca.Str, i)) {
581
utf32Char = char.ConvertToUtf32 (ca.Str, i);
584
utf32Char = (int)ca.Str [i];
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);
591
editor.OnIMProcessedKeyPressEvent ((Gdk.Key)0, (uint)utf32Char, Gdk.ModifierType.None);
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;
602
protected override bool OnFocusInEvent (EventFocus evnt)
604
var result = base.OnFocusInEvent (evnt);
605
imContextNeedsReset = true;
606
IMContext.FocusIn ();
607
RequestResetCaretBlink ();
608
Document.CommitLineUpdate (Caret.Line);
612
uint focusOutTimerId = 0;
613
void RemoveFocusOutTimerId ()
615
if (focusOutTimerId == 0)
617
GLib.Source.Remove (focusOutTimerId);
621
protected override bool OnFocusOutEvent (EventFocus evnt)
623
var result = base.OnFocusOutEvent (evnt);
624
imContextNeedsReset = true;
625
imContext.FocusOut ();
626
RemoveFocusOutTimerId ();
628
if (tipWindow != null && currentTooltipProvider != null) {
629
if (!currentTooltipProvider.IsInteractive (textEditorData.Parent, tipWindow))
630
DelayedHideTooltip ();
635
TextViewMargin.StopCaretThread ();
636
Document.CommitLineUpdate (Caret.Line);
640
protected override void OnRealized ()
642
WidgetFlags |= WidgetFlags.Realized;
643
WindowAttr attributes = new WindowAttr () {
644
WindowType = Gdk.WindowType.Child,
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,
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);
661
imContext.ClientWindow = this.GdkWindow;
662
Caret.PositionChanged += CaretPositionChanged;
665
protected override void OnUnrealized ()
667
imContext.ClientWindow = null;
668
CancelScheduledHide ();
669
base.OnUnrealized ();
672
void DocumentUpdatedHandler (object sender, EventArgs args)
674
foreach (DocumentUpdateRequest request in Document.UpdateRequests) {
675
request.Update (textEditorData.Parent);
679
public event EventHandler EditorOptionsChanged;
681
protected virtual void OptionsChanged (object sender, EventArgs args)
685
if (currentStyleName != Options.ColorScheme) {
686
currentStyleName = Options.ColorScheme;
687
this.textEditorData.ColorStyle = Options.GetColorStyle ();
688
SetWidgetBgFromStyle ();
691
iconMargin.IsVisible = Options.ShowIconMargin;
692
gutterMargin.IsVisible = Options.ShowLineNumberMargin;
693
foldMarkerMargin.IsVisible = Options.ShowFoldMargin || Options.EnableQuickDiff;
694
// dashedLineMargin.IsVisible = foldMarkerMargin.IsVisible || gutterMargin.IsVisible;
696
if (EditorOptionsChanged != null)
697
EditorOptionsChanged (this, args);
699
textViewMargin.OptionsChanged ();
700
foreach (Margin margin in this.margins) {
701
if (margin == textViewMargin)
703
margin.OptionsChanged ();
705
SetAdjustments (Allocation);
706
textEditorData.HeightTree.Rebuild ();
710
void SetWidgetBgFromStyle ()
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
718
this.ModifyBg (StateType.Normal, (HslColor)this.textEditorData.ColorStyle.PlainText.Background);
719
settingWidgetBg = false;
723
bool settingWidgetBg = false;
724
protected override void OnStyleSet (Gtk.Style previous_style)
726
base.OnStyleSet (previous_style);
727
if (!settingWidgetBg && textEditorData.ColorStyle != null) {
728
// textEditorData.ColorStyle.UpdateFromGtkStyle (this.Style);
729
SetWidgetBgFromStyle ();
733
protected override bool OnVisibilityNotifyEvent (EventVisibility evnt)
735
if (evnt.State == VisibilityState.FullyObscured)
737
return base.OnVisibilityNotifyEvent (evnt);
739
protected override void OnDestroyed ()
741
if (popupWindow != null)
742
popupWindow.Destroy ();
745
Document.EndUndo -= HandleDocumenthandleEndUndo;
746
Document.TextReplaced -= OnDocumentStateChanged;
747
Document.TextSet -= OnTextSet;
748
Document.LineChanged -= UpdateLinesOnTextMarkerHeightChange;
749
Document.MarkerAdded -= HandleTextEditorDataDocumentMarkerChange;
750
Document.MarkerRemoved -= HandleTextEditorDataDocumentMarkerChange;
752
DisposeAnimations ();
754
RemoveFocusOutTimerId ();
755
RemoveScrollWindowTimer ();
756
if (invisibleCursor != null)
757
invisibleCursor.Dispose ();
759
Caret.PositionChanged -= CaretPositionChanged;
761
Document.DocumentUpdated -= DocumentUpdatedHandler;
762
if (textEditorData.Options != null)
763
textEditorData.Options.Changed -= OptionsChanged;
765
if (imContext != null){
767
imContext = imContext.Kill (x => x.Commit -= IMCommit);
770
UnregisterAdjustments ();
772
foreach (Margin margin in this.margins) {
773
if (margin is IDisposable)
774
((IDisposable)margin).Dispose ();
776
textEditorData.ClearTooltipProviders ();
778
this.textEditorData.SelectionChanged -= TextEditorDataSelectionChanged;
779
this.textEditorData.Dispose ();
784
[Obsolete("This method has been moved to TextEditorData. Will be removed in future versions.")]
785
public void ClearTooltipProviders ()
787
textEditorData.ClearTooltipProviders ();
790
[Obsolete("This method has been moved to TextEditorData. Will be removed in future versions.")]
791
public void AddTooltipProvider (TooltipProvider provider)
793
textEditorData.AddTooltipProvider (provider);
796
[Obsolete("This method has been moved to TextEditorData. Will be removed in future versions.")]
797
public void RemoveTooltipProvider (TooltipProvider provider)
799
textEditorData.RemoveTooltipProvider (provider);
802
internal void RedrawMargin (Margin margin)
806
QueueDrawArea ((int)margin.XOffset, 0, GetMarginWidth (margin), this.Allocation.Height);
809
public void RedrawMarginLine (Margin margin, int logicalLine)
814
double y = LineToY (logicalLine) - this.textEditorData.VAdjustment.Value;
815
double h = GetLineHeight (logicalLine);
818
QueueDrawArea ((int)margin.XOffset, (int)y, (int)GetMarginWidth (margin), (int)h);
821
int GetMarginWidth (Margin margin)
823
if (margin.Width < 0)
824
return Allocation.Width - (int)margin.XOffset;
825
return (int)margin.Width;
828
internal void RedrawLine (int logicalLine)
830
if (isDisposed || logicalLine > LineCount || logicalLine < DocumentLocation.MinLine)
832
double y = LineToY (logicalLine) - this.textEditorData.VAdjustment.Value;
833
double h = GetLineHeight (logicalLine);
836
QueueDrawArea (0, (int)y, this.Allocation.Width, (int)h);
839
public new void QueueDrawArea (int x, int y, int w, int h)
841
if (GdkWindow != null) {
842
GdkWindow.InvalidateRect (new Rectangle (x, y, w, h), false);
844
Console.WriteLine ("invalidated {0},{1} {2}x{3}", x, y, w, h);
849
public new void QueueDraw ()
853
Console.WriteLine ("invalidated entire widget");
857
internal void RedrawPosition (int logicalLine, int logicalColumn)
861
// Console.WriteLine ("Redraw position: logicalLine={0}, logicalColumn={1}", logicalLine, logicalColumn);
862
RedrawLine (logicalLine);
865
public void RedrawMarginLines (Margin margin, int start, int end)
871
double visualStart = -this.textEditorData.VAdjustment.Value + LineToY (start);
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));
878
internal void RedrawLines (int start, int end)
880
// Console.WriteLine ("redraw lines: start={0}, end={1}", start, end);
885
double visualStart = -this.textEditorData.VAdjustment.Value + LineToY (start);
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));
892
public void RedrawFromLine (int logicalLine)
894
// Console.WriteLine ("Redraw from line: logicalLine={0}", logicalLine);
897
int y = System.Math.Max (0, (int)(-this.textEditorData.VAdjustment.Value + LineToY (logicalLine)));
899
this.Allocation.Width, this.Allocation.Height - y);
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)
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 ();
915
bool IMFilterKeyPress (Gdk.EventKey evt, Gdk.Key mappedKey, uint mappedChar, Gdk.ModifierType mappedModifiers)
917
if (lastIMEvent == evt)
920
if (evt.Type == EventType.KeyPress) {
922
lastIMEventMappedChar = mappedChar;
923
lastIMEventMappedKey = mappedKey;
924
lastIMEventMappedModifier = mappedModifiers;
927
if (imContext.FilterKeypress (evt)) {
928
imContextNeedsReset = true;
935
Gdk.Cursor invisibleCursor;
937
internal void HideMouseCursor ()
939
if (GdkWindow != null)
940
GdkWindow.Cursor = invisibleCursor;
943
protected override bool OnKeyPressEvent (Gdk.EventKey evt)
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))) {
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);
961
if (key == Gdk.Key.F2 && textViewMargin.IsCodeSegmentPreviewWindowShown) {
962
textViewMargin.OpenCodeSegmentEditor ();
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;
970
uint unicodeChar = Gdk.Keyval.ToUnicode (keyVal);
972
if (CurrentMode.WantsToPreemptIM || CurrentMode.PreemptIM (key, unicodeChar, mod)) {
974
//FIXME: should call base.OnKeyPressEvent when SimulateKeyPress didn't handle the event
975
SimulateKeyPress (key, unicodeChar, mod);
978
bool filter = IMFilterKeyPress (evt, key, unicodeChar, mod);
982
//FIXME: OnIMProcessedKeyPressEvent should return false when it didn't handle the event
983
if (editor.OnIMProcessedKeyPressEvent (key, unicodeChar, mod))
986
return base.OnKeyPressEvent (evt);
990
protected override bool OnKeyReleaseEvent (EventKey evnt)
992
if (IMFilterKeyPress (evnt, 0, 0, ModifierType.None)) {
993
imContextNeedsReset = true;
998
uint mouseButtonPressed = 0;
1000
double pressPositionX, pressPositionY;
1001
protected override bool OnButtonPressEvent (Gdk.EventButton e)
1003
if (overChildWidget)
1006
pressPositionX = e.X;
1007
pressPositionY = e.Y;
1008
base.IsFocus = true;
1011
if (lastTime != e.Time) {// filter double clicks
1012
if (e.Type == EventType.TwoButtonPress) {
1017
mouseButtonPressed = e.Button;
1019
Margin margin = GetMarginAtX (e.X, out startPos);
1020
if (margin == textViewMargin) {
1022
if (DoPopupMenu != null && e.TriggersContextMenu ()) {
1023
DoClickedPopupMenu (e);
1028
margin.MousePressed (new MarginMouseEventArgs (textEditorData.Parent, e, e.Button, e.X - startPos, e.Y, e.State));
1030
return base.OnButtonPressEvent (e);
1033
bool DoClickedPopupMenu (Gdk.EventButton e)
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;
1044
this.ResetMouseState ();
1050
public Action<Gdk.EventButton> DoPopupMenu { get; set; }
1052
protected override bool OnPopupMenu ()
1054
if (DoPopupMenu != null) {
1058
return base.OnPopupMenu ();
1061
public Margin LockedMargin {
1066
Margin GetMarginAtX (double x, out double startingPos)
1069
foreach (Margin margin in this.margins) {
1070
if (!margin.IsVisible)
1072
if (LockedMargin != null) {
1073
if (LockedMargin == margin) {
1078
if (curX <= x && (x <= curX + margin.Width || margin.Width < 0)) {
1083
curX += margin.Width;
1089
protected override bool OnButtonReleaseEvent (EventButton e)
1091
RemoveScrollWindowTimer ();
1094
if (DoPopupMenu != null && e.IsContextMenuButton ()) {
1099
Margin margin = GetMarginAtX (e.X, out startPos);
1101
margin.MouseReleased (new MarginMouseEventArgs (textEditorData.Parent, e, e.Button, e.X - startPos, e.Y, e.State));
1103
return base.OnButtonReleaseEvent (e);
1106
protected void ResetMouseState ()
1108
mouseButtonPressed = 0;
1109
textViewMargin.inDrag = false;
1110
textViewMargin.InSelectionDrag = false;
1113
bool dragOver = false;
1114
ClipboardActions.CopyOperation dragContents = null;
1115
DocumentLocation defaultCaretPos, dragCaretPos;
1116
Selection selection = Selection.Empty;
1118
public bool IsInDrag {
1124
public void CaretToDragCaretPosition ()
1126
Caret.Location = defaultCaretPos = dragCaretPos;
1129
protected override void OnDragLeave (DragContext context, uint time_)
1132
Caret.PreserveSelection = true;
1133
Caret.Location = defaultCaretPos;
1134
Caret.PreserveSelection = false;
1138
base.OnDragLeave (context, time_);
1141
protected override void OnDragDataGet (DragContext context, SelectionData selection_data, uint info, uint time_)
1143
if (this.dragContents != null) {
1144
this.dragContents.SetData (selection_data, info);
1145
this.dragContents = null;
1147
base.OnDragDataGet (context, selection_data, info, time_);
1150
protected override void OnDragDataReceived (DragContext context, int x, int y, SelectionData selection_data, uint info, uint time_)
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;
1163
selection = Selection.Empty;
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);
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));
1182
mouseButtonPressed = 0;
1184
base.OnDragDataReceived (context, x, y, selection_data, info, time_);
1187
protected override bool OnDragMotion (DragContext context, int x, int y, uint time)
1192
defaultCaretPos = Caret.Location;
1195
DocumentLocation oldLocation = Caret.Location;
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;
1204
Gdk.Drag.Status (context, (context.Actions & DragAction.Move) == DragAction.Move ? DragAction.Move : DragAction.Copy, time);
1205
Caret.Location = dragCaretPos;
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);
1214
Margin oldMargin = null;
1215
bool overChildWidget;
1217
public event EventHandler BeginHover;
1219
protected virtual void OnBeginHover (EventArgs e)
1221
var handler = BeginHover;
1222
if (handler != null)
1226
protected override bool OnMotionNotifyEvent (Gdk.EventMotion e)
1228
OnBeginHover (EventArgs.Empty);
1230
// The coordinates have to be properly adjusted to the origin since
1231
// the event may come from a child widget
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));
1238
RemoveScrollWindowTimer ();
1239
Gdk.ModifierType mod = e.State;
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);
1250
selection = MainSelection;
1251
textViewMargin.inDrag = false;
1253
FireMotionEvent (x, y, mod);
1254
if (mouseButtonPressed != 0) {
1255
UpdateScrollWindowTimer (x, y, mod);
1258
} catch (Exception ex) {
1259
GLib.ExceptionManager.RaiseUnhandledException (ex, false);
1261
return base.OnMotionNotifyEvent (e);
1264
uint scrollWindowTimer = 0;
1265
double scrollWindowTimer_x;
1266
double scrollWindowTimer_y;
1267
Gdk.ModifierType scrollWindowTimer_mod;
1269
void UpdateScrollWindowTimer (double x, double y, Gdk.ModifierType mod)
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);
1282
void RemoveScrollWindowTimer ()
1284
if (scrollWindowTimer != 0) {
1285
GLib.Source.Remove (scrollWindowTimer);
1287
scrollWindowTimer = 0;
1291
Gdk.ModifierType lastState = ModifierType.None;
1293
void FireMotionEvent (double x, double y, Gdk.ModifierType state)
1296
mx = x - textViewMargin.XOffset;
1299
ShowTooltip (state);
1303
if (textViewMargin.InSelectionDrag) {
1304
margin = textViewMargin;
1305
startPos = textViewMargin.XOffset;
1307
margin = GetMarginAtX (x, out startPos);
1308
if (margin != null && GdkWindow != null) {
1309
if (!overChildWidget)
1310
GdkWindow.Cursor = margin.MarginCursor;
1312
// Set the default cursor when the mouse is over an embedded widget
1313
GdkWindow.Cursor = null;
1318
if (oldMargin != margin && oldMargin != null)
1319
oldMargin.MouseLeft ();
1322
margin.MouseHover (new MarginMouseEventArgs (textEditorData.Parent, EventType.MotionNotify,
1323
mouseButtonPressed, x - startPos, y, state));
1327
#region CustomDrag (for getting dnd data from toolbox items for example)
1329
Gtk.Widget customSource;
1330
public void BeginDrag (string text, Gtk.Widget source, DragContext context)
1333
customSource = source;
1334
source.DragDataGet += CustomDragDataGet;
1335
source.DragEnd += CustomDragEnd;
1337
void CustomDragDataGet (object sender, Gtk.DragDataGetArgs args)
1339
args.SelectionData.Text = customText;
1341
void CustomDragEnd (object sender, Gtk.DragEndArgs args)
1343
customSource.DragDataGet -= CustomDragDataGet;
1344
customSource.DragEnd -= CustomDragEnd;
1345
customSource = null;
1349
bool isMouseTrapped = false;
1351
protected override bool OnEnterNotifyEvent (EventCrossing evnt)
1353
isMouseTrapped = true;
1354
return base.OnEnterNotifyEvent (evnt);
1357
protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing e)
1359
isMouseTrapped = false;
1360
if (tipWindow != null && currentTooltipProvider != null) {
1361
if (!currentTooltipProvider.IsInteractive (textEditorData.Parent, tipWindow))
1362
DelayedHideTooltip ();
1366
textViewMargin.HideCodeSegmentPreviewWindow ();
1368
if (GdkWindow != null)
1369
GdkWindow.Cursor = null;
1370
if (oldMargin != null)
1371
oldMargin.MouseLeft ();
1373
return base.OnLeaveNotifyEvent (e);
1376
public double LineHeight {
1378
return this.textEditorData.LineHeight;
1381
this.textEditorData.LineHeight = value;
1385
public TextViewMargin TextViewMargin {
1387
return textViewMargin;
1391
public Margin IconMargin {
1392
get { return iconMargin; }
1395
public DocumentLocation LogicalToVisualLocation (DocumentLocation location)
1397
return textEditorData.LogicalToVisualLocation (location);
1400
public DocumentLocation LogicalToVisualLocation (int line, int column)
1402
return textEditorData.LogicalToVisualLocation (line, column);
1405
public void CenterToCaret ()
1407
CenterTo (Caret.Location);
1410
public void CenterTo (int offset)
1412
CenterTo (Document.OffsetToLocation (offset));
1415
public void CenterTo (int line, int column)
1417
CenterTo (new DocumentLocation (line, column));
1420
public void CenterTo (DocumentLocation p)
1422
if (isDisposed || p.Line < 0 || p.Line > Document.LineCount)
1424
SetAdjustments (this.Allocation);
1427
if (this.textEditorData.VAdjustment.Upper < Allocation.Height) {
1428
this.textEditorData.VAdjustment.Value = 0;
1432
// int yMargin = 1 * this.LineHeight;
1433
double caretPosition = LineToY (p.Line);
1434
caretPosition -= this.textEditorData.VAdjustment.PageSize / 2;
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;
1441
this.textEditorData.VAdjustment.Value = caretPosition;
1443
if (this.textEditorData.HAdjustment.Upper < Allocation.Width) {
1444
this.textEditorData.HAdjustment.Value = 0;
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;
1457
public void ScrollTo (int offset)
1459
ScrollTo (Document.OffsetToLocation (offset));
1462
public void ScrollTo (int line, int column)
1464
ScrollTo (new DocumentLocation (line, column));
1467
// class ScrollingActor
1469
// readonly TextEditor editor;
1470
// readonly double targetValue;
1471
// readonly double initValue;
1473
// public ScrollingActor (Mono.TextEditor.TextEditor editor, double targetValue)
1475
// this.editor = editor;
1476
// this.targetValue = targetValue;
1477
// this.initValue = editor.VAdjustment.Value;
1480
// public bool Step (Actor<ScrollingActor> actor)
1482
// if (actor.Expired) {
1483
// editor.VAdjustment.Value = targetValue;
1486
// var newValue = initValue + (targetValue - initValue) / 100 * actor.Percent;
1487
// editor.VAdjustment.Value = newValue;
1492
internal void SmoothScrollTo (double value)
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);
1500
scroll.ActorStep += scrollingActor.Step;
1504
public void ScrollTo (DocumentLocation p)
1506
if (isDisposed || p.Line < 0 || p.Line > Document.LineCount || inCaretScroll)
1508
inCaretScroll = true;
1510
if (this.textEditorData.VAdjustment.Upper < Allocation.Height) {
1511
this.textEditorData.VAdjustment.Value = 0;
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;
1521
if (this.textEditorData.HAdjustment.Upper < Allocation.Width) {
1522
this.textEditorData.HAdjustment.Value = 0;
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;
1534
inCaretScroll = false;
1539
/// Scrolls the editor as required for making the specified area visible
1541
public void ScrollTo (Gdk.Rectangle rect)
1543
inCaretScroll = true;
1545
var vad = this.textEditorData.VAdjustment;
1546
if (vad.Upper < Allocation.Height) {
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;
1556
var had = this.textEditorData.HAdjustment;
1557
if (had.Upper < Allocation.Width) {
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;
1567
inCaretScroll = false;
1571
bool inCaretScroll = false;
1572
public void ScrollToCaret ()
1574
ScrollTo (Caret.Location);
1577
public void TryToResetHorizontalScrollPosition ()
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;
1585
protected override void OnSizeAllocated (Gdk.Rectangle allocation)
1587
base.OnSizeAllocated (allocation);
1588
SetAdjustments (Allocation);
1589
sizeHasBeenAllocated = true;
1590
if (Options.WrapLines)
1591
textViewMargin.PurgeLayoutCache ();
1592
SetChildrenPositions (allocation);
1595
uint lastScrollTime;
1596
protected override bool OnScrollEvent (EventScroll evnt)
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);
1603
var hasZoomModifier = (evnt.State & modifier) != 0;
1604
if (hasZoomModifier && lastScrollTime != 0 && (evnt.Time - lastScrollTime) < 100)
1605
hasZoomModifier = false;
1607
if (hasZoomModifier) {
1608
if (evnt.Direction == ScrollDirection.Up)
1610
else if (evnt.Direction == ScrollDirection.Down)
1615
FireMotionEvent (mx + textViewMargin.XOffset, my, lastState);
1618
lastScrollTime = evnt.Time;
1619
return base.OnScrollEvent (evnt);
1622
void SetHAdjustment ()
1624
textEditorData.HeightTree.Rebuild ();
1626
if (textEditorData.HAdjustment == null || Options == null)
1628
textEditorData.HAdjustment.ValueChanged -= HAdjustmentValueChanged;
1629
if (Options.WrapLines) {
1630
this.textEditorData.HAdjustment.SetBounds (0, 0, 0, 0, 0);
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);
1639
foreach (var containerChild in editor.containerChildren.Concat (containerChildren)) {
1640
if (containerChild.Child == this)
1642
realMaxX = System.Math.Max (realMaxX, containerChild.X + containerChild.Child.Allocation.Width);
1645
this.textEditorData.HAdjustment.SetBounds (
1648
this.textViewMargin.CharWidth,
1651
if (realMaxX < width)
1652
this.textEditorData.HAdjustment.Value = 0;
1655
textEditorData.HAdjustment.ValueChanged += HAdjustmentValueChanged;
1658
internal void SetAdjustments ()
1660
SetAdjustments (Allocation);
1663
public const int EditorLineThreshold = 0;
1665
internal void SetAdjustments (Gdk.Rectangle allocation)
1669
if (this.textEditorData.VAdjustment != null) {
1670
double maxY = textEditorData.HeightTree.TotalHeight;
1671
if (maxY > allocation.Height)
1672
maxY += EditorLineThreshold * this.LineHeight;
1674
foreach (var containerChild in editor.containerChildren.Concat (containerChildren)) {
1675
maxY = System.Math.Max (maxY, containerChild.Y + containerChild.Child.SizeRequest().Height);
1678
if (VAdjustment.Value > maxY - allocation.Height) {
1679
VAdjustment.Value = System.Math.Max (0, maxY - allocation.Height);
1682
this.textEditorData.VAdjustment.SetBounds (0,
1683
System.Math.Max (allocation.Height, maxY),
1687
if (maxY < allocation.Height)
1688
this.textEditorData.VAdjustment.Value = 0;
1692
public int GetWidth (string text)
1694
return this.textViewMargin.GetWidth (text);
1697
void UpdateMarginXOffsets ()
1700
foreach (Margin margin in this.margins) {
1701
if (!margin.IsVisible)
1703
margin.XOffset = curX;
1704
curX += margin.Width;
1708
void RenderMargins (Cairo.Context cr, Cairo.Context textViewCr, Cairo.Rectangle cairoRectangle)
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);
1721
for (int visualLineNumber = textEditorData.LogicalToVisualLine (startLine);; visualLineNumber++) {
1722
int logicalLineNumber = textEditorData.VisualToLogicalLine (visualLineNumber);
1723
var line = Document.GetLine (logicalLineNumber);
1725
// Ensure that the correct line height is set.
1727
textViewMargin.GetLayout (line);
1729
double lineHeight = GetLineHeight (line);
1730
foreach (var margin in this.margins) {
1731
if (!margin.IsVisible)
1734
margin.Draw (margin == textViewMargin ? textViewCr : cr, cairoRectangle, line, logicalLineNumber, margin.XOffset, curY, lineHeight);
1735
} catch (Exception e) {
1736
System.Console.WriteLine (e);
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) {
1744
longestLineWidth = lineWidth;
1745
setLongestLine = true;
1748
if (curY > cairoRectangle.Y + cairoRectangle.Height)
1752
foreach (var margin in this.margins) {
1753
if (!margin.IsVisible)
1755
foreach (var drawer in margin.MarginDrawer)
1756
drawer.Draw (cr, cairoRectangle);
1764
protected override bool OnWidgetEvent (Event evnt)
1766
System.Console.WriteLine(evnt);
1767
return base.OnWidgetEvent (evnt);
1770
double oldVadjustment = 0;
1772
void UpdateAdjustments ()
1774
int lastVisibleLine = textEditorData.LogicalToVisualLine (Document.LineCount);
1775
if (oldRequest != lastVisibleLine) {
1776
SetAdjustments (this.Allocation);
1777
oldRequest = lastVisibleLine;
1782
DateTime started = DateTime.Now;
1784
protected override bool OnExposeEvent (Gdk.EventExpose e)
1786
if (this.isDisposed)
1788
UpdateAdjustments ();
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;
1800
UpdateMarginXOffsets ();
1802
cr.LineWidth = Options.Zoom;
1803
textViewCr.LineWidth = Options.Zoom;
1804
textViewCr.Rectangle (textViewMargin.XOffset, 0, Allocation.Width - textViewMargin.XOffset, Allocation.Height);
1807
RenderMargins (cr, textViewCr, cairoArea);
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);
1813
if (requestResetCaretBlink && HasFocus) {
1814
textViewMargin.ResetCaretBlink (200);
1815
requestResetCaretBlink = false;
1818
foreach (Animation animation in actors) {
1819
animation.Drawer.Draw (cr);
1823
textViewMargin.DrawCaret (e.Window, e.Area);
1825
OnPainted (new PaintEventArgs (cr, cairoArea));
1828
if (Caret.IsVisible)
1829
textViewMargin.DrawCaret (e.Window, Allocation);
1831
return base.OnExposeEvent (e);
1834
protected virtual void OnPainted (PaintEventArgs e)
1836
EventHandler<PaintEventArgs> handler = this.Painted;
1837
if (handler != null)
1841
public event EventHandler<PaintEventArgs> Painted;
1843
#region TextEditorData delegation
1844
public string EolMarker {
1846
return textEditorData.EolMarker;
1850
public Mono.TextEditor.Highlighting.ColorScheme ColorStyle {
1852
return this.textEditorData.ColorStyle;
1856
public EditMode CurrentMode {
1858
return this.textEditorData.CurrentMode;
1861
this.textEditorData.CurrentMode = value;
1865
public bool IsSomethingSelected {
1867
return this.textEditorData.IsSomethingSelected;
1871
public Selection MainSelection {
1873
return textEditorData.MainSelection;
1876
textEditorData.MainSelection = value;
1880
public SelectionMode SelectionMode {
1882
return textEditorData.SelectionMode;
1885
textEditorData.SelectionMode = value;
1889
public TextSegment SelectionRange {
1891
return this.textEditorData.SelectionRange;
1894
this.textEditorData.SelectionRange = value;
1898
public string SelectedText {
1900
return this.textEditorData.SelectedText;
1903
this.textEditorData.SelectedText = value;
1907
public int SelectionAnchor {
1909
return this.textEditorData.SelectionAnchor;
1912
this.textEditorData.SelectionAnchor = value;
1916
public IEnumerable<DocumentLine> SelectedLines {
1918
return this.textEditorData.SelectedLines;
1922
public Adjustment HAdjustment {
1924
return this.textEditorData.HAdjustment;
1928
public Adjustment VAdjustment {
1930
return this.textEditorData.VAdjustment;
1934
public int Insert (int offset, string value)
1936
return textEditorData.Insert (offset, value);
1939
public void Remove (DocumentRegion region)
1941
textEditorData.Remove (region);
1944
public void Remove (TextSegment removeSegment)
1946
textEditorData.Remove (removeSegment);
1949
public void Remove (int offset, int count)
1951
textEditorData.Remove (offset, count);
1954
public int Replace (int offset, int count, string value)
1956
return textEditorData.Replace (offset, count, value);
1959
public void ClearSelection ()
1961
this.textEditorData.ClearSelection ();
1964
public void DeleteSelectedText ()
1966
this.textEditorData.DeleteSelectedText ();
1969
public void DeleteSelectedText (bool clearSelection)
1971
this.textEditorData.DeleteSelectedText (clearSelection);
1974
public void RunEditAction (Action<TextEditorData> action)
1976
action (this.textEditorData);
1979
public void SetSelection (int anchorOffset, int leadOffset)
1981
this.textEditorData.SetSelection (anchorOffset, leadOffset);
1984
public void SetSelection (DocumentLocation anchor, DocumentLocation lead)
1986
this.textEditorData.SetSelection (anchor, lead);
1989
public void SetSelection (int anchorLine, int anchorColumn, int leadLine, int leadColumn)
1991
this.textEditorData.SetSelection (anchorLine, anchorColumn, leadLine, leadColumn);
1994
public void ExtendSelectionTo (DocumentLocation location)
1996
this.textEditorData.ExtendSelectionTo (location);
1998
public void ExtendSelectionTo (int offset)
2000
this.textEditorData.ExtendSelectionTo (offset);
2002
public void SetSelectLines (int from, int to)
2004
this.textEditorData.SetSelectLines (from, to);
2007
public void InsertAtCaret (string text)
2009
textEditorData.InsertAtCaret (text);
2012
public bool CanEdit (int line)
2014
return textEditorData.CanEdit (line);
2017
public string GetLineText (int line)
2019
return textEditorData.GetLineText (line);
2022
public string GetLineText (int line, bool includeDelimiter)
2024
return textEditorData.GetLineText (line, includeDelimiter);
2031
/// A <see cref="TextEditorData"/>
2033
public TextEditorData GetTextEditorData ()
2035
return this.textEditorData;
2038
public event EventHandler SelectionChanged;
2039
protected virtual void OnSelectionChanged (EventArgs args)
2041
CurrentMode.InternalSelectionChanged (editor, textEditorData);
2042
if (SelectionChanged != null)
2043
SelectionChanged (this, args);
2047
#region Document delegation
2050
return Document.TextLength;
2054
public string Text {
2056
return Document.Text;
2059
Document.Text = value;
2063
public string GetTextBetween (int startOffset, int endOffset)
2065
return Document.GetTextBetween (startOffset, endOffset);
2068
public string GetTextBetween (DocumentLocation start, DocumentLocation end)
2070
return Document.GetTextBetween (start, end);
2073
public string GetTextBetween (int startLine, int startColumn, int endLine, int endColumn)
2075
return Document.GetTextBetween (startLine, startColumn, endLine, endColumn);
2078
public string GetTextAt (int offset, int count)
2080
return Document.GetTextAt (offset, count);
2084
public string GetTextAt (TextSegment segment)
2086
return Document.GetTextAt (segment);
2089
public string GetTextAt (DocumentRegion region)
2091
return Document.GetTextAt (region);
2094
public char GetCharAt (int offset)
2096
return Document.GetCharAt (offset);
2099
public IEnumerable<DocumentLine> Lines {
2101
return Document.Lines;
2105
public int LineCount {
2107
return Document.LineCount;
2111
public int LocationToOffset (int line, int column)
2113
return Document.LocationToOffset (line, column);
2116
public int LocationToOffset (DocumentLocation location)
2118
return Document.LocationToOffset (location);
2121
public DocumentLocation OffsetToLocation (int offset)
2123
return Document.OffsetToLocation (offset);
2126
public string GetLineIndent (int lineNumber)
2128
return Document.GetLineIndent (lineNumber);
2131
public string GetLineIndent (DocumentLine segment)
2133
return Document.GetLineIndent (segment);
2136
public DocumentLine GetLine (int lineNumber)
2138
return Document.GetLine (lineNumber);
2141
public DocumentLine GetLineByOffset (int offset)
2143
return Document.GetLineByOffset (offset);
2146
public int OffsetToLineNumber (int offset)
2148
return Document.OffsetToLineNumber (offset);
2151
public IDisposable OpenUndoGroup()
2153
return Document.OpenUndoGroup ();
2157
#region Search & Replace
2159
bool highlightSearchPattern = false;
2161
public string SearchPattern {
2163
return this.textEditorData.SearchRequest.SearchPattern;
2166
if (this.textEditorData.SearchRequest.SearchPattern != value) {
2167
this.textEditorData.SearchRequest.SearchPattern = value;
2172
public ISearchEngine SearchEngine {
2174
return this.textEditorData.SearchEngine;
2177
Debug.Assert (value != null);
2178
this.textEditorData.SearchEngine = value;
2182
public event EventHandler HighlightSearchPatternChanged;
2183
public bool HighlightSearchPattern {
2185
return highlightSearchPattern;
2188
if (highlightSearchPattern != value) {
2189
this.highlightSearchPattern = value;
2190
if (HighlightSearchPatternChanged != null)
2191
HighlightSearchPatternChanged (this, EventArgs.Empty);
2192
textViewMargin.DisposeLayoutDict ();
2198
public bool IsCaseSensitive {
2200
return this.textEditorData.SearchRequest.CaseSensitive;
2203
this.textEditorData.SearchRequest.CaseSensitive = value;
2207
public bool IsWholeWordOnly {
2209
return this.textEditorData.SearchRequest.WholeWordOnly;
2213
this.textEditorData.SearchRequest.WholeWordOnly = value;
2217
public TextSegment SearchRegion {
2219
return this.textEditorData.SearchRequest.SearchRegion;
2223
this.textEditorData.SearchRequest.SearchRegion = value;
2227
public SearchResult SearchForward (int fromOffset)
2229
return textEditorData.SearchForward (fromOffset);
2232
public SearchResult SearchBackward (int fromOffset)
2234
return textEditorData.SearchBackward (fromOffset);
2237
class CaretPulseAnimation : IAnimationDrawer
2241
public double Percent { get; set; }
2243
public Gdk.Rectangle AnimationBounds {
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),
2252
(int)(editor.LineHeight + 2 * extend * editor.Options.Zoom));
2256
public CaretPulseAnimation (TextEditor editor)
2258
this.editor = editor;
2261
public void Draw (Cairo.Context cr)
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);
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),
2277
editor.LineHeight + 2 * extend * editor.Options.Zoom);
2278
Cairo.Color color = editor.ColorStyle.PlainText.Foreground;
2280
cr.LineWidth = editor.Options.Zoom;
2287
public enum PulseKind {
2291
public class RegionPulseAnimation : IAnimationDrawer
2295
public PulseKind Kind { get; set; }
2296
public double Percent { get; set; }
2298
Gdk.Rectangle region;
2300
public Gdk.Rectangle AnimationBounds {
2304
int animationPosition = (int)(100 * 100);
2305
int width = (int)(region.Width + 2 * animationPosition * editor.Options.Zoom / 2);
2307
return new Gdk.Rectangle ((int)(x - animationPosition * editor.Options.Zoom / 2),
2308
(int)(y - animationPosition * editor.Options.Zoom),
2310
(int)(region.Height + 2 * animationPosition * editor.Options.Zoom));
2314
public RegionPulseAnimation (TextEditor editor, Gdk.Point position, Gdk.Size size)
2315
: this (editor, new Gdk.Rectangle (position, size)) {}
2317
public RegionPulseAnimation (TextEditor editor, Gdk.Rectangle region)
2319
if (region.X < 0 || region.Y < 0 || region.Width < 0 || region.Height < 0)
2320
throw new ArgumentException ("region is invalid");
2322
this.editor = editor;
2323
this.region = region;
2326
public void Draw (Cairo.Context cr)
2330
int animationPosition = (int)(Percent * 100);
2332
cr.Rectangle (editor.TextViewMargin.XOffset, 0, editor.Allocation.Width - editor.TextViewMargin.XOffset, editor.Allocation.Height);
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),
2341
(int)(region.Height + 2 * animationPosition * editor.Options.Zoom));
2342
Cairo.Color color = editor.ColorStyle.PlainText.Foreground;
2344
cr.LineWidth = editor.Options.Zoom;
2351
Gdk.Rectangle RangeToRectangle (DocumentLocation start, DocumentLocation end)
2353
if (start.Column < 0 || start.Line < 0 || end.Column < 0 || end.Line < 0)
2354
return Gdk.Rectangle.Zero;
2356
var startPt = this.LocationToPoint (start);
2357
var endPt = this.LocationToPoint (end);
2358
int width = endPt.X - startPt.X;
2360
if (startPt.Y != endPt.Y || startPt.X < 0 || startPt.Y < 0 || width < 0)
2361
return Gdk.Rectangle.Zero;
2363
return new Gdk.Rectangle (startPt.X, startPt.Y, width, (int)this.LineHeight);
2367
/// Initiate a pulse at the specified document location
2369
/// <param name="pulseLocation">
2370
/// A <see cref="DocumentLocation"/>
2372
public void PulseCharacter (DocumentLocation pulseStart)
2374
if (pulseStart.Column < 0 || pulseStart.Line < 0)
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)
2379
StartAnimation (new RegionPulseAnimation (editor, rect) {
2380
Kind = PulseKind.Bounce
2385
public SearchResult FindNext (bool setSelection)
2387
SearchResult result = textEditorData.FindNext (setSelection);
2388
TryToResetHorizontalScrollPosition ();
2389
AnimateSearchResult (result);
2393
public void StartCaretPulseAnimation ()
2395
StartAnimation (new CaretPulseAnimation (editor));
2398
SearchHighlightPopupWindow popupWindow = null;
2400
public void StopSearchResultAnimation ()
2402
if (popupWindow == null)
2404
popupWindow.StopPlaying ();
2407
public void AnimateSearchResult (SearchResult result)
2409
if (!IsComposited || !Options.EnableAnimations || result == null)
2412
// Don't animate multi line search results
2413
if (OffsetToLineNumber (result.Segment.Offset) != OffsetToLineNumber (result.Segment.EndOffset))
2416
TextViewMargin.MainSearchResult = result.Segment;
2417
if (!TextViewMargin.MainSearchResult.IsInvalid) {
2418
if (popupWindow != null) {
2419
popupWindow.StopPlaying ();
2420
popupWindow.Destroy ();
2422
popupWindow = new SearchHighlightPopupWindow (editor);
2423
popupWindow.Result = result;
2424
popupWindow.Popup ();
2425
popupWindow.Destroyed += delegate {
2431
class SearchHighlightPopupWindow : BounceFadePopupWidget
2433
public SearchResult Result {
2438
public SearchHighlightPopupWindow (TextEditor editor) : base (editor)
2442
public override void Popup ()
2444
ExpandWidth = (uint)Editor.LineHeight;
2445
ExpandHeight = (uint)Editor.LineHeight / 2;
2446
BounceEasing = Easing.Sine;
2451
protected override void OnAnimationCompleted ()
2453
base.OnAnimationCompleted ();
2457
protected override void OnDestroyed ()
2459
base.OnDestroyed ();
2464
protected override Cairo.Rectangle CalculateInitialBounds ()
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 ();
2475
int index = Result.Offset - line.Offset - 1;
2477
lineLayout.Layout.IndexToLineX (index, true, out l, out x1);
2482
index = Result.Offset - line.Offset - 1 + Result.Length;
2484
lineLayout.Layout.IndexToLineX (index, true, out l, out x2);
2487
Console.WriteLine ("Invalid end index :" + index);
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;
2495
//adjust the width to match TextViewMargin
2496
w = System.Math.Ceiling (w + 1);
2498
//add space for the shadow
2502
return new Cairo.Rectangle (x, y, w, h);
2505
const int shadowOffset = 1;
2507
Pango.Layout layout = null;
2509
protected override void Draw (Cairo.Context cr, Cairo.Rectangle area)
2511
if (!Editor.Options.UseAntiAliasing)
2512
cr.Antialias = Cairo.Antialias.None;
2513
cr.LineWidth = Editor.Options.Zoom;
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);
2522
// subtract off the shadow again
2523
var width = area.Width - shadowOffset;
2524
var height = area.Height - shadowOffset;
2526
//from TextViewMargin's actual highlighting
2527
double corner = System.Math.Min (4, width) * Editor.Options.Zoom;
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);
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;
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)) {
2546
TextViewMargin.DimColor (Editor.ColorStyle.SearchResultMain.Color, 1.1),
2547
Editor.ColorStyle.SearchResultMain.Color,
2549
gradient.AddColorStop (0, color);
2551
TextViewMargin.DimColor (Editor.ColorStyle.SearchResultMain.Color, 0.9),
2552
Editor.ColorStyle.SearchResultMain.Color,
2554
gradient.AddColorStop (1, color);
2555
cr.Pattern = gradient;
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);
2565
static Cairo.Color ColorLerp (Cairo.Color from, Cairo.Color to, double scale)
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)
2575
static double Lerp (double from, double to, double scale)
2577
return from + scale * (to - from);
2581
public SearchResult FindPrevious (bool setSelection)
2583
SearchResult result = textEditorData.FindPrevious (setSelection);
2584
TryToResetHorizontalScrollPosition ();
2585
AnimateSearchResult (result);
2589
public bool Replace (string withPattern)
2591
return textEditorData.SearchReplace (withPattern, true);
2594
public int ReplaceAll (string withPattern)
2596
return textEditorData.SearchReplaceAll (withPattern);
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; }
2607
const int TooltipTimeout = 650;
2608
TooltipItem tipItem;
2611
uint tipHideTimeoutId = 0;
2612
uint tipShowTimeoutId = 0;
2613
static Gtk.Window tipWindow;
2614
static TooltipProvider currentTooltipProvider;
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
2622
void ShowTooltip (Gdk.ModifierType modifierState)
2624
if (mx < TextViewMargin.XOffset + TextViewMargin.TextStartPosition) {
2629
var loc = PointToLocation (mx, my, true);
2635
// Hide editor tooltips for text marker extended regions (message bubbles)
2636
double y = LineToY (loc.Line);
2637
if (y + LineHeight < my) {
2642
ShowTooltip (modifierState,
2643
Document.LocationToOffset (loc),
2648
void ShowTooltip (Gdk.ModifierType modifierState, int offset, int xloc, int yloc)
2650
CancelScheduledShow ();
2651
if (textEditorData.SuppressTooltips)
2653
if (tipWindow != null && currentTooltipProvider != null && currentTooltipProvider.IsInteractive (editor, tipWindow)) {
2655
tipWindow.GetSize (out ww, out wh);
2657
if (xloc >= wx && xloc < wx + ww && yloc >= tipY && yloc < tipY + 20 + wh)
2660
if (tipItem != null && !tipItem.ItemSegment.IsInvalid && !tipItem.ItemSegment.Contains (offset))
2664
nextTipOffset = offset;
2665
nextTipModifierState = modifierState;
2666
nextTipScheduledTime = DateTime.Now + TimeSpan.FromMilliseconds (TooltipTimeout);
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);
2673
bool TooltipTimer ()
2675
// This timer can't be reused, so reset the var now
2676
tipShowTimeoutId = 0;
2679
if (nextTipOffset == -1)
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);
2690
TooltipProvider provider = null;
2691
TooltipItem item = null;
2693
foreach (TooltipProvider tp in textEditorData.tooltipProviders) {
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);
2707
// Tip already being shown for this item?
2708
if (tipWindow != null && tipItem != null && tipItem.Equals (item)) {
2709
CancelScheduledHide ();
2716
Gtk.Window tw = null;
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);
2723
if (tw == tipWindow)
2729
CancelScheduledShow ();
2732
currentTooltipProvider = provider;
2734
tipShowTimeoutId = 0;
2740
public void HideTooltip (bool checkMouseOver = true)
2742
CancelScheduledHide ();
2743
CancelScheduledShow ();
2745
if (tipWindow != null) {
2746
if (checkMouseOver && tipWindow.GdkWindow != null) {
2747
// Don't hide the tooltip window if the mouse pointer is inside it.
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)
2755
tipWindow.Destroy ();
2760
void DelayedHideTooltip ()
2762
CancelScheduledHide ();
2763
tipHideTimeoutId = GLib.Timeout.Add (300, delegate {
2765
tipHideTimeoutId = 0;
2770
void CancelScheduledHide ()
2772
CancelScheduledShow ();
2773
if (tipHideTimeoutId != 0) {
2774
GLib.Source.Remove (tipHideTimeoutId);
2775
tipHideTimeoutId = 0;
2779
void CancelScheduledShow ()
2781
// Don't remove the timeout handler since it may be reused
2785
void OnDocumentStateChanged (object s, EventArgs a)
2790
void OnTextSet (object sender, EventArgs e)
2792
DocumentLine longest = longestLine;
2793
foreach (DocumentLine line in Document.Lines) {
2794
if (longest == null || line.Length > longest.Length)
2797
if (longest != longestLine) {
2798
int width = (int)(textViewMargin.GetLayout (longest).PangoWidth / Pango.Scale.PangoScale);
2800
if (width > this.longestLineWidth) {
2801
this.longestLineWidth = width;
2802
this.longestLine = longest;
2808
#region Coordinate transformation
2809
public DocumentLocation PointToLocation (double xp, double yp, bool endAtEol = false)
2811
return TextViewMargin.PointToLocation (xp, yp, endAtEol);
2814
public DocumentLocation PointToLocation (Cairo.Point p)
2816
return TextViewMargin.PointToLocation (p);
2819
public DocumentLocation PointToLocation (Cairo.PointD p)
2821
return TextViewMargin.PointToLocation (p);
2824
public Cairo.Point LocationToPoint (DocumentLocation loc)
2826
return TextViewMargin.LocationToPoint (loc);
2829
public Cairo.Point LocationToPoint (int line, int column)
2831
return TextViewMargin.LocationToPoint (line, column);
2834
public Cairo.Point LocationToPoint (int line, int column, bool useAbsoluteCoordinates)
2836
return TextViewMargin.LocationToPoint (line, column, useAbsoluteCoordinates);
2839
public Cairo.Point LocationToPoint (DocumentLocation loc, bool useAbsoluteCoordinates)
2841
return TextViewMargin.LocationToPoint (loc, useAbsoluteCoordinates);
2844
public double ColumnToX (DocumentLine line, int column)
2846
return TextViewMargin.ColumnToX (line, column);
2850
/// Calculates the line number at line start (in one visual line could be several logical lines be displayed).
2852
public int YToLine (double yPos)
2854
return TextViewMargin.YToLine (yPos);
2857
public double LineToY (int logicalLine)
2859
return TextViewMargin.LineToY (logicalLine);
2862
public double GetLineHeight (DocumentLine line)
2864
return TextViewMargin.GetLineHeight (line);
2867
public double GetLineHeight (int logicalLineNumber)
2869
return TextViewMargin.GetLineHeight (logicalLineNumber);
2874
Stage<Animation> animationStage = new Stage<Animation> ();
2875
List<Animation> actors = new List<Animation> ();
2877
protected void InitAnimations ()
2879
animationStage.ActorStep += OnAnimationActorStep;
2880
animationStage.Iteration += OnAnimationIteration;
2883
void DisposeAnimations ()
2885
if (animationStage != null) {
2886
animationStage.Playing = false;
2887
animationStage.ActorStep -= OnAnimationActorStep;
2888
animationStage.Iteration -= OnAnimationIteration;
2889
animationStage = null;
2892
if (actors != null) {
2893
foreach (Animation actor in actors) {
2894
if (actor is IDisposable)
2895
((IDisposable)actor).Dispose ();
2902
Animation StartAnimation (IAnimationDrawer drawer)
2904
return StartAnimation (drawer, 300);
2907
Animation StartAnimation (IAnimationDrawer drawer, uint duration)
2909
return StartAnimation (drawer, duration, Easing.Linear);
2912
Animation StartAnimation (IAnimationDrawer drawer, uint duration, Easing easing)
2914
if (!Options.EnableAnimations)
2916
Animation animation = new Animation (drawer, duration, easing, Blocking.Upstage);
2917
animationStage.Add (animation, duration);
2918
actors.Add (animation);
2922
bool OnAnimationActorStep (Actor<Animation> actor)
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;
2933
case AnimationState.Going:
2934
if (actor.Expired) {
2935
RemoveAnimation (actor.Target);
2938
actor.Target.Drawer.Percent = 1.0 - actor.Percent;
2944
void RemoveAnimation (Animation animation)
2946
if (animation == null)
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);
2955
void OnAnimationIteration (object sender, EventArgs args)
2957
foreach (Animation actor in actors) {
2958
Rectangle bounds = actor.Drawer.AnimationBounds;
2959
QueueDrawArea (bounds.X, bounds.Y, bounds.Width, bounds.Height);
2964
internal void FireLinkEvent (string link, uint button, ModifierType modifierState)
2966
if (LinkRequest != null)
2967
LinkRequest (this, new LinkEventArgs (link, button, modifierState));
2970
public event EventHandler<LinkEventArgs> LinkRequest;
2973
/// Inserts a margin at the specified list position
2975
public void InsertMargin (int index, Margin margin)
2977
margins.Insert (index, margin);
2982
/// Checks whether the editor has a margin of a given type
2984
public bool HasMargin (Type marginType)
2986
return margins.Exists((margin) => { return marginType.IsAssignableFrom (margin.GetType ()); });
2990
/// Gets the first margin of a given type
2992
public Margin GetMargin (Type marginType)
2994
return margins.Find((margin) => { return marginType.IsAssignableFrom (margin.GetType ()); });
2996
bool requestResetCaretBlink = false;
2997
public void RequestResetCaretBlink ()
3000
requestResetCaretBlink = true;
3003
void UpdateLinesOnTextMarkerHeightChange (object sender, LineEventArgs e)
3005
if (!e.Line.Markers.Any (m => m is IExtendingTextLineMarker))
3007
var line = e.Line.LineNumber;
3008
textEditorData.HeightTree.SetLineHeight (line, GetLineHeight (e.Line));
3015
bool highlightCaretLine;
3018
public SetCaret (TextEditor view, int line, int column, bool highlightCaretLine, bool centerCaret)
3022
this.column = column;
3023
this.highlightCaretLine = highlightCaretLine;
3024
this.centerCaret = centerCaret;
3027
public void Run (object sender, EventArgs e)
3029
if (view.IsDisposed)
3031
line = System.Math.Min (line, view.Document.LineCount);
3032
view.Caret.AutoScrollToCaret = false;
3034
view.Caret.Location = new DocumentLocation (line, column);
3037
view.CenterToCaret ();
3038
if (view.TextViewMargin.XOffset == 0)
3039
view.HAdjustment.Value = 0;
3040
view.TextArea.SizeAllocated -= Run;
3042
view.Caret.AutoScrollToCaret = true;
3043
if (highlightCaretLine) {
3044
view.TextViewMargin.HighlightCaretLine = true;
3045
view.StartCaretPulseAnimation ();
3051
public void SetCaretTo (int line, int column)
3053
SetCaretTo (line, column, true);
3056
public void SetCaretTo (int line, int column, bool highlight)
3058
SetCaretTo (line, column, highlight, true);
3061
public void SetCaretTo (int line, int column, bool highlight, bool centerCaret)
3063
if (line < DocumentLocation.MinLine)
3064
throw new ArgumentException ("line < MinLine");
3065
if (column < DocumentLocation.MinColumn)
3066
throw new ArgumentException ("column < MinColumn");
3068
if (!sizeHasBeenAllocated) {
3069
SetCaret setCaret = new SetCaret (editor, line, column, highlight, centerCaret);
3070
SizeAllocated += setCaret.Run;
3072
new SetCaret (editor, line, column, highlight, centerCaret).Run (null, null);
3077
public override ContainerChild this [Widget w] {
3079
return containerChildren.FirstOrDefault (info => info.Child == w || (info.Child is AnimatedWidget && ((AnimatedWidget)info.Child).Widget == w));
3083
public override GLib.GType ChildType ()
3085
return Gtk.Widget.GType;
3088
internal List<TextEditor.EditorContainerChild> containerChildren = new List<TextEditor.EditorContainerChild> ();
3090
public void AddTopLevelWidget (Gtk.Widget widget, int x, int y)
3092
widget.Parent = this;
3093
TextEditor.EditorContainerChild info = new TextEditor.EditorContainerChild (this, widget);
3096
containerChildren.Add (info);
3097
ResizeChild (Allocation, info);
3101
public void MoveTopLevelWidget (Gtk.Widget widget, int x, int y)
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)
3110
ResizeChild (Allocation, info);
3118
/// Returns the position of an embedded widget
3120
public void GetTopLevelWidgetPosition (Gtk.Widget widget, out int x, out int y)
3122
foreach (var info in containerChildren.ToArray ()) {
3123
if (info.Child == widget || (info.Child is AnimatedWidget && ((AnimatedWidget)info.Child).Widget == widget)) {
3132
public void MoveToTop (Gtk.Widget widget)
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 ();
3143
protected override void OnAdded (Widget widget)
3145
AddTopLevelWidget (widget, 0, 0);
3148
protected override void OnRemoved (Widget widget)
3150
foreach (var info in containerChildren.ToArray ()) {
3151
if (info.Child == widget) {
3153
containerChildren.Remove (info);
3160
protected override void ForAll (bool include_internals, Gtk.Callback callback)
3162
containerChildren.ForEach (child => callback (child.Child));
3165
protected override void OnMapped ()
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 (); });
3174
protected override void OnUnmapped ()
3176
WidgetFlags &= ~WidgetFlags.Mapped;
3178
// We hide the window first so that the user doesn't see widgets disappearing one by one.
3181
containerChildren.ForEach (child => child.Child.Unmap ());
3184
void ResizeChild (Rectangle allocation, TextEditor.EditorContainerChild child)
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);
3193
// childRectangle.X += allocation.X;
3194
// childRectangle.Y += allocation.Y;
3195
child.Child.SizeAllocate (childRectangle);
3198
void SetChildrenPositions (Rectangle allocation)
3200
foreach (var child in containerChildren.ToArray ()) {
3201
ResizeChild (allocation, child);
3208
public interface ITextEditorDataProvider
3210
TextEditorData GetTextEditorData ();
3214
public sealed class PaintEventArgs : EventArgs
3216
public Cairo.Context Context {
3221
public Cairo.Rectangle Area {
3226
public PaintEventArgs (Cairo.Context context, Cairo.Rectangle area)
3228
this.Context = context;