| // Copyright (C) 2009 The Android Open Source Project |
| // |
| // 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.google.gwtexpui.clippy.client; |
| |
| import com.google.gwt.core.client.Scheduler; |
| import com.google.gwt.dom.client.Element; |
| import com.google.gwt.dom.client.Style.Display; |
| import com.google.gwt.event.dom.client.BlurEvent; |
| import com.google.gwt.event.dom.client.BlurHandler; |
| import com.google.gwt.event.dom.client.ClickEvent; |
| import com.google.gwt.event.dom.client.ClickHandler; |
| import com.google.gwt.event.dom.client.KeyPressEvent; |
| import com.google.gwt.event.dom.client.KeyPressHandler; |
| import com.google.gwt.event.dom.client.KeyUpEvent; |
| import com.google.gwt.event.dom.client.KeyUpHandler; |
| import com.google.gwt.event.dom.client.MouseOutEvent; |
| import com.google.gwt.event.dom.client.MouseOutHandler; |
| import com.google.gwt.http.client.URL; |
| import com.google.gwt.user.client.Command; |
| import com.google.gwt.user.client.DOM; |
| import com.google.gwt.user.client.ui.Button; |
| import com.google.gwt.user.client.ui.Composite; |
| import com.google.gwt.user.client.ui.FlowPanel; |
| import com.google.gwt.user.client.ui.HasText; |
| import com.google.gwt.user.client.ui.InlineLabel; |
| import com.google.gwt.user.client.ui.Label; |
| import com.google.gwt.user.client.ui.TextBox; |
| import com.google.gwtexpui.safehtml.client.SafeHtml; |
| import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder; |
| import com.google.gwtexpui.user.client.Tooltip; |
| import com.google.gwtexpui.user.client.UserAgent; |
| |
| /** |
| * Label which permits the user to easily copy the complete content. |
| * |
| * <p>If the Flash plugin is available a "movie" is embedded that provides one-click copying of the |
| * content onto the system clipboard. The label (if visible) can also be clicked, switching from a |
| * label to an input box, allowing the user to copy the text with a keyboard shortcut. |
| */ |
| public class CopyableLabel extends Composite implements HasText { |
| private static final int SWF_WIDTH = 110; |
| private static final int SWF_HEIGHT = 14; |
| private static boolean flashEnabled = true; |
| |
| static { |
| ClippyResources.I.css().ensureInjected(); |
| } |
| |
| public static boolean isFlashEnabled() { |
| return flashEnabled; |
| } |
| |
| public static void setFlashEnabled(final boolean on) { |
| flashEnabled = on; |
| } |
| |
| private static String swfUrl() { |
| return ClippyResources.I.swf().getSafeUri().asString(); |
| } |
| |
| private final FlowPanel content; |
| private String text; |
| private int visibleLen; |
| private Label textLabel; |
| private TextBox textBox; |
| private Button copier; |
| private Element swf; |
| |
| public CopyableLabel() { |
| this(""); |
| } |
| |
| /** |
| * Create a new label |
| * |
| * @param str initial content |
| */ |
| public CopyableLabel(final String str) { |
| this(str, true); |
| } |
| |
| /** |
| * Create a new label |
| * |
| * @param str initial content |
| * @param showLabel if true, the content is shown, if false it is hidden from view and only the |
| * copy icon is displayed. |
| */ |
| public CopyableLabel(final String str, final boolean showLabel) { |
| content = new FlowPanel(); |
| initWidget(content); |
| |
| text = str; |
| visibleLen = text.length(); |
| |
| if (showLabel) { |
| textLabel = new InlineLabel(getText()); |
| textLabel.setStyleName(ClippyResources.I.css().label()); |
| textLabel.addClickHandler( |
| new ClickHandler() { |
| @Override |
| public void onClick(final ClickEvent event) { |
| showTextBox(); |
| } |
| }); |
| content.add(textLabel); |
| } |
| |
| if (UserAgent.hasJavaScriptClipboard()) { |
| copier = |
| new Button( |
| new SafeHtmlBuilder() |
| .openElement("img") |
| .setAttribute("src", ClippyResources.I.clipboard().getSafeUri().asString()) |
| .setWidth(14) |
| .setHeight(14) |
| .closeSelf()); |
| copier.setStyleName(ClippyResources.I.css().copier()); |
| Tooltip.addStyle(copier); |
| Tooltip.setLabel(copier, CopyableLabelText.I.tooltip()); |
| copier.addClickHandler( |
| new ClickHandler() { |
| @Override |
| public void onClick(ClickEvent event) { |
| copy(); |
| } |
| }); |
| copier.addMouseOutHandler( |
| new MouseOutHandler() { |
| @Override |
| public void onMouseOut(MouseOutEvent event) { |
| Tooltip.setLabel(copier, CopyableLabelText.I.tooltip()); |
| } |
| }); |
| |
| FlowPanel p = new FlowPanel(); |
| p.getElement().getStyle().setDisplay(Display.INLINE_BLOCK); |
| p.add(copier); |
| content.add(p); |
| } else { |
| embedMovie(); |
| } |
| } |
| |
| /** |
| * Change the text which is displayed in the clickable label. |
| * |
| * @param text the new preview text, should be shorter than the original text which would be |
| * copied to the clipboard. |
| */ |
| public void setPreviewText(final String text) { |
| if (textLabel != null) { |
| textLabel.setText(text); |
| } |
| } |
| |
| private void embedMovie() { |
| if (copier == null && flashEnabled && !text.isEmpty() && UserAgent.Flash.isInstalled()) { |
| final String flashVars = "text=" + URL.encodeQueryString(getText()); |
| final SafeHtmlBuilder h = new SafeHtmlBuilder(); |
| |
| h.openElement("div"); |
| h.setStyleName(ClippyResources.I.css().swf()); |
| |
| h.openElement("object"); |
| h.setWidth(SWF_WIDTH); |
| h.setHeight(SWF_HEIGHT); |
| h.setAttribute("classid", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"); |
| h.paramElement("movie", swfUrl()); |
| h.paramElement("FlashVars", flashVars); |
| |
| h.openElement("embed"); |
| h.setWidth(SWF_WIDTH); |
| h.setHeight(SWF_HEIGHT); |
| h.setAttribute("wmode", "transparent"); |
| h.setAttribute("type", "application/x-shockwave-flash"); |
| h.setAttribute("src", swfUrl()); |
| h.setAttribute("FlashVars", flashVars); |
| h.closeSelf(); |
| |
| h.closeElement("object"); |
| h.closeElement("div"); |
| |
| if (swf != null) { |
| getElement().removeChild(swf); |
| } |
| DOM.appendChild(getElement(), swf = SafeHtml.parse(h)); |
| } |
| } |
| |
| @Override |
| public String getText() { |
| return text; |
| } |
| |
| @Override |
| public void setText(final String newText) { |
| text = newText; |
| visibleLen = newText.length(); |
| |
| if (textLabel != null) { |
| textLabel.setText(getText()); |
| } |
| if (textBox != null) { |
| textBox.setText(getText()); |
| textBox.selectAll(); |
| } |
| embedMovie(); |
| } |
| |
| private void showTextBox() { |
| if (textBox == null) { |
| textBox = new TextBox(); |
| textBox.setText(getText()); |
| textBox.setVisibleLength(visibleLen); |
| textBox.setReadOnly(true); |
| textBox.addKeyPressHandler( |
| new KeyPressHandler() { |
| @Override |
| public void onKeyPress(final KeyPressEvent event) { |
| if (event.isControlKeyDown() || event.isMetaKeyDown()) { |
| switch (event.getCharCode()) { |
| case 'c': |
| case 'x': |
| textBox.addKeyUpHandler( |
| new KeyUpHandler() { |
| @Override |
| public void onKeyUp(final KeyUpEvent event) { |
| Scheduler.get() |
| .scheduleDeferred( |
| new Command() { |
| @Override |
| public void execute() { |
| hideTextBox(); |
| } |
| }); |
| } |
| }); |
| break; |
| } |
| } |
| } |
| }); |
| textBox.addBlurHandler( |
| new BlurHandler() { |
| @Override |
| public void onBlur(final BlurEvent event) { |
| hideTextBox(); |
| } |
| }); |
| content.insert(textBox, 1); |
| } |
| |
| textLabel.setVisible(false); |
| textBox.setVisible(true); |
| Scheduler.get() |
| .scheduleDeferred( |
| new Command() { |
| @Override |
| public void execute() { |
| textBox.selectAll(); |
| textBox.setFocus(true); |
| } |
| }); |
| } |
| |
| private void hideTextBox() { |
| if (textBox != null) { |
| textBox.removeFromParent(); |
| textBox = null; |
| } |
| textLabel.setVisible(true); |
| } |
| |
| private void copy() { |
| TextBox t = new TextBox(); |
| try { |
| t.setText(getText()); |
| content.add(t); |
| t.setFocus(true); |
| t.selectAll(); |
| |
| boolean ok = execCommand("copy"); |
| Tooltip.setLabel(copier, ok ? CopyableLabelText.I.copied() : CopyableLabelText.I.failed()); |
| if (!ok) { |
| // Disable JavaScript clipboard and try flash movie in another instance. |
| UserAgent.disableJavaScriptClipboard(); |
| } |
| } finally { |
| t.removeFromParent(); |
| } |
| } |
| |
| private static boolean execCommand(String command) { |
| try { |
| return nativeExec(command); |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| |
| private static native boolean nativeExec(String c) /*-{ return !! $doc.execCommand(c) }-*/; |
| } |