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