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.gwt.client.ui.input.category; 029 030import org.opencms.gwt.client.Messages; 031import org.opencms.gwt.client.ui.CmsList; 032import org.opencms.gwt.client.ui.CmsPushButton; 033import org.opencms.gwt.client.ui.CmsScrollPanel; 034import org.opencms.gwt.client.ui.CmsSimpleListItem; 035import org.opencms.gwt.client.ui.I_CmsButton; 036import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 037import org.opencms.gwt.client.ui.I_CmsButton.Size; 038import org.opencms.gwt.client.ui.I_CmsListItem; 039import org.opencms.gwt.client.ui.I_CmsTruncable; 040import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle; 041import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 042import org.opencms.gwt.client.ui.input.CmsCategoryField; 043import org.opencms.gwt.client.ui.input.CmsCheckBox; 044import org.opencms.gwt.client.ui.input.CmsSelectBox; 045import org.opencms.gwt.client.ui.input.CmsTextBox; 046import org.opencms.gwt.client.ui.tree.CmsTreeItem; 047import org.opencms.gwt.shared.CmsCategoryBean; 048import org.opencms.gwt.shared.CmsCategoryTreeEntry; 049import org.opencms.util.CmsStringUtil; 050 051import java.util.ArrayList; 052import java.util.Collection; 053import java.util.Collections; 054import java.util.HashMap; 055import java.util.Iterator; 056import java.util.LinkedHashMap; 057import java.util.List; 058import java.util.Map; 059 060import com.google.gwt.core.client.GWT; 061import com.google.gwt.core.client.Scheduler; 062import com.google.gwt.core.client.Scheduler.ScheduledCommand; 063import com.google.gwt.dom.client.Style; 064import com.google.gwt.dom.client.Style.Float; 065import com.google.gwt.dom.client.Style.Unit; 066import com.google.gwt.event.dom.client.ClickEvent; 067import com.google.gwt.event.dom.client.ClickHandler; 068import com.google.gwt.event.logical.shared.HasValueChangeHandlers; 069import com.google.gwt.event.logical.shared.ValueChangeEvent; 070import com.google.gwt.event.logical.shared.ValueChangeHandler; 071import com.google.gwt.event.shared.HandlerRegistration; 072import com.google.gwt.uibinder.client.UiBinder; 073import com.google.gwt.uibinder.client.UiField; 074import com.google.gwt.user.client.Timer; 075import com.google.gwt.user.client.rpc.IsSerializable; 076import com.google.gwt.user.client.ui.Composite; 077import com.google.gwt.user.client.ui.FlowPanel; 078import com.google.gwt.user.client.ui.HasText; 079import com.google.gwt.user.client.ui.Label; 080import com.google.gwt.user.client.ui.Widget; 081 082/** 083 * Builds the category tree.<p> 084 * */ 085public class CmsCategoryTree extends Composite implements I_CmsTruncable, HasValueChangeHandlers<List<String>> { 086 087 /** Sorting parameters. */ 088 public enum SortParams implements IsSerializable { 089 090 /** Date last modified ascending. */ 091 dateLastModified_asc, 092 093 /** Date last modified descending. */ 094 dateLastModified_desc, 095 096 /** Resource path ascending sorting. */ 097 path_asc, 098 099 /** Resource path descending sorting.*/ 100 path_desc, 101 102 /** Title ascending sorting. */ 103 title_asc, 104 105 /** Title descending sorting. */ 106 title_desc, 107 108 /** Tree.*/ 109 tree, 110 111 /** Resource type ascending sorting. */ 112 type_asc, 113 114 /** Resource type descending sorting. */ 115 type_desc; 116 } 117 118 /** 119 * @see com.google.gwt.uibinder.client.UiBinder 120 */ 121 interface I_CmsCategoryTreeUiBinder extends UiBinder<Widget, CmsCategoryTree> { 122 // GWT interface, nothing to do here 123 } 124 125 /** 126 * Inner class for select box handler.<p> 127 */ 128 private class CategoryValueChangeHandler implements ValueChangeHandler<String> { 129 130 /** 131 * Default Constructor.<p> 132 */ 133 public CategoryValueChangeHandler() { 134 135 // nothing to do 136 } 137 138 /** 139 * Will be triggered if the value in the select box changes.<p> 140 * 141 * @see com.google.gwt.event.logical.shared.ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent) 142 */ 143 public void onValueChange(ValueChangeEvent<String> event) { 144 145 cancelQuickFilterTimer(); 146 if (event.getSource() == m_sortSelectBox) { 147 148 List<CmsTreeItem> categories = new ArrayList<CmsTreeItem>(m_categories.values()); 149 SortParams sort = SortParams.valueOf(event.getValue()); 150 sort(categories, sort); 151 } 152 if ((event.getSource() == m_quickSearch)) { 153 if (!m_listView) { 154 m_listView = true; 155 m_sortSelectBox.setFormValueAsString(SortParams.title_asc.name()); 156 } 157 if (hasQuickFilter()) { 158 159 if ((CmsStringUtil.isEmptyOrWhitespaceOnly(event.getValue()) || (event.getValue().length() >= 2))) { 160 // only act if filter length is at least 3 characters or empty 161 scheduleQuickFilterTimer(); 162 } 163 } else { 164 checkQuickSearchStatus(); 165 } 166 } 167 } 168 169 } 170 171 /** 172 * Inner class for check box handler.<p> 173 */ 174 private class CheckBoxValueChangeHandler implements ValueChangeHandler<Boolean> { 175 176 /** Path of the TreeItem. */ 177 private CmsTreeItem m_item; 178 179 /** 180 * Default constructor.<p> 181 * @param item The CmsTreeItem of this check box 182 */ 183 public CheckBoxValueChangeHandler(CmsTreeItem item) { 184 185 m_item = item; 186 187 } 188 189 /** 190 * Is triggered if an check box is selected or deselected.<p> 191 * 192 * @param event The event that is triggered 193 */ 194 public void onValueChange(ValueChangeEvent<Boolean> event) { 195 196 toggleSelection(m_item, false); 197 } 198 199 } 200 201 /** 202 * Inner class for check box handler.<p> 203 */ 204 private class DataValueClickHander implements ClickHandler { 205 206 /** The TreeItem. */ 207 private CmsTreeItem m_item; 208 209 /** Constructor to set the right CmsTreeItem for this handler.<p> 210 * 211 * @param item the CmsTreeItem of this Handler 212 */ 213 public DataValueClickHander(CmsTreeItem item) { 214 215 m_item = item; 216 } 217 218 /** 219 * Is triggered if the DataValue widget is clicked.<p> 220 * If its check box was selected the click will deselect this box otherwise it will select it. 221 * 222 * @param event The event that is triggered 223 * */ 224 public void onClick(ClickEvent event) { 225 226 if (isEnabled()) { 227 toggleSelection(m_item, true); 228 } 229 } 230 231 } 232 233 /** The filtering delay. */ 234 private static final int FILTER_DELAY = 100; 235 236 /** Text metrics key. */ 237 private static final String TM_GALLERY_SORT = "gallerySort"; 238 239 /** The ui-binder instance for this class. */ 240 private static I_CmsCategoryTreeUiBinder uiBinder = GWT.create(I_CmsCategoryTreeUiBinder.class); 241 242 /** Map of categories. */ 243 protected Map<String, CmsTreeItem> m_categories; 244 245 /** Map from category paths to the paths of their children. */ 246 protected Map<String, List<String>> m_childrens; 247 248 /** A label for displaying additional information about the tab. */ 249 protected HasText m_infoLabel; 250 251 /** Vale to store the widget mode. True means the single selection. */ 252 protected boolean m_isSingleSelection; 253 254 /** Vale to store the view mode. True means the list view. */ 255 protected boolean m_listView; 256 257 /** The option panel. */ 258 @UiField 259 protected FlowPanel m_options; 260 261 /** The quick search box. */ 262 protected CmsTextBox m_quickSearch; 263 264 /** List of categories selected from the server. */ 265 protected List<CmsCategoryTreeEntry> m_resultList; 266 267 /** List of categories. */ 268 protected CmsList<CmsTreeItem> m_scrollList; 269 270 /** The quick search button. */ 271 protected CmsPushButton m_searchButton; 272 273 /** 274 * List of all selected categories. 275 * 276 * <p>IMPORTANT: This may unfortunately contain either category paths or category site paths. 277 * */ 278 protected Collection<String> m_selectedCategories; 279 280 /** Result string for single selection. */ 281 protected String m_singleResult = ""; 282 283 /** The select box to change the sort order. */ 284 protected CmsSelectBox m_sortSelectBox; 285 286 /** The scroll panel. */ 287 @UiField 288 CmsScrollPanel m_list; 289 290 /** The main panel. */ 291 @UiField 292 FlowPanel m_tab; 293 294 /** The disable reason, will be displayed as check box title. */ 295 private String m_disabledReason; 296 297 /** The quick filter timer. */ 298 private Timer m_filterTimer; 299 300 /** The category selection enabled flag. */ 301 private boolean m_isEnabled; 302 303 /** Flag, indicating if the category tree should be collapsed when shown first. */ 304 private boolean m_showCollapsed; 305 306 /** 307 * Default Constructor.<p> 308 */ 309 public CmsCategoryTree() { 310 311 uiBinder.createAndBindUi(this); 312 initWidget(uiBinder.createAndBindUi(this)); 313 m_isEnabled = true; 314 } 315 316 /** 317 * Constructor to collect all categories and build a view tree.<p> 318 * 319 * @param selectedCategories A list of all selected categories 320 * @param height The height of this widget 321 * @param isSingleValue Sets the modes of this widget 322 * @param categories the categories 323 **/ 324 public CmsCategoryTree( 325 Collection<String> selectedCategories, 326 int height, 327 boolean isSingleValue, 328 List<CmsCategoryTreeEntry> categories) { 329 330 this(selectedCategories, height, isSingleValue, categories, false); 331 } 332 333 /** 334 * Constructor to collect all categories and build a view tree.<p> 335 * 336 * @param selectedCategories A list of all selected categories 337 * @param height The height of this widget 338 * @param isSingleValue Sets the modes of this widget 339 * @param categories the categories 340 * @param showCollapsed if true, the category tree will be collapsed when opened. 341 **/ 342 public CmsCategoryTree( 343 Collection<String> selectedCategories, 344 int height, 345 boolean isSingleValue, 346 List<CmsCategoryTreeEntry> categories, 347 boolean showCollapsed) { 348 349 this(); 350 m_isSingleSelection = isSingleValue; 351 addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().categoryItem()); 352 m_list.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().categoryScrollPanel()); 353 m_selectedCategories = selectedCategories; 354 Iterator<String> it = selectedCategories.iterator(); 355 while (it.hasNext()) { 356 m_singleResult = it.next(); 357 } 358 m_scrollList = createScrollList(); 359 m_list.setHeight(height + "px"); 360 m_resultList = categories; 361 m_list.add(m_scrollList); 362 m_showCollapsed = showCollapsed; 363 m_childrens = new HashMap<>(); 364 m_categories = new HashMap<>(); 365 updateContentTree(); 366 normalizeSelectedCategories(); 367 init(); 368 } 369 370 /** 371 * Adds children item to the category tree and select the categories.<p> 372 * 373 * @param parent the parent item 374 * @param children the list of children 375 * @param selectedCategories the list of categories to select 376 */ 377 public void addChildren( 378 CmsTreeItem parent, 379 List<CmsCategoryTreeEntry> children, 380 Collection<String> selectedCategories) { 381 382 if (children != null) { 383 for (CmsCategoryTreeEntry child : children) { 384 // set the category tree item and add to parent tree item 385 CmsTreeItem treeItem = buildTreeItem(child, selectedCategories); 386 m_childrens.get(parent.getId()).add(treeItem.getId()); 387 m_childrens.put(treeItem.getId(), new ArrayList<>(child.getChildren().size())); 388 if ((selectedCategories != null) && selectedCategories.contains(child.getPath())) { 389 openWithParents(parent); 390 391 } 392 if (m_isSingleSelection) { 393 if (treeItem.getCheckBox().isChecked()) { 394 parent.getCheckBox().setChecked(false); 395 } 396 } 397 parent.addChild(treeItem); 398 addChildren(treeItem, child.getChildren(), selectedCategories); 399 } 400 } 401 } 402 403 /** 404 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 405 */ 406 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<List<String>> handler) { 407 408 return addHandler(handler, ValueChangeEvent.getType()); 409 } 410 411 /** 412 * Disabled the category selection.<p> 413 * 414 * @param disabledReason the disable reason, will be displayed as check box title 415 */ 416 public void disable(String disabledReason) { 417 418 if (m_isEnabled 419 || (CmsStringUtil.isNotEmptyOrWhitespaceOnly(disabledReason) && !disabledReason.equals(m_disabledReason))) { 420 m_isEnabled = false; 421 m_disabledReason = disabledReason; 422 m_scrollList.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().disabled()); 423 setListEnabled(m_scrollList, false, disabledReason); 424 } 425 } 426 427 /** 428 * Enables the category selection.<p> 429 */ 430 public void enable() { 431 432 if (!m_isEnabled) { 433 m_isEnabled = true; 434 m_disabledReason = null; 435 m_scrollList.removeStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().disabled()); 436 setListEnabled(m_scrollList, true, null); 437 } 438 } 439 440 /** 441 * Represents a value change event.<p> 442 */ 443 public void fireValueChange() { 444 445 ValueChangeEvent.fire(this, getAllSelected()); 446 } 447 448 /** 449 * Returns a list of all selected values.<p> 450 * 451 * @return a list of selected values 452 */ 453 public List<String> getAllSelected() { 454 455 List<String> result = new ArrayList<String>(); 456 for (String cat : m_selectedCategories) { 457 result.add(m_categories.get(cat).getId()); 458 } 459 return result; 460 } 461 462 /** 463 * Returns a list of all selected values as Sidepath.<p> 464 * 465 * @return a list of selected values 466 */ 467 public List<String> getAllSelectedSitePath() { 468 469 List<String> result = new ArrayList<String>(); 470 for (String cat : m_selectedCategories) { 471 result.add(((CmsDataValue)m_categories.get(cat).getMainWidget()).getParameter(2)); 472 } 473 return result; 474 } 475 476 /** 477 * Returns the scrollpanel of this widget.<p> 478 * 479 * @return CmsScrollPanel the scrollpanel of this widget 480 * */ 481 public CmsScrollPanel getScrollPanel() { 482 483 return m_list; 484 } 485 486 /** 487 * Returns the last selected value.<p> 488 * 489 * @return the last selected value 490 */ 491 public List<String> getSelected() { 492 493 List<String> result = new ArrayList<String>(); 494 result.add( 495 m_singleResult.isEmpty() 496 ? "" 497 : ((CmsDataValue)m_categories.get(m_singleResult).getMainWidget()).getParameter(2)); 498 return result; 499 } 500 501 /** 502 * Returns if the category selection is enabled.<p> 503 * 504 * @return <code>true</code> if the category selection is enabled 505 */ 506 public boolean isEnabled() { 507 508 return m_isEnabled; 509 } 510 511 /** 512 * Goes up the tree and opens the parents of the item.<p> 513 * 514 * @param item the child item to start from 515 */ 516 public void openWithParents(CmsTreeItem item) { 517 518 if (item != null) { 519 item.setOpen(true); 520 openWithParents(item.getParentItem()); 521 } 522 } 523 524 /** 525 * Shows the tab list is empty label.<p> 526 */ 527 public void showIsEmptyLabel() { 528 529 CmsSimpleListItem item = new CmsSimpleListItem(); 530 Label isEmptyLabel = new Label(Messages.get().key(Messages.GUI_CATEGORIES_IS_EMPTY_0)); 531 item.add(isEmptyLabel); 532 m_scrollList.add(item); 533 } 534 535 /** 536 * @see org.opencms.gwt.client.ui.I_CmsTruncable#truncate(java.lang.String, int) 537 */ 538 public void truncate(String textMetricsKey, int clientWidth) { 539 540 m_scrollList.truncate(textMetricsKey, clientWidth); 541 } 542 543 /** 544 * Updates the content of the categories list.<p> 545 * 546 * @param treeItemsToShow the updates list of categories tree item beans 547 */ 548 public void updateContentList(List<CmsTreeItem> treeItemsToShow) { 549 550 m_scrollList.clearList(); 551 if ((treeItemsToShow != null) && !treeItemsToShow.isEmpty()) { 552 for (CmsTreeItem dataValue : treeItemsToShow) { 553 dataValue.removeOpener(); 554 m_scrollList.add(dataValue); 555 CmsScrollPanel scrollparent = (CmsScrollPanel)m_scrollList.getParent(); 556 scrollparent.onResizeDescendant(); 557 } 558 } else { 559 showIsEmptyLabel(); 560 } 561 scheduleResize(); 562 } 563 564 /** 565 * Updates the content of the categories tree.<p> 566 */ 567 public void updateContentTree() { 568 569 m_scrollList.clearList(); 570 m_childrens.clear(); 571 m_categories.clear(); 572 if ((m_resultList != null) && !m_resultList.isEmpty()) { 573 // add the first level and children 574 for (CmsCategoryTreeEntry category : m_resultList) { 575 // set the category tree item and add to list 576 CmsTreeItem treeItem = buildTreeItem(category, m_selectedCategories); 577 m_childrens.put(treeItem.getId(), new ArrayList<>(category.getChildren().size())); 578 addChildren(treeItem, category.getChildren(), m_selectedCategories); 579 if (!category.getPath().isEmpty() || (treeItem.getChildCount() > 0)) { 580 m_scrollList.add(treeItem); 581 } 582 treeItem.setOpen(!m_showCollapsed); 583 } 584 } else { 585 showIsEmptyLabel(); 586 } 587 scheduleResize(); 588 } 589 590 /** 591 * Cancels the quick filter timer.<p> 592 */ 593 protected void cancelQuickFilterTimer() { 594 595 if (m_filterTimer != null) { 596 m_filterTimer.cancel(); 597 } 598 } 599 600 /** 601 * Checks the quick search input and enables/disables the search button accordingly.<p> 602 */ 603 protected void checkQuickSearchStatus() { 604 605 if ((m_quickSearch != null) && (m_searchButton != null)) { 606 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_quickSearch.getFormValueAsString())) { 607 m_searchButton.enable(); 608 } else { 609 m_searchButton.disable("Enter a search query"); 610 } 611 } 612 } 613 614 /** 615 * Creates the quick search/finder box.<p> 616 */ 617 protected void createQuickBox() { 618 619 m_quickSearch = new CmsTextBox(); 620 // m_quickFilter.setVisible(hasQuickFilter()); 621 m_quickSearch.getElement().getStyle().setFloat(Float.RIGHT); 622 m_quickSearch.setTriggerChangeOnKeyPress(true); 623 m_quickSearch.setGhostValue(Messages.get().key(Messages.GUI_QUICK_FINDER_SEARCH_0), true); 624 m_quickSearch.setGhostModeClear(true); 625 m_options.insert(m_quickSearch, 0); 626 m_searchButton = new CmsPushButton(); 627 m_searchButton.setImageClass(I_CmsButton.SEARCH); 628 m_searchButton.setButtonStyle(ButtonStyle.FONT_ICON, null); 629 m_searchButton.setSize(Size.small); 630 m_searchButton.getElement().getStyle().setFloat(Style.Float.RIGHT); 631 m_searchButton.getElement().getStyle().setMarginTop(4, Unit.PX); 632 m_searchButton.getElement().getStyle().setMarginLeft(4, Unit.PX); 633 m_options.insert(m_searchButton, 0); 634 m_quickSearch.addValueChangeHandler(new CategoryValueChangeHandler()); 635 636 m_filterTimer = new Timer() { 637 638 @Override 639 public void run() { 640 641 quickSearch(); 642 643 } 644 }; 645 m_searchButton.setTitle(Messages.get().key(Messages.GUI_QUICK_FINDER_SEARCH_0)); 646 647 m_searchButton.addClickHandler(new ClickHandler() { 648 649 public void onClick(ClickEvent arg0) { 650 651 quickSearch(); 652 } 653 }); 654 } 655 656 /** 657 * Creates the list which should contain the list items of the tab.<p> 658 * 659 * @return the newly created list widget 660 */ 661 protected CmsList<CmsTreeItem> createScrollList() { 662 663 return new CmsList<CmsTreeItem>(); 664 } 665 666 /** 667 * Gets the filtered list of categories.<p> 668 * 669 * @param filter the search string to use for filtering 670 * 671 * @return the filtered category beans 672 */ 673 protected List<CmsTreeItem> getFilteredCategories(String filter) { 674 675 List<CmsTreeItem> result = new ArrayList<CmsTreeItem>(); 676 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(filter)) { 677 result = new ArrayList<CmsTreeItem>(); 678 for (CmsTreeItem category : m_categories.values()) { 679 if (((CmsDataValue)category.getMainWidget()).matchesFilter(filter, 0, 1)) { 680 result.add(category); 681 } 682 } 683 } else { 684 Iterator<CmsTreeItem> it = m_categories.values().iterator(); 685 while (it.hasNext()) { 686 result.add(it.next()); 687 } 688 689 } 690 return result; 691 } 692 693 /** 694 * List of all sort parameters.<p> 695 * 696 * @return List of all sort parameters 697 */ 698 protected LinkedHashMap<String, String> getSortList() { 699 700 LinkedHashMap<String, String> list = new LinkedHashMap<String, String>(); 701 list.put(SortParams.tree.name(), Messages.get().key(Messages.GUI_SORT_LABEL_HIERARCHIC_0)); 702 list.put(SortParams.title_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_ASC_0)); 703 list.put(SortParams.title_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_DECS_0)); 704 list.put(SortParams.path_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_PATH_ASC_0)); 705 list.put(SortParams.path_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_PATH_DESC_0)); 706 707 return list; 708 } 709 710 /** 711 * Returns true if this widget hat an QuickFilter.<p> 712 * 713 * @return true if this widget hat an QuickFilter 714 */ 715 protected boolean hasQuickFilter() { 716 717 // allow filter if not in tree mode 718 return SortParams.tree != SortParams.valueOf(m_sortSelectBox.getFormValueAsString()); 719 } 720 721 /** 722 * Call after all handlers have been set.<p> 723 */ 724 protected void init() { 725 726 LinkedHashMap<String, String> sortList = getSortList(); 727 if (sortList != null) { 728 // generate the sort select box 729 m_sortSelectBox = new CmsSelectBox(sortList); 730 // add the right handler 731 m_sortSelectBox.addValueChangeHandler(new CategoryValueChangeHandler()); 732 // style the select box 733 m_sortSelectBox.getElement().getStyle().setWidth(200, Unit.PX); 734 m_sortSelectBox.truncate(TM_GALLERY_SORT, 200); 735 // add it to the right panel 736 m_options.add(m_sortSelectBox); 737 // create the box label 738 Label infoLabel = new Label(); 739 infoLabel.setStyleName(I_CmsLayoutBundle.INSTANCE.categoryDialogCss().infoLabel()); 740 m_infoLabel = infoLabel; 741 // add it to the right panel 742 m_options.insert(infoLabel, 0); 743 // create quick search box 744 createQuickBox(); 745 } 746 747 } 748 749 /** 750 * Sets the search query an selects the result tab.<p> 751 */ 752 protected void quickSearch() { 753 754 List<CmsTreeItem> categories = new ArrayList<CmsTreeItem>(); 755 if ((m_quickSearch != null)) { 756 categories = getFilteredCategories(hasQuickFilter() ? m_quickSearch.getFormValueAsString() : null); 757 sort(categories, SortParams.valueOf(m_sortSelectBox.getFormValueAsString())); 758 } 759 } 760 761 /** 762 * Removes the quick search/finder box.<p> 763 */ 764 protected void removeQuickBox() { 765 766 if (m_quickSearch != null) { 767 m_quickSearch.removeFromParent(); 768 m_quickSearch = null; 769 } 770 if (m_searchButton != null) { 771 m_searchButton.removeFromParent(); 772 m_searchButton = null; 773 } 774 } 775 776 /** 777 * Schedules the quick filter action.<p> 778 */ 779 protected void scheduleQuickFilterTimer() { 780 781 m_filterTimer.schedule(FILTER_DELAY); 782 } 783 784 /** 785 * Sorts a list of tree items according to the sort parameter.<p> 786 * 787 * @param items the items to sort 788 * @param sort the sort parameter 789 */ 790 protected void sort(List<CmsTreeItem> items, SortParams sort) { 791 792 int sortParam = -1; 793 boolean ascending = true; 794 switch (sort) { 795 case tree: 796 m_quickSearch.setFormValueAsString(""); 797 m_listView = false; 798 updateContentTree(); 799 break; 800 case title_asc: 801 sortParam = 0; 802 break; 803 case title_desc: 804 sortParam = 0; 805 ascending = false; 806 break; 807 case path_asc: 808 sortParam = 1; 809 break; 810 case path_desc: 811 sortParam = 1; 812 ascending = false; 813 break; 814 default: 815 break; 816 } 817 if (sortParam != -1) { 818 m_listView = true; 819 items = getFilteredCategories(hasQuickFilter() ? m_quickSearch.getFormValueAsString() : null); 820 Collections.sort(items, new CmsListItemDataComparator(sortParam, ascending)); 821 updateContentList(items); 822 } 823 } 824 825 /** 826 * Called if a category is selected/deselected. 827 * 828 * The checkbox state of the selected/deselected item has to be the state BEFORE toggling. 829 * 830 * @param item the tree item that should be selected/deselected. 831 * @param changeState flag, indicating if the checkbox state should be changed. 832 */ 833 protected void toggleSelection(CmsTreeItem item, boolean changeState) { 834 835 boolean select = item.getCheckBox().isChecked(); 836 select = changeState ? !select : select; 837 if (m_isSingleSelection) { 838 m_selectedCategories.clear(); 839 uncheckAll(m_scrollList); 840 } 841 if (select) { 842 if (m_isSingleSelection) { 843 m_singleResult = item.getId(); 844 } 845 CmsTreeItem currentItem = item; 846 do { 847 currentItem.getCheckBox().setChecked(true); 848 String id = currentItem.getId(); 849 if (!m_selectedCategories.contains(id)) { 850 m_selectedCategories.add(id); 851 } 852 currentItem = currentItem.getParentItem(); 853 } while (currentItem != null); 854 } else { 855 if (m_isSingleSelection) { 856 m_singleResult = ""; 857 } 858 deselectChildren(item); 859 CmsTreeItem currentItem = item; 860 do { 861 currentItem.getCheckBox().setChecked(false); 862 String id = currentItem.getId(); 863 if (m_selectedCategories.contains(id)) { 864 m_selectedCategories.remove(id); 865 } 866 currentItem = currentItem.getParentItem(); 867 } while ((currentItem != null) && !hasSelectedChildren(currentItem)); 868 } 869 fireValueChange(); 870 } 871 872 /** 873 * Builds a tree item for the given category.<p> 874 * 875 * @param category the category 876 * @param selectedCategories the selected categories 877 * 878 * @return the tree item widget 879 */ 880 private CmsTreeItem buildTreeItem(CmsCategoryTreeEntry category, Collection<String> selectedCategories) { 881 882 // generate the widget that should be shown in the list 883 CmsDataValue dataValue = new CmsDataValue( 884 600, 885 3, 886 CmsCategoryBean.SMALL_ICON_CLASSES, 887 true, 888 category.getTitle(), 889 "rtl:" + category.getPath(), 890 "hide:" + category.getSitePath()); 891 892 // create the check box for this item 893 CmsCheckBox checkBox = new CmsCheckBox(); 894 // if it has to be selected, select it 895 boolean isPartofPath = false; 896 isPartofPath = CmsCategoryField.isParentCategoryOfSelected(category.getPath(), selectedCategories); 897 if (isPartofPath) { 898 checkBox.setChecked(true); 899 } 900 if (!isEnabled()) { 901 checkBox.disable(m_disabledReason); 902 } 903 if (category.getPath().isEmpty()) { 904 checkBox.setVisible(false); 905 } 906 // bild the CmsTreeItem out of the widget and the check box 907 CmsTreeItem treeItem = new CmsTreeItem(true, checkBox, dataValue); 908 // abb the handler to the check box 909 dataValue.addClickHandler(new DataValueClickHander(treeItem)); 910 911 checkBox.addValueChangeHandler(new CheckBoxValueChangeHandler(treeItem)); 912 913 // set the right style for the small view 914 treeItem.setSmallView(true); 915 treeItem.setId(category.getPath()); 916 // add it to the list of all categories 917 m_categories.put(treeItem.getId(), treeItem); 918 return treeItem; 919 } 920 921 /** 922 * Deselects all child items of the provided item. 923 * @param item the item for which all childs should be deselected.d 924 */ 925 private void deselectChildren(CmsTreeItem item) { 926 927 for (String childId : m_childrens.get(item.getId())) { 928 CmsTreeItem child = m_categories.get(childId); 929 deselectChildren(child); 930 child.getCheckBox().setChecked(false); 931 if (m_selectedCategories.contains(childId)) { 932 m_selectedCategories.remove(childId); 933 } 934 } 935 } 936 937 /** 938 * Return true if at least one child of the given tree item is selected.<p> 939 * @param item The CmsTreeItem to start the check 940 * @return true if the given CmsTreeItem or its children is selected 941 */ 942 private boolean hasSelectedChildren(CmsTreeItem item) { 943 944 for (String childId : m_childrens.get(item.getId())) { 945 CmsTreeItem child = m_categories.get(childId); 946 if (child.getCheckBox().isChecked() || hasSelectedChildren(child)) { 947 return true; 948 } 949 } 950 return false; 951 } 952 953 /** 954 * Normalize the list of selected categories to fit for the ids of the tree items. 955 */ 956 private void normalizeSelectedCategories() { 957 958 Collection<String> normalizedCategories = new ArrayList<String>(m_selectedCategories.size()); 959 for (CmsTreeItem item : m_categories.values()) { 960 if (item.getCheckBox().isChecked()) { 961 normalizedCategories.add(item.getId()); 962 } 963 } 964 m_selectedCategories = normalizedCategories; 965 966 } 967 968 /** 969 * Schedules the execution of onResize deferred.<p> 970 */ 971 private void scheduleResize() { 972 973 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 974 975 public void execute() { 976 977 m_list.onResizeDescendant(); 978 } 979 }); 980 } 981 982 /** 983 * Sets the given tree list enabled/disabled.<p> 984 * 985 * @param list the list of tree items 986 * @param enabled <code>true</code> to enable 987 * @param disabledReason the disable reason, will be displayed as check box title 988 */ 989 private void setListEnabled(CmsList<? extends I_CmsListItem> list, boolean enabled, String disabledReason) { 990 991 for (Widget child : list) { 992 CmsTreeItem treeItem = (CmsTreeItem)child; 993 if (enabled) { 994 treeItem.getCheckBox().enable(); 995 } else { 996 treeItem.getCheckBox().disable(disabledReason); 997 } 998 setListEnabled(treeItem.getChildren(), enabled, disabledReason); 999 } 1000 } 1001 1002 /** 1003 * Uncheck all items in the list including all sub-items. 1004 * @param list list of CmsTreeItem entries. 1005 */ 1006 private void uncheckAll(CmsList<? extends I_CmsListItem> list) { 1007 1008 for (Widget it : list) { 1009 CmsTreeItem treeItem = (CmsTreeItem)it; 1010 treeItem.getCheckBox().setChecked(false); 1011 uncheckAll(treeItem.getChildren()); 1012 } 1013 } 1014 1015}