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