From c8dfe17cd67c667e03d1e610c481f5ebb127985d Mon Sep 17 00:00:00 2001
From: "ruX[Ruslan Zaharov]" <post4ruX@gmail.com>
Date: Fri, 20 Dec 2013 13:58:18 +0400
Subject: [PATCH] Render new line char in legend items

---
 .../xeiam/xchart/internal/chartpart/Axis.java |   2 +-
 .../xchart/internal/chartpart/Legend.java     | 246 ++++++++++--------
 2 files changed, 141 insertions(+), 107 deletions(-)

diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
index fedda1fc..eea409ad 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
@@ -200,7 +200,7 @@ public class Axis implements ChartPart {
 
       double chartLegendWidth = 0;
       if (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE) {
-        chartLegendWidth = getChartPainter().getChartLegend().getSizeHint()[0];
+        chartLegendWidth = getChartPainter().getChartLegend().getSizeHint(g)[0];
       }
 
       double width =
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java
index f39c4172..33c17baa 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java
@@ -15,15 +15,17 @@
  */
 package com.xeiam.xchart.internal.chartpart;
 
-import java.awt.BasicStroke;
-import java.awt.Graphics2D;
-import java.awt.Shape;
-import java.awt.font.FontRenderContext;
-import java.awt.font.TextLayout;
+import java.awt.*;
+import java.awt.font.*;
 import java.awt.geom.Line2D;
 import java.awt.geom.Rectangle2D;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.util.*;
+import java.util.List;
 
 import com.xeiam.xchart.Series;
+import com.xeiam.xchart.StyleManager;
 import com.xeiam.xchart.StyleManager.ChartType;
 import com.xeiam.xchart.internal.markers.Marker;
 
@@ -35,15 +37,19 @@ public class Legend implements ChartPart {
   private static final int LEGEND_MARGIN = 6;
   private static final int BOX_SIZE = 20;
 
-  /** parent */
+  /**
+   * parent
+   */
   private final ChartPainter chartPainter;
 
-  /** the bounds */
+  /**
+   * the bounds
+   */
   private Rectangle2D bounds;
 
   /**
    * Constructor
-   * 
+   *
    * @param chartPainter
    */
   public Legend(ChartPainter chartPainter) {
@@ -53,57 +59,65 @@ public class Legend implements ChartPart {
 
   /**
    * get the width of the chart legend
-   * 
+   *
+   * @param g
    * @return
    */
-  protected double[] getSizeHint() {
+  protected double[] getSizeHint(Graphics2D g) {
+    if (!chartPainter.getStyleManager().isLegendVisible()) {
+      return new double[]{0, 0};
+    }
 
-    if (chartPainter.getStyleManager().isLegendVisible()) {
+    StyleManager styleManager = getChartPainter().getStyleManager();
+    FontMetrics fontMetrics = g.getFontMetrics(chartPainter.getStyleManager().getLegendFont());
+    boolean isBar = styleManager.getChartType() == ChartType.Bar;
 
-      // determine legend text content max width
-      double legendTextContentMaxWidth = 0;
-      double legendTextContentMaxHeight = 0;
+    // determine legend text content max width
+    double legendTextContentMaxWidth = 0;
+    double legendTextContentMaxHeight = 0;
 
-      for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
+    // determine legend content height
+    double legendContentHeight = 0;
 
-        TextLayout textLayout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false));
-        Rectangle2D rectangle = textLayout.getBounds();
-        // System.out.println(rectangle);
-        if (rectangle.getWidth() > legendTextContentMaxWidth) {
-          legendTextContentMaxWidth = rectangle.getWidth();
-        }
-        if (rectangle.getHeight() > legendTextContentMaxHeight) {
-          legendTextContentMaxHeight = rectangle.getHeight();
-        }
+    for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
+      List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesBounds(series, g);
+      double blockHeight = 0;
+      for (Map.Entry<String, Rectangle2D> entry : seriesBounds) {
+        blockHeight += entry.getValue().getHeight();
+        legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth());
       }
 
-      // determine legend content height
-      double maxContentHeight = 0;
-      if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
-        maxContentHeight = Math.max(legendTextContentMaxHeight, Marker.SIZE);
-      }
-      else {
-        maxContentHeight = Math.max(legendTextContentMaxHeight, BOX_SIZE);
-      }
-      double legendContentHeight =
-          maxContentHeight * getChartPainter().getAxisPair().getSeriesMap().size() + chartPainter.getStyleManager().getLegendPadding() * (getChartPainter().getAxisPair().getSeriesMap().size() - 1);
+      blockHeight = Math.max(blockHeight, isBar ? BOX_SIZE : Marker.SIZE);
 
-      // determine legend content width
-      double legendContentWidth = 0;
-      if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
-        legendContentWidth = getChartPainter().getStyleManager().getLegendSeriesLineLength() + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth;
-      }
-      else {
-        legendContentWidth = BOX_SIZE + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth;
-      }
-      // Legend Box
-      double legendBoxWidth = legendContentWidth + 2 * chartPainter.getStyleManager().getLegendPadding();
-      double legendBoxHeight = legendContentHeight + 2 * chartPainter.getStyleManager().getLegendPadding();
-      return new double[] { legendBoxWidth, legendBoxHeight, maxContentHeight };
+      legendTextContentMaxHeight = Math.max(legendTextContentMaxHeight, blockHeight);
+      legendContentHeight += blockHeight;
     }
-    else {
-      return new double[] { 0, 0, 0 };
+
+    // vertical padding between items
+    double paddingSize = isBar ? styleManager.getLegendPadding() : fontMetrics.getDescent();
+    legendContentHeight += paddingSize * (chartPainter.getAxisPair().getSeriesMap().size() - 1);
+
+    // determine legend content width
+    double legendContentWidth = 0;
+    if (!isBar) {
+      legendContentWidth = styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding() + legendTextContentMaxWidth;
+    } else {
+      legendContentWidth = BOX_SIZE + styleManager.getLegendPadding() + legendTextContentMaxWidth;
     }
+    // Legend Box
+    double legendBoxWidth = legendContentWidth + 2 * styleManager.getLegendPadding();
+    double legendBoxHeight = legendContentHeight + 2 * styleManager.getLegendPadding();
+    return new double[]{legendBoxWidth, legendBoxHeight};
+  }
+
+  private List<Map.Entry<String, Rectangle2D>> getSeriesBounds(Series series, Graphics2D g) {
+    String lines[] = series.getName().split("\\n");
+    List<Map.Entry<String, Rectangle2D>> stringBounds = new ArrayList<Map.Entry<String, Rectangle2D>>(lines.length);
+    for (String line : lines) {
+      Rectangle2D bounds = g.getFontMetrics().getStringBounds(line, g);
+      stringBounds.add(new AbstractMap.SimpleEntry<String, Rectangle2D>(line, bounds));
+    }
+    return stringBounds;
   }
 
   @Override
@@ -112,18 +126,23 @@ public class Legend implements ChartPart {
     bounds = new Rectangle2D.Double();
     g.setFont(chartPainter.getStyleManager().getLegendFont());
 
-    if (chartPainter.getStyleManager().isLegendVisible()) {
+    StyleManager styleManager = getChartPainter().getStyleManager();
+
+    if (!styleManager.isLegendVisible()) return;
+
+    final double[] sizeHint = getSizeHint(g);
 
-      double legendBoxWidth = getSizeHint()[0];
-      double legendBoxHeight = getSizeHint()[1];
-      double maxContentHeight = getSizeHint()[2];
+    double legendBoxWidth = sizeHint[0];
+    double legendBoxHeight = sizeHint[1];
 
-      // legend draw position
-      double xOffset = 0;
-      double yOffset = 0;
-      switch (chartPainter.getStyleManager().getLegendPosition()) {
+    FontMetrics fontMetrics = g.getFontMetrics(styleManager.getLegendFont());
+
+    // legend draw position
+    double xOffset = 0;
+    double yOffset = 0;
+    switch (styleManager.getLegendPosition()) {
       case OutsideE:
-        xOffset = chartPainter.getWidth() - legendBoxWidth - chartPainter.getStyleManager().getChartPadding();
+        xOffset = chartPainter.getWidth() - legendBoxWidth - styleManager.getChartPadding();
         yOffset = chartPainter.getPlot().getBounds().getY() + (chartPainter.getPlot().getBounds().getHeight() - legendBoxHeight) / 2.0;
         break;
       case InsideNW:
@@ -149,65 +168,80 @@ public class Legend implements ChartPart {
 
       default:
         break;
-      }
-      g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
-      Shape rect = new Rectangle2D.Double(xOffset + 1, yOffset + 1, legendBoxWidth - 2, legendBoxHeight - 2);
-      g.setColor(chartPainter.getStyleManager().getLegendBackgroundColor());
-      g.fill(rect);
-      g.setColor(chartPainter.getStyleManager().getLegendBorderColor());
-      g.draw(rect);
-
-      // Draw legend content inside legend box
-      double startx = xOffset + chartPainter.getStyleManager().getLegendPadding();
-      double starty = yOffset + chartPainter.getStyleManager().getLegendPadding();
-
-      for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
-
-        if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
-
-          // paint line
-          if (getChartPainter().getStyleManager().getChartType() != ChartType.Scatter && series.getStroke() != null) {
-            g.setColor(series.getStrokeColor());
-            g.setStroke(series.getStroke());
-            Shape line = new Line2D.Double(startx, starty + maxContentHeight / 2.0, startx + getChartPainter().getStyleManager().getLegendSeriesLineLength(), starty + maxContentHeight / 2.0);
-            g.draw(line);
-          }
-
-          // paint marker
-          if (series.getMarker() != null) {
-            g.setColor(series.getMarkerColor());
-            series.getMarker().paint(g, startx + getChartPainter().getStyleManager().getLegendSeriesLineLength() / 2.0, starty + maxContentHeight / 2.0);
-          }
+    }
+
+    g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
+    Shape rect = new Rectangle2D.Double(xOffset + 1, yOffset + 1, legendBoxWidth - 2, legendBoxHeight - 2);
+    g.setColor(styleManager.getLegendBackgroundColor());
+    g.fill(rect);
+    g.setColor(styleManager.getLegendBorderColor());
+    g.draw(rect);
+
+    // Draw legend content inside legend box
+    double startx = xOffset + styleManager.getLegendPadding();
+    double starty = yOffset + styleManager.getLegendPadding();
+
+
+    for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
+      List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesBounds(series, g);
+      float blockHeight = 0;
+      for (Map.Entry<String, Rectangle2D> entry : seriesBounds)
+        blockHeight += entry.getValue().getHeight();
+
+      if (styleManager.getChartType() != ChartType.Bar) {
+        // paint line
+        if (styleManager.getChartType() != ChartType.Scatter && series.getStroke() != null) {
+          g.setColor(series.getStrokeColor());
+          g.setStroke(series.getStroke());
+          Shape line = new Line2D.Double(startx, starty + blockHeight / 2.0, startx + styleManager.getLegendSeriesLineLength(), starty + blockHeight / 2.0);
+          g.draw(line);
+        }
+
+        // paint marker
+        if (series.getMarker() != null) {
+          g.setColor(series.getMarkerColor());
+          series.getMarker().paint(g, startx + styleManager.getLegendSeriesLineLength() / 2.0, starty + blockHeight / 2.0);
         }
-        else {
-          // paint little box
-          if (series.getStroke() != null) {
-            g.setColor(series.getStrokeColor());
-            Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE);
-            g.fill(rectSmall);
-          }
+      } else {
+        // paint little box
+        if (series.getStroke() != null) {
+          g.setColor(series.getStrokeColor());
+          Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE);
+          g.fill(rectSmall);
         }
+      }
 
-        // paint series name
+      // paint series name
+      g.setColor(chartPainter.getStyleManager().getChartFontColor());
 
-        g.setColor(chartPainter.getStyleManager().getChartFontColor());
-        TextLayout layout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false));
-        if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
-          layout.draw(g, (float) (startx + getChartPainter().getStyleManager().getLegendSeriesLineLength() + chartPainter.getStyleManager().getLegendPadding()),
-              (float) (starty + (maxContentHeight - 1 + layout.getBounds().getHeight()) / 2.0));
+      float itemOffsetY = -fontMetrics.getDescent();
+      if (styleManager.getChartType() != ChartType.Bar) {
+        final float x = (float) (startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding());
+        for (Map.Entry<String, Rectangle2D> entry : seriesBounds) {
+          g.drawString(entry.getKey(), x, (float) (starty + entry.getValue().getHeight()) + itemOffsetY);
+          itemOffsetY += entry.getValue().getHeight();
         }
-        else {
-          layout.draw(g, (float) (startx + BOX_SIZE + chartPainter.getStyleManager().getLegendPadding()), (float) (starty + (maxContentHeight + layout.getBounds().getHeight()) / 2.0));
+        itemOffsetY = (float) Math.max(itemOffsetY, Marker.SIZE);
+        starty += blockHeight + fontMetrics.getDescent();
+      } else {
+        final float x = (float) (startx + BOX_SIZE + styleManager.getLegendPadding());
+        for (Map.Entry<String, Rectangle2D> entry : seriesBounds) {
+          double height = entry.getValue().getHeight();
+          double centerOffsetY = (Math.max(BOX_SIZE, height) - height) / 2.0;
+          g.drawString(entry.getKey(), x, (float)(starty + height + itemOffsetY + centerOffsetY));
+          itemOffsetY += height;
         }
-        starty = starty + maxContentHeight + chartPainter.getStyleManager().getLegendPadding();
+        starty += Math.max(BOX_SIZE, blockHeight) + styleManager.getLegendPadding();
       }
 
-      // bounds
-      bounds = new Rectangle2D.Double(xOffset, yOffset, legendBoxWidth, legendBoxHeight);
-      // g.setColor(Color.blue);
-      // g.draw(bounds);
     }
 
+    // bounds
+    bounds = new Rectangle2D.Double(xOffset, yOffset, legendBoxWidth, legendBoxHeight);
+
+//    g.setColor(Color.blue);
+//    g.draw(bounds);
+
   }
 
   @Override
-- 
GitLab