summaryrefslogtreecommitdiffstats
path: root/compatibility-client/src
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2016-08-18 22:54:48 +0300
committerArtur Signell <artur@vaadin.com>2016-08-20 00:22:06 +0300
commitf23880749d9c2dca3ef4fd31b21fcc94b5220b42 (patch)
tree51143b1f4a379017419e7a9f81a59e553eb4e06a /compatibility-client/src
parentbe6a0cfd847593985b6f226646c576df1906695b (diff)
downloadvaadin-framework-f23880749d9c2dca3ef4fd31b21fcc94b5220b42.tar.gz
vaadin-framework-f23880749d9c2dca3ef4fd31b21fcc94b5220b42.zip
Move RichTextArea to compatibility package
Change-Id: Ie73adbb0ddaf98aed6554f658625f1d812c3342b
Diffstat (limited to 'compatibility-client/src')
-rw-r--r--compatibility-client/src/main/java/com/vaadin/client/ui/VRichTextArea.java362
-rw-r--r--compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java137
-rw-r--r--compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java476
3 files changed, 975 insertions, 0 deletions
diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/VRichTextArea.java b/compatibility-client/src/main/java/com/vaadin/client/ui/VRichTextArea.java
new file mode 100644
index 0000000000..6814543f50
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/client/ui/VRichTextArea.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.
+ */
+
+package com.vaadin.client.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Focusable;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.RichTextArea;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ConnectorMap;
+import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
+import com.vaadin.client.ui.richtextarea.VRichTextToolbar;
+
+/**
+ * This class implements a basic client side rich text editor component.
+ *
+ * @author Vaadin Ltd.
+ *
+ */
+public class VRichTextArea extends Composite
+ implements Field, KeyPressHandler, KeyDownHandler, Focusable {
+
+ /**
+ * The input node CSS classname.
+ */
+ public static final String CLASSNAME = "v-richtextarea";
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public String id;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public ApplicationConnection client;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public boolean immediate = false;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public RichTextArea rta;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public VRichTextToolbar formatter;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public HTML html = new HTML();
+
+ private final FlowPanel fp = new FlowPanel();
+
+ private boolean enabled = true;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public int maxLength = -1;
+
+ private int toolbarNaturalWidth = 500;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public HandlerRegistration keyPressHandler;
+
+ private ShortcutActionHandlerOwner hasShortcutActionHandler;
+
+ private boolean readOnly = false;
+
+ private final Map<BlurHandler, HandlerRegistration> blurHandlers = new HashMap<BlurHandler, HandlerRegistration>();
+
+ public VRichTextArea() {
+ createRTAComponents();
+ fp.add(formatter);
+ fp.add(rta);
+
+ initWidget(fp);
+ setStyleName(CLASSNAME);
+
+ TouchScrollDelegate.enableTouchScrolling(html, html.getElement());
+ }
+
+ private void createRTAComponents() {
+ rta = new RichTextArea();
+ rta.setWidth("100%");
+ rta.addKeyDownHandler(this);
+ formatter = new VRichTextToolbar(rta);
+
+ // Add blur handlers
+ for (Entry<BlurHandler, HandlerRegistration> handler : blurHandlers
+ .entrySet()) {
+
+ // Remove old registration
+ handler.getValue().removeHandler();
+
+ // Add blur handlers
+ addBlurHandler(handler.getKey());
+ }
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (this.enabled != enabled) {
+ // rta.setEnabled(enabled);
+ swapEditableArea();
+ this.enabled = enabled;
+ }
+ }
+
+ /**
+ * Swaps html to rta and visa versa.
+ */
+ private void swapEditableArea() {
+ String value = getValue();
+ if (html.isAttached()) {
+ fp.remove(html);
+ if (BrowserInfo.get().isWebkit()) {
+ fp.remove(formatter);
+ createRTAComponents(); // recreate new RTA to bypass #5379
+ fp.add(formatter);
+ }
+ fp.add(rta);
+ } else {
+ fp.remove(rta);
+ fp.add(html);
+ }
+ setValue(value);
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public void selectAll() {
+ /*
+ * There is a timing issue if trying to select all immediately on first
+ * render. Simple deferred command is not enough. Using Timer with
+ * moderated timeout. If this appears to fail on many (most likely slow)
+ * environments, consider increasing the timeout.
+ *
+ * FF seems to require the most time to stabilize its RTA. On Vaadin
+ * tiergarden test machines, 200ms was not enough always (about 50%
+ * success rate) - 300 ms was 100% successful. This however was not
+ * enough on a sluggish old non-virtualized XP test machine. A bullet
+ * proof solution would be nice, GWT 2.1 might however solve these. At
+ * least setFocus has a workaround for this kind of issue.
+ */
+ new Timer() {
+ @Override
+ public void run() {
+ rta.getFormatter().selectAll();
+ }
+ }.schedule(320);
+ }
+
+ public void setReadOnly(boolean b) {
+ if (isReadOnly() != b) {
+ swapEditableArea();
+ readOnly = b;
+ }
+ // reset visibility in case enabled state changed and the formatter was
+ // recreated
+ formatter.setVisible(!readOnly);
+ }
+
+ private boolean isReadOnly() {
+ return readOnly;
+ }
+
+ @Override
+ public void setHeight(String height) {
+ super.setHeight(height);
+ if (height == null || height.equals("")) {
+ rta.setHeight("");
+ }
+ }
+
+ @Override
+ public void setWidth(String width) {
+ if (width.equals("")) {
+ /*
+ * IE cannot calculate the width of the 100% iframe correctly if
+ * there is no width specified for the parent. In this case we would
+ * use the toolbar but IE cannot calculate the width of that one
+ * correctly either in all cases. So we end up using a default width
+ * for a RichTextArea with no width definition in all browsers (for
+ * compatibility).
+ */
+
+ super.setWidth(toolbarNaturalWidth + "px");
+ } else {
+ super.setWidth(width);
+ }
+ }
+
+ @Override
+ public void onKeyPress(KeyPressEvent event) {
+ if (maxLength >= 0) {
+ Scheduler.get().scheduleDeferred(new Command() {
+ @Override
+ public void execute() {
+ if (rta.getHTML().length() > maxLength) {
+ rta.setHTML(rta.getHTML().substring(0, maxLength));
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onKeyDown(KeyDownEvent event) {
+ // delegate to closest shortcut action handler
+ // throw event from the iframe forward to the shortcuthandler
+ ShortcutActionHandler shortcutHandler = getShortcutHandlerOwner()
+ .getShortcutActionHandler();
+ if (shortcutHandler != null) {
+ shortcutHandler.handleKeyboardEvent(
+ com.google.gwt.user.client.Event.as(event.getNativeEvent()),
+ ConnectorMap.get(client).getConnector(this));
+ }
+ }
+
+ private ShortcutActionHandlerOwner getShortcutHandlerOwner() {
+ if (hasShortcutActionHandler == null) {
+ Widget parent = getParent();
+ while (parent != null) {
+ if (parent instanceof ShortcutActionHandlerOwner) {
+ break;
+ }
+ parent = parent.getParent();
+ }
+ hasShortcutActionHandler = (ShortcutActionHandlerOwner) parent;
+ }
+ return hasShortcutActionHandler;
+ }
+
+ @Override
+ public int getTabIndex() {
+ return rta.getTabIndex();
+ }
+
+ @Override
+ public void setAccessKey(char key) {
+ rta.setAccessKey(key);
+ }
+
+ @Override
+ public void setFocus(boolean focused) {
+ /*
+ * Similar issue as with selectAll. Focusing must happen before possible
+ * selectall, so keep the timeout here lower.
+ */
+ new Timer() {
+
+ @Override
+ public void run() {
+ rta.setFocus(true);
+ }
+ }.schedule(300);
+ }
+
+ @Override
+ public void setTabIndex(int index) {
+ rta.setTabIndex(index);
+ }
+
+ /**
+ * Set the value of the text area
+ *
+ * @param value
+ * The text value. Can be html.
+ */
+ public void setValue(String value) {
+ if (rta.isAttached()) {
+ rta.setHTML(value);
+ } else {
+ html.setHTML(value);
+ }
+ }
+
+ /**
+ * Get the value the text area
+ */
+ public String getValue() {
+ if (rta.isAttached()) {
+ return rta.getHTML();
+ } else {
+ return html.getHTML();
+ }
+ }
+
+ /**
+ * Browsers differ in what they return as the content of a visually empty
+ * rich text area. This method is used to normalize these to an empty
+ * string. See #8004.
+ *
+ * @return cleaned html string
+ */
+ public String getSanitizedValue() {
+ BrowserInfo browser = BrowserInfo.get();
+ String result = getValue();
+ if (browser.isFirefox()) {
+ if ("<br>".equals(result)) {
+ result = "";
+ }
+ } else if (browser.isWebkit() || browser.isEdge()) {
+ if ("<br>".equals(result) || "<div><br></div>".equals(result)) {
+ result = "";
+ }
+ } else if (browser.isIE()) {
+ if ("<P>&nbsp;</P>".equals(result)) {
+ result = "";
+ }
+ } else if (browser.isOpera()) {
+ if ("<br>".equals(result) || "<p><br></p>".equals(result)) {
+ result = "";
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Adds a blur handler to the component.
+ *
+ * @param blurHandler
+ * the blur handler to add
+ */
+ public void addBlurHandler(BlurHandler blurHandler) {
+ blurHandlers.put(blurHandler, rta.addBlurHandler(blurHandler));
+ }
+
+ /**
+ * Removes a blur handler.
+ *
+ * @param blurHandler
+ * the handler to remove
+ */
+ public void removeBlurHandler(BlurHandler blurHandler) {
+ HandlerRegistration registration = blurHandlers.remove(blurHandler);
+ if (registration != null) {
+ registration.removeHandler();
+ }
+ }
+}
diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
new file mode 100644
index 0000000000..a8aeb79d07
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.
+ */
+package com.vaadin.client.ui.richtextarea;
+
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.Paintable;
+import com.vaadin.client.UIDL;
+import com.vaadin.client.ui.AbstractFieldConnector;
+import com.vaadin.client.ui.SimpleManagedLayout;
+import com.vaadin.client.ui.VRichTextArea;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.Connect.LoadStyle;
+import com.vaadin.shared.ui.textarea.RichTextAreaState;
+import com.vaadin.shared.util.SharedUtil;
+import com.vaadin.ui.RichTextArea;
+
+@Connect(value = RichTextArea.class, loadStyle = LoadStyle.LAZY)
+public class RichTextAreaConnector extends AbstractFieldConnector
+ implements Paintable, SimpleManagedLayout {
+
+ /*
+ * Last value received from the server
+ */
+ private String cachedValue = "";
+
+ @Override
+ protected void init() {
+ getWidget().addBlurHandler(new BlurHandler() {
+
+ @Override
+ public void onBlur(BlurEvent event) {
+ flush();
+ }
+ });
+ getLayoutManager().registerDependency(this,
+ getWidget().formatter.getElement());
+ }
+
+ @Override
+ public void onUnregister() {
+ super.onUnregister();
+ getLayoutManager().unregisterDependency(this,
+ getWidget().formatter.getElement());
+ }
+
+ @Override
+ public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) {
+ getWidget().client = client;
+ getWidget().id = uidl.getId();
+
+ if (uidl.hasVariable("text")) {
+ String newValue = uidl.getStringVariable("text");
+ if (!SharedUtil.equals(newValue, cachedValue)) {
+ getWidget().setValue(newValue);
+ cachedValue = newValue;
+ }
+ }
+
+ if (!isRealUpdate(uidl)) {
+ return;
+ }
+
+ getWidget().setEnabled(isEnabled());
+ getWidget().setReadOnly(isReadOnly());
+ getWidget().immediate = getState().immediate;
+ int newMaxLength = uidl.hasAttribute("maxLength")
+ ? uidl.getIntAttribute("maxLength") : -1;
+ if (newMaxLength >= 0) {
+ if (getWidget().maxLength == -1) {
+ getWidget().keyPressHandler = getWidget().rta
+ .addKeyPressHandler(getWidget());
+ }
+ getWidget().maxLength = newMaxLength;
+ } else if (getWidget().maxLength != -1) {
+ getWidget().getElement().setAttribute("maxlength", "");
+ getWidget().maxLength = -1;
+ getWidget().keyPressHandler.removeHandler();
+ }
+
+ if (uidl.hasAttribute("selectAll")) {
+ getWidget().selectAll();
+ }
+
+ }
+
+ @Override
+ public VRichTextArea getWidget() {
+ return (VRichTextArea) super.getWidget();
+ }
+
+ @Override
+ public void flush() {
+ if (getConnection() != null && getConnectorId() != null) {
+ final String html = getWidget().getSanitizedValue();
+ if (!html.equals(cachedValue)) {
+ cachedValue = html;
+ getConnection().updateVariable(getConnectorId(), "text", html,
+ getState().immediate);
+ }
+ }
+ }
+
+ @Override
+ public void layout() {
+ if (!isUndefinedHeight()) {
+ int rootElementInnerHeight = getLayoutManager()
+ .getInnerHeight(getWidget().getElement());
+ int formatterHeight = getLayoutManager()
+ .getOuterHeight(getWidget().formatter.getElement());
+ int editorHeight = rootElementInnerHeight - formatterHeight;
+ if (editorHeight < 0) {
+ editorHeight = 0;
+ }
+ getWidget().rta.setHeight(editorHeight + "px");
+ }
+ }
+
+ @Override
+ public RichTextAreaState getState() {
+ return (RichTextAreaState) super.getState();
+ }
+}
diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java b/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java
new file mode 100644
index 0000000000..10c1de75e6
--- /dev/null
+++ b/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/VRichTextToolbar.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.
+ */
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * 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.
+ */
+package com.vaadin.client.ui.richtextarea;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.i18n.client.Constants;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.PushButton;
+import com.google.gwt.user.client.ui.RichTextArea;
+import com.google.gwt.user.client.ui.ToggleButton;
+
+/**
+ * A modified version of sample toolbar for use with {@link RichTextArea}. It
+ * provides a simple UI for all rich text formatting, dynamically displayed only
+ * for the available functionality.
+ */
+public class VRichTextToolbar extends Composite {
+
+ /**
+ * This {@link ClientBundle} is used for all the button icons. Using a
+ * bundle allows all of these images to be packed into a single image, which
+ * saves a lot of HTTP requests, drastically improving startup time.
+ */
+ public interface Images extends ClientBundle {
+
+ ImageResource bold();
+
+ ImageResource createLink();
+
+ ImageResource hr();
+
+ ImageResource indent();
+
+ ImageResource insertImage();
+
+ ImageResource italic();
+
+ ImageResource justifyCenter();
+
+ ImageResource justifyLeft();
+
+ ImageResource justifyRight();
+
+ ImageResource ol();
+
+ ImageResource outdent();
+
+ ImageResource removeFormat();
+
+ ImageResource removeLink();
+
+ ImageResource strikeThrough();
+
+ ImageResource subscript();
+
+ ImageResource superscript();
+
+ ImageResource ul();
+
+ ImageResource underline();
+ }
+
+ /**
+ * This {@link Constants} interface is used to make the toolbar's strings
+ * internationalizable.
+ */
+ public interface Strings extends Constants {
+
+ String black();
+
+ String blue();
+
+ String bold();
+
+ String color();
+
+ String createLink();
+
+ String font();
+
+ String green();
+
+ String hr();
+
+ String indent();
+
+ String insertImage();
+
+ String italic();
+
+ String justifyCenter();
+
+ String justifyLeft();
+
+ String justifyRight();
+
+ String large();
+
+ String medium();
+
+ String normal();
+
+ String ol();
+
+ String outdent();
+
+ String red();
+
+ String removeFormat();
+
+ String removeLink();
+
+ String size();
+
+ String small();
+
+ String strikeThrough();
+
+ String subscript();
+
+ String superscript();
+
+ String ul();
+
+ String underline();
+
+ String white();
+
+ String xlarge();
+
+ String xsmall();
+
+ String xxlarge();
+
+ String xxsmall();
+
+ String yellow();
+ }
+
+ /**
+ * We use an inner EventHandler class to avoid exposing event methods on the
+ * RichTextToolbar itself.
+ */
+ private class EventHandler
+ implements ClickHandler, ChangeHandler, KeyUpHandler {
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void onChange(ChangeEvent event) {
+ Object sender = event.getSource();
+ if (sender == backColors) {
+ basic.setBackColor(
+ backColors.getValue(backColors.getSelectedIndex()));
+ backColors.setSelectedIndex(0);
+ } else if (sender == foreColors) {
+ basic.setForeColor(
+ foreColors.getValue(foreColors.getSelectedIndex()));
+ foreColors.setSelectedIndex(0);
+ } else if (sender == fonts) {
+ basic.setFontName(fonts.getValue(fonts.getSelectedIndex()));
+ fonts.setSelectedIndex(0);
+ } else if (sender == fontSizes) {
+ basic.setFontSize(
+ fontSizesConstants[fontSizes.getSelectedIndex() - 1]);
+ fontSizes.setSelectedIndex(0);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void onClick(ClickEvent event) {
+ Object sender = event.getSource();
+ if (sender == bold) {
+ basic.toggleBold();
+ } else if (sender == italic) {
+ basic.toggleItalic();
+ } else if (sender == underline) {
+ basic.toggleUnderline();
+ } else if (sender == subscript) {
+ basic.toggleSubscript();
+ } else if (sender == superscript) {
+ basic.toggleSuperscript();
+ } else if (sender == strikethrough) {
+ extended.toggleStrikethrough();
+ } else if (sender == indent) {
+ extended.rightIndent();
+ } else if (sender == outdent) {
+ extended.leftIndent();
+ } else if (sender == justifyLeft) {
+ basic.setJustification(RichTextArea.Justification.LEFT);
+ } else if (sender == justifyCenter) {
+ basic.setJustification(RichTextArea.Justification.CENTER);
+ } else if (sender == justifyRight) {
+ basic.setJustification(RichTextArea.Justification.RIGHT);
+ } else if (sender == insertImage) {
+ final String url = Window.prompt("Enter an image URL:",
+ "http://");
+ if (url != null) {
+ extended.insertImage(url);
+ }
+ } else if (sender == createLink) {
+ final String url = Window.prompt("Enter a link URL:",
+ "http://");
+ if (url != null) {
+ extended.createLink(url);
+ }
+ } else if (sender == removeLink) {
+ extended.removeLink();
+ } else if (sender == hr) {
+ extended.insertHorizontalRule();
+ } else if (sender == ol) {
+ extended.insertOrderedList();
+ } else if (sender == ul) {
+ extended.insertUnorderedList();
+ } else if (sender == removeFormat) {
+ extended.removeFormat();
+ } else if (sender == richText) {
+ // We use the RichTextArea's onKeyUp event to update the toolbar
+ // status. This will catch any cases where the user moves the
+ // cursur using the keyboard, or uses one of the browser's
+ // built-in keyboard shortcuts.
+ updateStatus();
+ }
+ }
+
+ @Override
+ public void onKeyUp(KeyUpEvent event) {
+ if (event.getSource() == richText) {
+ // We use the RichTextArea's onKeyUp event to update the toolbar
+ // status. This will catch any cases where the user moves the
+ // cursor using the keyboard, or uses one of the browser's
+ // built-in keyboard shortcuts.
+ updateStatus();
+ }
+ }
+ }
+
+ private static final RichTextArea.FontSize[] fontSizesConstants = new RichTextArea.FontSize[] {
+ RichTextArea.FontSize.XX_SMALL, RichTextArea.FontSize.X_SMALL,
+ RichTextArea.FontSize.SMALL, RichTextArea.FontSize.MEDIUM,
+ RichTextArea.FontSize.LARGE, RichTextArea.FontSize.X_LARGE,
+ RichTextArea.FontSize.XX_LARGE };
+
+ private final Images images = (Images) GWT.create(Images.class);
+ private final Strings strings = (Strings) GWT.create(Strings.class);
+ private final EventHandler handler = new EventHandler();
+
+ private final RichTextArea richText;
+ @SuppressWarnings("deprecation")
+ private final RichTextArea.BasicFormatter basic;
+ @SuppressWarnings("deprecation")
+ private final RichTextArea.ExtendedFormatter extended;
+
+ private final FlowPanel outer = new FlowPanel();
+ private final FlowPanel topPanel = new FlowPanel();
+ private final FlowPanel bottomPanel = new FlowPanel();
+ private ToggleButton bold;
+ private ToggleButton italic;
+ private ToggleButton underline;
+ private ToggleButton subscript;
+ private ToggleButton superscript;
+ private ToggleButton strikethrough;
+ private PushButton indent;
+ private PushButton outdent;
+ private PushButton justifyLeft;
+ private PushButton justifyCenter;
+ private PushButton justifyRight;
+ private PushButton hr;
+ private PushButton ol;
+ private PushButton ul;
+ private PushButton insertImage;
+ private PushButton createLink;
+ private PushButton removeLink;
+ private PushButton removeFormat;
+
+ private ListBox backColors;
+ private ListBox foreColors;
+ private ListBox fonts;
+ private ListBox fontSizes;
+
+ /**
+ * Creates a new toolbar that drives the given rich text area.
+ *
+ * @param richText
+ * the rich text area to be controlled
+ */
+ @SuppressWarnings("deprecation")
+ public VRichTextToolbar(RichTextArea richText) {
+ this.richText = richText;
+ basic = richText.getBasicFormatter();
+ extended = richText.getExtendedFormatter();
+
+ outer.add(topPanel);
+ outer.add(bottomPanel);
+ topPanel.setStyleName("gwt-RichTextToolbar-top");
+ bottomPanel.setStyleName("gwt-RichTextToolbar-bottom");
+
+ initWidget(outer);
+ setStyleName("gwt-RichTextToolbar");
+
+ if (basic != null) {
+ topPanel.add(
+ bold = createToggleButton(images.bold(), strings.bold()));
+ topPanel.add(italic = createToggleButton(images.italic(),
+ strings.italic()));
+ topPanel.add(underline = createToggleButton(images.underline(),
+ strings.underline()));
+ topPanel.add(subscript = createToggleButton(images.subscript(),
+ strings.subscript()));
+ topPanel.add(superscript = createToggleButton(images.superscript(),
+ strings.superscript()));
+ topPanel.add(justifyLeft = createPushButton(images.justifyLeft(),
+ strings.justifyLeft()));
+ topPanel.add(justifyCenter = createPushButton(
+ images.justifyCenter(), strings.justifyCenter()));
+ topPanel.add(justifyRight = createPushButton(images.justifyRight(),
+ strings.justifyRight()));
+ }
+
+ if (extended != null) {
+ topPanel.add(strikethrough = createToggleButton(
+ images.strikeThrough(), strings.strikeThrough()));
+ topPanel.add(indent = createPushButton(images.indent(),
+ strings.indent()));
+ topPanel.add(outdent = createPushButton(images.outdent(),
+ strings.outdent()));
+ topPanel.add(hr = createPushButton(images.hr(), strings.hr()));
+ topPanel.add(ol = createPushButton(images.ol(), strings.ol()));
+ topPanel.add(ul = createPushButton(images.ul(), strings.ul()));
+ topPanel.add(insertImage = createPushButton(images.insertImage(),
+ strings.insertImage()));
+ topPanel.add(createLink = createPushButton(images.createLink(),
+ strings.createLink()));
+ topPanel.add(removeLink = createPushButton(images.removeLink(),
+ strings.removeLink()));
+ topPanel.add(removeFormat = createPushButton(images.removeFormat(),
+ strings.removeFormat()));
+ }
+
+ if (basic != null) {
+ bottomPanel.add(backColors = createColorList("Background"));
+ bottomPanel.add(foreColors = createColorList("Foreground"));
+ bottomPanel.add(fonts = createFontList());
+ bottomPanel.add(fontSizes = createFontSizes());
+
+ // We only use these handlers for updating status, so don't hook
+ // them up unless at least basic editing is supported.
+ richText.addKeyUpHandler(handler);
+ richText.addClickHandler(handler);
+ }
+ }
+
+ private ListBox createColorList(String caption) {
+ final ListBox lb = new ListBox();
+ lb.addChangeHandler(handler);
+ lb.setVisibleItemCount(1);
+
+ lb.addItem(caption);
+ lb.addItem(strings.white(), "white");
+ lb.addItem(strings.black(), "black");
+ lb.addItem(strings.red(), "red");
+ lb.addItem(strings.green(), "green");
+ lb.addItem(strings.yellow(), "yellow");
+ lb.addItem(strings.blue(), "blue");
+ lb.setTabIndex(-1);
+ return lb;
+ }
+
+ private ListBox createFontList() {
+ final ListBox lb = new ListBox();
+ lb.addChangeHandler(handler);
+ lb.setVisibleItemCount(1);
+
+ lb.addItem(strings.font(), "");
+ lb.addItem(strings.normal(), "inherit");
+ lb.addItem("Times New Roman", "Times New Roman");
+ lb.addItem("Arial", "Arial");
+ lb.addItem("Courier New", "Courier New");
+ lb.addItem("Georgia", "Georgia");
+ lb.addItem("Trebuchet", "Trebuchet");
+ lb.addItem("Verdana", "Verdana");
+ lb.setTabIndex(-1);
+ return lb;
+ }
+
+ private ListBox createFontSizes() {
+ final ListBox lb = new ListBox();
+ lb.addChangeHandler(handler);
+ lb.setVisibleItemCount(1);
+
+ lb.addItem(strings.size());
+ lb.addItem(strings.xxsmall());
+ lb.addItem(strings.xsmall());
+ lb.addItem(strings.small());
+ lb.addItem(strings.medium());
+ lb.addItem(strings.large());
+ lb.addItem(strings.xlarge());
+ lb.addItem(strings.xxlarge());
+ lb.setTabIndex(-1);
+ return lb;
+ }
+
+ private PushButton createPushButton(ImageResource img, String tip) {
+ final PushButton pb = new PushButton(new Image(img));
+ pb.addClickHandler(handler);
+ pb.setTitle(tip);
+ pb.setTabIndex(-1);
+ return pb;
+ }
+
+ private ToggleButton createToggleButton(ImageResource img, String tip) {
+ final ToggleButton tb = new ToggleButton(new Image(img));
+ tb.addClickHandler(handler);
+ tb.setTitle(tip);
+ tb.setTabIndex(-1);
+ return tb;
+ }
+
+ /**
+ * Updates the status of all the stateful buttons.
+ */
+ @SuppressWarnings("deprecation")
+ private void updateStatus() {
+ if (basic != null) {
+ bold.setDown(basic.isBold());
+ italic.setDown(basic.isItalic());
+ underline.setDown(basic.isUnderlined());
+ subscript.setDown(basic.isSubscript());
+ superscript.setDown(basic.isSuperscript());
+ }
+
+ if (extended != null) {
+ strikethrough.setDown(extended.isStrikethrough());
+ }
+ }
+}