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