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