001/*
002 * #%L
003 * GwtMaterial
004 * %%
005 * Copyright (C) 2015 - 2017 GwtMaterialDesign
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 * 
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 * 
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package gwt.material.design.addins.client.tree;
021
022import com.google.gwt.dom.client.Document;
023import com.google.gwt.event.logical.shared.*;
024import com.google.gwt.event.shared.HandlerRegistration;
025import com.google.gwt.user.client.ui.Widget;
026import gwt.material.design.addins.client.MaterialAddins;
027import gwt.material.design.addins.client.base.constants.AddinsCssName;
028import gwt.material.design.client.MaterialDesignBase;
029import gwt.material.design.client.base.MaterialWidget;
030
031//@formatter:off
032
033/**
034 * MaterialTree is a component that wraps all the tree items that provide lists of
035 * event handlers like open/close and selection event.
036 * <p>
037 * <h3>XML Namespace Declaration</h3>
038 * <pre>
039 * {@code
040 * xmlns:ma='urn:import:gwt.material.design.addins.client'
041 * }
042 * </pre>
043 * <p>
044 * <h3>UiBinder Usage:</h3>
045 * <pre>
046 * {@code
047 * <ma:tree.MaterialTree>
048 *     <ma:tree.MaterialTreeItem text="Folder 1" iconType="FOLDER" />
049 *     <ma:tree.MaterialTreeItem text="Folder 2" iconType="FOLDER">
050 *         &lt;-- Image support -->
051 *         <ma:tree.MaterialTreeItem text="Item 2.1" resource="{res.batman.png}" />
052 *         <ma:tree.MaterialTreeItem text="Item 2.2" resource="{res.superman.png}" />
053 *         <ma:tree.MaterialTreeItem text="Item 2.3" resource="{res.spiderman.png}" />
054 *     </ma:tree.MaterialTreeItem>
055 *     <ma:tree.MaterialTreeItem text="Folder 3" iconType="FOLDER" />
056 * </ma:tree.MaterialTree>
057 * }
058 * </pre>
059 *
060 * @author kevzlou7979
061 * @author Ben Dol
062 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#treeview">Tree View</a>
063 */
064// @formatter:on
065public class MaterialTree extends MaterialWidget implements HasCloseHandlers<MaterialTreeItem>,
066        HasOpenHandlers<MaterialTreeItem>, HasSelectionHandlers<MaterialTreeItem> {
067
068    static {
069        if (MaterialAddins.isDebug()) {
070            MaterialDesignBase.injectCss(MaterialTreeDebugClientBundle.INSTANCE.treeCssDebug());
071        } else {
072            MaterialDesignBase.injectCss(MaterialTreeClientBundle.INSTANCE.treeCss());
073        }
074    }
075
076    private MaterialTreeItem selectedItem;
077
078    public MaterialTree() {
079        super(Document.get().createDivElement(), AddinsCssName.TREE);
080    }
081
082    @Override
083    protected void onLoad() {
084        super.onLoad();
085
086        // Ensure all children know we are the root.
087        for (Widget child : getChildren()) {
088            if (child instanceof MaterialTreeItem) {
089                ((MaterialTreeItem) child).setTree(this);
090            }
091        }
092
093        // Add selection event
094        registerHandler(addSelectionHandler(event -> {
095            for (Widget item : getChildren()) {
096                if (item instanceof MaterialTreeItem) {
097                    clearSelectedStyles((MaterialTreeItem) item);
098                }
099            }
100            MaterialTreeItem treeItem = event.getSelectedItem();
101            treeItem.addStyleName(AddinsCssName.SELECTED);
102            setSelectedItem(treeItem);
103        }));
104    }
105
106    @Override
107    protected void add(Widget child, com.google.gwt.user.client.Element container) {
108        if (child instanceof MaterialTreeItem) {
109            super.add(child, container);
110            ((MaterialTreeItem) child).setTree(this);
111        } else {
112            throw new IllegalArgumentException("MaterialTree can only contain MaterialTreeItem");
113        }
114    }
115
116    @Override
117    protected void insert(Widget child, com.google.gwt.user.client.Element container, int beforeIndex, boolean domInsert) {
118        if (child instanceof MaterialTreeItem) {
119            super.insert(child, container, beforeIndex, domInsert);
120            ((MaterialTreeItem) child).setTree(this);
121        } else {
122            throw new IllegalArgumentException("MaterialTree can only contain MaterialTreeItem");
123        }
124    }
125
126    protected void clearSelectedStyles(MaterialTreeItem item) {
127        item.removeStyleName(AddinsCssName.SELECTED);
128
129        for (MaterialTreeItem treeItem : item.getTreeItems()) {
130            clearSelectedStyles(treeItem);
131        }
132    }
133
134    public MaterialTreeItem getSelectedItem() {
135        return selectedItem;
136    }
137
138    public void setSelectedItem(MaterialTreeItem selectedItem) {
139        this.selectedItem = selectedItem;
140    }
141
142    /**
143     * Expand all tree item's content
144     */
145    public void expand() {
146        for (Widget w : getChildren()) {
147            if (w instanceof MaterialTreeItem) {
148                expandItems((MaterialTreeItem) w);
149            }
150        }
151    }
152
153    /**
154     * Recursive function to expand each tree item.
155     */
156    protected void expandItems(MaterialTreeItem item) {
157        item.expand();
158        item.setHide(true);
159        item.getTreeItems().forEach(this::expandItems);
160    }
161
162    /**
163     * Collapse all tree item's content
164     */
165    public void collapse() {
166        for (Widget w : getChildren()) {
167            if (w instanceof MaterialTreeItem) {
168                collapseItems((MaterialTreeItem) w);
169            }
170        }
171    }
172
173    /**
174     * Deselect selected tree item
175     */
176    public void deselectSelectedItem() {
177        // Check whether tree has selected item
178        if (selectedItem != null) {
179            clearSelectedStyles(selectedItem);
180            setSelectedItem(null);
181            SelectionEvent.fire(this, null);
182        }
183    }
184
185    /**
186     * Recursive function to collapse each tree item.
187     */
188    protected void collapseItems(MaterialTreeItem item) {
189        item.collapse();
190        item.setHide(false);
191
192        item.getTreeItems().forEach(this::collapseItems);
193    }
194
195    @Override
196    public HandlerRegistration addCloseHandler(final CloseHandler<MaterialTreeItem> handler) {
197        return addHandler(new CloseHandler<MaterialTreeItem>() {
198            @Override
199            public void onClose(CloseEvent<MaterialTreeItem> event) {
200                if (isEnabled()) {
201                    handler.onClose(event);
202                }
203            }
204        }, CloseEvent.getType());
205    }
206
207    @Override
208    public HandlerRegistration addOpenHandler(final OpenHandler<MaterialTreeItem> handler) {
209        return addHandler(new OpenHandler<MaterialTreeItem>() {
210            @Override
211            public void onOpen(OpenEvent<MaterialTreeItem> event) {
212                if (isEnabled()) {
213                    handler.onOpen(event);
214                }
215            }
216        }, OpenEvent.getType());
217    }
218
219    @Override
220    public HandlerRegistration addSelectionHandler(final SelectionHandler<MaterialTreeItem> handler) {
221        return addHandler(new SelectionHandler<MaterialTreeItem>() {
222            @Override
223            public void onSelection(SelectionEvent<MaterialTreeItem> event) {
224                if (isEnabled()) {
225                    handler.onSelection(event);
226                }
227            }
228        }, SelectionEvent.getType());
229    }
230}