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