I want to add a rich text editor to my GWT application. TinyMCE is one candidate, and so is the rich text editor in SmartGWT. Do you have any advice about choosing between the two?
If you're using GWT anyway, you'd be shooting yourself in the foot by not using a GWT rich text editor component, be it from the SmartGWT library or elsewhere.
It's worth noting that GWT has a RichTextArea class too.
I would be so fast to dismiss TinyMCE - it has many advantages over the GWT's rich text editor - for me, it was the plugins - I needed a rich text editor that would produce bbcode, not html - can't do that with GWT's components, unfortunately. So I extended the standard bbcode plugin for TinyMCE to suit my needs and voila :)
Below is a class I used to integrate TinyMCE into GWT (slightly modified from the original class by Aaron Watkins in order to work around drag and drop issues and some other stuff ;)):
Edit: slightly changed to include suggestions by David
/**
* Created on 20/08/2007
*
* Wrapper for TinyMCE
* NOTE: Expects Javascript includes to be in enclosing HTML
*
* Author: Aaron Watkins (aaronDOTjDOTwatkinsATgmailDOTcom)
* Website: http://www.goannatravel.com
* Home Page for initial release of this widget: http://consult.goannatravel.com/code/gwt/tinymce.php
*
* Copyright [Aaron Watkins]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
/**
* TinyMCE -
*
* A wrapper widget for using TinyMCE. It contains a number of JSNI methods that
* I have found useful during development
*
* @author Aaron Watkins
*/
public class TinyMCE extends Composite implements HasText {
private TextArea ta;
private String id;
public TinyMCE(int width, int height) {
super();
VerticalPanel panel = new VerticalPanel();
initWidget(panel);
panel.setWidth("100%");
id = HTMLPanel.createUniqueId();
ta = new TextArea();
ta.setCharacterWidth(width);
ta.setVisibleLines(height);
DOM.setElementAttribute(ta.getElement(), "id", id);
DOM.setStyleAttribute(ta.getElement(), "width", "100%");
panel.add(ta);
}
/**
* getID() -
*
* @return the MCE element's ID
*/
public String getID() {
return id;
}
protected static native String getEditorContents(
String elementId) /*-{
return $wnd.tinyMCE.get(elementId).getContent();
}-*/;
protected static native void setEditorContents(
String elementId, String html) /*-{
$wnd.tinyMCE.execInstanceCommand(
elementId, 'mceSetContent', false, html, false);
}-*/;
public void setText(String text) {
setEditorContents(id, text);
}
public String getText() {
return getEditorContents(id);
}
public void setEnabled(boolean enabled) {
ta.setEnabled(enabled);
}
/**
* @see com.google.gwt.user.client.ui.Widget#onLoad()
*/
protected void onLoad() {
super.onLoad();
DeferredCommand.addCommand(new Command() {
public void execute() {
setWidth("100%");
setTextAreaToTinyMCE(id);
focusMCE(id);
}
});
}
/**
* focusMCE() -
*
* Use this to set the focus to the MCE area
* @param id - the element's ID
*/
protected native void focusMCE(String id) /*-{
$wnd.tinyMCE.execCommand('mceFocus', true, id);
}-*/;
/**
* resetMCE() -
*
* Use this if reusing the same MCE element, but losing focus
*/
public native void resetMCE() /*-{
$wnd.tinyMCE.execCommand('mceResetDesignMode', true);
}-*/;
/**
* unload() -
*
* Unload this MCE editor instance from active memory.
* I use this in the onHide function of the containing widget. This helps
* to avoid problems, especially when using tabs.
*/
public void unload() {
unloadMCE(id);
}
/**
* unloadMCE() -
*
* @param id - The element's ID
* JSNI method to implement unloading the MCE editor instance from memory
*/
protected native void unloadMCE(String id) /*-{
$wnd.tinyMCE.execCommand('mceFocus', false, id);
$wnd.tinyMCE.execCommand('mceRemoveControl', false, id);
}-*/;
/**
* updateContent() -
*
* Update the internal referenced content. Use this if you programatically change
* the original text area's content (eg. do a clear)
* @param id - the ID of the text area that contains the content you wish to copy
*/
protected native void updateContent(String id) /*-{
$wnd.tinyMCE.activeEditor = $wnd.tinyMCE.get(id);
$wnd.tinyMCE.activeEditor.setContent($wnd.document.getElementById(id).value);
}-*/;
/**
* getTextArea() -
*
*/
protected native void getTextData(String id) /*-{
$wnd.tinyMCE.activeEditor = $wnd.tinyMCE.get(id);
$wnd.tinyMCE.activeEditor.save();
$wnd.tinyMCE.triggerSave();
}-*/;
/**
* encodeURIComponent() -
*
* Wrapper for the native URL encoding methods
* @param text - the text to encode
* @return the encoded text
*/
protected native String encodeURIComponent(String text) /*-{
return encodeURIComponent(text);
}-*/;
/**
* setTextAreaToTinyMCE() -
*
* Change a text area to a tiny MCE editing field
* @param id - the text area's ID
*/
protected native void setTextAreaToTinyMCE(String id) /*-{
$wnd.tinyMCE.execCommand('mceAddControl', true, id);
}-*/;
/**
* removeMCE() -
*
* Remove a tiny MCE editing field from a text area
* @param id - the text area's ID
*/
public native void removeMCE(String id) /*-{
$wnd.tinyMCE.execCommand('mceRemoveControl', true, id);
}-*/;
}
Remember to init TinyMCE in your html file, something like (the key setting is mode : "textareas"
, the rest should be safe to change):
<script type="text/javascript" src="js/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({
theme : "advanced",
skin : "default",
mode : "textareas",
plugins : "bbcode",
theme_advanced_buttons1 : "bold,italic,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,sup,sub,|,undo,redo,separator,cut,copy,paste,|,fontsizeselect,forecolor,backcolor",
theme_advanced_buttons2 : "blockquote,link,unlink,image,styleselect,removeformat,|,charmap,code",
theme_advanced_buttons3 : "",
theme_advanced_toolbar_location : "bottom",
theme_advanced_toolbar_align : "center",
theme_advanced_styles : "Code=codeStyle;Quote=quoteStyle;PHP Code=phpCodeStyle",
entity_encoding : "raw",
add_unload_trigger : false,
remove_linebreaks : false,
button_tile_map : true
});
</script>
You can also try some other wrappers like http://www.ohloh.net/p/tinymce-gwt
For some reason, Aaron Watkins' code was not working properly in GWT hosted mode. I managed to fix the problem by changing the way that the contents of the editor are set and retrieved.
I added the following two methods:
protected static native String getEditorContents(
String elementId) /*-{
return $wnd.tinyMCE.get(elementId).getContent();
}-*/;
protected static native void setEditorContents(
String elementId, String html) /*-{
$wnd.tinyMCE.execInstanceCommand(
elementId, 'mceSetContent', false, html, false);
}-*/;
...and replaced the setText() and getText() methods as follows:
public void setText(String text) {
setEditorContents(id, text);
}
public String getText() {
return getEditorContents(id);
}
I'm not sure if this is 'best practice' - I'm sure that Aaron had reasons for doing it his way - but it made my code work in both Firefox and in the GWT hosted browser.