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