Add back GtkTimeline

Add back the GtkTimeline code that previously made private and
then removed. It will be hooked up to GdkFrameClock. This commit
purely adds the old code back.

https://bugzilla.gnome.org/show_bug.cgi?id=685460
This commit is contained in:
Owen W. Taylor 2012-07-27 04:56:51 -04:00
parent 344a69880a
commit db89b600e0
3 changed files with 878 additions and 0 deletions

View File

@ -544,6 +544,7 @@ gtk_private_h_sources = \
gtktextutil.h \
gtkthemingbackgroundprivate.h \
gtkthemingengineprivate.h \
gtktimeline.h \
gtktoolpaletteprivate.h \
gtktreedatalist.h \
gtktreeprivate.h \
@ -850,6 +851,7 @@ gtk_base_c_sources = \
gtktextview.c \
gtkthemingbackground.c \
gtkthemingengine.c \
gtktimeline.c \
gtktoggleaction.c \
gtktogglebutton.c \
gtktoggletoolbutton.c \

760
gtk/gtktimeline.c Normal file
View File

@ -0,0 +1,760 @@
/* GTK - The GIMP Toolkit
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <gtk/gtktimeline.h>
#include <gtk/gtktypebuiltins.h>
#include <gtk/gtksettings.h>
#include <math.h>
#define MSECS_PER_SEC 1000
#define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
#define DEFAULT_FPS 30
typedef struct GtkTimelinePriv GtkTimelinePriv;
struct GtkTimelinePriv
{
guint duration;
guint fps;
guint source_id;
GTimer *timer;
gdouble elapsed_time;
gdouble progress;
gdouble last_progress;
GdkScreen *screen;
GtkTimelineProgressType progress_type;
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 gtk_timeline_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_timeline_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void gtk_timeline_finalize (GObject *object);
G_DEFINE_TYPE (GtkTimeline, gtk_timeline, G_TYPE_OBJECT)
static void
gtk_timeline_class_init (GtkTimelineClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = gtk_timeline_set_property;
object_class->get_property = gtk_timeline_get_property;
object_class->finalize = gtk_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_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 (GtkTimelineClass, 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 (GtkTimelineClass, 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 (GtkTimelineClass, 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 (GtkTimelineClass, frame),
NULL, NULL,
g_cclosure_marshal_VOID__DOUBLE,
G_TYPE_NONE, 1,
G_TYPE_DOUBLE);
g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
}
static void
gtk_timeline_init (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
GTK_TYPE_TIMELINE,
GtkTimelinePriv);
priv->fps = DEFAULT_FPS;
priv->duration = 0.0;
priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
priv->screen = gdk_screen_get_default ();
priv->last_progress = 0;
}
static void
gtk_timeline_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTimeline *timeline;
timeline = GTK_TIMELINE (object);
switch (prop_id)
{
case PROP_FPS:
_gtk_timeline_set_fps (timeline, g_value_get_uint (value));
break;
case PROP_DURATION:
gtk_timeline_set_duration (timeline, g_value_get_uint (value));
break;
case PROP_LOOP:
gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
break;
case PROP_DIRECTION:
gtk_timeline_set_direction (timeline, g_value_get_enum (value));
break;
case PROP_SCREEN:
gtk_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
gtk_timeline_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTimeline *timeline;
GtkTimelinePriv *priv;
timeline = GTK_TIMELINE (object);
priv = timeline->priv;
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
gtk_timeline_finalize (GObject *object)
{
GtkTimelinePriv *priv;
GtkTimeline *timeline;
timeline = (GtkTimeline *) object;
priv = timeline->priv;
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 (gtk_timeline_parent_class)->finalize (object);
}
static gdouble
calculate_progress (gdouble linear_progress,
GtkTimelineProgressType progress_type)
{
gdouble progress;
progress = linear_progress;
switch (progress_type)
{
case GTK_TIMELINE_PROGRESS_LINEAR:
break;
case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
progress *= 2;
if (progress < 1)
progress = pow (progress, 3) / 2;
else
progress = (pow (progress - 2, 3) + 2) / 2;
break;
case GTK_TIMELINE_PROGRESS_EASE:
progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
break;
case GTK_TIMELINE_PROGRESS_EASE_IN:
progress = pow (progress, 3);
break;
case GTK_TIMELINE_PROGRESS_EASE_OUT:
progress = pow (progress - 1, 3) + 1;
break;
default:
g_warning ("Timeline progress type not implemented");
}
return progress;
}
static gboolean
gtk_timeline_run_frame (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
gdouble delta_progress, progress;
/* the user may unref us during the signals, so save ourselves */
g_object_ref (timeline);
priv = timeline->priv;
priv->elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
g_timer_start (priv->timer);
if (priv->animations_enabled)
{
delta_progress = (gdouble) priv->elapsed_time / priv->duration;
progress = priv->last_progress;
if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
progress -= delta_progress;
else
progress += delta_progress;
priv->last_progress = progress;
progress = CLAMP (progress, 0., 1.);
}
else
progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
priv->progress = progress;
g_signal_emit (timeline, signals [FRAME], 0,
calculate_progress (progress, priv->progress_type));
if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
(priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
{
gboolean loop;
loop = priv->loop && priv->animations_enabled;
if (loop)
gtk_timeline_rewind (timeline);
else
{
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);
g_object_unref (timeline);
return FALSE;
}
}
g_object_unref (timeline);
return TRUE;
}
/*
* gtk_timeline_new:
* @duration: duration in milliseconds for the timeline
*
* Creates a new #GtkTimeline with the specified number of frames.
*
* Return Value: the newly created #GtkTimeline
*/
GtkTimeline *
gtk_timeline_new (guint duration)
{
return g_object_new (GTK_TYPE_TIMELINE,
"duration", duration,
NULL);
}
GtkTimeline *
gtk_timeline_new_for_screen (guint duration,
GdkScreen *screen)
{
return g_object_new (GTK_TYPE_TIMELINE,
"duration", duration,
"screen", screen,
NULL);
}
/*
* gtk_timeline_start:
* @timeline: A #GtkTimeline
*
* Runs the timeline from the current frame.
*/
void
gtk_timeline_start (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
GtkSettings *settings;
gboolean enable_animations = FALSE;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
priv = timeline->priv;
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)
{
settings = gtk_settings_get_for_screen (priv->screen);
g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
}
priv->animations_enabled = enable_animations;
g_signal_emit (timeline, signals [STARTED], 0);
if (enable_animations)
priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
(GSourceFunc) gtk_timeline_run_frame,
timeline);
else
priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
timeline);
}
}
/*
* gtk_timeline_pause:
* @timeline: A #GtkTimeline
*
* Pauses the timeline.
*/
void
gtk_timeline_pause (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
priv = timeline->priv;
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);
}
}
/*
* gtk_timeline_rewind:
* @timeline: A #GtkTimeline
*
* Rewinds the timeline.
*/
void
gtk_timeline_rewind (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
priv = timeline->priv;
if (gtk_timeline_get_direction (timeline) != GTK_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);
}
}
/*
* gtk_timeline_is_running:
* @timeline: A #GtkTimeline
*
* Returns whether the timeline is running or not.
*
* Return Value: %TRUE if the timeline is running
*/
gboolean
gtk_timeline_is_running (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
priv = timeline->priv;
return (priv->source_id != 0);
}
/*
* gtk_timeline_get_elapsed_time:
* @timeline: A #GtkTimeline
*
* Returns the elapsed time since the last GtkTimeline::frame signal
*
* Return Value: elapsed time in milliseconds since the last frame
*/
guint
gtk_timeline_get_elapsed_time (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
priv = timeline->priv;
return priv->elapsed_time;
}
/*
* gtk_timeline_get_fps:
* @timeline: A #GtkTimeline
*
* Returns the number of frames per second.
*
* Return Value: frames per second
*/
guint
gtk_timeline_get_fps (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
priv = timeline->priv;
return priv->fps;
}
/*
* gtk_timeline_set_fps:
* @timeline: A #GtkTimeline
* @fps: frames per second
*
* Sets the number of frames per second that
* the timeline will play.
*/
void
gtk_timeline_set_fps (GtkTimeline *timeline,
guint fps)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
g_return_if_fail (fps > 0);
priv = timeline->priv;
priv->fps = fps;
if (gtk_timeline_is_running (timeline))
{
g_source_remove (priv->source_id);
priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
(GSourceFunc) gtk_timeline_run_frame,
timeline);
}
g_object_notify (G_OBJECT (timeline), "fps");
}
/**
* gtk_timeline_get_loop:
* @timeline: A #GtkTimeline
*
* Returns whether the timeline loops to the
* beginning when it has reached the end.
*
* Return Value: %TRUE if the timeline loops
*/
gboolean
gtk_timeline_get_loop (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
priv = timeline->priv;
return priv->loop;
}
/*
* gtk_timeline_set_loop:
* @timeline: A #GtkTimeline
* @loop: %TRUE to make the timeline loop
*
* Sets whether the timeline loops to the beginning
* when it has reached the end.
*/
void
gtk_timeline_set_loop (GtkTimeline *timeline,
gboolean loop)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
priv = timeline->priv;
if (loop != priv->loop)
{
priv->loop = loop;
g_object_notify (G_OBJECT (timeline), "loop");
}
}
void
gtk_timeline_set_duration (GtkTimeline *timeline,
guint duration)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
priv = timeline->priv;
if (duration != priv->duration)
{
priv->duration = duration;
g_object_notify (G_OBJECT (timeline), "duration");
}
}
guint
gtk_timeline_get_duration (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
priv = timeline->priv;
return priv->duration;
}
/*
* gtk_timeline_set_direction:
* @timeline: A #GtkTimeline
* @direction: direction
*
* Sets the direction of the timeline.
*/
void
gtk_timeline_set_direction (GtkTimeline *timeline,
GtkTimelineDirection direction)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
priv = timeline->priv;
priv->direction = direction;
}
/*
* gtk_timeline_get_direction:
* @timeline: A #GtkTimeline
*
* Returns the direction of the timeline.
*
* Return Value: direction
*/
GtkTimelineDirection
gtk_timeline_get_direction (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
priv = timeline->priv;
return priv->direction;
}
void
gtk_timeline_set_screen (GtkTimeline *timeline,
GdkScreen *screen)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
g_return_if_fail (GDK_IS_SCREEN (screen));
priv = timeline->priv;
if (priv->screen)
g_object_unref (priv->screen);
priv->screen = g_object_ref (screen);
g_object_notify (G_OBJECT (timeline), "screen");
}
GdkScreen *
gtk_timeline_get_screen (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
priv = timeline->priv;
return priv->screen;
}
gdouble
gtk_timeline_get_progress (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
priv = timeline->priv;
return calculate_progress (priv->progress, priv->progress_type);
}
GtkTimelineProgressType
gtk_timeline_get_progress_type (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
priv = timeline->priv;
return priv->progress_type;
}
void
gtk_timeline_set_progress_type (GtkTimeline *timeline,
GtkTimelineProgressType progress_type)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
priv = timeline->priv;
priv->progress_type = progress_type;
}

116
gtk/gtktimeline.h Normal file
View File

@ -0,0 +1,116 @@
/* GTK - The GIMP Toolkit
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_TIMELINE_H__
#define __GTK_TIMELINE_H__
#include <glib-object.h>
#include <gtk/gtkenums.h>
#include <gdk/gdk.h>
G_BEGIN_DECLS
#define GTK_TYPE_TIMELINE (gtk_timeline_get_type ())
#define GTK_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TIMELINE, GtkTimeline))
#define GTK_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TIMELINE, GtkTimelineClass))
#define GTK_IS_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TIMELINE))
#define GTK_IS_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TIMELINE))
#define GTK_TIMELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TIMELINE, GtkTimelineClass))
typedef struct GtkTimeline GtkTimeline;
typedef struct GtkTimelineClass GtkTimelineClass;
typedef enum {
GTK_TIMELINE_DIRECTION_FORWARD,
GTK_TIMELINE_DIRECTION_BACKWARD
} GtkTimelineDirection;
typedef enum {
GTK_TIMELINE_PROGRESS_LINEAR,
GTK_TIMELINE_PROGRESS_EASE,
GTK_TIMELINE_PROGRESS_EASE_IN,
GTK_TIMELINE_PROGRESS_EASE_OUT,
GTK_TIMELINE_PROGRESS_EASE_IN_OUT
} GtkTimelineProgressType;
struct GtkTimeline
{
GObject parent_instance;
gpointer priv;
};
struct GtkTimelineClass
{
GObjectClass parent_class;
void (* started) (GtkTimeline *timeline);
void (* finished) (GtkTimeline *timeline);
void (* paused) (GtkTimeline *timeline);
void (* frame) (GtkTimeline *timeline,
gdouble progress);
void (* __gtk_reserved1) (void);
void (* __gtk_reserved2) (void);
void (* __gtk_reserved3) (void);
void (* __gtk_reserved4) (void);
};
GType gtk_timeline_get_type (void) G_GNUC_CONST;
GtkTimeline * gtk_timeline_new (guint duration);
GtkTimeline * gtk_timeline_new_for_screen (guint duration,
GdkScreen *screen);
void gtk_timeline_start (GtkTimeline *timeline);
void gtk_timeline_pause (GtkTimeline *timeline);
void gtk_timeline_rewind (GtkTimeline *timeline);
gboolean gtk_timeline_is_running (GtkTimeline *timeline);
guint gtk_timeline_get_elapsed_time (GtkTimeline *timeline);
guint gtk_timeline_get_fps (GtkTimeline *timeline);
void gtk_timeline_set_fps (GtkTimeline *timeline,
guint fps);
gboolean gtk_timeline_get_loop (GtkTimeline *timeline);
void gtk_timeline_set_loop (GtkTimeline *timeline,
gboolean loop);
guint gtk_timeline_get_duration (GtkTimeline *timeline);
void gtk_timeline_set_duration (GtkTimeline *timeline,
guint duration);
GdkScreen * gtk_timeline_get_screen (GtkTimeline *timeline);
void gtk_timeline_set_screen (GtkTimeline *timeline,
GdkScreen *screen);
GtkTimelineDirection gtk_timeline_get_direction (GtkTimeline *timeline);
void gtk_timeline_set_direction (GtkTimeline *timeline,
GtkTimelineDirection direction);
gdouble gtk_timeline_get_progress (GtkTimeline *timeline);
GtkTimelineProgressType gtk_timeline_get_progress_type (GtkTimeline *timeline);
void gtk_timeline_set_progress_type (GtkTimeline *timeline,
GtkTimelineProgressType progress_type);
G_END_DECLS
#endif /* _GTK_TIMELINE_H__ */