/******************************************************************************* * Copyright (c) 2017 Dirk Fauth. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation ******************************************************************************/ package org.eclipse.nebula.widgets.nattable.viewport.action; import org.eclipse.nebula.widgets.nattable.NatTable; import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum; import org.eclipse.nebula.widgets.nattable.ui.action.IDragMode; import org.eclipse.nebula.widgets.nattable.util.GUIHelper; import org.eclipse.nebula.widgets.nattable.viewport.command.ViewportDragCommand; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; /** * Abstract {@link IDragMode} implementation to support auto-scrolling on * dragging. * * @since 1.5 */ public abstract class AutoScrollDragMode implements IDragMode { private final boolean horizontal; private final boolean vertical; private AutoScrollRunnable runnable; protected int horizontalBorderOffset = GUIHelper.convertHorizontalPixelToDpi(25); /** * * @param horizontal * <code>true</code> to support horizontal auto-scrolling. * @param vertical * <code>true</code> to support vertical auto-scrolling. */ public AutoScrollDragMode(boolean horizontal, boolean vertical) { this.horizontal = horizontal; this.vertical = vertical; } @Override public void mouseMove(NatTable natTable, MouseEvent event) { Rectangle clientArea = natTable.getClientAreaProvider().getClientArea(); int x = event.x; int y = event.y; int horizontalDiff = 0; MoveDirectionEnum horizontal = MoveDirectionEnum.NONE; if (this.horizontal) { if (event.x < this.horizontalBorderOffset) { horizontal = MoveDirectionEnum.LEFT; x = Math.max(0, event.x); horizontalDiff = -event.x; } else if (event.x > (clientArea.width - this.horizontalBorderOffset)) { horizontal = MoveDirectionEnum.RIGHT; x = clientArea.width - 1; horizontalDiff = event.x - clientArea.width; } } int verticalDiff = 0; MoveDirectionEnum vertical = MoveDirectionEnum.NONE; if (this.vertical) { if (event.y < 0) { vertical = MoveDirectionEnum.UP; y = 0; verticalDiff = -event.y; } else if (event.y > clientArea.height) { vertical = MoveDirectionEnum.DOWN; y = clientArea.height - 1; verticalDiff = event.y - clientArea.height; } } if ((!MoveDirectionEnum.NONE.equals(horizontal) || !MoveDirectionEnum.NONE.equals(vertical)) && this.runnable == null) { this.runnable = new AutoScrollRunnable(natTable, x, y, horizontal, vertical); this.runnable.schedule(); } else if (MoveDirectionEnum.NONE.equals(horizontal) && MoveDirectionEnum.NONE.equals(vertical) && this.runnable != null) { this.runnable.cancel(); this.runnable = null; } else if (this.runnable != null) { this.runnable.calculateRepeatDelay(horizontalDiff, verticalDiff); } performDragAction(natTable, x, y, horizontal, vertical); } @Override public void mouseUp(NatTable natTable, MouseEvent event) { if (this.runnable != null) { this.runnable.cancel(); this.runnable = null; } } /** * * @param natTable * The NatTable instance the drag operation is currently * performed on. * @param x * The x coordinate of the mouse pointer on mouse move. Corrected * to be inside the NatTable client area. * @param y * The y coordinate of the mouse pointer on mouse move. Corrected * to be inside the NatTable client area. * @param horizontal * The horizontal direction where the auto-scroll should be * performed to. * @param vertical * The vertical direction where the auto-scroll should be * performed to. */ protected void performDragAction( NatTable natTable, int x, int y, MoveDirectionEnum horizontal, MoveDirectionEnum vertical) { // do nothing by default } /** * Runnable that continuously scrolls the viewport. */ protected class AutoScrollRunnable implements Runnable { private final NatTable natTable; private final Display display; private final MoveDirectionEnum horizontal; private final MoveDirectionEnum vertical; private final int x, y; private int repeatDelay = 500; private boolean active = true; public AutoScrollRunnable(NatTable natTable, int x, int y, MoveDirectionEnum horizontal, MoveDirectionEnum vertical) { this.natTable = natTable; this.display = natTable.getDisplay(); this.horizontal = horizontal; this.vertical = vertical; this.x = x; this.y = y; } /** * Schedule this runnable to start with a delay of 500 ms. */ public void schedule() { if (this.display != null) { this.display.timerExec(500, this); } } /** * Calculates the delay of the repeated auto-scroll execution based on * the difference between the NatTable borders and the mouse cursor and * the move direction that is currently active. * * @param horizontalDiff * The horizontal difference between the NatTable border and * the mouse cursor. * @param verticalDiff * The vertical difference between the NatTable border and * the mouse cursor. */ public void calculateRepeatDelay(int horizontalDiff, int verticalDiff) { if (!MoveDirectionEnum.NONE.equals(this.horizontal)) { int factor = horizontalDiff / 5; factor = Math.min(factor, 10); this.repeatDelay = 500 - (factor * 49); } if (!MoveDirectionEnum.NONE.equals(this.vertical)) { int factor = verticalDiff / 5; factor = Math.min(factor, 10); this.repeatDelay = 500 - (factor * 49); } } /** * Cancels the repeated execution. */ public void cancel() { this.active = false; } @Override public void run() { if (this.active) { if (!MoveDirectionEnum.NONE.equals(this.horizontal) || !MoveDirectionEnum.NONE.equals(this.vertical)) { if (this.natTable.doCommand(new ViewportDragCommand(this.horizontal, this.vertical))) { performDragAction(this.natTable, this.x, this.y, this.horizontal, this.vertical); } } this.display.timerExec(this.repeatDelay, this); } } } }