blob: e4a3918c446c6e409273b47794dc2dd2348ed48f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.hop.ui.hopgui.perspective.execution;
import org.apache.hop.core.gui.DPoint;
import org.apache.hop.core.gui.Point;
import org.apache.hop.core.gui.plugin.key.GuiKeyboardShortcut;
import org.apache.hop.core.gui.plugin.key.GuiOsxKeyboardShortcut;
import org.apache.hop.ui.core.PropsUi;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
public abstract class DragViewZoomBase extends Composite {
protected Canvas canvas;
protected DPoint offset;
protected Point maximum;
protected float magnification = 1.0f;
protected org.apache.hop.core.gui.Rectangle viewPort;
protected org.apache.hop.core.gui.Rectangle graphPort;
protected boolean viewDrag;
protected Point viewDragStart;
protected DPoint viewDragBaseOffset;
protected boolean viewPortNavigation;
protected Point viewPortStart;
public DragViewZoomBase(Composite parent, int style) {
super(parent, style);
}
@Override
public abstract void redraw();
public Point screen2real(int x, int y) {
float correctedMagnification = calculateCorrectedMagnification();
DPoint real =
new DPoint(
((double) x - offset.x) / correctedMagnification - offset.x,
((double) y - offset.y) / correctedMagnification - offset.y);
return real.toPoint();
}
protected float calculateCorrectedMagnification() {
return (float) (magnification * PropsUi.getInstance().getZoomFactor());
}
public abstract void setZoomLabel();
@GuiKeyboardShortcut(key = SWT.HOME)
@GuiOsxKeyboardShortcut(key = SWT.HOME)
public void viewReset() {
offset = new DPoint(0.0, 0.0);
redraw();
}
@GuiKeyboardShortcut(key = SWT.ARROW_LEFT)
@GuiOsxKeyboardShortcut(key = SWT.ARROW_LEFT)
public void viewLeft() {
offset.x += 15 * magnification * PropsUi.getNativeZoomFactor();
validateOffset();
redraw();
}
@GuiKeyboardShortcut(key = SWT.ARROW_RIGHT)
@GuiOsxKeyboardShortcut(key = SWT.ARROW_RIGHT)
public void viewRight() {
offset.x -= 15 * magnification * PropsUi.getNativeZoomFactor();
validateOffset();
redraw();
}
@GuiKeyboardShortcut(key = SWT.ARROW_UP)
@GuiOsxKeyboardShortcut(key = SWT.ARROW_UP)
public void viewUp() {
offset.y += 15 * magnification * PropsUi.getNativeZoomFactor();
validateOffset();
redraw();
}
@GuiKeyboardShortcut(key = SWT.ARROW_DOWN)
@GuiOsxKeyboardShortcut(key = SWT.ARROW_DOWN)
public void viewDown() {
offset.y -= 15 * magnification * PropsUi.getNativeZoomFactor();
validateOffset();
redraw();
}
@GuiKeyboardShortcut(control = true, key = '+')
public void zoomIn() {
magnification += 0.1f;
// Minimum 1000%
if (magnification > 10f) {
magnification = 10f;
}
setZoomLabel();
redraw();
}
// Double keyboard shortcut zoom in '+' or '='
@GuiKeyboardShortcut(control = true, key = '=')
public void zoomIn2() {
zoomIn();
}
@GuiKeyboardShortcut(control = true, key = '-')
public void zoomOut() {
magnification -= 0.1f;
// Minimum 10%
if (magnification < 0.1f) {
magnification = 0.1f;
}
setZoomLabel();
redraw();
}
@GuiKeyboardShortcut(control = true, key = '0' )
public void zoom100Percent() {
magnification = 1.0f;
setZoomLabel();
redraw();
}
// Double keyboard shortcut zoom 100% '0' or keypad 0
@GuiKeyboardShortcut(control = true, key = SWT.KEYPAD_0 )
public void zoom100Percent2() {
zoom100Percent();
}
@GuiKeyboardShortcut(control = true, key = '*')
public void zoomFitToScreen() {
if (maximum.x <= 0 || maximum.y <= 0) {
return;
}
org.eclipse.swt.graphics.Rectangle canvasBounds = canvas.getBounds();
// Correct the maximum pipeline/workflow size for magnification, zoom factor, ...
//
double zoomXPct = canvasBounds.width / (maximum.x * PropsUi.getNativeZoomFactor()) * .9;
double zoomYPct = canvasBounds.height / (maximum.y * PropsUi.getNativeZoomFactor()) * .9;
// We take the minimum value of both to make sure everything fits on screen
//
magnification = (float) Math.min(zoomXPct, zoomYPct);
// Reset the offset as well, otherwise we might still be off-screen
//
offset = new DPoint(0.0, 0.0);
// Set this on the zoom level widget
//
setZoomLabel();
canvas.redraw();
}
// Double keyboard shortcut zoom fit to screen '*' or keypad *
@GuiKeyboardShortcut(control = true, key = SWT.KEYPAD_MULTIPLY )
public void zoomFitToScreen2() {
zoomFitToScreen();
}
/**
* There are 2 ways to drag the view-port around. One way is to use the navigation rectangle at
* the bottom. The other way is to click-drag the background.
*
* @param button
* @param control
* @param screenClick
* @return
*/
protected boolean setupDragView(int button, boolean control, Point screenClick) {
// See if this is a click on the navigation view inner rectangle with the goal of dragging it
// around a bit.
//
if (viewPort != null && viewPort.contains(screenClick)) {
viewPortNavigation = true;
viewPortStart = new Point(screenClick);
viewDragBaseOffset = new DPoint(offset);
return true;
}
// Middle button
// CTRL + left button
//
viewDrag = button == 2 || (control && button == 1);
if (viewDrag) {
viewDragStart = screenClick;
viewDragBaseOffset = new DPoint(offset);
return true;
}
return false;
}
protected void dragViewPort(Point clickLocation) {
// The delta is calculated
//
double deltaX = clickLocation.x - viewPortStart.x;
double deltaY = clickLocation.y - viewPortStart.y;
// What's the wiggle room for the little rectangle in the bigger one?
//
int wiggleX = viewPort.width;
int wiggleY = viewPort.height;
// What's that in percentages? We draw the little rectangle at 25% size.
//
double deltaXPct = wiggleX == 0 ? 0 : deltaX / (wiggleX / 0.25);
double deltaYPct = wiggleY == 0 ? 0 : deltaY / (wiggleY / 0.25);
// The offset is then a matter of setting a percentage of the graph size
//
double deltaOffSetX = deltaXPct * maximum.x;
double deltaOffSetY = deltaYPct * maximum.y;
offset = new DPoint(viewDragBaseOffset.x - deltaOffSetX, viewDragBaseOffset.y - deltaOffSetY);
// Make sure we don't catapult the view somewhere we can't find the graph anymore.
//
validateOffset();
redraw();
}
public void validateOffset() {
double zoomFactor = PropsUi.getNativeZoomFactor() * Math.max(0.1, magnification);
// What's the size of the graph when painted on screen?
//
double graphWidth = maximum.x;
double graphHeight = maximum.y;
// We need to know the size of the screen.
//
Point area = getArea();
double viewWidth = area.x / zoomFactor;
double viewHeight = area.y / zoomFactor;
// As a percentage of the view area, how much can we go outside the graph area?
//
double overshootPct = 0.75;
double overshootWidth = viewWidth * overshootPct;
double overshootHeight = viewHeight * overshootPct;
// Let's not move the graph off the screen to the top/left
//
double minX = -graphWidth - overshootWidth;
if (offset.x < minX) {
offset.x = minX;
}
double minY = -graphHeight - overshootHeight;
if (offset.y < minY) {
offset.y = minY;
}
// Are we moving the graph too far down/right?
//
double maxX = overshootWidth;
if (offset.x > maxX) {
offset.x = maxX;
}
double maxY = overshootHeight;
if (offset.y > maxY) {
offset.y = maxY;
}
}
protected Point getArea() {
org.eclipse.swt.graphics.Rectangle rect = canvas.getClientArea();
return new Point(rect.width, rect.height);
}
/**
* Calculate the differences for the scrollbars. We take the system zoom factor and current
* magnification into account
*/
protected void dragView(Point lastClick, Point moved) {
// The offset is in absolute numbers relative to the pipeline/workflow graph metadata.
// The screen coordinates need to be corrected. If the image is zoomed in we need to move less.
//
double zoomFactor = PropsUi.getNativeZoomFactor() * Math.max(0.1, magnification);
double deltaX = (lastClick.x - moved.x) / zoomFactor;
double deltaY = (lastClick.y - moved.y) / zoomFactor;
offset.x = viewDragBaseOffset.x - deltaX;
offset.y = viewDragBaseOffset.y - deltaY;
validateOffset();
redraw();
}
protected void mouseScrolled(MouseEvent mouseEvent) {
// Zoom in or out every time we get an event.
//
// In the future we do want to take the location of the mouse into account.
// That way we can adjust the offset accordingly to keep the screen centered on the mouse while
// zooming in or out.
//
if (mouseEvent.count > 0) {
zoomIn();
} else {
zoomOut();
}
}
}