com.haulmont.cuba.web.gui.components.WebAbstractTree.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.web.gui.components.WebAbstractTree.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * 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.haulmont.cuba.web.gui.components;

import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.data.HierarchicalDatasource;
import com.haulmont.cuba.gui.data.impl.CollectionDsListenersWrapper;
import com.haulmont.cuba.web.toolkit.ui.CubaTree;
import com.vaadin.event.ShortcutListener;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.HorizontalLayout;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

public abstract class WebAbstractTree<T extends CubaTree, E extends Entity> extends WebAbstractList<T, E>
        implements Tree<E> {

    private static final String HAS_TOP_PANEL_STYLENAME = "has-top-panel";
    // Style names used by tree itself
    protected List<String> internalStyles = new ArrayList<>();

    protected List<Tree.StyleProvider> styleProviders; // lazily initialized List
    protected StyleGeneratorAdapter styleGenerator; // lazily initialized field

    protected CollectionDsListenersWrapper collectionDsListenersWrapper;

    protected ButtonsPanel buttonsPanel;
    protected HorizontalLayout topPanel;
    protected com.vaadin.ui.CssLayout componentComposition;
    protected Action enterPressAction;
    protected IconProvider<? super E> iconProvider;

    @Override
    public HierarchicalDatasource getDatasource() {
        return (HierarchicalDatasource) super.getDatasource();
    }

    @Override
    public void collapseTree() {
        component.collapseAll();
    }

    @Override
    public void expandTree() {
        component.expandAll();
    }

    @Override
    public void collapse(Object itemId) {
        component.collapseItemRecursively(itemId);
    }

    @Override
    public void expand(Object itemId) {
        component.expandItemWithParents(itemId);
    }

    @Override
    public void expandUpTo(int level) {
        component.expandUpTo(level);
    }

    @Override
    public boolean isExpanded(Object itemId) {
        return component.isExpanded(itemId);
    }

    @Override
    public boolean isEditable() {
        return !component.isReadOnly();
    }

    @Override
    public void setEditable(boolean editable) {
        component.setReadOnly(!editable);
    }

    @Override
    public String getIcon() {
        return icon;
    }

    @Override
    public void setIcon(String icon) {
        this.icon = icon;

        getComposition().setIcon(WebComponentsHelper.getIcon(icon));
    }

    @Override
    public String getCaption() {
        return getComposition().getCaption();
    }

    @Override
    public void setCaption(String caption) {
        getComposition().setCaption(caption);
    }

    @Override
    public String getDescription() {
        return getComposition().getDescription();
    }

    @Override
    public void setDescription(String description) {
        if (getComposition() instanceof AbstractComponent) {
            ((AbstractComponent) getComposition()).setDescription(description);
        }
    }

    @Override
    public ButtonsPanel getButtonsPanel() {
        return buttonsPanel;
    }

    @Override
    public com.vaadin.ui.Component getComposition() {
        return componentComposition;
    }

    @Override
    public void setButtonsPanel(ButtonsPanel panel) {
        if (buttonsPanel != null && topPanel != null) {
            topPanel.removeComponent(buttonsPanel.unwrap(com.vaadin.ui.Component.class));
            buttonsPanel.setParent(null);
        }
        buttonsPanel = panel;
        if (panel != null) {
            if (panel.getParent() != null && panel.getParent() != this) {
                throw new IllegalStateException("Component already has parent");
            }

            if (topPanel == null) {
                topPanel = createTopPanel();
                topPanel.setWidth("100%");
                componentComposition.addComponentAsFirst(topPanel);
            }
            topPanel.addComponent(panel.unwrap(com.vaadin.ui.Component.class));
            if (panel instanceof VisibilityChangeNotifier) {
                ((VisibilityChangeNotifier) panel)
                        .addVisibilityChangeListener(event -> updateCompositionStylesTopPanelVisible());
            }
            panel.setParent(this);
        }

        updateCompositionStylesTopPanelVisible();
    }

    protected HorizontalLayout createTopPanel() {
        HorizontalLayout topPanel = new HorizontalLayout();
        topPanel.setStyleName("c-tree-top");
        return topPanel;
    }

    // if buttons panel becomes hidden we need to set top panel height to 0
    protected void updateCompositionStylesTopPanelVisible() {
        if (topPanel != null) {
            boolean hasChildren = topPanel.getComponentCount() > 0;
            boolean anyChildVisible = false;
            for (com.vaadin.ui.Component childComponent : topPanel) {
                if (childComponent.isVisible()) {
                    anyChildVisible = true;
                    break;
                }
            }
            boolean topPanelVisible = hasChildren && anyChildVisible;

            if (!topPanelVisible) {
                componentComposition.removeStyleName(HAS_TOP_PANEL_STYLENAME);

                internalStyles.remove(HAS_TOP_PANEL_STYLENAME);
            } else {
                componentComposition.addStyleName(HAS_TOP_PANEL_STYLENAME);

                if (!internalStyles.contains(HAS_TOP_PANEL_STYLENAME)) {
                    internalStyles.add(HAS_TOP_PANEL_STYLENAME);
                }
            }
        }
    }

    public void initComponent(CubaTree component) {
        componentComposition = new CssLayout();
        componentComposition.setPrimaryStyleName("c-tree-composition");
        componentComposition.setWidthUndefined();
        componentComposition.addComponent(component);

        component.setSizeFull();

        component.addShortcutListener(
                new ShortcutListener("tableEnter", com.vaadin.event.ShortcutAction.KeyCode.ENTER, null) {
                    @Override
                    public void handleAction(Object sender, Object target) {
                        if (target == WebAbstractTree.this.component) {
                            if (enterPressAction != null) {
                                enterPressAction.actionPerform(WebAbstractTree.this);
                            } else {
                                handleClickAction();
                            }
                        }
                    }
                });
    }

    protected void handleClickAction() {
        Action action = getItemClickAction();
        if (action == null) {
            action = getEnterPressAction();
            if (action == null) {
                action = getAction("edit");
                if (action == null) {
                    action = getAction("view");
                }
            }
        }

        if (action != null && action.isEnabled()) {
            Window window = ComponentsHelper.getWindowImplementation(WebAbstractTree.this);
            if (window instanceof Window.Wrapper) {
                window = ((Window.Wrapper) window).getWrappedWindow();
            }

            if (!(window instanceof Window.Lookup)) {
                action.actionPerform(WebAbstractTree.this);
            } else {
                Window.Lookup lookup = (Window.Lookup) window;

                com.haulmont.cuba.gui.components.Component lookupComponent = lookup.getLookupComponent();
                if (lookupComponent != this)
                    action.actionPerform(WebAbstractTree.this);
                else if (action.getId().equals(WindowDelegate.LOOKUP_ITEM_CLICK_ACTION_ID)) {
                    action.actionPerform(WebAbstractTree.this);
                }
            }
        }
    }

    @Override
    public void setLookupSelectHandler(Runnable selectHandler) {
        component.setDoubleClickMode(true);
        component.addItemClickListener(event -> {
            if (event.isDoubleClick()) {
                if (event.getItem() != null) {
                    component.setValue(event.getItemId());
                    selectHandler.run();
                }
            }
        });
    }

    @Override
    public Collection getLookupSelectedItems() {
        return getSelected();
    }

    @Override
    protected String getAlternativeDebugId() {
        if (id != null) {
            return id;
        }
        if (datasource != null && StringUtils.isNotEmpty(datasource.getId())) {
            return getClass().getSimpleName() + "_" + datasource.getId();
        }

        return getClass().getSimpleName();
    }

    protected StyleGeneratorAdapter createStyleGenerator() {
        return new StyleGeneratorAdapter();
    }

    @SuppressWarnings("unchecked")
    protected String getGeneratedStyle(Object itemId) {
        if (styleProviders == null) {
            return null;
        }

        Entity item = datasource.getItem(itemId);
        String joinedStyle = null;
        for (Tree.StyleProvider styleProvider : styleProviders) {
            String styleName = styleProvider.getStyleName(item);
            if (styleName != null) {
                if (joinedStyle == null) {
                    joinedStyle = styleName;
                } else {
                    joinedStyle += " " + styleName;
                }
            }
        }

        return joinedStyle;
    }

    @Override
    public void setMultiSelect(boolean multiselect) {
        super.setMultiSelect(multiselect);
    }

    @Override
    public void refresh() {
        datasource.refresh();
    }

    @Override
    public void setStyleName(String name) {
        super.setStyleName(name);

        for (String internalStyle : internalStyles) {
            componentComposition.addStyleName(internalStyle);
        }
    }

    @Override
    public String getStyleName() {
        String styleName = super.getStyleName();
        for (String internalStyle : internalStyles) {
            styleName = styleName.replace(internalStyle, "");
        }
        return StringUtils.normalizeSpace(styleName);
    }

    @Override
    public void setStyleProvider(@Nullable Tree.StyleProvider<? super E> styleProvider) {
        if (styleProvider != null) {
            if (this.styleProviders == null) {
                this.styleProviders = new LinkedList<>();
            } else {
                this.styleProviders.clear();
            }

            this.styleProviders.add(styleProvider);

        } else {
            this.styleProviders = null;
        }

        if (this.styleGenerator == null) {
            this.styleGenerator = createStyleGenerator();
            component.setItemStyleGenerator(this.styleGenerator);
        } else {
            component.markAsDirty();
        }
    }

    @Override
    public void addStyleProvider(Tree.StyleProvider<? super E> styleProvider) {
        if (this.styleProviders == null) {
            this.styleProviders = new LinkedList<>();
        }

        if (!this.styleProviders.contains(styleProvider)) {
            this.styleProviders.add(styleProvider);

            if (this.styleGenerator == null) {
                this.styleGenerator = createStyleGenerator();
                component.setItemStyleGenerator(this.styleGenerator);
            } else {
                component.markAsDirty();
            }
        }
    }

    @Override
    public void removeStyleProvider(Tree.StyleProvider<? super E> styleProvider) {
        if (this.styleProviders != null) {
            if (this.styleProviders.remove(styleProvider)) {
                component.markAsDirty();
            }
        }
    }

    protected class StyleGeneratorAdapter implements com.vaadin.ui.Tree.ItemStyleGenerator {
        protected boolean exceptionHandled = false;

        public static final String CUSTOM_STYLE_NAME_PREFIX = "cs ";

        @Override
        public String getStyle(com.vaadin.ui.Tree source, Object itemId) {
            try {
                String style = null;

                if (styleProviders != null) {
                    String generatedStyle = getGeneratedStyle(itemId);
                    if (generatedStyle != null) {
                        style = CUSTOM_STYLE_NAME_PREFIX + generatedStyle;
                    }
                }

                return style == null ? null : (CUSTOM_STYLE_NAME_PREFIX + style);
            } catch (Exception e) {
                LoggerFactory.getLogger(WebAbstractTree.class).error("Uncautch exception in Tree StyleProvider", e);
                this.exceptionHandled = true;
                return null;
            }
        }

        public void resetExceptionHandledFlag() {
            this.exceptionHandled = false;
        }
    }

    @Override
    public void repaint() {
        component.markAsDirty();
    }

    @Override
    public void setIconProvider(IconProvider<? super E> iconProvider) {
        if (this.iconProvider != iconProvider) {
            this.iconProvider = iconProvider;

            if (iconProvider == null) {
                component.setItemIconProvider(null);
            } else {
                component.setItemIconProvider(itemId -> {
                    @SuppressWarnings("unchecked")
                    E item = (E) datasource.getItem(itemId);
                    if (item == null) {
                        return null;
                    }

                    String resourceUrl = WebAbstractTree.this.iconProvider.getItemIcon(item);
                    if (StringUtils.isBlank(resourceUrl)) {
                        return null;
                    }
                    // noinspection ConstantConditions
                    if (!resourceUrl.contains(":")) {
                        resourceUrl = "theme:" + resourceUrl;
                    }
                    return WebComponentsHelper.getResource(resourceUrl);
                });
            }
        }
    }

    @Override
    public void setEnterPressAction(Action action) {
        enterPressAction = action;
    }

    @Override
    public Action getEnterPressAction() {
        return enterPressAction;
    }

    @Override
    public int getTabIndex() {
        return component.getTabIndex();
    }

    @Override
    public void setTabIndex(int tabIndex) {
        component.setTabIndex(tabIndex);
    }
}