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