com.spaceapplications.vaadin.addon.eventtimeline.gwt.client.VEventTimelineBrowser.java Source code

Java tutorial

Introduction

Here is the source code for com.spaceapplications.vaadin.addon.eventtimeline.gwt.client.VEventTimelineBrowser.java

Source

/*
@VaadinAddonLicenseForJavaFiles@
 */
package com.spaceapplications.vaadin.addon.eventtimeline.gwt.client;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import com.google.gwt.canvas.dom.client.CssColor;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.spaceapplications.vaadin.addon.eventtimeline.gwt.canvas.client.Canvas;
import com.vaadin.terminal.gwt.client.DateTimeService;

/**
 * VEventTimelineBrowser, based on original version from vaadin-timeline.
 * 
 * @author Thomas Neidhart / Space Applications Services NV/SA
 * @author Peter Lehto / IT Mill Oy Ltd
 * @author John Ahlroos / IT Mill Oy Ltd
 */
public class VEventTimelineBrowser extends Widget implements VDataListener, MouseDownHandler, MouseMoveHandler,
        MouseUpHandler, MouseWheelHandler, DoubleClickHandler, NativePreviewHandler {

    private static final String CLASSNAME_CANVAS = VEventTimelineWidget.BROWSER_CLASSNAME + "-canvas";
    private static final String CLASSNAME_SCROLLBAR = VEventTimelineWidget.BROWSER_CLASSNAME + "-scrollbar";
    private static final String CLASSNAME_SCROLLBAR_LEFT = CLASSNAME_SCROLLBAR + "-left";
    private static final String CLASSNAME_SCROLLBAR_RIGHT = CLASSNAME_SCROLLBAR + "-right";
    private static final String CLASSNAME_FADE = CLASSNAME_SCROLLBAR + "-fade";
    private static final String CLASSNAME_SCALE = VEventTimelineWidget.BROWSER_CLASSNAME + "-scale";
    private static final String CLASSNAME_SCALELABEL = CLASSNAME_SCALE + "-label";
    private static final String CLASSNAME_CURTAIN = VEventTimelineWidget.BROWSER_CLASSNAME + "-curtain";

    private final Element browserRoot;

    private final Canvas canvas;

    private final Element scrollBar;

    private final Element scrollLeft;
    private final Element scrollRight;

    private boolean mouseDown;
    private boolean mouseIsActive = false;

    private final VEventTimelineWidget timelineWidget;

    private boolean sizeAdjustLeft;
    private boolean sizeAdjustRight;

    private int dragStartX;

    private final VEventTimelineBrowserScroller scroller = new VEventTimelineBrowserScroller();

    private final Map<Integer, Set<VEvent>> currentEvents = new TreeMap<Integer, Set<VEvent>>();

    private final Element leftFade;
    private final Element rightFade;

    private Date selectedStartDate = null;
    private Date selectedEndDate = null;

    private final AbsolutePanel horizontalScalePanel;
    private final List<Label> horizontalScaleComponents = new ArrayList<Label>();

    private final HTML disabledCurtain;

    private HandlerRegistration mouseMoveReg, mouseUpReg, mouseDownReg, mouseScrollReg, mouseClickReg, preview;

    private DateTimeFormat yearFormatShort = DateTimeFormat.getFormat("''yy");
    private DateTimeFormat yearFormatLong = DateTimeFormat.getFormat(PredefinedFormat.YEAR);

    private DateTimeFormat monthFormatShort = DateTimeFormat.getFormat(PredefinedFormat.YEAR_MONTH_ABBR);
    private DateTimeFormat monthFormatLong = DateTimeFormat.getFormat(PredefinedFormat.YEAR_MONTH);

    private DateTimeFormat dayFormatShort = DateTimeFormat.getFormat(PredefinedFormat.YEAR_MONTH_ABBR_DAY);
    private DateTimeFormat dayFormatLong = DateTimeFormat.getFormat(PredefinedFormat.YEAR_MONTH_DAY);

    private DateTimeFormat timeFormatShort = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE);
    private DateTimeFormat timeFormatLong = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE_SECOND);

    public VEventTimelineBrowser(VEventTimelineWidget tw) {
        timelineWidget = tw;

        browserRoot = DOM.createDiv();
        setElement(browserRoot);

        setHeight("64px");
        setStyleName(VEventTimelineWidget.BROWSER_CLASSNAME);

        canvas = new Canvas(300, 44);
        canvas.setStyleName(CLASSNAME_CANVAS);
        browserRoot.appendChild(canvas.getElement());

        // Add the horizontal scale
        horizontalScalePanel = new AbsolutePanel();
        horizontalScalePanel.setStyleName(CLASSNAME_SCALE);
        browserRoot.appendChild(horizontalScalePanel.getElement());

        // Add fade-outs
        leftFade = DOM.createDiv();
        leftFade.setClassName(CLASSNAME_FADE);
        browserRoot.appendChild(leftFade);

        rightFade = DOM.createDiv();
        rightFade.setClassName(CLASSNAME_FADE);
        browserRoot.appendChild(rightFade);

        // Add the scrollbar
        scrollBar = DOM.createDiv();
        scrollBar.setClassName(CLASSNAME_SCROLLBAR);

        scrollLeft = DOM.createDiv();
        scrollLeft.setClassName(CLASSNAME_SCROLLBAR_LEFT);

        scrollRight = DOM.createDiv();
        scrollRight.setClassName(CLASSNAME_SCROLLBAR_RIGHT);

        scrollBar.appendChild(scrollLeft);
        scrollBar.appendChild(scrollRight);

        browserRoot.appendChild(scrollBar);
        browserRoot.appendChild(scroller.getElement());

        // Create the disabled curtain
        disabledCurtain = new HTML("");
        disabledCurtain.setVisible(false);
        disabledCurtain.setStyleName(CLASSNAME_CURTAIN);
        disabledCurtain.setWidth("100%");
        disabledCurtain.setHeight("100%");
        disabledCurtain.getElement().getStyle().setZIndex(2);
        horizontalScalePanel.add(disabledCurtain);
    }

    @Override
    protected void onLoad() {
        super.onLoad();
        mouseDownReg = addDomHandler(this, MouseDownEvent.getType());
        mouseUpReg = addDomHandler(this, MouseUpEvent.getType());
        mouseMoveReg = addDomHandler(this, MouseMoveEvent.getType());
        mouseScrollReg = addDomHandler(this, MouseWheelEvent.getType());
        mouseClickReg = addDomHandler(this, DoubleClickEvent.getType());
        preview = Event.addNativePreviewHandler(this);

    }

    @Override
    public void setWidth(String width) {
        super.setWidth(width);
        int w = getOffsetWidth() - 28 - 2;
        canvas.setWidth(w + "px");
        browserRoot.setAttribute("width", w + "px");
    }

    @Override
    protected void onUnload() {
        super.onUnload();
        if (mouseDownReg != null) {
            mouseDownReg.removeHandler();
            mouseDownReg = null;
        }
        if (mouseUpReg != null) {
            mouseUpReg.removeHandler();
            mouseUpReg = null;
        }
        if (mouseMoveReg != null) {
            mouseMoveReg.removeHandler();
            mouseMoveReg = null;
        }
        if (mouseScrollReg != null) {
            mouseScrollReg.removeHandler();
            mouseScrollReg = null;
        }
        if (mouseClickReg != null) {
            mouseClickReg.removeHandler();
            mouseClickReg = null;
        }
        if (preview != null) {
            preview.removeHandler();
            preview = null;
        }
    }

    /**
     * Initializes the canvas i.e. it will fetch the points for the whole
     * timeline and render it to the canvas.
     */
    public void refresh() {
        if (!isVisible() || timelineWidget.getStartDate() == null || timelineWidget.getEndDate() == null) {
            return;
        }

        // Get the events
        timelineWidget.getEvents(this, timelineWidget.getStartDate(), timelineWidget.getEndDate(), true);
    }

    public void redraw() {
        // Plot the points
        plotData();

        // Set the scroller range
        // disable as this causes problems when an event is updated
        //setRange(selectedStartDate, selectedEndDate);
    }

    /*
     * (non-Javadoc)
     */
    public void dataReceived(Integer band, List<VEvent> events) {
        if (events != null) {
            Set<VEvent> bandEvents = currentEvents.get(band);
            if (bandEvents == null) {
                bandEvents = new TreeSet<VEvent>();
                currentEvents.put(band, bandEvents);
            }

            // TODO - Thomas -> what do you think about #clear()?
            // Should be OK since events always contains all events
            bandEvents.clear();
            bandEvents.addAll(events);
        }
    }

    /*
     * (non-Javadoc)
     */
    public void dataReceivedAll() {
        // Plot the received data
        plotData();

        /*
         * If the selection is locked then we want to reset the range so it is
         * pointing to the same selection in case a point has been added or
         * removed from the graph. If the selection is not locked we need to
         * recalculate the new selection.
         */
        if (timelineWidget.isInitDone() && timelineWidget.isSelectionLocked() && !mouseDown) {
            // disable as this causes problems when an event is updated
            //setRange(selectedStartDate, selectedEndDate);
        } else {
            refreshSelection();
        }
    }

    @Override
    public void dataRemoved(Integer[] bands) {
        for (int i = 0; i < bands.length; i++) {
            Integer band = bands[i];
            currentEvents.remove(band);
        }
    }

    /**
     * Plots the horizontal scale
     */
    private void plotHorizontalScale(float xUnit, long startTime, long endTime) {
        long timeDiff = endTime - startTime;

        for (Label lbl : horizontalScaleComponents) {
            horizontalScalePanel.remove(lbl);
        }
        horizontalScaleComponents.clear();

        canvas.setGlobalCompositeOperation(Canvas.DESTINATION_OVER);

        // Selection is less than a minute
        if (timeDiff <= VEventTimelineDisplay.MINUTE) {
            plotHorizontalScale(startTime, endTime, 10 * VEventTimelineDisplay.SECOND, xUnit, true);
        }

        // Selection is less than five minutes
        else if (timeDiff <= 5 * VEventTimelineDisplay.MINUTE) {
            plotHorizontalScale(startTime, endTime, VEventTimelineDisplay.MINUTE, xUnit, true);
        }

        // Selection is less than 30 minutes
        else if (timeDiff <= 30 * VEventTimelineDisplay.MINUTE) {
            plotHorizontalScale(startTime, endTime, 5 * VEventTimelineDisplay.MINUTE, xUnit, true);
        }

        // Selection is less than 1 hour
        else if (timeDiff <= VEventTimelineDisplay.HOUR) {
            plotHorizontalScale(startTime, endTime, 10 * VEventTimelineDisplay.MINUTE, xUnit, true);
        }

        // Selection is less then 6 hours
        else if (timeDiff <= 6 * VEventTimelineDisplay.HOUR) {
            plotHorizontalScale(startTime, endTime, 30 * VEventTimelineDisplay.MINUTE, xUnit, true);
        }

        // Selection is less then a half day
        else if (timeDiff <= 12 * VEventTimelineDisplay.HOUR) {
            plotHorizontalScale(startTime, endTime, VEventTimelineDisplay.HOUR, xUnit, false);
        }

        // Selection is less than a day
        else if (timeDiff <= VEventTimelineDisplay.DAY) {
            plotHorizontalScale(startTime, endTime, 2 * VEventTimelineDisplay.HOUR, xUnit, true);
        }

        // Selection is less than 3 days
        else if (timeDiff <= 3 * VEventTimelineDisplay.DAY) {
            plotHorizontalScale(startTime, endTime, 6 * VEventTimelineDisplay.HOUR, xUnit, true);
        }

        // Selection is less than a week. Show dayly view
        else if (timeDiff <= VEventTimelineDisplay.WEEK) {
            plotHorizontalScale(startTime, endTime, VEventTimelineDisplay.DAY, xUnit, false);
        }

        // Selection is less than two weeks
        else if (timeDiff <= 2 * VEventTimelineDisplay.WEEK) {
            plotHorizontalScale(startTime, endTime, 3 * VEventTimelineDisplay.DAY, xUnit, true);
        }

        // Selection is less than a month. Show weekly view
        else if (timeDiff <= 2 * VEventTimelineDisplay.MONTH) {
            plotHorizontalScale(startTime, endTime, VEventTimelineDisplay.WEEK, xUnit, true);
        }

        // Selection is less than a year
        else if (timeDiff <= 6 * VEventTimelineDisplay.MONTH) {
            plotHorizontalScale(startTime, endTime, VEventTimelineDisplay.MONTH, xUnit, false);
        }

        // Selection is less than a year
        else if (timeDiff <= VEventTimelineDisplay.YEAR) {
            plotHorizontalScale(startTime, endTime, VEventTimelineDisplay.MONTH, xUnit, false);
        }

        // Selection is more than two years
        else {
            plotHorizontalScale(startTime, endTime, VEventTimelineDisplay.YEAR, xUnit, false);
        }

        canvas.setGlobalCompositeOperation(Canvas.SOURCE_OVER);
    }

    @SuppressWarnings("deprecation")
    private void plotHorizontalScale(long startTime, long endTime, long unitTime, float xUnit, boolean leftAlign) {

        float width = unitTime * xUnit;
        boolean shortDateFormat = width < 100;
        int year = timelineWidget.getStartDate().getYear();
        long time = (new Date(year, 0, 1)).getTime();

        DateTimeFormat formatter;
        if (unitTime < VEventTimelineDisplay.DAY) {
            formatter = shortDateFormat ? timeFormatShort : timeFormatLong;
        } else if (unitTime < VEventTimelineDisplay.MONTH) {
            formatter = shortDateFormat ? dayFormatShort : dayFormatLong;
        } else if (unitTime < VEventTimelineDisplay.YEAR) {
            formatter = shortDateFormat ? monthFormatShort : monthFormatLong;
        } else {
            formatter = shortDateFormat ? yearFormatShort : yearFormatLong;
        }

        canvas.setStrokeStyle("rgb(200,200,200)");
        canvas.beginPath();

        long stepsUntilInRange = (startTime - time) / unitTime;
        time += stepsUntilInRange * unitTime;

        while (time <= endTime) {
            if (time >= startTime - unitTime && time <= endTime + unitTime) {
                Label lbl = new Label();
                lbl.setStyleName(CLASSNAME_SCALELABEL);
                lbl.setWidth(width + "px");
                Date date = new Date(time);
                lbl.setText(formatter.format(date));

                long timeFromStart = time - startTime;
                float x = timeFromStart * xUnit;

                canvas.moveTo(x, 0);
                canvas.lineTo(x, canvas.getHeight());

                horizontalScalePanel.add(lbl, (int) x + 14 + 1,
                        horizontalScalePanel.getOffsetHeight() - scrollBar.getOffsetHeight() - 13);
                horizontalScaleComponents.add(lbl);
            }

            if (unitTime == VEventTimelineDisplay.MONTH) {
                /*
                 * Month resolution is not so easy since it changes depending on
                 * the month. We use the Date to resolve the new time
                 */
                time += DateTimeService.getNumberOfDaysInMonth(new Date(time)) * VEventTimelineDisplay.DAY;
            } else if (unitTime == VEventTimelineDisplay.YEAR) {
                /*
                 * Take leap years into account
                 */
                if (DateTimeService.isLeapYear(new Date(time))) {
                    time += unitTime + VEventTimelineDisplay.DAY;
                } else {
                    time += unitTime;
                }

            } else {
                time += unitTime;
            }
        }

        canvas.closePath();
        canvas.stroke();
    }

    /**
     * Plots the received data on to the canvas
     */
    private void plotData() {
        if (!isVisible()) {
            return;
        }

        Float canvasWidth = (float) getCanvasWidth();
        Float canvasHeight = new Float(canvas.getHeight());

        // clear old drawings
        canvas.clear();

        Long timeDiff = timelineWidget.getEndDate().getTime() - timelineWidget.getStartDate().getTime();
        float xUnit = canvasWidth / timeDiff.floatValue();

        // draw current time if it is in the current display
        Date d = new Date();
        float timeX = (d.getTime() - timelineWidget.getStartDate().getTime()) * xUnit;

        if (timeX > 0 && timeX < canvas.getWidth()) {
            canvas.beginPath();
            canvas.moveTo(timeX, canvas.getHeight());
            canvas.lineTo(timeX, 0);
            canvas.closePath();

            canvas.setStrokeStyle("#a00000");
            canvas.setLineWidth(1.0d);
            canvas.stroke();
        }

        // plot the events, the bands heights are evenly distributed
        int numBands = currentEvents.size();
        int bandHeight = canvasHeight.intValue() - 10;
        if (numBands > 0) {
            bandHeight = bandHeight / numBands - 2;
        }
        float y = 1;
        long startTime = timelineWidget.getStartDate().getTime();
        for (Set<VEvent> eventSet : currentEvents.values()) {
            float lastX = 0;
            for (VEvent event : eventSet) {
                Long eventStartTime = event.getStartTime();
                Long eventEndTime = event.getEndTime();

                Long timeFromStart = eventStartTime - startTime;
                float x = timeFromStart * xUnit;

                Long duration = eventEndTime - eventStartTime;
                float w = duration * xUnit;

                if (x < lastX) {
                    w -= lastX - x;
                    x = lastX;
                }

                plotEvent(event, x, y, w, bandHeight);
                lastX = x + w;
            }

            y += bandHeight + 2;
        }

        // plot the horizontal scale
        plotHorizontalScale(xUnit, timelineWidget.getStartDate().getTime(), timelineWidget.getEndDate().getTime());
    }

    /**
     * Plots an event onto the display
     * 
     * @param event
     *            The event
     * @param x
     *            The X-coordinate
     * @param y
     *            The Y-coordinate
     * @param w
     *            The width
     * @param h
     *            The height
     */
    private void plotEvent(VEvent event, float x, float y, float w, float h) {
        canvas.save();
        canvas.beginPath();
        canvas.rect(x, y, w, h);
        canvas.closePath();

        canvas.setLineWidth(0.5);
        canvas.setStrokeStyle("#ccc");

        String bgColor = event.getBackgroundColor();
        String fillStyle = (bgColor == null) ? "#ccc" : CssColor.make(bgColor).value();
        canvas.setFillStyle(fillStyle);

        canvas.stroke();
        canvas.fill();
        canvas.restore();
    }

    /**
     * Set the selected range
     * 
     * @param startDate
     *            The start date of the range
     * @param endDate
     *            The end date of the range
     */
    public void setRange(Date startDate, Date endDate) {
        Date start = timelineWidget.getStartDate();
        Date end = timelineWidget.getEndDate();

        if (startDate == null || startDate.before(start)) {
            startDate = start;
        }

        if (endDate == null || endDate.after(end)) {
            endDate = end;
        }

        selectedStartDate = startDate;
        selectedEndDate = endDate;

        Long timeDiff = end.getTime() - start.getTime();
        Float timeUnit = (float) canvas.getOffsetWidth() / (float) timeDiff;

        Long startTime = startDate.getTime() - start.getTime();
        int startPixel = (int) (startTime * timeUnit) + 14;

        Long endTime = endDate.getTime() - start.getTime();
        int endPixel = (int) (endTime * timeUnit) - 3;

        if (startPixel < 14) {
            startPixel = 14;
        }

        if (endPixel > getOffsetWidth() - 16) {
            endPixel = getOffsetWidth() - 16;
        }

        scroller.setLeftPosition(startPixel);
        scroller.setRightPosition(endPixel);

        DOM.setStyleAttribute(leftFade, "width", startPixel + "px");
        DOM.setStyleAttribute(rightFade, "left", (endPixel + 14 + 2) + "px");
        DOM.setStyleAttribute(rightFade, "width", (getOffsetWidth() - endPixel - 28) + "px");
    }

    /**
     * Converts the selectors start pixel position to a date
     * 
     * @return The calculated date
     */
    private Date calculateStartPoint() {
        Date start = timelineWidget.getStartDate();
        Date end = timelineWidget.getEndDate();
        Float canvasWidth = (float) getCanvasWidth();
        Long timeDiff = end.getTime() - start.getTime();
        Float timeUnit = timeDiff.floatValue() / canvasWidth;

        int selectorLeftPixel = scroller.getLeftPosition();
        Float time = selectorLeftPixel * timeUnit;

        Date date = new Date(start.getTime() + time.longValue());

        if (date.before(start)) {
            date = start;
        }

        if (date.after(end)) {
            date = end;
        }

        return date;
    }

    /**
     * Converts the selectors end pixel position to a date
     * 
     * @return The calculated date
     */
    private Date calculateEndPoint() {
        Date start = timelineWidget.getStartDate();
        Date end = timelineWidget.getEndDate();
        Float canvasWidth = (float) getCanvasWidth();
        Long timeDiff = end.getTime() - start.getTime();
        Float timeUnit = timeDiff.floatValue() / canvasWidth;

        int selectorRightPixel = scroller.getRightPosition();
        Float time = selectorRightPixel * timeUnit;

        Date date = new Date(start.getTime() + time.longValue());

        if (date.before(start)) {
            date = start;
        }

        if (date.after(end)) {
            date = end;
        }

        return date;
    }

    /**
     * Alias for calculateStartPoint
     * 
     * @return The calculated date
     */
    public Date getSelectedStartDate() {
        return selectedStartDate;
    }

    /**
     * Alias for calculateEndPoint
     * 
     * @return The calculated date
     */
    public Date getSelectedEndDate() {
        return selectedEndDate;
    }

    /**
     * Get the canvas width
     * 
     * @return The width in pixels
     */
    public int getCanvasWidth() {
        if (isVisible()) {
            return canvas.getOffsetWidth();
        } else {
            return timelineWidget.getWidgetWidth() - 28 - 2;
        }
    }

    /**
     * Checks if element exists in the browser
     * 
     * @param elem
     *            The element
     * @return True if the element exists, else false
     */
    public boolean hasElement(com.google.gwt.dom.client.Element elem) {
        if (elem == getElement() || elem == browserRoot || elem == leftFade || elem == rightFade
                || elem == scrollBar || elem == scrollLeft || elem == scrollRight || elem == canvas.getElement()
                || elem == horizontalScalePanel.getElement() || scroller.hasElement(elem)
                || elem == disabledCurtain.getElement()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Enables the browser
     * 
     * @param enabled
     *            Is the browser usable by the user
     */
    public void setEnabled(boolean enabled) {
        disabledCurtain.setVisible(!enabled);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.google.gwt.event.dom.client.MouseUpHandler#onMouseUp(com.google.gwt
     * .event.dom.client.MouseUpEvent)
     */
    public void onMouseUp(MouseUpEvent event) {
        mouseIsActive = false;

        DOM.releaseCapture(horizontalScalePanel.getElement());

        if (sizeAdjustLeft || sizeAdjustRight) {
            scroller.lockSize();
            selectedStartDate = calculateStartPoint();
            selectedEndDate = calculateEndPoint();
            setDateRangeTimer.cancel();
            setDateRangeTimer.schedule(100);

            DOM.setStyleAttribute(leftFade, "width", (scroller.getLeftPosition() + 14) + "px");
            DOM.setStyleAttribute(rightFade, "left", (scroller.getRightPosition() + 14 + 2) + "px");
            DOM.setStyleAttribute(rightFade, "width", (getOffsetWidth() - scroller.getRightPosition() - 28) + "px");
            timelineWidget.fireDateRangeChangedEvent();

        } else {
            selectedStartDate = calculateStartPoint();
            selectedEndDate = calculateEndPoint();
            setDateRangeTimer.cancel();
            setDateRangeTimer.schedule(100);

            DOM.setStyleAttribute(leftFade, "width", (scroller.getLeftPosition() + 14) + "px");
            DOM.setStyleAttribute(rightFade, "left", (scroller.getRightPosition() + 14 + 2) + "px");
            DOM.setStyleAttribute(rightFade, "width", (getOffsetWidth() - scroller.getRightPosition() - 28) + "px");

            timelineWidget.fireDateRangeChangedEvent();
        }

        sizeAdjustLeft = false;
        sizeAdjustRight = false;
    }

    private Timer setDateRangeTimer = new Timer() {
        @Override
        public void run() {
            timelineWidget.setDisplayRange(selectedStartDate, selectedEndDate);
        }
    };

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.google.gwt.event.dom.client.MouseMoveHandler#onMouseMove(com.google
     * .gwt.event.dom.client.MouseMoveEvent)
     */
    public void onMouseMove(MouseMoveEvent event) {
        NativeEvent mouseEvent = event.getNativeEvent();
        if (mouseDown) {
            int adjustment = mouseEvent.getClientX() - dragStartX;

            // The area size is being adjusted
            if (sizeAdjustLeft && scroller.getLeftPosition() + adjustment > 0) {
                if (scroller.getWidth() > adjustment) {
                    scroller.adjustLeftSideSize(adjustment);
                }
            } else if (sizeAdjustLeft) {
                scroller.adjustLeftSideSize(-scroller.getLeftPosition());
            } else if (sizeAdjustRight
                    && scroller.getRightPosition() + adjustment < scrollBar.getOffsetWidth() - 2 * 15) {
                scroller.adjustRightSideSize(adjustment);
            } else if (sizeAdjustRight) {
                int diff = (scrollBar.getOffsetWidth() - 2 * 15) - scroller.getRightPosition();
                scroller.adjustRightSideSize(diff - 1);
            } else {

                int scrollAreaX = mouseEvent.getClientX() - dragStartX;

                /*
                 * Calculate the right edge. Remember to add the left and right
                 * spacing 2*14px and the borders 2*1px to the calculation.
                 */
                int scrollAreaRight = scrollAreaX + scroller.getAreaWidth() + 14 + 2;

                if (scrollAreaRight <= scrollBar.getOffsetWidth()) {
                    int offset = scroller.getMouseOffset((Event) mouseEvent);
                    int leftPosition = mouseEvent.getClientX() - dragStartX;

                    if (offset != dragStartX) {
                        scroller.setLeftPosition(leftPosition);

                        if (leftPosition > 14) {
                            dragStartX = scroller.getMouseOffset((Event) mouseEvent);
                        }

                        selectedStartDate = calculateStartPoint();
                        selectedEndDate = calculateEndPoint();

                        setDateRangeTimer.cancel();
                        setDateRangeTimer.schedule(100);
                    }

                    /*
                     * Else if the cursor is over the right edge make sure the
                     * browser is in its rightmost position.
                     */
                } else {

                    int width = scroller.getAreaWidth();
                    int leftPosition = scrollBar.getOffsetWidth() - width - 14;

                    scroller.setLeftPosition(leftPosition);

                    selectedStartDate = calculateStartPoint();
                    selectedEndDate = calculateEndPoint();

                    setDateRangeTimer.cancel();
                    setDateRangeTimer.schedule(100);
                }
            }

            DOM.setStyleAttribute(leftFade, "width", (scroller.getLeftPosition() + 14) + "px");
            DOM.setStyleAttribute(rightFade, "left", (scroller.getRightPosition() + 14 + 2) + "px");
            DOM.setStyleAttribute(rightFade, "width", (getOffsetWidth() - scroller.getRightPosition() - 28) + "px");
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google
     * .gwt.event.dom.client.MouseDownEvent)
     */
    public void onMouseDown(MouseDownEvent event) {
        NativeEvent mouseEvent = event.getNativeEvent();
        Element mouseOver = (Element) Element.as(mouseEvent.getEventTarget());

        mouseIsActive = true;

        event.preventDefault();
        event.stopPropagation();

        DOM.setCapture(horizontalScalePanel.getElement());

        if (mouseOver == scrollLeft) {
            sizeAdjustLeft = false;
            sizeAdjustRight = false;

            if (scroller.getLeftPosition() > 0) {
                scroller.move(-1);
                timelineWidget.fireDateRangeChangedEvent();
            }

        } else if (mouseOver == scrollRight) {
            sizeAdjustLeft = false;
            sizeAdjustRight = false;

            if (scroller.getRightPosition() + 5 < getCanvasWidth()) {
                scroller.move(1);
                timelineWidget.fireDateRangeChangedEvent();
            }

        } else if (scroller.isMouseOverScrollElement((Event) mouseEvent)
                || scroller.isMouseOverScrollArea((Event) mouseEvent)) {
            sizeAdjustLeft = false;
            sizeAdjustRight = false;
            dragStartX = scroller.getMouseOffset((Event) mouseEvent);
        } else if (scroller.isMouseOverLeftSideSizeAdjuster((Event) mouseEvent)) {
            sizeAdjustRight = false;
            sizeAdjustLeft = true;
            dragStartX = mouseEvent.getClientX();
        } else if (scroller.isMouseOverRightSideSizeAdjuster((Event) mouseEvent)) {
            sizeAdjustLeft = false;
            sizeAdjustRight = true;
            dragStartX = mouseEvent.getClientX();
        } else if (mouseOver == horizontalScalePanel.getElement() || mouseOver == leftFade
                || mouseOver == rightFade) {
            sizeAdjustLeft = false;
            sizeAdjustRight = false;

            // Calculate the centering point
            int center = mouseEvent.getClientX() - getAbsoluteLeft();
            int area = scroller.getAreaWidth();

            if (center - area / 2 < 14) {
                center = area / 2;
            }

            if (center + area / 2 > getOffsetWidth() - 28) {
                center = getOffsetWidth() - 28 - area / 2;
            }

            // Center scoller and set fades
            scroller.center(center);
            DOM.setStyleAttribute(leftFade, "width", (scroller.getLeftPosition() + 14) + "px");
            DOM.setStyleAttribute(rightFade, "left", (scroller.getRightPosition() + 14 + 2) + "px");
            DOM.setStyleAttribute(rightFade, "width", (getOffsetWidth() - scroller.getRightPosition() - 28) + "px");

            dragStartX = scroller.getMouseOffset((Event) mouseEvent);

            setDateRangeTimer.cancel();
            setDateRangeTimer.schedule(100);
        }
    }

    private int scrollAdjustLeft = 0;
    private int scrollAdjustRight = 0;
    private Timer scrollTimer = new Timer() {
        @Override
        public void run() {

            // Check that we are not scrolling over the left side
            if (scroller.getLeftPosition() + scrollAdjustLeft >= 0) {
                scroller.adjustLeftSideSize(scrollAdjustLeft);
            } else {
                scroller.setLeftPosition(0);
            }

            // Check that we are not scrolling over the right side

            if (scroller.getRightPosition() + scrollAdjustRight <= getCanvasWidth() + 2) {
                scroller.adjustRightSideSize(scrollAdjustRight);
            }

            // Update
            refreshSelection();
            scrollAdjustLeft = 0;
            scrollAdjustRight = 0;
        }
    };

    public void onMouseWheel(MouseWheelEvent event) {
        NativeEvent mouseEvent = event.getNativeEvent();
        event.preventDefault();

        boolean up = mouseEvent.getMouseWheelVelocityY() > 0;

        // Only apply event to scroller
        if (!scroller.isMouseOverScrollArea((Event) mouseEvent)) {
            return;
        }

        sizeAdjustLeft = true;
        sizeAdjustRight = true;

        if (up) {
            scrollAdjustLeft += -5;
            scrollAdjustRight += 10;
        } else {
            scrollAdjustLeft += 5;
            scrollAdjustRight += -10;
        }

        scrollTimer.cancel();
        scrollTimer.schedule(50);
    }

    /**
     * Refreshes selection, by recalculating position
     */
    public void refreshSelection() {
        if (isVisible()) {
            selectedStartDate = calculateStartPoint();
            selectedEndDate = calculateEndPoint();
        } else {
            selectedStartDate = timelineWidget.getStartDate();
            selectedEndDate = timelineWidget.getEndDate();
        }

        setDateRangeTimer.cancel();
        setDateRangeTimer.schedule(100);

        if (!mouseDown) {
            scroller.lockSize();

            DOM.setStyleAttribute(leftFade, "width", (scroller.getLeftPosition() + 14) + "px");
            DOM.setStyleAttribute(rightFade, "left", (scroller.getRightPosition() + 14 + 2) + "px");
            DOM.setStyleAttribute(rightFade, "width", (getOffsetWidth() - scroller.getRightPosition() - 28) + "px");

            sizeAdjustLeft = false;
            sizeAdjustRight = false;

            timelineWidget.fireDateRangeChangedEvent();
        }
    }

    public void onPreviewNativeEvent(NativePreviewEvent event) {
        // Monitor mouse button state
        if (event.getTypeInt() == Event.ONMOUSEUP
                && event.getNativeEvent().getButton() == NativeEvent.BUTTON_LEFT) {
            mouseDown = false;
            if (mouseIsActive) {
                onMouseUp(null);
            }
        } else if (event.getTypeInt() == Event.ONMOUSEDOWN
                && event.getNativeEvent().getButton() == NativeEvent.BUTTON_LEFT) {
            mouseDown = true;
        }
    }

    public void onDoubleClick(DoubleClickEvent event) {
        NativeEvent mouseEvent = event.getNativeEvent();
        if (scroller.isMouseOverScrollElement((Event) mouseEvent)
                || scroller.isMouseOverScrollArea((Event) mouseEvent)) {
            if (mouseEvent.getButton() == NativeEvent.BUTTON_LEFT) {
                scroller.setLeftPosition(0);
                scroller.setRightPosition(getOffsetWidth() - 28 - 2);
                refreshSelection();
            }
        }
    }
}