1 /* GTK - The GIMP Toolkit
2 * Copyright © 2012 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 * Author: Cosimo Cecchi <cosimoc@gnome.org>
24 * @Short_description: A bar that can used as a level indicator
26 * The #GtkLevelBar is a bar widget that can be used
27 * as a level indicator. Typical use cases are displaying the strength
28 * of a password, or showing the charge level of a battery.
30 * Use gtk_level_bar_set_value() to set the current value, and
31 * gtk_level_bar_add_offset_value() to set the value offsets at which
32 * the bar will be considered in a different state. GTK will add two offsets
33 * by default on the level bar: #GTK_LEVEL_BAR_OFFSET_LOW and
34 * #GTK_LEVEL_BAR_OFFSET_HIGH, with values 0.25 and 0.75 respectively.
37 * <title>Adding a custom offset on the bar</title>
41 * create_level_bar (void)
43 * GtkWidget *level_bar;
45 * level_bar = gtk_level_bar_new ();
47 * /<!---->* This changes the value of the default low offset *<!---->/
48 * gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (level_bar),
49 * GTK_LEVEL_BAR_OFFSET_LOW, 0.10);
51 * /<!---->* This adds a new offset to the bar; the application will
52 * * be able to change its color by using the following selector,
53 * * either by adding it to its CSS file or using
54 * * gtk_css_provider_load_from_data() and gtk_style_context_add_provider()
56 * * .level-bar.fill-block.level-my-offset {
57 * * background-color: green;
58 * * border-style: solid;
59 * * border-color: black;
60 * * border-style: 1px;
63 * gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (level_bar),
71 * The default interval of values is between zero and one, but it's possible to
72 * modify the interval using gtk_level_bar_set_min_value() and
73 * gtk_level_bar_set_max_value(). The value will be always drawn in proportion to
74 * the admissible interval, i.e. a value of 15 with a specified interval between
75 * 10 and 20 is equivalent to a value of 0.5 with an interval between 0 and 1.
76 * When #GTK_LEVEL_BAR_MODE_DISCRETE is used, the bar level is rendered
77 * as a finite and number of separated blocks instead of a single one. The number
78 * of blocks that will be rendered is equal to the number of units specified by
79 * the admissible interval.
80 * For instance, to build a bar rendered with five blocks, it's sufficient to
81 * set the minimum value to 0 and the maximum value to 5 after changing the indicator
88 #include "gtkbuildable.h"
90 #include "gtkorientableprivate.h"
91 #include "gtklevelbar.h"
92 #include "gtkmarshalers.h"
93 #include "gtkstylecontext.h"
94 #include "gtktypebuiltins.h"
95 #include "gtkwidget.h"
100 #include "a11y/gtklevelbaraccessible.h"
102 #include "fallback-c89.c"
104 #define DEFAULT_BLOCK_SIZE 3
106 /* these don't make sense outside of GtkLevelBar, so we don't add
108 #define STYLE_CLASS_INDICATOR_CONTINUOUS "indicator-continuous"
109 #define STYLE_CLASS_INDICATOR_DISCRETE "indicator-discrete"
110 #define STYLE_CLASS_FILL_BLOCK "fill-block"
111 #define STYLE_CLASS_EMPTY_FILL_BLOCK "empty-fill-block"
113 static void gtk_level_bar_buildable_init (GtkBuildableIface *iface);
115 G_DEFINE_TYPE_WITH_CODE (GtkLevelBar, gtk_level_bar, GTK_TYPE_WIDGET,
116 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
117 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
118 gtk_level_bar_buildable_init))
127 PROP_ORIENTATION /* overridden */
131 SIGNAL_OFFSET_CHANGED,
135 static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
136 static guint signals[NUM_SIGNALS] = { 0, };
143 struct _GtkLevelBarPrivate {
144 GtkOrientation orientation;
152 GtkLevelBarMode bar_mode;
157 static void gtk_level_bar_set_value_internal (GtkLevelBar *self,
160 static GtkLevelBarOffset *
161 gtk_level_bar_offset_new (const gchar *name,
164 GtkLevelBarOffset *offset = g_slice_new0 (GtkLevelBarOffset);
166 offset->name = g_strdup (name);
167 offset->value = value;
173 gtk_level_bar_offset_free (GtkLevelBarOffset *offset)
175 g_free (offset->name);
176 g_slice_free (GtkLevelBarOffset, offset);
180 offset_find_func (gconstpointer data,
181 gconstpointer user_data)
183 const GtkLevelBarOffset *offset = data;
184 const gchar *name = user_data;
186 return g_strcmp0 (name, offset->name);
190 offset_sort_func (gconstpointer a,
193 const GtkLevelBarOffset *offset_a = a;
194 const GtkLevelBarOffset *offset_b = b;
196 return (offset_a->value > offset_b->value);
200 gtk_level_bar_ensure_offset (GtkLevelBar *self,
205 GtkLevelBarOffset *offset = NULL;
207 existing = g_list_find_custom (self->priv->offsets, name, offset_find_func);
209 offset = existing->data;
211 if (offset && (offset->value == value))
216 gtk_level_bar_offset_free (offset);
217 self->priv->offsets = g_list_delete_link (self->priv->offsets, existing);
220 offset = gtk_level_bar_offset_new (name, value);
221 self->priv->offsets = g_list_insert_sorted (self->priv->offsets, offset, offset_sort_func);
227 gtk_level_bar_value_in_interval (GtkLevelBar *self,
230 return ((value >= self->priv->min_value) &&
231 (value <= self->priv->max_value));
235 gtk_level_bar_get_min_block_size (GtkLevelBar *self,
239 GtkWidget *widget = GTK_WIDGET (self);
240 GtkStyleContext *context = gtk_widget_get_style_context (widget);
241 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
242 GtkBorder border, tmp, tmp2;
243 gint min_width, min_height;
245 gtk_style_context_save (context);
246 gtk_style_context_add_class (context, STYLE_CLASS_FILL_BLOCK);
247 gtk_style_context_get_border (context, flags, &border);
248 gtk_style_context_get_padding (context, flags, &tmp);
249 gtk_style_context_get_margin (context, flags, &tmp2);
250 gtk_style_context_restore (context);
252 gtk_style_context_get_style (context,
253 "min-block-width", &min_width,
254 "min-block-height", &min_height,
257 border.top += tmp.top;
258 border.right += tmp.right;
259 border.bottom += tmp.bottom;
260 border.left += tmp.left;
262 border.top += tmp2.top;
263 border.right += tmp2.right;
264 border.bottom += tmp2.bottom;
265 border.left += tmp2.left;
268 *block_width = MAX (border.left + border.right, min_width);
270 *block_height = MAX (border.top + border.bottom, min_height);
274 gtk_level_bar_get_num_blocks (GtkLevelBar *self)
276 if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_CONTINUOUS)
278 else if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_DISCRETE)
279 return MAX (1, (gint) (round (self->priv->max_value) - round (self->priv->min_value)));
285 gtk_level_bar_get_borders (GtkLevelBar *self,
286 GtkBorder *borders_out)
288 GtkWidget *widget = GTK_WIDGET (self);
289 GtkStyleContext *context = gtk_widget_get_style_context (widget);
290 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
291 GtkBorder border, tmp;
293 gtk_style_context_save (context);
294 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
295 gtk_style_context_get_border (context, flags, &border);
296 gtk_style_context_get_padding (context, flags, &tmp);
297 gtk_style_context_restore (context);
299 border.top += tmp.top;
300 border.right += tmp.right;
301 border.bottom += tmp.bottom;
302 border.left += tmp.left;
305 *borders_out = border;
309 gtk_level_bar_draw_fill_continuous (GtkLevelBar *self,
312 cairo_rectangle_int_t *fill_area)
314 GtkWidget *widget = GTK_WIDGET (self);
315 GtkStyleContext *context = gtk_widget_get_style_context (widget);
316 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
317 cairo_rectangle_int_t base_area, block_area;
318 GtkBorder block_margin;
319 gdouble fill_percentage;
321 gtk_style_context_save (context);
322 gtk_style_context_add_class (context, STYLE_CLASS_FILL_BLOCK);
323 gtk_style_context_get_margin (context, flags, &block_margin);
325 /* render the empty (unfilled) part */
326 base_area = *fill_area;
327 base_area.x += block_margin.left;
328 base_area.y += block_margin.top;
329 base_area.width -= block_margin.left + block_margin.right;
330 base_area.height -= block_margin.top + block_margin.bottom;
332 gtk_style_context_add_class (context, STYLE_CLASS_EMPTY_FILL_BLOCK);
334 gtk_render_background (context, cr, base_area.x, base_area.y,
335 base_area.width, base_area.height);
336 gtk_render_frame (context, cr, base_area.x, base_area.y,
337 base_area.width, base_area.height);
339 gtk_style_context_remove_class (context, STYLE_CLASS_EMPTY_FILL_BLOCK);
341 /* now render the filled part on top of it */
342 block_area = base_area;
344 fill_percentage = (self->priv->cur_value - self->priv->min_value) /
345 (self->priv->max_value - self->priv->min_value);
347 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
349 block_area.width = (gint) floor (block_area.width * fill_percentage);
352 block_area.x += base_area.width - block_area.width;
356 block_area.height = (gint) floor (block_area.height * fill_percentage);
359 block_area.y += base_area.height - block_area.height;
362 gtk_render_background (context, cr, block_area.x, block_area.y,
363 block_area.width, block_area.height);
364 gtk_render_frame (context, cr, block_area.x, block_area.y,
365 block_area.width, block_area.height);
367 gtk_style_context_restore (context);
371 gtk_level_bar_draw_fill_discrete (GtkLevelBar *self,
374 cairo_rectangle_int_t *fill_area)
376 GtkWidget *widget = GTK_WIDGET (self);
377 GtkStyleContext *context = gtk_widget_get_style_context (widget);
378 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
379 gint num_blocks, num_filled, idx;
380 gint block_width, block_height;
381 gint block_draw_width, block_draw_height;
382 GtkBorder block_margin;
383 cairo_rectangle_int_t block_area;
385 gtk_level_bar_get_min_block_size (self, &block_width, &block_height);
387 block_area = *fill_area;
388 num_blocks = gtk_level_bar_get_num_blocks (self);
389 num_filled = (gint) round (self->priv->cur_value) - (gint) round (self->priv->min_value);
391 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
392 block_width = MAX (block_width, (gint) floor (block_area.width / num_blocks));
394 block_height = MAX (block_height, (gint) floor (block_area.height / num_blocks));
396 gtk_style_context_save (context);
397 gtk_style_context_add_class (context, STYLE_CLASS_FILL_BLOCK);
398 gtk_style_context_get_margin (context, flags, &block_margin);
400 block_draw_width = block_width - block_margin.left - block_margin.right;
401 block_draw_height = block_height - block_margin.top - block_margin.bottom;
403 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
405 block_draw_height = MAX (block_draw_height, block_area.height - block_margin.top - block_margin.bottom);
406 block_area.y += block_margin.top;
409 block_area.x += block_area.width - block_draw_width;
413 block_draw_width = MAX (block_draw_width, block_area.width - block_margin.left - block_margin.right);
414 block_area.x += block_margin.left;
417 block_area.y += block_area.height - block_draw_height;
420 for (idx = 0; idx < num_blocks; idx++)
422 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
425 block_area.x -= block_margin.right;
427 block_area.x += block_margin.left;
432 block_area.y -= block_margin.bottom;
434 block_area.y += block_margin.top;
437 if (idx > num_filled - 1)
438 gtk_style_context_add_class (context, STYLE_CLASS_EMPTY_FILL_BLOCK);
440 gtk_render_background (context, cr,
441 block_area.x, block_area.y,
442 block_draw_width, block_draw_height);
443 gtk_render_frame (context, cr,
444 block_area.x, block_area.y,
445 block_draw_width, block_draw_height);
447 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
450 block_area.x -= block_draw_width + block_margin.left;
452 block_area.x += block_draw_width + block_margin.right;
457 block_area.y -= block_draw_height + block_margin.top;
459 block_area.y += block_draw_height + block_margin.bottom;
463 gtk_style_context_restore (context);
467 gtk_level_bar_draw_fill (GtkLevelBar *self,
470 GtkWidget *widget = GTK_WIDGET (self);
471 GtkBorder trough_borders;
473 cairo_rectangle_int_t fill_area;
475 gtk_level_bar_get_borders (self, &trough_borders);
477 fill_area.x = trough_borders.left;
478 fill_area.y = trough_borders.top;
479 fill_area.width = gtk_widget_get_allocated_width (widget) -
480 trough_borders.left - trough_borders.right;
481 fill_area.height = gtk_widget_get_allocated_height (widget) -
482 trough_borders.top - trough_borders.bottom;
484 inverted = self->priv->inverted;
485 if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
487 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
488 inverted = !inverted;
491 if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_CONTINUOUS)
492 gtk_level_bar_draw_fill_continuous (self, cr, inverted, &fill_area);
493 else if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_DISCRETE)
494 gtk_level_bar_draw_fill_discrete (self, cr, inverted, &fill_area);
498 gtk_level_bar_draw (GtkWidget *widget,
501 GtkLevelBar *self = GTK_LEVEL_BAR (widget);
502 GtkStyleContext *context = gtk_widget_get_style_context (widget);
505 width = gtk_widget_get_allocated_width (widget);
506 height = gtk_widget_get_allocated_height (widget);
508 gtk_style_context_save (context);
509 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
511 gtk_render_background (context, cr, 0, 0, width, height);
512 gtk_render_frame (context, cr, 0, 0, width, height);
514 gtk_style_context_restore (context);
516 gtk_level_bar_draw_fill (self, cr);
522 gtk_level_bar_get_preferred_width (GtkWidget *widget,
526 GtkLevelBar *self = GTK_LEVEL_BAR (widget);
529 gint width, block_width;
531 num_blocks = gtk_level_bar_get_num_blocks (self);
532 gtk_level_bar_get_min_block_size (self, &block_width, NULL);
534 gtk_level_bar_get_borders (self, &borders);
535 width = borders.left + borders.right;
537 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
538 width += num_blocks * block_width;
540 width += block_width;
549 gtk_level_bar_get_preferred_height (GtkWidget *widget,
553 GtkLevelBar *self = GTK_LEVEL_BAR (widget);
556 gint height, block_height;
558 num_blocks = gtk_level_bar_get_num_blocks (self);
559 gtk_level_bar_get_min_block_size (self, NULL, &block_height);
561 gtk_level_bar_get_borders (self, &borders);
562 height = borders.top + borders.bottom;
564 if (self->priv->orientation == GTK_ORIENTATION_VERTICAL)
565 height += num_blocks * block_height;
567 height += block_height;
576 gtk_level_bar_update_mode_style_classes (GtkLevelBar *self)
578 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (self));
580 if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_CONTINUOUS)
582 gtk_style_context_remove_class (context, STYLE_CLASS_INDICATOR_DISCRETE);
583 gtk_style_context_add_class (context, STYLE_CLASS_INDICATOR_CONTINUOUS);
585 else if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_DISCRETE)
587 gtk_style_context_remove_class (context, STYLE_CLASS_INDICATOR_CONTINUOUS);
588 gtk_style_context_add_class (context, STYLE_CLASS_INDICATOR_DISCRETE);
593 gtk_level_bar_update_level_style_classes (GtkLevelBar *self)
595 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (self));
596 gdouble value = gtk_level_bar_get_value (self);
597 gchar *offset_style_class, *value_class = NULL;
598 GtkLevelBarOffset *offset, *prev_offset;
601 for (l = self->priv->offsets; l != NULL; l = l->next)
605 offset_style_class = g_strconcat ("level-", offset->name, NULL);
606 gtk_style_context_remove_class (context, offset_style_class);
608 /* find the right offset for our style class */
609 if (((l->prev == NULL) && (value <= offset->value)) ||
610 ((l->next == NULL) && (value >= offset->value)))
612 value_class = offset_style_class;
614 else if ((l->next != NULL) && (l->prev != NULL))
616 prev_offset = l->prev->data;
617 if ((prev_offset->value <= value) && (value < offset->value))
618 value_class = offset_style_class;
622 g_free (offset_style_class);
626 if (value_class != NULL)
628 gtk_style_context_add_class (context, value_class);
629 g_free (value_class);
634 gtk_level_bar_ensure_offsets_in_range (GtkLevelBar *self)
636 GtkLevelBarOffset *offset;
637 GList *l = self->priv->offsets;
644 if (offset->value < self->priv->min_value)
645 gtk_level_bar_ensure_offset (self, offset->name, self->priv->min_value);
646 else if (offset->value > self->priv->max_value)
647 gtk_level_bar_ensure_offset (self, offset->name, self->priv->max_value);
657 offset_start_element (GMarkupParseContext *context,
658 const gchar *element_name,
660 const gchar **values,
664 OffsetsParserData *parser_data = user_data;
665 const gchar *name = NULL;
666 const gchar *value_str = NULL;
667 GtkLevelBarOffset *offset;
668 gint line_number, char_number;
671 if (strcmp (element_name, "offsets") == 0)
673 else if (strcmp (element_name, "offset") == 0)
675 for (idx = 0; names[idx] != NULL; idx++)
677 if (strcmp (names[idx], "name") == 0)
681 else if (strcmp (names[idx], "value") == 0)
683 value_str = values[idx];
687 g_markup_parse_context_get_position (context,
692 GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
693 "%s:%d:%d '%s' is not a valid attribute of <%s>",
695 line_number, char_number, names[idx], "offset");
701 if (name && value_str)
703 offset = gtk_level_bar_offset_new (name, g_ascii_strtod (value_str, NULL));
704 parser_data->offsets = g_list_prepend (parser_data->offsets, offset);
709 g_markup_parse_context_get_position (context,
714 GTK_BUILDER_ERROR_UNHANDLED_TAG,
715 "%s:%d:%d unsupported tag for GtkLevelBar: \"%s\"",
717 line_number, char_number, element_name);
721 static const GMarkupParser offset_parser =
727 gtk_level_bar_buildable_custom_tag_start (GtkBuildable *buildable,
730 const gchar *tagname,
731 GMarkupParser *parser,
734 OffsetsParserData *parser_data;
739 if (strcmp (tagname, "offsets") != 0)
742 parser_data = g_slice_new0 (OffsetsParserData);
743 parser_data->self = GTK_LEVEL_BAR (buildable);
744 parser_data->offsets = NULL;
746 *parser = offset_parser;
753 gtk_level_bar_buildable_custom_finished (GtkBuildable *buildable,
756 const gchar *tagname,
759 OffsetsParserData *parser_data;
761 GtkLevelBarOffset *offset;
764 parser_data = user_data;
765 self = parser_data->self;
767 if (strcmp (tagname, "offsets") != 0)
770 for (l = parser_data->offsets; l != NULL; l = l->next)
773 gtk_level_bar_add_offset_value (self, offset->name, offset->value);
777 g_list_free_full (parser_data->offsets, (GDestroyNotify) gtk_level_bar_offset_free);
778 g_slice_free (OffsetsParserData, parser_data);
782 gtk_level_bar_buildable_init (GtkBuildableIface *iface)
784 iface->custom_tag_start = gtk_level_bar_buildable_custom_tag_start;
785 iface->custom_finished = gtk_level_bar_buildable_custom_finished;
789 gtk_level_bar_set_orientation (GtkLevelBar *self,
790 GtkOrientation orientation)
792 if (self->priv->orientation != orientation)
794 self->priv->orientation = orientation;
795 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (self));
797 gtk_widget_queue_resize (GTK_WIDGET (self));
802 gtk_level_bar_get_property (GObject *obj,
807 GtkLevelBar *self = GTK_LEVEL_BAR (obj);
812 g_value_set_double (value, gtk_level_bar_get_value (self));
815 g_value_set_double (value, gtk_level_bar_get_min_value (self));
818 g_value_set_double (value, gtk_level_bar_get_max_value (self));
821 g_value_set_enum (value, gtk_level_bar_get_mode (self));
824 g_value_set_boolean (value, gtk_level_bar_get_inverted (self));
826 case PROP_ORIENTATION:
827 g_value_set_enum (value, self->priv->orientation);
830 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
836 gtk_level_bar_set_property (GObject *obj,
841 GtkLevelBar *self = GTK_LEVEL_BAR (obj);
846 gtk_level_bar_set_value (self, g_value_get_double (value));
849 gtk_level_bar_set_min_value (self, g_value_get_double (value));
852 gtk_level_bar_set_max_value (self, g_value_get_double (value));
855 gtk_level_bar_set_mode (self, g_value_get_enum (value));
858 gtk_level_bar_set_inverted (self, g_value_get_boolean (value));
860 case PROP_ORIENTATION:
861 gtk_level_bar_set_orientation (self, g_value_get_enum (value));
864 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
870 gtk_level_bar_finalize (GObject *obj)
872 GtkLevelBar *self = GTK_LEVEL_BAR (obj);
874 g_list_free_full (self->priv->offsets, (GDestroyNotify) gtk_level_bar_offset_free);
876 G_OBJECT_CLASS (gtk_level_bar_parent_class)->finalize (obj);
880 gtk_level_bar_class_init (GtkLevelBarClass *klass)
882 GObjectClass *oclass = G_OBJECT_CLASS (klass);
883 GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
885 oclass->get_property = gtk_level_bar_get_property;
886 oclass->set_property = gtk_level_bar_set_property;
887 oclass->finalize = gtk_level_bar_finalize;
889 wclass->draw = gtk_level_bar_draw;
890 wclass->get_preferred_width = gtk_level_bar_get_preferred_width;
891 wclass->get_preferred_height = gtk_level_bar_get_preferred_height;
893 g_object_class_override_property (oclass, PROP_ORIENTATION, "orientation");
896 * GtkLevelBar::offset-changed:
897 * @self: a #GtkLevelBar
898 * @name: the name of the offset that changed value
900 * Emitted when an offset specified on the bar changes value as an
901 * effect to gtk_level_bar_add_offset_value() being called.
903 * The signal supports detailed connections; you can connect to the
904 * detailed signal "changed::x" in order to only receive callbacks when
905 * the value of offset "x" changes.
909 signals[SIGNAL_OFFSET_CHANGED] =
910 g_signal_new ("offset-changed",
912 G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
913 G_STRUCT_OFFSET (GtkLevelBarClass, offset_changed),
915 _gtk_marshal_VOID__STRING,
922 * The #GtkLevelBar:value property determines the currently
923 * filled value of the level bar.
927 properties[PROP_VALUE] =
928 g_param_spec_double ("value",
929 P_("Currently filled value level"),
930 P_("Currently filled value level of the level bar"),
931 0.0, G_MAXDOUBLE, 0.0,
932 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
934 * GtkLevelBar:min-value:
936 * The #GtkLevelBar:min-value property determines the minimum value of
937 * the interval that can be displayed by the bar.
941 properties[PROP_MIN_VALUE] =
942 g_param_spec_double ("min-value",
943 P_("Minimum value level for the bar"),
944 P_("Minimum value level that can be displayed by the bar"),
945 0.0, G_MAXDOUBLE, 0.0,
946 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
948 * GtkLevelBar:max-value:
950 * The #GtkLevelBar:max-value property determaxes the maximum value of
951 * the interval that can be displayed by the bar.
955 properties[PROP_MAX_VALUE] =
956 g_param_spec_double ("max-value",
957 P_("Maximum value level for the bar"),
958 P_("Maximum value level that can be displayed by the bar"),
959 0.0, G_MAXDOUBLE, 1.0,
960 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
964 * The #GtkLevelBar:bar-mode property determines the way #GtkLevelBar
965 * interprets the value properties to draw the level fill area.
966 * Specifically, when the value is #GTK_LEVEL_BAR_MODE_CONTINUOUS,
967 * #GtkLevelBar will draw a single block representing the current value in
968 * that area; when the value is #GTK_LEVEL_BAR_MODE_DISCRETE,
969 * the widget will draw a succession of separate blocks filling the
970 * draw area, with the number of blocks being equal to the units separating
971 * the integral roundings of #GtkLevelBar:min-value and #GtkLevelBar:max-value.
975 properties[PROP_MODE] =
976 g_param_spec_enum ("mode",
977 P_("The mode of the value indicator"),
978 P_("The mode of the value indicator displayed by the bar"),
979 GTK_TYPE_LEVEL_BAR_MODE,
980 GTK_LEVEL_BAR_MODE_CONTINUOUS,
984 * GtkLevelBar:inverted:
986 * Level bars normally grow from top to bottom or left to right.
987 * Inverted level bars grow in the opposite direction.
991 properties[PROP_INVERTED] =
992 g_param_spec_boolean ("inverted",
994 P_("Invert the direction in which the level bar grows"),
999 * GtkLevelBar:min-block-height:
1001 * The min-block-height style property determines the minimum
1002 * height for blocks filling the #GtkLevelBar widget.
1006 gtk_widget_class_install_style_property
1007 (wclass, g_param_spec_int ("min-block-height",
1008 P_("Minimum height for filling blocks"),
1009 P_("Minimum height for blocks that fill the bar"),
1010 1, G_MAXINT, DEFAULT_BLOCK_SIZE,
1011 G_PARAM_READWRITE));
1013 * GtkLevelBar:min-block-width:
1015 * The min-block-width style property determines the minimum
1016 * width for blocks filling the #GtkLevelBar widget.
1020 gtk_widget_class_install_style_property
1021 (wclass, g_param_spec_int ("min-block-width",
1022 P_("Minimum width for filling blocks"),
1023 P_("Minimum width for blocks that fill the bar"),
1024 1, G_MAXINT, DEFAULT_BLOCK_SIZE,
1025 G_PARAM_READWRITE));
1027 g_type_class_add_private (klass, sizeof (GtkLevelBarPrivate));
1028 g_object_class_install_properties (oclass, LAST_PROPERTY, properties);
1030 gtk_widget_class_set_accessible_type (wclass, GTK_TYPE_LEVEL_BAR_ACCESSIBLE);
1034 gtk_level_bar_init (GtkLevelBar *self)
1036 GtkStyleContext *context;
1038 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_LEVEL_BAR, GtkLevelBarPrivate);
1040 context = gtk_widget_get_style_context (GTK_WIDGET (self));
1041 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEVEL_BAR);
1043 self->priv->cur_value = 0.0;
1044 self->priv->min_value = 0.0;
1045 self->priv->max_value = 1.0;
1047 gtk_level_bar_ensure_offset (self, GTK_LEVEL_BAR_OFFSET_LOW, 0.25);
1048 gtk_level_bar_ensure_offset (self, GTK_LEVEL_BAR_OFFSET_HIGH, 0.75);
1049 gtk_level_bar_update_level_style_classes (self);
1051 self->priv->bar_mode = GTK_LEVEL_BAR_MODE_CONTINUOUS;
1052 gtk_level_bar_update_mode_style_classes (self);
1054 /* set initial orientation and style classes */
1055 self->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
1056 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (self));
1058 self->priv->inverted = FALSE;
1060 gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
1064 * gtk_level_bar_new:
1066 * Creates a new #GtkLevelBar.
1068 * Returns: a #GtkLevelBar.
1073 gtk_level_bar_new (void)
1075 return g_object_new (GTK_TYPE_LEVEL_BAR, NULL);
1079 * gtk_level_bar_new_for_interval:
1080 * @min_value: a positive value
1081 * @max_value: a positive value
1083 * Utility constructor that creates a new #GtkLevelBar for the specified
1086 * Returns: a #GtkLevelBar
1091 gtk_level_bar_new_for_interval (gdouble min_value,
1094 return g_object_new (GTK_TYPE_LEVEL_BAR,
1095 "min-value", min_value,
1096 "max-value", max_value,
1101 * gtk_level_bar_get_min_value:
1102 * @self: a #GtkLevelBar
1104 * Returns the value of the #GtkLevelBar:min-value property.
1106 * Returns: a positive value
1111 gtk_level_bar_get_min_value (GtkLevelBar *self)
1113 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
1115 return self->priv->min_value;
1119 * gtk_level_bar_get_max_value:
1120 * @self: a #GtkLevelBar
1122 * Returns the value of the #GtkLevelBar:max-value property.
1124 * Returns: a positive value
1129 gtk_level_bar_get_max_value (GtkLevelBar *self)
1131 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
1133 return self->priv->max_value;
1137 * gtk_level_bar_get_value:
1138 * @self: a #GtkLevelBar
1140 * Returns the value of the #GtkLevelBar:value property.
1142 * Returns: a value in the interval between
1143 * #GtkLevelBar:min-value and #GtkLevelBar:max-value
1148 gtk_level_bar_get_value (GtkLevelBar *self)
1150 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
1152 return self->priv->cur_value;
1156 gtk_level_bar_set_value_internal (GtkLevelBar *self,
1159 self->priv->cur_value = value;
1160 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
1161 gtk_widget_queue_draw (GTK_WIDGET (self));
1165 * gtk_level_bar_set_min_value:
1166 * @self: a #GtkLevelBar
1167 * @value: a positive value
1169 * Sets the value of the #GtkLevelBar:min-value property.
1174 gtk_level_bar_set_min_value (GtkLevelBar *self,
1177 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1178 g_return_if_fail (value >= 0.0);
1180 if (value != self->priv->min_value)
1182 self->priv->min_value = value;
1183 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIN_VALUE]);
1185 if (self->priv->min_value > self->priv->cur_value)
1186 gtk_level_bar_set_value_internal (self, self->priv->min_value);
1188 gtk_level_bar_update_level_style_classes (self);
1193 * gtk_level_bar_set_max_value:
1194 * @self: a #GtkLevelBar
1195 * @value: a positive value
1197 * Sets the value of the #GtkLevelBar:max-value property.
1202 gtk_level_bar_set_max_value (GtkLevelBar *self,
1205 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1206 g_return_if_fail (value >= 0.0);
1208 if (value != self->priv->max_value)
1210 self->priv->max_value = value;
1211 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAX_VALUE]);
1213 if (self->priv->max_value < self->priv->cur_value)
1214 gtk_level_bar_set_value_internal (self, self->priv->max_value);
1216 gtk_level_bar_ensure_offsets_in_range (self);
1217 gtk_level_bar_update_level_style_classes (self);
1222 * gtk_level_bar_set_value:
1223 * @self: a #GtkLevelBar
1224 * @value: a value in the interval between
1225 * #GtkLevelBar:min-value and #GtkLevelBar:max-value
1227 * Sets the value of the #GtkLevelBar:value property.
1232 gtk_level_bar_set_value (GtkLevelBar *self,
1235 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1237 if (value != self->priv->cur_value)
1239 gtk_level_bar_set_value_internal (self, value);
1240 gtk_level_bar_update_level_style_classes (self);
1245 * gtk_level_bar_get_mode:
1246 * @self: a #GtkLevelBar
1248 * Returns the value of the #GtkLevelBar:mode property.
1250 * Returns: a #GtkLevelBarMode
1255 gtk_level_bar_get_mode (GtkLevelBar *self)
1257 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0);
1259 return self->priv->bar_mode;
1263 * gtk_level_bar_set_mode:
1264 * @self: a #GtkLevelBar
1265 * @mode: a #GtkLevelBarMode
1267 * Sets the value of the #GtkLevelBar:mode property.
1272 gtk_level_bar_set_mode (GtkLevelBar *self,
1273 GtkLevelBarMode mode)
1275 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1277 if (self->priv->bar_mode != mode)
1279 self->priv->bar_mode = mode;
1280 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODE]);
1282 gtk_level_bar_update_mode_style_classes (self);
1283 gtk_widget_queue_resize (GTK_WIDGET (self));
1288 * gtk_level_bar_get_inverted:
1289 * @self: a #GtkLevelBar
1291 * Return the value of the #GtkLevelBar:inverted property.
1293 * Return value: %TRUE if the level bar is inverted
1298 gtk_level_bar_get_inverted (GtkLevelBar *self)
1300 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), FALSE);
1302 return self->priv->inverted;
1306 * gtk_level_bar_set_inverted:
1307 * @self: a #GtkLevelBar
1308 * @inverted: %TRUE to invert the level bar
1310 * Sets the value of the #GtkLevelBar:inverted property.
1315 gtk_level_bar_set_inverted (GtkLevelBar *self,
1318 GtkLevelBarPrivate *priv;
1320 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1324 if (priv->inverted != inverted)
1326 priv->inverted = inverted;
1328 gtk_widget_queue_resize (GTK_WIDGET (self));
1330 g_object_notify (G_OBJECT (self), "inverted");
1335 * gtk_level_bar_remove_offset_value:
1336 * @self: a #GtkLevelBar
1337 * @name: (allow-none): the name of an offset in the bar
1339 * Removes an offset marker previously added with
1340 * gtk_level_bar_add_offset_value().
1345 gtk_level_bar_remove_offset_value (GtkLevelBar *self,
1350 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1352 existing = g_list_find_custom (self->priv->offsets, name, offset_find_func);
1355 gtk_level_bar_offset_free (existing->data);
1356 self->priv->offsets = g_list_delete_link (self->priv->offsets, existing);
1358 gtk_level_bar_update_level_style_classes (self);
1363 * gtk_level_bar_add_offset_value:
1364 * @self: a #GtkLevelBar
1365 * @name: the name of the new offset
1366 * @value: the value for the new offset
1368 * Adds a new offset marker on @self at the position specified by @value.
1369 * When the bar value is in the interval topped by @value (or between @value
1370 * and #GtkLevelBar:max-value in case the offset is the last one on the bar)
1371 * a style class named <literal>level-</literal>@name will be applied
1372 * when rendering the level bar fill.
1373 * If another offset marker named @name exists, its value will be
1374 * replaced by @value.
1379 gtk_level_bar_add_offset_value (GtkLevelBar *self,
1385 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1386 g_return_if_fail (gtk_level_bar_value_in_interval (self, value));
1388 if (!gtk_level_bar_ensure_offset (self, name, value))
1391 gtk_level_bar_update_level_style_classes (self);
1392 name_quark = g_quark_from_string (name);
1393 g_signal_emit (self, signals[SIGNAL_OFFSET_CHANGED], name_quark, name);
1397 * gtk_level_bar_get_offset_value:
1398 * @self: a #GtkLevelBar
1399 * @name: (allow-none): the name of an offset in the bar
1400 * @value: (out): location where to store the value
1402 * Fetches the value specified for the offset marker @name in @self,
1403 * returning %TRUE in case an offset named @name was found.
1405 * Returns: %TRUE if the specified offset is found
1410 gtk_level_bar_get_offset_value (GtkLevelBar *self,
1415 GtkLevelBarOffset *offset = NULL;
1417 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
1419 existing = g_list_find_custom (self->priv->offsets, name, offset_find_func);
1421 offset = existing->data;
1427 *value = offset->value;