View Javadoc
1   // Copyright (C) 2010-2017 Yozons, Inc.
2   // CKEditor for Vaadin - Widget linkage for using CKEditor within a Vaadin application.
3   //
4   // This software is released under the Apache License 2.0 <http://www.apache.org/licenses/LICENSE-2.0.html>
5   //
6   // This software was originally based on the Vaadin incubator component TinyMCEEditor written by Matti Tahvonen.
7   // It was later converted to extend AbstractField instead of com.vaadin.ui.TextField, at which time some of the code from TextField was used here.
8   //
9   package org.vaadin.openesignforms.ckeditor;
10  
11  import java.io.Serializable;
12  import java.lang.reflect.Method;
13  import java.util.LinkedList;
14  import java.util.Map;
15  import java.util.Set;
16  
17  import org.vaadin.openesignforms.ckeditor.widgetset.client.ui.VCKEditorTextField;
18  
19  import com.vaadin.v7.data.Property;
20  import com.vaadin.v7.data.util.converter.Converter;
21  import com.vaadin.event.ConnectorEventListener;
22  import com.vaadin.v7.event.FieldEvents;
23  import com.vaadin.event.FieldEvents.BlurEvent;
24  import com.vaadin.event.FieldEvents.BlurListener;
25  import com.vaadin.event.FieldEvents.FocusEvent;
26  import com.vaadin.event.FieldEvents.FocusListener;
27  import com.vaadin.server.PaintException;
28  import com.vaadin.server.PaintTarget;
29  import com.vaadin.v7.ui.AbstractField;
30  import com.vaadin.ui.Component;
31  import com.vaadin.ui.LegacyComponent;
32  import com.vaadin.util.ReflectTools;
33  
34  /**
35   * Server side component for the VCKEditorTextField widget.  
36   */
37  @SuppressWarnings("deprecation")
38  public class CKEditorTextField extends AbstractField<String> 
39  	implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, Component.Focusable, LegacyComponent  {
40  	private static final long serialVersionUID = -4416787199757304854L;
41  
42  	private CKEditorConfig config;
43  	private String version = "unknown";
44  	private String insertText = null;
45  	private String insertHtml = null;
46  	private boolean protectedBody = false;
47  	private boolean viewWithoutEditor = false;
48  	private boolean focusRequested = false;
49  	protected LinkedList<VaadinSaveListener> vaadinSaveListenerList;
50  
51  	private boolean textIsDirty;
52  
53  	public CKEditorTextField() {
54  		super.setValue("");
55  		setWidth("100%");
56  		setHeight("300px");
57  	}
58  	
59  	public CKEditorTextField(CKEditorConfig config) {
60  		this();
61  		setConfig(config);
62  	}
63  	
64  	public CKEditorTextField(CKEditorConfig config, String initialValue) {
65  		this();
66  		setValue(initialValue);
67  		setConfig(config);
68  	}
69  	
70  	public void setConfig(CKEditorConfig config) {
71  		this.config = config;
72  		if ( config.isReadOnly() )
73  			setReadOnly(true);
74  	}
75  	
76  	public String getVersion() {
77  		return version;
78  	}
79  	
80  	@Override
81      public void setValue(String newValue) throws Property.ReadOnlyException, Converter.ConversionException {
82      	if ( newValue == null )
83      		newValue = "";
84      	super.setValue(newValue, false);  // will call setInternalValue
85      	markAsDirty();
86      }
87  	
88  	@Override
89  	protected void setInternalValue(String newValue) {
90  		super.setInternalValue(newValue==null?"":newValue);
91      	textIsDirty = true;
92      }
93  	
94   	@SuppressWarnings("rawtypes")
95  	@Override
96   	public void setPropertyDataSource(Property newDataSource) {
97   		super.setPropertyDataSource(newDataSource);
98   		markAsDirty();
99       	textIsDirty = true;
100  	}
101  
102  	@Override
103  	protected void fireValueChange(boolean repaintIsNotNeeded) {
104  		super.fireValueChange(repaintIsNotNeeded);
105  		textIsDirty = true;
106  	}	
107  	
108  	@Override
109 	public void beforeClientResponse(boolean initial) {
110 		if (initial) {
111 			textIsDirty = true;
112 		}
113 		super.beforeClientResponse(initial);
114 	}
115 	
116 	@Override
117 	public void paintContent(PaintTarget target) throws PaintException {
118 		//super.paintContent(target);
119 		
120 		if (textIsDirty) {
121 			Object currValueObject = getValue();
122 			String currValue = currValueObject == null ? "" : currValueObject.toString();
123 			target.addVariable(this, VCKEditorTextField.VAR_TEXT, currValue);
124 			textIsDirty = false;
125 		}
126 		
127 		target.addAttribute(VCKEditorTextField.ATTR_READONLY, isReadOnly());
128 		target.addAttribute(VCKEditorTextField.ATTR_VIEW_WITHOUT_EDITOR, isViewWithoutEditor());
129 		//System.out.println("*** TRACE FROM SERVER paintContent() - sending value to browser (" + currValue.length() + ") >>>" + currValue + "<<< " + System.currentTimeMillis());
130 		
131 		if (config != null) {
132 			target.addAttribute(VCKEditorTextField.ATTR_INPAGECONFIG, config.getInPageConfig());
133 			
134 			if ( config.hasWriterRules() ) {
135 				int i = 0;
136 				Set<String> tagNameSet = config.getWriterRulesTagNames();
137 				for( String tagName : tagNameSet ) {
138 					target.addAttribute(VCKEditorTextField.ATTR_WRITERRULES_TAGNAME+i, tagName);
139 					target.addAttribute(VCKEditorTextField.ATTR_WRITERRULES_JSRULE+i, config.getWriterRuleByTagName(tagName));
140 					++i;
141 				}
142 			}
143 			
144 			if ( config.hasWriterIndentationChars() ) {
145 				target.addAttribute(VCKEditorTextField.ATTR_WRITER_INDENTATIONCHARS, config.getWriterIndentationChars());
146 			}
147 			
148 			if ( config.hasKeystrokeMappings() ) {
149 				int i = 0;
150 				Set<Integer> keystrokeSet = config.getKeystrokes();
151 				for( Integer keystroke : keystrokeSet ) {
152 					target.addAttribute(VCKEditorTextField.ATTR_KEYSTROKES_KEYSTROKE+i, keystroke);
153 					target.addAttribute(VCKEditorTextField.ATTR_KEYSTROKES_COMMAND+i, config.getKeystrokeCommandByKeystroke(keystroke));
154 					++i;
155 				}
156 			}
157 			
158 			if ( config.hasProtectedSource() ) {
159 				int i = 0;
160 				for( String protectedSourceRegex : config.getProtectedSource() ) {
161 					target.addAttribute(VCKEditorTextField.ATTR_PROTECTED_SOURCE+i, protectedSourceRegex);
162 					++i;
163 				}
164 			}
165 		}
166 		
167 		target.addAttribute(VCKEditorTextField.ATTR_PROTECTED_BODY, protectedBody);
168 		
169 		if (insertHtml != null) {
170 			target.addAttribute(VCKEditorTextField.ATTR_INSERT_HTML, insertHtml);
171 			insertHtml = null;
172 		}
173 		if (insertText != null) {
174 			target.addAttribute(VCKEditorTextField.ATTR_INSERT_TEXT, insertText);
175 			insertText = null;
176 		}	
177 		
178 		if ( focusRequested ) {
179 			target.addAttribute(VCKEditorTextField.ATTR_FOCUS, true);
180 			focusRequested = false;
181 		}
182 	}
183 	
184     public void changeVariables(Object source, Map<String, Object> variables) {
185         // Sets the CKEditor version
186         if (variables.containsKey(VCKEditorTextField.VAR_VERSION)) {
187         	version = (String)variables.get(VCKEditorTextField.VAR_VERSION);
188         }
189         
190         // Sets the text
191         if (variables.containsKey(VCKEditorTextField.VAR_TEXT) && ! isReadOnly()) {
192             // Only do the setting if the string representation of the value has been updated
193         	Object newVarTextObject = variables.get(VCKEditorTextField.VAR_TEXT);
194             String newValue = newVarTextObject == null ? "" : newVarTextObject.toString();
195 
196             Object currValueObject = getValue();
197             final String oldValue = currValueObject == null ? "" : currValueObject.toString();
198             if ( ! newValue.equals(oldValue) ) {
199         		//System.out.println("*** TRACE FROM CLIENT changeVariables() - new value (" + newValue.length() + ") >>>" + newValue + "<<< " + System.currentTimeMillis());
200                 setValue(newValue, true);
201             }
202         }
203         
204         if (variables.containsKey(FocusEvent.EVENT_ID)) {
205 			//System.out.println("------------------------------");
206     		//System.out.println("*** TRACE FROM CLIENT changeVariables() - FOCUS - " + System.currentTimeMillis());
207             fireEvent(new FocusEvent(this));
208         }
209         if (variables.containsKey(BlurEvent.EVENT_ID)) {
210     		//System.out.println("*** TRACE FROM CLIENT changeVariables() - BLUR - " + System.currentTimeMillis());
211             fireEvent(new BlurEvent(this));
212         }
213         
214         if (variables.containsKey(SelectionChangeEvent.EVENT_ID)) {
215         	Object selectedHtmlObject = variables.get(SelectionChangeEvent.EVENT_ID);
216             if ( selectedHtmlObject != null ) {
217             	String selectedHtml = selectedHtmlObject.toString();
218             	fireEvent(new SelectionChangeEvent(this,selectedHtml));
219             }
220         }
221 
222         // See if the vaadinsave button was pressed
223         if (variables.containsKey(VCKEditorTextField.VAR_VAADIN_SAVE_BUTTON_PRESSED) && ! isReadOnly()) {
224         	notifyVaadinSaveListeners();
225         }
226     }
227 
228 
229 	@Override
230 	public Class<String> getType() {
231 		return String.class;
232 	}
233 
234 	public void addListener(BlurListener listener) {
235         addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod);
236 	}
237 	@Override
238 	public void addBlurListener(BlurListener listener) {
239 		addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, BlurListener.blurMethod);
240 	}
241 
242 	public void removeListener(BlurListener listener) {
243         removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
244 	}
245 	@Override
246 	public void removeBlurListener(BlurListener listener) {
247 		removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
248 	}
249 
250 	public void addListener(FocusListener listener) {
251         addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod);
252 	}
253 	@Override
254 	public void addFocusListener(FocusListener listener) {
255 		addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, FocusListener.focusMethod);
256 	}
257 
258 	public void removeListener(FocusListener listener) {
259         removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
260 	}
261 	@Override
262 	public void removeFocusListener(FocusListener listener) {
263 		removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
264 	}
265 	
266 	/**
267 	 * @param listener
268 	 */
269 	public void addSelectionChangeListener(SelectionChangeListener listener) {
270 		addListener(SelectionChangeEvent.EVENT_ID, SelectionChangeEvent.class, listener, SelectionChangeListener.selectionChangeMethod);
271 	}
272 	public void removeSelectionChangeListener(SelectionChangeListener listener) {
273 		removeListener(SelectionChangeEvent.EVENT_ID, SelectionChangeEvent.class, listener);
274 	}
275 	
276 	@Override
277     public void setHeight(String height) {
278 		super.setHeight(height);
279 	}
280 
281 	@Override
282 	public void attach() {
283 		//System.out.println("** CKEDITOR DEBUG: attach()");
284 		super.attach();
285 	}
286 	
287 	@Override
288 	public void detach() {
289 		//System.out.println("** CKEDITOR DEBUG: detach()");
290 		super.detach();
291 	    textIsDirty = true;
292 	}
293 	
294 	// Part of Focusable
295 	@Override
296     public void focus() {
297 		super.focus();
298 		focusRequested = true;
299 		markAsDirty();
300     }
301 	
302 	public boolean isViewWithoutEditor() {
303 		return viewWithoutEditor;
304 	}
305 	public void setViewWithoutEditor(boolean v) {
306 		viewWithoutEditor = v;
307 		markAsDirty();
308 	}
309 	
310 	public void insertHtml(String html) {
311 		if (insertHtml == null) 
312 			insertHtml = html;
313 		else 
314 			insertHtml += html;
315 		markAsDirty();
316 	}
317 	
318 	public void insertText(String text) {
319 		if (insertText == null) 
320 			insertText = text;
321 		else 
322 			insertText += text;
323 		markAsDirty();
324 	}
325 
326 	public void setProtectedBody(boolean protectedBody) {
327 		this.protectedBody = protectedBody;
328 		markAsDirty();
329 	}
330 
331 	public boolean isProtectedBody() {
332 		return protectedBody;
333 	}
334 	
335 	
336 	public synchronized void addVaadinSaveListener(VaadinSaveListener listener) {
337 		if ( vaadinSaveListenerList == null )
338 			vaadinSaveListenerList = new LinkedList<VaadinSaveListener>();
339 		vaadinSaveListenerList.add(listener);
340 	}
341 	public synchronized void removeVaadinSaveListener(VaadinSaveListener listener) {
342 		if ( vaadinSaveListenerList != null )
343 			vaadinSaveListenerList.remove(listener);
344 	}
345 	synchronized void notifyVaadinSaveListeners() {
346 		if ( vaadinSaveListenerList != null ) {
347 			for( VaadinSaveListener listener : vaadinSaveListenerList )
348 				listener.vaadinSave(this);
349 		}
350 	}
351 	
352 	public interface VaadinSaveListener extends Serializable {
353 		/**
354 	     * Notifies this listener that the vaadinsave button in the editor was pressed.
355 	     * 
356 	     * @param editor the CKEditorTextField that was saved
357 	     */
358 	    public void vaadinSave(CKEditorTextField editor);
359 	}
360 	
361 	
362     @SuppressWarnings("serial")
363     public static class SelectionChangeEvent extends Component.Event {
364         public static final String EVENT_ID = VCKEditorTextField.EVENT_SELECTION_CHANGE;
365 
366         private String selectedHtml;
367         
368         public SelectionChangeEvent(Component source, String selectedHtml) {
369             super(source);
370             this.selectedHtml = selectedHtml;
371         }
372         
373         public String getSelectedHtml() {
374         	return selectedHtml;
375         }
376         
377         public boolean hasSelectedHtml() {
378         	return ! "".equals(selectedHtml);
379         }
380     }
381 
382     public interface SelectionChangeListener extends ConnectorEventListener {
383         public static final Method selectionChangeMethod = ReflectTools.findMethod(
384                 SelectionChangeListener.class, "selectionChange", SelectionChangeEvent.class);
385         
386         public void selectionChange(SelectionChangeEvent event);
387     }
388 
389 }