1 /*
2  * RGraphicsPlotManager.hpp
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 #ifndef R_SESSION_GRAPHICS_PLOT_MANAGER_HPP
17 #define R_SESSION_GRAPHICS_PLOT_MANAGER_HPP
18 
19 #include <string>
20 #include <vector>
21 
22 #include <boost/utility.hpp>
23 #include <boost/shared_ptr.hpp>
24 #include <boost/function.hpp>
25 #include <boost/regex.hpp>
26 #include <boost/circular_buffer.hpp>
27 
28 #include <core/BoostSignals.hpp>
29 #include <shared_core/Error.hpp>
30 #include <shared_core/FilePath.hpp>
31 
32 #include <r/session/RGraphics.hpp>
33 
34 #include "RGraphicsTypes.hpp"
35 #include "RGraphicsPlot.hpp"
36 
37 namespace rstudio {
38 namespace r {
39 namespace session {
40 namespace graphics {
41 
42 // singleton
43 class PlotManager;
44 PlotManager& plotManager();
45 
46 struct GraphicsDeviceEvents
47 {
48    RSTUDIO_BOOST_SIGNAL<void (SEXP)> onNewPage;
49    RSTUDIO_BOOST_SIGNAL<void ()> onDrawing;
50    RSTUDIO_BOOST_SIGNAL<void ()> onResized;
51    RSTUDIO_BOOST_SIGNAL<void ()> onClosed;
52 };
53 
54 class PlotManipulatorManager;
55 
56 class PlotManager : boost::noncopyable, public r::session::graphics::Display
57 {
58 private:
59    PlotManager();
60    friend PlotManager& plotManager();
61 
62 public:
~PlotManager()63    virtual ~PlotManager() {}
64 
65    core::Error initialize(const core::FilePath& graphicsPath,
66                           const GraphicsDeviceFunctions& graphicsDevice,
67                           GraphicsDeviceEvents* pEvents);
68 
69    // plot list
70    virtual int plotCount() const;
71    virtual core::Error plotImageFilename(int index,
72                                          std::string* pImageFilename) const;
73    virtual int activePlotIndex() const;
74    virtual core::Error setActivePlot(int index);
75    virtual core::Error removePlot(int index);
76 
77    // actions on active plot
78    virtual core::Error savePlotAsImage(const core::FilePath& filePath,
79                                        const std::string& format,
80                                        int widthPx,
81                                        int heightPx,
82                                        bool useDevicePixelRatio = false);
83 
84    virtual core::Error savePlotAsImage(const core::FilePath& filePath,
85                                        const std::string& format,
86                                        int widthPx,
87                                        int heightPx,
88                                        double devicePixelRatio);
89 
90    virtual core::Error savePlotAsPdf(const core::FilePath& filePath,
91                                      double widthInches,
92                                      double heightInches,
93                                      bool useCairoPdf);
94 
95    virtual core::Error savePlotAsMetafile(const core::FilePath& filePath,
96                                           int widthPx,
97                                           int heightPx);
98 
99    // display
100    virtual bool hasOutput() const;
101    virtual bool hasChanges() const;
102    virtual bool isActiveDevice() const;
103    virtual boost::posix_time::ptime lastChange() const;
104    virtual void render(boost::function<void(DisplayState)> outputFunction);
105    virtual std::string imageFilename() const;
106    virtual void refresh();
107 
108     // retrieve image path based on filename
109    virtual core::FilePath imagePath(const std::string& imageFilename) const;
110 
111    virtual void clear();
112 
113    virtual RSTUDIO_BOOST_SIGNAL<void ()>& onShowManipulator();
114    virtual void setPlotManipulatorValues(const core::json::Object& values);
115    virtual void manipulatorPlotClicked(int x, int y);
116 
117    virtual void onBeforeExecute();
118 
119    // manipulate persistent state
120    core::Error savePlotsState();
121    core::Error restorePlotsState();
122 
123    // fully serialize and deserialize to an external directory
124    core::Error serialize(const core::FilePath& saveToPath);
125    core::Error deserialize(const core::FilePath& restoreFromPath);
126 
127 private:
128 
129    // make plot manipulator manager a friend
130    friend class PlotManipulatorManager;
131 
132    // typedefs
133    typedef boost::shared_ptr<Plot> PtrPlot;
134 
135    // device events
136    void onDeviceNewPage(SEXP previousPageSnapshot);
137    void onDeviceDrawing();
138    void onDeviceResized();
139    void onDeviceClosed();
140 
141    // active plot
142    Plot& activePlot() const;
143    bool isValidPlotIndex(int index) const;
144    bool hasPlot() const;
145 
146    // set change flag
147    void setDisplayHasChanges(bool hasChanges);
148 
149    // invalidate the active plot
150    void invalidateActivePlot();
151 
152    // render active plot to display (used in setActivePlot and onSessionResume)
153    void renderActivePlotToDisplay();
154 
155    // render active plot file file
156    core::Error savePlotAsFile(const boost::function<core::Error()>&
157                                                          deviceCreationFunction);
158    core::Error savePlotAsFile(const std::string& fileDeviceCreationCode);
159 
160    core::Error savePlotAsBitmapFile(const core::FilePath& targetPath,
161                                     const std::string& bitmapFileType,
162                                     int width,
163                                     int height,
164                                     double pixelRatio);
165 
166    core::Error savePlotAsSvg(const core::FilePath& targetPath,
167                              int width,
168                              int height);
169 
170    core::Error savePlotAsPostscript(const core::FilePath& targetPath,
171                                     int width,
172                                     int height);
173 
174 
175    // error helpers
176    core::Error plotIndexError(int index, const core::ErrorLocation& location)
177                                                                          const;
178 
179    std::string emptyImageFilename() const;
180 
181 private:
182    friend class SuppressDeviceEventsScope;
183 
184    // storage paths
185    core::FilePath plotsStateFile_;
186    core::FilePath graphicsPath_;
187 
188    // interface to graphics device
189    GraphicsDeviceFunctions graphicsDevice_;
190 
191    // state
192    bool displayHasChanges_;
193    boost::posix_time::ptime lastChange_;
194    bool suppressDeviceEvents_;
195 
196    int activePlot_;
197    boost::circular_buffer<PtrPlot> plots_;
198 
199    boost::regex plotInfoRegex_;
200 };
201 
202 class SuppressDeviceEventsScope
203 {
204 public:
SuppressDeviceEventsScope(PlotManager & plotManager)205    SuppressDeviceEventsScope(PlotManager& plotManager)
206       : plotManager_(plotManager)
207    {
208       plotManager_.suppressDeviceEvents_ = true;
209    }
210 
~SuppressDeviceEventsScope()211    virtual ~SuppressDeviceEventsScope()
212    {
213       plotManager_.suppressDeviceEvents_ = false;
214    }
215 private:
216    PlotManager& plotManager_;
217 };
218 
219 
220 } // namespace graphics
221 } // namespace session
222 } // namespace r
223 } // namespace rstudio
224 
225 #endif // R_SESSION_GRAPHICS_PLOT_MANAGER_HPP
226 
227