1 /*
2  * PlotsPane.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 
16 package org.rstudio.studio.client.workbench.views.plots;
17 
18 import com.google.gwt.dom.client.Style.Unit;
19 import com.google.gwt.event.logical.shared.HasResizeHandlers;
20 import com.google.gwt.event.logical.shared.ResizeEvent;
21 import com.google.gwt.event.logical.shared.ResizeHandler;
22 import com.google.gwt.event.shared.HandlerRegistration;
23 import com.google.gwt.user.client.Command;
24 import com.google.gwt.user.client.ui.LayoutPanel;
25 import com.google.gwt.user.client.ui.Widget;
26 import com.google.inject.Inject;
27 
28 import org.rstudio.core.client.CommandWithArg;
29 import org.rstudio.core.client.ElementIds;
30 import org.rstudio.core.client.Size;
31 import org.rstudio.core.client.widget.ImageFrame;
32 import org.rstudio.core.client.widget.Toolbar;
33 import org.rstudio.studio.client.application.events.EventBus;
34 import org.rstudio.studio.client.common.SimpleRequestCallback;
35 import org.rstudio.studio.client.common.dependencies.DependencyManager;
36 import org.rstudio.studio.client.common.zoom.ZoomUtils;
37 import org.rstudio.studio.client.rsconnect.RSConnect;
38 import org.rstudio.studio.client.rsconnect.model.PublishHtmlSource;
39 import org.rstudio.studio.client.rsconnect.ui.RSConnectPublishButton;
40 import org.rstudio.studio.client.workbench.commands.Commands;
41 import org.rstudio.studio.client.workbench.ui.WorkbenchPane;
42 import org.rstudio.studio.client.workbench.views.plots.model.PlotsServerOperations;
43 import org.rstudio.studio.client.workbench.views.plots.ui.PlotsToolbar;
44 
45 import java.util.Iterator;
46 
47 public class PlotsPane extends WorkbenchPane implements Plots.Display,
48       HasResizeHandlers
49 {
50    @Inject
PlotsPane(Commands commands, EventBus events, PlotsServerOperations server, DependencyManager dependencies)51    public PlotsPane(Commands commands, EventBus events, PlotsServerOperations server,
52          DependencyManager dependencies)
53    {
54       super("Plots", events);
55       commands_ = commands;
56       server_ = server;
57       dependencies_ = dependencies;
58       ensureWidget();
59    }
60 
61    @Override
createMainToolbar()62    protected Toolbar createMainToolbar()
63    {
64       publishButton_ = new RSConnectPublishButton(RSConnectPublishButton.HOST_PLOTS,
65             RSConnect.CONTENT_TYPE_PLOT, true, commands_.savePlotAsImage());
66       publishButton_.setPublishHtmlSource(new PublishHtmlSource()
67       {
68          @Override
69          public String getTitle()
70          {
71             return "Current Plot";
72          }
73 
74          @Override
75          public void generatePublishHtml(
76                final CommandWithArg<String> onComplete)
77          {
78             dependencies_.withRMarkdown("Publishing plots", new Command()
79             {
80                @Override
81                public void execute()
82                {
83                  final Size size = ZoomUtils.getZoomedSize(
84                        getPlotFrameSize(),
85                        new Size(400, 350),
86                        new Size(800, 700));
87                   server_.plotsCreateRPubsHtml(
88                      "Plot",
89                      "",
90                      size.width,
91                      size.height,
92                      new SimpleRequestCallback<String>()
93                      {
94                         @Override
95                         public void onResponseReceived(String rpubsHtmlFile)
96                         {
97                            onComplete.execute(rpubsHtmlFile);
98                         }
99                      });
100                  }
101              });
102          }
103       });
104       plotsToolbar_ = new PlotsToolbar(commands_, publishButton_);
105       return plotsToolbar_;
106    }
107 
108    @Override
createMainWidget()109    protected Widget createMainWidget()
110    {
111       panel_ = new LayoutPanel();
112       panel_.addStyleName("ace_editor_theme");
113       panel_.setSize("100%", "100%");
114 
115       frame_ = new ImageFrame("Plots Pane");
116       frame_.setStyleName("rstudio-HelpFrame");
117       frame_.setMarginWidth(0);
118       frame_.setMarginHeight(0);
119       frame_.setUrl("about:blank");
120       frame_.setSize("100%", "100%");
121       ElementIds.assignElementId(frame_.getElement(),
122                                  ElementIds.PLOT_IMAGE_FRAME);
123 
124       panel_.add(frame_);
125       panel_.setWidgetTopBottom(frame_, 0, Unit.PX, 0, Unit.PX);
126       panel_.setWidgetLeftRight(frame_, 0, Unit.PX, 0, Unit.PX);
127 
128       // Provide a widget container where adornments can be added on top of the
129       // plots panel (e.g. manipulator button). Hidden initially so it doens't
130       // block context menu actions such as Copy Image.
131       plotsSurface_ = new PlotsSurface(panel_);
132       panel_.add(plotsSurface_);
133       plotsSurface_.disableSurface();
134 
135       // return the panel
136       return panel_;
137    }
138 
139    @Override
setProgress(boolean enabled)140    public void setProgress(boolean enabled)
141    {
142       // also set frame to about:blank during progress
143       if (enabled)
144          frame_.setImageUrl(null);
145 
146       super.setProgress(enabled);
147    }
148 
showEmptyPlot()149    public void showEmptyPlot()
150    {
151       frame_.setImageUrl(null);
152       plotsToolbar_.invalidateSeparators();
153    }
154 
showPlot(String plotUrl)155    public void showPlot(String plotUrl)
156    {
157       // save plot url for refresh
158       plotUrl_ = plotUrl;
159 
160       // use frame.contentWindow.location.replace to avoid having the plot
161       // enter the browser's history
162       frame_.setImageUrl(plotUrl);
163       plotsToolbar_.invalidateSeparators();
164    }
165 
getPlotUrl()166    public String getPlotUrl()
167    {
168       return plotUrl_;
169    }
170 
171 
refresh()172    public void refresh()
173    {
174       if (plotUrl_ != null)
175          frame_.setImageUrl(plotUrl_);
176    }
177 
getPlotsSurface()178    public PlotsSurface getPlotsSurface()
179    {
180       return plotsSurface_;
181    }
182 
getPlotsParent()183    public Plots.Parent getPlotsParent()
184    {
185       return plotsParent_;
186    }
187 
getPlotFrameSize()188    public Size getPlotFrameSize()
189    {
190       return new Size(frame_.getOffsetWidth(), frame_.getOffsetHeight());
191    }
192 
193    @Override
onResize()194    public void onResize()
195    {
196       super.onResize();
197       int width = getOffsetWidth();
198       ResizeEvent.fire(this, width, getOffsetHeight());
199 
200       if (width > 0 && publishButton_ != null)
201       {
202          publishButton_.setShowCaption(width > 500);
203       }
204    }
205 
addResizeHandler(ResizeHandler resizeHandler)206    public HandlerRegistration addResizeHandler(ResizeHandler resizeHandler)
207    {
208       return addHandler(resizeHandler, ResizeEvent.getType());
209    }
210 
211    private final Commands commands_;
212    private final PlotsServerOperations server_;
213    private final DependencyManager dependencies_;
214 
215    private RSConnectPublishButton publishButton_;
216    private LayoutPanel panel_;
217    private ImageFrame frame_;
218    private String plotUrl_;
219    private PlotsToolbar plotsToolbar_ = null;
220    private PlotsSurface plotsSurface_ = null;
221    private Plots.Parent plotsParent_ = new Plots.Parent() {
222       public void add(Widget w)
223       {
224          panel_.add(w);
225       }
226 
227       public boolean remove(Widget w)
228       {
229          return panel_.remove(w);
230       }
231 
232       public Iterator<Widget> iterator()
233       {
234          return panel_.iterator();
235       }
236 
237       public void clear()
238       {
239          panel_.clear();
240       }
241 
242       public void installCustomToolbar(Customizer customizer)
243       {
244          plotsToolbar_.installCustomToolbar(customizer);
245       }
246 
247       public void removeCustomToolbar()
248       {
249          plotsToolbar_.removeCustomToolbar();
250       }
251    };
252 
253 }
254