107
135
window.GetSize (out w, out maxy);
137
cell_area.Width += cell_area.X - leftSpace;
138
cell_area.X = leftSpace;
109
140
var treeview = widget as FileTreeView;
110
141
var p = treeview != null? treeview.CursorLocation : null;
112
int recty = cell_area.Y;
113
int recth = cell_area.Height - 1;
118
if (recth > maxy + 2)
121
window.DrawRectangle (widget.Style.BaseGC (Gtk.StateType.Normal), true, cell_area.X, recty, cell_area.Width - 1, recth);
143
cell_area.Width -= RightPadding;
145
window.DrawRectangle (widget.Style.BaseGC (Gtk.StateType.Normal), true, cell_area.X, cell_area.Y, cell_area.Width - 1, cell_area.Height);
123
147
Gdk.GC normalGC = widget.Style.TextGC (StateType.Normal);
124
148
Gdk.GC removedGC = new Gdk.GC (window);
125
149
removedGC.Copy (normalGC);
126
removedGC.RgbFgColor = new Color (255, 0, 0);
150
removedGC.RgbFgColor = baseRemoveColor.AddLight (-0.3);
127
151
Gdk.GC addedGC = new Gdk.GC (window);
128
152
addedGC.Copy (normalGC);
129
addedGC.RgbFgColor = new Color (0, 0, 255);
153
addedGC.RgbFgColor = baseAddColor.AddLight (-0.3);
130
154
Gdk.GC infoGC = new Gdk.GC (window);
131
155
infoGC.Copy (normalGC);
132
infoGC.RgbFgColor = new Color (0xa5, 0x2a, 0x2a);
156
infoGC.RgbFgColor = widget.Style.Text (StateType.Normal).AddLight (0.2);
158
Cairo.Context ctx = CairoHelper.Create (window);
160
// Rendering is done in two steps:
161
// 1) Get a list of blocks to render
162
// 2) render the blocks
134
164
int y = cell_area.Y + 2;
166
// cline keeps track of the current source code line (the one to jump to when double clicking)
136
168
bool inHeader = true;
169
BlockInfo currentBlock = null;
171
List<BlockInfo> blocks = new List<BlockInfo> ();
138
173
for (int n=0; n<lines.Length; n++, y += lineHeight) {
140
175
string line = lines [n];
141
if (line.Length == 0)
176
if (line.Length == 0) {
144
182
char tag = line [0];
146
// Keep track of the real file line
147
int thisLine = cline;
184
if (line.StartsWith ("---") || line.StartsWith ("+++")) {
185
// Ignore this part of the header.
148
190
if (tag == '@') {
149
191
int l = ParseCurrentLine (line);
150
if (l != -1) cline = thisLine = l;
192
if (l != -1) cline = l - 1;
151
193
inHeader = false;
152
194
} else if (tag != '-' && !inHeader)
155
if (y + lineHeight < 0)
162
case '-': gc = removedGC; break;
163
case '+': gc = addedGC; break;
164
case '@': gc = infoGC; break;
165
default: gc = normalGC; break;
168
if (p.HasValue && p.Value.X >= cell_area.X && p.Value.X <= cell_area.Right && p.Value.Y >= y && p.Value.Y < y + lineHeight) {
169
window.DrawRectangle (widget.Style.BaseGC (Gtk.StateType.Prelight), true, cell_area.X, y, cell_area.Width - 1, lineHeight);
170
selectedLine = thisLine;
199
case '-': type = BlockType.Removed; break;
200
case '+': type = BlockType.Added; break;
201
case '@': type = BlockType.Info; break;
202
default: type = BlockType.Unchanged; break;
205
if (currentBlock == null || type != currentBlock.Type) {
209
// Starting a new block. Mark section ends between a change block and a normal code block
210
if (currentBlock != null && IsChangeBlock (currentBlock.Type) && !IsChangeBlock (type))
211
currentBlock.SectionEnd = true;
213
currentBlock = new BlockInfo () {
217
SourceLineStart = cline,
218
SectionStart = (blocks.Count == 0 || !IsChangeBlock (blocks[blocks.Count - 1].Type)) && IsChangeBlock (type)
220
blocks.Add (currentBlock);
222
// Include the line in the current block
223
currentBlock.YEnd = y + lineHeight;
224
currentBlock.LastLine = n;
227
// Now render the blocks
229
// The y position of the highlighted line
230
int selectedLineRowTop = -1;
232
BlockInfo lastCodeSegmentStart = null;
233
BlockInfo lastCodeSegmentEnd = null;
235
foreach (BlockInfo block in blocks)
237
if (block.Type == BlockType.Info) {
238
// Finished drawing the content of a code segment. Now draw the segment border and label.
239
if (lastCodeSegmentStart != null)
240
DrawCodeSegmentBorder (infoGC, ctx, cell_area.X, cell_area.Width, lastCodeSegmentStart, lastCodeSegmentEnd, lines, widget, window);
241
lastCodeSegmentStart = block;
244
lastCodeSegmentEnd = block;
249
// Draw the block background
250
DrawBlockBg (ctx, cell_area.X + 1, cell_area.Width - 2, block);
252
// Get all text for the current block
253
StringBuilder sb = new StringBuilder ();
254
for (int n=block.FirstLine; n <= block.LastLine; n++) {
255
string s = ProcessLine (lines [n]);
256
if (n > block.FirstLine)
258
if (block.Type != BlockType.Info && s.Length > 0)
259
sb.Append (s, 1, s.Length - 1);
264
// Draw a special background for the selected line
266
if (block.Type != BlockType.Info && p.HasValue && p.Value.X >= cell_area.X && p.Value.X <= cell_area.Right && p.Value.Y >= block.YStart && p.Value.Y <= block.YEnd) {
267
int row = (p.Value.Y - block.YStart) / lineHeight;
268
double yrow = block.YStart + lineHeight * row + 0.5;
269
double xrow = cell_area.X + LeftPaddingBlock + 0.5;
270
int wrow = cell_area.Width - 1 - LeftPaddingBlock;
271
if (block.Type == BlockType.Added)
272
ctx.Color = baseAddColor.AddLight (0.1).ToCairoColor ();
273
else if (block.Type == BlockType.Removed)
274
ctx.Color = baseRemoveColor.AddLight (0.1).ToCairoColor ();
276
ctx.Color = widget.Style.Base (Gtk.StateType.Prelight).AddLight (0.1).ToCairoColor ();
277
xrow -= LeftPaddingBlock;
278
wrow += LeftPaddingBlock;
280
ctx.Rectangle (xrow, yrow, wrow, lineHeight);
282
selectedLine = block.SourceLineStart + row;
171
283
selctedPath = path;
174
layout.SetText (line);
175
window.DrawLayout (gc, cell_area.X + 2, y, layout);
284
selectedLineRowTop = (int)yrow;
287
// Draw the line text. Ignore header blocks, since they are drawn as labels in DrawCodeSegmentBorder
289
if (block.Type != BlockType.Info) {
290
layout.SetMarkup ("");
291
layout.SetText (sb.ToString ());
293
switch (block.Type) {
294
case BlockType.Removed: gc = removedGC; break;
295
case BlockType.Added: gc = addedGC; break;
296
case BlockType.Info: gc = infoGC; break;
297
default: gc = normalGC; break;
299
window.DrawLayout (gc, cell_area.X + 2 + LeftPaddingBlock, block.YStart, layout);
302
// Finally draw the change symbol at the left margin
304
DrawChangeSymbol (ctx, cell_area.X + 1, cell_area.Width - 2, block);
177
window.DrawRectangle (widget.Style.DarkGC (Gtk.StateType.Prelight), false, cell_area.X, recty, cell_area.Width - 1, recth);
307
// Finish the drawing of the code segment
308
if (lastCodeSegmentStart != null)
309
DrawCodeSegmentBorder (infoGC, ctx, cell_area.X, cell_area.Width, lastCodeSegmentStart, lastCodeSegmentEnd, lines, widget, window);
311
// Draw the source line number at the current selected line. It must be done at the end because it must
312
// be drawn over the source code text and segment borders.
313
if (selectedLineRowTop != -1)
314
DrawLineBox (normalGC, ctx, ((Gtk.TreeView)widget).VisibleRect.Right - 4, selectedLineRowTop, selectedLine, widget, window);
316
((IDisposable)ctx).Dispose ();
178
317
removedGC.Dispose ();
179
318
addedGC.Dispose ();
180
319
infoGC.Dispose ();
321
// Rendering a normal text row
182
322
int y = cell_area.Y + (cell_area.Height - height)/2;
183
323
window.DrawLayout (widget.Style.TextGC (GetState(flags)), cell_area.X, y, layout);
327
bool IsChangeBlock (BlockType t)
329
return t == BlockType.Added || t == BlockType.Removed;
334
public BlockType Type;
337
public int FirstLine;
339
public bool SectionStart;
340
public bool SectionEnd;
341
public int SourceLineStart;
352
void DrawCodeSegmentBorder (Gdk.GC gc, Cairo.Context ctx, double x, int width, BlockInfo firstBlock, BlockInfo lastBlock, string[] lines, Gtk.Widget widget, Gdk.Drawable window)
356
int bottomSpacing = (lineHeight - spacing) / 2;
358
ctx.Rectangle (x + shadowSize + 0.5, firstBlock.YStart + bottomSpacing + spacing - shadowSize + 0.5, width - shadowSize*2, shadowSize);
359
ctx.Color = new Cairo.Color (0.9, 0.9, 0.9);
363
ctx.Rectangle (x + shadowSize + 0.5, lastBlock.YEnd + bottomSpacing + 0.5, width - shadowSize*2, shadowSize);
364
ctx.Color = new Cairo.Color (0.9, 0.9, 0.9);
367
ctx.Rectangle (x + 0.5, firstBlock.YStart + bottomSpacing + spacing + 0.5, width, lastBlock.YEnd - firstBlock.YStart - spacing);
368
ctx.Color = new Cairo.Color (0.7,0.7,0.7);
371
string text = lines[firstBlock.FirstLine].Replace ("@","").Replace ("-","");
372
text = "<span size='x-small'>" + text.Replace ("+","</span><span size='small'>➜</span><span size='x-small'> ") + "</span>";
375
layout.SetMarkup (text);
377
layout.GetPixelSize (out tw, out th);
380
int dy = (lineHeight - th) / 2;
382
ctx.Rectangle (x + 2 + LeftPaddingBlock - 1 + 0.5, firstBlock.YStart + dy - 1 + 0.5, tw + 2, th + 2);
384
ctx.Color = widget.Style.Base (Gtk.StateType.Normal).ToCairoColor ();
386
ctx.Color = new Cairo.Color (0.7, 0.7, 0.7);
389
window.DrawLayout (gc, (int)(x + 2 + LeftPaddingBlock), firstBlock.YStart + dy, layout);
392
void DrawLineBox (Gdk.GC gc, Cairo.Context ctx, int right, int top, int line, Gtk.Widget widget, Gdk.Drawable window)
395
layout.SetMarkup ("<small>" + line.ToString () + "</small>");
397
layout.GetPixelSize (out tw, out th);
400
int dy = (lineHeight - th) / 2;
402
ctx.Rectangle (right - tw - 2 + 0.5, top + dy - 1 + 0.5, tw + 2, th + 2);
404
ctx.Color = widget.Style.Base (Gtk.StateType.Normal).ToCairoColor ();
406
ctx.Color = new Cairo.Color (0.7, 0.7, 0.7);
409
window.DrawLayout (gc, right - tw - 1, top + dy, layout);
412
void DrawBlockBg (Cairo.Context ctx, double x, int width, BlockInfo block)
414
if (!IsChangeBlock (block.Type))
418
Gdk.Color color = block.Type == BlockType.Added ? baseAddColor : baseRemoveColor;
419
double y = block.YStart + 0.5;
420
int height = block.YEnd - block.YStart;
422
double markerx = x + LeftPaddingBlock - 0.5;
423
double rd = RoundedSectionRadius;
424
if (block.SectionStart) {
425
ctx.Arc (x + rd, y + rd, rd, 180 * (Math.PI / 180), 270 * (Math.PI / 180));
426
ctx.LineTo (markerx, y);
428
ctx.MoveTo (markerx, y);
431
ctx.LineTo (markerx, y + height);
433
if (block.SectionEnd) {
434
ctx.LineTo (x + rd, y + height);
435
ctx.Arc (x + rd, y + height - rd, rd, 90 * (Math.PI / 180), 180 * (Math.PI / 180));
437
ctx.LineTo (x, y + height);
439
if (block.SectionStart) {
440
ctx.LineTo (x, y + rd);
444
ctx.Color = color.AddLight (0.1).ToCairoColor ();
447
ctx.Rectangle (markerx, y, width - markerx, height);
448
using (Cairo.Gradient pat = new Cairo.LinearGradient (x, y, x + width, y)) {
449
pat.AddColorStop (0, color.AddLight (0.21).ToCairoColor ());
450
pat.AddColorStop (1, color.AddLight (0.3).ToCairoColor ());
456
void DrawChangeSymbol (Cairo.Context ctx, double x, int width, BlockInfo block)
458
if (!IsChangeBlock (block.Type))
461
Gdk.Color color = block.Type == BlockType.Added ? baseAddColor : baseRemoveColor;
466
if (ssize - 2 > lineHeight)
467
ssize = lineHeight - 2;
471
double inSize = (ssize / 2) - (barSize / 2);
472
double py = block.YStart + ((block.YEnd - block.YStart) / 2 - ssize / 2) + 0.5;
473
double px = x + (LeftPaddingBlock/2) - (ssize / 2) + 0.5;
475
if (block.Type == BlockType.Added) {
476
ctx.MoveTo (px + inSize, py);
477
ctx.RelLineTo (barSize, 0);
478
ctx.RelLineTo (0, inSize);
479
ctx.RelLineTo (inSize, 0);
480
ctx.RelLineTo (0, barSize);
481
ctx.RelLineTo (-inSize, 0);
482
ctx.RelLineTo (0, inSize);
483
ctx.RelLineTo (-barSize, 0);
484
ctx.RelLineTo (0, -inSize);
485
ctx.RelLineTo (-inSize, 0);
486
ctx.RelLineTo (0, -barSize);
487
ctx.RelLineTo (inSize, 0);
488
ctx.RelLineTo (0, -inSize);
491
ctx.MoveTo (px, py + inSize);
492
ctx.RelLineTo (ssize, 0);
493
ctx.RelLineTo (0, barSize);
494
ctx.RelLineTo (-ssize, 0);
495
ctx.RelLineTo (0, -barSize);
499
ctx.Color = color.ToCairoColor ();
501
ctx.Color = color.AddLight (-0.2).ToCairoColor ();;
187
506
public override void GetSize (Widget widget, ref Rectangle cell_area, out int x_offset, out int y_offset, out int c_width, out int c_height)
189
508
x_offset = y_offset = 0;