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

org.opencms.gwt.client.ui.tree.CmsTreeItem Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 18.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.gwt.client.ui.tree;

import org.opencms.gwt.client.dnd.CmsDNDHandler.Orientation;
import org.opencms.gwt.client.dnd.I_CmsDraggable;
import org.opencms.gwt.client.dnd.I_CmsDropTarget;
import org.opencms.gwt.client.ui.CmsList;
import org.opencms.gwt.client.ui.CmsListItem;
import org.opencms.gwt.client.ui.CmsToggleButton;
import org.opencms.gwt.client.ui.I_CmsButton;
import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle;
import org.opencms.gwt.client.ui.I_CmsButton.Size;
import org.opencms.gwt.client.ui.I_CmsListItem;
import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle;
import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.I_CmsListTreeCss;
import org.opencms.gwt.client.ui.input.CmsCheckBox;
import org.opencms.gwt.client.util.CmsDomUtil;
import org.opencms.gwt.client.util.CmsStyleVariable;

import com.google.common.base.Function;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

/**
 * List tree item implementation.

* * Implemented as: *

 * <li class='listTreeItem listTreeItem*state*'>
 *   <span class='listTreeItemImage'></span>
 *   <div class='listTreeItemContent'>...*content*</div>
 *   <ul class='listTreeItemChildren'>
 *      *children*
 *   </ul>
 * </li>
 * 
* * Where state can be opened, closed or leaf.

* * @since 8.0.0 */ public class CmsTreeItem extends CmsListItem { /** The duration of the animations. */ public static final int ANIMATION_DURATION = 200; /** The CSS bundle used for this widget. */ private static final I_CmsListTreeCss CSS = I_CmsLayoutBundle.INSTANCE.listTreeCss(); /** The width of the opener. */ private static final int OPENER_WIDTH = 16; /** The children list. */ protected CmsList m_children; /** The element showing the open/close icon. */ protected CmsToggleButton m_opener; /** Flag to indicate if drag'n drop is enabled. 3-states: if null the tree decides. */ private Boolean m_dropEnabled; /** The style variable controlling this tree item's leaf/non-leaf state. */ private CmsStyleVariable m_leafStyleVar; /** Flag to indicate if open or closed. */ private boolean m_open; /** The item parent. */ private CmsTreeItem m_parentItem; /** The style variable controlling this tree item's open/closed state. */ private CmsStyleVariable m_styleVar; /** The tree reference. */ private CmsTree m_tree; /** * Creates a new list tree item containing a main widget and a check box.

* * @param showOpeners if true, show open/close icons * @param checkbox the check box * @param mainWidget the main widget */ public CmsTreeItem(boolean showOpeners, CmsCheckBox checkbox, Widget mainWidget) { this(showOpeners); addMainWidget(mainWidget); addCheckBox(checkbox); initContent(); if (!showOpeners) { hideOpeners(); } } /** * Creates a new list tree item containing a main widget.

* * @param showOpeners if true, show open/close icons * @param mainWidget the main widget */ public CmsTreeItem(boolean showOpeners, Widget mainWidget) { this(showOpeners); addMainWidget(mainWidget); initContent(); if (!showOpeners) { hideOpeners(); } } /** * Creates a new tree item with a 24px wide icon.

* * @param showOpeners if true, show open/close icons * @param mainWidget the main widget * @param icon the icon style name */ public CmsTreeItem(boolean showOpeners, Widget mainWidget, String icon) { this(showOpeners); addMainWidget(mainWidget); Label label = new Label(); label.addStyleName(icon); addDecoration(label, 28, true); initContent(); if (!showOpeners) { hideOpeners(); } } /** * Default constructor.

* * @param showOpeners if true, the opener icons should be shown */ protected CmsTreeItem(boolean showOpeners) { super(); m_styleVar = new CmsStyleVariable(this); m_leafStyleVar = new CmsStyleVariable(this); m_opener = createOpener(); addDecoration(m_opener, showOpeners ? OPENER_WIDTH : 0, true); m_children = new CmsList(); m_children.setStyleName(CSS.listTreeItemChildren()); m_panel.add(m_children); onChangeChildren(); m_open = true; setOpen(false); } /** * Returns the last opened item of a tree fragment.

* * @param item the tree item * @param stopLevel the level to stop at, set -1 to go to the very last opened item * @param requiresDropEnabled true if it is required the returned element to be drop enabled * * @return the last visible item of a tree fragment */ protected static CmsTreeItem getLastOpenedItem(CmsTreeItem item, int stopLevel, boolean requiresDropEnabled) { if (stopLevel != -1) { // stop level is set int currentLevel = getPathLevel(item.getPath()); if (currentLevel > stopLevel) { // we are past the stop level, prevent further checks stopLevel = -1; } else if (currentLevel == stopLevel) { // matches stop level return item; } } if (item.getChildCount() > 0) { int childIndex = item.getChildCount() - 1; CmsTreeItem child = item.getChild(childIndex); if (requiresDropEnabled) { while (!child.isDropEnabled()) { childIndex--; if (childIndex < 0) { return item; } child = item.getChild(childIndex); } } if (child.isOpen()) { return CmsTreeItem.getLastOpenedItem(child, stopLevel, requiresDropEnabled); } } return item; } /** * Method determining the path level by counting the number of '/'.

* Example: '/xxx/xxx/' has a path-level of 2.

* * @param path the path to test * * @return the path level */ protected static native int getPathLevel(String path)/*-{ return path.match(/\//g).length - 1; }-*/; /** * Unsupported operation.

* * @see org.opencms.gwt.client.ui.CmsListItem#add(com.google.gwt.user.client.ui.Widget) */ @Override public void add(Widget w) { throw new UnsupportedOperationException(); } /** * Adds a child list item.

* * @param item the child to add * * @see org.opencms.gwt.client.ui.CmsList#addItem(org.opencms.gwt.client.ui.I_CmsListItem) */ public void addChild(CmsTreeItem item) { m_children.addItem(item); adopt(item); } /** * @see com.google.gwt.user.client.ui.HasWidgets#clear() */ public void clear() { clearChildren(); } /** * Removes all children.

* * @see org.opencms.gwt.client.ui.CmsList#clearList() */ public void clearChildren() { for (int i = getChildCount(); i > 0; i--) { removeChild(i - 1); } } /** * Closes all empty child entries.

*/ public void closeAllEmptyChildren() { for (Widget child : m_children) { if (child instanceof CmsTreeItem) { CmsTreeItem item = (CmsTreeItem)child; if (item.isOpen()) { if (item.getChildCount() == 0) { item.setOpen(false); } else { item.closeAllEmptyChildren(); } } } } } /** * Returns the child tree item at the given position.

* * @param index the position * * @return the tree item * * @see org.opencms.gwt.client.ui.CmsList#getItem(int) */ public CmsTreeItem getChild(int index) { return m_children.getItem(index); } /** * Returns the tree item with the given id.

* * @param itemId the id of the item to retrieve * * @return the tree item * * @see org.opencms.gwt.client.ui.CmsList#getItem(String) */ public CmsTreeItem getChild(String itemId) { CmsTreeItem result = m_children.getItem(itemId); return result; } /** * Helper method which gets the number of children.

* * @return the number of children * * @see org.opencms.gwt.client.ui.CmsList#getWidgetCount() */ public int getChildCount() { return m_children.getWidgetCount(); } /** * Returns the children of this list item.

* * @return the children list */ public CmsList getChildren() { return m_children; } /** * @see org.opencms.gwt.client.dnd.I_CmsDraggable#getDragHelper(I_CmsDropTarget) */ @Override public Element getDragHelper(I_CmsDropTarget target) { // disable animation to get a drag helper without any visible children boolean isAnimated = getTree().isAnimationEnabled(); getTree().setAnimationEnabled(false); setOpen(false); getTree().setAnimationEnabled(isAnimated); return super.getDragHelper(target); } /** * Returns the given item position.

* * @param item the item to get the position for * * @return the item position */ public int getItemPosition(CmsTreeItem item) { return m_children.getWidgetIndex(item); } /** * Returns the parent item.

* * @return the parent item */ public CmsTreeItem getParentItem() { return m_parentItem; } /** * @see org.opencms.gwt.client.ui.CmsListItem#getParentTarget() */ @Override public I_CmsDropTarget getParentTarget() { return getTree(); } /** * Returns the path of IDs for the this item.

* * @return a path of IDs separated by slash */ public String getPath() { StringBuffer path = new StringBuffer("/"); CmsTreeItem current = this; while (current != null) { path.insert(0, current.getId()).insert(0, "/"); current = current.getParentItem(); } String result = path.toString(); if (result.startsWith("//")) { // This happens if the root item has an empty id. // In that case, we cut off the first slash. result = result.substring(1); } return result; } /** * Gets the tree to which this tree item belongs, or null if it does not belong to a tree.

* * @return a tree or null */ public CmsTree getTree() { return m_tree; } /** * Hides the open/close icons for this tree item and its descendants.

*/ public void hideOpeners() { addStyleName(CSS.listTreeItemNoOpeners()); } /** * Inserts the given item at the given position.

* * @param item the item to insert * @param position the position * * @see org.opencms.gwt.client.ui.CmsList#insertItem(org.opencms.gwt.client.ui.I_CmsListItem, int) */ public void insertChild(CmsTreeItem item, int position) { m_children.insert(item, position); adopt(item); } /** * Checks if dropping is enabled.

* * @return true if dropping is enabled */ public boolean isDropEnabled() { if (m_dropEnabled != null) { return m_dropEnabled.booleanValue(); } CmsTree tree = getTree(); if (tree == null) { return false; } return tree.isDropEnabled(); } /** * Checks if the item is open or closed.

* * @return true if open */ public boolean isOpen() { return m_open; } /** * @see org.opencms.gwt.client.dnd.I_CmsDraggable#onDragCancel() */ @Override public void onDragCancel() { CmsTreeItem parent = getParentItem(); if (parent != null) { parent.insertChild(this, parent.getItemPosition(this)); } super.onDragCancel(); } /** * Removes an item from the list.

* * @param item the item to remove * * @return the removed item * * @see org.opencms.gwt.client.ui.CmsList#removeItem(org.opencms.gwt.client.ui.I_CmsListItem) */ public CmsTreeItem removeChild(final CmsTreeItem item) { item.setParentItem(null); item.setTree(null); if ((m_tree != null) && m_tree.isAnimationEnabled()) { // could be null if already detached // animate (new Animation() { /** * @see com.google.gwt.animation.client.Animation#onComplete() */ @Override protected void onComplete() { super.onComplete(); m_children.removeItem(item); onChangeChildren(); } /** * @see com.google.gwt.animation.client.Animation#onUpdate(double) */ @Override protected void onUpdate(double progress) { item.getElement().getStyle().setOpacity(1 - progress); } }).run(ANIMATION_DURATION); } else { m_children.removeItem(item); onChangeChildren(); } return item; } /** * Removes the item identified by the given index from the list.

* * @param index the index of the item to remove * * @return the removed item * * @see org.opencms.gwt.client.ui.CmsList#remove(int) */ public CmsTreeItem removeChild(int index) { return removeChild(m_children.getItem(index)); } /** * Removes an item from the list.

* * @param itemId the id of the item to remove * * @return the removed item * * @see org.opencms.gwt.client.ui.CmsList#removeItem(String) */ public CmsTreeItem removeChild(String itemId) { return removeChild(m_children.getItem(itemId)); } /** * Removes the opener widget.

*/ public void removeOpener() { removeDecorationWidget(m_opener, OPENER_WIDTH); } /** * Positions the drag and drop placeholder as a sibling or descendant of this element.

* * @param x the cursor client x position * @param y the cursor client y position * @param placeholder the placeholder * @param orientation the drag and drop orientation * * @return the placeholder index */ public int repositionPlaceholder(int x, int y, Element placeholder, Orientation orientation) { I_CmsDraggable draggable = null; if (getTree().getDnDHandler() != null) { draggable = getTree().getDnDHandler().getDraggable(); } Element itemElement = getListItemWidget().getElement(); // check if the mouse pointer is within the height of the element int top = CmsDomUtil.getRelativeY(y, itemElement); int height = itemElement.getOffsetHeight(); int index; String parentPath; boolean isParentDndEnabled; CmsTreeItem parentItem = getParentItem(); if (parentItem == null) { index = getTree().getItemPosition(this); parentPath = "/"; isParentDndEnabled = getTree().isRootDropEnabled(); } else { index = parentItem.getItemPosition(this); parentPath = getParentItem().getPath(); isParentDndEnabled = getParentItem().isDropEnabled(); } if (top < height) { // the mouse pointer is within the widget int diff = x - getListItemWidget().getAbsoluteLeft(); if ((draggable != this) && isDropEnabled() && (diff > 0) && (diff < 32)) { // over icon getTree().setOpenTimer(this); m_children.getElement().insertBefore(placeholder, m_children.getElement().getFirstChild()); getTree().setPlaceholderPath(getPath()); return 0; } getTree().cancelOpenTimer(); // In this case try to drop on the parent if (!isParentDndEnabled) { // we are not allowed to drop here // keeping old position return getTree().getPlaceholderIndex(); } int originalPathLevel = -1; if (draggable instanceof CmsTreeItem) { originalPathLevel = getPathLevel(((CmsTreeItem)draggable).getPath()) - 1; } if (shouldInsertIntoSiblingList(originalPathLevel, parentItem, index)) { @SuppressWarnings("null") CmsTreeItem previousSibling = parentItem.getChild(index - 1); if (previousSibling.isOpen()) { // insert as last into the last opened of the siblings tree fragment return CmsTreeItem.getLastOpenedItem( previousSibling, originalPathLevel, true).insertPlaceholderAsLastChild(placeholder); } } // insert place holder at the parent before the current item getElement().getParentElement().insertBefore(placeholder, getElement()); getTree().setPlaceholderPath(parentPath); return index; } else if ((draggable != this) && isOpen()) { getTree().cancelOpenTimer(); // the mouse pointer is on children for (int childIndex = 0; childIndex < getChildCount(); childIndex++) { CmsTreeItem child = getChild(childIndex); Element childElement = child.getElement(); boolean over = false; switch (orientation) { case HORIZONTAL: over = CmsDomUtil.checkPositionInside(childElement, x, -1); break; case VERTICAL: over = CmsDomUtil.checkPositionInside(childElement, -1, y); break; case ALL: default: over = CmsDomUtil.checkPositionInside(childElement, x, y); } if (over) { return child.repositionPlaceholder(x, y, placeholder, orientation); } } } getTree().cancelOpenTimer(); // keeping old position return getTree().getPlaceholderIndex(); } /** * Enables/disables dropping.

* * @param enabled true to enable, or false to disable */ public void setDropEnabled(boolean enabled) { if ((m_dropEnabled != null) && (m_dropEnabled.booleanValue() == enabled)) { return; } m_dropEnabled = Boolean.valueOf(enabled); } /** * Sets the tree item style to leaf, hiding the list opener.

* * @param isLeaf true to set to leaf style */ public void setLeafStyle(boolean isLeaf) { if (isLeaf) { m_leafStyleVar.setValue(CSS.listTreeItemLeaf()); } else { m_leafStyleVar.setValue(CSS.listTreeItemInternal()); } } /** * Opens or closes this tree item (i.e. shows or hides its descendants).

* * @param open if true, open the tree item, else close it */ public void setOpen(boolean open) { setOpen(open, true); } /** * Opens or closes this tree item (i.e. shows or hides its descendants).

* * @param open if true, open the tree item, else close it * @param fireEvents true if the open/close events should be fired */ public void setOpen(boolean open, boolean fireEvents) { if (m_open == open) { return; } m_open = open; executeOpen(fireEvents); CmsDomUtil.resizeAncestor(getParent()); } /** * Sets the parent item.

* * @param parentItem the parent item to set */ public void setParentItem(CmsTreeItem parentItem) { m_parentItem = parentItem; } /** * Sets the tree to which this tree item belongs.

* * This is automatically called when this tree item or one of its ancestors is inserted into a tree.

* * @param tree the tree into which the item has been inserted */ public void setTree(CmsTree tree) { m_tree = tree; for (Widget widget : m_children) { if (widget instanceof CmsTreeItem) { ((CmsTreeItem)widget).setTree(tree); } } } /** * Shows the open/close icons for this tree item and its descendants.

*/ public void showOpeners() { removeStyleName(CSS.listTreeItemNoOpeners()); } /** * Visits all nested tree items with the given visitor function.

* * @param visitor the visitor */ public void visit(Function visitor) { visitor.apply(this); for (Widget child : m_children) { ((CmsTreeItem)child).visit(visitor); } } /** * Adopts the given item.

* * @param item the item to adopt */ protected void adopt(final CmsTreeItem item) { item.setParentItem(this); item.setTree(m_tree); onChangeChildren(); if ((m_tree != null) && m_tree.isAnimationEnabled()) { // could be null if not yet attached item.getElement().getStyle().setOpacity(0); // animate (new Animation() { /** * @see com.google.gwt.animation.client.Animation#onUpdate(double) */ @Override protected void onUpdate(double progress) { item.getElement().getStyle().setOpacity(progress); } }).run(ANIMATION_DURATION); } } /** * Creates the button for opening/closing this item.

* * @return a button */ protected CmsToggleButton createOpener() { final CmsToggleButton opener = new CmsToggleButton(); opener.setButtonStyle(ButtonStyle.FONT_ICON, null); opener.setSize(Size.small); opener.addStyleName(CSS.listTreeItemOpener()); opener.setUpFace("", I_CmsButton.TREE_PLUS); opener.setDownFace("", I_CmsButton.TREE_MINUS); opener.addClickHandler(new ClickHandler() { /** * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) */ public void onClick(ClickEvent e) { setOpen(opener.isDown()); e.stopPropagation(); e.preventDefault(); } }); return opener; } /** * Executes the open call.

* * @param fireEvents if true, open/close events will be fired */ protected void executeOpen(boolean fireEvents) { m_styleVar.setValue(m_open ? CSS.listTreeItemOpen() : CSS.listTreeItemClosed()); setLeafStyle(false); m_children.getElement().getStyle().clearDisplay(); if (m_opener.isDown() != m_open) { m_opener.setDown(m_open); } if (fireEvents) { if (m_open) { fireOpen(); } else { fireClose(); } } // reset the leaf style according to the child count setLeafStyle(0 == getChildCount()); } /** * Fires the close event.

*/ protected void fireClose() { if (m_tree != null) { m_tree.fireClose(this); } } /** * Fires the open event on the tree.

*/ protected void fireOpen() { if (m_tree != null) { m_tree.fireOpen(this); } } /** * Inserts the placeholder element as last child of the children list. * Setting it's path as the current placeholder path and returning the new index.

* * @param placeholder the placeholder element * * @return the new index */ protected int insertPlaceholderAsLastChild(Element placeholder) { m_children.getElement().appendChild(placeholder); getTree().setPlaceholderPath(getPath()); return getChildCount(); } /** * Helper method which is called when the list of children changes.

*/ protected void onChangeChildren() { setLeafStyle(0 == getChildCount()); } /** * Determines if the draggable should be inserted into the previous siblings children list.

* * @param originalPathLevel the original path level * @param parent the parent item * @param index the current index * * @return true if the item should be inserted into the previous siblings children list */ private boolean shouldInsertIntoSiblingList(int originalPathLevel, CmsTreeItem parent, int index) { if ((index <= 0) || (parent == null)) { return false; } return originalPathLevel != getPathLevel(parent.getPath()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy