Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
rb-segmented-bar.c:635:0 | cppcheck | uninitvar | Uninitialized variable: x | |
rb-segmented-bar.c:635:11 | clang-analyzer | The left operand of '<=' is a garbage value | ||
rb-segmented-bar.c:635:11 | clang-analyzer | The left operand of '<=' is a garbage value | ||
rb-segmented-bar.c:635:0 | cppcheck | uninitvar | Uninitialized variable: x |
1 /*
2 * Initial Author:
3 * Aaron Bockover <abockover@novell.com>
4 *
5 * Ported to C from Banshee's SegmentedBar.cs widget
6 *
7 * Copyright (C) 2008 Novell, Inc.
8 * Copyright (C) 2008 Christophe Fergeau <teuf@gnome.org>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining
11 * a copy of this software and associated documentation files (the
12 * "Software"), to deal in the Software without restriction, including
13 * without limitation the rights to use, copy, modify, merge, publish,
14 * distribute, sublicense, and/or sell copies of the Software, and to
15 * permit persons to whom the Software is furnished to do so, subject to
16 * the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 #include <math.h>
34 #include <locale.h>
35 #include <cairo/cairo.h>
36 #include <gtk/gtk.h>
37 #include "rb-segmented-bar.h"
38
39 #define MINIMUM_HEIGHT 26
40
41 static void rb_segmented_bar_finalize (GObject *object);
42 static void rb_segmented_bar_size_allocate(GtkWidget *widget,
43 GtkAllocation *allocation);
44 static gboolean rb_segmented_bar_draw (GtkWidget *widget, cairo_t *context);
45 static void rb_segmented_bar_get_property (GObject *object, guint param_id,
46 GValue *value, GParamSpec *pspec);
47 static void rb_segmented_bar_set_property (GObject *object, guint param_id,
48 const GValue *value, GParamSpec *pspec);
49
50 static gchar *rb_segmented_bar_default_value_formatter (gdouble percent,
51 gpointer data);
52 static void rb_segmented_bar_get_preferred_height (GtkWidget *widget,
53 int *minimum_height,
54 int *natural_height);
55 static void rb_segmented_bar_get_preferred_width (GtkWidget *widget,
56 int *minimum_width,
57 int *natural_width);
58
59 static void compute_layout_size (RBSegmentedBar *bar);
60
61 static AtkObject * rb_segmented_bar_get_accessible (GtkWidget *widget);
62 enum
63 {
64 PROP_0,
65 PROP_SHOW_REFLECTION,
66 PROP_SHOW_LABELS,
67 PROP_BAR_HEIGHT
68 };
69
70 struct _RBSegmentedBarPrivate {
71 GList *segments;
72 guint layout_width;
73 guint layout_height;
74
75 guint bar_height;
76 guint bar_label_spacing;
77 guint segment_label_spacing;
78 guint segment_box_size;
79 guint segment_box_spacing;
80 guint h_padding;
81
82 gboolean show_labels;
83 gboolean reflect;
84
85 RBSegmentedBarValueFormatter value_formatter;
86 gpointer value_formatter_data;
87
88 char *a11y_description;
89 char *a11y_locale;
90 };
91
92 G_DEFINE_TYPE (RBSegmentedBar, rb_segmented_bar, GTK_TYPE_WIDGET)
93 #define RB_SEGMENTED_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SEGMENTED_BAR, RBSegmentedBarPrivate))
94
95 struct _Color {
96 gdouble red;
97 gdouble green;
98 gdouble blue;
99 gdouble alpha;
100 };
101 typedef struct _Color Color;
102
103 struct _Segment {
104 gchar *label;
105 gdouble percent;
106 Color color;
107
108 gint layout_width;
109 gint layout_height;
110 };
111 typedef struct _Segment Segment;
112
113 static Segment *rb_segment_new (const gchar *label, gdouble percent, Color *color)
114 {
115 Segment *segment;
116
117 segment = g_new0 (Segment, 1);
118 segment->label = g_strdup (label);
119 segment->percent = percent;
120 segment->color.red = color->red;
121 segment->color.green = color->green;
122 segment->color.blue = color->blue;
123 segment->color.alpha = color->alpha;
124
125 return segment;
126 }
127
128 static void rb_segment_free (Segment *segment)
129 {
130 g_return_if_fail (segment != NULL);
131 g_free (segment->label);
132 g_free (segment);
133 }
134
135 static void
136 rb_segmented_bar_init (RBSegmentedBar *bar)
137 {
138 RBSegmentedBarPrivate *priv;
139
140 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (bar));
141 priv->bar_label_spacing = 8;
142 priv->segment_label_spacing = 16;
143 priv->segment_box_size = 12;
144 priv->segment_box_spacing = 6;
145 priv->value_formatter = rb_segmented_bar_default_value_formatter;
146 gtk_widget_set_has_window (GTK_WIDGET (bar), FALSE);
147 }
148
149 static void
150 rb_segmented_bar_class_init (RBSegmentedBarClass *klass)
151 {
152 GObjectClass *object_class = G_OBJECT_CLASS (klass);
153 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
154
155 object_class->finalize = rb_segmented_bar_finalize;
156 object_class->get_property = rb_segmented_bar_get_property;
157 object_class->set_property = rb_segmented_bar_set_property;
158
159 widget_class->draw = rb_segmented_bar_draw;
160 widget_class->get_preferred_height = rb_segmented_bar_get_preferred_height;
161 widget_class->get_preferred_width = rb_segmented_bar_get_preferred_width;
162 widget_class->size_allocate = rb_segmented_bar_size_allocate;
163 widget_class->get_accessible = rb_segmented_bar_get_accessible;
164
165 /**
166 * RBSegmentedBar:show-reflection:
167 *
168 * Set to TRUE if you want a reflection to be shown below the segmented
169 * bar.
170 */
171 g_object_class_install_property (object_class,
172 PROP_SHOW_REFLECTION,
173 g_param_spec_boolean ("show-reflection",
174 "show-reflection",
175 "Whether there will be a reflection below the segmented bar",
176 TRUE,
177 G_PARAM_READWRITE));
178
179 /**
180 * RBSegmentedBar:show-labels:
181 *
182 * Set to TRUE if you want labels describing the various segments
183 * to be shown.
184 */
185 g_object_class_install_property (object_class,
186 PROP_SHOW_LABELS,
187 g_param_spec_boolean ("show-labels",
188 "show-labels",
189 "Whether the labels describing the various segments should be shown",
190 TRUE,
191 G_PARAM_READWRITE));
192 /**
193 * RBSegmentedBar:bar-height:
194 *
195 * Height of the segmented bar
196 */
197 g_object_class_install_property (object_class,
198 PROP_BAR_HEIGHT,
199 g_param_spec_uint ("bar-height",
200 "bar-height",
201 "height of the segmented bar",
202 MINIMUM_HEIGHT, G_MAXUINT, MINIMUM_HEIGHT,
203 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
204
205 g_type_class_add_private (klass, sizeof (RBSegmentedBarPrivate));
206 }
207
208 static void
209 rb_segmented_bar_finalize (GObject *object)
210 {
211 RBSegmentedBarPrivate *priv;
212 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (object));
213 g_list_foreach (priv->segments, (GFunc)rb_segment_free, NULL);
214 g_list_free (priv->segments);
215 g_free (priv->a11y_description);
216 g_free (priv->a11y_locale);
217 G_OBJECT_CLASS (rb_segmented_bar_parent_class)->finalize (object);
218 }
219
220 static void
221 rb_segmented_bar_get_property (GObject *object,
222 guint param_id,
223 GValue *value,
224 GParamSpec *pspec)
225 {
226 RBSegmentedBarPrivate *priv;
227 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (object));
228
229 switch (param_id) {
230 case PROP_SHOW_REFLECTION:
231 g_value_set_boolean (value, priv->reflect);
232 break;
233 case PROP_SHOW_LABELS:
234 g_value_set_boolean (value, priv->show_labels);
235 break;
236 case PROP_BAR_HEIGHT:
237 g_value_set_uint (value, priv->bar_height);
238 break;
239 default:
240 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
241 break;
242 }
243 }
244
245 static void
246 rb_segmented_bar_set_property (GObject *object,
247 guint param_id,
248 const GValue *value,
249 GParamSpec *pspec)
250 {
251 RBSegmentedBarPrivate *priv;
252 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (object));
253
254 switch (param_id) {
255 case PROP_SHOW_REFLECTION:
256 priv->reflect = g_value_get_boolean (value);
257 break;
258 case PROP_SHOW_LABELS:
259 priv->show_labels = g_value_get_boolean (value);
260 break;
261 case PROP_BAR_HEIGHT:
262 priv->bar_height = g_value_get_uint (value);
263 break;
264 default:
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
266 break;
267 }
268 }
269
270 static gchar *
271 rb_segmented_bar_default_value_formatter (gdouble percent,
272 G_GNUC_UNUSED gpointer data)
273 {
274 return g_strdup_printf ("%.2f%%", percent*100.0);
275 }
276
277 static void
278 rb_segmented_bar_get_preferred_height (GtkWidget *widget, int *minimum_height, int *natural_height)
279 {
280 RBSegmentedBarPrivate *priv;
281 int height;
282
283
284 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (widget));
285 if (priv->reflect) {
286 height = MINIMUM_HEIGHT * 1.75;
287 } else {
288 height = MINIMUM_HEIGHT;
289 }
290
291 if (priv->show_labels) {
292 compute_layout_size (RB_SEGMENTED_BAR (widget));
293 height = MAX (MINIMUM_HEIGHT + priv->bar_label_spacing + priv->layout_height, height);
294 }
295
296 if (minimum_height)
297 *minimum_height = height;
298 if (natural_height)
299 *natural_height = height;
300 }
301
302 static void
303 rb_segmented_bar_get_preferred_width (GtkWidget *widget, int *minimum_width, int *natural_width)
304 {
305 RBSegmentedBarPrivate *priv;
306 int width;
307
308 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (widget));
309
310 compute_layout_size (RB_SEGMENTED_BAR (widget));
311 width = MAX (priv->layout_width, 200);
312
313 if (minimum_width)
314 *minimum_width = width;
315 if (natural_width)
316 *natural_width = width;
317 }
318
319 static PangoLayout *create_adapt_layout (GtkWidget *widget, PangoLayout *layout,
320 gboolean small, gboolean bold)
321 {
322 const PangoFontDescription *desc;
323 PangoFontDescription *new_desc;
324
325 int normal_font_size;
326 if (layout == NULL) {
327 layout = gtk_widget_create_pango_layout (GTK_WIDGET (widget),
328 NULL);
329 }
330 desc = pango_context_get_font_description (gtk_widget_get_pango_context (widget));
331 g_assert (desc != NULL);
332 normal_font_size = pango_font_description_get_size (desc);
333
334 desc = pango_context_get_font_description (pango_layout_get_context (layout));
335 g_assert (desc != NULL);
336 new_desc = pango_font_description_copy (desc);
337
338 if (small) {
339 pango_font_description_set_size (new_desc,
340 normal_font_size * PANGO_SCALE_SMALL);
341 } else {
342 pango_font_description_set_size (new_desc, normal_font_size);
343 }
344
345 if (bold) {
346 pango_font_description_set_weight (new_desc,
347 PANGO_WEIGHT_BOLD);
348 } else {
349 pango_font_description_set_weight (new_desc,
350 PANGO_WEIGHT_NORMAL);
351 }
352 pango_layout_set_font_description (layout, new_desc);
353 pango_font_description_free (new_desc);
354 return layout;
355 }
356
357 static void
358 compute_layout_size (RBSegmentedBar *bar)
359 {
360 RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
361 PangoLayout *layout = NULL;
362 GList *it;
363
364 if (priv->segments == NULL) {
365 return;
366 }
367
368 priv->layout_width = 0;
369 priv->layout_height = 0;
370
371 for (it = priv->segments; it != NULL; it = it->next) {
372 Segment *segment = (Segment *)it->data;
373 gint label_width;
374 gint label_height;
375 gint value_width;
376 gint value_height;
377 gint width;
378 gint height;
379 gchar *value_str;
380
381 layout = create_adapt_layout (GTK_WIDGET (bar), layout,
382 FALSE, TRUE);
383 pango_layout_set_text (layout, segment->label, -1);
384 pango_layout_get_pixel_size (layout,
385 &label_width,
386 &label_height);
387
388 layout = create_adapt_layout (GTK_WIDGET (bar), layout,
389 TRUE, FALSE);
390 g_assert (priv->value_formatter != NULL);
391 value_str = priv->value_formatter (segment->percent,
392 priv->value_formatter_data);
393 pango_layout_set_text (layout, value_str, -1);
394 g_free (value_str);
395 pango_layout_get_pixel_size (layout,
396 &value_width,
397 &value_height);
398
399 width = MAX (label_width, value_width);
400 height = label_height + value_height;
401
402 segment->layout_width = width;
403 segment->layout_height = MAX (height, priv->segment_box_size*2);
404
405 priv->layout_width += segment->layout_width + priv->segment_box_size + priv->segment_box_spacing;
406 if (it->next != NULL) {
407 priv->layout_width += priv->segment_label_spacing;
408 }
409 priv->layout_height = MAX (priv->layout_height, segment->layout_height);
410 }
411
412 g_object_unref (G_OBJECT (layout));
413 }
414
415 static void
416 rb_segmented_bar_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
417 {
418 gint real_height;
419 RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (widget);
420 GtkAllocation new_allocation;
421
422 g_return_if_fail(RB_IS_SEGMENTED_BAR(widget));
423 g_return_if_fail(allocation != NULL);
424
425 if (priv->reflect) {
426 real_height = priv->bar_height*1.75;
427 } else {
428 real_height = priv->bar_height;
429 }
430 gtk_widget_set_allocation (widget, allocation);
431 if (priv->show_labels) {
432 compute_layout_size (RB_SEGMENTED_BAR (widget));
433 new_allocation.height = MAX (priv->bar_height + priv->bar_label_spacing + priv->layout_height,
434 real_height);
435 } else {
436 new_allocation.height = real_height;
437 }
438 new_allocation.width = priv->layout_width + 2*(priv->h_padding);
439 gtk_widget_set_allocation (widget, &new_allocation);
440 GTK_WIDGET_CLASS(rb_segmented_bar_parent_class)->size_allocate(widget, allocation);
441 }
442
443
444 guint rb_segmented_bar_add_segment (RBSegmentedBar *bar,
445 const gchar *title, gdouble percent,
446 gdouble red, gdouble green,
447 gdouble blue, gdouble alpha)
448 {
449 Color color = { red, green, blue, alpha };
450 RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
451 guint index;
452 Segment *segment = rb_segment_new (title, percent, &color);
453 priv->segments = g_list_append (priv->segments, segment);
454 index = g_list_index (priv->segments, segment);
455
456 g_free (priv->a11y_description);
457 priv->a11y_description = NULL;
458
459 gtk_widget_queue_draw (GTK_WIDGET (bar));
460 gtk_widget_queue_resize (GTK_WIDGET (bar));
461
462 return index;
463 }
464
465 guint rb_segmented_bar_add_segment_default_color (RBSegmentedBar *bar,
466 const gchar *title,
467 gdouble percent)
468 {
469 return rb_segmented_bar_add_segment (bar, title, percent, 0.9, 0.9, 0.9, 1.0);
470 }
471
472 void rb_segmented_bar_update_segment (RBSegmentedBar *bar,
473 guint segment_index,
474 gdouble percent)
475 {
476 RBSegmentedBarPrivate *priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
477 Segment *segment = g_list_nth_data (priv->segments, segment_index);
478 if (segment != NULL) {
479 segment->percent = percent;
480 g_free (priv->a11y_description);
481 priv->a11y_description = NULL;
482
483 gtk_widget_queue_draw (GTK_WIDGET (bar));
484 }
485 }
486
487 static void draw_rounded_rectangle (cairo_t *context,
488 guint x, guint y,
489 guint width, guint height,
490 guint radius)
491 {
492 if (radius < 0.0001) {
493 cairo_rectangle (context, x, y, width, height);
494 return;
495 }
496 cairo_move_to (context, x+radius, y);
497 cairo_arc (context, x+width-radius, y+radius, radius, G_PI*1.5, G_PI*2);
498 cairo_arc (context, x+width-radius, y+height-radius, radius, 0, G_PI*0.5);
499 cairo_arc (context, x+radius, y+height-radius, radius, G_PI*0.5, G_PI);
500 cairo_arc (context, x+radius, y+radius, radius, G_PI, G_PI*1.5);
501 }
502
503 static void rb_segmented_bar_render_segments (RBSegmentedBar *bar,
504 cairo_t *context,
505 guint width, guint height,
506 guint radius)
507 {
508 cairo_pattern_t *grad;
509 gdouble last;
510 GList *it;
511 RBSegmentedBarPrivate *priv;
512
513 last = 0.0;
514 priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
515 grad = cairo_pattern_create_linear (0, 0, width, 0);
516 for (it = priv->segments; it != NULL; it = it->next) {
517 Segment *segment = (Segment *)it->data;
518 if (segment->percent > 0) {
519 cairo_pattern_add_color_stop_rgba (grad, last,
520 segment->color.red,
521 segment->color.green,
522 segment->color.blue,
523 segment->color.alpha);
524 last += segment->percent;
525 cairo_pattern_add_color_stop_rgba (grad, last,
526 segment->color.red,
527 segment->color.green,
528 segment->color.blue,
529 segment->color.alpha);
530 }
531 }
532
533 draw_rounded_rectangle (context, 0, 0, width, height, radius);
534 cairo_set_source (context, grad);
535 cairo_fill_preserve (context);
536 cairo_pattern_destroy (grad);
537
538 grad = cairo_pattern_create_linear (0, 0, 0, height);
539 cairo_pattern_add_color_stop_rgba (grad, 0.0, 1, 1, 1, 0.125);
540 cairo_pattern_add_color_stop_rgba (grad, 0.35, 1, 1, 1, 0.255);
541 cairo_pattern_add_color_stop_rgba (grad, 1, 0, 0, 0, 0.4);
542 cairo_set_source (context, grad);
543 cairo_fill (context);
544 cairo_pattern_destroy (grad);
545
546 }
547
548 static void hsb_from_color (Color *color, gdouble *hue,
549 gdouble *saturation, gdouble *brightness)
550 {
551 gtk_rgb_to_hsv (color->red, color->green, color->blue,
552 hue, saturation, brightness);
553 }
554
555 static Color *color_from_hsb (gdouble hue, gdouble saturation, gdouble brightness)
556 {
557 Color *color;
558
559 color = g_new0 (Color, 1);
560 gtk_hsv_to_rgb (hue, saturation, brightness,
561 &color->red, &color->green, &color->blue);
562
563 return color;
564 }
565
566 static Color *color_shade (Color *base, gdouble ratio)
567 {
568 gdouble h;
569 gdouble s;
570 gdouble b;
571 Color *color;
572
573 hsb_from_color (base, &h, &s, &b);
574
575 b = MAX (MIN (b*ratio, 1), 0);
576 s = MAX (MIN (s*ratio, 1), 0);
577
578 color = color_from_hsb (h, s, b);
579 color->alpha = base->alpha;
580
581 return color;
582 }
583
584 static cairo_pattern_t *make_segment_gradient (guint height,
585 gdouble red, gdouble green,
586 gdouble blue, gdouble alpha)
587 {
588 cairo_pattern_t *grad;
589 Color *shade;
590 Color color = { red, green, blue, alpha };
591
592 grad = cairo_pattern_create_linear (0, 0, 0, height);
593
594 shade = color_shade (&color, 1.1);
595 cairo_pattern_add_color_stop_rgba (grad, 0,
596 shade->red, shade->green,
597 shade->blue, shade->alpha);
598 g_free (shade);
599
600 shade = color_shade (&color, 1.2);
601 cairo_pattern_add_color_stop_rgba (grad, 0.35,
602 shade->red, shade->green,
603 shade->blue, shade->alpha);
604 g_free (shade);
605
606 shade = color_shade (&color, 0.8);
607 cairo_pattern_add_color_stop_rgba (grad, 1,
608 shade->red, shade->green,
609 shade->blue, shade->alpha);
610 g_free (shade);
611
612 return grad;
613 }
614
615 static void rb_segmented_bar_render_strokes (RBSegmentedBar *bar,
616 cairo_t *context,
617 guint width, guint height,
618 guint radius)
619 {
620 cairo_pattern_t *stroke = make_segment_gradient (height,
621 0, 0, 0, 0.25);
622 cairo_pattern_t *seg_sep_light = make_segment_gradient (height,
623 1, 1, 1, 0.125);
624 cairo_pattern_t *seg_sep_dark = make_segment_gradient (height,
625 0, 0, 0, 0.125);
626 gdouble seg_w = 20;
627 gdouble x;
628 if (seg_w > radius) {
629 x = seg_w;
630 } else {
631 seg_w = radius;
632 }
633 cairo_set_line_width (context, 1);
634
635 while (x <= width-radius) {
(emitted by cppcheck) (emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by cppcheck) 636 cairo_move_to (context, x - 0.5, 1);
637 cairo_line_to (context, x - 0.5, height - 1);
638 cairo_set_source (context, seg_sep_light);
639 cairo_stroke (context);
640
641 cairo_move_to (context, x + 0.5, 1);
642 cairo_line_to (context, x + 0.5, height - 1);
643 cairo_set_source (context, seg_sep_dark);
644 cairo_stroke (context);
645
646 x += seg_w;
647 }
648
649 draw_rounded_rectangle (context, 0.5, 0.5,
650 width - 1, height - 1, radius);
651 cairo_set_source (context, stroke);
652 cairo_stroke (context);
653
654 cairo_pattern_destroy (stroke);
655 cairo_pattern_destroy (seg_sep_light);
656 cairo_pattern_destroy (seg_sep_dark);
657 }
658
659 static cairo_pattern_t *rb_segmented_bar_render (RBSegmentedBar *bar,
660 guint width, guint height)
661 {
662 cairo_surface_t *surface;
663 cairo_t *context;
664 cairo_pattern_t *pattern;
665
666 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
667 width, height);
668 context = cairo_create (surface);
669 rb_segmented_bar_render_segments (bar, context,
670 width, height, height/2);
671 rb_segmented_bar_render_strokes (bar, context, width, height, height/2);
672 pattern = cairo_pattern_create_for_surface (surface);
673 cairo_surface_destroy (surface);
674 cairo_destroy (context);
675
676 return pattern;
677 }
678
679 static void rb_segmented_bar_render_labels (RBSegmentedBar *bar,
680 cairo_t *context)
681 {
682 RBSegmentedBarPrivate *priv;
683 PangoLayout *layout;
684 Color text_color;
685 GdkRGBA gdk_color;
686 gboolean ltr = TRUE;
687 int x = 0;
688 GList *it;
689
690 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (bar));
691
692 if (priv->segments == NULL) {
693 return;
694 }
695 gtk_style_context_get_color (gtk_widget_get_style_context (GTK_WIDGET (bar)),
696 gtk_widget_get_state (GTK_WIDGET (bar)),
697 &gdk_color);
698
699 if (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_RTL) {
700 ltr = FALSE;
701 x = priv->layout_width;
702 }
703
704 text_color.red = gdk_color.red;
705 text_color.green = gdk_color.green;
706 text_color.blue = gdk_color.blue;
707 text_color.alpha = 1.0;
708 layout = NULL;
709 for (it = priv->segments; it != NULL; it = it->next) {
710 cairo_pattern_t *grad;
711 int layout_width;
712 int layout_height;
713 gchar *value_str;
714 Segment *segment;
715
716 if (ltr == FALSE) {
717 x -= priv->segment_box_size + priv->segment_box_spacing;
718 }
719
720 segment = (Segment *)it->data;
721 cairo_set_line_width (context, 1);
722 cairo_rectangle (context, x + 0.5, 2 + 0.5,
723 priv->segment_box_size - 1,
724 priv->segment_box_size - 1);
725 grad = make_segment_gradient (priv->segment_box_size,
726 segment->color.red,
727 segment->color.green,
728 segment->color.blue,
729 segment->color.alpha);
730 cairo_set_source (context, grad);
731 cairo_fill_preserve (context);
732 cairo_set_source_rgba (context, 0, 0, 0, 0.6);
733 cairo_stroke (context);
734 cairo_pattern_destroy (grad);
735
736 if (ltr) {
737 x += priv->segment_box_size + priv->segment_box_spacing;
738 }
739
740 layout = create_adapt_layout (GTK_WIDGET (bar), layout,
741 FALSE, TRUE);
742 pango_layout_set_text (layout, segment->label, -1);
743 pango_layout_get_pixel_size (layout,
744 &layout_width, &layout_height);
745 if (ltr == FALSE) {
746 x -= priv->segment_box_spacing + layout_width;
747 }
748
749 cairo_move_to (context, x, 0);
750 cairo_set_source_rgba (context,
751 text_color.red, text_color.green,
752 text_color.blue, 0.9);
753 pango_cairo_show_layout (context, layout);
754 cairo_fill (context);
755
756 layout = create_adapt_layout (GTK_WIDGET (bar), layout,
757 TRUE, FALSE);
758 g_assert (priv->value_formatter != NULL);
759 value_str = priv->value_formatter (segment->percent,
760 priv->value_formatter_data);
761 pango_layout_set_text (layout, value_str, -1);
762 g_free (value_str);
763
764 cairo_move_to (context, x, layout_height);
765 cairo_set_source_rgba (context,
766 text_color.red, text_color.green,
767 text_color.blue, 0.75);
768 pango_cairo_show_layout (context, layout);
769 cairo_fill (context);
770
771 if (ltr) {
772 x += segment->layout_width + priv->segment_label_spacing;
773 } else {
774 x -= segment->layout_width - layout_width;
775 }
776 }
777 g_object_unref (G_OBJECT (layout));
778 }
779
780 static gboolean
781 rb_segmented_bar_draw (GtkWidget *widget, cairo_t *context_)
782 {
783 RBSegmentedBar *bar;
784 RBSegmentedBarPrivate *priv;
785 GtkAllocation allocation;
786 cairo_pattern_t *bar_pattern;
787 cairo_t *context;
788
789 g_return_val_if_fail (RB_IS_SEGMENTED_BAR (widget), FALSE);
790
791 bar = RB_SEGMENTED_BAR (widget);
792 priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
793
794 /* XXX should use the context passed in, but this currently
795 * doesn't work properly with pre-existing translation
796 */
797 context = gdk_cairo_create (gtk_widget_get_window (widget));
798 if (priv->reflect) {
799 cairo_push_group (context);
800 }
801
802 cairo_set_operator (context, CAIRO_OPERATOR_OVER);
803 gtk_widget_get_allocation (widget, &allocation);
804 if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_LTR) {
805 cairo_translate (context, allocation.x + priv->h_padding, allocation.y);
806 } else {
807 cairo_translate (context,
808 allocation.x + allocation.width - priv->h_padding,
809 allocation.y);
810 cairo_scale (context, -1.0, 1.0);
811 }
812 cairo_rectangle (context, 0, 0,
813 allocation.width - priv->h_padding,
814 MAX (2*priv->bar_height, priv->bar_height + priv->bar_label_spacing + priv->layout_height));
815 cairo_clip (context);
816
817 bar_pattern = rb_segmented_bar_render (bar,
818 allocation.width - 2*priv->h_padding,
819 priv->bar_height);
820
821 cairo_save (context);
822 cairo_set_source (context, bar_pattern);
823 cairo_paint (context);
824 cairo_restore (context);
825
826 if (priv->reflect) {
827 cairo_matrix_t matrix;
828 cairo_pattern_t *mask;
829
830 cairo_save (context);
831
832 cairo_rectangle (context, 0, priv->bar_height,
833 allocation.width - priv->h_padding,
834 priv->bar_height);
835 cairo_clip (context);
836 cairo_matrix_init_scale (&matrix, 1, -1);
837 cairo_matrix_translate (&matrix, 0, -(2*priv->bar_height) + 1);
838 cairo_transform (context, &matrix);
839
840 cairo_set_source (context, bar_pattern);
841
842 mask = cairo_pattern_create_linear (0, 0, 0, priv->bar_height);
843 cairo_pattern_add_color_stop_rgba (mask, 0.25, 0, 0, 0, 0);
844 cairo_pattern_add_color_stop_rgba (mask, 0.5, 0, 0, 0, 0.125);
845 cairo_pattern_add_color_stop_rgba (mask, 0.75, 0, 0, 0, 0.4);
846 cairo_pattern_add_color_stop_rgba (mask, 1.0, 0, 0, 0, 0.7);
847
848 cairo_mask (context, mask);
849 cairo_pattern_destroy (mask);
850
851 cairo_restore (context);
852
853 cairo_pop_group_to_source (context);
854 cairo_paint (context);
855 }
856
857 if (priv->show_labels) {
858 if (priv->reflect) {
859 cairo_translate (context,
860 allocation.x + (allocation.width - priv->layout_width)/2,
861 allocation.y + priv->bar_height + priv->bar_label_spacing);
862 } else {
863 cairo_translate (context,
864 -priv->h_padding + (allocation.width - priv->layout_width)/2,
865 priv->bar_height + priv->bar_label_spacing);
866 }
867 rb_segmented_bar_render_labels (bar, context);
868 }
869 cairo_pattern_destroy (bar_pattern);
870 cairo_destroy (context);
871
872 return TRUE;
873 }
874
875 GtkWidget *rb_segmented_bar_new (void)
876 {
877 return g_object_new (RB_TYPE_SEGMENTED_BAR, NULL);
878 }
879
880 /**
881 * rb_segmented_bar_set_value_formatter:
882 * @bar: a #RBSegmentedBar
883 * @formatter: (scope async): the formatter function to use
884 * @data: data to pass to the formatter
885 *
886 * Sets a value formatter function to use for the bar.
887 */
888 void rb_segmented_bar_set_value_formatter (RBSegmentedBar *bar,
889 RBSegmentedBarValueFormatter formatter,
890 gpointer data)
891 {
892 RBSegmentedBarPrivate *priv;
893
894 priv = RB_SEGMENTED_BAR_GET_PRIVATE (RB_SEGMENTED_BAR (bar));
895
896 priv->value_formatter = formatter;
897 priv->value_formatter_data = data;
898 }
899
900 static const char *
901 get_a11y_description (RBSegmentedBar *bar)
902 {
903 RBSegmentedBarPrivate *priv;
904
905 priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
906 if (priv->a11y_description == NULL) {
907 GList *i;
908 GString *desc = g_string_new ("");
909
910 for (i = priv->segments; i != NULL; i = i->next) {
911 Segment *segment;
912 char *value_str;
913
914 segment = (Segment *)i->data;
915
916 g_assert (priv->value_formatter != NULL);
917 value_str = priv->value_formatter (segment->percent,
918 priv->value_formatter_data);
919
920 g_string_append_printf (desc, "%s: %s\n", segment->label, value_str);
921 g_free (value_str);
922 }
923
924 priv->a11y_description = g_string_free (desc, FALSE);
925 }
926 return priv->a11y_description;
927 }
928
929 static const char *
930 get_a11y_locale (RBSegmentedBar *bar)
931 {
932 RBSegmentedBarPrivate *priv;
933
934 priv = RB_SEGMENTED_BAR_GET_PRIVATE (bar);
935 if (priv->a11y_locale == NULL) {
936 priv->a11y_locale = setlocale (LC_MESSAGES, "");
937 }
938 return priv->a11y_locale;
939 }
940
941 /* A11y type hack copied from nautilus/eel/eel-accessibility.c */
942
943 static GType
944 create_a11y_derived_type (const char *type_name, GType existing_type, GClassInitFunc class_init)
945 {
946 GType type;
947 GType parent_atk_type;
948 GTypeQuery query;
949 AtkObjectFactory *factory;
950 GTypeInfo typeinfo = {0,};
951
952 type = g_type_from_name (type_name);
953 if (type != G_TYPE_INVALID) {
954 return type;
955 }
956
957 factory = atk_registry_get_factory (atk_get_default_registry (), existing_type);
958 parent_atk_type = atk_object_factory_get_accessible_type (factory);
959 if (parent_atk_type == G_TYPE_INVALID) {
960 return G_TYPE_INVALID;
961 }
962
963 g_type_query (parent_atk_type, &query);
964 if (class_init) {
965 typeinfo.class_init = class_init;
966 }
967 typeinfo.class_size = query.class_size;
968 typeinfo.instance_size = query.instance_size;
969
970 type = g_type_register_static (parent_atk_type, type_name, &typeinfo, 0);
971 return type;
972 }
973
974 /* AtkObject implementation */
975
976 static gint
977 a11y_impl_get_n_children (AtkObject *obj)
978 {
979 return 0;
980 }
981
982 static AtkObject *
983 a11y_impl_ref_child (AtkObject *obj, gint i)
984 {
985 return NULL;
986 }
987
988 /* AtkImage */
989
990 static void
991 a11y_impl_get_image_position (AtkImage *image, gint *x, gint *y, AtkCoordType coord_type)
992 {
993 atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type);
994 }
995
996 static const char *
997 a11y_impl_get_image_description (AtkImage *image)
998 {
999 RBSegmentedBar *bar;
1000 bar = RB_SEGMENTED_BAR (g_object_get_data (G_OBJECT (image), "rb-atk-widget"));
1001 return get_a11y_description (bar);
1002 }
1003
1004 static void
1005 a11y_impl_get_image_size (AtkImage *image, gint *width, gint *height)
1006 {
1007 GtkAllocation alloc;
1008 GtkWidget *widget;
1009
1010 widget = GTK_WIDGET (g_object_get_data (G_OBJECT (image), "rb-atk-widget"));
1011
1012 gtk_widget_get_allocation (widget, &alloc);
1013 *width = alloc.width;
1014 *height = alloc.height;
1015 }
1016
1017 static const char *
1018 a11y_impl_get_image_locale (AtkImage *image)
1019 {
1020 RBSegmentedBar *bar;
1021 bar = RB_SEGMENTED_BAR (g_object_get_data (G_OBJECT (image), "rb-atk-widget"));
1022 return get_a11y_locale (bar);
1023 }
1024
1025 static void
1026 rb_segmented_bar_a11y_class_init (AtkObjectClass *klass)
1027 {
1028 AtkObjectClass *atkobject_class = ATK_OBJECT_CLASS (klass);
1029
1030 atkobject_class->get_n_children = a11y_impl_get_n_children;
1031 atkobject_class->ref_child = a11y_impl_ref_child;
1032 }
1033
1034 static void
1035 rb_segmented_bar_a11y_image_init (AtkImageIface *iface)
1036 {
1037 iface->get_image_position = a11y_impl_get_image_position;
1038 iface->get_image_description = a11y_impl_get_image_description;
1039 iface->get_image_size = a11y_impl_get_image_size;
1040 iface->get_image_locale = a11y_impl_get_image_locale;
1041 /* don't need set_image_description, do we? */
1042 }
1043
1044 static void
1045 destroy_accessible (gpointer data, GObject *obj)
1046 {
1047 atk_object_notify_state_change (ATK_OBJECT (data), ATK_STATE_DEFUNCT, TRUE);
1048 }
1049
1050 static AtkObject *
1051 rb_segmented_bar_get_accessible (GtkWidget *widget)
1052 {
1053 static GType a11ytype = G_TYPE_INVALID;
1054 AtkObject *accessible;
1055 accessible = g_object_get_data (G_OBJECT (widget), "rb-atk-object");
1056 if (accessible != NULL) {
1057 return accessible;
1058 }
1059
1060 if (a11ytype == G_TYPE_INVALID) {
1061 const GInterfaceInfo atk_image_info = {
1062 (GInterfaceInitFunc) rb_segmented_bar_a11y_image_init,
1063 (GInterfaceFinalizeFunc) NULL,
1064 NULL
1065 };
1066
1067 a11ytype = create_a11y_derived_type ("RBSegmentedBarA11y",
1068 GTK_TYPE_WIDGET,
1069 (GClassInitFunc) rb_segmented_bar_a11y_class_init);
1070 if (a11ytype == G_TYPE_INVALID) {
1071 g_warning ("unable to create a11y type for segmented bar");
1072 return NULL;
1073 }
1074
1075 g_type_add_interface_static (a11ytype, ATK_TYPE_IMAGE, &atk_image_info);
1076 }
1077
1078 accessible = g_object_new (a11ytype, NULL);
1079 atk_object_set_role (accessible, ATK_ROLE_IMAGE);
1080 atk_object_initialize (accessible, widget);
1081
1082 g_object_set_data_full (G_OBJECT (widget), "rb-atk-object", accessible, (GDestroyNotify) destroy_accessible);
1083 g_object_set_data (G_OBJECT (accessible), "rb-atk-widget", widget);
1084
1085 return accessible;
1086 }