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