/* * Copyright 2015 JBoss, by Red Hat, Inc * * 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.uberfire.ext.widgets.core.client.tree; import java.util.Iterator; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.SimplePanel; import org.gwtbootstrap3.client.ui.Icon; import org.gwtbootstrap3.client.ui.constants.IconType; import org.uberfire.ext.widgets.core.client.resources.TreeNavigatorResources; import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull; public class TreeItem extends Composite { private final Type type; private Tree tree; private Object userObject; private TreeItem parent; private State state = State.CLOSE; private boolean isSelected = false; private FlowPanel header; private Icon icon; private FlowPanel content; private FlowPanel item; public TreeItem(final Type type, final String value) { this.type = checkNotNull("type", type); if (type.equals(Type.FOLDER) || type.equals(Type.ROOT)) { final FlowPanel folder = new FlowPanel(); folder.setStylePrimaryName(TreeNavigatorResources.INSTANCE.css().treeFolder()); folder.getElement().getStyle().setDisplay(Style.Display.BLOCK); { this.header = GWT.create(FlowPanel.class); this.icon = new Icon(IconType.FOLDER); this.content = new FlowPanel(); final Anchor name = new Anchor(); { header.setStylePrimaryName(TreeNavigatorResources.INSTANCE.css().treeFolderHeader()); folder.add(header); { header.add(icon); } final FlowPanel folderName = new FlowPanel(); { folderName.setStylePrimaryName(TreeNavigatorResources.INSTANCE.css().treeFolderName()); header.add(folderName); { name.setText(value); folderName.add(name); } } header.addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { if (!isSelected) { updateSelected(); } if (state.equals(State.CLOSE)) { setState(State.OPEN, true); } else { setState(State.CLOSE, true); } } }, ClickEvent.getType()); } { content.setStylePrimaryName(TreeNavigatorResources.INSTANCE.css().treeFolderContent()); content.getElement().getStyle().setDisplay(Style.Display.NONE); folder.add(content); } initWidget(folder); } } else if (type.equals(Type.ITEM)) { this.item = GWT.create(FlowPanel.class); item.setStylePrimaryName(TreeNavigatorResources.INSTANCE.css().treeItem()); { this.icon = new Icon(IconType.FILE_O); final FlowPanel itemName = new FlowPanel(); final Anchor name = new Anchor(); { item.add(icon); } { itemName.setStylePrimaryName(TreeNavigatorResources.INSTANCE.css().treeItemName()); item.add(itemName); { name.setText(value); itemName.add(name); } } item.addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { tree.onSelection(TreeItem.this, true); } }, ClickEvent.getType()); } initWidget(item); } else { final FlowPanel loader = new FlowPanel(); { final SimplePanel loading = new SimplePanel(); loading.getElement().setInnerText(value); loader.add(loading); } initWidget(loader); } } private void updateSelected() { tree.onSelection(this, true); } public State getState() { return state; } public void setState(final State state) { setState(state, false, true); } public void setState(final State state, boolean fireEvents) { setState(state, false, fireEvents); } public void setState(final State state, boolean propagateParent, boolean fireEvents) { if (notFolder()) { return; } if (!this.state.equals(state)) { this.state = state; updateState(state); if (fireEvents && tree != null) { tree.fireStateChanged(this, state); } } if (propagateParent && parent != null) { parent.setState(state, true, false); } } private boolean notFolder() { return !type.equals(Type.FOLDER); } public Object getUserObject() { return userObject; } public void setUserObject(final Object userObject) { this.userObject = userObject; } public Type getType() { return this.type; } public TreeItem addItem(final Type type, final String value) { if (notFolder()) { return null; } final TreeItem child = makeChild(type, value); content.add(child); child.setTree(tree); child.setParent(this); return child; } protected TreeItem makeChild(final Type type, final String value) { return new TreeItem(type, value); } public void removeItems() { content.clear(); } public int getChildCount() { return content.getWidgetCount(); } public TreeItem getChild(final int i) { if (i + 1 > content.getWidgetCount()) { return null; } return (TreeItem) content.getWidget(i); } public Iterable<TreeItem> getChildren() { return new Iterable<TreeItem>() { @Override public Iterator<TreeItem> iterator() { return new TreeItemIterator(content); } }; } void setTree(final Tree tree) { this.tree = tree; } void setParent(final TreeItem parent) { this.parent = parent; } void updateState(final State state) { // If the tree hasn't been set, there is no visual state to update. // If the tree is not attached, then update will be called on attach. if (tree == null) { return; } switch (state) { case OPEN: content.getElement().getStyle().setDisplay(Style.Display.BLOCK); icon.setType(IconType.FOLDER_OPEN); break; case CLOSE: icon.setType(IconType.FOLDER); content.getElement().getStyle().setDisplay(Style.Display.NONE); } } /** * Removes this item from its tree. */ public void remove() { if (parent != null) { // If this item has a parent, remove self from it. parent.removeItem(this); } else if (tree != null) { // If the item has no parent, but is in the Tree, it must be a top-level // element. tree.removeItem(this); } } private void removeItem(final TreeItem treeItem) { content.remove(treeItem); } public String getText() { return getElement().getInnerText(); } public boolean isSelected() { return isSelected; } void setSelected(boolean selected) { isSelected = selected; if (selected) { if (header != null) { header.addStyleName(TreeNavigatorResources.INSTANCE.css().treeSelected()); } else { item.addStyleName(TreeNavigatorResources.INSTANCE.css().treeSelected()); } } else { if (header != null) { header.removeStyleName(TreeNavigatorResources.INSTANCE.css().treeSelected()); } else { item.removeStyleName(TreeNavigatorResources.INSTANCE.css().treeSelected()); } } } public boolean isEmpty() { return content.getWidgetCount() == 0; } public enum Type { ROOT, FOLDER, ITEM, LOADING } public enum State { NONE, OPEN, CLOSE } protected static class TreeItemIterator implements Iterator<TreeItem> { private final ComplexPanel container; private int index = 0; TreeItemIterator(ComplexPanel container) { this.container = container; } @Override public boolean hasNext() { if (container == null) { return false; } return index < container.getWidgetCount(); } @Override public TreeItem next() { return (TreeItem) container.getWidget(index++); } @Override public void remove() { throw new UnsupportedOperationException(); } } }