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.property.CmsPropertySelectBox;
032import org.opencms.gwt.client.ui.CmsPushButton;
033import org.opencms.gwt.client.ui.I_CmsAutoHider;
034import org.opencms.gwt.client.ui.I_CmsButton;
035import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle;
036import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle;
037import org.opencms.gwt.client.ui.history.CmsPropertyComboBox;
038import org.opencms.gwt.client.ui.input.form.CmsWidgetFactoryRegistry;
039import org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory;
040
041import java.util.HashMap;
042import java.util.Map;
043
044import com.google.common.base.Optional;
045import com.google.gwt.event.dom.client.ClickEvent;
046import com.google.gwt.event.dom.client.ClickHandler;
047import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
048import com.google.gwt.event.logical.shared.ValueChangeHandler;
049import com.google.gwt.event.shared.HandlerRegistration;
050import com.google.gwt.user.client.ui.Composite;
051import com.google.gwt.user.client.ui.FlowPanel;
052
053/**
054 * Hybrid select / combo box widget.<p>
055 *
056 * This widget behaves like a select box, until a button on it is pressed, after which the select box is transformed into a combo box.
057 * The reason for this is that the combo box always displays the currently selected value itself, rather than the label from the widget configuration,
058 * which may be confusing to nontechnical users in some cases.
059 */
060public class CmsSelectComboBox extends Composite
061implements I_CmsFormWidget, I_CmsHasInit, HasValueChangeHandlers<String>, I_CmsHasGhostValue {
062
063    /** Widget type identifier for the configuration. */
064    public static final String WIDGET_TYPE = "selectcombo";
065
066    /** Widget type identifier for the widget to use in the property dialog. */
067    private static final String WIDGET_TYPE_PROP = "selectcombo_prop";
068
069    /** The panel containing the actual widgets. */
070    private FlowPanel m_panel = new FlowPanel();
071
072    /** The select box initially displayed by this widget. */
073    private CmsSelectBox m_selectBox;
074
075    /** The options for the widget. */
076    private Map<String, String> m_options;
077
078    /** The combo box (initially null). */
079    private CmsComboBox m_comboBox;
080
081    /** True if the widget was switched from select box mode to combo box mode. */
082    private boolean m_comboMode;
083
084    /** An error which has been set before. */
085    private String m_error;
086
087    /**
088     * Creates a new widget instance.<p>
089     *
090     * @param options the widget options
091     * @param forProperties if true, use the special widget versions for the property dialog
092     * @param nullOption true if null option should be added
093     */
094    public CmsSelectComboBox(Map<String, String> options, boolean forProperties, boolean nullOption) {
095
096        m_options = options;
097        m_selectBox = forProperties
098        ? new CmsPropertySelectBox(new HashMap<>(options))
099        : new CmsSelectBox(new HashMap<>(options), nullOption);
100        m_comboBox = forProperties
101        ? new CmsPropertyComboBox(new HashMap<>(m_options))
102        : new CmsComboBox(new HashMap<>(m_options), nullOption);
103        m_panel.add(m_selectBox);
104        CmsPushButton comboButton = new CmsPushButton();
105        comboButton.setButtonStyle(ButtonStyle.FONT_ICON, null);
106        comboButton.setImageClass(I_CmsButton.PEN_SMALL);
107        comboButton.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().selectComboIcon());
108        m_selectBox.addWidget(comboButton);
109        comboButton.addClickHandler(new ClickHandler() {
110
111            public void onClick(ClickEvent event) {
112
113                enableComboMode();
114            }
115        });
116
117        initWidget(m_panel);
118    }
119
120    /**
121     * Initializes this class.<p>
122     */
123    public static void initClass() {
124
125        CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE, new I_CmsFormWidgetFactory() {
126
127            /**
128             * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional)
129             */
130            public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) {
131
132                return new CmsSelectComboBox(widgetParams, false, true);
133            }
134        });
135        CmsWidgetFactoryRegistry.instance().registerFactory(
136            WIDGET_TYPE + CmsSelectBox.NOTNULL_SUFFIX,
137            new I_CmsFormWidgetFactory() {
138
139                /**
140                 * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional)
141                 */
142                public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) {
143
144                    return new CmsSelectComboBox(widgetParams, false, false);
145                }
146            });
147        CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE_PROP, new I_CmsFormWidgetFactory() {
148
149            /**
150             * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional)
151             */
152            public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) {
153
154                return new CmsSelectComboBox(widgetParams, true, false);
155            }
156        });
157
158    }
159
160    /**
161     * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler)
162     */
163    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
164
165        final HandlerRegistration r1 = m_selectBox.addValueChangeHandler(handler);
166        final HandlerRegistration r2 = m_comboBox.addValueChangeHandler(handler);
167        return new HandlerRegistration() {
168
169            public void removeHandler() {
170
171                r1.removeHandler();
172                r2.removeHandler();
173            }
174        };
175    }
176
177    /**
178     * Returns whether the select options are being displayed below or above the widget.<p>
179     *
180     * @return <code>true</code> in case the select options are displayed above the widget
181     */
182    public boolean displayingAbove() {
183
184        return getActiveWidget().displayingAbove();
185    }
186
187    /**
188     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getApparentValue()
189     */
190    public String getApparentValue() {
191
192        return getActiveWidget().getApparentValue();
193    }
194
195    /**
196     * Gets the combo box instance.<p>
197     *
198     * @return the combo box
199     */
200    public CmsComboBox getComboBox() {
201
202        return m_comboBox;
203    }
204
205    /**
206     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFieldType()
207     */
208    public FieldType getFieldType() {
209
210        return getActiveWidget().getFieldType();
211    }
212
213    /**
214     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValue()
215     */
216    public Object getFormValue() {
217
218        return getActiveWidget().getFormValue();
219    }
220
221    /**
222     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValueAsString()
223     */
224    public String getFormValueAsString() {
225
226        return getActiveWidget().getFormValueAsString();
227    }
228
229    /**
230     * Gets the select box instance.<p>
231     *
232     * @return the select box instance
233     */
234    public CmsSelectBox getSelectBox() {
235
236        return m_selectBox;
237    }
238
239    /**
240     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#isEnabled()
241     */
242    public boolean isEnabled() {
243
244        return getActiveWidget().isEnabled();
245    }
246
247    /**
248     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#reset()
249     */
250    public void reset() {
251
252        getActiveWidget().reset();
253    }
254
255    /**
256     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setAutoHideParent(org.opencms.gwt.client.ui.I_CmsAutoHider)
257     */
258    public void setAutoHideParent(I_CmsAutoHider autoHideParent) {
259
260        // do nothing
261    }
262
263    /**
264     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setEnabled(boolean)
265     */
266    public void setEnabled(boolean enabled) {
267
268        getActiveWidget().setEnabled(enabled);
269    }
270
271    /**
272     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setErrorMessage(java.lang.String)
273     */
274    public void setErrorMessage(String errorMessage) {
275
276        m_error = errorMessage;
277
278        getActiveWidget().setErrorMessage(errorMessage);
279    }
280
281    /**
282     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setFormValueAsString(java.lang.String)
283     */
284    public void setFormValueAsString(String value) {
285
286        getActiveWidget().setFormValueAsString(value);
287    }
288
289    /**
290     * @see org.opencms.gwt.client.ui.input.I_CmsHasGhostValue#setGhostMode(boolean)
291     */
292    public void setGhostMode(boolean enable) {
293
294        m_selectBox.setGhostMode(enable);
295        m_comboBox.setGhostMode(enable);
296    }
297
298    /**
299     * @see org.opencms.gwt.client.ui.input.I_CmsHasGhostValue#setGhostValue(java.lang.String, boolean)
300     */
301    public void setGhostValue(String value, boolean isGhostMode) {
302
303        m_selectBox.setGhostValue(value, isGhostMode);
304        m_comboBox.setGhostValue(value, isGhostMode);
305    }
306
307    /**
308     * Switches to combo box mode.<p>
309     */
310    protected void enableComboMode() {
311
312        String value = m_selectBox.getFormValueAsString();
313        m_selectBox.removeFromParent();
314        m_comboMode = true;
315        m_panel.add(m_comboBox);
316        m_comboBox.setFormValueAsString(value);
317        m_comboBox.setErrorMessage(m_error);
318        m_comboBox.setEnabled(m_selectBox.isEnabled());
319    }
320
321    /**
322     * Gets the active widget (select or combo box).<p>
323     *
324     * @return the active widget
325     */
326    private A_CmsSelectBox<?> getActiveWidget() {
327
328        if (m_comboMode) {
329            return m_comboBox;
330        } else {
331            return m_selectBox;
332        }
333    }
334
335}