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