diff options
Diffstat (limited to 'baobab/src/baobab-treemap.c')
-rw-r--r-- | baobab/src/baobab-treemap.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/baobab/src/baobab-treemap.c b/baobab/src/baobab-treemap.c new file mode 100644 index 00000000..7262b819 --- /dev/null +++ b/baobab/src/baobab-treemap.c @@ -0,0 +1,342 @@ +/* + * baobab-treemap.c + * + * Copyright (C) 2008 Igalia + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: + * Fabio Marzocca <[email protected]> + * Paolo Borelli <[email protected]> + * Miguel Gomez <[email protected]> + * Eduardo Lima Mitev <[email protected]> + */ + +#include <math.h> +#include <string.h> +#include <gtk/gtk.h> + +#include "baobab-chart.h" +#include "baobab-treemap.h" + +#define BAOBAB_TREEMAP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + BAOBAB_TREEMAP_TYPE, \ + BaobabTreemapPrivate)) + +#define ITEM_TEXT_PADDING 3 +#define ITEM_BORDER_WIDTH 1 +#define ITEM_PADDING 6 + +#define ITEM_MIN_WIDTH 3 +#define ITEM_MIN_HEIGHT 3 + +#define ITEM_SHOW_LABEL TRUE + +G_DEFINE_TYPE (BaobabTreemap, baobab_treemap, BAOBAB_CHART_TYPE); + +struct _BaobabTreemapPrivate +{ + guint max_visible_depth; + gboolean more_visible_childs; +}; + +static void baobab_treemap_class_init (BaobabTreemapClass *class); +static void baobab_treemap_init (BaobabTreemap *object); +static void baobab_treemap_draw_rectangle (GtkWidget *chart, + cairo_t *cr, + gdouble x, gdouble y, gdouble width, gdouble height, + BaobabChartColor fill_color, + const char *text, + gboolean show_text); +static void baobab_treemap_draw_item (GtkWidget *chart, + cairo_t *cr, + BaobabChartItem *item, + gboolean highlighted); +static void baobab_treemap_calculate_item_geometry (GtkWidget *chart, + BaobabChartItem *item); +static gboolean baobab_treemap_is_point_over_item (GtkWidget *chart, + BaobabChartItem *item, + gdouble x, + gdouble y); +static void baobab_treemap_get_item_rectangle (GtkWidget *chart, + BaobabChartItem *item); +guint baobab_treemap_can_zoom_in (GtkWidget *chart); +guint baobab_treemap_can_zoom_out (GtkWidget *chart); + +static void +baobab_treemap_class_init (BaobabTreemapClass *class) +{ + GObjectClass *obj_class; + BaobabChartClass *chart_class; + + obj_class = G_OBJECT_CLASS (class); + chart_class = BAOBAB_CHART_CLASS (class); + + /* BaobabChart abstract methods */ + chart_class->draw_item = baobab_treemap_draw_item; + chart_class->calculate_item_geometry = baobab_treemap_calculate_item_geometry; + chart_class->is_point_over_item = baobab_treemap_is_point_over_item; + chart_class->get_item_rectangle = baobab_treemap_get_item_rectangle; + chart_class->can_zoom_in = baobab_treemap_can_zoom_in; + chart_class->can_zoom_out = baobab_treemap_can_zoom_out; + + g_type_class_add_private (obj_class, sizeof (BaobabTreemapPrivate)); +} + +static void +baobab_treemap_init (BaobabTreemap *chart) +{ + BaobabTreemapPrivate *priv; + + priv = BAOBAB_TREEMAP_GET_PRIVATE (chart); + + chart->priv = priv; +} + +static void +baobab_treemap_draw_rectangle (GtkWidget *chart, + cairo_t *cr, + gdouble x, gdouble y, gdouble width, gdouble height, + BaobabChartColor fill_color, + const char *text, + gboolean show_text) +{ + guint border = ITEM_BORDER_WIDTH; + PangoRectangle rect; + PangoLayout *layout; + + cairo_stroke (cr); + + cairo_set_line_width (cr, border); + cairo_rectangle (cr, x + border, y + border, + width - border*2, + height - border*2); + cairo_set_source_rgb (cr, fill_color.red, fill_color.green, fill_color.blue); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_stroke (cr); + + if ((show_text) && (ITEM_SHOW_LABEL)) + { + layout = gtk_widget_create_pango_layout (chart, NULL); + pango_layout_set_markup (layout, text, -1); + pango_layout_get_pixel_extents (layout, NULL, &rect); + + if ((rect.width + ITEM_TEXT_PADDING*2 <= width) && + (rect.height + ITEM_TEXT_PADDING*2 <= height)) + { + cairo_move_to (cr, x + width/2 - rect.width/2, + y + height/2 - rect.height/2); + pango_cairo_show_layout (cr, layout); + } + + g_object_unref (layout); + } +} + +static void +baobab_treemap_draw_item (GtkWidget *chart, + cairo_t *cr, + BaobabChartItem *item, + gboolean highlighted) +{ + cairo_rectangle_t * rect; + BaobabChartColor fill_color; + GtkAllocation allocation; + gdouble width, height; + + rect = (cairo_rectangle_t *) item->data; + + gtk_widget_get_allocation (chart, &allocation); + + if (item->depth % 2 != 0) + { + baobab_chart_get_item_color (&fill_color, rect->x/allocation.width*200, + item->depth, highlighted); + width = rect->width - ITEM_PADDING; + height = rect->height; + } + else + { + baobab_chart_get_item_color (&fill_color, rect->y/allocation.height*200, + item->depth, highlighted); + width = rect->width; + height = rect->height - ITEM_PADDING; + } + + baobab_treemap_draw_rectangle (chart, + cr, + rect->x, + rect->y, + width, + height, + fill_color, + item->name, + (! item->has_visible_children) ); +} + +static void +baobab_treemap_calculate_item_geometry (GtkWidget *chart, + BaobabChartItem *item) +{ + BaobabTreemapPrivate *priv; + cairo_rectangle_t p_area; + static cairo_rectangle_t *rect; + gdouble width, height; + BaobabChartItem *parent = NULL; + GtkAllocation allocation; + + priv = BAOBAB_TREEMAP (chart)->priv; + + if (item->depth == 0) + { + priv->max_visible_depth = 0; + priv->more_visible_childs = FALSE; + } + + item->visible = FALSE; + + if (item->parent == NULL) + { + gtk_widget_get_allocation (chart, &allocation); + p_area.x = 0 - ITEM_PADDING/2; + p_area.y = 0 - ITEM_PADDING/2; + p_area.width = allocation.width + ITEM_PADDING * 2; + p_area.height = allocation.height + ITEM_PADDING; + } + else + { + parent = (BaobabChartItem *) item->parent->data; + g_memmove (&p_area, parent->data, sizeof (cairo_rectangle_t)); + } + + if (item->data == NULL) + item->data = g_new (cairo_rectangle_t, 1); + + rect = (cairo_rectangle_t *) item->data; + + if (item->depth % 2 != 0) + { + width = p_area.width - ITEM_PADDING; + + rect->x = p_area.x + (item->rel_start * width / 100) + ITEM_PADDING; + rect->y = p_area.y + ITEM_PADDING; + rect->width = width * item->rel_size / 100; + rect->height = p_area.height - ITEM_PADDING * 3; + } + else + { + height = p_area.height - ITEM_PADDING; + + rect->x = p_area.x + ITEM_PADDING; + rect->y = p_area.y + (item->rel_start * height / 100) + ITEM_PADDING; + rect->width = p_area.width - ITEM_PADDING * 3; + rect->height = height * item->rel_size / 100; + } + + if ((rect->width - ITEM_PADDING < ITEM_MIN_WIDTH) || + (rect->height - ITEM_PADDING < ITEM_MIN_HEIGHT)) + return; + + rect->x = floor (rect->x) + 0.5; + rect->y = floor (rect->y) + 0.5; + rect->width = floor (rect->width); + rect->height = floor (rect->height); + + item->visible = TRUE; + + if (parent != NULL) + parent->has_visible_children = TRUE; + + baobab_treemap_get_item_rectangle (chart, item); + + if (item->depth == baobab_chart_get_max_depth (chart) + 1) + priv->more_visible_childs = TRUE; + else + priv->max_visible_depth = MAX (priv->max_visible_depth, item->depth); +} + +static gboolean +baobab_treemap_is_point_over_item (GtkWidget *chart, + BaobabChartItem *item, + gdouble x, + gdouble y) +{ + GdkRectangle *rect; + + rect = &item->rect; + return ((x >= rect->x) && (x <= rect->x + rect->width) && + (y >= rect->y) && (y <= rect->y + rect->height)); +} + +static void +baobab_treemap_get_item_rectangle (GtkWidget *chart, + BaobabChartItem *item) +{ + cairo_rectangle_t *_rect; + + _rect = (cairo_rectangle_t *) item->data; + + item->rect.x = _rect->x; + item->rect.y = _rect->y; + if (item->depth % 2 != 0) + { + item->rect.width = _rect->width - ITEM_PADDING; + item->rect.height = _rect->height; + } + else + { + item->rect.width = _rect->width; + item->rect.height = _rect->height - ITEM_PADDING; + } + +} + +guint +baobab_treemap_can_zoom_in (GtkWidget *chart) +{ + BaobabTreemapPrivate *priv; + + priv = BAOBAB_TREEMAP (chart)->priv; + + return MAX (0, (gint) (priv->max_visible_depth - 1)); +} + +guint +baobab_treemap_can_zoom_out (GtkWidget *chart) +{ + BaobabTreemapPrivate *priv; + + priv = BAOBAB_TREEMAP (chart)->priv; + + return priv->more_visible_childs ? 1 : 0; +} + +/* Public functions start here */ + +/** + * baobab_treemap_new: + * + * Constructor for the baobab_treemap class + * + * Returns: a new #BaobabTreemap object + * + **/ +GtkWidget* +baobab_treemap_new (void) +{ + return g_object_new (BAOBAB_TREEMAP_TYPE, NULL); +} |