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; 029 030import org.opencms.db.CmsResourceState; 031import org.opencms.gwt.client.CmsCoreProvider; 032import org.opencms.gwt.client.Messages; 033import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 034import org.opencms.gwt.client.ui.I_CmsButton.Size; 035import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle; 036import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 037import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.I_CmsListItemWidgetCss; 038import org.opencms.gwt.client.ui.input.CmsLabel; 039import org.opencms.gwt.client.util.CmsDomUtil; 040import org.opencms.gwt.client.util.CmsResourceStateUtil; 041import org.opencms.gwt.client.util.CmsStyleVariable; 042import org.opencms.gwt.client.util.DOMParser; 043import org.opencms.gwt.shared.CmsAdditionalInfoBean; 044import org.opencms.gwt.shared.CmsGwtLog; 045import org.opencms.gwt.shared.CmsListInfoBean; 046import org.opencms.gwt.shared.CmsListInfoBean.LockIcon; 047import org.opencms.gwt.shared.CmsListInfoBean.StateIcon; 048import org.opencms.util.CmsStringUtil; 049 050import java.util.ArrayList; 051import java.util.Iterator; 052import java.util.List; 053 054import com.google.gwt.core.client.GWT; 055import com.google.gwt.dom.client.Style; 056import com.google.gwt.dom.client.Style.Cursor; 057import com.google.gwt.dom.client.Style.Unit; 058import com.google.gwt.event.dom.client.BlurEvent; 059import com.google.gwt.event.dom.client.BlurHandler; 060import com.google.gwt.event.dom.client.ClickEvent; 061import com.google.gwt.event.dom.client.ClickHandler; 062import com.google.gwt.event.dom.client.DoubleClickEvent; 063import com.google.gwt.event.dom.client.DoubleClickHandler; 064import com.google.gwt.event.dom.client.HasClickHandlers; 065import com.google.gwt.event.dom.client.HasDoubleClickHandlers; 066import com.google.gwt.event.dom.client.HasMouseOutHandlers; 067import com.google.gwt.event.dom.client.HasMouseOverHandlers; 068import com.google.gwt.event.dom.client.KeyPressEvent; 069import com.google.gwt.event.dom.client.KeyPressHandler; 070import com.google.gwt.event.dom.client.MouseOutEvent; 071import com.google.gwt.event.dom.client.MouseOutHandler; 072import com.google.gwt.event.dom.client.MouseOverEvent; 073import com.google.gwt.event.dom.client.MouseOverHandler; 074import com.google.gwt.event.logical.shared.CloseEvent; 075import com.google.gwt.event.logical.shared.CloseHandler; 076import com.google.gwt.event.logical.shared.HasCloseHandlers; 077import com.google.gwt.event.logical.shared.HasOpenHandlers; 078import com.google.gwt.event.logical.shared.OpenEvent; 079import com.google.gwt.event.logical.shared.OpenHandler; 080import com.google.gwt.event.shared.HandlerRegistration; 081import com.google.gwt.uibinder.client.UiBinder; 082import com.google.gwt.uibinder.client.UiField; 083import com.google.gwt.user.client.ui.Composite; 084import com.google.gwt.user.client.ui.FlowPanel; 085import com.google.gwt.user.client.ui.HTML; 086import com.google.gwt.user.client.ui.Image; 087import com.google.gwt.user.client.ui.InlineLabel; 088import com.google.gwt.user.client.ui.SimplePanel; 089import com.google.gwt.user.client.ui.TextBox; 090import com.google.gwt.user.client.ui.Widget; 091 092import elemental2.dom.HTMLDocument; 093import jsinterop.base.Js; 094 095/** 096 * Provides a UI list item.<p> 097 * 098 * @since 8.0.0 099 */ 100public class CmsListItemWidget extends Composite 101implements HasOpenHandlers<CmsListItemWidget>, HasCloseHandlers<CmsListItemWidget>, HasMouseOutHandlers, 102HasClickHandlers, HasDoubleClickHandlers, HasMouseOverHandlers, I_CmsTruncable { 103 104 /** Additional info item HTML. */ 105 public static class AdditionalInfoItem extends Composite implements I_CmsTruncable { 106 107 /** The title element. */ 108 private CmsLabel m_titleLabel; 109 110 /** The value element. */ 111 private CmsLabel m_valueLabel; 112 113 /** 114 * Constructor.<p> 115 * 116 * @param additionalInfo the info to display 117 */ 118 public AdditionalInfoItem(CmsAdditionalInfoBean additionalInfo) { 119 120 this(additionalInfo.getName(), additionalInfo.getValue(), additionalInfo.getStyle()); 121 } 122 123 /** 124 * Constructor.<p> 125 * 126 * @param title info title 127 * @param value info value 128 * @param additionalStyle an additional class name 129 */ 130 public AdditionalInfoItem(String title, String value, String additionalStyle) { 131 132 super(); 133 FlowPanel panel = new FlowPanel(); 134 initWidget(panel); 135 136 I_CmsListItemWidgetCss style = I_CmsLayoutBundle.INSTANCE.listItemWidgetCss(); 137 panel.addStyleName(style.itemInfoRow()); 138 // create title 139 m_titleLabel = new CmsLabel(CmsStringUtil.isEmptyOrWhitespaceOnly(title) ? "" : title + ":"); 140 m_titleLabel.addStyleName(style.itemAdditionalTitle()); 141 panel.add(m_titleLabel); 142 // create value 143 m_valueLabel = new CmsLabel(); 144 if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) { 145 m_valueLabel.setHTML(CmsDomUtil.Entity.nbsp.html()); 146 } else { 147 m_valueLabel.setText(value); 148 } 149 m_valueLabel.addStyleName(style.itemAdditionalValue()); 150 if (additionalStyle != null) { 151 m_valueLabel.addStyleName(additionalStyle); 152 } 153 panel.add(m_valueLabel); 154 } 155 156 /** 157 * Returns the title element.<p> 158 * 159 * @return the title element 160 */ 161 public CmsLabel getTitleLabel() { 162 163 return m_titleLabel; 164 } 165 166 /** 167 * Returns the value element.<p> 168 * 169 * @return the value element 170 */ 171 public CmsLabel getValueLabel() { 172 173 return m_valueLabel; 174 } 175 176 /** 177 * @see org.opencms.gwt.client.ui.I_CmsTruncable#truncate(java.lang.String, int) 178 */ 179 public void truncate(String textMetricsPrefix, int widgetWidth) { 180 181 int titleWidth = widgetWidth / 4; 182 m_titleLabel.setWidth(titleWidth + "px"); 183 } 184 } 185 186 /** Background color values. */ 187 public enum Background { 188 /** Color blue. */ 189 BLUE, 190 /** Default color. */ 191 DEFAULT, 192 /** Color red. */ 193 RED, 194 /** Color yellow. */ 195 YELLOW 196 } 197 198 /** 199 * The interface for handling edits of the title field.<p> 200 */ 201 public interface I_CmsTitleEditHandler { 202 203 /** 204 * This method is called when the user has finished editing the title field.<p> 205 * 206 * @param title the label containing the title 207 * @param box the 208 */ 209 void handleEdit(CmsLabel title, TextBox box); 210 } 211 212 /** 213 * @see com.google.gwt.uibinder.client.UiBinder 214 */ 215 protected interface I_CmsListItemWidgetUiBinder extends UiBinder<CmsHoverPanel, CmsListItemWidget> { 216 // GWT interface, nothing to do here 217 } 218 219 /** The CSS class to set the additional info open. */ 220 protected static final String OPENCLASS = I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().open(); 221 222 /** Text metrics key. */ 223 private static final String TM_SUBTITLE = "Subtitle"; 224 225 /** The ui-binder instance for this class. */ 226 private static I_CmsListItemWidgetUiBinder uiBinder = GWT.create(I_CmsListItemWidgetUiBinder.class); 227 228 /** DIV for additional item info. */ 229 @UiField 230 protected FlowPanel m_additionalInfo; 231 232 /** Panel to hold buttons.*/ 233 @UiField 234 protected FlowPanel m_buttonPanel; 235 236 /** Panel to hold the content.*/ 237 @UiField 238 protected FlowPanel m_contentPanel; 239 240 /** A list of click handlers for the main icon. */ 241 protected List<ClickHandler> m_iconClickHandlers = new ArrayList<ClickHandler>(); 242 243 /** The DIV showing the list icon. */ 244 @UiField 245 protected SimplePanel m_iconPanel; 246 247 /** The open-close button for the additional info. */ 248 protected CmsPushButton m_openClose; 249 250 /** A label which is optionally displayed after the subtitle. */ 251 protected InlineLabel m_shortExtraInfoLabel; 252 253 /** Sub title label. */ 254 @UiField 255 protected CmsLabel m_subtitle; 256 257 /** Title label. */ 258 @UiField 259 protected CmsLabel m_title; 260 261 /** Container for the title. */ 262 @UiField 263 protected FlowPanel m_titleBox; 264 265 /** The title row, holding the title and the open-close button for the additional info. */ 266 @UiField 267 protected FlowPanel m_titleRow; 268 269 /** Variable for the background style. */ 270 private CmsStyleVariable m_backgroundStyle; 271 272 /** The child width in px for truncation. */ 273 private int m_childWidth; 274 275 /** The fixed icon classes which will always be added if the icon classes are set. */ 276 private String m_fixedIconClasses = ""; 277 278 /** The event handler registrations. */ 279 private List<HandlerRegistration> m_handlerRegistrations; 280 281 /** A click handler which triggers all icon click handlers. */ 282 private ClickHandler m_iconSuperClickHandler = new ClickHandler() { 283 284 public void onClick(ClickEvent event) { 285 286 for (ClickHandler iconClickHandler : m_iconClickHandlers) { 287 iconClickHandler.onClick(event); 288 } 289 } 290 }; 291 292 /** The main icon title. */ 293 private String m_iconTitle = ""; 294 295 /** The lock icon. */ 296 private HTML m_lockIcon; 297 298 /** The state icon. */ 299 private HTML m_stateIcon; 300 301 /** The handler registration for the click handler on the title field. */ 302 private HandlerRegistration m_titleClickHandlerRegistration; 303 304 /** A handler object for handling editing of the title field. */ 305 private I_CmsTitleEditHandler m_titleEditHandler; 306 307 /** The text metrics prefix. */ 308 private String m_tmPrefix; 309 310 /** Widget for the overlay icon in the top-right corner. */ 311 private HTML m_topRightIcon; 312 313 /** 314 * Constructor. Using a 'li'-tag as default root element.<p> 315 * 316 * @param infoBean bean holding the item information 317 */ 318 public CmsListItemWidget(CmsListInfoBean infoBean) { 319 320 initWidget(uiBinder.createAndBindUi(this)); 321 m_handlerRegistrations = new ArrayList<HandlerRegistration>(); 322 m_backgroundStyle = new CmsStyleVariable(this); 323 m_shortExtraInfoLabel = new InlineLabel(); 324 init(infoBean); 325 } 326 327 /** 328 * Adds an additional info item to the list.<p> 329 * 330 * @param additionalInfo the additional info to display 331 */ 332 public void addAdditionalInfo(CmsAdditionalInfoBean additionalInfo) { 333 334 m_additionalInfo.add(new AdditionalInfoItem(additionalInfo)); 335 ensureOpenCloseAdditionalInfo(); 336 } 337 338 /** 339 * Adds a widget to the button panel.<p> 340 * 341 * @param w the widget to add 342 */ 343 public void addButton(Widget w) { 344 345 m_buttonPanel.add(w); 346 if (CmsCoreProvider.get().isIe7()) { 347 m_buttonPanel.getElement().getStyle().setWidth(m_buttonPanel.getWidgetCount() * 22, Unit.PX); 348 } 349 } 350 351 /** 352 * Adds a widget to the front of the button panel.<p> 353 * 354 * @param w the widget to add 355 */ 356 public void addButtonToFront(Widget w) { 357 358 m_buttonPanel.insert(w, 0); 359 if (CmsCoreProvider.get().isIe7()) { 360 m_buttonPanel.getElement().getStyle().setWidth(m_buttonPanel.getWidgetCount() * 22, Unit.PX); 361 } 362 } 363 364 /** 365 * @see com.google.gwt.event.dom.client.HasClickHandlers#addClickHandler(ClickHandler) 366 */ 367 public HandlerRegistration addClickHandler(ClickHandler handler) { 368 369 return addDomHandler(handler, ClickEvent.getType()); 370 } 371 372 /** 373 * @see com.google.gwt.event.logical.shared.HasCloseHandlers#addCloseHandler(com.google.gwt.event.logical.shared.CloseHandler) 374 */ 375 public HandlerRegistration addCloseHandler(CloseHandler<CmsListItemWidget> handler) { 376 377 return addHandler(handler, CloseEvent.getType()); 378 } 379 380 /** 381 * @see com.google.gwt.event.dom.client.HasDoubleClickHandlers#addDoubleClickHandler(com.google.gwt.event.dom.client.DoubleClickHandler) 382 */ 383 public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) { 384 385 return addDomHandler(handler, DoubleClickEvent.getType()); 386 } 387 388 /** 389 * Adds a mouse click handler to the icon panel.<p> 390 * 391 * @param handler the click handler 392 * 393 * @return the handler registration 394 */ 395 public HandlerRegistration addIconClickHandler(final ClickHandler handler) { 396 397 final HandlerRegistration internalHandlerRegistration = m_iconPanel.addDomHandler( 398 handler, 399 ClickEvent.getType()); 400 m_iconClickHandlers.add(handler); 401 HandlerRegistration result = new HandlerRegistration() { 402 403 public void removeHandler() { 404 405 internalHandlerRegistration.removeHandler(); 406 m_iconClickHandlers.remove(handler); 407 } 408 }; 409 return result; 410 } 411 412 /** 413 * @see com.google.gwt.event.dom.client.HasMouseOutHandlers#addMouseOutHandler(com.google.gwt.event.dom.client.MouseOutHandler) 414 */ 415 public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) { 416 417 HandlerRegistration req = addDomHandler(handler, MouseOutEvent.getType()); 418 m_handlerRegistrations.add(req); 419 return req; 420 421 } 422 423 /** 424 * @see com.google.gwt.event.dom.client.HasMouseOverHandlers#addMouseOverHandler(com.google.gwt.event.dom.client.MouseOverHandler) 425 */ 426 public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) { 427 428 HandlerRegistration req = addDomHandler(handler, MouseOverEvent.getType()); 429 m_handlerRegistrations.add(req); 430 return req; 431 } 432 433 /** 434 * @see com.google.gwt.event.logical.shared.HasOpenHandlers#addOpenHandler(com.google.gwt.event.logical.shared.OpenHandler) 435 */ 436 public HandlerRegistration addOpenHandler(OpenHandler<CmsListItemWidget> handler) { 437 438 return addHandler(handler, OpenEvent.getType()); 439 } 440 441 /** 442 * Adds a style name to the subtitle label.<p> 443 * 444 * @param styleName the style name to add 445 */ 446 public void addSubtitleStyleName(String styleName) { 447 448 m_subtitle.addStyleName(styleName); 449 } 450 451 /** 452 * Adds a style name to the title label.<p> 453 * 454 * @param styleName the style name to add 455 */ 456 public void addTitleStyleName(String styleName) { 457 458 m_title.addStyleName(styleName); 459 } 460 461 /** 462 * Hides the icon of the list item widget.<p> 463 */ 464 public void clearIcon() { 465 466 m_iconPanel.setVisible(false); 467 } 468 469 /** 470 * Forces mouse out on self and contained buttons.<p> 471 */ 472 public void forceMouseOut() { 473 474 for (Widget w : m_buttonPanel) { 475 if (w instanceof CmsPushButton) { 476 ((CmsPushButton)w).clearHoverState(); 477 } 478 } 479 CmsDomUtil.ensureMouseOut(this); 480 removeStyleName(I_CmsLayoutBundle.INSTANCE.stateCss().cmsHovering()); 481 } 482 483 /** 484 * Returns the button at the given position.<p> 485 * 486 * @param index the button index 487 * 488 * @return the button at the given position 489 */ 490 public Widget getButton(int index) { 491 492 return m_buttonPanel.getWidget(index); 493 } 494 495 /** 496 * Returns the number of buttons.<p> 497 * 498 * @return the number of buttons 499 */ 500 public int getButtonCount() { 501 502 return m_buttonPanel.getWidgetCount(); 503 } 504 505 /** 506 * Returns the button panel.<p> 507 * 508 * @return the button panel 509 */ 510 public FlowPanel getButtonPanel() { 511 512 return m_buttonPanel; 513 } 514 515 /** 516 * Returns the content panel.<p> 517 * 518 * @return the content panel 519 */ 520 public FlowPanel getContentPanel() { 521 522 return m_contentPanel; 523 } 524 525 /** 526 * Returns the label after the subtitle.<p> 527 * 528 * @return the label after the subtitle 529 */ 530 public InlineLabel getShortExtraInfoLabel() { 531 532 return m_shortExtraInfoLabel; 533 } 534 535 /** 536 * Returns the subtitle label.<p> 537 * 538 * @return the subtitle label 539 */ 540 public String getSubtitleLabel() { 541 542 return m_subtitle.getText(); 543 } 544 545 /** 546 * Returns the title label text.<p> 547 * 548 * @return the title label text 549 */ 550 public String getTitleLabel() { 551 552 return m_title.getText(); 553 } 554 555 /** 556 * Gets the title widget.<p> 557 * 558 * @return the title widget 559 */ 560 public CmsLabel getTitleWidget() { 561 562 return m_title; 563 } 564 565 /** 566 * Returns if additional info items are present.<p> 567 * 568 * @return <code>true</code> if additional info items are present 569 */ 570 public boolean hasAdditionalInfo() { 571 572 return m_additionalInfo.getWidgetCount() > 0; 573 } 574 575 /** 576 * Re-initializes the additional infos.<p> 577 * 578 * @param infoBean the info bean 579 */ 580 public void reInitAdditionalInfo(CmsListInfoBean infoBean) { 581 582 m_additionalInfo.clear(); 583 boolean hadOpenClose = false; 584 boolean openCloseDown = false; 585 if (m_openClose != null) { 586 hadOpenClose = true; 587 openCloseDown = m_openClose.isDown(); 588 m_openClose.removeFromParent(); 589 m_openClose = null; 590 } 591 initAdditionalInfo(infoBean); 592 if (hadOpenClose) { 593 m_openClose.setDown(openCloseDown); 594 } 595 } 596 597 /** 598 * Removes a widget from the button panel.<p> 599 * 600 * @param w the widget to remove 601 */ 602 public void removeButton(Widget w) { 603 604 m_buttonPanel.remove(w); 605 if (CmsCoreProvider.get().isIe7()) { 606 m_buttonPanel.getElement().getStyle().setWidth(m_buttonPanel.getWidgetCount() * 22, Unit.PX); 607 } 608 } 609 610 /** 611 * Removes all registered mouse event handlers including the context menu handler.<p> 612 */ 613 public void removeMouseHandlers() { 614 615 Iterator<HandlerRegistration> it = m_handlerRegistrations.iterator(); 616 while (it.hasNext()) { 617 it.next().removeHandler(); 618 } 619 m_handlerRegistrations.clear(); 620 } 621 622 /** 623 * Removes a style name from the subtitle label.<p> 624 * 625 * @param styleName the style name to add 626 */ 627 public void removeSubtitleStyleName(String styleName) { 628 629 m_subtitle.removeStyleName(styleName); 630 } 631 632 /** 633 * Removes a style name from the title label.<p> 634 * 635 * @param styleName the style name to add 636 */ 637 public void removeTitleStyleName(String styleName) { 638 639 m_title.removeStyleName(styleName); 640 } 641 642 /** 643 * Sets the additional info value label at the given position.<p> 644 * 645 * @param index the additional info index 646 * @param label the new value to set 647 */ 648 public void setAdditionalInfoValue(int index, String label) { 649 650 ((AdditionalInfoItem)m_additionalInfo.getWidget(index)).getValueLabel().setText(label); 651 } 652 653 /** 654 * Sets the additional info visible if present.<p> 655 * 656 * @param visible <code>true</code> to show, <code>false</code> to hide 657 */ 658 public void setAdditionalInfoVisible(boolean visible) { 659 660 if (m_openClose == null) { 661 return; 662 } 663 if (visible) { 664 addStyleName(CmsListItemWidget.OPENCLASS); 665 m_openClose.setDown(true); 666 OpenEvent.fire(this, this); 667 } else { 668 removeStyleName(CmsListItemWidget.OPENCLASS); 669 m_openClose.setDown(false); 670 CloseEvent.fire(this, this); 671 } 672 CmsDomUtil.resizeAncestor(getParent()); 673 } 674 675 /** 676 * Sets the background color.<p> 677 * 678 * @param background the color 679 */ 680 public void setBackground(Background background) { 681 682 switch (background) { 683 case BLUE: 684 m_backgroundStyle.setValue(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().itemBlue()); 685 break; 686 case RED: 687 m_backgroundStyle.setValue(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().itemRed()); 688 break; 689 case YELLOW: 690 m_backgroundStyle.setValue(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().itemYellow()); 691 break; 692 case DEFAULT: 693 default: 694 m_backgroundStyle.setValue(null); 695 } 696 } 697 698 /** 699 * Sets the extra info text, and hides or displays the extra info label depending on whether 700 * the text is null or not null.<p> 701 * 702 * @param text the text to put into the subtitle suffix 703 */ 704 public void setExtraInfo(String text) { 705 706 if (text == null) { 707 if (m_shortExtraInfoLabel.getParent() != null) { 708 m_shortExtraInfoLabel.removeFromParent(); 709 } 710 } else { 711 if (m_shortExtraInfoLabel.getParent() == null) { 712 m_titleBox.add(m_shortExtraInfoLabel); 713 } 714 m_shortExtraInfoLabel.setText(text); 715 } 716 updateTruncation(); 717 } 718 719 /** 720 * Sets the icon of this item.<p> 721 * 722 * @param image the image to use as icon 723 */ 724 public void setIcon(Image image) { 725 726 m_iconPanel.setVisible(true); 727 if (image == null) { 728 return; 729 } 730 m_iconPanel.setWidget(image); 731 } 732 733 /** 734 * Sets the icon for this item using the given CSS classes.<p> 735 * 736 * @param iconClasses the CSS classes 737 */ 738 public void setIcon(String iconClasses) { 739 740 setIcon(iconClasses, null); 741 } 742 743 /** 744 * Sets the icon for this item using the given CSS classes.<p> 745 * 746 * @param iconClasses the CSS classes 747 * @param detailIconClasses the detail type icon classes if available 748 */ 749 public void setIcon(String iconClasses, String detailIconClasses) { 750 751 m_iconPanel.setVisible(true); 752 HTML iconWidget = new HTML(); 753 m_iconPanel.setWidget(iconWidget); 754 iconWidget.setStyleName(iconClasses + " " + m_fixedIconClasses); 755 // render the detail icon as an overlay above the main icon, if required 756 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(detailIconClasses)) { 757 iconWidget.setHTML( 758 "<span class=\"" 759 + detailIconClasses 760 + " " 761 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().pageDetailType() 762 + "\"></span>"); 763 } 764 } 765 766 /** 767 * Sets the cursor for the icon.<p> 768 * 769 * @param cursor the cursor for the icon 770 */ 771 public void setIconCursor(Cursor cursor) { 772 773 m_iconPanel.getElement().getStyle().setCursor(cursor); 774 775 } 776 777 /** 778 * Sets the icon title.<p> 779 * 780 * @param title the new icon title 781 */ 782 public void setIconTitle(String title) { 783 784 m_iconTitle = title; 785 m_iconPanel.setTitle(title); 786 } 787 788 /** 789 * Sets the lock icon.<p> 790 * 791 * @param icon the icon to use 792 * @param iconTitle the icon title 793 */ 794 public void setLockIcon(LockIcon icon, String iconTitle) { 795 796 if (m_lockIcon == null) { 797 m_lockIcon = new HTML(); 798 m_lockIcon.addClickHandler(m_iconSuperClickHandler); 799 m_contentPanel.add(m_lockIcon); 800 } 801 switch (icon) { 802 case CLOSED: 803 m_lockIcon.setStyleName( 804 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 805 + " " 806 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockClosed()); 807 break; 808 case OPEN: 809 m_lockIcon.setStyleName( 810 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 811 + " " 812 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockOpen()); 813 break; 814 case SHARED_CLOSED: 815 m_lockIcon.setStyleName( 816 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 817 + " " 818 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockSharedClosed()); 819 break; 820 case SHARED_OPEN: 821 m_lockIcon.setStyleName( 822 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 823 + " " 824 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockSharedOpen()); 825 break; 826 case NONE: 827 default: 828 m_lockIcon.setStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon()); 829 } 830 831 m_lockIcon.setTitle(concatIconTitles(m_iconTitle, iconTitle)); 832 m_lockIcon.getElement().getStyle().setCursor(Style.Cursor.POINTER); 833 } 834 835 /** 836 * Sets the state icon.<p> 837 * 838 * The state icon indicates if a resource is exported, secure, etc.<p> 839 * 840 * @param icon the state icon 841 */ 842 public void setStateIcon(StateIcon icon) { 843 844 if (m_stateIcon == null) { 845 m_stateIcon = new HTML(); 846 m_stateIcon.addClickHandler(m_iconSuperClickHandler); 847 m_contentPanel.add(m_stateIcon); 848 849 } 850 String iconTitle = null; 851 I_CmsListItemWidgetCss listItemWidgetCss = I_CmsLayoutBundle.INSTANCE.listItemWidgetCss(); 852 String styleStateIcon = I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().stateIcon(); 853 switch (icon) { 854 case export: 855 m_stateIcon.setStyleName(styleStateIcon + " " + listItemWidgetCss.export()); 856 iconTitle = Messages.get().key(Messages.GUI_ICON_TITLE_EXPORT_0); 857 break; 858 case secure: 859 m_stateIcon.setStyleName(styleStateIcon + " " + listItemWidgetCss.secure()); 860 iconTitle = Messages.get().key(Messages.GUI_ICON_TITLE_SECURE_0); 861 break; 862 case copy: 863 m_stateIcon.setStyleName(styleStateIcon + " " + listItemWidgetCss.copyModel()); 864 break; 865 case standard: 866 default: 867 m_stateIcon.setStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().stateIcon()); 868 break; 869 } 870 m_stateIcon.setTitle(concatIconTitles(m_iconTitle, iconTitle)); 871 m_stateIcon.getElement().getStyle().setCursor(Style.Cursor.POINTER); 872 } 873 874 /** 875 * Sets the subtitle label text.<p> 876 * 877 * @param label the new subtitle to set 878 */ 879 public void setSubtitleLabel(String label) { 880 881 if (label == null) { 882 label = ""; 883 } 884 DOMParser parser = new DOMParser(); 885 HTMLDocument doc = Js.cast(parser.parseFromString(label, "text/html")); 886 String stripped = doc.body.textContent; 887 if (CmsStringUtil.isEmptyOrWhitespaceOnly(stripped) && !CmsStringUtil.isEmptyOrWhitespaceOnly(label)) { 888 CmsGwtLog.log("Empty HTML stripping output for input: " + label); 889 } 890 m_subtitle.setText(stripped); 891 } 892 893 /** 894 * Enables or disabled editing of the title field.<p> 895 * 896 * @param editable if true, makes the title field editable 897 */ 898 public void setTitleEditable(boolean editable) { 899 900 boolean alreadyEditable = m_titleClickHandlerRegistration != null; 901 if (alreadyEditable == editable) { 902 return; 903 } 904 if (!editable) { 905 m_titleClickHandlerRegistration.removeHandler(); 906 m_titleClickHandlerRegistration = null; 907 m_title.removeStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().inlineEditable()); 908 } else { 909 m_title.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().inlineEditable()); 910 m_titleClickHandlerRegistration = m_title.addClickHandler(new ClickHandler() { 911 912 /** 913 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 914 */ 915 public void onClick(ClickEvent event) { 916 917 editTitle(); 918 } 919 }); 920 } 921 922 } 923 924 /** 925 * Sets the handler for editing the list item widget's title. 926 * 927 * @param handler the new title editing handler 928 */ 929 public void setTitleEditHandler(I_CmsTitleEditHandler handler) { 930 931 m_titleEditHandler = handler; 932 } 933 934 /** 935 * Sets the title label text.<p> 936 * 937 * @param label the new title to set 938 */ 939 public void setTitleLabel(String label) { 940 941 m_title.setText(label); 942 } 943 944 /** 945 * Sets the icon in the top right corner and its title.<p> 946 * 947 * @param iconClass the CSS class for the icon 948 * @param title the value for the title attribute of the icon 949 */ 950 public void setTopRightIcon(String iconClass, String title) { 951 952 if (m_topRightIcon == null) { 953 m_topRightIcon = new HTML(); 954 m_contentPanel.add(m_topRightIcon); 955 } 956 m_topRightIcon.setStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().topRightIcon() + " " + iconClass); 957 if (title != null) { 958 m_topRightIcon.setTitle(title); 959 } 960 } 961 962 /** 963 * Makes the content of the list info box unselectable.<p> 964 */ 965 public void setUnselectable() { 966 967 m_contentPanel.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().unselectable()); 968 } 969 970 /** 971 * @see org.opencms.gwt.client.ui.I_CmsTruncable#truncate(java.lang.String, int) 972 */ 973 public void truncate(final String textMetricsPrefix, final int widgetWidth) { 974 975 m_childWidth = widgetWidth; 976 m_tmPrefix = textMetricsPrefix; 977 int width = widgetWidth - 4; // just to be on the save side 978 if (m_openClose != null) { 979 width -= 16; 980 } 981 if (m_iconPanel.isVisible()) { 982 width -= 32; 983 } 984 if (width < 0) { 985 // IE fails with a JS error if the width is negative 986 width = 0; 987 } 988 m_titleBox.setWidth(Math.max(0, width - 30) + "px"); 989 m_subtitle.truncate(textMetricsPrefix + TM_SUBTITLE, width); 990 truncateAdditionalInfo(textMetricsPrefix, widgetWidth); 991 } 992 993 /** 994 * Truncates the additional info items.<p> 995 * 996 * @param textMetricsPrefix the text metrics prefix 997 * @param widgetWidth the width to truncate to 998 */ 999 public void truncateAdditionalInfo(final String textMetricsPrefix, final int widgetWidth) { 1000 1001 for (Widget addInfo : m_additionalInfo) { 1002 ((AdditionalInfoItem)addInfo).truncate(textMetricsPrefix, widgetWidth - 10); 1003 } 1004 } 1005 1006 /** 1007 * Updates the truncation of labels if needed.<p> 1008 * 1009 * Use after changing any text on the widget.<p> 1010 */ 1011 public void updateTruncation() { 1012 1013 truncate(m_tmPrefix, m_childWidth); 1014 } 1015 1016 /** 1017 * Internal method which is called when the user clicks on an editable title field.<p> 1018 */ 1019 protected void editTitle() { 1020 1021 m_title.setVisible(false); 1022 final TextBox box = new TextBox(); 1023 box.setText(m_title.getText()); 1024 box.getElement().setAttribute("size", "45"); 1025 box.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().labelInput()); 1026 box.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().titleInput()); 1027 final String originalTitle = m_title.getText(); 1028 // wrap the boolean flag in an array so we can change it from the event handlers 1029 final boolean[] checked = new boolean[] {false}; 1030 final boolean restoreUnselectable = CmsDomUtil.hasClass( 1031 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().unselectable(), 1032 m_contentPanel.getElement()); 1033 m_contentPanel.removeStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().unselectable()); 1034 box.addBlurHandler(new BlurHandler() { 1035 1036 /** 1037 * @see com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event.dom.client.BlurEvent) 1038 */ 1039 public void onBlur(BlurEvent event) { 1040 1041 if (restoreUnselectable) { 1042 setUnselectable(); 1043 } 1044 if (checked[0]) { 1045 return; 1046 } 1047 1048 onEditTitleTextBox(box); 1049 checked[0] = true; 1050 } 1051 }); 1052 1053 box.addKeyPressHandler(new KeyPressHandler() { 1054 1055 /** 1056 * @see com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google.gwt.event.dom.client.KeyPressEvent) 1057 */ 1058 public void onKeyPress(KeyPressEvent event) { 1059 1060 if (checked[0]) { 1061 return; 1062 } 1063 1064 int keycode = event.getNativeEvent().getKeyCode(); 1065 1066 if ((keycode == 10) || (keycode == 13)) { 1067 onEditTitleTextBox(box); 1068 checked[0] = true; 1069 } 1070 if (keycode == 27) { 1071 box.setText(originalTitle); 1072 onEditTitleTextBox(box); 1073 checked[0] = true; 1074 1075 } 1076 } 1077 }); 1078 m_titleBox.insert(box, 2); 1079 box.setFocus(true); 1080 } 1081 1082 /** 1083 * Ensures the open close button for the additional info list is present.<p> 1084 */ 1085 protected void ensureOpenCloseAdditionalInfo() { 1086 1087 if (m_openClose == null) { 1088 m_openClose = new CmsPushButton(I_CmsButton.TRIANGLE_RIGHT, I_CmsButton.TRIANGLE_DOWN); 1089 m_openClose.setButtonStyle(ButtonStyle.FONT_ICON, null); 1090 m_openClose.setSize(Size.small); 1091 m_titleBox.insert(m_openClose, 0); 1092 m_openClose.addClickHandler(new ClickHandler() { 1093 1094 /** 1095 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 1096 */ 1097 public void onClick(ClickEvent event) { 1098 1099 setAdditionalInfoVisible(!getElement().getClassName().contains(CmsListItemWidget.OPENCLASS)); 1100 CmsDomUtil.resizeAncestor(CmsListItemWidget.this); 1101 } 1102 }); 1103 } 1104 } 1105 1106 /** 1107 * Constructor.<p> 1108 * 1109 * @param infoBean bean holding the item information 1110 */ 1111 protected void init(CmsListInfoBean infoBean) { 1112 1113 m_iconPanel.setVisible(false); 1114 m_title.setText(infoBean.getTitle()); 1115 setSubtitleLabel(infoBean.getSubTitle()); 1116 1117 // set the resource type icon if present 1118 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(infoBean.getBigIconClasses())) { 1119 setIcon(infoBean.getBigIconClasses(), infoBean.getSmallIconClasses()); 1120 } 1121 1122 if (infoBean.getStateIcon() != null) { 1123 setStateIcon(infoBean.getStateIcon()); 1124 } 1125 if (infoBean.getLockIcon() != null) { 1126 setLockIcon(infoBean.getLockIcon(), infoBean.getLockIconTitle()); 1127 } 1128 1129 CmsResourceState resourceState = infoBean.getResourceState(); 1130 1131 if ((resourceState != null) && !resourceState.isUnchanged() && infoBean.isMarkChangedState()) { 1132 String title = Messages.get().key(Messages.GUI_UNPUBLISHED_CHANGES_TITLE_0); 1133 if (resourceState.isNew()) { 1134 title = Messages.get().key(Messages.GUI_UNPUBLISHED_CHANGES_NEW_TITLE_0); 1135 } 1136 setTopRightIcon(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().changed(), title); 1137 } 1138 1139 if ((resourceState != null) && resourceState.isDeleted()) { 1140 m_title.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().titleDeleted()); 1141 } 1142 1143 initAdditionalInfo(infoBean); 1144 } 1145 1146 /** 1147 * Initializes the additional info.<p> 1148 * 1149 * @param infoBean the info bean 1150 */ 1151 protected void initAdditionalInfo(CmsListInfoBean infoBean) { 1152 1153 // create the state info 1154 CmsResourceState state = infoBean.getResourceState(); 1155 if (state != null) { 1156 String stateKey = Messages.get().key(Messages.GUI_RESOURCE_STATE_0); 1157 String stateValue = CmsResourceStateUtil.getStateName(state); 1158 String stateStyle = CmsResourceStateUtil.getStateStyle(state); 1159 m_additionalInfo.add(new AdditionalInfoItem(new CmsAdditionalInfoBean(stateKey, stateValue, stateStyle))); 1160 ensureOpenCloseAdditionalInfo(); 1161 } 1162 1163 // set the additional info 1164 if (infoBean.hasAdditionalInfo()) { 1165 ensureOpenCloseAdditionalInfo(); 1166 for (CmsAdditionalInfoBean additionalInfo : infoBean.getAdditionalInfo()) { 1167 m_additionalInfo.add(new AdditionalInfoItem(additionalInfo)); 1168 } 1169 } 1170 } 1171 1172 /** 1173 * Internal method which is called when the user has finished editing the title. 1174 * 1175 * @param box the text box which has been edited 1176 */ 1177 protected void onEditTitleTextBox(TextBox box) { 1178 1179 if (m_titleEditHandler != null) { 1180 m_titleEditHandler.handleEdit(m_title, box); 1181 return; 1182 } 1183 1184 String text = box.getText(); 1185 box.removeFromParent(); 1186 m_title.setText(text); 1187 m_title.setVisible(true); 1188 1189 } 1190 1191 /** 1192 * Combines the main icon title with the title for a status icon overlayed over the main icon.<p> 1193 * 1194 * @param main the main icon title 1195 * @param secondary the secondary icon title 1196 * 1197 * @return the combined icon title for the secondary icon 1198 */ 1199 String concatIconTitles(String main, String secondary) { 1200 1201 if (main == null) { 1202 main = ""; 1203 } 1204 if (secondary == null) { 1205 secondary = ""; 1206 } 1207 1208 if (secondary.length() == 0) { 1209 return main; 1210 } 1211 return main + " [" + secondary + "]"; 1212 1213 } 1214 1215}