[rhythmbox] Add segmented bar widget
- From: Bastien Nocera <hadess src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [rhythmbox] Add segmented bar widget
- Date: Thu, 14 Jan 2010 12:07:11 +0000 (UTC)
commit 88e7e9c3d925d2df27ccc1bb346815cea53c411c
Author: Christophe Fergeau <cfergeau mandriva com>
Date: Thu Jan 14 10:46:17 2010 +0000
Add segmented bar widget
Port the segmented bar from Banshee.
https://bugzilla.gnome.org/show_bug.cgi?id=558576
widgets/Makefile.am | 5 +
widgets/rb-segmented-bar.c | 825 +++++++++++++++++++++++++++++++++++++++
widgets/rb-segmented-bar.h | 80 ++++
widgets/test-rb-segmented-bar.c | 76 ++++
4 files changed, 986 insertions(+), 0 deletions(-)
---
diff --git a/widgets/Makefile.am b/widgets/Makefile.am
index 82192b9..543e232 100644
--- a/widgets/Makefile.am
+++ b/widgets/Makefile.am
@@ -29,6 +29,8 @@ librbwidgets_la_SOURCES = \
rb-rating.c \
rb-rating.h \
rb-rating-helper.c \
+ rb-segmented-bar.c \
+ rb-segmented-bar.h \
rb-library-browser.h \
rb-library-browser.c \
rb-rating-helper.h \
@@ -60,3 +62,6 @@ INCLUDES = \
librbwidgets_la_LDFLAGS = -export-dynamic
librbwidgets_la_LIBADD = $(NOTIFY_LIBS) $(LIBSEXY_LIBS)
+noinst_PROGRAMS = test-rb-segmented-bar
+test_rb_segmented_bar_SOURCES = test-rb-segmented-bar.c
+test_rb_segmented_bar_LDADD = librbwidgets.la
diff --git a/widgets/rb-segmented-bar.c b/widgets/rb-segmented-bar.c
new file mode 100644
index 0000000..0385f75
--- /dev/null
+++ b/widgets/rb-segmented-bar.c
@@ -0,0 +1,825 @@
+/*
+ * Initial Author:
+ * Aaron Bockover <abockover novell com>
+ *
+ * Ported to C from Banshee's SegmentedBar.cs widget
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2008 Christophe Fergeau <teuf gnome org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <math.h>
+#include <cairo/cairo.h>
+#include <gtk/gtk.h>
+#include "rb-segmented-bar.h"
+
+#define MINIMUM_HEIGHT 26
+
+static void rb_segmented_bar_finalize (GObject *object);
+static void rb_segmented_bar_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void rb_segmented_bar_size_allocate(GtkWidget *widget,
+ GtkAllocation *allocation);
+static gboolean rb_segmented_bar_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static void rb_segmented_bar_get_property (GObject *object, guint param_id,
+ GValue *value, GParamSpec *pspec);
+static void rb_segmented_bar_set_property (GObject *object, guint param_id,
+ const GValue *value, GParamSpec *pspec);
+
+static gchar *rb_segmented_bar_default_value_formatter (gdouble percent,
+ gpointer data);
+
+static void compute_layout_size (RBSegmentedBar *bar);
+
+enum
+{
+ PROP_0,
+ PROP_SHOW_REFLECTION,
+ PROP_SHOW_LABELS,
+ PROP_BAR_HEIGHT
+};
+
+struct _RBSegmentedBarPrivate {
+ GList *segments;
+ guint layout_width;
+ guint layout_height;
+
+ guint bar_height;
+ guint bar_label_spacing;
+ guint segment_label_spacing;
+ guint segment_box_size;
+ guint segment_box_spacing;
+ guint h_padding;
+
+ gboolean show_labels;
+ gboolean reflect;
+
+ RBSegmentedBarValueFormatter value_formatter;
+ gpointer value_formatter_data;
+};
+
+G_DEFINE_TYPE (RBSegmentedBar, rb_segmented_bar, GTK_TYPE_WIDGET)
+#define RB_SEGMENTED_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SEGMENTED_BAR, RBSegmentedBarPrivate))
+
+struct _Color {
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+ gdouble alpha;
+};
+typedef struct _Color Color;
+
+struct _Segment {
+ gchar *label;
+ gdouble percent;
+ Color color;
+
+ gint layout_width;
+ gint layout_height;
+};
+typedef struct _Segment Segment;
+
+static Segment *rb_segment_new (const gchar *label, gdouble percent, Color *color)
+{
+ Segment *segment;
+
+ segment = g_new0 (Segment, 1);
+ segment->label = g_strdup (label);
+ segment->percent = percent;
+ segment->color.red = color->red;
+ segment->color.green = color->green;
+ segment->color.blue = color->blue;
+ segment->color.alpha = color->alpha;
+
+ return segment;
+}
+
+static void rb_segment_free (Segment *segment)
+{
+ g_return_if_fail (segment != NULL);
+ g_free (segment->label);
+ g_free (segment);
+}
+
+static void
+rb_segmented_bar_init (RBSegmentedBar *bar)
+{
+ RBSegmentedBarPrivate *priv;
+
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (bar));
+ priv->bar_label_spacing = 8;
+ priv->segment_label_spacing = 16;
+ priv->segment_box_size = 12;
+ priv->segment_box_spacing = 6;
+ priv->value_formatter = rb_segmented_bar_default_value_formatter;
+ GTK_WIDGET_SET_FLAGS (GTK_WIDGET (bar), GTK_NO_WINDOW);
+}
+
+static void
+rb_segmented_bar_class_init (RBSegmentedBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = rb_segmented_bar_finalize;
+ object_class->get_property = rb_segmented_bar_get_property;
+ object_class->set_property = rb_segmented_bar_set_property;
+
+ widget_class->expose_event = rb_segmented_bar_expose;
+ widget_class->size_request = rb_segmented_bar_size_request;
+ widget_class->size_allocate = rb_segmented_bar_size_allocate;
+
+ /**
+ * RBSegmentedBar::show-reflection
+ *
+ * Set to TRUE if you want a reflection to be shown below the segmented
+ * bar.
+ */
+ g_object_class_install_property (object_class,
+ PROP_SHOW_REFLECTION,
+ g_param_spec_boolean ("show-reflection",
+ "show-reflection",
+ "Whether there will be a reflection below the segmented bar",
+ TRUE,
+ G_PARAM_READWRITE));
+
+ /**
+ * RBSegmentedBar::show-labels
+ *
+ * Set to TRUE if you want labels describing the various segments
+ * to be shown.
+ */
+ g_object_class_install_property (object_class,
+ PROP_SHOW_LABELS,
+ g_param_spec_boolean ("show-labels",
+ "show-labels",
+ "Whether the labels describing the various segments should be shown",
+ TRUE,
+ G_PARAM_READWRITE));
+ /**
+ * RBSegmentedBar::bar-height
+ *
+ * Height of the segmented bar
+ */
+ g_object_class_install_property (object_class,
+ PROP_BAR_HEIGHT,
+ g_param_spec_uint ("bar-height",
+ "bar-height",
+ "height of the segmented bar",
+ MINIMUM_HEIGHT, G_MAXUINT, MINIMUM_HEIGHT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (RBSegmentedBarPrivate));
+}
+
+static void
+rb_segmented_bar_finalize (GObject *object)
+{
+ RBSegmentedBarPrivate *priv;
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (object));
+ g_list_foreach (priv->segments, (GFunc)rb_segment_free, NULL);
+ g_list_free (priv->segments);
+ G_OBJECT_CLASS (rb_segmented_bar_parent_class)->finalize (object);
+}
+
+static void
+rb_segmented_bar_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RBSegmentedBarPrivate *priv;
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (object));
+
+ switch (param_id) {
+ case PROP_SHOW_REFLECTION:
+ g_value_set_boolean (value, priv->reflect);
+ break;
+ case PROP_SHOW_LABELS:
+ g_value_set_boolean (value, priv->show_labels);
+ break;
+ case PROP_BAR_HEIGHT:
+ g_value_set_uint (value, priv->bar_height);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+rb_segmented_bar_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RBSegmentedBarPrivate *priv;
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (object));
+
+ switch (param_id) {
+ case PROP_SHOW_REFLECTION:
+ priv->reflect = g_value_get_boolean (value);
+ break;
+ case PROP_SHOW_LABELS:
+ priv->show_labels = g_value_get_boolean (value);
+ break;
+ case PROP_BAR_HEIGHT:
+ priv->bar_height = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static gchar *
+rb_segmented_bar_default_value_formatter (gdouble percent,
+ G_GNUC_UNUSED gpointer data)
+{
+ return g_strdup_printf ("%.2f%%", percent*100.0);
+}
+
+static void
+rb_segmented_bar_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ RBSegmentedBarPrivate *priv;
+ int real_height;
+
+ g_return_if_fail (requisition != NULL);
+
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (widget));
+
+ if (priv->reflect) {
+ real_height = MINIMUM_HEIGHT*1.75;
+ } else {
+ real_height = MINIMUM_HEIGHT;
+ }
+ if (priv->show_labels) {
+ compute_layout_size (RB_SEGMENTED_BAR (widget));
+ requisition->height = MAX (MINIMUM_HEIGHT + priv->bar_label_spacing + priv->layout_height,
+ real_height);
+ } else {
+ requisition->height = real_height;
+ }
+ requisition->width = MAX (priv->layout_width, 200);
+}
+
+static PangoLayout *create_adapt_layout (GtkWidget *widget, PangoLayout *layout,
+ gboolean small, gboolean bold)
+{
+ const PangoFontDescription *desc;
+ PangoFontDescription *new_desc;
+
+ int normal_font_size;
+ if (layout == NULL) {
+ layout = gtk_widget_create_pango_layout (GTK_WIDGET (widget),
+ NULL);
+ }
+ desc = pango_context_get_font_description (gtk_widget_get_pango_context (widget));
+ g_assert (desc != NULL);
+ normal_font_size = pango_font_description_get_size (desc);
+
+ desc = pango_context_get_font_description (pango_layout_get_context (layout));
+ g_assert (desc != NULL);
+ new_desc = pango_font_description_copy (desc);
+
+ if (small) {
+ pango_font_description_set_size (new_desc,
+ normal_font_size * PANGO_SCALE_SMALL);
+ } else {
+ pango_font_description_set_size (new_desc, normal_font_size);
+ }
+
+ if (bold) {
+ pango_font_description_set_weight (new_desc,
+ PANGO_WEIGHT_BOLD);
+ } else {
+ pango_font_description_set_weight (new_desc,
+ PANGO_WEIGHT_NORMAL);
+ }
+ pango_layout_set_font_description (layout, new_desc);
+ pango_font_description_free (new_desc);
+ return layout;
+}
+
+static void
+compute_layout_size (RBSegmentedBar *bar)
+{
+ RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
+ PangoLayout *layout = NULL;
+ GList *it;
+
+ if (priv->segments == NULL) {
+ return;
+ }
+
+ priv->layout_width = 0;
+ priv->layout_height = 0;
+
+ for (it = priv->segments; it != NULL; it = it->next) {
+ Segment *segment = (Segment *)it->data;
+ gint label_width;
+ gint label_height;
+ gint value_width;
+ gint value_height;
+ gint width;
+ gint height;
+ gchar *value_str;
+
+ layout = create_adapt_layout (GTK_WIDGET (bar), layout,
+ FALSE, TRUE);
+ pango_layout_set_text (layout, segment->label, -1);
+ pango_layout_get_pixel_size (layout,
+ &label_width,
+ &label_height);
+
+ layout = create_adapt_layout (GTK_WIDGET (bar), layout,
+ TRUE, FALSE);
+ g_assert (priv->value_formatter != NULL);
+ value_str = priv->value_formatter (segment->percent,
+ priv->value_formatter_data);
+ pango_layout_set_text (layout, value_str, -1);
+ g_free (value_str);
+ pango_layout_get_pixel_size (layout,
+ &value_width,
+ &value_height);
+
+ width = MAX (label_width, value_width);
+ height = label_height + value_height;
+
+ segment->layout_width = width;
+ segment->layout_height = MAX (height, priv->segment_box_size*2);
+
+ priv->layout_width += segment->layout_width + priv->segment_box_size + priv->segment_box_spacing;
+ if (it->next != NULL) {
+ priv->layout_width += priv->segment_label_spacing;
+ }
+ priv->layout_height = MAX (priv->layout_height, segment->layout_height);
+ }
+
+ g_object_unref (G_OBJECT (layout));
+}
+
+static void
+rb_segmented_bar_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ gint real_height;
+ RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (widget);
+
+ g_return_if_fail(RB_IS_SEGMENTED_BAR(widget));
+ g_return_if_fail(allocation != NULL);
+
+ if (priv->reflect) {
+ real_height = priv->bar_height*1.75;
+ } else {
+ real_height = priv->bar_height;
+ }
+ widget->allocation = *allocation;
+ if (priv->show_labels) {
+ compute_layout_size (RB_SEGMENTED_BAR (widget));
+ widget->allocation.height = MAX (priv->bar_height + priv->bar_label_spacing + priv->layout_height,
+ real_height);
+ } else {
+ widget->allocation.height = real_height;
+ }
+ widget->allocation.width = priv->layout_width + 2*(priv->h_padding);
+ GTK_WIDGET_CLASS(rb_segmented_bar_parent_class)->size_allocate(widget, allocation);
+}
+
+
+guint rb_segmented_bar_add_segment (RBSegmentedBar *bar,
+ const gchar *title, gdouble percent,
+ gdouble red, gdouble green,
+ gdouble blue, gdouble alpha)
+{
+ Color color = { red, green, blue, alpha };
+ RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
+ guint index;
+ Segment *segment = rb_segment_new (title, percent, &color);
+ priv->segments = g_list_append (priv->segments, segment);
+ index = g_list_index (priv->segments, segment);
+ gtk_widget_queue_draw (GTK_WIDGET (bar));
+ gtk_widget_queue_resize (GTK_WIDGET (bar));
+
+ return index;
+}
+
+guint rb_segmented_bar_add_segment_default_color (RBSegmentedBar *bar,
+ const gchar *title,
+ gdouble percent)
+{
+ return rb_segmented_bar_add_segment (bar, title, percent, 0.9, 0.9, 0.9, 1.0);
+}
+
+void rb_segmented_bar_update_segment (RBSegmentedBar *bar,
+ guint segment_index,
+ gdouble percent)
+{
+ RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
+ Segment *segment = g_list_nth_data (priv->segments, segment_index);
+ if (segment != NULL) {
+ segment->percent = percent;
+ gtk_widget_queue_draw (GTK_WIDGET (bar));
+ }
+}
+
+static void draw_rounded_rectangle (cairo_t *context,
+ guint x, guint y,
+ guint width, guint height,
+ guint radius)
+{
+ if (radius < 0.0001) {
+ cairo_rectangle (context, x, y, width, height);
+ return;
+ }
+ cairo_move_to (context, x+radius, y);
+ cairo_arc (context, x+width-radius, y+radius, radius, G_PI*1.5, G_PI*2);
+ cairo_arc (context, x+width-radius, y+height-radius, radius, 0, G_PI*0.5);
+ cairo_arc (context, x+radius, y+height-radius, radius, G_PI*0.5, G_PI);
+ cairo_arc (context, x+radius, y+radius, radius, G_PI, G_PI*1.5);
+}
+
+static void rb_segmented_bar_render_segments (RBSegmentedBar *bar,
+ cairo_t *context,
+ guint width, guint height,
+ guint radius)
+{
+ cairo_pattern_t *grad;
+ gdouble last;
+ GList *it;
+ RBSegmentedBarPrivate *priv;
+
+ last = 0.0;
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
+ grad = cairo_pattern_create_linear (0, 0, width, 0);
+ for (it = priv->segments; it != NULL; it = it->next) {
+ Segment *segment = (Segment *)it->data;
+ if (segment->percent > 0) {
+ cairo_pattern_add_color_stop_rgba (grad, last,
+ segment->color.red,
+ segment->color.green,
+ segment->color.blue,
+ segment->color.alpha);
+ last += segment->percent;
+ cairo_pattern_add_color_stop_rgba (grad, last,
+ segment->color.red,
+ segment->color.green,
+ segment->color.blue,
+ segment->color.alpha);
+ }
+ }
+
+ draw_rounded_rectangle (context, 0, 0, width, height, radius);
+ cairo_set_source (context, grad);
+ cairo_fill_preserve (context);
+ cairo_pattern_destroy (grad);
+
+ grad = cairo_pattern_create_linear (0, 0, 0, height);
+ cairo_pattern_add_color_stop_rgba (grad, 0.0, 1, 1, 1, 0.125);
+ cairo_pattern_add_color_stop_rgba (grad, 0.35, 1, 1, 1, 0.255);
+ cairo_pattern_add_color_stop_rgba (grad, 1, 0, 0, 0, 0.4);
+ cairo_set_source (context, grad);
+ cairo_fill (context);
+ cairo_pattern_destroy (grad);
+
+}
+
+static void hsb_from_color (Color *color, gdouble *hue,
+ gdouble *saturation, gdouble *brightness)
+{
+ gtk_rgb_to_hsv (color->red, color->green, color->blue,
+ hue, saturation, brightness);
+}
+
+static Color *color_from_hsb (gdouble hue, gdouble saturation, gdouble brightness)
+{
+ Color *color;
+
+ color = g_new0 (Color, 1);
+ gtk_hsv_to_rgb (hue, saturation, brightness,
+ &color->red, &color->green, &color->blue);
+
+ return color;
+}
+
+static Color *color_shade (Color *base, gdouble ratio)
+{
+ gdouble h;
+ gdouble s;
+ gdouble b;
+ Color *color;
+
+ hsb_from_color (base, &h, &s, &b);
+
+ b = MAX (MIN (b*ratio, 1), 0);
+ s = MAX (MIN (s*ratio, 1), 0);
+
+ color = color_from_hsb (h, s, b);
+ color->alpha = base->alpha;
+
+ return color;
+}
+
+static cairo_pattern_t *make_segment_gradient (guint height,
+ gdouble red, gdouble green,
+ gdouble blue, gdouble alpha)
+{
+ cairo_pattern_t *grad;
+ Color *shade;
+ Color color = { red, green, blue, alpha };
+
+ grad = cairo_pattern_create_linear (0, 0, 0, height);
+
+ shade = color_shade (&color, 1.1);
+ cairo_pattern_add_color_stop_rgba (grad, 0,
+ shade->red, shade->green,
+ shade->blue, shade->alpha);
+ g_free (shade);
+
+ shade = color_shade (&color, 1.2);
+ cairo_pattern_add_color_stop_rgba (grad, 0.35,
+ shade->red, shade->green,
+ shade->blue, shade->alpha);
+ g_free (shade);
+
+ shade = color_shade (&color, 0.8);
+ cairo_pattern_add_color_stop_rgba (grad, 1,
+ shade->red, shade->green,
+ shade->blue, shade->alpha);
+ g_free (shade);
+
+ return grad;
+}
+
+static void rb_segmented_bar_render_strokes (RBSegmentedBar *bar,
+ cairo_t *context,
+ guint width, guint height,
+ guint radius)
+{
+ cairo_pattern_t *stroke = make_segment_gradient (height,
+ 0, 0, 0, 0.25);
+ cairo_pattern_t *seg_sep_light = make_segment_gradient (height,
+ 1, 1, 1, 0.125);
+ cairo_pattern_t *seg_sep_dark = make_segment_gradient (height,
+ 0, 0, 0, 0.125);
+ gdouble seg_w = 20;
+ gdouble x;
+ if (seg_w > radius) {
+ x = seg_w;
+ } else {
+ seg_w = radius;
+ }
+ cairo_set_line_width (context, 1);
+
+ while (x <= width-radius) {
+ cairo_move_to (context, x - 0.5, 1);
+ cairo_line_to (context, x - 0.5, height - 1);
+ cairo_set_source (context, seg_sep_light);
+ cairo_stroke (context);
+
+ cairo_move_to (context, x + 0.5, 1);
+ cairo_line_to (context, x + 0.5, height - 1);
+ cairo_set_source (context, seg_sep_dark);
+ cairo_stroke (context);
+
+ x += seg_w;
+ }
+
+ draw_rounded_rectangle (context, 0.5, 0.5,
+ width - 1, height - 1, radius);
+ cairo_set_source (context, stroke);
+ cairo_stroke (context);
+
+ cairo_pattern_destroy (stroke);
+ cairo_pattern_destroy (seg_sep_light);
+ cairo_pattern_destroy (seg_sep_dark);
+}
+
+static cairo_pattern_t *rb_segmented_bar_render (RBSegmentedBar *bar,
+ guint width, guint height)
+{
+ cairo_surface_t *surface;
+ cairo_t *context;
+ cairo_pattern_t *pattern;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ width, height);
+ context = cairo_create (surface);
+ rb_segmented_bar_render_segments (bar, context,
+ width, height, height/2);
+ rb_segmented_bar_render_strokes (bar, context, width, height, height/2);
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_surface_destroy (surface);
+ cairo_destroy (context);
+
+ return pattern;
+}
+
+static void rb_segmented_bar_render_labels (RBSegmentedBar *bar,
+ cairo_t *context)
+{
+ RBSegmentedBarPrivate *priv;
+ PangoLayout *layout;
+ Color text_color;
+ GdkColor *gdk_color;
+ int x = 0;
+ GList *it;
+
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (bar));
+
+ if (priv->segments == NULL) {
+ return;
+ }
+ gdk_color = >K_WIDGET (bar)->style->fg[GTK_WIDGET_STATE (GTK_WIDGET (bar))];
+ text_color.red = gdk_color->red / 65535.0;
+ text_color.green = gdk_color->green / 65535.0;
+ text_color.blue = gdk_color->blue / 65535.0;
+ text_color.alpha = 1.0;
+ layout = NULL;
+ for (it = priv->segments; it != NULL; it = it->next) {
+ cairo_pattern_t *grad;
+ int layout_width;
+ int layout_height;
+ gchar *value_str;
+ Segment *segment;
+
+ segment = (Segment *)it->data;
+ cairo_set_line_width (context, 1);
+ cairo_rectangle (context, x + 0.5, 2 + 0.5,
+ priv->segment_box_size - 1,
+ priv->segment_box_size - 1);
+ grad = make_segment_gradient (priv->segment_box_size,
+ segment->color.red,
+ segment->color.green,
+ segment->color.blue,
+ segment->color.alpha);
+ cairo_set_source (context, grad);
+ cairo_fill_preserve (context);
+ cairo_set_source_rgba (context, 0, 0, 0, 0.6);
+ cairo_stroke (context);
+ cairo_pattern_destroy (grad);
+
+ x += priv->segment_box_size + priv->segment_box_spacing;
+
+ layout = create_adapt_layout (GTK_WIDGET (bar), layout,
+ FALSE, TRUE);
+ pango_layout_set_text (layout, segment->label, -1);
+ pango_layout_get_pixel_size (layout,
+ &layout_width, &layout_height);
+
+ cairo_move_to (context, x, 0);
+ cairo_set_source_rgba (context,
+ text_color.red, text_color.green,
+ text_color.blue, 0.9);
+ pango_cairo_show_layout (context, layout);
+ cairo_fill (context);
+
+ layout = create_adapt_layout (GTK_WIDGET (bar), layout,
+ TRUE, FALSE);
+ g_assert (priv->value_formatter != NULL);
+ value_str = priv->value_formatter (segment->percent,
+ priv->value_formatter_data);
+ pango_layout_set_text (layout, value_str, -1);
+ g_free (value_str);
+
+ cairo_move_to (context, x, layout_height);
+ cairo_set_source_rgba (context,
+ text_color.red, text_color.green,
+ text_color.blue, 0.75);
+ pango_cairo_show_layout (context, layout);
+ cairo_fill (context);
+
+ x += segment->layout_width + priv->segment_label_spacing;
+ }
+ g_object_unref (G_OBJECT (layout));
+}
+
+static gboolean
+rb_segmented_bar_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ RBSegmentedBar *bar;
+ RBSegmentedBarPrivate *priv;
+ cairo_t *context;
+ cairo_pattern_t *bar_pattern;
+
+ g_return_val_if_fail (RB_IS_SEGMENTED_BAR (widget), FALSE);
+ if (GTK_WIDGET_DRAWABLE (widget) == FALSE) {
+ return FALSE;
+ }
+
+ bar = RB_SEGMENTED_BAR (widget);
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
+
+ context = gdk_cairo_create (GDK_DRAWABLE (widget->window));
+
+ if (priv->reflect) {
+ cairo_push_group (context);
+ }
+
+ cairo_set_operator (context, CAIRO_OPERATOR_OVER);
+ cairo_translate (context, widget->allocation.x + priv->h_padding,
+ widget->allocation.y);
+ cairo_rectangle (context, 0, 0,
+ widget->allocation.width - priv->h_padding,
+ MAX (2*priv->bar_height, priv->bar_height + priv->bar_label_spacing + priv->layout_height));
+ cairo_clip (context);
+
+ bar_pattern = rb_segmented_bar_render (bar,
+ widget->allocation.width - 2*priv->h_padding,
+ priv->bar_height);
+
+ cairo_save (context);
+ cairo_set_source (context, bar_pattern);
+ cairo_paint (context);
+ cairo_restore (context);
+
+ if (priv->reflect) {
+ cairo_matrix_t matrix;
+ cairo_pattern_t *mask;
+
+ cairo_save (context);
+
+ cairo_rectangle (context, 0, priv->bar_height,
+ widget->allocation.width - priv->h_padding,
+ priv->bar_height);
+ cairo_clip (context);
+ cairo_matrix_init_scale (&matrix, 1, -1);
+ cairo_matrix_translate (&matrix, 0, -(2*priv->bar_height) + 1);
+ cairo_transform (context, &matrix);
+
+ cairo_set_source (context, bar_pattern);
+
+ mask = cairo_pattern_create_linear (0, 0, 0, priv->bar_height);
+ cairo_pattern_add_color_stop_rgba (mask, 0.25, 0, 0, 0, 0);
+ cairo_pattern_add_color_stop_rgba (mask, 0.5, 0, 0, 0, 0.125);
+ cairo_pattern_add_color_stop_rgba (mask, 0.75, 0, 0, 0, 0.4);
+ cairo_pattern_add_color_stop_rgba (mask, 1.0, 0, 0, 0, 0.7);
+
+ cairo_mask (context, mask);
+ cairo_pattern_destroy (mask);
+
+ cairo_restore (context);
+
+ cairo_pop_group_to_source (context);
+ cairo_paint (context);
+ }
+
+ if (priv->show_labels) {
+ if (priv->reflect) {
+ cairo_translate (context,
+ widget->allocation.x + (widget->allocation.width - priv->layout_width)/2,
+ widget->allocation.y + priv->bar_height + priv->bar_label_spacing);
+ } else {
+ cairo_translate (context,
+ -priv->h_padding + (widget->allocation.width - priv->layout_width)/2,
+ priv->bar_height + priv->bar_label_spacing);
+ }
+ rb_segmented_bar_render_labels (bar, context);
+ }
+ cairo_pattern_destroy (bar_pattern);
+ cairo_destroy (context);
+
+ return TRUE;
+}
+
+GtkWidget *rb_segmented_bar_new (void)
+{
+ return g_object_new (RB_TYPE_SEGMENTED_BAR, NULL);
+}
+
+void rb_segmented_bar_set_value_formatter (RBSegmentedBar *bar,
+ RBSegmentedBarValueFormatter formatter,
+ gpointer data)
+{
+ RBSegmentedBarPrivate *priv;
+
+ priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (bar));
+
+ priv->value_formatter = formatter;
+ priv->value_formatter_data = data;
+}
diff --git a/widgets/rb-segmented-bar.h b/widgets/rb-segmented-bar.h
new file mode 100644
index 0000000..eea47b6
--- /dev/null
+++ b/widgets/rb-segmented-bar.h
@@ -0,0 +1,80 @@
+/*
+ * Initial Author:
+ * Aaron Bockover <abockover novell com>
+ *
+ * Ported to C from Banshee's SegmentedBar.cs widget
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2008 Christophe Fergeau <teuf gnome org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef RB_SEGMENTED_BAR_H
+#define RB_SEGMENTED_BAR_H
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_SEGMENTED_BAR (rb_segmented_bar_get_type ())
+#define RB_SEGMENTED_BAR(obj) (GTK_CHECK_CAST ((obj), RB_TYPE_SEGMENTED_BAR, RBSegmentedBar))
+#define RB_SEGMENTED_BAR_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), RB_TYPE_SEGMENTED_BAR, RBSegmenterBarClass))
+#define RB_IS_SEGMENTED_BAR(obj) (GTK_CHECK_TYPE ((obj), RB_TYPE_SEGMENTED_BAR))
+#define RB_IS_SEGMENTER_BAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), RB_TYPE_SEGMENTED_BAR))
+
+typedef struct _RBSegmentedBar RBSegmentedBar;
+typedef struct _RBSegmentedBarClass RBSegmentedBarClass;
+typedef struct _RBSegmentedBarPrivate RBSegmentedBarPrivate;
+
+struct _RBSegmentedBar
+{
+ GtkWidget parent;
+
+ RBSegmentedBarPrivate *priv;
+};
+
+struct _RBSegmentedBarClass
+{
+ GtkWidgetClass parent;
+
+};
+
+typedef gchar *(*RBSegmentedBarValueFormatter) (gdouble percent, gpointer data);
+
+GtkType rb_segmented_bar_get_type (void);
+
+GtkWidget *rb_segmented_bar_new (void);
+guint rb_segmented_bar_add_segment (RBSegmentedBar *bar,
+ const gchar *title, gdouble percent,
+ gdouble red, gdouble green,
+ gdouble blue, gdouble alpha);
+guint rb_segmented_bar_add_segment_default_color (RBSegmentedBar *bar,
+ const gchar *title,
+ gdouble percent);
+void rb_segmented_bar_update_segment (RBSegmentedBar *bar,
+ guint segment_index,
+ gdouble percent);
+void rb_segmented_bar_set_value_formatter (RBSegmentedBar *bar,
+ RBSegmentedBarValueFormatter formatter,
+ gpointer data);
+G_END_DECLS
+
+#endif /* RB_SEGMENTED_BAR:_H */
diff --git a/widgets/test-rb-segmented-bar.c b/widgets/test-rb-segmented-bar.c
new file mode 100644
index 0000000..109432b
--- /dev/null
+++ b/widgets/test-rb-segmented-bar.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 Christophe Fergeau <teuf gnome org>
+ *
+ * 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.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your 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.
+ *
+ */
+
+/* compile with:
+ gcc -o test-rb-segmented-bar -I. $(pkg-config --cflags --libs gtk+-2.0)
+ ./rb-segmented-bar.c ./test-rb-segmented-bar.c
+ */
+#include <gtk/gtk.h>
+#include <rb-segmented-bar.h>
+
+static void window_destroyed_cb (void)
+{
+ gtk_main_quit ();
+}
+
+static gchar *value_formatter (gdouble percent, gpointer data)
+{
+ gsize total_size = GPOINTER_TO_SIZE (data);
+
+ return g_format_size_for_display (percent * total_size*1024*1024*1024);
+}
+
+int main (int argc, char **argv)
+{
+ GtkWidget *window;
+ RBSegmentedBar *bar;
+ const gsize total_size = 100; /* in GB */
+
+ gtk_init (&argc, &argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ bar = RB_SEGMENTED_BAR (rb_segmented_bar_new ());
+ g_object_set (G_OBJECT (bar),
+ "show-reflection", TRUE,
+ "show-labels", TRUE,
+ NULL);
+
+ rb_segmented_bar_add_segment (bar, "audio", 0.61, 0.2 , 0.4 , 0.65, 1);
+ rb_segmented_bar_add_segment (bar, "video", 0.11, 0.96, 0.47, 0 , 1);
+ rb_segmented_bar_add_segment (bar, "other", 0.05, 0.45, 0.82, 0.08, 1);
+ rb_segmented_bar_add_segment_default_color (bar, "empty", 0.23);
+ rb_segmented_bar_set_value_formatter (bar, value_formatter,
+ GSIZE_TO_POINTER (total_size));
+
+ g_signal_connect(G_OBJECT (window), "destroy",
+ G_CALLBACK (window_destroyed_cb), NULL);
+ gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (bar));
+ gtk_widget_show_all (GTK_WIDGET (window));
+ gtk_main ();
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]