|  | // 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(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(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(String str, 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(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(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(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(KeyPressEvent event) { | 
|  | if (event.isControlKeyDown() || event.isMetaKeyDown()) { | 
|  | switch (event.getCharCode()) { | 
|  | case 'c': | 
|  | case 'x': | 
|  | textBox.addKeyUpHandler( | 
|  | new KeyUpHandler() { | 
|  | @Override | 
|  | public void onKeyUp(KeyUpEvent event) { | 
|  | Scheduler.get() | 
|  | .scheduleDeferred( | 
|  | new Command() { | 
|  | @Override | 
|  | public void execute() { | 
|  | hideTextBox(); | 
|  | } | 
|  | }); | 
|  | } | 
|  | }); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | }); | 
|  | textBox.addBlurHandler( | 
|  | new BlurHandler() { | 
|  | @Override | 
|  | public void onBlur(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) }-*/; | 
|  | } |