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