/* * Copyright (C) 2004-2006 Jimmy Do * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "gloo-timer.h" static GObjectClass *parent_class = NULL; struct _GlooTimerPrivate { GlooTimerState state; guint duration; gchar *name; GTimer *timer; gboolean dispose_has_run; }; enum { PROP_STATE = 1 }; static void timer_set_state (GlooTimer *self, GlooTimerState state) { g_assert (self != NULL); g_assert (self->priv != NULL); self->priv->state = state; g_signal_emit (self, GLOO_TIMER_GET_CLASS (self)->state_changed_signal_id, 0, NULL); } static void timer_transition_to_state (GlooTimer *self, GlooTimerState dest_state) { GlooTimerState cur_state; g_assert (self != NULL); g_assert (self->priv != NULL); cur_state = self->priv->state; switch (dest_state) { case GLOO_TIMER_STATE_IDLE: g_timer_stop (self->priv->timer); g_timer_reset (self->priv->timer); break; case GLOO_TIMER_STATE_RUNNING: g_assert (self->priv->duration >= 0); if (cur_state == GLOO_TIMER_STATE_IDLE) { g_timer_start (self->priv->timer); } else if (cur_state == GLOO_TIMER_STATE_PAUSED) { g_timer_continue (self->priv->timer); } break; case GLOO_TIMER_STATE_PAUSED: g_timer_stop (self->priv->timer); break; case GLOO_TIMER_STATE_FINISHED: g_timer_stop (self->priv->timer); g_timer_reset (self->priv->timer); break; default: g_assert_not_reached (); break; } timer_set_state (self, dest_state); } static gboolean on_timeout (gpointer user_data) { GlooTimer *self; g_assert (user_data != NULL); self = (GlooTimer *)user_data; g_assert (self->priv != NULL); if (self->priv->dispose_has_run) { return FALSE; /* remove timeout source */ } if (self->priv->state == GLOO_TIMER_STATE_RUNNING) { guint elapsed = (guint)g_timer_elapsed (self->priv->timer, NULL); gint remaining = self->priv->duration - elapsed; g_signal_emit (self, GLOO_TIMER_GET_CLASS (self)->time_changed_signal_id, 0, NULL); if (remaining < 0) { timer_transition_to_state (self, GLOO_TIMER_STATE_FINISHED); } } return TRUE; /* keep timeout source */ } static void gloo_timer_instance_init (GTypeInstance *instance, gpointer g_class) { GlooTimer *self; self = (GlooTimer *)instance; self->priv = g_new (GlooTimerPrivate, 1); self->priv->state = GLOO_TIMER_STATE_IDLE; self->priv->duration = 0; self->priv->name = g_strdup (""); self->priv->dispose_has_run = FALSE; self->priv->timer = g_timer_new (); /* Timer starts running upon creation. * Let's stop it for now. */ g_timer_stop (self->priv->timer); g_timer_reset (self->priv->timer); g_timeout_add (500, (GSourceFunc)on_timeout, self); } static void gloo_timer_dispose (GObject *obj) { GlooTimer *self; self = (GlooTimer *)obj; if (self->priv->dispose_has_run) { return; } self->priv->dispose_has_run = TRUE; G_OBJECT_CLASS (parent_class)->dispose (obj); } static void gloo_timer_finalize (GObject *obj) { GlooTimer *self; self = (GlooTimer *)obj; g_assert (self->priv->name != NULL); g_free (self->priv->name); self->priv->name = NULL; g_timer_destroy (self->priv->timer); self->priv->timer = NULL; g_free (self->priv); self->priv = NULL; G_OBJECT_CLASS (parent_class)->finalize (obj); } static void gloo_timer_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GlooTimer *self; self = (GlooTimer *)object; switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gloo_timer_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GlooTimer *self; self = (GlooTimer *)object; switch (property_id) { case PROP_STATE: g_value_set_enum (value, self->priv->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } void gloo_timer_set_duration (GlooTimer *self, guint seconds) { g_assert (self != NULL); g_assert (self->priv != NULL); g_assert (self->priv->state == GLOO_TIMER_STATE_IDLE); self->priv->duration = seconds; } guint gloo_timer_get_duration (GlooTimer *self) { g_assert (self != NULL); g_assert (self->priv != NULL); return self->priv->duration; } void gloo_timer_set_name (GlooTimer *self, const gchar *name) { g_assert (self != NULL); g_assert (name != NULL); g_assert (self->priv != NULL); g_assert (self->priv->state == GLOO_TIMER_STATE_IDLE); /* self->priv->name should never be NULL. */ g_assert (self->priv->name != NULL); /* Free previously-stored string before assigning * new string. */ g_free (self->priv->name); self->priv->name = g_strdup (name); } const gchar * gloo_timer_get_name (GlooTimer *self) { g_assert (self != NULL); g_assert (self->priv != NULL); g_assert (self->priv->name != NULL); return self->priv->name; } void gloo_timer_start (GlooTimer *self) { g_assert (self != NULL); g_assert (self->priv != NULL); g_assert (self->priv->state == GLOO_TIMER_STATE_IDLE || self->priv->state == GLOO_TIMER_STATE_PAUSED); timer_transition_to_state (self, GLOO_TIMER_STATE_RUNNING); } void gloo_timer_stop (GlooTimer *self) { g_assert (self != NULL); g_assert (self->priv != NULL); g_assert (self->priv->state == GLOO_TIMER_STATE_RUNNING); timer_transition_to_state (self, GLOO_TIMER_STATE_PAUSED); } void gloo_timer_reset (GlooTimer *self) { g_assert (self != NULL); g_assert (self->priv != NULL); g_assert (self->priv->state != GLOO_TIMER_STATE_IDLE); timer_transition_to_state (self, GLOO_TIMER_STATE_IDLE); } GlooTimerState gloo_timer_get_state (GlooTimer *self) { g_assert (self != NULL); g_assert (self->priv != NULL); return self->priv->state; } guint gloo_timer_get_remaining_time (GlooTimer *self) { gint remaining_seconds; g_assert (self != NULL); g_assert (self->priv != NULL); if (self->priv->state == GLOO_TIMER_STATE_IDLE || self->priv->state == GLOO_TIMER_STATE_FINISHED) { return 0; } g_assert (self->priv->timer != NULL); remaining_seconds = self->priv->duration - g_timer_elapsed (self->priv->timer, NULL); remaining_seconds = MAX (0, remaining_seconds); g_assert (remaining_seconds >= 0); return remaining_seconds; } /** Class-related functions **/ static void gloo_timer_class_init (gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class; GlooTimerClass *klass; GParamSpec *pspec; gobject_class = G_OBJECT_CLASS (g_class); klass = GLOO_TIMER_CLASS (g_class); gobject_class->set_property = gloo_timer_set_property; gobject_class->get_property = gloo_timer_get_property; gobject_class->dispose = gloo_timer_dispose; gobject_class->finalize = gloo_timer_finalize; parent_class = g_type_class_peek_parent (klass); pspec = g_param_spec_enum ("state", "State of the timer", "Get the state of the timer", GLOO_TYPE_TIMER_STATE, GLOO_TIMER_STATE_IDLE, G_PARAM_READABLE); g_object_class_install_property (gobject_class, PROP_STATE, pspec); klass->state_changed_signal_id = g_signal_newv ("state-changed", G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, NULL, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); klass->time_changed_signal_id = g_signal_newv ("time-changed", G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, NULL, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); } GType gloo_timer_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (GlooTimerClass), NULL, NULL, gloo_timer_class_init, NULL, NULL, sizeof (GlooTimer), 0, gloo_timer_instance_init }; type = g_type_register_static (G_TYPE_OBJECT, "GlooTimerType", &info, 0); } return type; }