// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef CHART_WCARTESIAN_CHART_H_
#define CHART_WCARTESIAN_CHART_H_

#include <Wt/Chart/WAbstractChart>
#include <Wt/Chart/WAbstractChartModel>
#include <Wt/Chart/WAxis>
#include <Wt/Chart/WDataSeries>
#include <Wt/Chart/WLegend>
#include <Wt/WPaintDevice>
#include <Wt/WContainerWidget>

namespace Wt {

class WAbstractItemModel;
class WPainter;
class WPainterPath;

  namespace Chart {

class WAxisSliderWidget;

/*! \class SeriesIterator Wt/Chart/WCartesianChart Wt/Chart/WCartesianChart
 *  \brief Abstract base class for iterating over series data in a chart.
 *
 * This class is specialized for rendering series data.
 *
 * \sa WCartesianChart::iterateSeries()
 *
 * \ingroup charts
 */
class WT_API SeriesIterator {
public:
  /*! \brief Destructor.
   */
  virtual ~SeriesIterator();

  /*! \brief Start handling a new segment.
   *
   * Because of a 'break' specified in an axis, axes may be divided in
   * one or two segments (in fact only the API limits this now to
   * two). The iterator will iterate all segments seperately, but each time
   * with a different clipping region specified in the painter, corresponding
   * to that segment.
   *
   * The <i>currentSegmentArea</i> specifies the clipping area.
   */
  virtual void startSegment(int currentXSegment, int currentYSegment,
			    const WRectF& currentSegmentArea);

  /*! \brief End handling a particular segment.
   *
   * \sa startSegment()
   */
  virtual void endSegment();

  /*! \brief Start iterating a particular series.
   *
   * Returns whether the series values should be iterated.
   * The <i>groupWidth</i> is the width (in pixels) of a single bar
   * group. The chart contains <i>numBarGroups</i>, and the current
   * series is in the <i>currentBarGroup</i>'th group.
   */
  virtual bool startSeries(const WDataSeries& series, double groupWidth,
			   int numBarGroups, int currentBarGroup);

  /*! \brief End iterating a particular series.
   */
  virtual void endSeries();

  /*! \brief Process a value.
   *
   * Processes a value with model coordinates (<i>x</i>,
   * <i>y</i>). The y value may differ from the model's y value,
   * because of stacked series. The y value here corresponds to the
   * location on the chart, after stacking.
   *
   * The <i>stackY</i> argument is the y value from the previous
   * series (also after stacking). It will be 0, unless this series is
   * stacked.
   */
  virtual void newValue(const WDataSeries& series, double x, double y,
			double stackY, int xRow, int xColumn,
			int yRow, int yColumn);

  /*! \brief Returns the current X segment.
   */
  int currentXSegment() const { return currentXSegment_; }

  /*! \brief Returns the current Y segment.
   */
  int currentYSegment() const { return currentYSegment_; }

  static void setPenColor(WPen& pen, const WDataSeries& series,
		   int xRow, int xColumn,
		   int yRow, int yColumn,
		   int colorRole);

  static void setBrushColor(WBrush& brush, const WDataSeries& series,
		     int xRow, int xColumn,
		     int yRow, int yColumn,
		     int colorRole);

private:
  int currentXSegment_, currentYSegment_;
};

/*! \brief A curve label
 *
 * Curve labels can be added with WCartesianChart::addCurveLabel().
 * They are associated with a particular series, and are drawn at
 * the given point in model coordinates.
 * When the chart is transformed (zoom or pan)
 * or the associated series is manipulated, the curve label's position
 * will change, but not its size.
 *
 * \image html CurveLabel.png "A curve label"
 */
class WT_API CurveLabel {
public:
  /*! \brief Create a new curve label.
   *
   * Create a new curve label for given series,
   * at the given point with the given text.
   */
  CurveLabel(const WDataSeries &series, const WPointF &point, const WT_USTRING &label);

  /*! \brief Set the series this curve label is associated with.
   */
  void setSeries(const WDataSeries &series);

  /*! \brief Get the series this curve label is associated with.
   *
   * \sa setSeries()
   */
  const WDataSeries &series() const { return *series_; }

  /*! \brief Set the point in model coordinates this label is associated with.
   */
  void setPoint(const WPointF &point);

  /*! \brief Get the point in model coordinates this label is associated with.
   *
   * \sa setPoint()
   */
  const WPointF &point() const { return point_; }

  /*! \brief Set the label that should be drawn in the box.
   */
  void setLabel(const WT_USTRING &label);

  /*! \brief Get the label that should be drawn in the box.
   *
   * \sa setLabel()
   */
  const WT_USTRING &label() const { return label_; }

  /*! \brief Set the offset the text should be placed at.
   *
   * The offset is defined in pixels, with x values
   * going from left to right, and y values from top
   * to bottom.
   *
   * The default offset is (60, -20), which means the middle
   * of the label() is drawn 60 pixels to the right, and 20 pixels
   * above the point.
   */
  void setOffset(const WPointF &offset);

  /*! \brief Get the offset the text should be placed at.
   *
   * \sa setOffset()
   */
  const WPointF &offset() const { return offset_; }

  /*! \brief Set the width of the box in pixels.
   *
   * If the width is 0 (the default), server side font metrics
   * will be used to determine the size of the box.
   */
  void setWidth(int width);

  /*! \brief Get the width of the box in pixels.
   *
   * \sa setWidth()
   */
  int width() const { return width_; }

  /*! \brief Set the pen to use for the connecting line.
   *
   * This sets the pen to use for the line connecting the
   * \link point() point\endlink to the box with the
   * \link label() label\endlink at \link offset() offset\endlink
   * pixels from the point.
   */
  void setLinePen(const WPen &pen);

  /*! \brief Get the pen to use for the connecting line.
   *
   * \sa setLinePen()
   */
  const WPen &linePen() const { return linePen_; }

  /*! \brief Set the pen for the text in the box.
   */
  void setTextPen(const WPen &pen);

  /*! \brief Get the pen for the text in the box.
   *
   * \sa setTextPen()
   */
  const WPen &textPen() const { return textPen_; }

  /*! \brief Set the brush to use for the box around the text.
   *
   * This sets the brush used to fill the box with the
   * text defined in label().
   */
  void setBoxBrush(const WBrush &brush);

  /*! \brief Get the brush to use for the box around the text.
   *
   * \sa setBoxBrush()
   */
  const WBrush &boxBrush() const { return boxBrush_; }

  /*! \brief Set the brush used to fill the circle at point().
   */
  void setMarkerBrush(const WBrush &brush);

  /*! \brief Get the brush used to fill the circle at point().
   *
   * \sa setMarkerBrush()
   */
  const WBrush &markerBrush() const { return markerBrush_; }

  void render(WPainter &painter) const;

private:
  const WDataSeries *series_;
  WPointF point_;
  WT_USTRING label_;
  WPointF offset_;
  int width_;
  WPen linePen_;
  WPen textPen_;
  WBrush boxBrush_;
  WBrush markerBrush_;

  static bool checkIntersectHorizontal(const WPointF &p1, const WPointF &p2, double minX, double maxX, double y);
  static bool checkIntersectVertical(const WPointF &p1, const WPointF &p2, double minY, double maxY, double x);
};

class WChart2DImplementation;
class WChartPalette;

/*! \brief Enumeration of mouse wheel actions for interactive charts.
 *
 * \sa WCartesianChart::WheelActions
 * \sa WCartesianChart::setWheelActions(WheelActions)
 *
 * \ingroup charts
 */
enum InteractiveAction {
  ZoomX,                //!< Zoom x-axis
  ZoomY,                //!< Zoom y-axis
  ZoomXY,               //!< Zoom along both x and y-axes
  ZoomMatching,         //!< Zoom y-axis on vertical scroll, x-axis on horizontal scroll
  PanX,                 //!< Pan x-axis
  PanY,                 //!< Pan y-axis
  PanMatching           //!< Pan y-axis on vertical scroll, x-axis on horizontal scroll
};

/*! \brief A map of mouse wheel actions for interactive charts, indexed by WFlags<KeyboardModifier>
 *
 * \sa WCartesianChart::setWheelActions(WheelActions)
 * \sa WCartesianChart::wheelActions()
 *
 * \ingroup charts
 */
typedef std::map<WFlags<KeyboardModifier>, InteractiveAction> WheelActions;

/*! \class WCartesianChart Wt/Chart/WCartesianChart Wt/Chart/WCartesianChart
 *  \brief A cartesian chart.
 *
 * A cartesian chart is a chart that uses X and Y axes. It can display
 * one or multiple data series, which each may be rendered using bars,
 * lines, areas, or points.
 *
 * To use a cartesian chart, the minimum you need to do is set a model
 * using setModel(), set the model column that holds the X data using
 * setXSeriesColumn(int modelColumn), and add one or more series using
 * addSeries(WDataSeries*). Each series corresponds to one data
 * column that holds Y data.
 *
 * A cartesian chart is either a \link Chart::CategoryChart
 * CategoryChart\endlink or a \link Chart::ScatterPlot
 * ScatterPlot\endlink.
 *
 * In a <b>CategoryChart</b>, the X series represent different
 * categories, which are listed consecutively in model row order. The
 * X axis scale is set to \link Chart::CategoryScale
 * CategoryScale\endlink.
 *
 * \image html ChartWCartesianChart-1.png "A category chart with bar series"
 *
 * Each series may be rendered differently, and this is configured in
 * the data series (see WDataSeries for more information).
 *
 * In a <b>ScatterPlot</b>, the X series data are interpreted as
 * numbers on a numerical scale. The scale for the X axis defaults to
 * a \link Chart::LinearScale LinearScale\endlink, but this may be
 * changed to a \link Chart::DateScale DateScale\endlink when the X
 * series contains dates (of type WDate) to create a time series
 * chart, or to a \link Chart::LogScale LogScale\endlink. A
 * ScatterPlot supports the same types of data series as a
 * CategoryChart, but does not support stacking. In a scatter plot,
 * the X series do not need to be ordered in increasing values, and
 * may be set differently for each dataseries using
 * WDataSeries::setXSeriesColumn(int modelColumn).
 *
 * \image html ChartWCartesianChart-2.png "A time series scatter plot with line series"
 *
 * Missing data in a model series Y values is interpreted as a
 * <i>break</i>. For curve-like series, this breaks the curve (or
 * line).
 *
 * The cartesian chart has support for dual Y axes. Each data series may
 * be bound to one of the two Y axes. By default, only the first Y axis
 * is displayed. To show the second Y axis you will need to call:
 * 
 * \if cpp
 * \code
 * chart->axis(Y2Axis).setVisible(true);
 * \endcode
 * \endif
 *
 * By default a chart has a horizontal X axis and a vertical Y axis,
 * which corresponds to a \link Wt::Vertical Vertical\endlink
 * orientation. The orientation may be changed to \link Wt::Horizontal
 * Horizontal\endlink using setOrientation().
 *
 * The styling of the series data are dictated by a palette which may
 * be set using setPalette(WChartPalette *), but may be overridden by
 * settings in each data series.
 *
 * <h3>CSS</h3>
 *
 * Styling through CSS is not applicable.
 *
 * \if java
 * <h3>Client-side interaction</h3>
 *
 * %WCartesianChart has several features that allow interaction with the
 * chart without server roundtrips. These features include zoom, pan,
 * crosshair and follow curve functionality.
 * 
 * \note Client side interaction is only available if the chart is drawn
 *       on an HTML canvas. This is the default rendering method on
 *       modern browsers, see WPaintedWidget::setPreferredMethod()
 *
 * \note Some features are currently not supported in interactive mode:
 *       - Axes set at ZeroValue position will not always be drawn correctly.
 *	     They may be clipped off outside of the chart area, and when
 *         zooming, the axis ticks will change size.
 *       - WAxis::setBreak() is incompatible with interactive mode
 *
 * \endif
 *
 * \sa WDataSeries, WAxis
 * \sa WPieChart
 *
 * \ingroup charts modelview
 */
class WT_API WCartesianChart : public WAbstractChart
{

public:
  /*! \brief Creates a new cartesian chart.
   *
   * Creates a cartesian chart of type \link Wt::Chart::CategoryChart
   * CategoryChart\endlink.
   */
  WCartesianChart(WContainerWidget *parent = 0);

  /*! \brief Creates a new cartesian chart.
   *
   * Creates a cartesian chart of the indicated \p type.
   */
  WCartesianChart(ChartType type, WContainerWidget *parent = 0);

  ~WCartesianChart();

  /*! \brief Sets the chart type.
   *
   * The chart type determines how (x,y) data are interpreted. In a
   * \link Wt::Chart::CategoryChart CategoryChart\endlink, the X
   * values are categories, and these are plotted consecutively,
   * evenly spaced, and in row order. In a \link
   * Wt::Chart::ScatterPlot ScatterPlot\endlink, the X values are
   * interpreted numerically (as for Y values).
   *
   * The default chart type is a \link Wt::Chart::CategoryChart
   * CategoryChart\endlink.
   *
   * \sa type()
   * \sa WAxis::setScale(), axis(Axis)
   */
  void setType(ChartType type);

  /*! \brief Returns the chart type.
   *
   * \sa setType()
   */
  ChartType type() const { return type_; }

  /*! \brief Sets the chart orientation.
   *
   * Sets the chart orientation, which corresponds to the orientation of
   * the Y axis: a Wt::Vertical orientation corresponds to the conventional
   * way of a horizontal X axis and vertical Y axis. A Wt::Horizontal
   * orientation is the other way around.
   *
   * The default orientation is Wt::Vertical.
   *
   * \sa orientation()
   */
  void setOrientation(Orientation orientation);

  /*! \brief Returns the chart orientation.
   *
   * \sa setOrientation()
   */  
  Orientation orientation() const { return orientation_; }

  /*! \brief Sets the the model column for the X series.
   *
   * Use this method to specify the default data for the X series.
   * For a \link Wt::Chart::ScatterPlot ScatterPlot\endlink this is
   * mandatory if an X series is not specified for every
   * \link Wt::Chart::WDataSeries WDataSeries\endlink. For a
   * \link Wt::Chart::CategoryChart CategoryChart\endlink, if not
   * specified, an increasing series of integer numbers will be
   * used (1, 2, ...).
   *
   * Scatterplot dataseries may each individually be given its own
   * X series data using WDataSeries::setXSeriesColumn(int modelColumn)
   *
   * The default value is -1 (not specified).
   *
   * The series column is reset to -1 when the model is set (or
   * changed). Thus you need to set a model before configuring the
   * series.
   *
   * \sa XSeriesColumn()
   */
  void setXSeriesColumn(int modelColumn);

  /*! \brief set the pen used to render the labels
   *
   * This method overwrites the pen for all axes
   *
   * \sa WAxis::setTextPen()
   */
  void setTextPen(const WPen& pen);

  /*! \brief Returns the model column for the X series.
   *
   * \sa setXSeriesColumn()
   */
  int XSeriesColumn() const { return XSeriesColumn_; }

  //later, activates a 3D plot
  //void setXYData(int modelColumnX, int modelColumnY);
  //bool is3D() const;

#ifndef WT_TARGET_JAVA
  /*! \brief Adds a data series.
   *
   * A single chart may display one or more data series. Each data series
   * displays data from a single model column in the chart. Series are
   * plotted in the order that they have been added to the chart.
   *
   * The series column is reset to -1 when the model is set (or
   * changed). Thus you need to set a model before configuring the
   * series.
   *
   * \sa removeSeries(), setSeries()
   *
   * \deprecated Use addSeries(WDataSeries *) instead
   */
  void addSeries(const WDataSeries& series);
#endif

  /*! \brief Adds a data series.
   *
   * A single chart may display one or more data series. Each data series
   * displays data from a single model column in the chart. Series are
   * plotted in the order that they have been added to the chart.
   *
   * The series column is reset to -1 when the model is set (or
   * changed). Thus you need to set a model before configuring the
   * series.
   *
   * \if cpp
   * The chart will take ownership of the given series.
   * \endif
   *
   * \sa removeSeries(), setSeries()
   */
  void addSeries(WDataSeries *series);

  /*! \brief Removes a data series.
   *
   * This removes the first data series which plots the given
   * \p modelColumn.
   *
   * \sa addSeries(), setSeries()
   *
   * \deprecated Use removeSeries(WDataSeries *) instead
   */
  void removeSeries(int modelColumn);

  /*! \brief Removes a data series.
   *
   * \if cpp
   * The series will be deleted.
   * \endif
   *
   * This will disassociate the given series from any WAxisSliderWidgets.
   *
   * \sa addSeries(), setSeries()
   */
  void removeSeries(WDataSeries *series);

#ifndef WT_TARGET_JAVA
  /*! \brief Sets all data series.
   *
   * Replaces the current list of series with the new list.
   *
   * \sa series(), addSeries(), removeSeries()
   *
   * \deprecated Use setSeries(const std::vector<WDataSeries *> &series) instead
   */
  void setSeries(const std::vector<WDataSeries>& series);
#endif


  /*! \brief Sets all data series.
   *
   * Replaces the current list of series with the new list.
   * \if cpp
   * The chart takes ownership of all of the series.
   * \endif
   *
   * \note All series currently associated with the chart will be deleted.
   *       Hence, these series should be distinct from the currently associated
   *       series of this chart!
   *
   * \sa series(), addSeries(WDataSeries *), removeSeries(WDataSeries *)
   */
  void setSeries(const std::vector<WDataSeries *> &series);

  /*! \brief Returns a data series corresponding to a data column.
   *
   * Returns a reference to the first data series that plots data
   * from \p modelColumn.
   */
  WDataSeries& series(int modelColumn);

  /*! \brief Returns a data series corresponding to a data column.
   *
   * Returns a const reference to the first data series that plots data
   * from \p modelColumn.
   */
  const WDataSeries& series(int modelColumn) const;

  /*! \brief Returns a list with the current data series.
   *
   * Returns the complete list of current data series.
   *
   * \sa setSeries(const std::vector<WDataSeries>&)
   */
  const std::vector<WDataSeries *>& series() const { return series_; }

  /*! \brief Returns a chart axis.
   *
   * Returns a reference to the specified \p axis.
   */
  WAxis& axis(Axis axis);

  /*! \brief Accesses a chart axis.
   *
   * Returns a const reference to the specified \p axis.
   */
  const WAxis& axis(Axis axis) const;

  /*! \brief Sets an axis
   *
   * \if cpp
   * Ownership is transferred and a previously set (default) axis is deleted
   * first.
   * \endif
   *
   * \sa axis(Axis axis)
   */
  void setAxis(WAxis *waxis, Axis axis);

  /*! \brief Sets the margin between bars of different series.
   *
   * Use this method to change the margin that is set between bars of
   * different series. The margin is specified as a fraction of the
   * width. For example, a value of 0.1 adds a 10% margin between bars
   * of each series. Negative values are also allowed. For example, use
   * a margin of -1 to plot the bars of different series on top of
   * each other.
   *
   * The default value is 0.
   */
  void setBarMargin(double margin);

  /*! \brief Returns the margin between bars of different series.
   *
   * \sa setBarMargin(double)
   */
  double barMargin() const { return barMargin_; }

  /*! \brief Enables the legend.
   *
   * The location of the legend can be configured using
   * setLegendLocation(). Only series for which the legend is enabled
   * are included in this legend.
   *
   * The default value is \c false.
   *
   * \sa see WDataSeries::isLegendEnabled(), setLegendLocation()
   */
  void setLegendEnabled(bool enabled);

  /*! \brief Returns whether the legend is enabled.
   *
   * \sa setLegendEnabled()
   */
  bool isLegendEnabled() const { return legend_.isLegendEnabled(); }

  /*! \brief Configures the legend location.
   *
   * The legend can be renderd either inside or outside of the chart
   * area. When \p location is \link Wt::Chart::LegendInside
   * Chart::LegendInside\endlink, the legend will be rendered inside
   * the chart. When \p location is \link Wt::Chart::LegendOutside
   * Chart::Legendoutside\endlink, the legend is rendered outside the
   * chart, in the chart padding area.
   *
   * The provided \p side can either be Wt::Left, Wt::Right, Wt::Top,
   * Wt::Bottom and configures the side of the chart at which the
   * legend is displayed.
   *
   * The \p alignment specifies how the legend is aligned. This can be
   * a horizontal alignment flag (Wt::AlignLeft, Wt::AlignCenter, or
   * Wt::AlignRight), when the \p side is Bottom or Top, or a vertical
   * alignment flag (Wt::AlignTop, Wt::AlignMiddle, or Wt::AlignBottom)
   * when the \p side is Left or Right.
   *
   * The default location is \link Wt::Chart::LegendOutside
   * Chart::LegendOutside\endlink, Wt::Right and Wt::AlignMiddle.
   *
   * To have more control over the legend, you could reimplement the
   * renderLegendItem() method to customize how one item in the legend
   * is rendered, or, alternatively you can disable the legend
   * generated by the chart itself, and reimplement the paint() method
   * in which you use the renderLegendItem() method repeatedly to
   * render a customized legend.
   *
   * \sa WDataSeries::setLegendEnabled()
   */
  void setLegendLocation(LegendLocation location, Side side,
			 AlignmentFlag alignment);

  /*! \brief Configures the legend decoration.
   *
   * This configures the font, border and background for the legend.
   *
   * The default font is a 10pt sans serif font (the same as the
   * default axis label font), the default \p border is \link
   * Wt::NoPen NoPen\endlink and the default \p background is \link
   * Wt::NoBrush NoBrush\endlink.
   *
   * \sa setLegendEnabled()
   */
  void setLegendStyle(const WFont& font, const WPen& border,
		      const WBrush& background);

  /*! \brief Returns the legend location.
   *
   * \sa setLegendLocation()
   */
  LegendLocation legendLocation() const { return legend_.legendLocation(); }

  /*! \brief Returns the legend side.
   *
   * \sa setLegendLocation()
   */
  Side legendSide() const { return legend_.legendSide(); }

  /*! \brief Returns the legend alignment.
   *
   * \sa setLegendLocation()
   */
  AlignmentFlag legendAlignment() const { return legend_.legendAlignment(); }

  /*! \brief Returns the legend columns.
   *
   * \sa setLegendColumns()
   */
  int legendColumns() const { return legend_.legendColumns(); }

  /*! \brief Returns the legend column width.
   *
   * \sa setLegendColumns()
   */
  WLength legendColumnWidth() const { return legend_.legendColumnWidth(); }

  /*! \brief Returns the legend font.
   *
   * \sa setLegendStyle()
   */
  WFont legendFont() const { return legend_.legendFont(); }

  /*! \brief Returns the legend border pen.
   *
   * \sa setLegendStyle()
   */
  WPen legendBorder() const { return legend_.legendBorder(); }

  /*! \brief Returns the legend background brush.
   *
   * \sa setLegendStyle()
   */
  WBrush legendBackground() const { return legend_.legendBackground(); }

  /*! \brief Configures multiple legend columns.
   *
   * Multiple columns are typically useful when placing the legend at
   * the top or at the bottom of the chart.
   *
   * The default value is a single column, 100 pixels wide.
   *
   * When automatic chart layout is enabled, then the legend column width
   * is computed automatically, and this setting is ignored.
   *
   * \sa setAutoLayoutEnabled()
   */
  void setLegendColumns(int columns, const WLength& width);
  
  virtual void paint(WPainter& painter, const WRectF& rectangle = WRectF())
    const WT_CXX11ONLY(override);

  /*! \brief Draws the marker for a given data series.
   *
   * Draws the marker for the indicated \p series in the \p result.
   * This method is called while painting the chart, and you may
   * want to reimplement this method if you wish to provide a custom
   * marker for a particular data series.
   *
   * \sa setLegendEnabled()
   */
  virtual void drawMarker(const WDataSeries& series, WPainterPath& result)
    const;

  /*! \brief Renders the legend icon for a given data series.
   *
   * Renders the legend icon for the indicated \p series in the
   * \p painter at position \p pos.
   *
   * This method is called while rendering a legend item, and you may
   * want to reimplement this method if you wish to provide a custom
   * legend icon for a particular data series.
   *
   * \sa renderLegendItem()
   */
  virtual void renderLegendIcon(WPainter& painter, const WPointF& pos,
				const WDataSeries& series) const;

  /*! \brief Renders the legend item for a given data series.
   *
   * Renders the legend item for the indicated \p series in the
   * \p painter at position \p pos. The default implementation
   * draws the marker, and the series description to the right. The
   * series description is taken from the model's header data for that
   * series' data column.
   *
   * This method is called while painting the chart, and you may
   * want to reimplement this method if you wish to provide a custom
   * marker for a particular data series.
   *
   * \sa setLegendEnabled()
   */
  virtual void renderLegendItem(WPainter& painter, const WPointF& pos,
				const WDataSeries& series) const;

  /*! \brief Maps from device coordinates to model coordinates.
   *
   * Maps a position in the chart back to model coordinates.
   *
   * This uses the axis dimensions that are based on the latest chart
   * rendering. If you have not yet rendered the chart, or wish that
   * the mapping already reflects model changes since the last rendering,
   * you should call initLayout() first.
   *
   * If the chart is interactive, mapFromDevice will correctly take the current
   * zoom range into account.
   *
   * \sa mapToDevice()
   */
  WPointF mapFromDevice(const WPointF& point,
			Axis ordinateAxis = OrdinateAxis) const;

  /*! \brief Maps from device coordinates to model coordinates, ignoring the current zoom range
   *
   * Maps a position in the chart back to model coordinates, as if the
   * chart was not zoomed in (nor panned).
   *
   * This uses the axis dimensions that are based on the latest chart
   * rendering. If you have not yet rendered the chart, or wish that
   * the mapping already reflects model changes since the last rendering,
   * you should call initLayout() first.
   *
   * This function will not take the current zoom range into
   * account. The mapping will be performed as if zoomRangeTransform()
   * is the identity transform.
   *
   * \sa mapToDeviceWithoutTransform()
   */
  WPointF mapFromDeviceWithoutTransform(const WPointF& point,
			Axis ordinateAxis = OrdinateAxis) const;

  /*! \brief Maps model values onto chart coordinates.
   *
   * This returns the chart device coordinates for a (x,y) pair of model
   * values.
   *
   * This uses the axis dimensions that are based on the latest chart
   * rendering. If you have not yet rendered the chart, or wish that
   * the mapping already reflects model changes since the last rendering,
   * you should call initLayout() first.
   *
   * The \p xSegment and \p ySegment arguments are relevant only when
   * the corresponding axis is broken using WAxis::setBreak(). Then,
   * its possible values may be 0 (below the break) or 1 (above the
   * break).
   *
   * If the chart is interactive, mapToDevice will correctly take the current
   * zoom range into account.
   *
   * \sa mapFromDevice()
   */
  WPointF mapToDevice(const boost::any& xValue, const boost::any& yValue,
		      Axis axis = OrdinateAxis, int xSegment = 0,
		      int ySegment = 0) const;

  /*! \brief Maps model values onto chart coordinates, ignoring the current zoom range
   *
   * This returns the chart device coordinates for a (x,y) pair of model
   * values.
   *
   * This uses the axis dimensions that are based on the latest chart
   * rendering. If you have not yet rendered the chart, or wish that
   * the mapping already reflects model changes since the last rendering,
   * you should call initLayout() first.
   *
   * The \p xSegment and \p ySegment arguments are relevant only when
   * the corresponding axis is broken using WAxis::setBreak(). Then,
   * its possible values may be 0 (below the break) or 1 (above the
   * break).
   *
   * This function will not take the current zoom range into
   * account.The mapping will be performed as if zoomRangeTransform()
   * is the identity transform.
   *
   * \sa mapFromDeviceWithoutTransform()
   */
  WPointF mapToDeviceWithoutTransform(const boost::any& xValue, const boost::any& yValue,
		      Axis axis = OrdinateAxis, int xSegment = 0,
		      int ySegment = 0) const;

  /*! \brief Initializes the chart layout.
   *
   * The mapping between model and device coordinates is only
   * established after a rendering phase, or after calling initLayout
   * manually.
   *
   * You need a layout in order to use the mapFromDevice() and
   * mapToDevice() methods.
   *
   * Unless a specific chart rectangle is specified, the entire widget
   * area is assumed.
   */
  bool initLayout(const WRectF& rectangle = WRectF(), WPaintDevice *device = 0)
    const;

  /*! \brief Creates a widget which renders the a legend item.
   * 
   * The legend item widget will contain a text and a WPaintedWidget 
   * which draws the series' symbol.
   */
  WWidget* createLegendItemWidget(int index);

  /*! \brief Adds a data point area (used for displaying e.g. tooltips).
   *
   * You may want to specialize this is if you wish to modify (or
   * delete) the area.
   *
   * \note Currently, an area is only created if the Wt::ToolTipRole data
   *       at the data point is not empty.
   */
  virtual void addDataPointArea(const WDataSeries& series,
				int xRow, int xColumn,
				WAbstractArea *area);

  /*! \brief Sets the padding between the chart area and the axes.
   *
   * This calls WAxes::setPadding() on all axes.
   *
   * \sa axisPadding()
   */
  void setAxisPadding(int axisPadding);

  /*! \brief Returns the padding between the chart area and the axes.
   *
   * This number may not reflect the actual padding of the individual axes,
   * if another padding has been applied on the individual axes.
   *
   * \sa setAxisPadding()
   */
  int axisPadding() const { return axisPadding_; }

  /*! \brief Sets the pen of the border to be drawn around the chart area.
   *
   * \sa borderPen()
   */
  void setBorderPen(const WPen& pen);

  /*! \brief Returns the pen used to draw the border around the chart area.
   *
   * Defaults to NoPen.
   *
   * \sa setBorderPen()
   */
  const WPen& borderPen() const { return borderPen_; }

  /*! \brief Add a curve label.
   *
   * \sa CurveLabel::CurveLabel()
   */
  void addCurveLabel(const CurveLabel &label);

  /*! \brief Configure all curve labels at once.
   *
   * \sa addCurveLabel()
   */
  void setCurveLabels(const std::vector<CurveLabel>& labels);

  /*! \brief Clear all curve labels
   *
   * \sa addCurveLabel()
   */
  void clearCurveLabels();

  /*! \brief Get all of the registered curve labels
   *
   * \sa setCurveLabels()
   */
  const std::vector<CurveLabel>& curveLabels() const { return curveLabels_; }

  /*! @name Client side interaction
   *
   * These methods allow to activate the client side interactive features of
   * a %WCartesianChart.
   * 
   * \note Client side interaction is only available if the chart is drawn
   *       on an HTML canvas. This is the default rendering method on
   *       modern browsers, see WPaintedWidget::setPreferredMethod()
   *
   * \note Some features are currently not supported in interactive mode:
   *       - Axes set at ZeroValue position will not always be drawn correctly.
   *	     They may be clipped off outside of the chart area, and when
   *         zooming, the axis ticks will change size.
   *       - WAxis::setBreak() is incompatible with interactive mode
   *
   * @{
   */

  /*! \brief Returns whether this chart is interactive.
   *
   * Return true iff one of the interactive features is enabled, and
   * the chart is being rendered on an HTML canvas.
   */
  bool isInteractive() const;

  /*! \brief Enables zoom functionality.
   *
   * When using the mouse, press the ctrl key while scrolling to zoom
   * in/out a specific point on the chart. If you press shift+ctrl, it
   * will only zoom vertically. If you press alt+ctrl, it will only
   * zoom horizontally. To change these default mappings, use
   * setWheelActions().
   *
   * When using touch, you can use a pinch gesture to zoom in/out. If
   * the pinch gesture is vertical/horizontal, it will zoom only
   * vertically/horizontally, otherwise it will zoom both axes
   * equally.
   *
   * The default value is \c false.
   *
   * \sa zoomEnabled()
   * \sa setWheelActions(WheelActions)
   */
  void setZoomEnabled(bool zoom = true);

  /*! \brief Returns whether zoom is enabled.
   *
   * \sa setZoomEnabled()
   */
  bool zoomEnabled() const;

  /*! \brief Enables pan functionality.
   *
   * When using the mouse, you can click and drag to pan the chart (if
   * zoomed in), or use the scrollwheel.
   *
   * When using touch, you can drag to pan the chart. If the
   * rubberband effect is enabled, this is intertial (it will keep
   * scrolling after you let go) and there is an overscroll and bounce
   * back effect on the sides.
   *
   * The default value is \c false.
   *
   * \sa panEnabled()
   */
  void setPanEnabled(bool pan = true);
  
  /*! \brief Returns whether pan is enabled.
   *
   * \sa setPanEnabled()
   */
  bool panEnabled() const;

  /*! \brief Enables the crosshair functionality.
   *
   * When enabled, the crosshair will follow mouse movement,
   * and show in the top right corner the coordinate
   * (according to X axis and the first Y axis)
   * corresponding to this position.
   *
   * When using touch, the crosshair can be moved with a drag. If both
   * panning and the crosshair are enabled, the crosshair will be moved
   * when dragging close to the crosshair. Otherwise, the chart will pan.
   */
  void setCrosshairEnabled(bool crosshair = true);

  /*! \brief Returns whether the crosshair is enabled.
   *
   * \sa setCrosshairEnabled()
   */
  bool crosshairEnabled() const;

  /*! \brief Enables the follow curve functionality for a data series.
   *
   * This enables follow curve functionality for the data series
   * corresponding to the given column.
   *
   * If the data series is of type LineSeries or CurveSeries, the
   * crosshair can only be moved in the x direction. The y position of
   * the crosshair will be determined by the value of the data
   * series. The crosshair will snap to the nearest point that is
   * defined in the data series.
   *
   * When using the mouse, the x position will change on mouseover.
   * When using touch, the x position can be moved with a drag. The
   * follow curve functionality has priority over the crosshair functionality.
   *
   * Use column index -1 or disableFollowCurve() to disable the follow
   * curve feature.
   *
   * \note The follow curve functionality requires that the X axis values
   *       of the data series are monotonically increasing or decreasing.
   *
   * \deprecated Use setFollowCurve(const WDataSeries*) instead
   */
  void setFollowCurve(int modelColumn);

  /*! \brief Enabled the follow curve funtionality for a data series.
   *
   * This enables follow curve functionality for the data series
   * corresponding to the given column.
   *
   * If the data series is of type LineSeries or CurveSeries, the
   * crosshair can only be moved in the x direction. The y position of
   * the crosshair will be determined by the value of the data
   * series. The crosshair will snap to the nearest point that is
   * defined in the data series.
   *
   * When using the mouse, the x position will change on mouseover.
   * When using touch, the x position can be moved with a drag. The
   * follow curve functionality has priority over the crosshair functionality.
   *
   * Set to null to disable the follow curve feature.
   *
   * \note The follow curve functionality requires that the X axis values
   *       of the data series are monotonically increasing or decreasing.
   */
  void setFollowCurve(const WDataSeries *series);

  /*! \brief Disable the follow curve functionality.
   *
   * \sa setFollowCurve()
   */
  void disableFollowCurve();

  /*! \brief Returns the curve that is to be followed.
   *
   * If follow curve functionality is not enabled, returns -1.
   *
   * \sa setFollowCurve()
   */
  const WDataSeries *followCurve() const;

  /*! \brief Enables/disables the inertial scrolling and rubberband effect.
   *
   * \sa setPanEnabled()
   */
  void setRubberBandEffectEnabled(bool rubberBand = true);

  /*! \brief Checks whether the rubberband effect is enabled.
   * 
   * \see setRubberBandEffectEnabled()
   */
  bool rubberBandEffectEnabled() const;

  /*! \brief Sets the mapping of mouse wheel actions for interactive charts.
   *
   * \sa wheelActions()
   */
  void setWheelActions(WheelActions wheelActions);

  /*! \brief Returns the current mouse wheel actions for interactive charts.
   *
   * \sa setWheelActions()
   */
  WheelActions wheelActions() const { return wheelActions_; }
  
  /*! \brief Enables or disables soft label clipping on all axes.
   *
   * \sa WAxis::setSoftLabelClipping()
   */
  void setSoftLabelClipping(bool enabled);

  /*! \brief Sets whether series selection is enabled.
   *
   * If series selection is enabled, series can be selected with a mouse
   * click or long press. If the selected series is a LineSeries or CurveSeries,
   * it can be manipulated if \link setCurveManipulationEnabled()
   * curve manipulation\endlink is enabled. The series that are not selected,
   * will be shown in a lighter color.
   */
  void setSeriesSelectionEnabled(bool enabled = true);

  /*! \brief Returns whether series selection is enabled.
   *
   * \sa setSeriesSelectionEnabled()
   */
  bool seriesSelectionEnabled() const { return seriesSelectionEnabled_; }

  /*! \brief A signal that notifies the selection of a new curve.
   *
   * This signal is emitted if a series is selected using a mouse
   * click or long press. The first argument is the model column of the selected
   * series. The second argument is the point that was selected, in model
   * coordinates.
   *
   * \sa setSeriesSelectionEnabled()
   */
  Signal<const WDataSeries *, WPointF>& seriesSelected() { return seriesSelected_; }

  /*! \brief Sets the series that is currently selected.
   *
   * The series with the given model column will be selected. The other
   * series will be shown in a lighter color. The series that is currently
   * selected is the one that can be manipulated if \link setCurveManipulationEnabled()
   * curve manipulation\endlink is enabled, and it is a LineSeries or CurveSeries.
   *
   * The selected series can be changed using a long touch or mouse click.
   *
   * If the argument provided is null or \link setSeriesSelectionEnabled() series
   * selection\endlink is not enabled, no series will be selected.
   *
   * \sa setCurveManipulationEnabled()
   * \sa setSeriesSelectionEnabled()
   */
  void setSelectedSeries(const WDataSeries *series);

  /*! \brief Get the currently selected curve.
   *
   * -1 means that no curve is currently selected.
   *
   * \sa setSelectedSeries()
   */
  const WDataSeries *selectedSeries() const { return selectedSeries_; }

  /*! \brief Enable curve manipulation.
   *
   * If curve manipulation is enabled, the \link WDataSeries::setScale() scale\endlink
   * and \link WDataSeries::setOffset() offset\endlink of the \link selectedSeries()
   * selected curve\endlink can be manipulated interactively using drag, scroll,
   * and pinch.
   *
   * \sa WDataSeries::setOffset()
   * \sa WDataSeries::setScale()
   * \sa WDataSeries::selectedSeries()
   */
  void setCurveManipulationEnabled(bool enabled = true);

  /*! \brief Returns whether curve manipulation is enabled.
   *
   * \sa setCurveManipulationEnabled()
   */
  bool curveManipulationEnabled() const { return curveManipulationEnabled_; }

  /*! @}
   */

  void iterateSeries(SeriesIterator *iterator,
		     WPainter *painter, bool reverseStacked = false) const;

  // For use in WAxisSliderWidget
  void addAxisSliderWidget(WAxisSliderWidget *slider);
  void removeAxisSliderWidget(WAxisSliderWidget *slider);

private:
  WChart2DImplementation *interface_;

  Orientation orientation_;
  int XSeriesColumn_;
  ChartType type_;
  std::vector<WDataSeries *> series_;
  WAxis *axes_[3];
  double barMargin_;
  WLegend legend_;
  int axisPadding_;
  WPen borderPen_;
  WPen textPen_;

  /* render state */
  mutable int width_, height_;
  mutable WRectF chartArea_;
  mutable AxisValue location_[3];
  mutable bool hasDeferredToolTips_;

  bool jsDefined_;
  bool zoomEnabled_;
  bool panEnabled_;
  bool rubberBandEnabled_;
  bool crosshairEnabled_;
  bool seriesSelectionEnabled_;
  const WDataSeries *selectedSeries_;
  const WDataSeries *followCurve_;
  bool curveManipulationEnabled_;
  bool cObjCreated_;
  JSignal<> xTransformChanged_;
  JSignal<> yTransformChanged_;

  Signal<const WDataSeries *, WPointF> seriesSelected_;
  JSignal<double, double> jsSeriesSelected_;

  typedef std::map<const WDataSeries *, WJavaScriptHandle<WPainterPath> > PainterPathMap;
  PainterPathMap curvePaths_;
  std::vector<WJavaScriptHandle<WPainterPath> > freePainterPaths_;

  typedef std::map<const WDataSeries *, WJavaScriptHandle<WTransform> > TransformMap;
  TransformMap curveTransforms_;
  std::vector<WJavaScriptHandle<WTransform> > freeTransforms_;

  mutable WTransform xTransform_;
  mutable WTransform yTransform_;
  WJavaScriptHandle<WTransform> xTransformHandle_; // Scales and translates X axis
  WJavaScriptHandle<WTransform> yTransformHandle_; // Scales and translates Y axis

  struct PenAssignment {
    WJavaScriptHandle<WPen> pen;
    WJavaScriptHandle<WPen> textPen;
    WJavaScriptHandle<WPen> gridPen;

    PenAssignment(const WJavaScriptHandle<WPen>& pen,
		  const WJavaScriptHandle<WPen>& textPen,
		  const WJavaScriptHandle<WPen>& gridPen)
      : pen(pen), textPen(textPen), gridPen(gridPen)
    { }
  };
  typedef std::map<Axis,std::vector<PenAssignment> > PenMap;
  PenMap pens_;
  std::vector<WJavaScriptHandle<WPen> > freePens_;

  std::vector<CurveLabel> curveLabels_;

  std::vector<WAxisSliderWidget *> axisSliderWidgets_;

  WheelActions wheelActions_;

  JSignal<double, double> loadTooltip_;
  struct BarTooltip {
    BarTooltip(const WDataSeries &series, int xRow, int xColumn, int yRow, int yColumn)
      : series(&series),
	xRow(xRow),
	xColumn(xColumn),
	yRow(yRow),
	yColumn(yColumn)
    { }

    double xs[4];
    double ys[4];
    const WDataSeries *series;
    int xRow, xColumn, yRow, yColumn;
  };
  mutable std::vector<BarTooltip> barTooltips_;

  void init();
  static std::string wheelActionsToJson(WheelActions wheelActions);

  static WColor lightenColor(const WColor &in);

protected:
  virtual void modelChanged() WT_CXX11ONLY(override);
  virtual void modelReset() WT_CXX11ONLY(override);

  /** @name Rendering logic
   */
  //@{
  /*! \brief Paints the widget.
   *
   * This calls render() to paint on the paint device.
   */
  virtual void paintEvent(WPaintDevice *paintDevice) WT_CXX11ONLY(override);

  /*! \brief Renders the chart.
   *
   * Renders the chart within the given rectangle. To accomodate both
   * rendering of horizontal and vertically oriented charts, all rendering
   * logic assumes horizontal. This "chart coordinates" space is transformed
   * to painter coordinates using hv().
   *
   * \if cpp
   * The default implementation looks like:
   * \code
   * painter.save();
   * painter.translate(rectangle.topLeft());
   *
   * if (initLayout(rectangle)) {
   *   renderBackground(painter);
   *   renderGrid(painter, axis(XAxis));
   *   renderGrid(painter, axis(Y1Axis));
   *   renderGrid(painter, axis(Y2Axis));
   *   renderAxes(painter, Line);
   *   renderSeries(painter);
   *   renderAxes(painter, Labels);
   *   renderBorder(painter);
   *   renderCurveLabels(painter);
   *   renderLegend(painter);
   *   renderOther(painter);
   * }
   *
   * painter.restore();
   * \endcode
   * \endif
   */
  virtual void render(WPainter& painter, const WRectF& rectangle) const;

  /*! \brief Map (x, y) value pair to chart coordinates coordinates.
   *
   * The result needs further transformation using hv() to painter
   * coordinates.
   */
  virtual WPointF map(double xValue, double yValue, Axis yAxis = OrdinateAxis,
		      int currentXSegment = 0, int currentYSegment = 0)
    const;

  /*! \brief Utility function for rendering text.
   *
   * This method renders text on the chart position <i>pos</i>, with a
   * particular alignment <i>flags</i>. These are both specified in
   * chart coordinates. The position is converted to painter
   * coordinates using hv(), and the alignment flags are changed
   * accordingly. The rotation, indicated by <i>angle</i> is specified
   * in painter coordinates and thus an angle of 0 always indicates
   * horizontal text, regardless of the chart orientation.
   */
  virtual void renderLabel(WPainter& painter, const WString& text,
			   const WPointF& pos,
			   WFlags<AlignmentFlag> flags, double angle,
			   int margin) const;

  /*! \brief Conversion between chart and painter coordinates.
   *
   * Converts from chart coordinates to painter coordinates, taking
   * into account the chart orientation.
   */
  WPointF hv(double x, double y) const;

  /*! \brief Conversion between chart and painter coordinates.
   *
   * Converts from chart coordinates to painter coordinates, taking
   * into account the chart orientation.
   */
  WPointF hv(const WPointF& f) const;

  /*! \brief Conversion between chart and painter coordinates.
   *
   * Converts from chart coordinates to painter coordinates, taking
   * into account the chart orientation.
   */
  WRectF hv(const WRectF& f) const;

  /*! \brief Returns the segment area for a combination of X and Y
   *         segments.
   *
   * This segment area is used for clipping when rendering in a
   * particular segment.
   */
  WRectF chartSegmentArea(const WAxis& yAxis, int xSegment, int ySegment) const;

  /*! \brief Calculates the chart area.
   *
   * This calculates the chartArea(), which is the rectangle (in chart
   * coordinates) that bounds the actual chart (thus excluding axes,
   * labels, titles, legend, etc...).
   *
   * \sa plotAreaPadding()
   */
  virtual void calcChartArea() const;

  /*! \brief Prepares the axes for rendering.
   *
   * Computes axis properties such as the range (if not manually
   * specified), label interval (if not manually specified) and axis
   * locations. These properties are stored within the axes.
   *
   * \sa initLayout()
   */
  virtual bool prepareAxes() const;

  /*! \brief Renders the background.
   *
   * \sa render()
   */
  virtual void renderBackground(WPainter& painter) const;

  /*! \brief Renders one or more properties of the axes.
   *
   * This calls renderAxis() for each axis.
   *
   * \sa render()
   */
  virtual void renderAxes(WPainter& painter,
			  WFlags<AxisProperty> properties) const;

  /*! \brief Renders the border of the chart area.
   *
   * \sa render()
   * \sa setBorderPen()
   */
  virtual void renderBorder(WPainter& painter) const;

  /*! \brief Renders the curve labels.
   *
   * \sa render()
   * \sa addCurveLabel()
   */
  virtual void renderCurveLabels(WPainter& painter) const;

  /*! \brief Renders all series data, including value labels.
   *
   * \sa render()
   */
  virtual void renderSeries(WPainter& painter) const;

  /*! \brief Renders the (default) legend and chart titles.
   *
   * \sa render()
   */
  virtual void renderLegend(WPainter& painter) const;

  /*! \brief Renders properties of one axis.
   *
   * \sa renderAxes()
   */
  virtual void renderAxis(WPainter& painter, const WAxis& axis,
			  WFlags<AxisProperty> properties) const;

  /*! \brief Renders grid lines along the ticks of the given axis.
   *
   * \sa render()
   */
  virtual void renderGrid(WPainter& painter, const WAxis& axis) const;

  /*! \brief Renders other, user-defined things
   *
   * The default implementation sets the painter's
   * \link WPainter::setClipPath() clip path\endlink to
   * the chart area, but does not enable clipping.
   *
   * This method can be overridden to draw extra content onto
   * the chart.
   *
   * Chart coordinates can be mapped to device coordinates with
   * mapToDeviceWithoutTransform(). If these need to move and scale
   * along with the zoom range, those points can be transformed with
   * zoomRangeTransform().
   *
   * This method is called last by default. If you want to render
   * other things at some other moment, you can override
   * render(WPainter&, const WRectF&).
   */
  virtual void renderOther(WPainter& painter) const;

  /*! \brief Calculates the total number of bar groups.
   */
  int calcNumBarGroups();

  //@}

  virtual void render(WFlags<RenderFlag> flags) WT_CXX11ONLY(override) ;

  virtual void setFormData(const FormData& formData) WT_CXX11ONLY(override) ;

  /*! \brief Returns the current zoom range transform.
   *
   * This transform maps device coordinates from the fully zoomed out position
   * to the current zoom range.
   *
   * This transform is a \link WTransform::isJavaScriptBound()
   * JavaScript bound\endlink transform if this chart is
   * interactive. Otherwise, this transform is just the identity
   * transform.
   *
   * \sa setZoomEnabled()
   * \sa setPanEnabled
   * \sa WAxis::setZoomRange()
   */
  WTransform zoomRangeTransform() const;

private:
  int calcNumBarGroups() const;
  int seriesIndexOf(int modelColumn) const;
  int seriesIndexOf(const WDataSeries &series) const;
  void clearPens();
  void createPensForAxis(Axis axis);
  std::string cObjJsRef() const; // WCartesianChart JS object
  void assignJSHandlesForAllSeries();
  void freeJSHandlesForAllSeries();
  void assignJSPathsForSeries(const WDataSeries &series);
  void freeJSPathsForSeries(const WDataSeries &series);
  void freeAllJSPaths();
  void assignJSTransformsForSeries(const WDataSeries &series);
  void freeJSTransformsForSeries(const WDataSeries &series);
  void freeAllJSTransforms();
  void updateJSPens(WStringStream& js) const;
  void updateJSPensForAxis(WStringStream& js, Axis axis) const;
  void updateJSConfig(const std::string &key, boost::any value);
  WPainterPath pathForSeries(const WDataSeries &series) const; // For use in WAxisSliderWidget
  WTransform zoomRangeTransform(WTransform xTransform, WTransform yTransform) const;
  WTransform calculateCurveTransform(const WDataSeries &series) const;
  WTransform curveTransform(const WDataSeries &series) const;
  void setZoomAndPan();
  void addAreaMask();
  void xTransformChanged();
  void yTransformChanged();
  void jsSeriesSelected(double x, double y);
  void loadTooltip(double x, double y);
  WPointF hv(double x, double y, double width) const;
  WPointF inverseHv(double x, double y, double width) const;
  WPointF inverseHv(const WPointF &p) const;
  WRectF insideChartArea() const;
  void defineJavaScript();

  bool axisSliderWidgetForSeries(WDataSeries *series) const;

  class IconWidget : public WPaintedWidget {
  public:
    IconWidget(WCartesianChart *chart, 
	       int index, 
	       WContainerWidget *parent = 0);
    
  protected:
    virtual void paintEvent(WPaintDevice *paintDevice);
    
  private:
    WCartesianChart* chart_;
    int index_;
  };

  friend class WDataSeries;
  friend class SeriesRenderer;
  friend class LineSeriesRenderer;
  friend class BarSeriesRenderer;
  friend class LabelRenderIterator;
  friend class MarkerRenderIterator;
  friend class MarkerMatchIterator;
  friend class WChart2DImplementation;
  friend class WAxisSliderWidget;
};

  }
}

#endif // CHART_WCARTESIAN_CHART_H_
