Xcos: fix export all layers of a diagram
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / actions / ExportAllAction.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Vincent COUVERT
4  * Copyright (C) 2010 - DIGITEO - Clement DAVID
5  * Copyright (C) 2011-2015 - Scilab Enterprises - Clement DAVID
6  *
7  * Copyright (C) 2012 - 2016 - Scilab Enterprises
8  *
9  * This file is hereby licensed under the terms of the GNU GPL v2.0,
10  * pursuant to article 5.3.4 of the CeCILL v.2.1.
11  * This file was originally licensed under the terms of the CeCILL v2.1,
12  * and continues to be available under such terms.
13  * For more information, see the COPYING file which you should have received
14  * along with this program.
15  *
16  */
17
18 package org.scilab.modules.xcos.actions;
19
20 import java.awt.Color;
21 import java.awt.Toolkit;
22 import java.awt.event.ActionEvent;
23 import java.awt.image.BufferedImage;
24 import java.io.File;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Set;
30 import java.util.TreeSet;
31
32 import javax.imageio.ImageIO;
33 import javax.swing.JFileChooser;
34 import javax.swing.JOptionPane;
35
36 import org.scilab.modules.graph.ScilabGraph;
37 import org.scilab.modules.graph.actions.base.DefaultAction;
38 import org.scilab.modules.graph.utils.ScilabGraphRenderer;
39 import org.scilab.modules.gui.bridge.filechooser.FileMask;
40 import org.scilab.modules.gui.menuitem.MenuItem;
41 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
42 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
43 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
44 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
45 import org.scilab.modules.xcos.JavaController;
46 import org.scilab.modules.xcos.Kind;
47 import org.scilab.modules.xcos.ObjectProperties;
48 import org.scilab.modules.xcos.VectorOfScicosID;
49 import org.scilab.modules.xcos.XcosTab;
50 import org.scilab.modules.xcos.configuration.ConfigurationManager;
51 import org.scilab.modules.xcos.graph.XcosDiagram;
52 import org.scilab.modules.xcos.utils.XcosMessages;
53 import org.w3c.dom.Document;
54
55 import com.mxgraph.swing.mxGraphComponent;
56 import com.mxgraph.util.mxCellRenderer;
57 import com.mxgraph.util.mxUtils;
58 import com.mxgraph.util.mxXmlUtils;
59 import java.util.HashMap;
60 import java.util.LinkedHashSet;
61 import org.scilab.modules.xcos.graph.model.XcosCellFactory;
62
63 /**
64  * Diagram export management
65  */
66 @SuppressWarnings(value = { "serial" })
67 public final class ExportAllAction extends DefaultAction {
68     /** Name of the action */
69     public static final String NAME = XcosMessages.EXPORT_ALL;
70     /** Icon name of the action */
71     public static final String SMALL_ICON = "";
72     /** Mnemonic key of the action */
73     public static final int MNEMONIC_KEY = 0;
74     /** Accelerator key for the action */
75     public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit()
76             .getMenuShortcutKeyMask();
77
78     private static final String HTML = "html";
79     private static final String VML = "vml";
80     private static final String SVG = "svg";
81
82     private boolean useBackground = true;
83
84     /**
85      * Constructor
86      *
87      * @param scilabGraph
88      *            associated Scilab Graph
89      */
90     public ExportAllAction(ScilabGraph scilabGraph) {
91         super(scilabGraph);
92     }
93
94     /**
95      * Create export menu
96      *
97      * @param scilabGraph
98      *            associated Scilab Graph
99      * @return the menu
100      */
101     public static MenuItem createMenu(ScilabGraph scilabGraph) {
102         return createMenu(scilabGraph, ExportAllAction.class);
103     }
104
105     /**
106      * Action !!!
107      *
108      * @param e
109      *            parameter
110      * @see org.scilab.modules.graph.actions.base.DefaultAction#actionPerformed(java.awt.event.ActionEvent)
111      */
112     @Override
113     public void actionPerformed(ActionEvent e) {
114         final XcosDiagram graph = ((XcosDiagram) getGraph(null)).getRootDiagram();
115
116         // Adds a filter for each supported image format
117         Collection<String> imageFormats = Arrays.asList(ImageIO.getWriterFileSuffixes());
118
119         // The mask ordered collection
120         Set<String> mask = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
121
122         mask.add(SVG);
123         mask.add(HTML);
124         mask.add(VML);
125         mask.addAll(imageFormats);
126
127         JFileChooser fc = new JFileChooser();
128         fc.setDialogTitle(XcosMessages.EXPORT_ALL + XcosMessages.DOTS);
129         fc.setApproveButtonText(XcosMessages.EXPORT);
130         fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
131
132         fc.setAcceptAllFileFilterUsed(false);
133         for (String string : mask) {
134             fc.addChoosableFileFilter(new FileMask(string, string.toUpperCase()));
135         }
136
137         ConfigurationManager.configureCurrentDirectory(fc);
138
139         int selection = fc.showSaveDialog(getGraph(null).getAsComponent());
140         if (selection == JFileChooser.APPROVE_OPTION) {
141             File dir = fc.getSelectedFile();
142
143             /* getting the format */
144             String format = ((FileMask) fc.getFileFilter()).getExtensionFromFilter();
145
146             /* update states from the format */
147             if ((!format.equalsIgnoreCase("png"))
148                     || ScilabModalDialog.show(XcosTab.get((XcosDiagram) getGraph(null)),
149                                               XcosMessages.TRANSPARENT_BACKGROUND, XcosMessages.XCOS,
150                                               IconType.QUESTION_ICON, ButtonType.YES_NO) != AnswerOption.YES_OPTION) {
151                 useBackground = true;
152             }
153
154             /*
155              * Instantiate all the sub-diagrams
156              */
157             JavaController controller = new JavaController();
158             ArrayList<XcosDiagram> diagrams = new ArrayList<>();
159
160             ArrayList<Long> stash = new ArrayList<>();
161             allocateDiagrams(controller, diagrams, stash, graph.getUID(), Kind.DIAGRAM);
162             while (!stash.isEmpty()) {
163                 final long uid = stash.remove(stash.size() - 1);
164                 allocateDiagrams(controller, diagrams, stash, uid, Kind.BLOCK);
165             }
166
167             /*
168              * Create unique filenames
169              */
170             HashMap<File, Integer> filesSet = new HashMap<>(diagrams.size());
171             makeUniqueFile(filesSet, 0, dir, graph.getTitle(), format);
172             for (int i = 0; i < diagrams.size(); i++) {
173                 final XcosDiagram d = diagrams.get(i);
174                 makeUniqueFile(filesSet, i + 1, dir, d.getTitle(), format);
175             }
176             File[] files = new File[diagrams.size() + 1];
177             filesSet.forEach((f, i) -> { if (i >= 0) files[i] = f;});
178
179             /*
180              * Export
181              */
182             try {
183                 export(graph, files[0], format);
184
185                 for (int i = 0; i < diagrams.size(); i++) {
186                     export(diagrams.get(i), files[i + 1], format);
187                 }
188             } catch (IOException e1) {
189                 e1.printStackTrace();
190             }
191
192         }
193     }
194
195     private static void makeUniqueFile(HashMap<File, Integer> filesSet, int fileIndex, File dir, String name, String ext) {
196         final StringBuilder str = new StringBuilder(name).append(".").append(ext);
197
198         File f = new File(dir, str.toString());
199         Integer index = filesSet.get(f);
200
201         // rename the existing file with a 0 prefix
202         if (index != null && index >= 0) {
203             // flag the original and rename it
204             filesSet.put(f, -1);
205
206             str.delete(name.length(), str.length());
207             str.append("_").append(0).append(".").append(ext);
208             f = new File(dir, str.toString());
209
210             filesSet.put(f, index);
211         }
212         for (int i = 1; index != null; i++) {
213             str.delete(name.length(), str.length());
214             str.append("_").append(i).append(".").append(ext);
215
216             f = new File(dir, str.toString());
217             index = filesSet.get(f);
218         }
219
220         filesSet.put(f, fileIndex);
221     }
222
223     private void allocateDiagrams(JavaController controller, ArrayList<XcosDiagram> diagrams, ArrayList<Long> stash,
224                                   final long uid, final Kind kind) {
225         final VectorOfScicosID children = new VectorOfScicosID();
226         controller.getObjectProperty(uid, kind, ObjectProperties.CHILDREN, children);
227
228         final int len = children.size();
229         for (int i = 0; i < len ; i++) {
230             String[] interfaceFunction = new String[1];
231             long currentUID = children.get(i);
232             if (controller.getKind(currentUID) == Kind.BLOCK) {
233                 controller.getObjectProperty(children.get(i), Kind.BLOCK, ObjectProperties.INTERFACE_FUNCTION, interfaceFunction);
234                 if (!"SUPER_f".equals(interfaceFunction[0])) {
235                     continue;
236                 }
237
238
239                 if (diagrams.stream().noneMatch(d -> d.getUID() == currentUID)) {
240                     String[] strUID = new String[1];
241                     controller.getObjectProperty(currentUID, Kind.BLOCK, ObjectProperties.UID, strUID);
242
243                     final XcosDiagram child = new XcosDiagram(controller, currentUID, Kind.BLOCK, strUID[0]);
244                     XcosCellFactory.insertChildren(controller, child);
245                     diagrams.add(child);
246                     stash.add(currentUID);
247                 }
248             }
249
250         }
251     }
252
253     /**
254      * Export the graph into the filename.
255      *
256      * The filename extension is used find export format.
257      *
258      * @param graph
259      *            the current graph
260      * @param filename
261      *            the filename
262      * @param fileFormat
263      *            the format to save
264      * @throws IOException
265      *             when a write problem occurs.
266      */
267     private void export(XcosDiagram graph, File filename, String fileFormat)
268     throws IOException {
269         filename.getParentFile().mkdirs();
270
271         if (fileFormat.equalsIgnoreCase(SVG)) {
272             ScilabGraphRenderer.createSvgDocument(graph, null, 1, null, null,
273                                                   filename.getCanonicalPath());
274         } else if (fileFormat.equalsIgnoreCase(VML)) {
275             Document doc = mxCellRenderer.createVmlDocument(graph, null, 1,
276                            null, null);
277             if (doc != null) {
278                 mxUtils.writeFile(mxXmlUtils.getXml(doc.getDocumentElement()),
279                                   filename.getCanonicalPath());
280             }
281         } else if (fileFormat.equalsIgnoreCase(HTML)) {
282             Document doc = mxCellRenderer.createHtmlDocument(graph, null, 1,
283                            null, null);
284             if (doc != null) {
285                 mxUtils.writeFile(mxXmlUtils.getXml(doc.getDocumentElement()),
286                                   filename.getCanonicalPath());
287             }
288         } else {
289             exportBufferedImage(graph, filename, fileFormat);
290         }
291     }
292
293     /**
294      * Use the Java image capabilities to export the diagram
295      *
296      * @param graph
297      *            the current diagram
298      * @param filename
299      *            the current filename
300      * @param fileFormat
301      *            the file format
302      * @throws IOException
303      *             when an error occurs
304      */
305     private void exportBufferedImage(XcosDiagram graph, File filename,
306                                      String fileFormat) throws IOException {
307         final mxGraphComponent graphComponent = graph.getAsComponent();
308
309         Color bg = null;
310
311         if (useBackground) {
312             bg = graphComponent.getBackground();
313         }
314
315         BufferedImage image = mxCellRenderer.createBufferedImage(graph, null,
316                               1, bg, graphComponent.isAntiAlias(), null,
317                               graphComponent.getCanvas());
318
319         if (image != null) {
320             ImageIO.write(image, fileFormat, filename);
321         } else {
322             JOptionPane.showMessageDialog(graphComponent,
323                                           XcosMessages.NO_IMAGE_DATA);
324         }
325     }
326 }