]> Pileus Git - ~andy/gtk/blob - gtk/gtkprogressbar.c
progressbar: Set state flags on draw
[~andy/gtk] / gtk / gtkprogressbar.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include "config.h"
28
29 #include <string.h>
30
31 #include "gtkprogressbar.h"
32 #include "gtkorientableprivate.h"
33 #include "gtkprivate.h"
34 #include "gtkintl.h"
35
36 #include "a11y/gtkprogressbaraccessible.h"
37
38 /**
39  * SECTION:gtkprogressbar
40  * @Short_description: A widget which indicates progress visually
41  * @Title: GtkProgressBar
42  *
43  * The #GtkProgressBar is typically used to display the progress of a long
44  * running operation.  It provides a visual clue that processing
45  * is underway.  The #GtkProgressBar can be used in two different
46  * modes: percentage mode and activity mode.
47  *
48  * When an application can determine how much work needs to take place
49  * (e.g. read a fixed number of bytes from a file) and can monitor its
50  * progress, it can use the #GtkProgressBar in percentage mode and the user
51  * sees a growing bar indicating the percentage of the work that has
52  * been completed.  In this mode, the application is required to call
53  * gtk_progress_bar_set_fraction() periodically to update the progress bar.
54  *
55  * When an application has no accurate way of knowing the amount of work
56  * to do, it can use the #GtkProgressBar in activity mode, which shows
57  * activity by a block moving back and forth within the progress area. In
58  * this mode, the application is required to call gtk_progress_bar_pulse()
59  * periodically to update the progress bar.
60  *
61  * There is quite a bit of flexibility provided to control the appearance
62  * of the #GtkProgressBar.  Functions are provided to control the
63  * orientation of the bar, optional text can be displayed along with
64  * the bar, and the step size used in activity mode can be set.
65  */
66
67 #define MIN_HORIZONTAL_BAR_WIDTH   150
68 #define MIN_HORIZONTAL_BAR_HEIGHT  20
69 #define MIN_VERTICAL_BAR_WIDTH     22
70 #define MIN_VERTICAL_BAR_HEIGHT    80
71
72
73 struct _GtkProgressBarPrivate
74 {
75   gchar         *text;
76
77   gdouble        fraction;
78   gdouble        pulse_fraction;
79
80   gint           activity_pos;
81   guint          activity_blocks;
82   guint          activity_step;
83
84   GtkOrientation orientation;
85
86   guint          activity_dir  : 1;
87   guint          activity_mode : 1;
88   guint          ellipsize     : 3;
89   guint          show_text     : 1;
90   guint          inverted      : 1;
91 };
92
93 enum {
94   PROP_0,
95   PROP_FRACTION,
96   PROP_PULSE_STEP,
97   PROP_ORIENTATION,
98   PROP_INVERTED,
99   PROP_TEXT,
100   PROP_SHOW_TEXT,
101   PROP_ELLIPSIZE
102 };
103
104 static void gtk_progress_bar_set_property         (GObject        *object,
105                                                    guint           prop_id,
106                                                    const GValue   *value,
107                                                    GParamSpec     *pspec);
108 static void gtk_progress_bar_get_property         (GObject        *object,
109                                                    guint           prop_id,
110                                                    GValue         *value,
111                                                    GParamSpec     *pspec);
112 static void gtk_progress_bar_get_preferred_width  (GtkWidget      *widget,
113                                                    gint           *minimum,
114                                                    gint           *natural);
115 static void gtk_progress_bar_get_preferred_height (GtkWidget      *widget,
116                                                    gint           *minimum,
117                                                    gint           *natural);
118
119 static void     gtk_progress_bar_real_update      (GtkProgressBar *progress);
120 static gboolean gtk_progress_bar_draw             (GtkWidget      *widget,
121                                                    cairo_t        *cr);
122 static void     gtk_progress_bar_act_mode_enter   (GtkProgressBar *progress);
123 static void     gtk_progress_bar_finalize         (GObject        *object);
124 static void     gtk_progress_bar_set_orientation  (GtkProgressBar *progress,
125                                                    GtkOrientation  orientation);
126
127 G_DEFINE_TYPE_WITH_CODE (GtkProgressBar, gtk_progress_bar, GTK_TYPE_WIDGET,
128                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
129
130 static void
131 gtk_progress_bar_class_init (GtkProgressBarClass *class)
132 {
133   GObjectClass *gobject_class;
134   GtkWidgetClass *widget_class;
135
136   gobject_class = G_OBJECT_CLASS (class);
137   widget_class = (GtkWidgetClass *) class;
138
139   gobject_class->set_property = gtk_progress_bar_set_property;
140   gobject_class->get_property = gtk_progress_bar_get_property;
141   gobject_class->finalize = gtk_progress_bar_finalize;
142
143   widget_class->draw = gtk_progress_bar_draw;
144   widget_class->get_preferred_width = gtk_progress_bar_get_preferred_width;
145   widget_class->get_preferred_height = gtk_progress_bar_get_preferred_height;
146
147   g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
148
149   g_object_class_install_property (gobject_class,
150                                    PROP_INVERTED,
151                                    g_param_spec_boolean ("inverted",
152                                                          P_("Inverted"),
153                                                          P_("Invert the direction in which the progress bar grows"),
154                                                          FALSE,
155                                                          GTK_PARAM_READWRITE));
156
157   g_object_class_install_property (gobject_class,
158                                    PROP_FRACTION,
159                                    g_param_spec_double ("fraction",
160                                                         P_("Fraction"),
161                                                         P_("The fraction of total work that has been completed"),
162                                                         0.0, 1.0, 0.0,
163                                                         GTK_PARAM_READWRITE));
164
165   g_object_class_install_property (gobject_class,
166                                    PROP_PULSE_STEP,
167                                    g_param_spec_double ("pulse-step",
168                                                         P_("Pulse Step"),
169                                                         P_("The fraction of total progress to move the bouncing block when pulsed"),
170                                                         0.0, 1.0, 0.1,
171                                                         GTK_PARAM_READWRITE));
172
173   g_object_class_install_property (gobject_class,
174                                    PROP_TEXT,
175                                    g_param_spec_string ("text",
176                                                         P_("Text"),
177                                                         P_("Text to be displayed in the progress bar"),
178                                                         NULL,
179                                                         GTK_PARAM_READWRITE));
180
181   /**
182    * GtkProgressBar:show_text:
183    *
184    * Sets whether the progressbar will show text superimposed
185    * over the bar. The shown text is either the value of
186    * the #GtkProgressBar::text property or, if that is %NULL,
187    * the #GtkProgressBar::fraction value, as a percentage.
188    *
189    * To make a progress bar that is styled and sized suitably for containing
190    * text (even if the actual text is blank), set #GtkProgressBar:show-text to
191    * %TRUE and #GtkProgressBar:text to the empty string (not %NULL).
192    *
193    * Since: 3.0
194    */
195   g_object_class_install_property (gobject_class,
196                                    PROP_SHOW_TEXT,
197                                    g_param_spec_boolean ("show-text",
198                                                          P_("Show text"),
199                                                          P_("Whether the progress is shown as text."),
200                                                          FALSE,
201                                                          GTK_PARAM_READWRITE));
202
203   /**
204    * GtkProgressBar:ellipsize:
205    *
206    * The preferred place to ellipsize the string, if the progressbar does
207    * not have enough room to display the entire string, specified as a
208    * #PangoEllisizeMode.
209    *
210    * Note that setting this property to a value other than
211    * %PANGO_ELLIPSIZE_NONE has the side-effect that the progressbar requests
212    * only enough space to display the ellipsis "...". Another means to set a
213    * progressbar's width is gtk_widget_set_size_request().
214    *
215    * Since: 2.6
216    */
217   g_object_class_install_property (gobject_class,
218                                    PROP_ELLIPSIZE,
219                                    g_param_spec_enum ("ellipsize",
220                                                       P_("Ellipsize"),
221                                                       P_("The preferred place to ellipsize the string, if the progress bar "
222                                                          "does not have enough room to display the entire string, if at all."),
223                                                       PANGO_TYPE_ELLIPSIZE_MODE,
224                                                       PANGO_ELLIPSIZE_NONE,
225                                                       GTK_PARAM_READWRITE));
226   gtk_widget_class_install_style_property (widget_class,
227                                            g_param_spec_int ("xspacing",
228                                                              P_("X spacing"),
229                                                              P_("Extra spacing applied to the width of a progress bar."),
230                                                              0, G_MAXINT, 7,
231                                                              G_PARAM_READWRITE));
232   gtk_widget_class_install_style_property (widget_class,
233                                            g_param_spec_int ("yspacing",
234                                                              P_("Y spacing"),
235                                                              P_("Extra spacing applied to the height of a progress bar."),
236                                                              0, G_MAXINT, 7,
237                                                              G_PARAM_READWRITE));
238
239   /**
240    * GtkProgressBar:min-horizontal-bar-width:
241    *
242    * The minimum horizontal width of the progress bar.
243    *
244    * Since: 2.14
245    */
246   gtk_widget_class_install_style_property (widget_class,
247                                            g_param_spec_int ("min-horizontal-bar-width",
248                                                              P_("Minimum horizontal bar width"),
249                                                              P_("The minimum horizontal width of the progress bar"),
250                                                              1, G_MAXINT, MIN_HORIZONTAL_BAR_WIDTH,
251                                                              G_PARAM_READWRITE));
252   /**
253    * GtkProgressBar:min-horizontal-bar-height:
254    *
255    * Minimum horizontal height of the progress bar.
256    *
257    * Since: 2.14
258    */
259   gtk_widget_class_install_style_property (widget_class,
260                                            g_param_spec_int ("min-horizontal-bar-height",
261                                                              P_("Minimum horizontal bar height"),
262                                                              P_("Minimum horizontal height of the progress bar"),
263                                                              1, G_MAXINT, MIN_HORIZONTAL_BAR_HEIGHT,
264                                                              G_PARAM_READWRITE));
265   /**
266    * GtkProgressBar:min-vertical-bar-width:
267    *
268    * The minimum vertical width of the progress bar.
269    *
270    * Since: 2.14
271    */
272   gtk_widget_class_install_style_property (widget_class,
273                                            g_param_spec_int ("min-vertical-bar-width",
274                                                              P_("Minimum vertical bar width"),
275                                                              P_("The minimum vertical width of the progress bar"),
276                                                              1, G_MAXINT, MIN_VERTICAL_BAR_WIDTH,
277                                                              G_PARAM_READWRITE));
278   /**
279    * GtkProgressBar:min-vertical-bar-height:
280    *
281    * The minimum vertical height of the progress bar.
282    *
283    * Since: 2.14
284    */
285   gtk_widget_class_install_style_property (widget_class,
286                                            g_param_spec_int ("min-vertical-bar-height",
287                                                              P_("Minimum vertical bar height"),
288                                                              P_("The minimum vertical height of the progress bar"),
289                                                              1, G_MAXINT, MIN_VERTICAL_BAR_HEIGHT,
290                                                              G_PARAM_READWRITE));
291
292   g_type_class_add_private (class, sizeof (GtkProgressBarPrivate));
293
294   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_PROGRESS_BAR_ACCESSIBLE);
295 }
296
297 static void
298 gtk_progress_bar_init (GtkProgressBar *pbar)
299 {
300   GtkProgressBarPrivate *priv;
301
302   pbar->priv = G_TYPE_INSTANCE_GET_PRIVATE (pbar,
303                                             GTK_TYPE_PROGRESS_BAR,
304                                             GtkProgressBarPrivate);
305   priv = pbar->priv;
306
307   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
308   priv->inverted = FALSE;
309   priv->pulse_fraction = 0.1;
310   priv->activity_pos = 0;
311   priv->activity_dir = 1;
312   priv->activity_step = 3;
313   priv->activity_blocks = 5;
314   priv->ellipsize = PANGO_ELLIPSIZE_NONE;
315   priv->show_text = FALSE;
316
317   priv->text = NULL;
318   priv->fraction = 0.0;
319
320   gtk_widget_set_has_window (GTK_WIDGET (pbar), FALSE);
321 }
322
323 static void
324 gtk_progress_bar_set_property (GObject      *object,
325                                guint         prop_id,
326                                const GValue *value,
327                                GParamSpec   *pspec)
328 {
329   GtkProgressBar *pbar;
330
331   pbar = GTK_PROGRESS_BAR (object);
332
333   switch (prop_id)
334     {
335     case PROP_ORIENTATION:
336       gtk_progress_bar_set_orientation (pbar, g_value_get_enum (value));
337       break;
338     case PROP_INVERTED:
339       gtk_progress_bar_set_inverted (pbar, g_value_get_boolean (value));
340       break;
341     case PROP_FRACTION:
342       gtk_progress_bar_set_fraction (pbar, g_value_get_double (value));
343       break;
344     case PROP_PULSE_STEP:
345       gtk_progress_bar_set_pulse_step (pbar, g_value_get_double (value));
346       break;
347     case PROP_TEXT:
348       gtk_progress_bar_set_text (pbar, g_value_get_string (value));
349       break;
350     case PROP_SHOW_TEXT:
351       gtk_progress_bar_set_show_text (pbar, g_value_get_boolean (value));
352       break;
353     case PROP_ELLIPSIZE:
354       gtk_progress_bar_set_ellipsize (pbar, g_value_get_enum (value));
355       break;
356     default:
357       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358       break;
359     }
360 }
361
362 static void
363 gtk_progress_bar_get_property (GObject      *object,
364                                guint         prop_id,
365                                GValue       *value,
366                                GParamSpec   *pspec)
367 {
368   GtkProgressBar *pbar = GTK_PROGRESS_BAR (object);
369   GtkProgressBarPrivate* priv = pbar->priv;
370
371   switch (prop_id)
372     {
373     case PROP_ORIENTATION:
374       g_value_set_enum (value, priv->orientation);
375       break;
376     case PROP_INVERTED:
377       g_value_set_boolean (value, priv->inverted);
378       break;
379     case PROP_FRACTION:
380       g_value_set_double (value, priv->fraction);
381       break;
382     case PROP_PULSE_STEP:
383       g_value_set_double (value, priv->pulse_fraction);
384       break;
385     case PROP_TEXT:
386       g_value_set_string (value, priv->text);
387       break;
388     case PROP_SHOW_TEXT:
389       g_value_set_boolean (value, priv->show_text);
390       break;
391     case PROP_ELLIPSIZE:
392       g_value_set_enum (value, priv->ellipsize);
393       break;
394     default:
395       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
396       break;
397     }
398 }
399
400 /**
401  * gtk_progress_bar_new:
402  *
403  * Creates a new #GtkProgressBar.
404  *
405  * Returns: a #GtkProgressBar.
406  */
407 GtkWidget*
408 gtk_progress_bar_new (void)
409 {
410   GtkWidget *pbar;
411
412   pbar = g_object_new (GTK_TYPE_PROGRESS_BAR, NULL);
413
414   return pbar;
415 }
416
417 static void
418 gtk_progress_bar_real_update (GtkProgressBar *pbar)
419 {
420   GtkProgressBarPrivate *priv;
421   GtkWidget *widget;
422
423   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
424
425   priv = pbar->priv;
426   widget = GTK_WIDGET (pbar);
427
428   if (priv->activity_mode)
429     {
430       GtkAllocation allocation;
431       GtkStyleContext *context;
432       GtkStateFlags state;
433       GtkBorder padding;
434       gint size;
435
436       gtk_widget_get_allocation (widget, &allocation);
437       context = gtk_widget_get_style_context (widget);
438       state = gtk_widget_get_state_flags (widget);
439       gtk_style_context_get_padding (context, state, &padding);
440
441       /* advance the block */
442       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
443         {
444           /* Update our activity step. */
445           priv->activity_step = allocation.width * priv->pulse_fraction;
446
447           size = MAX (2, allocation.width / priv->activity_blocks);
448
449           if (priv->activity_dir == 0)
450             {
451               priv->activity_pos += priv->activity_step;
452               if (priv->activity_pos + size >= allocation.width - padding.left)
453                 {
454                   priv->activity_pos = allocation.width - padding.left - size;
455                   priv->activity_dir = 1;
456                 }
457             }
458           else
459             {
460               priv->activity_pos -= priv->activity_step;
461               if (priv->activity_pos <= padding.left)
462                 {
463                   priv->activity_pos = padding.left;
464                   priv->activity_dir = 0;
465                 }
466             }
467         }
468       else
469         {
470           /* Update our activity step. */
471           priv->activity_step = allocation.height * priv->pulse_fraction;
472
473           size = MAX (2, allocation.height / priv->activity_blocks);
474
475           if (priv->activity_dir == 0)
476             {
477               priv->activity_pos += priv->activity_step;
478               if (priv->activity_pos + size >= allocation.height - padding.top)
479                 {
480                   priv->activity_pos = allocation.height - padding.top - size;
481                   priv->activity_dir = 1;
482                 }
483             }
484           else
485             {
486               priv->activity_pos -= priv->activity_step;
487               if (priv->activity_pos <= padding.top)
488                 {
489                   priv->activity_pos = padding.top;
490                   priv->activity_dir = 0;
491                 }
492             }
493         }
494     }
495   gtk_widget_queue_draw (widget);
496 }
497
498 static void
499 gtk_progress_bar_finalize (GObject *object)
500 {
501   GtkProgressBar *pbar = GTK_PROGRESS_BAR (object);
502   GtkProgressBarPrivate *priv = pbar->priv;
503
504   g_free (priv->text);
505
506   G_OBJECT_CLASS (gtk_progress_bar_parent_class)->finalize (object);
507 }
508
509 static gchar *
510 get_current_text (GtkProgressBar *pbar)
511 {
512   GtkProgressBarPrivate *priv = pbar->priv;
513
514   if (priv->text)
515     return g_strdup (priv->text);
516   else
517     return g_strdup_printf ("%.0f %%", priv->fraction * 100.0);
518 }
519
520 static void
521 gtk_progress_bar_get_preferred_width (GtkWidget *widget,
522                                       gint      *minimum,
523                                       gint      *natural)
524 {
525   GtkProgressBar *pbar;
526   GtkProgressBarPrivate *priv;
527   GtkStyleContext *style_context;
528   GtkStateFlags state;
529   GtkBorder padding;
530   gchar *buf;
531   PangoRectangle logical_rect;
532   PangoLayout *layout;
533   gint width;
534   gint xspacing;
535   gint min_width;
536
537   g_return_if_fail (GTK_IS_PROGRESS_BAR (widget));
538
539   style_context = gtk_widget_get_style_context (widget);
540   state = gtk_widget_get_state_flags (widget);
541   gtk_style_context_get_padding (style_context, state, &padding);
542
543   gtk_widget_style_get (widget,
544                         "xspacing", &xspacing,
545                         NULL);
546
547   pbar = GTK_PROGRESS_BAR (widget);
548   priv = pbar->priv;
549
550   width = padding.left + padding.right + xspacing;
551
552   if (priv->show_text)
553     {
554       buf = get_current_text (pbar);
555       layout = gtk_widget_create_pango_layout (widget, buf);
556
557       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
558
559       if (priv->ellipsize)
560         {
561           const PangoFontDescription *font_desc;
562           PangoContext *context;
563           PangoFontMetrics *metrics;
564           gint char_width;
565
566           /* The minimum size for ellipsized text is ~ 3 chars */
567           context = pango_layout_get_context (layout);
568           font_desc = gtk_style_context_get_font (style_context, state);
569           metrics = pango_context_get_metrics (context, font_desc, pango_context_get_language (context));
570
571           char_width = pango_font_metrics_get_approximate_char_width (metrics);
572           pango_font_metrics_unref (metrics);
573
574           width += PANGO_PIXELS (char_width) * 3;
575         }
576       else
577         width += logical_rect.width;
578
579       g_object_unref (layout);
580       g_free (buf);
581     }
582
583   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
584     gtk_widget_style_get (widget,
585                           "min-horizontal-bar-width", &min_width,
586                           NULL);
587   else
588     gtk_widget_style_get (widget,
589                           "min-vertical-bar-width", &min_width,
590                           NULL);
591
592   *minimum = *natural = MAX (min_width, width);
593 }
594
595 static void
596 gtk_progress_bar_get_preferred_height (GtkWidget *widget,
597                                        gint      *minimum,
598                                        gint      *natural)
599 {
600   GtkProgressBar *pbar;
601   GtkProgressBarPrivate *priv;
602   GtkStyleContext *context;
603   GtkStateFlags state;
604   GtkBorder padding;
605   gchar *buf;
606   PangoRectangle logical_rect;
607   PangoLayout *layout;
608   gint height;
609   gint yspacing;
610   gint min_height;
611
612   g_return_if_fail (GTK_IS_PROGRESS_BAR (widget));
613
614   context = gtk_widget_get_style_context (widget);
615   state = gtk_widget_get_state_flags (widget);
616   gtk_style_context_get_padding (context, state, &padding);
617
618   gtk_widget_style_get (widget,
619                         "yspacing", &yspacing,
620                         NULL);
621
622   pbar = GTK_PROGRESS_BAR (widget);
623   priv = pbar->priv;
624
625   height = padding.top + padding.bottom + yspacing;
626
627   if (priv->show_text)
628     {
629       buf = get_current_text (pbar);
630       layout = gtk_widget_create_pango_layout (widget, buf);
631
632       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
633
634       height += logical_rect.height;
635
636       g_object_unref (layout);
637       g_free (buf);
638     }
639
640   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
641     gtk_widget_style_get (widget,
642                           "min-horizontal-bar-height", &min_height,
643                           NULL);
644   else
645     gtk_widget_style_get (widget,
646                           "min-vertical-bar-height", &min_height,
647                           NULL);
648
649   *minimum = *natural = MAX (min_height, height);
650 }
651
652 static void
653 gtk_progress_bar_act_mode_enter (GtkProgressBar *pbar)
654 {
655   GtkProgressBarPrivate *priv = pbar->priv;
656   GtkAllocation allocation;
657   GtkStyleContext *context;
658   GtkStateFlags state;
659   GtkBorder padding;
660   GtkWidget *widget = GTK_WIDGET (pbar);
661   GtkOrientation orientation;
662   gboolean inverted;
663
664   context = gtk_widget_get_style_context (widget);
665   state = gtk_widget_get_state_flags (widget);
666   gtk_style_context_get_padding (context, state, &padding);
667
668   orientation = priv->orientation;
669   inverted = priv->inverted;
670   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
671     {
672       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
673         inverted = !inverted;
674     }
675
676   /* calculate start pos */
677
678   if (orientation == GTK_ORIENTATION_HORIZONTAL)
679     {
680       if (!inverted)
681         {
682           priv->activity_pos = padding.left;
683           priv->activity_dir = 0;
684         }
685       else
686         {
687           gtk_widget_get_allocation (widget, &allocation);
688           priv->activity_pos = allocation.width - padding.left -
689                                (allocation.height - padding.top - padding.bottom);
690           priv->activity_dir = 1;
691         }
692     }
693   else
694     {
695       if (!inverted)
696         {
697           priv->activity_pos = padding.top;
698           priv->activity_dir = 0;
699         }
700       else
701         {
702           gtk_widget_get_allocation (widget, &allocation);
703           priv->activity_pos = allocation.height - padding.top -
704                                (allocation.width - padding.left - padding.right);
705           priv->activity_dir = 1;
706         }
707     }
708 }
709
710 static void
711 gtk_progress_bar_get_activity (GtkProgressBar *pbar,
712                                GtkOrientation  orientation,
713                                gint           *offset,
714                                gint           *amount)
715 {
716   GtkProgressBarPrivate *priv = pbar->priv;
717   GtkAllocation allocation;
718   GtkWidget *widget = GTK_WIDGET (pbar);
719
720   *offset = priv->activity_pos;
721
722   gtk_widget_get_allocation (widget, &allocation);
723
724   if (orientation == GTK_ORIENTATION_HORIZONTAL)
725     *amount = MAX (2, allocation.width / priv->activity_blocks);
726   else
727     *amount = MAX (2, allocation.height / priv->activity_blocks);
728 }
729
730 static void
731 gtk_progress_bar_paint_activity (GtkProgressBar *pbar,
732                                  cairo_t        *cr,
733                                  GtkOrientation  orientation,
734                                  gboolean        inverted,
735                                  int             width,
736                                  int             height)
737 {
738   GtkStyleContext *context;
739   GtkStateFlags state;
740   GtkBorder padding;
741   GtkWidget *widget = GTK_WIDGET (pbar);
742   GdkRectangle area;
743
744   context = gtk_widget_get_style_context (widget);
745   state = gtk_widget_get_state_flags (widget);
746   gtk_style_context_get_padding (context, state, &padding);
747
748   if (orientation == GTK_ORIENTATION_HORIZONTAL)
749     {
750       gtk_progress_bar_get_activity (pbar, orientation, &area.x, &area.width);
751       area.y = padding.top;
752       area.height = height - padding.top - padding.bottom;
753     }
754   else
755     {
756       gtk_progress_bar_get_activity (pbar, orientation, &area.y, &area.height);
757       area.x = padding.left;
758       area.width = width - padding.left - padding.right;
759     }
760
761   gtk_style_context_save (context);
762   gtk_style_context_set_state (context, state);
763   gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
764   gtk_style_context_add_class (context, GTK_STYLE_CLASS_PULSE);
765
766   gtk_render_activity (context, cr, area.x, area.y, area.width, area.height);
767
768   gtk_style_context_restore (context);
769 }
770
771 static void
772 gtk_progress_bar_paint_continuous (GtkProgressBar *pbar,
773                                    cairo_t        *cr,
774                                    gint            amount,
775                                    GtkOrientation  orientation,
776                                    gboolean        inverted,
777                                    int             width,
778                                    int             height)
779 {
780   GtkStyleContext *context;
781   GtkStateFlags state;
782   GtkBorder padding;
783   GtkWidget *widget = GTK_WIDGET (pbar);
784   GdkRectangle area;
785
786   if (amount <= 0)
787     return;
788
789   context = gtk_widget_get_style_context (widget);
790   state = gtk_widget_get_state_flags (widget);
791   gtk_style_context_get_padding (context, state, &padding);
792
793   if (orientation == GTK_ORIENTATION_HORIZONTAL)
794     {
795       area.width = amount;
796       area.height = height - padding.top - padding.bottom;
797       area.y = padding.top;
798
799       if (!inverted)
800         area.x = padding.left;
801       else
802         area.x = width - amount - padding.right;
803     }
804   else
805     {
806       area.width = width - padding.left - padding.right;
807       area.height = amount;
808       area.x = padding.left;
809
810       if (!inverted)
811         area.y = padding.top;
812       else
813         area.y = height - amount - padding.bottom;
814     }
815
816   gtk_style_context_save (context);
817   gtk_style_context_set_state (context, state);
818   gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
819
820   gtk_render_activity (context, cr, area.x, area.y, area.width, area.height);
821
822   gtk_style_context_restore (context);
823 }
824
825 static void
826 gtk_progress_bar_paint_text (GtkProgressBar *pbar,
827                              cairo_t        *cr,
828                              gint            offset,
829                              gint            amount,
830                              GtkOrientation  orientation,
831                              gboolean        inverted,
832                              int             width,
833                              int             height)
834 {
835   GtkProgressBarPrivate *priv = pbar->priv;
836   GtkStyleContext *context;
837   GtkStateFlags state;
838   GtkBorder padding;
839   GtkWidget *widget = GTK_WIDGET (pbar);
840   gint x;
841   gint y;
842   gchar *buf;
843   GdkRectangle rect;
844   PangoLayout *layout;
845   PangoRectangle logical_rect;
846   GdkRectangle prelight_clip, start_clip, end_clip;
847   gfloat text_xalign = 0.5;
848   gfloat text_yalign = 0.5;
849
850   context = gtk_widget_get_style_context (widget);
851   state = gtk_widget_get_state_flags (widget);
852   gtk_style_context_get_padding (context, state, &padding);
853
854   if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
855     text_xalign = 1.0 - text_xalign;
856
857   buf = get_current_text (pbar);
858
859   layout = gtk_widget_create_pango_layout (widget, buf);
860   pango_layout_set_ellipsize (layout, priv->ellipsize);
861   if (priv->ellipsize)
862     pango_layout_set_width (layout, width * PANGO_SCALE);
863
864   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
865
866   x = padding.left + 1 + text_xalign * (width - padding.left - padding.right - 2 - logical_rect.width);
867   y = padding.top + 1 + text_yalign * (height - padding.top - padding.bottom - 2 - logical_rect.height);
868
869   rect.x = padding.left;
870   rect.y = padding.top;
871   rect.width = width - padding.left - padding.right;
872   rect.height = height - padding.top - padding.bottom;
873
874   prelight_clip = start_clip = end_clip = rect;
875
876   if (orientation == GTK_ORIENTATION_HORIZONTAL)
877     {
878       if (!inverted)
879         {
880           if (offset != -1)
881             prelight_clip.x = offset;
882           prelight_clip.width = amount;
883           start_clip.width = prelight_clip.x - start_clip.x;
884           end_clip.x = start_clip.x + start_clip.width + prelight_clip.width;
885           end_clip.width -= prelight_clip.width + start_clip.width;
886         }
887       else
888         {
889           if (offset != -1)
890             prelight_clip.x = offset;
891           else
892             prelight_clip.x = rect.x + rect.width - amount;
893           prelight_clip.width = amount;
894           start_clip.width = prelight_clip.x - start_clip.x;
895           end_clip.x = start_clip.x + start_clip.width + prelight_clip.width;
896           end_clip.width -= prelight_clip.width + start_clip.width;
897         }
898     }
899   else
900     {
901       if (!inverted)
902         {
903           if (offset != -1)
904             prelight_clip.y = offset;
905           prelight_clip.height = amount;
906           start_clip.height = prelight_clip.y - start_clip.y;
907           end_clip.y = start_clip.y + start_clip.height + prelight_clip.height;
908           end_clip.height -= prelight_clip.height + start_clip.height;
909         }
910       else
911         {
912           if (offset != -1)
913             prelight_clip.y = offset;
914           else
915             prelight_clip.y = rect.y + rect.height - amount;
916           prelight_clip.height = amount;
917           start_clip.height = prelight_clip.y - start_clip.y;
918           end_clip.y = start_clip.y + start_clip.height + prelight_clip.height;
919           end_clip.height -= prelight_clip.height + start_clip.height;
920         }
921     }
922
923   gtk_style_context_save (context);
924   gtk_style_context_set_state (context, state);
925   gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
926
927   if (start_clip.width > 0 && start_clip.height > 0)
928     {
929       cairo_save (cr);
930       gdk_cairo_rectangle (cr, &start_clip);
931       cairo_clip (cr);
932
933       gtk_render_layout (context, cr, x, y, layout);
934       cairo_restore (cr);
935     }
936
937   if (end_clip.width > 0 && end_clip.height > 0)
938     {
939       cairo_save (cr);
940       gdk_cairo_rectangle (cr, &end_clip);
941       cairo_clip (cr);
942
943       gtk_render_layout (context, cr, x, y, layout);
944       cairo_restore (cr);
945     }
946
947   gtk_style_context_restore (context);
948
949   cairo_save (cr);
950   gdk_cairo_rectangle (cr, &prelight_clip);
951   cairo_clip (cr);
952
953   gtk_style_context_save (context);
954   gtk_style_context_set_state (context, state);
955   gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
956
957   gtk_render_layout (context, cr, x, y, layout);
958
959   gtk_style_context_restore (context);
960   cairo_restore (cr);
961
962   g_object_unref (layout);
963   g_free (buf);
964 }
965
966 static gboolean
967 gtk_progress_bar_draw (GtkWidget      *widget,
968                        cairo_t        *cr)
969 {
970   GtkProgressBar *pbar = GTK_PROGRESS_BAR (widget);
971   GtkProgressBarPrivate *priv = pbar->priv;
972   GtkOrientation orientation;
973   gboolean inverted;
974   GtkStyleContext *context;
975   GtkStateFlags state;
976   GtkBorder padding;
977   int width, height;
978
979   context = gtk_widget_get_style_context (widget);
980   state = gtk_widget_get_state_flags (widget);
981   gtk_style_context_get_padding (context, state, &padding);
982
983   orientation = priv->orientation;
984   inverted = priv->inverted;
985   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
986     {
987       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
988         inverted = !inverted;
989     }
990   width = gtk_widget_get_allocated_width (widget);
991   height = gtk_widget_get_allocated_height (widget);
992
993   gtk_style_context_save (context);
994   gtk_style_context_set_state (context, state);
995   gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
996
997   gtk_render_background (context, cr, 0, 0, width, height);
998   gtk_render_frame (context, cr, 0, 0, width, height);
999
1000   gtk_style_context_restore (context);
1001
1002   if (priv->activity_mode)
1003     {
1004       gtk_progress_bar_paint_activity (pbar, cr,
1005                                        orientation, inverted,
1006                                        width, height);
1007
1008       if (priv->show_text)
1009         {
1010           gint offset;
1011           gint amount;
1012
1013           gtk_progress_bar_get_activity (pbar, orientation, &offset, &amount);
1014           gtk_progress_bar_paint_text (pbar, cr,
1015                                        offset, amount,
1016                                        orientation, inverted,
1017                                        width, height);
1018         }
1019     }
1020   else
1021     {
1022       gint amount;
1023       gint space;
1024
1025       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1026         space = width - padding.left - padding.right;
1027       else
1028         space = height - padding.top - padding.bottom;
1029
1030       amount = space * gtk_progress_bar_get_fraction (pbar);
1031
1032       gtk_progress_bar_paint_continuous (pbar, cr, amount, orientation, inverted, width, height);
1033
1034       if (priv->show_text)
1035         gtk_progress_bar_paint_text (pbar, cr, -1, amount, orientation, inverted, width, height);
1036     }
1037
1038   return FALSE;
1039 }
1040
1041 static void
1042 gtk_progress_bar_set_activity_mode (GtkProgressBar *pbar,
1043                                     gboolean        activity_mode)
1044 {
1045   GtkProgressBarPrivate *priv = pbar->priv;
1046
1047   activity_mode = !!activity_mode;
1048
1049   if (priv->activity_mode != activity_mode)
1050     {
1051       priv->activity_mode = activity_mode;
1052
1053       if (priv->activity_mode)
1054         gtk_progress_bar_act_mode_enter (pbar);
1055
1056       gtk_widget_queue_resize (GTK_WIDGET (pbar));
1057     }
1058 }
1059
1060 /**
1061  * gtk_progress_bar_set_fraction:
1062  * @pbar: a #GtkProgressBar
1063  * @fraction: fraction of the task that's been completed
1064  *
1065  * Causes the progress bar to "fill in" the given fraction
1066  * of the bar. The fraction should be between 0.0 and 1.0,
1067  * inclusive.
1068  *
1069  */
1070 void
1071 gtk_progress_bar_set_fraction (GtkProgressBar *pbar,
1072                                gdouble         fraction)
1073 {
1074   GtkProgressBarPrivate* priv;
1075
1076   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1077
1078   priv = pbar->priv;
1079
1080   priv->fraction = CLAMP(fraction, 0.0, 1.0);
1081   gtk_progress_bar_set_activity_mode (pbar, FALSE);
1082   gtk_progress_bar_real_update (pbar);
1083
1084   g_object_notify (G_OBJECT (pbar), "fraction");
1085 }
1086
1087 /**
1088  * gtk_progress_bar_pulse:
1089  * @pbar: a #GtkProgressBar
1090  *
1091  * Indicates that some progress is made, but you don't know how much.
1092  * Causes the progress bar to enter "activity mode," where a block
1093  * bounces back and forth. Each call to gtk_progress_bar_pulse()
1094  * causes the block to move by a little bit (the amount of movement
1095  * per pulse is determined by gtk_progress_bar_set_pulse_step()).
1096  **/
1097 void
1098 gtk_progress_bar_pulse (GtkProgressBar *pbar)
1099 {
1100   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1101
1102   gtk_progress_bar_set_activity_mode (pbar, TRUE);
1103   gtk_progress_bar_real_update (pbar);
1104 }
1105
1106 /**
1107  * gtk_progress_bar_set_text:
1108  * @pbar: a #GtkProgressBar
1109  * @text: (allow-none): a UTF-8 string, or %NULL
1110  *
1111  * Causes the given @text to appear superimposed on the progress bar.
1112  *
1113  * If @text is %NULL and #GtkProgressBar:show-text is %TRUE, the current
1114  * value of #GtkProgressBar:fraction will be displayed as a percentage.
1115  *
1116  * If @text is non-%NULL and #GtkProgressBar:show-text is %TRUE, the text will
1117  * be displayed. In this case, it will not display the progress percentage.
1118  * If @text is the empty string, the progress bar will still be styled and sized
1119  * suitably for containing text, as long as #GtkProgressBar:show-text is %TRUE.
1120  **/
1121 void
1122 gtk_progress_bar_set_text (GtkProgressBar *pbar,
1123                            const gchar    *text)
1124 {
1125   GtkProgressBarPrivate *priv;
1126
1127   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1128
1129   priv = pbar->priv;
1130
1131   /* Don't notify again if nothing's changed. */
1132   if (g_strcmp0 (priv->text, text) == 0)
1133     return;
1134
1135   g_free (priv->text);
1136   priv->text = g_strdup (text);
1137
1138   gtk_widget_queue_resize (GTK_WIDGET (pbar));
1139
1140   g_object_notify (G_OBJECT (pbar), "text");
1141 }
1142
1143 /**
1144  * gtk_progress_bar_set_show_text:
1145  * @pbar: a #GtkProgressBar
1146  * @show_text: whether to show superimposed text
1147  *
1148  * Sets whether the progressbar will show text superimposed
1149  * over the bar. The shown text is either the value of
1150  * the #GtkProgressBar::text property or, if that is %NULL,
1151  * the #GtkProgressBar::fraction value, as a percentage.
1152  *
1153  * To make a progress bar that is styled and sized suitably for containing
1154  * text (even if the actual text is blank), set #GtkProgressBar:show-text to
1155  * %TRUE and #GtkProgressBar:text to the empty string (not %NULL).
1156  *
1157  * Since: 3.0
1158  */
1159 void
1160 gtk_progress_bar_set_show_text (GtkProgressBar *pbar,
1161                                 gboolean        show_text)
1162 {
1163   GtkProgressBarPrivate *priv;
1164
1165   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1166
1167   priv = pbar->priv;
1168
1169   show_text = !!show_text;
1170
1171   if (priv->show_text != show_text)
1172     {
1173       priv->show_text = show_text;
1174
1175       gtk_widget_queue_resize (GTK_WIDGET (pbar));
1176
1177       g_object_notify (G_OBJECT (pbar), "show-text");
1178     }
1179 }
1180
1181 /**
1182  * gtk_progress_bar_get_show_text:
1183  * @pbar: a #GtkProgressBar
1184  *
1185  * Gets the value of the #GtkProgressBar::show-text property.
1186  * See gtk_progress_bar_set_show_text().
1187  *
1188  * Returns: %TRUE if text is shown in the progress bar
1189  *
1190  * Since: 3.0
1191  */
1192 gboolean
1193 gtk_progress_bar_get_show_text (GtkProgressBar *pbar)
1194 {
1195   g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), FALSE);
1196
1197   return pbar->priv->show_text;
1198 }
1199
1200 /**
1201  * gtk_progress_bar_set_pulse_step:
1202  * @pbar: a #GtkProgressBar
1203  * @fraction: fraction between 0.0 and 1.0
1204  *
1205  * Sets the fraction of total progress bar length to move the
1206  * bouncing block for each call to gtk_progress_bar_pulse().
1207  **/
1208 void
1209 gtk_progress_bar_set_pulse_step (GtkProgressBar *pbar,
1210                                  gdouble         fraction)
1211 {
1212   GtkProgressBarPrivate *priv;
1213
1214   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1215
1216   priv = pbar->priv;
1217
1218   priv->pulse_fraction = fraction;
1219
1220   g_object_notify (G_OBJECT (pbar), "pulse-step");
1221 }
1222
1223 static void
1224 gtk_progress_bar_set_orientation (GtkProgressBar *pbar,
1225                                   GtkOrientation  orientation)
1226 {
1227   GtkProgressBarPrivate *priv = pbar->priv;
1228
1229   if (priv->orientation != orientation)
1230     {
1231       priv->orientation = orientation;
1232       _gtk_orientable_set_style_classes (GTK_ORIENTABLE (pbar));
1233
1234       gtk_widget_queue_resize (GTK_WIDGET (pbar));
1235     }
1236 }
1237
1238 /**
1239  * gtk_progress_bar_set_inverted:
1240  * @pbar: a #GtkProgressBar
1241  * @inverted: %TRUE to invert the progress bar
1242  *
1243  * Progress bars normally grow from top to bottom or left to right.
1244  * Inverted progress bars grow in the opposite direction.
1245  */
1246 void
1247 gtk_progress_bar_set_inverted (GtkProgressBar *pbar,
1248                                gboolean        inverted)
1249 {
1250   GtkProgressBarPrivate *priv;
1251
1252   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1253
1254   priv = pbar->priv;
1255
1256   if (priv->inverted != inverted)
1257     {
1258       priv->inverted = inverted;
1259
1260       gtk_widget_queue_resize (GTK_WIDGET (pbar));
1261
1262       g_object_notify (G_OBJECT (pbar), "inverted");
1263     }
1264 }
1265
1266 /**
1267  * gtk_progress_bar_get_text:
1268  * @pbar: a #GtkProgressBar
1269  *
1270  * Retrieves the text displayed superimposed on the progress bar,
1271  * if any, otherwise %NULL. The return value is a reference
1272  * to the text, not a copy of it, so will become invalid
1273  * if you change the text in the progress bar.
1274  *
1275  * Return value: text, or %NULL; this string is owned by the widget
1276  * and should not be modified or freed.
1277  **/
1278 const gchar*
1279 gtk_progress_bar_get_text (GtkProgressBar *pbar)
1280 {
1281   g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), NULL);
1282
1283   return pbar->priv->text;
1284 }
1285
1286 /**
1287  * gtk_progress_bar_get_fraction:
1288  * @pbar: a #GtkProgressBar
1289  *
1290  * Returns the current fraction of the task that's been completed.
1291  *
1292  * Return value: a fraction from 0.0 to 1.0
1293  **/
1294 gdouble
1295 gtk_progress_bar_get_fraction (GtkProgressBar *pbar)
1296 {
1297   g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), 0);
1298
1299   return pbar->priv->fraction;
1300 }
1301
1302 /**
1303  * gtk_progress_bar_get_pulse_step:
1304  * @pbar: a #GtkProgressBar
1305  *
1306  * Retrieves the pulse step set with gtk_progress_bar_set_pulse_step()
1307  *
1308  * Return value: a fraction from 0.0 to 1.0
1309  **/
1310 gdouble
1311 gtk_progress_bar_get_pulse_step (GtkProgressBar *pbar)
1312 {
1313   g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), 0);
1314
1315   return pbar->priv->pulse_fraction;
1316 }
1317
1318 /**
1319  * gtk_progress_bar_get_inverted:
1320  * @pbar: a #GtkProgressBar
1321  *
1322  * Gets the value set by gtk_progress_bar_set_inverted()
1323  *
1324  * Return value: %TRUE if the progress bar is inverted
1325  */
1326 gboolean
1327 gtk_progress_bar_get_inverted (GtkProgressBar *pbar)
1328 {
1329   g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), FALSE);
1330
1331   return pbar->priv->inverted;
1332 }
1333
1334 /**
1335  * gtk_progress_bar_set_ellipsize:
1336  * @pbar: a #GtkProgressBar
1337  * @mode: a #PangoEllipsizeMode
1338  *
1339  * Sets the mode used to ellipsize (add an ellipsis: "...") the text
1340  * if there is not enough space to render the entire string.
1341  *
1342  * Since: 2.6
1343  **/
1344 void
1345 gtk_progress_bar_set_ellipsize (GtkProgressBar     *pbar,
1346                                 PangoEllipsizeMode  mode)
1347 {
1348   GtkProgressBarPrivate *priv;
1349
1350   g_return_if_fail (GTK_IS_PROGRESS_BAR (pbar));
1351   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE &&
1352                     mode <= PANGO_ELLIPSIZE_END);
1353
1354   priv = pbar->priv;
1355
1356   if ((PangoEllipsizeMode)priv->ellipsize != mode)
1357     {
1358       priv->ellipsize = mode;
1359
1360       g_object_notify (G_OBJECT (pbar), "ellipsize");
1361       gtk_widget_queue_resize (GTK_WIDGET (pbar));
1362     }
1363 }
1364
1365 /**
1366  * gtk_progress_bar_get_ellipsize:
1367  * @pbar: a #GtkProgressBar
1368  *
1369  * Returns the ellipsizing position of the progressbar.
1370  * See gtk_progress_bar_set_ellipsize().
1371  *
1372  * Return value: #PangoEllipsizeMode
1373  *
1374  * Since: 2.6
1375  **/
1376 PangoEllipsizeMode
1377 gtk_progress_bar_get_ellipsize (GtkProgressBar *pbar)
1378 {
1379   g_return_val_if_fail (GTK_IS_PROGRESS_BAR (pbar), PANGO_ELLIPSIZE_NONE);
1380
1381   return pbar->priv->ellipsize;
1382 }