aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/Makefile.am15
-rw-r--r--example/messagedialog.c57
-rw-r--r--src/Makefile.am30
-rw-r--r--src/ido.list1
-rw-r--r--src/idomessagedialog.c416
-rw-r--r--src/idomessagedialog.h75
-rw-r--r--src/idotimeline.c721
-rw-r--r--src/idotimeline.h113
-rw-r--r--src/idotypebuiltins.c.template31
-rw-r--r--src/idotypebuiltins.h.template26
10 files changed, 1483 insertions, 2 deletions
diff --git a/example/Makefile.am b/example/Makefile.am
index c7f9c15..bd96642 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1,9 +1,21 @@
noinst_PROGRAMS = \
+ messagedialog \
menus
+messagedialog_SOURCES = \
+ messagedialog.c
+
menus_SOURCES = \
menus.c
+messagedialog_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ -I$(top_builddir)/src \
+ $(GCC_FLAGS) \
+ $(GTK_CFLAGS) \
+ $(MAINTAINER_CFLAGS)
+
menus_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/src \
@@ -12,5 +24,6 @@ menus_CPPFLAGS = \
$(GTK_CFLAGS) \
$(MAINTAINER_CFLAGS)
-menus_LDADD = $(top_builddir)/src/libido-0.1.la
+messagedialog_LDADD = $(top_builddir)/src/libido-0.1.la
+menus_LDADD = $(top_builddir)/src/libido-0.1.la
diff --git a/example/messagedialog.c b/example/messagedialog.c
new file mode 100644
index 0000000..bc24aff
--- /dev/null
+++ b/example/messagedialog.c
@@ -0,0 +1,57 @@
+#include <gtk/gtk.h>
+
+#include "idomessagedialog.h"
+
+static void
+response_cb (GtkDialog *dialog,
+ gint response,
+ gpointer user_data)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+button_clicked_cb (GtkWidget *button, gpointer data)
+{
+ GtkWidget *window = (GtkWidget *) data;
+ GtkWidget *dialog = ido_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CLOSE,
+ "This is a test of the emergency broadcasting system");
+ gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
+ "If this had been an actual emergency, you'd be dead already");
+ g_signal_connect (G_OBJECT (dialog),
+ "response",
+ G_CALLBACK (response_cb), NULL);
+ gtk_widget_show (dialog);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *button;
+
+ gtk_init (&argc, &argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Message Dialogs");
+ g_signal_connect (window, "destroy", gtk_main_quit, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
+ button = gtk_button_new_with_label ("Confirmation dialog");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (button_clicked_cb),
+ window);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (window);
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 3006b78..e58ee21 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,8 +1,33 @@
+ido_built_public_sources = \
+ idotypebuiltins.h
+
+stamp_files = \
+ idotypebuiltins.h \
+ idotypebuiltins.c
+
sources_h = \
idoentrymenuitem.h \
+ idomessagedialog.h \
idoscalemenuitem.h \
+ idotimeline.h \
libido.h
+EXTRA_DIST = \
+ ido.list
+
+idotypebuiltins.h: stamp-idotypebuiltins.h
+
+stamp-idotypebuiltins.h: $(sources_h)
+ ( cd $(srcdir) && $(GLIB_MKENUMS) --template idotypebuiltins.h.template \
+ $(sources_h) ) >> xgen-gtbh \
+ && (cmp -s xgen-gtbh idotypebuiltins.h || cp xgen-gtbh idotypebuiltins.h ) \
+ && rm -f xgen-gtbh && echo timestamp > $(@F)
+
+idotypebuiltins.c: stamp-idotypebuiltins.h
+ ( cd $(srcdir) && $(GLIB_MKENUMS) --template idotypebuiltins.c.template \
+ $(sources_h) ) > xgen-gtbc \
+ && cp xgen-gtbc idotypebuiltins.c && rm -f xgen-gtbc
+
INCLUDES = \
-I$(srcdir) \
-I$(top_srcdir) \
@@ -22,8 +47,11 @@ AM_CPPFLAGS = \
lib_LTLIBRARIES = libido-0.1.la
libido_0_1_la_SOURCES = \
+ idotypebuiltins.c \
idoentrymenuitem.c \
- idoscalemenuitem.c
+ idomessagedialog.c \
+ idoscalemenuitem.c \
+ idotimeline.c
libidoincludedir=$(includedir)/libido-0.1/libido
diff --git a/src/ido.list b/src/ido.list
new file mode 100644
index 0000000..cd4f64d
--- /dev/null
+++ b/src/ido.list
@@ -0,0 +1 @@
+VOID:POINTER,UINT
diff --git a/src/idomessagedialog.c b/src/idomessagedialog.c
new file mode 100644
index 0000000..8cb34a0
--- /dev/null
+++ b/src/idomessagedialog.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2010 Canonical, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Cody Russell <crussell@canonical.com>
+ *
+ * Design and specification:
+ * Matthew Paul Thomas <mpt@canonical.com>
+ */
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "idomessagedialog.h"
+#include "idotimeline.h"
+
+#define IDO_MESSAGE_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDO_TYPE_MESSAGE_DIALOG, IdoMessageDialogPrivate))
+
+static GtkWidget *ido_message_dialog_get_secondary_label (IdoMessageDialog *dialog);
+static GtkWidget *ido_message_dialog_get_primary_label (IdoMessageDialog *dialog);
+
+typedef struct _IdoMessageDialogPrivate IdoMessageDialogPrivate;
+typedef struct _IdoMessageDialogMorphContext IdoMessageDialogMorphContext;
+
+struct _IdoMessageDialogPrivate
+{
+ GtkWidget *action_area;
+ GtkWidget *primary_label;
+ GtkWidget *secondary_label;
+
+ gboolean expanded;
+};
+
+struct _IdoMessageDialogMorphContext
+{
+ GtkWidget *widget;
+ IdoTimeline *timeline;
+
+ GtkRequisition start;
+ GtkRequisition end;
+};
+
+G_DEFINE_TYPE (IdoMessageDialog, ido_message_dialog, GTK_TYPE_MESSAGE_DIALOG)
+
+static void
+ido_message_dialog_map (GtkWidget *widget)
+{
+ IdoMessageDialog *dialog = IDO_MESSAGE_DIALOG (widget);
+ IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (dialog);
+
+ GTK_WIDGET_CLASS (ido_message_dialog_parent_class)->map (widget);
+
+ priv->primary_label = ido_message_dialog_get_primary_label (dialog);
+ priv->secondary_label = ido_message_dialog_get_secondary_label (dialog);
+
+ gtk_widget_hide (priv->secondary_label);
+
+ gtk_label_set_selectable (GTK_LABEL (priv->primary_label), FALSE);
+ gtk_label_set_selectable (GTK_LABEL (priv->secondary_label), FALSE);
+
+ /* XXX: We really want to use gtk_window_set_deletable (GTK_WINDOW (widget), FALSE)
+ * here, but due to a bug in compiz this is more compatible.
+ *
+ * See: https://bugs.launchpad.net/ubuntu/+source/compiz/+bug/240794
+ */
+ gdk_window_set_functions (widget->window,
+ GDK_FUNC_RESIZE | GDK_FUNC_MOVE);
+
+ ido_message_dialog_get_secondary_label (IDO_MESSAGE_DIALOG (widget));
+}
+
+static IdoMessageDialogMorphContext *
+ido_message_dialog_morph_context_new (GtkWidget *widget,
+ IdoTimeline *timeline,
+ gpointer identifier,
+ GtkRequisition *start,
+ GtkRequisition *end)
+{
+ IdoMessageDialogMorphContext *context;
+
+ context = g_slice_new (IdoMessageDialogMorphContext);
+
+ context->widget = widget;
+ context->timeline = timeline;
+ context->start = *start;
+ context->end = *end;
+
+ return context;
+}
+
+static void
+ido_message_dialog_morph_context_free (IdoMessageDialogMorphContext *context)
+{
+ g_object_unref (context->timeline);
+
+ g_slice_free (IdoMessageDialogMorphContext, context);
+}
+
+static void
+timeline_frame_cb (IdoTimeline *timeline,
+ gdouble progress,
+ gpointer user_data)
+{
+ IdoMessageDialogMorphContext *context = user_data;
+ GtkRequisition start = context->start;
+ GtkRequisition end = context->end;
+ gint width_diff;
+ gint height_diff;
+ gint width, height;
+
+ width_diff = (MAX(start.width, end.width) - MIN(start.width, end.width)) * progress;
+ height_diff = (MAX(start.height, end.height) - MIN(start.height, end.height)) * progress;
+
+ gtk_window_get_size (GTK_WINDOW (context->widget),
+ &width,
+ &height);
+
+ gtk_widget_set_size_request (context->widget,
+ width_diff ? start.width + width_diff : -1,
+ height_diff ? start.height + height_diff : -1);
+}
+
+static void
+timeline_finished_cb (IdoTimeline *timeline,
+ gpointer user_data)
+{
+ IdoMessageDialogMorphContext *context = user_data;
+ IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (context->widget);
+
+ gtk_widget_show (priv->action_area);
+ gtk_widget_show (priv->secondary_label);
+
+ ido_message_dialog_morph_context_free (context);
+}
+
+static gboolean
+button_pressed (GtkWidget *event_box,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ IdoMessageDialog *dialog = (IdoMessageDialog *) user_data;
+ IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (dialog);
+
+ if (!priv->expanded)
+ {
+ GtkRequisition start;
+ GtkRequisition end;
+ IdoTimeline *timeline;
+ IdoMessageDialogMorphContext *context;
+
+ start = GTK_WIDGET (dialog)->requisition;
+
+ priv->expanded = TRUE;
+
+ gtk_widget_show (priv->action_area);
+ gtk_widget_show (priv->secondary_label);
+
+ gtk_widget_size_request (GTK_WIDGET (dialog), &end);
+
+ gtk_widget_hide (priv->action_area);
+ gtk_widget_hide (priv->secondary_label);
+
+ timeline = ido_timeline_new (500);
+ context = ido_message_dialog_morph_context_new (GTK_WIDGET (dialog),
+ timeline,
+ "foo",
+ &start,
+ &end);
+ g_signal_connect (timeline,
+ "frame",
+ G_CALLBACK (timeline_frame_cb),
+ context);
+ g_signal_connect (timeline,
+ "finished",
+ G_CALLBACK (timeline_finished_cb),
+ context);
+
+ ido_timeline_start (timeline);
+ }
+
+ return FALSE;
+}
+
+static void
+ido_message_dialog_constructed (GObject *object)
+{
+ IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (object);
+ GtkWidget *vbox;
+ GtkWidget *event_box;
+
+ event_box = gtk_event_box_new ();
+ gtk_widget_show (event_box);
+ g_signal_connect (event_box,
+ "button-press-event",
+ G_CALLBACK (button_pressed),
+ object);
+
+ vbox = GTK_DIALOG (object)->vbox;
+ priv->action_area = gtk_dialog_get_action_area (GTK_DIALOG (object));
+
+ g_object_ref (G_OBJECT (vbox));
+ gtk_container_remove (GTK_CONTAINER (object), vbox);
+ gtk_container_add (GTK_CONTAINER (event_box), vbox);
+ gtk_container_add (GTK_CONTAINER (object), event_box);
+
+ gtk_widget_hide (priv->action_area);
+}
+
+static void
+ido_message_dialog_class_init (IdoMessageDialogClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ object_class->constructed = ido_message_dialog_constructed;
+
+ widget_class->map = ido_message_dialog_map;
+
+ g_type_class_add_private (object_class, sizeof (IdoMessageDialogPrivate));
+}
+
+static void
+ido_message_dialog_init (IdoMessageDialog *dialog)
+{
+ IdoMessageDialogPrivate *priv;
+
+ priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (dialog);
+}
+
+/**
+ * ido_message_dialog_new:
+ * @parent: transient parent, or %NULL for none
+ * @flags: flags
+ * @type: type of message
+ * @buttons: a set of buttons to use
+ * @message_format: printf()-style format string, or %NULL
+ * @Varargs: arguments for @message_format
+ *
+ * Creates a new message dialog, which is based upon
+ * GtkMessageDialog so it shares API and functionality
+ * with it. IdoMessageDialog differs in that it has two
+ * states. The initial state hides the action buttons
+ * and the secondary message. When a user clicks on the
+ * dialog it will expand to provide the secondary message
+ * and the action buttons.
+ *
+ * Return value: a new #IdoMessageDialog
+ **/
+GtkWidget*
+ido_message_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ GtkMessageType type,
+ GtkButtonsType buttons,
+ const gchar *message_format,
+ ...)
+{
+ GtkWidget *widget;
+ GtkDialog *dialog;
+ gchar* msg = NULL;
+ va_list args;
+
+ g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
+
+ widget = g_object_new (IDO_TYPE_MESSAGE_DIALOG,
+ "message-type", type,
+ "buttons", buttons,
+ NULL);
+ dialog = GTK_DIALOG (widget);
+
+ if (flags & GTK_DIALOG_NO_SEPARATOR)
+ {
+ g_warning ("The GTK_DIALOG_NO_SEPARATOR flag cannot be used for IdoMessageDialog");
+ flags &= ~GTK_DIALOG_NO_SEPARATOR;
+ }
+
+ if (message_format)
+ {
+ va_start (args, message_format);
+ msg = g_strdup_vprintf (message_format, args);
+ va_end (args);
+
+ gtk_label_set_text (GTK_LABEL (GTK_MESSAGE_DIALOG (widget)->label),
+ msg);
+
+ g_free (msg);
+ }
+
+ if (parent != NULL)
+ gtk_window_set_transient_for (GTK_WINDOW (widget),
+ GTK_WINDOW (parent));
+
+ if (flags & GTK_DIALOG_MODAL)
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
+
+ return widget;
+}
+
+GtkWidget*
+ido_message_dialog_new_with_markup (GtkWindow *parent,
+ GtkDialogFlags flags,
+ GtkMessageType type,
+ GtkButtonsType buttons,
+ const gchar *message_format,
+ ...)
+{
+ GtkWidget *widget;
+ va_list args;
+ gchar *msg = NULL;
+
+ g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
+
+ widget = ido_message_dialog_new (parent, flags, type, buttons, NULL);
+
+ if (message_format)
+ {
+ va_start (args, message_format);
+ msg = g_markup_vprintf_escaped (message_format, args);
+ va_end (args);
+
+ gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (widget), msg);
+
+ g_free (msg);
+ }
+
+ return widget;
+}
+
+ /*
+ * This is almost humorously stupid. We jump through some hoops and kill
+ * a few kittens here because we want to preserve API compatibility with
+ * GtkMessageDialog and extend it instead of duplicating its functionality.
+ * If only GtkMessageDialog were easier to extend then maybe all those
+ * kittens wouldn't have had to die...
+ */
+static GtkWidget *
+ido_message_dialog_get_label (IdoMessageDialog *dialog, gboolean primary)
+{
+ GList *list;
+ gchar *text;
+ gchar *secondary_text;
+
+ g_object_get (G_OBJECT (dialog),
+ "text", &text,
+ "secondary-text", &secondary_text,
+ NULL);
+
+ g_return_val_if_fail (IDO_IS_MESSAGE_DIALOG (dialog), NULL);
+
+ for (list = GTK_BOX (GTK_DIALOG (dialog)->vbox)->children; list != NULL; list = list->next)
+ {
+ GtkBoxChild *child = (GtkBoxChild *)list->data;
+
+ if (G_TYPE_FROM_INSTANCE (child->widget) == GTK_TYPE_HBOX)
+ {
+ GList *hlist;
+ GtkWidget *hbox = child->widget;
+
+ for (hlist = GTK_BOX (hbox)->children; hlist != NULL; hlist = hlist->next)
+ {
+ GtkBoxChild *hchild = (GtkBoxChild *)hlist->data;
+
+ if (G_TYPE_FROM_INSTANCE (hchild->widget) == GTK_TYPE_VBOX)
+ {
+ GList *vlist;
+ GtkWidget *vbox = hchild->widget;
+
+ for (vlist = GTK_BOX (vbox)->children; vlist != NULL; vlist = vlist->next)
+ {
+ GtkBoxChild *vchild = (GtkBoxChild *)vlist->data;
+ GtkLabel *label;
+
+ label = GTK_LABEL (vchild->widget);
+
+ if (strcmp ((primary ? text : secondary_text), label->label) == 0)
+ {
+ return vchild->widget;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static GtkWidget *
+ido_message_dialog_get_secondary_label (IdoMessageDialog *dialog)
+{
+ return ido_message_dialog_get_label (dialog, FALSE);
+}
+
+static GtkWidget *
+ido_message_dialog_get_primary_label (IdoMessageDialog *dialog)
+{
+ return ido_message_dialog_get_label (dialog, TRUE);
+}
diff --git a/src/idomessagedialog.h b/src/idomessagedialog.h
new file mode 100644
index 0000000..06f733b
--- /dev/null
+++ b/src/idomessagedialog.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Canonical, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Cody Russell <crussell@canonical.com>
+ *
+ * Design and specification:
+ * Matthew Paul Thomas <mpt@canonical.com>
+ */
+
+#ifndef __IDO_MESSAGE_DIALOG_H__
+#define __IDO_MESSAGE_DIALOG_H__
+
+#include <gtk/gtkmessagedialog.h>
+
+#define IDO_TYPE_MESSAGE_DIALOG (ido_message_dialog_get_type ())
+#define IDO_MESSAGE_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), IDO_TYPE_MESSAGE_DIALOG, IdoMessageDialog))
+#define IDO_MESSAGE_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), IDO_TYPE_MESSAGE_DIALOG, IdoMessageDialogClass))
+#define IDO_IS_MESSAGE_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), IDO_TYPE_MESSAGE_DIALOG))
+#define IDO_IS_MESSAGE_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), IDO_TYPE_MESSAGE_DIALOG))
+#define IDO_MESSAGE_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), IDO_TYPE_MESSAGE_DIALOG, IdoMessageDialogClass))
+
+typedef struct _IdoMessageDialog IdoMessageDialog;
+typedef struct _IdoMessageDialogClass IdoMessageDialogClass;
+
+struct _IdoMessageDialog
+{
+ GtkMessageDialog parent_instance;
+};
+
+struct _IdoMessageDialogClass
+{
+ GtkMessageDialogClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_ido_reserved1) (void);
+ void (*_ido_reserved2) (void);
+ void (*_ido_reserved3) (void);
+ void (*_ido_reserved4) (void);
+};
+
+GType ido_message_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget* ido_message_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ GtkMessageType type,
+ GtkButtonsType buttons,
+ const gchar *message_format,
+ ...) G_GNUC_PRINTF (5, 6);
+
+GtkWidget* ido_message_dialog_new_with_markup (GtkWindow *parent,
+ GtkDialogFlags flags,
+ GtkMessageType type,
+ GtkButtonsType buttons,
+ const gchar *message_format,
+ ...) G_GNUC_PRINTF (5, 6);
+
+G_END_DECLS
+
+#endif /* __IDO_MESSAGE_DIALOG_H__ */
diff --git a/src/idotimeline.c b/src/idotimeline.c
new file mode 100644
index 0000000..f86f1f2
--- /dev/null
+++ b/src/idotimeline.c
@@ -0,0 +1,721 @@
+/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
+/* gtktimeline.c
+ *
+ * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "idotimeline.h"
+#include "idotypebuiltins.h"
+
+#include <gtk/gtksettings.h>
+#include <math.h>
+
+#define IDO_TIMELINE_GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), IDO_TYPE_TIMELINE, IdoTimelinePriv))
+#define MSECS_PER_SEC 1000
+#define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
+#define DEFAULT_FPS 30
+
+typedef struct IdoTimelinePriv IdoTimelinePriv;
+
+struct IdoTimelinePriv
+{
+ guint duration;
+ guint fps;
+ guint source_id;
+
+ GTimer *timer;
+
+ gdouble progress;
+ gdouble last_progress;
+
+ GdkScreen *screen;
+
+ guint animations_enabled : 1;
+ guint loop : 1;
+ guint direction : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_FPS,
+ PROP_DURATION,
+ PROP_LOOP,
+ PROP_DIRECTION,
+ PROP_SCREEN
+};
+
+enum {
+ STARTED,
+ PAUSED,
+ FINISHED,
+ FRAME,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+
+static void ido_timeline_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void ido_timeline_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void ido_timeline_finalize (GObject *object);
+
+
+G_DEFINE_TYPE (IdoTimeline, ido_timeline, G_TYPE_OBJECT)
+
+
+static void
+ido_timeline_class_init (IdoTimelineClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = ido_timeline_set_property;
+ object_class->get_property = ido_timeline_get_property;
+ object_class->finalize = ido_timeline_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_FPS,
+ g_param_spec_uint ("fps",
+ "FPS",
+ "Frames per second for the timeline",
+ 1, G_MAXUINT,
+ DEFAULT_FPS,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_DURATION,
+ g_param_spec_uint ("duration",
+ "Animation Duration",
+ "Animation Duration",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOOP,
+ g_param_spec_boolean ("loop",
+ "Loop",
+ "Whether the timeline loops or not",
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_DIRECTION,
+ g_param_spec_enum ("direction",
+ "Direction",
+ "Whether the timeline moves forward or backward in time",
+ IDO_TYPE_TIMELINE_DIRECTION,
+ IDO_TIMELINE_DIRECTION_FORWARD,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_SCREEN,
+ g_param_spec_object ("screen",
+ "Screen",
+ "Screen to get the settings from",
+ GDK_TYPE_SCREEN,
+ G_PARAM_READWRITE));
+
+ signals[STARTED] =
+ g_signal_new ("started",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdoTimelineClass, started),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[PAUSED] =
+ g_signal_new ("paused",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdoTimelineClass, paused),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[FINISHED] =
+ g_signal_new ("finished",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdoTimelineClass, finished),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[FRAME] =
+ g_signal_new ("frame",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdoTimelineClass, frame),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1,
+ G_TYPE_DOUBLE);
+
+ g_type_class_add_private (klass, sizeof (IdoTimelinePriv));
+}
+
+static void
+ido_timeline_init (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ priv->fps = DEFAULT_FPS;
+ priv->duration = 0.0;
+ priv->direction = IDO_TIMELINE_DIRECTION_FORWARD;
+ priv->screen = gdk_screen_get_default ();
+
+ priv->last_progress = 0;
+}
+
+static void
+ido_timeline_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdoTimeline *timeline;
+ IdoTimelinePriv *priv;
+
+ timeline = IDO_TIMELINE (object);
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ switch (prop_id)
+ {
+ case PROP_FPS:
+ ido_timeline_set_fps (timeline, g_value_get_uint (value));
+ break;
+ case PROP_DURATION:
+ ido_timeline_set_duration (timeline, g_value_get_uint (value));
+ break;
+ case PROP_LOOP:
+ ido_timeline_set_loop (timeline, g_value_get_boolean (value));
+ break;
+ case PROP_DIRECTION:
+ ido_timeline_set_direction (timeline, g_value_get_enum (value));
+ break;
+ case PROP_SCREEN:
+ ido_timeline_set_screen (timeline,
+ GDK_SCREEN (g_value_get_object (value)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ido_timeline_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdoTimeline *timeline;
+ IdoTimelinePriv *priv;
+
+ timeline = IDO_TIMELINE (object);
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ switch (prop_id)
+ {
+ case PROP_FPS:
+ g_value_set_uint (value, priv->fps);
+ break;
+ case PROP_DURATION:
+ g_value_set_uint (value, priv->duration);
+ break;
+ case PROP_LOOP:
+ g_value_set_boolean (value, priv->loop);
+ break;
+ case PROP_DIRECTION:
+ g_value_set_enum (value, priv->direction);
+ break;
+ case PROP_SCREEN:
+ g_value_set_object (value, priv->screen);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ido_timeline_finalize (GObject *object)
+{
+ IdoTimelinePriv *priv;
+
+ priv = IDO_TIMELINE_GET_PRIV (object);
+
+ if (priv->source_id)
+ {
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ }
+
+ if (priv->timer)
+ g_timer_destroy (priv->timer);
+
+ G_OBJECT_CLASS (ido_timeline_parent_class)->finalize (object);
+}
+
+static gboolean
+ido_timeline_run_frame (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+ gdouble delta_progress, progress;
+ guint elapsed_time;
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
+ g_timer_start (priv->timer);
+
+ if (priv->animations_enabled)
+ {
+ delta_progress = (gdouble) elapsed_time / priv->duration;
+ progress = priv->last_progress;
+
+ if (priv->direction == IDO_TIMELINE_DIRECTION_BACKWARD)
+ progress -= delta_progress;
+ else
+ progress += delta_progress;
+
+ priv->last_progress = progress;
+
+ progress = CLAMP (progress, 0., 1.);
+ }
+ else
+ progress = (priv->direction == IDO_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
+
+ priv->progress = progress;
+ g_signal_emit (timeline, signals [FRAME], 0, progress);
+
+ if ((priv->direction == IDO_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
+ (priv->direction == IDO_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
+ {
+ if (!priv->loop)
+ {
+ if (priv->source_id)
+ {
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ }
+ g_timer_stop (priv->timer);
+ g_signal_emit (timeline, signals [FINISHED], 0);
+ return FALSE;
+ }
+ else
+ ido_timeline_rewind (timeline);
+ }
+
+ return TRUE;
+}
+
+/**
+ * ido_timeline_new:
+ * @duration: duration in milliseconds for the timeline
+ *
+ * Creates a new #IdoTimeline with the specified number of frames.
+ *
+ * Return Value: the newly created #IdoTimeline
+ **/
+IdoTimeline *
+ido_timeline_new (guint duration)
+{
+ return g_object_new (IDO_TYPE_TIMELINE,
+ "duration", duration,
+ NULL);
+}
+
+IdoTimeline *
+ido_timeline_new_for_screen (guint duration,
+ GdkScreen *screen)
+{
+ return g_object_new (IDO_TYPE_TIMELINE,
+ "duration", duration,
+ "screen", screen,
+ NULL);
+}
+
+/**
+ * ido_timeline_start:
+ * @timeline: A #IdoTimeline
+ *
+ * Runs the timeline from the current frame.
+ **/
+void
+ido_timeline_start (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+ gboolean enable_animations = FALSE;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ if (!priv->source_id)
+ {
+ if (priv->timer)
+ g_timer_continue (priv->timer);
+ else
+ priv->timer = g_timer_new ();
+
+ /* sanity check */
+ g_assert (priv->fps > 0);
+
+ if (priv->screen)
+ {
+#if 0
+ GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
+ g_object_get (settings, "ido-enable-animations", &enable_animations, NULL);
+#else
+ // XXX
+ enable_animations = TRUE;
+#endif
+ }
+
+ priv->animations_enabled = (enable_animations == TRUE);
+
+ g_signal_emit (timeline, signals [STARTED], 0);
+
+ if (enable_animations)
+ priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
+ (GSourceFunc) ido_timeline_run_frame,
+ timeline);
+ else
+ priv->source_id = gdk_threads_add_idle ((GSourceFunc) ido_timeline_run_frame,
+ timeline);
+ }
+}
+
+/**
+ * ido_timeline_pause:
+ * @timeline: A #IdoTimeline
+ *
+ * Pauses the timeline.
+ **/
+void
+ido_timeline_pause (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ if (priv->source_id)
+ {
+ g_timer_stop (priv->timer);
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ g_signal_emit (timeline, signals [PAUSED], 0);
+ }
+}
+
+/**
+ * ido_timeline_rewind:
+ * @timeline: A #IdoTimeline
+ *
+ * Rewinds the timeline.
+ **/
+void
+ido_timeline_rewind (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ if (ido_timeline_get_direction(timeline) != IDO_TIMELINE_DIRECTION_FORWARD)
+ priv->progress = priv->last_progress = 1.;
+ else
+ priv->progress = priv->last_progress = 0.;
+
+ /* reset timer */
+ if (priv->timer)
+ {
+ g_timer_start (priv->timer);
+
+ if (!priv->source_id)
+ g_timer_stop (priv->timer);
+ }
+}
+
+/**
+ * ido_timeline_is_running:
+ * @timeline: A #IdoTimeline
+ *
+ * Returns whether the timeline is running or not.
+ *
+ * Return Value: %TRUE if the timeline is running
+ **/
+gboolean
+ido_timeline_is_running (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_val_if_fail (IDO_IS_TIMELINE (timeline), FALSE);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ return (priv->source_id != 0);
+}
+
+/**
+ * ido_timeline_get_fps:
+ * @timeline: A #IdoTimeline
+ *
+ * Returns the number of frames per second.
+ *
+ * Return Value: frames per second
+ **/
+guint
+ido_timeline_get_fps (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_val_if_fail (IDO_IS_TIMELINE (timeline), 1);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+ return priv->fps;
+}
+
+/**
+ * ido_timeline_set_fps:
+ * @timeline: A #IdoTimeline
+ * @fps: frames per second
+ *
+ * Sets the number of frames per second that
+ * the timeline will play.
+ **/
+void
+ido_timeline_set_fps (IdoTimeline *timeline,
+ guint fps)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+ g_return_if_fail (fps > 0);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ priv->fps = fps;
+
+ if (ido_timeline_is_running (timeline))
+ {
+ g_source_remove (priv->source_id);
+ priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
+ (GSourceFunc) ido_timeline_run_frame,
+ timeline);
+ }
+
+ g_object_notify (G_OBJECT (timeline), "fps");
+}
+
+/**
+ * ido_timeline_get_loop:
+ * @timeline: A #IdoTimeline
+ *
+ * Returns whether the timeline loops to the
+ * beginning when it has reached the end.
+ *
+ * Return Value: %TRUE if the timeline loops
+ **/
+gboolean
+ido_timeline_get_loop (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_val_if_fail (IDO_IS_TIMELINE (timeline), FALSE);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+ return priv->loop;
+}
+
+/**
+ * ido_timeline_set_loop:
+ * @timeline: A #IdoTimeline
+ * @loop: %TRUE to make the timeline loop
+ *
+ * Sets whether the timeline loops to the beginning
+ * when it has reached the end.
+ **/
+void
+ido_timeline_set_loop (IdoTimeline *timeline,
+ gboolean loop)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ if (loop != priv->loop)
+ {
+ priv->loop = loop;
+ g_object_notify (G_OBJECT (timeline), "loop");
+ }
+}
+
+void
+ido_timeline_set_duration (IdoTimeline *timeline,
+ guint duration)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ if (duration != priv->duration)
+ {
+ priv->duration = duration;
+ g_object_notify (G_OBJECT (timeline), "duration");
+ }
+}
+
+guint
+ido_timeline_get_duration (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_val_if_fail (IDO_IS_TIMELINE (timeline), 0);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ return priv->duration;
+}
+
+/**
+ * ido_timeline_set_direction:
+ * @timeline: A #IdoTimeline
+ * @direction: direction
+ *
+ * Sets the direction of the timeline.
+ **/
+void
+ido_timeline_set_direction (IdoTimeline *timeline,
+ IdoTimelineDirection direction)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ if (direction != priv->direction)
+ {
+ priv->direction = direction;
+ g_object_notify (G_OBJECT (timeline), "direction");
+ }
+}
+
+/**
+ * ido_timeline_get_direction:
+ * @timeline: A #IdoTimeline
+ *
+ * Returns the direction of the timeline.
+ *
+ * Return Value: direction
+ **/
+IdoTimelineDirection
+ido_timeline_get_direction (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_val_if_fail (IDO_IS_TIMELINE (timeline), IDO_TIMELINE_DIRECTION_FORWARD);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+ return priv->direction;
+}
+
+void
+ido_timeline_set_screen (IdoTimeline *timeline,
+ GdkScreen *screen)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_if_fail (IDO_IS_TIMELINE (timeline));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+
+ if (priv->screen)
+ g_object_unref (priv->screen);
+
+ priv->screen = g_object_ref (screen);
+
+ g_object_notify (G_OBJECT (timeline), "screen");
+}
+
+GdkScreen *
+ido_timeline_get_screen (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_val_if_fail (IDO_IS_TIMELINE (timeline), NULL);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+ return priv->screen;
+}
+
+gdouble
+ido_timeline_get_progress (IdoTimeline *timeline)
+{
+ IdoTimelinePriv *priv;
+
+ g_return_val_if_fail (IDO_IS_TIMELINE (timeline), 0.);
+
+ priv = IDO_TIMELINE_GET_PRIV (timeline);
+ return priv->progress;
+}
+
+gdouble
+ido_timeline_calculate_progress (gdouble linear_progress,
+ IdoTimelineProgressType progress_type)
+{
+ gdouble progress;
+
+ progress = linear_progress;
+
+ switch (progress_type)
+ {
+ case IDO_TIMELINE_PROGRESS_LINEAR:
+ break;
+ case IDO_TIMELINE_PROGRESS_SINUSOIDAL:
+ progress = sinf ((progress * G_PI) / 2);
+ break;
+ case IDO_TIMELINE_PROGRESS_EXPONENTIAL:
+ progress *= progress;
+ break;
+ case IDO_TIMELINE_PROGRESS_EASE_IN_EASE_OUT:
+ {
+ progress *= 2;
+
+ if (progress < 1)
+ progress = pow (progress, 3) / 2;
+ else
+ progress = (pow (progress - 2, 3) + 2) / 2;
+ }
+ }
+
+ return progress;
+}
diff --git a/src/idotimeline.h b/src/idotimeline.h
new file mode 100644
index 0000000..66f96a3
--- /dev/null
+++ b/src/idotimeline.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __IDO_TIMELINE_H__
+#define __IDO_TIMELINE_H__
+
+#include <glib-object.h>
+#include <gtk/gtkenums.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define IDO_TYPE_TIMELINE (ido_timeline_get_type ())
+#define IDO_TIMELINE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), IDO_TYPE_TIMELINE, IdoTimeline))
+#define IDO_TIMELINE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), IDO_TYPE_TIMELINE, IdoTimelineClass))
+#define IDO_IS_TIMELINE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), IDO_TYPE_TIMELINE))
+#define IDO_IS_TIMELINE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), IDO_TYPE_TIMELINE))
+#define IDO_TIMELINE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), IDO_TYPE_TIMELINE, IdoTimelineClass))
+
+typedef struct IdoTimeline IdoTimeline;
+typedef struct IdoTimelineClass IdoTimelineClass;
+
+typedef enum {
+ IDO_TIMELINE_DIRECTION_FORWARD,
+ IDO_TIMELINE_DIRECTION_BACKWARD
+} IdoTimelineDirection;
+
+typedef enum {
+ IDO_TIMELINE_PROGRESS_LINEAR,
+ IDO_TIMELINE_PROGRESS_SINUSOIDAL,
+ IDO_TIMELINE_PROGRESS_EXPONENTIAL,
+ IDO_TIMELINE_PROGRESS_EASE_IN_EASE_OUT
+} IdoTimelineProgressType;
+
+struct IdoTimeline
+{
+ GObject parent_instance;
+};
+
+struct IdoTimelineClass
+{
+ GObjectClass parent_class;
+
+ void (* started) (IdoTimeline *timeline);
+ void (* finished) (IdoTimeline *timeline);
+ void (* paused) (IdoTimeline *timeline);
+
+ void (* frame) (IdoTimeline *timeline,
+ gdouble progress);
+
+ void (* __ido_reserved1) (void);
+ void (* __ido_reserved2) (void);
+ void (* __ido_reserved3) (void);
+ void (* __ido_reserved4) (void);
+};
+
+
+GType ido_timeline_get_type (void) G_GNUC_CONST;
+
+IdoTimeline *ido_timeline_new (guint duration);
+IdoTimeline *ido_timeline_new_for_screen (guint duration,
+ GdkScreen *screen);
+
+void ido_timeline_start (IdoTimeline *timeline);
+void ido_timeline_pause (IdoTimeline *timeline);
+void ido_timeline_rewind (IdoTimeline *timeline);
+
+gboolean ido_timeline_is_running (IdoTimeline *timeline);
+
+guint ido_timeline_get_fps (IdoTimeline *timeline);
+void ido_timeline_set_fps (IdoTimeline *timeline,
+ guint fps);
+
+gboolean ido_timeline_get_loop (IdoTimeline *timeline);
+void ido_timeline_set_loop (IdoTimeline *timeline,
+ gboolean loop);
+
+guint ido_timeline_get_duration (IdoTimeline *timeline);
+void ido_timeline_set_duration (IdoTimeline *timeline,
+ guint duration);
+
+GdkScreen *ido_timeline_get_screen (IdoTimeline *timeline);
+void ido_timeline_set_screen (IdoTimeline *timeline,
+ GdkScreen *screen);
+
+IdoTimelineDirection ido_timeline_get_direction (IdoTimeline *timeline);
+void ido_timeline_set_direction (IdoTimeline *timeline,
+ IdoTimelineDirection direction);
+
+gdouble ido_timeline_get_progress (IdoTimeline *timeline);
+
+gdouble ido_timeline_calculate_progress (gdouble linear_progress,
+ IdoTimelineProgressType progress_type);
+
+G_END_DECLS
+
+#endif /* __IDO_TIMELINE_H__ */
diff --git a/src/idotypebuiltins.c.template b/src/idotypebuiltins.c.template
new file mode 100644
index 0000000..38adf1d
--- /dev/null
+++ b/src/idotypebuiltins.c.template
@@ -0,0 +1,31 @@
+/*** BEGIN file-header ***/
+#include "idotypebuiltins.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+#include "@filename@"
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void) {
+ static GType enum_type_id = 0;
+ if (G_UNLIKELY (!enum_type_id))
+ {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ enum_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+ return enum_type_id;
+}
+/*** END value-tail ***/
+
diff --git a/src/idotypebuiltins.h.template b/src/idotypebuiltins.h.template
new file mode 100644
index 0000000..0488673
--- /dev/null
+++ b/src/idotypebuiltins.h.template
@@ -0,0 +1,26 @@
+/*** BEGIN file-header ***/
+#ifndef __IDO_ENUM_TYPES_H__
+#define __IDO_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* !__IDO_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define IDO_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+
+/*** END value-header ***/
+