// 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 WPAINTED_WIDGET_H_
#define WPAINTED_WIDGET_H_

#include <Wt/WInteractWidget>
#include <Wt/WJavaScript>

namespace Wt {

class WAbstractArea;
class WImage;
class WPaintDevice;
class WWidgetPainter;

/*! \class WPaintedWidget Wt/WPaintedWidget Wt/WPaintedWidget
 *  \brief A widget that is painted using vector graphics.
 *
 * A painted widget is rendered from basic drawing
 * primitives. Rendering is done not on the server but on the browser,
 * using different rendering methods:
 *
 * <table>
 *   <tr><td><b>Browser</b></td><td><b>Methods</b></td>
 *       <td><b>Default method</b></td></tr>
 *   <tr><td>Firefox 1.5+</td><td>HtmlCanvas, InlineSVG, PngImage</td>
 *       <td>HtmlCanvas</td></tr>
 *   <tr><td>Internet Explorer 6.0+</td><td>InlineVML, PngImage</td>
 *       <td>InlineVML</td></tr>
 *   <tr><td>Internet Explorer 9+</td><td>HtmlCanvas, InlineSVG, PngImage</td>
 *       <td>HtmlCanvas</td></tr>
 *   <tr><td>Safari</td><td>HtmlCanvas, InlineSVG, PngImage</td>
 *       <td>HtmlCanvas</td></tr>
 *   <tr><td>Opera</td><td>InlineSVG, HtmlCanvas*, PngImage</td>
 *       <td>InlineSVG</td></tr>
 *   <tr><td>other</td><td>?</td><td>HtmlCanvas, PngImage</td></tr>
 * </table>
 *
 * <i>* HtmlCanvas occasionally suffers from rendering artefacts in Opera.</i>
 *
 * The different rendering methods correspond to different
 * WPaintDevice implementations, from which this widget choses a
 * suitable one depending on the browser capabilities and configuration.
 *
 * If no JavaScript is available, the JavaScript-based HtmlCanvas will
 * not be used, and InlineSVG will be used instead. The method used
 * may be changed by using setPreferredMethod().
 *
 * In some browsers, InlineSVG requires that the document is rendered as
 * XHTML. This must be enabled in the configuration file using the
 * <tt>&lt;send-xhtml-mime-type&gt;</tt> option. By default, this
 * option is off. Firefox 4 and Chrome do support svg in normal html mode.
 *
 * The PngImage is the most portable rendering method, and may be the
 * fastest if the painting is of high complexity and/or the image is
 * fairly small.
 *
 * To use a %WPaintedWidget, you must derive from it and reimplement
 * paintEvent(WPaintDevice *paintDevice). To paint on a WPaintDevice,
 * you will need to use a WPainter. Repainting is triggered by calling
 * the update() method.
 *
 * \if cpp
 * Usage example:
 * \code
 * class MyPaintedWidget : public Wt::WPaintedWidget
 * {
 * public:
 *   MyPaintedWidget(Wt::WContainerWidget *parent = 0)
 *     : Wt::WPaintedWidget(parent),
 *       foo_(100)
 *   {
 *      resize(200, 200); // provide a default size
 *   }
 *
 *   void setFoo(int foo) {
 *      foo_ = foo;
 *      update(); // trigger a repaint
 *   }
 *
 * protected:
 *   void paintEvent(Wt::WPaintDevice *paintDevice) {
 *     Wt::WPainter painter(paintDevice);
 *     painter.drawLine(20, 20, foo_, foo_);
 *     ...
 *   }
 *
 * private:
 *   int foo_;
 * };
 * \endcode
 * \endif
 * 
 * <h3>CSS</h3>
 *
 * Styling through CSS is not applicable.
 *
 * \note A %WPaintedWidget requires that it is given a size using resize() or
 *       by a layout manager.
 *
 * \sa WImage
 *
 * \ingroup painting
 */
class WT_API WPaintedWidget : public WInteractWidget
{
public:
  /*! \brief Enumeration that indicates a rendering method.
   */
  enum Method {
    /*! \brief SVG (Most browsers) or VML (Internet Explorer < 9) embedded in
     *         the page.
     */
    InlineSvgVml,

    /*! \brief The WHATWG HTML 5 canvas element.
     */
    HtmlCanvas,

    /*! \brief Using a PNG image resource.
     */
    PngImage
  };

  /*! \brief Create a new painted widget.
   */
  WPaintedWidget(WContainerWidget *parent = 0);

  /*! \brief Destructor.
   */
  ~WPaintedWidget();

  /*! \brief Sets the preferred rendering method.
   *
   * When \p method is supported by the browser, then it is chosen
   * for rendering.
   */
  void setPreferredMethod(Method method);

  /*! \brief Returns the preferred rendering method.
   *
   * \sa setPreferredMethod(Method)
   */
  Method preferredMethod() const { return preferredMethod_; }

  /*! \brief Lets the widget repaint itself.
   *
   * Repainting is not immediate, but happens after when the event loop
   * is exited.
   *
   * Unless a Wt::PaintUpdate paint flag is set, the widget is first
   * cleared.
   */
  void update(WFlags<PaintFlag> flags = 0);

  virtual void resize(const WLength& width, const WLength& height);

  /*! \brief Adds an interactive area.
   *
   * Adds the \p area which listens to events in a specific region
   * of the widget. Areas are organized in a list, to which the given
   * \p area is appended. When areas overlap, the area with the
   * lowest index receives the event.
   *
   * Ownership of the \p area is transferred to the widget.
   *
   * \note When defining at least one area, no more events will
   * propagate to the widget itself. As a work-around, you can emulate
   * this by listening for events on a WRectArea that corresponds to
   * the whole widget, and which is added as the last area (catching
   * all events that were not caught by preceding areas).
   *
   * \sa insertArea(int, WAbstractArea *)
   */
  void addArea(WAbstractArea *area);

  /*! \brief Inserts an interactive area.
   *
   * Inserts the \p area which listens to events in the
   * coresponding area of the widget. Areas are organized in a list,
   * and the <i>area</i> is inserted at index \p index. When areas
   * overlap, the area with the lowest index receives the event.
   *
   * Ownership of the \p Area is transferred to the widget.
   *
   * \note When defining at least one area, no more events will
   * propagate to the widget itself. As a work-around, you can emulate
   * this by listening for events on a WRectArea that corresponds to
   * the whole widget, and which is added as the last area (catching
   * all events that were not caught by preceding areas).
   *
   * \sa addArea(WAbstractArea *)
   */
  void insertArea(int index, WAbstractArea *area);

  /*! \brief Removes an interactive area.
   *
   * Removes the \p area from this widget, returning the
   * ownership.
   *
   * \sa addArea(WAbstractArea *)
   */
  void removeArea(WAbstractArea *area);

  /*! \brief Returns the interactive area at the given index.
   *
   * Returns \c 0 if \p index was invalid.
   *
   * \sa insertArea(int, WAbstractArea *)
   */
  WAbstractArea *area(int index) const;

  /*! \brief Returns the interactive areas set for this widget.
   *
   * \sa addArea()
   */
  const std::vector<WAbstractArea *> areas() const;

protected:
  virtual void layoutSizeChanged(int width, int height);

  /*! \brief Returns the actual method used for rendering.
   *
   * The default method considers browser capabilites and the preferred
   * method to make an actual choice for the implementation.
   *
   * You may want to reimplement this method to override this choice.
   */
  virtual Method getMethod() const;

  /*! \brief Paints the widget.
   *
   * You should reimplement this method to paint the contents of the widget,
   * using the given paintDevice.
   */
  virtual void paintEvent(WPaintDevice *paintDevice) = 0;

  /*! \brief Creates a paint device.
   *
   * Although it's usually not necessary to call this function, you may
   * want to reimplement this function to customize or specialize the
   * device used for painting the widget.
   *
   * \if cpp
   * Ownership of the device is transferred to the caller.
   * \endif
   */
  virtual WPaintDevice *createPaintDevice() const;

  virtual DomElementType domElementType() const;
  virtual void        updateDom(DomElement& element, bool all);
  virtual DomElement *createDomElement(WApplication *app);
  virtual void        getDomChanges(std::vector<DomElement *>& result,
				    WApplication *app);
  virtual void        propagateRenderOk(bool deep);

  virtual void enableAjax();

private:
  Method            preferredMethod_;
  WWidgetPainter   *painter_;
  bool              needRepaint_;
  bool              sizeChanged_;
  bool              areaImageAdded_;
  WFlags<PaintFlag> repaintFlags_;
  WImage           *areaImage_;
  int               renderWidth_, renderHeight_;

  void resizeCanvas(int width, int height);
  bool createPainter();
  void createAreaImage();

  friend class WWidgetVectorPainter;
  friend class WWidgetCanvasPainter;
  friend class WWidgetRasterPainter;
};

}

#endif // WPAINTED_WIDGET_H_
