Tooltips for Subtasks in Gantt Chart

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
SCS
Posts: 2
Joined: Sun Aug 10, 2008 6:27 pm

Tooltips for Subtasks in Gantt Chart

Post by SCS » Tue Aug 12, 2008 11:03 am

Hello All,

I have created a Gantt Chart. It is coming up fine.

As my Gantt Chart contains Subtasks, my chart is not displaying tooltips for subtasks but it is displaying for tasks.

How to display tooltips for subtasks?

Regards,
SCS

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Tue Aug 12, 2008 7:46 pm

Unfortunately the general tooltip mechanism works at the data item level, whereas the subtasks are a level below that. It would be possible to modify the renderer to handle the tooltips, but JFreeChart doesn't do it "out of the box". Sorry.

(Note that it is probably worth scanning through the patches on the SourceForge project page - it's possible that someone already sent a patch to fix this, I don't remember one but that doesn't mean there isn't one).
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

Tom
Posts: 32
Joined: Mon Dec 11, 2006 2:50 pm

Post by Tom » Tue Aug 19, 2008 9:55 am

Hey,

I figured out a way to do this by extending the CategoryIntervalTooltipRenderer and adding a field to it so you can set the number of the subtask (which is the Problem because the TooltipGenerator does not know the actual item).

Here is the code, its just a hack, but maybe the approach is helpful.

Code: Select all


import java.text.DateFormat;
import java.text.NumberFormat;

import java.util.Date;

import org.jfree.chart.labels.IntervalCategoryToolTipGenerator;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.IntervalCategoryDataset;
import org.jfree.data.gantt.Task;
import org.jfree.data.gantt.TaskSeriesCollection;
import org.jfree.data.time.TimePeriod;

public class MyIntervalCategoryGanttToolTipGenerator
   extends IntervalCategoryToolTipGenerator
{
   private int subTaskIndex;

   public MyIntervalCategoryGanttToolTipGenerator()
   {
      super();
      subTaskIndex = 0;
   }

   public MyIntervalCategoryGanttToolTipGenerator(String labelFormat, 
                                                  NumberFormat format)
   {
      super(labelFormat, format);
      subTaskIndex = 0;
   }

   public MyIntervalCategoryGanttToolTipGenerator(String labelFormat, 
                                                  DateFormat format)
   {
      super(labelFormat, format);
      subTaskIndex = 0;
   }

   /**
    * Creates the array of items that can be passed to the 
    * <code>MessageFormat</code> class for creating labels.
    *
    * @param dataset  the dataset (<code>null</code> not permitted).
    * @param row  the row index (zero-based).
    * @param column  the column index (zero-based).
    *
    * @return The items (never <code>null</code>).
    */
   protected Object[] createItemArray(CategoryDataset dataset, int row, 
                                      int column)
   {
      Object[] result = new Object[9];
      result[0] = dataset.getRowKey(row).toString();
      result[1] = dataset.getColumnKey(column).toString();
      Number value = dataset.getValue(row, column);
      if (getNumberFormat() != null)
      {
         result[2] = getNumberFormat().format(value);
      }
      else if (getDateFormat() != null)
      {
         result[2] = getDateFormat().format(value);
      }
      if (dataset instanceof TaskSeriesCollection)
      {
         try
         {
            TaskSeriesCollection data = (TaskSeriesCollection) dataset;
            Task subTask = 
               data.getSeries(row).get(column).getSubtask(subTaskIndex);
            Date start = subTask.getDuration().getStart();
            Date end = subTask.getDuration().getEnd();
            if (getNumberFormat() != null)
            {
               result[3] = getNumberFormat().format(0.0);
               result[4] = getNumberFormat().format(0.0);
               result[5] = "";
            }
            else if (getDateFormat() != null)
            {
               result[3] = getDateFormat().format(start);
               result[4] = getDateFormat().format(end);
               result[5] = 
                     data.getSeries(row).get(column).getSubtask(subTaskIndex).getDescription();
               if (subTask instanceof MySubTask)
               {
                  result[6] = ((MySubTask) subTask).getParam(0);
                  result[7] = ((MySubTask) subTask).getParam(1);
                  result[8] = ((MySubTask) subTask).getParam(2);
               }

            }
         }
         catch (Exception e)
         {
            if (getNumberFormat() != null)
            {
               result[3] = "NO DATA FOUND!";
               result[4] = "NO DATA FOUND!";
               result[5] = "NO DATA FOUND!";
               result[6] = "NO DATA FOUND!";
               result[7] = "NO DATA FOUND!";
            }
            else if (getDateFormat() != null)
            {
               result[3] = "NO DATA FOUND!";
               result[4] = "NO DATA FOUND!";
               result[5] = "NO DATA FOUND!";
               result[6] = "NO DATA FOUND!";
               result[7] = "NO DATA FOUND!";
               result[8] = "NO DATA FOUND!";
            }
            e.printStackTrace();
         }
      }
      else if (dataset instanceof IntervalCategoryDataset)
      {
         IntervalCategoryDataset icd = 
            (IntervalCategoryDataset) dataset;
         Number start = icd.getStartValue(row, column);
         Number end = icd.getEndValue(row, column);
         if (getNumberFormat() != null)
         {
            result[3] = getNumberFormat().format(start);
            result[4] = getNumberFormat().format(end);
         }
         else if (getDateFormat() != null)
         {
            result[3] = getDateFormat().format(start);
            result[4] = getDateFormat().format(end);
         }
      }
      return result;
   }

   public void setSubTaskIndex(int subTaskIndex)
   {
      this.subTaskIndex = subTaskIndex;
   }

}
You also need to override the Renderer to let it set the subIntervalIndex for the ToolTipGenerator:

Code: Select all


package com.tp.lisa.laborlogistik.auswertung;

import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.geom.Rectangle2D;

import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.CategoryItemEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.GanttRenderer;
import org.jfree.data.gantt.GanttCategoryDataset;
import org.jfree.ui.RectangleEdge;


public class MyGanttRenderer
   extends GanttRenderer
{
   private Paint unterlastPaint = null;
   private Paint ueberlastPaint = null;
   private Paint normalPaint = null;
   private Paint kritischPaint = null;
   
   public MyGanttRenderer()
   {
      super();
   }

   /**
    * Draws the tasks/subtasks for one item.
    *
    * @param g2  the graphics device.
    * @param state  the renderer state.
    * @param dataArea  the data plot area.
    * @param plot  the plot.
    * @param domainAxis  the domain axis.
    * @param rangeAxis  the range axis.
    * @param dataset  the data.
    * @param row  the row index (zero-based).
    * @param column  the column index (zero-based).
    */
   protected void drawTasks(Graphics2D g2, CategoryItemRendererState state, 
                            Rectangle2D dataArea, CategoryPlot plot, 
                            CategoryAxis domainAxis, ValueAxis rangeAxis, 
                            GanttCategoryDataset dataset, int row, 
                            int column)
   {
      try
      {
         int count = dataset.getSubIntervalCount(row, column);
         if (count == 0)
         {
            drawTask(g2, state, dataArea, plot, domainAxis, rangeAxis, 
                     dataset, row, column);
         }
   
         for (int subinterval = 0; subinterval < count; subinterval++)
         {
   
            RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
   
            // value 0
            Number value0 = dataset.getStartValue(row, column, subinterval);
            if (value0 == null)
            {
               return;
            }
            double translatedValue0 = 
               rangeAxis.valueToJava2D(value0.doubleValue(), dataArea, 
                                       rangeAxisLocation);
   
            // value 1
            Number value1 = dataset.getEndValue(row, column, subinterval);
            if (value1 == null)
            {
               return;
            }
            double translatedValue1 = 
               rangeAxis.valueToJava2D(value1.doubleValue(), dataArea, 
                                       rangeAxisLocation);
   
            if (translatedValue1 < translatedValue0)
            {
               double temp = translatedValue1;
               translatedValue1 = translatedValue0;
               translatedValue0 = temp;
            }
   
            double rectStart = 
               calculateBarW0(plot, plot.getOrientation(), dataArea, 
                              domainAxis, state, row, column);
            double rectLength = Math.abs(translatedValue1 - translatedValue0);
            double rectBreadth = state.getBarWidth();
   
            // DRAW THE BARS...
            Rectangle2D bar = null;
   
            if (plot.getOrientation() == PlotOrientation.HORIZONTAL)
            {
               bar = 
                     new Rectangle2D.Double(translatedValue0, rectStart, rectLength, 
                                            rectBreadth);
            }
            else if (plot.getOrientation() == PlotOrientation.VERTICAL)
            {
               bar = 
                     new Rectangle2D.Double(rectStart, translatedValue0, rectBreadth, 
                                            rectLength);
            }
   
            Rectangle2D completeBar = null;
            Rectangle2D incompleteBar = null;
            Number percent = 
               dataset.getPercentComplete(row, column, subinterval);
            double start = getStartPercent();
            double end = getEndPercent();
            if (percent != null)
            {
               double p = percent.doubleValue();
               if (plot.getOrientation() == PlotOrientation.HORIZONTAL)
               {
                  completeBar = 
                        new Rectangle2D.Double(translatedValue0, rectStart + 
                                               start * rectBreadth, 
                                               rectLength * p, 
                                               rectBreadth * (end - start));
                  incompleteBar = 
                        new Rectangle2D.Double(translatedValue0 + rectLength * 
                                               p, 
                                               rectStart + start * rectBreadth, 
                                               rectLength * (1 - p), 
                                               rectBreadth * (end - start));
               }
               else if (plot.getOrientation() == PlotOrientation.VERTICAL)
               {
                  completeBar = 
                        new Rectangle2D.Double(rectStart + start * rectBreadth, 
                                               translatedValue0 + 
                                               rectLength * (1 - p), 
                                               rectBreadth * (end - start), 
                                               rectLength * p);
                  incompleteBar = 
                        new Rectangle2D.Double(rectStart + start * rectBreadth, 
                                               translatedValue0, 
                                               rectBreadth * (end - start), 
                                               rectLength * (1 - p));
               }
   
            }
   
            Paint seriesPaint = getItemPaint(row, column);
            g2.setPaint(seriesPaint);
            g2.fill(bar);
            if (completeBar != null)
            {
               g2.setPaint(getCompletePaint());
               g2.fill(completeBar);
            }
            if (incompleteBar != null)
            {
               g2.setPaint(getIncompletePaint());
               g2.fill(incompleteBar);
            }
            if (isDrawBarOutline() && 
                state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD)
            {
               g2.setStroke(getItemStroke(row, column));
               g2.setPaint(getItemOutlinePaint(row, column));
               g2.draw(bar);
            }
   
            CategoryItemLabelGenerator generator = 
                getItemLabelGenerator(row, column);
            if (generator != null && isItemLabelVisible(row, column))
            {
               ((MyCategoryGanttItemLabelGenerator) generator).setSubTaskIndex(subinterval);
               drawItemLabel(g2, dataset, row, column, plot, generator, bar, 
                             false);
            }
   
            // collect entity and tool tip information...
            if (state.getInfo() != null)
            {
               EntityCollection entities = state.getEntityCollection();
               if (entities != null)
               {
                  String tip = null;
                  CategoryToolTipGenerator tooltip = getToolTipGenerator(row, column);
                  
                  if (tooltip != null)
                  {
 /* ---------------------    Here's the change ----------------------------*/
                     ((MyIntervalCategoryGanttToolTipGenerator) tooltip).setSubTaskIndex(subinterval); 
                     tip = 
                           tooltip.generateToolTip( dataset, 
                                                       row, 
                                                       column);
                  }
                  String url = null;
                  if (getItemURLGenerator(row, column) != null)
                  {
                     url = 
                           getItemURLGenerator(row, column).generateURL(dataset, 
                                                                        row, 
                                                                        column);
                  }
                  CategoryItemEntity entity = 
                     new CategoryItemEntity(bar, tip, url, dataset, row, 
                                            dataset.getColumnKey(column), 
                                            column);
                  entities.add(entity);
               }
            }
         }
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }
}


Hope this helps,

Tom[/code]

Locked