/*****************************************************************************
* Copyright (c) 2015, 2016 CEA LIST.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.nebula.widgets.richtext.toolbar;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.nebula.widgets.richtext.RichTextEditor;
import org.eclipse.nebula.widgets.richtext.RichTextEditorConfiguration;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.BrowserFunction;
/**
* The toolbar configuration of the CKEditor toolbar. Contains the default
* toolbar configuration via toolbar groups and gives the ability to dynamically
* add/remove custom buttons.
* <p>
* To customize the CKEditor buttons shown in the toolbar, you need to override
* {@link #getToolbarGroupConfiguration()} and
* {@link #getRemoveButtonConfiguration()}
* </p>
* <p>
* <b>Note:</b> A {@link ToolbarConfiguration} instance is directly connected to
* the {@link Browser} instance of the editor. It can therefore not be re-used
* for multiple {@link RichTextEditor} instances.
* </p>
*
* @deprecated Use the more general {@link RichTextEditorConfiguration}
*/
@Deprecated
public class ToolbarConfiguration {
/**
* Configure whether to remove the <i>paste text</i> button from the
* toolbar. Default is <code>true</code>.
*/
public boolean removePasteText = true;
/**
* Configure whether to remove the <i>paste from word</i> button from the
* toolbar. Default is <code>true</code>.
*/
public boolean removePasteFromWord = true;
/**
* Configure whether to remove the <i>styles</i> combo box from the toolbar.
* Default is <code>true</code>.
*/
public boolean removeStyles = true;
/**
* Configure whether to remove <i>format</i> combo box from the toolbar.
* Default is <code>true</code>.
*/
public boolean removeFormat = true;
/**
* Configure if the toolbar should be collapsible. Default is
* <code>false</code>.
*/
public boolean toolbarCollapsible = false;
/**
* Configure if the toolbar should be initially expanded. Is only
* interpreted if {@link #toolbarCollapsible} is set to <code>true</code>.
* Default is <code>true</code>.
*/
public boolean toolbarInitialExpanded = true;
private Browser browser;
private Set<ToolbarButton> customButtons = new LinkedHashSet<>();
private Map<String, BrowserFunction> buttonCallbacks = new HashMap<>();
private Set<String> removedButtons = new HashSet<>();
/**
* Configures the toolbar of the CKEditor based on the configurations
* applied in this {@link ToolbarConfiguration}.
*/
public void configureToolbar() {
browser.evaluate(getToolbarGroupConfiguration() + getRemoveButtonConfiguration() + getCustomButtonConfiguration());
}
/**
*
* @return The toolbar group configuration for the CKEditor toolbar.
*/
protected String getToolbarGroupConfiguration() {
return "CKEDITOR.config.toolbarGroups = ["
+ "{ name: 'clipboard', groups: [ 'clipboard', 'undo', 'find' ] },"
+ "{ name: 'other' },"
+ "'/',"
+ "{ name: 'paragraph', groups: [ 'list', 'indent', 'align' ] },"
+ "{ name: 'colors' },"
+ "'/',"
+ "{ name: 'styles' },"
+ "{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }"
+ "] ;";
}
/**
*
* @return The configuration which default buttons should be removed from
* the toolbar.
*/
protected String getRemoveButtonConfiguration() {
// Subscript and Superscript are not supported styling options for the
// Rich Text Viewer
StringBuilder builder = new StringBuilder("CKEDITOR.config.removeButtons = 'Subscript,Superscript");
if (removePasteText) {
builder.append(",PasteText");
}
if (removePasteFromWord) {
builder.append(",PasteFromWord");
}
if (removeStyles) {
builder.append(",Styles");
}
if (removeFormat) {
builder.append(",Format");
}
for (String removed : this.removedButtons) {
builder.append(",").append(removed);
}
builder.append("';");
return builder.toString();
}
/**
*
* @return The configuration for adding custom commands and buttons to the
* toolbar.
*/
protected String getCustomButtonConfiguration() {
StringBuilder builder = new StringBuilder();
for (ToolbarButton button : this.customButtons) {
// add the command for the callback
builder.append("CKEDITOR.instances.editor.addCommand('").append(button.getCommandName()).append("', {");
builder.append("exec: function(edt) {");
if (button.getJavascriptToExecute() == null) {
builder.append("javaExecutionStarted();");
BrowserFunction function = this.buttonCallbacks.get(button.getCommandName());
builder.append(function.getName()).append("();");
builder.append("javaExecutionFinished()");
}
else {
builder.append(button.getJavascriptToExecute());
}
builder.append("}});");
// add the button
builder.append("CKEDITOR.instances.editor.ui.addButton('").append(button.getButtonName()).append("', {");
builder.append("label: '").append(button.getButtonLabel()).append("',");
builder.append("command: '").append(button.getCommandName()).append("',");
builder.append("toolbar: '").append(button.getToolbar()).append("',");
if (button.getIconURL() != null) {
builder.append("icon: '").append(button.getIconURL().toString()).append("',");
}
builder.append("});");
}
return builder.toString();
}
/**
* Adds a custom button to the CKEditor toolbar. Internally creates an
* anonymous {@link BrowserFunction} that executes
* {@link ToolbarButton#execute()} via callback on pressing the button.
*
* @param button
* The button to add.
*/
public void addToolbarButton(final ToolbarButton button) {
if (this.browser != null) {
// create the BrowserFunction for the callback
addToolbarButton(button, new BrowserFunction(browser, button.getCommandName()) {
@Override
public Object function(Object[] arguments) {
return button.execute();
}
});
} else if (!this.customButtons.contains(button)) {
this.customButtons.add(button);
}
}
/**
* Adds a custom button to the CKEditor toolbar. Executes the given
* {@link BrowserFunction} via callback on pressing the button.
*
* @param button
* The button to add.
* @param function
* The {@link BrowserFunction} that should be called on pressing
* the button.
*/
public void addToolbarButton(ToolbarButton button, BrowserFunction function) {
if (this.buttonCallbacks.containsKey(button.getCommandName())) {
// if there is already a BrowserFunction registered for the command
// name we dispose it for clean resource handling so we can register
// the new one
this.buttonCallbacks.get(button.getCommandName()).dispose();
}
this.buttonCallbacks.put(button.getCommandName(), function);
if (!this.customButtons.contains(button)) {
this.customButtons.add(button);
}
// ensure that the added button wasn't removed before
this.removedButtons.remove(button.getButtonName());
}
/**
* Removes the given {@link ToolbarButton} from the local list of custom
* toolbar buttons.
*
* @param button
* The {@link ToolbarButton} to remove.
*/
public void removeToolbarButton(ToolbarButton button) {
// remove from local lists so it is not added again on reload
this.customButtons.remove(button);
if (this.buttonCallbacks.containsKey(button.getCommandName())) {
this.buttonCallbacks.get(button.getCommandName()).dispose();
this.buttonCallbacks.remove(button.getCommandName());
}
// remember the button that should be removed
// I currently don't know a better way to do this
this.removedButtons.add(button.getButtonName());
}
/**
* Adds the CKEditor default button for the given name to the toolbar.
* <p>
* <i>Note: This works only for buttons that have been removed using
* {@link #removeDefaultToolbarButton(String)}</i>
* </p>
*
* @param buttonName
* The name of the CKEditor default button to add.
*/
public void addDefaultToolbarButton(String buttonName) {
this.removedButtons.remove(buttonName);
}
/**
* Removes the CKEditor default button for the given name from the toolbar.
*
* @param buttonName
* The name of the CKEditor default button to remove.
*/
public void removeDefaultToolbarButton(String buttonName) {
// remember the button that should be removed
this.removedButtons.add(buttonName);
}
/**
* Dispose the registered {@link BrowserFunction}s.
*/
public void dispose() {
// dispose the registered BrowserFunctions
for (BrowserFunction function : this.buttonCallbacks.values()) {
function.dispose();
}
}
/**
* @return The {@link Browser} instance to which this
* {@link ToolbarConfiguration} is connected to.
*/
public Browser getBrowser() {
return browser;
}
/**
*
* @param browser
* The {@link Browser} instance to which this
* {@link ToolbarConfiguration} should be connected to.
*/
public void setBrowser(Browser browser) {
this.browser = browser;
// if a browser is set we ensure that the registered custom buttons
// are registered and already registered BrowserFunctions are disposed
for (ToolbarButton button : this.customButtons) {
addToolbarButton(button);
}
}
public Set<ToolbarButton> getCustomButtons() {
return Collections.unmodifiableSet(customButtons);
}
public Map<String, BrowserFunction> getButtonCallbacks() {
return Collections.unmodifiableMap(buttonCallbacks);
}
public Set<String> getRemovedButtons() {
return Collections.unmodifiableSet(removedButtons);
}
public String[] getToolbarButtonConfigurations() {
return new String[]{
getToolbarGroupConfiguration(),
getRemoveButtonConfiguration()
};
}
}