1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
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.
8 *
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.
13 *
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/>.
16 */
17
18 /*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 #include "config.h"
26
27 #include <string.h>
28
29 #include "gtkprogressbar.h"
30 #include "gtkorientableprivate.h"
31 #include "gtkwidgetprivate.h"
32 #include "gtkprivate.h"
33 #include "gtkintl.h"
34 #include "gtkcssshadowsvalueprivate.h"
35 #include "gtkstylecontextprivate.h"
36 #include "gtkcssnodeprivate.h"
37 #include "gtkcssstylepropertyprivate.h"
38 #include "gtkcsscustomgadgetprivate.h"
39 #include "gtkcssnumbervalueprivate.h"
40 #include "gtkprogresstrackerprivate.h"
41
42 #include "a11y/gtkprogressbaraccessible.h"
43
44 #include "fallback-c89.c"
45
46 /**
47 * SECTION:gtkprogressbar
48 * @Short_description: A widget which indicates progress visually
49 * @Title: GtkProgressBar
50 *
51 * The #GtkProgressBar is typically used to display the progress of a long
52 * running operation. It provides a visual clue that processing is underway.
53 * The GtkProgressBar can be used in two different modes: percentage mode
54 * and activity mode.
55 *
56 * When an application can determine how much work needs to take place
57 * (e.g. read a fixed number of bytes from a file) and can monitor its
58 * progress, it can use the GtkProgressBar in percentage mode and the
59 * user sees a growing bar indicating the percentage of the work that
60 * has been completed. In this mode, the application is required to call
61 * gtk_progress_bar_set_fraction() periodically to update the progress bar.
62 *
63 * When an application has no accurate way of knowing the amount of work
64 * to do, it can use the #GtkProgressBar in activity mode, which shows
65 * activity by a block moving back and forth within the progress area. In
66 * this mode, the application is required to call gtk_progress_bar_pulse()
67 * periodically to update the progress bar.
68 *
69 * There is quite a bit of flexibility provided to control the appearance
70 * of the #GtkProgressBar. Functions are provided to control the orientation
71 * of the bar, optional text can be displayed along with the bar, and the
72 * step size used in activity mode can be set.
73 *
74 * # CSS nodes
75 *
76 * |[<!-- language="plain" -->
77 * progressbar[.osd]
78 * ├── [text]
79 * ╰── trough[.empty][.full]
80 * ╰── progress[.pulse]
81 * ]|
82 *
83 * GtkProgressBar has a main CSS node with name progressbar and subnodes with
84 * names text and trough, of which the latter has a subnode named progress. The
85 * text subnode is only present if text is shown. The progress subnode has the
86 * style class .pulse when in activity mode. It gets the style classes .left,
87 * .right, .top or .bottom added when the progress 'touches' the corresponding
88 * end of the GtkProgressBar. The .osd class on the progressbar node is for use
89 * in overlays like the one Epiphany has for page loading progress.
90 */
91
92 #define MIN_HORIZONTAL_BAR_WIDTH 150
93 #define MIN_HORIZONTAL_BAR_HEIGHT 6
94 #define MIN_VERTICAL_BAR_WIDTH 7
95 #define MIN_VERTICAL_BAR_HEIGHT 80
96
97 #define DEFAULT_PULSE_DURATION 250000000
98
99 struct _GtkProgressBarPrivate
100 {
101 gchar *text;
102
103 GtkCssGadget *gadget;
104 GtkCssGadget *text_gadget;
105 GtkCssGadget *trough_gadget;
106 GtkCssGadget *progress_gadget;
107
108 gdouble fraction;
109 gdouble pulse_fraction;
110
111 double activity_pos;
112 guint activity_blocks;
113
114 GtkOrientation orientation;
115
116 guint tick_id;
117 GtkProgressTracker tracker;
118 gint64 pulse1;
119 gint64 pulse2;
120 gdouble last_iteration;
121
122 guint activity_dir : 1;
123 guint activity_mode : 1;
124 guint ellipsize : 3;
125 guint show_text : 1;
126 guint inverted : 1;
127 };
128
129 enum {
130 PROP_0,
131 PROP_FRACTION,
132 PROP_PULSE_STEP,
133 PROP_INVERTED,
134 PROP_TEXT,
135 PROP_SHOW_TEXT,
136 PROP_ELLIPSIZE,
137 PROP_ORIENTATION,
138 NUM_PROPERTIES = PROP_ORIENTATION
139 };
140
141 static GParamSpec *progress_props[NUM_PROPERTIES] = { NULL, };
142
143 static void gtk_progress_bar_set_property (GObject *object,
144 guint prop_id,
145 const GValue *value,
146 GParamSpec *pspec);
147 static void gtk_progress_bar_get_property (GObject *object,
148 guint prop_id,
149 GValue *value,
150 GParamSpec *pspec);
151 static void gtk_progress_bar_size_allocate (GtkWidget *widget,
152 GtkAllocation *allocation);
153 static void gtk_progress_bar_get_preferred_width (GtkWidget *widget,
154 gint *minimum,
155 gint *natural);
156 static void gtk_progress_bar_get_preferred_height (GtkWidget *widget,
157 gint *minimum,
158 gint *natural);
159
160 static gboolean gtk_progress_bar_draw (GtkWidget *widget,
161 cairo_t *cr);
162 static void gtk_progress_bar_act_mode_enter (GtkProgressBar *progress);
163 static void gtk_progress_bar_act_mode_leave (GtkProgressBar *progress);
164 static void gtk_progress_bar_finalize (GObject *object);
165 static void gtk_progress_bar_set_orientation (GtkProgressBar *progress,
166 GtkOrientation orientation);
167 static void gtk_progress_bar_direction_changed (GtkWidget *widget,
168 GtkTextDirection previous_dir);
169 static void gtk_progress_bar_state_flags_changed (GtkWidget *widget,
170 GtkStateFlags previous_state);
171
172 static void gtk_progress_bar_measure (GtkCssGadget *gadget,
173 GtkOrientation orientation,
174 gint for_size,
175 gint *minimum,
176 gint *natural,
177 gint *minimum_baseline,
178 gint *natural_baseline,
179 gpointer data);
180 static void gtk_progress_bar_allocate (GtkCssGadget *gadget,
181 const GtkAllocation *allocation,
182 gint baseline,
183 GtkAllocation *out_clip,
184 gpointer data);
185 static gboolean gtk_progress_bar_render (GtkCssGadget *gadget,
186 cairo_t *cr,
187 gint x,
188 gint y,
189 gint width,
190 gint height,
191 gpointer data);
192 static void gtk_progress_bar_allocate_trough (GtkCssGadget *gadget,
193 const GtkAllocation *allocation,
194 gint baseline,
195 GtkAllocation *out_clip,
196 gpointer data);
197 static void gtk_progress_bar_measure_trough (GtkCssGadget *gadget,
198 GtkOrientation orientation,
199 gint for_size,
200 gint *minimum,
201 gint *natural,
202 gint *minimum_baseline,
203 gint *natural_baseline,
204 gpointer data);
205 static gboolean gtk_progress_bar_render_trough (GtkCssGadget *gadget,
206 cairo_t *cr,
207 gint x,
208 gint y,
209 gint width,
210 gint height,
211 gpointer data);
212 static void gtk_progress_bar_measure_progress (GtkCssGadget *gadget,
213 GtkOrientation orientation,
214 gint for_size,
215 gint *minimum,
216 gint *natural,
217 gint *minimum_baseline,
218 gint *natural_baseline,
219 gpointer data);
220 static void gtk_progress_bar_measure_text (GtkCssGadget *gadget,
221 GtkOrientation orientation,
222 gint for_size,
223 gint *minimum,
224 gint *natural,
225 gint *minimum_baseline,
226 gint *natural_baseline,
227 gpointer data);
228 static gboolean gtk_progress_bar_render_text (GtkCssGadget *gadget,
229 cairo_t *cr,
230 gint x,
231 gint y,
232 gint width,
233 gint height,
234 gpointer data);
235
G_DEFINE_TYPE_WITH_CODE(GtkProgressBar,gtk_progress_bar,GTK_TYPE_WIDGET,G_ADD_PRIVATE (GtkProgressBar)G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,NULL))236 G_DEFINE_TYPE_WITH_CODE (GtkProgressBar, gtk_progress_bar, GTK_TYPE_WIDGET,
237 G_ADD_PRIVATE (GtkProgressBar)
238 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
239
240 static void
241 gtk_progress_bar_class_init (GtkProgressBarClass *class)
242 {
243 GObjectClass *gobject_class;
244 GtkWidgetClass *widget_class;
245
246 gobject_class = G_OBJECT_CLASS (class);
247 widget_class = (GtkWidgetClass *) class;
248
249 gobject_class->set_property = gtk_progress_bar_set_property;
250 gobject_class->get_property = gtk_progress_bar_get_property;
251 gobject_class->finalize = gtk_progress_bar_finalize;
252
253 widget_class->draw = gtk_progress_bar_draw;
254 widget_class->size_allocate = gtk_progress_bar_size_allocate;
255 widget_class->get_preferred_width = gtk_progress_bar_get_preferred_width;
256 widget_class->get_preferred_height = gtk_progress_bar_get_preferred_height;
257 widget_class->direction_changed = gtk_progress_bar_direction_changed;
258 widget_class->state_flags_changed = gtk_progress_bar_state_flags_changed;
259
260 g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
261
262 progress_props[PROP_INVERTED] =
263 g_param_spec_boolean ("inverted",
264 P_("Inverted"),
265 P_("Invert the direction in which the progress bar grows"),
266 FALSE,
267 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
268
269 progress_props[PROP_FRACTION] =
270 g_param_spec_double ("fraction",
271 P_("Fraction"),
272 P_("The fraction of total work that has been completed"),
273 0.0, 1.0,
274 0.0,
275 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
276
277 progress_props[PROP_PULSE_STEP] =
278 g_param_spec_double ("pulse-step",
279 P_("Pulse Step"),
280 P_("The fraction of total progress to move the bouncing block when pulsed"),
281 0.0, 1.0,
282 0.1,
283 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
284
285 progress_props[PROP_TEXT] =
286 g_param_spec_string ("text",
287 P_("Text"),
288 P_("Text to be displayed in the progress bar"),
289 NULL,
290 GTK_PARAM_READWRITE);
291
292 /**
293 * GtkProgressBar:show-text:
294 *
295 * Sets whether the progress bar will show a text in addition
296 * to the bar itself. The shown text is either the value of
297 * the #GtkProgressBar:text property or, if that is %NULL,
298 * the #GtkProgressBar:fraction value, as a percentage.
299 *
300 * To make a progress bar that is styled and sized suitably for
301 * showing text (even if the actual text is blank), set
302 * #GtkProgressBar:show-text to %TRUE and #GtkProgressBar:text
303 * to the empty string (not %NULL).
304 *
305 * Since: 3.0
306 */
307 progress_props[PROP_SHOW_TEXT] =
308 g_param_spec_boolean ("show-text",
309 P_("Show text"),
310 P_("Whether the progress is shown as text."),
311 FALSE,
312 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
313
314 /**
315 * GtkProgressBar:ellipsize:
316 *
317 * The preferred place to ellipsize the string, if the progress bar does
318 * not have enough room to display the entire string, specified as a
319 * #PangoEllipsizeMode.
320 *
321 * Note that setting this property to a value other than
322 * %PANGO_ELLIPSIZE_NONE has the side-effect that the progress bar requests
323 * only enough space to display the ellipsis ("..."). Another means to set a
324 * progress bar's width is gtk_widget_set_size_request().
325 *
326 * Since: 2.6
327 */
328 progress_props[PROP_ELLIPSIZE] =
329 g_param_spec_enum ("ellipsize",
330 P_("Ellipsize"),
331 P_("The preferred place to ellipsize the string, if the progress bar "
332 "does not have enough room to display the entire string, if at all."),
333 PANGO_TYPE_ELLIPSIZE_MODE,
334 PANGO_ELLIPSIZE_NONE,
335 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
336
337 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, progress_props);
338
339 /**
340 * GtkProgressBar:xspacing:
341 *
342 * Extra spacing applied to the width of a progress bar.
343 *
344 * Deprecated: 3.20: Use the standard CSS padding and margins; the
345 * value of this style property is ignored.
346 */
347 gtk_widget_class_install_style_property (widget_class,
348 g_param_spec_int ("xspacing",
349 P_("X spacing"),
350 P_("Extra spacing applied to the width of a progress bar."),
351 0, G_MAXINT, 2,
352 G_PARAM_READWRITE|G_PARAM_DEPRECATED));
353
354 /**
355 * GtkProgressBar:yspacing:
356 *
357 * Extra spacing applied to the height of a progress bar.
358 *
359 * Deprecated: 3.20: Use the standard CSS padding and margins; the
360 * value of this style property is ignored.
361 */
362 gtk_widget_class_install_style_property (widget_class,
363 g_param_spec_int ("yspacing",
364 P_("Y spacing"),
365 P_("Extra spacing applied to the height of a progress bar."),
366 0, G_MAXINT, 2,
367 G_PARAM_READWRITE|G_PARAM_DEPRECATED));
368
369 /**
370 * GtkProgressBar:min-horizontal-bar-width:
371 *
372 * The minimum horizontal width of the progress bar.
373 *
374 * Since: 2.14
375 *
376 * Deprecated: 3.20: Use the standard CSS property min-width.
377 */
378 gtk_widget_class_install_style_property (widget_class,
379 g_param_spec_int ("min-horizontal-bar-width",
380 P_("Minimum horizontal bar width"),
381 P_("The minimum horizontal width of the progress bar"),
382 1, G_MAXINT, MIN_HORIZONTAL_BAR_WIDTH,
383 G_PARAM_READWRITE|G_PARAM_DEPRECATED));
384 /**
385 * GtkProgressBar:min-horizontal-bar-height:
386 *
387 * Minimum horizontal height of the progress bar.
388 *
389 * Since: 2.14
390 *
391 * Deprecated: 3.20: Use the standard CSS property min-height.
392 */
393 gtk_widget_class_install_style_property (widget_class,
394 g_param_spec_int ("min-horizontal-bar-height",
395 P_("Minimum horizontal bar height"),
396 P_("Minimum horizontal height of the progress bar"),
397 1, G_MAXINT, MIN_HORIZONTAL_BAR_HEIGHT,
398 G_PARAM_READWRITE|G_PARAM_DEPRECATED));
399 /**
400 * GtkProgressBar:min-vertical-bar-width:
401 *
402 * The minimum vertical width of the progress bar.
403 *
404 * Since: 2.14
405 *
406 * Deprecated: 3.20: Use the standard CSS property min-width.
407 */
408 gtk_widget_class_install_style_property (widget_class,
409 g_param_spec_int ("min-vertical-bar-width",
410 P_("Minimum vertical bar width"),
411 P_("The minimum vertical width of the progress bar"),
412 1, G_MAXINT, MIN_VERTICAL_BAR_WIDTH,
413 G_PARAM_READWRITE|G_PARAM_DEPRECATED));
414 /**
415 * GtkProgressBar:min-vertical-bar-height:
416 *
417 * The minimum vertical height of the progress bar.
418 *
419 * Since: 2.14
420 *
421 * Deprecated: 3.20: Use the standard CSS property min-height.
422 */
423 gtk_widget_class_install_style_property (widget_class,
424 g_param_spec_int ("min-vertical-bar-height",
425 P_("Minimum vertical bar height"),
426 P_("The minimum vertical height of the progress bar"),
427 1, G_MAXINT, MIN_VERTICAL_BAR_HEIGHT,
428 G_PARAM_READWRITE|G_PARAM_DEPRECATED));
429
430 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_PROGRESS_BAR_ACCESSIBLE);
431 gtk_widget_class_set_css_name (widget_class, "progressbar");
432 }
433
434 static void
update_fraction_classes(GtkProgressBar * pbar)435 update_fraction_classes (GtkProgressBar *pbar)
436 {
437 GtkProgressBarPrivate *priv = pbar->priv;
438 gboolean empty = FALSE;
439 gboolean full = FALSE;
440
441 /* Here we set classes based on fill-level unless we're in activity-mode.
442 */
443
444 if (!priv->activity_mode)
445 {
446 if (priv->fraction <= 0.0)
447 empty = TRUE;
448 else if (priv->fraction >= 1.0)
449 full = TRUE;
450 }
451
452 if (empty)
453 gtk_css_gadget_add_class (priv->trough_gadget, "empty");
454 else
455 gtk_css_gadget_remove_class (priv->trough_gadget, "empty");
456
457 if (full)
458 gtk_css_gadget_add_class (priv->trough_gadget, "full");
459 else
460 gtk_css_gadget_remove_class (priv->trough_gadget, "full");
461 }
462
463 static void
update_node_classes(GtkProgressBar * pbar)464 update_node_classes (GtkProgressBar *pbar)
465 {
466 GtkProgressBarPrivate *priv = pbar->priv;
467 gboolean left = FALSE;
468 gboolean right = FALSE;
469 gboolean top = FALSE;
470 gboolean bottom = FALSE;
471
472 /* Here we set positional classes, depending on which end of the
473 * progressbar the progress touches.
474 */
475
476 if (priv->activity_mode)
477 {
478 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
479 {
480 left = priv->activity_pos <= 0.0;
481 right = priv->activity_pos >= 1.0;
482 }
483 else
484 {
485 top = priv->activity_pos <= 0.0;
486 bottom = priv->activity_pos >= 1.0;
487 }
488 }
489 else /* continuous */
490 {
491 gboolean inverted;
492
493 inverted = priv->inverted;
494 if (gtk_widget_get_direction (GTK_WIDGET (pbar)) == GTK_TEXT_DIR_RTL)
495 {
496 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
497 inverted = !inverted;
498 }
499
500 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
501 {
502 left = !inverted || priv->fraction >= 1.0;
503 right = inverted || priv->fraction >= 1.0;
504 }
505 else
506 {
507 top = !inverted || priv->fraction >= 1.0;
508 bottom = inverted || priv->fraction >= 1.0;
509 }
510 }
511
512 if (left)
513 gtk_css_gadget_add_class (priv->progress_gadget, GTK_STYLE_CLASS_LEFT);
514 else
515 gtk_css_gadget_remove_class (priv->progress_gadget, GTK_STYLE_CLASS_LEFT);
516
517 if (right)
518 gtk_css_gadget_add_class (priv->progress_gadget, GTK_STYLE_CLASS_RIGHT);
519 else
520 gtk_css_gadget_remove_class (priv->progress_gadget, GTK_STYLE_CLASS_RIGHT);
521
522 if (top)
523 gtk_css_gadget_add_class (priv->progress_gadget, GTK_STYLE_CLASS_TOP);
524 else
525 gtk_css_gadget_remove_class (priv->progress_gadget, GTK_STYLE_CLASS_TOP);
526
527 if (bottom)
528 gtk_css_gadget_add_class (priv->progress_gadget, GTK_STYLE_CLASS_BOTTOM);
529 else
530 gtk_css_gadget_remove_class (priv->progress_gadget, GTK_STYLE_CLASS_BOTTOM);
531
532 update_fraction_classes (pbar);
533 }
534
535 static void
update_node_state(GtkProgressBar * pbar)536 update_node_state (GtkProgressBar *pbar)
537 {
538 GtkProgressBarPrivate *priv = pbar->priv;
539 GtkStateFlags state;
540
541 state = gtk_widget_get_state_flags (GTK_WIDGET (pbar));
542
543 gtk_css_gadget_set_state (priv->gadget, state);
544 gtk_css_gadget_set_state (priv->trough_gadget, state);
545 gtk_css_gadget_set_state (priv->progress_gadget, state);
546 if (priv->text_gadget)
547 gtk_css_gadget_set_state (priv->text_gadget, state);
548 }
549
550 static void
gtk_progress_bar_init(GtkProgressBar * pbar)551 gtk_progress_bar_init (GtkProgressBar *pbar)
552 {
553 GtkProgressBarPrivate *priv;
554 GtkCssNode *widget_node;
555
556 pbar->priv = gtk_progress_bar_get_instance_private (pbar);
557 priv = pbar->priv;
558
559 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
560 priv->inverted = FALSE;
561 priv->pulse_fraction = 0.1;
562 priv->activity_pos = 0;
563 priv->activity_dir = 1;
564 priv->activity_blocks = 5;
565 priv->ellipsize = PANGO_ELLIPSIZE_NONE;
566 priv->show_text = FALSE;
567
568 priv->text = NULL;
569 priv->fraction = 0.0;
570
571 gtk_widget_set_has_window (GTK_WIDGET (pbar), FALSE);
572
573 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (pbar));
574
575 widget_node = gtk_widget_get_css_node (GTK_WIDGET (pbar));
576 priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
577 GTK_WIDGET (pbar),
578 gtk_progress_bar_measure,
579 gtk_progress_bar_allocate,
580 gtk_progress_bar_render,
581 NULL,
582 NULL);
583
584 priv->trough_gadget = gtk_css_custom_gadget_new ("trough",
585 GTK_WIDGET (pbar),
586 priv->gadget,
587 NULL,
588 gtk_progress_bar_measure_trough,
589 gtk_progress_bar_allocate_trough,
590 gtk_progress_bar_render_trough,
591 NULL,
592 NULL);
593
594 priv->progress_gadget = gtk_css_custom_gadget_new ("progress",
595 GTK_WIDGET (pbar),
596 priv->trough_gadget,
597 NULL,
598 gtk_progress_bar_measure_progress,
599 NULL,
600 NULL,
601 NULL,
602 NULL);
603
604 update_node_state (pbar);
605 update_node_classes (pbar);
606 }
607
608 static void
gtk_progress_bar_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)609 gtk_progress_bar_set_property (GObject *object,
610 guint prop_id,
611 const GValue *value,
612 GParamSpec *pspec)
613 {
614 GtkProgressBar *pbar;
615
616 pbar = GTK_PROGRESS_BAR (object);
617
618 switch (prop_id)
619 {
620 case PROP_ORIENTATION:
621 gtk_progress_bar_set_orientation (pbar, g_value_get_enum (value));
622 break;
623 case PROP_INVERTED:
624 gtk_progress_bar_set_inverted (pbar, g_value_get_boolean (value));
625 break;
626 case PROP_FRACTION:
627 gtk_progress_bar_set_fraction (pbar, g_value_get_double (value));
628 break;
629 case PROP_PULSE_STEP:
630 gtk_progress_bar_set_pulse_step (pbar, g_value_get_double (value));
631 break;
632 case PROP_TEXT:
633 gtk_progress_bar_set_text (pbar, g_value_get_string (value));
634 break;
635 case PROP_SHOW_TEXT:
636 gtk_progress_bar_set_show_text (pbar, g_value_get_boolean (value));
637 break;
638 case PROP_ELLIPSIZE:
639 gtk_progress_bar_set_ellipsize (pbar, g_value_get_enum (value));
640 break;
641 default:
642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
643 break;
644 }
645 }
646
647 static void
gtk_progress_bar_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)648 gtk_progress_bar_get_property (GObject *object,
649 guint prop_id,
650 GValue *value,
651 GParamSpec *pspec)
652 {
653 GtkProgressBar *pbar = GTK_PROGRESS_BAR (object);
654 GtkProgressBarPrivate* priv = pbar->priv;
655
656 switch (prop_id)
657 {
658 case PROP_ORIENTATION:
659 g_value_set_enum (value, priv->orientation);
660 break;
661 case PROP_INVERTED:
662 g_value_set_boolean (value, priv->inverted);
663 break;
664 case PROP_FRACTION:
665 g_value_set_double (value, priv->fraction);
666 break;
667 case PROP_PULSE_STEP:
668 g_value_set_double (value, priv->pulse_fraction);
669 break;
670 case PROP_TEXT:
671 g_value_set_string (value, priv->text);
672 break;
673 case PROP_SHOW_TEXT:
674 g_value_set_boolean (value, priv->show_text);
675 break;
676 case PROP_ELLIPSIZE:
677 g_value_set_enum (value, priv->ellipsize);
678 break;
679 default:
680 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
681 break;
682 }
683 }
684
685 /**
686 * gtk_progress_bar_new:
687 *
688 * Creates a new #GtkProgressBar.
689 *
690 * Returns: a #GtkProgressBar.
691 */
692 GtkWidget*
gtk_progress_bar_new(void)693 gtk_progress_bar_new (void)
694 {
695 GtkWidget *pbar;
696
697 pbar = g_object_new (GTK_TYPE_PROGRESS_BAR, NULL);
698
699 return pbar;
700 }
701
702 static void
gtk_progress_bar_finalize(GObject * object)703 gtk_progress_bar_finalize (GObject *object)
704 {
705 GtkProgressBar *pbar = GTK_PROGRESS_BAR (object);
706 GtkProgressBarPrivate *priv = pbar->priv;
707
708 if (priv->activity_mode)
709 gtk_progress_bar_act_mode_leave (pbar);
710
711 g_free (priv->text);
712
713 g_clear_object (&priv->text_gadget);
714 g_clear_object (&priv->progress_gadget);
715 g_clear_object (&priv->trough_gadget);
716 g_clear_object (&priv->gadget);
717
718 G_OBJECT_CLASS (gtk_progress_bar_parent_class)->finalize (object);
719 }
720
721 static gchar *
get_current_text(GtkProgressBar * pbar)722 get_current_text (GtkProgressBar *pbar)
723 {
724 GtkProgressBarPrivate *priv = pbar->priv;
725
726 if (priv->text)
727 return g_strdup (priv->text);
728 else
729 return g_strdup_printf (C_("progress bar label", "%.0f %%"), priv->fraction * 100.0);
730 }
731
732 static void
gtk_progress_bar_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)733 gtk_progress_bar_measure (GtkCssGadget *gadget,
734 GtkOrientation orientation,
735 int for_size,
736 int *minimum,
737 int *natural,
738 int *minimum_baseline,
739 int *natural_baseline,
740 gpointer data)
741 {
742 GtkWidget *widget;
743 GtkProgressBar *pbar;
744 GtkProgressBarPrivate *priv;
745 gint text_minimum, text_natural;
746 gint trough_minimum, trough_natural;
747
748 widget = gtk_css_gadget_get_owner (gadget);
749 pbar = GTK_PROGRESS_BAR (widget);
750 priv = pbar->priv;
751
752 if (priv->show_text)
753 gtk_css_gadget_get_preferred_size (priv->text_gadget,
754 orientation,
755 -1,
756 &text_minimum, &text_natural,
757 NULL, NULL);
758 else
759 text_minimum = text_natural = 0;
760
761 gtk_css_gadget_get_preferred_size (priv->trough_gadget,
762 orientation,
763 -1,
764 &trough_minimum, &trough_natural,
765 NULL, NULL);
766
767 if (orientation == GTK_ORIENTATION_HORIZONTAL)
768 {
769 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
770 {
771 *minimum = MAX (text_minimum, trough_minimum);
772 *natural = MAX (text_natural, trough_natural);
773 }
774 else
775 {
776 *minimum = text_minimum + trough_minimum;
777 *natural = text_natural + trough_natural;
778 }
779 }
780 else
781 {
782 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
783 {
784 *minimum = text_minimum + trough_minimum;
785 *natural = text_natural + trough_natural;
786 }
787 else
788 {
789 *minimum = MAX (text_minimum, trough_minimum);
790 *natural = MAX (text_natural, trough_natural);
791 }
792 }
793 }
794
795 static PangoLayout *
gtk_progress_bar_get_layout(GtkProgressBar * pbar)796 gtk_progress_bar_get_layout (GtkProgressBar *pbar)
797 {
798 PangoLayout *layout;
799 gchar *buf;
800 GtkCssStyle *style;
801 PangoAttrList *attrs;
802 PangoFontDescription *desc;
803
804 buf = get_current_text (pbar);
805 layout = gtk_widget_create_pango_layout (GTK_WIDGET (pbar), buf);
806
807 style = gtk_css_node_get_style (gtk_css_gadget_get_node (pbar->priv->text_gadget));
808
809 attrs = gtk_css_style_get_pango_attributes (style);
810 desc = gtk_css_style_get_pango_font (style);
811
812 pango_layout_set_attributes (layout, attrs);
813 pango_layout_set_font_description (layout, desc);
814
815 if (attrs)
816 pango_attr_list_unref (attrs);
817 pango_font_description_free (desc);
818
819 g_free (buf);
820
821 return layout;
822 }
823
824 static void
gtk_progress_bar_measure_text(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)825 gtk_progress_bar_measure_text (GtkCssGadget *gadget,
826 GtkOrientation orientation,
827 int for_size,
828 int *minimum,
829 int *natural,
830 int *minimum_baseline,
831 int *natural_baseline,
832 gpointer data)
833 {
834 GtkWidget *widget;
835 GtkProgressBar *pbar;
836 GtkProgressBarPrivate *priv;
837 PangoLayout *layout;
838 PangoRectangle logical_rect;
839
840 widget = gtk_css_gadget_get_owner (gadget);
841 pbar = GTK_PROGRESS_BAR (widget);
842 priv = pbar->priv;
843
844 layout = gtk_progress_bar_get_layout (pbar);
845
846 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
847
848 if (orientation == GTK_ORIENTATION_HORIZONTAL)
849 {
850 if (priv->ellipsize)
851 {
852 PangoContext *context;
853 PangoFontMetrics *metrics;
854 gint char_width;
855
856 /* The minimum size for ellipsized text is ~ 3 chars */
857 context = pango_layout_get_context (layout);
858 metrics = pango_context_get_metrics (context,
859 pango_layout_get_font_description (layout),
860 pango_context_get_language (context));
861
862 char_width = pango_font_metrics_get_approximate_char_width (metrics);
863 pango_font_metrics_unref (metrics);
864
865 *minimum = PANGO_PIXELS (char_width) * 3;
866 }
867 else
868 *minimum = logical_rect.width;
869
870 *natural = MAX (*minimum, logical_rect.width);
871 }
872 else
873 *minimum = *natural = logical_rect.height;
874
875 g_object_unref (layout);
876 }
877
878 static gint
get_number(GtkCssStyle * style,guint property)879 get_number (GtkCssStyle *style,
880 guint property)
881 {
882 double d = _gtk_css_number_value_get (gtk_css_style_get_value (style, property), 100.0);
883
884 if (d < 1)
885 return ceil (d);
886 else
887 return floor (d);
888 }
889
890 static void
gtk_progress_bar_measure_trough(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)891 gtk_progress_bar_measure_trough (GtkCssGadget *gadget,
892 GtkOrientation orientation,
893 int for_size,
894 int *minimum,
895 int *natural,
896 int *minimum_baseline,
897 int *natural_baseline,
898 gpointer data)
899 {
900 GtkWidget *widget;
901 GtkProgressBarPrivate *priv;
902 GtkCssStyle *style;
903
904 widget = gtk_css_gadget_get_owner (gadget);
905 priv = GTK_PROGRESS_BAR (widget)->priv;
906
907 style = gtk_css_gadget_get_style (gadget);
908 if (orientation == GTK_ORIENTATION_HORIZONTAL)
909 {
910 gdouble min_width;
911
912 min_width = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_MIN_WIDTH), 100.0);
913
914 if (min_width > 0.0)
915 *minimum = 0;
916 else if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
917 gtk_widget_style_get (widget, "min-horizontal-bar-width", minimum, NULL);
918 else
919 gtk_widget_style_get (widget, "min-vertical-bar-width", minimum, NULL);
920 }
921 else
922 {
923 gdouble min_height;
924
925 min_height = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_MIN_HEIGHT), 100.0);
926
927 if (min_height > 0.0)
928 *minimum = 0;
929 else if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
930 gtk_widget_style_get (widget, "min-horizontal-bar-height", minimum, NULL);
931 else
932 gtk_widget_style_get (widget, "min-vertical-bar-height", minimum, NULL);
933 }
934
935 *natural = *minimum;
936
937 if (minimum_baseline)
938 *minimum_baseline = -1;
939 if (natural_baseline)
940 *natural_baseline = -1;
941 }
942
943 static void
gtk_progress_bar_measure_progress(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)944 gtk_progress_bar_measure_progress (GtkCssGadget *gadget,
945 GtkOrientation orientation,
946 int for_size,
947 int *minimum,
948 int *natural,
949 int *minimum_baseline,
950 int *natural_baseline,
951 gpointer data)
952 {
953 GtkWidget *widget;
954 GtkProgressBar *pbar;
955 GtkProgressBarPrivate *priv;
956 GtkCssStyle *style;
957
958 widget = gtk_css_gadget_get_owner (gadget);
959 pbar = GTK_PROGRESS_BAR (widget);
960 priv = pbar->priv;
961
962 style = gtk_css_gadget_get_style (gadget);
963 if (orientation == GTK_ORIENTATION_HORIZONTAL)
964 {
965 gint min_width;
966
967 min_width = get_number (style, GTK_CSS_PROPERTY_MIN_WIDTH);
968
969 if (min_width != 0)
970 *minimum = min_width;
971 else if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
972 *minimum = 0;
973 else
974 gtk_widget_style_get (widget, "min-vertical-bar-width", minimum, NULL);
975 }
976 else
977 {
978 gint min_height;
979
980 min_height = get_number (style, GTK_CSS_PROPERTY_MIN_HEIGHT);
981
982 if (min_height != 0)
983 *minimum = min_height;
984 else if (priv->orientation == GTK_ORIENTATION_VERTICAL)
985 *minimum = 0;
986 else
987 gtk_widget_style_get (widget, "min-horizontal-bar-height", minimum, NULL);
988 }
989
990 *natural = *minimum;
991
992 if (minimum_baseline)
993 *minimum_baseline = -1;
994 if (natural_baseline)
995 *natural_baseline = -1;
996 }
997
998 static void
gtk_progress_bar_size_allocate(GtkWidget * widget,GtkAllocation * allocation)999 gtk_progress_bar_size_allocate (GtkWidget *widget,
1000 GtkAllocation *allocation)
1001 {
1002 GtkAllocation clip;
1003
1004 gtk_widget_set_allocation (widget, allocation);
1005
1006 gtk_css_gadget_allocate (GTK_PROGRESS_BAR (widget)->priv->gadget,
1007 allocation,
1008 gtk_widget_get_allocated_baseline (widget),
1009 &clip);
1010
1011 gtk_widget_set_clip (widget, &clip);
1012 }
1013
1014 static void
gtk_progress_bar_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)1015 gtk_progress_bar_allocate (GtkCssGadget *gadget,
1016 const GtkAllocation *allocation,
1017 int baseline,
1018 GtkAllocation *out_clip,
1019 gpointer data)
1020 {
1021 GtkWidget *widget;
1022 GtkProgressBarPrivate *priv;
1023 gint bar_width, bar_height;
1024 gint text_width, text_height, text_min, text_nat;
1025 GtkAllocation alloc;
1026 GtkAllocation text_clip;
1027
1028 widget = gtk_css_gadget_get_owner (gadget);
1029 priv = GTK_PROGRESS_BAR (widget)->priv;
1030
1031 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1032 {
1033 gtk_css_gadget_get_preferred_size (priv->trough_gadget,
1034 GTK_ORIENTATION_VERTICAL,
1035 -1,
1036 &bar_height, NULL,
1037 NULL, NULL);
1038 bar_width = allocation->width;
1039 }
1040 else
1041 {
1042 gtk_css_gadget_get_preferred_size (priv->trough_gadget,
1043 GTK_ORIENTATION_HORIZONTAL,
1044 -1,
1045 &bar_width, NULL,
1046 NULL, NULL);
1047 bar_height = allocation->height;
1048 }
1049
1050 alloc.x = allocation->x + allocation->width - bar_width;
1051 alloc.y = allocation->y + allocation->height - bar_height;
1052 alloc.width = bar_width;
1053 alloc.height = bar_height;
1054
1055 gtk_css_gadget_allocate (priv->trough_gadget, &alloc, -1, out_clip);
1056
1057 if (!priv->show_text)
1058 return;
1059
1060 gtk_css_gadget_get_preferred_size (priv->text_gadget,
1061 GTK_ORIENTATION_HORIZONTAL,
1062 -1,
1063 &text_min, &text_nat,
1064 NULL, NULL);
1065 gtk_css_gadget_get_preferred_size (priv->text_gadget,
1066 GTK_ORIENTATION_VERTICAL,
1067 -1,
1068 &text_height, NULL,
1069 NULL, NULL);
1070
1071 text_width = CLAMP (text_nat, text_min, allocation->width);
1072
1073 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1074 {
1075 alloc.x = allocation->x + (allocation->width - text_width) / 2;
1076 alloc.y = allocation->y;
1077 alloc.width = text_width;
1078 alloc.height = text_height;
1079 }
1080 else
1081 {
1082 alloc.x = allocation->x + allocation->width - text_width;
1083 alloc.y = allocation->y + (allocation->height - text_height) / 2;
1084 alloc.width = text_width;
1085 alloc.height = text_height;
1086 }
1087
1088 gtk_css_gadget_allocate (priv->text_gadget, &alloc, -1, &text_clip);
1089
1090 gdk_rectangle_union (out_clip, &text_clip, out_clip);
1091 }
1092
1093
1094 static void
gtk_progress_bar_allocate_trough(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)1095 gtk_progress_bar_allocate_trough (GtkCssGadget *gadget,
1096 const GtkAllocation *allocation,
1097 int baseline,
1098 GtkAllocation *out_clip,
1099 gpointer data)
1100 {
1101 GtkWidget *widget;
1102 GtkProgressBarPrivate *priv;
1103 GtkAllocation alloc;
1104 gint width, height;
1105 gboolean inverted;
1106
1107 widget = gtk_css_gadget_get_owner (gadget);
1108 priv = GTK_PROGRESS_BAR (widget)->priv;
1109
1110 inverted = priv->inverted;
1111 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1112 {
1113 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1114 inverted = !inverted;
1115 }
1116
1117 gtk_css_gadget_get_preferred_size (priv->progress_gadget,
1118 GTK_ORIENTATION_VERTICAL,
1119 -1,
1120 &height, NULL,
1121 NULL, NULL);
1122 gtk_css_gadget_get_preferred_size (priv->progress_gadget,
1123 GTK_ORIENTATION_HORIZONTAL,
1124 -1,
1125 &width, NULL,
1126 NULL, NULL);
1127
1128 if (priv->activity_mode)
1129 {
1130 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1131 {
1132 alloc.width = width + (allocation->width - width) / priv->activity_blocks;
1133 alloc.x = allocation->x + priv->activity_pos * (allocation->width - alloc.width);
1134 alloc.y = allocation->y + (allocation->height - height) / 2;
1135 alloc.height = height;
1136 }
1137 else
1138 {
1139
1140 alloc.height = height + (allocation->height - height) / priv->activity_blocks;
1141 alloc.y = allocation->y + priv->activity_pos * (allocation->height - alloc.height);
1142 alloc.x = allocation->x + (allocation->width - width) / 2;
1143 alloc.width = width;
1144 }
1145 }
1146 else
1147 {
1148 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1149 {
1150 alloc.width = width + (allocation->width - width) * priv->fraction;
1151 alloc.height = height;
1152 alloc.y = allocation->y + (allocation->height - height) / 2;
1153
1154 if (!inverted)
1155 alloc.x = allocation->x;
1156 else
1157 alloc.x = allocation->x + allocation->width - alloc.width;
1158 }
1159 else
1160 {
1161 alloc.width = width;
1162 alloc.height = height + (allocation->height - height) * priv->fraction;
1163 alloc.x = allocation->x + (allocation->width - width) / 2;
1164
1165 if (!inverted)
1166 alloc.y = allocation->y;
1167 else
1168 alloc.y = allocation->y + allocation->height - alloc.height;
1169 }
1170 }
1171
1172 gtk_css_gadget_allocate (priv->progress_gadget, &alloc, -1, out_clip);
1173 }
1174
1175 static void
gtk_progress_bar_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)1176 gtk_progress_bar_get_preferred_width (GtkWidget *widget,
1177 gint *minimum,
1178 gint *natural)
1179 {
1180 gtk_css_gadget_get_preferred_size (GTK_PROGRESS_BAR (widget)->priv->gadget,
1181 GTK_ORIENTATION_HORIZONTAL,
1182 -1,
1183 minimum, natural,
1184 NULL, NULL);
1185 }
1186
1187 static void
gtk_progress_bar_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)1188 gtk_progress_bar_get_preferred_height (GtkWidget *widget,
1189 gint *minimum,
1190 gint *natural)
1191 {
1192 gtk_css_gadget_get_preferred_size (GTK_PROGRESS_BAR (widget)->priv->gadget,
1193 GTK_ORIENTATION_VERTICAL,
1194 -1,
1195 minimum, natural,
1196 NULL, NULL);
1197 }
1198
1199 static gboolean
tick_cb(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)1200 tick_cb (GtkWidget *widget,
1201 GdkFrameClock *frame_clock,
1202 gpointer user_data)
1203 {
1204 GtkProgressBar *pbar = GTK_PROGRESS_BAR (widget);
1205 GtkProgressBarPrivate *priv = pbar->priv;
1206 gint64 frame_time;
1207 gdouble iteration, pulse_iterations, current_iterations, fraction;
1208
1209 if (priv->pulse2 == 0 && priv->pulse1 == 0)
1210 return G_SOURCE_CONTINUE;
1211
1212 frame_time = gdk_frame_clock_get_frame_time (frame_clock);
1213 gtk_progress_tracker_advance_frame (&priv->tracker, frame_time);
1214
1215 g_assert (priv->pulse2 > priv->pulse1);
1216
1217 pulse_iterations = (priv->pulse2 - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
1218 current_iterations = (frame_time - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
1219
1220 iteration = gtk_progress_tracker_get_iteration (&priv->tracker);
1221 /* Determine the fraction to move the block from one frame
1222 * to the next when pulse_fraction is how far the block should
1223 * move between two calls to gtk_progress_bar_pulse().
1224 */
1225 fraction = priv->pulse_fraction * (iteration - priv->last_iteration) / MAX (pulse_iterations, current_iterations);
1226 priv->last_iteration = iteration;
1227
1228 if (current_iterations > 3 * pulse_iterations)
1229 {
1230 priv->pulse1 = 0;
1231 return G_SOURCE_CONTINUE;
1232 }
1233
1234 /* advance the block */
1235 if (priv->activity_dir == 0)
1236 {
1237 priv->activity_pos += fraction;
1238 if (priv->activity_pos > 1.0)
1239 {
1240 priv->activity_pos = 1.0;
1241 priv->activity_dir = 1;
1242 }
1243 }
1244 else
1245 {
1246 priv->activity_pos -= fraction;
1247 if (priv->activity_pos <= 0)
1248 {
1249 priv->activity_pos = 0;
1250 priv->activity_dir = 0;
1251 }
1252 }
1253
1254 update_node_classes (pbar);
1255
1256 gtk_widget_queue_allocate (widget);
1257
1258 return G_SOURCE_CONTINUE;
1259 }
1260
1261 static void
gtk_progress_bar_act_mode_enter(GtkProgressBar * pbar)1262 gtk_progress_bar_act_mode_enter (GtkProgressBar *pbar)
1263 {
1264 GtkProgressBarPrivate *priv = pbar->priv;
1265 GtkWidget *widget = GTK_WIDGET (pbar);
1266 gboolean inverted;
1267
1268 gtk_css_gadget_add_class (priv->progress_gadget, GTK_STYLE_CLASS_PULSE);
1269
1270 inverted = priv->inverted;
1271 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1272 {
1273 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1274 inverted = !inverted;
1275 }
1276
1277 /* calculate start pos */
1278 if (!inverted)
1279 {
1280 priv->activity_pos = 0.0;
1281 priv->activity_dir = 0;
1282 }
1283 else
1284 {
1285 priv->activity_pos = 1.0;
1286 priv->activity_dir = 1;
1287 }
1288
1289 update_node_classes (pbar);
1290 /* No fixed schedule for pulses, will adapt after calls to update_pulse. Just
1291 * start the tracker to repeat forever with iterations every second.*/
1292 gtk_progress_tracker_start (&priv->tracker, G_USEC_PER_SEC, 0, INFINITY);
1293 priv->tick_id = gtk_widget_add_tick_callback (widget, tick_cb, NULL, NULL);
1294 priv->pulse2 = 0;
1295 priv->pulse1 = 0;
1296 priv->last_iteration = 0;
1297 }
1298
1299 static void
gtk_progress_bar_act_mode_leave(GtkProgressBar * pbar)1300 gtk_progress_bar_act_mode_leave (GtkProgressBar *pbar)
1301 {
1302 GtkProgressBarPrivate *priv = pbar->priv;
1303
1304 if (priv->tick_id)
1305 gtk_widget_remove_tick_callback (GTK_WIDGET (pbar), priv->tick_id);
1306 priv->tick_id = 0;
1307
1308 gtk_css_gadget_remove_class (priv->progress_gadget, GTK_STYLE_CLASS_PULSE);
1309 update_node_classes (pbar);
1310 }
1311
1312 static gboolean
gtk_progress_bar_render_text(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)1313 gtk_progress_bar_render_text (GtkCssGadget *gadget,
1314 cairo_t *cr,
1315 int x,
1316 int y,
1317 int width,
1318 int height,
1319 gpointer data)
1320 {
1321 GtkWidget *widget;
1322 GtkProgressBar *pbar;
1323 GtkProgressBarPrivate *priv;
1324 GtkStyleContext *context;
1325 PangoLayout *layout;
1326
1327 widget = gtk_css_gadget_get_owner (gadget);
1328 pbar = GTK_PROGRESS_BAR (widget);
1329 priv = pbar->priv;
1330
1331 context = gtk_widget_get_style_context (widget);
1332 gtk_style_context_save_to_node (context, gtk_css_gadget_get_node (gadget));
1333
1334 layout = gtk_progress_bar_get_layout (pbar);
1335 pango_layout_set_ellipsize (layout, priv->ellipsize);
1336 if (priv->ellipsize)
1337 pango_layout_set_width (layout, width * PANGO_SCALE);
1338
1339 gtk_render_layout (context, cr, x, y, layout);
1340
1341 g_object_unref (layout);
1342
1343 gtk_style_context_restore (context);
1344
1345 return FALSE;
1346 }
1347
1348 static gboolean
gtk_progress_bar_render_trough(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)1349 gtk_progress_bar_render_trough (GtkCssGadget *gadget,
1350 cairo_t *cr,
1351 int x,
1352 int y,
1353 int width,
1354 int height,
1355 gpointer data)
1356 {
1357 GtkWidget *widget;
1358 GtkProgressBarPrivate *priv;
1359
1360 widget = gtk_css_gadget_get_owner (gadget);
1361 priv = GTK_PROGRESS_BAR (widget)->priv;
1362
1363 gtk_css_gadget_draw (priv->progress_gadget, cr);
1364
1365 return FALSE;
1366 }
1367
1368 static gboolean
gtk_progress_bar_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)1369 gtk_progress_bar_render (GtkCssGadget *gadget,
1370 cairo_t *cr,
1371 int x,
1372 int y,
1373 int width,
1374 int height,
1375 gpointer data)
1376 {
1377 GtkWidget *widget;
1378 GtkProgressBarPrivate *priv;
1379
1380 widget = gtk_css_gadget_get_owner (gadget);
1381 priv = GTK_PROGRESS_BAR (widget)->priv;
1382
1383 gtk_css_gadget_draw (priv->trough_gadget, cr);
1384 if (priv->show_text)
1385 gtk_css_gadget_draw (priv->text_gadget, cr);
1386
1387 return FALSE;
1388 }
1389
1390 static gboolean
gtk_progress_bar_draw(GtkWidget * widget,cairo_t * cr)1391 gtk_progress_bar_draw (GtkWidget *widget,
1392 cairo_t *cr)
1393 {
1394 GtkProgressBar *pbar = GTK_PROGRESS_BAR (widget);
1395 GtkProgressBarPrivate *priv = pbar->priv;
1396
1397 gtk_css_gadget_draw (priv->gadget, cr);
1398
1399 return FALSE;
1400 }
1401
1402 static void
gtk_progress_bar_set_activity_mode(GtkProgressBar * pbar,gboolean activity_mode)1403 gtk_progress_bar_set_activity_mode (GtkProgressBar *pbar,
1404 gboolean activity_mode)
1405 {
1406 GtkProgressBarPrivate *priv = pbar->priv;
1407
1408 activity_mode = !!activity_mode;
1409
1410 if (priv->activity_mode != activity_mode)
1411 {
1412 priv->activity_mode = activity_mode;
1413
1414 if (priv->activity_mode)
1415 gtk_progress_bar_act_mode_enter (pbar);
1416 else
1417 gtk_progress_bar_act_mode_leave (pbar);
1418
1419 gtk_widget_queue_resize (GTK_WIDGET (pbar));
1420 }
1421 }
1422
1423 /**
1424 * gtk_progress_bar_set_fraction:
1425 * @pbar: a #GtkProgressBar
1426 * @fraction: fraction of the task that’s been completed
1427 *
1428 * Causes the progress bar to “fill in” the given fraction
1429 * of the bar. The fraction should be between 0.0 and 1.0,
1430 * inclusive.
1431 */
1432 void
gtk_progress_bar_set_fraction(GtkProgressBar * pbar,gdouble fraction)1433 gtk_progress_bar_set_fraction (GtkProgressBar *pbar,
1434 gdouble fraction)
1435 {
1436 GtkProgressBarPrivate* priv;
1437
1438 g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1439
1440 priv = pbar->priv;
1441
1442 priv->fraction = CLAMP (fraction, 0.0, 1.0);
1443 gtk_progress_bar_set_activity_mode (pbar, FALSE);
1444 gtk_widget_queue_allocate (GTK_WIDGET (pbar));
1445 update_fraction_classes (pbar);
1446
1447 g_object_notify_by_pspec (G_OBJECT (pbar), progress_props[PROP_FRACTION]);
1448 }
1449
1450 static void
gtk_progress_bar_update_pulse(GtkProgressBar * pbar)1451 gtk_progress_bar_update_pulse (GtkProgressBar *pbar)
1452 {
1453 GtkProgressBarPrivate *priv = pbar->priv;
1454 gint64 pulse_time = g_get_monotonic_time ();
1455
1456 if (priv->pulse2 == pulse_time)
1457 return;
1458
1459 priv->pulse1 = priv->pulse2;
1460 priv->pulse2 = pulse_time;
1461 }
1462
1463 /**
1464 * gtk_progress_bar_pulse:
1465 * @pbar: a #GtkProgressBar
1466 *
1467 * Indicates that some progress has been made, but you don’t know how much.
1468 * Causes the progress bar to enter “activity mode,” where a block
1469 * bounces back and forth. Each call to gtk_progress_bar_pulse()
1470 * causes the block to move by a little bit (the amount of movement
1471 * per pulse is determined by gtk_progress_bar_set_pulse_step()).
1472 */
1473 void
gtk_progress_bar_pulse(GtkProgressBar * pbar)1474 gtk_progress_bar_pulse (GtkProgressBar *pbar)
1475 {
1476 g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1477
1478 gtk_progress_bar_set_activity_mode (pbar, TRUE);
1479 gtk_progress_bar_update_pulse (pbar);
1480 }
1481
1482 /**
1483 * gtk_progress_bar_set_text:
1484 * @pbar: a #GtkProgressBar
1485 * @text: (allow-none): a UTF-8 string, or %NULL
1486 *
1487 * Causes the given @text to appear next to the progress bar.
1488 *
1489 * If @text is %NULL and #GtkProgressBar:show-text is %TRUE, the current
1490 * value of #GtkProgressBar:fraction will be displayed as a percentage.
1491 *
1492 * If @text is non-%NULL and #GtkProgressBar:show-text is %TRUE, the text
1493 * will be displayed. In this case, it will not display the progress
1494 * percentage. If @text is the empty string, the progress bar will still
1495 * be styled and sized suitably for containing text, as long as
1496 * #GtkProgressBar:show-text is %TRUE.
1497 */
1498 void
gtk_progress_bar_set_text(GtkProgressBar * pbar,const gchar * text)1499 gtk_progress_bar_set_text (GtkProgressBar *pbar,
1500 const gchar *text)
1501 {
1502 GtkProgressBarPrivate *priv;
1503
1504 g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1505
1506 priv = pbar->priv;
1507
1508 /* Don't notify again if nothing's changed. */
1509 if (g_strcmp0 (priv->text, text) == 0)
1510 return;
1511
1512 g_free (priv->text);
1513 priv->text = g_strdup (text);
1514
1515 gtk_widget_queue_resize (GTK_WIDGET (pbar));
1516
1517 g_object_notify_by_pspec (G_OBJECT (pbar), progress_props[PROP_TEXT]);
1518 }
1519
1520 static void
gtk_progress_bar_text_style_changed(GtkCssNode * node,GtkCssStyleChange * change,GtkProgressBar * pbar)1521 gtk_progress_bar_text_style_changed (GtkCssNode *node,
1522 GtkCssStyleChange *change,
1523 GtkProgressBar *pbar)
1524 {
1525 if (change == NULL ||
1526 gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS) ||
1527 gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_FONT))
1528 {
1529 gtk_widget_queue_resize (GTK_WIDGET (pbar));
1530 }
1531 }
1532
1533 /**
1534 * gtk_progress_bar_set_show_text:
1535 * @pbar: a #GtkProgressBar
1536 * @show_text: whether to show text
1537 *
1538 * Sets whether the progress bar will show text next to the bar.
1539 * The shown text is either the value of the #GtkProgressBar:text
1540 * property or, if that is %NULL, the #GtkProgressBar:fraction value,
1541 * as a percentage.
1542 *
1543 * To make a progress bar that is styled and sized suitably for containing
1544 * text (even if the actual text is blank), set #GtkProgressBar:show-text to
1545 * %TRUE and #GtkProgressBar:text to the empty string (not %NULL).
1546 *
1547 * Since: 3.0
1548 */
1549 void
gtk_progress_bar_set_show_text(GtkProgressBar * pbar,gboolean show_text)1550 gtk_progress_bar_set_show_text (GtkProgressBar *pbar,
1551 gboolean show_text)
1552 {
1553 GtkProgressBarPrivate *priv;
1554
1555 g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1556
1557 priv = pbar->priv;
1558
1559 show_text = !!show_text;
1560
1561 if (priv->show_text == show_text)
1562 return;
1563
1564 priv->show_text = show_text;
1565
1566 if (show_text)
1567 {
1568 priv->text_gadget = gtk_css_custom_gadget_new ("text",
1569 GTK_WIDGET (pbar),
1570 priv->gadget,
1571 priv->trough_gadget,
1572 gtk_progress_bar_measure_text,
1573 NULL,
1574 gtk_progress_bar_render_text,
1575 NULL,
1576 NULL);
1577 g_signal_connect (gtk_css_gadget_get_node (priv->text_gadget), "style-changed",
1578 G_CALLBACK (gtk_progress_bar_text_style_changed), pbar);
1579
1580 update_node_state (pbar);
1581 }
1582 else
1583 {
1584 if (priv->text_gadget)
1585 gtk_css_node_set_parent (gtk_css_gadget_get_node (priv->text_gadget), NULL);
1586 g_clear_object (&priv->text_gadget);
1587 }
1588
1589 gtk_widget_queue_resize (GTK_WIDGET (pbar));
1590
1591 g_object_notify_by_pspec (G_OBJECT (pbar), progress_props[PROP_SHOW_TEXT]);
1592 }
1593
1594 /**
1595 * gtk_progress_bar_get_show_text:
1596 * @pbar: a #GtkProgressBar
1597 *
1598 * Gets the value of the #GtkProgressBar:show-text property.
1599 * See gtk_progress_bar_set_show_text().
1600 *
1601 * Returns: %TRUE if text is shown in the progress bar
1602 *
1603 * Since: 3.0
1604 */
1605 gboolean
gtk_progress_bar_get_show_text(GtkProgressBar * pbar)1606 gtk_progress_bar_get_show_text (GtkProgressBar *pbar)
1607 {
1608 g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), FALSE);
1609
1610 return pbar->priv->show_text;
1611 }
1612
1613 /**
1614 * gtk_progress_bar_set_pulse_step:
1615 * @pbar: a #GtkProgressBar
1616 * @fraction: fraction between 0.0 and 1.0
1617 *
1618 * Sets the fraction of total progress bar length to move the
1619 * bouncing block for each call to gtk_progress_bar_pulse().
1620 */
1621 void
gtk_progress_bar_set_pulse_step(GtkProgressBar * pbar,gdouble fraction)1622 gtk_progress_bar_set_pulse_step (GtkProgressBar *pbar,
1623 gdouble fraction)
1624 {
1625 GtkProgressBarPrivate *priv;
1626
1627 g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1628
1629 priv = pbar->priv;
1630
1631 priv->pulse_fraction = fraction;
1632
1633 g_object_notify_by_pspec (G_OBJECT (pbar), progress_props[PROP_PULSE_STEP]);
1634 }
1635
1636 static void
gtk_progress_bar_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)1637 gtk_progress_bar_direction_changed (GtkWidget *widget,
1638 GtkTextDirection previous_dir)
1639 {
1640 GtkProgressBar *pbar = GTK_PROGRESS_BAR (widget);
1641
1642 update_node_classes (pbar);
1643 update_node_state (pbar);
1644
1645 GTK_WIDGET_CLASS (gtk_progress_bar_parent_class)->direction_changed (widget, previous_dir);
1646 }
1647
1648 static void
gtk_progress_bar_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)1649 gtk_progress_bar_state_flags_changed (GtkWidget *widget,
1650 GtkStateFlags previous_state)
1651 {
1652 GtkProgressBar *pbar = GTK_PROGRESS_BAR (widget);
1653
1654 update_node_state (pbar);
1655
1656 GTK_WIDGET_CLASS (gtk_progress_bar_parent_class)->state_flags_changed (widget, previous_state);
1657 }
1658
1659 static void
gtk_progress_bar_set_orientation(GtkProgressBar * pbar,GtkOrientation orientation)1660 gtk_progress_bar_set_orientation (GtkProgressBar *pbar,
1661 GtkOrientation orientation)
1662 {
1663 GtkProgressBarPrivate *priv = pbar->priv;
1664
1665 if (priv->orientation == orientation)
1666 return;
1667
1668 priv->orientation = orientation;
1669
1670 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (pbar));
1671 update_node_classes (pbar);
1672 gtk_widget_queue_resize (GTK_WIDGET (pbar));
1673
1674 g_object_notify (G_OBJECT (pbar), "orientation");
1675 }
1676
1677 /**
1678 * gtk_progress_bar_set_inverted:
1679 * @pbar: a #GtkProgressBar
1680 * @inverted: %TRUE to invert the progress bar
1681 *
1682 * Progress bars normally grow from top to bottom or left to right.
1683 * Inverted progress bars grow in the opposite direction.
1684 */
1685 void
gtk_progress_bar_set_inverted(GtkProgressBar * pbar,gboolean inverted)1686 gtk_progress_bar_set_inverted (GtkProgressBar *pbar,
1687 gboolean inverted)
1688 {
1689 GtkProgressBarPrivate *priv;
1690
1691 g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1692
1693 priv = pbar->priv;
1694
1695 if (priv->inverted == inverted)
1696 return;
1697
1698 priv->inverted = inverted;
1699
1700 update_node_classes (pbar);
1701 gtk_widget_queue_resize (GTK_WIDGET (pbar));
1702
1703 g_object_notify_by_pspec (G_OBJECT (pbar), progress_props[PROP_INVERTED]);
1704 }
1705
1706 /**
1707 * gtk_progress_bar_get_text:
1708 * @pbar: a #GtkProgressBar
1709 *
1710 * Retrieves the text that is displayed with the progress bar,
1711 * if any, otherwise %NULL. The return value is a reference
1712 * to the text, not a copy of it, so will become invalid
1713 * if you change the text in the progress bar.
1714 *
1715 * Returns: (nullable): text, or %NULL; this string is owned by the widget
1716 * and should not be modified or freed.
1717 */
1718 const gchar*
gtk_progress_bar_get_text(GtkProgressBar * pbar)1719 gtk_progress_bar_get_text (GtkProgressBar *pbar)
1720 {
1721 g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), NULL);
1722
1723 return pbar->priv->text;
1724 }
1725
1726 /**
1727 * gtk_progress_bar_get_fraction:
1728 * @pbar: a #GtkProgressBar
1729 *
1730 * Returns the current fraction of the task that’s been completed.
1731 *
1732 * Returns: a fraction from 0.0 to 1.0
1733 */
1734 gdouble
gtk_progress_bar_get_fraction(GtkProgressBar * pbar)1735 gtk_progress_bar_get_fraction (GtkProgressBar *pbar)
1736 {
1737 g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), 0);
1738
1739 return pbar->priv->fraction;
1740 }
1741
1742 /**
1743 * gtk_progress_bar_get_pulse_step:
1744 * @pbar: a #GtkProgressBar
1745 *
1746 * Retrieves the pulse step set with gtk_progress_bar_set_pulse_step().
1747 *
1748 * Returns: a fraction from 0.0 to 1.0
1749 */
1750 gdouble
gtk_progress_bar_get_pulse_step(GtkProgressBar * pbar)1751 gtk_progress_bar_get_pulse_step (GtkProgressBar *pbar)
1752 {
1753 g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), 0);
1754
1755 return pbar->priv->pulse_fraction;
1756 }
1757
1758 /**
1759 * gtk_progress_bar_get_inverted:
1760 * @pbar: a #GtkProgressBar
1761 *
1762 * Gets the value set by gtk_progress_bar_set_inverted().
1763 *
1764 * Returns: %TRUE if the progress bar is inverted
1765 */
1766 gboolean
gtk_progress_bar_get_inverted(GtkProgressBar * pbar)1767 gtk_progress_bar_get_inverted (GtkProgressBar *pbar)
1768 {
1769 g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), FALSE);
1770
1771 return pbar->priv->inverted;
1772 }
1773
1774 /**
1775 * gtk_progress_bar_set_ellipsize:
1776 * @pbar: a #GtkProgressBar
1777 * @mode: a #PangoEllipsizeMode
1778 *
1779 * Sets the mode used to ellipsize (add an ellipsis: "...") the
1780 * text if there is not enough space to render the entire string.
1781 *
1782 * Since: 2.6
1783 */
1784 void
gtk_progress_bar_set_ellipsize(GtkProgressBar * pbar,PangoEllipsizeMode mode)1785 gtk_progress_bar_set_ellipsize (GtkProgressBar *pbar,
1786 PangoEllipsizeMode mode)
1787 {
1788 GtkProgressBarPrivate *priv;
1789
1790 g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1791 g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE &&
1792 mode <= PANGO_ELLIPSIZE_END);
1793
1794 priv = pbar->priv;
1795
1796 if ((PangoEllipsizeMode)priv->ellipsize != mode)
1797 {
1798 priv->ellipsize = mode;
1799
1800 g_object_notify_by_pspec (G_OBJECT (pbar), progress_props[PROP_ELLIPSIZE]);
1801 gtk_widget_queue_resize (GTK_WIDGET (pbar));
1802 }
1803 }
1804
1805 /**
1806 * gtk_progress_bar_get_ellipsize:
1807 * @pbar: a #GtkProgressBar
1808 *
1809 * Returns the ellipsizing position of the progress bar.
1810 * See gtk_progress_bar_set_ellipsize().
1811 *
1812 * Returns: #PangoEllipsizeMode
1813 *
1814 * Since: 2.6
1815 */
1816 PangoEllipsizeMode
gtk_progress_bar_get_ellipsize(GtkProgressBar * pbar)1817 gtk_progress_bar_get_ellipsize (GtkProgressBar *pbar)
1818 {
1819 g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), PANGO_ELLIPSIZE_NONE);
1820
1821 return pbar->priv->ellipsize;
1822 }
1823