changeset 2:d6f44347373d

* Switched over to geometry done with structs instead of classes. * Removed direct access to gtk structs * Refactoring
author David Bryant <daveb@acres.com.au>
date Fri, 10 Jul 2009 15:15:27 +0930
parents c18e3f93d114
children 7d57cae10805
files build.sh cairo_support.d canvas.d gui.d handler.d icanvas.d itoolstack.d standard_tools.d tk/events.d tk/geometry2.d tk/types.d tool.d tool_stack.d tools.d
diffstat 14 files changed, 375 insertions(+), 269 deletions(-) [+]
line wrap: on
line diff
--- a/build.sh	Wed May 13 17:50:25 2009 +0930
+++ b/build.sh	Fri Jul 10 15:15:27 2009 +0930
@@ -2,9 +2,9 @@
 
 dmd \
         -ofgui \
-        gui.d handler.d icanvas.d canvas.d \
+        gui.d tool_stack.d tool.d icanvas.d canvas.d \
         tk/geometry.d tk/gtk_support.d tk/misc.d tk/types.d tk/events.d \
-        tk/geometry2.d \
+        tk/geometry2.d cairo_support.d \
         -od.obj \
         -I"${DMD_BASE}/include/d" \
         -L-lgtkd -L-ldl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cairo_support.d	Fri Jul 10 15:15:27 2009 +0930
@@ -0,0 +1,20 @@
+module cairo_support;
+
+import tk.geometry2;
+
+import cairo.Context;
+
+void rectangle(Context cr, Point2 corner1, Point2 corner2) {
+    double x = corner1.x;
+    double y = corner1.y;
+    double w = corner2.x - corner1.x;
+    double h = corner2.y - corner1.y;
+
+    if (w < 0.0) { x += w; w = -w; }
+    if (h < 0.0) { y += h; h = -h; }
+
+    //writefln("Rect: %f %f %f %f\n", x, y, w, h);
+
+    cr.rectangle(x, y, w, h);
+}
+
--- a/canvas.d	Wed May 13 17:50:25 2009 +0930
+++ b/canvas.d	Fri Jul 10 15:15:27 2009 +0930
@@ -20,7 +20,7 @@
 import gtk.Adjustment;
 
 import tk.misc;
-import tk.geometry;
+import tk.geometry2;
 import tk.types;
 import tk.events;
 import tk.gtk_support;
@@ -28,27 +28,14 @@
 private import gtkc.gtk;
 
 import icanvas;
+import cairo_support;
 
 // x and y run right and up respectively
 
-void rectangle(Context cr, Point corner1, Point corner2) {
-    double x = corner1.x;
-    double y = corner1.y;
-    double w = corner2.x - corner1.x;
-    double h = corner2.y - corner1.y;
-
-    if (w < 0.0) { x += w; w = -w; }
-    if (h < 0.0) { y += h; h = -h; }
-
-    //writefln("Rect: %f %f %f %f\n", x, y, w, h);
-
-    cr.rectangle(x, y, w, h);
-}
-
 class Canvas : Table, ICanvas {
     static this() {
-        ORIGIN = new Point(0.0, 0.0);
-        INITIAL_PAGE_SIZE = new Vector(210.0, 297.0);       // A4
+        ORIGIN = Point2(0.0, 0.0);
+        INITIAL_PAGE_SIZE = Vector2(210.0, 297.0);       // A4
     }
 
     this(ICanvasEventHandler event_handler) {
@@ -60,7 +47,7 @@
         const double MM_PER_INCH = 25.4;
         mZoom = 0.25 * PPI / MM_PER_INCH;
 
-        mPageLeftBottom = ORIGIN.clone();
+        mPageLeftBottom = ORIGIN;
         mPageRightTop = ORIGIN + INITIAL_PAGE_SIZE;
         mViewCentre = ORIGIN + INITIAL_PAGE_SIZE / 2.0;
 
@@ -101,7 +88,7 @@
 
         // 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, 1.0);
+        mHAdjustment = new Adjustment(0.25, 0.0, 1.0, 0.2, 0.5, 0.5);
         mHAdjustment.addOnValueChanged(&onValueChanged);
         mHScrollbar = new HScrollbar(mHAdjustment);
         attach(mHScrollbar,
@@ -110,7 +97,7 @@
                AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
                0, 0);
 
-        mVAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.4);
+        mVAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
         mVAdjustment.addOnValueChanged(&onValueChanged);
         mVScrollbar = new VScrollbar(mVAdjustment);
         attach(mVScrollbar,
@@ -121,17 +108,17 @@
                0, 0);
     }
 
-    override void rel_zoom(Point screen_datum, double factor) {
+    override void rel_zoom(Point2 screen_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 = screen_to_model(screen_datum);
-        Vector pixel_distance = model_to_screen(old_model_datum - mViewCentre);
+        Point2 old_model_datum = screen_to_model(screen_datum);
+        Vector2 pixel_distance = model_to_screen(old_model_datum - mViewCentre);
         mZoom = clamp_zoom(factor * mZoom);
         mViewCentre = old_model_datum - screen_to_model(pixel_distance);
 
-        Point new_model_datum = screen_to_model(screen_datum);
+        Point2 new_model_datum = screen_to_model(screen_datum);
 
         update_adjustments();
         //update_rulers(new_model_datum);
@@ -139,7 +126,7 @@
         queueDraw();
     }
 
-    override void rel_pan(Vector screen_displacement) {
+    override void rel_pan(Vector2 screen_displacement) {
         mViewCentre = mViewCentre + screen_to_model(screen_displacement);
 
         update_adjustments();
@@ -157,12 +144,12 @@
         bool onConfigure(GdkEventConfigure * event, Widget widget) {
             assert(widget is mDrawingArea);
 
-            mViewSize = new Vector(cast(double)event.width, cast(double)event.height);
+            mViewSize = Vector2(cast(double)event.width, cast(double)event.height);
             update_adjustments();
             update_rulers();
 
 
-            return 1;
+            return true;
         }
 
         bool onExpose(GdkEventExpose * event, Widget widget) {
@@ -194,8 +181,8 @@
             {
                 // Make the paper white, with a border
 
-                Point screen_page_left_bottom = model_to_screen(mPageLeftBottom);
-                Point screen_page_right_top = model_to_screen(mPageRightTop);
+                Point2 screen_page_left_bottom = model_to_screen(mPageLeftBottom);
+                Point2 screen_page_right_top = model_to_screen(mPageRightTop);
 
                 cr.setSourceRgba(1.0, 1.0, 1.0, 1.0);
                 rectangle(cr, screen_page_left_bottom, screen_page_right_top);
@@ -206,15 +193,15 @@
                 cr.stroke();
             }
 
-            return 1;
+            return true;
         }
 
         bool onButtonEvent(GdkEventButton * event, Widget widget) {
             assert(widget is mDrawingArea);
             //writefln("Got button event\n");
 
-            Point screen_point = new Point(event.x + 0.5, event.y + 0.5);
-            Point model_point = screen_to_model(screen_point);
+            Point2 screen_point = Point2(event.x + 0.5, event.y + 0.5);
+            Point2 model_point = screen_to_model(screen_point);
 
             auto button_event = new ButtonEvent(gtk2tk_click(event.type),
                                                 gtk2tk_button(event.button),
@@ -224,7 +211,7 @@
 
             //mEventHandle.handle_button_press
 
-            return 1;
+            return true;
         }
 
         bool onKeyEvent(GdkEventKey * event, Widget widget) {
@@ -234,7 +221,7 @@
             //auto key_event = new KeyEvent("",
             // mEventHandle.handle_key(key_event);
 
-            return 1;
+            return true;
         }
 
         bool onMotionNotify(GdkEventMotion * event, Widget widget) {
@@ -243,8 +230,8 @@
             gtk_widget_event(mHRuler.getWidgetStruct(), cast(GdkEvent *)event);
             gtk_widget_event(mVRuler.getWidgetStruct(), cast(GdkEvent *)event);
 
-            Point screen_point = new Point(event.x + 0.5, event.y + 0.5);
-            Point model_point = screen_to_model(screen_point);
+            Point2 screen_point = Point2(event.x + 0.5, event.y + 0.5);
+            Point2 model_point = screen_to_model(screen_point);
 
             auto motion_event = new MotionEvent(screen_point,
                                                 model_point,
@@ -252,15 +239,15 @@
 
             mEventHandler.handle_motion(this, motion_event);
 
-            return 1;
+            return true;
         }
 
         bool onScroll(GdkEventScroll * event, Widget widget) {
             assert(widget is mDrawingArea);
             //writefln("Got scroll\n");
 
-            Point screen_point = new Point(event.x + 0.5, event.y + 0.5);
-            Point model_point = screen_to_model(screen_point);
+            Point2 screen_point = Point2(event.x + 0.5, event.y + 0.5);
+            Point2 model_point = screen_to_model(screen_point);
 
             auto scroll_event = new ScrollEvent(gtk2tk_direction(event.direction),
                                                 screen_point,
@@ -269,16 +256,17 @@
 
             mEventHandler.handle_scroll(this, scroll_event);
 
-            return 1;
+            return true;
         }
 
         void onValueChanged(Adjustment adjustment) {
             GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct();
             GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct();
 
-            Point view_left_bottom = new Point(h_gtkAdjustment.value, v_gtkAdjustment.value);
+            Point2 view_left_bottom = Point2(gtk_adjustment_get_value(h_gtkAdjustment),
+                                             gtk_adjustment_get_value(v_gtkAdjustment));
             writefln("%s", view_left_bottom);
-            Vector model_size = screen_to_model(mViewSize);
+            Vector2 model_size = screen_to_model(mViewSize);
             mViewCentre = view_left_bottom + model_size / 2.0;
 
             update_rulers();
@@ -287,10 +275,10 @@
         }
 
         void update_rulers() {
-            Vector model_size = screen_to_model(mViewSize);
+            Vector2 model_size = screen_to_model(mViewSize);
 
-            Point view_left_bottom = mViewCentre - model_size / 2.0;
-            Point view_right_top = mViewCentre + model_size / 2.0;
+            Point2 view_left_bottom = mViewCentre - model_size / 2.0;
+            Point2 view_right_top = mViewCentre + model_size / 2.0;
 
             mHRuler.setRange(view_left_bottom.x,
                              view_right_top.x,
@@ -303,36 +291,36 @@
         }
 
         void update_adjustments() {
-            Vector model_size = screen_to_model(mViewSize);
+            Vector2 model_size = screen_to_model(mViewSize);
 
-            Point view_left_bottom = mViewCentre - model_size / 2.0;
-            Point view_right_top = mViewCentre + model_size / 2.0;
+            Point2 view_left_bottom = mViewCentre - model_size / 2.0;
+            Point2 view_right_top = mViewCentre + model_size / 2.0;
 
             // Adjust the canvas size if necessary
             mCanvasLeftBottom = min_extents(mCanvasLeftBottom, view_left_bottom);
             mCanvasRightTop = max_extents(mCanvasRightTop, view_right_top);
 
-            Vector canvas_size = mCanvasRightTop - mCanvasLeftBottom;
-            Vector page_size = mPageRightTop - mPageLeftBottom;
+            Vector2 canvas_size = mCanvasRightTop - mCanvasLeftBottom;
+            Vector2 page_size = mPageRightTop - mPageLeftBottom;
 
             // Update the adjustments
 
             GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct();
             GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct();
 
-            h_gtkAdjustment.lower = mCanvasLeftBottom.x;
-            h_gtkAdjustment.upper = mCanvasRightTop.x;
-            h_gtkAdjustment.value = view_left_bottom.x;
-            h_gtkAdjustment.step_increment = canvas_size.x / 10.0;
-            h_gtkAdjustment.page_increment = canvas_size.x / 5.0;
-            h_gtkAdjustment.page_size = model_size.x;
+            gtk_adjustment_set_lower(h_gtkAdjustment, mCanvasLeftBottom.x);
+            gtk_adjustment_set_upper(h_gtkAdjustment, mCanvasRightTop.x);
+            gtk_adjustment_set_value(h_gtkAdjustment, view_left_bottom.x);
+            gtk_adjustment_set_step_increment(h_gtkAdjustment, canvas_size.x / 10.0);
+            gtk_adjustment_set_page_increment(h_gtkAdjustment, canvas_size.x / 5.0);
+            gtk_adjustment_set_page_size(h_gtkAdjustment, model_size.x);
 
-            v_gtkAdjustment.lower = mCanvasLeftBottom.y;
-            v_gtkAdjustment.upper = mCanvasRightTop.y;
-            v_gtkAdjustment.value = view_left_bottom.y;
-            v_gtkAdjustment.step_increment = canvas_size.y / 10.0;
-            v_gtkAdjustment.page_increment = canvas_size.y / 5.0;
-            v_gtkAdjustment.page_size = model_size.y;
+            gtk_adjustment_set_lower(v_gtkAdjustment, mCanvasLeftBottom.y);
+            gtk_adjustment_set_upper(v_gtkAdjustment, mCanvasRightTop.y);
+            gtk_adjustment_set_value(v_gtkAdjustment, view_left_bottom.y);
+            gtk_adjustment_set_step_increment(v_gtkAdjustment, canvas_size.y / 10.0);
+            gtk_adjustment_set_page_increment(v_gtkAdjustment, canvas_size.y / 5.0);
+            gtk_adjustment_set_page_size(v_gtkAdjustment, model_size.y);
 
             mHAdjustment.changed();
             mHAdjustment.valueChanged();
@@ -340,32 +328,32 @@
             mVAdjustment.valueChanged();
         }
 
-        Point model_to_screen(Point model) { return ORIGIN + mViewSize / 2.0 + mZoom * (model - mViewCentre); }
-        Point screen_to_model(Point screen) { return mViewCentre + (screen - mViewSize / 2.0 - ORIGIN) / mZoom; }
-        Vector model_to_screen(Vector model) { return mZoom * model; }
-        Vector screen_to_model(Vector screen) { return screen / mZoom; }
+        Point2 model_to_screen(Point2 model) { return ORIGIN + mViewSize / 2.0 + mZoom * (model - mViewCentre); }
+        Point2 screen_to_model(Point2 screen) { return mViewCentre + (screen - mViewSize / 2.0 - ORIGIN) / mZoom; }
+        Vector2 model_to_screen(Vector2 model) { return mZoom * model; }
+        Vector2 screen_to_model(Vector2 screen) { return screen / mZoom; }
         double model_to_screen(double model) { return mZoom * model; }
         double screen_to_model(double screen) { return screen / mZoom; }
 
         double clamp_zoom(double zoom) { return clamp(zoom, 0.1, 10.0); }
 
-        static const Point ORIGIN;
-        static const Vector INITIAL_PAGE_SIZE;
+        static const Point2 ORIGIN;
+        static const Vector2 INITIAL_PAGE_SIZE;
 
         ICanvasEventHandler mEventHandler;
 
         // Model units are in millimetres
         // Screen units are in pixels
 
-        double mZoom;               // pixels-per-mm
-        Point mViewCentre;          // model: where in the model is the centre of our view
+        double mZoom;                // pixels-per-mm
+        Point2 mViewCentre;          // model: where in the model is the centre of our view
 
-        Point mCanvasLeftBottom;    // model: bottom left corner of canvas
-        Point mCanvasRightTop;      // model: top right corner of canvas
-        Point mPageLeftBottom;      // model: bottom left corner of page
-        Point mPageRightTop;        // model: top right corner of page
+        Point2 mCanvasLeftBottom;    // model: bottom left corner of canvas
+        Point2 mCanvasRightTop;      // model: top right corner of canvas
+        Point2 mPageLeftBottom;      // model: bottom left corner of page
+        Point2 mPageRightTop;        // model: top right corner of page
 
-        Vector mViewSize;           // screen: size of view window in pixels
+        Vector2 mViewSize;           // screen: size of view window in pixels
 
         HRuler mHRuler;
         VRuler mVRuler;
--- a/gui.d	Wed May 13 17:50:25 2009 +0930
+++ b/gui.d	Fri Jul 10 15:15:27 2009 +0930
@@ -1,7 +1,7 @@
 module gui;
 
 import canvas;
-import handler;
+import tool_stack;
 
 import gtk.Main;
 import gtk.MainWindow;
@@ -14,7 +14,6 @@
 import tk.geometry2;
 
 void main(string[] args) {
-    /*
     Main.init(args);
     auto window = new MainWindow("Title");
     auto event_handler = new ToolStack();
@@ -22,7 +21,9 @@
     window.add(canvas);
     window.showAll();
     Main.run();
-    */
+
+    /*
+    Point2 p3 = Point2.DEFAULT;
 
     Point2 p1 = Point2(3.0, 5.0);
     writefln("%s", p1);
@@ -34,4 +35,5 @@
 
     Rectangle2 r = Rectangle2(p1, p2);
     writefln("%s", r);
+    */
 }
--- a/handler.d	Wed May 13 17:50:25 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-module handler;
-
-import std.stdio;
-
-private import icanvas;
-private import tk.types;
-private import tk.geometry;
-private import tk.events;
-//IToolStack
-class ToolStack : ICanvasEventHandler {
-    override bool handle_button_press(ICanvas canvas, ButtonEvent event) {
-        writefln("%s", event);
-        return true;
-    }
-
-    override bool handle_button_release(ICanvas canvas, ButtonEvent event) {
-        writefln("%s", event);
-        return true;
-    }
-
-    override bool handle_key_press(ICanvas canvas, KeyEvent event) {
-        writefln("%s", event);
-        return true;
-    }
-
-    override bool handle_key_release(ICanvas canvas, KeyEvent event) {
-        writefln("%s", event);
-        return true;
-    }
-
-    override bool handle_motion(ICanvas canvas, MotionEvent event) {
-        writefln("%s", event);
-        return true;
-    }
-
-    override bool handle_scroll(ICanvas canvas, ScrollEvent event) {
-        writefln("%s", event);
-
-        if (event.mask.query(Modifier.CONTROL)) {
-            // Zoom about the pointer
-            double zoom = 1.44;
-
-            if (event.scroll_direction == ScrollDirection.DOWN) {
-                zoom = 1.0 / zoom;
-            }
-
-            canvas.rel_zoom(event.screen_point(), zoom);
-        }
-        else {
-            // Scroll
-
-            const double AMOUNT = 30.0;
-            Vector v;
-
-            if (event.mask.query(Modifier.SHIFT)) {
-                // left to right
-                v = new Vector(AMOUNT, 0.0);
-            }
-            else {
-                // down to up
-                v = new Vector(0.0, AMOUNT);
-            }
-
-            if (event.scroll_direction == ScrollDirection.UP) {
-                v = -v;
-            }
-
-            canvas.rel_pan(v);
-        }
-
-        return true;
-    }
-
-    /*
-    override void push(Tool tool) {
-    }
-
-    override void pop() {
-    }
-
-    override void replace(Tool tool) {
-    }
-
-    private {
-        Tool[] mTools;
-    }
-    */
-};
--- a/icanvas.d	Wed May 13 17:50:25 2009 +0930
+++ b/icanvas.d	Fri Jul 10 15:15:27 2009 +0930
@@ -1,10 +1,10 @@
 module icanvas;
 
-import tk.geometry;
+import tk.geometry2;
 
 interface ICanvas {
-    void rel_zoom(Point screen_datum, double factor);
-    void rel_pan(Vector screen_displacement);
+    void rel_zoom(Point2 screen_datum, double factor);
+    void rel_pan(Vector2 screen_displacement);
     //void damage();
 }
 
--- a/itoolstack.d	Wed May 13 17:50:25 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-module itoolstack;
-
-interface IToolStack {
-    void push(Tool tool);
-    void pop();
-    void replace(Tool tool);
-}
-
-abstract class Tool : ICanvasEventHandler {
-    /*
-    abstract bool is_sticky();
-    abstract bool is_replaceable();
-    */
-
-    abstract void start(IToolStack tool_stack);
-    abstract void stop(IToolStack tool_stack);
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/standard_tools.d	Fri Jul 10 15:15:27 2009 +0930
@@ -0,0 +1,56 @@
+module standard_tools;
+
+final class PanTool : Tool {
+    override bool handle_scroll(ICanvas canvas, ScrollEvent event) {
+        const double AMOUNT = 30.0;
+        Vector2 v;
+
+        if (event.mask.query(Modifier.SHIFT)) {
+            // left to right
+            v = new Vector2(AMOUNT, 0.0);
+        }
+        else {
+            // down to up
+            v = new Vector2(0.0, AMOUNT);
+        }
+
+        if (event.scroll_direction == ScrollDirection.UP) {
+            v = -v;
+        }
+
+        canvas.rel_pan(v);
+
+        return true;
+    }
+
+    bool handle_button_press(ICanvas canvas, ButtonEvent event) {
+    }
+
+    bool handle_button_release(ICanvas canvas, ButtonEvent event) {
+    }
+
+    bool handle_motion(ICanvas canvas, MotionEvent event) {
+    }
+}
+
+final class ZoomTool {
+    static invariant double ZOOM = 1.44;
+
+    override bool handle_scroll(ICanvas canvas, ScrollEvent event) {
+        if (event.mask.query(Modifier.CONTROL)) {
+            // Zoom about the pointer
+            double zoom = 1.44;
+
+            if (event.scroll_direction == ScrollDirection.DOWN) {
+                zoom = 1.0 / zoom;
+            }
+
+            canvas.rel_zoom(event.screen_point(), zoom);
+
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+}
--- a/tk/events.d	Wed May 13 17:50:25 2009 +0930
+++ b/tk/events.d	Fri Jul 10 15:15:27 2009 +0930
@@ -1,7 +1,7 @@
 module tk.events;
 
 import tk.types;
-import tk.geometry;
+import tk.geometry2;
 
 //
 // Should we pass the screen and model points into
@@ -66,26 +66,26 @@
 }
 
 abstract class PointerEvent : InputEvent {
-    this(Point screen_point, Point model_point, Mask mask) {
+    this(Point2 screen_point, Point2 model_point, Mask mask) {
         super(mask);
-        mScreenPoint = screen_point;
-        mModelPoint = model_point;
+        mScreenPoint2 = screen_point;
+        mModelPoint2 = model_point;
     }
 
-    Point screen_point() { return mScreenPoint; }
-    Point model_point() { return mModelPoint; }
+    Point2 screen_point() { return mScreenPoint2; }
+    Point2 model_point() { return mModelPoint2; }
 
     private {
-        Point mScreenPoint;
-        Point mModelPoint;
+        Point2 mScreenPoint2;
+        Point2 mModelPoint2;
     }
 }
 
 final class ButtonEvent : PointerEvent {
     this(ButtonPress button_press,
          ButtonNumber button_number,
-         Point screen_point,
-         Point model_point,
+         Point2 screen_point,
+         Point2 model_point,
          Mask mask) {   
         super(screen_point, model_point, mask);
         mButtonPress = button_press;
@@ -93,7 +93,7 @@
     }
 
     override string toString() {
-        return std.string.format("Button event: %s, %s, %s, %s, %s", mButtonPress, mButtonNumber, mScreenPoint, mModelPoint, mMask);
+        return std.string.format("Button event: %s, %s, %s, %s, %s", mButtonPress, mButtonNumber, mScreenPoint2, mModelPoint2, mMask);
     }
 
     ButtonPress button_press() { return mButtonPress; }
@@ -106,28 +106,28 @@
 }
 
 final class MotionEvent : PointerEvent {
-    this(Point screen_point,
-         Point model_point,
+    this(Point2 screen_point,
+         Point2 model_point,
          Mask mask) {
         super(screen_point, model_point, mask);
     }
 
     override string toString() {
-        return std.string.format("Motion event: %s, %s, %s", mScreenPoint, mModelPoint, mMask);
+        return std.string.format("Motion event: %s, %s, %s", mScreenPoint2, mModelPoint2, mMask);
     }
 }
 
 final class ScrollEvent : PointerEvent {
     this(ScrollDirection scroll_direction,
-         Point screen_point,
-         Point model_point,
+         Point2 screen_point,
+         Point2 model_point,
          Mask mask) {
         super(screen_point, model_point, mask);
         mScrollDirection = scroll_direction;
     }
 
     override string toString() {
-        return std.string.format("Scroll event: %s, %s, %s, %s", mScrollDirection, mScreenPoint, mModelPoint, mMask);
+        return std.string.format("Scroll event: %s, %s, %s, %s", mScrollDirection, mScreenPoint2, mModelPoint2, mMask);
     }
 
     ScrollDirection scroll_direction() { return mScrollDirection; }
--- a/tk/geometry2.d	Wed May 13 17:50:25 2009 +0930
+++ b/tk/geometry2.d	Fri Jul 10 15:15:27 2009 +0930
@@ -2,8 +2,15 @@
 
 private import std.stdio;
 private import std.math;
+private import tk.misc;
 
 struct Point2 {
+    static immutable Point2 DEFAULT;
+
+    static this() {
+        DEFAULT = Point2(0.0, 0.0);
+    }
+
     this(in double x, in double y) {
         _x = x;
         _y = y;
@@ -33,7 +40,21 @@
     }
 }
 
+Point2 min_extents(in Point2 a, in Point2 b) {
+    return Point2(min(a.x, b.x), min(a.y, b.y));
+}
+
+Point2 max_extents(in Point2 a, in Point2 b) {
+    return Point2(max(a.x, b.x), max(a.y, b.y));
+}
+
 struct Vector2 {
+    static Vector2 DEFAULT;
+
+    static this() {
+        DEFAULT = Vector2(0.0, 0.0);
+    }
+
     this(in double x, in double y) {
         _x = x;
         _y = y;
@@ -51,11 +72,11 @@
         return Vector2(-_x, -_y);
     }
 
-    Vector2 opMul_r(double d) const {
+    Vector2 opMul_r(in double d) const {
         return Vector2(d * _x, d * _y);
     }
 
-    Vector2 opDiv(double d) const {
+    Vector2 opDiv(in double d) const {
         return Vector2(_x / d, _y / d);
     }
 
@@ -76,39 +97,102 @@
 }
 
 struct Rectangle2 {
-    this(in Point2 position, Vector2 size) {
+    static Rectangle2 DEFAULT;
+
+    static this() {
+        DEFAULT = Rectangle2(Point2.DEFAULT, Vector2.DEFAULT);
+    }
+
+    /*
+    static Rectangle2 from_arbitrary_corners(in Point2 corner1, in Point2 corner2) {
+    }
+    */
+
+    this(in Point2 position, in Vector2 size) {
         this(position.x, position.y, size.x, size.y);
     }
 
-    this(Point2 corner1, Point2 corner2) {
+    this(in Point2 corner1, in Point2 corner2) {
         this(corner1.x, corner1.y, corner2.x - corner1.x, corner2.y - corner1.y);
     }
 
+    Point2 position() const {
+        return _position;
+    }
+
+    alias position min_corner;
+
+    Point2 max_corner() const {
+        return _position + _size;
+    }
+
+    bool valid() const {
+        return _size.x > 0.0 & _size.y > 0.0;
+    }
+
+    bool invalid() const {
+        return !valid();
+    }
+
+    double area() const {
+        return _size.x * _size.y;
+    }
+
     // Intersection
     Rectangle2 opAnd(in Rectangle2 r) const {
-        // FIXME
-        return Rectangle2(3.2, 5.3, 3.2, 2.3);
+        if (invalid() || r.invalid()) {
+            return DEFAULT;
+        }
+        else {
+            Point2 max = min_extents(max_corner(), r.max_corner());
+            Point2 min = max_extents(min_corner(), r.min_corner());
+
+            if (max.x < min.x || max.y < min.y) {
+                return DEFAULT;
+            }
+            else {
+                return Rectangle2(min, max);
+            }
+        }
     }
 
     // Union
     Rectangle2 opOr(in Rectangle2 r) const {
-        // FIXME
-        return Rectangle2(3.2, 5.3, 5.8, 2.1);
+        if (invalid()) {
+            return r;
+        }
+        else if (r.invalid()) {
+            return this;
+        }
+        else {
+            return Rectangle2(min_extents(min_corner(), r.min_corner()),
+                              max_extents(max_corner(), r.max_corner()));
+        }
     }
 
-    string toString() {
-        return std.string.format("{%s, %s}", mPosition, mSize);
+    /*
+    Rectangle2 moved(in Vector2 displacement) {
+        return Rectangle2(
+    }
+    Rectangle2 resized(in Vector2 new_size) {
+    }
+    Rectangle2 repositioned(in Point2 new_position) {
+    }
+    */
+
+    string toString() /* const */ {
+        return std.string.format("{%s, %s}", _position, _size);
     }
 
     private {
         this(double x, double y, double w, double h) {
             if (w < 0.0) { x += w; w = -w; }
             if (h < 0.0) { y += h; h = -h; }
-            mPosition = Point2(x, y);
-            mSize = Vector2(w, h);
+            _position = Point2(x, y);
+            _size = Vector2(w, h);
         }
 
-        Point2 mPosition;
-        Vector2 mSize;
+        Point2 _position;
+        Vector2 _size;
     }
 }
--- a/tk/types.d	Wed May 13 17:50:25 2009 +0930
+++ b/tk/types.d	Fri Jul 10 15:15:27 2009 +0930
@@ -40,10 +40,6 @@
 }
 
 class Mask {
-    this() {
-        //mBits = 0;
-    }
-
     void add(Modifier modifier) { mBits |= bit(modifier); }
     void remove(Modifier modifier) { mBits &= ~bit(modifier); }
     bool query(Modifier modifier) { return cast(bool)(mBits & bit(modifier)); }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tool.d	Fri Jul 10 15:15:27 2009 +0930
@@ -0,0 +1,20 @@
+module tool;
+
+import icanvas;
+
+interface IToolStack {
+    void push(Tool tool);
+    void pop();
+    void replace(Tool tool);
+}
+
+abstract class Tool : ICanvasEventHandler {
+    /*
+    abstract bool is_sticky();
+    abstract bool is_replaceable();
+    */
+
+    abstract void start(IToolStack tool_stack);
+    abstract void stop(IToolStack tool_stack);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tool_stack.d	Fri Jul 10 15:15:27 2009 +0930
@@ -0,0 +1,91 @@
+module tool_stack;
+
+import std.stdio;
+
+private import icanvas;
+private import tk.types;
+private import tk.geometry2;
+private import tk.events;
+
+//IToolStack
+
+class ToolStack : ICanvasEventHandler {
+    override bool handle_button_press(ICanvas canvas, ButtonEvent event) {
+        writefln("%s", event);
+        return true;
+    }
+
+    override bool handle_button_release(ICanvas canvas, ButtonEvent event) {
+        writefln("%s", event);
+        return true;
+    }
+
+    override bool handle_key_press(ICanvas canvas, KeyEvent event) {
+        writefln("%s", event);
+        return true;
+    }
+
+    override bool handle_key_release(ICanvas canvas, KeyEvent event) {
+        writefln("%s", event);
+        return true;
+    }
+
+    override bool handle_motion(ICanvas canvas, MotionEvent event) {
+        writefln("%s", event);
+        return true;
+    }
+
+    override bool handle_scroll(ICanvas canvas, ScrollEvent event) {
+        writefln("%s", event);
+
+        if (event.mask.query(Modifier.CONTROL)) {
+            // Zoom about the pointer
+            double zoom = 1.44;
+
+            if (event.scroll_direction == ScrollDirection.DOWN) {
+                zoom = 1.0 / zoom;
+            }
+
+            canvas.rel_zoom(event.screen_point(), zoom);
+        }
+        else {
+            // Scroll
+
+            const double AMOUNT = 30.0;
+            Vector2 v;
+
+            if (event.mask.query(Modifier.SHIFT)) {
+                // left to right
+                v = Vector2(AMOUNT, 0.0);
+            }
+            else {
+                // down to up
+                v = Vector2(0.0, AMOUNT);
+            }
+
+            if (event.scroll_direction == ScrollDirection.UP) {
+                v = -v;
+            }
+
+            canvas.rel_pan(v);
+        }
+
+        return true;
+    }
+
+    /*
+    override void push(Tool tool) {
+    }
+
+    override void pop() {
+    }
+
+    override void replace(Tool tool) {
+    }
+
+    private {
+        Tool[] mTools;
+        int mGrabbedToolIndex;  // -1 for none
+    }
+    */
+}
--- a/tools.d	Wed May 13 17:50:25 2009 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-module tools;
-
-final class PanTool : Tool {
-    override bool handle_scroll(ICanvas canvas, ScrollEvent event) {
-        const double AMOUNT = 30.0;
-        Vector v;
-
-        if (event.mask.query(Modifier.SHIFT)) {
-            // left to right
-            v = new Vector(AMOUNT, 0.0);
-        }
-        else {
-            // down to up
-            v = new Vector(0.0, AMOUNT);
-        }
-
-        if (event.scroll_direction == ScrollDirection.UP) {
-            v = -v;
-        }
-
-        canvas.rel_pan(v);
-
-        return true;
-    }
-}
-
-final class ZoomTool {
-    override bool handle_scroll(ICanvas canvas, ScrollEvent event) {
-        if (event.mask.query(Modifier.CONTROL)) {
-            // Zoom about the pointer
-            double zoom = 1.44;
-
-            if (event.scroll_direction == ScrollDirection.DOWN) {
-                zoom = 1.0 / zoom;
-            }
-
-            canvas.rel_zoom(event.screen_point(), zoom);
-
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-}