001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.gwt.client.ui.input;
029
030import org.opencms.gwt.client.I_CmsHasInit;
031import org.opencms.gwt.client.Messages;
032import org.opencms.gwt.client.ui.I_CmsAutoHider;
033import org.opencms.gwt.client.ui.input.form.CmsWidgetFactoryRegistry;
034import org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory;
035import org.opencms.gwt.client.util.CmsMessages;
036import org.opencms.util.CmsStringUtil;
037
038import java.util.HashMap;
039import java.util.Map;
040
041import com.google.common.base.Optional;
042import com.google.gwt.user.client.ui.FocusPanel;
043
044/**
045 * Widget for selecting one of multiple items from a drop-down list which opens
046 * after the user clicks on the widget.<p>
047 *
048 * @since 8.0.0
049 *
050 */
051public class CmsSelectBox extends A_CmsSelectBox<CmsLabelSelectCell> implements I_CmsHasInit, I_CmsHasGhostValue {
052
053    /** The key for the text which should be displayed in the opener if no option is available. */
054    public static final String NO_SELECTION_OPENER_TEXT = "%NO_SELECTION_OPENER_TEXT%";
055
056    /** The key for the text which should be displayed if no option is available. */
057    public static final String NO_SELECTION_TEXT = "%NO_SELECTION_TEXT%";
058
059    /** Text metrics key. */
060    private static final String TM_OPENER_LABEL = "OpenerLabel";
061
062    /** The widget type identifier. */
063    public static final String WIDGET_TYPE = "select";
064
065    /** Suffix for select/selectcombo widget names to prevent creation of 'no selection' value. */
066    public static final String NOTNULL_SUFFIX = "_notnull";
067
068    /** Widget type identifier (no null value). */
069    public static final String WIDGET_TYPE_NOTNULL = WIDGET_TYPE + NOTNULL_SUFFIX;
070
071    /** Widget parameter to control the resize behavior, defaults to 'true'. */
072    public static final String OPTION_RESIZABLE = "CmsSelectBox_resizable";
073
074    /** The ghost value. */
075    protected String m_ghostValue;
076
077    /** The widget displayed in the opener. */
078    protected CmsLabel m_openerWidget;
079
080    /** A map from select options to their label texts. */
081    private Map<String, String> m_items;
082
083    /** The text which should be displayed in the opener if there is no selection. */
084    private String m_noSelectionOpenerText;
085
086    /** The text which should be displayed if there is no selection. */
087    private String m_noSelectionText;
088
089    /** A map of titles for the select options which should  be displayed on mouseover. */
090    private Map<String, String> m_titles = new HashMap<String, String>();
091
092    /**
093     * Default constructor.<p>
094     */
095    public CmsSelectBox() {
096
097        super();
098
099    }
100
101    /**
102     * Constructs a new select box from a map.<p>
103     *
104     * The keys of the map are the values of the select options, and the values of the map are the labels to be displayed
105     * for each option.
106     *
107     * @param items the map of select options
108     */
109    public CmsSelectBox(Map<String, String> items) {
110
111        this();
112        setItems(items);
113    }
114
115    /**
116     * Creates a new select box, with the option of adding a "not selected" choice.<p>
117     *
118     * @param items the map of select options
119     * @param addNullOption if true, a "not selected" option will be added to the select box
120     */
121    public CmsSelectBox(Map<String, String> items, boolean addNullOption) {
122
123        this();
124        String resizable = items.remove(OPTION_RESIZABLE);
125        if ((resizable != null) && Boolean.FALSE.toString().equals(resizable)) {
126            setPopupResize(false);
127        }
128        if (items.containsKey(NO_SELECTION_TEXT)) {
129            m_noSelectionText = items.get(NO_SELECTION_TEXT);
130            m_noSelectionOpenerText = items.get(NO_SELECTION_OPENER_TEXT);
131            if (m_noSelectionOpenerText == null) {
132                m_noSelectionOpenerText = m_noSelectionText;
133            }
134            items.remove(NO_SELECTION_TEXT);
135            items.remove(NO_SELECTION_OPENER_TEXT);
136        }
137        if (addNullOption) {
138            String text = Messages.get().key(Messages.GUI_SELECTBOX_EMPTY_SELECTION_0);
139            items.put("", text);
140        }
141        setItems(items);
142        if (addNullOption) {
143            selectValue("");
144        }
145    }
146
147    /**
148     * Initializes this class.<p>
149     */
150    public static void initClass() {
151
152        // registers a factory for creating new instances of this widget
153        CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE, new I_CmsFormWidgetFactory() {
154
155            /**
156             * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional)
157             */
158            public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) {
159
160                return new CmsSelectBox(widgetParams, true);
161            }
162        });
163
164        CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE_NOTNULL, new I_CmsFormWidgetFactory() {
165
166            /**
167             * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional)
168             */
169            public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) {
170
171                return new CmsSelectBox(widgetParams, false);
172            }
173        });
174
175    }
176
177    /**
178     * Adds a new selection cell.<p>
179     *
180     * @param value the value of the select option
181     * @param text the text to be displayed for the select option
182     */
183    public void addOption(String value, String text) {
184
185        String title = getTitle(value, text);
186        CmsLabelSelectCell cell = new CmsLabelSelectCell(value, text, title);
187        addOption(cell);
188    }
189
190    /**
191     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getApparentValue()
192     */
193    public String getApparentValue() {
194
195        String val = getFormValueAsString();
196        if (val == null) {
197            val = m_ghostValue;
198        }
199        return val;
200
201    }
202
203    /**
204     * Returns the items as a map for option values to label text.<p>
205     *
206     * @return the items as a map for option values to label text
207     */
208    public Map<String, String> getItems() {
209
210        return m_items;
211    }
212
213    /**
214     * Returns the opener of this widget.<p>
215     *
216     * @return the opener of this widget
217     */
218    public FocusPanel getOpener() {
219
220        return m_opener;
221    }
222
223    /**
224     * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#selectValue(java.lang.String)
225     */
226    @Override
227    public void selectValue(String value) {
228
229        super.selectValue(value);
230        updateStyle();
231    }
232
233    /**
234     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setAutoHideParent(org.opencms.gwt.client.ui.I_CmsAutoHider)
235     */
236    public void setAutoHideParent(I_CmsAutoHider autoHideParent) {
237
238        // nothing to do
239
240    }
241
242    /**
243     * @see org.opencms.gwt.client.ui.input.I_CmsHasGhostValue#setGhostMode(boolean)
244     */
245    public void setGhostMode(boolean ghostMode) {
246
247        // do nothing for now
248
249    }
250
251    /**
252     * @see org.opencms.gwt.client.ui.input.I_CmsHasGhostValue#setGhostValue(java.lang.String, boolean)
253     */
254    public void setGhostValue(String value, boolean ghostMode) {
255
256        if (value == null) {
257            value = "";
258        }
259        String otherOptionText = m_items.get(value);
260        String message = m_noSelectionText != null
261        ? m_noSelectionText
262        : Messages.get().key(Messages.GUI_SELECTBOX_EMPTY_SELECTION_1);
263        message = CmsMessages.formatMessage(message, otherOptionText);
264        //setTextForNullSelection(message);
265        m_ghostValue = value;
266        updateCells();
267        if (ghostMode) {
268            selectValue("");
269        }
270    }
271
272    /**
273     * Sets the items using a map from option values to label texts.<p>
274     *
275     * @param items the map containing the select options
276     */
277    public void setItems(Map<String, String> items) {
278
279        clearItems();
280        m_items = items;
281        for (Map.Entry<String, String> entry : items.entrySet()) {
282            addOption(entry.getKey(), entry.getValue());
283        }
284    }
285
286    /**
287     * Sets the text that is used for the "not selected" option.<p>
288     *
289     * @param text the text which should be used for the "not selected" option
290     */
291    public void setTextForNullSelection(String text) {
292
293        // do nothing if there's no null option
294        CmsLabelSelectCell cell = m_selectCells.get("");
295        if (cell == null) {
296            return;
297        }
298        cell.setText(text);
299        // if the null option is selected, we still need to update the opener
300        if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_selectedValue)) {
301            selectValue("");
302        }
303    }
304
305    /**
306     * Sets the title for a select option.<p>
307     *
308     * Note: This will only affect select options added *after* calling this method!
309     *
310     * @param option the select option value
311     * @param title the new title for the option
312     */
313    public void setTitle(String option, String title) {
314
315        m_titles.put(option, title);
316    }
317
318    /**
319     * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#truncateOpener(java.lang.String, int)
320     */
321    @Override
322    public void truncateOpener(String prefix, int width) {
323
324        m_openerWidget.truncate(prefix + '_' + TM_OPENER_LABEL, width);
325    }
326
327    /**
328     * Updates a single select cell.<p>
329     *
330     * @param cell the select cell to update
331     */
332    public void updateCell(CmsLabelSelectCell cell) {
333
334        // do nothing
335    }
336
337    /**
338     * Updates the select cells.<p>
339     */
340    public void updateCells() {
341
342        for (CmsLabelSelectCell cell : m_selectCells.values()) {
343            updateCell(cell);
344        }
345    }
346
347    /**
348     * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#createUnknownOption(java.lang.String)
349     */
350    @Override
351    protected CmsLabelSelectCell createUnknownOption(String value) {
352
353        CmsLabelSelectCell cell = new CmsLabelSelectCell(value, value);
354        return cell;
355
356    }
357
358    /**
359     * Helper method to get the title for a given select option.<p>
360     *
361     * @param option the select option value
362     * @param defaultValue the value to return when no title for the value was found
363     *
364     * @return the title for the select option
365     */
366    protected String getTitle(String option, String defaultValue) {
367
368        String result;
369        if ((option != null) && m_titles.containsKey(option)) {
370            result = m_titles.get(option);
371        } else {
372            result = defaultValue;
373        }
374        if (result != null) {
375            result = result.trim();
376        }
377        return result;
378    }
379
380    /**
381     * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#initOpener()
382     */
383    @Override
384    protected void initOpener() {
385
386        m_openerWidget = new CmsLabel();
387        m_openerWidget.addStyleName(CSS.selectBoxOpener());
388        m_opener.add(m_openerWidget);
389    }
390
391    /**
392     * @see com.google.gwt.user.client.ui.Widget#onLoad()
393     */
394    @Override
395    protected void onLoad() {
396
397        super.onLoad();
398        updateStyle();
399    }
400
401    /**
402     * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#updateOpener(java.lang.String)
403     */
404    @Override
405    protected void updateOpener(String newValue) {
406
407        CmsLabel label = m_openerWidget;
408        CmsLabelSelectCell cell = m_selectCells.get(newValue);
409        String openerText = cell.getOpenerText();
410        label.setText(openerText);
411        label.setTitle(getTitle(cell.getValue(), openerText));
412    }
413
414    /**
415     * This method should be used to make changes to the CSS style of the select box when the value changes.<p>
416     */
417    protected void updateStyle() {
418
419        // do nothing
420    }
421}