// src/cairo_widget.hh - Copyright 2005, 2006, University
//               of Padova, dept. of Pure and Applied
//               Mathematics
//
// This file is part of SGPEMv2.
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// SGPEMv2 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SGPEMv2. If not, see http://www.gnu.org/licenses/.


#ifndef CAIRO_WIDGET_HH
#define CAIRO_WIDGET_HH 1

#include <cairomm/context.h>
#include <gtkmm/widget.h>
#include <gdkmm/pixbuf.h>

namespace sgpem
{
  /**
   * \brief Base abstract class to derive widgets that use cairo graphic output.
   *
   * This class is the starting point to derive graphics widgets using cairo.
   * It provides explicit in memory double buffering, and scaling.
   * In order to build a new widget that uses cairo output the developer should
   * use this as base class.
   *
   * The implementer:
   * - must
   *   - implement draw_widget() to draw in the widget client area \see draw_widget()
   * - should
   *   - define calc_drawing_size() to provide correct size calculation
   *   - call resize_redraw() to calc size and redraw
   * - can
   *   - define calc_widget_size() to get predefined widget dimensions
   *   - set the desired scaling mode \see set_scaling_mode()
   *   - call redraw() to only redraw the widget
   *
   */
  class CairoWidget : public Gtk::Widget
  {
  public:
    /**
     * \brief Defines cairo widget scaling modes.
     *
     * Scaling is the geometric operation that maps draw_widget()'s output
     * into client area.
     * There are some ways to scale the output to fit in client area.
     * The developer does'n need to know how to build the scaling but
     * only how it works.
     * The defined modes are:
     * - scaling_none without any scaling
     * - scaling_to_w uses width to calculate scale factor
     * - scaling_to_h uses height to calculate scale factor
     * - scaling_min  uses minimum scale factor from width, height
     * - scaling_max  uses maximum scale factor from width, height
     * - scaling_all  scale to stretch into client area
     *
     * Each mode has advantages and disvantages.
     * - scaling_none doesn't do any scaling at all.
     *   The graphics are rendered as they are drawn.
     * - scaling_to_w calc both x and y scaling to fit the client width.
     *   Probably the draw will exceed the client area height.
     * - scaling_to_h calc both x and y scaling to fit the client height.
     *   Probably the draw will exceed the client area width.
     * - scaling_min  calc both x and y scaling to enter in the client area.
     *   The draw will always fit in the window.
     * - scaling_max  calc both x and y scaling to not enter in the client area.
     *   The draw will rarely fit in the window.
     * - scaling_all  calc x and y scaling to fit exactly in the client area.
     *   The draw will always fit the entire client area.
     *   NOTE: This scaling mode stretch the drawing and not respect the
     *   ratio between width and height.
     *
     */
    enum scaling_mode
    {
      scaling_none   =  0,      // without any scaling
      scaling_to_w   =  1 << 0, // uses width to calculate scale factor
      scaling_to_h   =  1 << 1, // uses height to calculate scale factor
      scaling_min    =  1 << 2, // uses minimum scale factor from width, height
      scaling_max    =  1 << 3, // uses maximum scale factor from width, height
      scaling_all    =  1 << 4  // scale to stretch into client area
    };

    /**
     * \brief Unique constructor, initialize data members.
     */
    CairoWidget();

    /**
     * \brief Widget's destructor.
     */
    virtual ~CairoWidget();

    // scaling related functions
    /**
     * \brief Assign the desired scaling mode and returns the old one.
     *
     * Change scaling mode; default is scaling_none.
     * The programmer must call resize_redraw() becous it take effect.
     *
     * \param scaling_mode The new desired scaling mode.
     * \return Previous scaling mode.
     */
    scaling_mode set_scaling_mode(scaling_mode scaling_mode);

    /**
     * \brief Retrieve current scaling mode.
     *
     * \return Previous scaling mode.
     */
    scaling_mode get_scaling_mode() const;

    /**
     * \brief Force the widgets readrawing without calc new size.
     *
     * Useful for redrawing the widget (double buffering related).
     */
    void redraw();

    /**
     * \brief Force the widgets readrawing without calc new size.
     *
     * Need for auto resizing and redrawing the widget (double buffering related).
     */
    void resize_redraw();

  protected:
    /**
     * \brief Widgets size type.
     */
    typedef guint    size_t;

    // implementation of fundamental widget's method
    /**
     * \brief Implements standard widget's on_realize() mehtod.
     *
     * The on_realize() function is called by the Gtk windowing system
     * before it creates a new widget.
     * See gtkmm documentation for more informations.
     */
    virtual void on_realize();
    virtual void on_unrealize(); // doesn't need to be documented.

    /**
     * \brief Implements standard widget's on_size_allocate() mehtod.
     *
     * The on_size_allocate() function is called by the Gtk windowing system
     * after creation of a new widget asking the desired widget dimensions.
     * See gtkmm documentation for more informations.
     */
    virtual void on_size_allocate(const Gtk::Allocation& allocation);

    /**
     * \brief Implements standard widget's on_size_request() mehtod.
     *
     * The on_size_request() function is called by the Gtk windowing system
     * when need to change the widget dimensions.
     * See gtkmm documentation for more informations.
     */
    virtual void on_size_request(Gtk::Requisition* request);

    /**
     * \brief Implements standard widget's on_expose_event() mehtod.
     *
     * The on_draw() function is called by the Gtk windowing system
     * when the widgets' redrawing is requested after creation or to update.
     * See gtkmm documentation for more informations.
     */
    virtual bool on_draw (const Cairo::RefPtr<Cairo::Context>& ctx);

    /**
     * \brief Client area drawing method.
     *
     * In this method the implemented widget must "do the things"
     * and (obviously) its implementation is a need.
     * The programmer then use the passed cairo context, ctx, to
     * draw in the widgets client area.
     * See cairo documentation for more informations.
     */
    virtual void draw_widget(const Cairo::RefPtr<Cairo::Context>& ctx) = 0;

    /**
     * \brief Method to calculate widget dimensions.
     *
     * The user should implement this method to allow scaling factors calculation.
     * Is necessary if scaling is needed/used.
     */
    virtual void calc_drawing_size(const Cairo::RefPtr<Cairo::Context>& ctx, size_t& width, size_t& height);

    // with this method CairoWidget tells the needed widget dimensions
    /**
     * \brief Method to define default widget dimensions.
     *
     * The user can implement this method to give default dimensions.
     * It isn't necessary because scaling (and resizing) take care of calc_drawing_size() results.
     */
    virtual void calc_widget_size(int& width, int& height);

    /**
     * \brief Calculate scaling factors to apply during widget's redrawing.
     *
     * This method is used internally to recomute scaling factors.
     * Implementors of derived calsses doesn't worry about it.
     */
    void calc_scale_factors();

    /**
     * \brief Calculated width of drawing.
     */
    size_t      _draw_w;

    /**
     * \brief Calculated height of drawing.
     */
    size_t      _draw_h;

  private:
    /**
     * \brief Client area width of widget.
     */
    int      _client_w;

    /**
     * \brief Client area height of widget.
     */
    int      _client_h;

    /**
     * \brief Client area width of pixmap.
     */
    int      _pixbuf_w;

    /**
     * \brief Client area width of pixmap.
     */
    int      _pixbuf_h;

    /**
     * \brief Cairo x scale factor.
     */
    double      _scale_x;

    /**
     * \brief Cairo y scale factor.
     */
    double      _scale_y;

    /**
     * \brief Flag to signal require redrawing.
     */
    bool        _need_redraw;

    /**
     * \brief Actual scaling mode.
     */
    scaling_mode        _scaling_mode;

    /**
     * \brief Offscreen pixmap used for double-buffering.
     */
    Glib::RefPtr<Gdk::Pixbuf> _buf;

   /**
    *
    */
    Glib::RefPtr<Gdk::Window> _ref_gdk_window;
  };

} //~ namespace sgpem


#endif //~ CAIRO_WIDGET_HH
