hippo-canvas r7268 - in trunk: . common/hippo docs linux/hippo python tests



Author: otaylor
Date: Tue Jun 10 21:30:02 2008
New Revision: 7268
URL: http://svn.gnome.org/viewvc/hippo-canvas?rev=7268&view=rev

Log:
HippoAnimationManager: Manage the timing and progress of animations 
  in a canvas.
HippoAnimation: An abstract single animation
hippo-canvas-context.[ch] hippo-canvas-box.c: Add get_animation_manager()
 to HippoCanvasContext()

linux/hippo/hippo-canvas-helper.c: Hook up HippoAnimationManager

tests/expand-box.py: prototype of animated cross-fades when a
  HippoCanvasBox changes
tests/test-transparent.py tests/*.svg tests/*.png: Add animation
  to the transparent window example; make the decorations more
  like some of our bigboard mockups

common/hippo/hippo-canvas-style.c: 
  hippo_canvas_style_get_background_theme_image() was missing 
  'style' in the name.


Added:
   trunk/common/hippo/hippo-animation-manager.c
   trunk/common/hippo/hippo-animation-manager.h
   trunk/common/hippo/hippo-animation.c
   trunk/common/hippo/hippo-animation.h
   trunk/tests/bottom.svg
   trunk/tests/close-x-prelight.svg
   trunk/tests/close-x.svg
   trunk/tests/expand_box.py
   trunk/tests/grippy.png   (contents, props changed)
   trunk/tests/header-collapsed.svg
   trunk/tests/header-right-prelight.svg
   trunk/tests/header-right.svg
Modified:
   trunk/   (props changed)
   trunk/Makefile-canvas-sources.am
   trunk/common/hippo/   (props changed)
   trunk/common/hippo/hippo-canvas-box.c
   trunk/common/hippo/hippo-canvas-context.c
   trunk/common/hippo/hippo-canvas-context.h
   trunk/common/hippo/hippo-canvas-marshal.list
   trunk/common/hippo/hippo-canvas-style.c
   trunk/common/hippo/hippo-canvas-style.h
   trunk/docs/   (props changed)
   trunk/linux/hippo/hippo-canvas-helper.c
   trunk/python/hippo.defs
   trunk/tests/header.svg
   trunk/tests/main.svg
   trunk/tests/test-transparent.css
   trunk/tests/test-transparent.py

Modified: trunk/Makefile-canvas-sources.am
==============================================================================
--- trunk/Makefile-canvas-sources.am	(original)
+++ trunk/Makefile-canvas-sources.am	Tue Jun 10 21:30:02 2008
@@ -68,7 +68,9 @@
 	$(CANVAS_ENUMS_HEADER_STAMP)
 
 COMMON_CANVAS_HEADERFILES = 					\
-	$(CANVASSRCDIR)/common/hippo/hippo-canvas-box.h		\
+	$(CANVASSRCDIR)/common/hippo/hippo-animation.h			\
+	$(CANVASSRCDIR)/common/hippo/hippo-animation-manager.h		\
+	$(CANVASSRCDIR)/common/hippo/hippo-canvas-box.h			\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-container.h		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-context.h		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-gradient.h		\
@@ -94,6 +96,8 @@
 
 COMMON_CANVAS_SOURCEFILES = 					\
 	$(COMMON_CANVAS_HEADERFILES)				\
+	$(CANVASSRCDIR)/common/hippo/hippo-animation.c			\
+	$(CANVASSRCDIR)/common/hippo/hippo-animation-manager.c		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-internal.h		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-box.c		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-container.c		\

Added: trunk/common/hippo/hippo-animation-manager.c
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-animation-manager.c	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,261 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#include "hippo-animation-manager.h"
+#include "hippo-canvas-marshal.h"
+
+#define EVENT(animation, id) ((AnimationEvent *)g_ptr_array_index((animation)->events, (id)))
+
+enum {
+    AFTER_FRAME,
+    LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL];
+
+struct _HippoAnimationManager {
+    GObject parent;
+
+    GPtrArray *animations;
+    GArray *animation_start;
+
+    double last_frame_time;
+    guint frame_serial;
+    guint frame_timeout;
+    
+    guint frame_pending : 1;
+};
+
+struct _HippoAnimationManagerClass {
+    GObjectClass parent_clas;
+
+    void (*after_frame) (HippoAnimationManager *animation,
+                         guint                  frame_serial);
+};
+ 
+static void manager_remove_animation(HippoAnimationManager *manager,
+                                     int                    index);
+
+G_DEFINE_TYPE(HippoAnimationManager, hippo_animation_manager, G_TYPE_OBJECT)
+
+#define ANIMATION(manager, i) ((HippoAnimation *)g_ptr_array_index((manager)->animations, (i)))
+#define ANIMATION_START(manager, i) (g_array_index((manager)->animation_start, double, (i)))
+
+static double
+current_time(void)
+{
+    GTimeVal tv;
+
+    g_get_current_time(&tv);
+
+    return (double)tv.tv_sec + ((double)tv.tv_usec) / 1000000.;
+}
+
+static void
+hippo_animation_manager_finalize(GObject *object)
+{
+    HippoAnimationManager *manager = HIPPO_ANIMATION_MANAGER(object);
+    guint i;
+
+    for (i = 0; i < manager->animations->len; i++)
+        g_object_unref(ANIMATION(manager, i));
+
+    g_ptr_array_free(manager->animations, TRUE);
+    g_array_free(manager->animation_start, TRUE);
+    
+    G_OBJECT_CLASS(hippo_animation_manager_parent_class)->finalize(object);
+}
+
+static void
+hippo_animation_manager_init(HippoAnimationManager *manager)
+{
+    manager->animations = g_ptr_array_new();
+    manager->animation_start = g_array_new(FALSE, FALSE, sizeof(double));
+}
+
+static void
+hippo_animation_manager_class_init(HippoAnimationManagerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = hippo_animation_manager_finalize;
+    
+    /**
+     * HippoAnimationManager::after-frame
+     *
+     * Signal emitted after advancing to a new frame of the animation
+     */
+    signals[AFTER_FRAME] =
+        g_signal_new ("after-frame",
+                      G_TYPE_FROM_CLASS (object_class),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoAnimationManagerClass, after_frame),
+                      NULL, NULL,
+                      hippo_canvas_marshal_VOID__UINT,
+                      G_TYPE_NONE, 1, G_TYPE_UINT);
+}
+
+static void
+animation_manager_do_frame(HippoAnimationManager *manager)
+{
+    guint i;
+    
+    manager->last_frame_time = current_time();
+    manager->frame_pending = TRUE;
+
+    for (i = 0; i < manager->animations->len; i++) {
+        HippoAnimation *animation = ANIMATION(manager, i);
+        hippo_animation_advance(animation, manager->last_frame_time - ANIMATION_START(manager, i));
+    }
+        
+    manager->frame_serial++;
+
+    g_signal_emit(manager, signals[AFTER_FRAME], 0, manager->frame_serial);
+}
+
+static gboolean
+animation_manager_frame_timeout(gpointer data)
+{
+    HippoAnimationManager *manager = data;
+
+    manager->frame_timeout = 0;
+    animation_manager_do_frame(manager);
+
+    return FALSE;
+}
+
+static void
+animation_manager_update(HippoAnimationManager *manager)
+{
+    double now;
+    double min_next_time = -1.;
+    guint i;
+    
+    if (manager->frame_pending)
+        return;
+        
+    now = current_time();
+
+    i = 0;
+    while (i < manager->animations->len) {
+        HippoAnimation *animation = ANIMATION(manager, i);
+        double start = ANIMATION_START(manager, i);
+        double next_pos = hippo_animation_get_next_event_position(animation);
+        
+        if (next_pos < 0.) {
+            manager_remove_animation(manager,i);
+        } else {
+            if (i == 0)
+                min_next_time = start + next_pos;
+            else
+                min_next_time = MAX(min_next_time, start + next_pos);
+
+            i++;
+        }
+    }
+
+    if (i > 0) { /* At least one animation still activate */
+        double next_frame_time = manager->last_frame_time + 1/30.;
+        double next_time = MAX(min_next_time, next_frame_time);
+        
+        if (manager->frame_timeout) {
+            g_source_remove(manager->frame_timeout);
+            manager->frame_timeout = 0;
+        }
+        
+        /*        g_print("%.5f\t%.5f\n", now - manager->last_frame_time, next_time - now); */
+
+        if (next_time <= now) {
+            animation_manager_do_frame(manager);
+        } else {
+            manager->frame_timeout = g_timeout_add((int)(0.5 + 1000 * (next_time - now)),
+                                                   animation_manager_frame_timeout, manager);
+        }
+    }
+}
+
+static void
+on_animation_cancel(HippoAnimation        *animation,
+                    HippoAnimationManager *manager)
+{
+    guint i;
+    
+    for (i = 0; i < manager->animations->len; i++) {
+        if (animation == ANIMATION(manager, i))
+            manager_remove_animation(manager, i);
+    }
+}
+
+static void
+manager_remove_animation(HippoAnimationManager *manager,
+                         int                    index)
+{
+    HippoAnimation *animation = ANIMATION(manager, index);
+    
+    g_ptr_array_remove_index(manager->animations, index);
+    g_array_remove_index(manager->animation_start, index);
+
+    g_signal_handlers_disconnect_by_func(animation,
+                                         (gpointer)on_animation_cancel,
+                                         manager);
+    
+    g_object_unref(animation);
+}
+
+HippoAnimationManager *
+hippo_animation_manager_new (void)
+{
+    return g_object_new(HIPPO_TYPE_ANIMATION_MANAGER, NULL);
+}
+
+/**
+ * hippo_animation_manager_add_animation:
+ * @manager: the animation manager
+ * @animation: the animation to add
+ * @delay: time from the current time at which to start the animation, in seconds
+ *
+ * Add an animation to the animation manager. The animation starts at 
+ * 'delay' seconds fromthe current time.
+ */
+void
+hippo_animation_manager_add_animation (HippoAnimationManager *manager,
+                                       HippoAnimation        *animation,
+                                       double                 delay)
+{
+    double start_time;
+    
+    g_return_if_fail(HIPPO_IS_ANIMATION_MANAGER(manager));
+
+    start_time = current_time() + delay;
+
+    g_object_ref(animation);
+    g_ptr_array_add(manager->animations, animation);
+    g_array_append_val(manager->animation_start, start_time);
+
+    g_signal_connect(animation, "cancel",
+                     G_CALLBACK(on_animation_cancel), manager);
+
+    animation_manager_update(manager);
+}
+
+/**
+ * hippo_animation_manager_frame_complete:
+ * @manager: the animation manager
+ * @frame_serial: frame serial from the ::after-frame signal
+ *
+ * When the canvas container sees an ::after-frame signal from the animation
+ * manager it should wait for all resizing and drawing in the canvas to be
+ * complete (using whatever toolkit specific mechanisms are appropriate), and
+ * then call this function.
+ */
+void
+hippo_animation_manager_frame_complete (HippoAnimationManager *manager,
+                                        guint                  frame_serial)
+{
+    g_return_if_fail(HIPPO_IS_ANIMATION_MANAGER(manager));
+
+    manager->frame_pending = FALSE;
+    
+    animation_manager_update(manager);
+}
+
+

Added: trunk/common/hippo/hippo-animation-manager.h
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-animation-manager.h	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,44 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifndef __HIPPO_ANIMATION_MANAGER_H__
+#define __HIPPO_ANIMATION_MANAGER_H__
+
+#include <hippo/hippo-animation.h>
+
+G_BEGIN_DECLS
+
+/**
+ * HippoAnimationManager::
+ *
+ * The animation manager class takes care of running animations within a single canvas.
+ * While there are active animations, the animation manager advances all the active
+ * animations, then emits the ::after-frame signal. The canvas container (which will
+ * create and own the animation manager) watches for that signal, and when it sees
+ * it waits until all resizing and drawing is complete and calls
+ * hippo_animation_manager_frame_complete(). (The frame serial from ::after-frame
+ * should be passed to frame_complete() though the concept of frame serials isn't
+ * really fully worked out here.)
+ */
+
+#define HIPPO_TYPE_ANIMATION_MANAGER              (hippo_animation_manager_get_type ())
+#define HIPPO_ANIMATION_MANAGER(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), HIPPO_TYPE_ANIMATION_MANAGER, HippoAnimationManager))
+#define HIPPO_ANIMATION_MANAGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), HIPPO_TYPE_ANIMATION_MANAGER, HippoAnimationManagerClass))
+#define HIPPO_IS_ANIMATION_MANAGER(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), HIPPO_TYPE_ANIMATION_MANAGER))
+#define HIPPO_IS_ANIMATION_MANAGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), HIPPO_TYPE_ANIMATION_MANAGER))
+#define HIPPO_ANIMATION_MANAGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), HIPPO_TYPE_ANIMATION_MANAGER, HippoAnimationManagerClass))
+
+typedef struct _HippoAnimationManager HippoAnimationManager;
+typedef struct _HippoAnimationManagerClass HippoAnimationManagerClass;
+
+GType             hippo_animation_manager_get_type          (void) G_GNUC_CONST;
+
+HippoAnimationManager *hippo_animation_manager_new (void);
+
+void hippo_animation_manager_add_animation    (HippoAnimationManager *manager,
+                                               HippoAnimation        *animation,
+                                               double                 delay);
+void hippo_animation_manager_frame_complete   (HippoAnimationManager *manager,
+                                               guint                  frame_serial);
+
+G_END_DECLS
+
+#endif /* __HIPPO_ANIMATION_MANAGER_H__ */

Added: trunk/common/hippo/hippo-animation.c
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-animation.c	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,225 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#include "hippo-animation.h"
+#include "hippo-canvas-marshal.h"
+
+/* This defines the time before an event that counts as "in" the
+ * event. The attempt here is to waking up fractionally early, then
+ * sleeping again to get to the event
+ */
+#define FUZZ 0.01
+
+typedef struct _AnimationEvent AnimationEvent;
+
+struct _AnimationEvent {
+    int id;
+    double when;
+    double duration;
+};
+
+#define EVENT(animation, id) ((AnimationEvent *)g_ptr_array_index((animation)->events, (id)))
+
+enum {
+    EVENT,
+    CANCEL,
+    LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE(HippoAnimation, hippo_animation, G_TYPE_OBJECT)
+
+static void
+hippo_animation_finalize(GObject *object)
+{
+    HippoAnimation *animation = HIPPO_ANIMATION(object);
+    guint i;
+    
+    for (i = 0; i < animation->events->len; i++) {
+        AnimationEvent *event = EVENT(animation, i);
+        g_free(event);
+    }
+
+    g_ptr_array_free(animation->events, TRUE);
+
+    G_OBJECT_CLASS(hippo_animation_parent_class)->finalize(object);
+}
+
+static void
+hippo_animation_init(HippoAnimation *animation)
+{
+    animation->position = 0;
+    animation->first_current = 0;
+    animation->events = g_ptr_array_new();
+}
+
+static void
+hippo_animation_class_init(HippoAnimationClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = hippo_animation_finalize;
+    
+    /**
+     * HippoAnimation::event
+     *
+     * Signal emitted when the animation is advanced to a new position.
+     */
+    signals[EVENT] =
+        g_signal_new ("event",
+                      G_TYPE_FROM_CLASS (object_class),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoAnimationClass, event),
+                      NULL, NULL,
+                      hippo_canvas_marshal_VOID__INT_DOUBLE,
+                      G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_DOUBLE);
+    
+    /**
+     * HippoAnimation::cancel
+     *
+     * Signal emitted when the animation is cancelled
+     */
+    signals[CANCEL] =
+        g_signal_new ("cancel",
+                      G_TYPE_FROM_CLASS (object_class),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoAnimationClass, event),
+                      NULL, NULL,
+                      hippo_canvas_marshal_VOID__NONE,
+                      G_TYPE_NONE, 0);
+}
+
+/**
+ * hippo_animation_new:
+ * 
+ * Return value: A newly created HippoAnimation object. Unref with g_object_unref()
+ **/
+HippoAnimation *
+hippo_animation_new(void)
+{
+    return g_object_new(HIPPO_TYPE_ANIMATION, 0);
+}
+
+/**
+ * hippo_animation_add_event:
+ * @animation: the animation object
+ * @when: time that this event starts, in seconds from the beginning of the animation
+ * @duration: duration of the event, in seconds. If 0 or negative, the event is treated
+ *   as instantaneous.
+ *
+ * Adds an event to the animation object.
+ *
+ * Return value: the integer ID for the animation. Will be passed to the ::event signal.
+ **/
+int
+hippo_animation_add_event(HippoAnimation *animation,
+                          double          when,
+                          double          duration)
+{
+    AnimationEvent *event;
+    int id;
+
+    g_return_val_if_fail(HIPPO_IS_ANIMATION(animation), -1);
+
+    id = animation->events->len;
+    
+    if ((id == 0 && when < 0) || (id > 0 && when < EVENT(animation, id - 1)->when)) {
+        g_warning("Events must be added in time order");
+        return -1;
+    }
+
+    event = g_new0(AnimationEvent, 1);
+
+    event->id = id;
+    event->when = when;
+    event->duration = duration;
+
+    g_ptr_array_add(animation->events, event);
+
+    return event->id;
+}
+
+/**
+ * hippo_animation_cancel:
+ * @animation: the animation object
+ *
+ * Cancels the operation of the animation and removes it from the animation
+ * manager it has been added to, if any.
+ */
+void
+hippo_animation_cancel(HippoAnimation *animation)
+{
+    g_return_if_fail(HIPPO_IS_ANIMATION(animation));
+    
+    g_signal_emit(animation, signals[CANCEL], 0);
+}
+
+/**
+ * hippo_animation_advance
+ * @animation: the animation object
+ * @position: the new position, in seconds from the beginning of the animation
+ *
+ * Advances the animation to a new position. This is function is meant for
+ * HippoAnimationManager and is likely not useful for applications.
+ */
+void
+hippo_animation_advance(HippoAnimation *animation,
+                        double          position)
+{
+    gboolean seen_current = FALSE;
+    guint i;
+
+    for (i = 0; i < animation->events->len; i++) {
+        AnimationEvent *event = EVENT(animation, i);
+
+        if (event->when + event->duration < position) {
+            if (!seen_current)
+                animation->first_current = i + 1;
+        } else {
+            seen_current = TRUE;
+        }
+
+        if (event->when - FUZZ > position) /* future events */
+            break;
+                
+        if (event->duration <= 0) { /*  1-time event */
+            if (event->when - FUZZ > animation->position && event->when - FUZZ <= position)
+                g_signal_emit(animation, signals[EVENT], 0, event->id, 0);
+        } else { /* Continuous event */
+            if (event->when <= position + FUZZ && event->when + event->duration >= position) {
+                double fraction = MAX(0, (position - event->when) / event->duration);
+                g_signal_emit(animation, signals[EVENT], 0, event->id, fraction);
+            } else if (event->when + event->duration > animation->position && event->when + event->duration <= position) {
+                /* Always give a final update at position 1.0 */
+                g_signal_emit(animation, signals[EVENT], 0, event->id, 1.0);
+            }
+        }
+    }
+            
+    animation->position = position;
+}
+
+/**
+ * hippo_animation_advance
+ * @animation: the animation object
+ *
+ * Gets the start time of the first event that is not yet finished.
+ * This is function is meant for HippoAnimationManager and is likely not
+ * useful for applications.
+ *
+ * Return Value: time in seconds from the beginning of the start of the first
+ * still-current event. This time may be earlier than than the current time, which
+ * means that an event that started before the current time is still going on.
+ **/
+double
+hippo_animation_get_next_event_position(HippoAnimation *animation)
+{
+    AnimationEvent *event;
+        
+    if (animation->first_current == animation->events->len)
+        return -1.;
+
+    event = EVENT(animation, animation->first_current);
+
+    return event->when;
+}

Added: trunk/common/hippo/hippo-animation.h
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-animation.h	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,68 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifndef __HIPPO_ANIMATION_H__
+#define __HIPPO_ANIMATION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * HippoAnimation::
+ *
+ * The HippoAnimation class represents a single animation that can be managed by
+ * a HippoAnimationManager. An animation consists of a series of "events".
+ * events can either be instantantaneous, or they can have a duration. Each
+ * event is identified by an integer ID. (It was done this way rather than
+ * using objects to avoid forcing someone implementation an animation to
+ * define a new GObject type.)
+ *
+ * The base HippoAnimation class does nothing by itself. You can either
+ * subclass it, or you can create an instance of the base class and connect
+ * to the ::event and ::cancel signals.
+ */
+
+#define HIPPO_TYPE_ANIMATION              (hippo_animation_get_type ())
+#define HIPPO_ANIMATION(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), HIPPO_TYPE_ANIMATION, HippoAnimation))
+#define HIPPO_ANIMATION_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), HIPPO_TYPE_ANIMATION, HippoAnimationClass))
+#define HIPPO_IS_ANIMATION(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), HIPPO_TYPE_ANIMATION))
+#define HIPPO_IS_ANIMATION_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), HIPPO_TYPE_ANIMATION))
+#define HIPPO_ANIMATION_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), HIPPO_TYPE_ANIMATION, HippoAnimationClass))
+
+typedef struct _HippoAnimation HippoAnimation;
+typedef struct _HippoAnimationClass HippoAnimationClass;
+
+struct _HippoAnimation {
+    GObject parent;
+
+    GPtrArray *events;
+    guint first_current; /* Index of first event that is still current */
+    double position; /* current time value */
+};
+
+struct _HippoAnimationClass {
+    GObjectClass parent_class;
+
+    void (*event) (HippoAnimation  *animation,
+                   int              event_id,
+                   double           fraction);
+    void (*cancel) (HippoAnimation *animation);
+};
+ 
+GType             hippo_animation_get_type          (void) G_GNUC_CONST;
+
+HippoAnimation *hippo_animation_new (void);
+
+int hippo_animation_add_event(HippoAnimation *animation,
+                              double          when,
+                              double          duration);
+
+void hippo_animation_cancel  (HippoAnimation *animation);
+
+/* "Protected" methods used by HippoAnimationManager */
+void   hippo_animation_advance                 (HippoAnimation *animation,
+                                                double          position);
+double hippo_animation_get_next_event_position (HippoAnimation *animation);
+
+G_END_DECLS
+
+#endif /* __HIPPO_ANIMATION_H__ */

Modified: trunk/common/hippo/hippo-canvas-box.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-box.c	(original)
+++ trunk/common/hippo/hippo-canvas-box.c	Tue Jun 10 21:30:02 2008
@@ -70,6 +70,8 @@
 static HippoCanvasStyle *    hippo_canvas_box_get_style      (HippoCanvasContext *context);
 static PangoFontDescription *hippo_canvas_box_get_font       (HippoCanvasContext *context);
 
+HippoAnimationManager *      hippo_canvas_box_get_animation_manager (HippoCanvasContext *context);
+
 static void             hippo_canvas_box_style_changed          (HippoCanvasContext   *context,
                                                                  gboolean              resize_needed);
 
@@ -262,6 +264,7 @@
     klass->get_style = hippo_canvas_box_get_style;
     klass->get_resolution = hippo_canvas_box_get_resolution;
     klass->get_font = hippo_canvas_box_get_font;
+    klass->get_animation_manager = hippo_canvas_box_get_animation_manager;
     klass->style_changed = hippo_canvas_box_style_changed;
 }
 
@@ -1370,6 +1373,14 @@
     return hippo_canvas_context_get_font(box->context);
 }
 
+HippoAnimationManager *
+hippo_canvas_box_get_animation_manager (HippoCanvasContext *context)
+{
+    HippoCanvasBox *box = HIPPO_CANVAS_BOX(context);
+
+    return hippo_canvas_context_get_animation_manager(box->context);
+}
+
 static void
 hippo_canvas_box_style_changed(HippoCanvasContext   *context,
                                gboolean              resize_needed)
@@ -1635,7 +1646,7 @@
         cairo_fill(cr);
     }
 
-    background_theme_image = hippo_canvas_get_background_theme_image(style);
+    background_theme_image = hippo_canvas_style_get_background_theme_image(style);
     if (background_theme_image != NULL) {
         HippoRectangle area;
 
@@ -4193,8 +4204,17 @@
                  BoxChildPrivate  *c)
 {
     HippoCanvasItem *child;
-
+        
     child = c->public.item;
+
+    if (c->public.visible) {
+        /* The area under the child now needs to be repainted */
+        
+        int w, h;
+        hippo_canvas_item_get_allocation(child, &w, &h);
+        hippo_canvas_item_emit_paint_needed(HIPPO_CANVAS_ITEM(box), c->x, c->y, w, h);
+    }
+    
     c->public.item = NULL;
 
     box->children = g_slist_remove(box->children, c);

Modified: trunk/common/hippo/hippo-canvas-context.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-context.c	(original)
+++ trunk/common/hippo/hippo-canvas-context.c	Tue Jun 10 21:30:02 2008
@@ -156,6 +156,14 @@
     return HIPPO_CANVAS_CONTEXT_GET_IFACE(context)->get_font(context);
 }
 
+HippoAnimationManager *
+hippo_canvas_context_get_animation_manager (HippoCanvasContext *context)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_CONTEXT(context), NULL);
+    
+    return HIPPO_CANVAS_CONTEXT_GET_IFACE(context)->get_animation_manager(context);
+}
+
 void
 hippo_canvas_context_emit_style_changed(HippoCanvasContext *context,
                                         gboolean            resize_needed)

Modified: trunk/common/hippo/hippo-canvas-context.h
==============================================================================
--- trunk/common/hippo/hippo-canvas-context.h	(original)
+++ trunk/common/hippo/hippo-canvas-context.h	Tue Jun 10 21:30:02 2008
@@ -19,6 +19,7 @@
  */
 
 #include <hippo/hippo-graphics.h>
+#include <hippo/hippo-animation-manager.h>
 #include <pango/pango-layout.h>
 #include <cairo.h>
 
@@ -79,6 +80,8 @@
     HippoCanvasStyle *(* get_style)             (HippoCanvasContext *context);
     double            (* get_resolution)        (HippoCanvasContext *context);
     PangoFontDescription *(* get_font)          (HippoCanvasContext *context);
+    
+    HippoAnimationManager *( *get_animation_manager) (HippoCanvasContext *context);
 
     /* Signals */
 
@@ -120,6 +123,8 @@
 double                hippo_canvas_context_get_resolution (HippoCanvasContext *context);
 PangoFontDescription *hippo_canvas_context_get_font       (HippoCanvasContext *context);
 
+HippoAnimationManager *hippo_canvas_context_get_animation_manager (HippoCanvasContext *context);
+
 void hippo_canvas_context_emit_style_changed(HippoCanvasContext *context,
                                              gboolean            resize_needed);
 

Modified: trunk/common/hippo/hippo-canvas-marshal.list
==============================================================================
--- trunk/common/hippo/hippo-canvas-marshal.list	(original)
+++ trunk/common/hippo/hippo-canvas-marshal.list	Tue Jun 10 21:30:02 2008
@@ -1,3 +1,6 @@
 VOID:POINTER,BOXED
+VOID:NONE
+VOID:UINT
 VOID:INT,INT,INT,INT
+VOID:INT,DOUBLE
 BOOLEAN:BOXED

Modified: trunk/common/hippo/hippo-canvas-style.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-style.c	(original)
+++ trunk/common/hippo/hippo-canvas-style.c	Tue Jun 10 21:30:02 2008
@@ -1391,7 +1391,7 @@
 }
 
 HippoCanvasThemeImage *
-hippo_canvas_get_background_theme_image (HippoCanvasStyle *style)
+hippo_canvas_style_get_background_theme_image (HippoCanvasStyle *style)
 {
     int i;
 

Modified: trunk/common/hippo/hippo-canvas-style.h
==============================================================================
--- trunk/common/hippo/hippo-canvas-style.h	(original)
+++ trunk/common/hippo/hippo-canvas-style.h	Tue Jun 10 21:30:02 2008
@@ -105,7 +105,7 @@
 /* This is the getter for -hippo-background-image, which is different from
  * background-image in having provisions for unscaled borders.
  */
-HippoCanvasThemeImage *hippo_canvas_get_background_theme_image (HippoCanvasStyle *style);
+HippoCanvasThemeImage *hippo_canvas_style_get_background_theme_image (HippoCanvasStyle *style);
 
 gboolean hippo_canvas_style_paint (HippoCanvasStyle       *style,
                                    cairo_t                *cr,

Modified: trunk/linux/hippo/hippo-canvas-helper.c
==============================================================================
--- trunk/linux/hippo/hippo-canvas-helper.c	(original)
+++ trunk/linux/hippo/hippo-canvas-helper.c	Tue Jun 10 21:30:02 2008
@@ -71,8 +71,14 @@
 double                hippo_canvas_helper_get_resolution (HippoCanvasContext *context);
 PangoFontDescription *hippo_canvas_helper_get_font       (HippoCanvasContext *context);
 
+HippoAnimationManager *hippo_canvas_helper_get_animation_manager   (HippoCanvasContext *context);
+
 static void             hippo_canvas_helper_fixup_resize_state     (HippoCanvasHelper  *canvas);
 
+static void       on_animation_manager_after_frame (HippoAnimationManager *manager,
+                                                    guint                  frame_serial,
+                                                    HippoCanvasHelper     *helper);
+
 static void       tooltip_window_update   (GtkWidget      *tip,
                                            GdkScreen      *screen,
                                            int             mouse_x,
@@ -107,9 +113,13 @@
     
     GSList *widget_items;
 
+    HippoAnimationManager *animation_manager;
+    guint frame_serial;
+
     unsigned int root_hovering : 1;
     unsigned int fixing_up_resize_state : 1;
     unsigned int need_background_paint : 1;
+    unsigned int frame_pending : 1;
 };
 
 struct _HippoCanvasHelperClass {
@@ -138,6 +148,10 @@
     helper->pointer = HIPPO_CANVAS_POINTER_UNSET;
     helper->last_window_x = -1;
     helper->last_window_y = -1;
+
+    helper->animation_manager = hippo_animation_manager_new();
+    g_signal_connect(helper->animation_manager, "after-frame",
+                     G_CALLBACK(on_animation_manager_after_frame), helper);
 }
 
 static void
@@ -166,6 +180,7 @@
     klass->get_style = hippo_canvas_helper_get_style;
     klass->get_resolution = hippo_canvas_helper_get_resolution;
     klass->get_font = hippo_canvas_helper_get_font;
+    klass->get_animation_manager = hippo_canvas_helper_get_animation_manager;
 }
 
 static void
@@ -200,14 +215,23 @@
         gtk_object_destroy(GTK_OBJECT(helper->tooltip_window));
         helper->tooltip_window = NULL;
     }
-    
+
+    if (helper->animation_manager) {
+        g_signal_handlers_disconnect_by_func(helper->animation_manager,
+                                             (gpointer)on_animation_manager_after_frame,
+                                             helper);
+        
+        g_object_unref(helper->animation_manager);
+        helper->animation_manager = NULL;
+    }
+
     G_OBJECT_CLASS(hippo_canvas_helper_parent_class)->dispose(object);
 }
 
 static void
 hippo_canvas_helper_finalize(GObject *object)
 {
-    /* HippoCanvasHelper *helper = HIPPO_CANVAS(object); */
+    /* HippoCanvasHelper *helper = HIPPO_CANVAS_HELPER(object); */
 
     G_OBJECT_CLASS(hippo_canvas_helper_parent_class)->finalize(object);
 }
@@ -382,11 +406,47 @@
     }
 }
 
+static gboolean
+allocate_pending_on_widget(GtkWidget *widget)
+{
+    return GTK_WIDGET_ALLOC_NEEDED(widget);
+}
+
+static gboolean
+expose_pending_on_window(GdkWindow *window)
+{
+    /* This will not work properly for the OS X port of GTK+ */
+
+    return ((GdkWindowObject *)window)->update_area != NULL;
+}
+
+static void
+on_animation_manager_after_frame (HippoAnimationManager *manager,
+                                  guint                  frame_serial,
+                                  HippoCanvasHelper     *helper)
+{
+    /* A frame has finished; we now need to wait until all resizing
+     * and redrawing and then call
+     * hippo_animation_manager_frame_complete().
+     */
+    
+    if (!allocate_pending_on_widget(helper->widget) &&
+        !expose_pending_on_window(helper->widget->window))
+    {
+        /* Nothing to do, we can call frame_complete() immediately */
+        hippo_animation_manager_frame_complete(manager, frame_serial);
+    }
+    else
+    {
+        helper->frame_serial = frame_serial;
+        helper->frame_pending = TRUE;
+    }
+}
+
 gboolean
 hippo_canvas_helper_expose_event(HippoCanvasHelper *helper,
                                  GdkEventExpose    *event)
 {
-    
     cairo_t *cr;
 
     cr = gdk_cairo_create(event->window);
@@ -422,6 +482,26 @@
     
     cairo_destroy(cr);
 
+    if (helper->frame_pending) {
+        /* Resize occurs before redraw, so when we are done redrawing, we know we are
+         * done with the frame
+         */
+
+        /* In theory, we should actually wait for the frame to paint on the X server;
+         * we could do this by changing a property and watching for the PropertyNotify
+         * event or by using the XSync extension. Even better would be to communicate
+         * with the compositing manager and wait until a frame containing the new
+         * contents of our window has been drawn to the front buffer.
+         *
+         * In practice, the difference between that and just going ahead and starting
+         * the next frame is going to be pretty small unless the balance
+         * of server vs. client side efficiency changes a lot for cairo... the client
+         * side of cairo currently doesn't keep up with the server.
+         */
+        helper->frame_pending = FALSE;
+        hippo_animation_manager_frame_complete(helper->animation_manager, helper->frame_serial);
+    }
+    
     return FALSE;
 }
 
@@ -476,6 +556,15 @@
         /* Tooltip might be in the wrong place now */
         update_tooltip(helper, FALSE);
     }
+
+    if (helper->frame_pending) {
+        if (!expose_pending_on_window(helper->widget->window)) {
+            /* We resized, and there was nothing to draw, we can can call frame_complete() */
+            
+            helper->frame_pending = FALSE;
+            hippo_animation_manager_frame_complete(helper->animation_manager, helper->frame_serial);
+        }
+    }
 }
 
 gboolean
@@ -1158,6 +1247,14 @@
     return helper->widget->style->font_desc;
 }
 
+HippoAnimationManager *
+hippo_canvas_helper_get_animation_manager (HippoCanvasContext *context)
+{
+    HippoCanvasHelper *helper = HIPPO_CANVAS_HELPER(context);
+
+    return helper->animation_manager;
+}
+
 static void
 canvas_root_destroy(HippoCanvasItem   *root,
                     HippoCanvasHelper *helper)

Modified: trunk/python/hippo.defs
==============================================================================
--- trunk/python/hippo.defs	(original)
+++ trunk/python/hippo.defs	Tue Jun 10 21:30:02 2008
@@ -23,6 +23,20 @@
   )
 )
 
+(define-object Animation
+  (in-module "Hippo")
+  (parent "GObject")
+  (c-name "HippoAnimation")
+  (gtype-id "HIPPO_TYPE_ANIMATION")
+)
+
+(define-object AnimationManager
+  (in-module "Hippo")
+  (parent "GObject")
+  (c-name "HippoAnimationManager")
+  (gtype-id "HIPPO_TYPE_ANIMATION_MANAGER")
+)
+
 (define-object Canvas
   (in-module "Hippo")
   (parent "GtkContainer")
@@ -324,6 +338,96 @@
 )
 
 
+;; From hippo-animation.h
+
+(define-function animation_get_type
+  (c-name "hippo_animation_get_type")
+  (return-type "GType")
+)
+
+(define-function animation_new
+  (c-name "hippo_animation_new")
+  (is-constructor-of "HippoAnimation")
+  (return-type "HippoAnimation*")
+)
+
+(define-method add_event
+  (of-object "HippoAnimation")
+  (c-name "hippo_animation_add_event")
+  (return-type "int")
+  (parameters
+   '("double" "when")
+   '("double" "duration" (default "-1"))
+  )
+)
+
+(define-method cancel
+  (of-object "HippoAnimation")
+  (c-name "hippo_animation_cancel")
+  (return-type "none")
+)
+
+(define-method advance
+  (of-object "HippoAnimation")
+  (c-name "hippo_animation_advance")
+  (return-type "none")
+  (parameters
+   '("double" "position")
+  )
+)
+
+(define-method get_next_event_position
+  (of-object "HippoAnimation")
+  (c-name "hippo_animation_get_next_event_position")
+  (return-type "double")
+)
+
+(define-virtual event
+  (of-object "HippoAnimation")
+  (return-type "none")
+  (parameters
+   '("int" "event_id")
+   '("double" "fraction")
+  )
+)
+
+(define-virtual cancel
+  (of-object "HippoAnimation")
+  (return-type "none")
+)
+
+;; From hippo-animation-manager.h
+
+(define-function animation_manager_get_type
+  (c-name "hippo_animation_manager_get_type")
+  (return-type "GType")
+)
+
+(define-function animation_manager_new
+  (c-name "hippo_animation_manager_new")
+  (is-constructor-of "HippoAnimationManager")
+  (return-type "HippoAnimationManager*")
+)
+
+(define-method add_animation
+  (of-object "HippoAnimationManager")
+  (c-name "hippo_animation_manager_add_animation")
+  (return-type "none")
+  (parameters
+   '("HippoAnimation*" "animation")
+   '("double" "delay" (default "0"))
+   )
+)
+
+(define-method frame_complete
+  (of-object "HippoAnimationManager")
+  (c-name "hippo_animation_manager_frame_complete")
+  (return-type "none")
+  (parameters
+   '("guint" "frame_serial")
+   )
+)
+
 ;; From hippo-canvas.h
 
 (define-function canvas_get_type
@@ -837,6 +941,12 @@
   (return-type "HippoCanvasStyle*")
 )
 
+(define-method get_animation_manager
+  (of-object "HippoCanvasContext")
+  (c-name "hippo_canvas_context_get_animation_manager")
+  (return-type "HippoAnimationManager*")
+)
+
 (define-method register_widget_item
   (of-object "HippoCanvasContext")
   (c-name "hippo_canvas_context_register_widget_item")

Added: trunk/tests/bottom.svg
==============================================================================
--- (empty file)
+++ trunk/tests/bottom.svg	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="50"
+   height="17"
+   id="svg2715"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="bottom.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs2717">
+    <linearGradient
+       id="linearGradient3249">
+      <stop
+         style="stop-color:#577e9a;stop-opacity:1;"
+         offset="0"
+         id="stop3251" />
+      <stop
+         style="stop-color:#416782;stop-opacity:1;"
+         offset="1"
+         id="stop3253" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3225">
+      <stop
+         style="stop-color:#3e6179;stop-opacity:1;"
+         offset="0"
+         id="stop3227" />
+      <stop
+         style="stop-color:#839bab;stop-opacity:1;"
+         offset="1"
+         id="stop3229" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3531">
+      <stop
+         style="stop-color:#bcbcbc;stop-opacity:0.78431374;"
+         offset="0"
+         id="stop3533" />
+      <stop
+         style="stop-color:#0000ff;stop-opacity:0.50196081;"
+         offset="1"
+         id="stop3535" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective2723" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3537"
+       x1="5"
+       y1="5"
+       x2="45"
+       y2="45"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0,1)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3162"
+       gradientUnits="userSpaceOnUse"
+       x1="5"
+       y1="5"
+       x2="45"
+       y2="45" />
+    <inkscape:perspective
+       id="perspective3204"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3225"
+       id="linearGradient3247"
+       x1="25"
+       y1="20"
+       x2="25"
+       y2="39"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3249"
+       id="linearGradient3255"
+       x1="25"
+       y1="38"
+       x2="25"
+       y2="24"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313708"
+     inkscape:cx="33.911162"
+     inkscape:cy="11.066167"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     showborder="true"
+     inkscape:window-width="1400"
+     inkscape:window-height="1000"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2725"
+       visible="true"
+       enabled="true"
+       spacingx="1px"
+       spacingy="1px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata2720">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-33)">
+    <path
+       style="fill:url(#linearGradient3255);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 1,24 L 1,35 L 5,39 L 45,39 L 49,35 L 49,24 L 1,24 z"
+       transform="translate(0,10)"
+       id="path3529"
+       sodipodi:nodetypes="ccccccc" />
+    <g
+       id="g2388"
+       style="enable-background:new">
+      <path
+         style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 0,23 L 0,32.5 C 0,36.646351 3.3536486,40 7.5,40 L 42.5,40 C 46.646351,40 50,36.646351 50,32.5 L 50,23 L 49,23 L 49,32.5 C 49,36.109649 46.109649,39 42.5,39 L 7.5,39 C 3.8903514,39 1,36.109649 1,32.5 L 1,23 L 0,23 z"
+         transform="translate(0,10)"
+         id="rect3509"
+         sodipodi:nodetypes="ccccccccccccc" />
+      <path
+         style="opacity:1;fill:url(#linearGradient3247);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 1,23 L 1,32.5 C 1,36.092351 3.9076486,39 7.5,39 L 42.5,39 C 46.092351,39 49,36.092351 49,32.5 L 49,23 L 48,23 L 48,32.5 C 48,35.555649 45.555649,38 42.5,38 L 7.5,38 C 4.4443514,38 2,35.555649 2,32.5 L 2,23 L 1,23 z"
+         transform="translate(0,10)"
+         id="rect3511"
+         sodipodi:nodetypes="ccccccccccccc" />
+    </g>
+    <rect
+       style="opacity:1;fill:#cbd4db;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect3257"
+       width="46"
+       height="1"
+       x="2"
+       y="33" />
+  </g>
+</svg>

Added: trunk/tests/close-x-prelight.svg
==============================================================================
--- (empty file)
+++ trunk/tests/close-x-prelight.svg	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="10"
+   height="10"
+   id="svg3939"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="close-x-prelight.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs3941">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective3947" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="31.678384"
+     inkscape:cx="-2.7607264"
+     inkscape:cy="6.0370821"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="709"
+     inkscape:window-height="687"
+     inkscape:window-x="195"
+     inkscape:window-y="66">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3949"
+       visible="true"
+       enabled="true"
+       spacingx="0.5px"
+       spacingy="0.5px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3944">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 0,1 L 1.0191955,0.98080454 L 1,0 L 5,4 L 9,0 C 9.1343149,0.17192788 8.797482,0.84385576 9.0157836,1.0157836 L 10,1 L 6,5 L 10,9 L 8.9402773,9.0597227 L 9,10 L 5,6 L 1,10 L 1,9 L 0,9 L 4,5 L 0,1 z"
+       id="path3951"
+       sodipodi:nodetypes="ccccccccccccccccc" />
+  </g>
+</svg>

Added: trunk/tests/close-x.svg
==============================================================================
--- (empty file)
+++ trunk/tests/close-x.svg	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="10"
+   height="10"
+   id="svg3939"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="close-x.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs3941">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective3947" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="31.678384"
+     inkscape:cx="3.5211597"
+     inkscape:cy="6.0370821"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="709"
+     inkscape:window-height="687"
+     inkscape:window-x="195"
+     inkscape:window-y="66">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3949"
+       visible="true"
+       enabled="true"
+       spacingx="0.5px"
+       spacingy="0.5px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3944">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:#e6ebf0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 0,1 L 1.0191955,0.98080454 L 1,0 L 5,4 L 9,0 C 9.1343149,0.17192788 8.797482,0.84385576 9.0157836,1.0157836 L 10,1 L 6,5 L 10,9 L 8.9402773,9.0597227 L 9,10 L 5,6 L 1,10 L 1,9 L 0,9 L 4,5 L 0,1 z"
+       id="path3951"
+       sodipodi:nodetypes="ccccccccccccccccc" />
+  </g>
+</svg>

Added: trunk/tests/expand_box.py
==============================================================================
--- (empty file)
+++ trunk/tests/expand_box.py	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,123 @@
+import cairo
+import gtk
+import gobject
+import hippo
+
+class _ExpandAnimation(hippo.Animation):
+    __gsignals__ = { 'event': 'override' }
+    
+    def __init__(self, box, duration):
+        super(_ExpandAnimation, self).__init__()
+        
+        self.__box = box
+        self.add_event(0, duration)
+
+    def do_event(self, id, fraction):
+        if fraction == 1.0:
+            self.__box._animation_finished()
+        else:
+            self.__box._animation_step(fraction)
+        
+class ExpandBox(hippo.CanvasBox, hippo.CanvasItem):
+    __gtype_name__ = 'HippoExpandBox'
+
+    def __init__(self, **kwargs):
+        hippo.CanvasBox.__init__(self, **kwargs)
+        self.__animation = None
+
+    def do_get_width_request(self):
+        minimum, natural = hippo.CanvasBox.do_get_width_request(self)
+        if self.__animation:
+            old_minimum, old_natural = self.__start_width_request
+            fraction = self.__animation_fraction
+            minimum = int(0.5 + minimum * fraction + old_minimum * (1 - fraction))
+            natural = int(0.5 + natural * fraction + old_natural * (1 - fraction))
+
+        return minimum, natural
+    
+    def do_get_height_request(self, for_width):
+        minimum, natural = hippo.CanvasBox.do_get_height_request(self, for_width)
+        if self.__animation:
+            old_minimum, old_natural = self.__start_height_request
+            fraction = self.__animation_fraction
+            minimum = int(0.5 + minimum * fraction + old_minimum * (1 - fraction))
+            natural = int(0.5 + natural * fraction + old_natural * (1 - fraction))
+
+        return minimum, natural
+
+    def do_paint(self, cr, rect):
+        if self.__animation:
+            new_surface = cr.get_target().create_similar(cairo.CONTENT_COLOR_ALPHA,
+                                                         rect.width, rect.height)
+            new_cr = gtk.gdk.CairoContext(cairo.Context(new_surface))
+            new_cr.save()
+            new_cr.set_operator(cairo.OPERATOR_CLEAR)
+            new_cr.paint()
+            new_cr.restore()
+            new_cr.translate(- rect.x, - rect.y)
+            hippo.CanvasBox.do_paint(self, new_cr, rect)
+
+            tmp_surface = cr.get_target().create_similar(cairo.CONTENT_COLOR_ALPHA,
+                                                         rect.width, rect.height)
+            tmp_cr = gtk.gdk.CairoContext(cairo.Context(tmp_surface))
+            
+            tmp_cr.set_source_surface(self.__animation_surface, - rect.x, - rect.y)
+            tmp_cr.set_operator(cairo.OPERATOR_SOURCE)
+            tmp_cr.paint_with_alpha(1 - self.__animation_fraction)
+
+            tmp_cr.set_source_surface(new_surface, 0, 0)
+            tmp_cr.set_operator(cairo.OPERATOR_ADD)
+            tmp_cr.paint_with_alpha(self.__animation_fraction)
+
+            cr.set_source_surface(tmp_surface, rect.x, rect.y)
+            cr.paint()
+        else:
+            hippo.CanvasBox.do_paint(self, cr, rect)
+
+    def animate(self, duration):
+        # First snapshot the current state (which may be an animated state)
+        
+        width, height = self.get_allocation()
+        start_width_request = self.get_width_request()
+        start_height_request = self.get_height_request(width)
+
+        animation_surface = self.get_context().create_surface(cairo.CONTENT_COLOR_ALPHA,
+                                                              width, height)
+        cr = gtk.gdk.CairoContext(cairo.Context(animation_surface))
+        cr.save()
+        cr.set_operator(cairo.OPERATOR_CLEAR)
+        cr.paint()
+        cr.restore()
+        r = hippo.Rectangle(0, 0, width, height)
+        self.process_paint(cr,
+                           hippo.Rectangle(0, 0, width, height),
+                           0, 0)
+
+        # Then cancel any current animation
+
+        if self.__animation:
+            self.__animation.cancel()
+            self.__animation = None
+            self.__animation_surface = None
+
+        # And start the new animation from the current position
+        
+        self.__start_width_request = start_width_request
+        self.__start_height_request = start_height_request
+
+        self.__animation_surface = animation_surface
+
+        self.__animation = _ExpandAnimation(self, duration)
+        self.__animation_fraction = 0.
+        self.get_animation_manager().add_animation(self.__animation)
+        
+    def _animation_step(self, fraction):
+        self.__animation_fraction = fraction
+        self.emit_request_changed()
+        self.emit_paint_needed(0, 0, -1, -1)
+    
+    def _animation_finished(self):
+        self.__animation = None
+        self.__animation_surface = None
+        self.emit_request_changed()
+

Added: trunk/tests/grippy.png
==============================================================================
Binary file. No diff available.

Added: trunk/tests/header-collapsed.svg
==============================================================================
--- (empty file)
+++ trunk/tests/header-collapsed.svg	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="40"
+   height="20"
+   id="svg2715"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="header-collapsed.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs2717">
+    <linearGradient
+       id="linearGradient5058">
+      <stop
+         style="stop-color:#426781;stop-opacity:1;"
+         offset="0"
+         id="stop5060" />
+      <stop
+         style="stop-color:#425f74;stop-opacity:1;"
+         offset="1"
+         id="stop5062" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3531">
+      <stop
+         style="stop-color:#476d88;stop-opacity:1;"
+         offset="0"
+         id="stop3533" />
+      <stop
+         style="stop-color:#59809c;stop-opacity:1;"
+         offset="1"
+         id="stop3535" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective2723" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3537"
+       x1="5"
+       y1="5"
+       x2="45"
+       y2="45"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0,1)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3162"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3787"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3812"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5064"
+       x1="40"
+       y1="2"
+       x2="40"
+       y2="19"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="26.481784"
+     inkscape:cy="1.8001996"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     showborder="true"
+     inkscape:window-width="1400"
+     inkscape:window-height="1000"
+     inkscape:window-x="0"
+     inkscape:window-y="24">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2725"
+       visible="true"
+       enabled="true"
+       spacingx="1px"
+       spacingy="1px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata2720">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:url(#linearGradient3812);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 5,1 L 1,5 L 1,20 L 39,19 L 40,1 L 5,1 z"
+       id="path3529"
+       sodipodi:nodetypes="cccccc" />
+    <g
+       id="g2388"
+       style="enable-background:new">
+      <path
+         style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 7.5,0 C 3.3536486,0 0,3.3536486 0,7.5 L 0,8 L 1,8 L 1,7.5 C 1,3.8903514 3.8903514,1 7.5,1 L 8,1 C 8,1 8,0 8,0 L 7.5,0 z"
+         id="rect3509"
+         sodipodi:nodetypes="ccccccccc" />
+      <path
+         style="opacity:1;fill:#849bab;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 7.5,1 C 3.9076486,1 1,3.9076486 1,7.5 L 1,8 L 2,8 L 2,7.5 C 2,4.4443514 4.4443514,2 7.5,2 L 8,2 C 8,2 8,1 8,1 L 7.5,1 z"
+         id="rect3511"
+         sodipodi:nodetypes="ccccccccc" />
+    </g>
+    <rect
+       style="opacity:1;fill:#264356;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect3267"
+       width="38"
+       height="1"
+       x="2"
+       y="19" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient5064);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect5056"
+       width="1"
+       height="17"
+       x="39"
+       y="2" />
+    <rect
+       style="opacity:1;fill:#849bab;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2403"
+       width="32"
+       height="1"
+       x="8"
+       y="1" />
+    <rect
+       style="opacity:1;fill:#849bab;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2405"
+       width="1"
+       height="12"
+       x="1"
+       y="8" />
+    <rect
+       style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2407"
+       width="32"
+       height="1"
+       x="8"
+       y="0" />
+    <rect
+       style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2409"
+       width="1"
+       height="12"
+       x="0"
+       y="8" />
+  </g>
+</svg>

Added: trunk/tests/header-right-prelight.svg
==============================================================================
--- (empty file)
+++ trunk/tests/header-right-prelight.svg	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="40"
+   height="20"
+   id="svg2715"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="header-right-prelight.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs2717">
+    <linearGradient
+       id="linearGradient5058">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop5060" />
+      <stop
+         style="stop-color:#cbd9e4;stop-opacity:1;"
+         offset="1"
+         id="stop5062" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3531">
+      <stop
+         style="stop-color:#96afc0;stop-opacity:1;"
+         offset="0"
+         id="stop3533" />
+      <stop
+         style="stop-color:#c6c9d2;stop-opacity:1;"
+         offset="1"
+         id="stop3535" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective2723" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3537"
+       x1="5"
+       y1="5"
+       x2="45"
+       y2="45"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0,1)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3162"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3787"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3812"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19"
+       gradientTransform="matrix(-1,0,0,1,40,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5064"
+       x1="40"
+       y1="2"
+       x2="40"
+       y2="19"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-39,0)"
+       x1="40"
+       y1="2"
+       x2="40"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5093"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-39,0)"
+       x1="54"
+       y1="19"
+       x2="54"
+       y2="1" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="1.794284"
+     inkscape:cy="4.1751996"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     showborder="true"
+     inkscape:window-width="1400"
+     inkscape:window-height="1000"
+     inkscape:window-x="0"
+     inkscape:window-y="24">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2725"
+       visible="true"
+       enabled="true"
+       spacingx="1px"
+       spacingy="1px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata2720">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:url(#linearGradient3812);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 35,1 L 39,5 L 39,20 L 0,20 L 0,1 L 35,1 z"
+       id="path3529"
+       sodipodi:nodetypes="cccccc" />
+    <g
+       id="g2388"
+       style="enable-background:new"
+       transform="matrix(-1,0,0,1,40,0)">
+      <path
+         style="opacity:1;fill:url(#linearGradient5093);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 0,1 C 0,1 0,2 0,2 L 0,19 L 1,19 L 1,2 L 32.5,2 C 35.555649,2 38,4.4443514 38,7.5 L 38,19 L 39,19 L 39,7.5 C 39,3.9076486 36.092351,1 32.5,1 L 0,1 z"
+         transform="matrix(-1,0,0,1,40,0)"
+         id="rect5056"
+         sodipodi:nodetypes="cccccccccccc" />
+      <path
+         style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 7.5,0 C 3.3536486,0 0,3.3536486 0,7.5 L 0,20 L 1,20 L 1,7.5 C 1,3.8903514 3.8903514,1 7.5,1 L 40,1 C 40,1 40,0 40,0 L 7.5,0 z"
+         id="rect3509"
+         sodipodi:nodetypes="ccccccccc" />
+      <rect
+         style="opacity:1;fill:#849bab;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         id="rect5097"
+         width="0"
+         height="0"
+         x="33"
+         y="2"
+         transform="matrix(-1,0,0,1,40,0)" />
+      <rect
+         style="opacity:1;fill:#8b96ba;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         id="rect5099"
+         width="1"
+         height="1"
+         x="38"
+         y="19"
+         transform="matrix(-1,0,0,1,40,0)" />
+    </g>
+    <rect
+       style="opacity:1;fill:#264356;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect3267"
+       width="38"
+       height="1"
+       x="-38"
+       y="19"
+       transform="scale(-1,1)" />
+  </g>
+</svg>

Added: trunk/tests/header-right.svg
==============================================================================
--- (empty file)
+++ trunk/tests/header-right.svg	Tue Jun 10 21:30:02 2008
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="40"
+   height="20"
+   id="svg2715"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docname="header-right.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs2717">
+    <linearGradient
+       id="linearGradient5058">
+      <stop
+         style="stop-color:#b8c8d0;stop-opacity:1;"
+         offset="0"
+         id="stop5060" />
+      <stop
+         style="stop-color:#a3b6c4;stop-opacity:1;"
+         offset="1"
+         id="stop5062" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3531">
+      <stop
+         style="stop-color:#6a8aa0;stop-opacity:1;"
+         offset="0"
+         id="stop3533" />
+      <stop
+         style="stop-color:#8b96ba;stop-opacity:1;"
+         offset="1"
+         id="stop3535" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective2723" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3537"
+       x1="5"
+       y1="5"
+       x2="45"
+       y2="45"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0,1)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3162"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3787"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3531"
+       id="linearGradient3812"
+       gradientUnits="userSpaceOnUse"
+       x1="25"
+       y1="2"
+       x2="25"
+       y2="19"
+       gradientTransform="matrix(-1,0,0,1,40,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5064"
+       x1="40"
+       y1="2"
+       x2="40"
+       y2="19"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5090"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-39,0)"
+       x1="40"
+       y1="2"
+       x2="40"
+       y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5093"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-39,0)"
+       x1="54"
+       y1="19"
+       x2="54"
+       y2="1" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="14.231784"
+     inkscape:cy="4.1751996"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     showborder="true"
+     inkscape:window-width="1400"
+     inkscape:window-height="1000"
+     inkscape:window-x="0"
+     inkscape:window-y="24">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2725"
+       visible="true"
+       enabled="true"
+       spacingx="1px"
+       spacingy="1px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata2720">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:url(#linearGradient3812);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 35,1 L 39,5 L 39,20 L 0,20 L 0,1 L 35,1 z"
+       id="path3529"
+       sodipodi:nodetypes="cccccc" />
+    <g
+       id="g2388"
+       style="enable-background:new"
+       transform="matrix(-1,0,0,1,40,0)">
+      <path
+         style="opacity:1;fill:url(#linearGradient5093);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 0,1 C 0,1 0,2 0,2 L 0,19 L 1,19 L 1,2 L 32.5,2 C 35.555649,2 38,4.4443514 38,7.5 L 38,19 L 39,19 L 39,7.5 C 39,3.9076486 36.092351,1 32.5,1 L 0,1 z"
+         transform="matrix(-1,0,0,1,40,0)"
+         id="rect5056"
+         sodipodi:nodetypes="cccccccccccc" />
+      <path
+         style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 7.5,0 C 3.3536486,0 0,3.3536486 0,7.5 L 0,20 L 1,20 L 1,7.5 C 1,3.8903514 3.8903514,1 7.5,1 L 40,1 C 40,1 40,0 40,0 L 7.5,0 z"
+         id="rect3509"
+         sodipodi:nodetypes="ccccccccc" />
+      <rect
+         style="opacity:1;fill:#8b96ba;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         id="rect5099"
+         width="1"
+         height="1"
+         x="38"
+         y="19"
+         transform="matrix(-1,0,0,1,40,0)" />
+    </g>
+    <rect
+       style="opacity:1;fill:#264356;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect3267"
+       width="38"
+       height="1"
+       x="-38"
+       y="19"
+       transform="scale(-1,1)" />
+  </g>
+</svg>

Modified: trunk/tests/header.svg
==============================================================================
--- trunk/tests/header.svg	(original)
+++ trunk/tests/header.svg	Tue Jun 10 21:30:02 2008
@@ -9,7 +9,7 @@
    xmlns:xlink="http://www.w3.org/1999/xlink";
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
-   width="50"
+   width="40"
    height="20"
    id="svg2715"
    sodipodi:version="0.32"
@@ -20,13 +20,24 @@
   <defs
      id="defs2717">
     <linearGradient
+       id="linearGradient5058">
+      <stop
+         style="stop-color:#426781;stop-opacity:1;"
+         offset="0"
+         id="stop5060" />
+      <stop
+         style="stop-color:#425f74;stop-opacity:1;"
+         offset="1"
+         id="stop5062" />
+    </linearGradient>
+    <linearGradient
        id="linearGradient3531">
       <stop
-         style="stop-color:#bcbcbc;stop-opacity:1;"
+         style="stop-color:#476d88;stop-opacity:1;"
          offset="0"
          id="stop3533" />
       <stop
-         style="stop-color:#5b5bdf;stop-opacity:1;"
+         style="stop-color:#59809c;stop-opacity:1;"
          offset="1"
          id="stop3535" />
     </linearGradient>
@@ -74,6 +85,15 @@
        y1="2"
        x2="25"
        y2="19" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5058"
+       id="linearGradient5064"
+       x1="40"
+       y1="2"
+       x2="40"
+       y2="19"
+       gradientUnits="userSpaceOnUse" />
   </defs>
   <sodipodi:namedview
      id="base"
@@ -85,9 +105,9 @@
      objecttolerance="10"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="8"
-     inkscape:cx="0.053668"
-     inkscape:cy="25.046335"
+     inkscape:zoom="16"
+     inkscape:cx="26.481784"
+     inkscape:cy="1.8001996"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
@@ -95,7 +115,7 @@
      inkscape:window-width="1400"
      inkscape:window-height="1000"
      inkscape:window-x="0"
-     inkscape:window-y="24">
+     inkscape:window-y="0">
     <inkscape:grid
        type="xygrid"
        id="grid2725"
@@ -121,26 +141,36 @@
      id="layer1">
     <path
        style="fill:url(#linearGradient3812);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       d="M 5,1 L 1,5 L 1,20 L 49,20 L 49,5 L 45,1 L 5,1 z"
-       id="path3529" />
+       d="M 5,1 L 1,5 L 1,20 L 40,20 L 40,1 L 5,1 z"
+       id="path3529"
+       sodipodi:nodetypes="cccccc" />
     <g
        id="g2388"
        style="enable-background:new">
       <path
-         style="opacity:1;fill:#707070;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
-         d="M 7.5,0 C 3.3536486,0 0,3.3536486 0,7.5 L 0,20 L 1,20 L 1,7.5 C 1,3.8903514 3.8903514,1 7.5,1 L 42.5,1 C 46.109649,1 49,3.8903514 49,7.5 L 49,20 L 50,20 L 50,7.5 C 50,3.3536486 46.646351,0 42.5,0 L 7.5,0 z"
-         id="rect3509" />
+         style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 7.5,0 C 3.3536486,0 0,3.3536486 0,7.5 L 0,20 L 1,20 L 1,7.5 C 1,3.8903514 3.8903514,1 7.5,1 L 40,1 C 40,1 40,0 40,0 L 7.5,0 z"
+         id="rect3509"
+         sodipodi:nodetypes="ccccccccc" />
       <path
-         style="opacity:1;fill:#c3dff4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
-         d="M 7.5,1 C 3.9076486,1 1,3.9076486 1,7.5 L 1,20 L 2,20 L 2,7.5 C 2,4.4443514 4.4443514,2 7.5,2 L 42.5,2 C 45.555649,2 48,4.4443514 48,7.5 L 48,20 L 49,20 L 49,7.5 C 49,3.9076486 46.092351,1 42.5,1 L 7.5,1 z"
-         id="rect3511" />
+         style="opacity:1;fill:#849bab;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
+         d="M 7.5,1 C 3.9076486,1 1,3.9076486 1,7.5 L 1,20 L 2,20 L 2,7.5 C 2,4.4443514 4.4443514,2 7.5,2 L 40,2 C 40,2 40,1 40,1 L 7.5,1 z"
+         id="rect3511"
+         sodipodi:nodetypes="ccccccccc" />
     </g>
     <rect
-       style="opacity:1;fill:#0d1c59;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       style="opacity:1;fill:#264356;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
        id="rect3267"
-       width="46"
+       width="38"
        height="1"
        x="2"
        y="19" />
+    <rect
+       style="opacity:1;fill:url(#linearGradient5064);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect5056"
+       width="1"
+       height="17"
+       x="39"
+       y="2" />
   </g>
 </svg>

Modified: trunk/tests/main.svg
==============================================================================
--- trunk/tests/main.svg	(original)
+++ trunk/tests/main.svg	Tue Jun 10 21:30:02 2008
@@ -10,7 +10,7 @@
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
    width="50"
-   height="40"
+   height="20"
    id="svg2715"
    sodipodi:version="0.32"
    inkscape:version="0.46"
@@ -20,6 +20,17 @@
   <defs
      id="defs2717">
     <linearGradient
+       id="linearGradient3929">
+      <stop
+         style="stop-color:#849bab;stop-opacity:1;"
+         offset="0"
+         id="stop3931" />
+      <stop
+         style="stop-color:#3e6179;stop-opacity:1;"
+         offset="1"
+         id="stop3933" />
+    </linearGradient>
+    <linearGradient
        id="linearGradient3531">
       <stop
          style="stop-color:#bcbcbc;stop-opacity:0.78431374;"
@@ -56,15 +67,22 @@
        y1="5"
        x2="45"
        y2="45" />
+    <inkscape:perspective
+       id="perspective3403"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       sodipodi:type="inkscape:persp3d" />
     <linearGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient3531"
-       id="linearGradient3211"
-       gradientUnits="userSpaceOnUse"
-       x1="5"
-       y1="5"
-       x2="45"
-       y2="45" />
+       xlink:href="#linearGradient3929"
+       id="linearGradient3935"
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="20"
+       gradientUnits="userSpaceOnUse" />
   </defs>
   <sodipodi:namedview
      id="base"
@@ -76,9 +94,9 @@
      objecttolerance="10"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="8"
-     inkscape:cx="27.964833"
-     inkscape:cy="33.919797"
+     inkscape:zoom="4"
+     inkscape:cx="86.910825"
+     inkscape:cy="24.49684"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
@@ -86,7 +104,7 @@
      inkscape:window-width="1400"
      inkscape:window-height="1000"
      inkscape:window-x="0"
-     inkscape:window-y="24"
+     inkscape:window-y="0"
      showguides="true"
      inkscape:guide-bbox="true">
     <inkscape:grid
@@ -114,20 +132,19 @@
      id="layer1"
      transform="translate(0,-10)">
     <path
-       style="fill:url(#linearGradient3211);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       d="M 1,10 L 1,45 L 5,49 L 45,49 L 49,45 L 49,10 L 1,10 z"
+       style="fill:#345b75;fill-opacity:0.78431374;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 1,0 L 1,20 L 49,20 L 49,0 L 1,0 z"
+       transform="translate(0,10)"
        id="path3529" />
-    <g
-       id="g2388"
-       style="enable-background:new">
-      <path
-         style="opacity:1;fill:#707070;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
-         d="M 0,10 L 0,42.5 C 0,46.646351 3.3536486,50 7.5,50 L 42.5,50 C 46.646351,50 50,46.646351 50,42.5 L 50,10 L 49,10 L 49,42.5 C 49,46.109649 46.109649,49 42.5,49 L 7.5,49 C 3.8903514,49 1,46.109649 1,42.5 L 1,10 L 0,10 z"
-         id="rect3509" />
-      <path
-         style="opacity:1;fill:#c3dff4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;comp-op:plus"
-         d="M 1,10 L 1,42.5 C 1,46.092351 3.9076486,49 7.5,49 L 42.5,49 C 46.092351,49 49,46.092351 49,42.5 L 49,10 L 48,10 L 48,42.5 C 48,45.555649 45.555649,48 42.5,48 L 7.5,48 C 4.4443514,48 2,45.555649 2,42.5 L 2,10 L 1,10 z"
-         id="rect3511" />
-    </g>
+    <path
+       style="opacity:1;fill:url(#linearGradient3935);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="M 1,0 L 1,20 L 2,20 L 2,0 L 1,0 z M 48,0 L 48,20 L 49,20 L 49,0 L 48,0 z"
+       transform="translate(0,10)"
+       id="rect3511" />
+    <path
+       style="opacity:1;fill:#414d55;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="M 0,0 L 0,20 L 1,20 L 1,0 L 0,0 z M 49,0 L 49,20 L 50,20 L 50,0 L 49,0 z"
+       transform="translate(0,10)"
+       id="rect3509" />
   </g>
 </svg>

Modified: trunk/tests/test-transparent.css
==============================================================================
--- trunk/tests/test-transparent.css	(original)
+++ trunk/tests/test-transparent.css	Tue Jun 10 21:30:02 2008
@@ -3,9 +3,49 @@
 }
 
 #header {
-    -hippo-background-image: url("header.svg") 10px 10px 1px;
+    -hippo-background-image: url("header.svg") 8px 1px 1px 8px;
+    padding: 4px 8px 4px;
+}
+
+#header * {
+    color: white;
+    font-size: 11pt;
+    font-weight: bold;
+}
+
+#header-right {
+    -hippo-background-image: url("header-right.svg") 8px 8px 1px 1px;
+    padding: 4px 8px 4px;
+}
+
+#header-right.prelight {
+    -hippo-background-image: url("header-right-prelight.svg") 8px 8px 1px 1px;
+    padding: 4px 8px 4px;
+}
+
+#close-x {
+    -hippo-background-image: url("close-x.svg");
+}
+
+#header-right.prelight #close-x {
+    -hippo-background-image: url("close-x-prelight.svg");
 }
 
 #main {
-    -hippo-background-image: url("main.svg") 0px 10px 10px;
+    -hippo-background-image: url("main.svg") 0px 8px 0px;
+    padding: 4px 8px 4px;
+}
+
+#main * {
+    color: white;
+    font-size: 11pt;
+}
+
+#bottom {
+    -hippo-background-image: url("bottom.svg") 1px 8px 8px;
+    padding: 6px 10px 5px;
+}
+
+#grippy {
+    -hippo-background-image: url("grippy.png");
 }

Modified: trunk/tests/test-transparent.py
==============================================================================
--- trunk/tests/test-transparent.py	(original)
+++ trunk/tests/test-transparent.py	Tue Jun 10 21:30:02 2008
@@ -1,25 +1,23 @@
 #!/usr/bin/python
 # coding=UTF_8
 
-from __future__ import division
-
+import cairo
 import pygtk
 pygtk.require('2.0')
-import gobject
 import gtk
+from expand_box import ExpandBox
 import hippo
-import rsvg
 import sys
 
 window = hippo.CanvasWindow()
 
 if not window.get_screen().is_composited():
-    print sys.stderr, "Compositing manager must be running"
+    print >>sys.stderr, "Compositing manager must be running"
     sys.exit(1)
 
 colormap = window.get_screen().get_rgba_colormap()
 if colormap == None:
-    print sys.stderr, "RGBA visual not found"
+    print >>sys.stderr, "RGBA visual not found"
     sys.exit(1)
 
 window.set_colormap(colormap)
@@ -29,19 +27,31 @@
 window.set_theme(theme)
 
 window.connect('delete-event', lambda *args: gtk.main_quit())
-window.connect('button-press-event', lambda *args: gtk.main_quit())
 
 root = hippo.CanvasBox(id='root')
 window.set_root(root)
 
+top_box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL)
+root.append(top_box)
+
 header = hippo.CanvasBox(id='header')
-root.append(header)
+top_box.append(header, hippo.PACK_EXPAND)
+
+header_right = ExpandBox(id='header-right', yalign=hippo.ALIGNMENT_CENTER)
+header_right.set_clickable(True)
+top_box.append(header_right)
+
+close_x = hippo.CanvasBox(id='close-x', box_width=10, box_height=10)
+header_right.append(close_x)
 
-header_text = hippo.CanvasText(text="Khayyam via Fitzgerald", border=5, font='18')
+header_text = hippo.CanvasText(text="Khayyam via Fitzgerald", xalign=hippo.ALIGNMENT_START)
 header.append(header_text)
 
 main = hippo.CanvasBox(id='main')
-root.append(main)
+root.append(main, hippo.PACK_EXPAND)
+
+expand = ExpandBox()
+main.append(expand, hippo.PACK_EXPAND)
 
 text = u"""
 Think, in this batterâd Caravanserai
@@ -50,9 +60,99 @@
 Abode his Hour or two, and went his way.
 """.strip()
 
-text_item = hippo.CanvasText(text=text, border=10)
-main.append(text_item)
+text_item = hippo.CanvasText(text=text)
+expand.append(text_item)
+
+bottom = hippo.CanvasBox(id='bottom', orientation=hippo.ORIENTATION_HORIZONTAL, xalign=hippo.ALIGNMENT_CENTER)
+root.append(bottom)
+
+grippy = hippo.CanvasBox(id='grippy', box_width=20, box_height=8)
+bottom.append(grippy)
+
+expanded = True
+def toggle_expanded():
+    global expanded
+    expanded = not expanded
+    expand.animate(0.3333)
+    text_item.set_visible(expanded)
+    window.set_resizable(expanded)
+
+header_button_down = False
+header_button_down_x = 0;
+header_button_down_y = 0;
+    
+def on_header_button_press(item, event):
+    global header_button_down, header_button_down_x, header_button_down_y
+    
+    if event.button != 1:
+        return
+
+    if event.count == 2:
+        toggle_expanded()
+    elif event.count == 1:
+        header_button_down = True
+        header_button_down_x = event.x
+        header_button_down_y = event.y
+
+def on_header_button_release(item, event):
+    global header_button_down, header_button_down_x, header_button_down_y
+    
+    if event.button != 1:
+        return
+    header_button_down = False
+
+def begin_move():
+    global header_button_down, header_button_down_x, header_button_down_y
+    
+    event = gtk.get_current_event()
+    window.begin_move_drag(1,
+                           int(round(0.5 + event.x_root)), int(round(event.y_root)),
+                           event.time)
+    
+def on_header_motion_notify(item, event):
+    if not header_button_down:
+        return
+
+    distance = max(abs(event.x - header_button_down_x), abs(event.x - header_button_down_x))
+    double_click_distance = gtk.settings_get_for_screen(window.get_screen()).props.gtk_double_click_distance
+    if distance > double_click_distance:
+        begin_move()
+
+header.connect('button-press-event', on_header_button_press)
+header.connect('button-release-event', on_header_button_release)
+header.connect('motion-notify-event', on_header_motion_notify)
+
+def begin_resize(*args):
+    event = gtk.get_current_event()
+    window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH,
+                             event.button,
+                             int(round(0.5 + event.x_root)), int(round(event.y_root)),
+                             event.time)
+
+bottom.connect('button-press-event', begin_resize)
+
+def on_header_right_motion_notify(item, event):
+    if event.detail == hippo.MOTION_DETAIL_ENTER:
+        item.animate(0.20)
+        item.props.classes = 'prelight'
+    if event.detail == hippo.MOTION_DETAIL_LEAVE:
+        item.animate(0.20)
+        item.props.classes = ''
+
+header_right.connect('motion-notify-event', on_header_right_motion_notify)
+
+header_right.connect('activated', lambda *args: gtk.main_quit())
+
+width, _ = window.size_request()
+window.set_size_request(width, -1)
 
 window.show()
 
+def toggle_timeout():
+    toggle_expanded()
+    return True
+
+#gobject.timeout_add(4000, toggle_timeout)
+#toggle_expanded()
+
 gtk.main()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]