/* * This file is part of gitg * * Copyright (C) 2012 - Jesse van den Kieboom * * gitg is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * gitg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with gitg. If not, see . */ namespace Gitg { public class CellRendererLanes : Gtk.CellRendererText { public Commit? commit { get; set; } public Commit? next_commit { get; set; } public uint lane_width { get; set; default = 16; } public uint dot_width { get; set; default = 10; } public unowned SList labels { get; set; } private int d_last_height; private delegate double DirectionFunc(double i); private uint num_visible_lanes { get { int ret = 0; int trailing_hidden = 0; foreach (var lane in commit.get_lanes()) { ++ret; if ((lane.tag & LaneTag.HIDDEN) != 0) { trailing_hidden++; } else { trailing_hidden = 0; } } return ret - trailing_hidden; } } private uint total_width(Gtk.Widget widget) { return num_visible_lanes * lane_width + LabelRenderer.width(widget, font_desc, labels); } public override void get_preferred_width(Gtk.Widget widget, out int minimum_width, out int natural_width) { base.get_preferred_width(widget, out minimum_width, out natural_width); var w = (int)total_width(widget); if (w > minimum_width) { minimum_width = w; } } private void draw_arrow(Cairo.Context context, Gdk.Rectangle area, uint laneidx, bool top) { double cw = lane_width; double xpos = area.x + laneidx * cw + cw / 2.0; double df = (top ? -1 : 1) * 0.25 * area.height; double ypos = area.y + area.height / 2.0 + df; double q = cw / 4.0; context.move_to(xpos - q, ypos + (top ? q : -q)); context.line_to(xpos, ypos); context.line_to(xpos + q, ypos + (top ? q : -q)); context.stroke(); context.move_to(xpos, ypos); context.line_to(xpos, ypos - df); context.stroke(); } private void draw_arrows(Cairo.Context context, Gdk.Rectangle area) { uint to = 0; foreach (var lane in commit.get_lanes()) { var color = lane.color; context.set_source_rgb(color.r, color.g, color.b); if (lane.tag == LaneTag.START) { draw_arrow(context, area, to, true); } else if (lane.tag == LaneTag.END) { draw_arrow(context, area, to, false); } ++to; } } private void draw_paths_real(Cairo.Context context, Gdk.Rectangle area, Commit? commit, DirectionFunc f, double yoffset) { if (commit == null) { return; } int to = 0; double cw = lane_width; double ch = area.height / 2.0; foreach (var lane in commit.get_lanes()) { if ((lane.tag & LaneTag.HIDDEN) != 0) { ++to; continue; } var color = lane.color; context.set_source_rgb(color.r, color.g, color.b); foreach (var from in lane.from) { double x1 = area.x + f(from * cw + cw / 2.0); double x2 = area.x + f(to * cw + cw / 2.0); double y1 = area.y + yoffset * ch; double y2 = area.y + (yoffset + 1) * ch; double y3 = area.y + (yoffset + 2) * ch; context.move_to(x1, y1); context.curve_to(x1, y2, x2, y2, x2, y3); context.stroke(); } ++to; } } private void draw_top_paths(Cairo.Context context, Gdk.Rectangle area, DirectionFunc f) { draw_paths_real(context, area, commit, f, -1); } private void draw_bottom_paths(Cairo.Context context, Gdk.Rectangle area, DirectionFunc f) { draw_paths_real(context, area, next_commit, f, 1); } private void draw_paths(Cairo.Context context, Gdk.Rectangle area, DirectionFunc f) { context.set_line_width(2.0); context.set_line_cap(Cairo.LineCap.ROUND); context.save(); draw_top_paths(context, area, f); draw_bottom_paths(context, area, f); draw_arrows(context, area); context.restore(); } private void draw_indicator(Cairo.Context context, Gdk.Rectangle area, DirectionFunc f) { double offset; double radius; offset = commit.mylane * lane_width + (lane_width - dot_width) / 2.0; radius = dot_width / 2.0; context.set_line_width(2.0); context.arc(area.x + f(offset + radius), area.y + area.height / 2.0, radius, 0, 2 * Math.PI); context.set_source_rgb(0, 0, 0); context.stroke_preserve(); if (commit.lane != null) { var color = commit.lane.color; context.set_source_rgb(color.r, color.g, color.b); } context.fill(); } private void draw_labels(Cairo.Context context, Gtk.Widget widget, Gdk.Rectangle area) { int offset; offset = (int)(num_visible_lanes * lane_width); var rtl = (widget.get_style_context().get_state() & Gtk.StateFlags.DIR_RTL) != 0; if (rtl) { offset = -offset; } context.save(); context.translate(offset, 0); LabelRenderer.draw(widget, font_desc, context, labels, area); context.restore(); } private void draw_lane(Cairo.Context context, Gtk.Widget widget, Gdk.Rectangle area) { DirectionFunc f; var rtl = (widget.get_style_context().get_state() & Gtk.StateFlags.DIR_RTL) != 0; context.save(); if (rtl) { context.translate(area.width, 0); f = (a) => -a; } else { f = (a) => a; } draw_paths(context, area, f); draw_indicator(context, area, f); context.restore(); } public override void render(Cairo.Context context, Gtk.Widget widget, Gdk.Rectangle area, Gdk.Rectangle cell_area, Gtk.CellRendererState flags) { var ncell_area = cell_area; var narea = area; var rtl = (widget.get_style_context().get_state() & Gtk.StateFlags.DIR_RTL) != 0; d_last_height = area.height; if (commit != null) { context.save(); Gdk.cairo_rectangle(context, area); context.clip(); draw_lane(context, widget, area); draw_labels(context, widget, area); var tw = total_width(widget); if (!rtl) { narea.x += (int)tw; ncell_area.x += (int)tw; } else { narea.width -= (int)tw; ncell_area.width -= (int)tw; } context.restore(); } if (rtl == (Pango.find_base_dir(text, -1) != Pango.Direction.RTL)) { xalign = 1.0f; } base.render(context, widget, narea, ncell_area, flags); } public Ref? get_ref_at_pos(Gtk.Widget widget, int x, int cell_w, out int hot_x) { var rtl = (widget.get_style_context().get_state() & Gtk.StateFlags.DIR_RTL) != 0; var offset = (int)(labels.length() * lane_width); if (rtl) { x = cell_w - x; } return LabelRenderer.get_ref_at_pos(widget, font_desc, labels, x - offset, out hot_x); } } } // ex:ts=4 noet