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.acacia.client.widgets;
029
030import org.opencms.acacia.client.css.I_CmsLayoutBundle;
031import org.opencms.acacia.client.widgets.CmsTypografUtil.Typograf;
032import org.opencms.gwt.client.util.CmsDomUtil;
033import org.opencms.gwt.shared.CmsGwtConstants;
034import org.opencms.gwt.shared.CmsGwtLog;
035
036import java.util.Objects;
037
038import com.google.gwt.core.client.GWT;
039import com.google.gwt.dom.client.Element;
040import com.google.gwt.event.dom.client.BlurEvent;
041import com.google.gwt.event.dom.client.ClickEvent;
042import com.google.gwt.event.dom.client.FocusEvent;
043import com.google.gwt.event.dom.client.FocusHandler;
044import com.google.gwt.event.logical.shared.ValueChangeEvent;
045import com.google.gwt.event.logical.shared.ValueChangeHandler;
046import com.google.gwt.event.shared.HandlerRegistration;
047import com.google.gwt.uibinder.client.UiBinder;
048import com.google.gwt.uibinder.client.UiField;
049import com.google.gwt.uibinder.client.UiHandler;
050import com.google.gwt.user.client.ui.Composite;
051import com.google.gwt.user.client.ui.FocusPanel;
052import com.google.gwt.user.client.ui.HTMLPanel;
053import com.google.gwt.user.client.ui.TextBox;
054
055import elemental2.core.Global;
056import elemental2.dom.HTMLInputElement;
057import jsinterop.base.Js;
058import jsinterop.base.JsPropertyMap;
059
060/**
061 * Provides a display only widget, for use on a widget dialog.<p>
062 *
063 * */
064public class CmsTextboxWidget extends Composite implements I_CmsEditWidget {
065
066    /**
067     * The UI binder interface.<p>
068     */
069    interface I_CmsTextboxWidgetUiBinder extends UiBinder<HTMLPanel, CmsTextboxWidget> {
070        // nothing to do
071    }
072
073    /** The UI binder instance. */
074    private static I_CmsTextboxWidgetUiBinder uiBinder = GWT.create(I_CmsTextboxWidgetUiBinder.class);
075
076    /** The fader of this widget. */
077    @UiField
078    FocusPanel m_fadePanel;
079
080    /**The main panel of this widget. */
081    HTMLPanel m_mainPanel;
082
083    /** The input test area.*/
084    @UiField
085    TextBox m_textbox;
086
087    /** The token to control activation. */
088    private boolean m_active = true;
089
090    /** The previous value. */
091    private String m_previousValue;
092
093    /** Typograf instance used for typography, if configured. */
094    private Typograf m_typograf;
095
096    /** The value changed handler initialized flag. */
097    private boolean m_valueChangeHandlerInitialized;
098
099    /**
100     * Creates a new display widget.<p>
101     *
102     * @param config the widget configuration
103     */
104    public CmsTextboxWidget(String config) {
105
106        m_mainPanel = uiBinder.createAndBindUi(this);
107        if (config != null) {
108            try {
109                JsPropertyMap<String> configMap = Js.cast(Global.JSON.parse(config));
110                String locale = configMap.get(CmsGwtConstants.JSON_INPUT_LOCALE);
111                String typograf = configMap.get(CmsGwtConstants.JSON_INPUT_TYPOGRAF);
112                boolean typografEnabled = "auto".equals(typograf);
113                if ((locale != null) && typografEnabled && Typograf.hasLocale(locale)) {
114                    m_typograf = CmsTypografUtil.createLiveInstance(locale);
115                }
116            } catch (Exception e) {
117                CmsGwtLog.log(e.getMessage());
118            }
119        }
120
121        initWidget(m_mainPanel);
122    }
123
124    /**
125     * @see com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.google.gwt.event.dom.client.FocusHandler)
126     */
127    public HandlerRegistration addFocusHandler(FocusHandler handler) {
128
129        return addDomHandler(handler, FocusEvent.getType());
130    }
131
132    /**
133     * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler)
134     */
135    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
136
137        // Initialization code
138        if (!m_valueChangeHandlerInitialized) {
139            m_valueChangeHandlerInitialized = true;
140            HTMLInputElement inputElem = Js.cast(m_textbox.getElement());
141            inputElem.addEventListener("input", event -> {
142                if (m_typograf != null) {
143                    try {
144                        String oldValue = inputElem.value;
145                        String newValue = CmsTypografUtil.transform(m_typograf, oldValue);
146                        if (!Objects.equals(oldValue, newValue)) {
147                            int pos = inputElem.selectionStart;
148                            inputElem.value = newValue;
149                            inputElem.setSelectionRange(pos, pos);
150                        }
151                    } catch (Exception e) {
152                        CmsGwtLog.log(e.getMessage());
153                    }
154                }
155                fireValueChange(false);
156            });
157        }
158        return addHandler(handler, ValueChangeEvent.getType());
159    }
160
161    /**
162     * Represents a value change event.<p>
163     *
164     */
165    public void fireChangeEvent() {
166
167        String result = "";
168        if (m_textbox.getText() != null) {
169            result = m_textbox.getText();
170        }
171
172        ValueChangeEvent.fire(this, result);
173    }
174
175    /**
176     * @see com.google.gwt.user.client.ui.HasValue#getValue()
177     */
178    public String getValue() {
179
180        return m_textbox.getText();
181    }
182
183    /**
184     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#isActive()
185     */
186    public boolean isActive() {
187
188        return m_active;
189    }
190
191    /**
192     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#onAttachWidget()
193     */
194    public void onAttachWidget() {
195
196        super.onAttach();
197    }
198
199    /**
200     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#owns(com.google.gwt.dom.client.Element)
201     */
202    public boolean owns(Element element) {
203
204        // TODO implement this in case we want the delete behavior for optional fields
205        return false;
206
207    }
208
209    /**
210     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setActive(boolean)
211     */
212    public void setActive(boolean active) {
213
214        if (m_active == active) {
215            return;
216        }
217
218        m_active = active;
219        if (m_active) {
220            getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.form().inActive());
221            getElement().focus();
222        } else {
223            getElement().addClassName(I_CmsLayoutBundle.INSTANCE.form().inActive());
224        }
225        if (active) {
226            fireChangeEvent();
227        }
228    }
229
230    /**
231     * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setName(java.lang.String)
232     */
233    public void setName(String name) {
234
235        m_textbox.setName(name);
236
237    }
238
239    /**
240     * @see com.google.gwt.user.client.ui.UIObject#setTitle(java.lang.String)
241     */
242    @Override
243    public void setTitle(String title) {
244
245        if ((title.length() * 6.88) > m_mainPanel.getOffsetWidth()) {
246            m_mainPanel.getElement().setTitle(title);
247        } else {
248            m_mainPanel.getElement().setTitle("");
249        }
250    }
251
252    /**
253     * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object)
254     */
255    public void setValue(String value) {
256
257        m_textbox.setText(value);
258    }
259
260    /**
261     * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, boolean)
262     */
263    public void setValue(String value, boolean fireEvents) {
264
265        // set the saved value to the textArea
266        m_textbox.setText(value);
267        m_previousValue = value;
268        if (fireEvents) {
269            fireChangeEvent();
270        }
271    }
272
273    /**
274     * Fires the value change event, if the value has changed.<p>
275     *
276     * @param force <code>true</code> to force firing the event, not regarding an actually changed value
277     */
278    protected void fireValueChange(boolean force) {
279
280        String currentValue = getValue();
281        if (force || !currentValue.equals(m_previousValue)) {
282            m_previousValue = currentValue;
283            ValueChangeEvent.fire(this, currentValue);
284        }
285    }
286
287    /**
288     * Handles fade panel clicks.<p>
289     *
290     * @param event the click event
291     */
292    @UiHandler("m_fadePanel")
293    void onFadeClick(ClickEvent event) {
294
295        m_textbox.setFocus(true);
296        m_textbox.setCursorPos(m_textbox.getText().length());
297    }
298
299    /**
300     * Handles text box blur.<p>
301     *
302     * @param event the blur event
303     */
304    @UiHandler("m_textbox")
305    void onTextboxBlur(BlurEvent event) {
306
307        m_mainPanel.add(m_fadePanel);
308        setTitle(m_textbox.getText());
309    }
310
311    /**
312     * Handles text box focus.<p>
313     *
314     * @param event the focus event
315     */
316    @UiHandler("m_textbox")
317    void onTextboxFocus(FocusEvent event) {
318
319        m_mainPanel.remove(m_fadePanel);
320        setTitle("");
321        CmsDomUtil.fireFocusEvent(this);
322    }
323
324    /**
325     * Handles text box value change.<p>
326     *
327     * @param event the value change event
328     */
329    @UiHandler("m_textbox")
330    void onTextboxValueChange(ValueChangeEvent<String> event) {
331
332        fireValueChange(false);
333    }
334
335}