]> Pileus Git - ~andy/gtk/blob - gtk/gtkprogress.c
Include "config.h" instead of <config.h> Command used: find -name
[~andy/gtk] / gtk / gtkprogress.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 #include <math.h>
29 #include <string.h>
30 #undef GTK_DISABLE_DEPRECATED
31 #include "gtkprogress.h" 
32 #include "gtkprivate.h" 
33 #include "gtkintl.h"
34 #include "gtkalias.h"
35
36 #define EPSILON  1e-5
37 #define DEFAULT_FORMAT "%P %%"
38
39 enum {
40   PROP_0,
41   PROP_ACTIVITY_MODE,
42   PROP_SHOW_TEXT,
43   PROP_TEXT_XALIGN,
44   PROP_TEXT_YALIGN
45 };
46
47
48 static void gtk_progress_set_property    (GObject          *object,
49                                           guint             prop_id,
50                                           const GValue     *value,
51                                           GParamSpec       *pspec);
52 static void gtk_progress_get_property    (GObject          *object,
53                                           guint             prop_id,
54                                           GValue           *value,
55                                           GParamSpec       *pspec);
56 static void gtk_progress_destroy         (GtkObject        *object);
57 static void gtk_progress_finalize        (GObject          *object);
58 static void gtk_progress_realize         (GtkWidget        *widget);
59 static gboolean gtk_progress_expose      (GtkWidget        *widget,
60                                           GdkEventExpose   *event);
61 static void gtk_progress_size_allocate   (GtkWidget        *widget,
62                                           GtkAllocation    *allocation);
63 static void gtk_progress_create_pixmap   (GtkProgress      *progress);
64 static void gtk_progress_value_changed   (GtkAdjustment    *adjustment,
65                                           GtkProgress      *progress);
66 static void gtk_progress_changed         (GtkAdjustment    *adjustment,
67                                           GtkProgress      *progress);
68
69 G_DEFINE_TYPE (GtkProgress, gtk_progress, GTK_TYPE_WIDGET)
70
71 static void
72 gtk_progress_class_init (GtkProgressClass *class)
73 {
74   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
75   GtkObjectClass *object_class;
76   GtkWidgetClass *widget_class;
77
78   object_class = (GtkObjectClass *) class;
79   widget_class = (GtkWidgetClass *) class;
80
81   gobject_class->finalize = gtk_progress_finalize;
82
83   gobject_class->set_property = gtk_progress_set_property;
84   gobject_class->get_property = gtk_progress_get_property;
85   object_class->destroy = gtk_progress_destroy;
86
87   widget_class->realize = gtk_progress_realize;
88   widget_class->expose_event = gtk_progress_expose;
89   widget_class->size_allocate = gtk_progress_size_allocate;
90
91   /* to be overridden */
92   class->paint = NULL;
93   class->update = NULL;
94   class->act_mode_enter = NULL;
95   
96   g_object_class_install_property (gobject_class,
97                                    PROP_ACTIVITY_MODE,
98                                    g_param_spec_boolean ("activity-mode",
99                                                          P_("Activity mode"),
100                                                          P_("If TRUE, the GtkProgress is in activity mode, meaning that it signals "
101                                                             "something is happening, but not how much of the activity is finished. "
102                                                             "This is used when you're doing something but don't know how long it will take."),
103                                                          FALSE,
104                                                          GTK_PARAM_READWRITE));
105   g_object_class_install_property (gobject_class,
106                                    PROP_SHOW_TEXT,
107                                    g_param_spec_boolean ("show-text",
108                                                          P_("Show text"),
109                                                          P_("Whether the progress is shown as text."),
110                                                          FALSE,
111                                                          GTK_PARAM_READWRITE));
112   g_object_class_install_property (gobject_class,
113                                    PROP_TEXT_XALIGN,
114                                    g_param_spec_float ("text-xalign",
115                                                        P_("Text x alignment"),
116                                                        P_("The horizontal text alignment, from 0 (left) to 1 (right). Reversed for RTL layouts."),
117                                                        0.0, 1.0, 0.5,
118                                                        GTK_PARAM_READWRITE));  
119   g_object_class_install_property (gobject_class,
120                                    PROP_TEXT_YALIGN,
121                                    g_param_spec_float ("text-yalign",
122                                                        P_("Text y alignment"),
123                                                        P_("The vertical text alignment, from 0 (top) to 1 (bottom)."),
124                                                        0.0, 1.0, 0.5,
125                                                        GTK_PARAM_READWRITE));
126 }
127
128 static void
129 gtk_progress_set_property (GObject      *object,
130                            guint         prop_id,
131                            const GValue *value,
132                            GParamSpec   *pspec)
133 {
134   GtkProgress *progress;
135   
136   progress = GTK_PROGRESS (object);
137
138   switch (prop_id)
139     {
140     case PROP_ACTIVITY_MODE:
141       gtk_progress_set_activity_mode (progress, g_value_get_boolean (value));
142       break;
143     case PROP_SHOW_TEXT:
144       gtk_progress_set_show_text (progress, g_value_get_boolean (value));
145       break;
146     case PROP_TEXT_XALIGN:
147       gtk_progress_set_text_alignment (progress,
148                                        g_value_get_float (value),
149                                        progress->y_align);
150       break;
151     case PROP_TEXT_YALIGN:
152       gtk_progress_set_text_alignment (progress,
153                                        progress->x_align,
154                                        g_value_get_float (value));
155       break;
156     default:
157       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
158       break;
159     }
160 }
161
162 static void
163 gtk_progress_get_property (GObject      *object,
164                            guint         prop_id,
165                            GValue       *value,
166                            GParamSpec   *pspec)
167 {
168   GtkProgress *progress;
169   
170   progress = GTK_PROGRESS (object);
171
172   switch (prop_id)
173     {
174     case PROP_ACTIVITY_MODE:
175       g_value_set_boolean (value, (progress->activity_mode != FALSE));
176       break;
177     case PROP_SHOW_TEXT:
178       g_value_set_boolean (value, (progress->show_text != FALSE));
179       break;
180     case PROP_TEXT_XALIGN:
181       g_value_set_float (value, progress->x_align);
182       break;
183     case PROP_TEXT_YALIGN:
184       g_value_set_float (value, progress->y_align);
185       break;
186     default:
187       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188       break;
189     }
190 }
191
192 static void
193 gtk_progress_init (GtkProgress *progress)
194 {
195   progress->adjustment = NULL;
196   progress->offscreen_pixmap = NULL;
197   progress->format = g_strdup (DEFAULT_FORMAT);
198   progress->x_align = 0.5;
199   progress->y_align = 0.5;
200   progress->show_text = FALSE;
201   progress->activity_mode = FALSE;
202   progress->use_text_format = TRUE;
203 }
204
205 static void
206 gtk_progress_realize (GtkWidget *widget)
207 {
208   GtkProgress *progress = GTK_PROGRESS (widget);
209   GdkWindowAttr attributes;
210   gint attributes_mask;
211
212   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
213
214   attributes.window_type = GDK_WINDOW_CHILD;
215   attributes.x = widget->allocation.x;
216   attributes.y = widget->allocation.y;
217   attributes.width = widget->allocation.width;
218   attributes.height = widget->allocation.height;
219   attributes.wclass = GDK_INPUT_OUTPUT;
220   attributes.visual = gtk_widget_get_visual (widget);
221   attributes.colormap = gtk_widget_get_colormap (widget);
222   attributes.event_mask = gtk_widget_get_events (widget);
223   attributes.event_mask |= GDK_EXPOSURE_MASK;
224
225   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
226
227   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
228                                    &attributes, attributes_mask);
229   gdk_window_set_user_data (widget->window, progress);
230
231   widget->style = gtk_style_attach (widget->style, widget->window);
232   gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
233
234   gtk_progress_create_pixmap (progress);
235 }
236
237 static void
238 gtk_progress_destroy (GtkObject *object)
239 {
240   GtkProgress *progress = GTK_PROGRESS (object);
241
242   if (progress->adjustment)
243     {
244       g_signal_handlers_disconnect_by_func (progress->adjustment,
245                                             gtk_progress_value_changed,
246                                             progress);
247       g_signal_handlers_disconnect_by_func (progress->adjustment,
248                                             gtk_progress_changed,
249                                             progress);
250       g_object_unref (progress->adjustment);
251       progress->adjustment = NULL;
252     }
253
254   GTK_OBJECT_CLASS (gtk_progress_parent_class)->destroy (object);
255 }
256
257 static void
258 gtk_progress_finalize (GObject *object)
259 {
260   GtkProgress *progress = GTK_PROGRESS (object);
261
262   if (progress->offscreen_pixmap)
263     g_object_unref (progress->offscreen_pixmap);
264
265   g_free (progress->format);
266
267   G_OBJECT_CLASS (gtk_progress_parent_class)->finalize (object);
268 }
269
270 static gboolean
271 gtk_progress_expose (GtkWidget      *widget,
272                      GdkEventExpose *event)
273 {
274   if (GTK_WIDGET_DRAWABLE (widget))
275     gdk_draw_drawable (widget->window,
276                        widget->style->black_gc,
277                        GTK_PROGRESS (widget)->offscreen_pixmap,
278                        event->area.x, event->area.y,
279                        event->area.x, event->area.y,
280                        event->area.width,
281                        event->area.height);
282
283   return FALSE;
284 }
285
286 static void
287 gtk_progress_size_allocate (GtkWidget     *widget,
288                             GtkAllocation *allocation)
289 {
290   widget->allocation = *allocation;
291
292   if (GTK_WIDGET_REALIZED (widget))
293     {
294       gdk_window_move_resize (widget->window,
295                               allocation->x, allocation->y,
296                               allocation->width, allocation->height);
297
298       gtk_progress_create_pixmap (GTK_PROGRESS (widget));
299     }
300 }
301
302 static void
303 gtk_progress_create_pixmap (GtkProgress *progress)
304 {
305   GtkWidget *widget;
306
307   g_return_if_fail (GTK_IS_PROGRESS (progress));
308
309   if (GTK_WIDGET_REALIZED (progress))
310     {
311       widget = GTK_WIDGET (progress);
312
313       if (progress->offscreen_pixmap)
314         g_object_unref (progress->offscreen_pixmap);
315
316       progress->offscreen_pixmap = gdk_pixmap_new (widget->window,
317                                                    widget->allocation.width,
318                                                    widget->allocation.height,
319                                                    -1);
320
321       /* clear the pixmap for transparent themes */
322       gtk_paint_flat_box (widget->style,
323                           progress->offscreen_pixmap,
324                           GTK_STATE_NORMAL, GTK_SHADOW_NONE,
325                           NULL, widget, "trough", 0, 0, -1, -1);
326       
327       GTK_PROGRESS_GET_CLASS (progress)->paint (progress);
328     }
329 }
330
331 static void
332 gtk_progress_changed (GtkAdjustment *adjustment,
333                       GtkProgress   *progress)
334 {
335   /* A change in the value of adjustment->upper can change
336    * the size request
337    */
338   if (progress->use_text_format && progress->show_text)
339     gtk_widget_queue_resize (GTK_WIDGET (progress));
340   else
341     GTK_PROGRESS_GET_CLASS (progress)->update (progress);
342 }
343
344 static void
345 gtk_progress_value_changed (GtkAdjustment *adjustment,
346                             GtkProgress   *progress)
347 {
348   GTK_PROGRESS_GET_CLASS (progress)->update (progress);
349 }
350
351 static gchar *
352 gtk_progress_build_string (GtkProgress *progress,
353                            gdouble      value,
354                            gdouble      percentage)
355 {
356   gchar buf[256] = { 0 };
357   gchar tmp[256] = { 0 };
358   gchar *src;
359   gchar *dest;
360   gchar fmt[10];
361
362   src = progress->format;
363
364   /* This is the new supported version of this function */
365   if (!progress->use_text_format)
366     return g_strdup (src);
367
368   /* And here's all the deprecated goo. */
369   
370   dest = buf;
371  
372   while (src && *src)
373     {
374       if (*src != '%')
375         {
376           *dest = *src;
377           dest++;
378         }
379       else
380         {
381           gchar c;
382           gint digits;
383
384           c = *(src + sizeof(gchar));
385           digits = 0;
386
387           if (c >= '0' && c <= '2')
388             {
389               digits = (gint) (c - '0');
390               src++;
391               c = *(src + sizeof(gchar));
392             }
393
394           switch (c)
395             {
396             case '%':
397               *dest = '%';
398               src++;
399               dest++;
400               break;
401             case 'p':
402             case 'P':
403               if (digits)
404                 {
405                   g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
406                   g_snprintf (tmp, sizeof (tmp), fmt, 100 * percentage);
407                 }
408               else
409                 g_snprintf (tmp, sizeof (tmp), "%.0f", 100 * percentage);
410               strcat (buf, tmp);
411               dest = &(buf[strlen (buf)]);
412               src++;
413               break;
414             case 'v':
415             case 'V':
416               if (digits)
417                 {
418                   g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
419                   g_snprintf (tmp, sizeof (tmp), fmt, value);
420                 }
421               else
422                 g_snprintf (tmp, sizeof (tmp), "%.0f", value);
423               strcat (buf, tmp);
424               dest = &(buf[strlen (buf)]);
425               src++;
426               break;
427             case 'l':
428             case 'L':
429               if (digits)
430                 {
431                   g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
432                   g_snprintf (tmp, sizeof (tmp), fmt, progress->adjustment->lower);
433                 }
434               else
435                 g_snprintf (tmp, sizeof (tmp), "%.0f", progress->adjustment->lower);
436               strcat (buf, tmp);
437               dest = &(buf[strlen (buf)]);
438               src++;
439               break;
440             case 'u':
441             case 'U':
442               if (digits)
443                 {
444                   g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
445                   g_snprintf (tmp, sizeof (tmp), fmt, progress->adjustment->upper);
446                 }
447               else
448                 g_snprintf (tmp, sizeof (tmp), "%.0f", progress->adjustment->upper);
449               strcat (buf, tmp);
450               dest = &(buf[strlen (buf)]);
451               src++;
452               break;
453             default:
454               break;
455             }
456         }
457       src++;
458     }
459
460   return g_strdup (buf);
461 }
462
463 /***************************************************************/
464
465 void
466 gtk_progress_set_adjustment (GtkProgress   *progress,
467                              GtkAdjustment *adjustment)
468 {
469   g_return_if_fail (GTK_IS_PROGRESS (progress));
470   if (adjustment)
471     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
472   else
473     adjustment = (GtkAdjustment*) gtk_adjustment_new (0, 0, 100, 0, 0, 0);
474
475   if (progress->adjustment != adjustment)
476     {
477       if (progress->adjustment)
478         {
479           g_signal_handlers_disconnect_by_func (progress->adjustment,
480                                                 gtk_progress_changed,
481                                                 progress);
482           g_signal_handlers_disconnect_by_func (progress->adjustment,
483                                                 gtk_progress_value_changed,
484                                                 progress);
485           g_object_unref (progress->adjustment);
486         }
487       progress->adjustment = adjustment;
488       if (adjustment)
489         {
490           g_object_ref_sink (adjustment);
491           g_signal_connect (adjustment, "changed",
492                             G_CALLBACK (gtk_progress_changed),
493                             progress);
494           g_signal_connect (adjustment, "value_changed",
495                             G_CALLBACK (gtk_progress_value_changed),
496                             progress);
497         }
498
499       gtk_progress_changed (adjustment, progress);
500     }
501 }
502
503 void
504 gtk_progress_configure (GtkProgress *progress,
505                         gdouble      value,
506                         gdouble      min,
507                         gdouble      max)
508 {
509   GtkAdjustment *adj;
510   gboolean changed = FALSE;
511
512   g_return_if_fail (GTK_IS_PROGRESS (progress));
513   g_return_if_fail (min <= max);
514   g_return_if_fail (value >= min && value <= max);
515
516   if (!progress->adjustment)
517     gtk_progress_set_adjustment (progress, NULL);
518   adj = progress->adjustment;
519
520   if (fabs (adj->lower - min) > EPSILON || fabs (adj->upper - max) > EPSILON)
521     changed = TRUE;
522
523   adj->value = value;
524   adj->lower = min;
525   adj->upper = max;
526
527   gtk_adjustment_value_changed (adj);
528   if (changed)
529     gtk_adjustment_changed (adj);
530 }
531
532 void
533 gtk_progress_set_percentage (GtkProgress *progress,
534                              gdouble      percentage)
535 {
536   g_return_if_fail (GTK_IS_PROGRESS (progress));
537   g_return_if_fail (percentage >= 0 && percentage <= 1.0);
538
539   if (!progress->adjustment)
540     gtk_progress_set_adjustment (progress, NULL);
541   gtk_progress_set_value (progress, progress->adjustment->lower + percentage * 
542                  (progress->adjustment->upper - progress->adjustment->lower));
543 }
544
545 gdouble
546 gtk_progress_get_current_percentage (GtkProgress *progress)
547 {
548   g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
549
550   if (!progress->adjustment)
551     gtk_progress_set_adjustment (progress, NULL);
552
553   return gtk_progress_get_percentage_from_value (progress, progress->adjustment->value);
554 }
555
556 gdouble
557 gtk_progress_get_percentage_from_value (GtkProgress *progress,
558                                         gdouble      value)
559 {
560   g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
561
562   if (!progress->adjustment)
563     gtk_progress_set_adjustment (progress, NULL);
564
565   if (progress->adjustment->lower < progress->adjustment->upper &&
566       value >= progress->adjustment->lower &&
567       value <= progress->adjustment->upper)
568     return (value - progress->adjustment->lower) /
569       (progress->adjustment->upper - progress->adjustment->lower);
570   else
571     return 0.0;
572 }
573
574 void
575 gtk_progress_set_value (GtkProgress *progress,
576                         gdouble      value)
577 {
578   g_return_if_fail (GTK_IS_PROGRESS (progress));
579
580   if (!progress->adjustment)
581     gtk_progress_set_adjustment (progress, NULL);
582
583   if (fabs (progress->adjustment->value - value) > EPSILON)
584     gtk_adjustment_set_value (progress->adjustment, value);
585 }
586
587 gdouble
588 gtk_progress_get_value (GtkProgress *progress)
589 {
590   g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
591
592   return progress->adjustment ? progress->adjustment->value : 0;
593 }
594
595 void
596 gtk_progress_set_show_text (GtkProgress *progress,
597                             gboolean     show_text)
598 {
599   g_return_if_fail (GTK_IS_PROGRESS (progress));
600
601   if (progress->show_text != show_text)
602     {
603       progress->show_text = show_text;
604
605       gtk_widget_queue_resize (GTK_WIDGET (progress));
606
607       g_object_notify (G_OBJECT (progress), "show-text");
608     }
609 }
610
611 void
612 gtk_progress_set_text_alignment (GtkProgress *progress,
613                                  gfloat       x_align,
614                                  gfloat       y_align)
615 {
616   g_return_if_fail (GTK_IS_PROGRESS (progress));
617   g_return_if_fail (x_align >= 0.0 && x_align <= 1.0);
618   g_return_if_fail (y_align >= 0.0 && y_align <= 1.0);
619
620   if (progress->x_align != x_align || progress->y_align != y_align)
621     {
622       g_object_freeze_notify (G_OBJECT (progress));
623       if (progress->x_align != x_align)
624         {
625           progress->x_align = x_align;
626           g_object_notify (G_OBJECT (progress), "text-xalign");
627         }
628
629       if (progress->y_align != y_align)
630         {
631           progress->y_align = y_align;
632           g_object_notify (G_OBJECT (progress), "text-yalign");
633         }
634       g_object_thaw_notify (G_OBJECT (progress));
635
636       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (progress)))
637         gtk_widget_queue_resize (GTK_WIDGET (progress));
638     }
639 }
640
641 void
642 gtk_progress_set_format_string (GtkProgress *progress,
643                                 const gchar *format)
644 {
645   gchar *old_format;
646   
647   g_return_if_fail (GTK_IS_PROGRESS (progress));
648
649   /* Turn on format, in case someone called
650    * gtk_progress_bar_set_text() and turned it off.
651    */
652   progress->use_text_format = TRUE;
653
654   old_format = progress->format;
655
656   if (!format)
657     format = DEFAULT_FORMAT;
658
659   progress->format = g_strdup (format);
660   g_free (old_format);
661   
662   gtk_widget_queue_resize (GTK_WIDGET (progress));
663 }
664
665 gchar *
666 gtk_progress_get_current_text (GtkProgress *progress)
667 {
668   g_return_val_if_fail (GTK_IS_PROGRESS (progress), NULL);
669
670   if (!progress->adjustment)
671     gtk_progress_set_adjustment (progress, NULL);
672
673   return gtk_progress_build_string (progress, progress->adjustment->value,
674                                     gtk_progress_get_current_percentage (progress));
675 }
676
677 gchar *
678 gtk_progress_get_text_from_value (GtkProgress *progress,
679                                   gdouble      value)
680 {
681   g_return_val_if_fail (GTK_IS_PROGRESS (progress), NULL);
682
683   if (!progress->adjustment)
684     gtk_progress_set_adjustment (progress, NULL);
685
686   return gtk_progress_build_string (progress, value,
687                                     gtk_progress_get_percentage_from_value (progress, value));
688 }
689
690 void
691 gtk_progress_set_activity_mode (GtkProgress *progress,
692                                 gboolean     activity_mode)
693 {
694   g_return_if_fail (GTK_IS_PROGRESS (progress));
695
696   if (progress->activity_mode != (activity_mode != FALSE))
697     {
698       progress->activity_mode = (activity_mode != FALSE);
699
700       if (progress->activity_mode)
701         GTK_PROGRESS_GET_CLASS (progress)->act_mode_enter (progress);
702
703       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (progress)))
704         gtk_widget_queue_resize (GTK_WIDGET (progress));
705
706       g_object_notify (G_OBJECT (progress), "activity-mode");
707     }
708 }
709
710 #define __GTK_PROGRESS_C__
711 #include "gtkaliasdef.c"