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