001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.components.fileselect; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.main.CmsLog; 034import org.opencms.ui.CmsVaadinUtils; 035import org.opencms.ui.components.CmsFileTable; 036import org.opencms.ui.components.CmsResourceTableProperty; 037import org.opencms.ui.components.OpenCmsTheme; 038import org.opencms.ui.util.I_CmsItemSorter; 039import org.opencms.util.CmsFileUtil; 040import org.opencms.util.CmsUUID; 041 042import java.util.Collection; 043import java.util.HashSet; 044import java.util.List; 045import java.util.Set; 046 047import org.apache.commons.logging.Log; 048 049import com.google.common.base.Predicate; 050import com.google.common.base.Predicates; 051import com.google.common.collect.ComparisonChain; 052import com.google.common.collect.Lists; 053import com.vaadin.ui.themes.ValoTheme; 054import com.vaadin.v7.data.Container; 055import com.vaadin.v7.data.Item; 056import com.vaadin.v7.data.util.DefaultItemSorter; 057import com.vaadin.v7.event.ItemClickEvent; 058import com.vaadin.v7.event.ItemClickEvent.ItemClickListener; 059import com.vaadin.v7.shared.ui.label.ContentMode; 060import com.vaadin.v7.ui.Label; 061import com.vaadin.v7.ui.Table; 062import com.vaadin.v7.ui.Tree.CollapseEvent; 063import com.vaadin.v7.ui.Tree.CollapseListener; 064import com.vaadin.v7.ui.Tree.ExpandEvent; 065import com.vaadin.v7.ui.Tree.ExpandListener; 066import com.vaadin.v7.ui.TreeTable; 067 068/** 069 * Tree subclass used to display VFS resource trees.<p> 070 */ 071public class CmsResourceTreeTable extends TreeTable { 072 073 /** 074 * Extends the default sorting to differentiate between files and folder when sorting by name.<p> 075 * Also allows sorting by navPos property for the Resource icon column.<p> 076 */ 077 public static class FileSorter extends DefaultItemSorter implements I_CmsItemSorter { 078 079 /** The serial version id. */ 080 private static final long serialVersionUID = 1L; 081 082 /** 083 * @see org.opencms.ui.util.I_CmsItemSorter#getSortableContainerPropertyIds(com.vaadin.v7.data.Container) 084 */ 085 public Collection<?> getSortableContainerPropertyIds(Container container) { 086 087 Set<Object> result = new HashSet<Object>(); 088 result.add(CAPTION_FOLDERS); 089 result.add(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT); 090 return result; 091 } 092 093 /** 094 * @see com.vaadin.v7.data.util.DefaultItemSorter#compareProperty(java.lang.Object, boolean, com.vaadin.v7.data.Item, com.vaadin.v7.data.Item) 095 */ 096 @Override 097 protected int compareProperty(Object propertyId, boolean sortDirection, Item item1, Item item2) { 098 099 if (CAPTION_FOLDERS.equals(propertyId)) { 100 Boolean isFolder1 = (Boolean)item1.getItemProperty( 101 CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue(); 102 Boolean isFolder2 = (Boolean)item2.getItemProperty( 103 CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue(); 104 String name1 = (String)(item1.getItemProperty( 105 CmsResourceTableProperty.PROPERTY_RESOURCE_NAME).getValue()); 106 name1 = CmsFileUtil.removeTrailingSeparator(name1); 107 String name2 = (String)(item2.getItemProperty( 108 CmsResourceTableProperty.PROPERTY_RESOURCE_NAME).getValue()); 109 name2 = CmsFileUtil.removeTrailingSeparator(name2); 110 return (sortDirection ? 1 : -1) 111 * ComparisonChain.start().compareTrueFirst( 112 isFolder1.booleanValue(), 113 isFolder2.booleanValue()).compare(name1, name2).result(); 114 } else if (CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT.equals(propertyId) 115 && (item1.getItemProperty(CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION) != null)) { 116 int result; 117 Float pos1 = (Float)item1.getItemProperty( 118 CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION).getValue(); 119 Float pos2 = (Float)item2.getItemProperty( 120 CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION).getValue(); 121 if (pos1 == null) { 122 result = pos2 == null 123 ? compareProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, true, item1, item2) 124 : 1; 125 } else { 126 result = pos2 == null ? -1 : Float.compare(pos1.floatValue(), pos2.floatValue()); 127 } 128 if (!sortDirection) { 129 result = result * (-1); 130 } 131 return result; 132 } 133 return super.compareProperty(propertyId, sortDirection, item1, item2); 134 } 135 } 136 137 /** 138 * The style generator.<p> 139 */ 140 public class StyleGenerator implements CellStyleGenerator { 141 142 /** The serial version id. */ 143 private static final long serialVersionUID = 1L; 144 145 /** 146 * @see com.vaadin.ui.Table.CellStyleGenerator#getStyle(com.vaadin.ui.Table, java.lang.Object, java.lang.Object) 147 */ 148 public String getStyle(Table source, Object itemId, Object propertyId) { 149 150 Item item = source.getContainerDataSource().getItem(itemId); 151 String style = CmsFileTable.getStateStyle(item); 152 if (!isSelectable(item)) { 153 style += " " + OpenCmsTheme.DISABLED; 154 } 155 if (CAPTION_FOLDERS.equals(propertyId)) { 156 style += " o-name-column"; 157 } else if (CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT == propertyId) { 158 if ((item.getItemProperty(CmsResourceTableProperty.PROPERTY_IN_NAVIGATION) != null) 159 && ((Boolean)item.getItemProperty( 160 CmsResourceTableProperty.PROPERTY_IN_NAVIGATION).getValue()).booleanValue()) { 161 style += " " + OpenCmsTheme.IN_NAVIGATION; 162 } 163 } 164 return style; 165 } 166 } 167 168 /** The logger instance for this class. */ 169 private static final Log LOG = CmsLog.getLog(CmsResourceTreeTable.class); 170 171 /** Serial version id. */ 172 private static final long serialVersionUID = 1L; 173 174 /** The folder caption column id. */ 175 public static final String CAPTION_FOLDERS = "CAPTION_FOLDERS"; 176 177 /** The CMS context. */ 178 CmsObject m_cms; 179 180 /** The list of selection handlers. */ 181 private List<I_CmsSelectionHandler<CmsResource>> m_resourceSelectionHandlers = Lists.newArrayList(); 182 183 /** The root resource. */ 184 private CmsResource m_root; 185 186 /** 187 * Predicate which can be used to prevent selection of a node (this means the select handler will not be called 188 * if this returns false, but the Vaadin selection mechanism will still mark it as selected. 189 */ 190 private Predicate<Item> m_selectionFilter = Predicates.alwaysTrue(); 191 192 /** 193 * Creates a new instance.<p> 194 * 195 * @param cms the CMS context 196 * @param root the root resource 197 * @param filter the resource filter 198 */ 199 public CmsResourceTreeTable(CmsObject cms, CmsResource root, CmsResourceFilter filter) { 200 201 this(cms, root, new CmsResourceTreeContainer(filter)); 202 } 203 204 /** 205 * Creates a new instance.<p> 206 * 207 * @param cms the CMS context 208 * @param root the root resource 209 * @param container the data container for the tree 210 */ 211 public CmsResourceTreeTable(CmsObject cms, CmsResource root, CmsResourceTreeContainer container) { 212 213 m_cms = cms; 214 m_root = root; 215 FileSorter sorter = new FileSorter(); 216 sorter.setSortProperties( 217 container, 218 new Object[] {CAPTION_FOLDERS, CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT}, 219 new boolean[] {true, true}); 220 container.setItemSorter(sorter); 221 setContainerDataSource(container); 222 ColumnGenerator captionGenerator = new ColumnGenerator() { 223 224 private static final long serialVersionUID = 1L; 225 226 public Object generateCell(Table source, Object itemId, Object columnId) { 227 228 if (CAPTION_FOLDERS.equals(columnId)) { 229 String html = (String)source.getContainerDataSource().getItem(itemId).getItemProperty( 230 CmsResourceTableProperty.PROPERTY_TREE_CAPTION).getValue(); 231 Label label = new Label(html, ContentMode.HTML); 232 label.setStyleName("o-tree-table-caption"); 233 return label; 234 } else { 235 return null; 236 } 237 } 238 }; 239 addGeneratedColumn(CAPTION_FOLDERS, captionGenerator); 240 setVisibleColumns(CAPTION_FOLDERS, CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT); 241 setItemCaptionPropertyId(CAPTION_FOLDERS); 242 setColumnHeader( 243 CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT, 244 CmsVaadinUtils.getMessageText(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT.getHeaderKey())); 245 setColumnHeader( 246 CAPTION_FOLDERS, 247 CmsVaadinUtils.getMessageText(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME.getHeaderKey())); 248 249 // hide vertical and horizontal lines and disable alternating row background 250 addStyleName(ValoTheme.TABLE_NO_STRIPES); 251 addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES); 252 addStyleName(OpenCmsTheme.FILE_TREE); 253 addExpandListener(new ExpandListener() { 254 255 private static final long serialVersionUID = 1L; 256 257 public void nodeExpand(ExpandEvent event) { 258 259 getTreeContainer().readTreeLevel(m_cms, (CmsUUID)event.getItemId()); 260 getTreeContainer().updateSort(); 261 markAsDirtyRecursive(); // required so open / close arrows on folders without contents are rendered correctly 262 } 263 264 }); 265 266 addCollapseListener(new CollapseListener() { 267 268 private static final long serialVersionUID = 1L; 269 270 public void nodeCollapse(CollapseEvent event) { 271 272 getTreeContainer().removeChildren((CmsUUID)event.getItemId()); 273 } 274 }); 275 276 addItemClickListener(new ItemClickListener() { 277 278 private static final long serialVersionUID = 1L; 279 280 public void itemClick(ItemClickEvent event) { 281 282 if (isSelectable(event.getItem())) { 283 CmsResource resource = (CmsResource)(event.getItem().getItemProperty( 284 CmsResourceTreeContainer.PROPERTY_RESOURCE).getValue()); 285 handleSelection(resource); 286 } 287 } 288 }); 289 setCellStyleGenerator(new StyleGenerator()); 290 291 getTreeContainer().addTreeItem(cms, m_root, null); 292 try { 293 setCollapsed(m_root.getStructureId(), false); 294 markAsDirtyRecursive(); 295 296 } catch (Exception e) { 297 LOG.error(e.getLocalizedMessage(), e); 298 } 299 } 300 301 /** 302 * Adds a resource selection handler.<p> 303 * 304 * @param handler the resource selection handler 305 */ 306 public void addResourceSelectionHandler(I_CmsSelectionHandler<CmsResource> handler) { 307 308 m_resourceSelectionHandlers.add(handler); 309 } 310 311 /** 312 * Expands the item with the given id.<p> 313 * 314 * @param itemId the item id 315 */ 316 public void expandItem(CmsUUID itemId) { 317 318 setCollapsed(itemId, false); 319 } 320 321 /** 322 * Gets the tree container.<p> 323 * 324 * @return the tree container 325 */ 326 public CmsResourceTreeContainer getTreeContainer() { 327 328 return (CmsResourceTreeContainer)getContainerDataSource(); 329 } 330 331 /** 332 * Removes the given resource selection handler.<p> 333 * 334 * @param handler the resource selection handler 335 */ 336 public void removeResourceSelectionHandler(I_CmsSelectionHandler<CmsResource> handler) { 337 338 m_resourceSelectionHandlers.remove(handler); 339 } 340 341 /** 342 * Sets the selection filter, a predicate that, by returning false for an item, can veto a tree entry selection.<p> 343 * 344 * @param selectionFilter the selection filter 345 */ 346 public void setSelectionFilter(Predicate<Item> selectionFilter) { 347 348 m_selectionFilter = selectionFilter; 349 } 350 351 /** 352 * Shows the sitemap view.<p> 353 * 354 * @param showSitemap <code>true</code> to show the sitemap view 355 */ 356 public void showSitemapView(boolean showSitemap) { 357 358 if (showSitemap) { 359 setSortContainerPropertyId(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT); 360 setSortAscending(true); 361 } else { 362 setSortContainerPropertyId(CAPTION_FOLDERS); 363 setSortAscending(true); 364 } 365 } 366 367 /** 368 * Handles resource selection.<p> 369 * 370 * @param resource the selected resource 371 */ 372 void handleSelection(CmsResource resource) { 373 374 for (I_CmsSelectionHandler<CmsResource> handler : m_resourceSelectionHandlers) { 375 handler.onSelection(resource); 376 } 377 } 378 379 /** 380 * Returns whether the given item is selectable.<p> 381 * 382 * @param item the item to check 383 * 384 * @return <code>true</code> if the given item is selectable 385 */ 386 boolean isSelectable(Item item) { 387 388 return m_selectionFilter.apply(item); 389 } 390}