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