AbstractPlotRenderer.java

  1. /*
  2.  * Copyright (C) 2008, 2014 Shawn O. Pearce <spearce@spearce.org> and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.revplot;

  11. import org.eclipse.jgit.lib.Ref;
  12. import org.eclipse.jgit.revwalk.RevFlag;

  13. /**
  14.  * Basic commit graph renderer for graphical user interfaces.
  15.  * <p>
  16.  * Lanes are drawn as columns left-to-right in the graph, and the commit short
  17.  * message is drawn to the right of the lane lines for this cell. It is assumed
  18.  * that the commits are being drawn as rows of some sort of table.
  19.  * <p>
  20.  * Client applications can subclass this implementation to provide the necessary
  21.  * drawing primitives required to display a commit graph. Most of the graph
  22.  * layout is handled by this class, allowing applications to implement only a
  23.  * handful of primitive stubs.
  24.  * <p>
  25.  * This class is suitable for us within an AWT TableCellRenderer or within a SWT
  26.  * PaintListener registered on a Table instance. It is meant to rubber stamp the
  27.  * graphics necessary for one row of a plotted commit list.
  28.  * <p>
  29.  * Subclasses should call {@link #paintCommit(PlotCommit, int)} after they have
  30.  * otherwise configured their instance to draw one commit into the current
  31.  * location.
  32.  * <p>
  33.  * All drawing methods assume the coordinate space for the current commit's cell
  34.  * starts at (upper left corner is) 0,0. If this is not true (like say in SWT)
  35.  * the implementation must perform the cell offset computations within the
  36.  * various draw methods.
  37.  *
  38.  * @param <TLane>
  39.  *            type of lane being used by the application.
  40.  * @param <TColor>
  41.  *            type of color object used by the graphics library.
  42.  */
  43. public abstract class AbstractPlotRenderer<TLane extends PlotLane, TColor> {
  44.     private static final int LANE_WIDTH = 14;

  45.     private static final int LINE_WIDTH = 2;

  46.     private static final int LEFT_PAD = 2;

  47.     /**
  48.      * Paint one commit using the underlying graphics library.
  49.      *
  50.      * @param commit
  51.      *            the commit to render in this cell. Must not be null.
  52.      * @param h
  53.      *            total height (in pixels) of this cell.
  54.      */
  55.     @SuppressWarnings("unchecked")
  56.     protected void paintCommit(PlotCommit<TLane> commit, int h) {
  57.         final int dotSize = computeDotSize(h);
  58.         final TLane myLane = commit.getLane();
  59.         final int myLaneX = laneC(myLane);
  60.         final TColor myColor = laneColor(myLane);

  61.         int maxCenter = myLaneX;
  62.         for (TLane passingLane : (TLane[]) commit.passingLanes) {
  63.             final int cx = laneC(passingLane);
  64.             final TColor c = laneColor(passingLane);
  65.             drawLine(c, cx, 0, cx, h, LINE_WIDTH);
  66.             maxCenter = Math.max(maxCenter, cx);
  67.         }

  68.         final int dotX = myLaneX - dotSize / 2 - 1;
  69.         final int dotY = (h - dotSize) / 2;

  70.         final int nParent = commit.getParentCount();
  71.         if (nParent > 0) {
  72.             drawLine(myColor, myLaneX, h, myLaneX, (h + dotSize) / 2,
  73.                     LINE_WIDTH);

  74.             for (PlotLane mergingLane : commit.mergingLanes) {
  75.                 final TLane pLane = (TLane) mergingLane;
  76.                 final TColor pColor = laneColor(pLane);
  77.                 final int cx = laneC(pLane);
  78.                 if (Math.abs(myLaneX - cx) > LANE_WIDTH) {
  79.                     final int ix;
  80.                     if (myLaneX < cx) {
  81.                         ix = cx - LANE_WIDTH / 2;
  82.                     } else {
  83.                         ix = cx + LANE_WIDTH / 2;
  84.                     }

  85.                     drawLine(pColor, myLaneX, h / 2, ix, h / 2, LINE_WIDTH);
  86.                     drawLine(pColor, ix, h / 2, cx, h, LINE_WIDTH);
  87.                 } else
  88.                     drawLine(pColor, myLaneX, h / 2, cx, h, LINE_WIDTH);
  89.                 maxCenter = Math.max(maxCenter, cx);
  90.             }
  91.         }


  92.         if (commit.getChildCount() > 0) {
  93.             for (PlotLane forkingOffLane : commit.forkingOffLanes) {
  94.                 final TLane childLane = (TLane) forkingOffLane;
  95.                 final TColor cColor = laneColor(childLane);
  96.                 final int cx = laneC(childLane);
  97.                 if (Math.abs(myLaneX - cx) > LANE_WIDTH) {
  98.                     final int ix;
  99.                     if (myLaneX < cx) {
  100.                         ix = cx - LANE_WIDTH / 2;
  101.                     } else {
  102.                         ix = cx + LANE_WIDTH / 2;
  103.                     }

  104.                     drawLine(cColor, myLaneX, h / 2, ix, h / 2, LINE_WIDTH);
  105.                     drawLine(cColor, ix, h / 2, cx, 0, LINE_WIDTH);
  106.                 } else {
  107.                     drawLine(cColor, myLaneX, h / 2, cx, 0, LINE_WIDTH);
  108.                 }
  109.                 maxCenter = Math.max(maxCenter, cx);
  110.             }

  111.             int nonForkingChildren = commit.getChildCount()
  112.                     - commit.forkingOffLanes.length;
  113.             if (nonForkingChildren > 0)
  114.                         drawLine(myColor, myLaneX, 0, myLaneX, dotY, LINE_WIDTH);
  115.         }

  116.         if (commit.has(RevFlag.UNINTERESTING))
  117.             drawBoundaryDot(dotX, dotY, dotSize, dotSize);
  118.         else
  119.             drawCommitDot(dotX, dotY, dotSize, dotSize);

  120.         int textx = Math.max(maxCenter + LANE_WIDTH / 2, dotX + dotSize) + 8;
  121.         int n = commit.refs.length;
  122.         for (int i = 0; i < n; ++i) {
  123.             textx += drawLabel(textx + dotSize, h/2, commit.refs[i]);
  124.         }

  125.         final String msg = commit.getShortMessage();
  126.         drawText(msg, textx + dotSize, h);
  127.     }

  128.     /**
  129.      * Draw a decoration for the Ref ref at x,y
  130.      *
  131.      * @param x
  132.      *            left
  133.      * @param y
  134.      *            top
  135.      * @param ref
  136.      *            A peeled ref
  137.      * @return width of label in pixels
  138.      */
  139.     protected abstract int drawLabel(int x, int y, Ref ref);

  140.     private static int computeDotSize(int h) {
  141.         int d = (int) (Math.min(h, LANE_WIDTH) * 0.50f);
  142.         d += (d & 1);
  143.         return d;
  144.     }

  145.     /**
  146.      * Obtain the color reference used to paint this lane.
  147.      * <p>
  148.      * Colors returned by this method will be passed to the other drawing
  149.      * primitives, so the color returned should be application specific.
  150.      * <p>
  151.      * If a null lane is supplied the return value must still be acceptable to a
  152.      * drawing method. Usually this means the implementation should return a
  153.      * default color.
  154.      *
  155.      * @param myLane
  156.      *            the current lane. May be null.
  157.      * @return graphics specific color reference. Must be a valid color.
  158.      */
  159.     protected abstract TColor laneColor(TLane myLane);

  160.     /**
  161.      * Draw a single line within this cell.
  162.      *
  163.      * @param color
  164.      *            the color to use while drawing the line.
  165.      * @param x1
  166.      *            starting X coordinate, 0 based.
  167.      * @param y1
  168.      *            starting Y coordinate, 0 based.
  169.      * @param x2
  170.      *            ending X coordinate, 0 based.
  171.      * @param y2
  172.      *            ending Y coordinate, 0 based.
  173.      * @param width
  174.      *            number of pixels wide for the line. Always at least 1.
  175.      */
  176.     protected abstract void drawLine(TColor color, int x1, int y1, int x2,
  177.             int y2, int width);

  178.     /**
  179.      * Draw a single commit dot.
  180.      * <p>
  181.      * Usually the commit dot is a filled oval in blue, then a drawn oval in
  182.      * black, using the same coordinates for both operations.
  183.      *
  184.      * @param x
  185.      *            upper left of the oval's bounding box.
  186.      * @param y
  187.      *            upper left of the oval's bounding box.
  188.      * @param w
  189.      *            width of the oval's bounding box.
  190.      * @param h
  191.      *            height of the oval's bounding box.
  192.      */
  193.     protected abstract void drawCommitDot(int x, int y, int w, int h);

  194.     /**
  195.      * Draw a single boundary commit (aka uninteresting commit) dot.
  196.      * <p>
  197.      * Usually a boundary commit dot is a light gray oval with a white center.
  198.      *
  199.      * @param x
  200.      *            upper left of the oval's bounding box.
  201.      * @param y
  202.      *            upper left of the oval's bounding box.
  203.      * @param w
  204.      *            width of the oval's bounding box.
  205.      * @param h
  206.      *            height of the oval's bounding box.
  207.      */
  208.     protected abstract void drawBoundaryDot(int x, int y, int w, int h);

  209.     /**
  210.      * Draw a single line of text.
  211.      * <p>
  212.      * The font and colors used to render the text are left up to the
  213.      * implementation.
  214.      *
  215.      * @param msg
  216.      *            the text to draw. Does not contain LFs.
  217.      * @param x
  218.      *            first pixel from the left that the text can be drawn at.
  219.      *            Character data must not appear before this position.
  220.      * @param y
  221.      *            pixel coordinate of the baseline of the text. Implementations
  222.      *            must adjust this coordinate to account for the way their
  223.      *            implementation handles font rendering.
  224.      */
  225.     protected abstract void drawText(String msg, int x, int y);

  226.     private static int laneX(PlotLane myLane) {
  227.         final int p = myLane != null ? myLane.getPosition() : 0;
  228.         return LEFT_PAD + LANE_WIDTH * p;
  229.     }

  230.     private static int laneC(PlotLane myLane) {
  231.         return laneX(myLane) + LANE_WIDTH / 2;
  232.     }
  233. }