org.vaadin.tltv.multiscrolltable.client.ui.ContentPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.vaadin.tltv.multiscrolltable.client.ui.ContentPanel.java

Source

/*
 * Copyright 2013 Tomi Virtanen
 *
 * Licensed 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.vaadin.tltv.multiscrolltable.client.ui;

import java.util.LinkedList;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;

public class ContentPanel extends FlowPanel implements ScrollableContent {

    private final ScrollHandlerWidget scrollHandlerWidget;
    private Scrollable relatedHorizontalScrollable;

    private final RowContainer rowContainer;
    private HeaderContainer headerContainer;

    private ScrollPanel scrollPanel;

    private SimplePanel content;
    private FlowPanel contentForRows;

    boolean visibleScrollBarY;

    private int contentTop;

    private int totalRowCount = 0;

    /*
     * When visibleScrollBarY is false, we have to store vertical scroll
     * position here instead of trusting ScrollTable. ScrollTable's vertical
     * scroll bar is hidden when visibleScrollBarY is false. Which means that
     * ScrollTable gives zero scrollTop f.ex. when scrolling horizontally.
     */
    private int verticalScrollPos = 0;
    private boolean nextScrollEventIsSilent = false;
    private int prevHorizontalScrollpos = 0;
    private int prevVerticalScrollpos = 0;

    private final ScrollHandler scrollhandler = new ScrollHandler() {
        @Override
        public void onScroll(ScrollEvent event) {
            int scrollTop = getScrollTop();
            verticalScrollPos = scrollTop;
            boolean horScrolling = prevHorizontalScrollpos != scrollPanel.getHorizontalScrollPosition();
            boolean verScrolling = prevVerticalScrollpos != scrollTop;
            prevHorizontalScrollpos = scrollPanel.getHorizontalScrollPosition();
            prevVerticalScrollpos = scrollTop;
            // Enable silent scrolling only if nextScrollEventIsSilent is
            // true or content is scrolling only horizontally.
            boolean silent = nextScrollEventIsSilent || (!verScrolling && horScrolling);
            scroll(silent, scrollPanel.getHorizontalScrollPosition(), scrollTop, false);
            nextScrollEventIsSilent = false;
        }
    };

    public ContentPanel(ScrollHandlerWidget scrollHandlerWidget, boolean visibleScrollBarY) {
        this(new DefaultRowContainer(), scrollHandlerWidget, visibleScrollBarY);
    }

    public ContentPanel(RowContainer rowFactory, ScrollHandlerWidget scrollHandlerWidget,
            boolean visibleScrollBarY) {
        rowContainer = rowFactory;
        this.scrollHandlerWidget = scrollHandlerWidget;
        setStylePrimaryName("v-ct-content");
        initLayout(visibleScrollBarY);
    }

    private void initLayout(boolean visibleScrollBarY) {
        this.visibleScrollBarY = visibleScrollBarY;
        content = new SimplePanel();
        content.setStylePrimaryName("v-ct-content-panel");

        contentForRows = new FlowPanel();
        contentForRows.getElement().getStyle().setPosition(Position.RELATIVE);
        contentForRows.setStylePrimaryName("v-ct-content-panel-for-rows");
        content.setWidget(contentForRows);

        scrollPanel = new ScrollPanel();
        scrollPanel.setStylePrimaryName("v-ct-scroll-panel");
        scrollPanel.setWidget(content);

        if (!visibleScrollBarY) {
            scrollPanel.getElement().getStyle().setProperty("overflowY", "hidden");
            scrollPanel.getElement().getStyle().setProperty("overflowX", "auto");
            sinkEvents(Event.ONMOUSEWHEEL);
        } else {
            scrollPanel.getElement().getStyle().setOverflow(Overflow.AUTO);
        }

        scrollPanel.addScrollHandler(scrollhandler);

        // Add one element for measuring row height
        HTML measure = new HTML();
        measure.setStylePrimaryName("v-ct-row");
        contentForRows.add(measure);

        rowContainer.setRelatedPanel(contentForRows);
        rowContainer.setScrollableContent(this);

        add(scrollPanel);
    }

    @Override
    public void onBrowserEvent(Event event) {
        if (DOM.eventGetType(event) == Event.ONMOUSEWHEEL) {
            GWT.log("Scrolling over content panel:  " + event.getMouseWheelVelocityY() * 15);
            scrollVertically(event.getMouseWheelVelocityY() * 15, true, false);
        } else {
            super.onBrowserEvent(event);
        }
    }

    public int getMeasuredRowHeight() {
        if (rowContainer.getRowHeight() < 0) {
            rowContainer.setRowHeight(contentForRows.getWidget(0).getElement().getClientHeight());
        }
        return rowContainer.getRowHeight();
    }

    public void initContent(int totalRowCount) {
        rowContainer.setReConstruct(true);

        this.totalRowCount = totalRowCount;
        // Measure row height before destroying measure element
        getMeasuredRowHeight();

        // Clear content
        clearContentAndSetReconstructFlagOn();

        updateContentHeight(totalRowCount);
    }

    public void clearContentAndSetReconstructFlagOn() {
        while (contentForRows.getWidgetCount() > 0) {
            contentForRows.remove(0);
        }
        rowContainer.getRows().clear();
        rowContainer.setReConstruct(true);
    }

    public void updateContent(UIDL uidl) {
        if (uidl == null) {
            return;
        }

        int rowCount = uidl.getChildCount();
        int columnCount = (headerContainer != null) ? headerContainer.getColumnCount() : 0;
        if (rowCount == 0 || columnCount == 0) {
            return;
        }

        for (int i = 0; i < rowCount; i++) {
            UIDL rowUidl = uidl.getChildUIDL(i);

            Row row = rowContainer.createRow(i, rowUidl);
        }

        updateRowContentTop();
        setInternalContentTop();

        int[] widths = calculateMinWidthsForColumns(rowContainer.getRows());
        // Update headerContainer column widths by the cell widths
        if (headerContainer != null) {
            widths = headerContainer.setColumnMinWidths(widths);
            setColumnMinWidths(rowContainer.getRows(), widths);
        }

        rowContainer.setReConstruct(false);
    }

    private int[] calculateMinWidthsForColumns(List<Row> rows) {
        int[] widths = new int[headerContainer.getColumnCount()];
        int index = 0;
        for (Row r : rows) {
            index = 0;
            for (Cell c : r.getCells()) {
                widths[index] = Math.max(widths[index], c.getMinWidth());
                index++;
            }
        }
        return widths;
    }

    private void setColumnMinWidths(List<Row> rows, int[] widths) {
        int index = 0;
        int w;
        int minRowWidth = 0;
        LinkedList<Cell> cells;
        for (Row r : rows) {
            index = 0;
            minRowWidth = 0;
            cells = r.getCells();
            for (Cell c : cells) {
                w = c.getOffsetWidth();
                if (widths[index] != w) {
                    w = widths[index];
                    c.setWidth(w + "px");
                }
                minRowWidth += w;
                index++;
            }
            r.getElement().getStyle().setProperty("minWidth", minRowWidth + "px");
        }
    }

    public int getCalculatedContentTop() {
        if (!visibleScrollBarY) {
            return contentTop - verticalScrollPos;
        }
        return contentTop;
    }

    private void updateContentHeight(int totalRows) {
        int contentHeight = totalRows * getMeasuredRowHeight();
        content.setHeight(contentHeight + "px");
    }

    public HeaderContainer getHeaderContainer() {
        return headerContainer;
    }

    /**
     * Set a header container that can be used to get header related
     * information.
     * 
     * @param headerContainer
     */
    public void setHeaderContainer(HeaderContainer headerContainer) {
        this.headerContainer = headerContainer;
        rowContainer.setHeaderContainer(headerContainer);
    }

    /**
     * Set a related object that needs to be updated during horizontal
     * scrolling.
     * 
     * @param relatedHorizontalScrollable
     */
    public void setRelatedHorizontalScrollable(Scrollable relatedHorizontalScrollable) {
        this.relatedHorizontalScrollable = relatedHorizontalScrollable;
    }

    public int getContentTop() {
        return contentTop;
    }

    public void setContentTop(int top) {
        contentTop = top;
        setInternalContentTop();
    }

    /**
     * Update content top if vertical scroll bar is not visible.
     */
    private void updateContentTop() {
        if (visibleScrollBarY) {
            return;
        }

        setInternalContentTop();
    }

    private void setInternalContentTop() {
        // Update content tops
        contentForRows.getElement().getStyle().setTop(getCalculatedContentTop(), Unit.PX);
    }

    private void updateRowContentTop() {
        // Calculate row layout's top offset
        int scrollTop = getScrollTop();
        contentTop = scrollTop - (scrollTop % rowContainer.getRowHeight());
        // Notice buffer size
        int bufferHeight = scrollHandlerWidget.getBufferSize() * rowContainer.getRowHeight();
        if ((contentTop - bufferHeight) < 0) {
            contentTop = 0;
        } else {
            contentTop -= bufferHeight;
        }
    }

    public void resetVerticalScrollPosition() {
        scrollPanel.setVerticalScrollPosition(verticalScrollPos);
    }

    @Override
    public void setScrollTop(int verticalScrollPosition) {
        verticalScrollPos = verticalScrollPosition;
        if (visibleScrollBarY) {
            scrollPanel.setVerticalScrollPosition(verticalScrollPosition);
        }
    }

    @Override
    public void setScrollTop(int verticalScrollPosition, boolean silentScroll) {
        nextScrollEventIsSilent = silentScroll;
        verticalScrollPos = verticalScrollPosition;
        updateContentTop();
        if (!visibleScrollBarY) {
            scroll(silentScroll, getScrollLeft(), getScrollTop(), false);
        } else {
            // Launches a scroll event, which call scroll(...)
            scrollPanel.setVerticalScrollPosition(verticalScrollPosition);
        }
    }

    @Override
    public void setScrollLeft(int horizontalScrollPosition) {
        nextScrollEventIsSilent = true;
        scrollPanel.setHorizontalScrollPosition(horizontalScrollPosition);
    }

    /** Returns true, if this content panel shouldn't have vertical scroll bar. */
    public boolean isVisibleScrollBarY() {
        return visibleScrollBarY;
    }

    /*
     * Launches a scroll event. See VCustomScrollTable.scrollContent(...)
     * JavaDoc for variable descriptions.
     */
    private void scroll(boolean silent, int horScrollPos, int verScrollPos, boolean forceReset) {
        scrollHandlerWidget.scrollContent(this, horScrollPos, verScrollPos, forceReset, silent);
    }

    public void scrollVertically(int yAxisDelta, boolean byContentTop, boolean forceReset) {
        if (scrollPanel == null || !scrollHandlerWidget.isVerticalScrollbarVisible()) {
            return;
        }

        int p = getScrollTop();
        int max = getMaxScrollPosition();
        p += yAxisDelta;
        if (p < 0) {
            p = 0;
        } else if (p > max) {
            p = max;
        }
        verticalScrollPos = p;

        if (byContentTop) {
            updateContentTop();
            scroll(false, getScrollLeft(), getScrollTop(), forceReset);
        } else {
            scrollPanel.setVerticalScrollPosition(verticalScrollPos);
        }

    }

    public int getMaxScrollPosition() {
        int sh = scrollPanel.getElement().getScrollHeight();
        boolean hScrollbarVisible = scrollHandlerWidget.isHorizontalScrollbarVisible();
        int sph = scrollPanel.getOffsetHeight() - (hScrollbarVisible ? Util.getNativeScrollbarSize() : 0);
        return sh - sph;
    }

    @Override
    public boolean isHorizontalScrollbarVisible() {
        int w = scrollPanel.getElement().getPropertyInt("clientWidth");
        int cW = content.getElement().getPropertyInt("clientWidth");
        boolean horScrollbarVisible = w < cW;
        return horScrollbarVisible;
    }

    @Override
    public boolean isVerticalScrollbarVisible() {
        int h = scrollPanel.getElement().getPropertyInt("clientHeight");
        boolean verScrollbarVisible = h < content.getElement().getOffsetHeight();
        return verScrollbarVisible;
    }

    @Override
    public int getScrollLeft() {
        return scrollPanel.getHorizontalScrollPosition();
    }

    @Override
    public int getScrollTop() {
        int p = 0;
        if (!visibleScrollBarY) {
            p = verticalScrollPos;
        } else {
            p = scrollPanel.getVerticalScrollPosition();
        }

        int max = getMaxScrollPosition();
        if (p < 0) {
            p = 0;
        } else if (p > max) {
            p = max;
        }
        return p;
    }

    @Override
    public Scrollable getRelatedScrollable() {
        return relatedHorizontalScrollable;
    }

    public void setHeight(int contentHeight) {
        setHeight(contentHeight + "px");
        scrollPanel.setHeight(contentHeight + "px");
    }

}