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.CmsCoreProvider;
031import org.opencms.gwt.client.I_CmsHasInit;
032import org.opencms.gwt.client.Messages;
033import org.opencms.gwt.client.ui.CmsPopup;
034import org.opencms.gwt.client.ui.CmsPushButton;
035import org.opencms.gwt.client.ui.I_CmsAutoHider;
036import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle;
037import org.opencms.gwt.client.ui.input.form.CmsWidgetFactoryRegistry;
038import org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory;
039import org.opencms.gwt.client.util.CmsDomUtil;
040import org.opencms.util.CmsStringUtil;
041
042import java.util.Map;
043
044import com.google.common.base.Optional;
045import com.google.gwt.core.client.Scheduler;
046import com.google.gwt.core.client.Scheduler.ScheduledCommand;
047import com.google.gwt.dom.client.Document;
048import com.google.gwt.event.dom.client.BlurEvent;
049import com.google.gwt.event.dom.client.BlurHandler;
050import com.google.gwt.event.dom.client.ClickEvent;
051import com.google.gwt.event.dom.client.ClickHandler;
052import com.google.gwt.event.logical.shared.ValueChangeHandler;
053import com.google.gwt.user.client.ui.Composite;
054import com.google.gwt.user.client.ui.FlowPanel;
055import com.google.gwt.user.client.ui.Label;
056import com.google.gwt.user.client.ui.Panel;
057import com.google.gwt.user.client.ui.SimplePanel;
058import com.google.gwt.user.client.ui.TextBox;
059
060/**
061 * Basic text area widget for forms.<p>
062 *
063 * @since 8.0.0
064 *
065 */
066public class CmsColorPicker extends Composite implements I_CmsFormWidget, I_CmsHasInit {
067
068    /** The widget type identifier for this widget. */
069    private static final String WIDGET_TYPE = "colorPicker";
070
071    /** The color picker JS library path. */
072    private static final String COLOR_PICKER_JS = "components/widgets/vanilla-picker.min.js";
073
074    /** The field to display the color. */
075    protected SimplePanel m_colorField = new SimplePanel();
076
077    /** The color value.*/
078    protected String m_colorValue = "transparent";
079
080    /** The popup to choose the color. */
081    protected CmsPopup m_popup = new CmsPopup();
082
083    /** The parent to the native color picker. */
084    private Label m_nativePickerParent;
085
086    /** The field to display the value. */
087    protected SimplePanel m_textboxpanel = new SimplePanel();
088
089    /** The x-coords of the popup. */
090    protected int m_xcoordspopup;
091
092    /** The y-coords of the popup. */
093    protected int m_ycoordspopup;
094
095    /** The error display for this widget. */
096    private CmsErrorWidget m_error = new CmsErrorWidget();
097
098    /** The root panel containing the other components of this widget. */
099    private Panel m_panel = new FlowPanel();
100
101    /** The internal textbox used by this widget to display the color value. */
102    protected TextBox m_textboxColorValue = new TextBox();
103
104    /** THe counter to not set the buttons more then one time. */
105    int m_count;
106
107    /** The current native picker color value. */
108    private String m_nativePickerValue;
109
110    /**
111     * Text area widgets for ADE forms.<p>
112     */
113    public CmsColorPicker() {
114
115        super();
116        String jsUri = CmsStringUtil.joinPaths(CmsCoreProvider.get().getWorkplaceResourcesPrefix(), COLOR_PICKER_JS);
117        CmsDomUtil.ensureJavaScriptIncluded(jsUri);
118        initWidget(m_panel);
119        m_panel.add(m_colorField);
120        m_panel.add(m_textboxpanel);
121        m_panel.add(m_error);
122        m_textboxpanel.add(m_textboxColorValue);
123
124        m_panel.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().colorPicker());
125        m_textboxColorValue.addBlurHandler(new BlurHandler() {
126
127            public void onBlur(BlurEvent event) {
128
129                checkvalue(m_textboxColorValue.getValue());
130
131            }
132        });
133
134        m_colorField.addDomHandler(new ClickHandler() {
135
136            public void onClick(ClickEvent event) {
137
138                if (m_popup.isShowing()) {
139                    closePopup();
140                } else {
141                    openPopup();
142                }
143
144            }
145
146        }, ClickEvent.getType());
147
148    }
149
150    /**
151     * Initializes this class.<p>
152     */
153    public static void initClass() {
154
155        // registers a factory for creating new instances of this widget
156        CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE, new I_CmsFormWidgetFactory() {
157
158            /**
159             * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional)
160             */
161            public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) {
162
163                return new CmsColorPicker();
164            }
165        });
166    }
167
168    /**
169     * Adds a value change handler to the textbox.<p>
170     *
171     * @param handler the value change handler to add
172     */
173    public void addValueChangeHandler(ValueChangeHandler<String> handler) {
174
175        m_textboxColorValue.addValueChangeHandler(handler);
176    }
177
178    /**
179     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getApparentValue()
180     */
181    public String getApparentValue() {
182
183        return getFormValueAsString();
184    }
185
186    /**
187     * Returns the colorfield.<p>
188     *
189     * @return the colorfield
190     */
191    public SimplePanel getColorfield() {
192
193        return m_colorField;
194    }
195
196    /**
197     * Returns the color value textbox.<p>
198     *
199     * @return the color value textbox
200     * */
201    public TextBox getColorValueBox() {
202
203        return m_textboxColorValue;
204    }
205
206    /**
207     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFieldType()
208     */
209    public FieldType getFieldType() {
210
211        return I_CmsFormWidget.FieldType.STRING;
212    }
213
214    /**
215     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValue()
216     */
217    public Object getFormValue() {
218
219        if ((m_textboxColorValue.getText() == null) || !validateColorValue(m_textboxColorValue.getText())) {
220            m_textboxColorValue.setText("");
221        }
222        return m_textboxColorValue.getText();
223    }
224
225    /**
226     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValueAsString()
227     */
228    public String getFormValueAsString() {
229
230        return (String)getFormValue();
231    }
232
233    /**
234     * Returns the text contained in the text area.<p>
235     *
236     * @return the text in the text area
237     */
238    public String getText() {
239
240        return m_textboxColorValue.getText();
241    }
242
243    /**
244     * Returns the color value textboxpanel.<p>
245     *
246     * @return the color value textboxpanel
247     */
248    public SimplePanel getTextboxPanel() {
249
250        return m_textboxpanel;
251    }
252
253    /**
254     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#isEnabled()
255     */
256    public boolean isEnabled() {
257
258        return m_textboxColorValue.isEnabled();
259    }
260
261    /**
262     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#reset()
263     */
264    public void reset() {
265
266        m_textboxColorValue.setText("");
267    }
268
269    /**
270     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setAutoHideParent(org.opencms.gwt.client.ui.I_CmsAutoHider)
271     */
272    public void setAutoHideParent(I_CmsAutoHider autoHideParent) {
273
274        // nothing to do
275    }
276
277    /**
278     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setEnabled(boolean)
279     */
280    public void setEnabled(boolean enabled) {
281
282        // TODO: Auto-generated method stub
283
284    }
285
286    /**
287     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setErrorMessage(java.lang.String)
288     */
289    public void setErrorMessage(String errorMessage) {
290
291        m_error.setText(errorMessage);
292    }
293
294    /**
295     * Sets the value of the widget.<p>
296     *
297     * @param value the new value
298     */
299    public void setFormValue(Object value) {
300
301        if (value == null) {
302            value = "";
303        }
304        if (value instanceof String) {
305            String strValue = (String)value;
306            if (strValue.length() > 0) {
307                checkvalue(strValue);
308            }
309        }
310
311    }
312
313    /**
314     * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setFormValueAsString(java.lang.String)
315     */
316    public void setFormValueAsString(String newValue) {
317
318        setFormValue(newValue);
319    }
320
321    /**
322     * Sets the name of the input field.<p>
323     *
324     * @param name of the input field
325     * */
326    public void setName(String name) {
327
328        m_textboxColorValue.setName(name);
329
330    }
331
332    /**
333     * Sets the text in the text area.<p>
334     *
335     * @param text the new text
336     */
337    public void setText(String text) {
338
339        m_textboxColorValue.setText(text);
340    }
341
342    /**
343     * Validates the inputed color value.
344     * @param colorvalue the value of the color
345     * @return true if the inputed color value is valid
346     */
347    protected boolean checkvalue(String colorvalue) {
348
349        boolean valid = validateColorValue(colorvalue);
350        if (valid) {
351            if (colorvalue.length() == 4) {
352                char[] chr = colorvalue.toCharArray();
353                for (int i = 1; i < 4; i++) {
354                    String foo = String.valueOf(chr[i]);
355                    colorvalue = colorvalue.replaceFirst(foo, foo + foo);
356                }
357            }
358            m_textboxColorValue.setValue(colorvalue, true);
359            m_colorField.getElement().getStyle().setBackgroundColor(colorvalue);
360            m_colorValue = colorvalue;
361        }
362        return valid;
363    }
364
365    /**
366     * Close the popup and store the color value in the colorvalue field.<p>
367     *
368     */
369    protected void closePopup() {
370
371        if (checkvalue(m_nativePickerValue)) {
372            m_popup.hide();
373        }
374    }
375
376    /**
377     * Close the popup and store the old color value in the colorvalue field.<p>
378     *
379     */
380    protected void closePopupDefault() {
381
382        if (checkvalue(m_textboxColorValue.getText())) {
383            m_popup.hide();
384        }
385
386    }
387
388    /**
389     * Converts the integer value to an hex value.<p>
390     * @param i the integer value
391     * @return the hex string
392     */
393    protected String convertToHex(int i) {
394
395        String hexString = Integer.toHexString(i);
396        while (hexString.length() < 2) {
397            hexString = "0" + hexString;
398        }
399        return hexString;
400    }
401
402    /**
403     * @see com.google.gwt.user.client.ui.Composite#onAttach()
404     */
405    @Override
406    protected void onAttach() {
407
408        super.onAttach();
409        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
410
411            public void execute() {
412
413                m_colorField.getElement().getStyle().setBackgroundColor(m_colorValue);
414            }
415        });
416    }
417
418    /**
419     * Helper function  to open the popup.<p>
420     */
421    protected void openPopup() {
422
423        m_popup.setWidth(262);
424        m_popup.setAutoHideEnabled(false);
425        m_popup.showRelativeTo(m_colorField);
426        m_popup.setModal(true);
427        if (m_popup.getWidgetCount() != 0) {
428            m_popup.remove(m_popup.getWidget(0));
429        }
430        m_nativePickerParent = new Label();
431        String id = Document.get().createUniqueId();
432        m_nativePickerParent.getElement().setId(id);
433
434        m_popup.add(m_nativePickerParent);
435        initNativePicker(getFormValueAsString(), "#" + id);
436        if (m_count == 0) {
437            CmsPushButton close = new CmsPushButton();
438            CmsPushButton cancel = new CmsPushButton();
439            cancel.setText(Messages.get().key(Messages.GUI_CANCEL_0));
440            cancel.setTitle(Messages.get().key(Messages.GUI_CANCEL_0));
441            close.setText(Messages.get().key(Messages.GUI_OK_0));
442            close.setTitle(Messages.get().key(Messages.GUI_OK_0));
443            close.addClickHandler(new ClickHandler() {
444
445                public void onClick(ClickEvent event) {
446
447                    closePopup();
448
449                }
450            });
451            cancel.addClickHandler(new ClickHandler() {
452
453                public void onClick(ClickEvent event) {
454
455                    closePopupDefault();
456
457                }
458            });
459            m_popup.addButton(cancel);
460            m_popup.addButton(close);
461            m_count = 1;
462        }
463
464        m_xcoordspopup = m_popup.getPopupLeft();
465        m_ycoordspopup = m_popup.getPopupTop();
466        // reposition to take used height into account
467        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
468
469            public void execute() {
470
471                m_popup.showRelativeTo(m_colorField);
472            }
473        });
474
475    }
476
477    /**
478     * Initializes the native color picker.<p>
479     *
480     * @param value the current value
481     * @param selector the parent element selector
482     */
483    private native void initNativePicker(String value, String selector) /*-{
484        var self = this;
485        self.@org.opencms.gwt.client.ui.input.CmsColorPicker::m_nativePickerValue = value;
486        var parentEl = $doc.querySelector(selector);
487        var picker = new $wnd.Picker(
488                {
489                    parent : parentEl,
490                    color : value,
491                    popup : false,
492                    alpha : false,
493                    editorFormat : 'hex',
494                    editor : true,
495                    onChange : function(color) {
496                        // cut off the last two digits to remove the alpha value
497                        var hexVal = color.hex;
498                        hexVal = hexVal.substring(0, 7)
499                        self.@org.opencms.gwt.client.ui.input.CmsColorPicker::m_nativePickerValue = hexVal;
500                    },
501                });
502    }-*/;
503
504    /**
505     * Checks if the given string is a valid colorvalue.<p>
506     *
507     * @param colorvalue to check
508     * @return true if the value is valid otherwise false
509     */
510    private boolean validateColorValue(String colorvalue) {
511
512        boolean valid = colorvalue.matches("^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$");
513        return valid;
514    }
515}