/* gtkam-clock.c
*
* Copyright 2001-2002 Lutz Mueller
*
* 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, see .
*/
#include "config.h"
#include "gtkam-clock.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct _GtkamClockPrivate
{
GTimer *timer;
guint32 timeout;
GtkLabel *label;
time_t time;
GDateWeekday weekday;
};
#define PARENT_TYPE GTK_TYPE_HBOX
static GtkHBoxClass *parent_class;
enum {
CHANGED,
NEXT_DAY,
PREVIOUS_DAY,
SET,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = {0};
static void
gtkam_clock_destroy (GtkObject *object)
{
GtkamClock *clock = GTKAM_CLOCK (object);
if (clock->priv->timeout) {
gtk_timeout_remove (clock->priv->timeout);
clock->priv->timeout = 0;
}
if (clock->priv->timer) {
g_timer_destroy (clock->priv->timer);
clock->priv->timer = NULL;
}
GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
static void
gtkam_clock_finalize (GObject *object)
{
GtkamClock *clock = GTKAM_CLOCK (object);
g_free (clock->priv);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gtkam_clock_class_init (gpointer g_class, gpointer class_data)
{
GtkObjectClass *object_class;
GObjectClass *gobject_class;
object_class = GTK_OBJECT_CLASS (g_class);
object_class->destroy = gtkam_clock_destroy;
gobject_class = G_OBJECT_CLASS (g_class);
gobject_class->finalize = gtkam_clock_finalize;
signals[CHANGED] = g_signal_new ("changed",
G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkamClockClass, changed), NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
signals[NEXT_DAY] = g_signal_new ("next_day",
G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkamClockClass, next_day), NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
signals[PREVIOUS_DAY] = g_signal_new ("previous_day",
G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkamClockClass, previous_day), NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
signals[SET] = g_signal_new ("set",
G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkamClockClass, set), NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
parent_class = g_type_class_peek_parent (g_class);
}
static void
gtkam_clock_init (GTypeInstance *instance, gpointer g_class)
{
GtkamClock *clock = GTKAM_CLOCK (instance);
clock->priv = g_new0 (GtkamClockPrivate, 1);
clock->priv->timer = g_timer_new ();
clock->priv->time = time (NULL);
}
GType
gtkam_clock_get_type (void)
{
static GType type = 0;
if (!type) {
GTypeInfo ti;
memset (&ti, 0, sizeof (GTypeInfo));
ti.class_size = sizeof (GtkamClockClass);
ti.class_init = gtkam_clock_class_init;
ti.instance_size = sizeof (GtkamClock);
ti.instance_init = gtkam_clock_init;
type = g_type_register_static (PARENT_TYPE, "GtkamClock",
&ti, 0);
}
return (type);
}
static void
gtkam_clock_update (GtkamClock *clock)
{
struct tm tm;
time_t time;
gdouble elapsed;
gchar *string;
GDate *date;
GDateWeekday weekday;
g_return_if_fail (GTKAM_IS_CLOCK (clock));
elapsed = g_timer_elapsed (clock->priv->timer, NULL);
time = (time_t) ((guint) clock->priv->time + (guint) elapsed);
localtime_r (&time, &tm);
string = g_strdup_printf ("%02i:%02i:%02i", tm.tm_hour, tm.tm_min,
tm.tm_sec);
gtk_label_set_text (clock->priv->label, string);
g_free (string);
g_signal_emit (G_OBJECT (clock), signals[CHANGED], 0);
/* New day? */
date = g_date_new_dmy (tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
weekday = g_date_weekday (date);
g_date_free (date);
if (!clock->priv->weekday) {
/* Initialization */
clock->priv->weekday = weekday;
} else if (clock->priv->weekday == weekday) {
/* Nothing here */
} else if (clock->priv->weekday ==
((weekday == G_DATE_SUNDAY) ? G_DATE_MONDAY : weekday + 1)) {
clock->priv->weekday = weekday;
g_signal_emit (GTK_OBJECT (clock), signals[PREVIOUS_DAY], 0);
} else if (clock->priv->weekday ==
((weekday == G_DATE_MONDAY) ? G_DATE_SUNDAY : weekday - 1)) {
clock->priv->weekday = weekday;
g_signal_emit (GTK_OBJECT (clock), signals[NEXT_DAY], 0);
} else {
g_warning ("Strange day (%i,%i).", clock->priv->weekday,
weekday);
}
}
typedef struct
{
GtkButton *button;
GtkamClock *clock;
gboolean forward;
gboolean fast;
gboolean running;
guint x;
time_t time;
} AdjustData;
static gboolean
adjust_func (gpointer data)
{
AdjustData *ad = data;
gint diff;
g_assert (GTKAM_IS_CLOCK (ad->clock));
/* Delay */
if (time (NULL) <= ad->time + 1)
return (TRUE);
/* Still adjusting? */
if (!ad->button->button_down) {
if (ad->running)
gtkam_clock_start (ad->clock);
g_free (ad);
return (FALSE);
}
diff = 1;
if (ad->fast)
diff += 3600. * (1. - pow (0.999, ++(ad->x)));
if (!ad->forward)
diff *= -1;
ad->clock->priv->time += diff;
gtkam_clock_update (ad->clock);
g_signal_emit (GTK_OBJECT (ad->clock), signals[SET], 0);
return (TRUE);
}
static void
on_forward_pressed (GtkButton *button, GtkamClock *clock)
{
AdjustData *ad;
clock->priv->time++;
gtkam_clock_update (clock);
ad = g_new0 (AdjustData, 1);
ad->running = (clock->priv->timeout);
ad->clock = clock;
ad->button = button;
ad->forward = TRUE;
ad->fast = FALSE;
ad->time = time (NULL);
gtkam_clock_stop (clock);
gtk_timeout_add (100, adjust_func, ad);
}
static void
on_backward_pressed (GtkButton *button, GtkamClock *clock)
{
AdjustData *ad;
clock->priv->time--;
gtkam_clock_update (clock);
ad = g_new0 (AdjustData, 1);
ad->running = (clock->priv->timeout);
ad->clock = clock;
ad->button = button;
ad->forward = FALSE;
ad->fast = FALSE;
ad->time = time (NULL);
gtkam_clock_stop (clock);
gtk_timeout_add (100, adjust_func, ad);
}
static void
on_fforward_pressed (GtkButton *button, GtkamClock *clock)
{
AdjustData *ad;
ad = g_new0 (AdjustData, 1);
ad->running = (clock->priv->timeout);
ad->clock = clock;
ad->button = button;
ad->forward = TRUE;
ad->fast = TRUE;
ad->time = time (NULL);
gtkam_clock_stop (clock);
gtk_timeout_add (100, adjust_func, ad);
}
static void
on_fbackward_pressed (GtkButton *button, GtkamClock *clock)
{
AdjustData *ad;
ad = g_new0 (AdjustData, 1);
ad->running = (clock->priv->timeout);
ad->clock = clock;
ad->button = button;
ad->forward = FALSE;
ad->fast = TRUE;
ad->time = time (NULL);
gtkam_clock_stop (clock);
gtk_timeout_add (100, adjust_func, ad);
}
static gboolean
timeout_func (gpointer data)
{
gtkam_clock_update (GTKAM_CLOCK (data));
return (TRUE);
}
static void
on_label_toggled (GtkToggleButton *toggle, GtkamClock *clock)
{
if (toggle->active)
gtkam_clock_stop (clock);
else
gtkam_clock_start (clock);
}
GtkWidget *
gtkam_clock_new (void)
{
GtkamClock *clock;
GtkWidget *arrow, *label, *button, *toggle;
clock = g_object_new (GTKAM_TYPE_CLOCK, NULL);
gtk_box_set_spacing (GTK_BOX (clock), 0);
gtk_box_set_homogeneous (GTK_BOX (clock), FALSE);
button = gtk_button_new ();
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (clock), button, FALSE, FALSE, 0);
arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_OUT);
gtk_widget_show (arrow);
gtk_container_add (GTK_CONTAINER (button), arrow);
g_signal_connect (G_OBJECT (button), "pressed",
G_CALLBACK (on_fbackward_pressed), clock);
button = gtk_button_new ();
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (clock), button, FALSE, FALSE, 0);
arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_OUT);
gtk_widget_show (arrow);
gtk_container_add (GTK_CONTAINER (button), arrow);
g_signal_connect (GTK_OBJECT (button), "pressed",
G_CALLBACK (on_backward_pressed), clock);
toggle = gtk_toggle_button_new ();
gtk_button_set_relief (GTK_BUTTON (toggle), GTK_RELIEF_NONE);
gtk_widget_show (toggle);
gtk_box_pack_start (GTK_BOX (clock), toggle, FALSE, FALSE, 0);
g_signal_connect (GTK_OBJECT (toggle), "toggled",
G_CALLBACK (on_label_toggled), clock);
label = gtk_label_new ("00:00:00");
gtk_widget_show (label);
gtk_container_add (GTK_CONTAINER (toggle), label);
clock->priv->label = GTK_LABEL (label);
button = gtk_button_new ();
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (clock), button, FALSE, FALSE, 0);
arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
gtk_widget_show (arrow);
gtk_container_add (GTK_CONTAINER (button), arrow);
g_signal_connect (G_OBJECT (button), "pressed",
G_CALLBACK (on_forward_pressed), clock);
button = gtk_button_new ();
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (clock), button, FALSE, FALSE, 0);
arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
gtk_widget_show (arrow);
gtk_container_add (GTK_CONTAINER (button), arrow);
g_signal_connect (G_OBJECT (button), "pressed",
G_CALLBACK (on_fforward_pressed), clock);
gtkam_clock_start (clock);
return (GTK_WIDGET (clock));
}
void
gtkam_clock_set (GtkamClock *clock, guchar hour, guchar minute, guchar second)
{
struct tm tm;
g_return_if_fail (GTKAM_IS_CLOCK (clock));
localtime_r (&clock->priv->time, &tm);
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = second;
clock->priv->time = mktime (&tm);
g_signal_emit (G_OBJECT (clock), signals[SET], 0);
}
void
gtkam_clock_get (GtkamClock *clock, guchar *hour, guchar *minute,
guchar *second)
{
struct tm tm;
g_return_if_fail (GTKAM_IS_CLOCK (clock));
g_return_if_fail (hour && minute && second);
localtime_r (&clock->priv->time, &tm);
*hour = tm.tm_hour;
*minute = tm.tm_min;
*second = tm.tm_sec;
}
void
gtkam_clock_start (GtkamClock *clock)
{
g_return_if_fail (GTKAM_IS_CLOCK (clock));
if (!clock->priv->timeout) {
clock->priv->timeout = gtk_timeout_add (1000, timeout_func,
clock);
g_timer_start (clock->priv->timer);
}
}
void
gtkam_clock_stop (GtkamClock *clock)
{
g_return_if_fail (GTKAM_IS_CLOCK (clock));
if (clock->priv->timeout) {
gtk_timeout_remove (clock->priv->timeout);
g_timer_stop (clock->priv->timer);
clock->priv->timeout = 0;
clock->priv->time += g_timer_elapsed (clock->priv->timer, NULL);
}
}