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