1 /* 2 * CopyPlotToClipboardDesktopDialog.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 package org.rstudio.studio.client.workbench.exportplot.clipboard; 16 17 import org.rstudio.core.client.BrowseCap; 18 import org.rstudio.core.client.dom.ElementEx; 19 import org.rstudio.core.client.dom.WindowEx; 20 import org.rstudio.core.client.widget.Operation; 21 import org.rstudio.core.client.widget.OperationWithInput; 22 import org.rstudio.studio.client.application.Desktop; 23 import org.rstudio.studio.client.application.DesktopFrame; 24 import org.rstudio.studio.client.common.Timers; 25 import org.rstudio.studio.client.workbench.exportplot.ExportPlotPreviewer; 26 import org.rstudio.studio.client.workbench.exportplot.ExportPlotSizeEditor; 27 import org.rstudio.studio.client.workbench.exportplot.model.ExportPlotOptions; 28 29 import com.google.gwt.dom.client.Document; 30 import com.google.gwt.dom.client.Element; 31 import com.google.gwt.dom.client.NodeList; 32 import com.google.gwt.user.client.Command; 33 34 public class CopyPlotToClipboardDesktopDialog 35 extends CopyPlotToClipboardDesktopDialogBase 36 { 37 CopyPlotToClipboardDesktopDialog( ExportPlotPreviewer previewer, ExportPlotClipboard clipboard, ExportPlotOptions options, OperationWithInput<ExportPlotOptions> onClose)38 public CopyPlotToClipboardDesktopDialog( 39 ExportPlotPreviewer previewer, 40 ExportPlotClipboard clipboard, 41 ExportPlotOptions options, 42 OperationWithInput<ExportPlotOptions> onClose) 43 { 44 super(options, previewer, onClose); 45 46 clipboard_ = clipboard; 47 } 48 copyAsBitmap(final Operation onCompleted)49 protected void copyAsBitmap(final Operation onCompleted) 50 { 51 final ExportPlotSizeEditor sizeEditor = getSizeEditor(); 52 sizeEditor.setGripperVisible(false); 53 54 // NOTE: we use a timer here just to be absolutely sure the 55 // browser has re-rendered and hidden the gripper before attempting 56 // to get a screenshot. note that the usual tools, e.g. scheduleDeferred(), 57 // don't work as expected here 58 Timers.singleShot(200, () -> { doCopyAsBitmap(onCompleted); }); 59 } 60 doCopyAsBitmap(final Operation onCompleted)61 private void doCopyAsBitmap(final Operation onCompleted) 62 { 63 final ExportPlotSizeEditor sizeEditor = getSizeEditor(); 64 65 final Command completed = new Command() 66 { 67 @Override 68 public void execute() 69 { 70 sizeEditor.setGripperVisible(true); 71 onCompleted.execute(); 72 } 73 }; 74 75 sizeEditor.prepareForExport(() -> { 76 if (BrowseCap.isMacintoshDesktop()) 77 { 78 clipboard_.copyPlotToCocoaPasteboard( 79 sizeEditor.getImageWidth(), 80 sizeEditor.getImageHeight(), 81 completed); 82 } 83 else 84 { 85 WindowEx win = sizeEditor.getPreviewIFrame().getContentWindow(); 86 Document doc = win.getDocument(); 87 NodeList<Element> images = doc.getElementsByTagName("img"); 88 if (images.getLength() > 0) 89 { 90 Element img = images.getItem(0); 91 DesktopFrame frame = Desktop.getFrame(); 92 93 // NOTE: we use a one-pixel fudge factor here to avoid copying 94 // bits of the border; see https://github.com/rstudio/rstudio/issues/4864 95 frame.copyPageRegionToClipboard( 96 ElementEx.getClientLeft(img) + 1, 97 ElementEx.getClientTop(img) + 1, 98 img.getClientWidth(), 99 img.getClientHeight(), 100 completed); 101 } 102 else 103 { 104 // shouldn't happen but make sure we clean up after 105 completed.execute(); 106 } 107 } 108 }); 109 } 110 111 protected final ExportPlotClipboard clipboard_; 112 } 113