/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.flow; import java.awt.Container; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import javax.swing.JScrollPane; import javax.swing.RepaintManager; import javax.swing.SwingUtilities; import com.rapidminer.gui.flow.processrendering.view.ProcessRendererView; /** * Class for handling the scrolling in the {@link ProcessPanel}. Handles normal scrolling and * zooming (Ctrl + scroll). For zooming the scrollbars are adjusted such that the mouse pointer * stays over the same point in the process canvas. * * @author Gisa Schaefer * @since 7.1.0 * */ class ProcessPanelScroller { private final JScrollPane scrollPane; private final ProcessRendererView rendererView; private boolean zoomed = false; private int desiredHorizontalScrollValue = 0; private int desiredVerticalScrollValue = 0; /** the mouse wheel listener for the process panel */ private final transient MouseWheelListener wheelListener = new MouseWheelListener() { @Override public void mouseWheelMoved(final MouseWheelEvent e) { if (e.isControlDown() && e.getWheelRotation() != 0) { double oldZoomFactor = rendererView.getModel().getZoomFactor(); if (e.getWheelRotation() < 0) { rendererView.getModel().zoomIn(); } else { rendererView.getModel().zoomOut(); } rendererView.getModel().fireProcessZoomChanged(); // calculate how the scrollbar needs to be adjusted for centered zoom double relativeZoomFactor = rendererView.getModel().getZoomFactor() / oldZoomFactor; double differenceHorizontal = e.getPoint().getX() * (relativeZoomFactor - 1); double differenceVertical = e.getPoint().getY() * (relativeZoomFactor - 1); int newX = Math.max(0, (int) (scrollPane.getHorizontalScrollBar().getValue() + differenceHorizontal)); int newY = Math.max(0, (int) (scrollPane.getVerticalScrollBar().getValue() + differenceVertical)); scrollPane.getHorizontalScrollBar().setValue(newX); scrollPane.getVerticalScrollBar().setValue(newY); // prevent flickering when another adjustment of the scrollbars is needed RepaintManager.currentManager(scrollPane).markCompletelyClean(scrollPane); /** * Setting the value as above does not always work since the scrollbars are not yet * updated to the size changes caused by the zooming. Set flag an values to try * again after the resizing happened. */ zoomed = true; desiredVerticalScrollValue = newY; desiredHorizontalScrollValue = newX; return; } Container p = rendererView.getParent(); if (p != null) { p.dispatchEvent(SwingUtilities.convertMouseEvent(rendererView, e, p)); } } }; /** * Creates a handler for scrolling for the view and its surrounding scrollPane. * * @param view * the {@link ProcessRendererView} * @param scrollPane * the {@link JScrollPane} containing the view */ ProcessPanelScroller(final ProcessRendererView view, final JScrollPane scrollPane) { this.scrollPane = scrollPane; this.rendererView = view; rendererView.addMouseWheelListener(wheelListener); // add listener to check for size changes of the scrollbar scrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() { private int lastValue; @Override public void adjustmentValueChanged(AdjustmentEvent e) { if (lastValue == scrollPane.getHorizontalScrollBar().getMaximum()) { return; } // the maximum value changed, so the process canvas size changed lastValue = scrollPane.getHorizontalScrollBar().getMaximum(); if (zoomed) { // set the scrollbar values needed for centered zoom if needed if (scrollPane.getHorizontalScrollBar().getValue() != desiredHorizontalScrollValue) { scrollPane.getHorizontalScrollBar().setValue(desiredHorizontalScrollValue); } if (scrollPane.getVerticalScrollBar().getValue() != desiredVerticalScrollValue) { scrollPane.getVerticalScrollBar().setValue(desiredVerticalScrollValue); } zoomed = false; } } }); } }