diff options
65 files changed, 0 insertions, 14682 deletions
diff --git a/WebContent/VAADIN/themes/base/base.scss b/WebContent/VAADIN/themes/base/base.scss index 58274edacb..fd3c5d067d 100644 --- a/WebContent/VAADIN/themes/base/base.scss +++ b/WebContent/VAADIN/themes/base/base.scss @@ -15,10 +15,8 @@ @import "inlinedatefield/inlinedatefield.scss"; @import "dragwrapper/dragwrapper.scss"; @import "embedded/embedded.scss"; -@import "escalator/escalator.scss"; @import "fonts/fonts.scss"; @import "formlayout/formlayout.scss"; -@import "grid/grid.scss"; @import "gridlayout/gridlayout.scss"; @import "label/label.scss"; @import "link/link.scss"; @@ -92,9 +90,7 @@ $line-height: normal; @include base-inline-datefield; @include base-dragwrapper; @include base-embedded; - @include base-escalator; @include base-formlayout; - @include base-grid; @include base-gridlayout; @include base-label; @include base-link; diff --git a/WebContent/VAADIN/themes/base/escalator/escalator.scss b/WebContent/VAADIN/themes/base/escalator/escalator.scss deleted file mode 100644 index 6f85a541ee..0000000000 --- a/WebContent/VAADIN/themes/base/escalator/escalator.scss +++ /dev/null @@ -1,117 +0,0 @@ -@mixin base-escalator($primaryStyleName : v-escalator) { - -$background-color: white; -$border-color: #aaa; - -.#{$primaryStyleName} { - position: relative; - background-color: $background-color; -} - -.#{$primaryStyleName}-scroller { - position: absolute; - overflow: auto; - z-index: 20; -} - -.#{$primaryStyleName}-scroller-horizontal { - left: 0; /* Left position adjusted to align with frozen columns */ - right: 0; - bottom: 0; - overflow-y: hidden; - -ms-overflow-y: hidden; -} - -.#{$primaryStyleName}-scroller-vertical { - right: 0; - top: 0; /* this will be overridden by code, but it's a good default behavior */ - bottom: 0; /* this will be overridden by code, but it's a good default behavior */ - overflow-x: hidden; - -ms-overflow-x: hidden; -} - -.#{$primaryStyleName}-tablewrapper { - position: absolute; - overflow: hidden; -} - -.#{$primaryStyleName}-tablewrapper > table { - border-spacing: 0; - table-layout: fixed; - width: inherit; /* a decent default fallback */ -} - -.#{$primaryStyleName}-header, -.#{$primaryStyleName}-body, -.#{$primaryStyleName}-footer { - position: absolute; - left: 0; - width: inherit; - z-index: 10; -} - -.#{$primaryStyleName}-header { top: 0; } -.#{$primaryStyleName}-footer { bottom: 0; } - -.#{$primaryStyleName}-body { - z-index: 0; - top: 0; - - .#{$primaryStyleName}-row { - position: absolute; - top: 0; - left: 0; - } -} - -.#{$primaryStyleName}-row { - display: block; - - .v-ie8 & { - /* IE8 doesn't let table rows be longer than body only with display block. Moar hax. */ - float: left; - clear: left; - - /* - * The inline style of margin-top from the <tbody> to offset the header's dimension is, - * for some strange reason, inherited into each contained <tr>. - * We need to cancel it: - */ - margin-top: 0; - } - - > td, > th { - /* IE8 likes the bgcolor here instead of on the row */ - background-color: $background-color; - } -} - - -.#{$primaryStyleName}-row { - width: inherit; -} - -.#{$primaryStyleName}-cell { - display: block; - float: left; - border: 1px solid $border-color; - padding: 2px; - white-space: nowrap; - -moz-box-sizing: border-box; - box-sizing: border-box; - overflow:hidden; - - /* - * Because Vaadin changes the font size after the initial render, we - * need to mention the font size here explicitly, otherwise automatic - * row height detection gets broken. - */ - font-size: $font-size; -} - -.#{$primaryStyleName}-cell.frozen { - position: relative; - z-index: 0; -} - -} diff --git a/WebContent/VAADIN/themes/base/grid/grid.scss b/WebContent/VAADIN/themes/base/grid/grid.scss deleted file mode 100644 index 9f7a2d8664..0000000000 --- a/WebContent/VAADIN/themes/base/grid/grid.scss +++ /dev/null @@ -1,3 +0,0 @@ -@mixin base-grid($primaryStyleName : v-grid) { - @include base-escalator($primaryStyleName); -}
\ No newline at end of file diff --git a/WebContent/VAADIN/themes/reindeer-tests/styles.css b/WebContent/VAADIN/themes/reindeer-tests/styles.css index 9dd88707d1..679de01b9c 100644 --- a/WebContent/VAADIN/themes/reindeer-tests/styles.css +++ b/WebContent/VAADIN/themes/reindeer-tests/styles.css @@ -32,7 +32,3 @@ .popup-style .v-datefield-calendarpanel-body { background: yellow; } - -#escalator .v-escalator-body .v-escalator-cell { - height: 50px; -}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java deleted file mode 100644 index 127eb80696..0000000000 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.data; - -import java.util.HashMap; -import java.util.List; - -import com.google.gwt.core.client.Scheduler; -import com.google.gwt.core.client.Scheduler.ScheduledCommand; -import com.vaadin.client.Profiler; -import com.vaadin.shared.ui.grid.Range; - -/** - * Base implementation for data sources that fetch data from a remote system. - * This class takes care of caching data and communicating with the data source - * user. An implementation of this class should override - * {@link #requestRows(int, int)} to trigger asynchronously loading of data. - * When data is received from the server, new row data should be passed to - * {@link #setRowData(int, List)}. {@link #setEstimatedSize(int)} should be used - * based on estimations of how many rows are available. - * - * @since 7.2 - * @author Vaadin Ltd - * @param <T> - * the row type - */ -public abstract class AbstractRemoteDataSource<T> implements DataSource<T> { - - private boolean requestPending = false; - - private boolean coverageCheckPending = false; - - private Range requestedAvailability = Range.between(0, 0); - - private Range cached = Range.between(0, 0); - - private final HashMap<Integer, T> rowCache = new HashMap<Integer, T>(); - - private DataChangeHandler dataChangeHandler; - - private int estimatedSize; - - private final ScheduledCommand coverageChecker = new ScheduledCommand() { - @Override - public void execute() { - coverageCheckPending = false; - checkCacheCoverage(); - } - }; - - /** - * Sets the estimated number of rows in the data source. - * - * @param estimatedSize - * the estimated number of available rows - */ - protected void setEstimatedSize(int estimatedSize) { - // TODO update dataChangeHandler if size changes - this.estimatedSize = estimatedSize; - } - - private void ensureCoverageCheck() { - if (!coverageCheckPending) { - coverageCheckPending = true; - Scheduler.get().scheduleDeferred(coverageChecker); - } - } - - @Override - public void ensureAvailability(int firstRowIndex, int numberOfRows) { - requestedAvailability = Range.withLength(firstRowIndex, numberOfRows); - - /* - * Don't request any data right away since the data might be included in - * a message that has been received but not yet fully processed. - */ - ensureCoverageCheck(); - } - - private void checkCacheCoverage() { - if (requestPending) { - // Anyone clearing requestPending should run this method again - return; - } - - Profiler.enter("AbstractRemoteDataSource.checkCacheCoverage"); - - if (!requestedAvailability.intersects(cached) || cached.isEmpty()) { - /* - * Simple case: no overlap between cached data and needed data. - * Clear the cache and request new data - */ - rowCache.clear(); - cached = Range.between(0, 0); - - handleMissingRows(requestedAvailability); - } else { - discardStaleCacheEntries(); - - // Might need more rows -> request them - Range[] availabilityPartition = requestedAvailability - .partitionWith(cached); - handleMissingRows(availabilityPartition[0]); - handleMissingRows(availabilityPartition[2]); - } - - Profiler.leave("AbstractRemoteDataSource.checkCacheCoverage"); - } - - private void discardStaleCacheEntries() { - Range[] cacheParition = cached.partitionWith(requestedAvailability); - dropFromCache(cacheParition[0]); - cached = cacheParition[1]; - dropFromCache(cacheParition[2]); - } - - private void dropFromCache(Range range) { - for (int i = range.getStart(); i < range.getEnd(); i++) { - rowCache.remove(Integer.valueOf(i)); - } - } - - private void handleMissingRows(Range range) { - if (range.isEmpty()) { - return; - } - requestPending = true; - requestRows(range.getStart(), range.length()); - } - - /** - * Triggers fetching rows from the remote data source. - * {@link #setRowData(int, List)} should be invoked with data for the - * requested rows when they have been received. - * - * @param firstRowIndex - * the index of the first row to fetch - * @param numberOfRows - * the number of rows to fetch - */ - protected abstract void requestRows(int firstRowIndex, int numberOfRows); - - @Override - public int getEstimatedSize() { - return estimatedSize; - } - - @Override - public T getRow(int rowIndex) { - return rowCache.get(Integer.valueOf(rowIndex)); - } - - @Override - public void setDataChangeHandler(DataChangeHandler dataChangeHandler) { - this.dataChangeHandler = dataChangeHandler; - - if (dataChangeHandler != null && !cached.isEmpty()) { - // Push currently cached data to the implementation - dataChangeHandler.dataUpdated(cached.getStart(), cached.length()); - } - } - - /** - * Informs this data source that updated data has been sent from the server. - * - * @param firstRowIndex - * the index of the first received row - * @param rowData - * a list of rows, starting from <code>firstRowIndex</code> - */ - protected void setRowData(int firstRowIndex, List<T> rowData) { - requestPending = false; - - Profiler.enter("AbstractRemoteDataSource.setRowData"); - - Range received = Range.withLength(firstRowIndex, rowData.size()); - - Range[] partition = received.partitionWith(requestedAvailability); - - Range newUsefulData = partition[1]; - if (!newUsefulData.isEmpty()) { - // Update the parts that are actually inside - for (int i = newUsefulData.getStart(); i < newUsefulData.getEnd(); i++) { - rowCache.put(Integer.valueOf(i), rowData.get(i - firstRowIndex)); - } - - if (dataChangeHandler != null) { - Profiler.enter("AbstractRemoteDataSource.setRowData notify dataChangeHandler"); - dataChangeHandler.dataUpdated(newUsefulData.getStart(), - newUsefulData.length()); - Profiler.leave("AbstractRemoteDataSource.setRowData notify dataChangeHandler"); - } - - // Potentially extend the range - if (cached.isEmpty()) { - cached = newUsefulData; - } else { - discardStaleCacheEntries(); - - /* - * everything might've become stale so we need to re-check for - * emptiness. - */ - if (!cached.isEmpty()) { - cached = cached.combineWith(newUsefulData); - } else { - cached = newUsefulData; - } - } - } - - if (!partition[0].isEmpty() || !partition[2].isEmpty()) { - /* - * FIXME - * - * Got data that we might need in a moment if the container is - * updated before the widget settings. Support for this will be - * implemented later on. - */ - } - - // Eventually check whether all needed rows are now available - ensureCoverageCheck(); - - Profiler.leave("AbstractRemoteDataSource.setRowData"); - } - - /** - * Informs this data source that the server has removed data. - * - * @param firstRowIndex - * the index of the first removed row - * @param count - * the number of removed rows, starting from - * <code>firstRowIndex</code> - */ - protected void removeRowData(int firstRowIndex, int count) { - Profiler.enter("AbstractRemoteDataSource.removeRowData"); - - // pack the cached data - for (int i = 0; i < count; i++) { - Integer oldIndex = Integer.valueOf(firstRowIndex + count + i); - if (rowCache.containsKey(oldIndex)) { - Integer newIndex = Integer.valueOf(firstRowIndex + i); - rowCache.put(newIndex, rowCache.remove(oldIndex)); - } - } - - Range removedRange = Range.withLength(firstRowIndex, count); - if (removedRange.intersects(cached)) { - Range[] partitions = cached.partitionWith(removedRange); - Range remainsBefore = partitions[0]; - Range transposedRemainsAfter = partitions[2].offsetBy(-removedRange - .length()); - cached = remainsBefore.combineWith(transposedRemainsAfter); - } - estimatedSize -= count; - dataChangeHandler.dataRemoved(firstRowIndex, count); - checkCacheCoverage(); - - Profiler.leave("AbstractRemoteDataSource.removeRowData"); - } - - /** - * Informs this data source that new data has been inserted from the server. - * - * @param firstRowIndex - * the destination index of the new row data - * @param count - * the number of rows inserted - */ - protected void insertRowData(int firstRowIndex, int count) { - Profiler.enter("AbstractRemoteDataSource.insertRowData"); - - if (cached.contains(firstRowIndex)) { - int oldCacheEnd = cached.getEnd(); - /* - * We need to invalidate the cache from the inserted row onwards, - * since the cache wants to be a contiguous range. It doesn't - * support holes. - * - * If holes were supported, we could shift the higher part of - * "cached" and leave a hole the size of "count" in the middle. - */ - cached = cached.splitAt(firstRowIndex)[0]; - - for (int i = firstRowIndex; i < oldCacheEnd; i++) { - rowCache.remove(Integer.valueOf(i)); - } - } - - else if (firstRowIndex < cached.getStart()) { - Range oldCached = cached; - cached = cached.offsetBy(count); - - for (int i = 0; i < rowCache.size(); i++) { - Integer oldIndex = Integer.valueOf(oldCached.getEnd() - i); - Integer newIndex = Integer.valueOf(cached.getEnd() - i); - rowCache.put(newIndex, rowCache.remove(oldIndex)); - } - } - - estimatedSize += count; - dataChangeHandler.dataAdded(firstRowIndex, count); - checkCacheCoverage(); - - Profiler.leave("AbstractRemoteDataSource.insertRowData"); - } -} diff --git a/client/src/com/vaadin/client/data/DataChangeHandler.java b/client/src/com/vaadin/client/data/DataChangeHandler.java deleted file mode 100644 index 4c4cc7656d..0000000000 --- a/client/src/com/vaadin/client/data/DataChangeHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.data; - -/** - * Callback interface used by {@link DataSource} to inform its user about - * updates to the data. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface DataChangeHandler { - /** - * Called when the contents of the data source has changed. If the number of - * rows has changed or if rows have been moved around, - * {@link #dataAdded(int, int)} or {@link #dataRemoved(int, int)} should - * ideally be used instead. - * - * @param firstRowIndex - * the index of the first changed row - * @param numberOfRows - * the number of changed rows - */ - public void dataUpdated(int firstRowIndex, int numberOfRows); - - /** - * Called when rows have been removed from the data source. - * - * @param firstRowIndex - * the index that the first removed row had prior to removal - * @param numberOfRows - * the number of removed rows - */ - public void dataRemoved(int firstRowIndex, int numberOfRows); - - /** - * Called when the new rows have been added to the container. - * - * @param firstRowIndex - * the index of the first added row - * @param numberOfRows - * the number of added rows - */ - public void dataAdded(int firstRowIndex, int numberOfRows); -} diff --git a/client/src/com/vaadin/client/data/DataSource.java b/client/src/com/vaadin/client/data/DataSource.java deleted file mode 100644 index 9179b6d03d..0000000000 --- a/client/src/com/vaadin/client/data/DataSource.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.data; - -/** - * Source of data for widgets showing lazily loaded data based on indexable - * items (e.g. rows) of a specified type. The data source is a lazy view into a - * larger data set. - * - * @since 7.2 - * @author Vaadin Ltd - * @param <T> - * the row type - */ -public interface DataSource<T> { - /** - * Informs the data source that data for the given range is needed. A data - * source only has one active region at a time, so calling this method - * discards the previously set range. - * <p> - * This method triggers lazy loading of data if necessary. The change - * handler registered using {@link #setDataChangeHandler(DataChangeHandler)} - * is informed when new data has been loaded. - * - * @param firstRowIndex - * the index of the first needed row - * @param numberOfRows - * the number of needed rows - */ - public void ensureAvailability(int firstRowIndex, int numberOfRows); - - /** - * Retrieves the data for the row at the given index. If the row data is not - * available, returns <code>null</code>. - * <p> - * This method does not trigger loading of unavailable data. - * {@link #ensureAvailability(int, int)} should be used to signal what data - * will be needed. - * - * @param rowIndex - * the index of the row to retrieve data for - * @return data for the row; or <code>null</code> if no data is available - */ - public T getRow(int rowIndex); - - /** - * Returns the current best guess for the number of rows in the container. - * - * @return the current estimation of the container size - */ - public int getEstimatedSize(); - - /** - * Sets a data change handler to inform when data is updated, added or - * removed. - * - * @param dataChangeHandler - * the data change handler - */ - public void setDataChangeHandler(DataChangeHandler dataChangeHandler); - -} diff --git a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java deleted file mode 100644 index 4d22c10197..0000000000 --- a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.data; - -import java.util.List; - -import com.vaadin.client.ServerConnector; -import com.vaadin.client.extensions.AbstractExtensionConnector; -import com.vaadin.client.ui.grid.GridConnector; -import com.vaadin.shared.data.DataProviderRpc; -import com.vaadin.shared.data.DataProviderState; -import com.vaadin.shared.data.DataRequestRpc; -import com.vaadin.shared.ui.Connect; - -/** - * Connects a Vaadin server-side container data source to a Grid. This is - * currently implemented as an Extension hardcoded to support a specific - * connector type. This will be changed once framework support for something - * more flexible has been implemented. - * - * @since 7.2 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.data.RpcDataProviderExtension.class) -public class RpcDataSourceConnector extends AbstractExtensionConnector { - - private final AbstractRemoteDataSource<String[]> dataSource = new AbstractRemoteDataSource<String[]>() { - @Override - protected void requestRows(int firstRowIndex, int numberOfRows) { - getRpcProxy(DataRequestRpc.class).requestRows(firstRowIndex, - numberOfRows); - } - }; - - @Override - protected void extend(ServerConnector target) { - dataSource.setEstimatedSize(getState().containerSize); - ((GridConnector) target).getWidget().setDataSource(dataSource); - - registerRpc(DataProviderRpc.class, new DataProviderRpc() { - @Override - public void setRowData(int firstRow, List<String[]> rows) { - dataSource.setRowData(firstRow, rows); - } - - @Override - public void removeRowData(int firstRow, int count) { - dataSource.removeRowData(firstRow, count); - } - - @Override - public void insertRowData(int firstRow, int count) { - dataSource.insertRowData(firstRow, count); - } - }); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.client.ui.AbstractConnector#getState() - */ - @Override - public DataProviderState getState() { - return (DataProviderState) super.getState(); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/Cell.java b/client/src/com/vaadin/client/ui/grid/Cell.java deleted file mode 100644 index 3d42f082a6..0000000000 --- a/client/src/com/vaadin/client/ui/grid/Cell.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import com.google.gwt.dom.client.Element; -import com.google.gwt.user.client.ui.HasOneWidget; - -/** - * A representation of a single cell. - * <p> - * A Cell instance will be provided to the {@link EscalatorUpdater} responsible - * for rendering the cells in a certain {@link RowContainer}. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface Cell extends HasOneWidget { - - /** - * Gets the index of the row this cell is in. - * - * @return the index of the row this cell is in - */ - public int getRow(); - - /** - * Gets the index of the column this cell is in. - * - * @return the index of the column this cell is in - */ - public int getColumn(); - - /** - * Gets the root element for this cell. The {@link EscalatorUpdater} may - * update the class names of the element, add inline styles and freely - * modify the contents. - * <p> - * Avoid modifying the dimensions, positioning or colspan of the cell - * element. - * - * @return The root element for this cell. Never <code>null</code>. - */ - public Element getElement(); - - /** - * Sets the column span of the cell. - * <p> - * This will overwrite any possible "colspan" attribute in the current - * element (i.e. the object returned by {@link #getElement()}). This will - * also handle internal bookkeeping, skip the rendering of any affected - * adjacent cells, and make sure that the current cell's dimensions are - * handled correctly. - * - * @param numberOfCells - * the number of cells to span to the right, or <code>1</code> to - * unset any column spans - * @throws IllegalArgumentException - * if <code>numberOfCells < 1</code> - */ - public void setColSpan(int numberOfCells) throws IllegalArgumentException; -}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java b/client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java deleted file mode 100644 index 64104164cd..0000000000 --- a/client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -/** - * A representation of the columns in an instance of {@link Escalator}. - * - * @since 7.2 - * @author Vaadin Ltd - * @see Escalator#getColumnConfiguration() - */ -public interface ColumnConfiguration { - - /** - * Removes columns at a certain index. - * <p> - * If any of the removed columns were frozen, the number of frozen columns - * will be reduced by the number of the removed columns that were frozen. - * - * @param index - * the index of the first column to be removed - * @param numberOfColumns - * the number of rows to remove, starting from the index - * @throws IndexOutOfBoundsException - * if any integer in the range - * <code>[index..(index+numberOfColumns)]</code> is not an - * existing column index. - * @throws IllegalArgumentException - * if <code>numberOfColumns</code> is less than 1. - */ - public void removeColumns(int index, int numberOfColumns) - throws IndexOutOfBoundsException, IllegalArgumentException; - - /** - * Adds columns at a certain index. - * <p> - * The new columns will be inserted between the column at the index, and the - * column before (an index of 0 means that the columns are inserted at the - * beginning). Therefore, the columns at the index and afterwards will be - * moved to the right. - * <p> - * The contents of the inserted columns will be queried from the respective - * cell renderers in the header, body and footer. - * <p> - * If there are frozen columns and the first added column is to the left of - * the last frozen column, the number of frozen columns will be increased by - * the number of inserted columns. - * <p> - * <em>Note:</em> Only the contents of the inserted columns will be - * rendered. If inserting new columns affects the contents of existing - * columns, {@link RowContainer#refreshRows(int, int)} needs to be called as - * appropriate. - * - * @param index - * the index of the column before which new columns are inserted, - * or {@link #getColumnCount()} to add new columns at the end - * @param numberOfColumns - * the number of columns to insert after the <code>index</code> - * @throws IndexOutOfBoundsException - * if <code>index</code> is not an integer in the range - * <code>[0..{@link #getColumnCount()}]</code> - * @throws IllegalArgumentException - * if {@code numberOfColumns} is less than 1. - */ - public void insertColumns(int index, int numberOfColumns) - throws IndexOutOfBoundsException, IllegalArgumentException; - - /** - * Returns the number of columns in the escalator. - * - * @return the number of columns in the escalator - */ - public int getColumnCount(); - - /** - * Sets the number of leftmost columns that are not affected by horizontal - * scrolling. - * - * @param count - * the number of columns to freeze - * - * @throws IllegalArgumentException - * if the column count is < 0 or > the number of columns - * - */ - public void setFrozenColumnCount(int count) throws IllegalArgumentException; - - /** - * Get the number of leftmost columns that are not affected by horizontal - * scrolling. - * - * @return the number of frozen columns - */ - public int getFrozenColumnCount(); - - /** - * Sets (or unsets) an explicit width for a column. - * - * @param index - * the index of the column for which to set a width - * @param px - * the number of pixels the indicated column should be, or a - * negative number to let the escalator decide - * @throws IllegalArgumentException - * if <code>index</code> is not a valid column index - */ - public void setColumnWidth(int index, int px) - throws IllegalArgumentException; - - /** - * Returns the user-defined width of a column. - * - * @param index - * the index of the column for which to retrieve the width - * @return the column's width in pixels, or a negative number if the width - * is implicitly decided by the escalator - * @throws IllegalArgumentException - * if <code>index</code> is not a valid column index - */ - public int getColumnWidth(int index) throws IllegalArgumentException; - - /** - * Returns the actual width of a column. - * - * @param index - * the index of the column for which to retrieve the width - * @return the column's actual width in pixels - * @throws IllegalArgumentException - * if <code>index</code> is not a valid column index - */ - public int getColumnWidthActual(int index) throws IllegalArgumentException; -}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/grid/ColumnGroup.java b/client/src/com/vaadin/client/ui/grid/ColumnGroup.java deleted file mode 100644 index e48656bc6b..0000000000 --- a/client/src/com/vaadin/client/ui/grid/ColumnGroup.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import com.vaadin.client.ui.grid.renderers.TextRenderer; - -/** - * Column groups are used to group columns together for adding common auxiliary - * headers and footers. Columns groups are added to {@link ColumnGroupRow - * ColumnGroupRows}. - * - * @param <T> - * The row type of the grid. The row type is the POJO type from where - * the data is retrieved into the column cells. - * @since 7.2 - * @author Vaadin Ltd - */ -public class ColumnGroup<T> { - - /** - * The text shown in the header - */ - private String header; - - /** - * The text shown in the footer - */ - private String footer; - - /** - * Renders the header cells for the column group - */ - private Renderer<String> headerRenderer = new TextRenderer(); - - /** - * Renders the footer cells for the column group - */ - private Renderer<String> footerRenderer = new TextRenderer(); - - /** - * The columns included in the group when also accounting for subgroup - * columns - */ - private final List<GridColumn<?, T>> columns; - - /** - * The grid associated with the column group - */ - private final Grid<T> grid; - - /** - * Constructs a new column group - */ - ColumnGroup(Grid<T> grid, Collection<GridColumn<?, T>> columns) { - if (columns == null) { - throw new IllegalArgumentException( - "columns cannot be null. Pass an empty list instead."); - } - this.grid = grid; - this.columns = Collections - .unmodifiableList(new ArrayList<GridColumn<?, T>>(columns)); - } - - /** - * Gets the header text. - * - * @return the header text - */ - public String getHeaderCaption() { - return header; - } - - /** - * Sets the text shown in the header. - * - * @param header - * the header to set - */ - public void setHeaderCaption(String header) { - this.header = header; - grid.refreshHeader(); - } - - /** - * Gets the text shown in the footer. - * - * @return the text in the footer - */ - public String getFooterCaption() { - return footer; - } - - /** - * Sets the text displayed in the footer. - * - * @param footer - * the footer to set - */ - public void setFooterCaption(String footer) { - this.footer = footer; - grid.refreshFooter(); - } - - /** - * Returns all column in this group. It includes the subgroups columns as - * well. - * - * @return unmodifiable list of columns - */ - public List<GridColumn<?, T>> getColumns() { - return columns; - } - - /** - * Returns the renderer used for rendering the header cells - * - * @return a renderer that renders header cells - */ - public Renderer<String> getHeaderRenderer() { - return headerRenderer; - } - - /** - * Sets the renderer that renders header cells. - * - * @param renderer - * The renderer to use for rendering header cells. Must not be - * null. - * @throws IllegalArgumentException - * thrown when renderer is null - */ - public void setHeaderRenderer(Renderer<String> renderer) { - if (renderer == null) { - throw new IllegalArgumentException("Renderer cannot be null."); - } - this.headerRenderer = renderer; - grid.refreshHeader(); - } - - /** - * Returns the renderer used for rendering the footer cells - * - * @return a renderer that renders footer cells - */ - public Renderer<String> getFooterRenderer() { - return footerRenderer; - } - - /** - * Sets the renderer that renders footer cells. - * - * @param renderer - * The renderer to use for rendering footer cells. Must not be - * null. - * @throws IllegalArgumentException - * thrown when renderer is null - */ - public void setFooterRenderer(Renderer<String> renderer) { - if (renderer == null) { - throw new IllegalArgumentException("Renderer cannot be null."); - } - this.footerRenderer = renderer; - grid.refreshFooter(); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java b/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java deleted file mode 100644 index ebe4db508c..0000000000 --- a/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * A column group row represents an auxiliary header or footer row added to the - * grid. A column group row includes column groups that group columns together. - * - * @param <T> - * Row type - * @since 7.2 - * @author Vaadin Ltd - */ -public class ColumnGroupRow<T> { - - /** - * The column groups in this row - */ - private List<ColumnGroup<T>> groups = new ArrayList<ColumnGroup<T>>(); - - /** - * The grid associated with the column row - */ - private final Grid<T> grid; - - /** - * Is the header shown - */ - private boolean headerVisible = true; - - /** - * Is the footer shown - */ - private boolean footerVisible = false; - - /** - * Constructs a new column group row - * - * @param grid - * Grid associated with this column - * - */ - ColumnGroupRow(Grid<T> grid) { - this.grid = grid; - } - - /** - * Add a new group to the row by using column instances. - * - * @param columns - * The columns that should belong to the group - * @return a column group representing the collection of columns added to - * the group. - */ - public ColumnGroup<T> addGroup(GridColumn<?, T>... columns) - throws IllegalArgumentException { - - for (GridColumn<?, T> column : columns) { - if (isColumnGrouped(column)) { - throw new IllegalArgumentException("Column " - + String.valueOf(column.getHeaderCaption()) - + " already belongs to another group."); - } - } - - validateNewGroupProperties(Arrays.asList(columns)); - - ColumnGroup<T> group = new ColumnGroup<T>(grid, Arrays.asList(columns)); - groups.add(group); - grid.refreshHeader(); - grid.refreshFooter(); - return group; - } - - private void validateNewGroupProperties(Collection<GridColumn<?, T>> columns) { - - int rowIndex = grid.getColumnGroupRows().indexOf(this); - int parentRowIndex = rowIndex - 1; - - // Get the parent row of this row. - ColumnGroupRow<T> parentRow = null; - if (parentRowIndex > -1) { - parentRow = grid.getColumnGroupRows().get(parentRowIndex); - } - - if (parentRow == null) { - // A parentless row is always valid and is usually the first row - // added to the grid - return; - } - - for (GridColumn<?, T> column : columns) { - if (parentRow.hasColumnBeenGrouped(column)) { - /* - * If a property has been grouped in the parent row then all of - * the properties in the parent group also needs to be included - * in the child group for the groups to be valid - */ - ColumnGroup parentGroup = parentRow.getGroupForColumn(column); - if (!columns.containsAll(parentGroup.getColumns())) { - throw new IllegalArgumentException( - "Grouped properties overlaps previous grouping bounderies"); - } - } - } - } - - private boolean hasColumnBeenGrouped(GridColumn<?, T> column) { - return getGroupForColumn(column) != null; - } - - private ColumnGroup<T> getGroupForColumn(GridColumn<?, T> column) { - for (ColumnGroup<T> group : groups) { - if (group.getColumns().contains(column)) { - return group; - } - } - return null; - } - - /** - * Add a new group to the row by using other already greated groups - * - * @param groups - * The subgroups of the group. - * @return a column group representing the collection of columns added to - * the group. - * - */ - public ColumnGroup<T> addGroup(ColumnGroup<T>... groups) - throws IllegalArgumentException { - assert groups != null : "groups cannot be null"; - - Set<GridColumn<?, T>> columns = new HashSet<GridColumn<?, T>>(); - for (ColumnGroup<T> group : groups) { - columns.addAll(group.getColumns()); - } - - validateNewGroupProperties(columns); - - ColumnGroup<T> group = new ColumnGroup<T>(grid, columns); - this.groups.add(group); - grid.refreshHeader(); - grid.refreshFooter(); - return group; - } - - /** - * Removes a group from the row. - * - * @param group - * The group to remove - */ - public void removeGroup(ColumnGroup<T> group) { - groups.remove(group); - grid.refreshHeader(); - grid.refreshFooter(); - } - - /** - * Get the groups in the row - * - * @return unmodifiable list of groups in this row - */ - public List<ColumnGroup<T>> getGroups() { - return Collections.unmodifiableList(groups); - } - - /** - * Is the header visible for the row. - * - * @return <code>true</code> if header is visible - */ - public boolean isHeaderVisible() { - return headerVisible; - } - - /** - * Sets the header visible for the row. - * - * @param visible - * should the header be shown - */ - public void setHeaderVisible(boolean visible) { - headerVisible = visible; - grid.refreshHeader(); - } - - /** - * Is the footer visible for the row. - * - * @return <code>true</code> if footer is visible - */ - public boolean isFooterVisible() { - return footerVisible; - } - - /** - * Sets the footer visible for the row. - * - * @param visible - * should the footer be shown - */ - public void setFooterVisible(boolean visible) { - footerVisible = visible; - grid.refreshFooter(); - } - - /** - * Iterates all the column groups and checks if the columns alread has been - * added to a group. - */ - private boolean isColumnGrouped(GridColumn<?, T> column) { - for (ColumnGroup<T> group : groups) { - if (group.getColumns().contains(column)) { - return true; - } - } - return false; - } -} diff --git a/client/src/com/vaadin/client/ui/grid/Escalator.java b/client/src/com/vaadin/client/ui/grid/Escalator.java deleted file mode 100644 index 6112d6b139..0000000000 --- a/client/src/com/vaadin/client/ui/grid/Escalator.java +++ /dev/null @@ -1,4042 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.google.gwt.animation.client.AnimationScheduler; -import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; -import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle; -import com.google.gwt.core.client.Duration; -import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.core.client.Scheduler; -import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.dom.client.Node; -import com.google.gwt.dom.client.NodeList; -import com.google.gwt.dom.client.Style; -import com.google.gwt.dom.client.Style.Display; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.logging.client.LogConfiguration; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.UIObject; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.client.Profiler; -import com.vaadin.client.Util; -import com.vaadin.client.ui.grid.Escalator.JsniUtil.TouchHandlerBundle; -import com.vaadin.client.ui.grid.PositionFunction.AbsolutePosition; -import com.vaadin.client.ui.grid.PositionFunction.Translate3DPosition; -import com.vaadin.client.ui.grid.PositionFunction.TranslatePosition; -import com.vaadin.client.ui.grid.PositionFunction.WebkitTranslate3DPosition; -import com.vaadin.client.ui.grid.ScrollbarBundle.HorizontalScrollbarBundle; -import com.vaadin.client.ui.grid.ScrollbarBundle.VerticalScrollbarBundle; -import com.vaadin.shared.ui.grid.Range; -import com.vaadin.shared.ui.grid.ScrollDestination; -import com.vaadin.shared.util.SharedUtil; - -/*- - - Maintenance Notes! Reading these might save your day. - - - == Row Container Structure - - AbstractRowContainer - |-- AbstractStaticRowContainer - | |-- HeaderRowContainer - | `-- FooterContainer - `-- BodyRowContainer - - AbstractRowContainer is intended to contain all common logic - between RowContainers. It manages the bookkeeping of row - count, makes sure that all individual cells are rendered - the same way, and so on. - - AbstractStaticRowContainer has some special logic that is - required by all RowContainers that don't scroll (hence the - word "static"). HeaderRowContainer and FooterRowContainer - are pretty thin special cases of a StaticRowContainer - (mostly relating to positioning of the root element). - - BodyRowContainer could also be split into an additional - "AbstractScrollingRowContainer", but I felt that no more - inner classes were needed. So it contains both logic - required for making things scroll about, and equivalent - special cases for layouting, as are found in - Header/FooterRowContainers. - - - == The Three Indices - - Each RowContainer can be thought to have three levels of - indices for any given displayed row (but the distinction - matters primarily for the BodyRowContainer, because of the - way it scrolls through data): - - - Logical index - - Physical (or DOM) index - - Visual index - - LOGICAL INDEX is the index that is linked to the data - source. If you want your data source to represent a SQL - database with 10 000 rows, the 7 000:th row in the SQL has a - logical index of 6 999, since the index is 0-based (unless - that data source does some funky logic). - - PHYSICAL INDEX is the index for a row that you see in a - browser's DOM inspector. If your row is the second <tr> - element within a <tbody> tag, it has a physical index of 1 - (because of 0-based indices). In Header and - FooterRowContainers, you are safe to assume that the logical - index is the same as the physical index. But because the - BodyRowContainer never displays large data sources entirely - in the DOM, a physical index usually has no apparent direct - relationship with its logical index. - - VISUAL INDEX is the index relating to the order that you - see a row in, in the browser, as it is rendered. The - topmost row is 0, the second is 1, and so on. The visual - index is similar to the physical index in the sense that - Header and FooterRowContainers can assume a 1:1 - relationship between visual index and logical index. And - again, BodyRowContainer has no such relationship. The - body's visual index has additionally no apparent - relationship with its physical index. Because the <tr> tags - are reused in the body and visually repositioned with CSS - as the user scrolls, the relationship between physical - index and visual index is quickly broken. You can get an - element's visual index via the field - BodyRowContainer.visualRowOrder. - - */ - -/** - * A workaround-class for GWT and JSNI. - * <p> - * GWT is unable to handle some method calls to Java methods in inner-classes - * from within JSNI blocks. Having that inner class implement a non-inner-class - * (or interface), makes it possible for JSNI to indirectly refer to the inner - * class, by invoking methods and fields in the non-inner-class. - * - * @see Escalator.Scroller - */ -abstract class JsniWorkaround { - /** - * A JavaScript function that handles the scroll DOM event, and passes it on - * to Java code. - * - * @see #createScrollListenerFunction(Escalator) - * @see Escalator#onScroll(double,double) - * @see Escalator.Scroller#onScroll(double, double) - */ - protected final JavaScriptObject scrollListenerFunction; - - /** - * A JavaScript function that handles the mousewheel DOM event, and passes - * it on to Java code. - * - * @see #createMousewheelListenerFunction(Escalator) - * @see Escalator#onScroll(double,double) - * @see Escalator.Scroller#onScroll(double, double) - */ - protected final JavaScriptObject mousewheelListenerFunction; - - /** - * A JavaScript function that handles the touch start DOM event, and passes - * it on to Java code. - * - * @see TouchHandlerBundle#touchStart(Escalator.JsniUtil.TouchHandlerBundle.CustomTouchEvent) - */ - protected JavaScriptObject touchStartFunction; - - /** - * A JavaScript function that handles the touch move DOM event, and passes - * it on to Java code. - * - * @see TouchHandlerBundle#touchMove(Escalator.JsniUtil.TouchHandlerBundle.CustomTouchEvent) - */ - protected JavaScriptObject touchMoveFunction; - - /** - * A JavaScript function that handles the touch end and cancel DOM events, - * and passes them on to Java code. - * - * @see TouchHandlerBundle#touchEnd(Escalator.JsniUtil.TouchHandlerBundle.CustomTouchEvent) - */ - protected JavaScriptObject touchEndFunction; - - protected JsniWorkaround(final Escalator escalator) { - scrollListenerFunction = createScrollListenerFunction(escalator); - mousewheelListenerFunction = createMousewheelListenerFunction(escalator); - - final TouchHandlerBundle bundle = new TouchHandlerBundle(escalator); - touchStartFunction = bundle.getTouchStartHandler(); - touchMoveFunction = bundle.getTouchMoveHandler(); - touchEndFunction = bundle.getTouchEndHandler(); - } - - /** - * A method that constructs the JavaScript function that will be stored into - * {@link #scrollListenerFunction}. - * - * @param esc - * a reference to the current instance of {@link Escalator} - * @see Escalator#onScroll(double,double) - */ - protected abstract JavaScriptObject createScrollListenerFunction( - Escalator esc); - - /** - * A method that constructs the JavaScript function that will be stored into - * {@link #mousewheelListenerFunction}. - * - * @param esc - * a reference to the current instance of {@link Escalator} - * @see Escalator#onScroll(double,double) - */ - protected abstract JavaScriptObject createMousewheelListenerFunction( - Escalator esc); -} - -/** - * A low-level table-like widget that features a scrolling virtual viewport and - * lazily generated rows. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class Escalator extends Widget { - - // todo comments legend - /* - * [[optimize]]: There's an opportunity to rewrite the code in such a way - * that it _might_ perform better (rememeber to measure, implement, - * re-measure) - */ - /* - * [[rowheight]]: This code will require alterations that are relevant for - * being able to support variable row heights. NOTE: these bits can most - * often also be identified by searching for code reading the ROW_HEIGHT_PX - * constant. - */ - /* - * [[API]]: Implementing this suggestion would require a change in the - * public API. These suggestions usually don't come lightly. - */ - /* - * [[mpixscroll]]: This code will require alterations that are relevant for - * supporting the scrolling through more pixels than some browsers normally - * would support. (i.e. when we support more than "a million" pixels in the - * escalator DOM). NOTE: these bits can most often also be identified by - * searching for code that call scrollElem.getScrollTop();. - */ - - /** - * A utility class that contains utility methods that are usually called - * from JSNI. - * <p> - * The methods are moved in this class to minimize the amount of JSNI code - * as much as feasible. - */ - static class JsniUtil { - public static class TouchHandlerBundle { - - /** - * A <a href= - * "http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsOverlay.html" - * >JavaScriptObject overlay</a> for the <a - * href="http://www.w3.org/TR/touch-events/">JavaScript - * TouchEvent</a> object. - * <p> - * This needs to be used in the touch event handlers, since GWT's - * {@link com.google.gwt.event.dom.client.TouchEvent TouchEvent} - * can't be cast from the JSNI call, and the - * {@link com.google.gwt.dom.client.NativeEvent NativeEvent} isn't - * properly populated with the correct values. - */ - private final static class CustomTouchEvent extends - JavaScriptObject { - protected CustomTouchEvent() { - } - - public native NativeEvent getNativeEvent() - /*-{ - return this; - }-*/; - - public native int getPageX() - /*-{ - return this.targetTouches[0].pageX; - }-*/; - - public native int getPageY() - /*-{ - return this.targetTouches[0].pageY; - }-*/; - } - - private double touches = 0; - private int lastX = 0; - private int lastY = 0; - private double lastTime = 0; - private boolean snappedScrollEnabled = true; - private double deltaX = 0; - private double deltaY = 0; - - private final Escalator escalator; - private CustomTouchEvent latestTouchMoveEvent; - private AnimationCallback mover = new AnimationCallback() { - @Override - public void execute(double timestamp) { - if (touches != 1) { - return; - } - - final int x = latestTouchMoveEvent.getPageX(); - final int y = latestTouchMoveEvent.getPageY(); - deltaX = x - lastX; - deltaY = y - lastY; - lastX = x; - lastY = y; - lastTime = Duration.currentTimeMillis(); - - // snap the scroll to the major axes, at first. - if (snappedScrollEnabled) { - final double oldDeltaX = deltaX; - final double oldDeltaY = deltaY; - - /* - * Scrolling snaps to 40 degrees vs. flick scroll's 30 - * degrees, since slow movements have poor resolution - - * it's easy to interpret a slight angle as a steep - * angle, since the sample rate is "unnecessarily" high. - * 40 simply felt better than 30. - */ - final double[] snapped = Escalator.snapDeltas(deltaX, - deltaY, RATIO_OF_40_DEGREES); - deltaX = snapped[0]; - deltaY = snapped[1]; - - /* - * if the snap failed once, let's follow the pointer - * from now on. - */ - if (oldDeltaX != 0 && deltaX == oldDeltaX - && oldDeltaY != 0 && deltaY == oldDeltaY) { - snappedScrollEnabled = false; - } - } - - moveScrollFromEvent(escalator, -deltaX, -deltaY, - latestTouchMoveEvent.getNativeEvent()); - } - }; - private AnimationHandle animationHandle; - - public TouchHandlerBundle(final Escalator escalator) { - this.escalator = escalator; - } - - public native JavaScriptObject getTouchStartHandler() - /*-{ - // we need to store "this", since it won't be preserved on call. - var self = this; - return $entry(function (e) { - self.@com.vaadin.client.ui.grid.Escalator.JsniUtil.TouchHandlerBundle::touchStart(*)(e); - }); - }-*/; - - public native JavaScriptObject getTouchMoveHandler() - /*-{ - // we need to store "this", since it won't be preserved on call. - var self = this; - return $entry(function (e) { - self.@com.vaadin.client.ui.grid.Escalator.JsniUtil.TouchHandlerBundle::touchMove(*)(e); - }); - }-*/; - - public native JavaScriptObject getTouchEndHandler() - /*-{ - // we need to store "this", since it won't be preserved on call. - var self = this; - return $entry(function (e) { - self.@com.vaadin.client.ui.grid.Escalator.JsniUtil.TouchHandlerBundle::touchEnd(*)(e); - }); - }-*/; - - public void touchStart(final CustomTouchEvent event) { - touches++; - if (touches != 1) { - return; - } - - escalator.scroller.cancelFlickScroll(); - - lastX = event.getPageX(); - lastY = event.getPageY(); - - snappedScrollEnabled = true; - } - - public void touchMove(final CustomTouchEvent event) { - /* - * since we only use the getPageX/Y, and calculate the diff - * within the handler, we don't need to calculate any - * intermediate deltas. - */ - latestTouchMoveEvent = event; - - if (animationHandle != null) { - animationHandle.cancel(); - } - animationHandle = AnimationScheduler.get() - .requestAnimationFrame(mover, escalator.bodyElem); - event.getNativeEvent().preventDefault(); - } - - public void touchEnd(@SuppressWarnings("unused") - final CustomTouchEvent event) { - touches--; - - if (touches == 0) { - escalator.scroller.handleFlickScroll(deltaX, deltaY, - lastTime); - } - } - } - - public static void moveScrollFromEvent(final Escalator escalator, - final double deltaX, final double deltaY, - final NativeEvent event) { - - if (!Double.isNaN(deltaX)) { - escalator.horizontalScrollbar.setScrollPosByDelta((int) deltaX); - } - - if (!Double.isNaN(deltaY)) { - escalator.verticalScrollbar.setScrollPosByDelta((int) deltaY); - } - - /* - * TODO: only prevent if not scrolled to end/bottom. Or no? UX team - * needs to decide. - */ - final boolean warrantedYScroll = deltaY != 0 - && escalator.verticalScrollbar.showsScrollHandle(); - final boolean warrantedXScroll = deltaX != 0 - && escalator.horizontalScrollbar.showsScrollHandle(); - if (warrantedYScroll || warrantedXScroll) { - event.preventDefault(); - } - } - } - - /** - * The animation callback that handles the animation of a touch-scrolling - * flick with inertia. - */ - private class FlickScrollAnimator implements AnimationCallback { - private static final double MIN_MAGNITUDE = 0.005; - private static final double MAX_SPEED = 7; - - private double velX; - private double velY; - private double prevTime = 0; - private int millisLeft; - private double xFric; - private double yFric; - - private boolean cancelled = false; - private int lastLeft; - private int lastTop; - - /** - * Creates a new animation callback to handle touch-scrolling flick with - * inertia. - * - * @param deltaX - * the last scrolling delta in the x-axis in a touchmove - * @param deltaY - * the last scrolling delta in the y-axis in a touchmove - * @param lastTime - * the timestamp of the last touchmove - */ - public FlickScrollAnimator(final double deltaX, final double deltaY, - final double lastTime) { - final double currentTimeMillis = Duration.currentTimeMillis(); - velX = Math.max(Math.min(deltaX / (currentTimeMillis - lastTime), - MAX_SPEED), -MAX_SPEED); - velY = Math.max(Math.min(deltaY / (currentTimeMillis - lastTime), - MAX_SPEED), -MAX_SPEED); - - lastLeft = horizontalScrollbar.getScrollPos(); - lastTop = verticalScrollbar.getScrollPos(); - - /* - * If we're scrolling mainly in one of the four major directions, - * and only a teeny bit to any other side, snap the scroll to that - * major direction instead. - */ - final double[] snapDeltas = Escalator.snapDeltas(velX, velY, - RATIO_OF_30_DEGREES); - velX = snapDeltas[0]; - velY = snapDeltas[1]; - - if (velX * velX + velY * velY > MIN_MAGNITUDE) { - millisLeft = 1500; - xFric = velX / millisLeft; - yFric = velY / millisLeft; - } else { - millisLeft = 0; - } - - } - - @Override - public void execute(final double timestamp) { - if (millisLeft <= 0 || cancelled) { - scroller.currentFlickScroller = null; - return; - } - - if (prevTime == 0) { - prevTime = timestamp; - AnimationScheduler.get().requestAnimationFrame(this); - return; - } - - int currentLeft = horizontalScrollbar.getScrollPos(); - int currentTop = verticalScrollbar.getScrollPos(); - - final double timeDiff = timestamp - prevTime; - double left = currentLeft - velX * timeDiff; - setScrollLeft((int) left); - velX -= xFric * timeDiff; - - double top = currentTop - velY * timeDiff; - setScrollTop(top); - velY -= yFric * timeDiff; - - cancelBecauseOfEdgeOrCornerMaybe(); - - prevTime = timestamp; - millisLeft -= timeDiff; - lastLeft = currentLeft; - lastTop = currentTop; - AnimationScheduler.get().requestAnimationFrame(this); - } - - private void cancelBecauseOfEdgeOrCornerMaybe() { - if (lastLeft == horizontalScrollbar.getScrollPos() - && lastTop == verticalScrollbar.getScrollPos()) { - cancel(); - } - } - - public void cancel() { - cancelled = true; - } - } - - /** - * ScrollDestination case-specific handling logic. - */ - private static double getScrollPos(final ScrollDestination destination, - final double targetStartPx, final double targetEndPx, - final double viewportStartPx, final double viewportEndPx, - final int padding) { - - final double viewportLength = viewportEndPx - viewportStartPx; - - switch (destination) { - - /* - * Scroll as little as possible to show the target element. If the - * element fits into view, this works as START or END depending on the - * current scroll position. If the element does not fit into view, this - * works as START. - */ - case ANY: { - final double startScrollPos = targetStartPx - padding; - final double endScrollPos = targetEndPx + padding - viewportLength; - - if (startScrollPos < viewportStartPx) { - return startScrollPos; - } else if (targetEndPx + padding > viewportEndPx) { - return endScrollPos; - } else { - // NOOP, it's already visible - return viewportStartPx; - } - } - - /* - * Scrolls so that the element is shown at the end of the viewport. The - * viewport will, however, not scroll before its first element. - */ - case END: { - return targetEndPx + padding - viewportLength; - } - - /* - * Scrolls so that the element is shown in the middle of the viewport. - * The viewport will, however, not scroll beyond its contents, given - * more elements than what the viewport is able to show at once. Under - * no circumstances will the viewport scroll before its first element. - */ - case MIDDLE: { - final double targetMiddle = targetStartPx - + (targetEndPx - targetStartPx) / 2; - return targetMiddle - viewportLength / 2; - } - - /* - * Scrolls so that the element is shown at the start of the viewport. - * The viewport will, however, not scroll beyond its contents. - */ - case START: { - return targetStartPx - padding; - } - - /* - * Throw an error if we're here. This can only mean that - * ScrollDestination has been carelessly amended.. - */ - default: { - throw new IllegalArgumentException( - "Internal: ScrollDestination has been modified, " - + "but Escalator.getScrollPos has not been updated " - + "to match new values."); - } - } - - } - - /** An inner class that handles all logic related to scrolling. */ - private class Scroller extends JsniWorkaround { - private double lastScrollTop = 0; - private double lastScrollLeft = 0; - /** - * The current flick scroll animator. This is <code>null</code> if the - * view isn't animating a flick scroll at the moment. - */ - private FlickScrollAnimator currentFlickScroller; - - public Scroller() { - super(Escalator.this); - } - - @Override - protected native JavaScriptObject createScrollListenerFunction( - Escalator esc) - /*-{ - var vScroll = esc.@com.vaadin.client.ui.grid.Escalator::verticalScrollbar; - var vScrollElem = vScroll.@com.vaadin.client.ui.grid.ScrollbarBundle::getElement()(); - - var hScroll = esc.@com.vaadin.client.ui.grid.Escalator::horizontalScrollbar; - var hScrollElem = hScroll.@com.vaadin.client.ui.grid.ScrollbarBundle::getElement()(); - - return $entry(function(e) { - var target = e.target || e.srcElement; // IE8 uses e.scrElement - - // in case the scroll event was native (i.e. scrollbars were dragged, or - // the scrollTop/Left was manually modified), the bundles have old cache - // values. We need to make sure that the caches are kept up to date. - if (target === vScrollElem) { - vScroll.@com.vaadin.client.ui.grid.ScrollbarBundle::updateScrollPosFromDom()(); - } else if (target === hScrollElem) { - hScroll.@com.vaadin.client.ui.grid.ScrollbarBundle::updateScrollPosFromDom()(); - } else { - $wnd.console.error("unexpected scroll target: "+target); - } - - esc.@com.vaadin.client.ui.grid.Escalator::onScroll()(); - }); - }-*/; - - @Override - protected native JavaScriptObject createMousewheelListenerFunction( - Escalator esc) - /*-{ - return $entry(function(e) { - var deltaX = e.deltaX ? e.deltaX : -0.5*e.wheelDeltaX; - var deltaY = e.deltaY ? e.deltaY : -0.5*e.wheelDeltaY; - - // IE8 has only delta y - if (isNaN(deltaY)) { - deltaY = -0.5*e.wheelDelta; - } - - @com.vaadin.client.ui.grid.Escalator.JsniUtil::moveScrollFromEvent(*)(esc, deltaX, deltaY, e); - }); - }-*/; - - /** - * Recalculates the virtual viewport represented by the scrollbars, so - * that the sizes of the scroll handles appear correct in the browser - */ - public void recalculateScrollbarsForVirtualViewport() { - int scrollContentHeight = body.calculateEstimatedTotalRowHeight(); - int scrollContentWidth = columnConfiguration.calculateRowWidth(); - - double tableWrapperHeight = heightOfEscalator; - double tableWrapperWidth = widthOfEscalator; - - boolean verticalScrollNeeded = scrollContentHeight > tableWrapperHeight - - header.heightOfSection - footer.heightOfSection; - boolean horizontalScrollNeeded = scrollContentWidth > tableWrapperWidth; - - // One dimension got scrollbars, but not the other. Recheck time! - if (verticalScrollNeeded != horizontalScrollNeeded) { - if (!verticalScrollNeeded && horizontalScrollNeeded) { - verticalScrollNeeded = scrollContentHeight > tableWrapperHeight - - header.heightOfSection - - footer.heightOfSection - - horizontalScrollbar.getScrollbarThickness(); - } else { - horizontalScrollNeeded = scrollContentWidth > tableWrapperWidth - - verticalScrollbar.getScrollbarThickness(); - } - } - - // let's fix the table wrapper size, since it's now stable. - if (verticalScrollNeeded) { - tableWrapperWidth -= verticalScrollbar.getScrollbarThickness(); - } - if (horizontalScrollNeeded) { - tableWrapperHeight -= horizontalScrollbar - .getScrollbarThickness(); - } - tableWrapper.getStyle().setHeight(tableWrapperHeight, Unit.PX); - tableWrapper.getStyle().setWidth(tableWrapperWidth, Unit.PX); - - verticalScrollbar.setOffsetSize((int) (tableWrapperHeight - - footer.heightOfSection - header.heightOfSection)); - verticalScrollbar.setScrollSize(scrollContentHeight); - - /* - * If decreasing the amount of frozen columns, and scrolled to the - * right, the scroll position might reset. So we need to remember - * the scroll position, and re-apply it once the scrollbar size has - * been adjusted. - */ - int prevScrollPos = horizontalScrollbar.getScrollPos(); - - int unfrozenPixels = columnConfiguration - .getCalculatedColumnsWidth(Range.between( - columnConfiguration.getFrozenColumnCount(), - columnConfiguration.getColumnCount())); - int frozenPixels = scrollContentWidth - unfrozenPixels; - double hScrollOffsetWidth = tableWrapperWidth - frozenPixels; - horizontalScrollbar.setOffsetSize((int) hScrollOffsetWidth); - horizontalScrollbar.setScrollSize(unfrozenPixels); - horizontalScrollbar.getElement().getStyle() - .setLeft(frozenPixels, Unit.PX); - horizontalScrollbar.setScrollPos(prevScrollPos); - } - - /** - * Logical scrolling event handler for the entire widget. - * - * @param scrollLeft - * the current number of pixels that the user has scrolled - * from left - * @param scrollTop - * the current number of pixels that the user has scrolled - * from the top - */ - public void onScroll() { - if (internalScrollEventCalls > 0) { - internalScrollEventCalls--; - return; - } - - final int scrollLeft = horizontalScrollbar.getScrollPos(); - final int scrollTop = verticalScrollbar.getScrollPos(); - - if (lastScrollLeft != scrollLeft) { - for (int i = 0; i < columnConfiguration.frozenColumns; i++) { - header.updateFreezePosition(i, scrollLeft); - body.updateFreezePosition(i, scrollLeft); - footer.updateFreezePosition(i, scrollLeft); - } - - position.set(headElem, -scrollLeft, 0); - - /* - * TODO [[optimize]]: cache this value in case the instanceof - * check has undesirable overhead. This could also be a - * candidate for some deferred binding magic so that e.g. - * AbsolutePosition is not even considered in permutations that - * we know support something better. That would let the compiler - * completely remove the entire condition since it knows that - * the if will never be true. - */ - if (position instanceof AbsolutePosition) { - /* - * we don't want to put "top: 0" on the footer, since it'll - * render wrong, as we already have - * "bottom: $footer-height". - */ - footElem.getStyle().setLeft(-scrollLeft, Unit.PX); - } else { - position.set(footElem, -scrollLeft, 0); - } - - lastScrollLeft = scrollLeft; - } - - body.setBodyScrollPosition(scrollLeft, scrollTop); - - lastScrollTop = scrollTop; - body.updateEscalatorRowsOnScroll(); - /* - * TODO [[optimize]]: Might avoid a reflow by first calculating new - * scrolltop and scrolleft, then doing the escalator magic based on - * those numbers and only updating the positions after that. - */ - } - - public native void attachScrollListener(Element element) - /* - * Attaching events with JSNI instead of the GWT event mechanism because - * GWT didn't provide enough details in events, or triggering the event - * handlers with GWT bindings was unsuccessful. Maybe, with more time - * and skill, it could be done with better success. JavaScript overlay - * types might work. This might also get rid of the JsniWorkaround - * class. - */ - /*-{ - if (element.addEventListener) { - element.addEventListener("scroll", this.@com.vaadin.client.ui.grid.JsniWorkaround::scrollListenerFunction); - } else { - element.attachEvent("onscroll", this.@com.vaadin.client.ui.grid.JsniWorkaround::scrollListenerFunction); - } - }-*/; - - public native void detachScrollListener(Element element) - /* - * Attaching events with JSNI instead of the GWT event mechanism because - * GWT didn't provide enough details in events, or triggering the event - * handlers with GWT bindings was unsuccessful. Maybe, with more time - * and skill, it could be done with better success. JavaScript overlay - * types might work. This might also get rid of the JsniWorkaround - * class. - */ - /*-{ - if (element.addEventListener) { - element.removeEventListener("scroll", this.@com.vaadin.client.ui.grid.JsniWorkaround::scrollListenerFunction); - } else { - element.detachEvent("onscroll", this.@com.vaadin.client.ui.grid.JsniWorkaround::scrollListenerFunction); - } - }-*/; - - public native void attachMousewheelListener(Element element) - /* - * Attaching events with JSNI instead of the GWT event mechanism because - * GWT didn't provide enough details in events, or triggering the event - * handlers with GWT bindings was unsuccessful. Maybe, with more time - * and skill, it could be done with better success. JavaScript overlay - * types might work. This might also get rid of the JsniWorkaround - * class. - */ - /*-{ - if (element.addEventListener) { - // firefox likes "wheel", while others use "mousewheel" - var eventName = element.onwheel===undefined?"mousewheel":"wheel"; - element.addEventListener(eventName, this.@com.vaadin.client.ui.grid.JsniWorkaround::mousewheelListenerFunction); - } else { - // IE8 - element.attachEvent("onmousewheel", this.@com.vaadin.client.ui.grid.JsniWorkaround::mousewheelListenerFunction); - } - }-*/; - - public native void detachMousewheelListener(Element element) - /* - * Detaching events with JSNI instead of the GWT event mechanism because - * GWT didn't provide enough details in events, or triggering the event - * handlers with GWT bindings was unsuccessful. Maybe, with more time - * and skill, it could be done with better success. JavaScript overlay - * types might work. This might also get rid of the JsniWorkaround - * class. - */ - /*-{ - if (element.addEventListener) { - // firefox likes "wheel", while others use "mousewheel" - var eventName = element.onwheel===undefined?"mousewheel":"wheel"; - element.removeEventListener(eventName, this.@com.vaadin.client.ui.grid.JsniWorkaround::mousewheelListenerFunction); - } else { - // IE8 - element.detachEvent("onmousewheel", this.@com.vaadin.client.ui.grid.JsniWorkaround::mousewheelListenerFunction); - } - }-*/; - - public native void attachTouchListeners(Element element) - /* - * Detaching events with JSNI instead of the GWT event mechanism because - * GWT didn't provide enough details in events, or triggering the event - * handlers with GWT bindings was unsuccessful. Maybe, with more time - * and skill, it could be done with better success. JavaScript overlay - * types might work. This might also get rid of the JsniWorkaround - * class. - */ - /*-{ - if (element.addEventListener) { - element.addEventListener("touchstart", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchStartFunction); - element.addEventListener("touchmove", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchMoveFunction); - element.addEventListener("touchend", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchEndFunction); - element.addEventListener("touchcancel", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchEndFunction); - } else { - // this would be IE8, but we don't support it with touch - } - }-*/; - - public native void detachTouchListeners(Element element) - /* - * Detaching events with JSNI instead of the GWT event mechanism because - * GWT didn't provide enough details in events, or triggering the event - * handlers with GWT bindings was unsuccessful. Maybe, with more time - * and skill, it could be done with better success. JavaScript overlay - * types might work. This might also get rid of the JsniWorkaround - * class. - */ - /*-{ - if (element.removeEventListener) { - element.removeEventListener("touchstart", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchStartFunction); - element.removeEventListener("touchmove", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchMoveFunction); - element.removeEventListener("touchend", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchEndFunction); - element.removeEventListener("touchcancel", this.@com.vaadin.client.ui.grid.JsniWorkaround::touchEndFunction); - } else { - // this would be IE8, but we don't support it with touch - } - }-*/; - - private void cancelFlickScroll() { - if (currentFlickScroller != null) { - currentFlickScroller.cancel(); - } - } - - /** - * Handles a touch-based flick scroll. - * - * @param deltaX - * the last scrolling delta in the x-axis in a touchmove - * @param deltaY - * the last scrolling delta in the y-axis in a touchmove - * @param lastTime - * the timestamp of the last touchmove - */ - public void handleFlickScroll(double deltaX, double deltaY, - double lastTime) { - currentFlickScroller = new FlickScrollAnimator(deltaX, deltaY, - lastTime); - AnimationScheduler.get() - .requestAnimationFrame(currentFlickScroller); - } - - public void scrollToColumn(final int columnIndex, - final ScrollDestination destination, final int padding) { - assert columnIndex >= columnConfiguration.frozenColumns : "Can't scroll to a frozen column"; - - /* - * To cope with frozen columns, we just pretend those columns are - * not there at all when calculating the position of the target - * column and the boundaries of the viewport. The resulting - * scrollLeft will be correct without compensation since the DOM - * structure effectively means that scrollLeft also ignores the - * frozen columns. - */ - final int frozenPixels = columnConfiguration - .getCalculatedColumnsWidth(Range.withLength(0, - columnConfiguration.frozenColumns)); - - final int targetStartPx = columnConfiguration - .getCalculatedColumnsWidth(Range.withLength(0, columnIndex)) - - frozenPixels; - final int targetEndPx = targetStartPx - + columnConfiguration.getColumnWidthActual(columnIndex); - - final int viewportStartPx = getScrollLeft(); - int viewportEndPx = viewportStartPx + getElement().getOffsetWidth() - - frozenPixels; - if (verticalScrollbar.showsScrollHandle()) { - viewportEndPx -= Util.getNativeScrollbarSize(); - } - - final double scrollLeft = getScrollPos(destination, targetStartPx, - targetEndPx, viewportStartPx, viewportEndPx, padding); - - /* - * note that it doesn't matter if the scroll would go beyond the - * content, since the browser will adjust for that, and everything - * fall into line accordingly. - */ - setScrollLeft((int) scrollLeft); - } - - public void scrollToRow(final int rowIndex, - final ScrollDestination destination, final int padding) { - /* - * FIXME [[rowheight]]: coded to work only with default row heights - * - will not work with variable row heights - */ - final int targetStartPx = body.getDefaultRowHeight() * rowIndex; - final int targetEndPx = targetStartPx + body.getDefaultRowHeight(); - - final double viewportStartPx = getScrollTop(); - final double viewportEndPx = viewportStartPx - + body.calculateHeight(); - - final double scrollTop = getScrollPos(destination, targetStartPx, - targetEndPx, viewportStartPx, viewportEndPx, padding); - - /* - * note that it doesn't matter if the scroll would go beyond the - * content, since the browser will adjust for that, and everything - * falls into line accordingly. - */ - setScrollTop(scrollTop); - } - } - - private abstract class AbstractRowContainer implements RowContainer { - - private EscalatorUpdater updater = EscalatorUpdater.NULL; - - private int rows; - - /** - * The table section element ({@code <thead>}, {@code <tbody>} or - * {@code <tfoot>}) the rows (i.e. {@code <tr>} tags) are contained in. - */ - protected final Element root; - - /** The height of the combined rows in the DOM. */ - protected double heightOfSection = -1; - - /** - * The primary style name of the escalator. Most commonly provided by - * Escalator as "v-escalator". - */ - private String primaryStyleName = null; - - /** - * A map containing cached values of an element's current top position. - * <p> - * Don't use this field directly, because it will not take proper care - * of all the bookkeeping required. - * - * @deprecated Use {@link #setRowPosition(Element, int, int)}, - * {@link #getRowTop(Element)} and - * {@link #removeRowPosition(Element)} instead. - */ - @Deprecated - private final Map<Element, Integer> rowTopPositionMap = new HashMap<Element, Integer>(); - - private boolean defaultRowHeightShouldBeAutodetected = true; - - private int defaultRowHeight = INITIAL_DEFAULT_ROW_HEIGHT; - - public AbstractRowContainer(final Element rowContainerElement) { - root = rowContainerElement; - } - - /** - * Gets the tag name of an element to represent a cell in a row. - * <p> - * Usually {@code "th"} or {@code "td"}. - * <p> - * <em>Note:</em> To actually <em>create</em> such an element, use - * {@link #createCellElement()} instead. - * - * @return the tag name for the element to represent cells as - * @see #createCellElement() - */ - protected abstract String getCellElementTagName(); - - @Override - public EscalatorUpdater getEscalatorUpdater() { - return updater; - } - - /** - * {@inheritDoc} - * <p> - * <em>Implementation detail:</em> This method does no DOM modifications - * (i.e. is very cheap to call) if there is no data for rows or columns - * when this method is called. - * - * @see #hasColumnAndRowData() - */ - @Override - public void setEscalatorUpdater(final EscalatorUpdater escalatorUpdater) { - if (escalatorUpdater == null) { - throw new IllegalArgumentException( - "escalator updater cannot be null"); - } - - updater = escalatorUpdater; - - if (hasColumnAndRowData() && getRowCount() > 0) { - refreshRows(0, getRowCount()); - } - } - - /** - * {@inheritDoc} - * <p> - * <em>Implementation detail:</em> This method does no DOM modifications - * (i.e. is very cheap to call) if there are no rows in the DOM when - * this method is called. - * - * @see #hasSomethingInDom() - */ - @Override - public void removeRows(final int index, final int numberOfRows) { - assertArgumentsAreValidAndWithinRange(index, numberOfRows); - - rows -= numberOfRows; - - if (!isAttached()) { - return; - } - - if (hasSomethingInDom()) { - paintRemoveRows(index, numberOfRows); - } - } - - protected abstract void paintRemoveRows(final int index, - final int numberOfRows); - - private void assertArgumentsAreValidAndWithinRange(final int index, - final int numberOfRows) throws IllegalArgumentException, - IndexOutOfBoundsException { - if (numberOfRows < 1) { - throw new IllegalArgumentException( - "Number of rows must be 1 or greater (was " - + numberOfRows + ")"); - } - - if (index < 0 || index + numberOfRows > getRowCount()) { - throw new IndexOutOfBoundsException("The given " - + "row range (" + index + ".." + (index + numberOfRows) - + ") was outside of the current number of rows (" - + getRowCount() + ")"); - } - } - - @Override - public int getRowCount() { - return rows; - } - - /** - * {@inheritDoc} - * <p> - * <em>Implementation detail:</em> This method does no DOM modifications - * (i.e. is very cheap to call) if there is no data for columns when - * this method is called. - * - * @see #hasColumnAndRowData() - */ - @Override - public void insertRows(final int index, final int numberOfRows) { - if (index < 0 || index > getRowCount()) { - throw new IndexOutOfBoundsException("The given index (" + index - + ") was outside of the current number of rows (0.." - + getRowCount() + ")"); - } - - if (numberOfRows < 1) { - throw new IllegalArgumentException( - "Number of rows must be 1 or greater (was " - + numberOfRows + ")"); - } - - rows += numberOfRows; - - /* - * only add items in the DOM if the widget itself is attached to the - * DOM. We can't calculate sizes otherwise. - */ - if (isAttached()) { - paintInsertRows(index, numberOfRows); - } - } - - /** - * Actually add rows into the DOM, now that everything can be - * calculated. - * - * @param visualIndex - * the DOM index to add rows into - * @param numberOfRows - * the number of rows to insert - * @return a list of the added row elements - */ - protected List<Element> paintInsertRows(final int visualIndex, - final int numberOfRows) { - assert isAttached() : "Can't paint rows if Escalator is not attached"; - - final List<Element> addedRows = new ArrayList<Element>(); - - if (numberOfRows < 1) { - return addedRows; - } - - Node referenceRow; - if (root.getChildCount() != 0 && visualIndex != 0) { - // get the row node we're inserting stuff after - referenceRow = root.getChild(visualIndex - 1); - } else { - // index is 0, so just prepend. - referenceRow = null; - } - - for (int row = visualIndex; row < visualIndex + numberOfRows; row++) { - final int rowHeight = getDefaultRowHeight(); - final Element tr = DOM.createTR(); - addedRows.add(tr); - tr.addClassName(getStylePrimaryName() + "-row"); - referenceRow = insertAfterReferenceAndUpdateIt(root, tr, - referenceRow); - - for (int col = 0; col < columnConfiguration.getColumnCount(); col++) { - final int colWidth = columnConfiguration - .getColumnWidthActual(col); - final Element cellElem = createCellElement(rowHeight, - colWidth); - tr.appendChild(cellElem); - - // Set stylename and position if new cell is frozen - if (col < columnConfiguration.frozenColumns) { - cellElem.addClassName("frozen"); - position.set(cellElem, scroller.lastScrollLeft, 0); - } - } - - refreshRow(tr, row); - } - reapplyRowWidths(); - - recalculateSectionHeight(); - - return addedRows; - } - - private Node insertAfterReferenceAndUpdateIt(final Element parent, - final Element elem, final Node referenceNode) { - if (referenceNode != null) { - parent.insertAfter(elem, referenceNode); - } else { - /* - * referencenode being null means we have offset 0, i.e. make it - * the first row - */ - /* - * TODO [[optimize]]: Is insertFirst or append faster for an - * empty root? - */ - parent.insertFirst(elem); - } - return elem; - } - - abstract protected void recalculateSectionHeight(); - - /** - * Returns the estimated height of all rows in the row container. - * <p> - * The estimate is promised to be correct as long as there are no rows - * with calculated heights. - */ - protected int calculateEstimatedTotalRowHeight() { - return getDefaultRowHeight() * getRowCount(); - } - - /** - * {@inheritDoc} - * <p> - * <em>Implementation detail:</em> This method does no DOM modifications - * (i.e. is very cheap to call) if there is no data for columns when - * this method is called. - * - * @see #hasColumnAndRowData() - */ - @Override - public void refreshRows(final int index, final int numberOfRows) { - Profiler.enter("Escalator.AbstractRowContainer.refreshRows"); - - assertArgumentsAreValidAndWithinRange(index, numberOfRows); - - if (!isAttached()) { - return; - } - - /* - * TODO [[rowheight]]: even if no rows are evaluated in the current - * viewport, the heights of some unrendered rows might change in a - * refresh. This would cause the scrollbar to be adjusted (in - * scrollHeight and/or scrollTop). Do we want to take this into - * account? - */ - if (hasColumnAndRowData()) { - /* - * TODO [[rowheight]]: nudge rows down with - * refreshRowPositions() as needed - */ - for (int row = index; row < index + numberOfRows; row++) { - final Node tr = getTrByVisualIndex(row); - refreshRow(tr, row); - } - } - - Profiler.leave("Escalator.AbstractRowContainer.refreshRows"); - } - - void refreshRow(final Node tr, final int logicalRowIndex) { - flyweightRow.setup((Element) tr, logicalRowIndex, - columnConfiguration.getCalculatedColumnWidths()); - updater.updateCells(flyweightRow, flyweightRow.getCells()); - - /* - * the "assert" guarantees that this code is run only during - * development/debugging. - */ - assert flyweightRow.teardown(); - } - - /** - * Create and setup an empty cell element. - * - * @param width - * the width of the cell, in pixels - * @param height - * the height of the cell, in pixels - * - * @return a set-up empty cell element - */ - @SuppressWarnings("hiding") - public Element createCellElement(final int height, final int width) { - final Element cellElem = DOM.createElement(getCellElementTagName()); - cellElem.getStyle().setHeight(height, Unit.PX); - cellElem.getStyle().setWidth(width, Unit.PX); - cellElem.addClassName(getStylePrimaryName() + "-cell"); - return cellElem; - } - - /** - * Gets the child element that is visually at a certain index - * - * @param index - * the index of the element to retrieve - * @return the element at position {@code index} - * @throws IndexOutOfBoundsException - * if {@code index} is not valid within {@link #root} - */ - abstract protected Element getTrByVisualIndex(int index) - throws IndexOutOfBoundsException; - - protected void paintRemoveColumns(final int offset, - final int numberOfColumns, - final List<ColumnConfigurationImpl.Column> removedColumns) { - final NodeList<Node> childNodes = root.getChildNodes(); - for (int visualRowIndex = 0; visualRowIndex < childNodes - .getLength(); visualRowIndex++) { - final Node tr = childNodes.getItem(visualRowIndex); - - for (int column = 0; column < numberOfColumns; column++) { - Element cellElement = tr.getChild(offset).cast(); - detachPossibleWidgetFromCell(cellElement); - cellElement.removeFromParent(); - } - } - reapplyRowWidths(); - - final int firstRemovedColumnLeft = columnConfiguration - .getCalculatedColumnsWidth(Range.withLength(0, offset)); - final boolean columnsWereRemovedFromLeftOfTheViewport = scroller.lastScrollLeft > firstRemovedColumnLeft; - - if (columnsWereRemovedFromLeftOfTheViewport) { - int removedColumnsPxAmount = 0; - for (ColumnConfigurationImpl.Column removedColumn : removedColumns) { - removedColumnsPxAmount += removedColumn - .getCalculatedWidth(); - } - final int leftByDiff = (int) (scroller.lastScrollLeft - removedColumnsPxAmount); - final int newScrollLeft = Math.max(firstRemovedColumnLeft, - leftByDiff); - horizontalScrollbar.setScrollPos(newScrollLeft); - } - - // this needs to be after the scroll position adjustment above. - scroller.recalculateScrollbarsForVirtualViewport(); - - /* - * Because we might remove columns where affected by colspans, it's - * easiest to simply redraw everything when columns are modified. - * - * Yes, this is a TODO [[optimize]]. - */ - if (getRowCount() > 0 - && getColumnConfiguration().getColumnCount() > 0) { - refreshRows(0, getRowCount()); - } - } - - void detachPossibleWidgetFromCell(Node cellNode) { - // Detach possible widget - Widget widget = getWidgetFromCell(cellNode); - if (widget != null) { - // Orphan. - setParent(widget, null); - - // Physical detach. - cellNode.removeChild(widget.getElement()); - } - } - - protected void paintInsertColumns(final int offset, - final int numberOfColumns, boolean frozen) { - final NodeList<Node> childNodes = root.getChildNodes(); - - for (int row = 0; row < childNodes.getLength(); row++) { - final int rowHeight = getDefaultRowHeight(); - final Element tr = getTrByVisualIndex(row); - - Node referenceCell; - if (offset != 0) { - referenceCell = tr.getChild(offset - 1); - } else { - referenceCell = null; - } - - for (int col = offset; col < offset + numberOfColumns; col++) { - final int colWidth = columnConfiguration - .getColumnWidthActual(col); - final Element cellElem = createCellElement(rowHeight, - colWidth); - referenceCell = insertAfterReferenceAndUpdateIt(tr, - cellElem, referenceCell); - } - } - reapplyRowWidths(); - - if (frozen) { - for (int col = offset; col < offset + numberOfColumns; col++) { - setColumnFrozen(col, true); - } - } - - // this needs to be before the scrollbar adjustment. - scroller.recalculateScrollbarsForVirtualViewport(); - - int pixelsToInsertedColumn = columnConfiguration - .getCalculatedColumnsWidth(Range.withLength(0, offset)); - final boolean columnsWereAddedToTheLeftOfViewport = scroller.lastScrollLeft > pixelsToInsertedColumn; - - if (columnsWereAddedToTheLeftOfViewport) { - int insertedColumnsWidth = columnConfiguration - .getCalculatedColumnsWidth(Range.withLength(offset, - numberOfColumns)); - horizontalScrollbar - .setScrollPos((int) (scroller.lastScrollLeft + insertedColumnsWidth)); - } - - /* - * Because we might insert columns where affected by colspans, it's - * easiest to simply redraw everything when columns are modified. - * - * Yes, this is a TODO [[optimize]]. - */ - if (getRowCount() > 0 - && getColumnConfiguration().getColumnCount() > 1) { - refreshRows(0, getRowCount()); - } - } - - public void setColumnFrozen(int column, boolean frozen) { - final NodeList<Node> childNodes = root.getChildNodes(); - - for (int row = 0; row < childNodes.getLength(); row++) { - final Element tr = childNodes.getItem(row).cast(); - - Element cell = (Element) tr.getChild(column); - if (frozen) { - cell.addClassName("frozen"); - } else { - cell.removeClassName("frozen"); - position.reset(cell); - } - } - - if (frozen) { - updateFreezePosition(column, scroller.lastScrollLeft); - } - } - - public void updateFreezePosition(int column, double scrollLeft) { - final NodeList<Node> childNodes = root.getChildNodes(); - - for (int row = 0; row < childNodes.getLength(); row++) { - final Element tr = childNodes.getItem(row).cast(); - - Element cell = (Element) tr.getChild(column); - position.set(cell, scrollLeft, 0); - } - } - - /** - * Iterates through all the cells in a column and returns the width of - * the widest element in this RowContainer. - * - * @param index - * the index of the column to inspect - * @return the pixel width of the widest element in the indicated column - */ - public int calculateMaxColWidth(int index) { - Element row = root.getFirstChildElement(); - int maxWidth = 0; - while (row != null) { - final Element cell = (Element) row.getChild(index); - final boolean isVisible = !cell.getStyle().getDisplay() - .equals(Display.NONE.getCssName()); - if (isVisible) { - maxWidth = Math.max(maxWidth, cell.getScrollWidth()); - } - row = row.getNextSiblingElement(); - } - return maxWidth; - } - - /** - * Reapplies all the cells' widths according to the calculated widths in - * the column configuration. - */ - public void reapplyColumnWidths() { - Element row = root.getFirstChildElement(); - while (row != null) { - Element cell = row.getFirstChildElement(); - int columnIndex = 0; - while (cell != null) { - @SuppressWarnings("hiding") - final int width = getCalculatedColumnWidthWithColspan(cell, - columnIndex); - - /* - * TODO Should Escalator implement ProvidesResize at some - * point, this is where we need to do that. - */ - cell.getStyle().setWidth(width, Unit.PX); - - cell = cell.getNextSiblingElement(); - columnIndex++; - } - row = row.getNextSiblingElement(); - } - - reapplyRowWidths(); - } - - private int getCalculatedColumnWidthWithColspan(final Element cell, - final int columnIndex) { - final int colspan = cell.getPropertyInt(FlyweightCell.COLSPAN_ATTR); - Range spannedColumns = Range.withLength(columnIndex, colspan); - - /* - * Since browsers don't explode with overflowing colspans, escalator - * shouldn't either. - */ - if (spannedColumns.getEnd() > columnConfiguration.getColumnCount()) { - spannedColumns = Range.between(columnIndex, - columnConfiguration.getColumnCount()); - } - return columnConfiguration - .getCalculatedColumnsWidth(spannedColumns); - } - - /** - * Applies the total length of the columns to each row element. - * <p> - * <em>Note:</em> In contrast to {@link #reapplyColumnWidths()}, this - * method only modifies the width of the {@code <tr>} element, not the - * cells within. - */ - protected void reapplyRowWidths() { - int rowWidth = columnConfiguration.calculateRowWidth(); - - com.google.gwt.dom.client.Element row = root.getFirstChildElement(); - while (row != null) { - row.getStyle().setWidth(rowWidth, Unit.PX); - row = row.getNextSiblingElement(); - } - } - - /** - * The primary style name for the container. - * - * @param primaryStyleName - * the style name to use as prefix for all row and cell style - * names. - */ - protected void setStylePrimaryName(String primaryStyleName) { - String oldStyle = getStylePrimaryName(); - if (SharedUtil.equals(oldStyle, primaryStyleName)) { - return; - } - - this.primaryStyleName = primaryStyleName; - - // Update already rendered rows and cells - Node row = root.getFirstChild(); - while (row != null) { - Element rowElement = row.cast(); - UIObject.setStylePrimaryName(rowElement, primaryStyleName - + "-row"); - Node cell = row.getFirstChild(); - while (cell != null) { - Element cellElement = cell.cast(); - UIObject.setStylePrimaryName(cellElement, primaryStyleName - + "-cell"); - cell = cell.getNextSibling(); - } - row = row.getNextSibling(); - } - } - - /** - * Returns the primary style name of the container. - * - * @return The primary style name or <code>null</code> if not set. - */ - protected String getStylePrimaryName() { - return primaryStyleName; - } - - @Override - public void setDefaultRowHeight(int px) throws IllegalArgumentException { - if (px < 1) { - throw new IllegalArgumentException("Height must be positive. " - + px + " was given."); - } - - defaultRowHeightShouldBeAutodetected = false; - defaultRowHeight = px; - reapplyDefaultRowHeights(); - } - - @Override - public int getDefaultRowHeight() { - return defaultRowHeight; - } - - /** - * The default height of rows has (most probably) changed. - * <p> - * Make sure that the displayed rows with a default height are updated - * in height and top position. - * <p> - * <em>Note:</em>This implementation should not call - * {@link Escalator#recalculateElementSizes()} - it is done by the - * discretion of the caller of this method. - */ - protected abstract void reapplyDefaultRowHeights(); - - protected void reapplyRowHeight(final Element tr, final int heightPx) { - Element cellElem = tr.getFirstChildElement().cast(); - while (cellElem != null) { - cellElem.getStyle().setHeight(heightPx, Unit.PX); - cellElem = cellElem.getNextSiblingElement(); - } - - /* - * no need to apply height to tr-element, it'll be resized - * implicitly. - */ - } - - @SuppressWarnings("boxing") - protected void setRowPosition(final Element tr, final int x, final int y) { - position.set(tr, x, y); - rowTopPositionMap.put(tr, y); - } - - @SuppressWarnings("boxing") - protected int getRowTop(final Element tr) { - return rowTopPositionMap.get(tr); - } - - protected void removeRowPosition(Element tr) { - rowTopPositionMap.remove(tr); - } - - public void autodetectRowHeight() { - Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { - - @Override - public void execute() { - if (defaultRowHeightShouldBeAutodetected && isAttached()) { - final Element detectionTr = DOM.createTR(); - detectionTr - .setClassName(getStylePrimaryName() + "-row"); - - final Element cellElem = DOM - .createElement(getCellElementTagName()); - cellElem.setClassName(getStylePrimaryName() + "-cell"); - cellElem.setInnerHTML("foo"); - - detectionTr.appendChild(cellElem); - root.appendChild(detectionTr); - defaultRowHeight = Math.max(1, - cellElem.getOffsetHeight()); - root.removeChild(detectionTr); - - if (root.hasChildNodes()) { - reapplyDefaultRowHeights(); - } - - defaultRowHeightShouldBeAutodetected = false; - } - } - }); - } - } - - private abstract class AbstractStaticRowContainer extends - AbstractRowContainer { - public AbstractStaticRowContainer(final Element headElement) { - super(headElement); - } - - @Override - protected void paintRemoveRows(final int index, final int numberOfRows) { - for (int i = index; i < index + numberOfRows; i++) { - final Element tr = (Element) root.getChild(index); - for (int c = 0; c < tr.getChildCount(); c++) { - detachPossibleWidgetFromCell((Element) tr.getChild(c) - .cast()); - } - tr.removeFromParent(); - } - recalculateSectionHeight(); - } - - @Override - protected Element getTrByVisualIndex(final int index) - throws IndexOutOfBoundsException { - if (index >= 0 && index < root.getChildCount()) { - return (Element) root.getChild(index); - } else { - throw new IndexOutOfBoundsException("No such visual index: " - + index); - } - } - - @Override - public void insertRows(int index, int numberOfRows) { - super.insertRows(index, numberOfRows); - recalculateElementSizes(); - } - - @Override - public void removeRows(int index, int numberOfRows) { - super.removeRows(index, numberOfRows); - recalculateElementSizes(); - } - - @Override - protected void reapplyDefaultRowHeights() { - if (root.getChildCount() == 0) { - return; - } - - Profiler.enter("Escalator.AbstractStaticRowContainer.reapplyDefaultRowHeights"); - - Element tr = root.getFirstChildElement().cast(); - while (tr != null) { - reapplyRowHeight(tr, getDefaultRowHeight()); - tr = tr.getNextSiblingElement(); - } - - /* - * Because all rows are immediately displayed in the static row - * containers, the section's overall height has most probably - * changed. - */ - recalculateSectionHeight(); - - Profiler.leave("Escalator.AbstractStaticRowContainer.reapplyDefaultRowHeights"); - } - - @Override - protected void recalculateSectionHeight() { - Profiler.enter("Escalator.AbstractStaticRowContainer.recalculateSectionHeight"); - - int newHeight = calculateEstimatedTotalRowHeight(); - if (newHeight != heightOfSection) { - heightOfSection = newHeight; - sectionHeightCalculated(); - body.verifyEscalatorCount(); - } - - Profiler.leave("Escalator.AbstractStaticRowContainer.recalculateSectionHeight"); - } - - /** - * Informs the row container that the height of its respective table - * section has changed. - * <p> - * These calculations might affect some layouting logic, such as the - * body is being offset by the footer, the footer needs to be readjusted - * according to its height, and so on. - * <p> - * A table section is either header, body or footer. - */ - protected abstract void sectionHeightCalculated(); - } - - private class HeaderRowContainer extends AbstractStaticRowContainer { - public HeaderRowContainer(final Element headElement) { - super(headElement); - } - - @Override - protected void sectionHeightCalculated() { - bodyElem.getStyle().setMarginTop(heightOfSection, Unit.PX); - verticalScrollbar.getElement().getStyle() - .setTop(heightOfSection, Unit.PX); - } - - @Override - protected String getCellElementTagName() { - return "th"; - } - - @Override - public void setStylePrimaryName(String primaryStyleName) { - super.setStylePrimaryName(primaryStyleName); - UIObject.setStylePrimaryName(root, primaryStyleName + "-header"); - } - } - - private class FooterRowContainer extends AbstractStaticRowContainer { - public FooterRowContainer(final Element footElement) { - super(footElement); - } - - @Override - public void setStylePrimaryName(String primaryStyleName) { - super.setStylePrimaryName(primaryStyleName); - UIObject.setStylePrimaryName(root, primaryStyleName + "-footer"); - } - - @Override - protected String getCellElementTagName() { - return "td"; - } - - @Override - protected void sectionHeightCalculated() { - int vscrollHeight = (int) Math.floor(heightOfEscalator - - header.heightOfSection - footer.heightOfSection); - - final boolean horizontalScrollbarNeeded = columnConfiguration - .calculateRowWidth() > widthOfEscalator; - if (horizontalScrollbarNeeded) { - vscrollHeight -= horizontalScrollbar.getScrollbarThickness(); - } - - verticalScrollbar.setOffsetSize(vscrollHeight); - } - } - - private class BodyRowContainer extends AbstractRowContainer { - /* - * TODO [[optimize]]: check whether a native JsArray might be faster - * than LinkedList - */ - /** - * The order in which row elements are rendered visually in the browser, - * with the help of CSS tricks. Usually has nothing to do with the DOM - * order. - */ - private final LinkedList<Element> visualRowOrder = new LinkedList<Element>(); - - /** - * The logical index of the topmost row. - * - * @deprecated Use the accessors {@link #setTopRowLogicalIndex(int)}, - * {@link #updateTopRowLogicalIndex(int)} and - * {@link #getTopRowLogicalIndex()} instead - */ - @Deprecated - private int topRowLogicalIndex = 0; - - private void setTopRowLogicalIndex(int topRowLogicalIndex) { - if (LogConfiguration.loggingIsEnabled(Level.INFO)) { - Logger.getLogger("Escalator.BodyRowContainer").fine( - "topRowLogicalIndex: " + this.topRowLogicalIndex - + " -> " + topRowLogicalIndex); - } - assert topRowLogicalIndex >= 0 : "topRowLogicalIndex became negative"; - /* - * if there's a smart way of evaluating and asserting the max index, - * this would be a nice place to put it. I haven't found out an - * effective and generic solution. - */ - - this.topRowLogicalIndex = topRowLogicalIndex; - } - - private int getTopRowLogicalIndex() { - return topRowLogicalIndex; - } - - private void updateTopRowLogicalIndex(int diff) { - setTopRowLogicalIndex(topRowLogicalIndex + diff); - } - - public BodyRowContainer(final Element bodyElement) { - super(bodyElement); - } - - @Override - public void setStylePrimaryName(String primaryStyleName) { - super.setStylePrimaryName(primaryStyleName); - UIObject.setStylePrimaryName(root, primaryStyleName + "-body"); - } - - public void updateEscalatorRowsOnScroll() { - if (visualRowOrder.isEmpty()) { - return; - } - - boolean rowsWereMoved = false; - - final int topRowPos = getRowTop(visualRowOrder.getFirst()); - // TODO [[mpixscroll]] - final int scrollTop = tBodyScrollTop; - final int viewportOffset = topRowPos - scrollTop; - - /* - * TODO [[optimize]] this if-else can most probably be refactored - * into a neater block of code - */ - - if (viewportOffset > 0) { - // there's empty room on top - - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - int originalRowsToMove = (int) Math.ceil(viewportOffset - / (double) getDefaultRowHeight()); - int rowsToMove = Math.min(originalRowsToMove, - root.getChildCount()); - - final int end = root.getChildCount(); - final int start = end - rowsToMove; - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - final int logicalRowIndex = scrollTop / getDefaultRowHeight(); - moveAndUpdateEscalatorRows(Range.between(start, end), 0, - logicalRowIndex); - - updateTopRowLogicalIndex(-originalRowsToMove); - } - - else if (viewportOffset + getDefaultRowHeight() <= 0) { - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - - /* - * the viewport has been scrolled more than the topmost visual - * row. - */ - - /* - * Using the fact that integer division has implicit - * floor-function to our advantage here. - */ - int originalRowsToMove = Math.abs(viewportOffset - / getDefaultRowHeight()); - int rowsToMove = Math.min(originalRowsToMove, - root.getChildCount()); - - int logicalRowIndex; - if (rowsToMove < root.getChildCount()) { - /* - * We scroll so little that we can just keep adding the rows - * below the current escalator - */ - logicalRowIndex = getLogicalRowIndex(visualRowOrder - .getLast()) + 1; - } else { - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - /* - * Since we're moving all escalator rows, we need to - * calculate the first logical row index from the scroll - * position. - */ - logicalRowIndex = scrollTop / getDefaultRowHeight(); - } - - /* - * Since we're moving the viewport downwards, the visual index - * is always at the bottom. Note: Due to how - * moveAndUpdateEscalatorRows works, this will work out even if - * we move all the rows, and try to place them "at the end". - */ - final int targetVisualIndex = root.getChildCount(); - - // make sure that we don't move rows over the data boundary - boolean aRowWasLeftBehind = false; - if (logicalRowIndex + rowsToMove > getRowCount()) { - /* - * TODO [[rowheight]]: with constant row heights, there's - * always exactly one row that will be moved beyond the data - * source, when viewport is scrolled to the end. This, - * however, isn't guaranteed anymore once row heights start - * varying. - */ - rowsToMove--; - aRowWasLeftBehind = true; - } - - moveAndUpdateEscalatorRows(Range.between(0, rowsToMove), - targetVisualIndex, logicalRowIndex); - - if (aRowWasLeftBehind) { - /* - * To keep visualRowOrder as a spatially contiguous block of - * rows, let's make sure that the one row we didn't move - * visually still stays with the pack. - */ - final Range strayRow = Range.withOnly(0); - - /* - * We cannot trust getLogicalRowIndex, because it hasn't yet - * been updated. But since we're leaving rows behind, it - * means we've scrolled to the bottom. So, instead, we - * simply count backwards from the end. - */ - final int topLogicalIndex = getRowCount() - - visualRowOrder.size(); - moveAndUpdateEscalatorRows(strayRow, 0, topLogicalIndex); - } - - final int naiveNewLogicalIndex = getTopRowLogicalIndex() - + originalRowsToMove; - final int maxLogicalIndex = getRowCount() - - visualRowOrder.size(); - setTopRowLogicalIndex(Math.min(naiveNewLogicalIndex, - maxLogicalIndex)); - } - - if (rowsWereMoved) { - fireRowVisibilityChangeEvent(); - } - } - - @Override - protected List<Element> paintInsertRows(final int index, - final int numberOfRows) { - if (numberOfRows == 0) { - return Collections.emptyList(); - } - - /* - * TODO: this method should probably only add physical rows, and not - * populate them - let everything be populated as appropriate by the - * logic that follows. - * - * This also would lead to the fact that paintInsertRows wouldn't - * need to return anything. - */ - final List<Element> addedRows = fillAndPopulateEscalatorRowsIfNeeded( - index, numberOfRows); - - /* - * insertRows will always change the number of rows - update the - * scrollbar sizes. - */ - scroller.recalculateScrollbarsForVirtualViewport(); - - /* - * FIXME [[rowheight]]: coded to work only with default row heights - * - will not work with variable row heights - */ - final boolean addedRowsAboveCurrentViewport = index - * getDefaultRowHeight() < getScrollTop(); - final boolean addedRowsBelowCurrentViewport = index - * getDefaultRowHeight() > getScrollTop() - + calculateHeight(); - - if (addedRowsAboveCurrentViewport) { - /* - * We need to tweak the virtual viewport (scroll handle - * positions, table "scroll position" and row locations), but - * without re-evaluating any rows. - */ - - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - final int yDelta = numberOfRows * getDefaultRowHeight(); - adjustScrollPosIgnoreEvents(yDelta); - updateTopRowLogicalIndex(numberOfRows); - } - - else if (addedRowsBelowCurrentViewport) { - // NOOP, we already recalculated scrollbars. - } - - else { // some rows were added inside the current viewport - - final int unupdatedLogicalStart = index + addedRows.size(); - final int visualOffset = getLogicalRowIndex(visualRowOrder - .getFirst()); - - /* - * At this point, we have added new escalator rows, if so - * needed. - * - * If more rows were added than the new escalator rows can - * account for, we need to start to spin the escalator to update - * the remaining rows aswell. - */ - final int rowsStillNeeded = numberOfRows - addedRows.size(); - final Range unupdatedVisual = convertToVisual(Range.withLength( - unupdatedLogicalStart, rowsStillNeeded)); - final int end = root.getChildCount(); - final int start = end - unupdatedVisual.length(); - final int visualTargetIndex = unupdatedLogicalStart - - visualOffset; - moveAndUpdateEscalatorRows(Range.between(start, end), - visualTargetIndex, unupdatedLogicalStart); - - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - // move the surrounding rows to their correct places. - int rowTop = (unupdatedLogicalStart + (end - start)) - * getDefaultRowHeight(); - final ListIterator<Element> i = visualRowOrder - .listIterator(visualTargetIndex + (end - start)); - while (i.hasNext()) { - final Element tr = i.next(); - setRowPosition(tr, 0, rowTop); - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - rowTop += getDefaultRowHeight(); - } - - fireRowVisibilityChangeEvent(); - } - return addedRows; - } - - /** - * Move escalator rows around, and make sure everything gets - * appropriately repositioned and repainted. - * - * @param visualSourceRange - * the range of rows to move to a new place - * @param visualTargetIndex - * the visual index where the rows will be placed to - * @param logicalTargetIndex - * the logical index to be assigned to the first moved row - * @throws IllegalArgumentException - * if any of <code>visualSourceRange.getStart()</code>, - * <code>visualTargetIndex</code> or - * <code>logicalTargetIndex</code> is a negative number; or - * if <code>visualTargetInfo</code> is greater than the - * number of escalator rows. - */ - private void moveAndUpdateEscalatorRows(final Range visualSourceRange, - final int visualTargetIndex, final int logicalTargetIndex) - throws IllegalArgumentException { - - if (visualSourceRange.isEmpty()) { - return; - } - - if (visualSourceRange.getStart() < 0) { - throw new IllegalArgumentException( - "Logical source start must be 0 or greater (was " - + visualSourceRange.getStart() + ")"); - } else if (logicalTargetIndex < 0) { - throw new IllegalArgumentException( - "Logical target must be 0 or greater"); - } else if (visualTargetIndex < 0) { - throw new IllegalArgumentException( - "Visual target must be 0 or greater"); - } else if (visualTargetIndex > root.getChildCount()) { - throw new IllegalArgumentException( - "Visual target must not be greater than the number of escalator rows"); - } else if (logicalTargetIndex + visualSourceRange.length() > getRowCount()) { - final int logicalEndIndex = logicalTargetIndex - + visualSourceRange.length() - 1; - throw new IllegalArgumentException( - "Logical target leads to rows outside of the data range (" - + logicalTargetIndex + ".." + logicalEndIndex - + ")"); - } - - /* - * Since we move a range into another range, the indices might move - * about. Having 10 rows, if we move 0..1 to index 10 (to the end of - * the collection), the target range will end up being 8..9, instead - * of 10..11. - * - * This applies only if we move elements forward in the collection, - * not backward. - */ - final int adjustedVisualTargetIndex; - if (visualSourceRange.getStart() < visualTargetIndex) { - adjustedVisualTargetIndex = visualTargetIndex - - visualSourceRange.length(); - } else { - adjustedVisualTargetIndex = visualTargetIndex; - } - - if (visualSourceRange.getStart() != adjustedVisualTargetIndex) { - - /* - * Reorder the rows to their correct places within - * visualRowOrder (unless rows are moved back to their original - * places) - */ - - /* - * TODO [[optimize]]: move whichever set is smaller: the ones - * explicitly moved, or the others. So, with 10 escalator rows, - * if we are asked to move idx[0..8] to the end of the list, - * it's faster to just move idx[9] to the beginning. - */ - - final List<Element> removedRows = new ArrayList<Element>( - visualSourceRange.length()); - for (int i = 0; i < visualSourceRange.length(); i++) { - final Element tr = visualRowOrder.remove(visualSourceRange - .getStart()); - removedRows.add(tr); - } - visualRowOrder.addAll(adjustedVisualTargetIndex, removedRows); - } - - { // Refresh the contents of the affected rows - final ListIterator<Element> iter = visualRowOrder - .listIterator(adjustedVisualTargetIndex); - for (int logicalIndex = logicalTargetIndex; logicalIndex < logicalTargetIndex - + visualSourceRange.length(); logicalIndex++) { - final Element tr = iter.next(); - refreshRow(tr, logicalIndex); - } - } - - { // Reposition the rows that were moved - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - int newRowTop = logicalTargetIndex * getDefaultRowHeight(); - - final ListIterator<Element> iter = visualRowOrder - .listIterator(adjustedVisualTargetIndex); - for (int i = 0; i < visualSourceRange.length(); i++) { - final Element tr = iter.next(); - setRowPosition(tr, 0, newRowTop); - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - newRowTop += getDefaultRowHeight(); - } - } - } - - /** - * Adjust the scroll position without having the scroll handler have any - * side-effects. - * <p> - * <em>Note:</em> {@link Scroller#onScroll(double, double)} - * <em>will</em> be triggered, but it will not do anything, with the - * help of {@link Escalator#internalScrollEventCalls}. - * - * @param yDelta - * the delta of pixels to scrolls. A positive value moves the - * viewport downwards, while a negative value moves the - * viewport upwards - */ - public void adjustScrollPosIgnoreEvents(final int yDelta) { - if (yDelta == 0) { - return; - } - - internalScrollEventCalls++; - verticalScrollbar.setScrollPosByDelta(yDelta); - - /* - * FIXME [[rowheight]]: coded to work only with default row heights - * - will not work with variable row heights - */ - final int rowTopPos = yDelta - yDelta % getDefaultRowHeight(); - for (final Element tr : visualRowOrder) { - setRowPosition(tr, 0, getRowTop(tr) + rowTopPos); - } - setBodyScrollPosition(tBodyScrollLeft, tBodyScrollTop + yDelta); - } - - /** - * Adds new physical escalator rows to the DOM at the given index if - * there's still a need for more escalator rows. - * <p> - * If Escalator already is at (or beyond) max capacity, this method does - * nothing to the DOM. - * - * @param index - * the index at which to add new escalator rows. - * <em>Note:</em>It is assumed that the index is both the - * visual index and the logical index. - * @param numberOfRows - * the number of rows to add at <code>index</code> - * @return a list of the added rows - */ - private List<Element> fillAndPopulateEscalatorRowsIfNeeded( - final int index, final int numberOfRows) { - - final int escalatorRowsStillFit = getMaxEscalatorRowCapacity() - - root.getChildCount(); - final int escalatorRowsNeeded = Math.min(numberOfRows, - escalatorRowsStillFit); - - if (escalatorRowsNeeded > 0) { - - final List<Element> addedRows = super.paintInsertRows(index, - escalatorRowsNeeded); - visualRowOrder.addAll(index, addedRows); - - /* - * We need to figure out the top positions for the rows we just - * added. - */ - for (int i = 0; i < addedRows.size(); i++) { - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - setRowPosition(addedRows.get(i), 0, (index + i) - * getDefaultRowHeight()); - } - - /* Move the other rows away from above the added escalator rows */ - for (int i = index + addedRows.size(); i < visualRowOrder - .size(); i++) { - final Element tr = visualRowOrder.get(i); - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - setRowPosition(tr, 0, i * getDefaultRowHeight()); - } - - return addedRows; - } else { - return new ArrayList<Element>(); - } - } - - private int getMaxEscalatorRowCapacity() { - /* - * FIXME [[rowheight]]: coded to work only with default row heights - * - will not work with variable row heights - */ - final int maxEscalatorRowCapacity = (int) Math - .ceil(calculateHeight() / getDefaultRowHeight()) + 1; - - /* - * maxEscalatorRowCapacity can become negative if the headers and - * footers start to overlap. This is a crazy situation, but Vaadin - * blinks the components a lot, so it's feasible. - */ - return Math.max(0, maxEscalatorRowCapacity); - } - - @Override - protected void paintRemoveRows(final int index, final int numberOfRows) { - - final Range viewportRange = Range.withLength( - getLogicalRowIndex(visualRowOrder.getFirst()), - visualRowOrder.size()); - - final Range removedRowsRange = Range - .withLength(index, numberOfRows); - - final Range[] partitions = removedRowsRange - .partitionWith(viewportRange); - final Range removedAbove = partitions[0]; - final Range removedLogicalInside = partitions[1]; - final Range removedVisualInside = convertToVisual(removedLogicalInside); - - /* - * TODO: extract the following if-block to a separate method. I'll - * leave this be inlined for now, to make linediff-based code - * reviewing easier. Probably will be moved in the following patch - * set. - */ - - /* - * Adjust scroll position in one of two scenarios: - * - * 1) Rows were removed above. Then we just need to adjust the - * scrollbar by the height of the removed rows. - * - * 2) There are no logical rows above, and at least the first (if - * not more) visual row is removed. Then we need to snap the scroll - * position to the first visible row (i.e. reset scroll position to - * absolute 0) - * - * The logic is optimized in such a way that the - * adjustScrollPosIgnoreEvents is called only once, to avoid extra - * reflows, and thus the code might seem a bit obscure. - */ - final boolean firstVisualRowIsRemoved = !removedVisualInside - .isEmpty() && removedVisualInside.getStart() == 0; - - if (!removedAbove.isEmpty() || firstVisualRowIsRemoved) { - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - final int yDelta = removedAbove.length() - * getDefaultRowHeight(); - final int firstLogicalRowHeight = getDefaultRowHeight(); - final boolean removalScrollsToShowFirstLogicalRow = verticalScrollbar - .getScrollPos() - yDelta < firstLogicalRowHeight; - - if (removedVisualInside.isEmpty() - && (!removalScrollsToShowFirstLogicalRow || !firstVisualRowIsRemoved)) { - /* - * rows were removed from above the viewport, so all we need - * to do is to adjust the scroll position to account for the - * removed rows - */ - adjustScrollPosIgnoreEvents(-yDelta); - } else if (removalScrollsToShowFirstLogicalRow) { - /* - * It seems like we've removed all rows from above, and also - * into the current viewport. This means we'll need to even - * out the scroll position to exactly 0 (i.e. adjust by the - * current negative scrolltop, presto!), so that it isn't - * aligned funnily - */ - adjustScrollPosIgnoreEvents(-verticalScrollbar - .getScrollPos()); - } - } - - // ranges evaluated, let's do things. - if (!removedVisualInside.isEmpty()) { - int escalatorRowCount = bodyElem.getChildCount(); - - /* - * If we're left with less rows than the number of escalators, - * remove the unused ones. - */ - final int escalatorRowsToRemove = escalatorRowCount - - getRowCount(); - if (escalatorRowsToRemove > 0) { - for (int i = 0; i < escalatorRowsToRemove; i++) { - final Element tr = visualRowOrder - .remove(removedVisualInside.getStart()); - for (int c = 0; c < tr.getChildCount(); c++) { - detachPossibleWidgetFromCell((Element) tr.getChild( - c).cast()); - } - tr.removeFromParent(); - removeRowPosition(tr); - } - escalatorRowCount -= escalatorRowsToRemove; - - /* - * Because we're removing escalator rows, we don't have - * anything to scroll by. Let's make sure the viewport is - * scrolled to top, to render any rows possibly left above. - */ - body.setBodyScrollPosition(tBodyScrollLeft, 0); - - /* - * We might have removed some rows from the middle, so let's - * make sure we're not left with any holes. Also remember: - * visualIndex == logicalIndex applies now. - */ - final int dirtyRowsStart = removedLogicalInside.getStart(); - for (int i = dirtyRowsStart; i < escalatorRowCount; i++) { - final Element tr = visualRowOrder.get(i); - /* - * FIXME [[rowheight]]: coded to work only with default - * row heights - will not work with variable row heights - */ - setRowPosition(tr, 0, i * getDefaultRowHeight()); - } - - /* - * this is how many rows appeared into the viewport from - * below - */ - final int rowsToUpdateDataOn = numberOfRows - - escalatorRowsToRemove; - final int start = Math.max(0, escalatorRowCount - - rowsToUpdateDataOn); - final int end = escalatorRowCount; - for (int i = start; i < end; i++) { - final Element tr = visualRowOrder.get(i); - refreshRow(tr, i); - } - } - - else { - // No escalator rows need to be removed. - - /* - * Two things (or a combination thereof) can happen: - * - * 1) We're scrolled to the bottom, the last rows are - * removed. SOLUTION: moveAndUpdateEscalatorRows the - * bottommost rows, and place them at the top to be - * refreshed. - * - * 2) We're scrolled somewhere in the middle, arbitrary rows - * are removed. SOLUTION: moveAndUpdateEscalatorRows the - * removed rows, and place them at the bottom to be - * refreshed. - * - * Since a combination can also happen, we need to handle - * this in a smart way, all while avoiding - * double-refreshing. - */ - - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - final int contentBottom = getRowCount() - * getDefaultRowHeight(); - final int viewportBottom = (int) (tBodyScrollTop + calculateHeight()); - if (viewportBottom <= contentBottom) { - /* - * We're in the middle of the row container, everything - * is added to the bottom - */ - paintRemoveRowsAtMiddle(removedLogicalInside, - removedVisualInside, 0); - } - - else if (contentBottom - + (numberOfRows * getDefaultRowHeight()) - - viewportBottom < getDefaultRowHeight()) { - /* - * FIXME [[rowheight]]: above coded to work only with - * default row heights - will not work with variable row - * heights - */ - - /* - * We're at the end of the row container, everything is - * added to the top. - */ - paintRemoveRowsAtBottom(removedLogicalInside, - removedVisualInside); - updateTopRowLogicalIndex(-removedLogicalInside.length()); - } - - else { - /* - * We're in a combination, where we need to both scroll - * up AND show new rows at the bottom. - * - * Example: Scrolled down to show the second to last - * row. Remove two. Viewport scrolls up, revealing the - * row above row. The last element collapses up and into - * view. - * - * Reminder: this use case handles only the case when - * there are enough escalator rows to still render a - * full view. I.e. all escalator rows will _always_ be - * populated - */ - /*- - * 1 1 |1| <- newly rendered - * |2| |2| |2| - * |3| ==> |*| ==> |5| <- newly rendered - * |4| |*| - * 5 5 - * - * 1 1 |1| <- newly rendered - * |2| |*| |4| - * |3| ==> |*| ==> |5| <- newly rendered - * |4| |4| - * 5 5 - */ - - /* - * STEP 1: - * - * reorganize deprecated escalator rows to bottom, but - * don't re-render anything yet - */ - /*- - * 1 1 1 - * |2| |*| |4| - * |3| ==> |*| ==> |*| - * |4| |4| |*| - * 5 5 5 - */ - int newTop = getRowTop(visualRowOrder - .get(removedVisualInside.getStart())); - for (int i = 0; i < removedVisualInside.length(); i++) { - final Element tr = visualRowOrder - .remove(removedVisualInside.getStart()); - visualRowOrder.addLast(tr); - } - - for (int i = removedVisualInside.getStart(); i < escalatorRowCount; i++) { - final Element tr = visualRowOrder.get(i); - setRowPosition(tr, 0, newTop); - - /* - * FIXME [[rowheight]]: coded to work only with - * default row heights - will not work with variable - * row heights - */ - newTop += getDefaultRowHeight(); - } - - /* - * STEP 2: - * - * manually scroll - */ - /*- - * 1 |1| <-- newly rendered (by scrolling) - * |4| |4| - * |*| ==> |*| - * |*| - * 5 5 - */ - final double newScrollTop = contentBottom - - calculateHeight(); - setScrollTop(newScrollTop); - /* - * Manually call the scroll handler, so we get immediate - * effects in the escalator. - */ - scroller.onScroll(); - internalScrollEventCalls++; - - /* - * Move the bottommost (n+1:th) escalator row to top, - * because scrolling up doesn't handle that for us - * automatically - */ - moveAndUpdateEscalatorRows( - Range.withOnly(escalatorRowCount - 1), - 0, - getLogicalRowIndex(visualRowOrder.getFirst()) - 1); - updateTopRowLogicalIndex(-1); - - /* - * STEP 3: - * - * update remaining escalator rows - */ - /*- - * |1| |1| - * |4| ==> |4| - * |*| |5| <-- newly rendered - * - * 5 - */ - - /* - * FIXME [[rowheight]]: coded to work only with default - * row heights - will not work with variable row heights - */ - final int rowsScrolled = (int) (Math - .ceil((viewportBottom - (double) contentBottom) - / getDefaultRowHeight())); - final int start = escalatorRowCount - - (removedVisualInside.length() - rowsScrolled); - final Range visualRefreshRange = Range.between(start, - escalatorRowCount); - final int logicalTargetIndex = getLogicalRowIndex(visualRowOrder - .getFirst()) + start; - // in-place move simply re-renders the rows. - moveAndUpdateEscalatorRows(visualRefreshRange, start, - logicalTargetIndex); - } - } - } - - updateTopRowLogicalIndex(-removedAbove.length()); - - /* - * this needs to be done after the escalator has been shrunk down, - * or it won't work correctly (due to setScrollTop invocation) - */ - scroller.recalculateScrollbarsForVirtualViewport(); - - fireRowVisibilityChangeEvent(); - } - - private void paintRemoveRowsAtMiddle(final Range removedLogicalInside, - final Range removedVisualInside, final int logicalOffset) { - /*- - * : : : - * |2| |2| |2| - * |3| ==> |*| ==> |4| - * |4| |4| |6| <- newly rendered - * : : : - */ - - final int escalatorRowCount = visualRowOrder.size(); - - final int logicalTargetIndex = getLogicalRowIndex(visualRowOrder - .getLast()) - - (removedVisualInside.length() - 1) - + logicalOffset; - moveAndUpdateEscalatorRows(removedVisualInside, escalatorRowCount, - logicalTargetIndex); - - // move the surrounding rows to their correct places. - final ListIterator<Element> iterator = visualRowOrder - .listIterator(removedVisualInside.getStart()); - - /* - * FIXME [[rowheight]]: coded to work only with default row heights - * - will not work with variable row heights - */ - int rowTop = (removedLogicalInside.getStart() + logicalOffset) - * getDefaultRowHeight(); - for (int i = removedVisualInside.getStart(); i < escalatorRowCount - - removedVisualInside.length(); i++) { - final Element tr = iterator.next(); - setRowPosition(tr, 0, rowTop); - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - rowTop += getDefaultRowHeight(); - } - } - - private void paintRemoveRowsAtBottom(final Range removedLogicalInside, - final Range removedVisualInside) { - /*- - * : - * : : |4| <- newly rendered - * |5| |5| |5| - * |6| ==> |*| ==> |7| - * |7| |7| - */ - - final int logicalTargetIndex = getLogicalRowIndex(visualRowOrder - .getFirst()) - removedVisualInside.length(); - moveAndUpdateEscalatorRows(removedVisualInside, 0, - logicalTargetIndex); - - // move the surrounding rows to their correct places. - final ListIterator<Element> iterator = visualRowOrder - .listIterator(removedVisualInside.getEnd()); - /* - * FIXME [[rowheight]]: coded to work only with default row heights - * - will not work with variable row heights - */ - int rowTop = removedLogicalInside.getStart() - * getDefaultRowHeight(); - while (iterator.hasNext()) { - final Element tr = iterator.next(); - setRowPosition(tr, 0, rowTop); - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - rowTop += getDefaultRowHeight(); - } - } - - private int getLogicalRowIndex(final Element element) { - assert element.getParentNode() == root : "The given element isn't a row element in the body"; - int internalIndex = visualRowOrder.indexOf(element); - return getTopRowLogicalIndex() + internalIndex; - } - - @Override - protected void recalculateSectionHeight() { - // NOOP for body, since it doesn't make any sense. - } - - /** - * Adjusts the row index and number to be relevant for the current - * virtual viewport. - * <p> - * It converts a logical range of rows index to the matching visual - * range, truncating the resulting range with the viewport. - * <p> - * <ul> - * <li>Escalator contains logical rows 0..100 - * <li>Current viewport showing logical rows 20..29 - * <li>convertToVisual([20..29]) → [0..9] - * <li>convertToVisual([15..24]) → [0..4] - * <li>convertToVisual([25..29]) → [5..9] - * <li>convertToVisual([26..39]) → [6..9] - * <li>convertToVisual([0..5]) → [0..-1] <em>(empty)</em> - * <li>convertToVisual([35..1]) → [0..-1] <em>(empty)</em> - * <li>convertToVisual([0..100]) → [0..9] - * </ul> - * - * @return a logical range converted to a visual range, truncated to the - * current viewport. The first visual row has the index 0. - */ - private Range convertToVisual(final Range logicalRange) { - if (logicalRange.isEmpty()) { - return logicalRange; - } else if (visualRowOrder.isEmpty()) { - // empty range - return Range.withLength(0, 0); - } - - /* - * TODO [[rowheight]]: these assumptions will be totally broken with - * variable row heights. - */ - final int maxEscalatorRows = getMaxEscalatorRowCapacity(); - final int currentTopRowIndex = getLogicalRowIndex(visualRowOrder - .getFirst()); - - final Range[] partitions = logicalRange.partitionWith(Range - .withLength(currentTopRowIndex, maxEscalatorRows)); - final Range insideRange = partitions[1]; - return insideRange.offsetBy(-currentTopRowIndex); - } - - @Override - protected String getCellElementTagName() { - return "td"; - } - - /** - * Calculates the height of the {@code <tbody>} as it should be rendered - * in the DOM. - */ - private double calculateHeight() { - final int tableHeight = tableWrapper.getOffsetHeight(); - final double footerHeight = footer.heightOfSection; - final double headerHeight = header.heightOfSection; - return tableHeight - footerHeight - headerHeight; - } - - @Override - public void refreshRows(final int index, final int numberOfRows) { - Profiler.enter("Escalator.BodyRowContainer.refreshRows"); - - final Range visualRange = convertToVisual(Range.withLength(index, - numberOfRows)); - - if (!visualRange.isEmpty()) { - final int firstLogicalRowIndex = getLogicalRowIndex(visualRowOrder - .getFirst()); - for (int rowNumber = visualRange.getStart(); rowNumber < visualRange - .getEnd(); rowNumber++) { - refreshRow(visualRowOrder.get(rowNumber), - firstLogicalRowIndex + rowNumber); - } - } - - Profiler.leave("Escalator.BodyRowContainer.refreshRows"); - } - - @Override - protected Element getTrByVisualIndex(final int index) - throws IndexOutOfBoundsException { - if (index >= 0 && index < visualRowOrder.size()) { - return visualRowOrder.get(index); - } else { - throw new IndexOutOfBoundsException("No such visual index: " - + index); - } - } - - private void setBodyScrollPosition(final int scrollLeft, - final int scrollTop) { - tBodyScrollLeft = scrollLeft; - tBodyScrollTop = scrollTop; - position.set(bodyElem, -tBodyScrollLeft, -tBodyScrollTop); - } - - /** - * Make sure that there is a correct amount of escalator rows: Add more - * if needed, or remove any superfluous ones. - * <p> - * This method should be called when e.g. the height of the Escalator - * changes. - * <p> - * <em>Note:</em> This method will make sure that the escalator rows are - * placed in the proper places. By default new rows are added below, but - * if the content is scrolled down, the rows are populated on top - * instead. - */ - public void verifyEscalatorCount() { - /* - * This method indeed has a smell very similar to paintRemoveRows - * and paintInsertRows. - * - * Unfortunately, those the code can't trivially be shared, since - * there are some slight differences in the respective - * responsibilities. The "paint" methods fake the addition and - * removal of rows, and make sure to either push existing data out - * of view, or draw new data into view. Only in some special cases - * will the DOM element count change. - * - * This method, however, has the explicit responsibility to verify - * that when "something" happens, we still have the correct amount - * of escalator rows in the DOM, and if not, we make sure to modify - * that count. Only in some special cases do we need to take into - * account other things than simply modifying the DOM element count. - */ - - Profiler.enter("Escalator.BodyRowContainer.verifyEscalatorCount"); - - if (!isAttached()) { - return; - } - - final int maxEscalatorRows = getMaxEscalatorRowCapacity(); - final int neededEscalatorRows = Math.min(maxEscalatorRows, - body.getRowCount()); - final int neededEscalatorRowsDiff = neededEscalatorRows - - visualRowOrder.size(); - - if (neededEscalatorRowsDiff > 0) { - // needs more - - /* - * This is a workaround for the issue where we might be scrolled - * to the bottom, and the widget expands beyond the content - * range - */ - - final int index = visualRowOrder.size(); - final int nextLastLogicalIndex; - if (!visualRowOrder.isEmpty()) { - nextLastLogicalIndex = getLogicalRowIndex(visualRowOrder - .getLast()) + 1; - } else { - nextLastLogicalIndex = 0; - } - - final boolean contentWillFit = nextLastLogicalIndex < getRowCount() - - neededEscalatorRowsDiff; - if (contentWillFit) { - final List<Element> addedRows = fillAndPopulateEscalatorRowsIfNeeded( - index, neededEscalatorRowsDiff); - - /* - * Since fillAndPopulateEscalatorRowsIfNeeded operates on - * the assumption that index == visual index == logical - * index, we thank for the added escalator rows, but since - * they're painted in the wrong CSS position, we need to - * move them to their actual locations. - * - * Note: this is the second (see body.paintInsertRows) - * occasion where fillAndPopulateEscalatorRowsIfNeeded would - * behave "more correctly" if it only would add escalator - * rows to the DOM and appropriate bookkeping, and not - * actually populate them :/ - */ - moveAndUpdateEscalatorRows( - Range.withLength(index, addedRows.size()), index, - nextLastLogicalIndex); - } else { - /* - * TODO [[optimize]] - * - * We're scrolled so far down that all rows can't be simply - * appended at the end, since we might start displaying - * escalator rows that don't exist. To avoid the mess that - * is body.paintRemoveRows, this is a dirty hack that dumbs - * the problem down to a more basic and already-solved - * problem: - * - * 1) scroll all the way up 2) add the missing escalator - * rows 3) scroll back to the original position. - * - * Letting the browser scroll back to our original position - * will automatically solve any possible overflow problems, - * since the browser will not allow us to scroll beyond the - * actual content. - */ - - final double oldScrollTop = getScrollTop(); - setScrollTop(0); - scroller.onScroll(); - fillAndPopulateEscalatorRowsIfNeeded(index, - neededEscalatorRowsDiff); - setScrollTop(oldScrollTop); - scroller.onScroll(); - internalScrollEventCalls++; - } - } - - else if (neededEscalatorRowsDiff < 0) { - // needs less - - final ListIterator<Element> iter = visualRowOrder - .listIterator(visualRowOrder.size()); - for (int i = 0; i < -neededEscalatorRowsDiff; i++) { - final Element last = iter.previous(); - for (int c = 0; c < last.getChildCount(); c++) { - detachPossibleWidgetFromCell((Element) last.getChild(c) - .cast()); - } - last.removeFromParent(); - iter.remove(); - } - - /* - * If we were scrolled to the bottom so that we didn't have an - * extra escalator row at the bottom, we'll probably end up with - * blank space at the bottom of the escalator, and one extra row - * above the header. - * - * Experimentation idea #1: calculate "scrollbottom" vs content - * bottom and remove one row from top, rest from bottom. This - * FAILED, since setHeight has already happened, thus we never - * will detect ourselves having been scrolled all the way to the - * bottom. - */ - - if (!visualRowOrder.isEmpty()) { - final int firstRowTop = getRowTop(visualRowOrder.getFirst()); - /* - * FIXME [[rowheight]]: coded to work only with default row - * heights - will not work with variable row heights - */ - final double firstRowMinTop = tBodyScrollTop - - getDefaultRowHeight(); - if (firstRowTop < firstRowMinTop) { - final int newLogicalIndex = getLogicalRowIndex(visualRowOrder - .getLast()) + 1; - moveAndUpdateEscalatorRows(Range.withOnly(0), - visualRowOrder.size(), newLogicalIndex); - } - } - } - - if (neededEscalatorRowsDiff != 0) { - fireRowVisibilityChangeEvent(); - } - - Profiler.leave("Escalator.BodyRowContainer.verifyEscalatorCount"); - } - - @Override - protected void reapplyDefaultRowHeights() { - if (visualRowOrder.isEmpty()) { - return; - } - - /* - * As an intermediate step between hard-coded row heights to crazily - * varying row heights, Escalator will support the modification of - * the default row height (which is applied to all rows). - * - * This allows us to do some assumptions and simplifications for - * now. This code is intended to be quite short-lived, but gives - * insight into what needs to be done when row heights change in the - * body, in a general sense. - * - * TODO [[rowheight]] remove this comment once row heights may - * genuinely vary. - */ - - Profiler.enter("Escalator.BodyRowContainer.reapplyDefaultRowHeights"); - - /* step 1: resize and reposition rows */ - for (int i = 0; i < visualRowOrder.size(); i++) { - Element tr = visualRowOrder.get(i); - reapplyRowHeight(tr, getDefaultRowHeight()); - - final int logicalIndex = getTopRowLogicalIndex() + i; - setRowPosition(tr, 0, logicalIndex * getDefaultRowHeight()); - } - - /* - * step 2: move scrollbar so that it corresponds to its previous - * place - */ - - /* - * This ratio needs to be calculated with the scrollsize (not max - * scroll position) in order to align the top row with the new - * scroll position. - */ - double scrollRatio = (double) verticalScrollbar.getScrollPos() - / (double) verticalScrollbar.getScrollSize(); - scroller.recalculateScrollbarsForVirtualViewport(); - internalScrollEventCalls++; - verticalScrollbar.setScrollPos((int) (getDefaultRowHeight() - * getRowCount() * scrollRatio)); - setBodyScrollPosition(horizontalScrollbar.getScrollPos(), - verticalScrollbar.getScrollPos()); - scroller.onScroll(); - - /* step 3: make sure we have the correct amount of escalator rows. */ - verifyEscalatorCount(); - - /* - * TODO [[rowheight]] This simply doesn't work with variable rows - * heights. - */ - setTopRowLogicalIndex(getRowTop(visualRowOrder.getFirst()) - / getDefaultRowHeight()); - - Profiler.leave("Escalator.BodyRowContainer.reapplyDefaultRowHeights"); - } - } - - private class ColumnConfigurationImpl implements ColumnConfiguration { - public class Column { - private static final int DEFAULT_COLUMN_WIDTH_PX = 100; - - private int definedWidth = -1; - private int calculatedWidth = DEFAULT_COLUMN_WIDTH_PX; - - public void setWidth(int px) { - definedWidth = px; - calculatedWidth = (px >= 0) ? px : DEFAULT_COLUMN_WIDTH_PX; - } - - public int getDefinedWidth() { - return definedWidth; - } - - public int getCalculatedWidth() { - return calculatedWidth; - } - } - - private final List<Column> columns = new ArrayList<Column>(); - private int frozenColumns = 0; - - /** - * A cached array of all the calculated column widths. - * - * @see #getCalculatedColumnWidths() - */ - private int[] widthsArray = null; - - /** - * {@inheritDoc} - * <p> - * <em>Implementation detail:</em> This method does no DOM modifications - * (i.e. is very cheap to call) if there are no rows in the DOM when - * this method is called. - * - * @see #hasSomethingInDom() - */ - @Override - public void removeColumns(final int index, final int numberOfColumns) { - assertArgumentsAreValidAndWithinRange(index, numberOfColumns); - - flyweightRow.removeCells(index, numberOfColumns); - - // Cope with removing frozen columns - if (index < frozenColumns) { - if (index + numberOfColumns < frozenColumns) { - /* - * Last removed column was frozen, meaning that all removed - * columns were frozen. Just decrement the number of frozen - * columns accordingly. - */ - frozenColumns -= numberOfColumns; - } else { - /* - * If last removed column was not frozen, we have removed - * columns beyond the frozen range, so all remaining frozen - * columns are to the left of the removed columns. - */ - frozenColumns = index; - } - } - - List<Column> removedColumns = new ArrayList<Column>(); - for (int i = 0; i < numberOfColumns; i++) { - removedColumns.add(columns.remove(index)); - } - - if (hasSomethingInDom()) { - for (final AbstractRowContainer rowContainer : rowContainers) { - rowContainer.paintRemoveColumns(index, numberOfColumns, - removedColumns); - } - } - } - - /** - * Calculate the width of a row, as the sum of columns' widths. - * - * @return the width of a row, in pixels - */ - public int calculateRowWidth() { - return getCalculatedColumnsWidth(Range.between(0, getColumnCount())); - } - - private void assertArgumentsAreValidAndWithinRange(final int index, - final int numberOfColumns) { - if (numberOfColumns < 1) { - throw new IllegalArgumentException( - "Number of columns can't be less than 1 (was " - + numberOfColumns + ")"); - } - - if (index < 0 || index + numberOfColumns > getColumnCount()) { - throw new IndexOutOfBoundsException("The given " - + "column range (" + index + ".." - + (index + numberOfColumns) - + ") was outside of the current " - + "number of columns (" + getColumnCount() + ")"); - } - } - - /** - * {@inheritDoc} - * <p> - * <em>Implementation detail:</em> This method does no DOM modifications - * (i.e. is very cheap to call) if there is no data for rows when this - * method is called. - * - * @see #hasColumnAndRowData() - */ - @Override - public void insertColumns(final int index, final int numberOfColumns) { - if (index < 0 || index > getColumnCount()) { - throw new IndexOutOfBoundsException("The given index(" + index - + ") was outside of the current number of columns (0.." - + getColumnCount() + ")"); - } - - if (numberOfColumns < 1) { - throw new IllegalArgumentException( - "Number of columns must be 1 or greater (was " - + numberOfColumns); - } - - flyweightRow.addCells(index, numberOfColumns); - - for (int i = 0; i < numberOfColumns; i++) { - columns.add(index, new Column()); - } - - // Either all or none of the new columns are frozen - boolean frozen = index < frozenColumns; - if (frozen) { - frozenColumns += numberOfColumns; - } - - if (hasColumnAndRowData()) { - for (final AbstractRowContainer rowContainer : rowContainers) { - rowContainer.paintInsertColumns(index, numberOfColumns, - frozen); - } - } - } - - @Override - public int getColumnCount() { - return columns.size(); - } - - @Override - public void setFrozenColumnCount(int count) - throws IllegalArgumentException { - if (count < 0 || count > getColumnCount()) { - throw new IllegalArgumentException( - "count must be between 0 and the current number of columns (" - + columns + ")"); - } - int oldCount = frozenColumns; - if (count == oldCount) { - return; - } - - frozenColumns = count; - - if (hasSomethingInDom()) { - // Are we freezing or unfreezing? - boolean frozen = count > oldCount; - - int firstAffectedCol; - int firstUnaffectedCol; - - if (frozen) { - firstAffectedCol = oldCount; - firstUnaffectedCol = count; - } else { - firstAffectedCol = count; - firstUnaffectedCol = oldCount; - } - - for (int col = firstAffectedCol; col < firstUnaffectedCol; col++) { - header.setColumnFrozen(col, frozen); - body.setColumnFrozen(col, frozen); - footer.setColumnFrozen(col, frozen); - } - } - - scroller.recalculateScrollbarsForVirtualViewport(); - } - - @Override - public int getFrozenColumnCount() { - return frozenColumns; - } - - @Override - public void setColumnWidth(int index, int px) - throws IllegalArgumentException { - checkValidColumnIndex(index); - - columns.get(index).setWidth(px); - widthsArray = null; - - /* - * TODO [[optimize]]: only modify the elements that are actually - * modified. - */ - header.reapplyColumnWidths(); - body.reapplyColumnWidths(); - footer.reapplyColumnWidths(); - recalculateElementSizes(); - } - - private void checkValidColumnIndex(int index) - throws IllegalArgumentException { - if (!Range.withLength(0, getColumnCount()).contains(index)) { - throw new IllegalArgumentException("The given column index (" - + index + ") does not exist"); - } - } - - @Override - public int getColumnWidth(int index) throws IllegalArgumentException { - checkValidColumnIndex(index); - return columns.get(index).getDefinedWidth(); - } - - @Override - public int getColumnWidthActual(int index) { - return columns.get(index).getCalculatedWidth(); - } - - /** - * Calculates the width of the columns in a given range. - * - * @param columns - * the columns to calculate - * @return the total width of the columns in the given - * <code>columns</code> - */ - int getCalculatedColumnsWidth(@SuppressWarnings("hiding") - final Range columns) { - /* - * This is an assert instead of an exception, since this is an - * internal method. - */ - assert columns.isSubsetOf(Range.between(0, getColumnCount())) : "Range " - + "was outside of current column range (i.e.: " - + Range.between(0, getColumnCount()) - + ", but was given :" - + columns; - - int sum = 0; - for (int i = columns.getStart(); i < columns.getEnd(); i++) { - sum += getColumnWidthActual(i); - } - return sum; - } - - void setCalculatedColumnWidth(int index, int width) { - columns.get(index).calculatedWidth = width; - widthsArray = null; - } - - int[] getCalculatedColumnWidths() { - if (widthsArray == null || widthsArray.length != getColumnCount()) { - widthsArray = new int[getColumnCount()]; - for (int i = 0; i < columns.size(); i++) { - widthsArray[i] = columns.get(i).getCalculatedWidth(); - } - } - return widthsArray; - } - } - - // abs(atan(y/x))*(180/PI) = n deg, x = 1, solve y - /** - * The solution to - * <code>|tan<sup>-1</sup>(<i>x</i>)|×(180/π) = 30</code> - * . - * <p> - * This constant is placed in the Escalator class, instead of an inner - * class, since even mathematical expressions aren't allowed in non-static - * inner classes for constants. - */ - private static final double RATIO_OF_30_DEGREES = 1 / Math.sqrt(3); - /** - * The solution to - * <code>|tan<sup>-1</sup>(<i>x</i>)|×(180/π) = 40</code> - * . - * <p> - * This constant is placed in the Escalator class, instead of an inner - * class, since even mathematical expressions aren't allowed in non-static - * inner classes for constants. - */ - private static final double RATIO_OF_40_DEGREES = Math.tan(2 * Math.PI / 9); - - private static final String DEFAULT_WIDTH = "400.0px"; - private static final String DEFAULT_HEIGHT = "400.0px"; - - private FlyweightRow flyweightRow = new FlyweightRow(this); - - /** The {@code <thead/>} tag. */ - private final Element headElem = DOM.createTHead(); - /** The {@code <tbody/>} tag. */ - private final Element bodyElem = DOM.createTBody(); - /** The {@code <tfoot/>} tag. */ - private final Element footElem = DOM.createTFoot(); - - /** - * TODO: investigate whether this field is now unnecessary, as - * {@link ScrollbarBundle} now caches its values. - * - * @deprecated maybe... - */ - @Deprecated - private int tBodyScrollTop = 0; - - /** - * TODO: investigate whether this field is now unnecessary, as - * {@link ScrollbarBundle} now caches its values. - * - * @deprecated maybe... - */ - @Deprecated - private int tBodyScrollLeft = 0; - - private final VerticalScrollbarBundle verticalScrollbar = new VerticalScrollbarBundle(); - private final HorizontalScrollbarBundle horizontalScrollbar = new HorizontalScrollbarBundle(); - - private final HeaderRowContainer header = new HeaderRowContainer(headElem); - private final BodyRowContainer body = new BodyRowContainer(bodyElem); - private final FooterRowContainer footer = new FooterRowContainer(footElem); - - private final Scroller scroller = new Scroller(); - - private final AbstractRowContainer[] rowContainers = new AbstractRowContainer[] { - header, body, footer }; - - private final ColumnConfigurationImpl columnConfiguration = new ColumnConfigurationImpl(); - private final Element tableWrapper; - - private PositionFunction position; - - private int internalScrollEventCalls = 0; - - /** The cached width of the escalator, in pixels. */ - private double widthOfEscalator; - /** The cached height of the escalator, in pixels. */ - private double heightOfEscalator; - - private static native double getPreciseWidth(Element element) - /*-{ - if (element.getBoundingClientRect) { - var rect = element.getBoundingClientRect(); - return rect.right - rect.left; - } else { - return element.offsetWidth; - } - }-*/; - - private static native double getPreciseHeight(Element element) - /*-{ - if (element.getBoundingClientRect) { - var rect = element.getBoundingClientRect(); - return rect.bottom - rect.top; - } else { - return element.offsetHeight; - } - }-*/; - - /** - * Creates a new Escalator widget instance. - */ - public Escalator() { - - detectAndApplyPositionFunction(); - getLogger().info( - "Using " + position.getClass().getSimpleName() - + " for position"); - - final Element root = DOM.createDiv(); - setElement(root); - - root.appendChild(verticalScrollbar.getElement()); - root.appendChild(horizontalScrollbar.getElement()); - verticalScrollbar.setScrollbarThickness(Util.getNativeScrollbarSize()); - horizontalScrollbar - .setScrollbarThickness(Util.getNativeScrollbarSize()); - - tableWrapper = DOM.createDiv(); - - root.appendChild(tableWrapper); - - final Element table = DOM.createTable(); - tableWrapper.appendChild(table); - - table.appendChild(headElem); - table.appendChild(bodyElem); - table.appendChild(footElem); - - setStylePrimaryName("v-escalator"); - - // init default dimensions - setHeight(null); - setWidth(null); - } - - @Override - protected void onLoad() { - super.onLoad(); - - header.autodetectRowHeight(); - body.autodetectRowHeight(); - footer.autodetectRowHeight(); - - header.paintInsertRows(0, header.getRowCount()); - footer.paintInsertRows(0, footer.getRowCount()); - recalculateElementSizes(); - /* - * Note: There's no need to explicitly insert rows into the body. - * - * recalculateElementSizes will recalculate the height of the body. This - * has the side-effect that as the body's size grows bigger (i.e. from 0 - * to its actual height), more escalator rows are populated. Those - * escalator rows are then immediately rendered. This, in effect, is the - * same thing as inserting those rows. - * - * In fact, having an extra paintInsertRows here would lead to duplicate - * rows. - */ - - scroller.attachScrollListener(verticalScrollbar.getElement()); - scroller.attachScrollListener(horizontalScrollbar.getElement()); - scroller.attachMousewheelListener(getElement()); - scroller.attachTouchListeners(getElement()); - } - - @Override - protected void onUnload() { - - scroller.detachScrollListener(verticalScrollbar.getElement()); - scroller.detachScrollListener(horizontalScrollbar.getElement()); - scroller.detachMousewheelListener(getElement()); - scroller.detachTouchListeners(getElement()); - - header.paintRemoveRows(0, header.getRowCount()); - footer.paintRemoveRows(0, footer.getRowCount()); - body.paintRemoveRows(0, body.getRowCount()); - - super.onUnload(); - } - - private void detectAndApplyPositionFunction() { - /* - * firefox has a bug in its translate operation, showing white space - * when adjusting the scrollbar in BodyRowContainer.paintInsertRows - */ - if (Window.Navigator.getUserAgent().contains("Firefox")) { - position = new AbsolutePosition(); - return; - } - - final Style docStyle = Document.get().getBody().getStyle(); - if (hasProperty(docStyle, "transform")) { - if (hasProperty(docStyle, "transformStyle")) { - position = new Translate3DPosition(); - } else { - position = new TranslatePosition(); - } - } else if (hasProperty(docStyle, "webkitTransform")) { - position = new WebkitTranslate3DPosition(); - } else { - position = new AbsolutePosition(); - } - } - - private Logger getLogger() { - return Logger.getLogger(getClass().getName()); - } - - private static native boolean hasProperty(Style style, String name) - /*-{ - return style[name] !== undefined; - }-*/; - - /** - * Check whether there are both columns and any row data (for either - * headers, body or footer). - * - * @return <code>true</code> iff header, body or footer has rows && there - * are columns - */ - private boolean hasColumnAndRowData() { - return (header.getRowCount() > 0 || body.getRowCount() > 0 || footer - .getRowCount() > 0) && columnConfiguration.getColumnCount() > 0; - } - - /** - * Check whether there are any cells in the DOM. - * - * @return <code>true</code> iff header, body or footer has any child - * elements - */ - private boolean hasSomethingInDom() { - return headElem.hasChildNodes() || bodyElem.hasChildNodes() - || footElem.hasChildNodes(); - } - - /** - * Returns the representation of this Escalator header. - * - * @return the header. Never <code>null</code> - */ - public RowContainer getHeader() { - return header; - } - - /** - * Returns the representation of this Escalator body. - * - * @return the body. Never <code>null</code> - */ - public RowContainer getBody() { - return body; - } - - /** - * Returns the representation of this Escalator footer. - * - * @return the footer. Never <code>null</code> - */ - public RowContainer getFooter() { - return footer; - } - - /** - * Returns the configuration object for the columns in this Escalator. - * - * @return the configuration object for the columns in this Escalator. Never - * <code>null</code> - */ - public ColumnConfiguration getColumnConfiguration() { - return columnConfiguration; - } - - /* - * TODO remove method once RequiresResize and the Vaadin layoutmanager - * listening mechanisms are implemented (https://trello.com/c/r3Kh0Kfy) - */ - @Override - public void setWidth(final String width) { - super.setWidth(width != null && !width.isEmpty() ? width - : DEFAULT_WIDTH); - recalculateElementSizes(); - } - - /* - * TODO remove method once RequiresResize and the Vaadin layoutmanager - * listening mechanisms are implemented (https://trello.com/c/r3Kh0Kfy) - */ - @Override - public void setHeight(final String height) { - final int escalatorRowsBefore = body.visualRowOrder.size(); - - super.setHeight(height != null && !height.isEmpty() ? height - : DEFAULT_HEIGHT); - recalculateElementSizes(); - - if (escalatorRowsBefore != body.visualRowOrder.size()) { - fireRowVisibilityChangeEvent(); - } - } - - /** - * Returns the vertical scroll offset. Note that this is not necessarily the - * same as the scroll top in the DOM - * - * @return the logical vertical scroll offset - */ - public double getScrollTop() { - return verticalScrollbar.getScrollPos(); - } - - /** - * Sets the vertical scroll offset. Note that this is not necessarily the - * same as the scroll top in the DOM - * - * @param scrollTop - * the number of pixels to scroll vertically - */ - public void setScrollTop(final double scrollTop) { - verticalScrollbar.setScrollPos((int) scrollTop); - } - - /** - * Returns the logical horizontal scroll offset. Note that this is not - * necessarily the same as the scroll left in the DOM. - * - * @return the logical horizontal scroll offset - */ - public int getScrollLeft() { - return horizontalScrollbar.getScrollPos(); - } - - /** - * Sets the logical horizontal scroll offset. Note that this is not - * necessarily the same as the scroll left in the DOM. - * - * @param scrollLeft - * the number of pixels to scroll horizontally - */ - public void setScrollLeft(final int scrollLeft) { - horizontalScrollbar.setScrollPos(scrollLeft); - } - - /** - * Scrolls the body horizontally so that the column at the given index is - * visible and there is at least {@code padding} pixels to the given scroll - * destination. - * - * @param columnIndex - * the index of the column to scroll to - * @param destination - * where the column should be aligned visually after scrolling - * @param padding - * the number pixels to place between the scrolled-to column and - * the viewport edge. - * @throws IndexOutOfBoundsException - * if {@code columnIndex} is not a valid index for an existing - * column - * @throws IllegalArgumentException - * if {@code destination} is {@link ScrollDestination#MIDDLE} - * and padding is nonzero, because having a padding on a - * centered column is undefined behavior, or if the column is - * frozen - */ - public void scrollToColumn(final int columnIndex, - final ScrollDestination destination, final int padding) - throws IndexOutOfBoundsException, IllegalArgumentException { - if (destination == ScrollDestination.MIDDLE && padding != 0) { - throw new IllegalArgumentException( - "You cannot have a padding with a MIDDLE destination"); - } - verifyValidColumnIndex(columnIndex); - - if (columnIndex < columnConfiguration.frozenColumns) { - throw new IllegalArgumentException("The given column index " - + columnIndex + " is frozen."); - } - - scroller.scrollToColumn(columnIndex, destination, padding); - } - - private void verifyValidColumnIndex(final int columnIndex) - throws IndexOutOfBoundsException { - if (columnIndex < 0 - || columnIndex >= columnConfiguration.getColumnCount()) { - throw new IndexOutOfBoundsException("The given column index " - + columnIndex + " does not exist."); - } - } - - /** - * Scrolls the body vertically so that the row at the given index is visible - * and there is at least {@literal padding} pixels to the given scroll - * destination. - * - * @param rowIndex - * the index of the logical row to scroll to - * @param destination - * where the row should be aligned visually after scrolling - * @param padding - * the number pixels to place between the scrolled-to row and the - * viewport edge. - * @throws IndexOutOfBoundsException - * if {@code rowIndex} is not a valid index for an existing row - * @throws IllegalArgumentException - * if {@code destination} is {@link ScrollDestination#MIDDLE} - * and padding is nonzero, because having a padding on a - * centered row is undefined behavior - */ - public void scrollToRow(final int rowIndex, - final ScrollDestination destination, final int padding) - throws IndexOutOfBoundsException, IllegalArgumentException { - if (destination == ScrollDestination.MIDDLE && padding != 0) { - throw new IllegalArgumentException( - "You cannot have a padding with a MIDDLE destination"); - } - verifyValidRowIndex(rowIndex); - - scroller.scrollToRow(rowIndex, destination, padding); - } - - private void verifyValidRowIndex(final int rowIndex) { - if (rowIndex < 0 || rowIndex >= body.getRowCount()) { - throw new IndexOutOfBoundsException("The given row index " - + rowIndex + " does not exist."); - } - } - - /** - * Recalculates the dimensions for all elements that require manual - * calculations. Also updates the dimension caches. - * <p> - * <em>Note:</em> This method has the <strong>side-effect</strong> - * automatically makes sure that an appropriate amount of escalator rows are - * present. So, if the body area grows, more <strong>escalator rows might be - * inserted</strong>. Conversely, if the body area shrinks, - * <strong>escalator rows might be removed</strong>. - */ - private void recalculateElementSizes() { - if (!isAttached()) { - return; - } - - Profiler.enter("Escalator.recalculateElementSizes"); - widthOfEscalator = getPreciseWidth(getElement()); - heightOfEscalator = getPreciseHeight(getElement()); - for (final AbstractRowContainer rowContainer : rowContainers) { - rowContainer.recalculateSectionHeight(); - } - - scroller.recalculateScrollbarsForVirtualViewport(); - body.verifyEscalatorCount(); - Profiler.leave("Escalator.recalculateElementSizes"); - } - - /** - * A routing method for {@link Scroller#onScroll(double, double)}. - * <p> - * This is a workaround for GWT and JSNI unable to properly handle inner - * classes, so instead we call the outer class' method, which calls the - * inner class' respective method. - * <p> - * Ideally, this method would not exist, and - * {@link Scroller#onScroll(double, double)} would be called directly. - */ - private void onScroll() { - scroller.onScroll(); - } - - /** - * Snap deltas of x and y to the major four axes (up, down, left, right) - * with a threshold of a number of degrees from those axes. - * - * @param deltaX - * the delta in the x axis - * @param deltaY - * the delta in the y axis - * @param thresholdRatio - * the threshold in ratio (0..1) between x and y for when to snap - * @return a two-element array: <code>[snappedX, snappedY]</code> - */ - private static double[] snapDeltas(final double deltaX, - final double deltaY, final double thresholdRatio) { - - final double[] array = new double[2]; - if (deltaX != 0 && deltaY != 0) { - final double aDeltaX = Math.abs(deltaX); - final double aDeltaY = Math.abs(deltaY); - final double yRatio = aDeltaY / aDeltaX; - final double xRatio = aDeltaX / aDeltaY; - - array[0] = (xRatio < thresholdRatio) ? 0 : deltaX; - array[1] = (yRatio < thresholdRatio) ? 0 : deltaY; - } else { - array[0] = deltaX; - array[1] = deltaY; - } - - return array; - } - - /** - * Adds an event handler that gets notified when the range of visible rows - * changes e.g. because of scrolling. - * - * @param rowVisibilityChangeHandler - * the event handler - * @return a handler registration for the added handler - */ - public HandlerRegistration addRowVisibilityChangeHandler( - RowVisibilityChangeHandler rowVisibilityChangeHandler) { - return addHandler(rowVisibilityChangeHandler, - RowVisibilityChangeEvent.TYPE); - } - - private void fireRowVisibilityChangeEvent() { - if (!body.visualRowOrder.isEmpty()) { - int visibleRangeStart = body.getLogicalRowIndex(body.visualRowOrder - .getFirst()); - int visibleRangeEnd = body.getLogicalRowIndex(body.visualRowOrder - .getLast()) + 1; - - int visibleRowCount = visibleRangeEnd - visibleRangeStart; - - fireEvent(new RowVisibilityChangeEvent(visibleRangeStart, - visibleRowCount)); - } else { - fireEvent(new RowVisibilityChangeEvent(0, 0)); - } - } - - /** - * Accesses the package private method Widget#setParent() - * - * @param widget - * The widget to access - * @param parent - * The parent to set - */ - static native final void setParent(Widget widget, Widget parent) - /*-{ - widget.@com.google.gwt.user.client.ui.Widget::setParent(Lcom/google/gwt/user/client/ui/Widget;)(parent); - }-*/; - - /** - * Returns the widget from a cell node or <code>null</code> if there is no - * widget in the cell - * - * @param cellNode - * The cell node - */ - static Widget getWidgetFromCell(Node cellNode) { - Node possibleWidgetNode = cellNode.getFirstChild(); - if (possibleWidgetNode != null - && possibleWidgetNode.getNodeType() == Node.ELEMENT_NODE) { - @SuppressWarnings("deprecation") - com.google.gwt.user.client.Element castElement = (com.google.gwt.user.client.Element) possibleWidgetNode - .cast(); - return Util.findWidget(castElement, null); - } - return null; - } - - /** - * Forces the escalator to recalculate the widths of its columns. - * <p> - * All columns that haven't been assigned an explicit width will be resized - * to fit all currently visible contents. - * - * @see ColumnConfiguration#setColumnWidth(int, int) - */ - public void calculateColumnWidths() { - boolean widthsHaveChanged = false; - for (int colIndex = 0; colIndex < columnConfiguration.getColumnCount(); colIndex++) { - if (columnConfiguration.getColumnWidth(colIndex) >= 0) { - continue; - } - - final int oldColumnWidth = columnConfiguration - .getColumnWidthActual(colIndex); - - int maxColumnWidth = 0; - maxColumnWidth = Math.max(maxColumnWidth, - header.calculateMaxColWidth(colIndex)); - maxColumnWidth = Math.max(maxColumnWidth, - body.calculateMaxColWidth(colIndex)); - maxColumnWidth = Math.max(maxColumnWidth, - footer.calculateMaxColWidth(colIndex)); - - Logger.getLogger("Escalator.calculateColumnWidths").info( - "#" + colIndex + ": " + maxColumnWidth + "px"); - - if (oldColumnWidth != maxColumnWidth) { - columnConfiguration.setCalculatedColumnWidth(colIndex, - maxColumnWidth); - widthsHaveChanged = true; - } - } - - if (widthsHaveChanged) { - header.reapplyColumnWidths(); - body.reapplyColumnWidths(); - footer.reapplyColumnWidths(); - recalculateElementSizes(); - } - } - - @Override - public void setStylePrimaryName(String style) { - super.setStylePrimaryName(style); - - verticalScrollbar.setStylePrimaryName(style); - horizontalScrollbar.setStylePrimaryName(style); - - UIObject.setStylePrimaryName(tableWrapper, style + "-tablewrapper"); - - header.setStylePrimaryName(style); - body.setStylePrimaryName(style); - footer.setStylePrimaryName(style); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java b/client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java deleted file mode 100644 index 283517fcc4..0000000000 --- a/client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - - -/** - * A functional interface that allows client code to define how a certain row in - * Escalator will be displayed. The contents of an escalator's header, body and - * footer are rendered by their respective updaters. - * <p> - * The updater is responsible for internally handling all remote communication, - * should the displayed data need to be fetched remotely. - * - * @since 7.2 - * @author Vaadin Ltd - * @see RowContainer#setEscalatorUpdater(EscalatorUpdater) - * @see Escalator#getHeader() - * @see Escalator#getBody() - * @see Escalator#getFooter() - */ -public interface EscalatorUpdater { - /** An {@link EscalatorUpdater} that doesn't render anything. */ - public static final EscalatorUpdater NULL = new EscalatorUpdater() { - @Override - public void updateCells(final Row row, - final Iterable<Cell> cellsToUpdate) { - // NOOP - } - }; - - /** - * Renders a row contained in a row container. - * <p> - * <em>Note:</em> If rendering of cells is deferred (e.g. because - * asynchronous data retrieval), this method is responsible for explicitly - * displaying some placeholder data (empty content is valid). Because the - * cells (and rows) in an escalator are recycled, failing to reset a cell - * will lead to invalid data being displayed in the escalator. - * <p> - * For performance reasons, the escalator will never autonomously clear any - * data in a cell. - * - * @param row - * information about the row to update. <em>Note:</em> You should - * not store nor reuse this reference - * @param cellsToUpdate - * a collection of cells which need to be updated. <em>Note:</em> - * You should neither store nor reuse the reference to the list, - * nor to the individual cells - */ - public void updateCells(Row row, Iterable<Cell> cellsToUpdate); -} diff --git a/client/src/com/vaadin/client/ui/grid/FlyweightCell.java b/client/src/com/vaadin/client/ui/grid/FlyweightCell.java deleted file mode 100644 index 296a70934b..0000000000 --- a/client/src/com/vaadin/client/ui/grid/FlyweightCell.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import java.util.List; - -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Style.Display; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.user.client.ui.IsWidget; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.client.ui.grid.FlyweightRow.CellIterator; - -/** - * An internal implementation of the {@link Cell} interface. - * <p> - * These instances are populated into a {@link FlyweightRow} instance, and - * intended to be reused when rendering cells in an escalator. - * - * @since 7.2 - * @author Vaadin Ltd - * @see FlyweightRow#getCells() - * @see FlyweightRow#addCells(int, int) - * @see FlyweightRow#removeCells(int, int) - */ -class FlyweightCell implements Cell { - static final String COLSPAN_ATTR = "colSpan"; - - private final int column; - private final FlyweightRow row; - - private CellIterator currentIterator = null; - - private final Escalator escalator; - - public FlyweightCell(final FlyweightRow row, final int column, - Escalator escalator) { - this.row = row; - this.column = column; - this.escalator = escalator; - } - - @Override - public int getRow() { - assertSetup(); - return row.getRow(); - } - - @Override - public int getColumn() { - assertSetup(); - return column; - } - - @Override - public Element getElement() { - return (Element) row.getElement().getChild(column); - } - - void setup(final CellIterator cellIterator) { - currentIterator = cellIterator; - - final Element e = getElement(); - e.setPropertyInt(COLSPAN_ATTR, 1); - e.getStyle().setWidth(row.getColumnWidth(column), Unit.PX); - e.getStyle().clearDisplay(); - } - - /** - * Tear down the state of the Cell. - * <p> - * This is an internal check method, to prevent retrieving uninitialized - * data by calling {@link #getRow()}, {@link #getColumn()} or - * {@link #getElement()} at an improper time. - * <p> - * This should only be used with asserts (" - * <code>assert flyweightCell.teardown()</code> ") so that the code is never - * run when asserts aren't enabled. - * - * @return always <code>true</code> - * @see FlyweightRow#teardown() - */ - boolean teardown() { - currentIterator = null; - return true; - } - - /** - * Asserts that the flyweight cell has properly been set up before trying to - * access any of its data. - */ - private void assertSetup() { - assert currentIterator != null : "FlyweightCell was not properly " - + "initialized. This is either a bug in Grid/Escalator " - + "or a Cell reference has been stored and reused " - + "inappropriately."; - } - - @Override - public void setColSpan(final int numberOfCells) { - /*- - * This will default to 1 if unset, as per DOM specifications: - * http://www.w3.org/TR/html5/tabular-data.html#attributes-common-to-td-and-th-elements - */ - final int prevColSpan = getElement().getPropertyInt(COLSPAN_ATTR); - if (numberOfCells == 1 && prevColSpan == 1) { - return; - } - - getElement().setPropertyInt(COLSPAN_ATTR, numberOfCells); - adjustCellWidthForSpan(numberOfCells); - hideOrRevealAdjacentCellElements(numberOfCells, prevColSpan); - currentIterator.setSkipNext(numberOfCells - 1); - } - - private void adjustCellWidthForSpan(final int numberOfCells) { - final int cellsToTheRight = currentIterator.rawPeekNext( - numberOfCells - 1).size(); - - final int selfWidth = row.getColumnWidth(column); - int widthsOfColumnsToTheRight = 0; - for (int i = 0; i < cellsToTheRight; i++) { - widthsOfColumnsToTheRight += row.getColumnWidth(column + i + 1); - } - getElement().getStyle().setWidth(selfWidth + widthsOfColumnsToTheRight, - Unit.PX); - } - - private void hideOrRevealAdjacentCellElements(final int numberOfCells, - final int prevColSpan) { - final int affectedCellsNumber = Math.max(prevColSpan, numberOfCells); - final List<FlyweightCell> affectedCells = currentIterator - .rawPeekNext(affectedCellsNumber - 1); - if (prevColSpan < numberOfCells) { - for (int i = 0; i < affectedCells.size(); i++) { - affectedCells.get(prevColSpan + i - 1).getElement().getStyle() - .setDisplay(Display.NONE); - } - } else if (prevColSpan > numberOfCells) { - for (int i = 0; i < affectedCells.size(); i++) { - affectedCells.get(numberOfCells + i - 1).getElement() - .getStyle().clearDisplay(); - } - } - } - - @Override - public Widget getWidget() { - return Escalator.getWidgetFromCell(getElement()); - } - - @Override - public void setWidget(Widget widget) { - - Widget oldWidget = getWidget(); - - // Validate - if (oldWidget == widget) { - return; - } - - // Detach old child. - if (oldWidget != null) { - // Orphan. - Escalator.setParent(oldWidget, null); - - // Physical detach. - getElement().removeChild(oldWidget.getElement()); - } - - // Remove any previous text nodes from previous - // setInnerText/setInnerHTML - getElement().removeAllChildren(); - - // Attach new child. - if (widget != null) { - // Detach new child from old parent. - widget.removeFromParent(); - - // Physical attach. - getElement().appendChild(widget.getElement()); - - Escalator.setParent(widget, escalator); - } - } - - @Override - public void setWidget(IsWidget w) { - setWidget(Widget.asWidgetOrNull(w)); - } - -} diff --git a/client/src/com/vaadin/client/ui/grid/FlyweightRow.java b/client/src/com/vaadin/client/ui/grid/FlyweightRow.java deleted file mode 100644 index 6bfd368c6b..0000000000 --- a/client/src/com/vaadin/client/ui/grid/FlyweightRow.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Node; - -/** - * An internal implementation of the {@link Row} interface. - * <p> - * There is only one instance per Escalator. This is designed to be re-used when - * rendering rows. - * - * @since 7.2 - * @author Vaadin Ltd - * @see Escalator.AbstractRowContainer#refreshRow(Node, int) - */ -class FlyweightRow implements Row { - - static class CellIterator implements Iterator<Cell> { - /** A defensive copy of the cells in the current row. */ - private final ArrayList<FlyweightCell> cells; - private int cursor = 0; - private int skipNext = 0; - - public CellIterator(final Collection<FlyweightCell> cells) { - this.cells = new ArrayList<FlyweightCell>(cells); - } - - @Override - public boolean hasNext() { - return cursor + skipNext < cells.size(); - } - - @Override - public FlyweightCell next() { - // if we needed to skip some cells since the last invocation. - for (int i = 0; i < skipNext; i++) { - cells.remove(cursor); - } - skipNext = 0; - - final FlyweightCell cell = cells.get(cursor++); - cell.setup(this); - return cell; - } - - @Override - public void remove() { - throw new UnsupportedOperationException( - "Cannot remove cells via iterator"); - } - - /** - * Sets the number of cells to skip when {@link #next()} is called the - * next time. Cell hiding is also handled eagerly in this method. - * - * @param colspan - * the number of cells to skip on next invocation of - * {@link #next()} - */ - public void setSkipNext(final int colspan) { - assert colspan > 0 : "Number of cells didn't make sense: " - + colspan; - skipNext = colspan; - } - - /** - * Gets the next <code>n</code> cells in the iterator, ignoring any - * possibly spanned cells. - * - * @param n - * the number of next cells to retrieve - * @return A list of next <code>n</code> cells, or less if there aren't - * enough cells to retrieve - */ - public List<FlyweightCell> rawPeekNext(final int n) { - final int from = Math.min(cursor, cells.size()); - final int to = Math.min(cursor + n, cells.size()); - return cells.subList(from, to); - } - } - - private static final int BLANK = Integer.MIN_VALUE; - - private int row; - private Element element; - private int[] columnWidths = null; - private final Escalator escalator; - private final List<FlyweightCell> cells = new ArrayList<FlyweightCell>(); - - public FlyweightRow(final Escalator escalator) { - this.escalator = escalator; - } - - @Override - public Escalator getEscalator() { - return escalator; - } - - void setup(final Element e, final int row, int[] columnWidths) { - element = e; - this.row = row; - this.columnWidths = columnWidths; - } - - /** - * Tear down the state of the Row. - * <p> - * This is an internal check method, to prevent retrieving uninitialized - * data by calling {@link #getRow()}, {@link #getElement()} or - * {@link #getCells()} at an improper time. - * <p> - * This should only be used with asserts (" - * <code>assert flyweightRow.teardown()</code> ") so that the code is never - * run when asserts aren't enabled. - * - * @return always <code>true</code> - */ - boolean teardown() { - element = null; - row = BLANK; - columnWidths = null; - for (final FlyweightCell cell : cells) { - assert cell.teardown(); - } - return true; - } - - @Override - public int getRow() { - assertSetup(); - return row; - } - - @Override - public Element getElement() { - assertSetup(); - return element; - } - - void addCells(final int index, final int numberOfColumns) { - for (int i = 0; i < numberOfColumns; i++) { - final int col = index + i; - cells.add(col, new FlyweightCell(this, col, escalator)); - } - updateRestOfCells(index + numberOfColumns); - } - - void removeCells(final int index, final int numberOfColumns) { - for (int i = 0; i < numberOfColumns; i++) { - cells.remove(index); - } - updateRestOfCells(index); - } - - private void updateRestOfCells(final int startPos) { - // update the column number for the cells to the right - for (int col = startPos; col < cells.size(); col++) { - cells.set(col, new FlyweightCell(this, col, escalator)); - } - } - - /** - * Get flyweight cells for the client code to render. - * - * @return a list of {@link FlyweightCell FlyweightCells}. They are - * generified into {@link Cell Cells}, because Java's generics - * system isn't expressive enough. - * @see #setup(Element, int) - * @see #teardown() - */ - Iterable<Cell> getCells() { - assertSetup(); - return new Iterable<Cell>() { - @Override - public Iterator<Cell> iterator() { - return new CellIterator(cells); - } - }; - } - - /** - * Asserts that the flyweight row has properly been set up before trying to - * access any of its data. - */ - private void assertSetup() { - assert element != null && row != BLANK && columnWidths != null : "Flyweight row was not " - + "properly initialized. Make sure the setup-method is " - + "called before retrieving data. This is either a bug " - + "in Escalator, or the instance of the flyweight row " - + "has been stored and accessed."; - } - - int getColumnWidth(int column) { - assertSetup(); - return columnWidths[column]; - } -} diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java deleted file mode 100644 index 02aa194655..0000000000 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ /dev/null @@ -1,1318 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; - -import com.google.gwt.core.shared.GWT; -import com.google.gwt.dom.client.Element; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HasVisibility; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.client.data.DataChangeHandler; -import com.vaadin.client.data.DataSource; -import com.vaadin.client.ui.grid.renderers.TextRenderer; -import com.vaadin.shared.ui.grid.GridConstants; -import com.vaadin.shared.ui.grid.ScrollDestination; -import com.vaadin.shared.util.SharedUtil; - -/** - * A data grid view that supports columns and lazy loading of data rows from a - * data source. - * - * <h3>Columns</h3> - * <p> - * The {@link GridColumn} class defines the renderer used to render a cell in - * the grid. Implement {@link GridColumn#getValue(Object)} to retrieve the cell - * value from the row object and return the cell renderer to render that cell. - * </p> - * <p> - * {@link GridColumn}s contain other properties like the width of the column and - * the visiblity of the column. If you want to change a column's properties - * after it has been added to the grid you can get a column object for a - * specific column index using {@link Grid#getColumn(int)}. - * </p> - * <p> - * - * TODO Explain about headers/footers once the multiple header/footer api has - * been implemented - * - * <h3>Data sources</h3> - * <p> - * TODO Explain about what a data source is and how it should be implemented. - * </p> - * - * @param <T> - * The row type of the grid. The row type is the POJO type from where - * the data is retrieved into the column cells. - * @since 7.2 - * @author Vaadin Ltd - */ -public class Grid<T> extends Composite { - - /** - * Escalator used internally by grid to render the rows - */ - private Escalator escalator = GWT.create(Escalator.class); - - /** - * List of columns in the grid. Order defines the visible order. - */ - private final List<GridColumn<?, T>> columns = new ArrayList<GridColumn<?, T>>(); - - /** - * The datasource currently in use. <em>Note:</em> it is <code>null</code> - * on initialization, but not after that. - */ - private DataSource<T> dataSource; - - /** - * The column groups rows added to the grid - */ - private final List<ColumnGroupRow<T>> columnGroupRows = new ArrayList<ColumnGroupRow<T>>(); - - /** - * Are the headers for the columns visible - */ - private boolean columnHeadersVisible = true; - - /** - * Are the footers for the columns visible - */ - private boolean columnFootersVisible = false; - - /** - * The last column frozen counter from the left - */ - private GridColumn<?, T> lastFrozenColumn; - - /** - * Base class for grid columns internally used by the Grid. The user should - * use {@link GridColumn} when creating new columns. - * - * @param <C> - * the column type - * - * @param <T> - * the row type - */ - static abstract class AbstractGridColumn<C, T> implements HasVisibility { - - /** - * The grid the column is associated with - */ - private Grid<T> grid; - - /** - * Should the column be visible in the grid - */ - private boolean visible = true; - - /** - * The text displayed in the header of the column - */ - private String header; - - /** - * Text displayed in the column footer - */ - private String footer; - - /** - * Width of column in pixels - */ - private int width = 100; - - /** - * Renderer for rendering a value into the cell - */ - private Renderer<C> bodyRenderer = new Renderer<C>() { - - @Override - public void renderCell(Cell cell, C value) { - if (value instanceof Widget) { - cell.setWidget((Widget) value); - } else if (value instanceof String) { - cell.getElement().setInnerText(value.toString()); - } else { - throw new IllegalArgumentException( - "Cell value cannot be converted into a String. Please use a custom renderer to convert the value."); - } - } - }; - - /** - * Renderer for rendering the header cell value into the cell - */ - private Renderer<String> headerRenderer = new TextRenderer(); - - /** - * Renderer for rendering the footer cell value into the cell - */ - private Renderer<String> footerRenderer = new TextRenderer(); - - /** - * Constructs a new column. - */ - public AbstractGridColumn() { - - } - - /** - * Constructs a new column with a custom renderer. - * - * @param renderer - * The renderer to use for rendering the cells - */ - public AbstractGridColumn(Renderer<C> renderer) { - if (renderer == null) { - throw new IllegalArgumentException("Renderer cannot be null."); - } - this.bodyRenderer = renderer; - } - - /** - * Constructs a new column with custom renderers for rows, header and - * footer cells. - * - * @param bodyRenderer - * The renderer to use for rendering body cells - * @param headerRenderer - * The renderer to use for rendering header cells - * @param footerRenderer - * The renderer to use for rendering footer cells - */ - public AbstractGridColumn(Renderer<C> bodyRenderer, - Renderer<String> headerRenderer, Renderer<String> footerRenderer) { - this(bodyRenderer); - if (headerRenderer == null || footerRenderer == null) { - throw new IllegalArgumentException("Renderer cannot be null."); - } - - this.headerRenderer = headerRenderer; - this.footerRenderer = footerRenderer; - } - - /** - * Internally used by the grid to set itself - * - * @param grid - */ - private void setGrid(Grid<T> grid) { - if (this.grid != null && grid != null) { - // Trying to replace grid - throw new IllegalStateException( - "Column already is attached to grid. Remove the column first from the grid and then add it."); - } - - this.grid = grid; - - setVisible(this.visible); - setWidth(this.width); - setHeaderCaption(this.header); - setFooterCaption(this.footer); - } - - /** - * Gets text in the header of the column. By default the header caption - * is empty. - * - * @return the text displayed in the column caption - */ - public String getHeaderCaption() { - return header; - } - - /** - * Returns the renderer used for rendering the header cells - * - * @return a renderer that renders header cells - */ - public Renderer<String> getHeaderRenderer() { - return headerRenderer; - } - - /** - * Sets the renderer that renders header cells. Should not be null. - * - * @param renderer - * The renderer to use for rendering header cells. - */ - public void setHeaderRenderer(Renderer<String> renderer) { - if (renderer == null) { - throw new IllegalArgumentException("Renderer cannot be null."); - } - headerRenderer = renderer; - if (grid != null) { - grid.refreshHeader(); - } - } - - /** - * Returns the renderer used for rendering the footer cells - * - * @return a renderer that renders footer cells - */ - public Renderer<String> getFooterRenderer() { - return footerRenderer; - } - - /** - * Sets the renderer that renders footer cells. Should not be null. - * - * @param renderer - * The renderer to use for rendering footer cells. - */ - public void setFooterRenderer(Renderer<String> renderer) { - if (renderer == null) { - throw new IllegalArgumentException("Renderer cannot be null."); - } - footerRenderer = renderer; - if (grid != null) { - grid.refreshFooter(); - } - } - - /** - * Sets the text in the header of the column. - * - * @param caption - * the text displayed in the column header - */ - public void setHeaderCaption(String caption) { - if (SharedUtil.equals(caption, header)) { - return; - } - - header = caption; - - if (grid != null) { - grid.refreshHeader(); - } - } - - /** - * Gets text in the footer of the column. By default the footer caption - * is empty. - * - * @return The text displayed in the footer of the column - */ - public String getFooterCaption() { - return footer; - } - - /** - * Sets text in the footer of the column. - * - * @param caption - * the text displayed in the footer of the column - */ - public void setFooterCaption(String caption) { - if (SharedUtil.equals(caption, footer)) { - return; - } - - footer = caption; - - if (grid != null) { - grid.refreshFooter(); - } - } - - /** - * Is the column visible. By default all columns are visible. - * - * @return <code>true</code> if the column is visible - */ - @Override - public boolean isVisible() { - return visible; - } - - /** - * Sets a column as visible in the grid. - * - * @param visible - * <code>true</code> if the column should be displayed in the - * grid - */ - @Override - public void setVisible(boolean visible) { - if (this.visible == visible) { - return; - } - - this.visible = visible; - - // Remove column - if (grid != null) { - int index = findIndexOfColumn(); - ColumnConfiguration conf = grid.escalator - .getColumnConfiguration(); - - if (visible) { - conf.insertColumns(index, 1); - } else { - conf.removeColumns(index, 1); - } - } - - } - - /** - * Returns the data that should be rendered into the cell. By default - * returning Strings and Widgets are supported. If the return type is a - * String then it will be treated as preformatted text. - * <p> - * To support other types you will need to pass a custom renderer to the - * column via the column constructor. - * - * @param row - * The row object that provides the cell content. - * - * @return The cell content - */ - public abstract C getValue(T row); - - /** - * The renderer to render the cell width. By default renders the data as - * a String or adds the widget into the cell if the column type is of - * widget type. - * - * @return The renderer to render the cell content with - */ - public Renderer<C> getRenderer() { - return bodyRenderer; - } - - /** - * Finds the index of this column instance - * - */ - private int findIndexOfColumn() { - return grid.findVisibleColumnIndex((GridColumn<?, T>) this); - } - - /** - * Sets the pixel width of the column. Use a negative value for the grid - * to autosize column based on content and available space - * - * @param pixels - * the width in pixels or negative for auto sizing - */ - public void setWidth(int pixels) { - this.width = pixels; - - if (grid != null && isVisible()) { - int index = findIndexOfColumn(); - ColumnConfiguration conf = grid.escalator - .getColumnConfiguration(); - conf.setColumnWidth(index, pixels); - } - } - - /** - * Returns the pixel width of the column - * - * @return pixel width of the column - */ - public int getWidth() { - if (grid == null) { - return this.width; - } else { - int index = findIndexOfColumn(); - ColumnConfiguration conf = grid.escalator - .getColumnConfiguration(); - return conf.getColumnWidth(index); - } - } - } - - /** - * Base class for header / footer escalator updater - */ - protected abstract class HeaderFooterEscalatorUpdater implements - EscalatorUpdater { - - /** - * The row container which contains the header or footer rows - */ - private RowContainer rows; - - /** - * Should the index be counted from 0-> or 0<- - */ - private boolean inverted; - - /** - * Constructs an updater for updating a header / footer - * - * @param rows - * The row container - * @param inverted - * Should index counting be inverted - */ - public HeaderFooterEscalatorUpdater(RowContainer rows, boolean inverted) { - this.rows = rows; - this.inverted = inverted; - } - - /** - * Gets the header/footer caption value - * - * @param column - * The column to get the value for. - * - * @return The value that should be rendered for the column caption - */ - public abstract String getColumnValue(GridColumn<?, T> column); - - /** - * Gets the group caption value - * - * @param group - * The group for with the caption value should be returned - * @return The value that should be rendered for the column caption - */ - public abstract String getGroupValue(ColumnGroup<T> group); - - /** - * Is the row visible in the header/footer - * - * @param row - * the row to check - * - * @return <code>true</code> if the row should be visible - */ - public abstract boolean isRowVisible(ColumnGroupRow<T> row); - - /** - * Should the first row be visible - * - * @return <code>true</code> if the first row should be visible - */ - public abstract boolean firstRowIsVisible(); - - /** - * The renderer that renders the cell - * - * @param column - * The column for which the cell should be rendered - * - * @return renderer used for rendering - */ - public abstract Renderer<String> getRenderer(GridColumn<?, T> column); - - /** - * The renderer that renders the cell for column groups - * - * @param group - * The group that should be rendered - * @return renderer used for rendering - */ - public abstract Renderer<String> getGroupRenderer(ColumnGroup<T> group); - - @Override - public void updateCells(Row row, Iterable<Cell> cellsToUpdate) { - - int rowIndex; - if (inverted) { - rowIndex = rows.getRowCount() - row.getRow() - 1; - } else { - rowIndex = row.getRow(); - } - - if (firstRowIsVisible() && rowIndex == 0) { - // column headers - for (Cell cell : cellsToUpdate) { - GridColumn<?, T> column = getColumnFromVisibleIndex(cell - .getColumn()); - if (column != null) { - getRenderer(column).renderCell(cell, - getColumnValue(column)); - } - } - - } else if (columnGroupRows.size() > 0) { - // Adjust for headers - if (firstRowIsVisible()) { - rowIndex--; - } - - // Adjust for previous invisible header rows - ColumnGroupRow<T> groupRow = null; - for (int i = 0, realIndex = 0; i < columnGroupRows.size(); i++) { - groupRow = columnGroupRows.get(i); - if (isRowVisible(groupRow)) { - if (realIndex == rowIndex) { - rowIndex = realIndex; - break; - } - realIndex++; - } - } - - assert groupRow != null; - - for (Cell cell : cellsToUpdate) { - GridColumn<?, T> column = getColumnFromVisibleIndex(cell - .getColumn()); - ColumnGroup<T> group = getGroupForColumn(groupRow, column); - Element cellElement = cell.getElement(); - - if (group != null) { - getGroupRenderer(group).renderCell(cell, - getGroupValue(group)); - cell.setColSpan(group.getColumns().size()); - } else { - // Cells are reused - cellElement.setInnerHTML(null); - cell.setColSpan(1); - } - } - } - } - } - - /** - * Creates a new instance. - */ - public Grid() { - initWidget(escalator); - - setStylePrimaryName("v-grid"); - - escalator.getHeader().setEscalatorUpdater(createHeaderUpdater()); - escalator.getBody().setEscalatorUpdater(createBodyUpdater()); - escalator.getFooter().setEscalatorUpdater(createFooterUpdater()); - - refreshHeader(); - refreshFooter(); - - escalator - .addRowVisibilityChangeHandler(new RowVisibilityChangeHandler() { - @Override - public void onRowVisibilityChange( - RowVisibilityChangeEvent event) { - if (dataSource != null) { - dataSource.ensureAvailability( - event.getFirstVisibleRow(), - event.getVisibleRowCount()); - } - } - }); - - } - - @Override - public void setStylePrimaryName(String style) { - super.setStylePrimaryName(style); - escalator.setStylePrimaryName(style); - - } - - /** - * Creates the header updater that updates the escalator header rows from - * the column and column group rows. - * - * @return the updater that updates the data in the escalator. - */ - private EscalatorUpdater createHeaderUpdater() { - return new HeaderFooterEscalatorUpdater(escalator.getHeader(), true) { - - @Override - public boolean isRowVisible(ColumnGroupRow<T> row) { - return row.isHeaderVisible(); - } - - @Override - public String getGroupValue(ColumnGroup<T> group) { - return group.getHeaderCaption(); - } - - @Override - public String getColumnValue(GridColumn<?, T> column) { - return column.getHeaderCaption(); - } - - @Override - public boolean firstRowIsVisible() { - return isColumnHeadersVisible(); - } - - @Override - public Renderer<String> getRenderer(GridColumn<?, T> column) { - return column.getHeaderRenderer(); - } - - @Override - public Renderer<String> getGroupRenderer(ColumnGroup<T> group) { - return group.getHeaderRenderer(); - } - }; - } - - private EscalatorUpdater createBodyUpdater() { - return new EscalatorUpdater() { - - @Override - public void updateCells(Row row, Iterable<Cell> cellsToUpdate) { - int rowIndex = row.getRow(); - if (dataSource == null) { - setCellsLoading(cellsToUpdate); - return; - } - - T rowData = dataSource.getRow(rowIndex); - if (rowData == null) { - setCellsLoading(cellsToUpdate); - return; - } - - for (Cell cell : cellsToUpdate) { - GridColumn column = getColumnFromVisibleIndex(cell - .getColumn()); - if (column != null) { - Object value = column.getValue(rowData); - column.getRenderer().renderCell(cell, value); - } - } - } - - private void setCellsLoading(Iterable<Cell> cellsToUpdate) { - for (Cell cell : cellsToUpdate) { - cell.getElement().setInnerText("..."); - } - } - }; - } - - /** - * Creates the footer updater that updates the escalator footer rows from - * the column and column group rows. - * - * @return the updater that updates the data in the escalator. - */ - private EscalatorUpdater createFooterUpdater() { - return new HeaderFooterEscalatorUpdater(escalator.getFooter(), false) { - - @Override - public boolean isRowVisible(ColumnGroupRow<T> row) { - return row.isFooterVisible(); - } - - @Override - public String getGroupValue(ColumnGroup<T> group) { - return group.getFooterCaption(); - } - - @Override - public String getColumnValue(GridColumn<?, T> column) { - return column.getFooterCaption(); - } - - @Override - public boolean firstRowIsVisible() { - return isColumnFootersVisible(); - } - - @Override - public Renderer<String> getRenderer(GridColumn<?, T> column) { - return column.getFooterRenderer(); - } - - @Override - public Renderer<String> getGroupRenderer(ColumnGroup<T> group) { - return group.getFooterRenderer(); - } - }; - } - - /** - * Refreshes header or footer rows on demand - * - * @param rows - * The row container - * @param firstRowIsVisible - * is the first row visible - * @param isHeader - * <code>true</code> if we refreshing the header, else assumed - * the footer - */ - private void refreshRowContainer(RowContainer rows, - boolean firstRowIsVisible, boolean isHeader) { - - // Count needed rows - int totalRows = firstRowIsVisible ? 1 : 0; - for (ColumnGroupRow<T> row : columnGroupRows) { - if (isHeader ? row.isHeaderVisible() : row.isFooterVisible()) { - totalRows++; - } - } - - // Add or Remove rows on demand - int rowDiff = totalRows - rows.getRowCount(); - if (rowDiff > 0) { - rows.insertRows(0, rowDiff); - } else if (rowDiff < 0) { - rows.removeRows(0, -rowDiff); - } - - // Refresh all the rows - if (rows.getRowCount() > 0) { - rows.refreshRows(0, rows.getRowCount()); - } - } - - /** - * Refreshes all header rows - */ - void refreshHeader() { - refreshRowContainer(escalator.getHeader(), isColumnHeadersVisible(), - true); - } - - /** - * Refreshes all footer rows - */ - void refreshFooter() { - refreshRowContainer(escalator.getFooter(), isColumnFootersVisible(), - false); - } - - /** - * Adds a column as the last column in the grid. - * - * @param column - * the column to add - */ - public void addColumn(GridColumn<?, T> column) { - ColumnConfiguration conf = escalator.getColumnConfiguration(); - addColumn(column, conf.getColumnCount()); - } - - /** - * Inserts a column into a specific position in the grid. - * - * @param index - * the index where the column should be inserted into - * @param column - * the column to add - */ - public void addColumn(GridColumn<?, T> column, int index) { - - // Register column with grid - columns.add(index, column); - - // Insert column into escalator - if (column.isVisible()) { - int visibleIndex = findVisibleColumnIndex(column); - ColumnConfiguration conf = escalator.getColumnConfiguration(); - conf.insertColumns(visibleIndex, 1); - } - - // Register this grid instance with the column - ((AbstractGridColumn<?, T>) column).setGrid(this); - - if (lastFrozenColumn != null - && ((AbstractGridColumn<?, T>) lastFrozenColumn) - .findIndexOfColumn() < index) { - refreshFrozenColumns(); - } - } - - private int findVisibleColumnIndex(GridColumn<?, T> column) { - int idx = 0; - for (GridColumn<?, T> c : columns) { - if (c == column) { - return idx; - } else if (c.isVisible()) { - idx++; - } - } - return -1; - } - - private GridColumn<?, T> getColumnFromVisibleIndex(int index) { - int idx = -1; - for (GridColumn<?, T> c : columns) { - if (c.isVisible()) { - idx++; - } - if (index == idx) { - return c; - } - } - return null; - } - - /** - * Removes a column from the grid. - * - * @param column - * the column to remove - */ - public void removeColumn(GridColumn<?, T> column) { - - int columnIndex = columns.indexOf(column); - int visibleIndex = findVisibleColumnIndex(column); - columns.remove(columnIndex); - - // de-register column with grid - ((AbstractGridColumn<?, T>) column).setGrid(null); - - if (column.isVisible()) { - ColumnConfiguration conf = escalator.getColumnConfiguration(); - conf.removeColumns(visibleIndex, 1); - } - - if (column.equals(lastFrozenColumn)) { - setLastFrozenColumn(null); - } else { - refreshFrozenColumns(); - } - } - - /** - * Returns the amount of columns in the grid. - * - * @return The number of columns in the grid - */ - public int getColumnCount() { - return columns.size(); - } - - /** - * Returns a list of columns in the grid. - * - * @return A unmodifiable list of the columns in the grid - */ - public List<GridColumn<?, T>> getColumns() { - return Collections.unmodifiableList(new ArrayList<GridColumn<?, T>>( - columns)); - } - - /** - * Returns a column by its index in the grid. - * - * @param index - * the index of the column - * @return The column in the given index - * @throws IllegalArgumentException - * if the column index does not exist in the grid - */ - public GridColumn<?, T> getColumn(int index) - throws IllegalArgumentException { - if (index < 0 || index >= columns.size()) { - throw new IllegalStateException("Column not found."); - } - return columns.get(index); - } - - /** - * Set the column headers visible. - * - * <p> - * A column header is a single cell header on top of each column reserved - * for a specific header for that column. The column header can be set by - * {@link GridColumn#setHeaderCaption(String)} and column headers cannot be - * merged with other column headers. - * </p> - * - * <p> - * All column headers occupy the first header row of the grid. If you do not - * wish to show the column headers in the grid you should hide the row by - * setting visibility of the header row to <code>false</code>. - * </p> - * - * <p> - * If you want to merge the column headers into groups you can use - * {@link ColumnGroupRow}s to group columns together and give them a common - * header. See {@link #addColumnGroupRow()} for details. - * </p> - * - * <p> - * The header row is by default visible. - * </p> - * - * @param visible - * <code>true</code> if header rows should be visible - */ - public void setColumnHeadersVisible(boolean visible) { - if (visible == isColumnHeadersVisible()) { - return; - } - columnHeadersVisible = visible; - refreshHeader(); - } - - /** - * Are the column headers visible - * - * @return <code>true</code> if they are visible - */ - public boolean isColumnHeadersVisible() { - return columnHeadersVisible; - } - - /** - * Set the column footers visible. - * - * <p> - * A column footer is a single cell footer below of each column reserved for - * a specific footer for that column. The column footer can be set by - * {@link GridColumn#setFooterCaption(String)} and column footers cannot be - * merged with other column footers. - * </p> - * - * <p> - * All column footers occupy the first footer row of the grid. If you do not - * wish to show the column footers in the grid you should hide the row by - * setting visibility of the footer row to <code>false</code>. - * </p> - * - * <p> - * If you want to merge the column footers into groups you can use - * {@link ColumnGroupRow}s to group columns together and give them a common - * footer. See {@link #addColumnGroupRow()} for details. - * </p> - * - * <p> - * The footer row is by default hidden. - * </p> - * - * @param visible - * <code>true</code> if the footer row should be visible - */ - public void setColumnFootersVisible(boolean visible) { - if (visible == isColumnFootersVisible()) { - return; - } - this.columnFootersVisible = visible; - refreshFooter(); - } - - /** - * Are the column footers visible - * - * @return <code>true</code> if they are visible - * - */ - public boolean isColumnFootersVisible() { - return columnFootersVisible; - } - - /** - * Adds a new column group row to the grid. - * - * <p> - * Column group rows are rendered in the header and footer of the grid. - * Column group rows are made up of column groups which groups together - * columns for adding a common auxiliary header or footer for the columns. - * </p> - * - * Example usage: - * - * <pre> - * // Add a new column group row to the grid - * ColumnGroupRow row = grid.addColumnGroupRow(); - * - * // Group "Column1" and "Column2" together to form a header in the row - * ColumnGroup column12 = row.addGroup("Column1", "Column2"); - * - * // Set a common header for "Column1" and "Column2" - * column12.setHeader("Column 1&2"); - * - * // Set a common footer for "Column1" and "Column2" - * column12.setFooter("Column 1&2"); - * </pre> - * - * @return a column group row instance you can use to add column groups - */ - public ColumnGroupRow<T> addColumnGroupRow() { - ColumnGroupRow<T> row = new ColumnGroupRow<T>(this); - columnGroupRows.add(row); - refreshHeader(); - refreshFooter(); - return row; - } - - /** - * Adds a new column group row to the grid at a specific index. - * - * @see #addColumnGroupRow() {@link Grid#addColumnGroupRow()} for example - * usage - * - * @param rowIndex - * the index where the column group row should be added - * @return a column group row instance you can use to add column groups - */ - public ColumnGroupRow<T> addColumnGroupRow(int rowIndex) { - ColumnGroupRow<T> row = new ColumnGroupRow<T>(this); - columnGroupRows.add(rowIndex, row); - refreshHeader(); - refreshFooter(); - return row; - } - - /** - * Removes a column group row - * - * @param row - * The row to remove - */ - public void removeColumnGroupRow(ColumnGroupRow<T> row) { - columnGroupRows.remove(row); - refreshHeader(); - refreshFooter(); - } - - /** - * Get the column group rows - * - * @return a unmodifiable list of column group rows - * - */ - public List<ColumnGroupRow<T>> getColumnGroupRows() { - return Collections.unmodifiableList(new ArrayList<ColumnGroupRow<T>>( - columnGroupRows)); - } - - /** - * Returns the column group for a row and column - * - * @param row - * The row of the column - * @param column - * the column to get the group for - * @return A column group for the row and column or <code>null</code> if not - * found. - */ - private ColumnGroup<T> getGroupForColumn(ColumnGroupRow<T> row, - GridColumn<?, T> column) { - for (ColumnGroup<T> group : row.getGroups()) { - List<GridColumn<?, T>> columns = group.getColumns(); - if (columns.contains(column)) { - return group; - } - } - return null; - } - - @Override - public void setHeight(String height) { - escalator.setHeight(height); - } - - @Override - public void setWidth(String width) { - escalator.setWidth(width); - } - - /** - * Sets the data source used by this grid. - * - * @param dataSource - * the data source to use, not null - * @throws IllegalArgumentException - * if <code>dataSource</code> is <code>null</code> - */ - public void setDataSource(DataSource<T> dataSource) - throws IllegalArgumentException { - if (dataSource == null) { - throw new IllegalArgumentException("dataSource can't be null."); - } - - if (this.dataSource != null) { - this.dataSource.setDataChangeHandler(null); - } - - this.dataSource = dataSource; - dataSource.setDataChangeHandler(new DataChangeHandler() { - @Override - public void dataUpdated(int firstIndex, int numberOfItems) { - escalator.getBody().refreshRows(firstIndex, numberOfItems); - } - - @Override - public void dataRemoved(int firstIndex, int numberOfItems) { - escalator.getBody().removeRows(firstIndex, numberOfItems); - } - - @Override - public void dataAdded(int firstIndex, int numberOfItems) { - escalator.getBody().insertRows(firstIndex, numberOfItems); - } - }); - - int previousRowCount = escalator.getBody().getRowCount(); - if (previousRowCount != 0) { - escalator.getBody().removeRows(0, previousRowCount); - } - - int estimatedSize = dataSource.getEstimatedSize(); - if (estimatedSize > 0) { - escalator.getBody().insertRows(0, estimatedSize); - } - } - - /** - * Sets the rightmost frozen column in the grid. - * <p> - * All columns up to and including the given column will be frozen in place - * when the grid is scrolled sideways. - * - * @param lastFrozenColumn - * the rightmost column to freeze, or <code>null</code> to not - * have any columns frozen - * @throws IllegalArgumentException - * if {@code lastFrozenColumn} is not a column from this grid - */ - public void setLastFrozenColumn(GridColumn<?, T> lastFrozenColumn) { - this.lastFrozenColumn = lastFrozenColumn; - refreshFrozenColumns(); - } - - private void refreshFrozenColumns() { - final int frozenCount; - if (lastFrozenColumn != null) { - frozenCount = columns.indexOf(lastFrozenColumn) + 1; - if (frozenCount == 0) { - throw new IllegalArgumentException( - "The given column isn't attached to this grid"); - } - } else { - frozenCount = 0; - } - - escalator.getColumnConfiguration().setFrozenColumnCount(frozenCount); - } - - /** - * Gets the rightmost frozen column in the grid. - * <p> - * <em>Note:</em> Most usually, this method returns the very value set with - * {@link #setLastFrozenColumn(GridColumn)}. This value, however, can be - * reset to <code>null</code> if the column is removed from this grid. - * - * @return the rightmost frozen column in the grid, or <code>null</code> if - * no columns are frozen. - */ - public GridColumn<?, T> getLastFrozenColumn() { - return lastFrozenColumn; - } - - public HandlerRegistration addRowVisibilityChangeHandler( - RowVisibilityChangeHandler handler) { - /* - * Reusing Escalator's RowVisibilityChangeHandler, since a scroll - * concept is too abstract. e.g. the event needs to be re-sent when the - * widget is resized. - */ - return escalator.addRowVisibilityChangeHandler(handler); - } - - /** - * Scrolls to a certain row, using {@link ScrollDestination#ANY}. - * - * @param rowIndex - * zero-based index of the row to scroll to. - * @throws IllegalArgumentException - * if rowIndex is below zero, or above the maximum value - * supported by the data source. - */ - public void scrollToRow(int rowIndex) throws IllegalArgumentException { - scrollToRow(rowIndex, ScrollDestination.ANY, - GridConstants.DEFAULT_PADDING); - } - - /** - * Scrolls to a certain row, using user-specified scroll destination. - * - * @param rowIndex - * zero-based index of the row to scroll to. - * @param destination - * desired destination placement of scrolled-to-row. See - * {@link ScrollDestination} for more information. - * @throws IllegalArgumentException - * if rowIndex is below zero, or above the maximum value - * supported by the data source. - */ - public void scrollToRow(int rowIndex, ScrollDestination destination) - throws IllegalArgumentException { - scrollToRow(rowIndex, destination, - destination == ScrollDestination.MIDDLE ? 0 - : GridConstants.DEFAULT_PADDING); - } - - /** - * Scrolls to a certain row using only user-specified parameters. - * - * @param rowIndex - * zero-based index of the row to scroll to. - * @param destination - * desired destination placement of scrolled-to-row. See - * {@link ScrollDestination} for more information. - * @param paddingPx - * number of pixels to overscroll. Behavior depends on - * destination. - * @throws IllegalArgumentException - * if {@code destination} is {@link ScrollDestination#MIDDLE} - * and padding is nonzero, because having a padding on a - * centered row is undefined behavior, or if rowIndex is below - * zero or above the row count of the data source. - */ - private void scrollToRow(int rowIndex, ScrollDestination destination, - int paddingPx) throws IllegalArgumentException { - int maxsize = escalator.getBody().getRowCount() - 1; - - if (rowIndex < 0) { - throw new IllegalArgumentException("Row index (" + rowIndex - + ") is below zero!"); - } - - if (rowIndex > maxsize) { - throw new IllegalArgumentException("Row index (" + rowIndex - + ") is above maximum (" + maxsize + ")!"); - } - - escalator.scrollToRow(rowIndex, destination, paddingPx); - } - - /** - * Scrolls to the beginning of the very first row. - */ - public void scrollToStart() { - scrollToRow(0, ScrollDestination.START); - } - - /** - * Scrolls to the end of the very last row. - */ - public void scrollToEnd() { - scrollToRow(escalator.getBody().getRowCount() - 1, - ScrollDestination.END); - } - - private static final Logger getLogger() { - return Logger.getLogger(Grid.class.getName()); - } - -} diff --git a/client/src/com/vaadin/client/ui/grid/GridColumn.java b/client/src/com/vaadin/client/ui/grid/GridColumn.java deleted file mode 100644 index afd80a5f5a..0000000000 --- a/client/src/com/vaadin/client/ui/grid/GridColumn.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -/** - * Represents a column in the {@link Grid}. - * - * @param <C> - * The column type - * - * @param <T> - * The row type - * - * @since 7.2 - * @author Vaadin Ltd - */ -public abstract class GridColumn<C, T> extends Grid.AbstractGridColumn<C, T> { - - /* - * This class is a convenience class so you do not have to reference - * Grid.AbstractGridColumn in your production code. The real implementation - * should be in the abstract class. - */ - - /** - * Constructs a new column. - */ - public GridColumn() { - super(); - } - - /** - * Constructs a new column with a custom renderer. - * - * @param renderer - * The renderer to use for rendering the cells - */ - public GridColumn(Renderer<C> renderer) { - super(renderer); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java deleted file mode 100644 index 5e0664667d..0000000000 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.vaadin.client.communication.StateChangeEvent; -import com.vaadin.client.ui.AbstractComponentConnector; -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.grid.ColumnGroupRowState; -import com.vaadin.shared.ui.grid.ColumnGroupState; -import com.vaadin.shared.ui.grid.GridClientRpc; -import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridServerRpc; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.ScrollDestination; - -/** - * Connects the client side {@link Grid} widget with the server side - * {@link com.vaadin.ui.components.grid.Grid} component. - * - * @since 7.2 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.components.grid.Grid.class) -public class GridConnector extends AbstractComponentConnector { - - /** - * Custom implementation of the custom grid column using a String[]Â to - * represent the cell value and String as a column type. - */ - private class CustomGridColumn extends GridColumn<String, String[]> { - - private final int columnIndex; - - public CustomGridColumn(int columnIndex) { - this.columnIndex = columnIndex; - } - - @Override - public String getValue(String[] obj) { - return obj[columnIndex]; - } - } - - /** - * Maps a generated column id to a grid column instance - */ - private Map<String, CustomGridColumn> columnIdToColumn = new HashMap<String, CustomGridColumn>(); - - @Override - protected Grid<String[]> createWidget() { - // FIXME Shouldn't be needed after #12873 has been fixed. - return new Grid<String[]>(); - } - - @Override - @SuppressWarnings("unchecked") - public Grid<String[]> getWidget() { - return (Grid<String[]>) super.getWidget(); - } - - @Override - public GridState getState() { - return (GridState) super.getState(); - } - - @Override - protected void init() { - super.init(); - getWidget().addRowVisibilityChangeHandler( - new RowVisibilityChangeHandler() { - @Override - public void onRowVisibilityChange( - RowVisibilityChangeEvent event) { - getRpcProxy(GridServerRpc.class).setVisibleRows( - event.getFirstVisibleRow(), - event.getVisibleRowCount()); - } - }); - - registerRpc(GridClientRpc.class, new GridClientRpc() { - @Override - public void scrollToStart() { - getWidget().scrollToStart(); - } - - @Override - public void scrollToEnd() { - getWidget().scrollToEnd(); - } - - @Override - public void scrollToRow(int row, ScrollDestination destination) { - getWidget().scrollToRow(row, destination); - } - }); - } - - @Override - public void onStateChanged(StateChangeEvent stateChangeEvent) { - super.onStateChanged(stateChangeEvent); - - // Column updates - if (stateChangeEvent.hasPropertyChanged("columns")) { - - int totalColumns = getState().columns.size(); - - // Remove old columns - purgeRemovedColumns(); - - int currentColumns = getWidget().getColumnCount(); - - // Add new columns - for (int columnIndex = currentColumns; columnIndex < totalColumns; columnIndex++) { - addColumnFromStateChangeEvent(columnIndex); - } - - // Update old columns - for (int columnIndex = 0; columnIndex < currentColumns; columnIndex++) { - // FIXME Currently updating all column header / footers when a - // change in made in one column. When the framework supports - // quering a specific item in a list then it should do so here. - updateColumnFromStateChangeEvent(columnIndex); - } - } - - // Header - if (stateChangeEvent.hasPropertyChanged("columnHeadersVisible")) { - getWidget() - .setColumnHeadersVisible(getState().columnHeadersVisible); - } - - // Footer - if (stateChangeEvent.hasPropertyChanged("columnFootersVisible")) { - getWidget() - .setColumnFootersVisible(getState().columnFootersVisible); - } - - // Column row groups - if (stateChangeEvent.hasPropertyChanged("columnGroupRows")) { - updateColumnGroupsFromStateChangeEvent(); - } - - if (stateChangeEvent.hasPropertyChanged("lastFrozenColumnId")) { - String frozenColId = getState().lastFrozenColumnId; - if (frozenColId != null) { - CustomGridColumn column = columnIdToColumn.get(frozenColId); - assert column != null : "Column to be frozen could not be found (id:" - + frozenColId + ")"; - getWidget().setLastFrozenColumn(column); - } else { - getWidget().setLastFrozenColumn(null); - } - } - } - - /** - * Updates a column from a state change event. - * - * @param columnIndex - * The index of the column to update - */ - private void updateColumnFromStateChangeEvent(int columnIndex) { - GridColumn<?, String[]> column = getWidget().getColumn(columnIndex); - GridColumnState columnState = getState().columns.get(columnIndex); - updateColumnFromState(column, columnState); - } - - /** - * Adds a new column to the grid widget from a state change event - * - * @param columnIndex - * The index of the column, according to how it - */ - private void addColumnFromStateChangeEvent(int columnIndex) { - GridColumnState state = getState().columns.get(columnIndex); - CustomGridColumn column = new CustomGridColumn(columnIndex); - updateColumnFromState(column, state); - - columnIdToColumn.put(state.id, column); - - getWidget().addColumn(column, columnIndex); - } - - /** - * Updates the column values from a state - * - * @param column - * The column to update - * @param state - * The state to get the data from - */ - private static void updateColumnFromState(GridColumn<?, String[]> column, - GridColumnState state) { - column.setVisible(state.visible); - column.setHeaderCaption(state.header); - column.setFooterCaption(state.footer); - column.setWidth(state.width); - } - - /** - * Removes any orphan columns that has been removed from the state from the - * grid - */ - private void purgeRemovedColumns() { - - // Get columns still registered in the state - Set<String> columnsInState = new HashSet<String>(); - for (GridColumnState columnState : getState().columns) { - columnsInState.add(columnState.id); - } - - // Remove column no longer in state - Iterator<String> columnIdIterator = columnIdToColumn.keySet() - .iterator(); - while (columnIdIterator.hasNext()) { - String id = columnIdIterator.next(); - if (!columnsInState.contains(id)) { - CustomGridColumn column = columnIdToColumn.get(id); - columnIdIterator.remove(); - getWidget().removeColumn(column); - } - } - } - - /** - * Updates the column groups from a state change - */ - private void updateColumnGroupsFromStateChangeEvent() { - - // FIXME When something changes the header/footer rows will be - // re-created. At some point we should optimize this so partial updates - // can be made on the header/footer. - for (ColumnGroupRow<String[]> row : getWidget().getColumnGroupRows()) { - getWidget().removeColumnGroupRow(row); - } - - for (ColumnGroupRowState rowState : getState().columnGroupRows) { - ColumnGroupRow<String[]> row = getWidget().addColumnGroupRow(); - row.setFooterVisible(rowState.footerVisible); - row.setHeaderVisible(rowState.headerVisible); - - for (ColumnGroupState groupState : rowState.groups) { - List<GridColumn<String, String[]>> columns = new ArrayList<GridColumn<String, String[]>>(); - for (String columnId : groupState.columns) { - CustomGridColumn column = columnIdToColumn.get(columnId); - columns.add(column); - } - ColumnGroup<String[]> group = row.addGroup(columns - .toArray(new GridColumn[columns.size()])); - group.setFooterCaption(groupState.footer); - group.setHeaderCaption(groupState.header); - } - } - } -} diff --git a/client/src/com/vaadin/client/ui/grid/PositionFunction.java b/client/src/com/vaadin/client/ui/grid/PositionFunction.java deleted file mode 100644 index e41e533996..0000000000 --- a/client/src/com/vaadin/client/ui/grid/PositionFunction.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Style.Unit; - -/** - * A functional interface that can be used for positioning elements in the DOM. - * - * @since 7.2 - * @author Vaadin Ltd - */ -interface PositionFunction { - /** - * A position function using "transform: translate3d(x,y,z)" to position - * elements in the DOM. - */ - public static class Translate3DPosition implements PositionFunction { - @Override - public void set(Element e, double x, double y) { - e.getStyle().setProperty("transform", - "translate3d(" + x + "px, " + y + "px, 0)"); - } - - @Override - public void reset(Element e) { - e.getStyle().clearProperty("transform"); - } - } - - /** - * A position function using "transform: translate(x,y)" to position - * elements in the DOM. - */ - public static class TranslatePosition implements PositionFunction { - @Override - public void set(Element e, double x, double y) { - e.getStyle().setProperty("transform", - "translate(" + x + "px," + y + "px)"); - } - - @Override - public void reset(Element e) { - e.getStyle().clearProperty("transform"); - } - } - - /** - * A position function using "-webkit-transform: translate3d(x,y,z)" to - * position elements in the DOM. - */ - public static class WebkitTranslate3DPosition implements PositionFunction { - @Override - public void set(Element e, double x, double y) { - e.getStyle().setProperty("webkitTransform", - "translate3d(" + x + "px," + y + "px,0)"); - } - - @Override - public void reset(Element e) { - e.getStyle().clearProperty("webkitTransform"); - } - } - - /** - * A position function using "left: x" and "top: y" to position elements in - * the DOM. - */ - public static class AbsolutePosition implements PositionFunction { - @Override - public void set(Element e, double x, double y) { - e.getStyle().setLeft(x, Unit.PX); - e.getStyle().setTop(y, Unit.PX); - } - - @Override - public void reset(Element e) { - e.getStyle().clearLeft(); - e.getStyle().clearTop(); - } - } - - /** - * Position an element in an (x,y) coordinate system in the DOM. - * - * @param e - * the element to position. Never <code>null</code>. - * @param x - * the x coordinate, in pixels - * @param y - * the y coordinate, in pixels - */ - void set(Element e, double x, double y); - - /** - * Resets any previously applied positioning, clearing the used style - * attributes. - * - * @param e - * the element for which to reset the positioning - */ - void reset(Element e); -}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/grid/Renderer.java b/client/src/com/vaadin/client/ui/grid/Renderer.java deleted file mode 100644 index 3312ec87e4..0000000000 --- a/client/src/com/vaadin/client/ui/grid/Renderer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -/** - * Renderer for rending a value <T> into cell. - * <p> - * You can add a renderer to any column by overring the - * {@link GridColumn#getRenderer()} method and returning your own renderer. You - * can retrieve the cell element using {@link Cell#getElement()}. - * - * @param <T> - * The column type - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface Renderer<T> { - - /** - * Called whenever the {@link Grid} updates a cell - * - * @param cell - * The cell that gets updated - * - * @param data - * The column data object - */ - public void renderCell(Cell cell, T data); -} diff --git a/client/src/com/vaadin/client/ui/grid/Row.java b/client/src/com/vaadin/client/ui/grid/Row.java deleted file mode 100644 index 209da58fec..0000000000 --- a/client/src/com/vaadin/client/ui/grid/Row.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import com.google.gwt.dom.client.Element; - -/** - * A representation of a row in an {@link Escalator}. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface Row { - /** - * Gets the escalator containing the row. - * - * @return the escalator containing the row - */ - public Escalator getEscalator(); - - /** - * Gets the row index. - * - * @return the row index - */ - public int getRow(); - - /** - * Gets the root element for this row. - * <p> - * The {@link EscalatorUpdater} may update the class names of the element - * and add inline styles, but may not modify the contained DOM structure. - * <p> - * If you wish to modify the cells within this row element, access them via - * the <code>List<{@link Cell}></code> objects passed in to - * {@code EscalatorUpdater.updateCells(Row, List)} - * - * @return the root element of the row - */ - public Element getElement(); -}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/grid/RowContainer.java b/client/src/com/vaadin/client/ui/grid/RowContainer.java deleted file mode 100644 index 0312164569..0000000000 --- a/client/src/com/vaadin/client/ui/grid/RowContainer.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -/** - * A representation of the rows in each of the sections (header, body and - * footer) in an {@link Escalator}. - * - * @since 7.2 - * @author Vaadin Ltd - * @see Escalator#getHeader() - * @see Escalator#getBody() - * @see Escalator#getFooter() - */ -public interface RowContainer { - - /** - * An arbitrary pixel height of a row, before any autodetection for the row - * height has been made. - * */ - public static final int INITIAL_DEFAULT_ROW_HEIGHT = 20; - - /** - * Returns the current {@link EscalatorUpdater} used to render cells. - * - * @return the current escalator updater - */ - public EscalatorUpdater getEscalatorUpdater(); - - /** - * Sets the {@link EscalatorUpdater} to use when displaying data in the - * escalator. - * - * @param escalatorUpdater - * the escalator updater to use to render cells. May not be - * <code>null</code> - * @throws IllegalArgumentException - * if {@code cellRenderer} is <code>null</code> - * @see EscalatorUpdater#NULL - */ - public void setEscalatorUpdater(EscalatorUpdater escalatorUpdater) - throws IllegalArgumentException; - - /** - * Removes rows at a certain index in the current row container. - * - * @param index - * the index of the first row to be removed - * @param numberOfRows - * the number of rows to remove, starting from the index - * @throws IndexOutOfBoundsException - * if any integer number in the range - * <code>[index..(index+numberOfRows)]</code> is not an existing - * row index - * @throws IllegalArgumentException - * if {@code numberOfRows} is less than 1. - */ - public void removeRows(int index, int numberOfRows) - throws IndexOutOfBoundsException, IllegalArgumentException; - - /** - * Adds rows at a certain index in this row container. - * <p> - * The new rows will be inserted between the row at the index, and the row - * before (an index of 0 means that the rows are inserted at the beginning). - * Therefore, the rows currently at the index and afterwards will be moved - * downwards. - * <p> - * The contents of the inserted rows will subsequently be queried from the - * escalator updater. - * <p> - * <em>Note:</em> Only the contents of the inserted rows will be rendered. - * If inserting new rows affects the contents of existing rows, - * {@link #refreshRows(int, int)} needs to be called for those rows - * separately. - * - * @param index - * the index of the row before which new rows are inserted, or - * {@link #getRowCount()} to add rows at the end - * @param numberOfRows - * the number of rows to insert after the <code>index</code> - * @see #setEscalatorUpdater(EscalatorUpdater) - * @throws IndexOutOfBoundsException - * if <code>index</code> is not an integer in the range - * <code>[0..{@link #getRowCount()}]</code> - * @throws IllegalArgumentException - * if {@code numberOfRows} is less than 1. - */ - public void insertRows(int index, int numberOfRows) - throws IndexOutOfBoundsException, IllegalArgumentException; - - /** - * Refreshes a range of rows in the current row container. - * <p> - * The data for the refreshed rows are queried from the current cell - * renderer. - * - * @param index - * the index of the first row that will be updated - * @param numberOfRows - * the number of rows to update, starting from the index - * @see #setEscalatorUpdater(EscalatorUpdater) - * @throws IndexOutOfBoundsException - * if any integer number in the range - * <code>[index..(index+numberOfColumns)]</code> is not an - * existing column index. - * @throws IllegalArgumentException - * if {@code numberOfRows} is less than 1. - */ - public void refreshRows(int index, int numberOfRows) - throws IndexOutOfBoundsException, IllegalArgumentException; - - /** - * Gets the number of rows in the current row container. - * - * @return the number of rows in the current row container - */ - public int getRowCount(); - - /** - * The default height of the rows in this RowContainer. - * - * @param px - * the default height in pixels of the rows in this RowContainer - * @throws IllegalArgumentException - * if <code>px < 1</code> - * @see #getDefaultRowHeight() - */ - public void setDefaultRowHeight(int px) throws IllegalArgumentException; - - /** - * Returns the default height of the rows in this RowContainer. - * <p> - * This value will be equal to {@link #INITIAL_DEFAULT_ROW_HEIGHT} if the - * {@link Escalator} has not yet had a chance to autodetect the row height, - * or no explicit value has yet given via {@link #setDefaultRowHeight(int)} - * - * @return the default height of the rows in this RowContainer, in pixels - * @see #setDefaultRowHeight(int) - */ - public int getDefaultRowHeight(); -} diff --git a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java b/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java deleted file mode 100644 index 0e9652e215..0000000000 --- a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import com.google.gwt.event.shared.GwtEvent; - -/** - * Event fired when the range of visible rows changes e.g. because of scrolling. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class RowVisibilityChangeEvent extends - GwtEvent<RowVisibilityChangeHandler> { - /** - * The type of this event. - */ - public static final Type<RowVisibilityChangeHandler> TYPE = new Type<RowVisibilityChangeHandler>(); - - private final int firstVisibleRow; - private final int visibleRowCount; - - /** - * Creates a new row visibility change event - * - * @param firstVisibleRow - * the index of the first visible row - * @param visibleRowCount - * the number of visible rows - */ - public RowVisibilityChangeEvent(int firstVisibleRow, int visibleRowCount) { - this.firstVisibleRow = firstVisibleRow; - this.visibleRowCount = visibleRowCount; - } - - /** - * Gets the index of the first row that is at least partially visible. - * - * @return the index of the first visible row - */ - public int getFirstVisibleRow() { - return firstVisibleRow; - } - - /** - * Gets the number of at least partially visible rows. - * - * @return the number of visible rows - */ - public int getVisibleRowCount() { - return visibleRowCount; - } - - /* - * (non-Javadoc) - * - * @see com.google.gwt.event.shared.GwtEvent#getAssociatedType() - */ - @Override - public Type<RowVisibilityChangeHandler> getAssociatedType() { - return TYPE; - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.event.shared.GwtEvent#dispatch(com.google.gwt.event.shared - * .EventHandler) - */ - @Override - protected void dispatch(RowVisibilityChangeHandler handler) { - handler.onRowVisibilityChange(this); - } - -} diff --git a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java b/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java deleted file mode 100644 index dd24521499..0000000000 --- a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import com.google.gwt.event.shared.EventHandler; - -/** - * Event handler that gets notified when the range of visible rows changes e.g. - * because of scrolling. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface RowVisibilityChangeHandler extends EventHandler { - - /** - * Called when the range of visible rows changes e.g. because of scrolling. - * - * @param event - * the row visibility change event describing the change - */ - void onRowVisibilityChange(RowVisibilityChangeEvent event); - -} diff --git a/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java b/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java deleted file mode 100644 index b9267178c1..0000000000 --- a/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Style.Overflow; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.user.client.DOM; - -/** - * An element-like bundle representing a configurable and visual scrollbar in - * one axis. - * - * @since 7.2 - * @author Vaadin Ltd - * @see VerticalScrollbarBundle - * @see HorizontalScrollbarBundle - */ -abstract class ScrollbarBundle { - - /** - * The pixel size for OSX's invisible scrollbars. - * <p> - * Touch devices don't show a scrollbar at all, so the scrollbar size is - * irrelevant in their case. There doesn't seem to be any other popular - * platforms that has scrollbars similar to OSX. Thus, this behavior is - * tailored for OSX only, until additional platforms start behaving this - * way. - */ - private static final int OSX_INVISIBLE_SCROLLBAR_FAKE_SIZE_PX = 13; - - /** - * A representation of a single vertical scrollbar. - * - * @see VerticalScrollbarBundle#getElement() - */ - final static class VerticalScrollbarBundle extends ScrollbarBundle { - - @Override - public void setStylePrimaryName(String primaryStyleName) { - super.setStylePrimaryName(primaryStyleName); - root.addClassName(primaryStyleName + "-scroller-vertical"); - } - - @Override - protected void internalSetScrollPos(int px) { - root.setScrollTop(px); - } - - @Override - protected int internalGetScrollPos() { - return root.getScrollTop(); - } - - @Override - protected void internalSetScrollSize(int px) { - scrollSizeElement.getStyle().setHeight(px, Unit.PX); - } - - @Override - public int getScrollSize() { - return scrollSizeElement.getOffsetHeight(); - } - - @Override - protected void internalSetOffsetSize(int px) { - root.getStyle().setHeight(px, Unit.PX); - } - - @Override - public int getOffsetSize() { - return root.getOffsetHeight(); - } - - @Override - protected void internalSetScrollbarThickness(int px) { - root.getStyle().setWidth(px, Unit.PX); - scrollSizeElement.getStyle().setWidth(px, Unit.PX); - } - - @Override - protected int internalGetScrollbarThickness() { - return root.getOffsetWidth(); - } - - @Override - protected void forceScrollbar(boolean enable) { - if (enable) { - root.getStyle().setOverflowY(Overflow.SCROLL); - } else { - root.getStyle().clearOverflowY(); - } - } - } - - /** - * A representation of a single horizontal scrollbar. - * - * @see HorizontalScrollbarBundle#getElement() - */ - final static class HorizontalScrollbarBundle extends ScrollbarBundle { - - @Override - public void setStylePrimaryName(String primaryStyleName) { - super.setStylePrimaryName(primaryStyleName); - root.addClassName(primaryStyleName + "-scroller-horizontal"); - } - - @Override - protected void internalSetScrollPos(int px) { - root.setScrollLeft(px); - } - - @Override - protected int internalGetScrollPos() { - return root.getScrollLeft(); - } - - @Override - protected void internalSetScrollSize(int px) { - scrollSizeElement.getStyle().setWidth(px, Unit.PX); - } - - @Override - public int getScrollSize() { - return scrollSizeElement.getOffsetWidth(); - } - - @Override - protected void internalSetOffsetSize(int px) { - root.getStyle().setWidth(px, Unit.PX); - } - - @Override - public int getOffsetSize() { - return root.getOffsetWidth(); - } - - @Override - protected void internalSetScrollbarThickness(int px) { - root.getStyle().setHeight(px, Unit.PX); - scrollSizeElement.getStyle().setHeight(px, Unit.PX); - } - - @Override - protected int internalGetScrollbarThickness() { - return root.getOffsetHeight(); - } - - @Override - protected void forceScrollbar(boolean enable) { - if (enable) { - root.getStyle().setOverflowX(Overflow.SCROLL); - } else { - root.getStyle().clearOverflowX(); - } - } - } - - protected final Element root = DOM.createDiv(); - protected final Element scrollSizeElement = DOM.createDiv(); - protected boolean isInvisibleScrollbar = false; - - private int scrollPos = 0; - private int maxScrollPos = 0; - - private ScrollbarBundle() { - root.appendChild(scrollSizeElement); - } - - /** - * Sets the primary style name - * - * @param primaryStyleName - * The primary style name to use - */ - public void setStylePrimaryName(String primaryStyleName) { - root.setClassName(primaryStyleName + "-scroller"); - } - - /** - * Gets the root element of this scrollbar-composition. - * - * @return the root element - */ - public final Element getElement() { - return root; - } - - /** - * Modifies the scroll position of this scrollbar by a number of pixels - * - * @param delta - * the delta in pixels to change the scroll position by - */ - public final void setScrollPosByDelta(int delta) { - if (delta != 0) { - setScrollPos(getScrollPos() + delta); - } - } - - /** - * Modifies {@link #root root's} dimensions in the axis the scrollbar is - * representing. - * - * @param px - * the new size of {@link #root} in the dimension this scrollbar - * is representing - */ - protected abstract void internalSetOffsetSize(int px); - - /** - * Sets the length of the scrollbar. - * - * @param px - * the length of the scrollbar in pixels - */ - public final void setOffsetSize(int px) { - internalSetOffsetSize(px); - forceScrollbar(showsScrollHandle()); - recalculateMaxScrollPos(); - } - - /** - * Force the scrollbar to be visible with CSS. In practice, this means to - * set either <code>overflow-x</code> or <code>overflow-y</code> to " - * <code>scroll</code>" in the scrollbar's direction. - * <p> - * This is an IE8 workaround, since it doesn't always show scrollbars with - * <code>overflow: auto</code> enabled. - */ - protected abstract void forceScrollbar(boolean enable); - - /** - * Gets the length of the scrollbar - * - * @return the length of the scrollbar in pixels - */ - public abstract int getOffsetSize(); - - /** - * Sets the scroll position of the scrollbar in the axis the scrollbar is - * representing. - * - * @param px - * the new scroll position in pixels - */ - public final void setScrollPos(int px) { - int oldScrollPos = scrollPos; - scrollPos = Math.max(0, Math.min(maxScrollPos, px)); - - if (oldScrollPos != scrollPos) { - internalSetScrollPos(px); - } - } - - protected abstract void internalSetScrollPos(int px); - - /** - * Gets the scroll position of the scrollbar in the axis the scrollbar is - * representing. - * - * @return the new scroll position in pixels - */ - public final int getScrollPos() { - assert internalGetScrollPos() == scrollPos : "calculated scroll position (" - + scrollPos - + ") did not match the DOM element scroll position (" - + internalGetScrollPos() + ")"; - return scrollPos; - } - - protected abstract int internalGetScrollPos(); - - /** - * Modifies {@link #scrollSizeElement scrollSizeElement's} dimensions in - * such a way that the scrollbar is able to scroll a certain number of - * pixels in the axis it is representing. - * - * @param px - * the new size of {@link #scrollSizeElement} in the dimension - * this scrollbar is representing - */ - protected abstract void internalSetScrollSize(int px); - - /** - * Sets the amount of pixels the scrollbar needs to be able to scroll - * through. - * - * @param px - * the number of pixels the scrollbar should be able to scroll - * through - */ - public final void setScrollSize(int px) { - internalSetScrollSize(px); - forceScrollbar(showsScrollHandle()); - recalculateMaxScrollPos(); - } - - /** - * Gets the amount of pixels the scrollbar needs to be able to scroll - * through. - * - * @return the number of pixels the scrollbar should be able to scroll - * through - */ - public abstract int getScrollSize(); - - /** - * Modifies {@link #scrollSizeElement scrollSizeElement's} dimensions in the - * opposite axis to what the scrollbar is representing. - * - * @param px - * the dimension that {@link #scrollSizeElement} should take in - * the opposite axis to what the scrollbar is representing - */ - protected abstract void internalSetScrollbarThickness(int px); - - /** - * Sets the scrollbar's thickness. - * <p> - * If the thickness is set to 0, the scrollbar will be treated as an - * "invisible" scrollbar. This means, the DOM structure will be given a - * non-zero size, but {@link #getScrollbarThickness()} will still return the - * value 0. - * - * @param px - * the scrollbar's thickness in pixels - */ - public final void setScrollbarThickness(int px) { - isInvisibleScrollbar = (px == 0); - internalSetScrollbarThickness(px != 0 ? px - : OSX_INVISIBLE_SCROLLBAR_FAKE_SIZE_PX); - } - - /** - * Gets the scrollbar's thickness as defined in the DOM. - * - * @return the scrollbar's thickness as defined in the DOM, in pixels - */ - protected abstract int internalGetScrollbarThickness(); - - /** - * Gets the scrollbar's thickness. - * <p> - * This value will differ from the value in the DOM, if the thickness was - * set to 0 with {@link #setScrollbarThickness(int)}, as the scrollbar is - * then treated as "invisible." - * - * @return the scrollbar's thickness in pixels - */ - public final int getScrollbarThickness() { - if (!isInvisibleScrollbar) { - return internalGetScrollbarThickness(); - } else { - return 0; - } - } - - /** - * Checks whether the scrollbar's handle is visible. - * <p> - * In other words, this method checks whether the contents is larger than - * can visually fit in the element. - * - * @return <code>true</code> iff the scrollbar's handle is visible - */ - public boolean showsScrollHandle() { - return getOffsetSize() < getScrollSize(); - } - - public void recalculateMaxScrollPos() { - int scrollSize = getScrollSize(); - int offsetSize = getOffsetSize(); - maxScrollPos = Math.max(0, scrollSize - offsetSize); - - // make sure that the correct max scroll position is maintained. - setScrollPos(scrollPos); - } - - /** - * This is a method that JSNI can call to synchronize the object state from - * the DOM. - */ - @SuppressWarnings("unused") - private final void updateScrollPosFromDom() { - scrollPos = internalGetScrollPos(); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java b/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java deleted file mode 100644 index 0a3edbd349..0000000000 --- a/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid.datasources; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import com.vaadin.client.data.DataChangeHandler; -import com.vaadin.client.data.DataSource; - -/** - * A simple list based on an in-memory data source for simply adding a list of - * row pojos to the grid. Based on a wrapped list instance which supports adding - * and removing of items. - * - * <p> - * Usage: - * - * <pre> - * ListDataSource<Integer> ds = new ListDataSource<Integer>(1, 2, 3, 4); - * - * // Add item to the data source - * ds.asList().add(5); - * - * // Remove item from the data source - * ds.asList().remove(3); - * - * // Add multiple items - * ds.asList().addAll(Arrays.asList(5, 6, 7)); - * </pre> - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class ListDataSource<T> implements DataSource<T> { - - /** - * Wraps the datasource list and notifies the change handler of changing to - * the list - */ - private class ListWrapper implements List<T> { - - @Override - public int size() { - return ds.size(); - } - - @Override - public boolean isEmpty() { - return ds.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return contains(o); - } - - @Override - public Iterator<T> iterator() { - return new ListWrapperIterator(ds.iterator()); - } - - @Override - public Object[] toArray() { - return ds.toArray(); - } - - @Override - public <T> T[] toArray(T[] a) { - return toArray(a); - } - - @Override - public boolean add(T e) { - if (ds.add(e)) { - if (changeHandler != null) { - changeHandler.dataAdded(ds.size() - 1, 1); - } - return true; - } - return false; - } - - @Override - public boolean remove(Object o) { - int index = ds.indexOf(o); - if (ds.remove(o)) { - if (changeHandler != null) { - changeHandler.dataRemoved(index, 1); - } - return true; - } - return false; - } - - @Override - public boolean containsAll(Collection<?> c) { - return ds.containsAll(c); - } - - @Override - public boolean addAll(Collection<? extends T> c) { - int idx = ds.size(); - if (ds.addAll(c)) { - if (changeHandler != null) { - changeHandler.dataAdded(idx, c.size()); - } - return true; - } - return false; - } - - @Override - public boolean addAll(int index, Collection<? extends T> c) { - if (ds.addAll(index, c)) { - if (changeHandler != null) { - changeHandler.dataAdded(index, c.size()); - } - return true; - } - return false; - } - - @Override - public boolean removeAll(Collection<?> c) { - if (ds.removeAll(c)) { - if (changeHandler != null) { - // Have to update the whole list as the removal does not - // have to be a continuous range - changeHandler.dataUpdated(0, ds.size()); - } - return true; - } - return false; - } - - @Override - public boolean retainAll(Collection<?> c) { - if (ds.retainAll(c)) { - if (changeHandler != null) { - // Have to update the whole list as the retain does not - // have to be a continuous range - changeHandler.dataUpdated(0, ds.size()); - } - return true; - } - return false; - } - - @Override - public void clear() { - int size = ds.size(); - ds.clear(); - if (changeHandler != null) { - changeHandler.dataRemoved(0, size); - } - } - - @Override - public T get(int index) { - return ds.get(index); - } - - @Override - public T set(int index, T element) { - T prev = ds.set(index, element); - if (changeHandler != null) { - changeHandler.dataUpdated(index, 1); - } - return prev; - } - - @Override - public void add(int index, T element) { - ds.add(index, element); - if (changeHandler != null) { - changeHandler.dataAdded(index, 1); - } - } - - @Override - public T remove(int index) { - T removed = ds.remove(index); - if (changeHandler != null) { - changeHandler.dataRemoved(index, 1); - } - return removed; - } - - @Override - public int indexOf(Object o) { - return ds.indexOf(o); - } - - @Override - public int lastIndexOf(Object o) { - return ds.lastIndexOf(o); - } - - @Override - public ListIterator<T> listIterator() { - // TODO could be implemented by a custom iterator. - throw new UnsupportedOperationException( - "List iterators not supported at this time."); - } - - @Override - public ListIterator<T> listIterator(int index) { - // TODO could be implemented by a custom iterator. - throw new UnsupportedOperationException( - "List iterators not supported at this time."); - } - - @Override - public List<T> subList(int fromIndex, int toIndex) { - throw new UnsupportedOperationException("Sub lists not supported."); - } - } - - /** - * Iterator returned by {@link ListWrapper} - */ - private class ListWrapperIterator implements Iterator<T> { - - private final Iterator<T> iterator; - - /** - * Constructs a new iterator - */ - public ListWrapperIterator(Iterator<T> iterator) { - this.iterator = iterator; - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public T next() { - return iterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException( - "Iterator.remove() is not supported by this iterator."); - } - } - - /** - * Datasource for providing row pojo's - */ - private final List<T> ds; - - /** - * Wrapper that wraps the data source - */ - private final ListWrapper wrapper; - - /** - * Handler for listening to changes in the underlying list. - */ - private DataChangeHandler changeHandler; - - /** - * Constructs a new list data source. - * <p> - * Note: Modifications to the original list will not be reflected in the - * data source after the data source has been constructed. To add or remove - * items to the data source after it has been constructed use - * {@link ListDataSource#asList()}. - * - * - * @param datasource - * The list to use for providing the data to the grid - */ - public ListDataSource(List<T> datasource) { - if (datasource == null) { - throw new IllegalArgumentException("datasource cannot be null"); - } - ds = new ArrayList<T>(datasource); - wrapper = new ListWrapper(); - } - - /** - * Constructs a data source with a set of rows. You can dynamically add and - * remove rows from the data source via the list you get from - * {@link ListDataSource#asList()} - * - * @param rows - * The rows to initially add to the data source - */ - public ListDataSource(T... rows) { - if (rows == null) { - ds = new ArrayList<T>(); - } else { - ds = new ArrayList<T>(Arrays.asList(rows)); - } - wrapper = new ListWrapper(); - } - - @Override - public void ensureAvailability(int firstRowIndex, int numberOfRows) { - if (firstRowIndex >= ds.size()) { - throw new IllegalStateException( - "Trying to fetch rows outside of array"); - } - } - - @Override - public T getRow(int rowIndex) { - return ds.get(rowIndex); - } - - @Override - public int getEstimatedSize() { - return ds.size(); - } - - @Override - public void setDataChangeHandler(DataChangeHandler dataChangeHandler) { - this.changeHandler = dataChangeHandler; - } - - /** - * Gets the list that backs this datasource. Any changes made to this list - * will be reflected in the datasource. - * <p> - * Note: The list is not the same list as passed into the data source via - * the constructor. - * - * @return Returns a list implementation that wraps the real list that backs - * the data source and provides events for the data source - * listeners. - */ - public List<T> asList() { - return wrapper; - } -} diff --git a/client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java deleted file mode 100644 index d1f770f414..0000000000 --- a/client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid.renderers; - -import java.util.Date; - -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.i18n.client.TimeZone; -import com.vaadin.client.ui.grid.Cell; -import com.vaadin.client.ui.grid.Renderer; - -/** - * A renderer for rendering dates into cells - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class DateRenderer implements Renderer<Date> { - - private DateTimeFormat format = DateTimeFormat.getShortDateTimeFormat(); - - private TimeZone timeZone = TimeZone.createTimeZone(new Date() - .getTimezoneOffset()); - - @Override - public void renderCell(Cell cell, Date date) { - String dateStr = format.format(date, timeZone); - cell.getElement().setInnerText(dateStr); - } - - /** - * Gets the format of how the date is formatted. - * - * @return the format - * @see <a - * href="http://www.gwtproject.org/javadoc/latest/com/google/gwt/i18n/client/DateTimeFormat.html">GWT - * documentation on DateTimeFormat</a> - */ - public DateTimeFormat getFormat() { - return format; - } - - /** - * Sets the format used for formatting the dates. - * - * @param format - * the format to set - * @see <a - * href="http://www.gwtproject.org/javadoc/latest/com/google/gwt/i18n/client/DateTimeFormat.html">GWT - * documentation on DateTimeFormat</a> - */ - public void setFormat(DateTimeFormat format) { - if (format == null) { - throw new IllegalArgumentException("Format should not be null"); - } - this.format = format; - } - - /** - * Returns the time zone of the date. - * - * @return the time zone - */ - public TimeZone getTimeZone() { - return timeZone; - } - - /** - * Sets the time zone of the the date. By default uses the time zone of the - * browser. - * - * @param timeZone - * the timeZone to set - */ - public void setTimeZone(TimeZone timeZone) { - if (timeZone == null) { - throw new IllegalArgumentException("Timezone should not be null"); - } - this.timeZone = timeZone; - } -} diff --git a/client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java deleted file mode 100644 index 0787dc2332..0000000000 --- a/client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid.renderers; - -import com.google.gwt.safehtml.shared.SafeHtml; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.vaadin.client.ui.grid.Cell; -import com.vaadin.client.ui.grid.Renderer; - -/** - * Renders a string as HTML into a cell. - * <p> - * The html string is rendered as is without any escaping. It is up to the - * developer to ensure that the html string honors the {@link SafeHtml} - * contract. For more information see - * {@link SafeHtmlUtils#fromSafeConstant(String)}. - * - * @since 7.2 - * @author Vaadin Ltd - * @see SafeHtmlUtils#fromSafeConstant(String) - */ -public class HtmlRenderer implements Renderer<String> { - - @Override - public void renderCell(Cell cell, String htmlString) { - cell.getElement().setInnerSafeHtml( - SafeHtmlUtils.fromSafeConstant(htmlString)); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java deleted file mode 100644 index f4efea33a5..0000000000 --- a/client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid.renderers; - -import com.google.gwt.i18n.client.NumberFormat; -import com.vaadin.client.ui.grid.Cell; -import com.vaadin.client.ui.grid.Renderer; - -/** - * Renders a number into a cell using a specific {@link NumberFormat}. By - * default uses the default number format returned by - * {@link NumberFormat#getDecimalFormat()}. - * - * @since 7.2 - * @author Vaadin Ltd - * @param <T> - * The number type to render. - */ -public class NumberRenderer<T extends Number> implements Renderer<T> { - - private NumberFormat format = NumberFormat.getDecimalFormat(); - - /** - * Gets the number format that the number should be formatted in. - * - * @return the number format used to render the number - */ - public NumberFormat getFormat() { - return format; - } - - /** - * Sets the number format to use for formatting the number. - * - * @param format - * the format to use - * @throws IllegalArgumentException - * when the format is null - */ - public void setFormat(NumberFormat format) throws IllegalArgumentException { - if (format == null) { - throw new IllegalArgumentException("Format cannot be null"); - } - this.format = format; - } - - @Override - public void renderCell(Cell cell, Number number) { - cell.getElement().setInnerText(format.format(number)); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java deleted file mode 100644 index 1f06a555c3..0000000000 --- a/client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid.renderers; - -import com.vaadin.client.ui.grid.Cell; -import com.vaadin.client.ui.grid.Renderer; - -/** - * Renderer that renders text into a cell. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class TextRenderer implements Renderer<String> { - - @Override - public void renderCell(Cell cell, String text) { - cell.getElement().setInnerText(text); - } -} diff --git a/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java b/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java deleted file mode 100644 index 5c5e88bf69..0000000000 --- a/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; - -import org.easymock.EasyMock; -import org.junit.Test; - -import com.vaadin.client.data.DataChangeHandler; -import com.vaadin.client.ui.grid.datasources.ListDataSource; - -/** - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class ListDataSourceTest { - - @Test - public void testDataSourceConstruction() throws Exception { - - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - - assertEquals(4, ds.getEstimatedSize()); - assertEquals(0, (int) ds.getRow(0)); - assertEquals(1, (int) ds.getRow(1)); - assertEquals(2, (int) ds.getRow(2)); - assertEquals(3, (int) ds.getRow(3)); - - ds = new ListDataSource<Integer>(Arrays.asList(0, 1, 2, 3)); - - assertEquals(4, ds.getEstimatedSize()); - assertEquals(0, (int) ds.getRow(0)); - assertEquals(1, (int) ds.getRow(1)); - assertEquals(2, (int) ds.getRow(2)); - assertEquals(3, (int) ds.getRow(3)); - } - - @Test - public void testListAddOperation() throws Exception { - - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - - DataChangeHandler handler = EasyMock - .createNiceMock(DataChangeHandler.class); - ds.setDataChangeHandler(handler); - - handler.dataAdded(4, 1); - EasyMock.expectLastCall(); - - EasyMock.replay(handler); - - ds.asList().add(4); - - assertEquals(5, ds.getEstimatedSize()); - assertEquals(0, (int) ds.getRow(0)); - assertEquals(1, (int) ds.getRow(1)); - assertEquals(2, (int) ds.getRow(2)); - assertEquals(3, (int) ds.getRow(3)); - assertEquals(4, (int) ds.getRow(4)); - } - - @Test - public void testListAddAllOperation() throws Exception { - - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - - DataChangeHandler handler = EasyMock - .createNiceMock(DataChangeHandler.class); - ds.setDataChangeHandler(handler); - - handler.dataAdded(4, 3); - EasyMock.expectLastCall(); - - EasyMock.replay(handler); - - ds.asList().addAll(Arrays.asList(4, 5, 6)); - - assertEquals(7, ds.getEstimatedSize()); - assertEquals(0, (int) ds.getRow(0)); - assertEquals(1, (int) ds.getRow(1)); - assertEquals(2, (int) ds.getRow(2)); - assertEquals(3, (int) ds.getRow(3)); - assertEquals(4, (int) ds.getRow(4)); - assertEquals(5, (int) ds.getRow(5)); - assertEquals(6, (int) ds.getRow(6)); - } - - @Test - public void testListRemoveOperation() throws Exception { - - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - - DataChangeHandler handler = EasyMock - .createNiceMock(DataChangeHandler.class); - ds.setDataChangeHandler(handler); - - handler.dataRemoved(3, 1); - EasyMock.expectLastCall(); - - EasyMock.replay(handler); - - ds.asList().remove(2); - - assertEquals(3, ds.getEstimatedSize()); - assertEquals(0, (int) ds.getRow(0)); - assertEquals(1, (int) ds.getRow(1)); - assertEquals(3, (int) ds.getRow(2)); - } - - @Test - public void testListRemoveAllOperation() throws Exception { - - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - - DataChangeHandler handler = EasyMock - .createNiceMock(DataChangeHandler.class); - ds.setDataChangeHandler(handler); - - handler.dataRemoved(0, 3); - EasyMock.expectLastCall(); - - EasyMock.replay(handler); - - ds.asList().removeAll(Arrays.asList(0, 2, 3)); - - assertEquals(1, ds.getEstimatedSize()); - assertEquals(1, (int) ds.getRow(0)); - } - - @Test - public void testListClearOperation() throws Exception { - - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - - DataChangeHandler handler = EasyMock - .createNiceMock(DataChangeHandler.class); - ds.setDataChangeHandler(handler); - - handler.dataRemoved(0, 4); - EasyMock.expectLastCall(); - - EasyMock.replay(handler); - - ds.asList().clear(); - - assertEquals(0, ds.getEstimatedSize()); - } - - @Test(expected = IllegalStateException.class) - public void testFetchingNonExistantItem() { - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - ds.ensureAvailability(5, 1); - } - - @Test(expected = UnsupportedOperationException.class) - public void testUnsupportedIteratorRemove() { - ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); - ds.asList().iterator().remove(); - } - -} diff --git a/client/tests/src/com/vaadin/client/ui/grid/PartitioningTest.java b/client/tests/src/com/vaadin/client/ui/grid/PartitioningTest.java deleted file mode 100644 index e97bb339e4..0000000000 --- a/client/tests/src/com/vaadin/client/ui/grid/PartitioningTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.ui.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.shared.ui.grid.Range; - -@SuppressWarnings("static-method") -public class PartitioningTest { - - @Test - public void selfRangeTest() { - final Range range = Range.between(0, 10); - final Range[] partitioning = range.partitionWith(range); - - assertTrue("before is empty", partitioning[0].isEmpty()); - assertTrue("inside is self", partitioning[1].equals(range)); - assertTrue("after is empty", partitioning[2].isEmpty()); - } - - @Test - public void beforeRangeTest() { - final Range beforeRange = Range.between(0, 10); - final Range afterRange = Range.between(10, 20); - final Range[] partitioning = beforeRange.partitionWith(afterRange); - - assertTrue("before is self", partitioning[0].equals(beforeRange)); - assertTrue("inside is empty", partitioning[1].isEmpty()); - assertTrue("after is empty", partitioning[2].isEmpty()); - } - - @Test - public void afterRangeTest() { - final Range beforeRange = Range.between(0, 10); - final Range afterRange = Range.between(10, 20); - final Range[] partitioning = afterRange.partitionWith(beforeRange); - - assertTrue("before is empty", partitioning[0].isEmpty()); - assertTrue("inside is empty", partitioning[1].isEmpty()); - assertTrue("after is self", partitioning[2].equals(afterRange)); - } - - @Test - public void beforeAndInsideRangeTest() { - final Range beforeRange = Range.between(0, 10); - final Range afterRange = Range.between(5, 15); - final Range[] partitioning = beforeRange.partitionWith(afterRange); - - assertEquals("before", Range.between(0, 5), partitioning[0]); - assertEquals("inside", Range.between(5, 10), partitioning[1]); - assertTrue("after is empty", partitioning[2].isEmpty()); - } - - @Test - public void insideRangeTest() { - final Range fullRange = Range.between(0, 20); - final Range insideRange = Range.between(5, 15); - final Range[] partitioning = insideRange.partitionWith(fullRange); - - assertTrue("before is empty", partitioning[0].isEmpty()); - assertEquals("inside", Range.between(5, 15), partitioning[1]); - assertTrue("after is empty", partitioning[2].isEmpty()); - } - - @Test - public void insideAndBelowTest() { - final Range beforeRange = Range.between(0, 10); - final Range afterRange = Range.between(5, 15); - final Range[] partitioning = afterRange.partitionWith(beforeRange); - - assertTrue("before is empty", partitioning[0].isEmpty()); - assertEquals("inside", Range.between(5, 10), partitioning[1]); - assertEquals("after", Range.between(10, 15), partitioning[2]); - } - - @Test - public void aboveAndBelowTest() { - final Range fullRange = Range.between(0, 20); - final Range insideRange = Range.between(5, 15); - final Range[] partitioning = fullRange.partitionWith(insideRange); - - assertEquals("before", Range.between(0, 5), partitioning[0]); - assertEquals("inside", Range.between(5, 15), partitioning[1]); - assertEquals("after", Range.between(15, 20), partitioning[2]); - } -} diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java deleted file mode 100644 index b22e6a209b..0000000000 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.data; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import com.vaadin.data.Container.Indexed; -import com.vaadin.server.AbstractExtension; -import com.vaadin.shared.data.DataProviderRpc; -import com.vaadin.shared.data.DataProviderState; -import com.vaadin.shared.data.DataRequestRpc; -import com.vaadin.ui.components.grid.Grid; - -/** - * Provides Vaadin server-side container data source to a - * {@link com.vaadin.client.ui.grid.GridConnector}. This is currently - * implemented as an Extension hardcoded to support a specific connector type. - * This will be changed once framework support for something more flexible has - * been implemented. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class RpcDataProviderExtension extends AbstractExtension { - - private final Indexed container; - - /** - * Creates a new data provider using the given container. - * - * @param container - * the container to make available - */ - public RpcDataProviderExtension(Indexed container) { - this.container = container; - - // TODO support for reacting to events from the container added later - - registerRpc(new DataRequestRpc() { - @Override - public void requestRows(int firstRow, int numberOfRows) { - pushRows(firstRow, numberOfRows); - } - }); - - getState().containerSize = container.size(); - } - - private void pushRows(int firstRow, int numberOfRows) { - List<?> itemIds = container.getItemIds(firstRow, numberOfRows); - Collection<?> propertyIds = container.getContainerPropertyIds(); - List<String[]> rows = new ArrayList<String[]>(itemIds.size()); - for (Object itemId : itemIds) { - rows.add(getRowData(propertyIds, itemId)); - } - getRpcProxy(DataProviderRpc.class).setRowData(firstRow, rows); - } - - private String[] getRowData(Collection<?> propertyIds, Object itemId) { - Item item = container.getItem(itemId); - String[] row = new String[propertyIds.size()]; - - int i = 0; - for (Object propertyId : propertyIds) { - Object value = item.getItemProperty(propertyId).getValue(); - String stringValue = String.valueOf(value); - row[i++] = stringValue; - } - return row; - } - - @Override - protected DataProviderState getState() { - return (DataProviderState) super.getState(); - } - - /** - * Makes the data source available to the given {@link Grid} component. - * - * @param component - * the remote data grid component to extend - */ - public void extend(Grid component) { - super.extend(component); - } - - /** - * Informs the client side that new rows have been inserted into the data - * source. - * - * @param index - * the index at which new rows have been inserted - * @param count - * the number of rows inserted at <code>index</code> - */ - public void insertRowData(int index, int count) { - getState().containerSize += count; - getRpcProxy(DataProviderRpc.class).insertRowData(index, count); - } - - /** - * Informs the client side that rows have been removed from the data source. - * - * @param firstIndex - * the index of the first row removed - * @param count - * the number of rows removed - */ - public void removeRowData(int firstIndex, int count) { - getState().containerSize -= count; - getRpcProxy(DataProviderRpc.class).removeRowData(firstIndex, count); - } - - /** - * Informs the client side that data of a row has been modified in the data - * source. - * - * @param index - * the index of the row that was updated - */ - public void updateRowData(int index) { - /* - * TODO: ignore duplicate requests for the same index during the same - * roundtrip. - */ - Object itemId = container.getIdByIndex(index); - String[] row = getRowData(container.getContainerPropertyIds(), itemId); - getRpcProxy(DataProviderRpc.class).setRowData(index, - Collections.singletonList(row)); - } -} diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroup.java b/server/src/com/vaadin/ui/components/grid/ColumnGroup.java deleted file mode 100644 index 6b14ef81d4..0000000000 --- a/server/src/com/vaadin/ui/components/grid/ColumnGroup.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.ui.components.grid; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.vaadin.shared.ui.grid.ColumnGroupState; - -/** - * Column groups are used to group columns together for adding common auxiliary - * headers and footers. Columns groups are added to {@link ColumnGroupRow}'s. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class ColumnGroup implements Serializable { - - /** - * List of property ids belonging to this group - */ - private List<Object> columns; - - /** - * The grid the column group is associated with - */ - private final Grid grid; - - /** - * The column group row the column group is attached to - */ - private final ColumnGroupRow row; - - /** - * The common state between the server and the client - */ - private final ColumnGroupState state; - - /** - * Constructs a new column group - * - * @param grid - * the grid the column group is associated with - * @param state - * the state representing the data of the grid. Sent to the - * client - * @param propertyIds - * the property ids of the columns that belongs to the group - * @param groups - * the sub groups who should be included in this group - * - */ - ColumnGroup(Grid grid, ColumnGroupRow row, ColumnGroupState state, - List<Object> propertyIds) { - if (propertyIds == null) { - throw new IllegalArgumentException( - "propertyIds cannot be null. Use empty list instead."); - } - - this.state = state; - this.row = row; - columns = Collections.unmodifiableList(new ArrayList<Object>( - propertyIds)); - this.grid = grid; - } - - /** - * Sets the text displayed in the header of the column group. - * - * @param header - * the text displayed in the header of the column - */ - public void setHeaderCaption(String header) { - checkGroupIsAttached(); - state.header = header; - grid.markAsDirty(); - } - - /** - * Sets the text displayed in the header of the column group. - * - * @return the text displayed in the header of the column - */ - public String getHeaderCaption() { - checkGroupIsAttached(); - return state.header; - } - - /** - * Sets the text displayed in the footer of the column group. - * - * @param footer - * the text displayed in the footer of the column - */ - public void setFooterCaption(String footer) { - checkGroupIsAttached(); - state.footer = footer; - grid.markAsDirty(); - } - - /** - * The text displayed in the footer of the column group. - * - * @return the text displayed in the footer of the column - */ - public String getFooterCaption() { - checkGroupIsAttached(); - return state.footer; - } - - /** - * Is a property id in this group or in some sub group of this group. - * - * @param propertyId - * the property id to check for - * @return <code>true</code> if the property id is included in this group. - */ - public boolean isColumnInGroup(Object propertyId) { - if (columns.contains(propertyId)) { - return true; - } - return false; - } - - /** - * Returns a list of property ids where all also the child groups property - * ids are included. - * - * @return a unmodifiable list with all the columns in the group. Includes - * any subgroup columns as well. - */ - public List<Object> getColumns() { - return columns; - } - - /** - * Checks if column group is attached to a row and throws an - * {@link IllegalStateException} if it is not. - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - protected void checkGroupIsAttached() throws IllegalStateException { - if (!row.getState().groups.contains(state)) { - throw new IllegalStateException( - "Column Group has been removed from the row."); - } - } -} diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java b/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java deleted file mode 100644 index b90b4df2c5..0000000000 --- a/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.ui.components.grid; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.ColumnGroupRowState; -import com.vaadin.shared.ui.grid.ColumnGroupState; - -/** - * A column group row represents an auxiliary header or footer row added to the - * grid. A column group row includes column groups that group columns together. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class ColumnGroupRow implements Serializable { - - /** - * The common state shared between the client and server - */ - private final ColumnGroupRowState state; - - /** - * The column groups in this row - */ - private List<ColumnGroup> groups = new ArrayList<ColumnGroup>(); - - /** - * Grid that the group row belongs to - */ - private final Grid grid; - - /** - * The column keys used to identify the column on the client side - */ - private final KeyMapper<Object> columnKeys; - - /** - * Constructs a new column group - * - * @param grid - * The grid that the column group is associated to - * @param state - * The shared state which contains the data shared between server - * and client - * @param columnKeys - * The column key mapper for converting property ids to client - * side column identifiers - */ - ColumnGroupRow(Grid grid, ColumnGroupRowState state, - KeyMapper<Object> columnKeys) { - this.grid = grid; - this.columnKeys = columnKeys; - this.state = state; - } - - /** - * Gets the shared state for the column group row. Used internally to send - * the group row to the client. - * - * @return The current state of the row - */ - ColumnGroupRowState getState() { - return state; - } - - /** - * Add a new group to the row by using property ids for the columns. - * - * @param propertyIds - * The property ids of the columns that should be included in the - * group. A column can only belong in group on a row at a time. - * @return a column group representing the collection of columns added to - * the group - */ - public ColumnGroup addGroup(Object... propertyIds) - throws IllegalArgumentException { - assert propertyIds != null : "propertyIds cannot be null."; - - for (Object propertyId : propertyIds) { - if (hasColumnBeenGrouped(propertyId)) { - throw new IllegalArgumentException("Column " - + String.valueOf(propertyId) - + " already belongs to another group."); - } - } - - validateNewGroupProperties(Arrays.asList(propertyIds)); - - ColumnGroupState state = new ColumnGroupState(); - for (Object propertyId : propertyIds) { - assert propertyId != null : "null items in columns array not supported."; - state.columns.add(columnKeys.key(propertyId)); - } - this.state.groups.add(state); - - ColumnGroup group = new ColumnGroup(grid, this, state, - Arrays.asList(propertyIds)); - groups.add(group); - - grid.markAsDirty(); - return group; - } - - private void validateNewGroupProperties(List<Object> propertyIds) - throws IllegalArgumentException { - - /* - * Validate parent grouping - */ - int rowIndex = grid.getColumnGroupRows().indexOf(this); - int parentRowIndex = rowIndex - 1; - - // Get the parent row of this row. - ColumnGroupRow parentRow = null; - if (parentRowIndex > -1) { - parentRow = grid.getColumnGroupRows().get(parentRowIndex); - } - - if (parentRow == null) { - // A parentless row is always valid and is usually the first row - // added to the grid - return; - } - - for (Object id : propertyIds) { - if (parentRow.hasColumnBeenGrouped(id)) { - /* - * If a property has been grouped in the parent row then all of - * the properties in the parent group also needs to be included - * in the child group for the groups to be valid - */ - ColumnGroup parentGroup = parentRow.getGroupForProperty(id); - if (!propertyIds.containsAll(parentGroup.getColumns())) { - throw new IllegalArgumentException( - "Grouped properties overlaps previous grouping bounderies"); - } - } - } - } - - /** - * Add a new group to the row by using column instances. - * - * @param columns - * the columns that should belong to the group - * @return a column group representing the collection of columns added to - * the group - */ - public ColumnGroup addGroup(GridColumn... columns) - throws IllegalArgumentException { - assert columns != null : "columns cannot be null"; - - List<Object> propertyIds = new ArrayList<Object>(); - for (GridColumn column : columns) { - assert column != null : "null items in columns array not supported."; - - String columnId = column.getState().id; - Object propertyId = grid.getPropertyIdByColumnId(columnId); - propertyIds.add(propertyId); - } - return addGroup(propertyIds.toArray()); - } - - /** - * Add a new group to the row by using other already greated groups - * - * @param groups - * the subgroups of the group - * @return a column group representing the collection of columns added to - * the group - * - */ - public ColumnGroup addGroup(ColumnGroup... groups) - throws IllegalArgumentException { - assert groups != null : "groups cannot be null"; - - // Gather all groups columns into one list - List<Object> propertyIds = new ArrayList<Object>(); - for (ColumnGroup group : groups) { - propertyIds.addAll(group.getColumns()); - } - - validateNewGroupProperties(propertyIds); - - ColumnGroupState state = new ColumnGroupState(); - ColumnGroup group = new ColumnGroup(grid, this, state, propertyIds); - this.groups.add(group); - - // Update state - for (Object propertyId : group.getColumns()) { - state.columns.add(columnKeys.key(propertyId)); - } - this.state.groups.add(state); - - grid.markAsDirty(); - return group; - } - - /** - * Removes a group from the row. Does not remove the group from subgroups, - * to remove it from the subgroup invoke removeGroup on the subgroup. - * - * @param group - * the group to remove - */ - public void removeGroup(ColumnGroup group) { - int index = groups.indexOf(group); - groups.remove(index); - state.groups.remove(index); - grid.markAsDirty(); - } - - /** - * Get the groups in the row. - * - * @return unmodifiable list of groups in this row - */ - public List<ColumnGroup> getGroups() { - return Collections.unmodifiableList(groups); - } - - /** - * Checks if a property id has been added to a group in this row. - * - * @param propertyId - * the property id to check for - * @return <code>true</code> if the column is included in a group - */ - private boolean hasColumnBeenGrouped(Object propertyId) { - return getGroupForProperty(propertyId) != null; - } - - private ColumnGroup getGroupForProperty(Object propertyId) { - for (ColumnGroup group : groups) { - if (group.isColumnInGroup(propertyId)) { - return group; - } - } - return null; - } - - /** - * Is the header visible for the row. - * - * @return <code>true</code> if header is visible - */ - public boolean isHeaderVisible() { - return state.headerVisible; - } - - /** - * Sets the header visible for the row. - * - * @param visible - * should the header be shown - */ - public void setHeaderVisible(boolean visible) { - state.headerVisible = visible; - grid.markAsDirty(); - } - - /** - * Is the footer visible for the row. - * - * @return <code>true</code> if footer is visible - */ - public boolean isFooterVisible() { - return state.footerVisible; - } - - /** - * Sets the footer visible for the row. - * - * @param visible - * should the footer be shown - */ - public void setFooterVisible(boolean visible) { - state.footerVisible = visible; - grid.markAsDirty(); - } - -} diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java deleted file mode 100644 index 4126ec6d93..0000000000 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ /dev/null @@ -1,861 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.ui.components.grid; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Container.ItemSetChangeNotifier; -import com.vaadin.data.Container.PropertySetChangeEvent; -import com.vaadin.data.Container.PropertySetChangeListener; -import com.vaadin.data.Container.PropertySetChangeNotifier; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.data.Property.ValueChangeNotifier; -import com.vaadin.data.RpcDataProviderExtension; -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.ColumnGroupRowState; -import com.vaadin.shared.ui.grid.GridClientRpc; -import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridServerRpc; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.Range; -import com.vaadin.shared.ui.grid.ScrollDestination; -import com.vaadin.ui.AbstractComponent; -import com.vaadin.ui.Component; - -/** - * Data grid component - * - * <h3>Lazy loading</h3> TODO To be revised when the data data source - * implementation has been don. - * - * <h3>Columns</h3> The grid columns are based on the property ids of the - * underlying data source. Each property id represents one column in the grid. - * To retrive a column in the grid you can use {@link Grid#getColumn(Object)} - * with the property id of the column. A grid column contains properties like - * the width, the footer and header captions of the column. - * - * <h3>Auxiliary headers and footers</h3> TODO To be revised when column - * grouping is implemented. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class Grid extends AbstractComponent { - - /** - * A helper class that handles the client-side Escalator logic relating to - * making sure that whatever is currently visible to the user, is properly - * initialized and otherwise handled on the server side (as far as - * requried). - * <p> - * This bookeeping includes, but is not limited to: - * <ul> - * <li>listening to the currently visible {@link Property Properties'} value - * changes on the server side and sending those back to the client; and - * <li>attaching and detaching {@link Component Components} from the Vaadin - * Component hierarchy. - * </ul> - */ - private final class ActiveRowHandler { - /** - * A map from itemId to the value change listener used for all of its - * properties - */ - private final Map<Object, GridValueChangeListener> valueChangeListeners = new HashMap<Object, GridValueChangeListener>(); - - /** - * The currently active range. Practically, it's the range of row - * indices being displayed currently. - */ - private Range activeRange = Range.withLength(0, 0); - - /** - * A hook for making sure that appropriate data is "active". All other - * rows should be "inactive". - * <p> - * "Active" can mean different things in different contexts. For - * example, only the Properties in the active range need - * ValueChangeListeners. Also, whenever a row with a Component becomes - * active, it needs to be attached (and conversely, when inactive, it - * needs to be detached). - * - * @param firstActiveRow - * the first active row - * @param activeRowCount - * the number of active rows - */ - public void setActiveRows(int firstActiveRow, int activeRowCount) { - - final Range newActiveRange = Range.withLength(firstActiveRow, - activeRowCount); - - // TODO [[Components]] attach and detach components - - /*- - * Example - * - * New Range: [3, 4, 5, 6, 7] - * Old Range: [1, 2, 3, 4, 5] - * Result: [1, 2][3, 4, 5] [] - */ - final Range[] depractionPartition = activeRange - .partitionWith(newActiveRange); - removeValueChangeListeners(depractionPartition[0]); - removeValueChangeListeners(depractionPartition[2]); - - /*- - * Example - * - * Old Range: [1, 2, 3, 4, 5] - * New Range: [3, 4, 5, 6, 7] - * Result: [] [3, 4, 5][6, 7] - */ - final Range[] activationPartition = newActiveRange - .partitionWith(activeRange); - addValueChangeListeners(activationPartition[0]); - addValueChangeListeners(activationPartition[2]); - - activeRange = newActiveRange; - } - - private void addValueChangeListeners(Range range) { - for (int i = range.getStart(); i < range.getEnd(); i++) { - - final Object itemId = datasource.getIdByIndex(i); - final Item item = datasource.getItem(itemId); - - if (valueChangeListeners.containsKey(itemId)) { - /* - * This might occur when items are removed from above the - * viewport, the escalator scrolls up to compensate, but the - * same items remain in the view: It looks as if one row was - * scrolled, when in fact the whole viewport was shifted up. - */ - continue; - } - - GridValueChangeListener listener = new GridValueChangeListener( - itemId); - valueChangeListeners.put(itemId, listener); - - for (final Object propertyId : item.getItemPropertyIds()) { - final Property<?> property = item - .getItemProperty(propertyId); - if (property instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) property) - .addValueChangeListener(listener); - } - } - } - } - - private void removeValueChangeListeners(Range range) { - for (int i = range.getStart(); i < range.getEnd(); i++) { - final Object itemId = datasource.getIdByIndex(i); - final Item item = datasource.getItem(itemId); - final GridValueChangeListener listener = valueChangeListeners - .remove(itemId); - - if (listener != null) { - for (final Object propertyId : item.getItemPropertyIds()) { - final Property<?> property = item - .getItemProperty(propertyId); - - /* - * Because listener != null, we can be certain that this - * property is a ValueChangeNotifier: It wouldn't be - * inserted in addValueChangeListeners if the property - * wasn't a suitable type. I.e. No need for "instanceof" - * check. - */ - ((ValueChangeNotifier) property) - .removeValueChangeListener(listener); - } - } - } - } - - public void clear() { - removeValueChangeListeners(activeRange); - /* - * we're doing an assert for emptiness there (instead of a - * carte-blanche ".clear()"), to be absolutely sure that everything - * is cleaned up properly, and that we have no dangling listeners. - */ - assert valueChangeListeners.isEmpty() : "GridValueChangeListeners are leaking"; - - activeRange = Range.withLength(0, 0); - } - - /** - * Manages removed properties in active rows. - * - * @param removedPropertyIds - * the property ids that have been removed from the container - */ - public void propertiesRemoved(Collection<Object> removedPropertyIds) { - /* - * no-op, for now. - * - * The Container should be responsible for cleaning out any - * ValueChangeListeners from removed Properties. Components will - * benefit from this, however. - */ - } - - /** - * Manages added properties in active rows. - * - * @param addedPropertyIds - * the property ids that have been added to the container - */ - public void propertiesAdded(Collection<Object> addedPropertyIds) { - for (int i = activeRange.getStart(); i < activeRange.getEnd(); i++) { - final Object itemId = datasource.getIdByIndex(i); - final Item item = datasource.getItem(itemId); - final GridValueChangeListener listener = valueChangeListeners - .get(itemId); - assert (listener != null) : "a listener should've been pre-made by addValueChangeListeners"; - - for (final Object propertyId : addedPropertyIds) { - final Property<?> property = item - .getItemProperty(propertyId); - if (property instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) property) - .addValueChangeListener(listener); - } - } - } - } - - /** - * Handles the insertion of rows. - * <p> - * This method's responsibilities are to: - * <ul> - * <li>shift the internal bookkeeping by <code>count</code> if the - * insertion happens above currently active range - * <li>ignore rows inserted below the currently active range - * <li>shift (and deactivate) rows pushed out of view - * <li>activate rows that are inserted in the current viewport - * </ul> - * - * @param firstIndex - * the index of the first inserted rows - * @param count - * the number of rows inserted at <code>firstIndex</code> - */ - public void insertRows(int firstIndex, int count) { - if (firstIndex < activeRange.getStart()) { - activeRange = activeRange.offsetBy(count); - } else if (firstIndex < activeRange.getEnd()) { - final Range deprecatedRange = Range.withLength( - activeRange.getEnd(), count); - removeValueChangeListeners(deprecatedRange); - - final Range freshRange = Range.between(firstIndex, count); - addValueChangeListeners(freshRange); - } else { - // out of view, noop - } - } - - /** - * Removes a single item by its id. - * - * @param itemId - * the id of the removed id. <em>Note:</em> this item does - * not exist anymore in the datasource - */ - public void removeItemId(Object itemId) { - final GridValueChangeListener removedListener = valueChangeListeners - .remove(itemId); - if (removedListener != null) { - /* - * We removed an item from somewhere in the visible range, so we - * make the active range shorter. The empty hole will be filled - * by the client-side code when it asks for more information. - */ - activeRange = Range.withLength(activeRange.getStart(), - activeRange.length() - 1); - } - } - } - - /** - * A class to listen to changes in property values in the Container added - * with {@link Grid#setContainerDatasource(Container.Indexed)}, and notifies - * the data source to update the client-side representation of the modified - * item. - * <p> - * One instance of this class can (and should) be reused for all the - * properties in an item, since this class will inform that the entire row - * needs to be re-evaluated (in contrast to a property-based change - * management) - * <p> - * Since there's no Container-wide possibility to listen to any kind of - * value changes, an instance of this class needs to be attached to each and - * every Item's Property in the container. - * - * @see Grid#addValueChangeListener(Container, Object, Object) - * @see Grid#valueChangeListeners - */ - private class GridValueChangeListener implements ValueChangeListener { - private final Object itemId; - - public GridValueChangeListener(Object itemId) { - /* - * Using an assert instead of an exception throw, just to optimize - * prematurely - */ - assert itemId != null : "null itemId not accepted"; - this.itemId = itemId; - } - - @Override - public void valueChange(ValueChangeEvent event) { - datasourceExtension.updateRowData(datasource.indexOfId(itemId)); - } - } - - /** - * The data source attached to the grid - */ - private Container.Indexed datasource; - - /** - * Property id to column instance mapping - */ - private final Map<Object, GridColumn> columns = new HashMap<Object, GridColumn>(); - - /** - * Key generator for column server-to-client communication - */ - private final KeyMapper<Object> columnKeys = new KeyMapper<Object>(); - - /** - * The column groups added to the grid - */ - private final List<ColumnGroupRow> columnGroupRows = new ArrayList<ColumnGroupRow>(); - - /** - * Property listener for listening to changes in data source properties. - */ - private final PropertySetChangeListener propertyListener = new PropertySetChangeListener() { - - @Override - public void containerPropertySetChange(PropertySetChangeEvent event) { - Collection<?> properties = new HashSet<Object>(event.getContainer() - .getContainerPropertyIds()); - - // Cleanup columns that are no longer in grid - List<Object> removedColumns = new LinkedList<Object>(); - for (Object columnId : columns.keySet()) { - if (!properties.contains(columnId)) { - removedColumns.add(columnId); - } - } - for (Object columnId : removedColumns) { - GridColumn column = columns.remove(columnId); - columnKeys.remove(columnId); - getState().columns.remove(column.getState()); - } - activeRowHandler.propertiesRemoved(removedColumns); - - // Add new columns - HashSet<Object> addedPropertyIds = new HashSet<Object>(); - for (Object propertyId : properties) { - if (!columns.containsKey(propertyId)) { - appendColumn(propertyId); - addedPropertyIds.add(propertyId); - } - } - activeRowHandler.propertiesAdded(addedPropertyIds); - - Object frozenPropertyId = columnKeys - .get(getState(false).lastFrozenColumnId); - if (!columns.containsKey(frozenPropertyId)) { - setLastFrozenPropertyId(null); - } - } - }; - - private ItemSetChangeListener itemListener = new ItemSetChangeListener() { - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - - if (event instanceof ItemAddEvent) { - ItemAddEvent addEvent = (ItemAddEvent) event; - int firstIndex = addEvent.getFirstIndex(); - int count = addEvent.getAddedItemsCount(); - datasourceExtension.insertRowData(firstIndex, count); - activeRowHandler.insertRows(firstIndex, count); - } - - else if (event instanceof ItemRemoveEvent) { - ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; - int firstIndex = removeEvent.getFirstIndex(); - int count = removeEvent.getRemovedItemsCount(); - datasourceExtension.removeRowData(firstIndex, count); - - /* - * Unfortunately, there's no sane way of getting the rest of the - * removed itemIds. - * - * Fortunately, the only time _currently_ an event with more - * than one removed item seems to be when calling - * AbstractInMemoryContainer.removeAllElements(). Otherwise, - * it's only removing one item at a time. - * - * We _could_ have a backup of all the itemIds, and compare to - * that one, but we really really don't want to go there. - */ - activeRowHandler.removeItemId(removeEvent.getFirstItemId()); - } - - else { - // TODO no diff info available, redraw everything - throw new UnsupportedOperationException("bare " - + "ItemSetChangeEvents are currently " - + "not supported, use a container that " - + "uses AddItemEvents and RemoveItemEvents."); - } - } - }; - - private RpcDataProviderExtension datasourceExtension; - - private final ActiveRowHandler activeRowHandler = new ActiveRowHandler(); - - /** - * Creates a new Grid using the given datasource. - * - * @param datasource - * the data source for the grid - */ - public Grid(Container.Indexed datasource) { - setContainerDatasource(datasource); - - registerRpc(new GridServerRpc() { - @Override - public void setVisibleRows(int firstVisibleRow, int visibleRowCount) { - activeRowHandler - .setActiveRows(firstVisibleRow, visibleRowCount); - } - }); - } - - /** - * Sets the grid data source. - * - * @param container - * The container data source. Cannot be null. - * @throws IllegalArgumentException - * if the data source is null - */ - public void setContainerDatasource(Container.Indexed container) { - if (container == null) { - throw new IllegalArgumentException( - "Cannot set the datasource to null"); - } - if (datasource == container) { - return; - } - - // Remove old listeners - if (datasource instanceof PropertySetChangeNotifier) { - ((PropertySetChangeNotifier) datasource) - .removePropertySetChangeListener(propertyListener); - } - if (datasource instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) datasource) - .removeItemSetChangeListener(itemListener); - } - activeRowHandler.clear(); - - if (datasourceExtension != null) { - removeExtension(datasourceExtension); - } - - datasource = container; - datasourceExtension = new RpcDataProviderExtension(container); - datasourceExtension.extend(this); - - // Listen to changes in properties and remove columns if needed - if (datasource instanceof PropertySetChangeNotifier) { - ((PropertySetChangeNotifier) datasource) - .addPropertySetChangeListener(propertyListener); - } - if (datasource instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) datasource) - .addItemSetChangeListener(itemListener); - } - /* - * activeRowHandler will be updated by the client-side request that - * occurs on container change - no need to actively re-insert any - * ValueChangeListeners at this point. - */ - - getState().columns.clear(); - setLastFrozenPropertyId(null); - - // Add columns - for (Object propertyId : datasource.getContainerPropertyIds()) { - if (!columns.containsKey(propertyId)) { - GridColumn column = appendColumn(propertyId); - - // Add by default property id as column header - column.setHeaderCaption(String.valueOf(propertyId)); - } - } - - } - - /** - * Returns the grid data source. - * - * @return the container data source of the grid - */ - public Container.Indexed getContainerDatasource() { - return datasource; - } - - /** - * Returns a column based on the property id - * - * @param propertyId - * the property id of the column - * @return the column or <code>null</code> if not found - */ - public GridColumn getColumn(Object propertyId) { - return columns.get(propertyId); - } - - /** - * Sets the header rows visible. - * - * @param visible - * <code>true</code> if the header rows should be visible - */ - public void setColumnHeadersVisible(boolean visible) { - getState().columnHeadersVisible = visible; - } - - /** - * Are the header rows visible? - * - * @return <code>true</code> if the headers of the columns are visible - */ - public boolean isColumnHeadersVisible() { - return getState(false).columnHeadersVisible; - } - - /** - * Sets the footer rows visible. - * - * @param visible - * <code>true</code> if the footer rows should be visible - */ - public void setColumnFootersVisible(boolean visible) { - getState().columnFootersVisible = visible; - } - - /** - * Are the footer rows visible. - * - * @return <code>true</code> if the footer rows should be visible - */ - public boolean isColumnFootersVisible() { - return getState(false).columnFootersVisible; - } - - /** - * <p> - * Adds a new column group to the grid. - * - * <p> - * Column group rows are rendered in the header and footer of the grid. - * Column group rows are made up of column groups which groups together - * columns for adding a common auxiliary header or footer for the columns. - * </p> - * </p> - * - * <p> - * Example usage: - * - * <pre> - * // Add a new column group row to the grid - * ColumnGroupRow row = grid.addColumnGroupRow(); - * - * // Group "Column1" and "Column2" together to form a header in the row - * ColumnGroup column12 = row.addGroup("Column1", "Column2"); - * - * // Set a common header for "Column1" and "Column2" - * column12.setHeader("Column 1&2"); - * </pre> - * - * </p> - * - * @return a column group instance you can use to add column groups - */ - public ColumnGroupRow addColumnGroupRow() { - ColumnGroupRowState state = new ColumnGroupRowState(); - ColumnGroupRow row = new ColumnGroupRow(this, state, columnKeys); - columnGroupRows.add(row); - getState().columnGroupRows.add(state); - return row; - } - - /** - * Adds a new column group to the grid at a specific index - * - * @param rowIndex - * the index of the row - * @return a column group instance you can use to add column groups - */ - public ColumnGroupRow addColumnGroupRow(int rowIndex) { - ColumnGroupRowState state = new ColumnGroupRowState(); - ColumnGroupRow row = new ColumnGroupRow(this, state, columnKeys); - columnGroupRows.add(rowIndex, row); - getState().columnGroupRows.add(rowIndex, state); - return row; - } - - /** - * Removes a column group. - * - * @param row - * the row to remove - */ - public void removeColumnGroupRow(ColumnGroupRow row) { - columnGroupRows.remove(row); - getState().columnGroupRows.remove(row.getState()); - } - - /** - * Gets the column group rows. - * - * @return an unmodifiable list of column group rows - */ - public List<ColumnGroupRow> getColumnGroupRows() { - return Collections.unmodifiableList(new ArrayList<ColumnGroupRow>( - columnGroupRows)); - } - - /** - * Used internally by the {@link Grid} to get a {@link GridColumn} by - * referencing its generated state id. Also used by {@link GridColumn} to - * verify if it has been detached from the {@link Grid}. - * - * @param columnId - * the client id generated for the column when the column is - * added to the grid - * @return the column with the id or <code>null</code> if not found - */ - GridColumn getColumnByColumnId(String columnId) { - Object propertyId = getPropertyIdByColumnId(columnId); - return getColumn(propertyId); - } - - /** - * Used internally by the {@link Grid} to get a property id by referencing - * the columns generated state id. - * - * @param columnId - * The state id of the column - * @return The column instance or null if not found - */ - Object getPropertyIdByColumnId(String columnId) { - return columnKeys.get(columnId); - } - - @Override - protected GridState getState() { - return (GridState) super.getState(); - } - - @Override - protected GridState getState(boolean markAsDirty) { - return (GridState) super.getState(markAsDirty); - } - - /** - * Creates a new column based on a property id and appends it as the last - * column. - * - * @param datasourcePropertyId - * The property id of a property in the datasource - */ - private GridColumn appendColumn(Object datasourcePropertyId) { - if (datasourcePropertyId == null) { - throw new IllegalArgumentException("Property id cannot be null"); - } - assert datasource.getContainerPropertyIds().contains( - datasourcePropertyId) : "Datasource should contain the property id"; - - GridColumnState columnState = new GridColumnState(); - columnState.id = columnKeys.key(datasourcePropertyId); - getState().columns.add(columnState); - - GridColumn column = new GridColumn(this, columnState); - columns.put(datasourcePropertyId, column); - - return column; - } - - /** - * Sets (or unsets) the rightmost frozen column in the grid. - * <p> - * All columns up to and including the given column will be frozen in place - * when the grid is scrolled sideways. - * - * @param lastFrozenColumn - * the rightmost column to freeze, or <code>null</code> to not - * have any columns frozen - * @throws IllegalArgumentException - * if {@code lastFrozenColumn} is not a column from this grid - */ - void setLastFrozenColumn(GridColumn lastFrozenColumn) { - /* - * TODO: If and when Grid supports column reordering or insertion of - * columns before other columns, make sure to mention that adding - * columns before lastFrozenColumn will change the frozen column count - */ - - if (lastFrozenColumn == null) { - getState().lastFrozenColumnId = null; - } else if (columns.containsValue(lastFrozenColumn)) { - getState().lastFrozenColumnId = lastFrozenColumn.getState().id; - } else { - throw new IllegalArgumentException( - "The given column isn't attached to this grid"); - } - } - - /** - * Sets (or unsets) the rightmost frozen column in the grid. - * <p> - * All columns up to and including the indicated property will be frozen in - * place when the grid is scrolled sideways. - * <p> - * <em>Note:</em> If the container used by this grid supports a propertyId - * <code>null</code>, it can never be defined as the last frozen column, as - * a <code>null</code> parameter will always reset the frozen columns in - * Grid. - * - * @param propertyId - * the property id corresponding to the column that should be the - * last frozen column, or <code>null</code> to not have any - * columns frozen. - * @throws IllegalArgumentException - * if {@code lastFrozenColumn} is not a column from this grid - */ - public void setLastFrozenPropertyId(Object propertyId) { - final GridColumn column; - if (propertyId == null) { - column = null; - } else { - column = getColumn(propertyId); - if (column == null) { - throw new IllegalArgumentException( - "property id does not exist."); - } - } - setLastFrozenColumn(column); - } - - /** - * Gets the rightmost frozen column in the grid. - * <p> - * <em>Note:</em> Most often, this method returns the very value set with - * {@link #setLastFrozenPropertyId(Object)}. This value, however, can be - * reset to <code>null</code> if the column is detached from this grid. - * - * @return the rightmost frozen column in the grid, or <code>null</code> if - * no columns are frozen. - */ - public Object getLastFrozenPropertyId() { - return columnKeys.get(getState().lastFrozenColumnId); - } - - /** - * Scrolls to a certain item, using {@link ScrollDestination#ANY}. - * - * @param itemId - * id of item to scroll to. - * @throws IllegalArgumentException - * if the provided id is not recognized by the data source. - */ - public void scrollToItem(Object itemId) throws IllegalArgumentException { - scrollToItem(itemId, ScrollDestination.ANY); - } - - /** - * Scrolls to a certain item, using user-specified scroll destination. - * - * @param itemId - * id of item to scroll to. - * @param destination - * value specifying desired position of scrolled-to row. - * @throws IllegalArgumentException - * if the provided id is not recognized by the data source. - */ - public void scrollToItem(Object itemId, ScrollDestination destination) - throws IllegalArgumentException { - - int row = datasource.indexOfId(itemId); - - if (row == -1) { - throw new IllegalArgumentException( - "Item with specified ID does not exist in data source"); - } - - GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); - clientRPC.scrollToRow(row, destination); - } - - /** - * Scrolls to the beginning of the first data row. - */ - public void scrollToStart() { - GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); - clientRPC.scrollToStart(); - } - - /** - * Scrolls to the end of the last data row. - */ - public void scrollToEnd() { - GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); - clientRPC.scrollToEnd(); - } -} diff --git a/server/src/com/vaadin/ui/components/grid/GridColumn.java b/server/src/com/vaadin/ui/components/grid/GridColumn.java deleted file mode 100644 index 852db21275..0000000000 --- a/server/src/com/vaadin/ui/components/grid/GridColumn.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.ui.components.grid; - -import java.io.Serializable; - -import com.vaadin.shared.ui.grid.GridColumnState; - -/** - * A column in the grid. Can be obtained by calling - * {@link Grid#getColumn(Object propertyId)}. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class GridColumn implements Serializable { - - /** - * The state of the column shared to the client - */ - private final GridColumnState state; - - /** - * The grid this column is associated with - */ - private final Grid grid; - - /** - * Internally used constructor. - * - * @param grid - * The grid this column belongs to. Should not be null. - * @param state - * the shared state of this column - */ - GridColumn(Grid grid, GridColumnState state) { - this.grid = grid; - this.state = state; - } - - /** - * Returns the serializable state of this column that is sent to the client - * side connector. - * - * @return the internal state of the column - */ - GridColumnState getState() { - return state; - } - - /** - * Returns the caption of the header. By default the header caption is the - * property id of the column. - * - * @return the text in the header - * - * @throws IllegalStateException - * if the column no longer is attached to the grid - */ - public String getHeaderCaption() throws IllegalStateException { - checkColumnIsAttached(); - return state.header; - } - - /** - * Sets the caption of the header. - * - * @param caption - * the text to show in the caption - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public void setHeaderCaption(String caption) throws IllegalStateException { - checkColumnIsAttached(); - state.header = caption; - grid.markAsDirty(); - } - - /** - * Returns the caption of the footer. By default the captions are - * <code>null</code>. - * - * @return the text in the footer - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public String getFooterCaption() throws IllegalStateException { - checkColumnIsAttached(); - return state.footer; - } - - /** - * Sets the caption of the footer. - * - * @param caption - * the text to show in the caption - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public void setFooterCaption(String caption) throws IllegalStateException { - checkColumnIsAttached(); - state.footer = caption; - grid.markAsDirty(); - } - - /** - * Returns the width (in pixels). By default a column is 100px wide. - * - * @return the width in pixels of the column - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public int getWidth() throws IllegalStateException { - checkColumnIsAttached(); - return state.width; - } - - /** - * Sets the width (in pixels). - * - * @param pixelWidth - * the new pixel width of the column - * @throws IllegalStateException - * if the column is no longer attached to any grid - * @throws IllegalArgumentException - * thrown if pixel width is less than zero - */ - public void setWidth(int pixelWidth) throws IllegalStateException, - IllegalArgumentException { - checkColumnIsAttached(); - if (pixelWidth < 0) { - throw new IllegalArgumentException( - "Pixel width should be greated than 0"); - } - state.width = pixelWidth; - grid.markAsDirty(); - } - - /** - * Marks the column width as undefined meaning that the grid is free to - * resize the column based on the cell contents and available space in the - * grid. - */ - public void setWidthUndefined() { - checkColumnIsAttached(); - state.width = -1; - grid.markAsDirty(); - } - - /** - * Is this column visible in the grid. By default all columns are visible. - * - * @return <code>true</code> if the column is visible - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public boolean isVisible() throws IllegalStateException { - checkColumnIsAttached(); - return state.visible; - } - - /** - * Set the visibility of this column - * - * @param visible - * is the column visible - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public void setVisible(boolean visible) throws IllegalStateException { - checkColumnIsAttached(); - state.visible = visible; - grid.markAsDirty(); - } - - /** - * Checks if column is attached and throws an {@link IllegalStateException} - * if it is not - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - protected void checkColumnIsAttached() throws IllegalStateException { - if (grid.getColumnByColumnId(state.id) == null) { - throw new IllegalStateException("Column no longer exists."); - } - } - - /** - * Sets this column as the last frozen column in its grid. - * - * @throws IllegalArgumentException - * if the column is no longer attached to any grid - * @see Grid#setLastFrozenColumn(GridColumn) - */ - public void setLastFrozenColumn() { - checkColumnIsAttached(); - grid.setLastFrozenColumn(this); - } -} diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumnGroups.java b/server/tests/src/com/vaadin/tests/server/component/grid/GridColumnGroups.java deleted file mode 100644 index 4350bf1a7b..0000000000 --- a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumnGroups.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.server.component.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.ui.components.grid.ColumnGroup; -import com.vaadin.ui.components.grid.ColumnGroupRow; -import com.vaadin.ui.components.grid.Grid; - -/** - * - * @since - * @author Vaadin Ltd - */ -public class GridColumnGroups { - - private Grid grid; - - private GridState state; - - private Method getStateMethod; - - private Field columnIdGeneratorField; - - private KeyMapper<Object> columnIdMapper; - - @Before - public void setup() throws Exception { - IndexedContainer ds = new IndexedContainer(); - for (int c = 0; c < 10; c++) { - ds.addContainerProperty("column" + c, String.class, ""); - } - grid = new Grid(ds); - - getStateMethod = Grid.class.getDeclaredMethod("getState"); - getStateMethod.setAccessible(true); - - state = (GridState) getStateMethod.invoke(grid); - - columnIdGeneratorField = Grid.class.getDeclaredField("columnKeys"); - columnIdGeneratorField.setAccessible(true); - - columnIdMapper = (KeyMapper<Object>) columnIdGeneratorField.get(grid); - } - - @Test - public void testColumnGroupRows() throws Exception { - - // No column group rows by default - List<ColumnGroupRow> rows = grid.getColumnGroupRows(); - assertEquals(0, rows.size()); - - // Add some rows - ColumnGroupRow row1 = grid.addColumnGroupRow(); - ColumnGroupRow row3 = grid.addColumnGroupRow(); - ColumnGroupRow row2 = grid.addColumnGroupRow(1); - - rows = grid.getColumnGroupRows(); - assertEquals(3, rows.size()); - assertEquals(row1, rows.get(0)); - assertEquals(row2, rows.get(1)); - assertEquals(row3, rows.get(2)); - - // Header should be visible by default, footer should not - assertTrue(row1.isHeaderVisible()); - assertFalse(row1.isFooterVisible()); - - row1.setHeaderVisible(false); - assertFalse(row1.isHeaderVisible()); - row1.setHeaderVisible(true); - assertTrue(row1.isHeaderVisible()); - - row1.setFooterVisible(true); - assertTrue(row1.isFooterVisible()); - row1.setFooterVisible(false); - assertFalse(row1.isFooterVisible()); - - row1.setHeaderVisible(true); - row1.setFooterVisible(true); - assertTrue(row1.isHeaderVisible()); - assertTrue(row1.isFooterVisible()); - - row1.setHeaderVisible(false); - row1.setFooterVisible(false); - assertFalse(row1.isHeaderVisible()); - assertFalse(row1.isFooterVisible()); - } - - @Test - public void testColumnGroupsInState() throws Exception { - - // Add a new row - ColumnGroupRow row = grid.addColumnGroupRow(); - assertTrue(state.columnGroupRows.size() == 1); - - // Add a group by property id - ColumnGroup columns12 = row.addGroup("column1", "column2"); - assertTrue(state.columnGroupRows.get(0).groups.size() == 1); - - // Set header of column - columns12.setHeaderCaption("Column12"); - assertEquals("Column12", - state.columnGroupRows.get(0).groups.get(0).header); - - // Set footer of column - columns12.setFooterCaption("Footer12"); - assertEquals("Footer12", - state.columnGroupRows.get(0).groups.get(0).footer); - - // Add another group by column instance - ColumnGroup columns34 = row.addGroup(grid.getColumn("column3"), - grid.getColumn("column4")); - assertTrue(state.columnGroupRows.get(0).groups.size() == 2); - - // add another group row - ColumnGroupRow row2 = grid.addColumnGroupRow(); - assertTrue(state.columnGroupRows.size() == 2); - - // add a group by combining the two previous groups - ColumnGroup columns1234 = row2.addGroup(columns12, columns34); - assertTrue(columns1234.getColumns().size() == 4); - - // Insert a group as the second group - ColumnGroupRow newRow2 = grid.addColumnGroupRow(1); - assertTrue(state.columnGroupRows.size() == 3); - } - - @Test - public void testAddingColumnGroups() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - - // By property id - ColumnGroup columns01 = row.addGroup("column0", "column1"); - assertEquals(2, columns01.getColumns().size()); - assertEquals("column0", columns01.getColumns().get(0)); - assertTrue(columns01.isColumnInGroup("column0")); - assertEquals("column1", columns01.getColumns().get(1)); - assertTrue(columns01.isColumnInGroup("column1")); - - // By grid column - ColumnGroup columns23 = row.addGroup(grid.getColumn("column2"), - grid.getColumn("column3")); - assertEquals(2, columns23.getColumns().size()); - assertEquals("column2", columns23.getColumns().get(0)); - assertTrue(columns23.isColumnInGroup("column2")); - assertEquals("column3", columns23.getColumns().get(1)); - assertTrue(columns23.isColumnInGroup("column3")); - - // Combine groups - ColumnGroupRow row2 = grid.addColumnGroupRow(); - ColumnGroup columns0123 = row2.addGroup(columns01, columns23); - assertEquals(4, columns0123.getColumns().size()); - assertEquals("column0", columns0123.getColumns().get(0)); - assertTrue(columns0123.isColumnInGroup("column0")); - assertEquals("column1", columns0123.getColumns().get(1)); - assertTrue(columns0123.isColumnInGroup("column1")); - assertEquals("column2", columns0123.getColumns().get(2)); - assertTrue(columns0123.isColumnInGroup("column2")); - assertEquals("column3", columns0123.getColumns().get(3)); - assertTrue(columns0123.isColumnInGroup("column3")); - } - - @Test - public void testColumnGroupHeadersAndFooters() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - ColumnGroup group = row.addGroup("column1", "column2"); - - // Header - assertNull(group.getHeaderCaption()); - group.setHeaderCaption("My header"); - assertEquals("My header", group.getHeaderCaption()); - group.setHeaderCaption(null); - assertNull(group.getHeaderCaption()); - - // Footer - assertNull(group.getFooterCaption()); - group.setFooterCaption("My footer"); - assertEquals("My footer", group.getFooterCaption()); - group.setFooterCaption(null); - assertNull(group.getFooterCaption()); - } - - @Test - public void testColumnGroupDetachment() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - ColumnGroup group = row.addGroup("column1", "column2"); - - // Remove group - row.removeGroup(group); - - try { - group.setHeaderCaption("Header"); - fail("Should throw exception for setting header caption on detached group"); - } catch (IllegalStateException ise) { - - } - - try { - group.setFooterCaption("Footer"); - fail("Should throw exception for setting footer caption on detached group"); - } catch (IllegalStateException ise) { - - } - } - - @Test - public void testColumnGroupLimits() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - row.addGroup("column1", "column2"); - row.addGroup("column3", "column4"); - - try { - row.addGroup("column2", "column3"); - fail("Adding a group with already grouped properties should throw exception"); - } catch (IllegalArgumentException iae) { - - } - - ColumnGroupRow row2 = grid.addColumnGroupRow(); - - try { - row2.addGroup("column2", "column3"); - fail("Adding a group that breaks previous grouping boundaries should throw exception"); - } catch (IllegalArgumentException iae) { - - } - - // This however should not throw an exception as it spans completely - // over the parent rows groups - row2.addGroup("column1", "column2", "column3", "column4"); - - } -} diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java b/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java deleted file mode 100644 index da07611b48..0000000000 --- a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.server.component.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.ui.components.grid.Grid; -import com.vaadin.ui.components.grid.GridColumn; - -public class GridColumns { - - private Grid grid; - - private GridState state; - - private Method getStateMethod; - - private Field columnIdGeneratorField; - - private KeyMapper<Object> columnIdMapper; - - @Before - public void setup() throws Exception { - IndexedContainer ds = new IndexedContainer(); - for (int c = 0; c < 10; c++) { - ds.addContainerProperty("column" + c, String.class, ""); - } - grid = new Grid(ds); - - getStateMethod = Grid.class.getDeclaredMethod("getState"); - getStateMethod.setAccessible(true); - - state = (GridState) getStateMethod.invoke(grid); - - columnIdGeneratorField = Grid.class.getDeclaredField("columnKeys"); - columnIdGeneratorField.setAccessible(true); - - columnIdMapper = (KeyMapper<Object>) columnIdGeneratorField.get(grid); - } - - @Test - public void testColumnGeneration() throws Exception { - - for (Object propertyId : grid.getContainerDatasource() - .getContainerPropertyIds()) { - - // All property ids should get a column - GridColumn column = grid.getColumn(propertyId); - assertNotNull(column); - - // Property id should be the column header by default - assertEquals(propertyId.toString(), column.getHeaderCaption()); - } - } - - @Test - public void testModifyingColumnProperties() throws Exception { - - // Modify first column - GridColumn column = grid.getColumn("column1"); - assertNotNull(column); - - column.setFooterCaption("CustomFooter"); - assertEquals("CustomFooter", column.getFooterCaption()); - assertEquals(column.getFooterCaption(), - getColumnState("column1").footer); - - column.setHeaderCaption("CustomHeader"); - assertEquals("CustomHeader", column.getHeaderCaption()); - assertEquals(column.getHeaderCaption(), - getColumnState("column1").header); - - column.setVisible(false); - assertFalse(column.isVisible()); - assertFalse(getColumnState("column1").visible); - - column.setVisible(true); - assertTrue(column.isVisible()); - assertTrue(getColumnState("column1").visible); - - column.setWidth(100); - assertEquals(100, column.getWidth()); - assertEquals(column.getWidth(), getColumnState("column1").width); - - try { - column.setWidth(-1); - fail("Setting width to -1 should throw exception"); - } catch (IllegalArgumentException iae) { - - } - - assertEquals(100, column.getWidth()); - assertEquals(100, getColumnState("column1").width); - } - - @Test - public void testRemovingColumn() throws Exception { - - GridColumn column = grid.getColumn("column1"); - assertNotNull(column); - - // Remove column - grid.getContainerDatasource().removeContainerProperty("column1"); - - try { - column.setHeaderCaption("asd"); - - fail("Succeeded in modifying a detached column"); - } catch (IllegalStateException ise) { - // Detached state should throw exception - } - - try { - column.setFooterCaption("asd"); - fail("Succeeded in modifying a detached column"); - } catch (IllegalStateException ise) { - // Detached state should throw exception - } - - try { - column.setVisible(false); - fail("Succeeded in modifying a detached column"); - } catch (IllegalStateException ise) { - // Detached state should throw exception - } - - try { - column.setWidth(123); - fail("Succeeded in modifying a detached column"); - } catch (IllegalStateException ise) { - // Detached state should throw exception - } - - assertNull(grid.getColumn("column1")); - assertNull(getColumnState("column1")); - } - - @Test - public void testAddingColumn() throws Exception { - grid.getContainerDatasource().addContainerProperty("columnX", - String.class, ""); - GridColumn column = grid.getColumn("columnX"); - assertNotNull(column); - } - - @Test - public void testHeaderVisiblility() throws Exception { - - assertTrue(grid.isColumnHeadersVisible()); - assertTrue(state.columnHeadersVisible); - - grid.setColumnHeadersVisible(false); - assertFalse(grid.isColumnHeadersVisible()); - assertFalse(state.columnHeadersVisible); - - grid.setColumnHeadersVisible(true); - assertTrue(grid.isColumnHeadersVisible()); - assertTrue(state.columnHeadersVisible); - } - - @Test - public void testFooterVisibility() throws Exception { - - assertFalse(grid.isColumnFootersVisible()); - assertFalse(state.columnFootersVisible); - - grid.setColumnFootersVisible(false); - assertFalse(grid.isColumnFootersVisible()); - assertFalse(state.columnFootersVisible); - - grid.setColumnFootersVisible(true); - assertTrue(grid.isColumnFootersVisible()); - assertTrue(state.columnFootersVisible); - } - - @Test - public void testFrozenColumnByPropertyId() { - assertNull("Grid should not start with a frozen column", - grid.getLastFrozenPropertyId()); - - Object propertyId = grid.getContainerDatasource() - .getContainerPropertyIds().iterator().next(); - grid.setLastFrozenPropertyId(propertyId); - assertEquals(propertyId, grid.getLastFrozenPropertyId()); - - grid.getContainerDatasource().removeContainerProperty(propertyId); - assertNull(grid.getLastFrozenPropertyId()); - } - - private GridColumnState getColumnState(Object propertyId) { - String columnId = columnIdMapper.key(propertyId); - for (GridColumnState columnState : state.columns) { - if (columnState.id.equals(columnId)) { - return columnState; - } - } - return null; - } - -} diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java deleted file mode 100644 index 79e3f17f8d..0000000000 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.data; - -import java.util.List; - -import com.vaadin.shared.communication.ClientRpc; - -/** - * RPC interface used for pushing container data to the client. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface DataProviderRpc extends ClientRpc { - - /** - * Sends updated row data to a client. - * - * @param firstRowIndex - * the index of the first updated row - * @param rowData - * the updated row data - */ - public void setRowData(int firstRowIndex, List<String[]> rowData); - - /** - * Informs the client to remove row data. - * - * @param firstRowIndex - * the index of the first removed row - * @param count - * the number of rows removed from <code>firstRowIndex</code> and - * onwards - */ - public void removeRowData(int firstRowIndex, int count); - - /** - * Informs the client to insert new row data. - * - * @param firstRowIndex - * the index of the first new row - * @param count - * the number of rows inserted at <code>firstRowIndex</code> - */ - public void insertRowData(int firstRowIndex, int count); -} diff --git a/shared/src/com/vaadin/shared/data/DataProviderState.java b/shared/src/com/vaadin/shared/data/DataProviderState.java deleted file mode 100644 index 2eabe0b0e1..0000000000 --- a/shared/src/com/vaadin/shared/data/DataProviderState.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.data; - -import com.vaadin.shared.communication.SharedState; - -/** - * Shared state used by client-side data sources. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class DataProviderState extends SharedState { - /** - * The size of the container. - */ - public int containerSize; -} diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java deleted file mode 100644 index eaf17df8f6..0000000000 --- a/shared/src/com/vaadin/shared/data/DataRequestRpc.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.data; - -import com.vaadin.shared.communication.ServerRpc; - -/** - * RPC interface used for requesting container data to the client. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface DataRequestRpc extends ServerRpc { - - /** - * Request rows from the server. - * - * @param firstRowIndex - * the index of the first requested row - * @param numberOfRows - * the number of requested rows - */ - public void requestRows(int firstRowIndex, int numberOfRows); -} diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java deleted file mode 100644 index a8e0f87457..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -/** - * The column group row data shared between the server and client - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class ColumnGroupRowState implements Serializable { - - /** - * The groups that has been added to the row - */ - public List<ColumnGroupState> groups = new ArrayList<ColumnGroupState>(); - - /** - * Is the header shown - */ - public boolean headerVisible = true; - - /** - * Is the footer shown - */ - public boolean footerVisible = false; - -} diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java deleted file mode 100644 index 3992b6611f..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -/** - * The column group data shared between the server and the client - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class ColumnGroupState implements Serializable { - - /** - * The columns that is included in the group - */ - public List<String> columns = new ArrayList<String>(); - - /** - * The header text of the group - */ - public String header; - - /** - * The footer text of the group - */ - public String footer; -} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java deleted file mode 100644 index 00cc93d371..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -import com.vaadin.shared.communication.ClientRpc; - -/** - * Server-to-client RPC interface for the Grid component. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface GridClientRpc extends ClientRpc { - - /** - * Command client Grid to scroll to a specific data row. - * - * @param row - * zero-based row index. If the row index is below zero or above - * the row count of the client-side data source, a client-side - * exception will be triggered. Since this exception has no - * handling by default, an out-of-bounds value will cause a - * client-side crash. - * @param destination - * desired placement of scrolled-to row. See the documentation - * for {@link ScrollDestination} for more information. - */ - public void scrollToRow(int row, ScrollDestination destination); - - /** - * Command client Grid to scroll to the first row. - */ - public void scrollToStart(); - - /** - * Command client Grid to scroll to the last row. - */ - public void scrollToEnd(); - -} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java deleted file mode 100644 index 0301c5ead2..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -import java.io.Serializable; - -/** - * Column state DTO for transferring column properties from the server to the - * client - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class GridColumnState implements Serializable { - - /** - * Id used by grid connector to map server side column with client side - * column - */ - public String id; - - /** - * Header caption for the column - */ - public String header; - - /** - * Footer caption for the column - */ - public String footer; - - /** - * Has the column been hidden. By default the column is visible. - */ - public boolean visible = true; - - /** - * Column width in pixels. Default column width is 100px. - */ - public int width = 100; - -} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java deleted file mode 100644 index 5b88fad5a8..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -/** - * Container class for common constants and default values used by the Grid - * component. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public final class GridConstants { - - /** - * Default padding in pixels when scrolling programmatically, without an - * explicitly defined padding value. - */ - public static final int DEFAULT_PADDING = 0; - -} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java deleted file mode 100644 index db0a31ed2c..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -import com.vaadin.shared.communication.ServerRpc; - -/** - * TODO - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface GridServerRpc extends ServerRpc { - - /** - * TODO - * - * @param firstVisibleRow - * the index of the first visible row - * @param visibleRowCount - * the number of rows visible, counted from - * <code>firstVisibleRow</code> - */ - void setVisibleRows(int firstVisibleRow, int visibleRowCount); - -} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java deleted file mode 100644 index 93e602a539..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -import java.util.ArrayList; -import java.util.List; - -import com.vaadin.shared.AbstractComponentState; - -/** - * The shared state for the {@link com.vaadin.ui.components.grid.Grid} component - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class GridState extends AbstractComponentState { - { - // FIXME Grid currently does not support undefined size - width = "400px"; - height = "400px"; - } - - /** - * Columns in grid. Column order implicitly deferred from list order. - */ - public List<GridColumnState> columns = new ArrayList<GridColumnState>(); - - /** - * Is the column header row visible - */ - public boolean columnHeadersVisible = true; - - /** - * Is the column footer row visible - */ - public boolean columnFootersVisible = false; - - /** - * The column groups added to the grid - */ - public List<ColumnGroupRowState> columnGroupRows = new ArrayList<ColumnGroupRowState>(); - - /** - * The id for the last frozen column. - * - * @see GridColumnState#id - */ - public String lastFrozenColumnId = null; - -} diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java deleted file mode 100644 index 3114a79c82..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/Range.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -/** - * An immutable representation of a range, marked by start and end points. - * <p> - * The range is treated as inclusive at the start, and exclusive at the end. - * I.e. the range [0..1[ has the length 1, and represents one integer: 0. - * <p> - * The range is considered {@link #isEmpty() empty} if the start is the same as - * the end. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public final class Range { - private final int start; - private final int end; - - /** - * Creates a range object representing a single integer. - * - * @param integer - * the number to represent as a range - * @return the range represented by <code>integer</code> - */ - public static Range withOnly(final int integer) { - return new Range(integer, integer + 1); - } - - /** - * Creates a range between two integers. - * <p> - * The range start is <em>inclusive</em> and the end is <em>exclusive</em>. - * So, a range "between" 0 and 5 represents the numbers 0, 1, 2, 3 and 4, - * but not 5. - * - * @param start - * the start of the the range, inclusive - * @param end - * the end of the range, exclusive - * @return a range representing <code>[start..end[</code> - * @throws IllegalArgumentException - * if <code>start > end</code> - */ - public static Range between(final int start, final int end) - throws IllegalArgumentException { - return new Range(start, end); - } - - /** - * Creates a range from a start point, with a given length. - * - * @param start - * the first integer to include in the range - * @param length - * the length of the resulting range - * @return a range starting from <code>start</code>, with - * <code>length</code> number of integers following - * @throws IllegalArgumentException - * if length < 0 - */ - public static Range withLength(final int start, final int length) - throws IllegalArgumentException { - if (length < 0) { - /* - * The constructor of Range will throw an exception if start > - * start+length (i.e. if length is negative). We're throwing the - * same exception type, just with a more descriptive message. - */ - throw new IllegalArgumentException("length must not be negative"); - } - return new Range(start, start + length); - } - - /** - * Creates a new range between two numbers: <code>[start..end[</code>. - * - * @param start - * the start integer, inclusive - * @param end - * the end integer, exclusive - * @throws IllegalArgumentException - * if <code>start > end</code> - */ - private Range(final int start, final int end) - throws IllegalArgumentException { - if (start > end) { - throw new IllegalArgumentException( - "start must not be greater than end"); - } - - this.start = start; - this.end = end; - } - - /** - * Returns the <em>inclusive</em> start point of this range. - * - * @return the start point of this range - */ - public int getStart() { - return start; - } - - /** - * Returns the <em>exclusive</em> end point of this range. - * - * @return the end point of this range - */ - public int getEnd() { - return end; - } - - /** - * The number of integers contained in the range. - * - * @return the number of integers contained in the range - */ - public int length() { - return getEnd() - getStart(); - } - - /** - * Checks whether the range has no elements between the start and end. - * - * @return <code>true</code> iff the range contains no elements. - */ - public boolean isEmpty() { - return getStart() >= getEnd(); - } - - /** - * Checks whether this range and another range are at least partially - * covering the same values. - * - * @param other - * the other range to check against - * @return <code>true</code> if this and <code>other</code> intersect - */ - public boolean intersects(final Range other) { - return getStart() < other.getEnd() && other.getStart() < getEnd(); - } - - /** - * Checks whether an integer is found within this range. - * - * @param integer - * an integer to test for presence in this range - * @return <code>true</code> iff <code>integer</code> is in this range - */ - public boolean contains(final int integer) { - return getStart() <= integer && integer < getEnd(); - } - - /** - * Checks whether this range is a subset of another range. - * - * @return <code>true</code> iff <code>other</code> completely wraps this - * range - */ - public boolean isSubsetOf(final Range other) { - return other.getStart() <= getStart() && getEnd() <= other.getEnd(); - } - - /** - * Overlay this range with another one, and partition the ranges according - * to how they position relative to each other. - * <p> - * The three partitions are returned as a three-element Range array: - * <ul> - * <li>Elements in this range that occur before elements in - * <code>other</code>. - * <li>Elements that are shared between the two ranges. - * <li>Elements in this range that occur after elements in - * <code>other</code>. - * </ul> - * - * @param other - * the other range to act as delimiters. - * @return a three-element Range array of partitions depicting the elements - * before (index 0), shared/inside (index 1) and after (index 2). - */ - public Range[] partitionWith(final Range other) { - final Range[] splitBefore = splitAt(other.getStart()); - final Range rangeBefore = splitBefore[0]; - final Range[] splitAfter = splitBefore[1].splitAt(other.getEnd()); - final Range rangeInside = splitAfter[0]; - final Range rangeAfter = splitAfter[1]; - return new Range[] { rangeBefore, rangeInside, rangeAfter }; - } - - /** - * Get a range that is based on this one, but offset by a number. - * - * @param offset - * the number to offset by - * @return a copy of this range, offset by <code>offset</code> - */ - public Range offsetBy(final int offset) { - if (offset == 0) { - return this; - } else { - return new Range(start + offset, end + offset); - } - } - - @Override - public String toString() { - return getClass().getSimpleName() + " [" + getStart() + ".." + getEnd() - + "[" + (isEmpty() ? " (empty)" : ""); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + end; - result = prime * result + start; - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Range other = (Range) obj; - if (end != other.end) { - return false; - } - if (start != other.start) { - return false; - } - return true; - } - - /** - * Checks whether this range starts before the start of another range. - * - * @param other - * the other range to compare against - * @return <code>true</code> iff this range starts before the - * <code>other</code> - */ - public boolean startsBefore(final Range other) { - return getStart() < other.getStart(); - } - - /** - * Checks whether this range ends before the start of another range. - * - * @param other - * the other range to compare against - * @return <code>true</code> iff this range ends before the - * <code>other</code> - */ - public boolean endsBefore(final Range other) { - return getEnd() <= other.getStart(); - } - - /** - * Checks whether this range ends after the end of another range. - * - * @param other - * the other range to compare against - * @return <code>true</code> iff this range ends after the - * <code>other</code> - */ - public boolean endsAfter(final Range other) { - return getEnd() > other.getEnd(); - } - - /** - * Checks whether this range starts after the end of another range. - * - * @param other - * the other range to compare against - * @return <code>true</code> iff this range starts after the - * <code>other</code> - */ - public boolean startsAfter(final Range other) { - return getStart() >= other.getEnd(); - } - - /** - * Split the range into two at a certain integer. - * <p> - * <em>Example:</em> <code>[5..10[.splitAt(7) == [5..7[, [7..10[</code> - * - * @param integer - * the integer at which to split the range into two - * @return an array of two ranges, with <code>[start..integer[</code> in the - * first element, and <code>[integer..end[</code> in the second - * element. - * <p> - * If {@code integer} is less than {@code start}, [empty, - * {@code this} ] is returned. if <code>integer</code> is equal to - * or greater than {@code end}, [{@code this}, empty] is returned - * instead. - */ - public Range[] splitAt(final int integer) { - if (integer < start) { - return new Range[] { Range.withLength(start, 0), this }; - } else if (integer >= end) { - return new Range[] { this, Range.withLength(end, 0) }; - } else { - return new Range[] { new Range(start, integer), - new Range(integer, end) }; - } - } - - /** - * Split the range into two after a certain number of integers into the - * range. - * <p> - * Calling this method is equivalent to calling - * <code>{@link #splitAt(int) splitAt}({@link #getStart()}+length);</code> - * <p> - * <em>Example:</em> - * <code>[5..10[.splitAtFromStart(2) == [5..7[, [7..10[</code> - * - * @param length - * the length at which to split this range into two - * @return an array of two ranges, having the <code>length</code>-first - * elements of this range, and the second range having the rest. If - * <code>length</code> ≤ 0, the first element will be empty, and - * the second element will be this range. If <code>length</code> - * ≥ {@link #length()}, the first element will be this range, - * and the second element will be empty. - */ - public Range[] splitAtFromStart(final int length) { - return splitAt(getStart() + length); - } - - /** - * Combines two ranges to create a range containing all values in both - * ranges, provided there are no gaps between the ranges. - * - * @param other - * the range to combine with this range - * - * @return the combined range - * - * @throws IllegalArgumentException - * if the two ranges aren't connected - */ - public Range combineWith(Range other) throws IllegalArgumentException { - if (getStart() > other.getEnd() || other.getStart() > getEnd()) { - throw new IllegalArgumentException("There is a gap between " + this - + " and " + other); - } - - return Range.between(Math.min(getStart(), other.getStart()), - Math.max(getEnd(), other.getEnd())); - } -} diff --git a/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java b/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java deleted file mode 100644 index decc2fab5f..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -/** - * Enumeration, specifying the destinations that are supported when scrolling - * rows or columns into view. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public enum ScrollDestination { - - /** - * Scroll as little as possible to show the target element. If the element - * fits into view, this works as START or END depending on the current - * scroll position. If the element does not fit into view, this works as - * START. - */ - ANY, - - /** - * Scrolls so that the element is shown at the start of the viewport. The - * viewport will, however, not scroll beyond its contents. - */ - START, - - /** - * Scrolls so that the element is shown in the middle of the viewport. The - * viewport will, however, not scroll beyond its contents, given more - * elements than what the viewport is able to show at once. Under no - * circumstances will the viewport scroll before its first element. - */ - MIDDLE, - - /** - * Scrolls so that the element is shown at the end of the viewport. The - * viewport will, however, not scroll before its first element. - */ - END - -} diff --git a/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java b/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java deleted file mode 100644 index b042cee509..0000000000 --- a/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.shared.ui.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -@SuppressWarnings("static-method") -public class RangeTest { - - @Test(expected = IllegalArgumentException.class) - public void startAfterEndTest() { - Range.between(10, 9); - } - - @Test(expected = IllegalArgumentException.class) - public void negativeLengthTest() { - Range.withLength(10, -1); - } - - @Test - public void constructorEquivalenceTest() { - assertEquals("10 == [10,11[", Range.withOnly(10), Range.between(10, 11)); - assertEquals("[10,20[ == 10, length 10", Range.between(10, 20), - Range.withLength(10, 10)); - assertEquals("10 == 10, length 1", Range.withOnly(10), - Range.withLength(10, 1)); - } - - @Test - public void boundsTest() { - { - final Range range = Range.between(0, 10); - assertEquals("between(0, 10) start", 0, range.getStart()); - assertEquals("between(0, 10) end", 10, range.getEnd()); - } - - { - final Range single = Range.withOnly(10); - assertEquals("withOnly(10) start", 10, single.getStart()); - assertEquals("withOnly(10) end", 11, single.getEnd()); - } - - { - final Range length = Range.withLength(10, 5); - assertEquals("withLength(10, 5) start", 10, length.getStart()); - assertEquals("withLength(10, 5) end", 15, length.getEnd()); - } - } - - @Test - @SuppressWarnings("boxing") - public void equalsTest() { - final Range range1 = Range.between(0, 10); - final Range range2 = Range.withLength(0, 11); - - assertTrue("null", !range1.equals(null)); - assertTrue("reflexive", range1.equals(range1)); - assertEquals("symmetric", range1.equals(range2), range2.equals(range1)); - } - - @Test - public void containsTest() { - final int start = 0; - final int end = 10; - final Range range = Range.between(start, end); - - assertTrue("start should be contained", range.contains(start)); - assertTrue("start-1 should not be contained", - !range.contains(start - 1)); - assertTrue("end should not be contained", !range.contains(end)); - assertTrue("end-1 should be contained", range.contains(end - 1)); - - assertTrue("[0..10[ contains 5", Range.between(0, 10).contains(5)); - assertTrue("empty range does not contain 5", !Range.between(5, 5) - .contains(5)); - } - - @Test - public void emptyTest() { - assertTrue("[0..0[ should be empty", Range.between(0, 0).isEmpty()); - assertTrue("Range of length 0 should be empty", Range.withLength(0, 0) - .isEmpty()); - - assertTrue("[0..1[ should not be empty", !Range.between(0, 1).isEmpty()); - assertTrue("Range of length 1 should not be empty", - !Range.withLength(0, 1).isEmpty()); - } - - @Test - public void splitTest() { - final Range startRange = Range.between(0, 10); - final Range[] splitRanges = startRange.splitAt(5); - assertEquals("[0..10[ split at 5, lower", Range.between(0, 5), - splitRanges[0]); - assertEquals("[0..10[ split at 5, upper", Range.between(5, 10), - splitRanges[1]); - } - - @Test - public void split_valueBefore() { - Range range = Range.between(10, 20); - Range[] splitRanges = range.splitAt(5); - - assertEquals(Range.between(10, 10), splitRanges[0]); - assertEquals(range, splitRanges[1]); - } - - @Test - public void split_valueAfter() { - Range range = Range.between(10, 20); - Range[] splitRanges = range.splitAt(25); - - assertEquals(range, splitRanges[0]); - assertEquals(Range.between(20, 20), splitRanges[1]); - } - - @Test - public void emptySplitTest() { - final Range range = Range.between(5, 10); - final Range[] split1 = range.splitAt(0); - assertTrue("split1, [0]", split1[0].isEmpty()); - assertEquals("split1, [1]", range, split1[1]); - - final Range[] split2 = range.splitAt(15); - assertEquals("split2, [0]", range, split2[0]); - assertTrue("split2, [1]", split2[1].isEmpty()); - } - - @Test - public void lengthTest() { - assertEquals("withLength length", 5, Range.withLength(10, 5).length()); - assertEquals("between length", 5, Range.between(10, 15).length()); - assertEquals("withOnly 10 length", 1, Range.withOnly(10).length()); - } - - @Test - public void intersectsTest() { - assertTrue("[0..10[ intersects [5..15[", Range.between(0, 10) - .intersects(Range.between(5, 15))); - assertTrue("[0..10[ does not intersect [10..20[", !Range.between(0, 10) - .intersects(Range.between(10, 20))); - } - - @Test - public void intersects_emptyInside() { - assertTrue("[5..5[ does intersect with [0..10[", Range.between(5, 5) - .intersects(Range.between(0, 10))); - assertTrue("[0..10[ does intersect with [5..5[", Range.between(0, 10) - .intersects(Range.between(5, 5))); - } - - @Test - public void intersects_emptyOutside() { - assertTrue("[15..15[ does not intersect with [0..10[", - !Range.between(15, 15).intersects(Range.between(0, 10))); - assertTrue("[0..10[ does not intersect with [15..15[", - !Range.between(0, 10).intersects(Range.between(15, 15))); - } - - @Test - public void subsetTest() { - assertTrue("[5..10[ is subset of [0..20[", Range.between(5, 10) - .isSubsetOf(Range.between(0, 20))); - - final Range range = Range.between(0, 10); - assertTrue("range is subset of self", range.isSubsetOf(range)); - - assertTrue("[0..10[ is not subset of [5..15[", !Range.between(0, 10) - .isSubsetOf(Range.between(5, 15))); - } - - @Test - public void offsetTest() { - assertEquals(Range.between(5, 15), Range.between(0, 10).offsetBy(5)); - } - - @Test - public void rangeStartsBeforeTest() { - final Range former = Range.between(0, 5); - final Range latter = Range.between(1, 5); - assertTrue("former should starts before latter", - former.startsBefore(latter)); - assertTrue("latter shouldn't start before latter", - !latter.startsBefore(former)); - - assertTrue("no overlap allowed", - !Range.between(0, 5).startsBefore(Range.between(0, 10))); - } - - @Test - public void rangeStartsAfterTest() { - final Range former = Range.between(0, 5); - final Range latter = Range.between(5, 10); - assertTrue("latter should start after former", - latter.startsAfter(former)); - assertTrue("former shouldn't start after latter", - !former.startsAfter(latter)); - - assertTrue("no overlap allowed", - !Range.between(5, 10).startsAfter(Range.between(0, 6))); - } - - @Test - public void rangeEndsBeforeTest() { - final Range former = Range.between(0, 5); - final Range latter = Range.between(5, 10); - assertTrue("latter should end before former", former.endsBefore(latter)); - assertTrue("former shouldn't end before latter", - !latter.endsBefore(former)); - - assertTrue("no overlap allowed", - !Range.between(5, 10).endsBefore(Range.between(9, 15))); - } - - @Test - public void rangeEndsAfterTest() { - final Range former = Range.between(1, 5); - final Range latter = Range.between(1, 6); - assertTrue("latter should end after former", latter.endsAfter(former)); - assertTrue("former shouldn't end after latter", - !former.endsAfter(latter)); - - assertTrue("no overlap allowed", - !Range.between(0, 10).endsAfter(Range.between(5, 10))); - } - - @Test(expected = IllegalArgumentException.class) - public void combine_notOverlappingFirstSmaller() { - Range.between(0, 10).combineWith(Range.between(11, 20)); - } - - @Test(expected = IllegalArgumentException.class) - public void combine_notOverlappingSecondLarger() { - Range.between(11, 20).combineWith(Range.between(0, 10)); - } - - @Test(expected = IllegalArgumentException.class) - public void combine_firstEmptyNotOverlapping() { - Range.between(15, 15).combineWith(Range.between(0, 10)); - } - - @Test(expected = IllegalArgumentException.class) - public void combine_secondEmptyNotOverlapping() { - Range.between(0, 10).combineWith(Range.between(15, 15)); - } - - @Test - public void combine_barelyOverlapping() { - Range r1 = Range.between(0, 10); - Range r2 = Range.between(10, 20); - - // Test both ways, should give the same result - Range combined1 = r1.combineWith(r2); - Range combined2 = r2.combineWith(r1); - assertEquals(combined1, combined2); - - assertEquals(0, combined1.getStart()); - assertEquals(20, combined1.getEnd()); - } - - @Test - public void combine_subRange() { - Range r1 = Range.between(0, 10); - Range r2 = Range.between(2, 8); - - // Test both ways, should give the same result - Range combined1 = r1.combineWith(r2); - Range combined2 = r2.combineWith(r1); - assertEquals(combined1, combined2); - - assertEquals(r1, combined1); - } - - @Test - public void combine_intersecting() { - Range r1 = Range.between(0, 10); - Range r2 = Range.between(5, 15); - - // Test both ways, should give the same result - Range combined1 = r1.combineWith(r2); - Range combined2 = r2.combineWith(r1); - assertEquals(combined1, combined2); - - assertEquals(0, combined1.getStart()); - assertEquals(15, combined1.getEnd()); - - } - - @Test - public void combine_emptyInside() { - Range r1 = Range.between(0, 10); - Range r2 = Range.between(5, 5); - - // Test both ways, should give the same result - Range combined1 = r1.combineWith(r2); - Range combined2 = r2.combineWith(r1); - assertEquals(combined1, combined2); - - assertEquals(r1, combined1); - } - -} diff --git a/uitest/src/com/vaadin/tests/components/grid/BasicEscalator.html b/uitest/src/com/vaadin/tests/components/grid/BasicEscalator.html deleted file mode 100644 index 70aa0fe195..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/BasicEscalator.html +++ /dev/null @@ -1,176 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head profile="http://selenium-ide.openqa.org/profiles/test-case"> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -<link rel="selenium.base" href="http://localhost:8888/" /> -<title>BasicEscalator</title> -</head> -<body> -<table cellpadding="1" cellspacing="1" border="1"> -<thead> -<tr><td rowspan="1" colspan="3">BasicEscalator</td></tr> -</thead><tbody> -<tr> - <td>open</td> - <td>/run/com.vaadin.tests.components.grid.BasicEscalator?restartApplication</td> - <td></td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td> - <td>Row 0: 0,0 (0)</td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[17]/domChild[9]</td> - <td>Cell: 9,17 (179)</td> -</tr> -<tr> - <td>verifyTextNotPresent</td> - <td>Cell: 0,100</td> - <td></td> -</tr> -<tr> - <td>verifyTextNotPresent</td> - <td>Cell: 0,101</td> - <td></td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VTextField[0]</td> - <td>0</td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[1]/VTextField[0]</td> - <td>1</td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[18]/domChild[0]</td> - <td>Row 0: 0,100 (190)</td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VTextField[0]</td> - <td>11</td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[17]/domChild[0]</td> - <td>Row 11: 0,101 (200)</td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VTextField[0]</td> - <td>0</td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[1]/VTextField[0]</td> - <td>100</td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[16]/domChild[0]</td> - <td>Row 0: 0,102 (210)</td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[0]</td> - <td>Row 16: 0,118 (370)</td> -</tr> -<tr> - <td>scroll</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[0]</td> - <td>1109</td> -</tr> -<tr> - <td>verifyTextPresent</td> - <td>Row 56: 0,158</td> - <td></td> -</tr> -<tr> - <td>verifyTextPresent</td> - <td>Row 72: 0,174</td> - <td></td> -</tr> -<tr> - <td>scroll</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[0]</td> - <td>3690</td> -</tr> -<tr> - <td>verifyTextPresent</td> - <td>Row 201: 0,99</td> - <td></td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VHorizontalLayout[0]/Slot[0]/VTextField[0]</td> - <td>201</td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VHorizontalLayout[0]/Slot[1]/VTextField[0]</td> - <td>1</td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VHorizontalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[0]</td> - <td>Row 200: 0,98 (960)</td> -</tr> -<tr> - <td>verifyTextNotPresent</td> - <td>Row 201:</td> - <td></td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VHorizontalLayout[0]/Slot[0]/VTextField[0]</td> - <td>0</td> -</tr> -<tr> - <td>type</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VHorizontalLayout[0]/Slot[1]/VTextField[0]</td> - <td>2</td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VHorizontalLayout[0]/Slot[2]/VButton[0]</td> - <td></td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[16]/domChild[0]</td> - <td>Row 184: 10,82 (974)</td> -</tr> -<tr> - <td>verifyText</td> - <td>vaadin=runcomvaadintestscomponentsgridBasicEscalator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[0]</td> - <td>Row 200: 10,98 (1006)</td> -</tr> -</tbody></table> -</body> -</html> diff --git a/uitest/src/com/vaadin/tests/components/grid/BasicEscalator.java b/uitest/src/com/vaadin/tests/components/grid/BasicEscalator.java deleted file mode 100644 index 61d263f433..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/BasicEscalator.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.components.grid; - -import java.util.Random; - -import com.vaadin.annotations.Widgetset; -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.tests.widgetset.TestingWidgetSet; -import com.vaadin.tests.widgetset.server.grid.TestGrid; -import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.Layout; -import com.vaadin.ui.NativeSelect; -import com.vaadin.ui.TextField; - -/** - * @since 7.2 - * @author Vaadin Ltd - */ -@Widgetset(TestingWidgetSet.NAME) -public class BasicEscalator extends AbstractTestUI { - public static final String ESCALATOR = "escalator"; - public static final String INSERT_ROWS_OFFSET = "iro"; - public static final String INSERT_ROWS_AMOUNT = "ira"; - public static final String INSERT_ROWS_BUTTON = "irb"; - - private final Random random = new Random(); - - @Override - protected void setup(final VaadinRequest request) { - final TestGrid grid = new TestGrid(); - grid.setId(ESCALATOR); - addComponent(grid); - - final Layout insertRowsLayout = new HorizontalLayout(); - final TextField insertRowsOffset = new TextField(); - insertRowsOffset.setId(INSERT_ROWS_OFFSET); - insertRowsLayout.addComponent(insertRowsOffset); - final TextField insertRowsAmount = new TextField(); - insertRowsAmount.setId(INSERT_ROWS_AMOUNT); - insertRowsLayout.addComponent(insertRowsAmount); - insertRowsLayout.addComponent(new Button("insert rows", - new Button.ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - final int offset = Integer.parseInt(insertRowsOffset - .getValue()); - final int amount = Integer.parseInt(insertRowsAmount - .getValue()); - grid.insertRows(offset, amount); - } - }) { - { - setId(INSERT_ROWS_BUTTON); - } - }); - addComponent(insertRowsLayout); - - final Layout removeRowsLayout = new HorizontalLayout(); - final TextField removeRowsOffset = new TextField(); - removeRowsLayout.addComponent(removeRowsOffset); - final TextField removeRowsAmount = new TextField(); - removeRowsLayout.addComponent(removeRowsAmount); - removeRowsLayout.addComponent(new Button("remove rows", - new Button.ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - final int offset = Integer.parseInt(removeRowsOffset - .getValue()); - final int amount = Integer.parseInt(removeRowsAmount - .getValue()); - grid.removeRows(offset, amount); - } - })); - addComponent(removeRowsLayout); - - final Layout insertColumnsLayout = new HorizontalLayout(); - final TextField insertColumnsOffset = new TextField(); - insertColumnsLayout.addComponent(insertColumnsOffset); - final TextField insertColumnsAmount = new TextField(); - insertColumnsLayout.addComponent(insertColumnsAmount); - insertColumnsLayout.addComponent(new Button("insert columns", - new Button.ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - final int offset = Integer.parseInt(insertColumnsOffset - .getValue()); - final int amount = Integer.parseInt(insertColumnsAmount - .getValue()); - grid.insertColumns(offset, amount); - } - })); - addComponent(insertColumnsLayout); - - final Layout removeColumnsLayout = new HorizontalLayout(); - final TextField removeColumnsOffset = new TextField(); - removeColumnsLayout.addComponent(removeColumnsOffset); - final TextField removeColumnsAmount = new TextField(); - removeColumnsLayout.addComponent(removeColumnsAmount); - removeColumnsLayout.addComponent(new Button("remove columns", - new Button.ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - final int offset = Integer.parseInt(removeColumnsOffset - .getValue()); - final int amount = Integer.parseInt(removeColumnsAmount - .getValue()); - grid.removeColumns(offset, amount); - } - })); - addComponent(removeColumnsLayout); - - final HorizontalLayout rowScroll = new HorizontalLayout(); - final NativeSelect destination = new NativeSelect(); - destination.setNullSelectionAllowed(false); - destination.addItem("any"); - destination.setValue("any"); - destination.addItem("start"); - destination.addItem("end"); - destination.addItem("middle"); - rowScroll.addComponent(destination); - final TextField rowIndex = new TextField(); - rowScroll.addComponent(rowIndex); - final TextField rowPadding = new TextField(); - rowScroll.addComponent(rowPadding); - rowScroll.addComponent(new Button("scroll to row", - new Button.ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - int index; - try { - index = Integer.parseInt(rowIndex.getValue()); - } catch (NumberFormatException e) { - index = 0; - } - - int padding; - try { - padding = Integer.parseInt(rowPadding.getValue()); - } catch (NumberFormatException e) { - padding = 0; - } - - grid.scrollToRow(index, - (String) destination.getValue(), padding); - } - })); - addComponent(rowScroll); - - final HorizontalLayout colScroll = new HorizontalLayout(); - final NativeSelect colDestination = new NativeSelect(); - colDestination.setNullSelectionAllowed(false); - colDestination.addItem("any"); - colDestination.setValue("any"); - colDestination.addItem("start"); - colDestination.addItem("end"); - colDestination.addItem("middle"); - colScroll.addComponent(colDestination); - final TextField colIndex = new TextField(); - colScroll.addComponent(colIndex); - final TextField colPadding = new TextField(); - colScroll.addComponent(colPadding); - colScroll.addComponent(new Button("scroll to column", - new Button.ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - int index; - try { - index = Integer.parseInt(colIndex.getValue()); - } catch (NumberFormatException e) { - index = 0; - } - - int padding; - try { - padding = Integer.parseInt(colPadding.getValue()); - } catch (NumberFormatException e) { - padding = 0; - } - - grid.scrollToColumn(index, - (String) colDestination.getValue(), padding); - } - })); - addComponent(colScroll); - - final TextField freezeCount = new TextField(); - freezeCount.setConverter(Integer.class); - freezeCount.setNullRepresentation(""); - addComponent(new HorizontalLayout(freezeCount, new Button( - "set frozen columns", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - grid.setFrozenColumns(((Integer) freezeCount - .getConvertedValue()).intValue()); - freezeCount.setValue(null); - } - }))); - - addComponent(new Button("Resize randomly", new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - int width = random.nextInt(300) + 500; - int height = random.nextInt(300) + 200; - grid.setWidth(width + "px"); - grid.setHeight(height + "px"); - } - })); - - addComponent(new Button("Random headers count", - new Button.ClickListener() { - private int headers = 1; - - @Override - public void buttonClick(ClickEvent event) { - int diff = 0; - while (diff == 0) { - final int nextHeaders = random.nextInt(4); - diff = nextHeaders - headers; - headers = nextHeaders; - } - if (diff > 0) { - grid.insertHeaders(0, diff); - } else if (diff < 0) { - grid.removeHeaders(0, -diff); - } - } - })); - - addComponent(new Button("Random footers count", - new Button.ClickListener() { - private int footers = 1; - - @Override - public void buttonClick(ClickEvent event) { - int diff = 0; - while (diff == 0) { - final int nextFooters = random.nextInt(4); - diff = nextFooters - footers; - footers = nextFooters; - } - if (diff > 0) { - grid.insertFooters(0, diff); - } else if (diff < 0) { - grid.removeFooters(0, -diff); - } - } - })); - - final Layout resizeColumnsLayout = new HorizontalLayout(); - final TextField resizeColumnIndex = new TextField(); - resizeColumnsLayout.addComponent(resizeColumnIndex); - final TextField resizeColumnPx = new TextField(); - resizeColumnsLayout.addComponent(resizeColumnPx); - resizeColumnsLayout.addComponent(new Button("resize column", - new Button.ClickListener() { - @Override - public void buttonClick(final ClickEvent event) { - final int index = Integer.parseInt(resizeColumnIndex - .getValue()); - final int px = Integer.parseInt(resizeColumnPx - .getValue()); - grid.setColumnWidth(index, px); - } - })); - addComponent(resizeColumnsLayout); - - addComponent(new Button("Autoresize columns", - new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - grid.calculateColumnWidths(); - } - })); - - addComponent(new Button("Randomize row heights", - new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - grid.randomizeDefaultRowHeight(); - } - })); - } - - @Override - protected String getTestDescription() { - return null; - } - - @Override - protected Integer getTicketNumber() { - return null; - } - -} diff --git a/uitest/src/com/vaadin/tests/components/grid/BasicEscalatorTest.java b/uitest/src/com/vaadin/tests/components/grid/BasicEscalatorTest.java deleted file mode 100644 index 5afe826196..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/BasicEscalatorTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.components.grid; - -import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; - -import com.vaadin.tests.tb3.MultiBrowserTest; - -public class BasicEscalatorTest extends MultiBrowserTest { - - @Test - public void testNormalHeight() throws Exception { - openTestURL(); - compareScreen("normalHeight"); - } - - @Test - public void testModifiedHeight() throws Exception { - openTestURLWithTheme("reindeer-tests"); - compareScreen("modifiedHeight"); - } - - private WebElement getFirstBodyRowCell() { - return getDriver().findElement( - By.xpath("//tbody/tr[@class='v-escalator-row'][1]/td[1]")); - } - - private void openTestURLWithTheme(String themeName) { - String testUrl = getTestUrl(); - testUrl += (testUrl.contains("?")) ? "&" : "?"; - testUrl += "theme=" + themeName; - getDriver().get(testUrl); - } -} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java deleted file mode 100644 index c28feb8d10..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.components.grid; - -import java.util.ArrayList; -import java.util.LinkedHashMap; - -import com.vaadin.data.Item; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.tests.components.AbstractComponentTest; -import com.vaadin.ui.components.grid.ColumnGroup; -import com.vaadin.ui.components.grid.ColumnGroupRow; -import com.vaadin.ui.components.grid.Grid; -import com.vaadin.ui.components.grid.GridColumn; - -/** - * Tests the basic features like columns, footers and headers - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class GridBasicFeatures extends AbstractComponentTest<Grid> { - - private final int COLUMNS = 10; - - private int columnGroupRows = 0; - - private final int ROWS = 1000; - - private IndexedContainer ds; - - @Override - protected Grid constructComponent() { - - // Build data source - ds = new IndexedContainer(); - - for (int col = 0; col < COLUMNS; col++) { - ds.addContainerProperty(getColumnProperty(col), String.class, ""); - } - - for (int row = 0; row < ROWS; row++) { - Item item = ds.addItem(Integer.valueOf(row)); - for (int col = 0; col < COLUMNS; col++) { - item.getItemProperty(getColumnProperty(col)).setValue( - "(" + row + ", " + col + ")"); - } - } - - // Create grid - Grid grid = new Grid(ds); - - // Add footer values (header values are automatically created) - for (int col = 0; col < COLUMNS; col++) { - grid.getColumn(getColumnProperty(col)).setFooterCaption( - "Footer " + col); - } - - // Set varying column widths - for (int col = 0; col < COLUMNS; col++) { - grid.getColumn("Column" + col).setWidth(100 + col * 50); - } - - createGridActions(); - - createColumnActions(); - - createHeaderActions(); - - createFooterActions(); - - createColumnGroupActions(); - - createRowActions(); - - return grid; - } - - protected void createGridActions() { - LinkedHashMap<String, String> primaryStyleNames = new LinkedHashMap<String, String>(); - primaryStyleNames.put("v-grid", "v-grid"); - primaryStyleNames.put("v-escalator", "v-escalator"); - primaryStyleNames.put("my-grid", "my-grid"); - - createMultiClickAction("Primary style name", "State", - primaryStyleNames, new Command<Grid, String>() { - - @Override - public void execute(Grid grid, String value, Object data) { - grid.setPrimaryStyleName(value); - - } - }, primaryStyleNames.get("v-grid")); - } - - protected void createHeaderActions() { - createCategory("Headers", null); - - createBooleanAction("Visible", "Headers", true, - new Command<Grid, Boolean>() { - - @Override - public void execute(Grid grid, Boolean value, Object data) { - grid.setColumnHeadersVisible(value); - } - }); - } - - protected void createFooterActions() { - createCategory("Footers", null); - - createBooleanAction("Visible", "Footers", false, - new Command<Grid, Boolean>() { - - @Override - public void execute(Grid grid, Boolean value, Object data) { - grid.setColumnFootersVisible(value); - } - }); - } - - protected void createColumnActions() { - createCategory("Columns", null); - - for (int c = 0; c < COLUMNS; c++) { - createCategory(getColumnProperty(c), "Columns"); - - createBooleanAction("Visible", getColumnProperty(c), true, - new Command<Grid, Boolean>() { - - @Override - public void execute(Grid grid, Boolean value, - Object columnIndex) { - Object propertyId = (new ArrayList(grid - .getContainerDatasource() - .getContainerPropertyIds()) - .get((Integer) columnIndex)); - GridColumn column = grid.getColumn(propertyId); - column.setVisible(!column.isVisible()); - } - }, c); - - createClickAction("Remove", getColumnProperty(c), - new Command<Grid, String>() { - - @Override - public void execute(Grid grid, String value, Object data) { - grid.getContainerDatasource() - .removeContainerProperty("Column" + data); - } - }, null, c); - - createClickAction("Freeze", getColumnProperty(c), - new Command<Grid, String>() { - - @Override - public void execute(Grid grid, String value, Object data) { - grid.setLastFrozenPropertyId("Column" + data); - } - }, null, c); - - createCategory("Column" + c + " Width", getColumnProperty(c)); - - createClickAction("Auto", "Column" + c + " Width", - new Command<Grid, Integer>() { - - @Override - public void execute(Grid grid, Integer value, - Object columnIndex) { - Object propertyId = (new ArrayList(grid - .getContainerDatasource() - .getContainerPropertyIds()) - .get((Integer) columnIndex)); - GridColumn column = grid.getColumn(propertyId); - column.setWidthUndefined(); - } - }, -1, c); - - for (int w = 50; w < 300; w += 50) { - createClickAction(w + "px", "Column" + c + " Width", - new Command<Grid, Integer>() { - - @Override - public void execute(Grid grid, Integer value, - Object columnIndex) { - Object propertyId = (new ArrayList(grid - .getContainerDatasource() - .getContainerPropertyIds()) - .get((Integer) columnIndex)); - GridColumn column = grid.getColumn(propertyId); - column.setWidth(value); - } - }, w, c); - } - } - } - - private static String getColumnProperty(int c) { - return "Column" + c; - } - - protected void createColumnGroupActions() { - createCategory("Column groups", null); - - createClickAction("Add group row", "Column groups", - new Command<Grid, String>() { - - @Override - public void execute(Grid grid, String value, Object data) { - final ColumnGroupRow row = grid.addColumnGroupRow(); - columnGroupRows++; - createCategory("Column group row " + columnGroupRows, - "Column groups"); - - createBooleanAction("Header Visible", - "Column group row " + columnGroupRows, true, - new Command<Grid, Boolean>() { - - @Override - public void execute(Grid grid, - Boolean value, Object columnIndex) { - row.setHeaderVisible(value); - } - }, row); - - createBooleanAction("Footer Visible", - "Column group row " + columnGroupRows, false, - new Command<Grid, Boolean>() { - - @Override - public void execute(Grid grid, - Boolean value, Object columnIndex) { - row.setFooterVisible(value); - } - }, row); - - for (int i = 0; i < COLUMNS; i++) { - final int columnIndex = i; - createClickAction("Group Column " + columnIndex - + " & " + (columnIndex + 1), - "Column group row " + columnGroupRows, - new Command<Grid, Integer>() { - - @Override - public void execute(Grid c, - Integer value, Object data) { - final ColumnGroup group = row - .addGroup( - "Column" + value, - "Column" - + (value + 1)); - - group.setHeaderCaption("Column " - + value + " & " - + (value + 1)); - - group.setFooterCaption("Column " - + value + " & " - + (value + 1)); - } - }, i, row); - } - } - }, null, null); - - } - - protected void createRowActions() { - createCategory("Body rows", null); - - createClickAction("Add first row", "Body rows", - new Command<Grid, String>() { - @Override - public void execute(Grid c, String value, Object data) { - Item item = ds.addItemAt(0, new Object()); - for (int i = 0; i < COLUMNS; i++) { - item.getItemProperty(getColumnProperty(i)) - .setValue("newcell: " + i); - } - } - }, null); - - createClickAction("Remove first row", "Body rows", - new Command<Grid, String>() { - @Override - public void execute(Grid c, String value, Object data) { - Object firstItemId = ds.getIdByIndex(0); - ds.removeItem(firstItemId); - } - }, null); - - createClickAction("Modify first row (getItemProperty)", "Body rows", - new Command<Grid, String>() { - @Override - public void execute(Grid c, String value, Object data) { - Object firstItemId = ds.getIdByIndex(0); - Item item = ds.getItem(firstItemId); - for (int i = 0; i < COLUMNS; i++) { - item.getItemProperty(getColumnProperty(i)) - .setValue("modified: " + i); - } - } - }, null); - - createClickAction("Modify first row (getContainerProperty)", - "Body rows", new Command<Grid, String>() { - @Override - public void execute(Grid c, String value, Object data) { - Object firstItemId = ds.getIdByIndex(0); - for (Object containerPropertyId : ds - .getContainerPropertyIds()) { - ds.getContainerProperty(firstItemId, - containerPropertyId).setValue( - "modified: " + containerPropertyId); - } - } - }, null); - } - - @Override - protected Integer getTicketNumber() { - return 12829; - } - - @Override - protected Class<Grid> getTestClass() { - return Grid.class; - } - -} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java deleted file mode 100644 index bc43f2be98..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.components.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.interactions.Actions; - -import com.vaadin.tests.tb3.MultiBrowserTest; - -/** - * - * @since - * @author Vaadin Ltd - */ -public class GridBasicFeaturesTest extends MultiBrowserTest { - - @Test - public void testColumnHeaderCaptions() throws Exception { - openTestURL(); - - // Column headers should be visible - List<WebElement> cells = getGridHeaderRowCells(); - assertEquals(10, cells.size()); - assertEquals("Column0", cells.get(0).getText()); - assertEquals("Column1", cells.get(1).getText()); - assertEquals("Column2", cells.get(2).getText()); - } - - @Test - public void testColumnFooterCaptions() throws Exception { - openTestURL(); - - // footer row should by default be hidden - List<WebElement> cells = getGridFooterRowCells(); - assertEquals(0, cells.size()); - - // Open footer row - selectMenuPath("Component", "Footers", "Visible"); - - // Footers should now be visible - cells = getGridFooterRowCells(); - assertEquals(10, cells.size()); - assertEquals("Footer 0", cells.get(0).getText()); - assertEquals("Footer 1", cells.get(1).getText()); - assertEquals("Footer 2", cells.get(2).getText()); - } - - @Test - public void testColumnGroupHeaders() throws Exception { - openTestURL(); - - // Hide column headers for this test - selectMenuPath("Component", "Headers", "Visible"); - - List<WebElement> cells = getGridHeaderRowCells(); - - // header row should be empty - assertEquals(0, cells.size()); - - // add a group row - selectMenuPath("Component", "Column groups", "Add group row"); - - // Empty group row cells should be present - cells = getGridHeaderRowCells(); - assertEquals(10, cells.size()); - - // Group columns 0 & 1 - selectMenuPath("Component", "Column groups", "Column group row 1", - "Group Column 0 & 1"); - - cells = getGridHeaderRowCells(); - assertEquals("Column 0 & 1", cells.get(0).getText()); - } - - @Test - public void testColumnGroupFooters() throws Exception { - openTestURL(); - - // add a group row - selectMenuPath("Component", "Column groups", "Add group row"); - - // Set footer visible - selectMenuPath("Component", "Column groups", "Column group row 1", - "Footer Visible"); - - // Group columns 0 & 1 - selectMenuPath("Component", "Column groups", "Column group row 1", - "Group Column 0 & 1"); - - List<WebElement> cells = getGridFooterRowCells(); - assertEquals("Column 0 & 1", cells.get(0).getText()); - } - - @Test - public void testGroupingSameColumnsOnRowThrowsException() throws Exception { - openTestURL(); - - // add a group row - selectMenuPath("Component", "Column groups", "Add group row"); - - // Group columns 0 & 1 - selectMenuPath("Component", "Column groups", "Column group row 1", - "Group Column 0 & 1"); - - // Group columns 1 & 2 shoud fail - selectMenuPath("Component", "Column groups", "Column group row 1", - "Group Column 1 & 2"); - - assertTrue(getLogRow(0) - .contains( - "Exception occured, java.lang.IllegalArgumentExceptionColumn Column1 already belongs to another group.")); - } - - @Test - public void testHidingColumn() throws Exception { - openTestURL(); - - // Column 0 should be visible - List<WebElement> cells = getGridHeaderRowCells(); - assertEquals("Column0", cells.get(0).getText()); - - // Hide column 0 - selectMenuPath("Component", "Columns", "Column0", "Visible"); - - // Column 1 should now be the first cell - cells = getGridHeaderRowCells(); - assertEquals("Column1", cells.get(0).getText()); - } - - @Test - public void testRemovingColumn() throws Exception { - openTestURL(); - - // Column 0 should be visible - List<WebElement> cells = getGridHeaderRowCells(); - assertEquals("Column0", cells.get(0).getText()); - - // Hide column 0 - selectMenuPath("Component", "Columns", "Column0", "Remove"); - - // Column 1 should now be the first cell - cells = getGridHeaderRowCells(); - assertEquals("Column1", cells.get(0).getText()); - } - - @Test - public void testFreezingColumn() throws Exception { - openTestURL(); - - // Freeze column 2 - selectMenuPath("Component", "Columns", "Column2", "Freeze"); - - WebElement cell = getBodyCellByRowAndColumn(1, 1); - assertTrue(cell.getAttribute("class").contains("frozen")); - - cell = getBodyCellByRowAndColumn(1, 2); - assertTrue(cell.getAttribute("class").contains("frozen")); - } - - @Test - public void testInitialColumnWidths() throws Exception { - openTestURL(); - - // Default borders and margins implemented by escalator - int cellBorder = 1 + 1; - int cellMargin = 2 + 2; - - WebElement cell = getBodyCellByRowAndColumn(1, 1); - assertEquals((100 - cellBorder - cellMargin) + "px", - cell.getCssValue("width")); - - cell = getBodyCellByRowAndColumn(1, 2); - assertEquals((150 - cellBorder - cellMargin) + "px", - cell.getCssValue("width")); - - cell = getBodyCellByRowAndColumn(1, 3); - assertEquals((200 - cellBorder - cellMargin) + "px", - cell.getCssValue("width")); - } - - @Test - public void testColumnWidths() throws Exception { - openTestURL(); - - // Default borders and margins implemented by escalator - int cellBorder = 1 + 1; - int cellMargin = 2 + 2; - - // Default column width is 100px - WebElement cell = getBodyCellByRowAndColumn(1, 1); - assertEquals((100 - cellBorder - cellMargin) + "px", - cell.getCssValue("width")); - - // Set first column to be 200px wide - selectMenuPath("Component", "Columns", "Column0", "Column0 Width", - "200px"); - - cell = getBodyCellByRowAndColumn(1, 1); - assertEquals((200 - cellBorder - cellMargin) + "px", - cell.getCssValue("width")); - - // Set second column to be 150px wide - selectMenuPath("Component", "Columns", "Column1", "Column1 Width", - "150px"); - cell = getBodyCellByRowAndColumn(1, 2); - assertEquals((150 - cellBorder - cellMargin) + "px", - cell.getCssValue("width")); - - // Set first column to be auto sized (defaults to 100px currently) - selectMenuPath("Component", "Columns", "Column0", "Column0 Width", - "Auto"); - - cell = getBodyCellByRowAndColumn(1, 1); - assertEquals((100 - cellBorder - cellMargin) + "px", - cell.getCssValue("width")); - } - - @Test - public void testPrimaryStyleNames() throws Exception { - openTestURL(); - - // v-grid is default primary style namea - assertPrimaryStylename("v-grid"); - - selectMenuPath("Component", "State", "Primary style name", - "v-escalator"); - assertPrimaryStylename("v-escalator"); - - selectMenuPath("Component", "State", "Primary style name", "my-grid"); - assertPrimaryStylename("my-grid"); - - selectMenuPath("Component", "State", "Primary style name", "v-grid"); - assertPrimaryStylename("v-grid"); - } - - /** - * Test that the current view is updated when a server-side container change - * occurs (without scrolling back and forth) - */ - @Test - public void testItemSetChangeEvent() throws Exception { - openTestURL(); - - final By newRow = By.xpath("//td[text()='newcell: 0']"); - - assertTrue("Unexpected initial state", !elementIsFound(newRow)); - - selectMenuPath("Component", "Body rows", "Add first row"); - assertTrue("Add row failed", elementIsFound(newRow)); - - selectMenuPath("Component", "Body rows", "Remove first row"); - assertTrue("Remove row failed", !elementIsFound(newRow)); - } - - /** - * Test that the current view is updated when a property's value is reflect - * to the client, when the value is modified server-side. - */ - @Test - public void testPropertyValueChangeEvent() throws Exception { - openTestURL(); - - assertEquals("Unexpected cell initial state", "(0, 0)", - getBodyCellByRowAndColumn(1, 1).getText()); - - selectMenuPath("Component", "Body rows", - "Modify first row (getItemProperty)"); - assertEquals("(First) modification with getItemProperty failed", - "modified: 0", getBodyCellByRowAndColumn(1, 1).getText()); - - selectMenuPath("Component", "Body rows", - "Modify first row (getContainerProperty)"); - assertEquals("(Second) modification with getItemProperty failed", - "modified: Column0", getBodyCellByRowAndColumn(1, 1).getText()); - } - - private boolean elementIsFound(By locator) { - try { - return driver.findElement(locator) != null; - } catch (NoSuchElementException e) { - return false; - } - } - - private void assertPrimaryStylename(String stylename) { - assertTrue(getGridElement().getAttribute("class").contains(stylename)); - - String tableWrapperStyleName = getTableWrapper().getAttribute("class"); - assertTrue(tableWrapperStyleName.contains(stylename + "-tablewrapper")); - - String hscrollStyleName = getHorizontalScroller().getAttribute("class"); - assertTrue(hscrollStyleName.contains(stylename + "-scroller")); - assertTrue(hscrollStyleName - .contains(stylename + "-scroller-horizontal")); - - String vscrollStyleName = getVerticalScroller().getAttribute("class"); - assertTrue(vscrollStyleName.contains(stylename + "-scroller")); - assertTrue(vscrollStyleName.contains(stylename + "-scroller-vertical")); - } - - private WebElement getBodyCellByRowAndColumn(int row, int column) { - return getDriver().findElement( - By.xpath("//div[@id='testComponent']//tbody/tr[" + row - + "]/td[" + column + "]")); - } - - private void selectSubMenu(String menuCaption) { - selectMenu(menuCaption); - new Actions(getDriver()).moveByOffset(100, 0).build().perform(); - } - - private void selectMenu(String menuCaption) { - getDriver().findElement( - By.xpath("//span[text() = '" + menuCaption + "']")).click(); - } - - private void selectMenuPath(String... menuCaptions) { - selectMenu(menuCaptions[0]); - for (int i = 1; i < menuCaptions.length; i++) { - selectSubMenu(menuCaptions[i]); - } - } - - private WebElement getVerticalScroller() { - return getDriver().findElement( - By.xpath("//div[@id='testComponent']/div[1]")); - } - - private WebElement getHorizontalScroller() { - return getDriver().findElement( - By.xpath("//div[@id='testComponent']/div[2]")); - } - - private WebElement getTableWrapper() { - return getDriver().findElement( - By.xpath("//div[@id='testComponent']/div[3]")); - } - - private WebElement getGridElement() { - return getDriver().findElement(By.id("testComponent")); - } - - private List<WebElement> getGridHeaderRowCells() { - return getDriver().findElements( - By.xpath("//div[@id='testComponent']//thead//th")); - } - - private List<WebElement> getGridFooterRowCells() { - return getDriver().findElements( - By.xpath("//div[@id='testComponent']//tfoot//td")); - } -} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java b/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java deleted file mode 100644 index 66e7651f76..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.components.grid; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.ui.components.grid.ColumnGroup; -import com.vaadin.ui.components.grid.ColumnGroupRow; -import com.vaadin.ui.components.grid.Grid; -import com.vaadin.ui.components.grid.GridColumn; - -/** - * - * @since - * @author Vaadin Ltd - */ -public class GridColumnGroups extends AbstractTestUI { - - private final int COLUMNS = 4; - - @Override - protected void setup(VaadinRequest request) { - - // Setup grid - IndexedContainer ds = new IndexedContainer(); - for (int col = 0; col < COLUMNS; col++) { - ds.addContainerProperty("Column" + col, String.class, ""); - } - Grid grid = new Grid(ds); - addComponent(grid); - - /*- - * --------------------------------------------- - * | Header 1 | <- Auxiliary row 2 - * |-------------------------------------------| - * | Header 2 | Header 3 | <- Auxiliary row 1 - * |-------------------------------------------| - * | Column 1 | Column 2 | Column 3 | Column 4 | <- Column headers - * --------------------------------------------| - * | ... | ... | ... | ... | - * | ... | ... | ... | ... | - * --------------------------------------------| - * | Column 1 | Column 2 | Column 3 | Column 4 | <- Column footers - * --------------------------------------------| - * | Footer 2 | Footer 3 | <- Auxiliary row 1 - * --------------------------------------------| - * | Footer 1 | <- Auxiliary row 2 - * --------------------------------------------- - -*/ - - // Set column footers (headers are generated automatically) - grid.setColumnFootersVisible(true); - for (Object propertyId : ds.getContainerPropertyIds()) { - GridColumn column = grid.getColumn(propertyId); - column.setFooterCaption(String.valueOf(propertyId)); - } - - // First auxiliary row - ColumnGroupRow auxRow1 = grid.addColumnGroupRow(); - - // Using property id to create a column group - ColumnGroup columns12 = auxRow1.addGroup("Column0", "Column1"); - columns12.setHeaderCaption("Header 2"); - columns12.setFooterCaption("Footer 2"); - - // Using grid columns to create a column group - GridColumn column3 = grid.getColumn("Column2"); - GridColumn column4 = grid.getColumn("Column3"); - ColumnGroup columns34 = auxRow1.addGroup(column3, column4); - columns34.setHeaderCaption("Header 3"); - columns34.setFooterCaption("Footer 3"); - - // Second auxiliary row - ColumnGroupRow auxRow2 = grid.addColumnGroupRow(); - - // Using previous groups to create a column group - ColumnGroup columns1234 = auxRow2.addGroup(columns12, columns34); - columns1234.setHeaderCaption("Header 1"); - columns1234.setFooterCaption("Footer 1"); - - } - - @Override - protected String getTestDescription() { - return "Grid should support headers and footer groups"; - } - - @Override - protected Integer getTicketNumber() { - return 12894; - } - -} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridScrolling.java b/uitest/src/com/vaadin/tests/components/grid/GridScrolling.java deleted file mode 100644 index d514fbd0c5..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/GridScrolling.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.components.grid; - -import com.vaadin.data.Item; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.VaadinRequest; -import com.vaadin.shared.ui.grid.ScrollDestination; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.ui.Button.ClickListener; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.VerticalLayout; -import com.vaadin.ui.components.grid.Grid; - -/** - * - */ -@SuppressWarnings("serial") -public class GridScrolling extends AbstractTestUI { - - private Grid grid; - - private IndexedContainer ds; - - @Override - @SuppressWarnings("unchecked") - protected void setup(VaadinRequest request) { - // Build data source - ds = new IndexedContainer(); - - for (int col = 0; col < 5; col++) { - ds.addContainerProperty("col" + col, String.class, ""); - } - - for (int row = 0; row < 65536; row++) { - Item item = ds.addItem(Integer.valueOf(row)); - for (int col = 0; col < 5; col++) { - item.getItemProperty("col" + col).setValue( - "(" + row + ", " + col + ")"); - } - } - - grid = new Grid(ds); - - HorizontalLayout hl = new HorizontalLayout(); - hl.addComponent(grid); - hl.setMargin(true); - hl.setSpacing(true); - - VerticalLayout vl = new VerticalLayout(); - vl.setSpacing(true); - - // Add scroll buttons - Button scrollUpButton = new Button("Top", new ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - grid.scrollToStart(); - } - }); - scrollUpButton.setSizeFull(); - vl.addComponent(scrollUpButton); - - for (int i = 1; i < 7; ++i) { - final int row = (ds.size() / 7) * i; - Button scrollButton = new Button("Scroll to row " + row, - new ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - grid.scrollToItem(Integer.valueOf(row), - ScrollDestination.MIDDLE); - } - }); - scrollButton.setSizeFull(); - vl.addComponent(scrollButton); - } - - Button scrollDownButton = new Button("Bottom", new ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - grid.scrollToEnd(); - } - }); - scrollDownButton.setSizeFull(); - vl.addComponent(scrollDownButton); - - hl.addComponent(vl); - addComponent(hl); - } - - @Override - protected String getTestDescription() { - return "Test Grid programmatic scrolling features"; - } - - @Override - protected Integer getTicketNumber() { - return 13327; - } - -} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridClientRpc.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridClientRpc.java deleted file mode 100644 index ae2799d228..0000000000 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridClientRpc.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.widgetset.client.grid; - -import com.vaadin.shared.communication.ClientRpc; - -public interface TestGridClientRpc extends ClientRpc { - void insertRows(int offset, int amount); - - void removeRows(int offset, int amount); - - void insertColumns(int offset, int amount); - - void removeColumns(int offset, int amount); - - void scrollToRow(int index, String destination, int padding); - - void scrollToColumn(int index, String destination, int padding); - - void setFrozenColumns(int frozenColumns); - - void insertHeaders(int index, int amount); - - void removeHeaders(int index, int amount); - - void insertFooters(int index, int amount); - - void removeFooters(int index, int amount); - - void setColumnWidth(int index, int px); - - void calculateColumnWidths(); - - void randomRowHeight(); -} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java deleted file mode 100644 index e2d88c57f2..0000000000 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.widgetset.client.grid; - -import com.google.gwt.user.client.Random; -import com.vaadin.client.ui.AbstractComponentConnector; -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.grid.ScrollDestination; -import com.vaadin.tests.widgetset.server.grid.TestGrid; - -/** - * @since 7.2 - * @author Vaadin Ltd - */ -@Connect(TestGrid.class) -public class TestGridConnector extends AbstractComponentConnector { - @Override - protected void init() { - super.init(); - registerRpc(TestGridClientRpc.class, new TestGridClientRpc() { - @Override - public void insertRows(int offset, int amount) { - getWidget().insertRows(offset, amount); - } - - @Override - public void removeRows(int offset, int amount) { - getWidget().removeRows(offset, amount); - } - - @Override - public void removeColumns(int offset, int amount) { - getWidget().removeColumns(offset, amount); - } - - @Override - public void insertColumns(int offset, int amount) { - getWidget().insertColumns(offset, amount); - } - - @Override - public void scrollToRow(int index, String destination, int padding) { - getWidget().scrollToRow(index, getDestination(destination), - padding); - } - - @Override - public void scrollToColumn(int index, String destination, - int padding) { - getWidget().scrollToColumn(index, getDestination(destination), - padding); - } - - private ScrollDestination getDestination(String destination) { - final ScrollDestination d; - if (destination.equals("start")) { - d = ScrollDestination.START; - } else if (destination.equals("middle")) { - d = ScrollDestination.MIDDLE; - } else if (destination.equals("end")) { - d = ScrollDestination.END; - } else { - d = ScrollDestination.ANY; - } - return d; - } - - @Override - public void setFrozenColumns(int frozenColumns) { - getWidget().getColumnConfiguration().setFrozenColumnCount( - frozenColumns); - } - - @Override - public void insertHeaders(int index, int amount) { - getWidget().getHeader().insertRows(index, amount); - } - - @Override - public void removeHeaders(int index, int amount) { - getWidget().getHeader().removeRows(index, amount); - } - - @Override - public void insertFooters(int index, int amount) { - getWidget().getFooter().insertRows(index, amount); - } - - @Override - public void removeFooters(int index, int amount) { - getWidget().getFooter().removeRows(index, amount); - } - - @Override - public void setColumnWidth(int index, int px) { - getWidget().getColumnConfiguration().setColumnWidth(index, px); - } - - @Override - public void calculateColumnWidths() { - getWidget().calculateColumnWidths(); - } - - @Override - public void randomRowHeight() { - getWidget().getHeader().setDefaultRowHeight( - Random.nextInt(20) + 20); - getWidget().getBody().setDefaultRowHeight( - Random.nextInt(20) + 20); - getWidget().getFooter().setDefaultRowHeight( - Random.nextInt(20) + 20); - } - }); - } - - @Override - public VTestGrid getWidget() { - return (VTestGrid) super.getWidget(); - } - - @Override - public TestGridState getState() { - return (TestGridState) super.getState(); - } -} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java deleted file mode 100644 index 73d6ba311c..0000000000 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.widgetset.client.grid; - -import com.vaadin.shared.AbstractComponentState; - -/** - * @since 7.2 - * @author Vaadin Ltd - */ -public class TestGridState extends AbstractComponentState { - public static final String DEFAULT_HEIGHT = "400.0px"; - - /* TODO: this should be "100%" before setting final. */ - public static final String DEFAULT_WIDTH = "800.0px"; -} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/VTestGrid.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/VTestGrid.java deleted file mode 100644 index 28e650edc1..0000000000 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/VTestGrid.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.vaadin.tests.widgetset.client.grid; - -import java.util.ArrayList; -import java.util.List; - -import com.google.gwt.user.client.ui.Composite; -import com.vaadin.client.ui.grid.Cell; -import com.vaadin.client.ui.grid.ColumnConfiguration; -import com.vaadin.client.ui.grid.Escalator; -import com.vaadin.client.ui.grid.EscalatorUpdater; -import com.vaadin.client.ui.grid.Row; -import com.vaadin.client.ui.grid.RowContainer; -import com.vaadin.shared.ui.grid.ScrollDestination; - -public class VTestGrid extends Composite { - - private static class Data { - private int columnCounter = 0; - private int rowCounter = 0; - private final List<Integer> columns = new ArrayList<Integer>(); - private final List<Integer> rows = new ArrayList<Integer>(); - - @SuppressWarnings("boxing") - public void insertRows(final int offset, final int amount) { - final List<Integer> newRows = new ArrayList<Integer>(); - for (int i = 0; i < amount; i++) { - newRows.add(rowCounter++); - } - rows.addAll(offset, newRows); - } - - @SuppressWarnings("boxing") - public void insertColumns(final int offset, final int amount) { - final List<Integer> newColumns = new ArrayList<Integer>(); - for (int i = 0; i < amount; i++) { - newColumns.add(columnCounter++); - } - columns.addAll(offset, newColumns); - } - - public EscalatorUpdater createHeaderUpdater() { - return new EscalatorUpdater() { - @Override - public void updateCells(final Row row, - final Iterable<Cell> cellsToUpdate) { - for (final Cell cell : cellsToUpdate) { - if (cell.getColumn() % 3 == 0) { - cell.setColSpan(2); - } - - final Integer columnName = columns - .get(cell.getColumn()); - cell.getElement().setInnerText("Header " + columnName); - } - } - }; - } - - public EscalatorUpdater createFooterUpdater() { - return new EscalatorUpdater() { - @Override - public void updateCells(final Row row, - final Iterable<Cell> cellsToUpdate) { - for (final Cell cell : cellsToUpdate) { - if (cell.getColumn() % 3 == 1) { - cell.setColSpan(2); - } - - final Integer columnName = columns - .get(cell.getColumn()); - cell.getElement().setInnerText("Footer " + columnName); - } - } - }; - } - - public EscalatorUpdater createBodyUpdater() { - return new EscalatorUpdater() { - private int i = 0; - - public void renderCell(final Cell cell) { - final Integer columnName = columns.get(cell.getColumn()); - final Integer rowName = rows.get(cell.getRow()); - final String cellInfo = columnName + "," + rowName + " (" - + i + ")"; - - if (cell.getColumn() > 0) { - cell.getElement().setInnerText("Cell: " + cellInfo); - } else { - cell.getElement().setInnerText( - "Row " + cell.getRow() + ": " + cellInfo); - } - - if (cell.getColumn() % 3 == cell.getRow() % 3) { - cell.setColSpan(3); - } - - final double c = i * .1; - final int r = (int) ((Math.cos(c) + 1) * 128); - final int g = (int) ((Math.cos(c / Math.PI) + 1) * 128); - final int b = (int) ((Math.cos(c / (Math.PI * 2)) + 1) * 128); - cell.getElement() - .getStyle() - .setBackgroundColor( - "rgb(" + r + "," + g + "," + b + ")"); - if ((r * .8 + g * 1.3 + b * .9) / 3 < 127) { - cell.getElement().getStyle().setColor("white"); - } else { - cell.getElement().getStyle().clearColor(); - } - - i++; - } - - @Override - public void updateCells(final Row row, - final Iterable<Cell> cellsToUpdate) { - for (final Cell cell : cellsToUpdate) { - renderCell(cell); - } - } - }; - } - - public void removeRows(final int offset, final int amount) { - for (int i = 0; i < amount; i++) { - rows.remove(offset); - } - } - - public void removeColumns(final int offset, final int amount) { - for (int i = 0; i < amount; i++) { - columns.remove(offset); - } - } - } - - private final Escalator escalator = new Escalator(); - private final Data data = new Data(); - - public VTestGrid() { - initWidget(escalator); - final RowContainer header = escalator.getHeader(); - header.setEscalatorUpdater(data.createHeaderUpdater()); - header.insertRows(0, 1); - - final RowContainer footer = escalator.getFooter(); - footer.setEscalatorUpdater(data.createFooterUpdater()); - footer.insertRows(0, 1); - - escalator.getBody().setEscalatorUpdater(data.createBodyUpdater()); - - insertRows(0, 100); - insertColumns(0, 10); - - setWidth(TestGridState.DEFAULT_WIDTH); - setHeight(TestGridState.DEFAULT_HEIGHT); - - } - - public void insertRows(final int offset, final int number) { - data.insertRows(offset, number); - escalator.getBody().insertRows(offset, number); - } - - public void insertColumns(final int offset, final int number) { - data.insertColumns(offset, number); - escalator.getColumnConfiguration().insertColumns(offset, number); - } - - public ColumnConfiguration getColumnConfiguration() { - return escalator.getColumnConfiguration(); - } - - public void scrollToRow(final int index, - final ScrollDestination destination, final int padding) { - escalator.scrollToRow(index, destination, padding); - } - - public void scrollToColumn(final int index, - final ScrollDestination destination, final int padding) { - escalator.scrollToColumn(index, destination, padding); - } - - public void removeRows(final int offset, final int amount) { - data.removeRows(offset, amount); - escalator.getBody().removeRows(offset, amount); - } - - public void removeColumns(final int offset, final int amount) { - data.removeColumns(offset, amount); - escalator.getColumnConfiguration().removeColumns(offset, amount); - } - - @Override - public void setWidth(String width) { - escalator.setWidth(width); - } - - @Override - public void setHeight(String height) { - escalator.setHeight(height); - } - - public RowContainer getHeader() { - return escalator.getHeader(); - } - - public RowContainer getBody() { - return escalator.getBody(); - } - - public RowContainer getFooter() { - return escalator.getFooter(); - } - - public void calculateColumnWidths() { - escalator.calculateColumnWidths(); - } -} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java b/uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java deleted file mode 100644 index 4e218ebba1..0000000000 --- a/uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.tests.widgetset.server.grid; - -import com.vaadin.tests.widgetset.client.grid.TestGridClientRpc; -import com.vaadin.tests.widgetset.client.grid.TestGridState; -import com.vaadin.ui.AbstractComponent; - -/** - * @since 7.2 - * @author Vaadin Ltd - */ -public class TestGrid extends AbstractComponent { - public TestGrid() { - setWidth(TestGridState.DEFAULT_WIDTH); - setHeight(TestGridState.DEFAULT_HEIGHT); - } - - @Override - protected TestGridState getState() { - return (TestGridState) super.getState(); - } - - public void insertRows(int offset, int amount) { - rpc().insertRows(offset, amount); - } - - public void removeRows(int offset, int amount) { - rpc().removeRows(offset, amount); - } - - public void insertColumns(int offset, int amount) { - rpc().insertColumns(offset, amount); - } - - public void removeColumns(int offset, int amount) { - rpc().removeColumns(offset, amount); - } - - private TestGridClientRpc rpc() { - return getRpcProxy(TestGridClientRpc.class); - } - - public void scrollToRow(int index, String destination, int padding) { - rpc().scrollToRow(index, destination, padding); - } - - public void scrollToColumn(int index, String destination, int padding) { - rpc().scrollToColumn(index, destination, padding); - } - - public void setFrozenColumns(int frozenColumns) { - rpc().setFrozenColumns(frozenColumns); - } - - public void insertHeaders(int index, int amount) { - rpc().insertHeaders(index, amount); - } - - public void removeHeaders(int index, int amount) { - rpc().removeHeaders(index, amount); - } - - public void insertFooters(int index, int amount) { - rpc().insertFooters(index, amount); - } - - public void removeFooters(int index, int amount) { - rpc().removeFooters(index, amount); - } - - public void setColumnWidth(int index, int px) { - rpc().setColumnWidth(index, px); - } - - public void calculateColumnWidths() { - rpc().calculateColumnWidths(); - } - - public void randomizeDefaultRowHeight() { - rpc().randomRowHeight(); - } -} |