gnome-media r4092 - in trunk/gnome-volume-control: . src
- From: mccann svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-media r4092 - in trunk/gnome-volume-control: . src
- Date: Sat, 13 Dec 2008 08:41:53 +0000 (UTC)
Author: mccann
Date: Sat Dec 13 08:41:52 2008
New Revision: 4092
URL: http://svn.gnome.org/viewvc/gnome-media?rev=4092&view=rev
Log:
2008-12-13 William Jon McCann <jmccann redhat com>
* src/Makefile.am:
* src/gvc-channel-bar.c (_scale_box_new):
* src/gvc-mixer-control.c (gvc_mixer_control_get_pa_context),
(update_default_source_from_name):
* src/gvc-mixer-control.h:
* src/gvc-mixer-dialog.c (update_input_peak), (update_input_meter),
(on_monitor_suspended_callback), (on_monitor_read_callback),
(create_monitor_stream_for_source),
(on_mixer_control_default_source_changed), (add_stream),
(gvc_mixer_dialog_constructor):
* src/rb-segmented-bar.c (rb_segment_new), (rb_segment_free),
(rb_segmented_bar_init), (rb_segmented_bar_class_init),
(rb_segmented_bar_finalize), (rb_segmented_bar_get_property),
(rb_segmented_bar_set_property),
(rb_segmented_bar_default_value_formatter),
(rb_segmented_bar_size_request), (create_adapt_layout),
(compute_layout_size), (rb_segmented_bar_size_allocate),
(rb_segmented_bar_add_segment),
(rb_segmented_bar_add_segment_default_color),
(rb_segmented_bar_update_segment), (draw_rounded_rectangle),
(rb_segmented_bar_render_segments), (modula), (hsb_from_color),
(color_from_hsb), (color_shade), (make_segment_gradient),
(rb_segmented_bar_render_strokes), (rb_segmented_bar_render),
(rb_segmented_bar_render_labels), (rb_segmented_bar_expose),
(rb_segmented_bar_new), (rb_segmented_bar_set_value_formatter):
* src/rb-segmented-bar.h:
Add input level monitor.
Added:
trunk/gnome-volume-control/src/rb-segmented-bar.c
trunk/gnome-volume-control/src/rb-segmented-bar.h
Modified:
trunk/gnome-volume-control/ChangeLog
trunk/gnome-volume-control/src/Makefile.am
trunk/gnome-volume-control/src/gvc-channel-bar.c
trunk/gnome-volume-control/src/gvc-mixer-control.c
trunk/gnome-volume-control/src/gvc-mixer-control.h
trunk/gnome-volume-control/src/gvc-mixer-dialog.c
Modified: trunk/gnome-volume-control/src/Makefile.am
==============================================================================
--- trunk/gnome-volume-control/src/Makefile.am (original)
+++ trunk/gnome-volume-control/src/Makefile.am Sat Dec 13 08:41:52 2008
@@ -73,6 +73,8 @@
sound-theme-file-utils.c \
gvc-mixer-dialog.h \
gvc-mixer-dialog.c \
+ rb-segmented-bar.h \
+ rb-segmented-bar.c \
dialog-main.c \
$(NULL)
Modified: trunk/gnome-volume-control/src/gvc-channel-bar.c
==============================================================================
--- trunk/gnome-volume-control/src/gvc-channel-bar.c (original)
+++ trunk/gnome-volume-control/src/gvc-channel-bar.c Sat Dec 13 08:41:52 2008
@@ -123,8 +123,8 @@
gtk_box_pack_end (GTK_BOX (sbox), priv->low_image, FALSE, FALSE, 0);
gtk_widget_show (priv->low_image);
- gtk_box_pack_end (GTK_BOX (sbox), priv->label, FALSE, FALSE, 0);
- gtk_box_pack_end (GTK_BOX (sbox), priv->image, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (sbox), priv->image, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (sbox), priv->label, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, TRUE, 0);
Modified: trunk/gnome-volume-control/src/gvc-mixer-control.c
==============================================================================
--- trunk/gnome-volume-control/src/gvc-mixer-control.c (original)
+++ trunk/gnome-volume-control/src/gvc-mixer-control.c Sat Dec 13 08:41:52 2008
@@ -84,6 +84,13 @@
G_DEFINE_TYPE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT)
+pa_context *
+gvc_mixer_control_get_pa_context (GvcMixerControl *control)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+ return control->priv->pa_context;
+}
+
GvcMixerStream *
gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
{
@@ -405,6 +412,7 @@
if (changed) {
GvcMixerStream *stream;
+
g_free (control->priv->default_source_name);
control->priv->default_source_name = g_strdup (name);
Modified: trunk/gnome-volume-control/src/gvc-mixer-control.h
==============================================================================
--- trunk/gnome-volume-control/src/gvc-mixer-control.h (original)
+++ trunk/gnome-volume-control/src/gvc-mixer-control.h Sat Dec 13 08:41:52 2008
@@ -22,6 +22,7 @@
#define __GVC_MIXER_CONTROL_H
#include <glib-object.h>
+#include <pulse/pulseaudio.h>
#include "gvc-mixer-stream.h"
G_BEGIN_DECLS
@@ -64,6 +65,7 @@
gboolean gvc_mixer_control_close (GvcMixerControl *control);
gboolean gvc_mixer_control_is_ready (GvcMixerControl *control);
+pa_context * gvc_mixer_control_get_pa_context (GvcMixerControl *control);
GSList * gvc_mixer_control_get_streams (GvcMixerControl *control);
GSList * gvc_mixer_control_get_sinks (GvcMixerControl *control);
GSList * gvc_mixer_control_get_sources (GvcMixerControl *control);
Modified: trunk/gnome-volume-control/src/gvc-mixer-dialog.c
==============================================================================
--- trunk/gnome-volume-control/src/gvc-mixer-dialog.c (original)
+++ trunk/gnome-volume-control/src/gvc-mixer-dialog.c Sat Dec 13 08:41:52 2008
@@ -36,6 +36,7 @@
#include "gvc-mixer-source.h"
#include "gvc-mixer-dialog.h"
#include "gvc-sound-theme-chooser.h"
+#include "rb-segmented-bar.h"
#define SCALE_SIZE 128
@@ -48,6 +49,7 @@
GtkWidget *notebook;
GtkWidget *output_bar;
GtkWidget *input_bar;
+ GtkWidget *input_level_bar;
GtkWidget *effects_bar;
GtkWidget *output_stream_box;
GtkWidget *sound_effects_box;
@@ -61,6 +63,8 @@
GtkWidget *audible_bell_button;
GtkSizeGroup *size_group;
GtkSizeGroup *apps_size_group;
+
+ gdouble last_input_peak;
};
enum {
@@ -177,6 +181,151 @@
update_default_output (dialog);
}
+
+#define DECAY_STEP .15
+
+static void
+update_input_peak (GvcMixerDialog *dialog,
+ gdouble v)
+{
+
+ if (dialog->priv->last_input_peak >= DECAY_STEP) {
+ if (v < dialog->priv->last_input_peak - DECAY_STEP) {
+ v = dialog->priv->last_input_peak - DECAY_STEP;
+ }
+ }
+
+ v = (gdouble)floor (20 * v) / 20;
+ dialog->priv->last_input_peak = v;
+
+ if (v >= 0) {
+ rb_segmented_bar_update_segment (RB_SEGMENTED_BAR (dialog->priv->input_level_bar),
+ 0,
+ v);
+ } else {
+ rb_segmented_bar_update_segment (RB_SEGMENTED_BAR (dialog->priv->input_level_bar),
+ 0,
+ 0.0);
+ }
+}
+
+static void
+update_input_meter (GvcMixerDialog *dialog,
+ uint32_t source_index,
+ uint32_t sink_input_idx,
+ double v)
+{
+ update_input_peak (dialog, v);
+}
+
+static void
+on_monitor_suspended_callback (pa_stream *s,
+ void *userdata)
+{
+ GvcMixerDialog *dialog;
+
+ dialog = userdata;
+
+ if (pa_stream_is_suspended (s)) {
+ g_debug ("Stream suspended");
+ update_input_meter (dialog,
+ pa_stream_get_device_index (s),
+ PA_INVALID_INDEX,
+ -1);
+ }
+}
+
+static void
+on_monitor_read_callback (pa_stream *s,
+ size_t length,
+ void *userdata)
+{
+ GvcMixerDialog *dialog;
+ const void *data;
+ double v;
+
+ dialog = userdata;
+
+ if (pa_stream_peek (s, &data, &length) < 0) {
+ g_warning ("Failed to read data from stream");
+ return;
+ }
+
+ assert (length > 0);
+ assert (length % sizeof (float) == 0);
+
+ v = ((const float *) data)[length / sizeof (float) -1];
+
+ pa_stream_drop (s);
+
+ if (v < 0) {
+ v = 0;
+ }
+ if (v > 1) {
+ v = 1;
+ }
+
+ update_input_meter (dialog,
+ pa_stream_get_device_index (s),
+ pa_stream_get_monitor_stream (s),
+ v);
+}
+
+static void
+create_monitor_stream_for_source (GvcMixerDialog *dialog,
+ GvcMixerStream *stream)
+{
+ pa_stream *s;
+ char t[16];
+ pa_buffer_attr attr;
+ pa_sample_spec ss;
+ pa_context *context;
+ int res;
+
+ if (stream == NULL) {
+ return;
+ }
+
+ g_debug ("Create monitor for %u",
+ gvc_mixer_stream_get_index (stream));
+
+ context = gvc_mixer_control_get_pa_context (dialog->priv->mixer_control);
+
+ if (pa_context_get_server_protocol_version (context) < 13) {
+ return;
+ }
+
+ ss.channels = 1;
+ ss.format = PA_SAMPLE_FLOAT32;
+ ss.rate = 25;
+
+ memset (&attr, 0, sizeof (attr));
+ attr.fragsize = sizeof (float);
+ attr.maxlength = (uint32_t) -1;
+
+ snprintf (t, sizeof (t), "%u", gvc_mixer_stream_get_index (stream));
+
+ s = pa_stream_new (context, _("Peak detect"), &ss, NULL);
+ if (s == NULL) {
+ g_warning ("Failed to create monitoring stream");
+ return;
+ }
+
+ pa_stream_set_read_callback (s, on_monitor_read_callback, dialog);
+ pa_stream_set_suspended_callback (s, on_monitor_suspended_callback, dialog);
+
+ res = pa_stream_connect_record (s,
+ t,
+ &attr,
+ (pa_stream_flags_t) (PA_STREAM_DONT_MOVE
+ |PA_STREAM_PEAK_DETECT
+ |PA_STREAM_ADJUST_LATENCY));
+ if (res < 0) {
+ g_warning ("Failed to connect monitoring stream");
+ pa_stream_unref (s);
+ }
+}
+
static void
on_mixer_control_default_source_changed (GvcMixerControl *control,
guint id,
@@ -190,6 +339,8 @@
id);
bar_set_stream (dialog, dialog->priv->input_bar, stream);
+ create_monitor_stream_for_source (dialog, stream);
+
update_default_input (dialog);
}
@@ -459,6 +610,8 @@
} else if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
bar = dialog->priv->input_bar;
is_default = TRUE;
+
+ create_monitor_stream_for_source (dialog, stream);
} else if (stream == gvc_mixer_control_get_event_sink_input (dialog->priv->mixer_control)) {
bar = dialog->priv->effects_bar;
g_debug ("Adding effects stream");
@@ -766,6 +919,8 @@
GtkWidget *label;
GtkWidget *alignment;
GtkWidget *box;
+ GtkWidget *sbox;
+ GtkWidget *ebox;
GSList *streams;
GSList *l;
GvcMixerStream *stream;
@@ -836,7 +991,46 @@
"audio-input-microphone-high");
gtk_widget_set_sensitive (self->priv->input_bar, FALSE);
gtk_box_pack_start (GTK_BOX (self->priv->input_box),
- self->priv->input_bar, FALSE, FALSE, 12);
+ self->priv->input_bar,
+ FALSE, FALSE, 12);
+
+ box = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (self->priv->input_box),
+ box,
+ FALSE, FALSE, 12);
+
+ sbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (box),
+ sbox,
+ FALSE, FALSE, 0);
+
+ label = gtk_label_new (_("Input level:"));
+ gtk_box_pack_start (GTK_BOX (sbox),
+ label,
+ FALSE, FALSE, 0);
+ gtk_size_group_add_widget (self->priv->size_group, sbox);
+
+ self->priv->input_level_bar = rb_segmented_bar_new ();
+ g_object_set (G_OBJECT (self->priv->input_level_bar),
+ "show-reflection", FALSE,
+ "show-labels", FALSE,
+ "bar-height", 22,
+ NULL);
+
+ rb_segmented_bar_add_segment (RB_SEGMENTED_BAR (self->priv->input_level_bar),
+ "audio", 0.0, 0.2 , 0.4 , 0.65, 1);
+ rb_segmented_bar_add_segment_default_color (RB_SEGMENTED_BAR (self->priv->input_level_bar),
+ "empty", 0.23);
+
+ gtk_box_pack_start (GTK_BOX (box),
+ self->priv->input_level_bar,
+ TRUE, TRUE, 0);
+
+ ebox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (box),
+ ebox,
+ FALSE, FALSE, 0);
+ gtk_size_group_add_widget (self->priv->size_group, ebox);
box = gtk_frame_new (_("Choose a device for sound input"));
label = gtk_frame_get_label_widget (GTK_FRAME (box));
Added: trunk/gnome-volume-control/src/rb-segmented-bar.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/rb-segmented-bar.c Sat Dec 13 08:41:52 2008
@@ -0,0 +1,901 @@
+/*
+ * 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 "rb-segmented-bar.h"
+
+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);
+
+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->bar_height = 26; /* why is that necessary? the corresponding
+ * property has a default value */
+ 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",
+ 0, G_MAXUINT, 26,
+ G_PARAM_READWRITE));
+
+ 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)
+{
+ g_return_if_fail (requisition != NULL);
+
+ requisition->width = 200;
+ requisition->height = 0;
+}
+
+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);
+}
+
+
+void 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);
+ Segment *segment = rb_segment_new (title, percent, &color);
+ priv->segments = g_list_append (priv->segments, segment);
+ gtk_widget_queue_draw (GTK_WIDGET (bar));
+}
+
+void rb_segmented_bar_add_segment_default_color (RBSegmentedBar *bar,
+ const gchar *title,
+ gdouble percent)
+{
+ 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 gdouble modula (gdouble number, gdouble divisor)
+{
+
+ return ((int)number % (int)divisor) + (number - (int)number);
+}
+
+static void hsb_from_color (Color *color, gdouble *hue,
+ gdouble *saturation, gdouble *brightness)
+{
+ gdouble min;
+ gdouble max;
+ gdouble delta;
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+
+ *hue = 0;
+ *saturation = 0;
+ *brightness = 0;
+ red = color->red;
+ green = color->green;
+ blue = color->blue;
+
+ if (red > green) {
+ max = MAX (red, blue);
+ min = MIN (green, blue);
+ } else {
+ max = MAX(green, blue);
+ min = MIN (red, blue);
+ }
+
+ *brightness = (max+min)/2;
+
+ if (fabs (max-min) < 0.0001) {
+ *hue = 0;
+ *saturation = 0;
+ } else {
+ if (*brightness <= 0.5) {
+ *saturation = (max - min) / (max + min);
+ } else {
+ *saturation = (max - min) / (2 - max - min);
+ }
+
+ delta = max - min;
+
+ if (red == max) {
+ *hue = (green - blue) / delta;
+ } else if (green == max) {
+ *hue = 2 + (blue - red) / delta;
+ } else if (blue == max) {
+ *hue = 4 + (red - green) / delta;
+ }
+
+ *hue *= 60;
+ if (*hue < 0) {
+ *hue += 360;
+ }
+ }
+}
+
+static Color *color_from_hsb (gdouble hue, gdouble saturation, gdouble brightness)
+{
+ int i;
+ gdouble hue_shift[] = { 0, 0, 0 };
+ gdouble color_shift[] = { 0, 0, 0 };
+ gdouble m1, m2, m3;
+ Color *color;
+
+ if (brightness <= 0.5) {
+ m2 = brightness * (1 + saturation);
+ } else {
+ m2 = brightness + saturation - brightness * saturation;
+ }
+
+ m1 = 2*brightness - m2;
+
+ hue_shift[0] = hue + 120;
+ hue_shift[1] = hue;
+ hue_shift[2] = hue - 120;
+
+ color_shift[0] = brightness;
+ color_shift[1] = brightness;
+ color_shift[2] = brightness;
+
+ if (saturation == 0) {
+ i = 3;
+ } else {
+ i = 0;
+ }
+
+ for (; i < 3; i++) {
+ m3 = hue_shift[i];
+
+ if (m3 > 360) {
+ m3 = modula (m3, 360);
+ } else if (m3 < 0) {
+ m3 = 360 - modula (fabs(m3), 360);
+ }
+
+ if (m3 < 60) {
+ color_shift[i] = m1 + (m2 - m1)*m3/60;
+ } else if (m3 < 180) {
+ color_shift[i] = m2;
+ } else if (m3 < 240) {
+ color_shift[i] = m1 + (m2 - m1)*(240-m3)/60;
+ } else {
+ color_shift[i] = m1;
+ }
+ }
+
+ color = g_new0 (Color, 1);
+ color->red = color_shift[0];
+ color->green = color_shift[1];
+ color->blue = color_shift[2];
+
+ 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;
+}
Added: trunk/gnome-volume-control/src/rb-segmented-bar.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/rb-segmented-bar.h Sat Dec 13 08:41:52 2008
@@ -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);
+void rb_segmented_bar_add_segment (RBSegmentedBar *bar,
+ const gchar *title, gdouble percent,
+ gdouble red, gdouble green,
+ gdouble blue, gdouble alpha);
+void 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 */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]