Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Chart.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Chart.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Chart.cs	(revision 13045)
@@ -23,5 +23,4 @@
 using System.Collections.Generic;
 using System.Drawing;
-using System.Linq;
 using System.Windows.Forms;
 
@@ -30,14 +29,19 @@
     private PointD originalLowerLeft;
     private PointD originalUpperRight;
-    private List<Offset> zoomHistory;
-
-    private ChartMode myMode;
-    public virtual ChartMode Mode {
-      get { return myMode; }
+    protected List<Offset> zoomHistory;
+
+    private bool enabled;
+    public bool Enabled {
+      get { return enabled; }
       set {
-        myMode = value;
-        OnUpdate();
-      }
-    }
+        if (enabled == value) return;
+        enabled = value;
+        OnRedrawRequired();
+      }
+    }
+
+    protected bool SuppressRedraw { get; set; }
+
+    public bool SuppressEvents { get; set; }
 
     private PointD myLowerLeft;
@@ -53,4 +57,5 @@
     }
     private Size mySizeInPixels;
+
     public Size SizeInPixels {
       get { return mySizeInPixels; }
@@ -62,48 +67,42 @@
       get { return new SizeD(SizeInPixels.Width / Size.Width, SizeInPixels.Height / Size.Height); }
     }
-
-    private IGroup myGroup;
-    public IGroup Group {
-      get { return myGroup; }
-    }
-
-    private bool myUpdateEnabled;
-    public bool UpdateEnabled {
-      get { return myUpdateEnabled; }
-      set { myUpdateEnabled = value; }
-    }
+    public double Scale { get; set; }
+    public double MinimumZoomDistance { get; set; }
+    public IGroup Group { get; protected set; }
 
     public Chart(PointD lowerLeft, PointD upperRight) {
-      myUpdateEnabled = false;
       zoomHistory = new List<Offset>();
-      myMode = ChartMode.Move;
       SetPosition(lowerLeft, upperRight);
-      mySizeInPixels = new Size(100, 100);
-      myGroup = new Group(this);
-      Group.Update += new EventHandler(Group_Update);
-      myUpdateEnabled = true;
-    }
+      mySizeInPixels = new Size((int)Size.Width, (int)Size.Height);
+      Scale = 1.0;
+      Group = new Group(this);
+      Group.RedrawRequired += GroupOnRedrawRequired;
+      MinimumZoomDistance = 0.01;
+    }
+
     public Chart(double x1, double y1, double x2, double y2)
-      : this(new PointD(x1, y1), new PointD(x2, y2)) {
-    }
+      : this(new PointD(x1, y1), new PointD(x2, y2)) { }
 
     public PointD TransformPixelToWorld(Point point) {
-      double x = LowerLeft.X + point.X * PixelToWorldRatio.Width;
-      double y = LowerLeft.Y + (SizeInPixels.Height - point.Y) * PixelToWorldRatio.Height;
+      var x = LowerLeft.X + point.X * PixelToWorldRatio.Width;
+      var y = LowerLeft.Y + (SizeInPixels.Height - point.Y) * PixelToWorldRatio.Height;
       return new PointD(x, y);
     }
+
     public SizeD TransformPixelToWorld(Size size) {
-      double width = size.Width * PixelToWorldRatio.Width;
-      double height = size.Height * PixelToWorldRatio.Height;
+      var width = size.Width * PixelToWorldRatio.Width;
+      var height = size.Height * PixelToWorldRatio.Height;
       return new SizeD(width, height);
     }
+
     public Point TransformWorldToPixel(PointD point) {
-      int x = (int)((point.X - LowerLeft.X) * WorldToPixelRatio.Width);
-      int y = (int)((UpperRight.Y - point.Y) * WorldToPixelRatio.Height);
+      var x = (int)((point.X - LowerLeft.X) * WorldToPixelRatio.Width);
+      var y = (int)((UpperRight.Y - point.Y) * WorldToPixelRatio.Height);
       return new Point(x, y);
     }
+
     public Size TransformWorldToPixel(SizeD size) {
-      int width = (int)(size.Width * WorldToPixelRatio.Width);
-      int height = (int)(size.Height * WorldToPixelRatio.Height);
+      var width = (int)(size.Width * WorldToPixelRatio.Width);
+      var height = (int)(size.Height * WorldToPixelRatio.Height);
       return new Size(width, height);
     }
@@ -118,34 +117,61 @@
       myLowerLeft = lowerLeft;
       myUpperRight = upperRight;
-      OnUpdate();
-    }
-    public void SetPosition(double x1, double y1, double x2, double y2) {
-      SetPosition(new PointD(x1, y1), new PointD(x2, y2));
-    }
+      OnRedrawRequired();
+    }
+
     public virtual void Move(Offset delta) {
       myLowerLeft += delta;
       myUpperRight += delta;
-      OnUpdate();
-    }
-    public void Move(double dx, double dy) {
-      Move(new Offset(dx, dy));
+      OnRedrawRequired();
+    }
+
+    public virtual void Zoom(Point mousePosition, double delta) {
+      var cursor = TransformPixelToWorld(mousePosition);
+
+      double zoomLevel = (100 + delta) / 100;
+      double width = this.UpperRight.X - this.LowerLeft.X;
+      double height = this.UpperRight.Y - this.LowerLeft.Y;
+      double newWidth = width * zoomLevel;
+      double newHeight = height * zoomLevel;
+      double xAdaption = (UpperRight.X - cursor.X) / width;
+      double yAdaption = (UpperRight.Y - cursor.Y) / height;
+
+      ZoomIn(new PointD(cursor.X - newWidth * (1 - xAdaption), cursor.Y - newHeight * (1 - yAdaption)), new PointD(cursor.X + newWidth * xAdaption, cursor.Y + newHeight * yAdaption));
+      OnRedrawRequired();
+    }
+
+    public virtual void ZoomIn(Point mousePosition) {
+      Zoom(mousePosition, -10);
     }
 
     public virtual void ZoomIn(PointD lowerLeft, PointD upperRight) {
+      // maintain a 1:1 ratio between x and y axis
+      var pixels = SizeInPixels;
+      var diagonal = upperRight - lowerLeft;
+      var windowRatio = pixels.Height / (double)pixels.Width;
+      var viewRatio = diagonal.DY / diagonal.DX;
+      if (viewRatio < windowRatio) {
+        var neededHeight = windowRatio * diagonal.DX;
+        var diff = (neededHeight - diagonal.DY) / 2.0;
+        lowerLeft.Y -= diff;
+        upperRight.Y += diff;
+      } else {
+        var neededWidth = diagonal.DY / windowRatio;
+        var diff = (neededWidth - diagonal.DX) / 2.0;
+        lowerLeft.X -= diff;
+        upperRight.X += diff;
+      }
+      if ((lowerLeft - upperRight).Length < MinimumZoomDistance) return;
       zoomHistory.Insert(0, LowerLeft - lowerLeft);
       zoomHistory.Insert(0, UpperRight - upperRight);
       myLowerLeft = lowerLeft;
       myUpperRight = upperRight;
-      OnUpdate();
-    }
-    public void ZoomIn(double x1, double y1, double x2, double y2) {
-      ZoomIn(new PointD(x1, y1), new PointD(x2, y2));
-    }
+      OnRedrawRequired();
+    }
+
     public virtual void ZoomIn(Point lowerLeft, Point upperRight) {
       ZoomIn(TransformPixelToWorld(lowerLeft), TransformPixelToWorld(upperRight));
     }
-    public void ZoomIn(int x1, int y1, int x2, int y2) {
-      ZoomIn(new Point(x1, y1), new Point(x2, y2));
-    }
+
     public virtual void ZoomOut() {
       if (zoomHistory.Count > 0) {
@@ -156,101 +182,46 @@
         myLowerLeft = LowerLeft + lowerLeft;
         myUpperRight = UpperRight + upperRight;
-        OnUpdate();
+        OnRedrawRequired();
       } else {
-        myLowerLeft.X -= Size.Width / 4;
-        myLowerLeft.Y -= Size.Height / 4;
-        myUpperRight.X += Size.Width / 4;
-        myUpperRight.Y += Size.Height / 4;
-        OnUpdate();
-      }
-    }
+        var lowerLeft = new PointD(myLowerLeft.X - Size.Width / 4, myLowerLeft.Y - Size.Height / 4);
+        var upperRight = new PointD(myUpperRight.X + Size.Width / 4, myUpperRight.Y + Size.Height / 4);
+        ZoomIn(lowerLeft, upperRight);
+        if (zoomHistory.Count >= 2) {
+          zoomHistory.RemoveRange(0, 2);
+        }
+      }
+    }
+
     public virtual void Unzoom() {
       SetPosition(originalLowerLeft, originalUpperRight);
     }
 
+    public virtual void IntoForeground(IPrimitive primitive) {
+      Group.IntoForeground(primitive);
+    }
+    public virtual void IntoBackground(IPrimitive primitive) {
+      Group.IntoBackground(primitive);
+    }
+    public virtual void OneLayerUp(IPrimitive primitive) {
+      Group.OneLayerUp(primitive);
+    }
+    public virtual void OneLayerDown(IPrimitive primitive) {
+      Group.OneLayerDown(primitive);
+    }
+
+    public virtual Cursor GetCursor(Point point) {
+      return Group.GetCursor(TransformPixelToWorld(point)) ?? Cursors.Default;
+    }
+
+    public virtual string GetToolTipText(Point point) {
+      return Group.GetToolTipText(TransformPixelToWorld(point));
+    }
+
     public virtual IPrimitive GetPrimitive(Point point) {
       return Group.GetPrimitive(TransformPixelToWorld(point));
     }
-    public IPrimitive GetPrimitive(int x, int y) {
-      return GetPrimitive(new Point(x, y));
-    }
-    public virtual IList<IPrimitive> GetAllPrimitives(Point point) {
+
+    public virtual IEnumerable<IPrimitive> GetAllPrimitives(Point point) {
       return Group.GetAllPrimitives(TransformPixelToWorld(point));
-    }
-    public IList<IPrimitive> GetAllPrimitives(int x, int y) {
-      return GetAllPrimitives(new Point(x, y));
-    }
-
-    public virtual Cursor GetCursor(Point point) {
-      Cursor cursor = Group.GetCursor(TransformPixelToWorld(point));
-      if (cursor != null) return cursor;
-
-      if (Mode == ChartMode.Move) return Cursors.Hand;
-      if (Mode == ChartMode.Select) return Cursors.Arrow;
-      if (Mode == ChartMode.Zoom) return Cursors.Arrow;
-      return Cursors.Default;
-    }
-    public Cursor GetCursor(int x, int y) {
-      return GetCursor(new Point(x, y));
-    }
-    public virtual string GetToolTipText(Point point) {
-      return Group.GetToolTipText(TransformPixelToWorld(point));
-    }
-    public string GetToolTipText(int x, int y) {
-      return GetToolTipText(new Point(x, y));
-    }
-
-    public virtual void MouseClick(Point point, MouseButtons button) {
-      if (Mode == ChartMode.Select) {
-        if (button == MouseButtons.Left) {
-          IPrimitive clicked = GetPrimitive(point);
-          UpdateEnabled = false;
-          foreach (IPrimitive primitive in Group.SelectedPrimitives)
-            primitive.Selected = false;
-          UpdateEnabled = true;
-          if (clicked != null) {
-            PointD p = TransformPixelToWorld(point);
-            clicked.MouseClick(p, button);
-          } else OnUpdate();
-        }
-      } else if (Mode == ChartMode.Move) {
-        if (button == MouseButtons.Middle) {
-          PointD center = LowerLeft + new Offset(Size.Width / 2, Size.Height / 2);
-          Move(TransformPixelToWorld(point) - center);
-        }
-      }
-    }
-    public void MouseClick(int x, int y, MouseButtons button) {
-      MouseClick(new Point(x, y), button);
-    }
-    public virtual void MouseDoubleClick(Point point, MouseButtons button) {
-    }
-    public void MouseDoubleClick(int x, int y, MouseButtons button) {
-      MouseDoubleClick(new Point(x, y), button);
-    }
-    public virtual void MouseMove(Point start, Point end) {
-    }
-    public void MouseMove(int x1, int y1, int x2, int y2) {
-      MouseMove(new Point(x1, y1), new Point(x2, y2));
-    }
-    public virtual void MouseDrag(Point start, Point end, MouseButtons button) {
-      PointD p1 = TransformPixelToWorld(start);
-      PointD p2 = TransformPixelToWorld(end);
-      Offset offset = p2 - p1;
-      if (Mode == ChartMode.Move) {
-        Move(-1 * offset.DX, -1 * offset.DY);
-      } else if (Mode == ChartMode.Select) {
-        if (button == MouseButtons.Left) {
-          IList<IPrimitive> selected = Group.SelectedPrimitives.Where(p => p.ContainsPoint(p1)).ToList();
-          UpdateEnabled = false;
-          foreach (IPrimitive primitive in selected)
-            primitive.MouseDrag(p1, offset, button);
-          UpdateEnabled = true;
-          OnUpdate();
-        }
-      }
-    }
-    public void MouseDrag(int x1, int y1, int x2, int y2, MouseButtons button) {
-      MouseDrag(new Point(x1, y1), new Point(x2, y2), button);
     }
 
@@ -260,19 +231,26 @@
       Group.Draw(graphics);
       Group.PostDraw(graphics);
-    }
-
-    public event EventHandler Update;
-    public void EnforceUpdate() {
-      if (Update != null) {
-        Update(this, new EventArgs());
-      }
-    }
-    protected virtual void OnUpdate() {
-      if ((UpdateEnabled) && (Update != null)) {
-        Update(this, new EventArgs());
-      }
-    }
-    private void Group_Update(object sender, EventArgs e) {
-      OnUpdate();
+      if (!enabled) {
+        var disabledColor = Color.FromArgb(150, SystemColors.Control);
+        var brush = new SolidBrush(disabledColor);
+        var rectangle = new System.Drawing.Rectangle(new Point(0, 0), mySizeInPixels);
+        graphics.FillRectangle(brush, rectangle);
+      }
+    }
+
+    public event EventHandler RedrawRequired;
+    protected virtual void OnRedrawRequired() {
+      if (SuppressRedraw) return;
+      var handler = RedrawRequired;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    public void RaiseRedrawRequired() {
+      var handler = RedrawRequired;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    private void GroupOnRedrawRequired(object sender, EventArgs e) {
+      OnRedrawRequired();
     }
   }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.Designer.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.Designer.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.Designer.cs	(revision 13045)
@@ -48,19 +48,22 @@
       this.pictureBox = new System.Windows.Forms.PictureBox();
       this.pictureBoxContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
-      this.moveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-      this.zoomToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-      this.selectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-      this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
       this.oneLayerUpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
       this.oneLayerDownToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
       this.intoForegroundToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
       this.intoBackgroundToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+      this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
+      this.propertiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      this.selectButton = new System.Windows.Forms.RadioButton();
+      this.moveButton = new System.Windows.Forms.RadioButton();
+      this.zoomInButton = new System.Windows.Forms.RadioButton();
+      this.zoomOutButton = new System.Windows.Forms.RadioButton();
+      this.buttonPanel = new System.Windows.Forms.FlowLayoutPanel();
+      this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
+      this.saveImageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
       this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
-      this.propertiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-      this.saveImageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
-      this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
       ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit();
       this.pictureBoxContextMenuStrip.SuspendLayout();
+      this.buttonPanel.SuspendLayout();
       this.SuspendLayout();
       // 
@@ -71,63 +74,39 @@
             | System.Windows.Forms.AnchorStyles.Right)));
       this.pictureBox.BackColor = System.Drawing.Color.White;
+      this.pictureBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
       this.pictureBox.ContextMenuStrip = this.pictureBoxContextMenuStrip;
-      this.pictureBox.Location = new System.Drawing.Point(0, 0);
+      this.pictureBox.Location = new System.Drawing.Point(35, 0);
       this.pictureBox.Name = "pictureBox";
-      this.pictureBox.Size = new System.Drawing.Size(266, 236);
+      this.pictureBox.Size = new System.Drawing.Size(685, 500);
       this.pictureBox.TabIndex = 0;
       this.pictureBox.TabStop = false;
-      this.pictureBox.SizeChanged += new System.EventHandler(this.pictureBox_SizeChanged);
-      this.pictureBox.VisibleChanged += new System.EventHandler(this.pictureBox_VisibleChanged);
-      this.pictureBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBox_MouseDown);
-      this.pictureBox.MouseEnter += new System.EventHandler(this.pictureBox_MouseEnter);
-      this.pictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBox_MouseMove);
-      this.pictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pictureBox_MouseUp);
-      this.pictureBox.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.pictureBox_MouseWheel);
-
+      this.pictureBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OnKeyUp);
+      this.pictureBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnKeyDown);
+      this.pictureBox.SizeChanged += new System.EventHandler(this.PictureBoxOnSizeChanged);
+      this.pictureBox.VisibleChanged += new System.EventHandler(this.PictureBoxOnVisibleChanged);
+      this.pictureBox.Click += new System.EventHandler(this.PictureBoxOnClick);
+      this.pictureBox.MouseClick += new System.Windows.Forms.MouseEventHandler(this.PictureBoxOnMouseClick);
+      this.pictureBox.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.PictureBoxOnMouseDoubleClick);
+      this.pictureBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PictureBoxOnMouseDown);
+      this.pictureBox.MouseEnter += new System.EventHandler(this.PictureBoxOnMouseEnter);
+      this.pictureBox.MouseLeave += new System.EventHandler(this.PictureBoxOnMouseLeave);
+      this.pictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBoxOnMouseMove);
+      this.pictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.PictureBoxOnMouseUp);
+      this.pictureBox.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.PictureBoxOnMouseWheel);
       // 
       // pictureBoxContextMenuStrip
       // 
       this.pictureBoxContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.moveToolStripMenuItem,
-            this.zoomToolStripMenuItem,
-            this.selectToolStripMenuItem,
-            this.toolStripMenuItem1,
             this.oneLayerUpToolStripMenuItem,
             this.oneLayerDownToolStripMenuItem,
             this.intoForegroundToolStripMenuItem,
             this.intoBackgroundToolStripMenuItem,
+            this.toolStripMenuItem1,
+            this.propertiesToolStripMenuItem,
             this.toolStripMenuItem2,
-            this.propertiesToolStripMenuItem,
             this.saveImageToolStripMenuItem});
       this.pictureBoxContextMenuStrip.Name = "pictureBoxContextMenuStrip";
-      this.pictureBoxContextMenuStrip.Size = new System.Drawing.Size(163, 214);
-      // 
-      // moveToolStripMenuItem
-      // 
-      this.moveToolStripMenuItem.Checked = true;
-      this.moveToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
-      this.moveToolStripMenuItem.Name = "moveToolStripMenuItem";
-      this.moveToolStripMenuItem.Size = new System.Drawing.Size(162, 22);
-      this.moveToolStripMenuItem.Text = "&Move";
-      this.moveToolStripMenuItem.Click += new System.EventHandler(this.moveToolStripMenuItem_Click);
-      // 
-      // zoomToolStripMenuItem
-      // 
-      this.zoomToolStripMenuItem.Name = "zoomToolStripMenuItem";
-      this.zoomToolStripMenuItem.Size = new System.Drawing.Size(162, 22);
-      this.zoomToolStripMenuItem.Text = "&Zoom";
-      this.zoomToolStripMenuItem.Click += new System.EventHandler(this.zoomToolStripMenuItem_Click);
-      // 
-      // selectToolStripMenuItem
-      // 
-      this.selectToolStripMenuItem.Name = "selectToolStripMenuItem";
-      this.selectToolStripMenuItem.Size = new System.Drawing.Size(162, 22);
-      this.selectToolStripMenuItem.Text = "&Select";
-      this.selectToolStripMenuItem.Click += new System.EventHandler(this.selectToolStripMenuItem_Click);
-      // 
-      // toolStripMenuItem1
-      // 
-      this.toolStripMenuItem1.Name = "toolStripMenuItem1";
-      this.toolStripMenuItem1.Size = new System.Drawing.Size(159, 6);
+      this.pictureBoxContextMenuStrip.Size = new System.Drawing.Size(163, 170);
+      this.pictureBoxContextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(this.PictureBoxContextMenuStripOnOpening);
       // 
       // oneLayerUpToolStripMenuItem
@@ -163,8 +142,8 @@
       this.intoBackgroundToolStripMenuItem.Click += new System.EventHandler(this.intoBackgroundToolStripMenuItem_Click);
       // 
-      // toolStripMenuItem2
-      // 
-      this.toolStripMenuItem2.Name = "toolStripMenuItem2";
-      this.toolStripMenuItem2.Size = new System.Drawing.Size(159, 6);
+      // toolStripMenuItem1
+      // 
+      this.toolStripMenuItem1.Name = "toolStripMenuItem1";
+      this.toolStripMenuItem1.Size = new System.Drawing.Size(159, 6);
       // 
       // propertiesToolStripMenuItem
@@ -176,9 +155,14 @@
       this.propertiesToolStripMenuItem.Click += new System.EventHandler(this.propertiesToolStripMenuItem_Click);
       // 
+      // toolStripMenuItem2
+      // 
+      this.toolStripMenuItem2.Name = "toolStripMenuItem2";
+      this.toolStripMenuItem2.Size = new System.Drawing.Size(159, 6);
+      // 
       // saveImageToolStripMenuItem
       // 
       this.saveImageToolStripMenuItem.Name = "saveImageToolStripMenuItem";
       this.saveImageToolStripMenuItem.Size = new System.Drawing.Size(162, 22);
-      this.saveImageToolStripMenuItem.Text = "Save Image";
+      this.saveImageToolStripMenuItem.Text = "&Save Image";
       this.saveImageToolStripMenuItem.Click += new System.EventHandler(this.saveImageToolStripMenuItem_Click);
       // 
@@ -187,17 +171,91 @@
       this.saveFileDialog.DefaultExt = "emf";
       this.saveFileDialog.FileName = "*.emf";
-      this.saveFileDialog.Filter = "Enhanced metafile|*.emf|Bitmap files|*.bmp";
+      this.saveFileDialog.Filter = "Bitmap (*.bmp)|*.bmp|JPEG (*.jpg)|*.jpg|EMF (*.emf)|*.emf|PNG (*.png)|*.png|GIF (" +
+    "*.gif)|*.gif|TIFF (*.tif)|*.tif";
+      // 
+      // selectButton
+      // 
+      this.selectButton.Appearance = System.Windows.Forms.Appearance.Button;
+      this.selectButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Pointer;
+      this.selectButton.Location = new System.Drawing.Point(3, 3);
+      this.selectButton.Name = "selectButton";
+      this.selectButton.Size = new System.Drawing.Size(26, 24);
+      this.selectButton.TabIndex = 1;
+      this.selectButton.TabStop = true;
+      this.selectButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+      this.toolTip.SetToolTip(this.selectButton, "Select");
+      this.selectButton.UseVisualStyleBackColor = true;
+      this.selectButton.CheckedChanged += new System.EventHandler(this.SelectButtonOnCheckedChanged);
+      // 
+      // moveButton
+      // 
+      this.moveButton.Appearance = System.Windows.Forms.Appearance.Button;
+      this.moveButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Breakpoint;
+      this.moveButton.Location = new System.Drawing.Point(3, 33);
+      this.moveButton.Name = "moveButton";
+      this.moveButton.Size = new System.Drawing.Size(26, 24);
+      this.moveButton.TabIndex = 1;
+      this.moveButton.TabStop = true;
+      this.moveButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+      this.toolTip.SetToolTip(this.moveButton, "Move");
+      this.moveButton.UseVisualStyleBackColor = true;
+      this.moveButton.CheckedChanged += new System.EventHandler(this.MoveButtonOnCheckedChanged);
+      // 
+      // zoomInButton
+      // 
+      this.zoomInButton.Appearance = System.Windows.Forms.Appearance.Button;
+      this.zoomInButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.ZoomIn;
+      this.zoomInButton.Location = new System.Drawing.Point(3, 63);
+      this.zoomInButton.Name = "zoomInButton";
+      this.zoomInButton.Size = new System.Drawing.Size(26, 24);
+      this.zoomInButton.TabIndex = 1;
+      this.zoomInButton.TabStop = true;
+      this.zoomInButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+      this.toolTip.SetToolTip(this.zoomInButton, "Zoom In");
+      this.zoomInButton.UseVisualStyleBackColor = true;
+      this.zoomInButton.CheckedChanged += new System.EventHandler(this.ZoomInButtonOnCheckedChanged);
+      // 
+      // zoomOutButton
+      // 
+      this.zoomOutButton.Appearance = System.Windows.Forms.Appearance.Button;
+      this.zoomOutButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.ZoomOut;
+      this.zoomOutButton.Location = new System.Drawing.Point(3, 93);
+      this.zoomOutButton.Name = "zoomOutButton";
+      this.zoomOutButton.Size = new System.Drawing.Size(26, 24);
+      this.zoomOutButton.TabIndex = 1;
+      this.zoomOutButton.TabStop = true;
+      this.zoomOutButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+      this.toolTip.SetToolTip(this.zoomOutButton, "Zoom Out");
+      this.zoomOutButton.UseVisualStyleBackColor = true;
+      this.zoomOutButton.CheckedChanged += new System.EventHandler(this.ZoomOutButtonOnCheckedChanged);
+      // 
+      // buttonPanel
+      // 
+      this.buttonPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+            | System.Windows.Forms.AnchorStyles.Left)));
+      this.buttonPanel.Controls.Add(this.selectButton);
+      this.buttonPanel.Controls.Add(this.moveButton);
+      this.buttonPanel.Controls.Add(this.zoomInButton);
+      this.buttonPanel.Controls.Add(this.zoomOutButton);
+      this.buttonPanel.Location = new System.Drawing.Point(0, 0);
+      this.buttonPanel.Name = "buttonPanel";
+      this.buttonPanel.Size = new System.Drawing.Size(32, 500);
+      this.buttonPanel.TabIndex = 2;
       // 
       // ChartControl
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.BackColor = System.Drawing.SystemColors.Control;
+      this.Controls.Add(this.buttonPanel);
       this.Controls.Add(this.pictureBox);
+      this.DoubleBuffered = true;
       this.Name = "ChartControl";
-      this.Size = new System.Drawing.Size(266, 236);
-      this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ChartControl_KeyDown);
+      this.Size = new System.Drawing.Size(720, 500);
+      this.Load += new System.EventHandler(this.ChartControl_Load);
+      this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnKeyDown);
+      this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OnKeyUp);
       ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit();
       this.pictureBoxContextMenuStrip.ResumeLayout(false);
+      this.buttonPanel.ResumeLayout(false);
       this.ResumeLayout(false);
 
@@ -209,8 +267,5 @@
     protected System.Windows.Forms.ToolTip toolTip;
     protected System.Windows.Forms.ContextMenuStrip pictureBoxContextMenuStrip;
-    protected System.Windows.Forms.ToolStripMenuItem moveToolStripMenuItem;
-    protected System.Windows.Forms.ToolStripMenuItem zoomToolStripMenuItem;
-    protected System.Windows.Forms.ToolStripMenuItem selectToolStripMenuItem;
-    protected System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
+    protected System.Windows.Forms.ToolStripMenuItem saveImageToolStripMenuItem;
     protected System.Windows.Forms.ToolStripMenuItem propertiesToolStripMenuItem;
     protected System.Windows.Forms.ToolStripMenuItem oneLayerUpToolStripMenuItem;
@@ -218,8 +273,12 @@
     protected System.Windows.Forms.ToolStripMenuItem intoForegroundToolStripMenuItem;
     protected System.Windows.Forms.ToolStripMenuItem intoBackgroundToolStripMenuItem;
+    protected System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
     protected System.Windows.Forms.ToolStripSeparator toolStripMenuItem2;
-    private System.Windows.Forms.ToolStripMenuItem saveImageToolStripMenuItem;
-    private System.Windows.Forms.SaveFileDialog saveFileDialog;
-
+    protected System.Windows.Forms.RadioButton selectButton;
+    protected System.Windows.Forms.RadioButton moveButton;
+    protected System.Windows.Forms.RadioButton zoomInButton;
+    protected System.Windows.Forms.RadioButton zoomOutButton;
+    protected System.Windows.Forms.FlowLayoutPanel buttonPanel;
+    protected System.Windows.Forms.SaveFileDialog saveFileDialog;
   }
 }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.cs	(revision 13045)
@@ -21,275 +21,393 @@
 
 using System;
-using System.Collections.Generic;
+using System.ComponentModel;
 using System.Drawing;
 using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
+using System.Linq;
 using System.Windows.Forms;
 
 namespace HeuristicLab.Visualization {
   public partial class ChartControl : UserControl {
-    public Bitmap Bitmap { get; private set; }
-
-    private bool renderingRequired;
-    private Point buttonDownPoint;
-    private Point mousePosition;
-    private int mouseClickCount;
-
-    private Chart myChart;
-    public Chart Chart {
-      get { return myChart; }
+    public static readonly Cursor HandCursor = new Cursor(typeof(ChartControl), "Resources.Hand.cur");
+    public static readonly Cursor ZoomOutCursor = new Cursor(typeof(ChartControl), "Resources.ZoomOut.cur");
+    public static readonly Cursor ZoomInCursor = new Cursor(typeof(ChartControl), "Resources.ZoomIn.cur");
+
+    protected Bitmap Picture;
+    protected bool RenderingRequired;
+    protected Point ButtonDownPoint;
+    protected Point PreviousLocation;
+    protected int MouseClickCount;
+
+    protected bool SuppressEvents { get; set; }
+    private bool SuppressRender { get; set; }
+
+    private IChart chart;
+    public IChart Chart {
+      get { return chart; }
+      protected set {
+        if (chart != null) DeregisterChartEvents(chart);
+        chart = value;
+        chart.Enabled = Enabled;
+        pictureBox.Enabled = Enabled;
+        if (chart != null) RegisterChartEvents(chart);
+      }
+    }
+
+    private ChartMode mode;
+    public ChartMode Mode {
+      get { return mode; }
       set {
-        if (myChart != null) myChart.Update -= new EventHandler(myChart_Update);
-        myChart = value;
-        if (myChart != null) {
-          myChart.Update += new EventHandler(myChart_Update);
-          SetMode(Chart.Mode);
-        }
-        GenerateImage();
-      }
-    }
-    private bool myScaleOnResize;
-    public bool ScaleOnResize {
-      get { return myScaleOnResize; }
-      set { myScaleOnResize = value; }
-    }
-
-    public ChartControl() {
+        if (mode == value) return;
+        mode = value;
+        SetButtonCheckedSate();
+        SetPictureBoxCursor(GetPictureBoxCursor());
+        OnModeChanged();
+        if (chart != null) {
+          GenerateImage();
+        }
+      }
+    }
+
+    public bool ScaleOnResize { get; set; }
+
+    public bool IsRenderingPaused { get { return SuppressRender; } }
+
+    public ChartControl() : this(null) { }
+    public ChartControl(IChart defaultChart) {
       InitializeComponent();
-      myScaleOnResize = true;
+      ScaleOnResize = true;
+      Mode = ChartMode.Select;
+      Chart = defaultChart ?? new Chart(0, 0, Width, Height);
+    }
+
+    public void SuspendRendering() {
+      SuppressRender = true;
+    }
+
+    public void ResumeRendering() {
+      SuppressRender = false;
+      if (Chart != null) GenerateImage();
+    }
+
+    protected virtual void SetButtonCheckedSate() {
+      selectButton.Checked = false;
+      moveButton.Checked = false;
+      zoomInButton.Checked = false;
+      zoomOutButton.Checked = false;
+      if (mode == ChartMode.Select) selectButton.Checked = true;
+      else if (mode == ChartMode.Move) moveButton.Checked = true;
+      else if (mode == ChartMode.ZoomIn) zoomInButton.Checked = true;
+      else if (mode == ChartMode.ZoomOut) zoomOutButton.Checked = true;
+    }
+
+    protected virtual void SetPictureBoxCursor(Cursor cursor) {
+      if (pictureBox.Cursor != cursor) pictureBox.Cursor = cursor;
+    }
+
+    protected virtual Cursor GetPictureBoxCursor() {
+      if (Mode == ChartMode.Select)
+        return DefaultCursor;
+      if (Mode == ChartMode.Move)
+        return HandCursor;
+      if (Mode == ChartMode.ZoomIn)
+        return ZoomInCursor;
+      if (Mode == ChartMode.ZoomOut)
+        return ZoomOutCursor;
+      return null;
+    }
+
+    protected virtual void RegisterChartEvents(IChart chart) {
+      chart.RedrawRequired += ChartOnUpdateRequired;
+    }
+
+    protected virtual void DeregisterChartEvents(IChart chart) {
+      chart.RedrawRequired -= ChartOnUpdateRequired;
+    }
+
+    protected virtual void ChartOnUpdateRequired(object sender, EventArgs e) {
+      if (SuppressRender) return;
       GenerateImage();
     }
 
-    private void myChart_Update(object sender, EventArgs e) {
-      GenerateImage();
-    }
-    private void pictureBox_SizeChanged(object sender, EventArgs e) {
+    #region PictureBox Events
+    protected virtual void PictureBoxOnSizeChanged(object sender, EventArgs e) {
+      if (Chart == null) return;
       if (ScaleOnResize) {
         if ((pictureBox.Width > 0) && (pictureBox.Height > 0) && (Chart != null)) {
-          PointD point = Chart.TransformPixelToWorld(new Point(pictureBox.Width, Chart.SizeInPixels.Height - pictureBox.Height));
-          Chart.SetPosition(Chart.LowerLeft, point);
+          var point = Chart.TransformPixelToWorld(new Point(pictureBox.Width, Chart.SizeInPixels.Height - pictureBox.Height));
+          var diff = Chart.UpperRight - point;
+          Chart.SetPosition(new PointD(Chart.LowerLeft.X + (diff.DX / 2.0), Chart.LowerLeft.Y + (diff.DY / 2.0)),
+            new PointD(Chart.UpperRight.X - (diff.DX / 2.0), Chart.UpperRight.Y - (diff.DY / 2.0)));
         }
       }
       GenerateImage();
     }
-    private void pictureBox_VisibleChanged(object sender, EventArgs e) {
-      if (pictureBox.Visible && renderingRequired) {
-        GenerateImage();
-      }
-    }
-
-    private void pictureBox_MouseDown(object sender, MouseEventArgs e) {
-      buttonDownPoint = e.Location;
-      mouseClickCount = e.Clicks;
-    }
-    protected virtual void pictureBox_MouseUp(object sender, MouseEventArgs e) {
+
+    protected virtual void PictureBoxOnVisibleChanged(object sender, EventArgs e) {
+      if (pictureBox.Visible && RenderingRequired) GenerateImage();
+    }
+
+    protected virtual void PictureBoxOnMouseClick(object sender, MouseEventArgs e) {
+
+    }
+
+    protected virtual void PictureBoxOnMouseDoubleClick(object sender, MouseEventArgs e) {
+
+    }
+
+    protected virtual void OnKeyDown(object sender, KeyEventArgs e) {
+      if (Mode == ChartMode.Select) {
+        if (e.KeyCode == Keys.Delete) {
+          try {
+            SuspendRendering();
+            foreach (var primitive in Chart.Group.SelectedPrimitives)
+              Chart.Group.Remove(primitive);
+          } finally { ResumeRendering(); }
+        }
+      }
+    }
+
+    protected virtual void OnKeyUp(object sender, KeyEventArgs e) {
+
+    }
+
+    protected virtual void PictureBoxOnMouseWheel(object sender, MouseEventArgs e) {
+
+    }
+
+    protected virtual void PictureBoxOnMouseDown(object sender, MouseEventArgs e) {
+      if (Chart == null) return;
+      MouseClickCount = e.Clicks;
+      ButtonDownPoint = e.Location;
+      if (Mode != ChartMode.Select) return;
+
+      try {
+        SuspendRendering();
+        var worldLocation = Chart.TransformPixelToWorld(e.Location);
+        foreach (var sp in Chart.Group.SelectedPrimitives.Where(x => !x.ContainsPoint(worldLocation)))
+          sp.Selected = false;
+        var p = Chart.GetPrimitive(e.Location);
+        if (p != null) p.Selected = true;
+      } finally { ResumeRendering(); }
+    }
+
+    protected virtual void PictureBoxOnMouseUp(object sender, MouseEventArgs e) {
+      if (Chart == null) return;
       if (e.Button == MouseButtons.Left) {
-        if (Chart.Mode == ChartMode.Zoom) {
+        if (Mode == ChartMode.ZoomIn) {
+          //pictureBox.Refresh();
+          var lowerLeft = new Point(Math.Min(e.X, ButtonDownPoint.X), Math.Max(e.Y, ButtonDownPoint.Y));
+          var upperRight = new Point(Math.Max(e.X, ButtonDownPoint.X), Math.Min(e.Y, ButtonDownPoint.Y));
+          if ((lowerLeft.X != upperRight.X) && (lowerLeft.Y != upperRight.Y))
+            Chart.ZoomIn(lowerLeft, upperRight);
+          else Chart.ZoomIn(ButtonDownPoint);
+        } else if (Mode == ChartMode.ZoomOut) {
+          Chart.ZoomOut();
+        }
+      } else if (e.Button == MouseButtons.Right) {
+        propertiesToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1;
+        oneLayerUpToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1;
+        oneLayerDownToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1;
+        intoForegroundToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1;
+        intoBackgroundToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count() == 1;
+      } else if (e.Button == MouseButtons.Middle) {
+        if (Mode == ChartMode.Select) {
+          try {
+            SuspendRendering();
+            foreach (var p in Chart.Group.SelectedPrimitives)
+              p.Selected = false;
+          } finally { ResumeRendering(); }
+        } else if (Mode == ChartMode.Move || Mode == ChartMode.ZoomIn || Mode == ChartMode.ZoomOut) {
+          Chart.Unzoom();
+        }
+      }
+    }
+
+    protected virtual void PictureBoxOnMouseMove(object sender, MouseEventArgs e) {
+      if (InvokeRequired) {
+        Invoke((Action<Object, MouseEventArgs>)PictureBoxOnMouseMove, sender, e);
+        return;
+      }
+      var toolTipText = Chart.GetToolTipText(e.Location);
+      if (toolTip.GetToolTip(pictureBox) != toolTipText) toolTip.SetToolTip(pictureBox, toolTipText);
+
+      Cursor cursor;
+      if (Mode == ChartMode.Select) {
+        cursor = Chart.GetCursor(e.Location) ?? GetPictureBoxCursor();
+      } else cursor = GetPictureBoxCursor();
+      SetPictureBoxCursor(cursor);
+
+      try {
+        if (e.Button == MouseButtons.None) return;
+
+        if (Mode == ChartMode.Select && e.Button == MouseButtons.Left) {
+          var previousWorldLocation = Chart.TransformPixelToWorld(PreviousLocation);
+          var worldLocation = Chart.TransformPixelToWorld(e.Location);
+          var offset = worldLocation - previousWorldLocation;
+          try {
+            SuspendRendering();
+            foreach (var primitive in Chart.Group.SelectedPrimitives.Where(p => p.ContainsPoint(previousWorldLocation)))
+              primitive.Move(previousWorldLocation, offset);
+          } finally { ResumeRendering(); }
+        } else if (Mode == ChartMode.Move) {
+          var previousWorldLocation = Chart.TransformPixelToWorld(PreviousLocation);
+          var worldLocation = Chart.TransformPixelToWorld(e.Location);
+          var offset = worldLocation - previousWorldLocation;
+          Chart.Move(new Offset(-1 * offset.DX, -1 * offset.DY));
+        } else if (Mode == ChartMode.ZoomIn && e.Button == MouseButtons.Left) {
           pictureBox.Refresh();
-          // some code to preserve the aspect ratio when zooming
-          //       +---------------------+ (Width,Height)
-          //       |  (x1,y1)            |
-          //       |      +-------+      |
-          //       |      |       |      | 
-          //       |      |       |      |
-          //       |      +-------+      |
-          //       |             (x2,y2) |
-          // (0,0) +---------------------+
-          // 
-          double aspectRatio = (double)Width / Height;
-          double x1 = Math.Min(e.X, buttonDownPoint.X);
-          double y1 = Math.Max(e.Y, buttonDownPoint.Y);
-          double x2 = Math.Max(e.X, buttonDownPoint.X);
-          double y2 = Math.Min(e.Y, buttonDownPoint.Y);
-          // consider the relative ratios between the X and Y dimensions of the selection 
-          // area and the Width and Height, respectively. The "dominant" dimension is the 
-          // one with a ratio closer to 1, and it stays fixed while the other is adjusted 
-          // in order to preserve the aspect ratio
-          if ((x2 - x1) / Width > (y1 - y2) / Height) {
-            y1 = y2 + (x2 - x1) / aspectRatio;
-          } else {
-            x2 = aspectRatio * (y1 - y2) + x1;
+          using (var graphics = pictureBox.CreateGraphics()) {
+            using (var pen = new Pen(Color.Gray) { DashStyle = DashStyle.Dash }) {
+              graphics.DrawRectangle(pen,
+                Math.Min(e.X, ButtonDownPoint.X),
+                Math.Min(e.Y, ButtonDownPoint.Y),
+                Math.Abs(e.X - ButtonDownPoint.X),
+                Math.Abs(e.Y - ButtonDownPoint.Y));
+            }
           }
-          var lowerLeft = new Point((int)x1, (int)y1);
-          var upperRight = new Point((int)x2, (int)y2);
-          if ((lowerLeft.X != upperRight.X) && (lowerLeft.Y != upperRight.Y)) {
-            Chart.ZoomIn(lowerLeft, upperRight);
+        }
+      } finally {
+        PreviousLocation = e.Location;
+        pictureBox.Update();
+      }
+    }
+
+    protected virtual void PictureBoxOnMouseEnter(object sender, EventArgs e) {
+      if (InvokeRequired) {
+        Invoke((Action<Object, MouseEventArgs>)PictureBoxOnMouseEnter, sender, e);
+        return;
+      }
+      if (!Focused) this.pictureBox.Focus();
+    }
+
+    protected virtual void PictureBoxOnMouseLeave(object sender, EventArgs e) {
+
+    }
+
+    protected virtual void PictureBoxOnClick(object sender, EventArgs e) {
+      if (InvokeRequired) {
+        Invoke((Action<Object, MouseEventArgs>)PictureBoxOnClick, sender, e);
+        return;
+      }
+      if (!Focused) this.pictureBox.Focus();
+    }
+    #endregion
+
+    protected virtual void PictureBoxContextMenuStripOnOpening(object sender, CancelEventArgs e) { }
+
+    protected virtual void oneLayerUpToolStripMenuItem_Click(object sender, EventArgs e) {
+      if (Chart == null) return;
+      if (Chart.Group.SelectedPrimitives.Count() == 1) {
+        Chart.OneLayerUp(Chart.Group.SelectedPrimitives.First());
+      }
+    }
+    protected virtual void oneLayerDownToolStripMenuItem_Click(object sender, EventArgs e) {
+      if (Chart == null) return;
+      if (Chart.Group.SelectedPrimitives.Count() == 1) {
+        Chart.OneLayerDown(Chart.Group.SelectedPrimitives.First());
+      }
+    }
+    protected virtual void intoForegroundToolStripMenuItem_Click(object sender, EventArgs e) {
+      if (Chart == null) return;
+      if (Chart.Group.SelectedPrimitives.Count() == 1) {
+        Chart.IntoForeground(Chart.Group.SelectedPrimitives.First());
+      }
+    }
+    protected virtual void intoBackgroundToolStripMenuItem_Click(object sender, EventArgs e) {
+      if (Chart == null) return;
+      if (Chart.Group.SelectedPrimitives.Count() == 1) {
+        Chart.IntoBackground(Chart.Group.SelectedPrimitives.First());
+      }
+    }
+    protected virtual void propertiesToolStripMenuItem_Click(object sender, EventArgs e) {
+      if (Chart == null) return;
+      if (Chart.Group.SelectedPrimitives.Count() == 1) {
+        using (var dialog = new PropertiesDialog(Chart.Group.SelectedPrimitives.First())) {
+          dialog.ShowDialog(this);
+        }
+      }
+    }
+    protected virtual void saveImageToolStripMenuItem_Click(object sender, EventArgs e) {
+      if (Picture == null) return;
+
+      if (saveFileDialog.ShowDialog() == DialogResult.OK) {
+        var format = ImageFormat.Bmp;
+        var filename = saveFileDialog.FileName.ToLower();
+        if (filename.EndsWith("emf")) {
+          using (var g1 = Graphics.FromImage(Picture)) {
+            var rectangle = new System.Drawing.Rectangle(0, 0, Picture.Width, Picture.Height);
+            using (var metafile = new Metafile(filename, g1.GetHdc(), rectangle, MetafileFrameUnit.Pixel, EmfType.EmfPlusDual))
+            using (var g2 = Graphics.FromImage(metafile))
+              Chart.Render(g2, pictureBox.Width, pictureBox.Height);
           }
-        }
-      } else if (e.Button == MouseButtons.Right) {
-        propertiesToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count == 1;
-        oneLayerUpToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count == 1;
-        oneLayerDownToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count == 1;
-        intoForegroundToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count == 1;
-        intoBackgroundToolStripMenuItem.Enabled = Chart.Group.SelectedPrimitives.Count == 1;
-      } else if (e.Button == MouseButtons.Middle) {
-        if (Chart.Mode == ChartMode.Zoom) {
-          if (mouseClickCount == 1) Chart.ZoomOut();
-          else if (mouseClickCount == 2) Chart.Unzoom();
-        }
-      }
-      if (buttonDownPoint == e.Location) {
-        if (mouseClickCount == 1)
-          Chart.MouseClick(e.Location, e.Button);
-        else if (mouseClickCount == 2)
-          Chart.MouseDoubleClick(e.Location, e.Button);
-      }
-    }
-
-    protected virtual void pictureBox_MouseMove(object sender, MouseEventArgs e) {
-      var text = Chart.GetToolTipText(e.Location);
-      if (toolTip.GetToolTip(pictureBox) != text)
-        toolTip.SetToolTip(pictureBox, text);
-      Cursor cursor = Chart.GetCursor(e.Location);
-      if (cursor != null) pictureBox.Cursor = cursor;
-      else pictureBox.Cursor = Cursors.Default;
-
-      if (e.Button != MouseButtons.None) {
-        if ((Chart.Mode == ChartMode.Zoom) && (e.Button == MouseButtons.Left)) {
-          pictureBox.Refresh();
-          Graphics graphics = pictureBox.CreateGraphics();
-          Pen pen = new Pen(Color.Gray);
-          pen.DashStyle = DashStyle.Dash;
-          graphics.DrawRectangle(pen,
-                                 Math.Min(e.X, buttonDownPoint.X),
-                                 Math.Min(e.Y, buttonDownPoint.Y),
-                                 Math.Abs(e.X - buttonDownPoint.X),
-                                 Math.Abs(e.Y - buttonDownPoint.Y));
-          pen.Dispose();
-          graphics.Dispose();
-        }
-        Chart.MouseDrag(mousePosition, e.Location, e.Button);
-      } else {
-        Chart.MouseMove(mousePosition, e.Location);
-      }
-      mousePosition = e.Location;
-    }
-
-    protected virtual void pictureBox_MouseWheel(object sender, MouseEventArgs e) {
-      if (e.Delta > 0) {
-        // zoom in
-        double x1 = Chart.LowerLeft.X + Chart.Size.Width / 8;
-        double y1 = Chart.LowerLeft.Y + Chart.Size.Height / 8;
-        double x2 = Chart.UpperRight.X - Chart.Size.Width / 8;
-        double y2 = Chart.UpperRight.Y - Chart.Size.Height / 8;
-        Chart.ZoomIn(x1, y1, x2, y2);
-      } else if (e.Delta < 0) {
-        // zoom out
-        double x1 = Chart.LowerLeft.X - Chart.Size.Width / 8;
-        double y1 = Chart.LowerLeft.Y - Chart.Size.Height / 8;
-        double x2 = Chart.UpperRight.X + Chart.Size.Width / 8;
-        double y2 = Chart.UpperRight.Y + Chart.Size.Height / 8;
-        Chart.ZoomIn(x1, y1, x2, y2);
-      }
-    }
-
-    private void pictureBox_MouseEnter(object sender, EventArgs e) {
-      pictureBox.Focus();
-    }
-
-    protected virtual void ChartControl_KeyDown(object sender, KeyEventArgs e) {
-      if (Chart.Mode == ChartMode.Select) {
-        if (e.KeyCode == Keys.Delete) {
-          IList<IPrimitive> selected = Chart.Group.SelectedPrimitives;
-          Chart.UpdateEnabled = false;
-          foreach (IPrimitive primitive in selected)
-            Chart.Group.Remove(primitive);
-          Chart.UpdateEnabled = true;
-          Chart.EnforceUpdate();
-        }
-      }
-    }
-
-    private void moveToolStripMenuItem_Click(object sender, EventArgs e) {
-      SetMode(ChartMode.Move);
-    }
-    private void zoomToolStripMenuItem_Click(object sender, EventArgs e) {
-      SetMode(ChartMode.Zoom);
-    }
-    private void selectToolStripMenuItem_Click(object sender, EventArgs e) {
-      SetMode(ChartMode.Select);
-    }
-    private void oneLayerUpToolStripMenuItem_Click(object sender, EventArgs e) {
-      if (Chart.Group.SelectedPrimitives.Count == 1) {
-        Chart.Group.SelectedPrimitives[0].OneLayerUp();
-      }
-    }
-    private void oneLayerDownToolStripMenuItem_Click(object sender, EventArgs e) {
-      if (Chart.Group.SelectedPrimitives.Count == 1) {
-        Chart.Group.SelectedPrimitives[0].OneLayerDown();
-      }
-    }
-    private void intoForegroundToolStripMenuItem_Click(object sender, EventArgs e) {
-      if (Chart.Group.SelectedPrimitives.Count == 1) {
-        Chart.Group.SelectedPrimitives[0].IntoForeground();
-      }
-    }
-    private void intoBackgroundToolStripMenuItem_Click(object sender, EventArgs e) {
-      if (Chart.Group.SelectedPrimitives.Count == 1) {
-        Chart.Group.SelectedPrimitives[0].IntoBackground();
-      }
-    }
-    private void propertiesToolStripMenuItem_Click(object sender, EventArgs e) {
-      if (Chart.Group.SelectedPrimitives.Count == 1) {
-        PropertiesDialog dialog = new PropertiesDialog(Chart.Group.SelectedPrimitives[0]);
-        dialog.ShowDialog(this);
-        dialog.Dispose();
-      }
-    }
-
-    private void GenerateImage() {
+        } else {
+          using (var graphics = Graphics.FromImage(Picture))
+            Chart.Render(graphics, Picture.Width, Picture.Height);
+
+          if (filename.EndsWith("jpg")) {
+            format = ImageFormat.Jpeg;
+          } else if (filename.EndsWith("gif")) {
+            format = ImageFormat.Gif;
+          } else if (filename.EndsWith("png")) {
+            format = ImageFormat.Png;
+          } else if (filename.EndsWith("tif")) {
+            format = ImageFormat.Tiff;
+          }
+
+          Picture.Save(saveFileDialog.FileName, format);
+        }
+      }
+    }
+
+    protected virtual void GenerateImage() {
+      if (InvokeRequired) {
+        Invoke((Action)GenerateImage);
+        return;
+      }
       if (!Visible) {
-        renderingRequired = true;
+        RenderingRequired = true;
       } else {
         if ((pictureBox.Width == 0) || (pictureBox.Height == 0)) {
-          Bitmap = null;
+          pictureBox.Image = pictureBox.ErrorImage;
+          if (Picture != null) Picture.Dispose();
+          Picture = null;
         } else {
-          Bitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
-          if (Chart != null) {
-            Graphics graphics = Graphics.FromImage(Bitmap);
+          if (Picture != null) Picture.Dispose();
+          Picture = new Bitmap(pictureBox.Width, pictureBox.Height);
+          using (var graphics = Graphics.FromImage(Picture)) {
             graphics.SetClip(new System.Drawing.Rectangle(0, 0, pictureBox.Width, pictureBox.Height));
             Chart.Render(graphics, pictureBox.Width, pictureBox.Height);
-            graphics.Dispose();
           }
         }
-        if (pictureBox.Image != null) pictureBox.Image.Dispose();
-        pictureBox.Image = Bitmap;
-        renderingRequired = false;
-      }
-    }
-
-    private void SetMode(ChartMode mode) {
-      moveToolStripMenuItem.Checked = false;
-      zoomToolStripMenuItem.Checked = false;
-      selectToolStripMenuItem.Checked = false;
-      if (mode == ChartMode.Move) moveToolStripMenuItem.Checked = true;
-      else if (mode == ChartMode.Zoom) zoomToolStripMenuItem.Checked = true;
-      else if (mode == ChartMode.Select) selectToolStripMenuItem.Checked = true;
-      Chart.Mode = mode;
-    }
-
-    private void SaveImageAsBitmap(string filename) {
-      if (Bitmap == null) throw new Exception("Bitmap is null.");
-      Bitmap.Save(filename);
-    }
-
-    private void SaveImageAsEmf(string filename) {
-      using (Graphics g = CreateGraphics()) {
-        using (Metafile file = new Metafile(filename, g.GetHdc())) {
-          using (Graphics emfFile = Graphics.FromImage(file)) {
-            Chart.Render(emfFile, pictureBox.Width, pictureBox.Height);
-          }
-        }
-        g.ReleaseHdc();
-      }
-    }
-
-    private void saveImageToolStripMenuItem_Click(object sender, EventArgs e) {
-      if (saveFileDialog.ShowDialog() == DialogResult.OK) {
-        string filename = saveFileDialog.FileName.ToLower();
-        if (filename.EndsWith("bmp")) SaveImageAsBitmap(filename);
-        else if (filename.EndsWith("emf")) SaveImageAsEmf(filename);
-        else SaveImageAsBitmap(filename);
-      }
+        pictureBox.Image = Picture;
+        RenderingRequired = false;
+      }
+    }
+
+    public event EventHandler ModeChanged;
+    protected void OnModeChanged() {
+      var handler = ModeChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    protected virtual void SelectButtonOnCheckedChanged(object sender, EventArgs e) {
+      if (selectButton.Checked) Mode = ChartMode.Select;
+    }
+
+    protected virtual void MoveButtonOnCheckedChanged(object sender, EventArgs e) {
+      if (moveButton.Checked) Mode = ChartMode.Move;
+    }
+
+    protected virtual void ZoomInButtonOnCheckedChanged(object sender, EventArgs e) {
+      if (zoomInButton.Checked) Mode = ChartMode.ZoomIn;
+    }
+
+    protected virtual void ZoomOutButtonOnCheckedChanged(object sender, EventArgs e) {
+      if (zoomOutButton.Checked) Mode = ChartMode.ZoomOut;
+    }
+
+    private void ChartControl_Load(object sender, EventArgs e) {
+      if (!SuppressRender) GenerateImage();
     }
   }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.resx
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartControl.resx	(revision 12537)
+++ 	(revision )
@@ -1,129 +1,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
-  <!-- 
-    Microsoft ResX Schema 
-    
-    Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
-    associated with the data types.
-    
-    Example:
-    
-    ... ado.net/XML headers & schema ...
-    <resheader name="resmimetype">text/microsoft-resx</resheader>
-    <resheader name="version">2.0</resheader>
-    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
-    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
-    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
-    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
-    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
-        <value>[base64 mime encoded serialized .NET Framework object]</value>
-    </data>
-    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
-        <comment>This is a comment</comment>
-    </data>
-                
-    There are any number of "resheader" rows that contain simple 
-    name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
-    mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
-    extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
-    read any of the formats listed below.
-    
-    mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
-            : and then encoded with base64 encoding.
-    
-    mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
-            : and then encoded with base64 encoding.
-
-    mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
-            : using a System.ComponentModel.TypeConverter
-            : and then encoded with base64 encoding.
-    -->
-  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-    <xsd:element name="root" msdata:IsDataSet="true">
-      <xsd:complexType>
-        <xsd:choice maxOccurs="unbounded">
-          <xsd:element name="metadata">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-              </xsd:sequence>
-              <xsd:attribute name="name" use="required" type="xsd:string" />
-              <xsd:attribute name="type" type="xsd:string" />
-              <xsd:attribute name="mimetype" type="xsd:string" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="assembly">
-            <xsd:complexType>
-              <xsd:attribute name="alias" type="xsd:string" />
-              <xsd:attribute name="name" type="xsd:string" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="data">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="resheader">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" />
-            </xsd:complexType>
-          </xsd:element>
-        </xsd:choice>
-      </xsd:complexType>
-    </xsd:element>
-  </xsd:schema>
-  <resheader name="resmimetype">
-    <value>text/microsoft-resx</value>
-  </resheader>
-  <resheader name="version">
-    <value>2.0</value>
-  </resheader>
-  <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <metadata name="pictureBoxContextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>102, 17</value>
-  </metadata>
-  <metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>17, 17</value>
-  </metadata>
-  <metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>311, 17</value>
-  </metadata>
-</root>
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartMode.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartMode.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/ChartMode.cs	(revision 13045)
@@ -21,8 +21,11 @@
 
 namespace HeuristicLab.Visualization {
-  public enum ChartMode {
-    Move,
-    Zoom,
-    Select
+  public class ChartMode {
+    protected ChartMode() { }
+
+    public static readonly ChartMode Select = new ChartMode();
+    public static readonly ChartMode Move = new ChartMode();
+    public static readonly ChartMode ZoomIn = new ChartMode();
+    public static readonly ChartMode ZoomOut = new ChartMode();
   }
 }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/HeuristicLab.Visualization-3.3.csproj
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/HeuristicLab.Visualization-3.3.csproj	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/HeuristicLab.Visualization-3.3.csproj	(revision 13045)
@@ -104,6 +104,13 @@
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="HeuristicLab.PluginInfrastructure-3.3">
-      <HintPath>C:\Program Files\HeuristicLab 3.3\HeuristicLab.PluginInfrastructure-3.3.dll</HintPath>
+    <Reference Include="HeuristicLab.Common.Resources-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Common.Resources-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.PluginInfrastructure-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.PluginInfrastructure-3.3.dll</HintPath>
+      <Private>False</Private>
     </Reference>
     <Reference Include="System" />
@@ -123,4 +130,10 @@
     <Compile Include="ChartMode.cs" />
     <Compile Include="AxisType.cs" />
+    <Compile Include="Primitives\Grid.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
     <Compile Include="Trees\LayoutEngines\BoxesLayoutEngine.cs" />
     <Compile Include="Trees\LayoutEngines\ILayoutEngine.cs" />
@@ -131,4 +144,5 @@
     <None Include="Plugin.cs.frame" />
     <Compile Include="Plugin.cs" />
+    <Compile Include="Interfaces\IGrid.cs" />
     <Compile Include="Interfaces\IGroup.cs" />
     <Compile Include="Interfaces\IChart.cs" />
@@ -159,15 +173,10 @@
   </ItemGroup>
   <ItemGroup>
-    <EmbeddedResource Include="ChartControl.resx">
-      <DependentUpon>ChartControl.cs</DependentUpon>
-      <SubType>Designer</SubType>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
-    <EmbeddedResource Include="PropertiesDialog.resx">
-      <DependentUpon>PropertiesDialog.cs</DependentUpon>
-      <SubType>Designer</SubType>
-    </EmbeddedResource>
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="ClassDiagram.cd" />
+  </ItemGroup>
+  <ItemGroup>
     <None Include="HeuristicLab.snk" />
     <None Include="Properties\AssemblyInfo.frame" />
@@ -190,4 +199,13 @@
     </BootstrapperPackage>
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\Hand.cur" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\ZoomIn.cur" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\ZoomOut.cur" />
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IChart.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IChart.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IChart.cs	(revision 13045)
@@ -27,5 +27,6 @@
 namespace HeuristicLab.Visualization {
   public interface IChart {
-    ChartMode Mode { get; set; }
+    bool Enabled { get; set; }
+
     PointD LowerLeft { get; }
     PointD UpperRight { get; }
@@ -35,5 +36,4 @@
     SizeD WorldToPixelRatio { get; }
     IGroup Group { get; }
-    bool UpdateEnabled { get; }
 
     PointD TransformPixelToWorld(Point point);
@@ -43,38 +43,27 @@
 
     void SetPosition(PointD lowerLeft, PointD upperRight);
-    void SetPosition(double x1, double y1, double x2, double y2);
     void Move(Offset delta);
-    void Move(double dx, double dy);
 
+    void ZoomIn(Point mousePosition);
     void ZoomIn(PointD lowerLeft, PointD upperRight);
-    void ZoomIn(double x1, double y1, double x2, double y2);
     void ZoomIn(Point lowerLeft, Point upperRight);
-    void ZoomIn(int x1, int y1, int x2, int y2);
     void ZoomOut();
     void Unzoom();
 
+    void IntoForeground(IPrimitive primitive);
+    void IntoBackground(IPrimitive primitive);
+    void OneLayerUp(IPrimitive primitive);
+    void OneLayerDown(IPrimitive primitive);
+
+    string GetToolTipText(Point point);
+    Cursor GetCursor(Point point);
+
     IPrimitive GetPrimitive(Point point);
-    IPrimitive GetPrimitive(int x, int y);
-    IList<IPrimitive> GetAllPrimitives(Point point);
-    IList<IPrimitive> GetAllPrimitives(int x, int y);
-
-    Cursor GetCursor(Point point);
-    Cursor GetCursor(int x, int y);
-    string GetToolTipText(Point point);
-    string GetToolTipText(int x, int y);
-
-    void MouseClick(Point point, MouseButtons button);
-    void MouseClick(int x, int y, MouseButtons button);
-    void MouseDoubleClick(Point point, MouseButtons button);
-    void MouseDoubleClick(int x, int y, MouseButtons button);
-    void MouseMove(Point start, Point end);
-    void MouseMove(int x1, int y1, int x2, int y2);
-    void MouseDrag(Point start, Point end, MouseButtons button);
-    void MouseDrag(int x1, int y1, int x2, int y2, MouseButtons button);
+    IEnumerable<IPrimitive> GetAllPrimitives(Point point);
 
     void Render(Graphics graphics, int width, int height);
 
-    event EventHandler Update;
-    void EnforceUpdate();
+    event EventHandler RedrawRequired;
+    void RaiseRedrawRequired();
   }
 }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IGrid.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IGrid.cs	(revision 13045)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IGrid.cs	(revision 13045)
@@ -0,0 +1,12 @@
+﻿
+namespace HeuristicLab.Visualization {
+  public interface IGrid {
+    PointD Origin { get; set; }
+    double XPrecision { get; }
+    double YPrecision { get; }
+
+    PointD GetBottomLeftGridPoint(PointD point);
+    PointD GetUpperRightGridPoint(PointD point);
+    PointD GetNearestGridPoint(PointD point);
+  }
+}
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IGroup.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IGroup.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IGroup.cs	(revision 13045)
@@ -20,13 +20,11 @@
 #endregion
 
-using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
-using System.Text;
 
 namespace HeuristicLab.Visualization {
-  public interface IGroup : IPrimitive {
-    ReadOnlyCollection<IPrimitive> Primitives { get; }
-    ReadOnlyCollection<IPrimitive> SelectedPrimitives { get; }
+  public interface IGroup : IPrimitive, IEnumerable<IPrimitive> {
+    IEnumerable<IPrimitive> Primitives { get; }
+    IEnumerable<IPrimitive> SelectedPrimitives { get; }
 
     void Add(IPrimitive primitive);
@@ -37,7 +35,5 @@
 
     IPrimitive GetPrimitive(PointD point);
-    IPrimitive GetPrimitive(double x, double y);
-    IList<IPrimitive> GetAllPrimitives(PointD point);
-    IList<IPrimitive> GetAllPrimitives(double x, double y);
+    IEnumerable<IPrimitive> GetAllPrimitives(PointD point);
 
     void OneLayerUp(IPrimitive primitive);
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IPrimitive.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IPrimitive.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Interfaces/IPrimitive.cs	(revision 13045)
@@ -21,6 +21,4 @@
 
 using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Drawing;
 using System.Windows.Forms;
@@ -28,9 +26,6 @@
 namespace HeuristicLab.Visualization {
   public interface IPrimitive {
-    IChart Chart { get; }
-    IGroup Group { get; set; }
     Pen Pen { get; set; }
     Brush Brush { get; set; }
-    bool UpdateEnabled { get; }
     bool Selected { get; set; }
     string ToolTipText { get; set; }
@@ -38,27 +33,12 @@
 
     void Move(Offset delta);
-    void Move(double dx, double dy);
+    void Move(PointD point, Offset delta);
+
+    void SnapToGrid(IGrid grid);
+    void SnapToGrid(PointD point, IGrid grid);
 
     bool ContainsPoint(PointD point);
-    bool ContainsPoint(double x, double y);
-
     Cursor GetCursor(PointD point);
-    Cursor GetCursor(double x, double y);
     string GetToolTipText(PointD point);
-    string GetToolTipText(double x, double y);
-
-    void OneLayerUp();
-    void OneLayerDown();
-    void IntoForeground();
-    void IntoBackground();
-
-    void MouseClick(PointD point, MouseButtons button);
-    void MouseClick(double x, double y, MouseButtons button);
-    void MouseDoubleClick(PointD point, MouseButtons button);
-    void MouseDoubleClick(double x, double y, MouseButtons button);
-    void MouseMove(PointD point, Offset offset);
-    void MouseMove(double x, double y, double dx, double dy);
-    void MouseDrag(PointD point, Offset offset, MouseButtons button);
-    void MouseDrag(double x, double y, double dx, double dy, MouseButtons button);
 
     void PreDraw(Graphics graphics);
@@ -66,6 +46,5 @@
     void PostDraw(Graphics graphics);
 
-    event EventHandler Update;
-    void EnforceUpdate();
+    event EventHandler RedrawRequired;
   }
 }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Plugin.cs.frame
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Plugin.cs.frame	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Plugin.cs.frame	(revision 13045)
@@ -26,6 +26,7 @@
 
 namespace HeuristicLab.Visualization {
-  [Plugin("HeuristicLab.Visualization", "3.3.1.$WCREV$")]
+  [Plugin("HeuristicLab.Visualization", "3.3.12.$WCREV$")]
   [PluginFile("HeuristicLab.Visualization-3.3.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.Common.Resources", "3.3")]
   public class HeuristicLabVisualizationPlugin : PluginBase { }
 }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Axis.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Axis.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Axis.cs	(revision 13045)
@@ -21,30 +21,23 @@
 
 using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Drawing;
 
 namespace HeuristicLab.Visualization {
   public class Axis : AxisPrimitiveBase {
+    protected const int PixelsPerInterval = 100;
+
     public Axis(IChart chart, PointD point, AxisType axisType)
       : base(chart, point, axisType) {
-    }
-    public Axis(IChart chart, double x, double y, AxisType axisType)
-      : this(chart, new PointD(x, y), axisType) {
     }
     public Axis(IChart chart, PointD point, AxisType axisType, Pen pen, Brush brush)
       : base(chart, point, axisType, pen, brush) {
     }
-    public Axis(IChart chart, double x, double y, AxisType axisType, Pen pen, Brush brush)
-      : this(chart, new PointD(x, y), axisType, pen, brush) {
-    }
 
     public override void Draw(Graphics graphics) {
-      int pixelsPerInterval = 100;
-      Font axisValuesFont = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular, GraphicsUnit.Pixel);
-      Font axisLabelsFont = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Regular, GraphicsUnit.Pixel);
+      var axisValuesFont = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular, GraphicsUnit.Pixel);
+      var axisLabelsFont = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Regular, GraphicsUnit.Pixel);
 
       if ((AxisType & AxisType.Horizontal) == AxisType.Horizontal) {
-        int intervals = Chart.SizeInPixels.Width / pixelsPerInterval;
+        int intervals = Chart.SizeInPixels.Width / PixelsPerInterval;
 
         if (intervals > 0) {
@@ -57,5 +50,5 @@
 
           double x = Math.Floor(Chart.LowerLeft.X / step) * step;
-          PointD current = new PointD(x, Point.Y);
+          var current = new PointD(x, Point.Y);
           while (current.X <= Chart.UpperRight.X) {
             Point p = Chart.TransformWorldToPixel(current);
@@ -83,5 +76,5 @@
       }
       if ((AxisType & AxisType.Vertical) == AxisType.Vertical) {
-        int intervals = Chart.SizeInPixels.Height / pixelsPerInterval;
+        int intervals = Chart.SizeInPixels.Height / PixelsPerInterval;
 
         if (intervals > 0) {
@@ -94,5 +87,5 @@
 
           double y = Math.Floor(Chart.LowerLeft.Y / step) * step;
-          PointD current = new PointD(Point.X, y);
+          var current = new PointD(Point.X, y);
           while (current.Y <= Chart.UpperRight.Y) {
             Point p = Chart.TransformWorldToPixel(current);
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/AxisPrimitiveBase.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/AxisPrimitiveBase.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/AxisPrimitiveBase.cs	(revision 13045)
@@ -20,7 +20,4 @@
 #endregion
 
-using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Drawing;
 
@@ -41,5 +38,5 @@
         if (value != myShowGrid) {
           myShowGrid = value;
-          OnUpdate();
+          OnRedrawRequired();
         }
       }
@@ -51,5 +48,5 @@
         if (value != myHorizontalLabel) {
           myHorizontalLabel = value;
-          OnUpdate();
+          OnRedrawRequired();
         }
       }
@@ -61,5 +58,5 @@
         if (value != myVerticalLabel) {
           myVerticalLabel = value;
-          OnUpdate();
+          OnRedrawRequired();
         }
       }
@@ -67,13 +64,5 @@
 
     protected AxisPrimitiveBase(IChart chart, PointD point, AxisType axisType)
-      : base(chart) {
-      Brush = Brushes.Black;
-      myPoint = point;
-      myAxisType = axisType;
-      myShowGrid = true;
-    }
-    protected AxisPrimitiveBase(IChart chart, double x, double y, AxisType axisType)
-      : this(chart, new PointD(x, y), axisType) {
-    }
+      : this(chart, point, axisType, Pens.Black, Brushes.Black) { }
     protected AxisPrimitiveBase(IChart chart, PointD point, AxisType axisType, Pen pen, Brush brush)
       : base(chart, pen, brush) {
@@ -82,21 +71,28 @@
       myShowGrid = true;
     }
-    protected AxisPrimitiveBase(IChart chart, double x, double y, AxisType axisType, Pen pen, Brush brush)
-      : this(chart, new PointD(x, y), axisType, pen, brush) {
-    }
 
     public virtual void SetPosition(PointD point) {
       myPoint = point;
-      OnUpdate();
+      OnRedrawRequired();
     }
-    public void SetPosition(double x, double y) {
-      SetPosition(new PointD(x, y));
-    }
+
     public override void Move(Offset delta) {
       SetPosition(Point + delta);
     }
 
+    public override void Move(PointD point, Offset delta) {
+      Move(delta);
+    }
+
+    public override void SnapToGrid(IGrid grid) {
+      Move(grid.GetBottomLeftGridPoint(myPoint) - myPoint);
+    }
+
+    public override void SnapToGrid(PointD point, IGrid grid) {
+      SnapToGrid(grid);
+    }
+
     public override bool ContainsPoint(PointD point) {
-      SizeD size = Chart.TransformPixelToWorld(new Size(5, 5));
+      var size = Chart.TransformPixelToWorld(new Size(5, 5));
       bool result = false;
       if ((AxisType & AxisType.Horizontal) == AxisType.Horizontal)
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Ellipse.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Ellipse.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Ellipse.cs	(revision 13045)
@@ -28,12 +28,6 @@
       : base(chart, lowerLeft, upperRight) {
     }
-    public Ellipse(IChart chart, double x1, double y1, double x2, double y2)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2)) {
-    }
     public Ellipse(IChart chart, PointD lowerLeft, PointD upperRight, Pen pen, Brush brush)
       : base(chart, lowerLeft, upperRight, pen, brush) {
-    }
-    public Ellipse(IChart chart, double x1, double y1, double x2, double y2, Pen pen, Brush brush)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2), pen, brush) {
     }
 
@@ -67,6 +61,6 @@
 
     public override void Draw(Graphics graphics) {
-      Point p = Chart.TransformWorldToPixel(new PointD(LowerLeft.X, LowerLeft.Y + Size.Height));
-      Size s = Chart.TransformWorldToPixel(Size);
+      var p = Chart.TransformWorldToPixel(new PointD(LowerLeft.X, LowerLeft.Y + Size.Height));
+      var s = Chart.TransformWorldToPixel(Size);
       if (Brush != null)
         graphics.FillEllipse(Brush, p.X, p.Y, s.Width, s.Height);
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizeCircle.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizeCircle.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizeCircle.cs	(revision 13045)
@@ -27,12 +27,6 @@
       : base(chart, point, new Size(diameter, diameter)) {
     }
-    public FixedSizeCircle(IChart chart, double x, double y, int diameter)
-      : this(chart, new PointD(x, y), diameter) {
-    }
     public FixedSizeCircle(IChart chart, PointD point, int diameter, Pen pen, Brush brush)
       : base(chart, point, new Size(diameter, diameter), pen, brush) {
-    }
-    public FixedSizeCircle(IChart chart, double x, double y, int diameter, Pen pen, Brush brush)
-      : this(chart, new PointD(x, y), diameter, pen, brush) {
     }
 
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizePrimitiveBase.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizePrimitiveBase.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizePrimitiveBase.cs	(revision 13045)
@@ -21,6 +21,4 @@
 
 using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Drawing;
 
@@ -41,7 +39,4 @@
       mySize = size;
     }
-    protected FixedSizePrimitiveBase(IChart chart, double x, double y, Size size)
-      : this(chart, new PointD(x, y), size) {
-    }
     protected FixedSizePrimitiveBase(IChart chart, PointD point, Size size, Pen pen, Brush brush)
       : base(chart, pen, brush) {
@@ -49,17 +44,28 @@
       mySize = size;
     }
-    protected FixedSizePrimitiveBase(IChart chart, double x, double y, Size size, Pen pen, Brush brush)
-      : this(chart, new PointD(x, y), size, pen, brush) {
-    }
 
     public virtual void SetPosition(PointD point) {
       myPoint = point;
-      OnUpdate();
+      OnRedrawRequired();
     }
-    public void SetPosition(double x, double y) {
+
+    public override void Move(Offset delta) {
+      SetPosition(Point + delta);
+    }
+
+    public override void Move(PointD point, Offset delta) {
+      Move(delta);
+    }
+
+    public override void SnapToGrid(IGrid grid) {
+      var bl = grid.GetBottomLeftGridPoint(Point);
+      var ur = grid.GetUpperRightGridPoint(Point);
+      var x = Math.Abs(bl.X - Point.X) <= Math.Abs(ur.X - Point.X) ? bl.X : ur.X;
+      var y = Math.Abs(bl.Y - Point.Y) <= Math.Abs(ur.Y - Point.Y) ? bl.Y : ur.Y;
       SetPosition(new PointD(x, y));
     }
-    public override void Move(Offset delta) {
-      SetPosition(Point + delta);
+
+    public override void SnapToGrid(PointD point, IGrid grid) {
+      SnapToGrid(grid);
     }
 
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizeRectangle.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizeRectangle.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/FixedSizeRectangle.cs	(revision 13045)
@@ -20,7 +20,4 @@
 #endregion
 
-using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Drawing;
 
@@ -30,12 +27,6 @@
       : base(chart, point, size) {
     }
-    public FixedSizeRectangle(IChart chart, double x, double y, Size size)
-      : this(chart, new PointD(x, y), size) {
-    }
     public FixedSizeRectangle(IChart chart, PointD point, Size size, Pen pen, Brush brush)
       : base(chart, point, size, pen, brush) {
-    }
-    public FixedSizeRectangle(IChart chart, double x, double y, Size size, Pen pen, Brush brush)
-      : this(chart, new PointD(x, y), size, pen, brush) {
     }
 
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Grid.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Grid.cs	(revision 13045)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Grid.cs	(revision 13045)
@@ -0,0 +1,114 @@
+﻿using System;
+using System.Drawing;
+
+namespace HeuristicLab.Visualization {
+  public class Grid : PrimitiveBase, IGrid {
+    protected const int CellSpacing = 10;
+
+    public virtual PointD Origin { get; set; }
+    public virtual PointD Offset { get; set; }
+    public virtual SizeD Size { get; set; }
+
+    public virtual double XPrecision { get; set; }
+    public virtual double YPrecision { get; set; }
+
+    public virtual PointD GetBottomLeftGridPoint(PointD point) {
+      var nx = (int)Math.Floor((point.X - Origin.X) / XPrecision);
+      var ny = (int)Math.Floor((point.Y - Origin.Y) / YPrecision);
+      return new PointD(Origin.X + nx * XPrecision, Origin.Y + ny * YPrecision);
+    }
+
+    public virtual PointD GetUpperRightGridPoint(PointD point) {
+      var nx = (int)Math.Ceiling((point.X - Origin.X) / XPrecision);
+      var ny = (int)Math.Ceiling((point.Y - Origin.Y) / YPrecision);
+      return new PointD(Origin.X + nx * XPrecision, Origin.Y + ny * YPrecision);
+    }
+
+    public virtual PointD GetNearestGridPoint(PointD point) {
+      var bl = GetBottomLeftGridPoint(point);
+      var ur = GetUpperRightGridPoint(point);
+      return new PointD(
+        Math.Abs(bl.X - point.X) <= Math.Abs(ur.X - point.X) ? bl.X : ur.X,
+        Math.Abs(bl.Y - point.Y) <= Math.Abs(ur.Y - point.Y) ? bl.Y : ur.Y);
+    }
+
+    public Grid(IChart chart, PointD offset, SizeD size)
+      : this(chart, offset, size, 1) { }
+    public Grid(IChart chart, PointD offset, SizeD size, double precision)
+      : base(chart) {
+      Origin = new PointD(0, 0);
+      Offset = offset;
+      Size = size;
+      XPrecision = precision;
+      YPrecision = precision;
+    }
+
+    public override bool ContainsPoint(PointD point) {
+      return false;
+    }
+
+    public override void Move(Offset delta) {
+      Offset += delta;
+    }
+
+    public override void Move(PointD point, Offset delta) {
+      Move(delta);
+    }
+
+    public override void SnapToGrid(IGrid grid) { }
+
+    public override void SnapToGrid(PointD point, IGrid grid) { }
+
+    public override void Draw(Graphics graphics) {
+      if (!(Size.Width > 0) || !(Size.Height > 0)) return;
+      graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
+      var pen = new Pen(Color.LightGray, 1.0f);
+      var pixelSize = Chart.TransformWorldToPixel(Size); // size of the drawing area in pixel coordinates
+
+      var numberOfXParallelLines = (int)Math.Floor(pixelSize.Height / (double)CellSpacing); // how many x parallel lines can be drawn
+      var numberOfYParallelLines = (int)Math.Floor(pixelSize.Width / (double)CellSpacing); // how many y parallel lines can be drawn
+      if (numberOfXParallelLines <= 0 || numberOfYParallelLines <= 0) return;
+      var cellWorldSize = new SizeD(Size.Width / numberOfYParallelLines, Size.Height / numberOfXParallelLines); // the cellSize in world coordinates
+      cellWorldSize.Width = Math.Pow(10, Math.Floor(Math.Log10(cellWorldSize.Width)));
+      cellWorldSize.Height = Math.Pow(10, Math.Floor(Math.Log10(cellWorldSize.Width)));
+      var cellPixelSize = Chart.TransformWorldToPixel(cellWorldSize);
+      while (cellPixelSize.Width < 25 || cellPixelSize.Height < 25) {
+        if (cellPixelSize.Width < 25) cellWorldSize.Width *= 2;
+        if (cellPixelSize.Height < 25) cellWorldSize.Height *= 2;
+        cellPixelSize = Chart.TransformWorldToPixel(cellWorldSize);
+        if (cellPixelSize.Width < 25) cellWorldSize.Width *= 2.5;
+        if (cellPixelSize.Height < 25) cellWorldSize.Height *= 2.5;
+        cellPixelSize = Chart.TransformWorldToPixel(cellWorldSize);
+      }
+      var firstX = Math.Floor(Offset.X / cellWorldSize.Width) * cellWorldSize.Width;
+      var firstY = Math.Floor(Offset.Y / cellWorldSize.Height) * cellWorldSize.Height;
+      double x = firstX, y = firstY;
+      var axisyLabel = new Font(FontFamily.GenericMonospace, 7.0f, FontStyle.Regular, GraphicsUnit.Point, 0);
+      var axisxLabel = new Font(FontFamily.GenericMonospace, 7.0f, FontStyle.Regular, GraphicsUnit.Point, 0, true);
+      var axisDrawing = new SolidBrush(Color.DarkGray);
+      try {
+        var bottom = Chart.TransformWorldToPixel(Size).Height;
+        while (x <= Offset.X + Size.Width) {
+          var start = Chart.TransformWorldToPixel(new PointD(x, firstY));
+          var end = Chart.TransformWorldToPixel(new PointD(x, Offset.Y + Size.Height));
+          graphics.DrawLine(pen, start, end);
+          var axis = x.ToString("0.##");
+          double stringwidth = graphics.MeasureString(axis, axisyLabel).Width;
+          graphics.DrawString(axis, axisyLabel, axisDrawing, end.X - (int)(stringwidth / 2.0), bottom - 15);
+          x += cellWorldSize.Width;
+        }
+        while (y <= Offset.Y + Size.Height) {
+          var start = Chart.TransformWorldToPixel(new PointD(firstX, y));
+          var end = Chart.TransformWorldToPixel(new PointD(Offset.X + Size.Width, y));
+          graphics.DrawLine(pen, start, end);
+          graphics.DrawString(y.ToString("0.##"), axisxLabel, axisDrawing, 0, start.Y);
+          y += cellWorldSize.Height;
+        }
+      } finally {
+        axisDrawing.Dispose();
+        axisxLabel.Dispose();
+        axisyLabel.Dispose();
+      }
+    }
+  }
+}
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Group.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Group.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Group.cs	(revision 13045)
@@ -21,6 +21,6 @@
 
 using System;
+using System.Collections;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.Drawing;
 using System.Linq;
@@ -29,27 +29,26 @@
 namespace HeuristicLab.Visualization {
   public class Group : PrimitiveBase, IGroup {
-    private LinkedList<IPrimitive> myPrimitives;
-    private HashSet<IPrimitive> myPrimitivesLookup;
-    public virtual ReadOnlyCollection<IPrimitive> Primitives {
-      get { return new ReadOnlyCollection<IPrimitive>(new List<IPrimitive>(myPrimitives)); }
-    }
-    public virtual ReadOnlyCollection<IPrimitive> SelectedPrimitives {
-      get {
-        List<IPrimitive> selected = new List<IPrimitive>();
-        foreach (IPrimitive primitive in myPrimitives) {
-          if (primitive.Selected) selected.Add(primitive);
-        }
-        return new ReadOnlyCollection<IPrimitive>(selected);
-      }
-    }
+    private readonly LinkedList<IPrimitive> primitives;
+    private readonly HashSet<IPrimitive> primitivesLookup;
+
+    public virtual IEnumerable<IPrimitive> Primitives {
+      get { return primitives.ToArray(); }
+    }
+    public virtual IEnumerable<IPrimitive> SelectedPrimitives {
+      get { return primitives.Where(x => x.Selected).ToArray(); }
+    }
+
     public override bool Selected {
       get { return base.Selected; }
       set {
-        bool updateEnabled = UpdateEnabled;
-        UpdateEnabled = false;
-        foreach (IPrimitive primitive in myPrimitives)
-          primitive.Selected = value;
-        UpdateEnabled = updateEnabled;
-        base.Selected = value;
+        SuppressEvents = true;
+        try {
+          foreach (var primitive in primitives)
+            primitive.Selected = value;
+          base.Selected = value;
+        } finally {
+          SuppressEvents = false;
+          OnRedrawRequired();
+        }
       }
     }
@@ -57,81 +56,104 @@
     public Group(IChart chart)
       : base(chart) {
-      myPrimitives = new LinkedList<IPrimitive>();
-      myPrimitivesLookup = new HashSet<IPrimitive>();
+      primitives = new LinkedList<IPrimitive>();
+      primitivesLookup = new HashSet<IPrimitive>();
     }
 
     public virtual void Add(IPrimitive primitive) {
-      if (Contains(primitive))
-        throw new ArgumentException("Primitive already added");
-
-      myPrimitives.AddFirst(primitive);
-      myPrimitivesLookup.Add(primitive);
-      primitive.Group = this;
-      primitive.Update += new EventHandler(primitive_Update);
-      OnUpdate();
+      if (Contains(primitive)) throw new ArgumentException("Primitive already added");
+      primitives.AddFirst(primitive);
+      primitivesLookup.Add(primitive);
+      primitive.RedrawRequired += PrimitiveOnRedrawRequired;
+      OnRedrawRequired();
     }
     public virtual void AddRange(IEnumerable<IPrimitive> primitives) {
-      foreach (IPrimitive primitive in primitives) {
-        if (Contains(primitive))
-          throw new ArgumentException("Primitive already added");
-
-        myPrimitives.AddFirst(primitive);
-        myPrimitivesLookup.Add(primitive);
-        primitive.Group = this;
-        primitive.Update += new EventHandler(primitive_Update);
-      }
-      OnUpdate();
-    }
+      foreach (var primitive in this.primitives) {
+        if (Contains(primitive)) throw new ArgumentException("Primitive already added");
+        this.primitives.AddFirst(primitive);
+        primitivesLookup.Add(primitive);
+        primitive.RedrawRequired += PrimitiveOnRedrawRequired;
+      }
+      OnRedrawRequired();
+    }
+
     public virtual bool Contains(IPrimitive primitive) {
-      return myPrimitivesLookup.Contains(primitive);
-    }
+      return primitivesLookup.Contains(primitive);
+    }
+
     public virtual bool Remove(IPrimitive primitive) {
-      if (myPrimitives.Remove(primitive)) {
-        primitive.Group = null;
-        primitive.Update -= new EventHandler(primitive_Update);
-        OnUpdate();
-        return true;
-      } else {
-        return false;
-      }
-    }
+      if (!primitives.Remove(primitive)) return false;
+      primitivesLookup.Remove(primitive);
+
+      primitive.RedrawRequired -= PrimitiveOnRedrawRequired;
+      OnRedrawRequired();
+      return true;
+    }
+
     public virtual void Clear() {
-      foreach (IPrimitive primitive in myPrimitives) {
-        primitive.Group = null;
-        primitive.Update -= new EventHandler(primitive_Update);
-      }
-      myPrimitives.Clear();
-      myPrimitivesLookup.Clear();
-      OnUpdate();
+      foreach (var primitive in primitives) {
+        primitive.RedrawRequired -= PrimitiveOnRedrawRequired;
+      }
+      primitives.Clear();
+      primitivesLookup.Clear();
+      OnRedrawRequired();
     }
 
     public virtual IPrimitive GetPrimitive(PointD point) {
-      return myPrimitives.FirstOrDefault(primitive => primitive.ContainsPoint(point));
-    }
-
-    public IPrimitive GetPrimitive(double x, double y) {
-      return GetPrimitive(new PointD(x, y));
-    }
-    public virtual IList<IPrimitive> GetAllPrimitives(PointD point) {
-      var primitives = new List<IPrimitive>();
-      foreach (var primitive in myPrimitives.Where(primitive => primitive.ContainsPoint(point))) {
-        primitives.Add(primitive);
+      return primitives.FirstOrDefault(primitive => primitive.ContainsPoint(point));
+    }
+
+    public virtual IEnumerable<IPrimitive> GetAllPrimitives(PointD point) {
+      var result = new List<IPrimitive>();
+      foreach (var primitive in primitives.Where(primitive => primitive.ContainsPoint(point))) {
+        result.Add(primitive);
         var group = primitive as IGroup;
         if (group != null)
-          primitives.AddRange(group.Primitives);
-      }
-      return primitives;
-    }
-    public IList<IPrimitive> GetAllPrimitives(double x, double y) {
-      return GetAllPrimitives(new PointD(x, y));
+          result.AddRange(group.GetAllPrimitives(point));
+      }
+      return result;
     }
 
     public override void Move(Offset delta) {
-      bool updateEnabled = UpdateEnabled;
-      UpdateEnabled = false;
-      foreach (IPrimitive primitive in myPrimitives)
-        primitive.Move(delta);
-      UpdateEnabled = updateEnabled;
-      OnUpdate();
+      SuppressEvents = true;
+      try {
+        foreach (var primitive in primitives)
+          primitive.Move(delta);
+      } finally {
+        SuppressEvents = false;
+        OnRedrawRequired();
+      }
+    }
+
+    public override void Move(PointD point, Offset delta) {
+      SuppressEvents = true;
+      try {
+        foreach (var primitive in primitives)
+          primitive.Move(point, delta);
+      } finally {
+        SuppressEvents = false;
+        OnRedrawRequired();
+      }
+    }
+
+    public override void SnapToGrid(IGrid grid) {
+      SuppressEvents = true;
+      try {
+        foreach (var primitive in primitives)
+          primitive.SnapToGrid(grid);
+      } finally {
+        SuppressEvents = false;
+        OnRedrawRequired();
+      }
+    }
+
+    public override void SnapToGrid(PointD point, IGrid grid) {
+      SuppressEvents = true;
+      try {
+        foreach (var primitive in primitives)
+          primitive.SnapToGrid(point, grid);
+      } finally {
+        SuppressEvents = false;
+        OnRedrawRequired();
+      }
     }
 
@@ -141,19 +163,17 @@
 
     public override Cursor GetCursor(PointD point) {
-      IPrimitive primitive = GetPrimitive(point);
-      if (primitive != null) return primitive.GetCursor(point);
-      else return base.GetCursor(point);
-    }
+      var primitive = GetPrimitive(point);
+      return primitive != null ? primitive.GetCursor(point) : base.GetCursor(point);
+    }
+
     public override string GetToolTipText(PointD point) {
-      IPrimitive primitive = GetPrimitive(point);
-      if (primitive != null) return primitive.GetToolTipText(point);
-      else return base.GetToolTipText(point);
+      var primitive = GetPrimitive(point);
+      return primitive != null ? primitive.GetToolTipText(point) : base.GetToolTipText(point);
     }
 
     public void OneLayerUp(IPrimitive primitive) {
-      if (!Contains(primitive))
-        throw new ArgumentException("Primitive not found");
-
-      var curr = myPrimitives.Find(primitive);
+      if (!Contains(primitive)) throw new ArgumentException("Primitive not found");
+
+      var curr = primitives.Find(primitive);
       if (curr == null) return;
       var prev = curr.Previous;
@@ -162,12 +182,10 @@
       prev.Value = temp;
 
-      OnUpdate();
-    }
-
+      OnRedrawRequired();
+    }
     public void OneLayerDown(IPrimitive primitive) {
-      if (!Contains(primitive))
-        throw new ArgumentException("Primitive not found");
-
-      var curr = myPrimitives.Find(primitive);
+      if (!Contains(primitive)) throw new ArgumentException("Primitive not found");
+
+      var curr = primitives.Find(primitive);
       if (curr == null) return;
       var next = curr.Next;
@@ -177,22 +195,19 @@
     }
     public void IntoForeground(IPrimitive primitive) {
-      if (!Contains(primitive))
-        throw new ArgumentException("Primitive not found");
-
-      myPrimitives.Remove(primitive);
-      myPrimitives.AddFirst(primitive);
-      OnUpdate();
+      if (!Contains(primitive)) throw new ArgumentException("Primitive not found");
+      primitives.Remove(primitive);
+      primitives.AddFirst(primitive);
+      OnRedrawRequired();
     }
     public void IntoBackground(IPrimitive primitive) {
-      if (!Contains(primitive))
-        throw new ArgumentException("Primitive not found");
-
-      myPrimitives.Remove(primitive);
-      myPrimitives.AddLast(primitive);
-      OnUpdate();
+      if (!Contains(primitive)) throw new ArgumentException("Primitive not found");
+
+      primitives.Remove(primitive);
+      primitives.AddLast(primitive);
+      OnRedrawRequired();
     }
 
     public override void Draw(Graphics graphics) {
-      var current = myPrimitives.Last;
+      var current = primitives.Last;
       while (current != null) {
         var primitive = current.Value;
@@ -204,6 +219,15 @@
     }
 
-    private void primitive_Update(object sender, EventArgs e) {
-      OnUpdate();
+    private void PrimitiveOnRedrawRequired(object sender, EventArgs e) {
+      if (SuppressEvents) return;
+      OnRedrawRequired();
+    }
+
+    public IEnumerator<IPrimitive> GetEnumerator() {
+      return primitives.GetEnumerator();
+    }
+
+    IEnumerator IEnumerable.GetEnumerator() {
+      return GetEnumerator();
     }
   }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Line.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Line.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Line.cs	(revision 13045)
@@ -28,12 +28,6 @@
       : base(chart, start, end) {
     }
-    public Line(IChart chart, double x1, double y1, double x2, double y2)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2)) {
-    }
     public Line(IChart chart, PointD start, PointD end, Pen pen)
       : base(chart, start, end, pen, null) {
-    }
-    public Line(IChart chart, double x1, double y1, double x2, double y2, Pen pen)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2), pen) {
     }
 
@@ -41,6 +35,6 @@
       if (base.ContainsPoint(point)) return true;
 
-      double penWidthX = Chart.PixelToWorldRatio.Width * (Pen.Width + 1);
-      double penWidthY = Chart.PixelToWorldRatio.Height * (Pen.Width + 1);
+      double penWidthX = Chart.PixelToWorldRatio.Width * (Pen.Width + 3);
+      double penWidthY = Chart.PixelToWorldRatio.Height * (Pen.Width + 3);
 
       if ((point.X < (Math.Min(Start.X, End.X) - (penWidthX / 2))) ||
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/LinearPrimitiveBase.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/LinearPrimitiveBase.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/LinearPrimitiveBase.cs	(revision 13045)
@@ -20,4 +20,5 @@
 #endregion
 
+using System;
 using System.Drawing;
 using System.Drawing.Drawing2D;
@@ -26,5 +27,5 @@
 namespace HeuristicLab.Visualization {
   public abstract class LinearPrimitiveBase : PrimitiveBase {
-    protected IGroup selectionRectangles;
+    protected IGroup SelectionRectangles;
 
     private PointD myStart;
@@ -45,19 +46,13 @@
     protected LinearPrimitiveBase(IChart chart, PointD start, PointD end)
       : base(chart) {
-      selectionRectangles = new Group(chart);
+      SelectionRectangles = new Group(chart);
       myStart = start;
       myEnd = end;
     }
-    protected LinearPrimitiveBase(IChart chart, double x1, double y1, double x2, double y2)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2)) {
-    }
     protected LinearPrimitiveBase(IChart chart, PointD start, PointD end, Pen pen, Brush brush)
       : base(chart, pen, brush) {
-      selectionRectangles = new Group(chart);
+      SelectionRectangles = new Group(chart);
       myStart = start;
       myEnd = end;
-    }
-    protected LinearPrimitiveBase(IChart chart, double x1, double y1, double x2, double y2, Pen pen, Brush brush)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2), pen, brush) {
     }
 
@@ -65,40 +60,65 @@
       myStart = start;
       myEnd = end;
-      OnUpdate();
+      OnRedrawRequired();
     }
-    public void SetPosition(double x1, double y1, double x2, double y2) {
-      SetPosition(new PointD(x1, y1), new PointD(x2, y2));
-    }
+
     public override void Move(Offset delta) {
       SetPosition(Start + delta, End + delta);
     }
 
-    public override bool ContainsPoint(PointD point) {
-      if (Selected) {
-        if (selectionRectangles.GetPrimitive(point) != null) return true;
-      }
-      return false;
+    public override void Move(PointD point, Offset delta) {
+      if (Selected && SelectionRectangles.ContainsPoint(point)) {
+        var rect = (SelectionRectangle)SelectionRectangles.GetPrimitive(point);
+        if (rect.Point == Start) {
+          SetPosition(Start + delta, End);
+        } else if (rect.Point == End) {
+          SetPosition(Start, End + delta);
+        }
+      } else SetPosition(Start + delta, End + delta);
     }
 
-    public override void MouseDrag(PointD point, Offset offset, MouseButtons button) {
-      if (button == MouseButtons.Left) {
-        if (Selected) {
-          if (selectionRectangles.ContainsPoint(point)) {
-            SelectionRectangle rect = (SelectionRectangle)selectionRectangles.GetPrimitive(point);
-            if (rect.Point == Start) {
-              SetPosition(Start + offset, End);
-            } else if (rect.Point == End) {
-              SetPosition(Start, End + offset);
-            }
-          } else {
-            base.MouseDrag(point, offset, button);
-          }
+    public override void SnapToGrid(IGrid grid) {
+      var bl = grid.GetBottomLeftGridPoint(Start);
+      var ur = grid.GetUpperRightGridPoint(Start);
+      var sx = Math.Abs(bl.X - Start.X) <= Math.Abs(ur.X - Start.X) ? bl.X : ur.X;
+      var sy = Math.Abs(bl.Y - Start.Y) <= Math.Abs(ur.Y - Start.Y) ? bl.Y : ur.Y;
+
+      var end = new PointD(End.X + (sx - Start.X), End.Y + (sy - Start.Y));
+
+      bl = grid.GetBottomLeftGridPoint(end);
+      ur = grid.GetUpperRightGridPoint(end);
+      var ex = Math.Abs(bl.X - end.X) <= Math.Abs(ur.X - end.X) ? bl.X : ur.X;
+      var ey = Math.Abs(bl.Y - end.Y) <= Math.Abs(ur.Y - end.Y) ? bl.Y : ur.Y;
+
+      SetPosition(new PointD(sx, sy), new PointD(ex, ey));
+    }
+
+    public override void SnapToGrid(PointD point, IGrid grid) {
+      if (Selected && SelectionRectangles.ContainsPoint(point)) {
+        var rect = (SelectionRectangle)SelectionRectangles.GetPrimitive(point);
+        if (rect.Point == Start) {
+          var bl = grid.GetBottomLeftGridPoint(Start);
+          var ur = grid.GetUpperRightGridPoint(Start);
+          var sx = Math.Abs(bl.X - Start.X) <= Math.Abs(ur.X - Start.X) ? bl.X : ur.X;
+          var sy = Math.Abs(bl.Y - Start.Y) <= Math.Abs(ur.Y - Start.Y) ? bl.Y : ur.Y;
+          SetPosition(new PointD(sx, sy), End);
+        } else if (rect.Point == End) {
+          var bl = grid.GetBottomLeftGridPoint(End);
+          var ur = grid.GetUpperRightGridPoint(End);
+          var ex = Math.Abs(bl.X - End.X) <= Math.Abs(ur.X - End.X) ? bl.X : ur.X;
+          var ey = Math.Abs(bl.Y - End.Y) <= Math.Abs(ur.Y - End.Y) ? bl.Y : ur.Y;
+          SetPosition(Start, new PointD(ex, ey));
         }
-      }
+      } else SnapToGrid(grid);
+    }
+
+    public override bool ContainsPoint(PointD point) {
+      if (Selected && SelectionRectangles.GetPrimitive(point) != null) return true;
+      return false;
     }
 
     public override Cursor GetCursor(PointD point) {
       if (Selected) {
-        Cursor cursor = selectionRectangles.GetCursor(point);
+        var cursor = SelectionRectangles.GetCursor(point);
         if (cursor != null) return cursor;
       }
@@ -107,15 +127,14 @@
 
     public override void PostDraw(Graphics graphics) {
-      selectionRectangles.Clear();
+      SelectionRectangles.Clear();
       if (Selected) {
-        Pen pen = new Pen(Color.LightGray, 3);
-        pen.DashStyle = DashStyle.Dash;
+        var pen = new Pen(Color.LightGray, 3) { DashStyle = DashStyle.Dash };
         graphics.DrawLine(pen,
                           Chart.TransformWorldToPixel(Start),
                           Chart.TransformWorldToPixel(End));
-        selectionRectangles.Add(new SelectionRectangle(Chart, Start, Cursors.SizeAll));
-        selectionRectangles.Add(new SelectionRectangle(Chart, End, Cursors.SizeAll));
+        SelectionRectangles.Add(new SelectionRectangle(Chart, Start, Cursors.SizeAll));
+        SelectionRectangles.Add(new SelectionRectangle(Chart, End, Cursors.SizeAll));
 
-        selectionRectangles.Draw(graphics);
+        SelectionRectangles.Draw(graphics);
       }
     }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/PrimitiveBase.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/PrimitiveBase.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/PrimitiveBase.cs	(revision 13045)
@@ -26,154 +26,78 @@
 namespace HeuristicLab.Visualization {
   public abstract class PrimitiveBase : IPrimitive {
-    private IChart myChart;
+    private readonly IChart chart;
     public IChart Chart {
-      get { return myChart; }
-    }
-    private IGroup myGroup;
-    public IGroup Group {
-      get { return myGroup; }
-      set { myGroup = value; }
+      get { return chart; }
     }
 
-    private Pen myPen;
+    private Pen pen;
     public virtual Pen Pen {
-      get { return myPen; }
+      get { return pen; }
       set {
-        myPen = value;
-        OnUpdate();
+        pen = value;
+        OnRedrawRequired();
       }
     }
-    private Brush myBrush;
+    private Brush brush;
     public virtual Brush Brush {
-      get { return myBrush; }
+      get { return brush; }
       set {
-        myBrush = value;
-        OnUpdate();
+        brush = value;
+        OnRedrawRequired();
       }
     }
 
-    private bool myUpdateEnabled;
-    public bool UpdateEnabled {
-      get { return myUpdateEnabled; }
-      set { myUpdateEnabled = value; }
-    }
+    public string ToolTipText { get; set; }
 
-    private bool mySelected;
+    public object Tag { get; set; }
+
+    protected bool SuppressEvents { get; set; }
+
+    private bool selected;
     public virtual bool Selected {
-      get { return mySelected; }
+      get { return selected; }
       set {
-        mySelected = value;
-        OnUpdate();
+        if (selected == value) return;
+        selected = value;
+        OnRedrawRequired();
       }
     }
 
-    private string myToolTipText;
-    public string ToolTipText {
-      get { return myToolTipText; }
-      set { myToolTipText = value; }
+    protected PrimitiveBase(IChart chart)
+      : this(chart, Pens.Black, Brushes.White) {
     }
-
-    private object myTag;
-    public object Tag {
-      get { return myTag; }
-      set { myTag = value; }
-    }
-
-    protected PrimitiveBase(IChart chart) {
-      myChart = chart;
-      myGroup = null;
-      myPen = Pens.Black;
-      myBrush = Brushes.White;
-      myUpdateEnabled = true;
-      mySelected = false;
-      myToolTipText = null;
-      myTag = null;
-    }
-    protected PrimitiveBase(IChart chart, Pen pen, Brush brush)
-      : this(chart) {
-      myPen = pen;
-      myBrush = brush;
+    protected PrimitiveBase(IChart chart, Pen pen, Brush brush) {
+      this.chart = chart;
+      this.pen = pen;
+      this.brush = brush;
+      selected = false;
+      ToolTipText = null;
+      Tag = null;
     }
 
     public abstract void Move(Offset delta);
-    public void Move(double dx, double dy) {
-      Move(new Offset(dx, dy));
-    }
+    public abstract void Move(PointD point, Offset delta);
+
+    public abstract void SnapToGrid(IGrid grid);
+    public abstract void SnapToGrid(PointD point, IGrid grid);
 
     public abstract bool ContainsPoint(PointD point);
-    public bool ContainsPoint(double x, double y) {
-      return ContainsPoint(new PointD(x, y));
-    }
 
     public virtual Cursor GetCursor(PointD point) {
       return null;
     }
-    public Cursor GetCursor(double x, double y) {
-      return GetCursor(new PointD(x, y));
-    }
+
     public virtual string GetToolTipText(PointD point) {
       return ToolTipText;
     }
-    public string GetToolTipText(double x, double y) {
-      return GetToolTipText(new PointD(x, y));
-    }
 
-    public void OneLayerUp() {
-      Group.OneLayerUp(this);
-    }
-    public void OneLayerDown() {
-      Group.OneLayerDown(this);
-    }
-    public void IntoForeground() {
-      Group.IntoForeground(this);
-    }
-    public void IntoBackground() {
-      Group.IntoBackground(this);
-    }
+    public virtual void PreDraw(Graphics graphics) { }
+    public virtual void Draw(Graphics graphics) { }
+    public virtual void PostDraw(Graphics graphics) { }
 
-    public virtual void MouseClick(PointD point, MouseButtons button) {
-      if (button == MouseButtons.Left) {
-        Selected = true;
-      }
-    }
-    public void MouseClick(double x, double y, MouseButtons button) {
-      MouseClick(new PointD(x, y), button);
-    }
-    public virtual void MouseDoubleClick(PointD point, MouseButtons button) {
-    }
-    public void MouseDoubleClick(double x, double y, MouseButtons button) {
-      MouseDoubleClick(new PointD(x, y), button);
-    }
-    public virtual void MouseMove(PointD point, Offset offset) {
-    }
-    public void MouseMove(double x, double y, double dx, double dy) {
-      MouseMove(new PointD(x, y), new Offset(dx, dy));
-    }
-    public virtual void MouseDrag(PointD point, Offset offset, MouseButtons button) {
-      if (button == MouseButtons.Left) {
-        Move(offset);
-      }
-    }
-    public void MouseDrag(double x, double y, double dx, double dy, MouseButtons button) {
-      MouseDrag(new PointD(x, y), new Offset(dx, dy), button);
-    }
-
-    public virtual void PreDraw(Graphics graphics) {
-    }
-    public virtual void Draw(Graphics graphics) {
-    }
-    public virtual void PostDraw(Graphics graphics) {
-    }
-
-    public event EventHandler Update;
-    public void EnforceUpdate() {
-      if (Update != null) {
-        Update(this, new EventArgs());
-      }
-    }
-    protected virtual void OnUpdate() {
-      if ((UpdateEnabled) && (Update != null)) {
-        Update(this, new EventArgs());
-      }
+    public event EventHandler RedrawRequired;
+    protected virtual void OnRedrawRequired() {
+      var handler = RedrawRequired;
+      if (!SuppressEvents && handler != null) handler(this, EventArgs.Empty);
     }
   }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Rectangle.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Rectangle.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/Rectangle.cs	(revision 13045)
@@ -27,12 +27,6 @@
       : base(chart, lowerLeft, upperRight) {
     }
-    public Rectangle(IChart chart, double x1, double y1, double x2, double y2)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2)) {
-    }
     public Rectangle(IChart chart, PointD lowerLeft, PointD upperRight, Pen pen, Brush brush)
       : base(chart, lowerLeft, upperRight, pen, brush) {
-    }
-    public Rectangle(IChart chart, double x1, double y1, double x2, double y2, Pen pen, Brush brush)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2), pen, brush) {
     }
 
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/RectangularPrimitiveBase.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/RectangularPrimitiveBase.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/RectangularPrimitiveBase.cs	(revision 13045)
@@ -41,17 +41,8 @@
     }
 
-    public string Text { get; set; }
-    public Font Font { get; set; }
-    public float MaximumFontSize { get; set; }
-
     protected RectangularPrimitiveBase(IChart chart, PointD lowerLeft, PointD upperRight)
       : base(chart) {
       selectionRectangles = new Group(chart);
       SetPosition(lowerLeft, upperRight);
-
-      MaximumFontSize = 12;
-    }
-    protected RectangularPrimitiveBase(IChart chart, double x1, double y1, double x2, double y2)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2)) {
     }
     protected RectangularPrimitiveBase(IChart chart, PointD lowerLeft, PointD upperRight, Pen pen, Brush brush)
@@ -59,7 +50,4 @@
       selectionRectangles = new Group(chart);
       SetPosition(lowerLeft, upperRight);
-    }
-    protected RectangularPrimitiveBase(IChart chart, double x1, double y1, double x2, double y2, Pen pen, Brush brush)
-      : this(chart, new PointD(x1, y1), new PointD(x2, y2), pen, brush) {
     }
 
@@ -70,11 +58,87 @@
       myLowerLeft = lowerLeft;
       myUpperRight = upperRight;
-      OnUpdate();
+      OnRedrawRequired();
     }
-    public void SetPosition(double x1, double y1, double x2, double y2) {
-      SetPosition(new PointD(x1, y1), new PointD(x2, y2));
-    }
+
     public override void Move(Offset delta) {
       SetPosition(LowerLeft + delta, UpperRight + delta);
+    }
+
+    public override void Move(PointD point, Offset delta) {
+      if (Selected && selectionRectangles.ContainsPoint(point)) {
+        var rect = (SelectionRectangle)selectionRectangles.GetPrimitive(point);
+        var point1 = PointD.Empty;
+        var point2 = PointD.Empty;
+        if (rect.Point == LowerLeft) {
+          point1 = LowerLeft + delta;
+          point2 = UpperRight;
+        } else if (rect.Point == UpperRight) {
+          point1 = LowerLeft;
+          point2 = UpperRight + delta;
+        } else if (rect.Point.X == LowerLeft.X && rect.Point.Y == UpperRight.Y) {
+          point1 = new PointD(LowerLeft.X + delta.DX, LowerLeft.Y);
+          point2 = new PointD(UpperRight.X, UpperRight.Y + delta.DY);
+        } else if (rect.Point.X == UpperRight.X && rect.Point.Y == LowerLeft.Y) {
+          point1 = new PointD(LowerLeft.X, LowerLeft.Y + delta.DY);
+          point2 = new PointD(UpperRight.X + delta.DX, UpperRight.Y);
+        }
+        SetPosition(new PointD(Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y)),
+                    new PointD(Math.Max(point1.X, point2.X), Math.Max(point1.Y, point2.Y)));
+      } else SetPosition(LowerLeft + delta, UpperRight + delta);
+    }
+
+    public override void SnapToGrid(IGrid grid) {
+      var bl = grid.GetBottomLeftGridPoint(LowerLeft);
+      var ur = grid.GetUpperRightGridPoint(LowerLeft);
+      var sx = Math.Abs(bl.X - LowerLeft.X) <= Math.Abs(ur.X - LowerLeft.X) ? bl.X : ur.X;
+      var sy = Math.Abs(bl.Y - LowerLeft.Y) <= Math.Abs(ur.Y - LowerLeft.Y) ? bl.Y : ur.Y;
+
+      var end = new PointD(UpperRight.X + (sx - LowerLeft.X), UpperRight.Y + (sy - LowerLeft.Y));
+
+      bl = grid.GetBottomLeftGridPoint(end);
+      ur = grid.GetUpperRightGridPoint(end);
+      var ex = Math.Abs(bl.X - end.X) <= Math.Abs(ur.X - end.X) ? bl.X : ur.X;
+      var ey = Math.Abs(bl.Y - end.Y) <= Math.Abs(ur.Y - end.Y) ? bl.Y : ur.Y;
+
+      SetPosition(new PointD(sx, sy), new PointD(ex, ey));
+    }
+
+    public override void SnapToGrid(PointD point, IGrid grid) {
+      if (Selected && selectionRectangles.ContainsPoint(point)) {
+        var rect = (SelectionRectangle)selectionRectangles.GetPrimitive(point);
+        var point1 = LowerLeft;
+        var point2 = UpperRight;
+        if (rect.Point == LowerLeft) {
+          var bl = grid.GetBottomLeftGridPoint(LowerLeft);
+          var ur = grid.GetUpperRightGridPoint(LowerLeft);
+          var x = Math.Abs(bl.X - LowerLeft.X) <= Math.Abs(ur.X - LowerLeft.X) ? bl.X : ur.X;
+          var y = Math.Abs(bl.Y - LowerLeft.Y) <= Math.Abs(ur.Y - LowerLeft.Y) ? bl.Y : ur.Y;
+          point1 = new PointD(x, y);
+        } else if (rect.Point == UpperRight) {
+          var bl = grid.GetBottomLeftGridPoint(UpperRight);
+          var ur = grid.GetUpperRightGridPoint(UpperRight);
+          var x = Math.Abs(bl.X - UpperRight.X) <= Math.Abs(ur.X - UpperRight.X) ? bl.X : ur.X;
+          var y = Math.Abs(bl.Y - UpperRight.Y) <= Math.Abs(ur.Y - UpperRight.Y) ? bl.Y : ur.Y;
+          point2 = new PointD(x, y);
+        } else if (rect.Point.X == LowerLeft.X && rect.Point.Y == UpperRight.Y) {
+          var upperLeft = new PointD(LowerLeft.X, UpperRight.Y);
+          var bl = grid.GetBottomLeftGridPoint(upperLeft);
+          var ur = grid.GetUpperRightGridPoint(upperLeft);
+          var x = Math.Abs(bl.X - upperLeft.X) <= Math.Abs(ur.X - upperLeft.X) ? bl.X : ur.X;
+          var y = Math.Abs(bl.Y - upperLeft.Y) <= Math.Abs(ur.Y - upperLeft.Y) ? bl.Y : ur.Y;
+          point1 = new PointD(x, LowerLeft.Y);
+          point2 = new PointD(UpperRight.X, y);
+        } else if (rect.Point.X == UpperRight.X && rect.Point.Y == LowerLeft.Y) {
+          var lowerRight = new PointD(UpperRight.X, LowerLeft.Y);
+          var bl = grid.GetBottomLeftGridPoint(lowerRight);
+          var ur = grid.GetUpperRightGridPoint(lowerRight);
+          var x = Math.Abs(bl.X - lowerRight.X) <= Math.Abs(ur.X - lowerRight.X) ? bl.X : ur.X;
+          var y = Math.Abs(bl.Y - lowerRight.Y) <= Math.Abs(ur.Y - lowerRight.Y) ? bl.Y : ur.Y;
+          point1 = new PointD(LowerLeft.X, y);
+          point2 = new PointD(x, UpperRight.Y);
+        }
+        SetPosition(new PointD(Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y)),
+                    new PointD(Math.Max(point1.X, point2.X), Math.Max(point1.Y, point2.Y)));
+      } else SnapToGrid(grid);
     }
 
@@ -86,36 +150,7 @@
     }
 
-    public override void MouseDrag(PointD point, Offset offset, MouseButtons button) {
-      if (button == MouseButtons.Left) {
-        if (Selected) {
-          if (selectionRectangles.ContainsPoint(point)) {
-            SelectionRectangle rect = (SelectionRectangle)selectionRectangles.GetPrimitive(point);
-            PointD point1 = PointD.Empty;
-            PointD point2 = PointD.Empty;
-            if (rect.Point == LowerLeft) {
-              point1 = LowerLeft + offset;
-              point2 = UpperRight;
-            } else if (rect.Point == UpperRight) {
-              point1 = LowerLeft;
-              point2 = UpperRight + offset;
-            } else if ((rect.Point.X == LowerLeft.X) && (rect.Point.Y == UpperRight.Y)) {
-              point1 = new PointD(LowerLeft.X + offset.DX, LowerLeft.Y);
-              point2 = new PointD(UpperRight.X, UpperRight.Y + offset.DY);
-            } else if ((rect.Point.X == UpperRight.X) && (rect.Point.Y == LowerLeft.Y)) {
-              point1 = new PointD(LowerLeft.X, LowerLeft.Y + offset.DY);
-              point2 = new PointD(UpperRight.X + offset.DX, UpperRight.Y);
-            }
-            SetPosition(Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y),
-                        Math.Max(point1.X, point2.X), Math.Max(point1.Y, point2.Y));
-          } else {
-            base.MouseDrag(point, offset, button);
-          }
-        }
-      }
-    }
-
     public override Cursor GetCursor(PointD point) {
       if (Selected) {
-        Cursor cursor = selectionRectangles.GetCursor(point);
+        var cursor = selectionRectangles.GetCursor(point);
         if (cursor != null) return cursor;
       }
@@ -139,23 +174,4 @@
       }
     }
-
-    protected virtual void DrawText(Graphics graphics) {
-      // draw the text in the center of the bounding rectangle
-      var p = Chart.TransformWorldToPixel(new PointD(LowerLeft.X, LowerLeft.Y));
-      var s = Chart.TransformWorldToPixel(Size);
-
-      var stringSize = graphics.MeasureString(Text, Font);
-      var fontSize = (float)(Math.Round(Font.Size * Math.Min(s.Width / stringSize.Width, s.Height / stringSize.Height)));
-      if (fontSize > MaximumFontSize) { fontSize = MaximumFontSize; }
-      Font = new Font(Font.FontFamily, fontSize, GraphicsUnit.Pixel);
-      stringSize = graphics.MeasureString(Text, Font);
-      graphics.DrawString(Text, Font, new SolidBrush(Pen.Color), p.X + (s.Width - stringSize.Width) / 2f, p.Y - (s.Height + stringSize.Height) / 2f);
-    }
-
-    public override void Draw(Graphics graphics) {
-      if (!string.IsNullOrEmpty(Text))
-        DrawText(graphics);
-      base.Draw(graphics);
-    }
   }
 }
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/SelectionRectangle.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/SelectionRectangle.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Primitives/SelectionRectangle.cs	(revision 13045)
@@ -20,7 +20,4 @@
 #endregion
 
-using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Drawing;
 using System.Windows.Forms;
@@ -41,4 +38,7 @@
     }
 
+    public override void SnapToGrid(IGrid grid) { }
+    public override void SnapToGrid(PointD point, IGrid grid) { }
+
     public override Cursor GetCursor(PointD point) {
       return cursor;
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/AssemblyInfo.frame
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/AssemblyInfo.frame	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/AssemblyInfo.frame	(revision 13045)
@@ -53,4 +53,4 @@
 // You can specify all the values or you can default the Revision and Build Numbers 
 // by using the '*' as shown below:
-[assembly: AssemblyVersion("3.3.0.$WCREV$")]
-[assembly: AssemblyFileVersion("3.3.1.$WCREV$")]
+[assembly: AssemblyVersion("3.3.0.0")]
+[assembly: AssemblyFileVersion("3.3.12.$WCREV$")]
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/Resources.Designer.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/Resources.Designer.cs	(revision 13045)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/Resources.Designer.cs	(revision 13045)
@@ -0,0 +1,93 @@
+﻿//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace HeuristicLab.Visualization.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HeuristicLab.Visualization.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Byte[].
+        /// </summary>
+        internal static byte[] Hand {
+            get {
+                object obj = ResourceManager.GetObject("Hand", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Byte[].
+        /// </summary>
+        internal static byte[] ZoomIn {
+            get {
+                object obj = ResourceManager.GetObject("ZoomIn", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Byte[].
+        /// </summary>
+        internal static byte[] ZoomOut {
+            get {
+                object obj = ResourceManager.GetObject("ZoomOut", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+    }
+}
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/Resources.resx
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/Resources.resx	(revision 13045)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Properties/Resources.resx	(revision 13045)
@@ -0,0 +1,130 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="Hand" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Hand.cur;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="ZoomIn" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\ZoomIn.cur;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="ZoomOut" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\ZoomOut.cur;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+</root>
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.Designer.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.Designer.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.Designer.cs	(revision 13045)
@@ -74,5 +74,5 @@
       this.AcceptButton = this.okButton;
       this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.ClientSize = new System.Drawing.Size(506, 306);
       this.Controls.Add(this.propertyGrid);
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.cs	(revision 13045)
@@ -23,5 +23,4 @@
 using System.Collections.Generic;
 using System.ComponentModel;
-using System.Data;
 using System.Drawing;
 using System.Text;
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.resx
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/PropertiesDialog.resx	(revision 12537)
+++ 	(revision )
@@ -1,120 +1,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
-  <!-- 
-    Microsoft ResX Schema 
-    
-    Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
-    associated with the data types.
-    
-    Example:
-    
-    ... ado.net/XML headers & schema ...
-    <resheader name="resmimetype">text/microsoft-resx</resheader>
-    <resheader name="version">2.0</resheader>
-    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
-    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
-    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
-    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
-    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
-        <value>[base64 mime encoded serialized .NET Framework object]</value>
-    </data>
-    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
-        <comment>This is a comment</comment>
-    </data>
-                
-    There are any number of "resheader" rows that contain simple 
-    name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
-    mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
-    extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
-    read any of the formats listed below.
-    
-    mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
-            : and then encoded with base64 encoding.
-    
-    mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
-            : and then encoded with base64 encoding.
-
-    mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
-            : using a System.ComponentModel.TypeConverter
-            : and then encoded with base64 encoding.
-    -->
-  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-    <xsd:element name="root" msdata:IsDataSet="true">
-      <xsd:complexType>
-        <xsd:choice maxOccurs="unbounded">
-          <xsd:element name="metadata">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-              </xsd:sequence>
-              <xsd:attribute name="name" use="required" type="xsd:string" />
-              <xsd:attribute name="type" type="xsd:string" />
-              <xsd:attribute name="mimetype" type="xsd:string" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="assembly">
-            <xsd:complexType>
-              <xsd:attribute name="alias" type="xsd:string" />
-              <xsd:attribute name="name" type="xsd:string" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="data">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="resheader">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" />
-            </xsd:complexType>
-          </xsd:element>
-        </xsd:choice>
-      </xsd:complexType>
-    </xsd:element>
-  </xsd:schema>
-  <resheader name="resmimetype">
-    <value>text/microsoft-resx</value>
-  </resheader>
-  <resheader name="version">
-    <value>2.0</value>
-  </resheader>
-  <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-</root>
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Structs/Offset.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Structs/Offset.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Structs/Offset.cs	(revision 13045)
@@ -21,7 +21,4 @@
 
 using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Drawing;
 
 namespace HeuristicLab.Visualization {
Index: branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Structs/PointD.cs
===================================================================
--- branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Structs/PointD.cs	(revision 12537)
+++ branches/HeuristicLab.Visualization/HeuristicLab.Visualization/3.3/Structs/PointD.cs	(revision 13045)
@@ -21,6 +21,4 @@
 
 using System;
-using System.Collections.Generic;
-using System.Text;
 using System.Drawing;
 
@@ -86,5 +84,13 @@
     }
     public static bool operator !=(PointD point1, PointD point2) {
-      return (point1.X != point2.X) && (point1.Y != point2.Y);
+      return !(point1 == point2);
+    }
+    public static explicit operator Point(PointD point) {
+      return new Point((int)Math.Round(point.X), (int)Math.Round(point.Y));
+    }
+    public static explicit operator PointF(PointD point) {
+      float fx = (float)point.X;
+      float fy = (float)point.Y;
+      return new PointF(float.IsPositiveInfinity(fx) ? float.MaxValue : fx, float.IsNegativeInfinity(fy) ? float.MinValue : fy);
     }
     public static explicit operator SizeD(PointD point) {
