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