/*******************************************************************************
* Copyright (c) Emil Crumhorn - Hexapixel.com - emil.crumhorn@gmail.com
* 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:
* emil.crumhorn@gmail.com - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.ganttchart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
class HorizontalScrollbarHandler implements Listener {
private static final int _minScrollRange = 0;
private static final int _maxScrollRange = 500;
private static final int _center = (_maxScrollRange / 2) - 20;
private GanttComposite _gc;
private ScrollBar _scrollBar;
private boolean _scrolling;
private int _lastScrollbarPosition;
private boolean _infinite;
private boolean _none;
private boolean _fixed;
private int _lastDetailEvent;
public HorizontalScrollbarHandler(GanttComposite parent, ScrollBar scrollBar, int style) {
_gc = parent;
_scrollBar = scrollBar;
if ((style & GanttFlags.H_SCROLL_FIXED_RANGE) != 0) {
_fixed = true;
}
else if ((style & GanttFlags.H_SCROLL_INFINITE) != 0) {
_infinite = true;
}
else if ((style & GanttFlags.H_SCROLL_NONE) != 0) {
_none = true;
}
_scrollBar.addListener(SWT.Selection, this);
if (_infinite) {
_scrollBar.setVisible(true);
_scrollBar.setMinimum(_minScrollRange);
_scrollBar.setMaximum(_maxScrollRange);
_scrollBar.setSelection(_center);
_scrollBar.setThumb(20);
return;
}
if (_fixed) {
_scrollBar.setPageIncrement(7);
}
_scrollBar.setMinimum(0);
_scrollBar.setIncrement(1);
if (_none || _fixed) {
_scrollBar.setVisible(false);
}
}
public void resetScrollPosition() {
_lastScrollbarPosition = getScrollBarPosition();
}
public void handleEvent(Event e) {
_scrolling = true;
int cur = getScrollBarPosition();
// same as last (happens when user clicks the thumb without dragging)
if (_lastScrollbarPosition == cur) {
// if the mouse lets go of a scrollbar drag, update the scrollbars again
if (e.detail == 0 && _lastDetailEvent != 0 && _fixed) {
recalculate();
}
if (_infinite && e.detail == 0) {
_scrollBar.setSelection(_center);
_lastScrollbarPosition = _center;
}
_gc.killDialogs();
return;
}
_lastDetailEvent = e.detail;
boolean left = false;
int diff = cur - _lastScrollbarPosition;
if (diff < 0) {
left = true;
diff = _lastScrollbarPosition - cur;
}
boolean moveToCenter = false;
if (e.detail == SWT.PAGE_UP || e.detail == SWT.PAGE_DOWN) {
scrollViewportByPage(scrollDirectionForEventDetail(e.detail), diff);
moveToCenter = true;
}
else if (e.detail == SWT.ARROW_UP || e.detail == SWT.ARROW_DOWN) {
scrollViewportByOffset(scrollDirectionForEventDetail(e.detail), diff);
}
else if (e.detail == SWT.DRAG) {
scrollViewportTo(left ? SWT.LEFT : SWT.RIGHT, _scrollBar.getSelection(), diff);
}
if ((_infinite && e.detail == 0) || moveToCenter) {
_scrollBar.setSelection(_center);
_lastScrollbarPosition = _center;
_gc.killDialogs();
return;
}
_lastScrollbarPosition = getScrollBarPosition();
_scrolling = false;
}
public int getScrollBarPosition() {
return _scrollBar.getSelection();
}
private void scrollViewportByPage(int direction, int diff) {
scrollViewportByOffset(direction, diff);
}
private void scrollViewportByOffset(int direction, int diff) {
if (direction == SWT.LEFT) {
_gc.getViewPortHandler().scrollingLeft(diff);
}
else {
_gc.getViewPortHandler().scrollingRight(diff);
}
}
private void scrollViewportTo(int direction, int position, int diff) {
scrollViewportByOffset(direction, diff);
}
private int scrollDirectionForEventDetail(int eventDetail) {
return (eventDetail == SWT.PAGE_UP || eventDetail == SWT.ARROW_UP) ? SWT.LEFT : SWT.RIGHT;
}
public boolean isScrolling() {
return _scrolling;
}
public void recalculate() {
if (_gc.isDisposed() || !_gc.isChartReady()) {
return;
}
// no scrollbar? nothing to recalculate
if (_none) {
return;
}
// infinite scrollbar is reset to center
if (_infinite) {
_scrollBar.setSelection(_center);
_lastScrollbarPosition = _center;
return;
}
// deal with fixed horizontal scrollbar
if (_fixed) {
float dayWidth = (float) _gc.getDayWidth();
int xLeftMostPixel = _gc.getLeftMostPixel();
int xRightMostPixel = _gc.getRightMostPixel();
//System.err.println(xLeftMostPixel + " " + xRightMostPixel);
int xStart = 0; //_gc.getXForDate(_gc.getRootCalendar());
int xEnd = _gc.getVisibleBounds().width; //_gc.getXForDate(_gc.getRootEndCalendar());
int rangeBonus = 0;
// take sections into account
int gSectionWidth = 0;
if (_gc.getSettings().drawSectionBar()) {
gSectionWidth += _gc.getSettings().getSectionBarWidth();
}
if (_gc.getSettings().drawSectionDetails()) {
gSectionWidth += _gc.getSettings().getSectionDetailWidth();
}
if (_gc.isShowingGanttSections()) {
if (_gc.getSettings().getSectionSide() == SWT.LEFT) {
xStart = gSectionWidth;
}
else {
xEnd -= gSectionWidth;
}
// add it to the range too
rangeBonus += gSectionWidth;
}
//System.err.println(xStart + " : " + xEnd + " " + lastEvent.getActualBounds() + " " + _gc.getVisibleBounds().width + " " + xRightMostPixel);
int vScrollSize = 0;
if (_gc.getVerticalBar().isVisible()) {
vScrollSize = _gc.getVerticalBar().getSize().x;
xEnd -= vScrollSize;
}
int extraLeft = xLeftMostPixel - xStart;
int extraRight = xEnd - xRightMostPixel;
if (extraLeft > 0) {
rangeBonus += extraLeft;
}
if (extraRight > 0) {
rangeBonus += extraRight;
}
// positive extraLeft means we're manually further to the left of the start event
// negative extraLeft means we're to the right of it
// positive extraRight means we're to the right of the last event
// negative extraRight means we're to the left of it
// thus, the "range" to display is the diff of the two divided by the day width
// we don't need a scrollbar if all events are in the visible area
if (extraLeft >= 0 && extraRight >= 0 || extraLeft == Integer.MAX_VALUE || extraRight == Integer.MIN_VALUE) {
_scrollBar.setMaximum(0);
_scrollBar.setSelection(0);
_scrollBar.setThumb(1000000);
if (_scrollBar.isVisible()) {
// redraw chart as there's now a new area that is not drawn (where the scrollbar was before)
_gc.redraw();
}
_scrollBar.setVisible(false);
return;
}
// always add on a day as text might be cut on half a day
rangeBonus += dayWidth;
float pixelRange = (float) (xRightMostPixel - xLeftMostPixel + rangeBonus);
pixelRange -= _gc.getVisibleBounds().width;
if (_gc.getCurrentView() != ISettings.VIEW_YEAR) {
pixelRange /= dayWidth;
}
else {
pixelRange /= dayWidth;
// avg month width is ~30 days, over time it's an ok number, year is rather zoomed out anyway
pixelRange /= 30;
pixelRange += 1;
}
_scrollBar.setVisible(true);
//System.err.println("RANGE: " + Math.ceil(pixelRange));// + " (debug1: " + debug1 + ", debug2: " + debug2 + "). Bonus: " + rangeBonus);
_scrollBar.setMaximum((int) Math.ceil(pixelRange));
// System.err.println("extra left: " + extraLeft + " extra right: " + extraRight + " range: " + pixelRange + " " + dayWidth);
_scrollBar.setThumb(1);
boolean setMax = false;
boolean setMin = false;
int scrollLocation = 0;
// all the way to the right
if (extraRight > 0 && extraLeft < 0) {
setMax = true;
}
// all the way left
if (extraLeft > 0 && extraRight < 0) {
setMin = true;
}
// middle somewhere
if (!setMax && !setMin) {
if (extraLeft > 0) {
scrollLocation -= extraLeft;
}
else {
scrollLocation += Math.abs(extraLeft);
}
if (extraRight > 0) {
scrollLocation -= extraRight;
}
}
if (setMax) {
_scrollBar.setSelection(_scrollBar.getMaximum());
}
else if (setMin) {
_scrollBar.setSelection(0);
}
else {
_scrollBar.setSelection(scrollLocation / (int)dayWidth);
}
_lastScrollbarPosition = _scrollBar.getSelection();
}
}
}