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