# HG changeset patch # User "David Bryant " # Date 1247666489 -34200 # Node ID 17c2df87b4596ac7a4f3119b3758c5d65af8a15d # Parent 993ac3a183c8b178abc52383e8ce74f5a4cbdc10 Package refactoring. diff -r 993ac3a183c8 -r 17c2df87b459 build.sh --- 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 diff -r 993ac3a183c8 -r 17c2df87b459 cairo/routines.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); +} diff -r 993ac3a183c8 -r 17c2df87b459 cairo_support.d --- 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); -} diff -r 993ac3a183c8 -r 17c2df87b459 canvas.d --- 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; - } -} diff -r 993ac3a183c8 -r 17c2df87b459 dia/grid_layer.d --- /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 { + } +} diff -r 993ac3a183c8 -r 17c2df87b459 dia/icanvas.d --- /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; + } +} diff -r 993ac3a183c8 -r 17c2df87b459 dia/page_layer.d --- /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; + } +} diff -r 993ac3a183c8 -r 17c2df87b459 dia/standard_tools.d --- /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; + } +} diff -r 993ac3a183c8 -r 17c2df87b459 dia/tool.d --- /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 { } +} diff -r 993ac3a183c8 -r 17c2df87b459 dia/tool_layer.d --- /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; + } +} diff -r 993ac3a183c8 -r 17c2df87b459 doodle.d --- 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; diff -r 993ac3a183c8 -r 17c2df87b459 grid_layer.d --- 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 { - } -} diff -r 993ac3a183c8 -r 17c2df87b459 gtk/canvas.d --- /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; + } +} diff -r 993ac3a183c8 -r 17c2df87b459 gtk/conversions.d --- /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); + } +} diff -r 993ac3a183c8 -r 17c2df87b459 gtk/gtk_conversions.d --- 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); - } -} diff -r 993ac3a183c8 -r 17c2df87b459 icanvas.d --- 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; - } -} diff -r 993ac3a183c8 -r 17c2df87b459 page_layer.d --- 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; - } -} diff -r 993ac3a183c8 -r 17c2df87b459 standard_tools.d --- 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; - } -} diff -r 993ac3a183c8 -r 17c2df87b459 tool.d --- 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 { } -} diff -r 993ac3a183c8 -r 17c2df87b459 tool_layer.d --- 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; - } -}