/* Copyright (C) 2010 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

_DEFS(gtkmm,gtk)
_PINCLUDE(glibmm/private/object_p.h)
_PINCLUDE(gtk/gtk.h)

#include <gtkmm/styleprovider.h>
#include <gtkmm/border.h>
#include <gtkmm/enums.h>
#include <gdkmm/window.h>
#include <gdkmm/screen.h>

#include <gtkmm/enums.h>
#include <gtkmm/iconsource.h>
#include <gtkmm/iconset.h>
#include <gtkmm/widgetpath.h>
#include <pangomm/context.h>
#include <pangomm/fontdescription.h>
#include <pangomm/layout.h>
#include <gtkmmconfig.h>

namespace Gdk
{
class GTKMM_API FrameClock;
}

namespace Gtk
{

class GTKMM_API IconSet;

//TODO: Add (and translate to C++) the code examples from the C documentation.
/** This object stores styling information affecting a widget defined by WidgetPath.
 *
 * In order to construct the final style information, StyleContext
 * queries information from all attached StyleProviders. Style providers
 * can be either attached explicitly to the context through
 * add_provider(), or to the screen through
 * add_provider_for_screen(). The resulting style is a
 * combination of all providers' information in priority order.
 *
 * For GTK+ widgets, any StyleContext returned by
 * Widget::get_style_context() will already have a WidgetPath, a
 * Gdk::Screen and RTL/LTR information set, The style context will be also
 * updated automatically if any of these settings change on the widget.
 *
 * If you are using the theming layer standalone, you will need to set a
 * widget path and a screen yourself to the created style context through
 * set_path() and set_screen(), as well
 * as updating the context yourself using invalidate()
 * whenever any of the conditions change, such as a change in the
 * Settings::property_gtk_theme_name() setting or a hierarchy change in the rendered
 * widget.
 *
 * <h2>Transition animations</h2>
 *
 * StyleContext has built-in support for state change transitions.
 * Note that these animations respect the Settings::property_gtk_enable_animations()
 * setting.
 *
 * For simple widgets where state changes affect the whole widget area,
 * calling notify_state_change() with a no region
 * is sufficient to trigger the transition animation. And GTK+ already
 * does that when Widget::set_state() or Widget::set_state_flags()
 * are called.
 *
 * If a widget needs to declare several animatable regions (i.e. not
 * affecting the whole widget area), its Widget::signal_draw() signal handler
 * needs to wrap the render operations for the different regions with
 * calls to push_animatable_region() and
 * pop_animatable_region(). These methods take an
 * identifier for the region which must be unique within the style context.
 * For simple widgets with a fixed set of animatable regions, using an
 * enumeration works well.
 *
 * For complex widgets with an arbitrary number of animatable regions, it
 * is up to the implementation to come up with a way to uniquely identify
 * each animatable region. Using pointers to internal objects is one way
 * to achieve this.
 *
 * The widget also needs to notify the style context about a state change
 * for a given animatable region so the animation is triggered.
 * notify_state_change() can take no region IDs, meaning that the whole widget
 * area will be updated by the animation.
 *
 * @newin{3,0}
 */
class GTKMM_API StyleContext : public Glib::Object
{
  _CLASS_GOBJECT(StyleContext, GtkStyleContext, GTK_STYLE_CONTEXT, Glib::Object, GObject, , , GTKMM_API)
protected:
  _CTOR_DEFAULT()

public:
  _WRAP_CREATE()
  _IGNORE(gtk_style_context_new)

  _WRAP_METHOD(static void add_provider_for_screen(const Glib::RefPtr<Gdk::Screen>& screen, const Glib::RefPtr<StyleProvider>& provider, guint priority), gtk_style_context_add_provider_for_screen)
  _WRAP_METHOD(static void remove_provider_for_screen(const Glib::RefPtr<Gdk::Screen>& screen, const Glib::RefPtr<StyleProvider>& provider), gtk_style_context_remove_provider_for_screen)

  _WRAP_METHOD(void add_provider(const Glib::RefPtr<StyleProvider>& provider, guint priority), gtk_style_context_add_provider)

  _WRAP_METHOD(void remove_provider(const Glib::RefPtr<StyleProvider>& provider), gtk_style_context_remove_provider)

  _WRAP_METHOD(void context_save(), gtk_style_context_save)
  _WRAP_METHOD(void context_restore(), gtk_style_context_restore)

//TODO: GtkCssSection * gtk_style_context_get_section (GtkStyleContext *context, const gchar     *property);
/* TODO:
  _WRAP_METHOD(void get_property (
                                     const Glib::ustring& property,
                                     StateFlags    state,
                                     GValue          *value), gtk_style_context_get_property)
  _WRAP_METHOD(void get_valist   (
                                     StateFlags    state,
                                     va_list          args), gtk_style_context_get_valist)
  _WRAP_METHOD(void get          (
                                     StateFlags    state,
                                     ...) G_GNUC_NULL_TERMINATED;
*/

  _WRAP_METHOD(void set_state(StateFlags flags), gtk_style_context_set_state)
  _WRAP_METHOD(StateFlags get_state() const, gtk_style_context_get_state)

  _WRAP_METHOD(void set_scale(int scale), gtk_style_context_set_scale)
  _WRAP_METHOD(int get_scale() const, gtk_style_context_get_scale)

  _WRAP_METHOD(bool state_is_running(StateType state, gdouble* progress), gtk_style_context_state_is_running, deprecated "This function always returns <tt>false</tt>.")

  _WRAP_METHOD(void set_path(const WidgetPath& path), gtk_style_context_set_path)

#m4 _CONVERSION(`const GtkWidgetPath*',`WidgetPath',`WidgetPath(const_cast<GtkWidgetPath*>($3), true)')
  _WRAP_METHOD(WidgetPath get_path() const, gtk_style_context_get_path)

  _WRAP_METHOD(void set_parent(const Glib::RefPtr<StyleContext>& parent), gtk_style_context_set_parent)
  void unset_parent();

  _WRAP_METHOD(Glib::RefPtr<StyleContext> get_parent(), gtk_style_context_get_parent, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const StyleContext> get_parent() const, gtk_style_context_get_parent, refreturn, constversion)

#m4 _CONVERSION(`GList*',`std::vector<Glib::ustring>',`Glib::ListHandler<Glib::ustring>::list_to_vector($3, Glib::OWNERSHIP_SHALLOW)')
  _WRAP_METHOD(std::vector<Glib::ustring> list_classes() const, gtk_style_context_list_classes)

  _WRAP_METHOD(void add_class(const Glib::ustring& class_name), gtk_style_context_add_class)
  _WRAP_METHOD(void remove_class(const Glib::ustring& class_name), gtk_style_context_remove_class)
  _WRAP_METHOD(bool has_class(const Glib::ustring& class_name), gtk_style_context_has_class)

  _WRAP_METHOD(GList* list_regions(), gtk_style_context_list_regions, deprecated "There is no replacement.")
  _WRAP_METHOD(void add_region(const Glib::ustring& region_name, RegionFlags flags), gtk_style_context_add_region, deprecated "There is no replacement.")
  _WRAP_METHOD(void remove_region(const Glib::ustring& region_name), gtk_style_context_remove_region, deprecated "There is no replacement.")
  _WRAP_METHOD(bool has_region(const Glib::ustring& region_name, RegionFlags& flags_return), gtk_style_context_has_region, deprecated "There is no replacement.")

  /** Gets the value of a style property
   * @param property_name The name of a style property.
   * @param value Location to return the property value.
   */
  template <class PropertyType>
  void get_style_property(const Glib::ustring& property_name, PropertyType& value) const;

  _WRAP_METHOD(void get_style_property_value(
                   const Glib::ustring& property_name, Glib::ValueBase& value) const,
               gtk_style_context_get_style_property)
  _IGNORE(gtk_style_context_get_style, gtk_style_context_get_style, )

  _WRAP_METHOD(Glib::RefPtr<IconSet> lookup_icon_set(const Glib::ustring& stock_id), gtk_style_context_lookup_icon_set, refreturn, deprecated "Use IconTheme::lookup_icon() instead.")

  // gtk_icon_set_render_icon_pixbuf() in gtkstylecontext.h is wrapped in IconSet::render_icon_pixbuf().

  _WRAP_METHOD(void set_screen(const Glib::RefPtr<Gdk::Screen>& screen), gtk_style_context_set_screen)

  _WRAP_METHOD(Glib::RefPtr<Gdk::Screen> get_screen(), gtk_style_context_get_screen, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Gdk::Screen> get_screen() const, gtk_style_context_get_screen, refreturn, constversion)

  _WRAP_METHOD(void set_direction(TextDirection direction), gtk_style_context_set_direction,
    deprecated "Use set_state() with Gtk::STATE_FLAG_DIR_LTR and Gtk::STATE_FLAG_DIR_RTL instead.")
  _WRAP_METHOD(TextDirection get_direction() const, gtk_style_context_get_direction,
    deprecated "Use get_state() and check for Gtk::STATE_FLAG_DIR_LTR and Gtk::STATE_FLAG_DIR_RTL instead.")

  _WRAP_METHOD(void set_junction_sides(JunctionSides sides), gtk_style_context_set_junction_sides)
  _WRAP_METHOD(JunctionSides get_junction_sides() const, gtk_style_context_get_junction_sides)

  _WRAP_METHOD(void set_frame_clock(const Glib::RefPtr<Gdk::FrameClock>& frame_clock), gtk_style_context_set_frame_clock, newin "3,24")
  _WRAP_METHOD(Glib::RefPtr<Gdk::FrameClock> get_frame_clock(), gtk_style_context_get_frame_clock, refreturn, newin "3,24")
  _WRAP_METHOD(Glib::RefPtr<const Gdk::FrameClock> get_frame_clock() const, gtk_style_context_get_frame_clock, refreturn, constversion, newin "3,24")

  _WRAP_METHOD(bool lookup_color(const Glib::ustring& color_name, Gdk::RGBA& color), gtk_style_context_lookup_color)

  _WRAP_METHOD(void notify_state_change(const Glib::RefPtr<Gdk::Window>& window, gpointer region_id, StateType state, bool state_value), gtk_style_context_notify_state_change, deprecated "This function does nothing.")

  _WRAP_METHOD(void cancel_animations(gpointer region_id), gtk_style_context_cancel_animations, deprecated "This function does nothing.")
  _WRAP_METHOD(void scroll_animations(const Glib::RefPtr<Gdk::Window>& window, int dx, int dy), gtk_style_context_scroll_animations, deprecated "This function does nothing.")

  _WRAP_METHOD(void push_animatable_region(gpointer region_id), gtk_style_context_push_animatable_region, deprecated "This function does nothing.")
  _WRAP_METHOD(void pop_animatable_region(), gtk_style_context_pop_animatable_region, deprecated "This function does nothing.")

  /** Gets the foreground color for a given state.
   *
   * @newin{3,0}
   *
   * @param state State to retrieve the color for.
   * @return The foreground color for the given state.
   */
  Gdk::RGBA get_color(StateFlags state = (StateFlags)0) const;
  _IGNORE(gtk_style_context_get_color)

_DEPRECATE_IFDEF_START
  /** @deprecated Use render_background() instead.
   */
  Gdk::RGBA get_background_color(StateFlags state = (StateFlags)0) const;
  _IGNORE(gtk_style_context_get_background_color)

  /** @deprecated Use render_frame() instead.
   */
  Gdk::RGBA  get_border_color(StateFlags state = (StateFlags)0) const;
  _IGNORE(gtk_style_context_get_border_color)
_DEPRECATE_IFDEF_END

  /** Returns the font description for a given state.
   *
   * @newin{3,0}
   *
   * @param state State to retrieve the font for.
   * @return The Pango::FontDescription for the given state.
   */
  Pango::FontDescription get_font(StateFlags state =  (StateFlags)0) const;
  _IGNORE(gtk_style_context_get_font)

  Border get_border(StateFlags state = (StateFlags)0) const;
  _IGNORE(gtk_style_context_get_border)

  Border get_padding(StateFlags state = (StateFlags)0) const;
  _IGNORE(gtk_style_context_get_padding)

  Border get_margin (StateFlags state = (StateFlags)0) const;
  _IGNORE(gtk_style_context_get_margin)

  _WRAP_METHOD(void invalidate(), gtk_style_context_invalidate, deprecated "Style contexts are invalidated automatically.")
  //TODO: _WRAP_METHOD(void reset_widgets(const Glib::RefPtr<Gdk::Screen>& screen), gtk_style_context_reset_widgets)

  _WRAP_METHOD(void set_background(const Glib::RefPtr<Gdk::Window>& window), gtk_style_context_set_background,
    deprecated "Use render_background() instead. Note that clients still using this function are now responsible for calling this function again whenever the context is invalidated.")

  _WRAP_METHOD(void render_check(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height), gtk_render_check)
  _WRAP_METHOD(void render_option(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height), gtk_render_option)
  _WRAP_METHOD(void render_arrow(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double angle, double x, double y, double size), gtk_render_arrow)
  _WRAP_METHOD(void render_background(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x,  double y, double width, double height), gtk_render_background)
  _WRAP_METHOD(void render_frame(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x,  double y, double width, double height), gtk_render_frame)
  _WRAP_METHOD(void render_expander(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height), gtk_render_expander)
  _WRAP_METHOD(void render_focus(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height), gtk_render_focus)
  _WRAP_METHOD(void render_layout(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, PangoLayout* layout), gtk_render_layout, deprecated "Use the render_layout() taking a const Glib::RefPtr<Pango::Layout>& layout.")
  _WRAP_METHOD(void render_layout(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, const Glib::RefPtr<Pango::Layout>& layout), gtk_render_layout)
  _WRAP_METHOD(void render_line(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x0, double y0,  double x1, double y1), gtk_render_line)
  _WRAP_METHOD(void render_slider(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height,  Orientation orientation), gtk_render_slider)
  _WRAP_METHOD(void render_frame_gap(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height,  PositionType  gap_side, double xy0_gap, double xy1_gap), gtk_render_frame_gap,
    deprecated "Use render_frame() instead. Themes can create gaps by omitting borders via CSS.")
  _WRAP_METHOD(void render_extension(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height,  PositionType gap_side), gtk_render_extension)
  _WRAP_METHOD(void render_handle(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, double width, double height), gtk_render_handle)
  _WRAP_METHOD(void render_activity(const ::Cairo::RefPtr< ::Cairo::Context>& cr,  double x,  double y, double width, double height), gtk_render_activity)
  _WRAP_METHOD(Glib::RefPtr<Gdk::Pixbuf> render_icon_pixbuf(const IconSource& source, IconSize size), gtk_render_icon_pixbuf, deprecated "Use IconTheme::load_icon() instead.")
  _WRAP_METHOD(void render_icon(const ::Cairo::RefPtr< ::Cairo::Context>& cr, const Glib::RefPtr<Gdk::Pixbuf>& pixbuf, double x, double y), gtk_render_icon)
  _WRAP_METHOD(void render_insertion_cursor(const ::Cairo::RefPtr< ::Cairo::Context>& cr, double x, double y, const Glib::RefPtr<Pango::Layout>& layout, int index, Pango::Direction direction), gtk_render_insertion_cursor)


  _WRAP_SIGNAL(void changed(), "changed")

  _WRAP_PROPERTY("screen", Glib::RefPtr<Gdk::Screen>)
  _WRAP_PROPERTY("direction", TextDirection,
    deprecated "Use set_state()/get_state() and Gtk::STATE_FLAG_DIR_LTR/Gtk::STATE_FLAG_DIR_RTL instead.")
  _WRAP_PROPERTY("paint-clock", Glib::RefPtr<Gdk::FrameClock>, newin "3,24")
  _WRAP_PROPERTY("parent", Glib::RefPtr<StyleContext>)
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS

template <class PropertyType>
void StyleContext::get_style_property(const Glib::ustring& property_name, PropertyType& value) const
{
  Glib::Value<PropertyType> property_value;
  property_value.init(Glib::Value<PropertyType>::value_type());

  this->get_style_property_value(property_name, property_value);

  value = property_value.get();
}

#endif
} // namespace Gtk
