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