/**
   * @param aEvent the event to test, may be <code>null</code>.
   * @return
   */
  private boolean isCursorHover(final MouseEvent aEvent) {
    if (!isCursorPopupTrigger(aEvent)) {
      return false;
    }

    final Point point = aEvent.getPoint();
    return (findCursor(point) != null);
  }
  /** {@inheritDoc} */
  @Override
  public void mouseDragged(final MouseEvent aEvent) {
    final MouseEvent event = convertEvent(aEvent);
    final Point point = event.getPoint();

    // Update the selected channel while dragging...
    this.controller.setSelectedChannel(point);

    if (getModel().isCursorMode() && (this.movingCursor >= 0)) {
      this.controller.moveCursor(this.movingCursor, getCursorDropPoint(point));

      aEvent.consume();
    } else {
      if ((this.lastClickPosition == null)
          && ((aEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0)) {
        this.lastClickPosition = new Point(point);
      }

      final JScrollPane scrollPane =
          getAncestorOfClass(JScrollPane.class, (Component) aEvent.getSource());
      if ((scrollPane != null) && (this.lastClickPosition != null)) {
        final JViewport viewPort = scrollPane.getViewport();
        final Component signalView = this.controller.getSignalDiagram().getSignalView();

        boolean horizontalOnly = (aEvent.getModifiersEx() & InputEvent.ALT_DOWN_MASK) != 0;
        boolean verticalOnly =
            horizontalOnly && ((aEvent.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0);

        int dx = aEvent.getX() - this.lastClickPosition.x;
        int dy = aEvent.getY() - this.lastClickPosition.y;

        Point scrollPosition = viewPort.getViewPosition();
        int newX = scrollPosition.x;
        if (!verticalOnly) {
          newX -= dx;
        }
        int newY = scrollPosition.y;
        if (verticalOnly || !horizontalOnly) {
          newY -= dy;
        }

        int diagramWidth = signalView.getWidth();
        int viewportWidth = viewPort.getWidth();
        int maxX = diagramWidth - viewportWidth - 1;
        scrollPosition.x = Math.max(0, Math.min(maxX, newX));

        int diagramHeight = signalView.getHeight();
        int viewportHeight = viewPort.getHeight();
        int maxY = diagramHeight - viewportHeight;
        scrollPosition.y = Math.max(0, Math.min(maxY, newY));

        viewPort.setViewPosition(scrollPosition);
      }

      // Use UNCONVERTED/ORIGINAL mouse event!
      handleZoomRegion(aEvent, this.lastClickPosition);
    }
  }
  /**
   * @param aEvent
   * @param aStartPoint
   */
  protected void handleZoomRegion(final MouseEvent aEvent, final Point aStartPoint) {
    // For now, disabled by default as it isn't 100% working yet...
    if (Boolean.FALSE.equals(Boolean.valueOf(System.getProperty("zoomregionenabled", "false")))) {
      return;
    }

    final JComponent source = (JComponent) aEvent.getComponent();
    final boolean dragging = (aEvent.getID() == MouseEvent.MOUSE_DRAGGED);

    final GhostGlassPane glassPane =
        (GhostGlassPane) SwingUtilities.getRootPane(source).getGlassPane();

    Rectangle viewRect;
    final JScrollPane scrollPane =
        SwingComponentUtils.getAncestorOfClass(JScrollPane.class, source);
    if (scrollPane != null) {
      final JViewport viewport = scrollPane.getViewport();
      viewRect = SwingUtilities.convertRectangle(viewport, viewport.getVisibleRect(), glassPane);
    } else {
      viewRect = SwingUtilities.convertRectangle(source, source.getVisibleRect(), glassPane);
    }

    final Point start = SwingUtilities.convertPoint(source, aStartPoint, glassPane);
    final Point current = SwingUtilities.convertPoint(source, aEvent.getPoint(), glassPane);

    if (dragging) {
      if (!glassPane.isVisible()) {
        glassPane.setVisible(true);
        glassPane.setRenderer(new RubberBandRenderer(), start, current, viewRect);
      } else {
        glassPane.updateRenderer(start, current, viewRect);
      }

      glassPane.repaintPartially();
    } else
    /* if ( !dragging ) */
    {
      // Fire off a signal to the zoom controller to do its job...
      this.controller.getZoomController().zoomRegion(aStartPoint, aEvent.getPoint());

      glassPane.setVisible(false);
    }
  }
  /** {@inheritDoc} */
  @Override
  public void mouseClicked(final MouseEvent aEvent) {
    // Ensure the focus is moved to the main signal diagram component...
    getSignalDiagram().requestFocusInWindow();

    if (getModel().isCursorMode() && (aEvent.getClickCount() == 2)) {
      final MouseEvent event = convertEvent(aEvent);

      final Cursor hoveredCursor = findCursor(event.getPoint());
      if (hoveredCursor != null) {
        editCursorProperties(hoveredCursor);
        // Consume the event to stop further processing...
        aEvent.consume();
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public void mouseReleased(final MouseEvent aEvent) {
    final MouseEvent event = convertEvent(aEvent);
    final Point point = event.getPoint();

    if (!isCursorHover(event)) {
      setMouseCursor(event, null);
    }

    if (!handlePopupTrigger(point, aEvent)) {
      this.movingCursor = -1;
    }

    if ((aEvent.getModifiersEx() & InputEvent.BUTTON1_MASK) != 0) {
      // Use UNCONVERTED/ORIGINAL mouse event!
      handleZoomRegion(aEvent, this.lastClickPosition);

      this.lastClickPosition = null;
    }
  }
  /** {@inheritDoc} */
  @Override
  public void mousePressed(final MouseEvent aEvent) {
    final MouseEvent event = convertEvent(aEvent);
    final Point point = event.getPoint();

    if (!handlePopupTrigger(point, aEvent)) {
      if (getModel().isCursorMode()) {
        Cursor hoveredCursor = findCursor(point);
        if (hoveredCursor != null) {
          this.movingCursor = hoveredCursor.getIndex();
          setMouseCursor(aEvent, SignalView.CURSOR_MOVE_CURSOR);
        } else {
          this.movingCursor = -1;
        }
      }

      if ((aEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) {
        this.lastClickPosition = point;
      }
    }
  }