All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.v7.client.ui.table.TableConnector Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Copyright (C) 2000-2023 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */
package com.vaadin.v7.client.ui.table;

import java.util.Collections;
import java.util.List;

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.EventTarget;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
import com.vaadin.client.DirectionalManagedLayout;
import com.vaadin.client.HasChildMeasurementHintConnector;
import com.vaadin.client.Paintable;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.ui.PostLayoutListener;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.ui.Connect;
import com.vaadin.v7.client.ui.AbstractFieldConnector;
import com.vaadin.v7.client.ui.VScrollTable;
import com.vaadin.v7.client.ui.VScrollTable.ContextMenuDetails;
import com.vaadin.v7.client.ui.VScrollTable.FooterCell;
import com.vaadin.v7.client.ui.VScrollTable.HeaderCell;
import com.vaadin.v7.client.ui.VScrollTable.VScrollTableBody.VScrollTableRow;
import com.vaadin.v7.shared.ui.table.TableConstants;
import com.vaadin.v7.shared.ui.table.TableConstants.Section;
import com.vaadin.v7.shared.ui.table.TableServerRpc;
import com.vaadin.v7.shared.ui.table.TableState;

@Connect(com.vaadin.v7.ui.Table.class)
public class TableConnector extends AbstractFieldConnector
        implements ConnectorHierarchyChangeHandler,
        Paintable, DirectionalManagedLayout, PostLayoutListener,
        HasChildMeasurementHintConnector {

    private List childComponents;

    public TableConnector() {
        addConnectorHierarchyChangeHandler(this);
    }

    @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();
    }

    @Override
    protected void sendContextClickEvent(MouseEventDetails details,
            EventTarget eventTarget) {

        if (!Element.is(eventTarget)) {
            return;
        }
        Element e = Element.as(eventTarget);

        Section section;
        String colKey = null;
        String rowKey = null;
        if (getWidget().tFoot.getElement().isOrHasChild(e)) {
            section = Section.FOOTER;
            FooterCell w = WidgetUtil.findWidget(e, FooterCell.class);
            colKey = w.getColKey();
        } else if (getWidget().tHead.getElement().isOrHasChild(e)) {
            section = Section.HEADER;
            HeaderCell w = WidgetUtil.findWidget(e, HeaderCell.class);
            colKey = w.getColKey();
        } else {
            section = Section.BODY;
            if (getWidget().scrollBody.getElement().isOrHasChild(e)) {
                VScrollTableRow w = getScrollTableRow(e);
                /*
                 * if w is null because we've clicked on an empty area, we will
                 * let rowKey and colKey be null too, which will then lead to
                 * the server side returning a null object.
                 */
                if (w != null) {
                    rowKey = w.getKey();
                    colKey = getWidget().tHead
                            .getHeaderCell(getElementIndex(e, w.getElement()))
                            .getColKey();
                }
            }
        }

        getRpcProxy(TableServerRpc.class).contextClick(rowKey, colKey, section,
                details);

        WidgetUtil.clearTextSelection();
    }

    protected VScrollTableRow getScrollTableRow(Element e) {
        return WidgetUtil.findWidget(e, VScrollTableRow.class);
    }

    private int getElementIndex(Element e,
            com.google.gwt.user.client.Element element) {
        int i = 0;
        Element current = element.getFirstChildElement();
        while (!current.isOrHasChild(e)) {
            current = current.getNextSiblingElement();
            ++i;
        }
        return i;
    }

    /*
     * (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().paintableId = uidl.getStringAttribute("id");
        getWidget().immediate = getState().immediate;

        int previousTotalRows = getWidget().totalRows;
        getWidget().updateTotalRows(uidl);
        boolean totalRowsHaveChanged = (getWidget().totalRows != previousTotalRows);

        getWidget().updateDragMode(uidl);

        // Update child measure hint
        int childMeasureHint = uidl.hasAttribute("measurehint")
                ? uidl.getIntAttribute("measurehint")
                : 0;
        getWidget().setChildMeasurementHint(
                ChildMeasurementHint.values()[childMeasureHint]);

        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 {
                    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.isEmpty()) {
                    // 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();

        Scheduler.get().scheduleFinally(new ScheduledCommand() {

            @Override
            public void execute() {
                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;

        getWidget().collapsibleMenuContent = getState().collapseMenuContent;
    }

    @Override
    public void updateEnabledState(boolean enabledState) {
        super.updateEnabledState(enabledState);
        getWidget().enabled = isEnabled();

        // IE8 is no longer supported
    }

    @Override
    public VScrollTable getWidget() {
        return (VScrollTable) super.getWidget();
    }

    @Override
    public void updateCaption(ComponentConnector component) {
        // NOP, not rendered
    }

    @Override
    public void layoutVertically() {
        getWidget().updateHeight();
    }

    @Override
    public void layoutHorizontally() {
        getWidget().updateWidth();
    }

    @Override
    public void postLayout() {
        VScrollTable table = getWidget();
        if (table.sizeNeedsInit) {
            table.sizeInit();
            Scheduler.get().scheduleFinally(new ScheduledCommand() {
                @Override
                public void execute() {
                    // IE8 is no longer supported
                    getLayoutManager().setNeedsMeasure(TableConnector.this);
                    ServerConnector parent = getParent();
                    if (parent instanceof ComponentConnector) {
                        getLayoutManager()
                                .setNeedsMeasure((ComponentConnector) parent);
                    }
                    getLayoutManager()
                            .setNeedsVerticalLayout(TableConnector.this);
                    getLayoutManager().layoutNow();
                }
            });
        }
    }

    @Override
    public boolean isReadOnly() {
        return super.isReadOnly() || getState().propertyReadOnly;
    }

    @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
     */
    public void showSavedContextMenu(ContextMenuDetails savedContextMenu) {
        if (isEnabled() && savedContextMenu != null) {
            for (Widget w : getWidget().scrollBody) {
                VScrollTableRow row = (VScrollTableRow) w;
                if (row.getKey().equals(savedContextMenu.rowKey)) {
                    row.showContextMenu(savedContextMenu.left,
                            savedContextMenu.top);
                }
            }
        }
    }

    @Override
    public TooltipInfo getTooltipInfo(Element element) {

        TooltipInfo info = null;

        if (element != getWidget().getElement()) {
            Object node = WidgetUtil.findWidget(element, VScrollTableRow.class);

            if (node != null) {
                VScrollTableRow row = (VScrollTableRow) node;
                info = row.getTooltip(element);
            }
        }

        if (info == null) {
            info = super.getTooltipInfo(element);
        }

        return info;
    }

    @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;
    }

    @Override
    public void onConnectorHierarchyChange(
            ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) {
        // TODO Move code from updateFromUIDL to this method
    }

    @Override
    protected void updateComponentSize(String newWidth, String newHeight) {
        super.updateComponentSize(newWidth, newHeight);

        if ("".equals(newWidth)) {
            getWidget().updateWidth();
        }
        if ("".equals(newHeight)) {
            getWidget().updateHeight();
        }
    }

    @Override
    public List getChildComponents() {
        if (childComponents == null) {
            return Collections.emptyList();
        }

        return childComponents;
    }

    @Override
    public void setChildComponents(List childComponents) {
        this.childComponents = childComponents;
    }

    @Override
    public HandlerRegistration addConnectorHierarchyChangeHandler(
            ConnectorHierarchyChangeHandler handler) {
        return ensureHandlerManager()
                .addHandler(ConnectorHierarchyChangeEvent.TYPE, handler);
    }

    @Override
    public void setChildMeasurementHint(ChildMeasurementHint hint) {
        getWidget().setChildMeasurementHint(hint);
    }

    @Override
    public ChildMeasurementHint getChildMeasurementHint() {
        return getWidget().getChildMeasurementHint();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy