private void mouseMove(GwtEvent<? extends EventHandler> event) {
    int[] coord = getCoordinates(event);
    int x = coord[0];
    int y = coord[1];

    if (isMouseDown) {
      // Do panning
      int dx = x - previousX;
      int dy = y - previousY;

      if (draggableNode != null) {
        WidgetStyle.setCursor(canvas, Cursor.POINTER);

        double scale = canvas.getScale();
        int scaleDeltaX = (int) (dx / scale);
        int scaleDeltaY = (int) (dy / scale);
        diagramPane.drag(draggableNode, scaleDeltaX, scaleDeltaY);
      } else {
        WidgetStyle.setCursor(canvas, Cursor.DEFAULT);
        diagramPane.translate(dx, dy);
        diagramPane.update();
      }

      previousX = x;
      previousY = y;
      isDragging = true;
    } else {
      diagramPane.hover(x, y);
    }
  }
  private void hiliteAndCentreObjects(SelectionEvent e) {
    if (canvas.getObjectsForRendering() == null) return;
    // Un-hilight objects first
    for (GraphObject obj : canvas.getObjectsForRendering()) {
      obj.setHighlighted(false);
    }

    // Highlight the selected object and connected objects
    List<GraphObject> selectedObjects = e.getSelectedObjects();
    for (GraphObject obj : selectedObjects) {
      if (obj instanceof Node) {
        Node node = (Node) obj;
        List<HyperEdge> reactions = node.getConnectedReactions();
        for (HyperEdge edge : reactions) edge.setHighlighted(true);
      } else if (obj instanceof HyperEdge) {
        HyperEdge edge = (HyperEdge) obj;
        List<Node> nodes = edge.getConnectedNodes();
        for (Node node : nodes) node.setHighlighted(true);
      }

      if (e.isDoCentring()) {
        centreObject(obj);
      }
    }
    diagramPane.update();
  }
  private void centreObject(GraphObject obj) {
    // Centre selected object
    // PathwayCanvas c = diagramPane.getCanvas();

    double scale = canvas.getScale();

    Point objPos = obj.getPosition();
    double objX = objPos.getX();
    double objY = objPos.getY();

    double x = (objX * -1.0 * scale) + (canvas.getCoordinateSpaceWidth() / 2);
    double y = (objY * -1.0 * scale) + (canvas.getCoordinateSpaceHeight() / 2);

    diagramPane.reset();
    diagramPane.scale(scale);
    diagramPane.translate(x, y, true);
    diagramPane.hideTooltip();
  }
  private void arrowKeyUp(KeyUpEvent event) {
    if (!diagramPane.getPopupMenu().isShowing()) {
      int x = 0;
      int y = 0;

      if (event.isLeftArrow()) {
        x = Parameters.MOVEX;
      } else if (event.isRightArrow()) {
        x = -Parameters.MOVEX;
      } else if (event.isUpArrow()) {
        y = Parameters.MOVEY;
      } else if (event.isDownArrow()) {
        y = -Parameters.MOVEY;
      }

      diagramPane.translate(x, y);
      diagramPane.update();
    }
  }
  private void mouseDown(GwtEvent<? extends EventHandler> event) {
    int[] coord = getCoordinates(event);
    previousX = coord[0];
    previousY = coord[1];
    isMouseDown = true;

    if (canvas instanceof InteractorCanvas && draggableNode == null)
      draggableNode = ((InteractorCanvas) canvas).getDraggableNode(new Point(previousX, previousY));

    diagramPane.hideTooltip();
  }
  public void installEventHandlersForCanvas() {
    installUserInputHandlers();

    final OverviewCanvas overview = diagramPane.getOverview();

    canvas.addHandler(diagramPane.getOverview(), ViewChangeEvent.TYPE);

    // highlight linked objects
    SelectionEventHandler selectionHandler =
        new SelectionEventHandler() {

          @Override
          public void onSelectionChanged(SelectionEvent e) {
            hiliteAndCentreObjects(e);
            overview.setSelectedObjects(e.getSelectedObjects());
            overview.update();
          }
        };

    diagramPane.addSelectionEventHandler(selectionHandler);
  }
  private void mouseWheel(MouseWheelEvent event) {
    if (diagramPane.getPopupMenu().isShowing() || ignoreMouseWheelEvent) return;

    Timer stopIgnoringEventTimer =
        new Timer() {

          @Override
          public void run() {
            ignoreMouseWheelEvent = false;
          }
        };
    ignoreMouseWheelEvent = true;
    stopIgnoringEventTimer.schedule(250);

    Point scrollPoint = new Point(event.getX(), event.getY());

    if (event.getDeltaY() < 0) {
      diagramPane.zoomIn(scrollPoint);
    } else if (event.getDeltaY() > 0) {
      diagramPane.zoomOut(scrollPoint);
    }

    diagramPane.update();
  }
  private void mouseUp(GwtEvent<? extends EventHandler> event) {
    if (!isMouseDown) return;

    int[] coord = getCoordinates(event);
    int x = coord[0];
    int y = coord[1];

    isMouseDown = false;

    if (isDragging) {
      isDragging = false;
      draggableNode = null;
      WidgetStyle.setCursor(canvas, Cursor.DEFAULT);
    } else { // Do click selection
      // TODO: selection cannot work under iPad. Need to check touchEnd event.
      diagramPane.select(event, x, y);
    }
  }
  public void installOverviewEventHandler() {
    final OverviewCanvas overview = diagramPane.getOverview();

    // To catch overview dragging
    ViewChangeEventHandler overviewEventHandler =
        new ViewChangeEventHandler() {
          @Override
          public void onViewChange(ViewChangeEvent event) {
            double dx = event.getTranslationEvent().getTranslateX();
            double dy = event.getTranslationEvent().getTranslateY();
            double scale = event.getZoomEvent().getScale();
            double canvasScale = canvas.getScale();

            // System.out.println("responding to overview click");

            diagramPane.translate(-dx / scale * canvasScale, -dy / scale * canvasScale);
            diagramPane.update();
            overview.setIsFromOverview(false); // Overview Canvas can now respond to ViewChangeEvent
            // now that it's own event firing has finished
          }
        };

    overview.addHandler(overviewEventHandler, ViewChangeEvent.TYPE);
  }