5
// Mike KrĆ¼ger <mkrueger@novell.com>
7
// Copyright (c) 2010 Novell, Inc (http://www.novell.com)
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31
using System.Collections.Generic;
32
using Mono.TextEditor;
33
using MonoDevelop.Ide;
34
using System.Threading;
35
using MonoDevelop.Core;
36
using System.Text.RegularExpressions;
38
using MonoDevelop.Components.Commands;
40
namespace MonoDevelop.VersionControl.Views
42
public enum BlameCommands {
48
public class BlameWidget : Bin
50
Adjustment vAdjustment;
51
Gtk.VScrollbar vScrollBar;
53
Adjustment hAdjustment;
54
Gtk.HScrollbar hScrollBar;
56
BlameRenderer overview;
59
List<ContainerChild> children = new List<ContainerChild> ();
61
public Adjustment Vadjustment {
62
get { return this.vAdjustment; }
65
public Adjustment Hadjustment {
66
get { return this.hAdjustment; }
69
public override ContainerChild this [Widget w] {
71
foreach (ContainerChild info in children.ToArray ()) {
79
public TextEditor Editor {
84
VersionControlDocumentInfo info;
86
public Ide.Gui.Document Document {
91
public VersionControlItem VersionControlItem {
97
protected BlameWidget (IntPtr ptr) : base (ptr)
101
public BlameWidget (VersionControlDocumentInfo info)
105
vAdjustment = new Adjustment (0, 0, 0, 0, 0, 0);
106
vAdjustment.Changed += HandleAdjustmentChanged;
108
vScrollBar = new VScrollbar (vAdjustment);
109
AddChild (vScrollBar);
111
hAdjustment = new Adjustment (0, 0, 0, 0, 0, 0);
112
hAdjustment.Changed += HandleAdjustmentChanged;
114
hScrollBar = new HScrollbar (hAdjustment);
115
AddChild (hScrollBar);
117
editor = new TextEditor (info.Document.Editor.Document, info.Document.Editor.Options);
119
editor.SetScrollAdjustments (hAdjustment, vAdjustment);
121
overview = new BlameRenderer (this);
124
this.DoubleBuffered = true;
125
editor.Painted += HandleEditorExposeEvent;
126
editor.EditorOptionsChanged += delegate {
127
overview.OptionsChanged ();
129
editor.Caret.PositionChanged += ComparisonWidget.CaretPositionChanged;
130
editor.FocusInEvent += ComparisonWidget.EditorFocusIn;
131
editor.Document.Folded += delegate {
134
editor.Document.FoldTreeUpdated += delegate {
137
editor.ButtonPressEvent += OnPopupMenu;
141
void OnPopupMenu (object sender, Gtk.ButtonPressEventArgs args)
143
if (args.Event.Button == 3) {
144
int textEditorXOffset = (int)args.Event.X - (int)editor.TextViewMargin.XOffset;
145
if (textEditorXOffset < 0)
147
this.menuPopupLocation = new Cairo.Point ((int)args.Event.X, (int)args.Event.Y);
148
DocumentLocation loc = editor.PointToLocation (textEditorXOffset, (int)args.Event.Y);
149
if (!editor.IsSomethingSelected || !editor.SelectionRange.Contains (editor.Document.LocationToOffset (loc)))
150
editor.Caret.Location = loc;
158
CommandEntrySet cset = IdeApp.CommandService.CreateCommandEntrySet ("/MonoDevelop/VersionControl/BlameView/ContextMenu");
159
Gtk.Menu menu = IdeApp.CommandService.CreateMenu (cset);
160
menu.Destroyed += delegate {
164
menu.Popup (null, null, new Gtk.MenuPositionFunc (PositionPopupMenu), 0, Gtk.Global.CurrentEventTime);
167
Cairo.Point menuPopupLocation;
168
void PositionPopupMenu (Menu menu, out int x, out int y, out bool pushIn)
170
this.GdkWindow.GetOrigin (out x, out y);
171
x += this.menuPopupLocation.X;
172
y += this.menuPopupLocation.Y;
173
Requisition request = menu.SizeRequest ();
174
Gdk.Rectangle geometry = Screen.GetMonitorGeometry (Screen.GetMonitorAtPoint (x, y));
176
y = Math.Max (geometry.Top, Math.Min (y, geometry.Bottom - request.Height));
177
x = Math.Max (geometry.Left, Math.Min (x, geometry.Right - request.Width));
181
void HandleAdjustmentChanged (object sender, EventArgs e)
183
Adjustment adjustment = (Adjustment)sender;
184
Scrollbar scrollbar = adjustment == vAdjustment ? (Scrollbar)vScrollBar : hScrollBar;
185
bool newVisible = adjustment.Upper - adjustment.Lower > adjustment.PageSize;
186
if (scrollbar.Visible != newVisible) {
187
scrollbar.Visible = newVisible;
192
public override GLib.GType ChildType ()
194
return Gtk.Widget.GType;
197
protected override void ForAll (bool include_internals, Gtk.Callback callback)
199
base.ForAll (include_internals, callback);
201
if (include_internals)
202
children.ForEach (child => callback (child.Child));
205
public void AddChild (Gtk.Widget child)
208
children.Add (new ContainerChild (this, child));
212
protected override void OnAdded (Widget widget)
214
base.OnAdded (widget);
216
widget.SetScrollAdjustments (hAdjustment, vAdjustment);
219
protected override void OnRemoved (Widget widget)
222
foreach (var info in children.ToArray ()) {
223
if (info.Child == widget) {
224
children.Remove (info);
230
protected override void OnDestroyed ()
233
children.ForEach (child => child.Child.Destroy ());
237
protected override void OnSizeAllocated (Rectangle allocation)
239
base.OnSizeAllocated (allocation);
240
int vwidth = vScrollBar.Visible ? vScrollBar.Requisition.Width : 0;
241
int hheight = hScrollBar.Visible ? hScrollBar.Requisition.Height : 0;
242
Rectangle childRectangle = new Rectangle (allocation.X + 1, allocation.Y + 1, allocation.Width - vwidth - 1, allocation.Height - hheight - 1);
244
if (vScrollBar.Visible) {
245
int right = childRectangle.Right;
246
int vChildTopHeight = -1;
247
int v = hScrollBar.Visible ? hScrollBar.Requisition.Height : 0;
248
vScrollBar.SizeAllocate (new Rectangle (right, childRectangle.Y + vChildTopHeight, vwidth, Allocation.Height - v - vChildTopHeight - 1));
249
vScrollBar.Value = System.Math.Max (System.Math.Min (vAdjustment.Upper - vAdjustment.PageSize, vScrollBar.Value), vAdjustment.Lower);
251
int overviewWidth = overview.WidthRequest;
252
overview.SizeAllocate (new Rectangle (childRectangle.Right - overviewWidth, childRectangle.Top, overviewWidth, childRectangle.Height));
253
editor.SizeAllocate (new Rectangle (childRectangle.X, childRectangle.Top, childRectangle.Width - overviewWidth, childRectangle.Height));
255
if (hScrollBar.Visible) {
256
hScrollBar.SizeAllocate (new Rectangle (childRectangle.X, childRectangle.Bottom, childRectangle.Width, hheight));
257
hScrollBar.Value = System.Math.Max (System.Math.Min (hAdjustment.Upper - hAdjustment.PageSize, hScrollBar.Value), hAdjustment.Lower);
261
static double GetWheelDelta (Scrollbar scrollbar, ScrollDirection direction)
263
double delta = System.Math.Pow (scrollbar.Adjustment.PageSize, 2.0 / 3.0);
264
if (direction == ScrollDirection.Up || direction == ScrollDirection.Left)
266
if (scrollbar.Inverted)
271
protected override bool OnScrollEvent (EventScroll evnt)
273
Scrollbar scrollWidget = (evnt.Direction == ScrollDirection.Up || evnt.Direction == ScrollDirection.Down) ? (Scrollbar)vScrollBar : hScrollBar;
274
if (scrollWidget.Visible) {
275
double newValue = scrollWidget.Adjustment.Value + GetWheelDelta (scrollWidget, evnt.Direction);
276
newValue = System.Math.Max (System.Math.Min (scrollWidget.Adjustment.Upper - scrollWidget.Adjustment.PageSize, newValue), scrollWidget.Adjustment.Lower);
277
scrollWidget.Adjustment.Value = newValue;
279
return base.OnScrollEvent (evnt);
282
protected override void OnSizeRequested (ref Gtk.Requisition requisition)
284
base.OnSizeRequested (ref requisition);
285
children.ForEach (child => child.Child.SizeRequest ());
288
void HandleEditorExposeEvent (object o, PaintEventArgs args)
290
var cr = args.Context;
291
int startLine = Editor.YToLine (Editor.VAdjustment.Value);
292
double startY = Editor.LineToY (startLine);
293
double curY = startY - Editor.VAdjustment.Value;
294
int line = startLine;
295
var color = Style.Dark (State);
297
while (curY < editor.Allocation.Bottom) {
298
Annotation ann = line <= overview.annotations.Count ? overview.annotations[line - 1] : null;
299
double curStart = curY;
301
JumpOverFoldings (ref line);
303
} while (curY < editor.Allocation.Bottom && line <= overview.annotations.Count && ann != null && overview.annotations[line - 1] != null && overview.annotations[line - 1].Revision == ann.Revision);
304
curY = Editor.LineToY (line) - Editor.VAdjustment.Value;
306
if (overview.highlightAnnotation != null) {
307
if (ann != null && overview.highlightAnnotation.Revision == ann.Revision && curStart <= overview.highlightPositon && overview.highlightPositon < curY) {
309
cr.Rectangle (Editor.TextViewMargin.XOffset, curStart + cr.LineWidth, Editor.Allocation.Width - Editor.TextViewMargin.XOffset, curY - curStart - cr.LineWidth);
310
cr.Color = new Cairo.Color (color.Red / (double)ushort.MaxValue,
311
color.Green / (double)ushort.MaxValue,
312
color.Blue / (double)ushort.MaxValue,
319
cr.MoveTo (Editor.TextViewMargin.XOffset, curY + 0.5);
320
cr.LineTo (Editor.Allocation.Width, curY + 0.5);
322
cr.Color = new Cairo.Color (color.Red / (double)ushort.MaxValue,
323
color.Green / (double)ushort.MaxValue,
324
color.Blue / (double)ushort.MaxValue,
331
protected override bool OnExposeEvent (EventExpose evnt)
333
Gdk.GC gc = Style.DarkGC (State);
334
evnt.Window.DrawLine (gc, Allocation.X, Allocation.Top, Allocation.X, Allocation.Bottom);
335
evnt.Window.DrawLine (gc, Allocation.Right, Allocation.Top, Allocation.Right, Allocation.Bottom);
337
evnt.Window.DrawLine (gc, Allocation.Left, Allocation.Y, Allocation.Right, Allocation.Y);
338
evnt.Window.DrawLine (gc, Allocation.Left, Allocation.Bottom, Allocation.Right, Allocation.Bottom);
341
return base.OnExposeEvent (evnt);
344
void JumpOverFoldings (ref int line)
347
foreach (FoldSegment fs in Editor.Document.GetStartFoldings (line).Where (fs => fs.IsFolded)) {
348
lastFold = System.Math.Max (fs.EndOffset, lastFold);
351
line = Editor.Document.OffsetToLineNumber (lastFold);
354
internal static string FormatMessage (string msg)
356
StringBuilder sb = new StringBuilder ();
358
foreach (char ch in msg) {
359
if (ch == ' ' || ch == '\t') {
369
Document doc = new Document ();
370
doc.Text = sb.ToString ();
371
for (int i = 1; i <= doc.LineCount; i++) {
372
string text = doc.GetLineText (i).Trim ();
373
int idx = text.IndexOf (':');
374
if (text.StartsWith ("*") && idx >= 0 && idx < text.Length - 1) {
375
int offset = doc.GetLine (i).EndOffset;
376
msg = text.Substring (idx + 1) + doc.GetTextAt (offset, doc.Length - offset);
380
return msg.TrimStart (' ', '\t');
383
class BlameRenderer : DrawingArea
385
static readonly Annotation locallyModified = new Annotation ("", "?", DateTime.MinValue);
388
internal List<Annotation> annotations;
391
double dragPosition = -1;
392
public BlameRenderer (BlameWidget widget)
394
this.widget = widget;
395
widget.info.Updated += delegate { QueueDraw (); };
396
annotations = new List<Annotation> ();
397
UpdateAnnotations (null, null);
398
// widget.Document.Saved += UpdateAnnotations;
399
widget.Editor.Document.TextReplacing += EditorDocumentTextReplacing;
400
widget.Editor.Document.LineChanged += EditorDocumentLineChanged;
401
widget.vScrollBar.ValueChanged += delegate {
405
layout = new Pango.Layout (PangoContext);
406
Events |= EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask | EventMask.LeaveNotifyMask;
411
public void OptionsChanged ()
413
var description = Pango.FontDescription.FromString ("Tahoma " + (int)(10 * widget.Editor.Options.Zoom));
414
layout.FontDescription = description;
418
protected override void OnDestroyed ()
421
// widget.Document.Saved -= UpdateAnnotations;
422
widget.Editor.Document.TextReplacing -= EditorDocumentTextReplacing;
423
widget.Editor.Document.LineChanged -= EditorDocumentLineChanged;
424
if (layout != null) {
430
internal double highlightPositon;
431
internal Annotation highlightAnnotation;
432
protected override bool OnMotionNotifyEvent (EventMotion evnt)
435
if (dragPosition >= 0) {
437
widget.GetPointer (out x, out y);
438
int newWidthRequest = widget.Allocation.Width - x;
439
newWidthRequest = Math.Min (widget.Allocation.Width - (int)widget.Editor.TextViewMargin.XOffset, Math.Max (leftSpacer, newWidthRequest));
441
WidthRequest = newWidthRequest;
444
int startLine = widget.Editor.YToLine (widget.Editor.VAdjustment.Value + evnt.Y);
445
var ann = startLine > 0 && startLine <= annotations.Count ? annotations[startLine - 1] : null;
447
TooltipText = GetCommitMessage (startLine);
449
highlightPositon = evnt.Y;
450
if (highlightAnnotation != ann) {
451
highlightAnnotation = ann;
455
return base.OnMotionNotifyEvent (evnt);
459
protected override bool OnLeaveNotifyEvent (EventCrossing evnt)
461
highlightAnnotation = null;
463
return base.OnLeaveNotifyEvent (evnt);
467
protected override bool OnButtonPressEvent (EventButton evnt)
469
if (evnt.Button == 3) {
470
CommandEntrySet opset = new CommandEntrySet ();
471
opset.AddItem (BlameCommands.ShowDiff);
472
opset.AddItem (BlameCommands.ShowLog);
473
opset.AddItem (Command.Separator);
474
opset.AddItem (BlameCommands.CopyRevision);
475
IdeApp.CommandService.ShowContextMenu (opset, this);
477
if (evnt.X < leftSpacer) {
478
grabTime = evnt.Time;
479
var status = Gdk.Pointer.Grab (this.GdkWindow, false, EventMask.PointerMotionHintMask | EventMask.Button1MotionMask | EventMask.ButtonReleaseMask | EventMask.EnterNotifyMask | EventMask.LeaveNotifyMask, null, null, grabTime);
480
if (status == GrabStatus.Success) {
481
dragPosition = evnt.X;
485
return base.OnButtonPressEvent (evnt);
488
[CommandHandler (BlameCommands.CopyRevision)]
489
protected void OnCopyRevision ()
491
if (highlightAnnotation == null)
493
var clipboard = Clipboard.Get (Gdk.Atom.Intern ("CLIPBOARD", false));
494
clipboard.Text = highlightAnnotation.Revision.ToString ();
495
clipboard = Clipboard.Get (Gdk.Atom.Intern ("PRIMARY", false));
496
clipboard.Text = highlightAnnotation.Revision.ToString ();
499
[CommandHandler (BlameCommands.ShowDiff)]
500
protected void OnShowDiff ()
502
if (highlightAnnotation == null)
505
foreach (var content in widget.info.Document.Window.SubViewContents) {
506
DiffView diffView = content as DiffView;
507
if (diffView != null) {
508
widget.info.Document.Window.SwitchView (i);
509
var rev = widget.info.History.FirstOrDefault (h => h.ToString () == highlightAnnotation.Revision);
512
diffView.ComparisonWidget.SetRevision (diffView.ComparisonWidget.OriginalEditor, rev.GetPrevious ());
513
diffView.ComparisonWidget.SetRevision (diffView.ComparisonWidget.DiffEditor, rev);
520
[CommandHandler (BlameCommands.ShowLog)]
521
protected void OnShowLog ()
523
if (highlightAnnotation == null)
526
foreach (var content in widget.info.Document.Window.SubViewContents) {
527
LogView logView = content as LogView;
528
if (logView != null) {
529
widget.info.Document.Window.SwitchView (i);
530
var rev = widget.info.History.FirstOrDefault (h => h.ToString () == highlightAnnotation.Revision);
533
logView.LogWidget.SelectedRevision = rev;
541
protected override bool OnButtonReleaseEvent (EventButton evnt)
543
if (dragPosition >= 0) {
544
Gdk.Pointer.Ungrab (grabTime);
547
return base.OnButtonReleaseEvent (evnt);
549
DateTime minDate, maxDate;
551
/// Reloads annotations for the current document
553
internal void UpdateAnnotations (object sender, EventArgs e)
555
StatusBarContext ctx = IdeApp.Workbench.StatusBar.CreateContext ();
556
ctx.AutoPulse = true;
557
ctx.ShowMessage (ImageService.GetImage ("md-version-control", IconSize.Menu), GettextCatalog.GetString ("Retrieving history"));
559
ThreadPool.QueueUserWorkItem (delegate {
561
annotations = new List<Annotation> (widget.VersionControlItem.Repository.GetAnnotations (widget.Document.FileName));
563
// for (int i = 0; i < annotations.Count; i++) {
564
// Annotation varname = annotations[i];
565
// System.Console.WriteLine (i + ":" + varname);
567
minDate = annotations.Min (a => a.Date);
568
maxDate = annotations.Max (a => a.Date);
569
} catch (Exception ex) {
570
LoggingService.LogError ("Error retrieving history", ex);
573
DispatchService.GuiDispatch (delegate {
582
/// Marks a line as locally modified
584
private void EditorDocumentLineChanged (object sender, LineEventArgs e)
586
int startLine = widget.Editor.Document.OffsetToLineNumber (e.Line.Offset);
587
SetAnnotation (startLine, locallyModified);
591
/// Marks necessary lines modified when text is replaced
593
private void EditorDocumentTextReplacing (object sender, ReplaceEventArgs e)
595
int startLine = widget.Editor.Document.OffsetToLineNumber (e.Offset),
596
endLine = widget.Editor.Document.OffsetToLineNumber (e.Offset + Math.Max (e.Count, e.Value != null ? e.Value.Length : 0)),
598
string[] tokens = null;
600
if (startLine < endLine) {
601
// change crosses line boundary
603
lineCount = endLine - startLine;
604
lineCount = Math.Min (lineCount, annotations.Count - startLine);
607
annotations.RemoveRange (startLine - 1, lineCount);
608
if (!string.IsNullOrEmpty (e.Value)) {
609
for (int i=0; i<lineCount; ++i)
610
annotations.Insert (startLine - 1, locallyModified);
613
} else if (0 == e.Count) {
615
tokens = e.Value.Split (new string[]{Environment.NewLine}, StringSplitOptions.None);
616
lineCount = tokens.Length - 1;
617
for (int i=0; i<lineCount; ++i) {
618
annotations.Insert (Math.Min (startLine, annotations.Count), locallyModified);
620
} else if (startLine > endLine) {
622
UpdateAnnotations (null, null);
626
SetAnnotation (startLine, locallyModified);
629
void SetAnnotation (int index, Annotation text)
633
for (int i = annotations.Count; i <= index; ++i)
634
annotations.Add (locallyModified);
635
annotations[index] = text;
639
/// Gets the commit message matching a given annotation index.
641
internal string GetCommitMessage (int index)
643
Annotation annotation = (index < annotations.Count)? annotations[index]: null;
644
var history = widget.info.History;
645
if (null != history && annotation != null) {
646
foreach (Revision rev in history) {
647
if (rev.ToString () == annotation.Revision) {
655
string TruncRevision (string revision)
657
return TruncRevision (revision, 8);
661
/// Truncates the revision. This is done by trying to find the shortest matching number.
664
/// The shortest revision number (down to a minimum length of initialLength).
666
/// <param name='revision'>
669
/// <param name='initalLength'>
672
string TruncRevision (string revision, int initalLength)
674
if (initalLength >= revision.Length)
676
string truncated = revision.Substring (0, initalLength);
677
var history = widget.info.History;
678
if (history != null) {
679
bool isMisleadingMatch = history.Select (r => r.ToString ()).Any (rev => rev != revision && rev.StartsWith (truncated));
680
if (isMisleadingMatch)
681
truncated = TruncRevision (revision, initalLength + 1);
688
int tmpwidth, height, width = 120;
689
int dateTimeLength = -1;
690
foreach (Annotation note in annotations) {
691
if (!string.IsNullOrEmpty (note.Author)) {
692
if (dateTimeLength < 0 && note.HasDate) {
693
layout.SetText (note.Date.ToShortDateString ());
694
layout.GetPixelSize (out dateTimeLength, out height);
696
layout.SetText (note.Author + TruncRevision (note.Revision));
697
layout.GetPixelSize (out tmpwidth, out height);
698
width = Math.Max (width, tmpwidth);
701
WidthRequest = dateTimeLength + width + 30 + leftSpacer + margin * 2;
705
const int leftSpacer = 4;
706
const int margin = 4;
709
protected override bool OnExposeEvent (Gdk.EventExpose e)
711
using (Cairo.Context cr = Gdk.CairoHelper.Create (e.Window)) {
712
cr.LineWidth = Math.Max (1.0, widget.Editor.Options.Zoom);
714
cr.Rectangle (leftSpacer, 0, Allocation.Width, Allocation.Height);
715
cr.Color = new Cairo.Color (0.95, 0.95, 0.95);
718
int startLine = widget.Editor.YToLine ((int)widget.Editor.VAdjustment.Value);
719
double startY = widget.Editor.LineToY (startLine);
720
while (startLine > 1 && startLine < annotations.Count && annotations[startLine - 1] != null && annotations[startLine] != null && annotations[startLine - 1].Revision == annotations[startLine].Revision) {
722
startY -= widget.Editor.GetLineHeight (widget.Editor.Document.GetLine (startLine));
724
double curY = startY - widget.Editor.VAdjustment.Value;
725
int line = startLine;
726
while (curY < Allocation.Bottom) {
727
double curStart = curY;
728
// widget.JumpOverFoldings (ref line);
729
int lineStart = line;
730
int w = 0, w2 = 0, h = 16;
731
Annotation ann = line <= annotations.Count ? annotations[line - 1] : null;
734
widget.JumpOverFoldings (ref line);
736
} while (line <= annotations.Count && annotations[line - 1] != null && annotations[line - 1].Revision == ann.Revision);
737
double nextY = widget.editor.LineToY (line) - widget.editor.VAdjustment.Value;
738
if (highlightAnnotation != null && highlightAnnotation.Revision == ann.Revision && curStart <= highlightPositon && highlightPositon < nextY) {
739
cr.Rectangle (leftSpacer, curStart + cr.LineWidth, Allocation.Width - leftSpacer, nextY - curStart - cr.LineWidth);
740
cr.Color = new Cairo.Color (1, 1, 1);
743
layout.SetText (ann.Author);
744
layout.GetPixelSize (out w, out h);
745
e.Window.DrawLayout (Style.BlackGC, leftSpacer + margin, (int)(curY + (widget.Editor.LineHeight - h) / 2), layout);
748
layout.SetText (TruncRevision (ann.Revision));
749
layout.GetPixelSize (out w2, out h);
750
e.Window.DrawLayout (Style.BlackGC, Allocation.Width - w2 - margin, (int)(curY + (widget.Editor.LineHeight - h) / 2), layout);
753
string dateTime = ann.Date.ToShortDateString ();
754
int middle = w + (Allocation.Width - margin * 2 - leftSpacer - w - w2) / 2;
755
layout.SetText (dateTime);
756
layout.GetPixelSize (out w, out h);
757
e.Window.DrawLayout (Style.BlackGC, leftSpacer + margin + middle - w / 2, (int)(curY + (widget.Editor.LineHeight - h) / 2), layout);
761
curY += widget.Editor.GetLineHeight (line);
763
widget.JumpOverFoldings (ref line);
766
if (ann != null && line - lineStart > 1) {
767
string msg = GetCommitMessage (lineStart);
768
if (!string.IsNullOrEmpty (msg)) {
769
msg = FormatMessage (msg);
770
layout.SetText (msg);
771
layout.Width = (int)(Allocation.Width * Pango.Scale.PangoScale);
772
using (var gc = new Gdk.GC (e.Window)) {
773
gc.RgbFgColor = Style.Dark (State);
774
gc.ClipRectangle = new Rectangle (0, (int)curStart, Allocation.Width, (int)(curY - curStart));
775
e.Window.DrawLayout (gc, (int)(leftSpacer + margin), (int)(curStart + h), layout);
780
cr.Rectangle (0, curStart, leftSpacer, curY - curStart);
782
if (ann != null && ann != locallyModified && !string.IsNullOrEmpty (ann.Author)) {
785
if (ann != null && (maxDate - minDate).TotalHours > 0) {
786
a = 1 - (ann.Date - minDate).TotalHours / (maxDate - minDate).TotalHours;
790
HslColor color = new Cairo.Color (0.90, 0.90, 1);
791
color.L = 0.4 + a / 2;
795
cr.Color = ann != null ? new Cairo.Color (1, 1, 0) : new Cairo.Color (0.95, 0.95, 0.95);
800
cr.MoveTo (0, curY + 0.5);
801
cr.LineTo (Allocation.Width, curY + 0.5);
802
cr.Color = new Cairo.Color (0.6, 0.6, 0.6);