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