public void scrollToAttribute(Attribute a) {
    if (!a.getDescriptor().getConfig().equals(getConfig())) {
      logger.fine("Cannot scroll to attribute that isn't attached to this type of descriptor");
      return;
    }
    int rowIndex = getCurrentModel().getRowForDescriptor(a.getDescriptor());
    int colIndex = getCurrentModel().getColumnForAttribute(a);
    JScrollPane scrollPane = (JScrollPane) this.getComponent(0);
    JViewport viewport = (JViewport) scrollPane.getViewport();
    EnhancedTable table = (EnhancedTable) viewport.getView();

    // This rectangle is relative to the table where the
    // northwest corner of cell (0,0) is always (0,0).
    Rectangle rect = table.getCellRect(rowIndex, colIndex, true);

    // The location of the viewport relative to the table
    Point pt = viewport.getViewPosition();

    // Translate the cell location so that it is relative
    // to the view, assuming the northwest corner of the
    // view is (0,0)
    rect.setLocation(rect.x - pt.x, rect.y - pt.y);

    // Scroll the area into view
    viewport.scrollRectToVisible(rect);
  }
  //
  //  Implement the ChangeListener
  //
  public void stateChanged(ChangeEvent e) {
    //  Keep the scrolling of the row table in sync with main table

    JViewport viewport = (JViewport) e.getSource();
    JScrollPane scrollPane = (JScrollPane) viewport.getParent();
    scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y);
  }
  /** {@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);
    }
  }