changeset 63:20d6327c4a75

Event progress. Got key press/release working and panning via keyboard.
author daveb
date Thu, 12 Aug 2010 16:35:24 +0930
parents 6c3993f4c3eb
children eb5436b47d13
files doodle/dia/icanvas.d doodle/dia/standard_tools.d doodle/dia/tool_layer.d doodle/fig/select_tool.d doodle/gtk/canvas.d doodle/main/prog/doodler.d doodle/tk/events.d doodle/tk/types.d options
diffstat 9 files changed, 317 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/doodle/dia/icanvas.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/dia/icanvas.d	Thu Aug 12 16:35:24 2010 +0930
@@ -11,7 +11,7 @@
 }
 
 mixin(defineEnum!("Cursor",
-                  "DEFAULT", "HAND", "CROSSHAIR"));
+                  "DEFAULT", "HAND", "CROSSHAIR", "PENCIL"));
 
 interface IViewport {
     void zoomRelative(in Point pixelDatum, in double factor);
--- a/doodle/dia/standard_tools.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/dia/standard_tools.d	Thu Aug 12 16:35:24 2010 +0930
@@ -5,9 +5,7 @@
 }
 
 private {
-    import doodle.cairo.routines;
-    import std.math;
-    import std.stdio;
+    import gdk.Keysyms;
 }
 
 final class PanTool : Tool {
@@ -43,16 +41,16 @@
 
             switch (event.scrollDirection) {
             case ScrollDirection.UP:
-                delta = event.mask.isSet(Modifier.SHIFT) ? Vector(-AMOUNT, 0.0) : Vector(0.0, AMOUNT);
+                delta = event.mask.isSet(Modifier.SHIFT) ? Vector(-SCROLL_AMOUNT, 0.0) : Vector(0.0, SCROLL_AMOUNT);
                 break;
             case ScrollDirection.DOWN:
-                delta = event.mask.isSet(Modifier.SHIFT) ? Vector(AMOUNT, 0.0) : Vector(0.0, -AMOUNT);
+                delta = event.mask.isSet(Modifier.SHIFT) ? Vector(SCROLL_AMOUNT, 0.0) : Vector(0.0, -SCROLL_AMOUNT);
                 break;
             case ScrollDirection.LEFT:
-                delta = Vector(-AMOUNT, 0.0);
+                delta = Vector(-SCROLL_AMOUNT, 0.0);
                 break;
             case ScrollDirection.RIGHT:
-                delta = Vector(AMOUNT, 0.0);
+                delta = Vector(SCROLL_AMOUNT, 0.0);
                 break;
             default:
                 assert(0);
@@ -64,9 +62,39 @@
         return true;
     }
 
+    override bool handleKeyPress(scope IViewport viewport, in KeyEvent event) {
+        // Respond to arrow keys and pg-up/pg-down
+
+        switch (event.value) {
+        case GdkKeysyms.GDK_Up:
+            viewport.panRelative(Vector(0.0, ARROW_AMOUNT));
+            return true;
+        case GdkKeysyms.GDK_Right:
+            viewport.panRelative(Vector(ARROW_AMOUNT, 0.0));
+            return true;
+        case GdkKeysyms.GDK_Left:
+            viewport.panRelative(Vector(-ARROW_AMOUNT, 0.0));
+            return true;
+        case GdkKeysyms.GDK_Down:
+            viewport.panRelative(Vector(0.0, -ARROW_AMOUNT));
+            return true;
+        case GdkKeysyms.GDK_Page_Up:
+            viewport.panRelative(Vector(0.0, PAGE_AMOUNT));
+            return true;
+        case GdkKeysyms.GDK_Page_Down:
+            viewport.panRelative(Vector(0.0, -PAGE_AMOUNT));
+            return true;
+        default:
+            // Just a key we don't handle
+            return false;
+        }
+    }
+
     private {
         Point mLastPosition;
-        static immutable double AMOUNT = 60.0;
+        static immutable SCROLL_AMOUNT = 60.0;
+        static immutable ARROW_AMOUNT = 30.0;
+        static immutable PAGE_AMOUNT = 240.0;
     }
 }
 
@@ -95,75 +123,6 @@
     }
 
     private {
-        static immutable double ZOOM = sqrt(2.0);
+        static immutable double ZOOM = 2^^0.5;
     }
 }
-
-final class SelectTool : Tool {
-    this() {
-        super("Select");
-    }
-
-    override bool handleButtonPress(scope IViewport viewport, in ButtonEvent event) {
-        if (event.buttonName == ButtonName.LEFT) {
-            _active = true;
-            _anchorPoint = _currentPoint = event.pixelPoint;
-            viewport.setCursor(Cursor.HAND);
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    override bool handleButtonRelease(scope IViewport viewport, in ButtonEvent event) {
-        if (event.buttonName == ButtonName.LEFT && _active) {
-            _active = false;
-            viewport.damagePixel(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
-            viewport.setCursor(Cursor.DEFAULT);
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    override bool handleMotion(scope IViewport viewport, in MotionEvent event) {
-        if (_active) {
-            viewport.damagePixel(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
-            _currentPoint = event.pixelPoint;
-            viewport.damagePixel(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
-        }
-
-        return false;
-    }
-
-    override void draw(in IViewport viewport,
-                       in Rectangle pixelDamage, scope Context pixelCr) const {
-        if (_active) {
-            /*
-            pixelCr.save; {
-                pixelCr.setSourceRgba(0.0, 0.0, 0.8, 0.3);
-                rectangle(pixelCr, Rectangle(_currentPoint, _anchorPoint));
-                pixelCr.fill();
-            } pixelCr.restore();
-            */
-
-            pixelCr.save(); {
-                double[] dashes = [ 4.0, 4.0 ];
-                pixelCr.setDash(dashes, 0.0);
-                pixelCr.setSourceRgba(0.0, 0.0, 0.5, 1.0);
-                pixelCr.setLineWidth(LINE_WIDTH);
-                rectangle(pixelCr, Rectangle(_currentPoint, _anchorPoint));
-                pixelCr.stroke;
-            } pixelCr.restore;
-        }
-    }
-
-    private {
-        bool _active;
-        Point _currentPoint;
-        Point _anchorPoint;      // Pixel
-        static immutable double LINE_WIDTH = 1.0;
-    }
-}
--- a/doodle/dia/tool_layer.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/dia/tool_layer.d	Thu Aug 12 16:35:24 2010 +0930
@@ -19,9 +19,9 @@
 }
 
 final class ToolLayer : Layer, IEventHandler, IToolStack {
-    this(in Tool[] tools, IToolStackObserver observer, in string name = "Tool") {
+    this(in Tool[] staticTools, IToolStackObserver observer, in string name = "Tool") {
         super(name);
-        _tools = tools.dup;
+        _staticTools = staticTools.dup;
         _observer = observer;
     }
 
@@ -30,7 +30,7 @@
     void use(Tool tool) {
         assert(_grabbedTool is null);
         message("using new tool: %s", tool.name);
-        _tools ~= tool;
+        _staticTools ~= tool;
         _observer.toolChanged(tool);
     }
 
@@ -54,9 +54,9 @@
         // writefln("%s", event);
 
         if (_grabbedTool is null) {
-            foreach_reverse(ref tool; _tools) {
+            foreach_reverse(ref tool; _staticTools) {
                 if (tool.handleButtonPress(viewport, event)) {
-                    _grabbedTool = &tool;
+                    _grabbedTool = tool;
                     _grabbedButton = event.buttonName;
                     break;
                 }
@@ -86,12 +86,26 @@
     bool handleKeyPress(scope IViewport viewport, in KeyEvent event) {
         // writefln("%s", event);
 
+        // FIXME not sure how these should work
+        foreach_reverse(ref tool; _staticTools) {
+            if (tool.handleKeyPress(viewport, event)) {
+                break;
+            }
+        }
+
         return true;
     }
 
     bool handleKeyRelease(scope IViewport viewport, in KeyEvent event) {
         // writefln("%s", event);
 
+        // FIXME not sure how these should work
+        foreach_reverse(ref tool; _staticTools) {
+            if (tool.handleKeyRelease(viewport, event)) {
+                break;
+            }
+        }
+
         return true;
     }
 
@@ -99,7 +113,7 @@
         //writefln("%s", event);
 
         if (_grabbedTool is null) {
-            foreach_reverse(ref tool; _tools) {
+            foreach_reverse(ref tool; _staticTools) {
                 if (tool.handleMotion(viewport, event)) {
                     break;
                 }
@@ -116,7 +130,7 @@
         // writefln("%s", event);
 
         if (_grabbedTool is null) {
-            foreach_reverse(ref tool; _tools) {
+            foreach_reverse(ref tool; _staticTools) {
                 if (tool.handleScroll(viewport, event)) {
                     break;
                 }
@@ -130,10 +144,10 @@
     }
 
     private {
-        Tool[] _tools;
+        Tool[] _staticTools;
         IToolStackObserver _observer;
 
-        Tool * _grabbedTool;
+        Tool _grabbedTool;
         ButtonName _grabbedButton;
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doodle/fig/select_tool.d	Thu Aug 12 16:35:24 2010 +0930
@@ -0,0 +1,78 @@
+module doodle.fig.select_tool;
+
+public {
+    import doodle.dia.tool;
+}
+
+private {
+    import doodle.cairo.routines;
+}
+
+final class SelectTool : Tool {
+    this() {
+        super("Select");
+    }
+
+    override bool handleButtonPress(scope IViewport viewport, in ButtonEvent event) {
+        if (event.buttonName == ButtonName.LEFT) {
+            _active = true;
+            _anchorPoint = _currentPoint = event.pixelPoint;
+            viewport.setCursor(Cursor.HAND);
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    override bool handleButtonRelease(scope IViewport viewport, in ButtonEvent event) {
+        if (event.buttonName == ButtonName.LEFT && _active) {
+            _active = false;
+            viewport.damagePixel(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
+            viewport.setCursor(Cursor.DEFAULT);
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    override bool handleMotion(scope IViewport viewport, in MotionEvent event) {
+        if (_active) {
+            viewport.damagePixel(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
+            _currentPoint = event.pixelPoint;
+            viewport.damagePixel(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
+        }
+
+        return false;
+    }
+
+    override void draw(in IViewport viewport,
+                       in Rectangle pixelDamage, scope Context pixelCr) const {
+        if (_active) {
+            /*
+            pixelCr.save; {
+                pixelCr.setSourceRgba(0.0, 0.0, 0.8, 0.3);
+                rectangle(pixelCr, Rectangle(_currentPoint, _anchorPoint));
+                pixelCr.fill();
+            } pixelCr.restore();
+            */
+
+            pixelCr.save(); {
+                double[] dashes = [ 4.0, 4.0 ];
+                pixelCr.setDash(dashes, 0.0);
+                pixelCr.setSourceRgba(0.0, 0.0, 0.5, 1.0);
+                pixelCr.setLineWidth(LINE_WIDTH);
+                rectangle(pixelCr, Rectangle(_currentPoint, _anchorPoint));
+                pixelCr.stroke;
+            } pixelCr.restore;
+        }
+    }
+
+    private {
+        bool _active;
+        Point _currentPoint;
+        Point _anchorPoint;      // Pixel
+        static immutable double LINE_WIDTH = 1.0;
+    }
+}
--- a/doodle/gtk/canvas.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/gtk/canvas.d	Thu Aug 12 16:35:24 2010 +0930
@@ -13,9 +13,6 @@
 
     import cairo.Surface;
 
-    import std.math;
-    import std.stdio;
-
     import gtk.Widget;
     import gtk.Toolbar;
     import gtk.Table;
@@ -30,6 +27,13 @@
     import gdk.Drawable;
 
     import gtkc.gtk;
+    import gtkc.gtktypes;
+    //import gtkc.gdktypes;
+
+    import std.math;
+    import std.stdio;
+
+    import core.stdc.string : strlen;
 }
 
 // x and y run right and up respectively
@@ -46,7 +50,7 @@
 //   zoom about a point (hold control and move scroll wheel)
 //   resize the widget
 
-class Canvas : Table, IViewport {
+final class Canvas : Table, IViewport {
     this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double ppi) {
         super(3, 3, 0);
 
@@ -85,14 +89,26 @@
         _drawingArea.addOnRealize(&onRealize);
         _drawingArea.addOnConfigure(&onConfigure);
         _drawingArea.addOnExpose(&onExpose);
-        _drawingArea.addOnButtonPress(&onButtonPress);
+        _drawingArea.addOnButtonPress(&onButtonPress);              // FIXME merge delegate with next
         _drawingArea.addOnButtonRelease(&onButtonRelease);
-        _drawingArea.addOnKeyPress(&onKeyEvent);
-        _drawingArea.addOnKeyRelease(&onKeyEvent);
+        _drawingArea.addOnKeyPress(&onKeyPressEvent);               // FIXME merge delegate with next
+        _drawingArea.addOnKeyRelease(&onKeyReleaseEvent);
         _drawingArea.addOnMotionNotify(&onMotionNotify);
         _drawingArea.addOnScroll(&onScroll);
-        _drawingArea.addOnEnterNotify(&onEnterNotify);
+        _drawingArea.addOnEnterNotify(&onEnterNotify);              // FIXME merge delegate with next
         _drawingArea.addOnLeaveNotify(&onLeaveNotify);
+
+        _drawingArea.addOnFocusIn(&onFocusIn);
+        _drawingArea.addOnFocusOut(&onFocusOut);
+        _drawingArea.addOnMoveFocus(&onMoveFocus);
+        /*
+        _drawingArea.addOnGrabBroken(&onGrabBroken);
+        */
+        _drawingArea.addOnGrabFocus(&onGrabFocus);
+        _drawingArea.addOnGrabNotify(&onGrabNotify);
+        // addOnPopupMenu
+        // addOnQueryTooltip
+        // addOnSelection*
         _drawingArea.setEvents(EventMask.EXPOSURE_MASK |
                                EventMask.POINTER_MOTION_MASK |
                                EventMask.POINTER_MOTION_HINT_MASK |
@@ -106,6 +122,8 @@
                                EventMask.FOCUS_CHANGE_MASK |
                                EventMask.SCROLL_MASK);
 
+        _drawingArea.setCanFocus(true);
+
         attach(_drawingArea,
                1, 2,
                1, 2, 
@@ -121,7 +139,8 @@
         attach(_hScrollbar,
                1, 2,
                2, 3,
-               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
+               AttachOptions.FILL | AttachOptions.EXPAND,
+               AttachOptions.SHRINK,
                0, 0);
 
         _vAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
@@ -138,7 +157,7 @@
 
     // IViewport overrides:
 
-    override void zoomRelative(in Point pixelDatum, in double factor) {
+    void zoomRelative(in Point pixelDatum, in 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
@@ -148,51 +167,58 @@
         _zoom = clampZoom(factor * _zoom);
         _viewCentre = oldModelDatum - pixelToModel(pixelDistance);
 
+        consolidateBounds;
+
         updateAdjustments;
         updateRulers;
         _grid.zoomChanged(_zoom);
         queueDraw;
     }
 
-    override void panRelative(in Vector pixelDisplacement) {
+    void panRelative(in Vector pixelDisplacement) {
         _viewCentre = _viewCentre + pixelToModel(pixelDisplacement);
 
+        consolidateBounds;
+
         updateAdjustments;
         updateRulers;
         queueDraw;
     }
 
-    override void setCursor(in Cursor cursor) {
-        CursorType cursor_type;
+    void setCursor(in Cursor cursor) {
+        CursorType cursorType;
 
         switch (cursor) {
         case Cursor.DEFAULT:
-            cursor_type = CursorType.ARROW;
+            cursorType = CursorType.ARROW;
             break;
         case Cursor.HAND:
-            cursor_type = CursorType.HAND1;
+            cursorType = CursorType.HAND1;
             break;
         case Cursor.CROSSHAIR:
-            cursor_type = CursorType.CROSSHAIR;
+            cursorType = CursorType.CROSSHAIR;
+            break;
+        case Cursor.PENCIL:
+            cursorType = CursorType.PENCIL;
             break;
         default:
             assert(0);
         }
 
-        _drawingArea.setCursor(new gdk.Cursor.Cursor(cursor_type));
+        _drawingArea.setCursor(new gdk.Cursor.Cursor(cursorType));
     }
 
-    override void damageModel(in Rectangle area) {
+    void damageModel(in Rectangle area) {
         _damage = _damage | modelToPixel(area);
     }
 
-    override void damagePixel(in Rectangle area) {
+    void damagePixel(in Rectangle area) {
         _damage = _damage | area;
     }
 
     private {
 
-        void initialise() {
+        void initialiseBounds() {
             Rectangle layerBounds = Rectangle.DEFAULT;
 
             foreach (ref layer; _layers) {
@@ -201,11 +227,12 @@
 
             assert(layerBounds.valid);
 
+            // FIXME use a function that grows a rectangle about its centre
+            // and change 2.0 to a class-level constant
             Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size);
 
             //
 
-            const double MM_PER_INCH = 25.4;
             _zoom = 0.25 * _ppi / MM_PER_INCH;
 
             _canvasBounds = paddedLayerBounds;
@@ -241,10 +268,9 @@
 
             _viewSize = Vector(cast(double)event.width, cast(double)event.height);
 
-
-            if (!_hadConfigure) {
-                initialise;
-                _hadConfigure = true;
+            if (!_boundsValid) {
+                initialiseBounds;
+                _boundsValid = true;
             }
             else {
                 consolidateBounds;
@@ -260,32 +286,32 @@
 
             int width, height;
             dr.getSize(width, height);
-            trace("Got expose %dx%d\n", width, height);
+            //trace("Got expose %dx%d\n", width, height);
 
             scope modelCr = new Context(dr);
             scope pixelCr = new Context(dr);
 
-            Rectangle pixel_damage =
+            Rectangle pixelDamage =
                 event is null ?
                 Rectangle(Point(0.0, 0.0), _viewSize) :
                 Rectangle(Point(cast(double)event.area.x, _viewSize.y - cast(double)(event.area.y + event.area.height)),
                           Vector(cast(double)event.area.width, cast(double)event.area.height));
 
-            Rectangle model_damage = pixelToModel(pixel_damage);
+            Rectangle modelDamage = pixelToModel(pixelDamage);
 
-            //trace("Pixel damage: %s, model damage: %s", pixel_damage, model_damage);
+            //trace("Pixel damage: %s, model damage: %s", pixelDamage, modelDamage);
 
             modelCr.save; pixelCr.save; {
                 // Setup model context and clip
 
-                GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct;
-                GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct;
+                GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
+                GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
 
                 modelCr.scale(_zoom, -_zoom);
-                modelCr.translate(-gtk_adjustment_get_value(h_gtkAdjustment),
-                                  -gtk_adjustment_get_value(v_gtkAdjustment) - gtk_adjustment_get_page_size(v_gtkAdjustment));
+                modelCr.translate(-gtk_adjustment_get_value(hGtkAdjustment),
+                                  -gtk_adjustment_get_value(vGtkAdjustment) - gtk_adjustment_get_page_size(vGtkAdjustment));
 
-                rectangle(modelCr, model_damage);
+                rectangle(modelCr, modelDamage);
                 modelCr.clip;
 
                 // Setup pixel context and clip
@@ -293,7 +319,7 @@
                 pixelCr.translate(0.0, _viewSize.y);
                 pixelCr.scale(1.0, -1.0);
 
-                rectangle(pixelCr, pixel_damage);
+                rectangle(pixelCr, pixelDamage);
                 pixelCr.clip;
 
                 // Fill the background
@@ -301,7 +327,7 @@
                 pixelCr.save; {
                     // Make the window light grey
                     pixelCr.setSourceRgba(0.9, 0.9, 0.9, 1.0);
-                    rectangle(pixelCr, pixel_damage);
+                    rectangle(pixelCr, pixelDamage);
                     pixelCr.fill;
                 } pixelCr.restore;
 
@@ -309,7 +335,7 @@
 
                 foreach(ref layer; _layers) {
                     modelCr.save; pixelCr.save; {
-                        layer.draw(this, pixel_damage, pixelCr, model_damage, modelCr);
+                        layer.draw(this, pixelDamage, pixelCr, modelDamage, modelCr);
                     } pixelCr.restore; modelCr.restore;
                 }
             } pixelCr.restore; modelCr.restore;
@@ -319,7 +345,7 @@
 
         bool onButtonPress(GdkEventButton * event, Widget widget) {
             assert(widget is _drawingArea);
-            trace("Got button event\n");
+            //trace("Got button event\n");
 
             Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
             Point modelPoint = pixelToModel(pixelPoint);
@@ -356,12 +382,45 @@
             return true;
         }
 
-        bool onKeyEvent(GdkEventKey * event, Widget widget) {
+        /*
+           public struct GdkEventKey {
+           GdkEventType type;
+           GdkWindow *window;
+           byte sendEvent;
+           uint time;
+           uint state;
+           uint keyval;
+           int length;
+           char *string;
+           ushort hardwareKeycode;
+           ubyte group;
+           uint bitfield0;
+        //uint isModifier : 1;
+        }
+         */
+        bool onKeyPressEvent(GdkEventKey * event, Widget widget) {
             assert(widget is _drawingArea);
-            //writefln("Got key event\n");
+            message("Got key event");
+
+            auto keyEvent = new KeyEvent(event.string[0..strlen(event.string)].idup,
+                                         event.keyval,
+                                         gtk2tkMask(event.state));
+            message("Got key press %s", keyEvent);
+            _eventHandler.handleKeyPress(this, keyEvent);
+
+            fixDamage;
 
-            //auto key_event = new KeyEvent("",
-            // mEventHandle.handle_key(key_event);
+            return true;
+        }
+
+        bool onKeyReleaseEvent(GdkEventKey * event, Widget widget) {
+            assert(widget is _drawingArea);
+
+            auto keyEvent = new KeyEvent(event.string[0..strlen(event.string)].idup,
+                                         event.keyval,
+                                         gtk2tkMask(event.state));
+            message("Got key release %s", keyEvent);
+            _eventHandler.handleKeyRelease(this, keyEvent);
 
             fixDamage;
 
@@ -377,11 +436,11 @@
             Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
             Point modelPoint = pixelToModel(pixelPoint);
 
-            auto motion_event = new MotionEvent(pixelPoint,
+            auto motionEvent = new MotionEvent(pixelPoint,
                                                 modelPoint,
                                                 gtk2tkMask(event.state));
 
-            _eventHandler.handleMotion(this, motion_event);
+            _eventHandler.handleMotion(this, motionEvent);
 
             fixDamage;
 
@@ -395,12 +454,12 @@
             Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
             Point modelPoint = pixelToModel(pixelPoint);
 
-            auto scroll_event = new ScrollEvent(gtk2tkDirection(event.direction),
+            auto scrollEvent = new ScrollEvent(gtk2tkDirection(event.direction),
                                                 pixelPoint,
                                                 modelPoint,
                                                 gtk2tkMask(event.state));
 
-            _eventHandler.handleScroll(this, scroll_event);
+            _eventHandler.handleScroll(this, scrollEvent);
 
             fixDamage;
 
@@ -436,24 +495,61 @@
 
         bool onEnterNotify(GdkEventCrossing * event, Widget widget) {
             assert(widget is _drawingArea);
-            //writefln("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
+            message("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
             // TODO
             return true;
         }
 
         bool onLeaveNotify(GdkEventCrossing * event, Widget widget) {
             assert(widget is _drawingArea);
-            //writefln("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
+            message("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
             // TODO
             return true;
         }
 
+        /*
+           public struct GdkEventFocus {
+           GdkEventType type;
+           GdkWindow *window;
+           byte sendEvent;
+           short inn;
+           }
+         */
+        bool onFocusIn(GdkEventFocus * event, Widget widget) {
+            trace("onFocusIn");
+            return true;
+        }
+
+        bool onFocusOut(GdkEventFocus * event, Widget widget) {
+            trace("onFocusOut");
+            return true;
+        }
+
+        void onMoveFocus(GtkDirectionType direction, Widget widget) {
+            trace("onMoveFocus");
+        }
+
+        /*
+        bool onGrabBroken(Event event, Widget widget) {
+            trace("onGrabBroken");
+            return true;
+        }
+        */
+
+        void onGrabFocus(Widget widget) {
+            trace("onGrabFocus");
+        }
+
+        void onGrabNotify(gboolean what, Widget widget){
+            trace("onGrabNotify");
+        }
+
         void onValueChanged(Adjustment adjustment) {
-            GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct;
-            GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct;
+            GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
+            GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
 
-            Point viewLeftTop = Point(gtk_adjustment_get_value(h_gtkAdjustment),
-                                      gtk_adjustment_get_value(v_gtkAdjustment));
+            Point viewLeftTop = Point(gtk_adjustment_get_value(hGtkAdjustment),
+                                      gtk_adjustment_get_value(vGtkAdjustment));
 
             Vector modelSize = pixelToModel(_viewSize);
 
@@ -501,22 +597,22 @@
 
             // Update the adjustments
 
-            GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct;
-            GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct;
+            GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
+            GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
 
-            gtk_adjustment_set_lower(h_gtkAdjustment, _canvasBounds.minCorner.x);
-            gtk_adjustment_set_upper(h_gtkAdjustment, _canvasBounds.maxCorner.x);
-            gtk_adjustment_set_value(h_gtkAdjustment, viewLeftBottom.x);
-            gtk_adjustment_set_step_increment(h_gtkAdjustment, _canvasBounds.size.x / 16.0);
-            gtk_adjustment_set_page_increment(h_gtkAdjustment, _canvasBounds.size.x / 4.0);
-            gtk_adjustment_set_page_size(h_gtkAdjustment, modelSize.x);
+            gtk_adjustment_set_lower(hGtkAdjustment, _canvasBounds.minCorner.x);
+            gtk_adjustment_set_upper(hGtkAdjustment, _canvasBounds.maxCorner.x);
+            gtk_adjustment_set_value(hGtkAdjustment, viewLeftBottom.x);
+            gtk_adjustment_set_step_increment(hGtkAdjustment, _canvasBounds.size.x / 16.0);
+            gtk_adjustment_set_page_increment(hGtkAdjustment, _canvasBounds.size.x / 4.0);
+            gtk_adjustment_set_page_size(hGtkAdjustment, modelSize.x);
 
-            gtk_adjustment_set_lower(v_gtkAdjustment, _canvasBounds.minCorner.y);
-            gtk_adjustment_set_upper(v_gtkAdjustment, _canvasBounds.maxCorner.y);
-            gtk_adjustment_set_value(v_gtkAdjustment, viewLeftBottom.y);
-            gtk_adjustment_set_step_increment(v_gtkAdjustment, _canvasBounds.size.y / 16.0);
-            gtk_adjustment_set_page_increment(v_gtkAdjustment, _canvasBounds.size.y / 4.0);
-            gtk_adjustment_set_page_size(v_gtkAdjustment, modelSize.y);
+            gtk_adjustment_set_lower(vGtkAdjustment, _canvasBounds.minCorner.y);
+            gtk_adjustment_set_upper(vGtkAdjustment, _canvasBounds.maxCorner.y);
+            gtk_adjustment_set_value(vGtkAdjustment, viewLeftBottom.y);
+            gtk_adjustment_set_step_increment(vGtkAdjustment, _canvasBounds.size.y / 16.0);
+            gtk_adjustment_set_page_increment(vGtkAdjustment, _canvasBounds.size.y / 4.0);
+            gtk_adjustment_set_page_size(vGtkAdjustment, modelSize.y);
 
             _hAdjustment.changed;
             _hAdjustment.valueChanged;
@@ -526,18 +622,14 @@
 
         void fixDamage() {
             if (_damage.valid) {
-                //writefln("Damage: %s", _damage);
                 int x, y, w, h;
                 _damage.getQuantised(x, y, w, h);
                 _drawingArea.queueDrawArea(x, cast(int)_viewSize.y - (y + h), w, h);
                 _damage = Rectangle.DEFAULT;
             }
-            else {
-                //writefln("No damage");
-            }
         }
 
-        double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); }
+        static double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); }
 
         Point modelToPixel(in Point model) const {
             return Point.DEFAULT + _viewSize / 2.0 + _zoom * (model - _viewCentre);
@@ -566,9 +658,10 @@
         void onRealize(Widget widget) {
             assert(widget is _drawingArea);
             //writefln("Got realize\n");
+            _drawingArea.grabFocus();
         }
 
-        bool _hadConfigure;
+        bool _boundsValid;
         Rectangle _damage;          // pixels
 
         // Model units are in millimetres
@@ -591,5 +684,7 @@
         IEventHandler _eventHandler;
         IGrid _grid;
         double _ppi;
+
+        static immutable MM_PER_INCH = 25.4;
     }
 }
--- a/doodle/main/prog/doodler.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/main/prog/doodler.d	Thu Aug 12 16:35:24 2010 +0930
@@ -6,8 +6,10 @@
     import doodle.dia.standard_tools;
     import doodle.dia.page_layer;
     import doodle.dia.grid_layer;
+    import doodle.dia.tool_layer;
+
     import doodle.fig.diagram_layer;
-    import doodle.dia.tool_layer;
+    import doodle.fig.select_tool;
 
     import doodle.fig.tools;
 
--- a/doodle/tk/events.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/tk/events.d	Thu Aug 12 16:35:24 2010 +0930
@@ -5,6 +5,11 @@
     import doodle.tk.geometry;
 }
 
+// FIXME
+// Do we need FocusEvent, Enter/LeaveEvent ?
+// Note, FocusEvent has no mask but Enter/Leave do.
+// Hence would need to refactor hierarchy slightly, eg InputEvent
+
 abstract class Event {
     this(in Mask mask) {
         _mask = mask;
@@ -17,11 +22,6 @@
     }
 }
 
-/+
-final class FocusEvent : Event {
-}
-+/
-
 final class KeyEvent : Event {
     this(in string str, in uint value, in Mask mask) {
         super(mask);
@@ -30,6 +30,7 @@
     }
 
     string str() const { return _str; }
+    uint value() const { return _value; }
 
     override string toString() const {
         return std.string.format("Key event: %s, %d, %s", _str, _value, _mask);
--- a/doodle/tk/types.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/tk/types.d	Thu Aug 12 16:35:24 2010 +0930
@@ -30,7 +30,7 @@
         }
     }
 
-    string toString() const {
+    string toString() {
         string s;
 
         // FIXME this is terrible
--- a/options	Thu Aug 12 11:48:55 2010 +0930
+++ b/options	Thu Aug 12 16:35:24 2010 +0930
@@ -1,4 +1,4 @@
--I~/source/d/local/include/d
+-I~/source/d/dmd/include/d
 -L-lgtkd
 -L-ldl
 -w