1 /*******************************************************************************
2 * Copyright (c) 2007, 2017 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
12 *******************************************************************************/
13 package org.simantics.modeling.ui.pdf;
16 import java.lang.reflect.InvocationTargetException;
17 import java.util.HashSet;
20 import org.eclipse.core.runtime.IPath;
21 import org.eclipse.core.runtime.Path;
22 import org.eclipse.core.runtime.SubMonitor;
23 import org.eclipse.jface.layout.GridDataFactory;
24 import org.eclipse.jface.wizard.WizardPage;
25 import org.eclipse.swt.SWT;
26 import org.eclipse.swt.custom.CCombo;
27 import org.eclipse.swt.events.ModifyEvent;
28 import org.eclipse.swt.events.ModifyListener;
29 import org.eclipse.swt.events.SelectionAdapter;
30 import org.eclipse.swt.events.SelectionEvent;
31 import org.eclipse.swt.events.SelectionListener;
32 import org.eclipse.swt.layout.GridData;
33 import org.eclipse.swt.layout.GridLayout;
34 import org.eclipse.swt.widgets.Button;
35 import org.eclipse.swt.widgets.Combo;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Display;
38 import org.eclipse.swt.widgets.FileDialog;
39 import org.eclipse.swt.widgets.Label;
40 import org.simantics.databoard.Bindings;
41 import org.simantics.db.ReadGraph;
42 import org.simantics.db.Resource;
43 import org.simantics.db.common.NamedResource;
44 import org.simantics.db.common.request.ReadRequest;
45 import org.simantics.db.exception.DatabaseException;
46 import org.simantics.modeling.requests.CollectionResult;
47 import org.simantics.modeling.requests.Node;
48 import org.simantics.modeling.requests.Nodes;
49 import org.simantics.ui.utils.ResourceAdaptionUtils;
50 import org.simantics.utils.FileUtils;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 public class PDFExportPage extends WizardPage {
56 private static final Logger LOGGER = LoggerFactory.getLogger(PDFExportPage.class);
58 protected Display display;
60 protected PDFExportPlan exportModel;
62 protected Combo modelSelector;
63 protected SelectionListener modelSelectorListener;
65 protected NodeTree nodeTree;
67 protected CCombo exportLocation;
68 protected ModifyListener exportLocationListener;
70 protected Set<Node> selectedNodes;
72 protected Label toFileLabel;
74 protected boolean exportLocationTouchedByUser = false;
76 protected PDFExportPage(PDFExportPlan model) {
77 super("Export Diagrams to PDF", "Define Exported Items", null);
78 this.exportModel = model;
79 this.selectedNodes = exportModel.selectedNodeSet;
83 public void createControl(Composite parent) {
84 this.display = parent.getDisplay();
86 Composite container = new Composite(parent, SWT.NONE);
88 GridLayout layout = new GridLayout();
89 layout.horizontalSpacing = 20;
90 layout.verticalSpacing = 10;
91 layout.numColumns = 3;
92 container.setLayout(layout);
95 Label modelSelectorLabel = new Label(container, SWT.NONE);
96 modelSelectorLabel.setText("Model Selector:");
97 GridDataFactory.fillDefaults().span(1, 1).applyTo(modelSelectorLabel);
98 modelSelector = new Combo(container, SWT.BORDER | SWT.READ_ONLY);
99 GridDataFactory.fillDefaults().span(2, 1).applyTo(modelSelector);
100 modelSelectorListener = new SelectionAdapter() {
102 public void widgetSelected(SelectionEvent e) {
103 NamedResource data = (NamedResource) modelSelector.getData(String.valueOf(modelSelector.getSelectionIndex()));
104 scheduleInitializeData(data);
108 // Fill model selector combo
109 for (int i = 0; i < exportModel.selectableModels.size(); ++i) {
110 NamedResource nr = exportModel.selectableModels.get(i);
111 modelSelector.add(nr.getName());
112 modelSelector.setData("" + i, nr);
115 modelSelector.addSelectionListener(modelSelectorListener);
117 nodeTree = new NodeTree(container, selectedNodes);
118 GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(nodeTree);
119 nodeTree.setSelectionChangeListener(this::validatePage);
121 toFileLabel = new Label(container, SWT.NONE);
122 toFileLabel.setText("&To file:");
123 exportLocation = new CCombo(container, SWT.BORDER);
125 exportLocation.setText("");
126 GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(exportLocation);
128 for (String path : exportModel.recentLocations) {
129 exportLocation.add(path);
132 exportLocationListener = new ModifyListener() {
134 public void modifyText(ModifyEvent e) {
135 //System.out.println("export location changed by user");
136 exportLocationTouchedByUser = true;
140 exportLocation.addModifyListener(exportLocationListener);
142 Button browseFileButton = new Button(container, SWT.PUSH);
144 browseFileButton.setText("Browse...");
145 browseFileButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
146 browseFileButton.addSelectionListener(new SelectionAdapter() {
148 public void widgetSelected(SelectionEvent e) {
149 FileDialog dialog = new FileDialog(getShell(), SWT.SAVE);
150 dialog.setFilterExtensions(new String[] { "*.pdf" });
151 dialog.setFilterNames(new String[] { "PDF Document" });
152 String loc = exportLocation.getText();
154 IPath p = new Path(loc);
156 if (f.isDirectory()) {
157 dialog.setFilterPath(f.toString());
158 } else if (f.isFile()) {
159 IPath path = p.removeLastSegments(1);
160 String name = p.lastSegment();
161 dialog.setFilterPath(path.toOSString());
162 dialog.setFileName(name);
164 dialog.setFilterPath(f.toString());
165 IPath path = p.removeLastSegments(1);
166 String name = p.lastSegment();
168 if (f.isDirectory()) {
169 dialog.setFilterPath(path.toOSString());
171 dialog.setFileName(name);
174 String file = dialog.open();
177 exportLocation.setText(file);
183 final Button zoomToFitButton = new Button(container, SWT.CHECK);
184 GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(zoomToFitButton);
185 zoomToFitButton.setText("F&it by content");
186 zoomToFitButton.setSelection(exportModel.fitContentToPageMargins);
187 zoomToFitButton.addSelectionListener(new SelectionAdapter() {
189 public void widgetSelected(SelectionEvent e) {
190 exportModel.fitContentToPageMargins = zoomToFitButton.getSelection();
195 final Button attachTGButton = new Button(container, SWT.CHECK);
196 GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( attachTGButton );
197 attachTGButton.setText("Attach &TG (Importable diagram)");
198 attachTGButton.setSelection(exportModel.attachTG);
199 attachTGButton.addSelectionListener(new SelectionAdapter() {
201 public void widgetSelected(SelectionEvent e) {
202 exportModel.attachTG = attachTGButton.getSelection();
207 final Button attachWikiButton = new Button(container, SWT.CHECK);
208 GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( attachWikiButton );
209 attachWikiButton.setText("Attach &Wiki page");
210 attachWikiButton.setSelection(exportModel.attachWiki);
211 attachWikiButton.addSelectionListener(new SelectionAdapter() {
213 public void widgetSelected(SelectionEvent e) {
214 exportModel.attachWiki = attachWikiButton.getSelection();
218 final Button addPageNumbers = new Button(container, SWT.CHECK);
219 GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( addPageNumbers );
220 addPageNumbers.setText("Add page &numbers");
221 addPageNumbers.setSelection(exportModel.addPageNumbers);
222 addPageNumbers.addSelectionListener(new SelectionAdapter() {
224 public void widgetSelected(SelectionEvent e) {
225 exportModel.addPageNumbers = addPageNumbers.getSelection();
229 setControl(container);
232 scheduleInitializeData(exportModel.selection);
235 private void scheduleInitializeData(final NamedResource modelSelection) {
236 display.asyncExec(() -> {
238 if (!nodeTree.isDisposed())
239 initializeData(modelSelection);
240 } catch (DatabaseException | InterruptedException e) {
241 LOGGER.error("Input data initialization failed.", e);
242 } catch (InvocationTargetException e) {
243 LOGGER.error("Input data initialization failed.", e.getTargetException());
248 private NamedResource getSelectedModel() {
249 int sel = modelSelector.getSelectionIndex();
251 NamedResource nr = (NamedResource) modelSelector.getData("" + sel);
257 private void setExportLocationWithoutNotification(String text) {
258 exportLocation.removeModifyListener(exportLocationListener);
259 exportLocation.setText(text);
260 exportLocation.addModifyListener(exportLocationListener);
263 private void initializeData(final NamedResource modelSelection) throws DatabaseException, InvocationTargetException, InterruptedException {
264 Set<Node> toBeSelected = new HashSet<>();
266 if (modelSelection != null) {
267 // Process input selection to find the model/state selected by default.
269 // This may take longer than the user wants to wait without
273 long time = System.nanoTime();
275 getWizard().getContainer().run(true, true, monitor -> {
277 SubMonitor mon = SubMonitor.convert(monitor, "Searching for exportable diagrams...", 100);
278 exportModel.sessionContext.getSession().syncRequest(new ReadRequest() {
280 public void run(ReadGraph graph) throws DatabaseException {
281 CollectionResult coll = exportModel.nodes = DiagramPrinter.browse(mon.newChild(100), graph, new Resource[] { modelSelection.getResource() });
283 // Decide initial selection based on exportModel.initialSelection
284 if (modelSelection.equals(exportModel.initialModelSelection)) {
285 Set<Resource> selectedResources = new HashSet<>();
286 for (Object o : exportModel.initialSelection.toList()) {
287 Resource r = ResourceAdaptionUtils.toSingleResource(o);
289 selectedResources.add(r);
291 coll.walkTree(node -> {
292 if (node.getDiagramResource() != null) {
293 if (Nodes.parentIsInSet(toBeSelected, node))
294 toBeSelected.add(node);
296 for (Resource r : node.getDefiningResources())
297 if (selectedResources.contains(r))
298 toBeSelected.add(node);
304 // Filter out any excess nodes from the tree.
305 exportModel.nodes = coll = coll.withRoots(Nodes.depthFirstFilter(Nodes.DIAGRAM_RESOURCE_PREDICATE, coll.roots));
307 // Select all if initial selection doesn't dictate anything.
308 if (toBeSelected.isEmpty())
309 toBeSelected.addAll(coll.breadthFirstFlatten(CollectionResult.DIAGRAM_RESOURCE_FILTER));
312 } catch (DatabaseException e) {
313 throw new InvocationTargetException(e);
320 long endTime = System.nanoTime();
321 if (exportModel.nodes != null)
322 LOGGER.info("Found " + exportModel.nodes.diagrams.size() + " diagrams in " + ((endTime - time)*1e-9) + " seconds.");
325 // Browsing was canceled by user.
326 if (exportModel.nodes == null)
329 // Setup selected states, select everything by default.
330 selectedNodes.clear();
331 selectedNodes.addAll(toBeSelected);
333 // Fully refresh node tree
334 nodeTree.setInput(exportModel.nodes);
336 modelSelector.removeSelectionListener(modelSelectorListener);
337 int selectedIndex = -1;
338 for (int i = 0; i < modelSelector.getItemCount(); ++i) {
339 Object obj = modelSelector.getData("" + i);
340 if (org.simantics.utils.ObjectUtils.objectEquals(obj, modelSelection)) {
344 if (selectedIndex == -1 && modelSelector.getItemCount() > 0)
346 if (selectedIndex != -1)
347 modelSelector.select(selectedIndex);
348 modelSelector.addSelectionListener(modelSelectorListener);
353 void validatePage() {
354 int diagramCount = 0;
355 Node singleDiagram = null;
356 for (Node n : selectedNodes)
357 if (n.getDiagramResource() != null) {
362 //System.out.println("VALIDATE PAGE: " + exportLocationTouchedByUser);
363 if (diagramCount == 0) {
364 setMessage("Select the diagrams to export.");
365 setErrorMessage(null);
366 setPageComplete(false);
370 if (!exportLocationTouchedByUser) {
371 String generatedName = null;
372 // Generate file name automatically if user hasn't touched the name manually.
373 NamedResource nr = getSelectedModel();
375 if (diagramCount == 1 && singleDiagram != null) {
376 generatedName = nr.getName() + "-" + singleDiagram.getName();
378 generatedName = nr.getName();
381 //System.out.println("generate name: " + generatedName);
382 if (generatedName != null) {
383 if (!FileUtils.isValidFileName(generatedName))
384 generatedName = (String) Bindings.STR_VARIANT.createUnchecked(Bindings.STRING, generatedName);
385 String name = generatedName + ".pdf";
388 if ( !exportModel.recentLocations.isEmpty() ) {
390 for ( String loc : exportModel.recentLocations )
392 if ( loc.endsWith(name) && !loc.equals(name) ) {
398 String firstLine = exportModel.recentLocations.iterator().next();
399 File f = new File(firstLine);
400 File parentFile = f.getParentFile();
401 if ( parentFile!=null ) {
402 name = new File( f.getParentFile(), name ).getAbsolutePath();
405 setExportLocationWithoutNotification(name);
409 String exportLoc = exportLocation.getText();
410 if (exportLoc.isEmpty()) {
411 setMessage("Select an export target file.");
412 setErrorMessage(null);
413 setPageComplete(false);
416 File file = new File(exportLoc);
418 if (file.isDirectory()) {
419 setErrorMessage("The target already exists and it is a directory.");
420 setPageComplete(false);
423 if (!file.isFile()) {
424 setErrorMessage("The target already exists and it is a not a regular file.");
425 setPageComplete(false);
429 exportModel.exportLocation = file;
431 String msg = diagramCount + " diagrams selected for export.";
434 setErrorMessage(null);
435 setPageComplete(true);