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