/******************************************************************************* * Copyright (c) 2007, 2017 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation * Semantum Oy - #7297 *******************************************************************************/ package org.simantics.modeling.ui.pdf; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import java.util.Set; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.NamedResource; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.modeling.requests.CollectionResult; import org.simantics.modeling.requests.Node; import org.simantics.modeling.requests.Nodes; import org.simantics.ui.utils.ResourceAdaptionUtils; import org.simantics.utils.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PDFExportPage extends WizardPage { private static final Logger LOGGER = LoggerFactory.getLogger(PDFExportPage.class); protected Display display; protected PDFExportPlan exportModel; protected Combo modelSelector; protected SelectionListener modelSelectorListener; protected NodeTree nodeTree; protected CCombo exportLocation; protected ModifyListener exportLocationListener; protected Set selectedNodes; protected Label toFileLabel; protected boolean exportLocationTouchedByUser = false; protected PDFExportPage(PDFExportPlan model) { super("Export Diagrams to PDF", "Define Exported Items", null); this.exportModel = model; this.selectedNodes = exportModel.selectedNodeSet; } @Override public void createControl(Composite parent) { this.display = parent.getDisplay(); Composite container = new Composite(parent, SWT.NONE); { GridLayout layout = new GridLayout(); layout.horizontalSpacing = 20; layout.verticalSpacing = 10; layout.numColumns = 3; container.setLayout(layout); } Label modelSelectorLabel = new Label(container, SWT.NONE); modelSelectorLabel.setText("Model Selector:"); GridDataFactory.fillDefaults().span(1, 1).applyTo(modelSelectorLabel); modelSelector = new Combo(container, SWT.BORDER | SWT.READ_ONLY); GridDataFactory.fillDefaults().span(2, 1).applyTo(modelSelector); modelSelectorListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { NamedResource data = (NamedResource) modelSelector.getData(String.valueOf(modelSelector.getSelectionIndex())); scheduleInitializeData(data); } }; // Fill model selector combo for (int i = 0; i < exportModel.selectableModels.size(); ++i) { NamedResource nr = exportModel.selectableModels.get(i); modelSelector.add(nr.getName()); modelSelector.setData("" + i, nr); } modelSelector.addSelectionListener(modelSelectorListener); nodeTree = new NodeTree(container, selectedNodes); GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(nodeTree); nodeTree.setSelectionChangeListener(this::validatePage); toFileLabel = new Label(container, SWT.NONE); toFileLabel.setText("&To file:"); exportLocation = new CCombo(container, SWT.BORDER); { exportLocation.setText(""); GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(exportLocation); for (String path : exportModel.recentLocations) { exportLocation.add(path); } exportLocationListener = new ModifyListener() { @Override public void modifyText(ModifyEvent e) { //System.out.println("export location changed by user"); exportLocationTouchedByUser = true; validatePage(); } }; exportLocation.addModifyListener(exportLocationListener); } Button browseFileButton = new Button(container, SWT.PUSH); { browseFileButton.setText("Browse..."); browseFileButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); browseFileButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { FileDialog dialog = new FileDialog(getShell(), SWT.SAVE); dialog.setFilterExtensions(new String[] { "*.pdf" }); dialog.setFilterNames(new String[] { "PDF Document" }); String loc = exportLocation.getText(); if (loc != null) { IPath p = new Path(loc); File f = p.toFile(); if (f.isDirectory()) { dialog.setFilterPath(f.toString()); } else if (f.isFile()) { IPath path = p.removeLastSegments(1); String name = p.lastSegment(); dialog.setFilterPath(path.toOSString()); dialog.setFileName(name); } else { dialog.setFilterPath(f.toString()); IPath path = p.removeLastSegments(1); String name = p.lastSegment(); f = path.toFile(); if (f.isDirectory()) { dialog.setFilterPath(path.toOSString()); } dialog.setFileName(name); } } String file = dialog.open(); if (file == null) return; exportLocation.setText(file); validatePage(); } }); } final Button zoomToFitButton = new Button(container, SWT.CHECK); GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(zoomToFitButton); zoomToFitButton.setText("F&it by content"); zoomToFitButton.setSelection(exportModel.fitContentToPageMargins); zoomToFitButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { exportModel.fitContentToPageMargins = zoomToFitButton.getSelection(); } }); /* final Button attachTGButton = new Button(container, SWT.CHECK); GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( attachTGButton ); attachTGButton.setText("Attach &TG (Importable diagram)"); attachTGButton.setSelection(exportModel.attachTG); attachTGButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { exportModel.attachTG = attachTGButton.getSelection(); } }); */ final Button attachWikiButton = new Button(container, SWT.CHECK); GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( attachWikiButton ); attachWikiButton.setText("Attach &Wiki page"); attachWikiButton.setSelection(exportModel.attachWiki); attachWikiButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { exportModel.attachWiki = attachWikiButton.getSelection(); } }); final Button addPageNumbers = new Button(container, SWT.CHECK); GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( addPageNumbers ); addPageNumbers.setText("Add page &numbers"); addPageNumbers.setSelection(exportModel.addPageNumbers); addPageNumbers.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { exportModel.addPageNumbers = addPageNumbers.getSelection(); } }); setControl(container); validatePage(); scheduleInitializeData(exportModel.selection); } private void scheduleInitializeData(final NamedResource modelSelection) { display.asyncExec(() -> { try { if (!nodeTree.isDisposed()) initializeData(modelSelection); } catch (DatabaseException | InterruptedException e) { LOGGER.error("Input data initialization failed.", e); } catch (InvocationTargetException e) { LOGGER.error("Input data initialization failed.", e.getTargetException()); } }); } private NamedResource getSelectedModel() { int sel = modelSelector.getSelectionIndex(); if (sel != -1) { NamedResource nr = (NamedResource) modelSelector.getData("" + sel); return nr; } return null; } private void setExportLocationWithoutNotification(String text) { exportLocation.removeModifyListener(exportLocationListener); exportLocation.setText(text); exportLocation.addModifyListener(exportLocationListener); } private void initializeData(final NamedResource modelSelection) throws DatabaseException, InvocationTargetException, InterruptedException { Set toBeSelected = new HashSet<>(); if (modelSelection != null) { // Process input selection to find the model/state selected by default. // This may take longer than the user wants to wait without // notification. // !PROFILE long time = System.nanoTime(); getWizard().getContainer().run(true, true, monitor -> { try { SubMonitor mon = SubMonitor.convert(monitor, "Searching for exportable diagrams...", 100); exportModel.sessionContext.getSession().syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { CollectionResult coll = exportModel.nodes = DiagramPrinter.browse(mon.newChild(100), graph, new Resource[] { modelSelection.getResource() }); // Decide initial selection based on exportModel.initialSelection if (modelSelection.equals(exportModel.initialModelSelection)) { Set selectedResources = new HashSet<>(); for (Object o : exportModel.initialSelection.toList()) { Resource r = ResourceAdaptionUtils.toSingleResource(o); if (r != null) selectedResources.add(r); } coll.walkTree(node -> { if (node.getDiagramResource() != null) { if (Nodes.parentIsInSet(toBeSelected, node)) toBeSelected.add(node); else for (Resource r : node.getDefiningResources()) if (selectedResources.contains(r)) toBeSelected.add(node); } return true; }); } // Filter out any excess nodes from the tree. exportModel.nodes = coll = coll.withRoots(Nodes.depthFirstFilter(Nodes.DIAGRAM_RESOURCE_PREDICATE, coll.roots)); // Select all if initial selection doesn't dictate anything. if (toBeSelected.isEmpty()) toBeSelected.addAll(coll.breadthFirstFlatten(CollectionResult.DIAGRAM_RESOURCE_FILTER)); } }); } catch (DatabaseException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } }); // !PROFILE long endTime = System.nanoTime(); if (exportModel.nodes != null) LOGGER.info("Found " + exportModel.nodes.diagrams.size() + " diagrams in " + ((endTime - time)*1e-9) + " seconds."); } // Browsing was canceled by user. if (exportModel.nodes == null) return; // Setup selected states, select everything by default. selectedNodes.clear(); selectedNodes.addAll(toBeSelected); // Fully refresh node tree nodeTree.setInput(exportModel.nodes); modelSelector.removeSelectionListener(modelSelectorListener); int selectedIndex = -1; for (int i = 0; i < modelSelector.getItemCount(); ++i) { Object obj = modelSelector.getData("" + i); if (org.simantics.utils.ObjectUtils.objectEquals(obj, modelSelection)) { selectedIndex = i; } } if (selectedIndex == -1 && modelSelector.getItemCount() > 0) selectedIndex = 0; if (selectedIndex != -1) modelSelector.select(selectedIndex); modelSelector.addSelectionListener(modelSelectorListener); validatePage(); } void validatePage() { int diagramCount = 0; Node singleDiagram = null; for (Node n : selectedNodes) if (n.getDiagramResource() != null) { ++diagramCount; singleDiagram = n; } //System.out.println("VALIDATE PAGE: " + exportLocationTouchedByUser); if (diagramCount == 0) { setMessage("Select the diagrams to export."); setErrorMessage(null); setPageComplete(false); return; } if (!exportLocationTouchedByUser) { String generatedName = null; // Generate file name automatically if user hasn't touched the name manually. NamedResource nr = getSelectedModel(); if (nr != null) { if (diagramCount == 1 && singleDiagram != null) { generatedName = nr.getName() + "-" + singleDiagram.getName(); } else { generatedName = nr.getName(); } } //System.out.println("generate name: " + generatedName); if (generatedName != null) { if (!FileUtils.isValidFileName(generatedName)) generatedName = (String) Bindings.STR_VARIANT.createUnchecked(Bindings.STRING, generatedName); String name = generatedName + ".pdf"; abu: if ( !exportModel.recentLocations.isEmpty() ) { for ( String loc : exportModel.recentLocations ) { if ( loc.endsWith(name) && !loc.equals(name) ) { name = loc; break abu; } } String firstLine = exportModel.recentLocations.iterator().next(); File f = new File(firstLine); File parentFile = f.getParentFile(); if ( parentFile!=null ) { name = new File( f.getParentFile(), name ).getAbsolutePath(); } } setExportLocationWithoutNotification(name); } } String exportLoc = exportLocation.getText(); if (exportLoc.isEmpty()) { setMessage("Select an export target file."); setErrorMessage(null); setPageComplete(false); return; } File file = new File(exportLoc); if (file.exists()) { if (file.isDirectory()) { setErrorMessage("The target already exists and it is a directory."); setPageComplete(false); return; } if (!file.isFile()) { setErrorMessage("The target already exists and it is a not a regular file."); setPageComplete(false); return; } } exportModel.exportLocation = file; String msg = diagramCount + " diagrams selected for export."; setMessage(msg); setErrorMessage(null); setPageComplete(true); } }