001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.extensions.ajax.markup.html; 018 019import org.apache.wicket.AttributeModifier; 020import org.apache.wicket.MarkupContainer; 021import org.apache.wicket.ajax.attributes.AjaxCallListener; 022import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; 023import org.apache.wicket.ajax.attributes.AjaxRequestAttributes.Method; 024import org.apache.wicket.markup.ComponentTag; 025import org.apache.wicket.markup.MarkupStream; 026import org.apache.wicket.markup.html.basic.MultiLineLabel; 027import org.apache.wicket.markup.html.form.FormComponent; 028import org.apache.wicket.markup.html.form.TextArea; 029import org.apache.wicket.model.IModel; 030import org.apache.wicket.util.convert.IConverter; 031import org.apache.wicket.util.string.Strings; 032 033/** 034 * An inplace editor much like {@link AjaxEditableLabel}, but now with support for multi line 035 * content and a {@link TextArea text area} as its editor. 036 * <p> 037 * <strong>Note</strong>: attach this component to a block HTML element (like <div>) 038 * because its label uses block elements to show the content. 039 * </p> 040 * 041 * @author eelcohillenius 042 * 043 * @param <T> 044 * Model object type 045 */ 046public class AjaxEditableMultiLineLabel<T> extends AjaxEditableLabel<T> 047{ 048 private static final long serialVersionUID = 1L; 049 050 /** text area's number of rows. */ 051 private int rows = 10; 052 053 /** text area's number of columns. */ 054 private int cols = 40; 055 056 /** 057 * Construct. 058 * 059 * @param id 060 * The component id 061 */ 062 public AjaxEditableMultiLineLabel(final String id) 063 { 064 super(id); 065 } 066 067 /** 068 * Construct. 069 * 070 * @param id 071 * The component id 072 * @param model 073 * The model 074 */ 075 public AjaxEditableMultiLineLabel(final String id, final IModel<T> model) 076 { 077 super(id, model); 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 @Override 084 protected MultiLineLabel newLabel(final MarkupContainer parent, final String componentId, 085 final IModel<T> model) 086 { 087 MultiLineLabel label = new MultiLineLabel(componentId, model) 088 { 089 private static final long serialVersionUID = 1L; 090 091 @Override 092 public <C> IConverter<C> getConverter(final Class<C> type) 093 { 094 return AjaxEditableMultiLineLabel.this.getConverter(type); 095 } 096 097 @Override 098 protected void onConfigure() 099 { 100 super.onConfigure(); 101 setEscapeModelStrings(AjaxEditableMultiLineLabel.this.getEscapeModelStrings()); 102 } 103 104 @Override 105 public void onComponentTagBody(final MarkupStream markupStream, 106 final ComponentTag openTag) 107 { 108 Object modelObject = getDefaultModelObject(); 109 if ((modelObject == null) || (modelObject instanceof String && ((String) modelObject).isEmpty())) 110 { 111 String nullLabel = defaultNullLabel(); 112 replaceComponentTagBody(markupStream, openTag, 113 getEscapeModelStrings() ? Strings.escapeMarkup(nullLabel) : nullLabel); 114 } 115 else 116 { 117 super.onComponentTagBody(markupStream, openTag); 118 } 119 } 120 }; 121 label.setOutputMarkupId(true); 122 label.add(new LabelAjaxBehavior(getLabelAjaxEvent())); 123 return label; 124 } 125 126 /** 127 * By default this returns "click", users can overwrite this on which event the label behavior 128 * should be triggered 129 * 130 * @return The event name 131 */ 132 @Override 133 protected String getLabelAjaxEvent() 134 { 135 return "click"; 136 } 137 138 @Override 139 protected FormComponent<T> newEditor(final MarkupContainer parent, final String componentId, 140 final IModel<T> model) 141 { 142 TextArea<T> editor = new TextArea<T>(componentId, model) 143 { 144 private static final long serialVersionUID = 1L; 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override 150 public <C> IConverter<C> getConverter(final Class<C> type) 151 { 152 return AjaxEditableMultiLineLabel.this.getConverter(type); 153 } 154 155 @Override 156 protected boolean shouldTrimInput() 157 { 158 return AjaxEditableMultiLineLabel.this.shouldTrimInput(); 159 } 160 161 @Override 162 protected void onModelChanged() 163 { 164 AjaxEditableMultiLineLabel.this.onModelChanged(); 165 } 166 167 @Override 168 protected void onModelChanging() 169 { 170 AjaxEditableMultiLineLabel.this.onModelChanging(); 171 } 172 }; 173 editor.add(new AttributeModifier("rows", (IModel<Integer>) () -> rows)); 174 editor.add(new AttributeModifier("cols", (IModel<Integer>) () -> cols)); 175 editor.setOutputMarkupId(true); 176 editor.setVisible(false); 177 editor.add(new EditorAjaxBehavior() 178 { 179 private static final long serialVersionUID = 1L; 180 181 @Override 182 protected void updateAjaxAttributes(AjaxRequestAttributes attributes) 183 { 184 super.updateAjaxAttributes(attributes); 185 attributes.setMethod(Method.POST); 186 attributes.setEventNames("blur", "keyup"); 187 CharSequence dynamicExtraParameters = 188 "var result = [], " + 189 "kc=Wicket.Event.keyCode(attrs.event)," + 190 "evtType=attrs.event.type;" + 191 "if (evtType === 'keyup') {" + 192 // ESCAPE key 193 "if (kc===27) { result.push( { name: 'save', value: false } ); }" + 194 "}" + 195 "else if (evtType==='blur') { result = Wicket.Form.serializeElement(attrs.c); result.push( { name: 'save', value: true } ); }" + 196 "return result;"; 197 attributes.getDynamicExtraParameters().add(dynamicExtraParameters); 198 199 CharSequence precondition = 200 "var kc=Wicket.Event.keyCode(attrs.event),"+ 201 "evtType=attrs.event.type,"+ 202 "ret=false;"+ 203 "if(evtType==='blur' || (evtType==='keyup' && (kc===27))) ret = true;"+ 204 "return ret;"; 205 AjaxCallListener ajaxCallListener = new AjaxCallListener(); 206 ajaxCallListener.onPrecondition(precondition); 207 attributes.getAjaxCallListeners().add(ajaxCallListener); 208 } 209 }); 210 return editor; 211 } 212 213 /** 214 * Gets text area's number of columns. 215 * 216 * @return text area's number of columns 217 */ 218 public final int getCols() 219 { 220 return cols; 221 } 222 223 /** 224 * Sets text area's number of columns. 225 * 226 * @param cols 227 * text area's number of columns 228 */ 229 public final void setCols(final int cols) 230 { 231 this.cols = cols; 232 } 233 234 /** 235 * Gets text area's number of rows. 236 * 237 * @return text area's number of rows 238 */ 239 public final int getRows() 240 { 241 return rows; 242 } 243 244 /** 245 * Sets text area's number of rows. 246 * 247 * @param rows 248 * text area's number of rows 249 */ 250 public final void setRows(final int rows) 251 { 252 this.rows = rows; 253 } 254 255 /** 256 * Override this to display a different value when the model object is null. Default is 257 * <code>...</code> 258 * 259 * @return The string which should be displayed when the model object is null. 260 */ 261 @Override 262 protected String defaultNullLabel() 263 { 264 return "..."; 265 } 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override 271 protected void onModelChanged() 272 { 273 super.onModelChanged(); 274 } 275 276 /** 277 * {@inheritDoc} 278 */ 279 @Override 280 protected void onModelChanging() 281 { 282 super.onModelChanging(); 283 } 284}