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