changeset 22:17c2df87b459

Package refactoring.
author "David Bryant <bagnose@gmail.com>"
date Wed, 15 Jul 2009 23:31:29 +0930
parents 993ac3a183c8
children d5cd97f55cb6
files build.sh cairo/routines.d cairo_support.d canvas.d dia/grid_layer.d dia/icanvas.d dia/page_layer.d dia/standard_tools.d dia/tool.d dia/tool_layer.d doodle.d grid_layer.d gtk/canvas.d gtk/conversions.d gtk/gtk_conversions.d icanvas.d page_layer.d standard_tools.d tool.d tool_layer.d
diffstat 20 files changed, 1093 insertions(+), 1091 deletions(-) [+]
line wrap: on
line diff
--- a/build.sh	Wed Jul 15 23:23:57 2009 +0930
+++ b/build.sh	Wed Jul 15 23:31:29 2009 +0930
@@ -2,14 +2,16 @@
 
 dmd \
         -ofdoodle \
-        doodle.d tool_layer.d tool.d icanvas.d canvas.d \
-        tk/geometry.d tk/gtk_support.d tk/misc.d tk/types.d tk/events.d \
-        cairo_support.d standard_tools.d \
-        page_layer.d \
         -od.obj \
+        doodle.d \
+        cairo/routines.d \
+        dia/grid_layer.d dia/icanvas.d dia/page_layer.d dia/standard_tools.d dia/tool.d dia/tool_layer.d \
+        gtk/canvas.d gtk/conversions.d \
+        tk/events.d tk/geometry.d tk/misc.d tk/types.d
         -I"${DMD_BASE}/include/d" \
         -L-lgtkd -L-ldl
 
 
 #dmd gui.d -ofgui -I"${DMD_BASE}/include/d" -S"${DMD_BASE}/lib" -no-export-dynamic -L-ldl -oq.obj
 #rebuild gui.d -ofgui -I"${D_BASE}/local/include/d" -S"${D_BASE}/local/lib" -no-export-dynamic -L-ldl -oq.obj
+#import/canvas.d import/geometry.d import/model.d import/network.d import/new.d import/p-model.d import/types.d import/undo.d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cairo/routines.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,11 @@
+module cairo.routines;
+
+public {
+    import tk.geometry;
+    import cairo.Context;
+}
+
+void rectangle(Context cr, Rectangle rectangle) {
+    cr.rectangle(rectangle.position.x, rectangle.position.y,
+                 rectangle.size.x, rectangle.size.y);
+}
--- a/cairo_support.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-module cairo_support;
-
-public {
-    import tk.geometry;
-    import cairo.Context;
-}
-
-void rectangle(Context cr, Rectangle rectangle) {
-    cr.rectangle(rectangle.position.x, rectangle.position.y,
-                 rectangle.size.x, rectangle.size.y);
-}
--- a/canvas.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,548 +0,0 @@
-module canvas;
-
-public {
-    import icanvas;
-    import tk.geometry;
-    import tk.events;
-}
-
-private {
-    import tk.gtk_support;
-    import tk.misc;
-
-    import cairo.Surface;
-    import cairo_support;
-
-    import std.math;
-    import std.stdio;
-
-    import gtk.Widget;
-    import gtk.Toolbar;
-    import gtk.Table;
-    import gtk.HRuler;
-    import gtk.VRuler;
-    import gtk.Range;
-    import gtk.HScrollbar;
-    import gtk.VScrollbar;
-    import gtk.DrawingArea;
-    import gtk.Adjustment;
-
-    import gdk.Drawable;
-
-    import gtkc.gtk;
-}
-
-// x and y run right and up respectively
-
-class Canvas : Table, Viewport {
-    this(in Layer[] layers, EventHandler event_handler, in double ppi) {
-        super(3, 3, 0);
-
-        mDamage = Rectangle.DEFAULT;
-
-        mLayers = layers.dup;
-        mEventHandler = event_handler;
-        mPPI = ppi;
-
-        /*
-        writefln("Layer bounds: %s", layer_bounds);
-        writefln("Canvas bounds: %s", mCanvasBounds);
-        writefln("View centre: %s", mViewCentre);
-        */
-
-        // Create our child widgets and register callbacks
-
-        mHRuler = new HRuler;
-        attach(mHRuler,
-               1, 2,
-               0, 1,
-               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
-               0, 0);
-        mHRuler.setMetric(MetricType.PIXELS);
-
-        mVRuler = new VRuler;
-        attach(mVRuler,
-               0, 1,
-               1, 2,
-               AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND,
-               0, 0);
-        mVRuler.setMetric(MetricType.PIXELS);
-
-        mDrawingArea = new DrawingArea;
-        mDrawingArea.addOnRealize(&onRealize);
-        mDrawingArea.addOnConfigure(&onConfigure);
-        mDrawingArea.addOnExpose(&onExpose);
-        mDrawingArea.addOnButtonPress(&onButtonPress);
-        mDrawingArea.addOnButtonRelease(&onButtonRelease);
-        mDrawingArea.addOnKeyPress(&onKeyEvent);
-        mDrawingArea.addOnKeyRelease(&onKeyEvent);
-        mDrawingArea.addOnMotionNotify(&onMotionNotify);
-        mDrawingArea.addOnScroll(&onScroll);
-        mDrawingArea.addOnEnterNotify(&onEnterNotify);
-        mDrawingArea.addOnLeaveNotify(&onLeaveNotify);
-        mDrawingArea.setEvents(EventMask.EXPOSURE_MASK |
-                               EventMask.POINTER_MOTION_MASK |
-                               EventMask.POINTER_MOTION_HINT_MASK |
-                               EventMask.BUTTON_MOTION_MASK |
-                               EventMask.BUTTON_PRESS_MASK |
-                               EventMask.BUTTON_RELEASE_MASK |
-                               EventMask.KEY_PRESS_MASK |
-                               EventMask.KEY_RELEASE_MASK |
-                               EventMask.ENTER_NOTIFY_MASK |
-                               EventMask.LEAVE_NOTIFY_MASK |
-                               EventMask.FOCUS_CHANGE_MASK |
-                               EventMask.SCROLL_MASK);
-
-        attach(mDrawingArea,
-               1, 2,
-               1, 2, 
-               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND,
-               0, 0);
-
-        // value, lower, upper, step-inc, page-inc, page-size
-        // Give the adjustments dummy values until we receive a configure
-        mHAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
-        mHAdjustment.addOnValueChanged(&onValueChanged);
-        mHScrollbar = new HScrollbar(mHAdjustment);
-        mHScrollbar.setInverted(false);
-        attach(mHScrollbar,
-               1, 2,
-               2, 3,
-               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
-               0, 0);
-
-        mVAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
-        mVAdjustment.addOnValueChanged(&onValueChanged);
-        mVScrollbar = new VScrollbar(mVAdjustment);
-        mVScrollbar.setInverted(true);
-        attach(mVScrollbar,
-               2, 3,
-               1, 2,
-               AttachOptions.SHRINK,
-               AttachOptions.FILL | AttachOptions.EXPAND,
-               0, 0);
-    }
-
-    override void zoom_relative(Point pixel_datum, double factor) {
-        // Work out pixel distance from current centre to datum,
-        // Do the zoom, then work out the new centre that keeps the
-        // pixel distance the same
-
-        Point old_model_datum = pixel_to_model(pixel_datum);
-        Vector pixel_distance = model_to_pixel(old_model_datum - mViewCentre);
-        mZoom = clamp_zoom(factor * mZoom);
-        mViewCentre = old_model_datum - pixel_to_model(pixel_distance);
-
-        update_adjustments;
-        update_rulers;
-        queueDraw;
-    }
-
-    override void pan_relative(Vector pixel_displacement) {
-        mViewCentre = mViewCentre + pixel_to_model(pixel_displacement);
-
-        update_adjustments;
-        update_rulers;
-        queueDraw;
-    }
-
-    override void damage_model(Rectangle area) {
-        mDamage = mDamage | model_to_pixel(area);
-    }
-
-    override void damage_pixel(Rectangle area) {
-        mDamage = mDamage | area;
-    }
-
-    override double zoom() const {
-        return mZoom;
-    }
-
-    override Point model_to_pixel(Point model) const {
-        return Point.DEFAULT + mViewSize / 2.0 + mZoom * (model - mViewCentre);
-    }
-
-    override Point pixel_to_model(Point pixel) const {
-        return mViewCentre + (pixel - mViewSize / 2.0 - Point.DEFAULT) / mZoom;
-    }
-
-    override Vector model_to_pixel(Vector model) const {
-        return mZoom * model;
-    }
-
-    override Vector pixel_to_model(Vector pixel) const {
-        return pixel / mZoom;
-    }
-
-    override double model_to_pixel(double model) const {
-        return mZoom * model;
-    }
-
-    override double pixel_to_model(double pixel) const {
-        return pixel / mZoom;
-    }
-
-    override Rectangle model_to_pixel(Rectangle model) const {
-        return Rectangle(model_to_pixel(model.position), model_to_pixel(model.size));
-    }
-
-    override Rectangle pixel_to_model(Rectangle model) const {
-        return Rectangle(pixel_to_model(model.position), pixel_to_model(model.size));
-    }
-
-    private {
-
-        void onRealize(Widget widget) {
-            assert(widget is mDrawingArea);
-            //writefln("Got realize\n");
-        }
-
-        bool onConfigure(GdkEventConfigure * event, Widget widget) {
-            assert(widget is mDrawingArea);
-
-            if (!mHadConfigure) {
-                const double MM_PER_INCH = 25.4;
-                mZoom = 0.25 * mPPI / MM_PER_INCH;
-
-                // Take the union of the bounds of each layer to
-                // determine the canvas size
-
-                Rectangle layer_bounds = Rectangle.DEFAULT;
-
-                foreach (ref layer; mLayers) {
-                    layer_bounds = layer_bounds | layer.bounds;
-                }
-
-                assert(layer_bounds.valid);
-
-                mCanvasBounds = layer_bounds.moved(-layer_bounds.size).expanded(2.0 * layer_bounds.size);
-                mViewCentre = mCanvasBounds.centre;
-
-                mHadConfigure = true;
-            }
-
-            mViewSize = Vector(cast(double)event.width, cast(double)event.height);
-            update_adjustments;
-            update_rulers;
-
-            //writefln("Canvas bounds: %s", mCanvasBounds);
-            //writefln("View centre: %s", mViewCentre);
-
-            return true;
-        }
-
-        bool onExpose(GdkEventExpose * event, Widget widget) {
-            assert(widget is mDrawingArea);
-
-            Drawable dr = mDrawingArea.getWindow;
-
-            int width, height;
-            dr.getSize(width, height);
-            //writefln("Got expose %dx%d\n", width, height);
-
-            scope model_cr = new Context(dr);
-            scope pixel_cr = new Context(dr);
-
-            Rectangle pixel_damage =
-                event is null ?
-                Rectangle(Point(0.0, 0.0), mViewSize) :
-                Rectangle(Point(cast(double)event.area.x, mViewSize.y - cast(double)(event.area.y + event.area.height)),
-                          Vector(cast(double)event.area.width, cast(double)event.area.height));
-
-            Rectangle model_damage = pixel_to_model(pixel_damage);
-
-            //writefln("Pixel damage: %s, model damage: %s", pixel_damage, model_damage);
-
-            model_cr.save; pixel_cr.save; {
-                // Setup model context and clip
-
-                GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
-                GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
-
-                model_cr.scale(mZoom, -mZoom);
-                model_cr.translate(-gtk_adjustment_get_value(h_gtkAdjustment),
-                                   -gtk_adjustment_get_value(v_gtkAdjustment) - gtk_adjustment_get_page_size(v_gtkAdjustment));
-
-                rectangle(model_cr, model_damage);
-                model_cr.clip;
-
-                // Setup pixel context and clip
-
-                pixel_cr.translate(0.0, mViewSize.y);
-                pixel_cr.scale(1.0, -1.0);
-
-                rectangle(pixel_cr, pixel_damage);
-                pixel_cr.clip;
-
-                // Fill the background
-
-                pixel_cr.save; {
-                    // Make the window light grey
-                    pixel_cr.setSourceRgba(0.6, 0.6, 0.6, 1.0);
-                    rectangle(pixel_cr, pixel_damage);
-                    pixel_cr.fill;
-                } pixel_cr.restore;
-
-                // Draw each layer
-
-                foreach(ref layer; mLayers) {
-                    model_cr.save; pixel_cr.save; {
-                        layer.draw(this, pixel_damage, pixel_cr, model_damage, model_cr);
-                    } pixel_cr.restore; model_cr.restore;
-                }
-            } pixel_cr.restore; model_cr.restore;
-
-            return true;
-        }
-
-        bool onButtonPress(GdkEventButton * event, Widget widget) {
-            assert(widget is mDrawingArea);
-            //writefln("Got button event\n");
-
-            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
-            Point model_point = pixel_to_model(pixel_point);
-
-            auto button_event = new ButtonEvent(gtk2tk_button_action(event.type),
-                                                gtk2tk_button_name(event.button),
-                                                pixel_point,
-                                                model_point,
-                                                gtk2tk_mask(event.state));
-
-            mEventHandler.handle_button_press(this, button_event);
-
-            process_damage;
-
-            return true;
-        }
-
-        bool onButtonRelease(GdkEventButton * event, Widget widget) {
-            assert(widget is mDrawingArea);
-            //writefln("Got button event\n");
-
-            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
-            Point model_point = pixel_to_model(pixel_point);
-
-            auto button_event = new ButtonEvent(gtk2tk_button_action(event.type),
-                                                gtk2tk_button_name(event.button),
-                                                pixel_point,
-                                                model_point,
-                                                gtk2tk_mask(event.state));
-
-            mEventHandler.handle_button_release(this, button_event);
-
-            process_damage;
-
-            return true;
-        }
-
-        bool onKeyEvent(GdkEventKey * event, Widget widget) {
-            assert(widget is mDrawingArea);
-            //writefln("Got key event\n");
-
-            //auto key_event = new KeyEvent("",
-            // mEventHandle.handle_key(key_event);
-
-            process_damage;
-
-            return true;
-        }
-
-        bool onMotionNotify(GdkEventMotion * event, Widget widget) {
-            assert(widget is mDrawingArea);
-            //writefln("Got motion notify\n");
-            gtk_widget_event(mHRuler.getWidgetStruct(), cast(GdkEvent *)event);
-            gtk_widget_event(mVRuler.getWidgetStruct(), cast(GdkEvent *)event);
-
-            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
-            Point model_point = pixel_to_model(pixel_point);
-
-            auto motion_event = new MotionEvent(pixel_point,
-                                                model_point,
-                                                gtk2tk_mask(event.state));
-
-            mEventHandler.handle_motion(this, motion_event);
-
-            process_damage;
-
-            return true;
-        }
-
-        bool onScroll(GdkEventScroll * event, Widget widget) {
-            assert(widget is mDrawingArea);
-            //writefln("Got scroll\n");
-
-            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
-            Point model_point = pixel_to_model(pixel_point);
-
-            auto scroll_event = new ScrollEvent(gtk2tk_direction(event.direction),
-                                                pixel_point,
-                                                model_point,
-                                                gtk2tk_mask(event.state));
-
-            mEventHandler.handle_scroll(this, scroll_event);
-
-            process_damage;
-
-            return true;
-        }
-
-        /*
-        public enum GdkCrossingMode {       
-            NORMAL,
-            GRAB,
-            UNGRAB,
-            GTK_GRAB,
-            GTK_UNGRAB,
-            STATE_CHANGED
-        }
-
-        public struct GdkEventCrossing {
-            GdkEventType type;
-            GdkWindow *window;
-            byte sendEvent;
-            GdkWindow *subwindow;
-            uint time;
-            double x;
-            double y;
-            double xRoot;
-            double yRoot;
-            GdkCrossingMode mode;
-            GdkNotifyType detail;
-            int focus;
-            uint state;
-        }
-        */
-
-        bool onEnterNotify(GdkEventCrossing * event, Widget widget) {
-            assert(widget is mDrawingArea);
-            writefln("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
-            return true;
-        }
-
-        bool onLeaveNotify(GdkEventCrossing * event, Widget widget) {
-            assert(widget is mDrawingArea);
-            writefln("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
-            return true;
-        }
-
-        void onValueChanged(Adjustment adjustment) {
-            GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
-            GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
-
-            Point view_left_top = Point(gtk_adjustment_get_value(h_gtkAdjustment),
-                                        gtk_adjustment_get_value(v_gtkAdjustment));
-
-            Vector model_size = pixel_to_model(mViewSize);
-
-            //writefln("%s", view_left_bottom);
-            mViewCentre = view_left_top + model_size / 2.0;
-            //writefln("onValueChanged mViewCentre: %s", mViewCentre);
-
-            update_rulers;
-
-            queueDraw;
-        }
-
-        void update_rulers() {
-            Vector model_size = pixel_to_model(mViewSize);
-
-            Point view_left_bottom = mViewCentre - model_size / 2.0;
-            Point view_right_top = mViewCentre + model_size / 2.0;
-
-            // Define these just to obtain the position
-            // below and we can preserve it
-            double lower, upper, position, max_size;
-
-
-            mHRuler.getRange(lower, upper, position, max_size);
-            mHRuler.setRange(view_left_bottom.x,
-                             view_right_top.x,
-                             position,
-                             mZoom * 50.0);
-
-            mVRuler.getRange(lower, upper, position, max_size);
-            mVRuler.setRange(view_right_top.y,
-                             view_left_bottom.y,
-                             position,
-                             mZoom * 50.0);
-        }
-
-        void update_adjustments() {
-            Vector model_size = pixel_to_model(mViewSize);
-
-            Point view_left_bottom = mViewCentre - model_size / 2.0;
-            Point view_right_top = mViewCentre + model_size / 2.0;
-
-            // Adjust the canvas size if necessary
-            mCanvasBounds = Rectangle(min_extents(mCanvasBounds.min_corner, view_left_bottom),
-                                      max_extents(mCanvasBounds.max_corner, view_right_top));
-
-            // Update the adjustments
-
-            GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
-            GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
-
-            gtk_adjustment_set_lower(h_gtkAdjustment, mCanvasBounds.min_corner.x);
-            gtk_adjustment_set_upper(h_gtkAdjustment, mCanvasBounds.max_corner.x);
-            gtk_adjustment_set_value(h_gtkAdjustment, view_left_bottom.x);
-            gtk_adjustment_set_step_increment(h_gtkAdjustment, mCanvasBounds.size.x / 16.0);
-            gtk_adjustment_set_page_increment(h_gtkAdjustment, mCanvasBounds.size.x / 4.0);
-            gtk_adjustment_set_page_size(h_gtkAdjustment, model_size.x);
-
-            gtk_adjustment_set_lower(v_gtkAdjustment, mCanvasBounds.min_corner.y);
-            gtk_adjustment_set_upper(v_gtkAdjustment, mCanvasBounds.max_corner.y);
-            gtk_adjustment_set_value(v_gtkAdjustment, view_left_bottom.y);
-            gtk_adjustment_set_step_increment(v_gtkAdjustment, mCanvasBounds.size.y / 16.0);
-            gtk_adjustment_set_page_increment(v_gtkAdjustment, mCanvasBounds.size.y / 4.0);
-            gtk_adjustment_set_page_size(v_gtkAdjustment, model_size.y);
-
-            mHAdjustment.changed;
-            mHAdjustment.valueChanged;
-            mVAdjustment.changed;
-            mVAdjustment.valueChanged;
-        }
-
-        void process_damage() {
-            if (mDamage.valid) {
-                //writefln("Damage: %s", mDamage);
-                int x, y, w, h;
-                mDamage.get_quantised(x, y, w, h);
-                //writefln("Quantised damage: %d %d %d %d", x, y, w, h);
-                y = cast(int)mViewSize.y - (y + h);
-                //writefln("Flipped Quantised damage: %d %d %d %d", x, y, w, h);
-                mDrawingArea.queueDrawArea(x, y, w, h);
-                //mDrawingArea.queueDraw();
-                mDamage = Rectangle.DEFAULT;
-            }
-            else {
-                //writefln("No damage");
-            }
-        }
-
-        double clamp_zoom(double zoom) { return clamp(zoom, 0.2, 10.0); }
-
-        bool mHadConfigure;
-        Rectangle mDamage;          // pixels
-
-        // Model units are in millimetres
-        // Screen units are in pixels
-        double mZoom;               // pixels-per-model-unit (mm)
-        Vector mViewSize;           // pixel: size of view window in pixels
-        Point mViewCentre;          // model: where in the model is the centre of our view
-        Rectangle mCanvasBounds;    // model:
-
-        // Child widgets:
-        HRuler mHRuler;
-        VRuler mVRuler;
-        DrawingArea mDrawingArea;
-        Adjustment mHAdjustment;
-        HScrollbar mHScrollbar;
-        Adjustment mVAdjustment;
-        VScrollbar mVScrollbar;
-
-        // Layers:
-        Layer[] mLayers;
-        EventHandler mEventHandler;
-        double mPPI;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dia/grid_layer.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,28 @@
+module dia.grid_layer;
+
+// TODO fix imports
+import icanvas;
+import tk.geometry;
+import cairo.Context;
+import cairo_support;
+import std.math;
+
+interface Grid {
+}
+
+class GridLayer : Layer, Grid {
+    override Rectangle bounds() const {
+        // We don't require any geometry
+        return Rectangle();
+    }
+
+    override void draw(const Viewport viewport, in Rectangle damage,
+                       Context model_cr, Context screen_cr) const {
+        double zoom = viewport.zoom;
+
+        double start_x = modf(damage.min_corner.x, zoom);
+    }
+
+    private {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dia/icanvas.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,54 @@
+module dia.icanvas;
+
+import tk.geometry;
+import tk.events;
+import cairo.Context;
+
+interface Viewport {
+    void zoom_relative(Point pixel_datum, double factor);
+    void pan_relative(Vector pixel_displacement);
+    void damage_model(Rectangle area);      // FIXME could be an inout parameter of the event handling, or a special scope Damage object that supports growth only
+    void damage_pixel(Rectangle area);      // FIXME as above
+
+    // FIXME not sure about these:
+    double zoom() const;
+    Point model_to_pixel(Point model) const;
+    Point pixel_to_model(Point pixel) const;
+    Vector model_to_pixel(Vector model) const;
+    Vector pixel_to_model(Vector pixel) const;
+    Rectangle model_to_pixel(Rectangle model) const;
+    Rectangle pixel_to_model(Rectangle model) const;
+    double model_to_pixel(double model) const;
+    double pixel_to_model(double pixel) const;
+}
+
+interface EventHandler {
+    bool handle_button_press(Viewport viewport, in ButtonEvent event);
+    bool handle_button_release(Viewport viewport, in ButtonEvent event);
+    bool handle_motion(Viewport viewport, in MotionEvent event);
+    bool handle_scroll(Viewport viewport, in ScrollEvent event);
+    //bool handle_enter(Viewport viewport, CrossingEvent event);
+    //bool handle_leave(Viewport viewport, CrossingEvent event);
+    //bool handle_focus_in(Viewport viewport, FocusEvent event);
+    //bool handle_focus_out(Viewport viewport, FocusEvent event);
+    bool handle_key_press(Viewport viewport, in KeyEvent event);
+    bool handle_key_release(Viewport viewport, in KeyEvent event);
+}
+
+abstract class Layer {
+    this(in string name) {
+        mName = name;
+    }
+
+    string name() const { return mName; }
+
+    Rectangle bounds() const;
+    //void zoom_changed
+    void draw(const Viewport viewport,
+              in Rectangle pixel_damage, scope Context pixel_cr,
+              in Rectangle model_damage, scope Context model_cr) const;
+
+    private {
+        invariant string mName;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dia/page_layer.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,46 @@
+module dia.page_layer;
+
+private {
+    import cairo_support;
+}
+
+public {
+    import icanvas;
+}
+
+interface Page {
+}
+
+class PageLayer : Layer, Page {
+    this(in string name) {
+        super(name);
+        mPageGeometry = Rectangle(Point.DEFAULT, Vector(210.0, 297.0));
+        //mPageGeometry = Rectangle(Point.DEFAULT, Vector(100.0, 100.0));
+    }
+
+    override Rectangle bounds() const {
+        return mPageGeometry;
+    }
+
+    override void draw(const Viewport viewport,
+                       in Rectangle pixel_damage, scope Context pixel_cr,
+                       in Rectangle model_damage, scope Context model_cr) const {
+        // Make the paper white, with a border
+
+        model_cr.save; {
+            model_cr.setSourceRgba(1.0, 1.0, 1.0, 1.0);
+            rectangle(model_cr, mPageGeometry);
+            model_cr.fill;
+        } model_cr.restore;
+
+        model_cr.save; {
+            model_cr.setSourceRgba(0.0, 0.0, 0.0, 1.0);
+            rectangle(model_cr, mPageGeometry);
+            model_cr.stroke;
+        } model_cr.restore;
+    }
+
+    private {
+        Rectangle mPageGeometry;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dia/standard_tools.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,147 @@
+module dia.standard_tools;
+
+public {
+    import tool;
+}
+
+private {
+    import cairo_support;
+    import std.math;
+    import std.stdio;
+}
+
+final class PanTool : Tool {
+    override bool handle_button_press(Viewport viewport, in ButtonEvent event) {
+        if (event.button_name == ButtonName.MIDDLE) {
+            mLastPosition = event.pixel_point;
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    override bool handle_motion(Viewport viewport, in MotionEvent event) {
+        if (event.mask.is_set(Modifier.MIDDLE_BUTTON)) {
+            viewport.pan_relative(mLastPosition - event.pixel_point);
+            mLastPosition = event.pixel_point;
+
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    override bool handle_scroll(Viewport viewport, in ScrollEvent event) {
+        if (event.mask.is_unset(Modifier.MIDDLE_BUTTON)) {
+            Vector delta;
+
+            switch (event.scroll_direction) {
+            case ScrollDirection.UP:
+                delta = event.mask.is_set(Modifier.SHIFT) ? Vector(AMOUNT, 0.0) : Vector(0.0, AMOUNT);
+                break;
+            case ScrollDirection.DOWN:
+                delta = event.mask.is_set(Modifier.SHIFT) ? Vector(-AMOUNT, 0.0) : Vector(0.0, -AMOUNT);
+                break;
+            case ScrollDirection.LEFT:
+                delta = Vector(-AMOUNT, 0.0);
+                break;
+            case ScrollDirection.RIGHT:
+                delta = Vector(AMOUNT, 0.0);
+                break;
+            }
+
+            viewport.pan_relative(delta);
+        }
+
+        return true;
+    }
+
+    private {
+        Point mLastPosition;
+        static invariant double AMOUNT = 60.0;
+    }
+}
+
+final class ZoomTool : Tool {
+    override bool handle_scroll(Viewport viewport, in ScrollEvent event) {
+        if (event.mask.is_set(Modifier.CONTROL)) {
+            if (event.scroll_direction == ScrollDirection.DOWN) {
+                viewport.zoom_relative(event.pixel_point, 1.0 / ZOOM);
+                return true;
+            }
+            else if (event.scroll_direction == ScrollDirection.UP) {
+                viewport.zoom_relative(event.pixel_point, ZOOM);
+                return true;
+            }
+            else {
+                return false;
+            }
+        }
+        else {
+            return false;
+        }
+    }
+
+    private {
+        static invariant double ZOOM = sqrt(2.0);
+    }
+}
+
+final class LassoTool : Tool {
+    override bool handle_button_press(Viewport viewport, in ButtonEvent event) {
+        if (event.button_name == ButtonName.LEFT) {
+            mActive = true;
+            mAnchorPoint = mCurrentPoint = event.pixel_point;
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    override bool handle_button_release(Viewport viewport, in ButtonEvent event) {
+        if (event.button_name == ButtonName.LEFT && mActive) {
+            mActive = false;
+            viewport.damage_pixel(Rectangle(mAnchorPoint, mCurrentPoint).feathered(LINE_WIDTH / 2.0));
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    override bool handle_motion(Viewport viewport, in MotionEvent event) {
+        if (mActive) {
+            viewport.damage_pixel(Rectangle(mAnchorPoint, mCurrentPoint).feathered(LINE_WIDTH / 2.0));
+            mCurrentPoint = event.pixel_point;
+            viewport.damage_pixel(Rectangle(mAnchorPoint, mCurrentPoint).feathered(LINE_WIDTH / 2.0));
+        }
+
+        return false;
+    }
+
+    override void draw(const Viewport viewport,
+                       in Rectangle pixel_damage, scope Context pixel_cr,
+                       in Rectangle model_damage, scope Context model_cr) const {
+        if (mActive) {
+            pixel_cr.save; {
+                double[] dashes = [ 4.0, 4.0 ];
+                pixel_cr.setDash(dashes, 0.0);
+                pixel_cr.setSourceRgba(0.0, 0.0, 0.5, 1.0);
+                pixel_cr.setLineWidth(LINE_WIDTH);
+                //writefln("Drawing rectangle: %s", Rectangle(mCurrentPoint, mAnchorPoint));
+                rectangle(pixel_cr, Rectangle(mCurrentPoint, mAnchorPoint));
+                pixel_cr.stroke;
+            } pixel_cr.restore;
+        }
+    }
+
+    private {
+        bool mActive;
+        Point mCurrentPoint;
+        Point mAnchorPoint;      // Pixel
+        static invariant double LINE_WIDTH = 1.0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dia/tool.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,49 @@
+module dia.tool;
+
+public {
+    import cairo_support;
+    import icanvas;
+    import tk.events;
+}
+
+/*
+interface IToolStack {
+    void push(Tool tool);
+    void pop();
+    void replace(Tool tool);
+}
+*/
+
+abstract class Tool {
+    /*
+    enum Response {
+    START, CONTINUE, FINISH
+
+    }
+    */
+
+    /*
+    abstract bool is_sticky();
+    abstract bool is_replaceable();
+    */
+
+    /*
+    abstract void start(IToolStack tool_stack);
+    abstract void stop(IToolStack tool_stack);
+    */
+
+    bool handle_button_press(Viewport viewport, in ButtonEvent event) { return false; }
+    bool handle_button_release(Viewport viewport, in ButtonEvent event) { return false; }
+    bool handle_motion(Viewport viewport, in MotionEvent event) { return false; }
+    bool handle_scroll(Viewport viewport, in ScrollEvent event) { return false; }
+    //bool handle_enter(Viewport viewport, CrossingEvent event) { return false; }
+    //bool handle_leave(Viewport viewport, CrossingEvent event) { return false; }
+    //bool handle_focus_in(Viewport viewport, FocusEvent event) { return false; }
+    //bool handle_focus_out(Viewport viewport, FocusEvent event) { return false; }
+    bool handle_key_press(Viewport viewport, in KeyEvent event) { return false; }
+    bool handle_key_release(Viewport viewport, in KeyEvent event) { return false; }
+
+    void draw(const Viewport viewport,
+              in Rectangle pixel_damage, scope Context pixel_cr,
+              in Rectangle model_damage, scope Context model_cr) const { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dia/tool_layer.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,126 @@
+module dia.tool_layer;
+
+private {
+    import std.stdio;
+    import cairo_support;
+}
+
+public {
+    import tool;
+}
+
+class ToolLayer : Layer, EventHandler {
+    this(in Tool[] tools, in string name) {
+        super(name);
+        mTools = tools.dup;
+    }
+
+    override bool handle_button_press(Viewport viewport, in ButtonEvent event) {
+        // writefln("%s", event);
+
+        if (mGrabbedTool is null) {
+            foreach_reverse(ref tool; mTools) {
+                if (tool.handle_button_press(viewport, event)) {
+                    mGrabbedTool = &tool;
+                    mGrabbedButton = event.button_name;
+                    break;
+                }
+            }
+        }
+        else {
+            mGrabbedTool.handle_button_press(viewport, event);
+        }
+
+        return true;
+    }
+
+    override bool handle_button_release(Viewport viewport, in ButtonEvent event) {
+        // writefln("%s", event);
+
+        if (mGrabbedTool !is null) {
+            mGrabbedTool.handle_button_release(viewport, event);
+
+            if (mGrabbedButton == event.button_name) {
+                mGrabbedTool = null;
+            }
+        }
+
+        return true;
+    }
+
+    override bool handle_key_press(Viewport viewport, in KeyEvent event) {
+        // writefln("%s", event);
+
+        return true;
+    }
+
+    override bool handle_key_release(Viewport viewport, in KeyEvent event) {
+        // writefln("%s", event);
+
+        return true;
+    }
+
+    override bool handle_motion(Viewport viewport, in MotionEvent event) {
+        //writefln("%s", event);
+
+        if (mGrabbedTool is null) {
+            foreach_reverse(ref tool; mTools) {
+                if (tool.handle_motion(viewport, event)) {
+                    break;
+                }
+            }
+        }
+        else {
+            mGrabbedTool.handle_motion(viewport, event);
+        }
+
+        return true;
+    }
+
+    override bool handle_scroll(Viewport viewport, in ScrollEvent event) {
+        // writefln("%s", event);
+
+        if (mGrabbedTool is null) {
+            foreach_reverse(ref tool; mTools) {
+                if (tool.handle_scroll(viewport, event)) {
+                    break;
+                }
+            }
+        }
+        else {
+            mGrabbedTool.handle_scroll(viewport, event);
+        }
+
+        return true;
+    }
+
+    override Rectangle bounds() const {
+        return Rectangle();
+    }
+
+    override void draw(const Viewport viewport,
+                       in Rectangle pixel_damage, scope Context pixel_cr,
+                       in Rectangle model_damage, scope Context model_cr) const {
+        // FIXME this isn't how we will really draw the tools...
+        foreach (const Tool tool; mTools) {
+            tool.draw(viewport, pixel_damage, pixel_cr, model_damage, model_cr);
+        }
+    }
+
+    /*
+    override void push(Tool tool) {
+    }
+
+    override void pop() {
+    }
+
+    override void replace(Tool tool) {
+    }
+    */
+
+    private {
+        Tool[] mTools;
+        Tool * mGrabbedTool;
+        ButtonName mGrabbedButton;
+    }
+}
--- a/doodle.d	Wed Jul 15 23:23:57 2009 +0930
+++ b/doodle.d	Wed Jul 15 23:31:29 2009 +0930
@@ -1,10 +1,10 @@
 module doodle;
 
 private {
-    import canvas;
-    import tool_layer;
-    import standard_tools;
-    import page_layer;
+    import gtk.canvas;
+    import dia.tool_layer;
+    import dia.standard_tools;
+    import dia.page_layer;
 
     import gtk.Main;
     import gtk.MainWindow;
--- a/grid_layer.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-module grid_layer;
-
-// TODO fix imports
-import icanvas;
-import tk.geometry;
-import cairo.Context;
-import cairo_support;
-import std.math;
-
-interface Grid {
-}
-
-class GridLayer : Layer, Grid {
-    override Rectangle bounds() const {
-        // We don't require any geometry
-        return Rectangle();
-    }
-
-    override void draw(const Viewport viewport, in Rectangle damage,
-                       Context model_cr, Context screen_cr) const {
-        double zoom = viewport.zoom;
-
-        double start_x = modf(damage.min_corner.x, zoom);
-    }
-
-    private {
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gtk/canvas.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,548 @@
+module gtk.canvas;
+
+public {
+    import dia.icanvas;
+    import tk.geometry;
+    import tk.events;
+}
+
+private {
+    import tk.gtk_support;
+    import tk.misc;
+
+    import cairo.Surface;
+    import cairo_support;
+
+    import std.math;
+    import std.stdio;
+
+    import gtk.Widget;
+    import gtk.Toolbar;
+    import gtk.Table;
+    import gtk.HRuler;
+    import gtk.VRuler;
+    import gtk.Range;
+    import gtk.HScrollbar;
+    import gtk.VScrollbar;
+    import gtk.DrawingArea;
+    import gtk.Adjustment;
+
+    import gdk.Drawable;
+
+    import gtkc.gtk;
+}
+
+// x and y run right and up respectively
+
+class Canvas : Table, Viewport {
+    this(in Layer[] layers, EventHandler event_handler, in double ppi) {
+        super(3, 3, 0);
+
+        mDamage = Rectangle.DEFAULT;
+
+        mLayers = layers.dup;
+        mEventHandler = event_handler;
+        mPPI = ppi;
+
+        /*
+        writefln("Layer bounds: %s", layer_bounds);
+        writefln("Canvas bounds: %s", mCanvasBounds);
+        writefln("View centre: %s", mViewCentre);
+        */
+
+        // Create our child widgets and register callbacks
+
+        mHRuler = new HRuler;
+        attach(mHRuler,
+               1, 2,
+               0, 1,
+               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
+               0, 0);
+        mHRuler.setMetric(MetricType.PIXELS);
+
+        mVRuler = new VRuler;
+        attach(mVRuler,
+               0, 1,
+               1, 2,
+               AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND,
+               0, 0);
+        mVRuler.setMetric(MetricType.PIXELS);
+
+        mDrawingArea = new DrawingArea;
+        mDrawingArea.addOnRealize(&onRealize);
+        mDrawingArea.addOnConfigure(&onConfigure);
+        mDrawingArea.addOnExpose(&onExpose);
+        mDrawingArea.addOnButtonPress(&onButtonPress);
+        mDrawingArea.addOnButtonRelease(&onButtonRelease);
+        mDrawingArea.addOnKeyPress(&onKeyEvent);
+        mDrawingArea.addOnKeyRelease(&onKeyEvent);
+        mDrawingArea.addOnMotionNotify(&onMotionNotify);
+        mDrawingArea.addOnScroll(&onScroll);
+        mDrawingArea.addOnEnterNotify(&onEnterNotify);
+        mDrawingArea.addOnLeaveNotify(&onLeaveNotify);
+        mDrawingArea.setEvents(EventMask.EXPOSURE_MASK |
+                               EventMask.POINTER_MOTION_MASK |
+                               EventMask.POINTER_MOTION_HINT_MASK |
+                               EventMask.BUTTON_MOTION_MASK |
+                               EventMask.BUTTON_PRESS_MASK |
+                               EventMask.BUTTON_RELEASE_MASK |
+                               EventMask.KEY_PRESS_MASK |
+                               EventMask.KEY_RELEASE_MASK |
+                               EventMask.ENTER_NOTIFY_MASK |
+                               EventMask.LEAVE_NOTIFY_MASK |
+                               EventMask.FOCUS_CHANGE_MASK |
+                               EventMask.SCROLL_MASK);
+
+        attach(mDrawingArea,
+               1, 2,
+               1, 2, 
+               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND,
+               0, 0);
+
+        // value, lower, upper, step-inc, page-inc, page-size
+        // Give the adjustments dummy values until we receive a configure
+        mHAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
+        mHAdjustment.addOnValueChanged(&onValueChanged);
+        mHScrollbar = new HScrollbar(mHAdjustment);
+        mHScrollbar.setInverted(false);
+        attach(mHScrollbar,
+               1, 2,
+               2, 3,
+               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
+               0, 0);
+
+        mVAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
+        mVAdjustment.addOnValueChanged(&onValueChanged);
+        mVScrollbar = new VScrollbar(mVAdjustment);
+        mVScrollbar.setInverted(true);
+        attach(mVScrollbar,
+               2, 3,
+               1, 2,
+               AttachOptions.SHRINK,
+               AttachOptions.FILL | AttachOptions.EXPAND,
+               0, 0);
+    }
+
+    override void zoom_relative(Point pixel_datum, double factor) {
+        // Work out pixel distance from current centre to datum,
+        // Do the zoom, then work out the new centre that keeps the
+        // pixel distance the same
+
+        Point old_model_datum = pixel_to_model(pixel_datum);
+        Vector pixel_distance = model_to_pixel(old_model_datum - mViewCentre);
+        mZoom = clamp_zoom(factor * mZoom);
+        mViewCentre = old_model_datum - pixel_to_model(pixel_distance);
+
+        update_adjustments;
+        update_rulers;
+        queueDraw;
+    }
+
+    override void pan_relative(Vector pixel_displacement) {
+        mViewCentre = mViewCentre + pixel_to_model(pixel_displacement);
+
+        update_adjustments;
+        update_rulers;
+        queueDraw;
+    }
+
+    override void damage_model(Rectangle area) {
+        mDamage = mDamage | model_to_pixel(area);
+    }
+
+    override void damage_pixel(Rectangle area) {
+        mDamage = mDamage | area;
+    }
+
+    override double zoom() const {
+        return mZoom;
+    }
+
+    override Point model_to_pixel(Point model) const {
+        return Point.DEFAULT + mViewSize / 2.0 + mZoom * (model - mViewCentre);
+    }
+
+    override Point pixel_to_model(Point pixel) const {
+        return mViewCentre + (pixel - mViewSize / 2.0 - Point.DEFAULT) / mZoom;
+    }
+
+    override Vector model_to_pixel(Vector model) const {
+        return mZoom * model;
+    }
+
+    override Vector pixel_to_model(Vector pixel) const {
+        return pixel / mZoom;
+    }
+
+    override double model_to_pixel(double model) const {
+        return mZoom * model;
+    }
+
+    override double pixel_to_model(double pixel) const {
+        return pixel / mZoom;
+    }
+
+    override Rectangle model_to_pixel(Rectangle model) const {
+        return Rectangle(model_to_pixel(model.position), model_to_pixel(model.size));
+    }
+
+    override Rectangle pixel_to_model(Rectangle model) const {
+        return Rectangle(pixel_to_model(model.position), pixel_to_model(model.size));
+    }
+
+    private {
+
+        void onRealize(Widget widget) {
+            assert(widget is mDrawingArea);
+            //writefln("Got realize\n");
+        }
+
+        bool onConfigure(GdkEventConfigure * event, Widget widget) {
+            assert(widget is mDrawingArea);
+
+            if (!mHadConfigure) {
+                const double MM_PER_INCH = 25.4;
+                mZoom = 0.25 * mPPI / MM_PER_INCH;
+
+                // Take the union of the bounds of each layer to
+                // determine the canvas size
+
+                Rectangle layer_bounds = Rectangle.DEFAULT;
+
+                foreach (ref layer; mLayers) {
+                    layer_bounds = layer_bounds | layer.bounds;
+                }
+
+                assert(layer_bounds.valid);
+
+                mCanvasBounds = layer_bounds.moved(-layer_bounds.size).expanded(2.0 * layer_bounds.size);
+                mViewCentre = mCanvasBounds.centre;
+
+                mHadConfigure = true;
+            }
+
+            mViewSize = Vector(cast(double)event.width, cast(double)event.height);
+            update_adjustments;
+            update_rulers;
+
+            //writefln("Canvas bounds: %s", mCanvasBounds);
+            //writefln("View centre: %s", mViewCentre);
+
+            return true;
+        }
+
+        bool onExpose(GdkEventExpose * event, Widget widget) {
+            assert(widget is mDrawingArea);
+
+            Drawable dr = mDrawingArea.getWindow;
+
+            int width, height;
+            dr.getSize(width, height);
+            //writefln("Got expose %dx%d\n", width, height);
+
+            scope model_cr = new Context(dr);
+            scope pixel_cr = new Context(dr);
+
+            Rectangle pixel_damage =
+                event is null ?
+                Rectangle(Point(0.0, 0.0), mViewSize) :
+                Rectangle(Point(cast(double)event.area.x, mViewSize.y - cast(double)(event.area.y + event.area.height)),
+                          Vector(cast(double)event.area.width, cast(double)event.area.height));
+
+            Rectangle model_damage = pixel_to_model(pixel_damage);
+
+            //writefln("Pixel damage: %s, model damage: %s", pixel_damage, model_damage);
+
+            model_cr.save; pixel_cr.save; {
+                // Setup model context and clip
+
+                GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
+                GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
+
+                model_cr.scale(mZoom, -mZoom);
+                model_cr.translate(-gtk_adjustment_get_value(h_gtkAdjustment),
+                                   -gtk_adjustment_get_value(v_gtkAdjustment) - gtk_adjustment_get_page_size(v_gtkAdjustment));
+
+                rectangle(model_cr, model_damage);
+                model_cr.clip;
+
+                // Setup pixel context and clip
+
+                pixel_cr.translate(0.0, mViewSize.y);
+                pixel_cr.scale(1.0, -1.0);
+
+                rectangle(pixel_cr, pixel_damage);
+                pixel_cr.clip;
+
+                // Fill the background
+
+                pixel_cr.save; {
+                    // Make the window light grey
+                    pixel_cr.setSourceRgba(0.6, 0.6, 0.6, 1.0);
+                    rectangle(pixel_cr, pixel_damage);
+                    pixel_cr.fill;
+                } pixel_cr.restore;
+
+                // Draw each layer
+
+                foreach(ref layer; mLayers) {
+                    model_cr.save; pixel_cr.save; {
+                        layer.draw(this, pixel_damage, pixel_cr, model_damage, model_cr);
+                    } pixel_cr.restore; model_cr.restore;
+                }
+            } pixel_cr.restore; model_cr.restore;
+
+            return true;
+        }
+
+        bool onButtonPress(GdkEventButton * event, Widget widget) {
+            assert(widget is mDrawingArea);
+            //writefln("Got button event\n");
+
+            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
+            Point model_point = pixel_to_model(pixel_point);
+
+            auto button_event = new ButtonEvent(gtk2tk_button_action(event.type),
+                                                gtk2tk_button_name(event.button),
+                                                pixel_point,
+                                                model_point,
+                                                gtk2tk_mask(event.state));
+
+            mEventHandler.handle_button_press(this, button_event);
+
+            process_damage;
+
+            return true;
+        }
+
+        bool onButtonRelease(GdkEventButton * event, Widget widget) {
+            assert(widget is mDrawingArea);
+            //writefln("Got button event\n");
+
+            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
+            Point model_point = pixel_to_model(pixel_point);
+
+            auto button_event = new ButtonEvent(gtk2tk_button_action(event.type),
+                                                gtk2tk_button_name(event.button),
+                                                pixel_point,
+                                                model_point,
+                                                gtk2tk_mask(event.state));
+
+            mEventHandler.handle_button_release(this, button_event);
+
+            process_damage;
+
+            return true;
+        }
+
+        bool onKeyEvent(GdkEventKey * event, Widget widget) {
+            assert(widget is mDrawingArea);
+            //writefln("Got key event\n");
+
+            //auto key_event = new KeyEvent("",
+            // mEventHandle.handle_key(key_event);
+
+            process_damage;
+
+            return true;
+        }
+
+        bool onMotionNotify(GdkEventMotion * event, Widget widget) {
+            assert(widget is mDrawingArea);
+            //writefln("Got motion notify\n");
+            gtk_widget_event(mHRuler.getWidgetStruct(), cast(GdkEvent *)event);
+            gtk_widget_event(mVRuler.getWidgetStruct(), cast(GdkEvent *)event);
+
+            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
+            Point model_point = pixel_to_model(pixel_point);
+
+            auto motion_event = new MotionEvent(pixel_point,
+                                                model_point,
+                                                gtk2tk_mask(event.state));
+
+            mEventHandler.handle_motion(this, motion_event);
+
+            process_damage;
+
+            return true;
+        }
+
+        bool onScroll(GdkEventScroll * event, Widget widget) {
+            assert(widget is mDrawingArea);
+            //writefln("Got scroll\n");
+
+            Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
+            Point model_point = pixel_to_model(pixel_point);
+
+            auto scroll_event = new ScrollEvent(gtk2tk_direction(event.direction),
+                                                pixel_point,
+                                                model_point,
+                                                gtk2tk_mask(event.state));
+
+            mEventHandler.handle_scroll(this, scroll_event);
+
+            process_damage;
+
+            return true;
+        }
+
+        /*
+        public enum GdkCrossingMode {       
+            NORMAL,
+            GRAB,
+            UNGRAB,
+            GTK_GRAB,
+            GTK_UNGRAB,
+            STATE_CHANGED
+        }
+
+        public struct GdkEventCrossing {
+            GdkEventType type;
+            GdkWindow *window;
+            byte sendEvent;
+            GdkWindow *subwindow;
+            uint time;
+            double x;
+            double y;
+            double xRoot;
+            double yRoot;
+            GdkCrossingMode mode;
+            GdkNotifyType detail;
+            int focus;
+            uint state;
+        }
+        */
+
+        bool onEnterNotify(GdkEventCrossing * event, Widget widget) {
+            assert(widget is mDrawingArea);
+            writefln("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
+            return true;
+        }
+
+        bool onLeaveNotify(GdkEventCrossing * event, Widget widget) {
+            assert(widget is mDrawingArea);
+            writefln("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
+            return true;
+        }
+
+        void onValueChanged(Adjustment adjustment) {
+            GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
+            GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
+
+            Point view_left_top = Point(gtk_adjustment_get_value(h_gtkAdjustment),
+                                        gtk_adjustment_get_value(v_gtkAdjustment));
+
+            Vector model_size = pixel_to_model(mViewSize);
+
+            //writefln("%s", view_left_bottom);
+            mViewCentre = view_left_top + model_size / 2.0;
+            //writefln("onValueChanged mViewCentre: %s", mViewCentre);
+
+            update_rulers;
+
+            queueDraw;
+        }
+
+        void update_rulers() {
+            Vector model_size = pixel_to_model(mViewSize);
+
+            Point view_left_bottom = mViewCentre - model_size / 2.0;
+            Point view_right_top = mViewCentre + model_size / 2.0;
+
+            // Define these just to obtain the position
+            // below and we can preserve it
+            double lower, upper, position, max_size;
+
+
+            mHRuler.getRange(lower, upper, position, max_size);
+            mHRuler.setRange(view_left_bottom.x,
+                             view_right_top.x,
+                             position,
+                             mZoom * 50.0);
+
+            mVRuler.getRange(lower, upper, position, max_size);
+            mVRuler.setRange(view_right_top.y,
+                             view_left_bottom.y,
+                             position,
+                             mZoom * 50.0);
+        }
+
+        void update_adjustments() {
+            Vector model_size = pixel_to_model(mViewSize);
+
+            Point view_left_bottom = mViewCentre - model_size / 2.0;
+            Point view_right_top = mViewCentre + model_size / 2.0;
+
+            // Adjust the canvas size if necessary
+            mCanvasBounds = Rectangle(min_extents(mCanvasBounds.min_corner, view_left_bottom),
+                                      max_extents(mCanvasBounds.max_corner, view_right_top));
+
+            // Update the adjustments
+
+            GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
+            GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
+
+            gtk_adjustment_set_lower(h_gtkAdjustment, mCanvasBounds.min_corner.x);
+            gtk_adjustment_set_upper(h_gtkAdjustment, mCanvasBounds.max_corner.x);
+            gtk_adjustment_set_value(h_gtkAdjustment, view_left_bottom.x);
+            gtk_adjustment_set_step_increment(h_gtkAdjustment, mCanvasBounds.size.x / 16.0);
+            gtk_adjustment_set_page_increment(h_gtkAdjustment, mCanvasBounds.size.x / 4.0);
+            gtk_adjustment_set_page_size(h_gtkAdjustment, model_size.x);
+
+            gtk_adjustment_set_lower(v_gtkAdjustment, mCanvasBounds.min_corner.y);
+            gtk_adjustment_set_upper(v_gtkAdjustment, mCanvasBounds.max_corner.y);
+            gtk_adjustment_set_value(v_gtkAdjustment, view_left_bottom.y);
+            gtk_adjustment_set_step_increment(v_gtkAdjustment, mCanvasBounds.size.y / 16.0);
+            gtk_adjustment_set_page_increment(v_gtkAdjustment, mCanvasBounds.size.y / 4.0);
+            gtk_adjustment_set_page_size(v_gtkAdjustment, model_size.y);
+
+            mHAdjustment.changed;
+            mHAdjustment.valueChanged;
+            mVAdjustment.changed;
+            mVAdjustment.valueChanged;
+        }
+
+        void process_damage() {
+            if (mDamage.valid) {
+                //writefln("Damage: %s", mDamage);
+                int x, y, w, h;
+                mDamage.get_quantised(x, y, w, h);
+                //writefln("Quantised damage: %d %d %d %d", x, y, w, h);
+                y = cast(int)mViewSize.y - (y + h);
+                //writefln("Flipped Quantised damage: %d %d %d %d", x, y, w, h);
+                mDrawingArea.queueDrawArea(x, y, w, h);
+                //mDrawingArea.queueDraw();
+                mDamage = Rectangle.DEFAULT;
+            }
+            else {
+                //writefln("No damage");
+            }
+        }
+
+        double clamp_zoom(double zoom) { return clamp(zoom, 0.2, 10.0); }
+
+        bool mHadConfigure;
+        Rectangle mDamage;          // pixels
+
+        // Model units are in millimetres
+        // Screen units are in pixels
+        double mZoom;               // pixels-per-model-unit (mm)
+        Vector mViewSize;           // pixel: size of view window in pixels
+        Point mViewCentre;          // model: where in the model is the centre of our view
+        Rectangle mCanvasBounds;    // model:
+
+        // Child widgets:
+        HRuler mHRuler;
+        VRuler mVRuler;
+        DrawingArea mDrawingArea;
+        Adjustment mHAdjustment;
+        HScrollbar mHScrollbar;
+        Adjustment mVAdjustment;
+        VScrollbar mVScrollbar;
+
+        // Layers:
+        Layer[] mLayers;
+        EventHandler mEventHandler;
+        double mPPI;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gtk/conversions.d	Wed Jul 15 23:31:29 2009 +0930
@@ -0,0 +1,74 @@
+module gtk.conversions;
+
+public {
+    import tk.types;
+}
+
+private {
+    static import gdk.Event;
+}
+
+ButtonAction gtk2tk_button_action(gdk.Event.EventType event_type) {
+    switch (event_type) {
+    case gdk.Event.EventType.BUTTON_PRESS:
+        return ButtonAction.SINGLE_PRESS;
+    case gdk.Event.EventType.DOUBLE_BUTTON_PRESS:
+        return ButtonAction.DOUBLE_PRESS;
+    case gdk.Event.EventType.TRIPLE_BUTTON_PRESS:
+        return ButtonAction.TRIPLE_PRESS;
+    case gdk.Event.EventType.BUTTON_RELEASE:
+        return ButtonAction.RELEASE;
+    default:
+        assert(false);
+    }
+}
+
+ButtonName gtk2tk_button_name(gdk.Event.guint button) {
+    switch (button) {
+    case 1:
+        return ButtonName.LEFT;
+    case 2:
+        return ButtonName.MIDDLE;
+    case 3:
+        return ButtonName.RIGHT;
+    case 4:
+        return ButtonName.FOUR;
+    case 5:
+        return ButtonName.FIVE;
+    default:
+        assert(false);
+    }
+}
+
+Mask gtk2tk_mask(gdk.Event.guint state) {
+    Modifier[] modifiers;
+
+    if (state & gdk.Event.GdkModifierType.SHIFT_MASK)   modifiers ~= Modifier.SHIFT;
+    if (state & gdk.Event.GdkModifierType.CONTROL_MASK) modifiers ~= Modifier.CONTROL;
+    if (state & gdk.Event.GdkModifierType.MOD1_MASK)    modifiers ~= Modifier.ALT;
+    if (state & gdk.Event.GdkModifierType.MOD2_MASK)    modifiers ~= Modifier.META;
+    if (state & gdk.Event.GdkModifierType.BUTTON1_MASK) modifiers ~= Modifier.LEFT_BUTTON;
+    if (state & gdk.Event.GdkModifierType.BUTTON2_MASK) modifiers ~= Modifier.MIDDLE_BUTTON;
+    if (state & gdk.Event.GdkModifierType.BUTTON3_MASK) modifiers ~= Modifier.RIGHT_BUTTON;
+    if (state & gdk.Event.GdkModifierType.BUTTON4_MASK) modifiers ~= Modifier.UNUSED_BUTTON_1;
+    if (state & gdk.Event.GdkModifierType.BUTTON5_MASK) modifiers ~= Modifier.UNUSED_BUTTON_2;
+
+    Mask m = Mask(modifiers);
+
+    return Mask(modifiers);
+}
+
+ScrollDirection gtk2tk_direction(gdk.Event.ScrollDirection direction) {
+    switch (direction) {
+    case gdk.Event.ScrollDirection.UP:
+        return ScrollDirection.UP;
+    case gdk.Event.ScrollDirection.DOWN:
+        return ScrollDirection.DOWN;
+    case gdk.Event.ScrollDirection.LEFT:
+        return ScrollDirection.LEFT;
+    case gdk.Event.ScrollDirection.RIGHT:
+        return ScrollDirection.RIGHT;
+    default:
+        assert(false);
+    }
+}
--- a/gtk/gtk_conversions.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-module gtk.conversions;
-
-public {
-    import tk.types;
-}
-
-private {
-    static import gdk.Event;
-}
-
-ButtonAction gtk2tk_button_action(gdk.Event.EventType event_type) {
-    switch (event_type) {
-    case gdk.Event.EventType.BUTTON_PRESS:
-        return ButtonAction.SINGLE_PRESS;
-    case gdk.Event.EventType.DOUBLE_BUTTON_PRESS:
-        return ButtonAction.DOUBLE_PRESS;
-    case gdk.Event.EventType.TRIPLE_BUTTON_PRESS:
-        return ButtonAction.TRIPLE_PRESS;
-    case gdk.Event.EventType.BUTTON_RELEASE:
-        return ButtonAction.RELEASE;
-    default:
-        assert(false);
-    }
-}
-
-ButtonName gtk2tk_button_name(gdk.Event.guint button) {
-    switch (button) {
-    case 1:
-        return ButtonName.LEFT;
-    case 2:
-        return ButtonName.MIDDLE;
-    case 3:
-        return ButtonName.RIGHT;
-    case 4:
-        return ButtonName.FOUR;
-    case 5:
-        return ButtonName.FIVE;
-    default:
-        assert(false);
-    }
-}
-
-Mask gtk2tk_mask(gdk.Event.guint state) {
-    Modifier[] modifiers;
-
-    if (state & gdk.Event.GdkModifierType.SHIFT_MASK)   modifiers ~= Modifier.SHIFT;
-    if (state & gdk.Event.GdkModifierType.CONTROL_MASK) modifiers ~= Modifier.CONTROL;
-    if (state & gdk.Event.GdkModifierType.MOD1_MASK)    modifiers ~= Modifier.ALT;
-    if (state & gdk.Event.GdkModifierType.MOD2_MASK)    modifiers ~= Modifier.META;
-    if (state & gdk.Event.GdkModifierType.BUTTON1_MASK) modifiers ~= Modifier.LEFT_BUTTON;
-    if (state & gdk.Event.GdkModifierType.BUTTON2_MASK) modifiers ~= Modifier.MIDDLE_BUTTON;
-    if (state & gdk.Event.GdkModifierType.BUTTON3_MASK) modifiers ~= Modifier.RIGHT_BUTTON;
-    if (state & gdk.Event.GdkModifierType.BUTTON4_MASK) modifiers ~= Modifier.UNUSED_BUTTON_1;
-    if (state & gdk.Event.GdkModifierType.BUTTON5_MASK) modifiers ~= Modifier.UNUSED_BUTTON_2;
-
-    Mask m = Mask(modifiers);
-
-    return Mask(modifiers);
-}
-
-ScrollDirection gtk2tk_direction(gdk.Event.ScrollDirection direction) {
-    switch (direction) {
-    case gdk.Event.ScrollDirection.UP:
-        return ScrollDirection.UP;
-    case gdk.Event.ScrollDirection.DOWN:
-        return ScrollDirection.DOWN;
-    case gdk.Event.ScrollDirection.LEFT:
-        return ScrollDirection.LEFT;
-    case gdk.Event.ScrollDirection.RIGHT:
-        return ScrollDirection.RIGHT;
-    default:
-        assert(false);
-    }
-}
--- a/icanvas.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-module icanvas;
-
-import tk.geometry;
-import tk.events;
-import cairo.Context;
-
-interface Viewport {
-    void zoom_relative(Point pixel_datum, double factor);
-    void pan_relative(Vector pixel_displacement);
-    void damage_model(Rectangle area);      // FIXME could be an inout parameter of the event handling, or a special scope Damage object that supports growth only
-    void damage_pixel(Rectangle area);      // FIXME as above
-
-    // FIXME not sure about these:
-    double zoom() const;
-    Point model_to_pixel(Point model) const;
-    Point pixel_to_model(Point pixel) const;
-    Vector model_to_pixel(Vector model) const;
-    Vector pixel_to_model(Vector pixel) const;
-    Rectangle model_to_pixel(Rectangle model) const;
-    Rectangle pixel_to_model(Rectangle model) const;
-    double model_to_pixel(double model) const;
-    double pixel_to_model(double pixel) const;
-}
-
-interface EventHandler {
-    bool handle_button_press(Viewport viewport, in ButtonEvent event);
-    bool handle_button_release(Viewport viewport, in ButtonEvent event);
-    bool handle_motion(Viewport viewport, in MotionEvent event);
-    bool handle_scroll(Viewport viewport, in ScrollEvent event);
-    //bool handle_enter(Viewport viewport, CrossingEvent event);
-    //bool handle_leave(Viewport viewport, CrossingEvent event);
-    //bool handle_focus_in(Viewport viewport, FocusEvent event);
-    //bool handle_focus_out(Viewport viewport, FocusEvent event);
-    bool handle_key_press(Viewport viewport, in KeyEvent event);
-    bool handle_key_release(Viewport viewport, in KeyEvent event);
-}
-
-abstract class Layer {
-    this(in string name) {
-        mName = name;
-    }
-
-    string name() const { return mName; }
-
-    Rectangle bounds() const;
-    //void zoom_changed
-    void draw(const Viewport viewport,
-              in Rectangle pixel_damage, scope Context pixel_cr,
-              in Rectangle model_damage, scope Context model_cr) const;
-
-    private {
-        invariant string mName;
-    }
-}
--- a/page_layer.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-module page_layer;
-
-private {
-    import cairo_support;
-}
-
-public {
-    import icanvas;
-}
-
-interface Page {
-}
-
-class PageLayer : Layer, Page {
-    this(in string name) {
-        super(name);
-        mPageGeometry = Rectangle(Point.DEFAULT, Vector(210.0, 297.0));
-        //mPageGeometry = Rectangle(Point.DEFAULT, Vector(100.0, 100.0));
-    }
-
-    override Rectangle bounds() const {
-        return mPageGeometry;
-    }
-
-    override void draw(const Viewport viewport,
-                       in Rectangle pixel_damage, scope Context pixel_cr,
-                       in Rectangle model_damage, scope Context model_cr) const {
-        // Make the paper white, with a border
-
-        model_cr.save; {
-            model_cr.setSourceRgba(1.0, 1.0, 1.0, 1.0);
-            rectangle(model_cr, mPageGeometry);
-            model_cr.fill;
-        } model_cr.restore;
-
-        model_cr.save; {
-            model_cr.setSourceRgba(0.0, 0.0, 0.0, 1.0);
-            rectangle(model_cr, mPageGeometry);
-            model_cr.stroke;
-        } model_cr.restore;
-    }
-
-    private {
-        Rectangle mPageGeometry;
-    }
-}
--- a/standard_tools.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-module standard_tools;
-
-public {
-    import tool;
-}
-
-private {
-    import cairo_support;
-    import std.math;
-    import std.stdio;
-}
-
-final class PanTool : Tool {
-    override bool handle_button_press(Viewport viewport, in ButtonEvent event) {
-        if (event.button_name == ButtonName.MIDDLE) {
-            mLastPosition = event.pixel_point;
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    override bool handle_motion(Viewport viewport, in MotionEvent event) {
-        if (event.mask.is_set(Modifier.MIDDLE_BUTTON)) {
-            viewport.pan_relative(mLastPosition - event.pixel_point);
-            mLastPosition = event.pixel_point;
-
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    override bool handle_scroll(Viewport viewport, in ScrollEvent event) {
-        if (event.mask.is_unset(Modifier.MIDDLE_BUTTON)) {
-            Vector delta;
-
-            switch (event.scroll_direction) {
-            case ScrollDirection.UP:
-                delta = event.mask.is_set(Modifier.SHIFT) ? Vector(AMOUNT, 0.0) : Vector(0.0, AMOUNT);
-                break;
-            case ScrollDirection.DOWN:
-                delta = event.mask.is_set(Modifier.SHIFT) ? Vector(-AMOUNT, 0.0) : Vector(0.0, -AMOUNT);
-                break;
-            case ScrollDirection.LEFT:
-                delta = Vector(-AMOUNT, 0.0);
-                break;
-            case ScrollDirection.RIGHT:
-                delta = Vector(AMOUNT, 0.0);
-                break;
-            }
-
-            viewport.pan_relative(delta);
-        }
-
-        return true;
-    }
-
-    private {
-        Point mLastPosition;
-        static invariant double AMOUNT = 60.0;
-    }
-}
-
-final class ZoomTool : Tool {
-    override bool handle_scroll(Viewport viewport, in ScrollEvent event) {
-        if (event.mask.is_set(Modifier.CONTROL)) {
-            if (event.scroll_direction == ScrollDirection.DOWN) {
-                viewport.zoom_relative(event.pixel_point, 1.0 / ZOOM);
-                return true;
-            }
-            else if (event.scroll_direction == ScrollDirection.UP) {
-                viewport.zoom_relative(event.pixel_point, ZOOM);
-                return true;
-            }
-            else {
-                return false;
-            }
-        }
-        else {
-            return false;
-        }
-    }
-
-    private {
-        static invariant double ZOOM = sqrt(2.0);
-    }
-}
-
-final class LassoTool : Tool {
-    override bool handle_button_press(Viewport viewport, in ButtonEvent event) {
-        if (event.button_name == ButtonName.LEFT) {
-            mActive = true;
-            mAnchorPoint = mCurrentPoint = event.pixel_point;
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    override bool handle_button_release(Viewport viewport, in ButtonEvent event) {
-        if (event.button_name == ButtonName.LEFT && mActive) {
-            mActive = false;
-            viewport.damage_pixel(Rectangle(mAnchorPoint, mCurrentPoint).feathered(LINE_WIDTH / 2.0));
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    override bool handle_motion(Viewport viewport, in MotionEvent event) {
-        if (mActive) {
-            viewport.damage_pixel(Rectangle(mAnchorPoint, mCurrentPoint).feathered(LINE_WIDTH / 2.0));
-            mCurrentPoint = event.pixel_point;
-            viewport.damage_pixel(Rectangle(mAnchorPoint, mCurrentPoint).feathered(LINE_WIDTH / 2.0));
-        }
-
-        return false;
-    }
-
-    override void draw(const Viewport viewport,
-                       in Rectangle pixel_damage, scope Context pixel_cr,
-                       in Rectangle model_damage, scope Context model_cr) const {
-        if (mActive) {
-            pixel_cr.save; {
-                double[] dashes = [ 4.0, 4.0 ];
-                pixel_cr.setDash(dashes, 0.0);
-                pixel_cr.setSourceRgba(0.0, 0.0, 0.5, 1.0);
-                pixel_cr.setLineWidth(LINE_WIDTH);
-                //writefln("Drawing rectangle: %s", Rectangle(mCurrentPoint, mAnchorPoint));
-                rectangle(pixel_cr, Rectangle(mCurrentPoint, mAnchorPoint));
-                pixel_cr.stroke;
-            } pixel_cr.restore;
-        }
-    }
-
-    private {
-        bool mActive;
-        Point mCurrentPoint;
-        Point mAnchorPoint;      // Pixel
-        static invariant double LINE_WIDTH = 1.0;
-    }
-}
--- a/tool.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-module tool;
-
-public {
-    import cairo_support;
-    import icanvas;
-    import tk.events;
-}
-
-/*
-interface IToolStack {
-    void push(Tool tool);
-    void pop();
-    void replace(Tool tool);
-}
-*/
-
-abstract class Tool {
-    /*
-    enum Response {
-    START, CONTINUE, FINISH
-
-    }
-    */
-
-    /*
-    abstract bool is_sticky();
-    abstract bool is_replaceable();
-    */
-
-    /*
-    abstract void start(IToolStack tool_stack);
-    abstract void stop(IToolStack tool_stack);
-    */
-
-    bool handle_button_press(Viewport viewport, in ButtonEvent event) { return false; }
-    bool handle_button_release(Viewport viewport, in ButtonEvent event) { return false; }
-    bool handle_motion(Viewport viewport, in MotionEvent event) { return false; }
-    bool handle_scroll(Viewport viewport, in ScrollEvent event) { return false; }
-    //bool handle_enter(Viewport viewport, CrossingEvent event) { return false; }
-    //bool handle_leave(Viewport viewport, CrossingEvent event) { return false; }
-    //bool handle_focus_in(Viewport viewport, FocusEvent event) { return false; }
-    //bool handle_focus_out(Viewport viewport, FocusEvent event) { return false; }
-    bool handle_key_press(Viewport viewport, in KeyEvent event) { return false; }
-    bool handle_key_release(Viewport viewport, in KeyEvent event) { return false; }
-
-    void draw(const Viewport viewport,
-              in Rectangle pixel_damage, scope Context pixel_cr,
-              in Rectangle model_damage, scope Context model_cr) const { }
-}
--- a/tool_layer.d	Wed Jul 15 23:23:57 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-module tool_layer;
-
-private {
-    import std.stdio;
-    import cairo_support;
-}
-
-public {
-    import tool;
-}
-
-class ToolLayer : Layer, EventHandler {
-    this(in Tool[] tools, in string name) {
-        super(name);
-        mTools = tools.dup;
-    }
-
-    override bool handle_button_press(Viewport viewport, in ButtonEvent event) {
-        // writefln("%s", event);
-
-        if (mGrabbedTool is null) {
-            foreach_reverse(ref tool; mTools) {
-                if (tool.handle_button_press(viewport, event)) {
-                    mGrabbedTool = &tool;
-                    mGrabbedButton = event.button_name;
-                    break;
-                }
-            }
-        }
-        else {
-            mGrabbedTool.handle_button_press(viewport, event);
-        }
-
-        return true;
-    }
-
-    override bool handle_button_release(Viewport viewport, in ButtonEvent event) {
-        // writefln("%s", event);
-
-        if (mGrabbedTool !is null) {
-            mGrabbedTool.handle_button_release(viewport, event);
-
-            if (mGrabbedButton == event.button_name) {
-                mGrabbedTool = null;
-            }
-        }
-
-        return true;
-    }
-
-    override bool handle_key_press(Viewport viewport, in KeyEvent event) {
-        // writefln("%s", event);
-
-        return true;
-    }
-
-    override bool handle_key_release(Viewport viewport, in KeyEvent event) {
-        // writefln("%s", event);
-
-        return true;
-    }
-
-    override bool handle_motion(Viewport viewport, in MotionEvent event) {
-        //writefln("%s", event);
-
-        if (mGrabbedTool is null) {
-            foreach_reverse(ref tool; mTools) {
-                if (tool.handle_motion(viewport, event)) {
-                    break;
-                }
-            }
-        }
-        else {
-            mGrabbedTool.handle_motion(viewport, event);
-        }
-
-        return true;
-    }
-
-    override bool handle_scroll(Viewport viewport, in ScrollEvent event) {
-        // writefln("%s", event);
-
-        if (mGrabbedTool is null) {
-            foreach_reverse(ref tool; mTools) {
-                if (tool.handle_scroll(viewport, event)) {
-                    break;
-                }
-            }
-        }
-        else {
-            mGrabbedTool.handle_scroll(viewport, event);
-        }
-
-        return true;
-    }
-
-    override Rectangle bounds() const {
-        return Rectangle();
-    }
-
-    override void draw(const Viewport viewport,
-                       in Rectangle pixel_damage, scope Context pixel_cr,
-                       in Rectangle model_damage, scope Context model_cr) const {
-        // FIXME this isn't how we will really draw the tools...
-        foreach (const Tool tool; mTools) {
-            tool.draw(viewport, pixel_damage, pixel_cr, model_damage, model_cr);
-        }
-    }
-
-    /*
-    override void push(Tool tool) {
-    }
-
-    override void pop() {
-    }
-
-    override void replace(Tool tool) {
-    }
-    */
-
-    private {
-        Tool[] mTools;
-        Tool * mGrabbedTool;
-        ButtonName mGrabbedButton;
-    }
-}