From 13728943d65135f5a2f2a5348ce25585be447fd6 Mon Sep 17 00:00:00 2001 From: Gergely Polonkai Date: Fri, 26 Sep 2014 14:51:39 +0200 Subject: [PATCH] Add AgChartRenderer class It is a subclass of GdkCellRendererPixbuf specialised in displaying Astrognome chart previews. --- configure.ac | 1 + src/Makefile.am | 5 +- src/ag-chart-renderer.c | 305 ++++++++++++++++++++++++++++++++++++++++ src/ag-chart-renderer.h | 58 ++++++++ 4 files changed, 367 insertions(+), 2 deletions(-) create mode 100644 src/ag-chart-renderer.c create mode 100644 src/ag-chart-renderer.h diff --git a/configure.ac b/configure.ac index f50537a..02c8741 100644 --- a/configure.ac +++ b/configure.ac @@ -40,6 +40,7 @@ PKG_CHECK_MODULES([GDA], [libgda-5.0 libgda-sqlite-5.0]) PKG_CHECK_MODULES([PIXBUF], [gdk-pixbuf-2.0]) PKG_CHECK_MODULES([RSVG], [librsvg-2.0]) PKG_CHECK_MODULES([SWE_GLIB], [swe-glib >= 2.1.0]) +PKG_CHECK_MODULES([CAIRO], [cairo]); LIBGD_INIT([ main-view diff --git a/src/Makefile.am b/src/Makefile.am index bf17202..c39ca17 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,7 @@ astrognome_source_files = \ ag-preferences.c \ ag-db.c \ ag-display-theme.c \ + ag-chart-renderer.c \ astrognome.c \ $(NULL) @@ -32,9 +33,9 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"Astrognome\" -DLOCALEDIR=\"$(localedir)\" -DPKGDA bin_PROGRAMS = astrognome astrognome_SOURCES = $(astrognome_source_files) $(BUILT_SOURCES) -astrognome_LDADD = $(SWE_GLIB_LIBS) $(GTK_LIBS) $(LIBXML_LIBS) $(LIBXSLT_LIBS) $(WEBKIT_LIBS) $(GDA_LIBS) $(PIXBUF_LIBS) $(RSVG_LIBS) $(top_builddir)/libgd/libgd.la +astrognome_LDADD = $(SWE_GLIB_LIBS) $(GTK_LIBS) $(LIBXML_LIBS) $(LIBXSLT_LIBS) $(WEBKIT_LIBS) $(GDA_LIBS) $(PIXBUF_LIBS) $(RSVG_LIBS) $(CAIRO_LIBS) $(top_builddir)/libgd/libgd.la astrognome_LDFLAGS = -rdynamic -astrognome_CFLAGS = $(SWE_GLIB_CFLAGS) $(CFLAGS) $(GTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS) $(WEBKIT_CFLAGS) $(GDA_CFLAGS) $(PIXBUF_CFLAGS) $(RSVG_CFLAGS) -Wall +astrognome_CFLAGS = $(SWE_GLIB_CFLAGS) $(CFLAGS) $(GTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS) $(WEBKIT_CFLAGS) $(GDA_CFLAGS) $(PIXBUF_CFLAGS) $(RSVG_CFLAGS) $(CAIRO_CFLAGS) -Wall # The following two lines generate a .dir-locals.el file, so # company-mode won’t die due to unknown includes diff --git a/src/ag-chart-renderer.c b/src/ag-chart-renderer.c new file mode 100644 index 0000000..f194c2b --- /dev/null +++ b/src/ag-chart-renderer.c @@ -0,0 +1,305 @@ +#include + +#include "ag-chart-renderer.h" + +typedef struct { + gchar *css_class; + gboolean checked; + gboolean toggle_visible; +} AgChartRendererPrivate; + +enum { + PROP_0, + AG_CHART_RENDERER_PROP_CSS_CLASS, + AG_CHART_RENDERER_PROP_CHECKED, + AG_CHART_RENDERER_PROP_TOGGLE_VISIBLE, +}; + +static void ag_chart_renderer_dispose(GObject *gobject); +static void ag_chart_renderer_finalize(GObject *gobject); + +G_DEFINE_TYPE_WITH_PRIVATE(AgChartRenderer, ag_chart_renderer, GTK_TYPE_CELL_RENDERER_PIXBUF); + +static void +ag_chart_renderer_render(GtkCellRenderer *renderer, + cairo_t *cr, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(AG_CHART_RENDERER(renderer)); + int margin; + GtkStyleContext *context = gtk_widget_get_style_context(widget); + GdkPixbuf *pixbuf; + + gtk_style_context_save(context); + gtk_style_context_add_class(context, "ag-chart-renderer"); + + if (priv->css_class) { + gtk_style_context_add_class(context, priv->css_class); + } + + cairo_save(cr); + gdk_cairo_rectangle(cr, cell_area); + cairo_clip(cr); + + cairo_translate(cr, cell_area->x, cell_area->y); + + margin = MAX(AG_CHART_RENDERER_TILE_MARGIN, (int)((cell_area->width - AG_CHART_RENDERER_TILE_SIZE) / 2)); + + g_object_get(renderer, "pixbuf", &pixbuf, NULL); + + if (pixbuf != NULL) { + GdkRectangle area = {margin, margin, AG_CHART_RENDERER_TILE_SIZE, AG_CHART_RENDERER_TILE_SIZE}; + + g_debug("Rendering chart with preview image"); + + GTK_CELL_RENDERER_CLASS(ag_chart_renderer_parent_class)->render(renderer, cr, widget, &area, &area, flags); + } else { + g_debug("Rendering chart without preview image"); + + gtk_render_frame(context, cr, margin, margin, AG_CHART_RENDERER_TILE_SIZE, AG_CHART_RENDERER_TILE_SIZE); + gtk_render_background(context, cr, margin, margin, AG_CHART_RENDERER_TILE_SIZE, AG_CHART_RENDERER_TILE_SIZE); + } + + gtk_style_context_restore(context); + + if (priv->toggle_visible) { + gint xpad, + ypad, + x_offset, + check_x, + check_y; + + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(renderer), &xpad, &ypad); + + if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) { + x_offset = xpad; + } else { + x_offset = cell_area->width - AG_CHART_RENDERER_CHECK_ICON_SIZE - xpad; + } + + check_x = x_offset; + check_y = cell_area->height - AG_CHART_RENDERER_CHECK_ICON_SIZE - ypad; + + gtk_style_context_save(context); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_CHECK); + + if (priv->checked) { + gtk_style_context_set_state(context, GTK_STATE_FLAG_CHECKED); + } + + gtk_render_background(context, cr, check_x, check_y, AG_CHART_RENDERER_CHECK_ICON_SIZE, AG_CHART_RENDERER_CHECK_ICON_SIZE); + gtk_render_frame(context, cr, check_x, check_y, AG_CHART_RENDERER_CHECK_ICON_SIZE, AG_CHART_RENDERER_CHECK_ICON_SIZE); + gtk_render_check(context, cr, check_x, check_y, AG_CHART_RENDERER_CHECK_ICON_SIZE, AG_CHART_RENDERER_CHECK_ICON_SIZE); + + gtk_style_context_restore(context); + } + + cairo_restore(cr); +} + +void +ag_chart_renderer_set_css_class(AgChartRenderer *chart_renderer, const gchar *css_class) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(chart_renderer); + + g_free(priv->css_class); + priv->css_class = g_strdup(css_class); +} + +const gchar * +ag_chart_renderer_get_css_class(AgChartRenderer *chart_renderer) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(chart_renderer); + + return priv->css_class; +} + +void +ag_chart_renderer_set_checked(AgChartRenderer *chart_renderer, gboolean checked) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(chart_renderer); + + priv->checked = checked; +} + +gboolean +ag_chart_renderer_get_checked(AgChartRenderer *chart_renderer) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(chart_renderer); + + return priv->checked; +} + +void +ag_chart_renderer_set_toggle_visible(AgChartRenderer *chart_renderer, gboolean toggle_visible) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(chart_renderer); + + priv->toggle_visible = toggle_visible; +} + +gboolean +ag_chart_renderer_get_toggle_visible(AgChartRenderer *chart_renderer) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(chart_renderer); + + return priv->toggle_visible; +} + +static void +ag_chart_renderer_get_property(GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AgChartRenderer *chart_renderer = AG_CHART_RENDERER(gobject); + + switch (prop_id) { + case AG_CHART_RENDERER_PROP_CSS_CLASS: + g_value_set_string(value, ag_chart_renderer_get_css_class(chart_renderer)); + + break; + + case AG_CHART_RENDERER_PROP_CHECKED: + g_value_set_boolean(value, ag_chart_renderer_get_checked(chart_renderer)); + + break; + + case AG_CHART_RENDERER_PROP_TOGGLE_VISIBLE: + g_value_set_boolean(value, ag_chart_renderer_get_toggle_visible(chart_renderer)); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec); + + break; + } +} + +static void +ag_chart_renderer_set_property(GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AgChartRenderer *chart_renderer = AG_CHART_RENDERER(gobject); + + switch (prop_id) { + case AG_CHART_RENDERER_PROP_CSS_CLASS: + ag_chart_renderer_set_css_class(chart_renderer, g_value_get_string(value)); + + break; + + case AG_CHART_RENDERER_PROP_CHECKED: + ag_chart_renderer_set_checked(chart_renderer, g_value_get_boolean(value)); + + break; + + case AG_CHART_RENDERER_PROP_TOGGLE_VISIBLE: + ag_chart_renderer_set_toggle_visible(chart_renderer, g_value_get_boolean(value)); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec); + + break; + } +} + +static void +ag_chart_renderer_class_init(AgChartRendererClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *)klass; + GtkCellRendererClass *cell_renderer_class = (GtkCellRendererClass *)klass; + + gobject_class->dispose = ag_chart_renderer_dispose; + gobject_class->finalize = ag_chart_renderer_finalize; + gobject_class->set_property = ag_chart_renderer_set_property; + gobject_class->get_property = ag_chart_renderer_get_property; + cell_renderer_class->render = ag_chart_renderer_render; + + g_object_class_install_property( + G_OBJECT_CLASS(klass), + AG_CHART_RENDERER_PROP_CSS_CLASS, + g_param_spec_string( + "css-class", + "css-class", + "CSS Class", + NULL, + G_PARAM_STATIC_NAME + | G_PARAM_STATIC_NICK + | G_PARAM_STATIC_BLURB + | G_PARAM_READABLE + | G_PARAM_WRITABLE + ) + ); + + g_object_class_install_property( + G_OBJECT_CLASS(klass), + AG_CHART_RENDERER_PROP_CHECKED, + g_param_spec_boolean( + "checked", + "checked", + "Checked", + FALSE, + G_PARAM_STATIC_NAME + | G_PARAM_STATIC_NICK + | G_PARAM_STATIC_BLURB + | G_PARAM_READABLE + | G_PARAM_WRITABLE + ) + ); + + g_object_class_install_property( + G_OBJECT_CLASS(klass), + AG_CHART_RENDERER_PROP_TOGGLE_VISIBLE, + g_param_spec_boolean( + "toggle-visible", + "toggle-visible", + "Toggle visible", + FALSE, + G_PARAM_STATIC_NAME + | G_PARAM_STATIC_NICK + | G_PARAM_STATIC_BLURB + | G_PARAM_READABLE + | G_PARAM_WRITABLE + ) + ); +} + +static void +ag_chart_renderer_init(AgChartRenderer *chart_renderer) +{ + AgChartRendererPrivate *priv = ag_chart_renderer_get_instance_private(chart_renderer); + + priv->checked = FALSE; + priv->toggle_visible = FALSE; +} + +static void +ag_chart_renderer_dispose(GObject *gobject) +{ + G_OBJECT_CLASS(ag_chart_renderer_parent_class)->dispose(gobject); +} + +static void +ag_chart_renderer_finalize (GObject *gobject) +{ + g_signal_handlers_destroy(gobject); + G_OBJECT_CLASS(ag_chart_renderer_parent_class)->finalize(gobject); +} + +AgChartRenderer * +ag_chart_renderer_new(void) +{ + AgChartRenderer *chart_renderer = NULL; + + chart_renderer = g_object_new(AG_TYPE_CHART_RENDERER, NULL); + + return chart_renderer; +} diff --git a/src/ag-chart-renderer.h b/src/ag-chart-renderer.h new file mode 100644 index 0000000..ae9c108 --- /dev/null +++ b/src/ag-chart-renderer.h @@ -0,0 +1,58 @@ +#ifndef __AG_CHART_RENDERER_H__ +#define __AG_CHART_RENDERER_H__ + +#include + + +G_BEGIN_DECLS + +#define AG_TYPE_CHART_RENDERER \ + (ag_chart_renderer_get_type()) +#define AG_CHART_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + AG_TYPE_CHART_RENDERER, \ + AgChartRenderer)) +#define AG_CHART_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + AG_TYPE_CHART_RENDERER, \ + AgChartRendererClass)) +#define IS_AG_CHART_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + AG_TYPE_CHART_RENDERER)) +#define IS_AG_CHART_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + AG_TYPE_CHART_RENDERER)) +#define AG_CHART_RENDERER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + AG_TYPE_CHART_RENDERER, \ + AgChartRendererClass)) + +typedef struct _AgChartRenderer AgChartRenderer; +typedef struct _AgChartRendererClass AgChartRendererClass; + +struct _AgChartRendererClass +{ + GtkCellRendererPixbufClass parent_class; +}; + +struct _AgChartRenderer +{ + GtkCellRendererPixbuf parent; +}; + +#define AG_CHART_RENDERER_TILE_SIZE 256 +#define AG_CHART_RENDERER_CHECK_ICON_SIZE 40 +#define AG_CHART_RENDERER_TILE_MARGIN AG_CHART_RENDERER_CHECK_ICON_SIZE / 4 +#define AG_CHART_RENDERER_TILE_MARGIN_BOTTOM AG_CHART_RENDERER_CHECK_ICON_SIZE / 8 + +GType ag_chart_renderer_get_type (void) G_GNUC_CONST; + +AgChartRenderer *ag_chart_renderer_new(void); + +void ag_chart_renderer_set_toggle_visible(AgChartRenderer *chart_renderer, gboolean toggle_visible); + +gboolean ag_chart_renderer_get_toggle_visible(AgChartRenderer *chart_renderer); + +G_END_DECLS + +#endif /* __AG_CHART_RENDERER_H__ */