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