blob: 65dfae2b0539afe8080c6422683de1f850eca95e [file] [log] [blame]
/*
* Copyright 2000-2014 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 org.tepi.filtertable.gwt.client.ui;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.tepi.filtertable.FilterTable;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.DirectionalManagedLayout;
import com.vaadin.client.Paintable;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.ui.AbstractHasComponentsConnector;
import com.vaadin.client.ui.PostLayoutListener;
import com.vaadin.client.ui.VCustomScrollTable;
import com.vaadin.client.ui.VCustomScrollTable.ContextMenuDetails;
import com.vaadin.client.ui.VCustomScrollTable.VScrollTableBody.VScrollTableRow;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.table.TableConstants;
import com.vaadin.shared.ui.table.TableState;
/**
* The Class FilterTableConnector.
*/
@Connect(FilterTable.class)
public class FilterTableConnector extends AbstractHasComponentsConnector
implements Paintable, DirectionalManagedLayout, PostLayoutListener {
/* (non-Javadoc)
* @see com.vaadin.client.ui.AbstractConnector#init()
*/
@Override
protected void init() {
super.init();
getWidget().init(getConnection());
}
/*
* (non-Javadoc)
*
* @see com.vaadin.client.ui.AbstractComponentConnector#onUnregister()
*/
@Override
public void onUnregister() {
super.onUnregister();
getWidget().onUnregister();
}
/*
* (non-Javadoc)
*
* @see com.vaadin.client.Paintable#updateFromUIDL(com.vaadin.client.UIDL,
* com.vaadin.client.ApplicationConnection)
*/
@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
getWidget().rendering = true;
// If a row has an open context menu, it will be closed as the row is
// detached. Retain a reference here so we can restore the menu if
// required.
ContextMenuDetails contextMenuBeforeUpdate = getWidget().contextMenu;
if (uidl.hasAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST)) {
getWidget().serverCacheFirst = uidl
.getIntAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST);
getWidget().serverCacheLast = uidl
.getIntAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_LAST);
} else {
getWidget().serverCacheFirst = -1;
getWidget().serverCacheLast = -1;
}
/*
* We need to do this before updateComponent since updateComponent calls
* this.setHeight() which will calculate a new body height depending on
* the space available.
*/
if (uidl.hasAttribute("colfooters")) {
getWidget().showColFooters = uidl.getBooleanAttribute("colfooters");
}
getWidget().tFoot.setVisible(getWidget().showColFooters);
if (!isRealUpdate(uidl)) {
getWidget().rendering = false;
return;
}
getWidget().enabled = isEnabled();
if (BrowserInfo.get().isIE8() && !getWidget().enabled) {
/*
* The disabled shim will not cover the table body if it is relative
* in IE8. See #7324
*/
getWidget().scrollBodyPanel.getElement().getStyle()
.setPosition(Position.STATIC);
} else if (BrowserInfo.get().isIE8()) {
getWidget().scrollBodyPanel.getElement().getStyle()
.setPosition(Position.RELATIVE);
}
getWidget().paintableId = uidl.getStringAttribute("id");
getWidget().immediate = getState().immediate;
int previousTotalRows = getWidget().totalRows;
getWidget().updateTotalRows(uidl);
boolean totalRowsChanged = (getWidget().totalRows != previousTotalRows);
getWidget().updateDragMode(uidl);
getWidget().updateSelectionProperties(uidl, getState(), isReadOnly());
if (uidl.hasAttribute("alb")) {
getWidget().bodyActionKeys = uidl.getStringArrayAttribute("alb");
} else {
// Need to clear the actions if the action handlers have been
// removed
getWidget().bodyActionKeys = null;
}
getWidget().setCacheRateFromUIDL(uidl);
getWidget().recalcWidths = uidl.hasAttribute("recalcWidths");
if (getWidget().recalcWidths) {
getWidget().tHead.clear();
getWidget().tFoot.clear();
}
getWidget().updatePageLength(uidl);
getWidget().updateFirstVisibleAndScrollIfNeeded(uidl);
getWidget().showRowHeaders = uidl.getBooleanAttribute("rowheaders");
getWidget().showColHeaders = uidl.getBooleanAttribute("colheaders");
getWidget().updateSortingProperties(uidl);
getWidget().updateActionMap(uidl);
getWidget().updateColumnProperties(uidl);
UIDL ac = uidl.getChildByTagName("-ac");
if (ac == null) {
if (getWidget().dropHandler != null) {
// remove dropHandler if not present anymore
getWidget().dropHandler = null;
}
} else {
if (getWidget().dropHandler == null) {
getWidget().dropHandler = getWidget().new VScrollTableDropHandler();
}
getWidget().dropHandler.updateAcceptRules(ac);
}
UIDL partialRowAdditions = uidl.getChildByTagName("prows");
UIDL partialRowUpdates = uidl.getChildByTagName("urows");
if (partialRowUpdates != null || partialRowAdditions != null) {
getWidget().postponeSanityCheckForLastRendered = true;
// we may have pending cache row fetch, cancel it. See #2136
getWidget().rowRequestHandler.cancel();
getWidget().updateRowsInBody(partialRowUpdates);
getWidget().addAndRemoveRows(partialRowAdditions);
// sanity check (in case the value has slipped beyond the total
// amount of rows)
getWidget().scrollBody.setLastRendered(getWidget().scrollBody
.getLastRendered());
getWidget().updateMaxIndent();
} else {
getWidget().postponeSanityCheckForLastRendered = false;
UIDL rowData = uidl.getChildByTagName("rows");
if (rowData != null) {
// we may have pending cache row fetch, cancel it. See #2136
getWidget().rowRequestHandler.cancel();
if (!getWidget().recalcWidths
&& getWidget().initializedAndAttached) {
getWidget().updateBody(rowData,
uidl.getIntAttribute("firstrow"),
uidl.getIntAttribute("rows"));
if (getWidget().headerChangedDuringUpdate) {
getWidget().triggerLazyColumnAdjustment(true);
} else if (!getWidget().isScrollPositionVisible()
|| totalRowsChanged
|| getWidget().lastRenderedHeight != getWidget().scrollBody
.getOffsetHeight()) {
// webkits may still bug with their disturbing scrollbar
// bug, see #3457
// Run overflow fix for the scrollable area
// #6698 - If there's a scroll going on, don't abort it
// by changing overflows as the length of the contents
// *shouldn't* have changed (unless the number of rows
// or the height of the widget has also changed)
Scheduler.get().scheduleDeferred(new Command() {
@Override
public void execute() {
Util.runWebkitOverflowAutoFix(getWidget().scrollBodyPanel
.getElement());
}
});
}
} else {
getWidget().initializeRows(uidl, rowData);
}
}
}
boolean keyboardSelectionOverRowFetchInProgress = getWidget()
.selectSelectedRows(uidl);
// If a row had an open context menu before the update, and after the
// update there's a row with the same key as that row, restore the
// context menu. See #8526.
showSavedContextMenu(contextMenuBeforeUpdate);
if (!getWidget().isSelectable()) {
getWidget().scrollBody.addStyleName(getWidget()
.getStylePrimaryName() + "-body-noselection");
} else {
getWidget().scrollBody.removeStyleName(getWidget()
.getStylePrimaryName() + "-body-noselection");
}
getWidget().hideScrollPositionAnnotation();
// selection is no in sync with server, avoid excessive server visits by
// clearing to flag used during the normal operation
if (!keyboardSelectionOverRowFetchInProgress) {
getWidget().selectionChanged = false;
}
/*
* This is called when the Home or page up button has been pressed in
* selectable mode and the next selected row was not yet rendered in the
* client
*/
if (getWidget().selectFirstItemInNextRender
|| getWidget().focusFirstItemInNextRender) {
getWidget().selectFirstRenderedRowInViewPort(
getWidget().focusFirstItemInNextRender);
getWidget().selectFirstItemInNextRender = getWidget().focusFirstItemInNextRender = false;
}
/*
* This is called when the page down or end button has been pressed in
* selectable mode and the next selected row was not yet rendered in the
* client
*/
if (getWidget().selectLastItemInNextRender
|| getWidget().focusLastItemInNextRender) {
getWidget().selectLastRenderedRowInViewPort(
getWidget().focusLastItemInNextRender);
getWidget().selectLastItemInNextRender = getWidget().focusLastItemInNextRender = false;
}
getWidget().multiselectPending = false;
if (getWidget().focusedRow != null) {
if (!getWidget().focusedRow.isAttached()
&& !getWidget().rowRequestHandler.isRequestHandlerRunning()) {
// focused row has been orphaned, can't focus
if (getWidget().selectedRowKeys.contains(getWidget().focusedRow
.getKey())) {
// if row cache was refreshed, focused row should be
// in selection and exists with same index
getWidget().setRowFocus(
getWidget().getRenderedRowByKey(
getWidget().focusedRow.getKey()));
} else if (getWidget().selectedRowKeys.size() > 0) {
// try to focus any row in selection
getWidget().setRowFocus(
getWidget().getRenderedRowByKey(
getWidget().selectedRowKeys.iterator()
.next()));
} else {
// try to focus any row
getWidget().focusRowFromBody();
}
}
}
/*
* If the server has (re)initialized the rows, our selectionRangeStart
* row will point to an index that the server knows nothing about,
* causing problems if doing multi selection with shift. The field will
* be cleared a little later when the row focus has been restored.
* (#8584)
*/
if (uidl.hasAttribute(TableConstants.ATTRIBUTE_KEY_MAPPER_RESET)
&& uidl.getBooleanAttribute(TableConstants.ATTRIBUTE_KEY_MAPPER_RESET)
&& getWidget().selectionRangeStart != null) {
assert !getWidget().selectionRangeStart.isAttached();
getWidget().selectionRangeStart = getWidget().focusedRow;
}
getWidget().tabIndex = getState().tabIndex;
getWidget().setProperTabIndex();
getWidget().resizeSortedColumnForSortIndicator();
// Remember this to detect situations where overflow hack might be
// needed during scrolling
getWidget().lastRenderedHeight = getWidget().scrollBody
.getOffsetHeight();
getWidget().rendering = false;
getWidget().headerChangedDuringUpdate = false;
updateFiltersFromUIDL(uidl.getChildByTagName("filters"), client);
if (uidl.hasAttribute("columnheaderstylenames")) {
String[] columnHeaderStylenames = uidl
.getStringArrayAttribute("columnheaderstylenames");
getWidget().setColumnHeaderStylenames(columnHeaderStylenames);
}
}
/** Update filters from uidl.
*
* @param uidl
* the uidl
* @param client
* the client
*/
public void updateFiltersFromUIDL(UIDL uidl, ApplicationConnection client) {
if (uidl == null) {
return;
}
getWidget().filters.filtersVisible = uidl
.hasAttribute("filtersvisible") ? uidl
.getBooleanAttribute("filtersvisible") : false;
getWidget().filters.wrapFilters = uidl.hasAttribute("wrapFilters") ? uidl
.getBooleanAttribute("wrapFilters") : false;
/* If filters are not set visible, clear and hide filter panel */
getWidget().filters.setVisible(getWidget().filters.filtersVisible);
getWidget().setContainerHeight();
boolean forceRender = uidl.getBooleanAttribute("forceRender");
if (!getWidget().filters.filtersVisible) {
getWidget().filters.container.clear();
getWidget().filters.filters.clear();
} else {
/* Prepare and paint filter components */
Map<String, Widget> newWidgets = new HashMap<String, Widget>();
boolean allCached = true;
for (final Iterator<Object> it = uidl.getChildIterator(); it
.hasNext();) {
final UIDL cc = (UIDL) it.next();
if (cc.getTag().startsWith("filtercomponent-")) {
String cid = cc.getStringAttribute("columnid");
UIDL uidld = cc.getChildUIDL(0);
if (uidld == null) {
newWidgets.put(cid, null);
} else {
ComponentConnector connector = client
.getPaintable(uidld);
newWidgets.put(cid, connector.getWidget());
if (!uidld.hasAttribute("cached")
|| !uidld.getBooleanAttribute("cached")) {
allCached = false;
}
}
}
}
if (forceRender || !allCached
|| getWidget().filters.filters.isEmpty()) {
getWidget().filters.filters.clear();
for (String cid : newWidgets.keySet()) {
getWidget().filters.filters.put(cid, newWidgets.get(cid));
}
getWidget().filters.reRenderFilterComponents();
} else {
getWidget().filters.resetFilterWidths();
}
}
}
/* (non-Javadoc)
* @see com.vaadin.client.ui.AbstractComponentConnector#getWidget()
*/
@Override
public VFilterTable getWidget() {
return (VFilterTable) super.getWidget();
}
/* (non-Javadoc)
* @see com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin.client.ComponentConnector)
*/
@Override
public void updateCaption(ComponentConnector component) {
// NOP, not rendered
}
/* (non-Javadoc)
* @see com.vaadin.client.DirectionalManagedLayout#layoutVertically()
*/
@Override
public void layoutVertically() {
getWidget().updateHeight();
}
/* (non-Javadoc)
* @see com.vaadin.client.DirectionalManagedLayout#layoutHorizontally()
*/
@Override
public void layoutHorizontally() {
getWidget().updateWidth();
}
/* (non-Javadoc)
* @see com.vaadin.client.ui.PostLayoutListener#postLayout()
*/
@Override
public void postLayout() {
VCustomScrollTable table = getWidget();
if (table.sizeNeedsInit) {
table.sizeInit();
Scheduler.get().scheduleFinally(new ScheduledCommand() {
@Override
public void execute() {
// IE8 needs some hacks to measure sizes correctly
Util.forceIE8Redraw(getWidget().getElement());
getLayoutManager().setNeedsMeasure(
FilterTableConnector.this);
ServerConnector parent = getParent();
if (parent instanceof ComponentConnector) {
getLayoutManager().setNeedsMeasure(
(ComponentConnector) parent);
}
getLayoutManager().setNeedsVerticalLayout(
FilterTableConnector.this);
getLayoutManager().layoutNow();
}
});
}
}
/* (non-Javadoc)
* @see com.vaadin.client.ui.AbstractComponentConnector#isReadOnly()
*/
@Override
public boolean isReadOnly() {
return super.isReadOnly() || getState().propertyReadOnly;
}
/* (non-Javadoc)
* @see com.vaadin.client.ui.AbstractComponentConnector#getState()
*/
@Override
public TableState getState() {
return (TableState) super.getState();
}
/** Shows a saved row context menu if the row for the context menu is
* still visible. Does nothing if a context menu has not been saved.
*
* @param savedContextMenu
* the saved context menu
*/
public void showSavedContextMenu(ContextMenuDetails savedContextMenu) {
if (isEnabled() && savedContextMenu != null) {
Iterator<Widget> iterator = getWidget().scrollBody.iterator();
while (iterator.hasNext()) {
Widget w = iterator.next();
VScrollTableRow row = (VScrollTableRow) w;
if (row.getKey().equals(savedContextMenu.rowKey)) {
row.showContextMenu(savedContextMenu.left,
savedContextMenu.top);
}
}
}
}
/* (non-Javadoc)
* @see com.vaadin.client.ui.AbstractComponentConnector#getTooltipInfo(com.google.gwt.dom.client.Element)
*/
@Override
public TooltipInfo getTooltipInfo(Element element) {
TooltipInfo info = null;
if (element != getWidget().getElement()) {
Object node = Util.findWidget(element, VScrollTableRow.class);
if (node != null) {
VScrollTableRow row = (VScrollTableRow) node;
info = row.getTooltip(element);
}
}
if (info == null) {
info = super.getTooltipInfo(element);
}
return info;
}
/* (non-Javadoc)
* @see com.vaadin.client.ui.AbstractComponentConnector#hasTooltip()
*/
@Override
public boolean hasTooltip() {
/*
* Tooltips for individual rows and cells are not processed until
* updateFromUIDL, so we can't be sure that there are no tooltips during
* onStateChange when this method is used.
*/
return true;
}
/* (non-Javadoc)
* @see com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler#onConnectorHierarchyChange(com.vaadin.client.ConnectorHierarchyChangeEvent)
*/
@Override
public void onConnectorHierarchyChange(
ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) {
// TODO Move code from updateFromUIDL to this method
}
}