/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG and others.
* 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:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.navigation.ui.swt.views;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.riena.navigation.IModuleNode;
import org.eclipse.riena.navigation.INavigationNode;
import org.eclipse.riena.navigation.ISubModuleNode;
import org.eclipse.riena.ui.swt.utils.SwtUtilities;
/**
* This class provides scrolling logic for the navigation with a scroll bar.
*
* @since 3.0
*/
public class ScrollBarSupport extends AbstractScrollingSupport {
private final ScrolledComposite scrolledComposite;
/**
* Creates a new instance of the support of scrolling with scroll bars.
*
* @param scrolledComposite
* the composite around the content that should be scrolled
* @param navigationComponentProvider
*/
public ScrollBarSupport(final ScrolledComposite scrolledComposite, final IModuleNavigationComponentProvider navigationComponentProvider) {
super(navigationComponentProvider);
this.scrolledComposite = scrolledComposite;
}
@Override
public void scroll() {
final IModuleNode activeModule = getActiveModule(getActiveNode());
if (canScroll(activeModule)) {
scrollTo(activeModule);
}
}
/**
* Returns the active module.
*
* @param activeNode
* the active navigation node
* @return active module
*/
private IModuleNode getActiveModule(final INavigationNode<?> activeNode) {
if (activeNode instanceof IModuleNode) {
return (IModuleNode) activeNode;
} else if (activeNode instanceof ISubModuleNode) {
final ISubModuleNode subModuleNode = (ISubModuleNode) activeNode;
return subModuleNode.getParentOfType(IModuleNode.class);
}
return null;
}
@Override
protected boolean scrollTo(final Composite topComp, final Composite bottomComp) {
final int pixels = getScrollPixels(topComp, bottomComp);
if (canScroll(pixels)) {
scroll(pixels);
return true;
}
return false;
}
@Override
protected boolean scrollTo(final Tree tree) {
final int pixels = getScrollPixels(tree);
if (canScroll(pixels)) {
scroll(pixels);
return true;
}
return false;
}
/**
* Returns whether scrolling is necessary to make the given module (and maybe the selected sub-module) visible.
*
* @param module
* node of the module
* @return {@code true} scrolling is necessary; otherwise {@code false}
*/
private boolean canScroll(final IModuleNode module) {
if (module == null) {
return false;
}
final ModuleView moduleView = navigationComponentProvider.getModuleViewForNode(module);
if (moduleView == null) {
return false;
}
final boolean isClosed = moduleView.getOpenHeight() == 0;
if (isClosed) {
return canScroll(moduleView.getTitle(), moduleView.getBody());
} else {
return canScroll(moduleView.getTree());
}
}
/**
* Returns whether scrolling is necessary to make the given composites visible.
*
* @param topComp
* top composite
* @param bottomComp
* bottom composite
* @return {@code true} scrolling is necessary; otherwise {@code false}
*/
private boolean canScroll(final Composite topComp, final Composite bottomComp) {
return canScroll(getScrollPixels(topComp, bottomComp));
}
private boolean canScroll(final Tree tree) {
return canScroll(getScrollPixels(tree));
}
/**
* Returns whether scrolling is necessary to make the selected tree item visible.
*
* @param tree
* tree
* @return {@code true} scrolling is necessary; otherwise {@code false}
*/
private boolean canScroll(final int pixels) {
return pixels != 0;
}
/**
* Returns the amount of pixels which are necessary for scrolling so that the given composites are visible.
*
* @param topComp
* top composite
* @param bottomComp
* bottom composite
* @return amount of pixels
*/
private int getScrollPixels(final Composite topComp, final Composite bottomComp) {
final int ty = scrolledComposite.getDisplay().map(topComp, scrolledComposite, 0, topComp.getBounds().y).y;
if (ty < 0) {
return ty;
}
final int clientHeight = scrolledComposite.getClientArea().height;
final int by = scrolledComposite.getDisplay().map(bottomComp, scrolledComposite, 0, bottomComp.getBounds().height).y;
if (by > clientHeight) {
return by - clientHeight;
}
return 0;
}
/**
* Returns the amount of pixels which are necessary for scrolling so that the selected tree item is visible.
*
* @param tree
* tree
* @return amount of pixels
*/
private int getScrollPixels(final Tree tree) {
if (SwtUtilities.isDisposed(tree)) {
return 0;
}
if (tree.getSelectionCount() > 0) {
final TreeItem item = tree.getSelection()[0];
final Rectangle itemBounds = item.getBounds();
int y = scrolledComposite.getDisplay().map(tree, scrolledComposite, 0, itemBounds.y).y;
if (y < 0) {
return y;
}
final int clientHeight = scrolledComposite.getClientArea().height;
y = scrolledComposite.getDisplay().map(tree, scrolledComposite, 0, itemBounds.y + itemBounds.height).y;
if (y > clientHeight) {
return y - clientHeight;
}
}
return 0;
}
@Override
protected int getNavigationComponentHeight() {
return scrolledComposite.getBounds().height;
}
@Override
protected void scrollUp(final int pixels) {
scroll(-pixels);
}
@Override
protected void scrollDown(final int pixels) {
scroll(pixels);
}
private void scroll(final int pixels) {
final Point origin = scrolledComposite.getOrigin();
origin.y += pixels;
scrolledComposite.setOrigin(origin);
}
}