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