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 private CmsListInfoBean m_infoBean; 314 315 /** 316 * Constructor. Using a 'li'-tag as default root element.<p> 317 * 318 * @param infoBean bean holding the item information 319 */ 320 public CmsListItemWidget(CmsListInfoBean infoBean) { 321 322 initWidget(uiBinder.createAndBindUi(this)); 323 m_handlerRegistrations = new ArrayList<HandlerRegistration>(); 324 m_backgroundStyle = new CmsStyleVariable(this); 325 m_shortExtraInfoLabel = new InlineLabel(); 326 init(infoBean); 327 } 328 329 330 /** 331 * Adds an additional info item to the list.<p> 332 * 333 * @param additionalInfo the additional info to display 334 */ 335 public void addAdditionalInfo(CmsAdditionalInfoBean additionalInfo) { 336 337 m_additionalInfo.add(new AdditionalInfoItem(additionalInfo)); 338 ensureOpenCloseAdditionalInfo(); 339 } 340 341 /** 342 * Adds a widget to the button panel.<p> 343 * 344 * @param w the widget to add 345 */ 346 public void addButton(Widget w) { 347 348 m_buttonPanel.add(w); 349 if (CmsCoreProvider.get().isIe7()) { 350 m_buttonPanel.getElement().getStyle().setWidth(m_buttonPanel.getWidgetCount() * 22, Unit.PX); 351 } 352 } 353 354 /** 355 * Adds a widget to the front of the button panel.<p> 356 * 357 * @param w the widget to add 358 */ 359 public void addButtonToFront(Widget w) { 360 361 m_buttonPanel.insert(w, 0); 362 if (CmsCoreProvider.get().isIe7()) { 363 m_buttonPanel.getElement().getStyle().setWidth(m_buttonPanel.getWidgetCount() * 22, Unit.PX); 364 } 365 } 366 367 /** 368 * @see com.google.gwt.event.dom.client.HasClickHandlers#addClickHandler(ClickHandler) 369 */ 370 public HandlerRegistration addClickHandler(ClickHandler handler) { 371 372 return addDomHandler(handler, ClickEvent.getType()); 373 } 374 375 /** 376 * @see com.google.gwt.event.logical.shared.HasCloseHandlers#addCloseHandler(com.google.gwt.event.logical.shared.CloseHandler) 377 */ 378 public HandlerRegistration addCloseHandler(CloseHandler<CmsListItemWidget> handler) { 379 380 return addHandler(handler, CloseEvent.getType()); 381 } 382 383 /** 384 * @see com.google.gwt.event.dom.client.HasDoubleClickHandlers#addDoubleClickHandler(com.google.gwt.event.dom.client.DoubleClickHandler) 385 */ 386 public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) { 387 388 return addDomHandler(handler, DoubleClickEvent.getType()); 389 } 390 391 /** 392 * Adds a mouse click handler to the icon panel.<p> 393 * 394 * @param handler the click handler 395 * 396 * @return the handler registration 397 */ 398 public HandlerRegistration addIconClickHandler(final ClickHandler handler) { 399 400 final HandlerRegistration internalHandlerRegistration = m_iconPanel.addDomHandler( 401 handler, 402 ClickEvent.getType()); 403 m_iconClickHandlers.add(handler); 404 HandlerRegistration result = new HandlerRegistration() { 405 406 public void removeHandler() { 407 408 internalHandlerRegistration.removeHandler(); 409 m_iconClickHandlers.remove(handler); 410 } 411 }; 412 return result; 413 } 414 415 /** 416 * @see com.google.gwt.event.dom.client.HasMouseOutHandlers#addMouseOutHandler(com.google.gwt.event.dom.client.MouseOutHandler) 417 */ 418 public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) { 419 420 HandlerRegistration req = addDomHandler(handler, MouseOutEvent.getType()); 421 m_handlerRegistrations.add(req); 422 return req; 423 424 } 425 426 /** 427 * @see com.google.gwt.event.dom.client.HasMouseOverHandlers#addMouseOverHandler(com.google.gwt.event.dom.client.MouseOverHandler) 428 */ 429 public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) { 430 431 HandlerRegistration req = addDomHandler(handler, MouseOverEvent.getType()); 432 m_handlerRegistrations.add(req); 433 return req; 434 } 435 436 /** 437 * @see com.google.gwt.event.logical.shared.HasOpenHandlers#addOpenHandler(com.google.gwt.event.logical.shared.OpenHandler) 438 */ 439 public HandlerRegistration addOpenHandler(OpenHandler<CmsListItemWidget> handler) { 440 441 return addHandler(handler, OpenEvent.getType()); 442 } 443 444 /** 445 * Adds a style name to the subtitle label.<p> 446 * 447 * @param styleName the style name to add 448 */ 449 public void addSubtitleStyleName(String styleName) { 450 451 m_subtitle.addStyleName(styleName); 452 } 453 454 /** 455 * Adds a style name to the title label.<p> 456 * 457 * @param styleName the style name to add 458 */ 459 public void addTitleStyleName(String styleName) { 460 461 m_title.addStyleName(styleName); 462 } 463 464 /** 465 * Hides the icon of the list item widget.<p> 466 */ 467 public void clearIcon() { 468 469 m_iconPanel.setVisible(false); 470 } 471 472 /** 473 * Forces mouse out on self and contained buttons.<p> 474 */ 475 public void forceMouseOut() { 476 477 for (Widget w : m_buttonPanel) { 478 if (w instanceof CmsPushButton) { 479 ((CmsPushButton)w).clearHoverState(); 480 } 481 } 482 CmsDomUtil.ensureMouseOut(this); 483 removeStyleName(I_CmsLayoutBundle.INSTANCE.stateCss().cmsHovering()); 484 } 485 486 /** 487 * Returns the button at the given position.<p> 488 * 489 * @param index the button index 490 * 491 * @return the button at the given position 492 */ 493 public Widget getButton(int index) { 494 495 return m_buttonPanel.getWidget(index); 496 } 497 498 /** 499 * Returns the number of buttons.<p> 500 * 501 * @return the number of buttons 502 */ 503 public int getButtonCount() { 504 505 return m_buttonPanel.getWidgetCount(); 506 } 507 508 /** 509 * Returns the button panel.<p> 510 * 511 * @return the button panel 512 */ 513 public FlowPanel getButtonPanel() { 514 515 return m_buttonPanel; 516 } 517 518 /** 519 * Returns the content panel.<p> 520 * 521 * @return the content panel 522 */ 523 public FlowPanel getContentPanel() { 524 525 return m_contentPanel; 526 } 527 528 public CmsListInfoBean getInfoBean() { 529 530 return m_infoBean; 531 } 532 533 public CmsPushButton getOpenClose() { 534 return m_openClose; 535 } 536 537 /** 538 * Returns the label after the subtitle.<p> 539 * 540 * @return the label after the subtitle 541 */ 542 public InlineLabel getShortExtraInfoLabel() { 543 544 return m_shortExtraInfoLabel; 545 } 546 547 /** 548 * Returns the subtitle label.<p> 549 * 550 * @return the subtitle label 551 */ 552 public String getSubtitleLabel() { 553 554 return m_subtitle.getText(); 555 } 556 557 /** 558 * Returns the title label text.<p> 559 * 560 * @return the title label text 561 */ 562 public String getTitleLabel() { 563 564 return m_title.getText(); 565 } 566 567 /** 568 * Gets the title widget.<p> 569 * 570 * @return the title widget 571 */ 572 public CmsLabel getTitleWidget() { 573 574 return m_title; 575 } 576 577 /** 578 * Returns if additional info items are present.<p> 579 * 580 * @return <code>true</code> if additional info items are present 581 */ 582 public boolean hasAdditionalInfo() { 583 584 return m_additionalInfo.getWidgetCount() > 0; 585 } 586 587 /** 588 * Re-initializes the additional infos.<p> 589 * 590 * @param infoBean the info bean 591 */ 592 public void reInitAdditionalInfo(CmsListInfoBean infoBean) { 593 594 m_additionalInfo.clear(); 595 boolean hadOpenClose = false; 596 boolean openCloseDown = false; 597 if (m_openClose != null) { 598 hadOpenClose = true; 599 openCloseDown = m_openClose.isDown(); 600 m_openClose.removeFromParent(); 601 m_openClose = null; 602 } 603 initAdditionalInfo(infoBean); 604 if (hadOpenClose) { 605 m_openClose.setDown(openCloseDown); 606 } 607 } 608 609 /** 610 * Removes a widget from the button panel.<p> 611 * 612 * @param w the widget to remove 613 */ 614 public void removeButton(Widget w) { 615 616 m_buttonPanel.remove(w); 617 if (CmsCoreProvider.get().isIe7()) { 618 m_buttonPanel.getElement().getStyle().setWidth(m_buttonPanel.getWidgetCount() * 22, Unit.PX); 619 } 620 } 621 622 /** 623 * Removes all registered mouse event handlers including the context menu handler.<p> 624 */ 625 public void removeMouseHandlers() { 626 627 Iterator<HandlerRegistration> it = m_handlerRegistrations.iterator(); 628 while (it.hasNext()) { 629 it.next().removeHandler(); 630 } 631 m_handlerRegistrations.clear(); 632 } 633 634 /** 635 * Removes a style name from the subtitle label.<p> 636 * 637 * @param styleName the style name to add 638 */ 639 public void removeSubtitleStyleName(String styleName) { 640 641 m_subtitle.removeStyleName(styleName); 642 } 643 644 /** 645 * Removes a style name from the title label.<p> 646 * 647 * @param styleName the style name to add 648 */ 649 public void removeTitleStyleName(String styleName) { 650 651 m_title.removeStyleName(styleName); 652 } 653 654 /** 655 * Sets the additional info value label at the given position.<p> 656 * 657 * @param index the additional info index 658 * @param label the new value to set 659 */ 660 public void setAdditionalInfoValue(int index, String label) { 661 662 ((AdditionalInfoItem)m_additionalInfo.getWidget(index)).getValueLabel().setText(label); 663 } 664 665 /** 666 * Sets the additional info visible if present.<p> 667 * 668 * @param visible <code>true</code> to show, <code>false</code> to hide 669 */ 670 public void setAdditionalInfoVisible(boolean visible) { 671 672 if (m_openClose == null) { 673 return; 674 } 675 if (visible) { 676 addStyleName(CmsListItemWidget.OPENCLASS); 677 m_openClose.setDown(true); 678 OpenEvent.fire(this, this); 679 } else { 680 removeStyleName(CmsListItemWidget.OPENCLASS); 681 m_openClose.setDown(false); 682 CloseEvent.fire(this, this); 683 } 684 CmsDomUtil.resizeAncestor(getParent()); 685 } 686 687 /** 688 * Sets the background color.<p> 689 * 690 * @param background the color 691 */ 692 public void setBackground(Background background) { 693 694 switch (background) { 695 case BLUE: 696 m_backgroundStyle.setValue(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().itemBlue()); 697 break; 698 case RED: 699 m_backgroundStyle.setValue(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().itemRed()); 700 break; 701 case YELLOW: 702 m_backgroundStyle.setValue(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().itemYellow()); 703 break; 704 case DEFAULT: 705 default: 706 m_backgroundStyle.setValue(null); 707 } 708 } 709 710 /** 711 * Sets the extra info text, and hides or displays the extra info label depending on whether 712 * the text is null or not null.<p> 713 * 714 * @param text the text to put into the subtitle suffix 715 */ 716 public void setExtraInfo(String text) { 717 718 if (text == null) { 719 if (m_shortExtraInfoLabel.getParent() != null) { 720 m_shortExtraInfoLabel.removeFromParent(); 721 } 722 } else { 723 if (m_shortExtraInfoLabel.getParent() == null) { 724 m_titleBox.add(m_shortExtraInfoLabel); 725 } 726 m_shortExtraInfoLabel.setText(text); 727 } 728 updateTruncation(); 729 } 730 731 /** 732 * Sets the icon of this item.<p> 733 * 734 * @param image the image to use as icon 735 */ 736 public void setIcon(Image image) { 737 738 m_iconPanel.setVisible(true); 739 if (image == null) { 740 return; 741 } 742 m_iconPanel.setWidget(image); 743 } 744 745 /** 746 * Sets the icon for this item using the given CSS classes.<p> 747 * 748 * @param iconClasses the CSS classes 749 */ 750 public void setIcon(String iconClasses) { 751 752 setIcon(iconClasses, null); 753 } 754 755 /** 756 * Sets the icon for this item using the given CSS classes.<p> 757 * 758 * @param iconClasses the CSS classes 759 * @param detailIconClasses the detail type icon classes if available 760 */ 761 public void setIcon(String iconClasses, String detailIconClasses) { 762 763 m_iconPanel.setVisible(true); 764 HTML iconWidget = new HTML(); 765 m_iconPanel.setWidget(iconWidget); 766 iconWidget.setStyleName(iconClasses + " " + m_fixedIconClasses); 767 // render the detail icon as an overlay above the main icon, if required 768 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(detailIconClasses)) { 769 iconWidget.setHTML( 770 "<span class=\"" 771 + detailIconClasses 772 + " " 773 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().pageDetailType() 774 + "\"></span>"); 775 } 776 } 777 778 /** 779 * Sets the cursor for the icon.<p> 780 * 781 * @param cursor the cursor for the icon 782 */ 783 public void setIconCursor(Cursor cursor) { 784 785 m_iconPanel.getElement().getStyle().setCursor(cursor); 786 787 } 788 789 /** 790 * Sets the icon title.<p> 791 * 792 * @param title the new icon title 793 */ 794 public void setIconTitle(String title) { 795 796 m_iconTitle = title; 797 m_iconPanel.setTitle(title); 798 } 799 800 /** 801 * Sets the lock icon.<p> 802 * 803 * @param icon the icon to use 804 * @param iconTitle the icon title 805 */ 806 public void setLockIcon(LockIcon icon, String iconTitle) { 807 808 if (m_lockIcon == null) { 809 m_lockIcon = new HTML(); 810 m_lockIcon.addClickHandler(m_iconSuperClickHandler); 811 m_contentPanel.add(m_lockIcon); 812 } 813 switch (icon) { 814 case CLOSED: 815 m_lockIcon.setStyleName( 816 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 817 + " " 818 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockClosed()); 819 break; 820 case OPEN: 821 m_lockIcon.setStyleName( 822 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 823 + " " 824 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockOpen()); 825 break; 826 case SHARED_CLOSED: 827 m_lockIcon.setStyleName( 828 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 829 + " " 830 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockSharedClosed()); 831 break; 832 case SHARED_OPEN: 833 m_lockIcon.setStyleName( 834 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon() 835 + " " 836 + I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockSharedOpen()); 837 break; 838 case NONE: 839 default: 840 m_lockIcon.setStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().lockIcon()); 841 } 842 843 m_lockIcon.setTitle(concatIconTitles(m_iconTitle, iconTitle)); 844 m_lockIcon.getElement().getStyle().setCursor(Style.Cursor.POINTER); 845 } 846 847 /** 848 * Sets the state icon.<p> 849 * 850 * The state icon indicates if a resource is exported, secure, etc.<p> 851 * 852 * @param icon the state icon 853 */ 854 public void setStateIcon(StateIcon icon) { 855 856 if (m_stateIcon == null) { 857 m_stateIcon = new HTML(); 858 m_stateIcon.addClickHandler(m_iconSuperClickHandler); 859 m_contentPanel.add(m_stateIcon); 860 861 } 862 String iconTitle = null; 863 I_CmsListItemWidgetCss listItemWidgetCss = I_CmsLayoutBundle.INSTANCE.listItemWidgetCss(); 864 String styleStateIcon = I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().stateIcon(); 865 switch (icon) { 866 case export: 867 m_stateIcon.setStyleName(styleStateIcon + " " + listItemWidgetCss.export()); 868 iconTitle = Messages.get().key(Messages.GUI_ICON_TITLE_EXPORT_0); 869 break; 870 case secure: 871 m_stateIcon.setStyleName(styleStateIcon + " " + listItemWidgetCss.secure()); 872 iconTitle = Messages.get().key(Messages.GUI_ICON_TITLE_SECURE_0); 873 break; 874 case copy: 875 m_stateIcon.setStyleName(styleStateIcon + " " + listItemWidgetCss.copyModel()); 876 break; 877 case standard: 878 default: 879 m_stateIcon.setStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().stateIcon()); 880 break; 881 } 882 m_stateIcon.setTitle(concatIconTitles(m_iconTitle, iconTitle)); 883 m_stateIcon.getElement().getStyle().setCursor(Style.Cursor.POINTER); 884 } 885 886 /** 887 * Sets the subtitle label text.<p> 888 * 889 * @param label the new subtitle to set 890 */ 891 public void setSubtitleLabel(String label) { 892 893 if (label == null) { 894 label = ""; 895 } 896 DOMParser parser = new DOMParser(); 897 HTMLDocument doc = Js.cast(parser.parseFromString(label, "text/html")); 898 String stripped = doc.body.textContent; 899 if (CmsStringUtil.isEmptyOrWhitespaceOnly(stripped) && !CmsStringUtil.isEmptyOrWhitespaceOnly(label)) { 900 CmsGwtLog.log("Empty HTML stripping output for input: " + label); 901 } 902 m_subtitle.setText(stripped); 903 } 904 905 /** 906 * Enables or disabled editing of the title field.<p> 907 * 908 * @param editable if true, makes the title field editable 909 */ 910 public void setTitleEditable(boolean editable) { 911 912 boolean alreadyEditable = m_titleClickHandlerRegistration != null; 913 if (alreadyEditable == editable) { 914 return; 915 } 916 if (!editable) { 917 m_titleClickHandlerRegistration.removeHandler(); 918 m_titleClickHandlerRegistration = null; 919 m_title.removeStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().inlineEditable()); 920 } else { 921 m_title.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().inlineEditable()); 922 m_titleClickHandlerRegistration = m_title.addClickHandler(new ClickHandler() { 923 924 /** 925 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 926 */ 927 public void onClick(ClickEvent event) { 928 929 editTitle(); 930 } 931 }); 932 } 933 934 } 935 936 /** 937 * Sets the handler for editing the list item widget's title. 938 * 939 * @param handler the new title editing handler 940 */ 941 public void setTitleEditHandler(I_CmsTitleEditHandler handler) { 942 943 m_titleEditHandler = handler; 944 } 945 946 /** 947 * Sets the title label text.<p> 948 * 949 * @param label the new title to set 950 */ 951 public void setTitleLabel(String label) { 952 953 m_title.setText(label); 954 } 955 956 /** 957 * Sets the icon in the top right corner and its title.<p> 958 * 959 * @param iconClass the CSS class for the icon 960 * @param title the value for the title attribute of the icon 961 */ 962 public void setTopRightIcon(String iconClass, String title) { 963 964 if (m_topRightIcon == null) { 965 m_topRightIcon = new HTML(); 966 m_contentPanel.add(m_topRightIcon); 967 } 968 m_topRightIcon.setStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().topRightIcon() + " " + iconClass); 969 if (title != null) { 970 m_topRightIcon.setTitle(title); 971 } 972 } 973 974 /** 975 * Makes the content of the list info box unselectable.<p> 976 */ 977 public void setUnselectable() { 978 979 m_contentPanel.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().unselectable()); 980 } 981 982 /** 983 * @see org.opencms.gwt.client.ui.I_CmsTruncable#truncate(java.lang.String, int) 984 */ 985 public void truncate(final String textMetricsPrefix, final int widgetWidth) { 986 987 m_childWidth = widgetWidth; 988 m_tmPrefix = textMetricsPrefix; 989 int width = widgetWidth - 4; // just to be on the save side 990 if (m_openClose != null) { 991 width -= 16; 992 } 993 if (m_iconPanel.isVisible()) { 994 width -= 32; 995 } 996 if (width < 0) { 997 // IE fails with a JS error if the width is negative 998 width = 0; 999 } 1000 m_titleBox.setWidth(Math.max(0, width - 30) + "px"); 1001 m_subtitle.truncate(textMetricsPrefix + TM_SUBTITLE, width); 1002 truncateAdditionalInfo(textMetricsPrefix, widgetWidth); 1003 } 1004 1005 /** 1006 * Truncates the additional info items.<p> 1007 * 1008 * @param textMetricsPrefix the text metrics prefix 1009 * @param widgetWidth the width to truncate to 1010 */ 1011 public void truncateAdditionalInfo(final String textMetricsPrefix, final int widgetWidth) { 1012 1013 for (Widget addInfo : m_additionalInfo) { 1014 ((AdditionalInfoItem)addInfo).truncate(textMetricsPrefix, widgetWidth - 10); 1015 } 1016 } 1017 1018 /** 1019 * Updates the truncation of labels if needed.<p> 1020 * 1021 * Use after changing any text on the widget.<p> 1022 */ 1023 public void updateTruncation() { 1024 1025 truncate(m_tmPrefix, m_childWidth); 1026 } 1027 1028 /** 1029 * Internal method which is called when the user clicks on an editable title field.<p> 1030 */ 1031 protected void editTitle() { 1032 1033 m_title.setVisible(false); 1034 final TextBox box = new TextBox(); 1035 box.setText(m_title.getText()); 1036 box.getElement().setAttribute("size", "45"); 1037 box.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().labelInput()); 1038 box.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().titleInput()); 1039 final String originalTitle = m_title.getText(); 1040 // wrap the boolean flag in an array so we can change it from the event handlers 1041 final boolean[] checked = new boolean[] {false}; 1042 final boolean restoreUnselectable = CmsDomUtil.hasClass( 1043 I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().unselectable(), 1044 m_contentPanel.getElement()); 1045 m_contentPanel.removeStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().unselectable()); 1046 box.addBlurHandler(new BlurHandler() { 1047 1048 /** 1049 * @see com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event.dom.client.BlurEvent) 1050 */ 1051 public void onBlur(BlurEvent event) { 1052 1053 if (restoreUnselectable) { 1054 setUnselectable(); 1055 } 1056 if (checked[0]) { 1057 return; 1058 } 1059 1060 onEditTitleTextBox(box); 1061 checked[0] = true; 1062 } 1063 }); 1064 1065 box.addKeyPressHandler(new KeyPressHandler() { 1066 1067 /** 1068 * @see com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google.gwt.event.dom.client.KeyPressEvent) 1069 */ 1070 public void onKeyPress(KeyPressEvent event) { 1071 1072 if (checked[0]) { 1073 return; 1074 } 1075 1076 int keycode = event.getNativeEvent().getKeyCode(); 1077 1078 if ((keycode == 10) || (keycode == 13)) { 1079 onEditTitleTextBox(box); 1080 checked[0] = true; 1081 } 1082 if (keycode == 27) { 1083 box.setText(originalTitle); 1084 onEditTitleTextBox(box); 1085 checked[0] = true; 1086 1087 } 1088 } 1089 }); 1090 m_titleBox.insert(box, 2); 1091 box.setFocus(true); 1092 } 1093 1094 /** 1095 * Ensures the open close button for the additional info list is present.<p> 1096 */ 1097 protected void ensureOpenCloseAdditionalInfo() { 1098 1099 if (m_openClose == null) { 1100 m_openClose = new CmsPushButton(I_CmsButton.TRIANGLE_RIGHT, I_CmsButton.TRIANGLE_DOWN); 1101 m_openClose.setButtonStyle(ButtonStyle.FONT_ICON, null); 1102 m_openClose.setSize(Size.small); 1103 m_titleBox.insert(m_openClose, 0); 1104 m_openClose.addClickHandler(new ClickHandler() { 1105 1106 /** 1107 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 1108 */ 1109 public void onClick(ClickEvent event) { 1110 1111 setAdditionalInfoVisible(!getElement().getClassName().contains(CmsListItemWidget.OPENCLASS)); 1112 CmsDomUtil.resizeAncestor(CmsListItemWidget.this); 1113 } 1114 }); 1115 } 1116 } 1117 1118 /** 1119 * Constructor.<p> 1120 * 1121 * @param infoBean bean holding the item information 1122 */ 1123 protected void init(CmsListInfoBean infoBean) { 1124 1125 m_infoBean = infoBean; 1126 m_iconPanel.setVisible(false); 1127 m_title.setText(infoBean.getTitle()); 1128 setSubtitleLabel(infoBean.getSubTitle()); 1129 1130 // set the resource type icon if present 1131 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(infoBean.getBigIconClasses())) { 1132 setIcon(infoBean.getBigIconClasses(), infoBean.getSmallIconClasses()); 1133 } 1134 1135 if (infoBean.getStateIcon() != null) { 1136 setStateIcon(infoBean.getStateIcon()); 1137 } 1138 if (infoBean.getLockIcon() != null) { 1139 setLockIcon(infoBean.getLockIcon(), infoBean.getLockIconTitle()); 1140 } 1141 1142 CmsResourceState resourceState = infoBean.getResourceState(); 1143 1144 if ((resourceState != null) && !resourceState.isUnchanged() && infoBean.isMarkChangedState()) { 1145 String title = Messages.get().key(Messages.GUI_UNPUBLISHED_CHANGES_TITLE_0); 1146 if (resourceState.isNew()) { 1147 title = Messages.get().key(Messages.GUI_UNPUBLISHED_CHANGES_NEW_TITLE_0); 1148 } 1149 setTopRightIcon(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().changed(), title); 1150 } 1151 1152 if ((resourceState != null) && resourceState.isDeleted()) { 1153 m_title.addStyleName(I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().titleDeleted()); 1154 } 1155 1156 initAdditionalInfo(infoBean); 1157 } 1158 1159 /** 1160 * Initializes the additional info.<p> 1161 * 1162 * @param infoBean the info bean 1163 */ 1164 protected void initAdditionalInfo(CmsListInfoBean infoBean) { 1165 1166 // create the state info 1167 CmsResourceState state = infoBean.getResourceState(); 1168 if (state != null) { 1169 String stateKey = Messages.get().key(Messages.GUI_RESOURCE_STATE_0); 1170 String stateValue = CmsResourceStateUtil.getStateName(state); 1171 String stateStyle = CmsResourceStateUtil.getStateStyle(state); 1172 m_additionalInfo.add(new AdditionalInfoItem(new CmsAdditionalInfoBean(stateKey, stateValue, stateStyle))); 1173 ensureOpenCloseAdditionalInfo(); 1174 } 1175 1176 // set the additional info 1177 if (infoBean.hasAdditionalInfo()) { 1178 ensureOpenCloseAdditionalInfo(); 1179 for (CmsAdditionalInfoBean additionalInfo : infoBean.getAdditionalInfo()) { 1180 m_additionalInfo.add(new AdditionalInfoItem(additionalInfo)); 1181 } 1182 } 1183 } 1184 1185 /** 1186 * Internal method which is called when the user has finished editing the title. 1187 * 1188 * @param box the text box which has been edited 1189 */ 1190 protected void onEditTitleTextBox(TextBox box) { 1191 1192 if (m_titleEditHandler != null) { 1193 m_titleEditHandler.handleEdit(m_title, box); 1194 return; 1195 } 1196 1197 String text = box.getText(); 1198 box.removeFromParent(); 1199 m_title.setText(text); 1200 m_title.setVisible(true); 1201 1202 } 1203 1204 /** 1205 * Combines the main icon title with the title for a status icon overlayed over the main icon.<p> 1206 * 1207 * @param main the main icon title 1208 * @param secondary the secondary icon title 1209 * 1210 * @return the combined icon title for the secondary icon 1211 */ 1212 String concatIconTitles(String main, String secondary) { 1213 1214 if (main == null) { 1215 main = ""; 1216 } 1217 if (secondary == null) { 1218 secondary = ""; 1219 } 1220 1221 if (secondary.length() == 0) { 1222 return main; 1223 } 1224 return main + " [" + secondary + "]"; 1225 1226 } 1227 1228}