/* * This library is part of OpenCms - * the Open Source Content Management System * * Copyright (c) Alkacon Software GmbH (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; import org.opencms.gwt.client.dnd.CmsDNDHandler; 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.css.I_CmsLayoutBundle; import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.I_CmsListTreeCss; import org.opencms.gwt.client.util.CmsDomUtil; import java.util.HashMap; import java.util.Map; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Widget; /** * A very basic list implementation to hold {@link CmsListItemWidget}.<p> * * @param <I> the specific list item implementation * * @since 8.0.0 */ public class CmsList<I extends I_CmsListItem> extends ComplexPanel implements I_CmsTruncable, I_CmsDropTarget { /** The css bundle used for this widget. */ private static final I_CmsListTreeCss CSS = I_CmsLayoutBundle.INSTANCE.listTreeCss(); /** The drag'n drop handler. */ protected CmsDNDHandler m_dndHandler; /** The current place holder. */ protected Element m_placeholder; /** The placeholder position index. */ protected int m_placeholderIndex = -1; /** The child width in px for truncation. */ private int m_childWidth; /** Flag to indicate if drag'n drop on the root node is allowed. */ private boolean m_dropEnabled; /** The map of items. */ private Map<String, I> m_items; /** Flag to indicate if the list will always return <code>true</code> on check target requests within drag and drop. */ private boolean m_takeAll; /** The text metrics prefix. */ private String m_tmPrefix; /** * Constructor.<p> */ public CmsList() { setElement(DOM.createElement(CmsDomUtil.Tag.ul.name())); setStyleName(CSS.list()); m_items = new HashMap<String, I>(); } /** * @see com.google.gwt.user.client.ui.Panel#add(com.google.gwt.user.client.ui.Widget) */ @SuppressWarnings("unchecked") @Override public void add(Widget widget) { assert widget instanceof I_CmsListItem; add(widget, getElement()); registerItem((I)widget); } /** * Adds an item to the list.<p> * * @param item the item to add * * @see #add(Widget) */ public void addItem(I item) { add((Widget)item); } /** * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#checkPosition(int, int, Orientation) */ public boolean checkPosition(int x, int y, Orientation orientation) { if (!isDropEnabled()) { return false; } if (isDNDTakeAll()) { return true; } switch (orientation) { case HORIZONTAL: return CmsDomUtil.checkPositionInside(getElement(), x, -1); case VERTICAL: return CmsDomUtil.checkPositionInside(getElement(), -1, y); case ALL: default: return CmsDomUtil.checkPositionInside(getElement(), x, y); } } /** * Clears the list.<p> */ public void clearList() { clear(); m_items.clear(); } /** * Returns the drag'n drop handler.<p> * * @return the handler */ public CmsDNDHandler getDnDHandler() { return m_dndHandler; } /** * Returns the list item at the given position.<p> * * @param index the position * * @return the list item * * @see #getWidget(int) */ @SuppressWarnings("unchecked") public I getItem(int index) { return (I)getWidget(index); } /** * Returns the list item with the given id.<p> * * @param itemId the id of the item to retrieve * * @return the list item * * @see #getWidget(int) */ public I getItem(String itemId) { return m_items.get(itemId); } /** * Returns the given item position.<p> * * @param item the item to get the position for * * @return the item position */ public int getItemPosition(I item) { return getWidgetIndex((Widget)item); } /** * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#getPlaceholderIndex() */ public int getPlaceholderIndex() { return m_placeholderIndex; } /** * Inserts the given widget at the given position.<p> * * @param widget the widget to insert * @param position the position */ @SuppressWarnings("unchecked") public void insert(Widget widget, int position) { assert widget instanceof I_CmsListItem; insert(widget, getElement(), position, true); registerItem((I)widget); } /** * Inserts the given item at the given position.<p> * * @param item the item to insert * @param position the position */ public void insertItem(I item, int position) { insert((Widget)item, position); } /** * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#insertPlaceholder(com.google.gwt.dom.client.Element, int, int, Orientation) */ public void insertPlaceholder(Element placeholder, int x, int y, Orientation orientation) { m_placeholder = placeholder; repositionPlaceholder(x, y, orientation); } /** * Returns if the list will always return <code>true</code> on check target requests within drag and drop.<p> * * @return <code>true</code> if take all is enabled for drag and drop */ public boolean isDNDTakeAll() { return m_takeAll; } /** * Checks if dropping is enabled.<p> * * @return <code>true</code> if dropping is enabled */ public boolean isDropEnabled() { return m_dropEnabled; } /** * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#onDrop(org.opencms.gwt.client.dnd.I_CmsDraggable) */ public void onDrop(I_CmsDraggable draggable) { return; } /** * @see com.google.gwt.user.client.ui.ComplexPanel#remove(com.google.gwt.user.client.ui.Widget) */ @Override public boolean remove(Widget w) { boolean result = super.remove(w); if (result && (w instanceof I_CmsListItem)) { String id = ((I_CmsListItem)w).getId(); if (id != null) { m_items.remove(id); } } return result; } /** * @see org.opencms.gwt.client.ui.dnd.I_CmsDropTarget#setPlaceholder(int, int, CmsDropEvent) */ // public CmsDropPosition setPlaceholder(int x, int y, CmsDropEvent event) { // // Element targetElement = getElement(); // // TODO: use binary search instead of this linear search, will improve performance from O(n) to O(log(n))! // for (int index = 0; index < targetElement.getChildCount(); index++) { // Node node = targetElement.getChild(index); // if (!(node instanceof Element)) { // continue; // } // Element child = (Element)node; // // String positioning = child.getStyle().getPosition(); // if (positioning.equals(Position.ABSOLUTE.getCssName()) || positioning.equals(Position.FIXED.getCssName())) { // // only not 'position:absolute' elements into account, // // not visible children will be excluded in the next condition // continue; // } // // // check if the mouse pointer is within the width of the element // int left = CmsDomUtil.getRelativeX(x, child); // if ((left <= 0) || (left >= child.getOffsetWidth())) { // continue; // } // // // check if the mouse pointer is within the height of the element // int top = CmsDomUtil.getRelativeY(y, child); // int height = child.getOffsetHeight(); // if ((top <= 0) || (top >= height)) { // continue; // } // // CmsDropPosition position = null; // boolean checkPos = ((event.getTarget() == this) && (event.getPosition() != null)); // I_CmsDraggable draggable = event.getDraggable(); // if (draggable.getElement() == child) { // // this case occurs when start dragging // if (checkPos && (event.getPosition().getPosition() == index)) { // // nothing has changed // return null; // } // // insert place holder before the current child // removePlaceholder(); // m_placeholder = draggable.getPlaceHolder(this); // targetElement.insertBefore(m_placeholder, child); // position = new CmsDropPosition(null, index, null, m_placeholder); // } else if (top < height / 2) { // // the mouse pointer is within the upper half of the element // if (checkPos && (event.getPosition().getPosition() == index)) { // // nothing has changed // return null; // } // removePlaceholder(); // m_placeholder = draggable.getPlaceHolder(this); // targetElement.insertBefore(m_placeholder, child); // position = new CmsDropPosition(null, index, null, m_placeholder); // } else { // // the mouse pointer is within the bottom half of the element // if (checkPos && (event.getPosition().getPosition() == index + 1)) { // // nothing has changed // return null; // } // removePlaceholder(); // m_placeholder = draggable.getPlaceHolder(this); // targetElement.insertAfter(m_placeholder, child); // position = new CmsDropPosition(null, index + 1, null, m_placeholder); // } // if (draggable instanceof CmsListItem) { // // add name if available // String name = ((CmsListItem)draggable).getId(); // position.setName(name); // } // return position; // } // return null; // } /** * Removes an item from the list.<p> * * @param item the item to remove * * @return the removed item * * @see #remove(Widget) */ public I removeItem(I item) { remove((Widget)item); return item; } /** * Removes an item from the list.<p> * * @param itemId the id of the item to remove * * @return the removed item * * @see #remove(Widget) */ public I removeItem(String itemId) { I item = m_items.get(itemId); remove((Widget)item); return item; } /** * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#removePlaceholder() */ public void removePlaceholder() { if (m_placeholder == null) { return; } m_placeholder.removeFromParent(); m_placeholder = null; m_placeholderIndex = -1; } /** * @see org.opencms.gwt.client.dnd.I_CmsDropTarget#repositionPlaceholder(int, int, Orientation) */ public void repositionPlaceholder(int x, int y, Orientation orientation) { switch (orientation) { case HORIZONTAL: m_placeholderIndex = CmsDomUtil.positionElementInside( m_placeholder, getElement(), m_placeholderIndex, x, -1); break; case VERTICAL: m_placeholderIndex = CmsDomUtil.positionElementInside( m_placeholder, getElement(), m_placeholderIndex, -1, y); break; case ALL: default: m_placeholderIndex = CmsDomUtil.positionElementInside( m_placeholder, getElement(), m_placeholderIndex, x, y); break; } } /** * Sets the drag'n drop handler.<p> * * @param handler the handler to set */ public void setDNDHandler(CmsDNDHandler handler) { m_dndHandler = handler; } /** * Sets if the list will always return <code>true</code> on check target requests within drag and drop.<p> * * @param takeAll <code>true</code> to enable take all for drag and drop */ public void setDNDTakeAll(boolean takeAll) { m_takeAll = takeAll; } /** * Enables/disables dropping.<p> * * @param enabled <code>true</code> to enable, or <code>false</code> to disable */ public void setDropEnabled(boolean enabled) { m_dropEnabled = enabled; } /** * @see org.opencms.gwt.client.ui.I_CmsTruncable#truncate(java.lang.String, int) */ public void truncate(String textMetricsPrefix, int widgetWidth) { m_childWidth = widgetWidth; for (Widget item : this) { if (item instanceof I_CmsTruncable) { ((I_CmsTruncable)item).truncate(textMetricsPrefix, widgetWidth); } } m_tmPrefix = textMetricsPrefix; } /** * Changes the id for the given item.<p> * * @param item the item to change the id for * @param id the new id */ protected void changeId(I item, String id) { if (m_items.remove(item.getId()) != null) { m_items.put(id, item); } } /** * Registers the given item on this list.<p> * * @param item the item to register */ protected void registerItem(I item) { if (item.getId() != null) { m_items.put(item.getId(), item); } if (m_tmPrefix != null) { item.truncate(m_tmPrefix, m_childWidth); } } /** * Sets the current drag'n drop place holder.<p> * * @param placeholder the element to set as place holder */ protected void setPlaceholder(Element placeholder) { removePlaceholder(); m_placeholder = placeholder; } }