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 level
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.
36 * The default interval of values is between zero and one, but it's possible to
37 * modify the interval using #gtk_level_bar_set_min_value and
38 * #gtk_level_bar_set_max_value. The value will be always drawn in proportion to
39 * the admissible interval, i.e. a value of 15 with a specified interval between
40 * 10 and 20 is equivalent to a value of 0.5 with an interval between 0 and 1.
41 * When #GTK_LEVEL_BAR_MODE_DISCRETE is used, the bar level is rendered
42 * as a finite and number of separated blocks instead of a single one. The number
43 * of blocks that will be rendered is equal to the number of units specified by
44 * the admissible interval.
45 * For instance, to build a bar rendered with five blocks, it's sufficient to
46 * set the minimum value to 0 and the maximum value to 5 after changing the indicator
53 #include "gtkbuildable.h"
55 #include "gtkorientableprivate.h"
56 #include "gtklevelbar.h"
57 #include "gtkmarshalers.h"
58 #include "gtkstylecontext.h"
59 #include "gtktypebuiltins.h"
60 #include "gtkwidget.h"
65 #define DEFAULT_BLOCK_SIZE 3
67 /* these don't make sense outside of GtkLevelBar, so we don't add
69 #define STYLE_CLASS_INDICATOR_CONTINUOUS "indicator-continuous"
70 #define STYLE_CLASS_INDICATOR_DISCRETE "indicator-discrete"
71 #define STYLE_CLASS_FILL_BLOCK "fill-block"
72 #define STYLE_CLASS_EMPTY_FILL_BLOCK "empty-fill-block"
74 static void gtk_level_bar_buildable_init (GtkBuildableIface *iface);
76 G_DEFINE_TYPE_WITH_CODE (GtkLevelBar, gtk_level_bar, GTK_TYPE_WIDGET,
77 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
78 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
79 gtk_level_bar_buildable_init))
87 PROP_ORIENTATION /* overridden */
91 SIGNAL_OFFSET_CHANGED,
95 static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
96 static guint signals[NUM_SIGNALS] = { 0, };
103 struct _GtkLevelBarPrivate {
104 GtkOrientation orientation;
112 GtkLevelBarMode bar_mode;
115 static void gtk_level_bar_set_value_internal (GtkLevelBar *self,
118 static GtkLevelBarOffset *
119 gtk_level_bar_offset_new (const gchar *name,
122 GtkLevelBarOffset *offset = g_slice_new0 (GtkLevelBarOffset);
124 offset->name = g_strdup (name);
125 offset->value = value;
131 gtk_level_bar_offset_free (GtkLevelBarOffset *offset)
133 g_free (offset->name);
134 g_slice_free (GtkLevelBarOffset, offset);
138 offset_find_func (gconstpointer data,
139 gconstpointer user_data)
141 const GtkLevelBarOffset *offset = data;
142 const gchar *name = user_data;
144 return g_strcmp0 (name, offset->name);
148 offset_sort_func (gconstpointer a,
151 const GtkLevelBarOffset *offset_a = a;
152 const GtkLevelBarOffset *offset_b = b;
154 return (offset_a->value > offset_b->value);
158 gtk_level_bar_ensure_offset (GtkLevelBar *self,
163 GtkLevelBarOffset *offset = NULL;
165 existing = g_list_find_custom (self->priv->offsets, name, offset_find_func);
167 offset = existing->data;
169 if (offset && (offset->value == value))
174 gtk_level_bar_offset_free (offset);
175 self->priv->offsets = g_list_delete_link (self->priv->offsets, existing);
178 offset = gtk_level_bar_offset_new (name, value);
179 self->priv->offsets = g_list_insert_sorted (self->priv->offsets, offset, offset_sort_func);
185 gtk_level_bar_value_in_interval (GtkLevelBar *self,
188 return ((value >= self->priv->min_value) &&
189 (value <= self->priv->max_value));
193 gtk_level_bar_get_min_block_size (GtkLevelBar *self,
197 GtkWidget *widget = GTK_WIDGET (self);
198 GtkStyleContext *context = gtk_widget_get_style_context (widget);
199 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
200 GtkBorder border, tmp, tmp2;
201 gint min_width, min_height;
203 gtk_style_context_save (context);
204 gtk_style_context_add_class (context, STYLE_CLASS_FILL_BLOCK);
205 gtk_style_context_get_border (context, flags, &border);
206 gtk_style_context_get_padding (context, flags, &tmp);
207 gtk_style_context_get_margin (context, flags, &tmp2);
208 gtk_style_context_restore (context);
210 gtk_style_context_get_style (context,
211 "min-block-width", &min_width,
212 "min-block-height", &min_height,
215 border.top += tmp.top;
216 border.right += tmp.right;
217 border.bottom += tmp.bottom;
218 border.left += tmp.left;
220 border.top += tmp2.top;
221 border.right += tmp2.right;
222 border.bottom += tmp2.bottom;
223 border.left += tmp2.left;
226 *block_width = MAX (border.left + border.right, min_width);
228 *block_height = MAX (border.top + border.bottom, min_height);
232 gtk_level_bar_get_num_blocks (GtkLevelBar *self)
234 if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_CONTINUOUS)
236 else if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_DISCRETE)
237 return MAX (1, (gint) (round (self->priv->max_value) - round (self->priv->min_value)));
243 gtk_level_bar_get_borders (GtkLevelBar *self,
244 GtkBorder *borders_out)
246 GtkWidget *widget = GTK_WIDGET (self);
247 GtkStyleContext *context = gtk_widget_get_style_context (widget);
248 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
249 GtkBorder border, tmp;
251 gtk_style_context_save (context);
252 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
253 gtk_style_context_get_border (context, flags, &border);
254 gtk_style_context_get_padding (context, flags, &tmp);
255 gtk_style_context_restore (context);
257 border.top += tmp.top;
258 border.right += tmp.right;
259 border.bottom += tmp.bottom;
260 border.left += tmp.left;
263 *borders_out = border;
267 gtk_level_bar_draw_fill_continuous (GtkLevelBar *self,
269 cairo_rectangle_int_t *fill_area)
271 GtkWidget *widget = GTK_WIDGET (self);
272 GtkStyleContext *context = gtk_widget_get_style_context (widget);
273 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
274 cairo_rectangle_int_t base_area, block_area;
275 GtkBorder block_margin;
276 gdouble fill_percentage;
278 gtk_style_context_save (context);
279 gtk_style_context_add_class (context, STYLE_CLASS_FILL_BLOCK);
280 gtk_style_context_get_margin (context, flags, &block_margin);
282 /* render the empty (unfilled) part */
283 base_area = *fill_area;
284 base_area.x += block_margin.left;
285 base_area.y += block_margin.top;
286 base_area.width -= block_margin.left + block_margin.right;
287 base_area.height -= block_margin.top + block_margin.bottom;
289 gtk_style_context_add_class (context, STYLE_CLASS_EMPTY_FILL_BLOCK);
291 gtk_render_background (context, cr, base_area.x, base_area.y,
292 base_area.width, base_area.height);
293 gtk_render_frame (context, cr, base_area.x, base_area.y,
294 base_area.width, base_area.height);
296 /* now render the filled part on top of it */
297 block_area = base_area;
299 fill_percentage = (self->priv->cur_value - self->priv->min_value) /
300 (self->priv->max_value - self->priv->min_value);
302 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
303 block_area.width = (gint) floor (block_area.width * fill_percentage);
305 block_area.height = (gint) floor (block_area.height * fill_percentage);
307 gtk_style_context_remove_class (context, STYLE_CLASS_EMPTY_FILL_BLOCK);
309 gtk_render_background (context, cr, block_area.x, block_area.y,
310 block_area.width, block_area.height);
311 gtk_render_frame (context, cr, block_area.x, block_area.y,
312 block_area.width, block_area.height);
314 gtk_style_context_restore (context);
318 gtk_level_bar_draw_fill_discrete (GtkLevelBar *self,
320 cairo_rectangle_int_t *fill_area)
322 GtkWidget *widget = GTK_WIDGET (self);
323 GtkStyleContext *context = gtk_widget_get_style_context (widget);
324 GtkStateFlags flags = gtk_widget_get_state_flags (widget);
325 gint num_blocks, num_filled, idx;
326 gint block_width, block_height;
327 gint block_draw_width, block_draw_height;
328 GtkBorder block_margin;
329 cairo_rectangle_int_t block_area;
331 gtk_level_bar_get_min_block_size (self, &block_width, &block_height);
333 block_area = *fill_area;
334 num_blocks = gtk_level_bar_get_num_blocks (self);
335 num_filled = (gint) round (self->priv->cur_value) - (gint) round (self->priv->min_value);
337 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
338 block_width = MAX (block_width, (gint) floor (block_area.width / num_blocks));
340 block_height = MAX (block_height, (gint) floor (block_area.height / num_blocks));
342 gtk_style_context_save (context);
343 gtk_style_context_add_class (context, STYLE_CLASS_FILL_BLOCK);
344 gtk_style_context_get_margin (context, flags, &block_margin);
346 block_draw_width = block_width - block_margin.left - block_margin.right;
347 block_draw_height = block_height - block_margin.top - block_margin.bottom;
349 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
351 block_draw_height = MAX (block_draw_height, block_area.height - block_margin.top - block_margin.bottom);
352 block_area.y += block_margin.top;
356 block_draw_width = MAX (block_draw_width, block_area.width - block_margin.left - block_margin.right);
357 block_area.x += block_margin.left;
360 for (idx = 0; idx < num_blocks; idx++)
362 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
363 block_area.x += block_margin.left;
365 block_area.y += block_margin.top;
367 if (idx > num_filled - 1)
368 gtk_style_context_add_class (context, STYLE_CLASS_EMPTY_FILL_BLOCK);
370 gtk_render_background (context, cr,
371 block_area.x, block_area.y,
372 block_draw_width, block_draw_height);
373 gtk_render_frame (context, cr,
374 block_area.x, block_area.y,
375 block_draw_width, block_draw_height);
377 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
378 block_area.x += block_draw_width + block_margin.right;
380 block_area.y += block_draw_height + block_margin.bottom;
383 gtk_style_context_restore (context);
387 gtk_level_bar_draw_fill (GtkLevelBar *self,
390 GtkWidget *widget = GTK_WIDGET (self);
391 GtkBorder trough_borders;
392 cairo_rectangle_int_t fill_area;
394 gtk_level_bar_get_borders (self, &trough_borders);
396 fill_area.x = trough_borders.left;
397 fill_area.y = trough_borders.top;
398 fill_area.width = gtk_widget_get_allocated_width (widget) -
399 trough_borders.left - trough_borders.right;
400 fill_area.height = gtk_widget_get_allocated_height (widget) -
401 trough_borders.top - trough_borders.bottom;
403 if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_CONTINUOUS)
404 gtk_level_bar_draw_fill_continuous (self, cr, &fill_area);
405 else if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_DISCRETE)
406 gtk_level_bar_draw_fill_discrete (self, cr, &fill_area);
410 gtk_level_bar_draw (GtkWidget *widget,
413 GtkLevelBar *self = GTK_LEVEL_BAR (widget);
414 GtkStyleContext *context = gtk_widget_get_style_context (widget);
417 width = gtk_widget_get_allocated_width (widget);
418 height = gtk_widget_get_allocated_height (widget);
420 gtk_style_context_save (context);
421 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
423 gtk_render_background (context, cr, 0, 0, width, height);
424 gtk_render_frame (context, cr, 0, 0, width, height);
426 gtk_style_context_restore (context);
428 gtk_level_bar_draw_fill (self, cr);
434 gtk_level_bar_get_preferred_width (GtkWidget *widget,
438 GtkLevelBar *self = GTK_LEVEL_BAR (widget);
441 gint width, block_width;
443 num_blocks = gtk_level_bar_get_num_blocks (self);
444 gtk_level_bar_get_min_block_size (self, &block_width, NULL);
446 gtk_level_bar_get_borders (self, &borders);
447 width = borders.left + borders.right;
449 if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
450 width += num_blocks * block_width;
452 width += block_width;
461 gtk_level_bar_get_preferred_height (GtkWidget *widget,
465 GtkLevelBar *self = GTK_LEVEL_BAR (widget);
468 gint height, block_height;
470 num_blocks = gtk_level_bar_get_num_blocks (self);
471 gtk_level_bar_get_min_block_size (self, NULL, &block_height);
473 gtk_level_bar_get_borders (self, &borders);
474 height = borders.top + borders.bottom;
476 if (self->priv->orientation == GTK_ORIENTATION_VERTICAL)
477 height += num_blocks * block_height;
479 height += block_height;
488 gtk_level_bar_update_mode_style_classes (GtkLevelBar *self)
490 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (self));
492 if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_CONTINUOUS)
494 gtk_style_context_remove_class (context, STYLE_CLASS_INDICATOR_DISCRETE);
495 gtk_style_context_add_class (context, STYLE_CLASS_INDICATOR_CONTINUOUS);
497 else if (self->priv->bar_mode == GTK_LEVEL_BAR_MODE_DISCRETE)
499 gtk_style_context_remove_class (context, STYLE_CLASS_INDICATOR_CONTINUOUS);
500 gtk_style_context_add_class (context, STYLE_CLASS_INDICATOR_DISCRETE);
505 gtk_level_bar_update_level_style_classes (GtkLevelBar *self)
507 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (self));
508 gdouble value = gtk_level_bar_get_value (self);
509 gchar *offset_style_class, *value_class = NULL;
510 GtkLevelBarOffset *offset, *prev_offset;
513 for (l = self->priv->offsets; l != NULL; l = l->next)
517 offset_style_class = g_strconcat ("level-", offset->name, NULL);
518 gtk_style_context_remove_class (context, offset_style_class);
520 /* find the right offset for our style class */
521 if (((l->prev == NULL) && (value < offset->value)) ||
522 ((l->next == NULL) && (value > offset->value)))
524 value_class = offset_style_class;
526 else if ((l->next != NULL) && (l->prev != NULL))
528 prev_offset = l->prev->data;
529 if ((prev_offset->value <= value) && (value < offset->value))
530 value_class = offset_style_class;
534 g_free (offset_style_class);
538 if (value_class != NULL)
540 gtk_style_context_add_class (context, value_class);
541 g_free (value_class);
546 gtk_level_bar_ensure_offsets_in_range (GtkLevelBar *self)
548 GtkLevelBarOffset *offset;
549 GList *l = self->priv->offsets;
556 if (offset->value < self->priv->min_value)
557 gtk_level_bar_ensure_offset (self, offset->name, self->priv->min_value);
558 else if (offset->value > self->priv->max_value)
559 gtk_level_bar_ensure_offset (self, offset->name, self->priv->max_value);
563 #define OFFSETS_ELEMENT "offsets"
564 #define OFFSET_ELEMENT "offset"
565 #define OFFSET_NAME "name"
566 #define OFFSET_VALUE "value"
574 offset_start_element (GMarkupParseContext *context,
575 const gchar *element_name,
577 const gchar **values,
581 OffsetsParserData *parser_data = user_data;
582 const gchar *name = NULL;
583 const gchar *value_str = NULL;
584 GtkLevelBarOffset *offset;
587 if (strcmp (element_name, OFFSET_ELEMENT) == 0)
589 for (idx = 0; names[idx] != NULL; idx++)
591 if (strcmp (names[idx], OFFSET_NAME) == 0)
593 else if (strcmp (names[idx], OFFSET_VALUE) == 0)
594 value_str = values[idx];
597 if (name && value_str)
599 offset = gtk_level_bar_offset_new (name, strtof (value_str, NULL));
600 parser_data->offsets = g_list_prepend (parser_data->offsets, offset);
603 else if (strcmp (element_name, OFFSETS_ELEMENT) == 0)
609 g_warning ("Unsupported type tag for GtkLevelBar: %s\n",
614 static const GMarkupParser offset_parser =
620 gtk_level_bar_buildable_custom_tag_start (GtkBuildable *buildable,
623 const gchar *tagname,
624 GMarkupParser *parser,
627 OffsetsParserData *parser_data;
632 if (strcmp (tagname, OFFSETS_ELEMENT) != 0)
635 parser_data = g_slice_new0 (OffsetsParserData);
636 parser_data->self = GTK_LEVEL_BAR (buildable);
637 parser_data->offsets = NULL;
639 *parser = offset_parser;
646 gtk_level_bar_buildable_custom_finished (GtkBuildable *buildable,
649 const gchar *tagname,
652 OffsetsParserData *parser_data;
654 GtkLevelBarOffset *offset;
657 if (strcmp (tagname, OFFSETS_ELEMENT) != 0)
660 parser_data = user_data;
661 self = parser_data->self;
663 for (l = parser_data->offsets; l != NULL; l = l->next)
666 gtk_level_bar_add_offset_value (self, offset->name, offset->value);
669 g_list_free_full (parser_data->offsets, (GDestroyNotify) gtk_level_bar_offset_free);
670 g_slice_free (OffsetsParserData, parser_data);
674 gtk_level_bar_buildable_init (GtkBuildableIface *iface)
676 iface->custom_tag_start = gtk_level_bar_buildable_custom_tag_start;
677 iface->custom_finished = gtk_level_bar_buildable_custom_finished;
681 gtk_level_bar_set_orientation (GtkLevelBar *self,
682 GtkOrientation orientation)
684 if (self->priv->orientation != orientation)
686 self->priv->orientation = orientation;
687 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (self));
689 gtk_widget_queue_resize (GTK_WIDGET (self));
694 gtk_level_bar_get_property (GObject *obj,
699 GtkLevelBar *self = GTK_LEVEL_BAR (obj);
704 g_value_set_double (value, gtk_level_bar_get_value (self));
707 g_value_set_double (value, gtk_level_bar_get_min_value (self));
710 g_value_set_double (value, gtk_level_bar_get_max_value (self));
713 g_value_set_enum (value, gtk_level_bar_get_mode (self));
715 case PROP_ORIENTATION:
716 g_value_set_enum (value, self->priv->orientation);
719 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
725 gtk_level_bar_set_property (GObject *obj,
730 GtkLevelBar *self = GTK_LEVEL_BAR (obj);
735 gtk_level_bar_set_value (self, g_value_get_double (value));
738 gtk_level_bar_set_min_value (self, g_value_get_double (value));
741 gtk_level_bar_set_max_value (self, g_value_get_double (value));
744 gtk_level_bar_set_mode (self, g_value_get_enum (value));
746 case PROP_ORIENTATION:
747 gtk_level_bar_set_orientation (self, g_value_get_enum (value));
750 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
756 gtk_level_bar_finalize (GObject *obj)
758 GtkLevelBar *self = GTK_LEVEL_BAR (obj);
760 g_list_free_full (self->priv->offsets, (GDestroyNotify) gtk_level_bar_offset_free);
762 G_OBJECT_CLASS (gtk_level_bar_parent_class)->finalize (obj);
766 gtk_level_bar_class_init (GtkLevelBarClass *klass)
768 GObjectClass *oclass = G_OBJECT_CLASS (klass);
769 GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
771 oclass->get_property = gtk_level_bar_get_property;
772 oclass->set_property = gtk_level_bar_set_property;
773 oclass->finalize = gtk_level_bar_finalize;
775 wclass->draw = gtk_level_bar_draw;
776 wclass->get_preferred_width = gtk_level_bar_get_preferred_width;
777 wclass->get_preferred_height = gtk_level_bar_get_preferred_height;
779 g_object_class_override_property (oclass, PROP_ORIENTATION, "orientation");
781 signals[SIGNAL_OFFSET_CHANGED] =
782 g_signal_new ("offset-changed",
784 G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
785 G_STRUCT_OFFSET (GtkLevelBarClass, offset_changed),
787 _gtk_marshal_VOID__STRING,
794 * The #GtkLevelBar:value property determines the currently
795 * filled value of the level bar.
799 properties[PROP_VALUE] =
800 g_param_spec_double ("value",
801 P_("Currently filled value level"),
802 P_("Currently filled value level of the level bar"),
803 0.0, G_MAXDOUBLE, 0.0,
804 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
806 * GtkLevelBar:min-value:
808 * The #GtkLevelBar:min-value property determines the minimum value of
809 * the interval that can be displayed by the bar.
813 properties[PROP_MIN_VALUE] =
814 g_param_spec_double ("min-value",
815 P_("Minimum value level for the bar"),
816 P_("Minimum value level that can be displayed by the bar"),
817 0.0, G_MAXDOUBLE, 0.0,
818 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
820 * GtkLevelBar:max-value:
822 * The #GtkLevelBar:max-value property determaxes the maximum value of
823 * the interval that can be displayed by the bar.
827 properties[PROP_MAX_VALUE] =
828 g_param_spec_double ("max-value",
829 P_("Maximum value level for the bar"),
830 P_("Maximum value level that can be displayed by the bar"),
831 0.0, G_MAXDOUBLE, 1.0,
832 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
834 * GtkLevelBar:bar-mode:
836 * The #GtkLevelBar:bar-mode property determines the way #GtkLevelBar
837 * interprets the value properties to draw the level fill area.
838 * Specifically, when the value is #GTK_LEVEL_BAR_MODE_CONTINUOUS,
839 * #GtkLevelBar will draw a single block representing the current value in
840 * that area; when the value is #GTK_LEVEL_BAR_MODE_DISCRETE,
841 * the widget will draw a succession of separate blocks filling the
842 * draw area, with the number of blocks being equal to the units separating
843 * the integral roundings of #GtkLevelBar:min-value and #GtkLevelBar:max-value.
847 properties[PROP_MODE] =
848 g_param_spec_enum ("mode",
849 P_("The mode of the value indicator"),
850 P_("The mode of the value indicator displayed by the bar"),
851 GTK_TYPE_LEVEL_BAR_MODE,
852 GTK_LEVEL_BAR_MODE_CONTINUOUS,
855 gtk_widget_class_install_style_property
856 (wclass, g_param_spec_int ("min-block-height",
857 P_("Minimum height for filling blocks"),
858 P_("Minimum height for blocks that fill the bar"),
859 1, G_MAXINT, DEFAULT_BLOCK_SIZE,
861 gtk_widget_class_install_style_property
862 (wclass, g_param_spec_int ("min-block-width",
863 P_("Minimum width for filling blocks"),
864 P_("Minimum width for blocks that fill the bar"),
865 1, G_MAXINT, DEFAULT_BLOCK_SIZE,
868 g_type_class_add_private (klass, sizeof (GtkLevelBarPrivate));
869 g_object_class_install_properties (oclass, LAST_PROPERTY, properties);
873 gtk_level_bar_init (GtkLevelBar *self)
875 GtkStyleContext *context;
877 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_LEVEL_BAR, GtkLevelBarPrivate);
879 context = gtk_widget_get_style_context (GTK_WIDGET (self));
880 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEVEL_BAR);
882 self->priv->cur_value = 0.0;
883 self->priv->min_value = 0.0;
884 self->priv->max_value = 1.0;
886 gtk_level_bar_ensure_offset (self, GTK_LEVEL_BAR_OFFSET_LOW, 0.25);
887 gtk_level_bar_ensure_offset (self, GTK_LEVEL_BAR_OFFSET_HIGH, 0.75);
888 gtk_level_bar_update_level_style_classes (self);
890 self->priv->bar_mode = GTK_LEVEL_BAR_MODE_CONTINUOUS;
891 gtk_level_bar_update_mode_style_classes (self);
893 /* set initial orientation and style classes */
894 self->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
895 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (self));
897 gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
903 * Creates a new #GtkLevelBar.
905 * Returns: a #GtkLevelBar.
910 gtk_level_bar_new (void)
912 return g_object_new (GTK_TYPE_LEVEL_BAR, NULL);
916 * gtk_level_bar_new_for_interval:
917 * @min_value: a positive value
918 * @max_value: a positive value
920 * Utility constructor that creates a new #GtkLevelBar for the specified
923 * Returns: a #GtkLevelBar
928 gtk_level_bar_new_for_interval (gdouble min_value,
931 return g_object_new (GTK_TYPE_LEVEL_BAR,
932 "min-value", min_value,
933 "max-value", max_value,
938 * gtk_level_bar_get_min_value:
939 * @self: a #GtkLevelBar
941 * Returns the value of the #GtkLevelBar:min-value property.
943 * Returns: a positive value
948 gtk_level_bar_get_min_value (GtkLevelBar *self)
950 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
952 return self->priv->min_value;
956 * gtk_level_bar_get_max_value:
957 * @self: a #GtkLevelBar
959 * Returns the value of the #GtkLevelBar:max-value property.
961 * Returns: a positive value
966 gtk_level_bar_get_max_value (GtkLevelBar *self)
968 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
970 return self->priv->max_value;
974 * gtk_level_bar_get_value:
975 * @self: a #GtkLevelBar
977 * Returns the value of the #GtkLevelBar:value property.
979 * Returns: a value in the interval between
980 * #GtkLevelBar:min-value and #GtkLevelBar:max-value
985 gtk_level_bar_get_value (GtkLevelBar *self)
987 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
989 return self->priv->cur_value;
993 gtk_level_bar_set_value_internal (GtkLevelBar *self,
996 self->priv->cur_value = value;
997 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
998 gtk_widget_queue_draw (GTK_WIDGET (self));
1002 * gtk_level_bar_set_min_value:
1003 * @self: a #GtkLevelBar
1004 * @value: a positive value
1006 * Sets the value of the #GtkLevelBar:min-value property.
1011 gtk_level_bar_set_min_value (GtkLevelBar *self,
1014 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1015 g_return_if_fail (value >= 0.0);
1017 if (value != self->priv->min_value)
1019 self->priv->min_value = value;
1020 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIN_VALUE]);
1022 if (self->priv->min_value > self->priv->cur_value)
1023 gtk_level_bar_set_value_internal (self, self->priv->min_value);
1025 gtk_level_bar_update_level_style_classes (self);
1030 * gtk_level_bar_set_max_value:
1031 * @self: a #GtkLevelBar
1032 * @value: a positive value
1034 * Sets the value of the #GtkLevelBar:max-value property.
1039 gtk_level_bar_set_max_value (GtkLevelBar *self,
1042 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1043 g_return_if_fail (value >= 0.0);
1045 if (value != self->priv->max_value)
1047 self->priv->max_value = value;
1048 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAX_VALUE]);
1050 if (self->priv->max_value < self->priv->cur_value)
1051 gtk_level_bar_set_value_internal (self, self->priv->max_value);
1053 gtk_level_bar_ensure_offsets_in_range (self);
1054 gtk_level_bar_update_level_style_classes (self);
1059 * gtk_level_bar_set_value:
1060 * @self: a #GtkLevelBar
1061 * @value: a value in the interval between
1062 * #GtkLevelBar:min-value and #GtkLevelBar:max-value
1064 * Sets the value of the #GtkLevelBar:value property.
1069 gtk_level_bar_set_value (GtkLevelBar *self,
1072 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1074 if (value != self->priv->cur_value)
1076 gtk_level_bar_set_value_internal (self, value);
1077 gtk_level_bar_update_level_style_classes (self);
1082 * gtk_level_bar_get_mode:
1083 * @self: a #GtkLevelBar
1085 * Returns the value of the #GtkLevelBar:bar-mode property
1087 * Returns: a #GtkLevelBarMode
1092 gtk_level_bar_get_mode (GtkLevelBar *self)
1094 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0);
1096 return self->priv->bar_mode;
1100 * gtk_level_bar_set_mode:
1101 * @self: a #GtkLevelBar
1102 * @mode: a #GtkLevelBarMode
1104 * Sets the value of the #GtkLevelBar:bar-mode property
1109 gtk_level_bar_set_mode (GtkLevelBar *self,
1110 GtkLevelBarMode mode)
1112 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1114 if (self->priv->bar_mode != mode)
1116 self->priv->bar_mode = mode;
1117 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODE]);
1119 gtk_level_bar_update_mode_style_classes (self);
1120 gtk_widget_queue_resize (GTK_WIDGET (self));
1125 * gtk_level_bar_remove_offset_value:
1126 * @self: a #GtkLevelBar
1127 * @name: (allow-none): the name of an offset in the bar
1129 * Removes an offset marker previously added with
1130 * gtk_level_bar_add_offset_value().
1135 gtk_level_bar_remove_offset_value (GtkLevelBar *self,
1140 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1142 existing = g_list_find_custom (self->priv->offsets, name, offset_find_func);
1145 gtk_level_bar_offset_free (existing->data);
1146 self->priv->offsets = g_list_delete_link (self->priv->offsets, existing);
1148 gtk_level_bar_update_level_style_classes (self);
1153 * gtk_level_bar_add_offset_value:
1154 * @self: a #GtkLevelBar
1155 * @name: the name of the new offset
1156 * @value: the value for the new offset
1158 * Adds a new offset marker on @self at the position specified by @value.
1159 * When the bar value is in the interval topped by @value (or between @value
1160 * and #GtkLevelBar:max-value in case the offset is the last one on the bar)
1161 * a style class named <literal>level-</literal>@name will be applied
1162 * when rendering the level bar fill.
1163 * If another offset marker named @name exists, its value will be
1164 * replaced by @value.
1169 gtk_level_bar_add_offset_value (GtkLevelBar *self,
1175 g_return_if_fail (GTK_IS_LEVEL_BAR (self));
1176 g_return_if_fail (gtk_level_bar_value_in_interval (self, value));
1178 if (!gtk_level_bar_ensure_offset (self, name, value))
1181 gtk_level_bar_update_level_style_classes (self);
1182 name_quark = g_quark_from_string (name);
1183 g_signal_emit (self, signals[SIGNAL_OFFSET_CHANGED], name_quark, name);
1187 * gtk_level_bar_get_offset_value:
1188 * @self: a #GtkLevelBar
1189 * @name: (allow-none): the name of an offset in the bar
1191 * Returns the value specified for the offset marker @name in @self, or
1192 * zero if it's not found.
1194 * Returns: a value in the interval between
1195 * #GtkLevelBar:min-value and #GtkLevelBar:max-value, or zero.
1200 gtk_level_bar_get_offset_value (GtkLevelBar *self,
1204 GtkLevelBarOffset *offset = NULL;
1206 g_return_val_if_fail (GTK_IS_LEVEL_BAR (self), 0.0);
1208 existing = g_list_find_custom (self->priv->offsets, name, offset_find_func);
1210 offset = existing->data;
1213 return offset->value;