summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Sandmann <sandmann@redhat.com>2009-03-26 18:18:39 -0400
committerSøren Sandmann <sandmann@redhat.com>2009-03-26 18:18:39 -0400
commit7f4f44e95a55356e0ebff3a2d410b9308f44b60f (patch)
tree4b2508437af381f03c6bc855bdb0e96b7929e9b5
parentc2520b87ac06bdd92fe785d5907bc0d707df377f (diff)
Update scrollarea.[ch]
-rw-r--r--backingstore.c833
-rw-r--r--backingstore.h42
-rw-r--r--scrollarea.c1069
-rw-r--r--scrollarea.h5
-rw-r--r--toolbar.c18
5 files changed, 1226 insertions, 741 deletions
diff --git a/backingstore.c b/backingstore.c
index e52377b..a9ea014 100644
--- a/backingstore.c
+++ b/backingstore.c
@@ -1,28 +1,54 @@
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
#include "backingstore.h"
+typedef struct InputPath InputPath;
+typedef struct InputRegion InputRegion;
+
struct BackingStore
{
- GdkPixmap *pixmap;
- GdkRegion *update_region;
- int width;
- int height;
+ GdkPixmap * pixmap;
+ GdkRegion * update_region;
+ int width;
+ int height;
+
+ /* Input */
+ InputRegion * current_input;
+ GPtrArray * input_regions;
+ int current_x;
+ int current_y;
+ BackingStoreEventFunc last_func;
+ gpointer last_data;
+ int last_x;
+ int last_y;
+ gboolean last_was_motion;
+ gboolean implicit_grab;
+ gboolean grabbed;
+ BackingStoreEventFunc grab_func;
+ gpointer grab_data;
+
+ guint32 motion_notify_time;
+ gboolean need_fake_motion;
+ gboolean in_process_updates;
};
-BackingStore *
-backing_store_new (GdkWindow *window,
- int width,
- int height)
+struct InputPath
{
- BackingStore *store = g_new0 (BackingStore, 1);
- GdkRectangle rect = { 0, 0, width, height };
+ cairo_fill_rule_t fill_rule;
+ double line_width;
+ cairo_path_t *path;
+ gboolean is_stroke;
- store->pixmap = gdk_pixmap_new (window, width, height, -1);
- store->update_region = gdk_region_rectangle (&rect);
- store->width = width;
- store->height = height;
+ BackingStoreEventFunc func;
+ gpointer data;
+};
- return store;
-}
+/* InputRegions are mutually disjoint */
+struct InputRegion
+{
+ GdkRegion *region;
+ GArray *paths;
+};
void
backing_store_free (BackingStore *store)
@@ -33,55 +59,82 @@ backing_store_free (BackingStore *store)
}
void
-backing_store_scroll (BackingStore *store,
- int dx,
- int dy)
+backing_store_move_area (BackingStore *store,
+ int x,
+ int y,
+ int width,
+ int height,
+ int dx,
+ int dy)
{
- GdkGC *gc = gdk_gc_new (store->pixmap);
+ GdkRegion *moving_invalid;
+ GdkRegion *region;
GdkRectangle rect;
+ GdkGC *gc;
+ gc = gdk_gc_new (store->pixmap);
gdk_draw_drawable (store->pixmap, gc, store->pixmap,
- 0, 0, dx, dy,
- store->width, store->height);
+ x, y, x + dx, y + dy,
+ width, height);
+ g_object_unref (gc);
- gdk_region_offset (store->update_region, dx, dy);
-
- /* Invalidate vertically */
- rect.x = 0;
- rect.width = store->width;
+ rect.x = x;
+ rect.y = y;
+ rect.width = width;
+ rect.height = height;
- if (dy > 0)
- {
- rect.y = 0;
- rect.height = dy;
- }
- else
- {
- rect.y = store->height + dy;
- rect.height = -dy;
- }
+ region = gdk_region_rectangle (&rect);
- gdk_region_union_with_rect (store->update_region, &rect);
-
- /* Invalidate horizontally */
+ /* Compute the invalid part of the region that moves */
+ moving_invalid = gdk_region_copy (region);
+ gdk_region_intersect (moving_invalid, store->update_region);
+
+ /* Invalidate the source rectangle */
+ gdk_region_union (store->update_region, region);
+
+ /* Uninvalidate the destination rectangle */
+ gdk_region_offset (region, dx, dy);
+ gdk_region_subtract (store->update_region, region);
+
+ /* Invalidate the invalid parts that moved */
+ gdk_region_union (store->update_region, moving_invalid);
+
+ gdk_region_destroy (moving_invalid);
+ gdk_region_destroy (region);
+
+ /* Intersect invalid area with store extents */
+ rect.x = 0;
rect.y = 0;
+ rect.width = store->width;
rect.height = store->height;
- if (dx > 0)
- {
- rect.x = 0;
- rect.width = dx;
- }
- else
- {
- rect.x = store->width + dx;
- rect.width = -dx;
- }
+ region = gdk_region_rectangle (&rect);
+ gdk_region_intersect (store->update_region, region);
+ gdk_region_destroy (region);
+
+ /* FIXME: move the input areas instead */
+ backing_store_invalidate_all (store);
- gdk_region_union_with_rect (store->update_region, &rect);
+ if (!gdk_region_empty (store->update_region))
+ store->need_fake_motion = TRUE;
+}
+
+static void
+process_event (BackingStore *store,
+ BackingStoreEventType input_type,
+ int x,
+ int y,
+ int button);
+
+
+void
+backing_store_scroll (BackingStore *store,
+ int dx,
+ int dy)
+{
+ backing_store_move_area (store, 0, 0, store->width, store->height, dx, dy);
}
-#if 0
static void
print_region (const char *header, GdkRegion *region)
{
@@ -101,7 +154,465 @@ print_region (const char *header, GdkRegion *region)
g_free (rects);
}
+
+static void
+begin_grab_internal (BackingStore *store,
+ gboolean implicit,
+ BackingStoreEventFunc func,
+ gpointer data)
+{
+#if 0
+ g_print ("begin%s grab\n", implicit? " implicit" : "");
+#endif
+
+ /* FIXME: we should probably take a server grab */
+ /* Also, there should be support for setting the grab cursor */
+
+ /* Grabs can be overridden if desired - FIXME: make sure this right */
+ /* We may need a grab counter here if there are both implicit and
+ * explicit grabs in play at the same time
+ */
+ store->implicit_grab = implicit;
+ store->grabbed = TRUE;
+ store->grab_func = func;
+ store->grab_data = data;
+}
+
+static void
+end_grab_internal (BackingStore *store,
+ gboolean only_implicit)
+{
+ if (only_implicit && !store->implicit_grab)
+ return;
+
+#if 0
+ g_print ("end grab\n");
+#endif
+
+ store->implicit_grab = FALSE;
+ store->grabbed = FALSE;
+ store->grab_func = NULL;
+ store->grab_data = NULL;
+}
+
+static void
+emit_input (BackingStore *store,
+ BackingStoreEventType type,
+ int x,
+ int y,
+ int button,
+ BackingStoreEventFunc func,
+ gpointer data)
+{
+ BackingStoreEvent event;
+ gboolean finished_grab = FALSE;
+
+ event.x = x;
+ event.y = y;
+ event.button = button;
+
+#if 0
+ if (store->grabbed)
+ g_print ("grabbed\n");
#endif
+
+ /* Handle crossing events */
+ if (store->last_func != func || store->last_data != data)
+ {
+ if (!store->grabbed ||
+ (store->last_func == store->grab_func &&
+ store->last_data == store->grab_data))
+ {
+ event.type = BACKING_STORE_LEAVE;
+
+ if (store->last_func)
+ {
+#if 0
+ g_print ("leave: %d\n", ec);
+ g_print ("leave %p -> %p\n", store->last_func, func);
+#endif
+
+ store->last_func (store, &event, store->last_data);
+ }
+ }
+
+ if (!store->grabbed ||
+ (func == store->grab_func &&
+ data == store->grab_data))
+ {
+ event.type = BACKING_STORE_ENTER;
+
+ if (func)
+ {
+#if 0
+ g_print ("enter %d\n", ec);
+#endif
+ func (store, &event, data);
+ }
+ }
+ }
+
+ event.type = type;
+
+ if (event.type != BACKING_STORE_NONE)
+ {
+ BackingStoreEventFunc emit_func;
+ gpointer emit_data;
+
+ if (store->grabbed)
+ {
+ emit_func = store->grab_func;
+ emit_data = store->grab_data;
+ }
+ else
+ {
+ emit_func = func;
+ emit_data = data;
+ }
+
+ if (emit_func)
+ {
+ if (event.type == BACKING_STORE_BUTTON_PRESS)
+ begin_grab_internal (store, TRUE, emit_func, emit_data);
+
+#if 0
+ g_print ("other %d\n", ec);
+#endif
+
+ emit_func (store, &event, emit_data);
+
+ if (event.type == BACKING_STORE_BUTTON_RELEASE)
+ {
+ end_grab_internal (store, TRUE);
+
+ finished_grab = TRUE;
+ }
+ }
+ }
+
+ store->last_func = func;
+ store->last_data = data;
+ store->last_x = x;
+ store->last_y = y;
+ store->last_was_motion = (event.type == BACKING_STORE_MOTION);
+
+ if (finished_grab)
+ {
+ /* When a grab has finished, fake a motion to where
+ * the last grabbed event happened.
+ */
+ process_event (store, BACKING_STORE_MOTION, x, y, 0);
+ }
+}
+
+static void
+process_event (BackingStore *store,
+ BackingStoreEventType input_type,
+ int x,
+ int y,
+ int button)
+{
+ int i;
+
+ for (i = 0; i < store->input_regions->len; ++i)
+ {
+ InputRegion *region = store->input_regions->pdata[i];
+
+ if (gdk_region_point_in (region->region, x, y))
+ {
+ int j;
+
+ /* Iterate backwards since the topmost paths are at
+ * the back.
+ */
+ for (j = region->paths->len - 1; j >= 0; --j)
+ {
+ InputPath *path = &g_array_index (region->paths, InputPath, j);
+ cairo_t *cr;
+ gboolean inside;
+
+ if (store->grabbed &&
+ (path->func != store->grab_func ||
+ path->data != store->grab_data))
+ {
+ /* When there is a grab in place we only care
+ * about input regions for the purposes of
+ * generating enter events for the grab
+ * function.
+ */
+ continue;
+ }
+
+ cr = gdk_cairo_create (store->pixmap);
+ cairo_set_fill_rule (cr, path->fill_rule);
+ cairo_set_line_width (cr, path->line_width);
+ cairo_append_path (cr, path->path);
+
+ if (path->is_stroke)
+ inside = cairo_in_stroke (cr, x, y);
+ else
+ inside = cairo_in_fill (cr, x, y);
+
+ cairo_destroy (cr);
+
+ if (inside)
+ {
+ emit_input (store, input_type,
+ x, y, button,
+ path->func,
+ path->data);
+
+ return;
+ }
+ }
+
+ /* Since the regions are all disjoint, no other region
+ * can match. Of course we could be clever and try and
+ * sort the regions, but so far I have been unable to
+ * make this loop show up on a profile.
+ */
+ break;
+ }
+ }
+
+ /* If we got here, no event was emitted, so we emit
+ * an event with a NULL event function. This will cause some
+ * internal bookkeeping to be updated, and an event to be
+ * emitted if a grab happens to be active.
+ */
+ emit_input (store, input_type, x, y, button, NULL, NULL);
+}
+
+typedef struct
+{
+ Window window;
+ guint32 last_motion_time;
+ gboolean stop_compressing;
+} EventScannerData;
+
+static Bool
+get_motion_time (Display *dpy,
+ XEvent *xevent,
+ XPointer arg)
+{
+ EventScannerData *esd = (EventScannerData *)arg;
+
+ if (esd->stop_compressing)
+ return FALSE;
+
+ if (esd->window != xevent->xany.window)
+ {
+ esd->stop_compressing = TRUE;
+ return FALSE;
+ }
+
+ if (xevent->type == MotionNotify)
+ {
+ esd->last_motion_time = xevent->xmotion.time;
+ return FALSE;
+ }
+
+ if (xevent->type == EnterNotify ||
+ xevent->type == LeaveNotify)
+ {
+ esd->last_motion_time = xevent->xcrossing.time;
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+use_this_motion_notify (GdkWindow *window,
+ guint32 event_time,
+ guint32 *motion_notify_time)
+{
+ if (*motion_notify_time)
+ {
+ if (event_time == *motion_notify_time)
+ {
+ *motion_notify_time = 0;
+
+ return TRUE;
+ }
+ else
+ {
+ /* There are more motion events already queued up */
+
+ return FALSE;
+ }
+ }
+ else
+ {
+ Display *dpy = gdk_x11_get_default_xdisplay();
+ EventScannerData esd = { GDK_WINDOW_XWINDOW (window), 0, FALSE };
+ XEvent dummy;
+
+ XCheckIfEvent (dpy, &dummy, get_motion_time, (XPointer)&esd);
+
+ if (esd.last_motion_time == 0)
+ {
+ return TRUE;
+ }
+ else
+ {
+ *motion_notify_time = esd.last_motion_time;
+ return FALSE;
+ }
+ }
+}
+
+/* Input */
+void
+backing_store_process_event (BackingStore *store,
+ GdkEvent *event)
+{
+ BackingStoreEventType input_type = BACKING_STORE_NONE;
+ int button = 0;
+ int x, y;
+
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ input_type = BACKING_STORE_BUTTON_PRESS;
+ button = event->button.button;
+ x = event->button.x;
+ y = event->button.y;
+ }
+ else if (event->type == GDK_BUTTON_RELEASE)
+ {
+ input_type = BACKING_STORE_BUTTON_RELEASE;
+ button = event->button.button;
+ x = event->button.x;
+ y = event->button.y;
+ }
+ else if (event->type == GDK_MOTION_NOTIFY ||
+ event->type == GDK_LEAVE_NOTIFY ||
+ event->type == GDK_ENTER_NOTIFY)
+ {
+ GdkWindow *window;
+ guint32 time;
+
+ input_type = BACKING_STORE_MOTION;
+ button = 0;
+
+ if (event->type == GDK_MOTION_NOTIFY)
+ {
+ x = event->motion.x;
+ y = event->motion.y;
+ window = event->motion.window;
+ time = event->motion.time;
+ }
+ else
+ {
+ x = event->crossing.x;
+ y = event->crossing.y;
+ window = event->crossing.window;
+ time = event->crossing.time;
+ }
+
+ if (!use_this_motion_notify (window, time, &store->motion_notify_time))
+ return;
+ }
+ else
+ {
+ return;
+ }
+
+ process_event (store, input_type, x, y, button);
+
+ store->current_x = x;
+ store->current_y = y;
+}
+
+static void
+user_to_device (double *x, double *y,
+ gpointer data)
+{
+ cairo_surface_t *surface = cairo_get_target (data);
+ double dev_x;
+ double dev_y;
+
+ cairo_surface_get_device_offset (surface, &dev_x, &dev_y);
+
+ cairo_user_to_device (data, x, y);
+ *x += dev_x;
+ *y += dev_y;
+
+
+}
+
+typedef void (* PathForeachFunc) (double *x,
+ double *y,
+ gpointer data);
+
+static void
+path_foreach_point (cairo_path_t *path,
+ PathForeachFunc func,
+ gpointer user_data)
+{
+ int i;
+
+ for (i = 0; i < path->num_data; i += path->data[i].header.length)
+ {
+ cairo_path_data_t *data = &(path->data[i]);
+
+ switch (data->header.type)
+ {
+ case CAIRO_PATH_MOVE_TO:
+ case CAIRO_PATH_LINE_TO:
+ func (&(data[1].point.x), &(data[1].point.y), user_data);
+ break;
+
+ case CAIRO_PATH_CURVE_TO:
+ func (&(data[1].point.x), &(data[1].point.y), user_data);
+ func (&(data[2].point.x), &(data[2].point.y), user_data);
+ func (&(data[3].point.x), &(data[3].point.y), user_data);
+ break;
+
+ case CAIRO_PATH_CLOSE_PATH:
+ break;
+ }
+ }
+}
+
+void
+backing_store_add_input (BackingStore *store,
+ cairo_t *cr,
+ gboolean is_stroke,
+ BackingStoreEventFunc func,
+ gpointer data)
+{
+ InputPath path;
+
+ path.is_stroke = is_stroke;
+ path.fill_rule = cairo_get_fill_rule (cr);
+ path.line_width = cairo_get_line_width (cr);
+ path.path = cairo_copy_path (cr);
+ path_foreach_point (path.path, user_to_device, cr);
+ path.func = func;
+ path.data = data;
+
+ g_array_append_val (store->current_input->paths, path);
+}
+
+BackingStore *
+backing_store_new (GdkWindow *window,
+ int width,
+ int height)
+{
+ BackingStore *store = g_new0 (BackingStore, 1);
+ GdkRectangle rect = { 0, 0, width, height };
+
+ store->pixmap = gdk_pixmap_new (window, width, height, -1);
+ store->update_region = gdk_region_rectangle (&rect);
+ store->width = width;
+ store->height = height;
+
+ store->current_input = NULL;
+ store->input_regions = g_ptr_array_new ();
+
+ return store;
+}
void
backing_store_invalidate_rect (BackingStore *store,
@@ -140,23 +651,6 @@ backing_store_invalidate_all (BackingStore *store)
store->update_region = gdk_region_rectangle (&rect);
}
-static void
-simple_draw_drawable (GdkDrawable *dst,
- GdkDrawable *src,
- int src_x,
- int src_y,
- int dst_x,
- int dst_y,
- int width,
- int height)
-{
- GdkGC *gc = gdk_gc_new (dst);
-
- gdk_draw_drawable (dst, gc, src, src_x, src_y, dst_x, dst_y, width, height);
-
- g_object_unref (gc);
-}
-
void
backing_store_resize (BackingStore *store,
int width,
@@ -165,7 +659,8 @@ backing_store_resize (BackingStore *store,
GdkPixmap *pixmap;
GdkRegion *old, *invalid;
GdkRectangle r;
-
+ GdkGC *gc;
+
width = MAX (width, 1);
height = MAX (height, 1);
@@ -178,8 +673,10 @@ backing_store_resize (BackingStore *store,
* That might just work, actually. We need to make sure metacity uses
* static gravity for the window before this will be useful.
*/
- simple_draw_drawable (pixmap, store->pixmap, 0, 0, 0, 0, -1, -1);
-
+ gc = gdk_gc_new (pixmap);
+ gdk_draw_drawable (pixmap, gc, store->pixmap, 0, 0, 0, 0, -1, -1);
+ g_object_unref (gc);
+
g_object_unref (store->pixmap);
store->pixmap = pixmap;
@@ -206,9 +703,93 @@ backing_store_resize (BackingStore *store,
gdk_region_destroy (invalid);
}
+void
+backing_store_draw (BackingStore *store,
+ GdkDrawable *dest,
+ GdkRegion *clip,
+ int dest_x,
+ int dest_y)
+{
+ GdkGC *gc;
+
+ if (store->in_process_updates)
+ {
+ g_critical ("backing_store_draw() cannot be called when "
+ "backing_store_process_updates() is running\n");
+
+ return;
+ }
+
+ gc = gdk_gc_new (dest);
+
+ gdk_gc_set_clip_region (gc, clip);
+
+ gdk_draw_drawable (dest, gc, store->pixmap,
+ 0, 0, dest_x, dest_y, store->width, store->height);
+
+ g_object_unref (gc);
+}
+
+static GdkRegion *
+make_store_region (BackingStore *store)
+{
+ GdkRectangle store_rect;
+
+ store_rect.x = 0;
+ store_rect.y = 0;
+ store_rect.width = store->width;
+ store_rect.height = store->height;
+
+ return gdk_region_rectangle (&store_rect);
+}
static void
-cclip_to_region (cairo_t *cr, GdkRegion *region)
+input_region_free (InputRegion *region)
+{
+ int i;
+
+ for (i = 0; i < region->paths->len; ++i)
+ {
+ InputPath *path = &g_array_index (region->paths, InputPath, i);
+
+ cairo_path_destroy (path->path);
+ }
+
+ g_array_free (region->paths, TRUE);
+
+ gdk_region_destroy (region->region);
+
+ g_free (region);
+}
+
+static void
+clear_exposed_input_region (BackingStore *store,
+ GdkRegion *region)
+{
+ int i;
+ GdkRegion *store_reg = make_store_region (store);
+
+ gdk_region_subtract (store_reg, region);
+
+ for (i = 0; i < store->input_regions->len; ++i)
+ {
+ InputRegion *region = store->input_regions->pdata[i];
+
+ gdk_region_intersect (region->region, store_reg);
+
+ if (gdk_region_empty (region->region))
+ {
+ input_region_free (region);
+
+ g_ptr_array_remove_index_fast (store->input_regions, i--);
+ }
+ }
+
+ gdk_region_destroy (store_reg);
+}
+
+static void
+clip_to_region (cairo_t *cr, GdkRegion *region)
{
int n_rects;
GdkRectangle *rects;
@@ -228,38 +809,88 @@ cclip_to_region (cairo_t *cr, GdkRegion *region)
}
void
-backing_store_draw (BackingStore *store,
- GdkDrawable *dest,
- GdkRegion *clip,
- int dest_x,
- int dest_y)
-{
- GdkGC *gc = gdk_gc_new (dest);
-
- gdk_gc_set_clip_region (gc, clip);
-
- gdk_draw_drawable (dest, gc, store->pixmap,
- 0, 0, dest_x, dest_y, store->width, store->height);
- g_object_unref (gc);
-}
-
-void
backing_store_process_updates (BackingStore *store,
BackingPaintFunc func,
gpointer data)
{
- cairo_t *cr = gdk_cairo_create (store->pixmap);
- GdkRegion *region = store->update_region;
- store->update_region = gdk_region_new ();
-
- cclip_to_region (cr, region);
-
- cairo_set_source_rgb (cr, g_random_double(), 0, 0);
-
- cairo_paint (cr);
+ store->in_process_updates = TRUE;
- func (cr, region, data);
+ /* FIXME:
+ *
+ * The expose process itself can generate invalidations,
+ * so we need to continue to process updates until
+ * the update region is empty
+ *
+ * Yes, this can lead to infinite loops. Something should be
+ * done about that, but it's not completely clear what exactly.
+ *
+ * Consider the case where hovering an object causes the object to
+ * move out of the way, which causes non-hovering, which causes
+ * the object to move back - and so on. If the application is
+ * actually in this situation, then an infnitely loop is
+ * in some sense the logical result.
+ *
+ * A possibility is to only loop, say, five times, and just let
+ * applications sort it out by itself. (If they don't do anything,
+ * they will get flickering back and forth, which is probably the
+ * correct default behavior).
+ */
+ while (!gdk_region_empty (store->update_region))
+ {
+ cairo_t *cr = gdk_cairo_create (store->pixmap);
+ GdkRegion *region = store->update_region;
+ GdkRegion *store_reg = make_store_region (store);
+
+ gdk_region_intersect (region, store_reg);
+
+ gdk_region_destroy (store_reg);
+
+ store->update_region = gdk_region_new ();
+
+ clip_to_region (cr, region);
+
+ {
+ /* To make sure all of the clip region is painted */
+ cairo_set_source_rgb (cr, g_random_double(), 0, 0);
+ cairo_paint (cr);
+ }
+
+ clear_exposed_input_region (store, region);
+
+ store->current_input = g_new0 (InputRegion, 1);
+ store->current_input->region = region;
+ store->current_input->paths = g_array_new (
+ FALSE, FALSE, sizeof (InputPath));
+
+ func (cr, region, data);
+
+ g_ptr_array_add (store->input_regions, store->current_input);
+
+ store->current_input = NULL;
+
+ cairo_destroy (cr);
+
+ if (store->need_fake_motion)
+ {
+ /* The backing store was scrolled, so generate a fake
+ * motion event so that scrolling while dragging produces
+ * the correct result.
+ */
+ process_event (store, BACKING_STORE_MOTION,
+ store->current_x, store->current_y, 0);
+
+ store->need_fake_motion = FALSE;
+ }
+ else
+ {
+ /* Make sure any crossing events generated during the
+ * expose process are emitted here
+ */
+
+ process_event (store, BACKING_STORE_NONE,
+ store->current_x, store->current_y, 0);
+ }
+ }
- gdk_region_destroy (region);
- cairo_destroy (cr);
+ store->in_process_updates = FALSE;
}
diff --git a/backingstore.h b/backingstore.h
index 2d39214..3237885 100644
--- a/backingstore.h
+++ b/backingstore.h
@@ -3,12 +3,32 @@
#include <cairo.h>
typedef struct BackingStore BackingStore;
+typedef struct BackingStoreEvent BackingStoreEvent;
+
+typedef enum
+{
+ BACKING_STORE_NONE,
+ BACKING_STORE_BUTTON_PRESS,
+ BACKING_STORE_BUTTON_RELEASE,
+ BACKING_STORE_ENTER,
+ BACKING_STORE_LEAVE,
+ BACKING_STORE_MOTION,
+} BackingStoreEventType;
+
+struct BackingStoreEvent
+{
+ BackingStoreEventType type;
+ int x;
+ int y;
+ int button;
+};
typedef void (* BackingPaintFunc) (cairo_t *cr,
GdkRegion *region,
gpointer data);
-typedef gboolean (* BackingInputFunc) (gpointer input_data,
- gpointer user_data);
+typedef gboolean (* BackingStoreEventFunc) (BackingStore *store,
+ BackingStoreEvent *event,
+ gpointer *data);
BackingStore *backing_store_new (GdkWindow *window,
int width,
@@ -19,6 +39,14 @@ void backing_store_draw (BackingStore *store,
GdkRegion *clip,
int dest_x,
int dest_y);
+
+void backing_store_move_area (BackingStore *store,
+ int x,
+ int y,
+ int width,
+ int height,
+ int dx,
+ int dy);
void backing_store_scroll (BackingStore *store,
int dx,
int dy);
@@ -33,3 +61,13 @@ void backing_store_resize (BackingStore *store,
void backing_store_process_updates (BackingStore *store,
BackingPaintFunc func,
gpointer data);
+
+/* Input */
+void backing_store_process_event (BackingStore *store,
+ GdkEvent *event);
+void backing_store_add_input (BackingStore *store,
+ cairo_t *cr,
+ gboolean is_stroke,
+ BackingStoreEventFunc func,
+ gpointer data);
+
diff --git a/scrollarea.c b/scrollarea.c
index 622a402..dde63d8 100644
--- a/scrollarea.c
+++ b/scrollarea.c
@@ -5,38 +5,12 @@
#include "foo-marshal.h"
#include "backingstore.h"
-
-
G_DEFINE_TYPE (FooScrollArea, foo_scroll_area, GTK_TYPE_CONTAINER);
static GtkWidgetClass *parent_class;
-typedef struct InputPath InputPath;
-typedef struct InputRegion InputRegion;
typedef struct AutoScrollInfo AutoScrollInfo;
-struct InputPath
-{
- gboolean is_stroke;
- cairo_fill_rule_t fill_rule;
- double line_width;
- cairo_path_t *path; /* In canvas coordinates */
-
- FooScrollAreaEventFunc func;
- gpointer data;
-
- InputPath *next;
-};
-
-/* InputRegions are mutually disjoint */
-struct InputRegion
-{
- GdkRegion *region; /* the boundary of this area in
- * canvas coordinates
- */
- InputPath *paths;
-};
-
struct AutoScrollInfo
{
int dx;
@@ -71,8 +45,6 @@ struct FooScrollAreaPrivate
*
* It is used for clipping of input areas
*/
- InputRegion *current_input;
-
gboolean implicit_grab;
gboolean grabbed;
FooScrollAreaEventFunc grab_func;
@@ -80,13 +52,20 @@ struct FooScrollAreaPrivate
BackingStore *store;
- guint32 motion_notify_time;
-
gboolean in_size_allocate;
+ gboolean paint_updates;
/* Last emitted event went here - used to generate enters and leaves */
FooScrollAreaEventFunc last_func;
gpointer last_data;
+ int last_x;
+ int last_y;
+ gboolean last_was_motion;
+
+ int current_x;
+ int current_y;
+
+ gboolean need_flush;
};
enum
@@ -106,7 +85,7 @@ static gboolean foo_scroll_area_expose (GtkWidget *widget
GdkEventExpose *expose);
static void foo_scroll_area_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
-static void foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
+static void foo_scroll_area_set_scroll_adjustments (FooScrollArea *area,
GtkAdjustment *hadj,
GtkAdjustment *vadj);
static void foo_scroll_area_realize (GtkWidget *widget);
@@ -122,11 +101,18 @@ static gboolean foo_scroll_area_motion (GtkWidget *widget
static gboolean foo_scroll_area_crossing (GtkWidget *widget,
GdkEventCrossing *event);
-
static void
foo_scroll_area_map (GtkWidget *widget)
{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
+
GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+ gdk_window_get_pointer (widget->window,
+ &priv->current_x,
+ &priv->current_y,
+ NULL);
}
static void
@@ -138,14 +124,15 @@ foo_scroll_area_unmap (GtkWidget *widget)
static void
foo_scroll_area_finalize (GObject *object)
{
- FooScrollArea *scroll_area = FOO_SCROLL_AREA (object);
+ FooScrollArea *area = FOO_SCROLL_AREA (object);
+ FooScrollAreaPrivate *priv = area->priv;
- g_object_unref (scroll_area->priv->hadj);
- g_object_unref (scroll_area->priv->vadj);
+ g_object_unref (priv->hadj);
+ g_object_unref (priv->vadj);
- g_ptr_array_free (scroll_area->priv->input_regions, TRUE);
+ g_ptr_array_free (priv->input_regions, TRUE);
- g_free (scroll_area->priv);
+ g_free (priv);
G_OBJECT_CLASS (foo_scroll_area_parent_class)->finalize (object);
}
@@ -233,60 +220,27 @@ new_adjustment (void)
}
static void
-foo_scroll_area_init (FooScrollArea *scroll_area)
+foo_scroll_area_init (FooScrollArea *area)
{
-#if 0
- GTK_WIDGET_SET_FLAGS (scroll_area, GTK_NO_WINDOW);
-#endif
-
- gtk_widget_set_redraw_on_allocate (GTK_WIDGET (scroll_area), FALSE);
+ FooScrollAreaPrivate *priv;
- scroll_area->priv = g_new0 (FooScrollAreaPrivate, 1);
- scroll_area->priv->width = 0;
- scroll_area->priv->height = 0;
- scroll_area->priv->hadj = g_object_ref_sink (new_adjustment());
- scroll_area->priv->vadj = g_object_ref_sink (new_adjustment());
- scroll_area->priv->x_offset = 0.0;
- scroll_area->priv->y_offset = 0.0;
- scroll_area->priv->min_width = -1;
- scroll_area->priv->min_height = -1;
- scroll_area->priv->auto_scroll_info = NULL;
- scroll_area->priv->input_regions = g_ptr_array_new ();
+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (area), FALSE);
- gtk_widget_set_double_buffered (GTK_WIDGET (scroll_area), FALSE);
-}
+ priv = g_new0 (FooScrollAreaPrivate, 1);
+ priv->width = 0;
+ priv->height = 0;
+ priv->hadj = g_object_ref_sink (new_adjustment());
+ priv->vadj = g_object_ref_sink (new_adjustment());
+ priv->x_offset = 0.0;
+ priv->y_offset = 0.0;
+ priv->min_width = -1;
+ priv->min_height = -1;
+ priv->auto_scroll_info = NULL;
+ priv->input_regions = g_ptr_array_new ();
-static void
-translate_cairo_device (cairo_t *cr,
- int x_offset,
- int y_offset)
-{
- cairo_surface_t *surface = cairo_get_target (cr);
- double dev_x;
- double dev_y;
+ area->priv = priv;
- cairo_surface_get_device_offset (surface, &dev_x, &dev_y);
- dev_x += x_offset;
- dev_y += y_offset;
- cairo_surface_set_device_offset (surface, dev_x, dev_y);
-}
-
-static void
-print_region (const char *header, GdkRegion *region)
-{
- GdkRectangle *rects;
- int n_rects;
- int i;
-
- g_print ("%s\n", header);
-
- gdk_region_get_rectangles (region, &rects, &n_rects);
- for (i = 0; i < n_rects; ++i)
- {
- GdkRectangle *rect = &(rects[i]);
- g_print (" %d %d %d %d\n",
- rect->x, rect->y, rect->width, rect->height);
- }
+ gtk_widget_set_double_buffered (GTK_WIDGET (area), FALSE);
}
typedef void (* PathForeachFunc) (double *x,
@@ -368,76 +322,19 @@ path_compute_extents (cairo_path_t *path,
#endif
static void
-input_path_free_list (InputPath *paths)
-{
- if (!paths)
- return;
-
- input_path_free_list (paths->next);
- cairo_path_destroy (paths->path);
- g_free (paths);
-}
-
-static void
-input_region_free (InputRegion *region)
-{
- input_path_free_list (region->paths);
- gdk_region_destroy (region->region);
-
- g_free (region);
-}
-
-static void
-get_viewport (FooScrollArea *scroll_area,
+get_viewport (FooScrollArea *area,
GdkRectangle *viewport)
{
- GtkWidget *widget = GTK_WIDGET (scroll_area);
+ FooScrollAreaPrivate *priv = area->priv;
+ GtkWidget *widget = GTK_WIDGET (area);
- viewport->x = scroll_area->priv->x_offset;
- viewport->y = scroll_area->priv->y_offset;
+ viewport->x = priv->x_offset;
+ viewport->y = priv->y_offset;
viewport->width = widget->allocation.width;
viewport->height = widget->allocation.height;
}
static void
-allocation_to_canvas (FooScrollArea *area,
- int *x,
- int *y)
-{
- *x += area->priv->x_offset;
- *y += area->priv->y_offset;
-}
-
-static void
-clear_exposed_input_region (FooScrollArea *area,
- GdkRegion *exposed) /* in canvas coordinates */
-{
- int i;
- GdkRegion *viewport_reg;
- GdkRectangle viewport;
-
- get_viewport (area, &viewport);
-
- viewport_reg = gdk_region_rectangle (&viewport);
- gdk_region_subtract (viewport_reg, exposed);
-
- for (i = 0; i < area->priv->input_regions->len; ++i)
- {
- InputRegion *region = area->priv->input_regions->pdata[i];
-
- gdk_region_intersect (region->region, viewport_reg);
-
- if (gdk_region_empty (region->region))
- {
- input_region_free (region);
- g_ptr_array_remove_index_fast (area->priv->input_regions, i--);
- }
- }
-
- gdk_region_destroy (viewport_reg);
-}
-
-static void
setup_background_cr (GdkWindow *window,
cairo_t *cr,
int x_offset,
@@ -465,39 +362,51 @@ setup_background_cr (GdkWindow *window,
}
static void
+allocation_to_canvas (FooScrollArea *area,
+ int *x,
+ int *y)
+{
+ FooScrollAreaPrivate *priv = area->priv;
+
+ *x += priv->x_offset;
+ *y += priv->y_offset;
+}
+
+static void
canvas_to_window (FooScrollArea *area,
GdkRegion *region)
{
- gdk_region_offset (region,
- -area->priv->x_offset,
- -area->priv->y_offset);
+ FooScrollAreaPrivate *priv = area->priv;
+
+ gdk_region_offset (region, - priv->x_offset, - priv->y_offset);
}
static void
window_to_canvas (FooScrollArea *area,
GdkRegion *region)
{
- gdk_region_offset (region,
- area->priv->x_offset,
- area->priv->y_offset);
+ FooScrollAreaPrivate *priv = area->priv;
+
+ gdk_region_offset (region, priv->x_offset, priv->y_offset);
}
static void
canvas_to_store (FooScrollArea *area,
GdkRegion *region)
{
- gdk_region_offset (region, -area->priv->x_offset, -area->priv->y_offset);
-
+ FooScrollAreaPrivate *priv = area->priv;
+
+ gdk_region_offset (region, - priv->x_offset, - priv->y_offset);
}
static void
store_to_canvas (FooScrollArea *area,
GdkRegion *region)
{
- gdk_region_offset (region, area->priv->x_offset, area->priv->y_offset);
-}
+ FooScrollAreaPrivate *priv = area->priv;
-static gboolean paint_updates = FALSE;
+ gdk_region_offset (region, priv->x_offset, priv->y_offset);
+}
static void
paint_region (FooScrollArea *area, GdkRegion *region, double r, double g, double b, double a)
@@ -558,6 +467,9 @@ clip_to_32bit_rect (cairo_t *cr, GdkRectangle *rect)
#if 0
/* This is the code we'll use when cairo is fixed */
+ /* It is fixed in 1.8.0, so we can turn it on when 1.8.0 is more
+ * common
+ */
cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
cairo_clip (cr);
#endif
@@ -568,17 +480,18 @@ do_exposes (cairo_t *cr,
GdkRegion *store_region,
gpointer data)
{
- FooScrollArea *scroll_area = data;
+ FooScrollArea *area = data;
+ FooScrollAreaPrivate *priv = area->priv;
GdkRegion *region;
GdkRectangle rect;
GdkRegion *r;
/* Paint background */
- setup_background_cr (GTK_WIDGET (scroll_area)->window, cr, 0, 0);
+ setup_background_cr (GTK_WIDGET (area)->window, cr, 0, 0);
cairo_paint (cr);
- if (paint_updates)
- paint_region (scroll_area, store_region, 0.2, 0.2, 0.8, 0.4);
+ if (priv->paint_updates)
+ paint_region (area, store_region, 1.0, 0.0, 0.0, 0.8);
/* Note that this function can be called at a time
* where the adj->value is different from x_offset.
@@ -591,31 +504,19 @@ do_exposes (cairo_t *cr,
*/
region = gdk_region_copy (store_region);
- store_to_canvas (scroll_area, region);
-
- /* Set up input areas */
- clear_exposed_input_region (scroll_area, region);
-
- scroll_area->priv->current_input = g_new0 (InputRegion, 1);
- scroll_area->priv->current_input->region = gdk_region_copy (region);
- scroll_area->priv->current_input->paths = NULL;
- g_ptr_array_add (scroll_area->priv->input_regions,
- scroll_area->priv->current_input);
-
+ store_to_canvas (area, region);
+
/* Set up cairo context and emit the signal */
#if 0
- g_print ("translating %d %d\n", -scroll_area->priv->x_offset,
- -scroll_area->priv->y_offset);
+ g_print ("translating %d %d\n", -priv->x_offset, -priv->y_offset);
#endif
- translate_cairo_device (cr,
- -scroll_area->priv->x_offset,
- -scroll_area->priv->y_offset);
+ cairo_translate (cr, -priv->x_offset, -priv->y_offset);
/* Restrict to the actual canvas */
rect.x = 0;
rect.y = 0;
- rect.width = scroll_area->priv->width;
- rect.height = scroll_area->priv->height;
+ rect.width = priv->width;
+ rect.height = priv->height;
r = gdk_region_rectangle (&rect);
gdk_region_intersect (region, r);
@@ -624,8 +525,8 @@ do_exposes (cairo_t *cr,
clip_to_32bit_rect (cr, &rect);
#if 0
- g_print ("clipping to %d %d %d %d\n", 0, 0, scroll_area->priv->width,
- scroll_area->priv->height);
+ g_print ("clipping to %d %d %d %d\n", 0, 0, priv->width,
+ area->priv->height);
#endif
#if 0
@@ -638,56 +539,80 @@ do_exposes (cairo_t *cr,
{
gdk_region_get_clipbox (region, &rect);
- g_signal_emit (scroll_area, signals[PAINT], 0, cr, &rect, region);
+ g_signal_emit (area, signals[PAINT], 0, cr, &rect, region);
}
- scroll_area->priv->current_input = NULL;
gdk_region_destroy (region);
}
+static void
+print_region (const char *header, GdkRegion *region)
+{
+ GdkRectangle *rects;
+ int n_rects;
+ int i;
+
+ g_print ("%s\n", header);
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+ for (i = 0; i < n_rects; ++i)
+ {
+ GdkRectangle *rect = &(rects[i]);
+ g_print (" %d %d %d %d\n",
+ rect->x, rect->y, rect->width, rect->height);
+ }
+
+ g_free (rects);
+}
+
static gboolean
foo_scroll_area_expose (GtkWidget *widget,
GdkEventExpose *expose)
{
FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
+
+ backing_store_process_updates (priv->store, do_exposes, area);
- backing_store_process_updates (area->priv->store, do_exposes, area);
-
- if (paint_updates)
+ if (priv->paint_updates)
{
- paint_region (area, expose->region, 0.8, 0.2, 0.2, 0.4);
-
+ paint_region (area, expose->region, 0, 0, 1, 0.3);
+
gdk_flush ();
- g_usleep (70000);
+ g_usleep (100000);
}
-
- backing_store_draw (area->priv->store, widget->window,
- expose->region, 0, 0);
+
+ if (!gdk_region_empty (expose->region))
+ {
+ backing_store_draw (area->priv->store, widget->window,
+ expose->region, 0, 0);
+
+ XImage *image;
+
+ image = XGetImage (GDK_WINDOW_XDISPLAY (widget->window),
+ GDK_WINDOW_XID (gdk_get_default_root_window()),
+ 0, 0, 1, 1,
+ AllPlanes, ZPixmap);
+ }
+
return TRUE;
}
void
-foo_scroll_area_get_viewport (FooScrollArea *scroll_area,
+foo_scroll_area_get_viewport (FooScrollArea *area,
GdkRectangle *viewport)
{
- g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
if (!viewport)
return;
- get_viewport (scroll_area, viewport);
+ get_viewport (area, viewport);
}
static void
-process_event (FooScrollArea *scroll_area,
- FooScrollAreaEventType input_type,
- int x,
- int y,
- int button);
-
-static void
-emit_viewport_signals (FooScrollArea *scroll_area,
+emit_viewport_signals (FooScrollArea *area,
GdkRectangle *new_viewport,
GdkRectangle *old_viewport)
{
@@ -702,24 +627,14 @@ emit_viewport_signals (FooScrollArea *scroll_area,
if (size_changed || position_changed)
{
- int px, py;
-
- g_signal_emit (scroll_area, signals[VIEWPORT_CHANGED], 0,
+ g_signal_emit (area, signals[VIEWPORT_CHANGED], 0,
new_viewport, old_viewport);
if (size_changed)
{
- g_signal_emit (scroll_area, signals[VIEWPORT_SIZE_CHANGED], 0,
+ g_signal_emit (area, signals[VIEWPORT_SIZE_CHANGED], 0,
new_viewport, old_viewport);
}
-
- /* Fake an event when the viewport changes
- * FIXME: keep track of the last position in the window
- */
- gdk_window_get_pointer (GTK_WIDGET (scroll_area)->window,
- &px, &py, NULL);
-
- process_event (scroll_area, FOO_MOTION, px, py, 0);
}
}
@@ -740,26 +655,27 @@ clamp_adjustment (GtkAdjustment *adj)
}
static gboolean
-set_adjustment_values (FooScrollArea *scroll_area)
+set_adjustment_values (FooScrollArea *area)
{
- GtkAllocation *allocation = &GTK_WIDGET (scroll_area)->allocation;
+ FooScrollAreaPrivate *priv = area->priv;
+ GtkAllocation *allocation = &GTK_WIDGET (area)->allocation;
- GtkAdjustment *hadj = scroll_area->priv->hadj;
- GtkAdjustment *vadj = scroll_area->priv->vadj;
+ GtkAdjustment *hadj = priv->hadj;
+ GtkAdjustment *vadj = priv->vadj;
/* Horizontal */
hadj->page_size = allocation->width;
hadj->step_increment = 0.1 * allocation->width;
hadj->page_increment = 0.9 * allocation->width;
hadj->lower = 0.0;
- hadj->upper = scroll_area->priv->width;
+ hadj->upper = area->priv->width;
/* Vertical */
vadj->page_size = allocation->height;
vadj->step_increment = 0.1 * allocation->height;
vadj->page_increment = 0.9 * allocation->height;
vadj->lower = 0.0;
- vadj->upper = scroll_area->priv->height;
+ vadj->upper = priv->height;
clamp_adjustment (hadj);
clamp_adjustment (vadj);
@@ -771,6 +687,7 @@ static void
foo_scroll_area_realize (GtkWidget *widget)
{
FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
GdkWindowAttr attributes;
gint attributes_mask;
@@ -799,10 +716,12 @@ foo_scroll_area_realize (GtkWidget *widget)
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
+
gdk_window_set_user_data (widget->window, area);
- area->priv->store = backing_store_new (widget->window, widget->allocation.width,
- widget->allocation.height);
+ priv->store = backing_store_new (widget->window,
+ widget->allocation.width,
+ widget->allocation.height);
widget->style = gtk_style_attach (widget->style, widget->window);
@@ -813,17 +732,9 @@ static void
foo_scroll_area_unrealize (GtkWidget *widget)
{
FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
-#if 0
- if (area->priv->input_window)
- {
- gdk_window_set_user_data (area->priv->input_window, NULL);
- gdk_window_destroy (area->priv->input_window);
- area->priv->input_window = NULL;
- }
-#endif
-
- backing_store_free (area->priv->store);
+ backing_store_free (priv->store);
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
@@ -833,7 +744,9 @@ static void
allocation_to_canvas_region (FooScrollArea *area,
GdkRegion *region)
{
- gdk_region_offset (region, area->priv->x_offset, area->priv->y_offset);
+ FooScrollAreaPrivate *priv = area->priv;
+
+ gdk_region_offset (region, priv->x_offset, priv->y_offset);
}
#endif
@@ -841,13 +754,14 @@ static void
foo_scroll_area_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
- FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
GdkRectangle new_viewport;
GdkRectangle old_viewport;
- scroll_area->priv->in_size_allocate = TRUE;
+ priv->in_size_allocate = TRUE;
- get_viewport (scroll_area, &old_viewport);
+ get_viewport (area, &old_viewport);
widget->allocation = *allocation;
@@ -863,32 +777,36 @@ foo_scroll_area_size_allocate (GtkWidget *widget,
* we still need to paint the background of the exposed
* areas outside the area.
*/
- backing_store_resize (scroll_area->priv->store,
+ backing_store_resize (priv->store,
widget->allocation.width,
widget->allocation.height);
}
- set_adjustment_values (scroll_area);
+ set_adjustment_values (area);
- get_viewport (scroll_area, &new_viewport);
+ get_viewport (area, &new_viewport);
- emit_viewport_signals (scroll_area, &new_viewport, &old_viewport);
+ emit_viewport_signals (area, &new_viewport, &old_viewport);
- scroll_area->priv->in_size_allocate = FALSE;
+ priv->in_size_allocate = FALSE;
}
gboolean
foo_scroll_area_is_resizing (FooScrollArea *area)
{
- return area->priv->in_size_allocate;
+ FooScrollAreaPrivate *priv = area->priv;
+
+ return priv->in_size_allocate;
}
static void
-begin_grab_internal (FooScrollArea *scroll_area,
+begin_grab_internal (FooScrollArea *area,
gboolean implicit,
FooScrollAreaEventFunc func,
gpointer data)
{
+ FooScrollAreaPrivate *priv = area->priv;
+
#if 0
g_print ("begin%s grab\n", implicit? " implicit" : "");
#endif
@@ -897,76 +815,32 @@ begin_grab_internal (FooScrollArea *scroll_area,
/* Also, there should be support for setting the grab cursor */
/* Grabs can be overridden if desired - FIXME: make sure this right */
- scroll_area->priv->implicit_grab = implicit;
- scroll_area->priv->grabbed = TRUE;
- scroll_area->priv->grab_func = func;
- scroll_area->priv->grab_data = data;
+ /* We may need a grab counter here if there are both implicit and
+ * explicit grabs in play at the same time
+ */
+ priv->implicit_grab = implicit;
+ priv->grabbed = TRUE;
+ priv->grab_func = func;
+ priv->grab_data = data;
}
static void
-end_grab_internal (FooScrollArea *scroll_area,
+end_grab_internal (FooScrollArea *area,
gboolean only_implicit)
{
- if (only_implicit && !scroll_area->priv->implicit_grab)
+ FooScrollAreaPrivate *priv = area->priv;
+
+ if (only_implicit && !priv->implicit_grab)
return;
#if 0
g_print ("end grab\n");
#endif
- scroll_area->priv->implicit_grab = FALSE;
- scroll_area->priv->grabbed = FALSE;
- scroll_area->priv->grab_func = NULL;
- scroll_area->priv->grab_data = NULL;
-}
-
-static void
-emit_input (FooScrollArea *scroll_area,
- FooScrollAreaEventType type,
- int x,
- int y,
- int button,
- FooScrollAreaEventFunc func,
- gpointer data)
-{
- FooScrollAreaEvent event;
-
- event.x = x;
- event.y = y;
- event.button = button;
-
- if (scroll_area->priv->last_func != func ||
- scroll_area->priv->last_data != data)
- {
- if (scroll_area->priv->last_func)
- {
- event.type = FOO_LEAVE;
- scroll_area->priv->last_func (scroll_area, &event,
- scroll_area->priv->last_data);
- }
-
- if (func)
- {
- event.type = FOO_ENTER;
- func (scroll_area, &event, data);
- }
- }
-
- event.type = type;
-
- if (func)
- {
- if (event.type == FOO_BUTTON_PRESS)
- begin_grab_internal (scroll_area, TRUE, func, data);
-
- func (scroll_area, &event, data);
-
- if (event.type == FOO_BUTTON_RELEASE)
- end_grab_internal (scroll_area, TRUE);
- }
-
- scroll_area->priv->last_func = func;
- scroll_area->priv->last_data = data;
+ priv->implicit_grab = FALSE;
+ priv->grabbed = FALSE;
+ priv->grab_func = NULL;
+ priv->grab_data = NULL;
}
#if 0
@@ -1005,121 +879,15 @@ print_path (const char *header,
}
#endif
-static void
-process_event (FooScrollArea *scroll_area,
- FooScrollAreaEventType input_type,
- int x,
- int y,
- int button)
-{
- GtkWidget *widget = GTK_WIDGET (scroll_area);
- int i;
-
- allocation_to_canvas (scroll_area, &x, &y);
-
- if (scroll_area->priv->grabbed)
- {
- emit_input (scroll_area, input_type, x, y, button,
- scroll_area->priv->grab_func,
- scroll_area->priv->grab_data);
- return;
- }
-
- for (i = 0; i < scroll_area->priv->input_regions->len; ++i)
- {
- InputRegion *region = scroll_area->priv->input_regions->pdata[i];
-
- if (gdk_region_point_in (region->region, x, y))
- {
- InputPath *path;
- int n_paths = 0;
-
- path = region->paths;
- while (path)
- {
- cairo_t *cr;
- gboolean inside;
-
- cr = gdk_cairo_create (widget->window);
- cairo_set_fill_rule (cr, path->fill_rule);
- cairo_set_line_width (cr, path->line_width);
- cairo_append_path (cr, path->path);
-
- if (path->is_stroke)
- inside = cairo_in_stroke (cr, x, y);
- else
- inside = cairo_in_fill (cr, x, y);
-
- cairo_destroy (cr);
-
- if (inside)
- {
- emit_input (scroll_area, input_type,
- x, y, button,
- path->func,
- path->data);
- return;
- }
-
- path = path->next;
- n_paths++;
- }
-
- /* Since the regions are all disjoint, no other region
- * can match. Of course we could be clever and try and
- * sort the regions, but so far I have been unable to
- * make this loop show up on a profile.
- */
- break;
- }
- }
-
- /* If we got here, no event was emitted, so we emit
- * a "nothing" event, that will just cause some internal bookkeeping
- * be updated
- */
- emit_input (scroll_area, FOO_NONE, 0, 0, 0, NULL, NULL);
-}
-
-static void
-process_gdk_event (FooScrollArea *scroll_area,
- int x,
- int y,
- GdkEvent *event)
-{
- FooScrollAreaEventType input_type = FOO_NONE;
- int button = 0;
-
- if (event->type == GDK_BUTTON_PRESS)
- {
- input_type = FOO_BUTTON_PRESS;
- button = event->button.button;
- }
- else if (event->type == GDK_BUTTON_RELEASE)
- {
- input_type = FOO_BUTTON_RELEASE;
- button = event->button.button;
- }
- else if (event->type == GDK_MOTION_NOTIFY ||
- event->type == GDK_LEAVE_NOTIFY ||
- event->type == GDK_ENTER_NOTIFY)
- {
- input_type = FOO_MOTION;
- button = 0;
- }
-
- if (input_type != FOO_NONE)
- process_event (scroll_area, input_type, x, y, button);
-}
-
static gboolean
foo_scroll_area_button_press (GtkWidget *widget,
GdkEventButton *event)
{
FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
- process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
-
+ backing_store_process_event (priv->store, (GdkEvent *)event);
+
return TRUE;
}
@@ -1128,103 +896,21 @@ foo_scroll_area_button_release (GtkWidget *widget,
GdkEventButton *event)
{
FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
- process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
-
- return FALSE;
-}
-
-typedef struct
-{
- Window window;
- guint32 last_motion_time;
- gboolean stop_compressing;
-} EventScannerData;
-
-static Bool
-get_motion_time (Display *dpy,
- XEvent *xevent,
- XPointer arg)
-{
- EventScannerData *esd = (EventScannerData *)arg;
-
- if (esd->stop_compressing)
- return FALSE;
-
- if (esd->window != xevent->xany.window)
- {
- esd->stop_compressing = TRUE;
- return FALSE;
- }
-
- if (xevent->type == MotionNotify)
- {
- esd->last_motion_time = xevent->xmotion.time;
- return FALSE;
- }
-
- if (xevent->type == EnterNotify ||
- xevent->type == LeaveNotify)
- {
- esd->last_motion_time = xevent->xcrossing.time;
- return FALSE;
- }
+ backing_store_process_event (priv->store, (GdkEvent *)event);
return FALSE;
}
static gboolean
-use_this_motion_notify (GdkWindow *window,
- guint32 event_time,
- guint32 *motion_notify_time)
-{
- if (*motion_notify_time)
- {
- if (event_time == *motion_notify_time)
- {
- *motion_notify_time = 0;
-
- return TRUE;
- }
- else
- {
- /* There are more motion events already queued up */
-
- return FALSE;
- }
- }
- else
- {
- Display *dpy = gdk_x11_get_default_xdisplay();
- EventScannerData esd = { GDK_WINDOW_XWINDOW (window), 0, FALSE };
- XEvent dummy;
-
- XCheckIfEvent (dpy, &dummy, get_motion_time, (XPointer)&esd);
-
- if (esd.last_motion_time == 0)
- {
- return TRUE;
- }
- else
- {
- *motion_notify_time = esd.last_motion_time;
- return FALSE;
- }
- }
-}
-
-static gboolean
foo_scroll_area_motion (GtkWidget *widget,
GdkEventMotion *event)
{
FooScrollArea *area = FOO_SCROLL_AREA (widget);
-
- if (use_this_motion_notify (event->window,
- event->time,
- &(area->priv->motion_notify_time)))
- {
- process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
- }
+ FooScrollAreaPrivate *priv = area->priv;
+
+ backing_store_process_event (priv->store, (GdkEvent *)event);
return TRUE;
}
@@ -1234,43 +920,42 @@ foo_scroll_area_crossing (GtkWidget *widget,
GdkEventCrossing *event)
{
FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
- if (use_this_motion_notify (event->window,
- event->time,
- &(area->priv->motion_notify_time)))
- {
- process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
- }
+ backing_store_process_event (priv->store, (GdkEvent *)event);
return TRUE;
}
void
-foo_scroll_area_set_size_fixed_y (FooScrollArea *scroll_area,
+foo_scroll_area_set_size_fixed_y (FooScrollArea *area,
int width,
int height,
int old_y,
int new_y)
{
+ FooScrollAreaPrivate *priv = area->priv;
int dy = new_y - old_y;
- scroll_area->priv->width = width;
- scroll_area->priv->height = height;
+ priv->width = width;
+ priv->height = height;
- scroll_area->priv->vadj->value += dy;
+ priv->vadj->value += dy;
- set_adjustment_values (scroll_area);
+ set_adjustment_values (area);
if (dy != 0)
- gtk_adjustment_value_changed (scroll_area->priv->vadj);
+ gtk_adjustment_value_changed (priv->vadj);
}
void
-foo_scroll_area_set_size (FooScrollArea *scroll_area,
+foo_scroll_area_set_size (FooScrollArea *area,
int width,
int height)
{
- g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ FooScrollAreaPrivate *priv = area->priv;
+
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
/* FIXME: Default scroll algorithm should probably be to
* keep the same *area* outside the screen as before.
@@ -1287,20 +972,21 @@ foo_scroll_area_set_size (FooScrollArea *scroll_area,
* fixed points?
*/
- scroll_area->priv->width = width;
- scroll_area->priv->height = height;
+ priv->width = width;
+ priv->height = height;
- set_adjustment_values (scroll_area);
+ set_adjustment_values (area);
}
static void
foo_scroll_area_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
- FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ FooScrollAreaPrivate *priv = area->priv;
- requisition->width = scroll_area->priv->min_width;
- requisition->height = scroll_area->priv->min_height;
+ requisition->width = priv->min_width;
+ requisition->height = priv->min_height;
#if 0
g_print ("request %d %d\n", requisition->width, requisition->height);
@@ -1309,9 +995,10 @@ foo_scroll_area_size_request (GtkWidget *widget,
static void
foo_scrollbar_adjustment_changed (GtkAdjustment *adj,
- FooScrollArea *scroll_area)
+ FooScrollArea *area)
{
- GtkWidget *widget = GTK_WIDGET (scroll_area);
+ FooScrollAreaPrivate *priv = area->priv;
+ GtkWidget *widget = GTK_WIDGET (area);
gint dx = 0;
gint dy = 0;
GdkRectangle old_viewport, new_viewport;
@@ -1322,20 +1009,20 @@ foo_scrollbar_adjustment_changed (GtkAdjustment *adj,
* On the other hand with the constant time scrolling, it shouldn't
* really matter.
*/
- get_viewport (scroll_area, &old_viewport);
+ get_viewport (area, &old_viewport);
- if (adj == scroll_area->priv->hadj)
+ if (adj == priv->hadj)
{
/* FIXME: do we treat the offset as int or double, and,
* if int, how do we round?
*/
- dx = (int)adj->value - scroll_area->priv->x_offset;
- scroll_area->priv->x_offset = adj->value;
+ dx = (int)adj->value - priv->x_offset;
+ priv->x_offset = adj->value;
}
- else if (adj == scroll_area->priv->vadj)
+ else if (adj == priv->vadj)
{
- dy = (int)adj->value - scroll_area->priv->y_offset;
- scroll_area->priv->y_offset = adj->value;
+ dy = (int)adj->value - priv->y_offset;
+ priv->y_offset = adj->value;
}
else
{
@@ -1344,18 +1031,20 @@ foo_scrollbar_adjustment_changed (GtkAdjustment *adj,
if (GTK_WIDGET_REALIZED (widget) && (dx || dy))
{
- backing_store_scroll (scroll_area->priv->store, -dx, -dy);
+ backing_store_scroll (priv->store, -dx, -dy);
- gtk_widget_queue_draw (GTK_WIDGET (scroll_area));
+ gtk_widget_queue_draw (GTK_WIDGET (area));
}
- get_viewport (scroll_area, &new_viewport);
+ get_viewport (area, &new_viewport);
- emit_viewport_signals (scroll_area, &new_viewport, &old_viewport);
+ emit_viewport_signals (area, &new_viewport, &old_viewport);
+
+ gdk_window_process_all_updates();
}
static void
-set_one_adjustment (FooScrollArea *scroll_area,
+set_one_adjustment (FooScrollArea *area,
GtkAdjustment *adjustment,
GtkAdjustment **location)
{
@@ -1372,7 +1061,7 @@ set_one_adjustment (FooScrollArea *scroll_area,
if (*location)
{
g_signal_handlers_disconnect_by_func (
- *location, foo_scrollbar_adjustment_changed, scroll_area);
+ *location, foo_scrollbar_adjustment_changed, area);
g_object_unref (*location);
}
@@ -1383,18 +1072,20 @@ set_one_adjustment (FooScrollArea *scroll_area,
g_signal_connect (*location, "value_changed",
G_CALLBACK (foo_scrollbar_adjustment_changed),
- scroll_area);
+ area);
}
static void
-foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
+foo_scroll_area_set_scroll_adjustments (FooScrollArea *area,
GtkAdjustment *hadjustment,
GtkAdjustment *vadjustment)
{
- set_one_adjustment (scroll_area, hadjustment, &scroll_area->priv->hadj);
- set_one_adjustment (scroll_area, vadjustment, &scroll_area->priv->vadj);
+ FooScrollAreaPrivate *priv = area->priv;
- set_adjustment_values (scroll_area);
+ set_one_adjustment (area, hadjustment, &priv->hadj);
+ set_one_adjustment (area, vadjustment, &priv->vadj);
+
+ set_adjustment_values (area);
}
FooScrollArea *
@@ -1404,12 +1095,14 @@ foo_scroll_area_new (void)
}
void
-foo_scroll_area_set_min_size (FooScrollArea *scroll_area,
- int min_width,
+foo_scroll_area_set_min_size (FooScrollArea *area,
+ int min_width,
int min_height)
{
- scroll_area->priv->min_width = min_width;
- scroll_area->priv->min_height = min_height;
+ FooScrollAreaPrivate *priv = area->priv;
+
+ priv->min_width = min_width;
+ priv->min_height = min_height;
/* FIXME: think through invalidation.
*
@@ -1417,7 +1110,7 @@ foo_scroll_area_set_min_size (FooScrollArea *scroll_area,
* - make sure input boxes are invalidated when
* needed
*/
- gtk_widget_queue_resize (GTK_WIDGET (scroll_area));
+ gtk_widget_queue_resize (GTK_WIDGET (area));
}
#if 0
@@ -1445,6 +1138,63 @@ user_to_device (double *x, double *y,
cairo_user_to_device (cr, x, y);
}
+typedef struct
+{
+ FooScrollArea *area;
+ FooScrollAreaEventFunc func;
+ gpointer data;
+} Info;
+
+static void
+backing_store_event (BackingStore *store,
+ BackingStoreEvent *event,
+ gpointer data)
+{
+ Info *info = data;
+ BackingStoreEvent e = *event;
+ const char *type;
+
+ e.x = event->x + info->area->priv->x_offset;
+ e.y = event->y + info->area->priv->y_offset;
+
+ if (info->func)
+ info->func (info->area, &e, info->data);
+
+#if 0
+ if (event->type == BACKING_STORE_BUTTON_PRESS)
+ type = "press";
+ else if (event->type == BACKING_STORE_BUTTON_RELEASE)
+ type = "reelase";
+ else if (event->type == BACKING_STORE_LEAVE)
+ type = "leave";
+ else if (event->type == BACKING_STORE_ENTER)
+ type = "enter";
+ else if (event->type == BACKING_STORE_MOTION)
+ type = "motion";
+ else
+ type = "unknown";
+
+ g_print ("backing: %s %d %d %p %p\n", type,
+
+ e.x, e.y, info->func, info->data);
+#endif
+}
+
+static guint
+hash_info (gpointer a)
+{
+ Info *info = a;
+
+ return (gsize)info->func ^ (gsize)info->data;
+}
+
+static gboolean
+equal_info (gconstpointer a, gconstpointer b)
+{
+ const Info *ia = a, *ib = b;
+ return ia->func == ib->func && ia->data == ib->data;
+}
+
static void
add_path (FooScrollArea *area,
cairo_t *cr,
@@ -1452,10 +1202,21 @@ add_path (FooScrollArea *area,
FooScrollAreaEventFunc func,
gpointer data)
{
- InputPath *path = g_new0 (InputPath, 1);
+ FooScrollAreaPrivate *priv = area->priv;
#if 0
double x1, y1, x2, y2;
GdkRectangle r;
+ static GHashTable *hash;
+
+ /* FIXME: This doesn't work because the rectangle we
+ * get from _extents() is in user coordinates, so
+ * if we just transform it back to device coordinates
+ * we may get the wrong bounding box
+ *
+ * Ironically, the extent that cairo produces is not
+ * actually tight - because cairo first converted
+ * from device coordinates ...
+ */
if (is_stroke)
cairo_stroke_extents (cr, &x1, &y1, &x2, &y2);
@@ -1464,24 +1225,41 @@ add_path (FooScrollArea *area,
cairo_user_to_device (cr, &x1, &y1);
cairo_user_to_device (cr, &x2, &y2);
+
+ r.x = MIN (x1, x2) - 1;
+ r.y = MIN (y1, y2) - 1;
+ r.width = MAX (x1, x2) - r.x + 2;
+ r.width = MAX (y1, y2) - r.y + 2;
+
+ path->extents = r;
+#endif
+
+ static GHashTable *hash;
+ if (!hash)
+ hash = g_hash_table_new (hash_info, equal_info);
- r.x = x1 - 1;
- r.y = y1 - 1;
- r.width = x2 - x1 + 1;
- r.height = y2 - y1 + 1;
+ Info *info = g_new0 (Info, 1);
- if (gdk_region_rect_in (area->priv->current_input->region, &r) != GDK_OVERLAP_RECTANGLE_OUT)
+ info->area = area;
+ info->func = func;
+ info->data = data;
+
+ if (g_hash_table_lookup (hash, info))
+ {
+ Info *old = info;
+ info = g_hash_table_lookup (hash, info);
+ g_free (old);
+ }
+ else
+ g_hash_table_insert (hash, info, info);
+
+ backing_store_add_input (priv->store, cr, is_stroke,
+ backing_store_event, info);
+
+#if 0
+ if (gdk_region_rect_in (priv->current_input->region, &r) != GDK_OVERLAP_RECTANGLE_OUT)
{
#endif
- path->is_stroke = is_stroke;
- path->fill_rule = cairo_get_fill_rule (cr);
- path->line_width = cairo_get_line_width (cr);
- path->path = cairo_copy_path (cr);
- path_foreach_point (path->path, user_to_device, cr);
- path->func = func;
- path->data = data;
- path->next = area->priv->current_input->paths;
- area->priv->current_input->paths = path;
#if 0
}
#endif
@@ -1495,41 +1273,45 @@ add_path (FooScrollArea *area,
* as well.
*/
void
-foo_scroll_area_add_input_from_fill (FooScrollArea *scroll_area,
+foo_scroll_area_add_input_from_fill (FooScrollArea *area,
cairo_t *cr,
FooScrollAreaEventFunc func,
gpointer data)
{
- g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ FooScrollAreaPrivate *priv = area->priv;
+
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
g_return_if_fail (cr != NULL);
- g_return_if_fail (scroll_area->priv->current_input);
- add_path (scroll_area, cr, FALSE, func, data);
+ add_path (area, cr, FALSE, func, data);
}
void
-foo_scroll_area_add_input_from_stroke (FooScrollArea *scroll_area,
+foo_scroll_area_add_input_from_stroke (FooScrollArea *area,
cairo_t *cr,
FooScrollAreaEventFunc func,
gpointer data)
{
- g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ FooScrollAreaPrivate *priv = area->priv;
+
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
g_return_if_fail (cr != NULL);
- g_return_if_fail (scroll_area->priv->current_input);
- add_path (scroll_area, cr, TRUE, func, data);
+ add_path (area, cr, TRUE, func, data);
}
void
foo_scroll_area_invalidate_region (FooScrollArea *area,
GdkRegion *region)
{
+ FooScrollAreaPrivate *priv = area->priv;
+
g_return_if_fail (FOO_IS_SCROLL_AREA (area));
if (GTK_WIDGET_REALIZED (area))
{
canvas_to_store (area, region);
- backing_store_invalidate_region (area->priv->store, region);
+ backing_store_invalidate_region (priv->store, region);
store_to_canvas (area, region);
canvas_to_window (area, region);
@@ -1539,7 +1321,7 @@ foo_scroll_area_invalidate_region (FooScrollArea *area,
}
void
-foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area,
+foo_scroll_area_invalidate_rect (FooScrollArea *area,
int x,
int y,
int width,
@@ -1548,61 +1330,65 @@ foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area,
GdkRectangle rect = { x, y, width, height };
GdkRegion *region;
- g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
region = gdk_region_rectangle (&rect);
- foo_scroll_area_invalidate_region (scroll_area, region);
+ foo_scroll_area_invalidate_region (area, region);
gdk_region_destroy (region);
}
void
-foo_scroll_area_invalidate (FooScrollArea *scroll_area)
+foo_scroll_area_invalidate (FooScrollArea *area)
{
- GtkWidget *widget = GTK_WIDGET (scroll_area);
+ FooScrollAreaPrivate *priv = area->priv;
+ GtkWidget *widget = GTK_WIDGET (area);
- foo_scroll_area_invalidate_rect (scroll_area,
- scroll_area->priv->x_offset, scroll_area->priv->y_offset,
+ foo_scroll_area_invalidate_rect (area,
+ priv->x_offset, priv->y_offset,
widget->allocation.width,
widget->allocation.height);
}
void
-foo_scroll_area_begin_grab (FooScrollArea *scroll_area,
+foo_scroll_area_begin_grab (FooScrollArea *area,
FooScrollAreaEventFunc func,
gpointer input_data)
{
- g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
- begin_grab_internal (scroll_area, FALSE, func, input_data);
+ begin_grab_internal (area, FALSE, func, input_data);
}
void
-foo_scroll_area_end_grab (FooScrollArea *scroll_area)
+foo_scroll_area_end_grab (FooScrollArea *area)
{
- g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
- end_grab_internal (scroll_area, FALSE);
+ end_grab_internal (area, FALSE);
}
gboolean
-foo_scroll_area_is_grabbed (FooScrollArea *scroll_area)
+foo_scroll_area_is_grabbed (FooScrollArea *area)
{
- return scroll_area->priv->grabbed;
+ FooScrollAreaPrivate *priv = area->priv;
+
+ return priv->grabbed;
}
void
-foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area,
+foo_scroll_area_set_viewport_pos (FooScrollArea *area,
int x,
int y)
{
+ FooScrollAreaPrivate *priv = area->priv;
int x_changed;
int y_changed;
GtkAdjustment *hadj, *vadj;
- hadj = scroll_area->priv->hadj;
- vadj = scroll_area->priv->vadj;
+ hadj = priv->hadj;
+ vadj = priv->vadj;
x_changed = (int)hadj->value != x;
y_changed = (int)vadj->value != y;
@@ -1610,7 +1396,7 @@ foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area,
hadj->value = x;
vadj->value = y;
- set_adjustment_values (scroll_area);
+ set_adjustment_values (area);
if (x_changed && hadj->upper >= hadj->page_size)
{
@@ -1641,16 +1427,20 @@ rect_contains (const GdkRectangle *rect, int x, int y)
static void
stop_scrolling (FooScrollArea *area)
{
+ FooScrollAreaPrivate *priv = area->priv;
#if 0
g_print ("stop scrolling\n");
#endif
- if (area->priv->auto_scroll_info)
+ if (priv->auto_scroll_info)
{
- g_source_remove (area->priv->auto_scroll_info->timeout_id);
- g_timer_destroy (area->priv->auto_scroll_info->timer);
- g_free (area->priv->auto_scroll_info);
+ g_source_remove (priv->auto_scroll_info->timeout_id);
+#if 0
+ g_print ("removed idle\n");
+#endif
+ g_timer_destroy (priv->auto_scroll_info->timer);
+ g_free (priv->auto_scroll_info);
- area->priv->auto_scroll_info = NULL;
+ priv->auto_scroll_info = NULL;
}
}
@@ -1659,12 +1449,17 @@ scroll_idle (gpointer data)
{
GdkRectangle viewport, new_viewport;
FooScrollArea *area = data;
- AutoScrollInfo *info = area->priv->auto_scroll_info;
+ FooScrollAreaPrivate *priv = area->priv;
+ AutoScrollInfo *info = priv->auto_scroll_info;
#if 0
int dx, dy;
#endif
int new_x, new_y;
double elapsed;
+
+#if 0
+ g_print ("idle\n");
+#endif
get_viewport (area, &viewport);
@@ -1682,6 +1477,12 @@ scroll_idle (gpointer data)
#if 0
g_print ("new info %d %d\n", info->dx, info->dy);
#endif
+
+ if (priv->need_flush)
+ {
+ gdk_flush ();
+ priv->need_flush = FALSE;
+ }
elapsed = g_timer_elapsed (info->timer, NULL);
@@ -1689,7 +1490,7 @@ scroll_idle (gpointer data)
info->res_y = elapsed * info->dy / 0.2;
#if 0
- g_print ("%f %f\n", info->res_x, info->res_y);
+ g_print ("%f: %f %f\n", elapsed, info->res_x, info->res_y);
#endif
new_x = viewport.x + info->res_x;
@@ -1702,29 +1503,25 @@ scroll_idle (gpointer data)
#if 0
g_print ("new_x, new_y\n: %d %d\n", new_x, new_y);
#endif
-
- foo_scroll_area_set_viewport_pos (area, new_x, new_y);
-#if 0
- viewport.x + info->dx,
- viewport.y + info->dy);
-#endif
-get_viewport (area, &new_viewport);
+ foo_scroll_area_set_viewport_pos (area, new_x, new_y);
-if (viewport.x == new_viewport.x &&
- viewport.y == new_viewport.y &&
- (info->res_x > 1.0 ||
- info->res_y > 1.0 ||
- info->res_x < -1.0 ||
- info->res_y < -1.0))
-{
- stop_scrolling (area);
+ get_viewport (area, &new_viewport);
- /* stop scrolling if it didn't have an effect */
- return FALSE;
-}
-
-return TRUE;
+ if (viewport.x == new_viewport.x &&
+ viewport.y == new_viewport.y &&
+ (info->res_x > 1.0 ||
+ info->res_y > 1.0 ||
+ info->res_x < -1.0 ||
+ info->res_y < -1.0))
+ {
+ stop_scrolling (area);
+
+ /* stop scrolling if it didn't have an effect */
+ return FALSE;
+ }
+
+ return TRUE;
}
static void
@@ -1732,15 +1529,20 @@ ensure_scrolling (FooScrollArea *area,
int dx,
int dy)
{
- if (!area->priv->auto_scroll_info)
+ FooScrollAreaPrivate *priv = area->priv;
+
+ if (!priv->auto_scroll_info)
{
#if 0
g_print ("start scrolling\n");
#endif
- area->priv->auto_scroll_info = g_new0 (AutoScrollInfo, 1);
- area->priv->auto_scroll_info->timeout_id =
+ priv->auto_scroll_info = g_new0 (AutoScrollInfo, 1);
+ priv->auto_scroll_info->timeout_id =
g_idle_add (scroll_idle, area);
- area->priv->auto_scroll_info->timer = g_timer_new ();
+#if 0
+ g_print ("added idle\n");
+#endif
+ priv->auto_scroll_info->timer = g_timer_new ();
}
#if 0
@@ -1751,19 +1553,19 @@ ensure_scrolling (FooScrollArea *area,
g_print ("dx, dy: %d %d\n", dx, dy);
#endif
- area->priv->auto_scroll_info->dx = dx;
- area->priv->auto_scroll_info->dy = dy;
+ priv->auto_scroll_info->dx = dx;
+ priv->auto_scroll_info->dy = dy;
}
void
-foo_scroll_area_auto_scroll (FooScrollArea *scroll_area,
+foo_scroll_area_auto_scroll (FooScrollArea *area,
FooScrollAreaEvent *event)
{
#define SCROLL_MARGIN 4
GdkRectangle viewport;
- get_viewport (scroll_area, &viewport);
+ get_viewport (area, &viewport);
viewport.x += SCROLL_MARGIN;
viewport.y += SCROLL_MARGIN;
@@ -1776,7 +1578,7 @@ foo_scroll_area_auto_scroll (FooScrollArea *scroll_area,
g_print ("in viewport\n");
#endif
- stop_scrolling (scroll_area);
+ stop_scrolling (area);
}
else
{
@@ -1815,18 +1617,27 @@ foo_scroll_area_auto_scroll (FooScrollArea *scroll_area,
g_print ("dx, dy: %d %d\n", dx, dy);
#endif
- ensure_scrolling (scroll_area, dx, dy);
+ ensure_scrolling (area, dx, dy);
}
}
void
-foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area)
+foo_scroll_area_begin_auto_scroll (FooScrollArea *area)
{
/* noop for now */
}
void
-foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area)
+foo_scroll_area_end_auto_scroll (FooScrollArea *area)
+{
+ stop_scrolling (area);
+}
+
+void
+foo_scroll_area_set_paint_updates (FooScrollArea *area,
+ gboolean paint_upd)
{
- stop_scrolling (scroll_area);
+ FooScrollAreaPrivate *priv = area->priv;
+
+ priv->paint_updates = paint_upd;
}
diff --git a/scrollarea.h b/scrollarea.h
index 57b0eaf..205a667 100644
--- a/scrollarea.h
+++ b/scrollarea.h
@@ -138,4 +138,9 @@ void foo_scroll_area_auto_scroll (FooScrollArea *sc
void foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area);
+/* Debug */
+void foo_scroll_area_set_paint_updates (FooScrollArea *scroll_area,
+ gboolean paint_upd);
+
+
#endif
diff --git a/toolbar.c b/toolbar.c
index 23897b1..f669fd1 100644
--- a/toolbar.c
+++ b/toolbar.c
@@ -16,15 +16,15 @@ typedef struct
static const ToolInfo tools[] =
{
- { "T", NULL },
- { "R", NULL },
- { "E", NULL },
- { "P", NULL },
- { "L", NULL },
- { "I", NULL },
- { "H", NULL },
- { "V", NULL },
- { "N", NULL },
+ { "T", NULL }, /* text */
+ { "R", NULL }, /* rectangle */
+ { "E", NULL }, /* ellipse */
+ { "P", NULL }, /* polygon */
+ { "L", NULL }, /* line */
+ { "I", NULL }, /* image */
+ { "H", NULL }, /* horizontal helper line */
+ { "V", NULL }, /* vertical helper line */
+ { "N", NULL }, /* post-it note */
};
struct Toolbar