protected override void Render(Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags) { if (isDisposed) { return; } if (diffMode) { if (path.Equals(selctedPath)) { selectedLine = -1; selctedPath = null; } int w, maxy; window.GetSize(out w, out maxy); if (DrawLeft) { cell_area.Width += cell_area.X - leftSpace; cell_area.X = leftSpace; } var treeview = widget as FileTreeView; var p = treeview != null? treeview.CursorLocation : null; cell_area.Width -= RightPadding; window.DrawRectangle(widget.Style.BaseGC(Gtk.StateType.Normal), true, cell_area.X, cell_area.Y, cell_area.Width - 1, cell_area.Height); Gdk.GC normalGC = widget.Style.TextGC(StateType.Normal); Gdk.GC removedGC = new Gdk.GC(window); removedGC.Copy(normalGC); removedGC.RgbFgColor = Styles.LogView.DiffRemoveBackgroundColor.AddLight(-0.3).ToGdkColor(); Gdk.GC addedGC = new Gdk.GC(window); addedGC.Copy(normalGC); addedGC.RgbFgColor = Styles.LogView.DiffAddBackgroundColor.AddLight(-0.3).ToGdkColor(); Gdk.GC infoGC = new Gdk.GC(window); infoGC.Copy(normalGC); infoGC.RgbFgColor = widget.Style.Text(StateType.Normal).AddLight(0.2); Cairo.Context ctx = CairoHelper.Create(window); // Rendering is done in two steps: // 1) Get a list of blocks to render // 2) render the blocks int y = cell_area.Y + 2; // cline keeps track of the current source code line (the one to jump to when double clicking) int cline = 1; bool inHeader = true; BlockInfo currentBlock = null; List <BlockInfo> blocks = new List <BlockInfo> (); for (int n = 0; n < lines.Length; n++, y += lineHeight) { string line = lines [n]; if (line.Length == 0) { currentBlock = null; y -= lineHeight; continue; } char tag = line [0]; if (line.StartsWith("---", StringComparison.Ordinal) || line.StartsWith("+++", StringComparison.Ordinal)) { // Ignore this part of the header. currentBlock = null; y -= lineHeight; continue; } if (tag == '@') { int l = ParseCurrentLine(line); if (l != -1) { cline = l - 1; } inHeader = false; } else if (tag == '+' && !inHeader) { cline++; } BlockType type; switch (tag) { case '-': type = BlockType.Removed; break; case '+': type = BlockType.Added; break; case '@': type = BlockType.Info; break; default: type = BlockType.Unchanged; break; } if (currentBlock == null || type != currentBlock.Type) { if (y > maxy) { break; } // Starting a new block. Mark section ends between a change block and a normal code block if (currentBlock != null && IsChangeBlock(currentBlock.Type) && !IsChangeBlock(type)) { currentBlock.SectionEnd = true; } currentBlock = new BlockInfo() { YStart = y, FirstLine = n, Type = type, SourceLineStart = cline, SectionStart = (blocks.Count == 0 || !IsChangeBlock(blocks[blocks.Count - 1].Type)) && IsChangeBlock(type) }; blocks.Add(currentBlock); } // Include the line in the current block currentBlock.YEnd = y + lineHeight; currentBlock.LastLine = n; } // Now render the blocks // The y position of the highlighted line int selectedLineRowTop = -1; BlockInfo lastCodeSegmentStart = null; BlockInfo lastCodeSegmentEnd = null; foreach (BlockInfo block in blocks) { if (block.Type == BlockType.Info) { // Finished drawing the content of a code segment. Now draw the segment border and label. if (lastCodeSegmentStart != null) { DrawCodeSegmentBorder(infoGC, ctx, cell_area.X, cell_area.Width, lastCodeSegmentStart, lastCodeSegmentEnd, lines, widget, window); } lastCodeSegmentStart = block; } lastCodeSegmentEnd = block; if (block.YEnd < 0) { continue; } // Draw the block background DrawBlockBg(ctx, cell_area.X + 1, cell_area.Width - 2, block); // Get all text for the current block StringBuilder sb = new StringBuilder(); for (int n = block.FirstLine; n <= block.LastLine; n++) { string s = ProcessLine(lines [n]); if (n > block.FirstLine) { sb.Append('\n'); } if ((block.Type == BlockType.Added || block.Type == BlockType.Removed) && s.Length > 0) { sb.Append(' '); sb.Append(s, 1, s.Length - 1); } else { sb.Append(s); } } // Draw a special background for the selected line 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) { int row = (p.Value.Y - block.YStart) / lineHeight; double yrow = block.YStart + lineHeight * row; double xrow = cell_area.X + LeftPaddingBlock; int wrow = cell_area.Width - 1 - LeftPaddingBlock; if (block.Type == BlockType.Added) { ctx.SetSourceColor(Styles.LogView.DiffAddBackgroundColor.AddLight(0.1).ToCairoColor()); } else if (block.Type == BlockType.Removed) { ctx.SetSourceColor(Styles.LogView.DiffRemoveBackgroundColor.AddLight(0.1).ToCairoColor()); } else { ctx.SetSourceColor(Styles.LogView.DiffHighlightColor.ToCairoColor()); xrow -= LeftPaddingBlock; wrow += LeftPaddingBlock; } ctx.Rectangle(xrow, yrow, wrow, lineHeight); ctx.Fill(); selectedLine = block.SourceLineStart + row; selctedPath = path; selectedLineRowTop = (int)yrow; } // Draw the line text. Ignore header blocks, since they are drawn as labels in DrawCodeSegmentBorder if (block.Type != BlockType.Info) { layout.SetMarkup(""); layout.SetText(sb.ToString()); Gdk.GC gc; switch (block.Type) { case BlockType.Removed: gc = removedGC; break; case BlockType.Added: gc = addedGC; break; case BlockType.Info: gc = infoGC; break; default: gc = normalGC; break; } window.DrawLayout(gc, cell_area.X + 2 + LeftPaddingBlock, block.YStart, layout); } // Finally draw the change symbol at the left margin DrawChangeSymbol(ctx, widget, cell_area.X + 1, cell_area.Width - 2, block); } // Finish the drawing of the code segment if (lastCodeSegmentStart != null) { DrawCodeSegmentBorder(infoGC, ctx, cell_area.X, cell_area.Width, lastCodeSegmentStart, lastCodeSegmentEnd, lines, widget, window); } // Draw the source line number at the current selected line. It must be done at the end because it must // be drawn over the source code text and segment borders. if (selectedLineRowTop != -1) { DrawLineBox(normalGC, ctx, ((Gtk.TreeView)widget).VisibleRect.Right - 4, selectedLineRowTop, selectedLine, widget, window); } ((IDisposable)ctx).Dispose(); removedGC.Dispose(); addedGC.Dispose(); infoGC.Dispose(); } else { // Rendering a normal text row int y = cell_area.Y + (cell_area.Height - height) / 2; window.DrawLayout(widget.Style.TextGC(GetState(widget, flags)), cell_area.X, y, layout); } }
protected override void Render(Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags) { if (isDisposed || layout == null) { return; } if (diffMode) { if (path.Equals(selctedPath)) { selectedLine = -1; selctedPath = null; } int w, maxy; window.GetSize(out w, out maxy); if (DrawLeft) { cell_area.Width += cell_area.X - leftSpace; cell_area.X = leftSpace; } var treeview = widget as FileTreeView; var p = treeview != null? treeview.CursorLocation : null; cell_area.Width -= RightPadding; window.DrawRectangle(widget.Style.BaseGC(Gtk.StateType.Normal), true, cell_area.X, cell_area.Y, cell_area.Width - 1, cell_area.Height); Gdk.GC normalGC = widget.Style.TextGC(StateType.Normal); Gdk.GC removedGC = new Gdk.GC(window); removedGC.Copy(normalGC); removedGC.RgbFgColor = Styles.LogView.DiffRemoveBackgroundColor.AddLight(-0.3).ToGdkColor(); Gdk.GC addedGC = new Gdk.GC(window); addedGC.Copy(normalGC); addedGC.RgbFgColor = Styles.LogView.DiffAddBackgroundColor.AddLight(-0.3).ToGdkColor(); Gdk.GC infoGC = new Gdk.GC(window); infoGC.Copy(normalGC); infoGC.RgbFgColor = widget.Style.Text(StateType.Normal).AddLight(0.2); Cairo.Context ctx = CairoHelper.Create(window); // Rendering is done in two steps: // 1) Get a list of blocks to render // 2) render the blocks var blocks = CalculateBlocks(maxy, cell_area.Y + 2); // Now render the blocks // The y position of the highlighted line int selectedLineRowTop = -1; BlockInfo lastCodeSegmentStart = null; BlockInfo lastCodeSegmentEnd = null; foreach (BlockInfo block in blocks) { if (block.Type == BlockType.Info) { // Finished drawing the content of a code segment. Now draw the segment border and label. if (lastCodeSegmentStart != null) { DrawCodeSegmentBorder(infoGC, ctx, cell_area.X, cell_area.Width, lastCodeSegmentStart, lastCodeSegmentEnd, lines, widget, window); } lastCodeSegmentStart = block; } lastCodeSegmentEnd = block; if (block.YEnd < 0) { continue; } // Draw the block background DrawBlockBg(ctx, cell_area.X + 1, cell_area.Width - 2, block); // Get all text for the current block StringBuilder sb = new StringBuilder(); for (int n = block.FirstLine; n <= block.LastLine; n++) { string s = ProcessLine(lines [n]); if (n > block.FirstLine) { sb.Append('\n'); } if ((block.Type == BlockType.Added || block.Type == BlockType.Removed) && s.Length > 0) { sb.Append(' '); sb.Append(s, 1, s.Length - 1); } else { sb.Append(s); } } // Draw a special background for the selected line 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) { int row = (p.Value.Y - block.YStart) / lineHeight; double yrow = block.YStart + lineHeight * row; double xrow = cell_area.X + LeftPaddingBlock; int wrow = cell_area.Width - 1 - LeftPaddingBlock; if (block.Type == BlockType.Added) { ctx.SetSourceColor(Styles.LogView.DiffAddBackgroundColor.AddLight(0.1).ToCairoColor()); } else if (block.Type == BlockType.Removed) { ctx.SetSourceColor(Styles.LogView.DiffRemoveBackgroundColor.AddLight(0.1).ToCairoColor()); } else { ctx.SetSourceColor(Styles.LogView.DiffHighlightColor.ToCairoColor()); xrow -= LeftPaddingBlock; wrow += LeftPaddingBlock; } ctx.Rectangle(xrow, yrow, wrow, lineHeight); ctx.Fill(); selectedLine = block.SourceLineStart + row; selctedPath = path; selectedLineRowTop = (int)yrow; } // Draw the line text. Ignore header blocks, since they are drawn as labels in DrawCodeSegmentBorder if (block.Type != BlockType.Info) { layout.SetMarkup(""); layout.SetText(sb.ToString()); Gdk.GC gc; switch (block.Type) { case BlockType.Removed: gc = removedGC; break; case BlockType.Added: gc = addedGC; break; case BlockType.Info: gc = infoGC; break; default: gc = normalGC; break; } window.DrawLayout(gc, cell_area.X + 2 + LeftPaddingBlock, block.YStart, layout); } // Finally draw the change symbol at the left margin DrawChangeSymbol(ctx, widget, cell_area.X + 1, cell_area.Width - 2, block); } // Finish the drawing of the code segment if (lastCodeSegmentStart != null) { DrawCodeSegmentBorder(infoGC, ctx, cell_area.X, cell_area.Width, lastCodeSegmentStart, lastCodeSegmentEnd, lines, widget, window); } // Draw the source line number at the current selected line. It must be done at the end because it must // be drawn over the source code text and segment borders. if (selectedLineRowTop != -1) { DrawLineBox(normalGC, ctx, ((Gtk.TreeView)widget).VisibleRect.Right - 4, selectedLineRowTop, selectedLine, widget, window); } ((IDisposable)ctx).Dispose(); removedGC.Dispose(); addedGC.Dispose(); infoGC.Dispose(); } else { // Rendering a normal text row int y = cell_area.Y + (cell_area.Height - height) / 2; window.DrawLayout(widget.Style.TextGC(GetState(widget, flags)), cell_area.X, y, layout); } }
private void OnDataRowDeleted(object o, RowDeletedArgs args) { if (model == null || args.Path == null) { return; } bool sel_paths_changed = false; // Don't update the real n_cells, as doing this will // throw off ScrollToPath if called before SizeAllocate // is run int n_cells = model.IterNChildren(); for (int i = 0; i < selected_paths.Count; i++) { TreePath path = (TreePath)selected_paths[i]; int cmp = path.Compare(args.Path); if (cmp == 0) { selected_paths.RemoveAt(i); i--; sel_paths_changed = true; continue; } // decrement each path that follows the one we // just deleted if (cmp > 0) { path.Prev(); selected_paths[i] = path; continue; } } if (sel_paths_changed && SelectionChanged != null) { SelectionChanged(this, new EventArgs()); } if (focused_path != null && focused_path.Equals(args.Path)) { focused_path = focused_path.Copy(); // try to advance the focus forward focused_path.Next(); if (!PathIsValid(focused_path, n_cells) && !focused_path.Prev()) { focused_path = null; } } if (selection_anchor != null && selection_anchor.Equals(args.Path)) { selection_anchor = null; } QueueResize(); }
protected override void Render(Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags) { if (isDisposed) { return; } if (diffMode) { if (path.Equals(selctedPath)) { selectedLine = -1; selctedPath = null; } int w, maxy; window.GetSize(out w, out maxy); var treeview = widget as FileTreeView; var p = treeview != null? treeview.CursorLocation : null; int recty = cell_area.Y; int recth = cell_area.Height - 1; if (recty < 0) { recth += recty + 1; recty = -1; } if (recth > maxy + 2) { recth = maxy + 2; } window.DrawRectangle(widget.Style.BaseGC(Gtk.StateType.Normal), true, cell_area.X, recty, cell_area.Width - 1, recth); Gdk.GC normalGC = widget.Style.TextGC(StateType.Normal); Gdk.GC removedGC = new Gdk.GC(window); removedGC.Copy(normalGC); removedGC.RgbFgColor = new Color(255, 0, 0); Gdk.GC addedGC = new Gdk.GC(window); addedGC.Copy(normalGC); addedGC.RgbFgColor = new Color(0, 0, 255); Gdk.GC infoGC = new Gdk.GC(window); infoGC.Copy(normalGC); infoGC.RgbFgColor = new Color(0xa5, 0x2a, 0x2a); int y = cell_area.Y + 2; int cline = 1; bool inHeader = true; for (int n = 0; n < lines.Length; n++, y += lineHeight) { string line = lines [n]; if (line.Length == 0) { continue; } char tag = line [0]; // Keep track of the real file line int thisLine = cline; if (tag == '@') { int l = ParseCurrentLine(line); if (l != -1) { cline = thisLine = l; } inHeader = false; } else if (tag != '-' && !inHeader) { cline++; } if (y + lineHeight < 0) { continue; } if (y > maxy) { break; } Gdk.GC gc; switch (tag) { case '-': gc = removedGC; break; case '+': gc = addedGC; break; case '@': gc = infoGC; break; default: gc = normalGC; break; } if (p.HasValue && p.Value.X >= cell_area.X && p.Value.X <= cell_area.Right && p.Value.Y >= y && p.Value.Y < y + lineHeight) { window.DrawRectangle(widget.Style.BaseGC(Gtk.StateType.Prelight), true, cell_area.X, y, cell_area.Width - 1, lineHeight); selectedLine = thisLine; selctedPath = path; } layout.SetText(line); window.DrawLayout(gc, cell_area.X + 2, y, layout); } window.DrawRectangle(widget.Style.DarkGC(Gtk.StateType.Prelight), false, cell_area.X, recty, cell_area.Width - 1, recth); removedGC.Dispose(); addedGC.Dispose(); infoGC.Dispose(); } else { int y = cell_area.Y + (cell_area.Height - height) / 2; window.DrawLayout(widget.Style.TextGC(GetState(flags)), cell_area.X, y, layout); } }