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