diff options
author | Artur Signell <artur@vaadin.com> | 2016-08-18 22:54:48 +0300 |
---|---|---|
committer | Artur Signell <artur@vaadin.com> | 2016-08-20 00:22:06 +0300 |
commit | f23880749d9c2dca3ef4fd31b21fcc94b5220b42 (patch) | |
tree | 51143b1f4a379017419e7a9f81a59e553eb4e06a /compatibility-client/src | |
parent | be6a0cfd847593985b6f226646c576df1906695b (diff) | |
download | vaadin-framework-f23880749d9c2dca3ef4fd31b21fcc94b5220b42.tar.gz vaadin-framework-f23880749d9c2dca3ef4fd31b21fcc94b5220b42.zip |
Move RichTextArea to compatibility package
Change-Id: Ie73adbb0ddaf98aed6554f658625f1d812c3342b
Diffstat (limited to 'compatibility-client/src')
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> </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()); + } + } +} |