diff options
-rw-r--r-- | example/Makefile.am | 15 | ||||
-rw-r--r-- | example/messagedialog.c | 57 | ||||
-rw-r--r-- | src/Makefile.am | 30 | ||||
-rw-r--r-- | src/ido.list | 1 | ||||
-rw-r--r-- | src/idomessagedialog.c | 416 | ||||
-rw-r--r-- | src/idomessagedialog.h | 75 | ||||
-rw-r--r-- | src/idotimeline.c | 721 | ||||
-rw-r--r-- | src/idotimeline.h | 113 | ||||
-rw-r--r-- | src/idotypebuiltins.c.template | 31 | ||||
-rw-r--r-- | src/idotypebuiltins.h.template | 26 |
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 ***/ + |