1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
27 #include <glib/gprintf.h>
30 #include "gtkprogress.h"
34 #define DEFAULT_FORMAT "%P %%"
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,
51 static void gtk_progress_get_property (GObject *object,
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 static void gtk_progress_changed (GtkAdjustment *adjustment,
66 GtkProgress *progress);
69 static GtkWidgetClass *parent_class = NULL;
73 gtk_progress_get_type (void)
75 static GType progress_type = 0;
79 static const GTypeInfo progress_info =
81 sizeof (GtkProgressClass),
83 NULL, /* base_finalize */
84 (GClassInitFunc) gtk_progress_class_init,
85 NULL, /* class_finalize */
86 NULL, /* class_data */
89 (GInstanceInitFunc) gtk_progress_init,
92 progress_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkProgress",
100 gtk_progress_class_init (GtkProgressClass *class)
102 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
103 GtkObjectClass *object_class;
104 GtkWidgetClass *widget_class;
106 object_class = (GtkObjectClass *) class;
107 widget_class = (GtkWidgetClass *) class;
109 parent_class = gtk_type_class (GTK_TYPE_WIDGET);
111 gobject_class->finalize = gtk_progress_finalize;
113 gobject_class->set_property = gtk_progress_set_property;
114 gobject_class->get_property = gtk_progress_get_property;
115 object_class->destroy = gtk_progress_destroy;
117 widget_class->realize = gtk_progress_realize;
118 widget_class->expose_event = gtk_progress_expose;
119 widget_class->size_allocate = gtk_progress_size_allocate;
121 /* to be overridden */
123 class->update = NULL;
124 class->act_mode_enter = NULL;
126 g_object_class_install_property (gobject_class,
128 g_param_spec_boolean ("activity_mode",
130 _("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"),
134 g_object_class_install_property (gobject_class,
136 g_param_spec_boolean ("show_text",
138 _("Whether the progress is shown as text"),
142 g_object_class_install_property (gobject_class,
144 g_param_spec_float ("text_xalign",
145 _("Text x alignment"),
146 _("A number between 0.0 and 1.0 specifying the horizontal alignment of the text in the progress widget"),
151 g_object_class_install_property (gobject_class,
153 g_param_spec_float ("text_yalign",
154 _("Text y alignment"),
155 _("A number between 0.0 and 1.0 specifying the vertical alignment of the text in the progress widget"),
163 gtk_progress_set_property (GObject *object,
168 GtkProgress *progress;
170 progress = GTK_PROGRESS (object);
174 case PROP_ACTIVITY_MODE:
175 gtk_progress_set_activity_mode (progress, g_value_get_boolean (value));
178 gtk_progress_set_show_text (progress, g_value_get_boolean (value));
180 case PROP_TEXT_XALIGN:
181 gtk_progress_set_text_alignment (progress,
182 g_value_get_float (value),
185 case PROP_TEXT_YALIGN:
186 gtk_progress_set_text_alignment (progress,
188 g_value_get_float (value));
191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
197 gtk_progress_get_property (GObject *object,
202 GtkProgress *progress;
204 progress = GTK_PROGRESS (object);
208 case PROP_ACTIVITY_MODE:
209 g_value_set_boolean (value, (progress->activity_mode != FALSE));
212 g_value_set_boolean (value, (progress->show_text != FALSE));
214 case PROP_TEXT_XALIGN:
215 g_value_set_float (value, progress->x_align);
217 case PROP_TEXT_YALIGN:
218 g_value_set_float (value, progress->y_align);
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227 gtk_progress_init (GtkProgress *progress)
229 progress->adjustment = NULL;
230 progress->offscreen_pixmap = NULL;
231 progress->format = g_strdup (DEFAULT_FORMAT);
232 progress->x_align = 0.5;
233 progress->y_align = 0.5;
234 progress->show_text = FALSE;
235 progress->activity_mode = FALSE;
236 progress->use_text_format = TRUE;
240 gtk_progress_realize (GtkWidget *widget)
242 GtkProgress *progress;
243 GdkWindowAttr attributes;
244 gint attributes_mask;
246 g_return_if_fail (GTK_IS_PROGRESS (widget));
248 progress = GTK_PROGRESS (widget);
249 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
251 attributes.window_type = GDK_WINDOW_CHILD;
252 attributes.x = widget->allocation.x;
253 attributes.y = widget->allocation.y;
254 attributes.width = widget->allocation.width;
255 attributes.height = widget->allocation.height;
256 attributes.wclass = GDK_INPUT_OUTPUT;
257 attributes.visual = gtk_widget_get_visual (widget);
258 attributes.colormap = gtk_widget_get_colormap (widget);
259 attributes.event_mask = gtk_widget_get_events (widget);
260 attributes.event_mask |= GDK_EXPOSURE_MASK;
262 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
264 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
265 &attributes, attributes_mask);
266 gdk_window_set_user_data (widget->window, progress);
268 widget->style = gtk_style_attach (widget->style, widget->window);
269 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
271 gtk_progress_create_pixmap (progress);
275 gtk_progress_destroy (GtkObject *object)
277 GtkProgress *progress;
279 g_return_if_fail (GTK_IS_PROGRESS (object));
281 progress = GTK_PROGRESS (object);
283 if (progress->adjustment)
285 g_signal_handlers_disconnect_by_func (progress->adjustment,
286 gtk_progress_value_changed,
288 g_signal_handlers_disconnect_by_func (progress->adjustment,
289 gtk_progress_changed,
291 g_object_unref (progress->adjustment);
292 progress->adjustment = NULL;
295 GTK_OBJECT_CLASS (parent_class)->destroy (object);
299 gtk_progress_finalize (GObject *object)
301 GtkProgress *progress;
303 g_return_if_fail (GTK_IS_PROGRESS (object));
305 progress = GTK_PROGRESS (object);
307 if (progress->offscreen_pixmap)
308 g_object_unref (progress->offscreen_pixmap);
310 if (progress->format)
311 g_free (progress->format);
313 G_OBJECT_CLASS (parent_class)->finalize (object);
317 gtk_progress_expose (GtkWidget *widget,
318 GdkEventExpose *event)
320 g_return_val_if_fail (GTK_IS_PROGRESS (widget), FALSE);
321 g_return_val_if_fail (event != NULL, FALSE);
323 if (GTK_WIDGET_DRAWABLE (widget))
324 gdk_draw_drawable (widget->window,
325 widget->style->black_gc,
326 GTK_PROGRESS (widget)->offscreen_pixmap,
327 event->area.x, event->area.y,
328 event->area.x, event->area.y,
336 gtk_progress_size_allocate (GtkWidget *widget,
337 GtkAllocation *allocation)
339 g_return_if_fail (GTK_IS_PROGRESS (widget));
340 g_return_if_fail (allocation != NULL);
342 widget->allocation = *allocation;
344 if (GTK_WIDGET_REALIZED (widget))
346 gdk_window_move_resize (widget->window,
347 allocation->x, allocation->y,
348 allocation->width, allocation->height);
350 gtk_progress_create_pixmap (GTK_PROGRESS (widget));
355 gtk_progress_create_pixmap (GtkProgress *progress)
359 g_return_if_fail (GTK_IS_PROGRESS (progress));
361 if (GTK_WIDGET_REALIZED (progress))
363 widget = GTK_WIDGET (progress);
365 if (progress->offscreen_pixmap)
366 g_object_unref (progress->offscreen_pixmap);
368 progress->offscreen_pixmap = gdk_pixmap_new (widget->window,
369 widget->allocation.width,
370 widget->allocation.height,
372 GTK_PROGRESS_GET_CLASS (progress)->paint (progress);
377 gtk_progress_changed (GtkAdjustment *adjustment,
378 GtkProgress *progress)
380 /* A change in the value of adjustment->upper can change
383 if (progress->use_text_format && progress->show_text)
384 gtk_widget_queue_resize (progress);
386 GTK_PROGRESS_GET_CLASS (progress)->update (progress);
390 gtk_progress_value_changed (GtkAdjustment *adjustment,
391 GtkProgress *progress)
393 GTK_PROGRESS_GET_CLASS (progress)->update (progress);
397 gtk_progress_build_string (GtkProgress *progress,
401 gchar buf[256] = { 0 };
402 gchar tmp[256] = { 0 };
407 src = progress->format;
409 /* This is the new supported version of this function */
410 if (!progress->use_text_format)
411 return g_strdup (src);
413 /* And here's all the deprecated goo. */
429 c = *(src + sizeof(gchar));
432 if (c >= '0' && c <= '2')
434 digits = (gint) (c - '0');
436 c = *(src + sizeof(gchar));
450 g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
451 g_snprintf (tmp, sizeof (tmp), fmt, 100 * percentage);
454 g_snprintf (tmp, sizeof (tmp), "%.0f", 100 * percentage);
456 dest = &(buf[strlen (buf)]);
463 g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
464 g_snprintf (tmp, sizeof (tmp), fmt, value);
467 g_snprintf (tmp, sizeof (tmp), "%.0f", value);
469 dest = &(buf[strlen (buf)]);
476 g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
477 g_snprintf (tmp, sizeof (tmp), fmt, progress->adjustment->lower);
480 g_snprintf (tmp, sizeof (tmp), "%.0f", progress->adjustment->lower);
482 dest = &(buf[strlen (buf)]);
489 g_snprintf (fmt, sizeof (fmt), "%%.%df", digits);
490 g_snprintf (tmp, sizeof (tmp), fmt, progress->adjustment->upper);
493 g_snprintf (tmp, sizeof (tmp), "%.0f", progress->adjustment->upper);
495 dest = &(buf[strlen (buf)]);
505 return g_strdup (buf);
508 /***************************************************************/
511 gtk_progress_set_adjustment (GtkProgress *progress,
512 GtkAdjustment *adjustment)
514 g_return_if_fail (GTK_IS_PROGRESS (progress));
516 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
518 adjustment = (GtkAdjustment*) gtk_adjustment_new (0, 0, 100, 0, 0, 0);
520 if (progress->adjustment != adjustment)
522 if (progress->adjustment)
524 g_signal_handlers_disconnect_by_func (progress->adjustment,
525 gtk_progress_changed,
527 g_signal_handlers_disconnect_by_func (progress->adjustment,
528 gtk_progress_value_changed,
530 g_object_unref (progress->adjustment);
532 progress->adjustment = adjustment;
535 g_object_ref (adjustment);
536 gtk_object_sink (GTK_OBJECT (adjustment));
537 g_signal_connect (adjustment, "changed",
538 G_CALLBACK (gtk_progress_changed),
540 g_signal_connect (adjustment, "value_changed",
541 G_CALLBACK (gtk_progress_value_changed),
545 gtk_progress_changed (adjustment, progress);
550 gtk_progress_configure (GtkProgress *progress,
556 gboolean changed = FALSE;
558 g_return_if_fail (GTK_IS_PROGRESS (progress));
559 g_return_if_fail (min <= max);
560 g_return_if_fail (value >= min && value <= max);
562 if (!progress->adjustment)
563 gtk_progress_set_adjustment (progress, NULL);
564 adj = progress->adjustment;
566 if (fabs (adj->lower - min) > EPSILON || fabs (adj->upper - max) > EPSILON)
573 gtk_adjustment_value_changed (adj);
575 gtk_adjustment_changed (adj);
579 gtk_progress_set_percentage (GtkProgress *progress,
582 g_return_if_fail (GTK_IS_PROGRESS (progress));
583 g_return_if_fail (percentage >= 0 && percentage <= 1.0);
585 if (!progress->adjustment)
586 gtk_progress_set_adjustment (progress, NULL);
587 gtk_progress_set_value (progress, progress->adjustment->lower + percentage *
588 (progress->adjustment->upper - progress->adjustment->lower));
592 gtk_progress_get_current_percentage (GtkProgress *progress)
594 g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
596 if (!progress->adjustment)
597 gtk_progress_set_adjustment (progress, NULL);
599 return gtk_progress_get_percentage_from_value (progress, progress->adjustment->value);
603 gtk_progress_get_percentage_from_value (GtkProgress *progress,
606 g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
608 if (!progress->adjustment)
609 gtk_progress_set_adjustment (progress, NULL);
611 if (progress->adjustment->lower < progress->adjustment->upper &&
612 value >= progress->adjustment->lower &&
613 value <= progress->adjustment->upper)
614 return (value - progress->adjustment->lower) /
615 (progress->adjustment->upper - progress->adjustment->lower);
621 gtk_progress_set_value (GtkProgress *progress,
624 g_return_if_fail (GTK_IS_PROGRESS (progress));
626 if (!progress->adjustment)
627 gtk_progress_set_adjustment (progress, NULL);
629 if (fabs (progress->adjustment->value - value) > EPSILON)
630 gtk_adjustment_set_value (progress->adjustment, value);
634 gtk_progress_get_value (GtkProgress *progress)
636 g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
638 return progress->adjustment ? progress->adjustment->value : 0;
642 gtk_progress_set_show_text (GtkProgress *progress,
645 g_return_if_fail (GTK_IS_PROGRESS (progress));
647 if (progress->show_text != show_text)
649 progress->show_text = show_text;
651 gtk_widget_queue_resize (GTK_WIDGET (progress));
653 g_object_notify (G_OBJECT (progress), "show_text");
658 gtk_progress_set_text_alignment (GtkProgress *progress,
662 g_return_if_fail (GTK_IS_PROGRESS (progress));
663 g_return_if_fail (x_align >= 0.0 && x_align <= 1.0);
664 g_return_if_fail (y_align >= 0.0 && y_align <= 1.0);
666 if (progress->x_align != x_align || progress->y_align != y_align)
668 g_object_freeze_notify (G_OBJECT (progress));
669 if (progress->x_align != x_align)
671 progress->x_align = x_align;
672 g_object_notify (G_OBJECT (progress), "text_xalign");
675 if (progress->y_align != y_align)
677 progress->y_align = y_align;
678 g_object_notify (G_OBJECT (progress), "text_yalign");
680 g_object_thaw_notify (G_OBJECT (progress));
682 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (progress)))
683 gtk_widget_queue_resize (GTK_WIDGET (progress));
688 gtk_progress_set_format_string (GtkProgress *progress,
693 g_return_if_fail (GTK_IS_PROGRESS (progress));
695 /* Turn on format, in case someone called
696 * gtk_progress_bar_set_text() and turned it off.
698 progress->use_text_format = TRUE;
700 old_format = progress->format;
703 format = DEFAULT_FORMAT;
705 progress->format = g_strdup (format);
708 gtk_widget_queue_resize (GTK_WIDGET (progress));
712 gtk_progress_get_current_text (GtkProgress *progress)
714 g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
716 if (!progress->adjustment)
717 gtk_progress_set_adjustment (progress, NULL);
719 return gtk_progress_build_string (progress, progress->adjustment->value,
720 gtk_progress_get_current_percentage (progress));
724 gtk_progress_get_text_from_value (GtkProgress *progress,
727 g_return_val_if_fail (GTK_IS_PROGRESS (progress), 0);
729 if (!progress->adjustment)
730 gtk_progress_set_adjustment (progress, NULL);
732 return gtk_progress_build_string (progress, value,
733 gtk_progress_get_percentage_from_value (progress, value));
737 gtk_progress_set_activity_mode (GtkProgress *progress,
738 gboolean activity_mode)
740 g_return_if_fail (GTK_IS_PROGRESS (progress));
742 if (progress->activity_mode != (activity_mode != FALSE))
744 progress->activity_mode = (activity_mode != FALSE);
746 if (progress->activity_mode)
747 GTK_PROGRESS_GET_CLASS (progress)->act_mode_enter (progress);
749 if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (progress)))
750 gtk_widget_queue_resize (GTK_WIDGET (progress));
752 g_object_notify (G_OBJECT (progress), "activity_mode");