]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/PDFExportPage.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / pdf / PDFExportPage.java
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/PDFExportPage.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/PDFExportPage.java
new file mode 100644 (file)
index 0000000..ac6dd5d
--- /dev/null
@@ -0,0 +1,716 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.modeling.ui.pdf;\r
+\r
+import java.io.File;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.util.ArrayDeque;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Deque;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.eclipse.core.runtime.IPath;\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.Path;\r
+import org.eclipse.core.runtime.SubMonitor;\r
+import org.eclipse.jface.layout.GridDataFactory;\r
+import org.eclipse.jface.operation.IRunnableWithProgress;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.jface.viewers.CellLabelProvider;\r
+import org.eclipse.jface.viewers.CheckStateChangedEvent;\r
+import org.eclipse.jface.viewers.CheckboxTreeViewer;\r
+import org.eclipse.jface.viewers.ICheckStateListener;\r
+import org.eclipse.jface.viewers.ICheckStateProvider;\r
+import org.eclipse.jface.viewers.ITreeContentProvider;\r
+import org.eclipse.jface.viewers.StructuredSelection;\r
+import org.eclipse.jface.viewers.TreeViewer;\r
+import org.eclipse.jface.viewers.Viewer;\r
+import org.eclipse.jface.viewers.ViewerCell;\r
+import org.eclipse.jface.viewers.ViewerComparator;\r
+import org.eclipse.jface.viewers.ViewerFilter;\r
+import org.eclipse.jface.wizard.WizardPage;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.custom.CCombo;\r
+import org.eclipse.swt.events.DisposeEvent;\r
+import org.eclipse.swt.events.DisposeListener;\r
+import org.eclipse.swt.events.ModifyEvent;\r
+import org.eclipse.swt.events.ModifyListener;\r
+import org.eclipse.swt.events.SelectionAdapter;\r
+import org.eclipse.swt.events.SelectionEvent;\r
+import org.eclipse.swt.events.SelectionListener;\r
+import org.eclipse.swt.graphics.Color;\r
+import org.eclipse.swt.layout.GridData;\r
+import org.eclipse.swt.layout.GridLayout;\r
+import org.eclipse.swt.layout.RowLayout;\r
+import org.eclipse.swt.widgets.Button;\r
+import org.eclipse.swt.widgets.Combo;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.FileDialog;\r
+import org.eclipse.swt.widgets.Label;\r
+import org.eclipse.swt.widgets.Text;\r
+import org.eclipse.swt.widgets.TreeItem;\r
+import org.simantics.browsing.ui.common.views.DefaultFilterStrategy;\r
+import org.simantics.browsing.ui.common.views.IFilterStrategy;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.NamedResource;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.modeling.requests.Node;\r
+import org.simantics.utils.FileUtils;\r
+import org.simantics.utils.strings.AlphanumComparator;\r
+import org.simantics.utils.ui.ISelectionUtils;\r
+\r
+public class PDFExportPage extends WizardPage {\r
+\r
+       protected Display              display;\r
+\r
+    protected PDFExportPlan       exportModel;\r
+\r
+    protected IFilterStrategy      filterStrategy     = new DefaultFilterStrategy();\r
+\r
+    protected Combo                modelSelector;\r
+    protected SelectionListener    modelSelectorListener;\r
+\r
+    protected Text                 filter;\r
+\r
+    protected Matcher              matcher            = null;\r
+\r
+    protected CheckboxTreeViewer   tree;\r
+\r
+    protected CCombo               exportLocation;\r
+    protected ModifyListener       exportLocationListener;\r
+\r
+    protected Set<Node>            selectedNodes;\r
+\r
+    protected LocalResourceManager resourceManager;\r
+    protected Color                noDiagramColor;\r
+    \r
+    protected Label                               toFileLabel;\r
+\r
+    protected boolean              exportLocationTouchedByUser = false;\r
+\r
+    ICheckStateProvider checkStateProvider = new ICheckStateProvider() {\r
+        @Override\r
+        public boolean isChecked(Object element) {\r
+            Node node = (Node) element;\r
+\r
+            // Primarily checked if any children are selected.\r
+            Collection<Node> children = node.getChildren();\r
+            if (!children.isEmpty()) {\r
+                for (Node child : node.getChildren())\r
+                    if (isChecked(child))\r
+                        return true;\r
+\r
+                // No children are checked, not checked.\r
+                return false;\r
+            }\r
+\r
+            // Otherwise checked only if selected.\r
+            return selectedNodes.contains(node);\r
+        }\r
+        @Override\r
+        public boolean isGrayed(Object element) {\r
+            Node node = (Node) element;\r
+\r
+            // Grayed if there are children but not all of them are selected.\r
+            Collection<Node> children = node.getChildren();\r
+            if (!children.isEmpty()) {\r
+                for (Node child : children)\r
+                    if (!selectedNodes.contains(child))\r
+                        return true;\r
+            }\r
+\r
+            // Grayed if the node itself contains no diagram.\r
+            if (node.getDiagramResource() == null)\r
+                return true;\r
+\r
+            // Otherwise never grayed.\r
+            return false;\r
+        }\r
+    };\r
+\r
+    protected PDFExportPage(PDFExportPlan model) {\r
+        super("Export Diagrams to PDF", "Define Exported Items", null);\r
+        this.exportModel = model;\r
+        this.selectedNodes = exportModel.selectedNodeSet;\r
+    }\r
+\r
+    @Override\r
+    public void createControl(Composite parent) {\r
+        this.display = parent.getDisplay();\r
+\r
+        Composite container = new Composite(parent, SWT.NONE);\r
+        {\r
+            GridLayout layout = new GridLayout();\r
+            layout.horizontalSpacing = 20;\r
+            layout.verticalSpacing = 10;\r
+            layout.numColumns = 3;\r
+            container.setLayout(layout);\r
+        }\r
+        resourceManager = new LocalResourceManager(JFaceResources.getResources());\r
+        container.addDisposeListener(new DisposeListener() {\r
+            @Override\r
+            public void widgetDisposed(DisposeEvent e) {\r
+                resourceManager.dispose();\r
+            }\r
+        });\r
+        noDiagramColor = container.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);\r
+\r
+        Label modelSelectorLabel = new Label(container, SWT.NONE);\r
+        modelSelectorLabel.setText("Model Selector:");\r
+        GridDataFactory.fillDefaults().span(1, 1).applyTo(modelSelectorLabel);\r
+        modelSelector = new Combo(container, SWT.BORDER | SWT.READ_ONLY);\r
+        GridDataFactory.fillDefaults().span(2, 1).applyTo(modelSelector);\r
+        modelSelectorListener = new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                NamedResource data = (NamedResource) modelSelector.getData(String.valueOf(modelSelector.getSelectionIndex()));\r
+                scheduleInitializeData(data);\r
+            }\r
+        };\r
+\r
+        // Fill model selector combo\r
+        for (int i = 0; i < exportModel.selectableModels.size(); ++i) {\r
+            NamedResource nr = exportModel.selectableModels.get(i);\r
+            modelSelector.add(nr.getName());\r
+            modelSelector.setData("" + i, nr);\r
+        }\r
+\r
+        modelSelector.addSelectionListener(modelSelectorListener);\r
+\r
+//        Label label = new Label(container, SWT.NONE);\r
+//        label.setText("Diagrams to Export:");\r
+//        GridDataFactory.fillDefaults().span(3, 1).applyTo(label);\r
+\r
+        Label filterLabel = new Label(container, SWT.NONE);\r
+        filterLabel.setText("Fi&lter:");\r
+        GridDataFactory.fillDefaults().span(1, 1).applyTo(filterLabel);\r
+        filter = new Text(container, SWT.BORDER);\r
+        GridDataFactory.fillDefaults().span(2, 1).applyTo(filter);\r
+        filter.addModifyListener(new ModifyListener() {\r
+            @Override\r
+            public void modifyText(ModifyEvent e) {\r
+                resetFilterString(filter.getText());\r
+            }\r
+        });\r
+\r
+        tree = new CheckboxTreeViewer(container, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);\r
+        {\r
+            tree.setUseHashlookup(true);\r
+            GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(tree.getControl());\r
+            tree.getControl().setToolTipText("Selects the diagram to include in the exported document.");\r
+            tree.setAutoExpandLevel(TreeViewer.ALL_LEVELS);\r
+            tree.addCheckStateListener(new ICheckStateListener(){\r
+                void addOrRemoveSelection(Node node, boolean add) {\r
+                    if (add)\r
+                        selectedNodes.add(node);\r
+                    else\r
+                        selectedNodes.remove(node);\r
+                }\r
+                void addOrRemoveSelectionRec(Node node, boolean add) {\r
+                    addOrRemoveSelection(node, add);\r
+                    for (Node child : node.getChildren())\r
+                        addOrRemoveSelectionRec(child, add);\r
+                }\r
+                @Override\r
+                public void checkStateChanged(CheckStateChangedEvent event) {\r
+                    final boolean checked = event.getChecked();\r
+                    Node checkedNode = (Node) event.getElement();\r
+\r
+                    Set<Node> nodes = new HashSet<Node>();\r
+                    Set<Node> selection = ISelectionUtils.filterSetSelection(tree.getSelection(), Node.class);\r
+                    if (selection.contains(checkedNode))\r
+                        nodes.addAll(selection);\r
+                    else\r
+                        tree.setSelection(StructuredSelection.EMPTY);\r
+                    nodes.add(checkedNode);\r
+\r
+                    for (Node node : nodes) {\r
+                        addOrRemoveSelectionRec(node, checked);\r
+\r
+//                        tree.setSubtreeChecked(node, checked);\r
+//                         The checked node is always either checked or not checked, never grayed.\r
+//                        tree.setGrayed(node, checkStateProvider.isGrayed(node));\r
+\r
+//                        Node parent = node.getParent();\r
+//                        if (parent != null) {\r
+//                            tree.setChecked(parent, checkStateProvider.isChecked(parent));\r
+//                            tree.setGrayed(parent, checkStateProvider.isGrayed(parent));\r
+//                        }\r
+                    }\r
+\r
+                    refreshAndExpandTree();\r
+                    validatePage();\r
+                }\r
+            });\r
+\r
+            tree.setContentProvider(new ITreeContentProvider(){\r
+                @Override\r
+                public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
+                }\r
+                @Override\r
+                public void dispose() {\r
+                }\r
+                @Override\r
+                public Object[] getElements(Object inputElement) {\r
+                    return exportModel.nodes.roots.toArray();\r
+                }\r
+                @Override\r
+                public boolean hasChildren(Object element) {\r
+                    Node n = (Node) element;\r
+                    if (n.getChildren().isEmpty()) return false;\r
+                       for (Node c : n.getChildren()) if (hasDiagram(c)) return true; \r
+                    return false;\r
+                    \r
+                }\r
+                @Override\r
+                public Object getParent(Object element) {\r
+                    Node n = (Node) element;\r
+                    return n.getParent();\r
+                }\r
+                @Override\r
+                public Object[] getChildren(Object parentElement) {\r
+                    Node n = (Node) parentElement;\r
+                       List<Object> result = new ArrayList<Object>( n.getChildren().size() );\r
+                       for (Node c : n.getChildren()) \r
+                               if (hasDiagram(c)) \r
+                                       result.add(c);\r
+                    return result.toArray();\r
+                }\r
+                \r
+                boolean hasDiagram(Node n)\r
+                {                      \r
+                       if (n.getDiagramResource()!=null) return true;\r
+                       for (Node c : n.getChildren()) if (hasDiagram(c)) return true;\r
+                       return false;\r
+                }\r
+            });\r
+            tree.setLabelProvider(new CellLabelProvider() {\r
+                @Override\r
+                public void update(ViewerCell cell) {\r
+                    Object e = cell.getElement();\r
+                    if (e instanceof Node) {\r
+                        Node n = (Node) e;\r
+                        String name = DiagramPrinter.formDiagramName(n, false);\r
+                        cell.setText(name);\r
+\r
+                        if (n.getDiagramResource() == null)\r
+                            cell.setForeground(noDiagramColor);\r
+                        else\r
+                            cell.setForeground(null);\r
+                    } else {\r
+                        cell.setText("invalid input: " + e.getClass().getSimpleName());\r
+                    }\r
+                }\r
+            });\r
+            tree.setComparator(new ViewerComparator(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR));\r
+            tree.setFilters(new ViewerFilter[] {\r
+                    new ViewerFilter() {\r
+                        @Override\r
+                        public boolean select(Viewer viewer, Object parentElement, Object element) {\r
+                            if (matcher == null)\r
+                                return true;\r
+\r
+                            Node node = (Node) element;\r
+                            // If any children are in sight, show this element.\r
+                            for (Node child : node.getChildren()) {\r
+                                if (select(viewer, element, child))\r
+                                    return true;\r
+                            }\r
+\r
+                            return matcher.reset(node.getName().toLowerCase()).matches();\r
+                        }\r
+                    }\r
+            });\r
+            tree.setCheckStateProvider(checkStateProvider);\r
+        }\r
+\r
+        Composite bar = new Composite(container, SWT.NONE);\r
+        GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(bar);\r
+        bar.setLayout(new RowLayout());\r
+        Button selectAll = new Button(bar, SWT.PUSH);\r
+        selectAll.setText("Select &All");\r
+        selectAll.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                selectedNodes.addAll(exportModel.nodes.breadthFirstFlatten());\r
+                for (Node root : exportModel.nodes.roots)\r
+                    tree.setSubtreeChecked(root, true);\r
+                validatePage();\r
+            }\r
+        });\r
+        Button clearSelection = new Button(bar, SWT.PUSH);\r
+        clearSelection.setText("&Clear Selection");\r
+        clearSelection.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                selectedNodes.clear();\r
+                for (Node root : exportModel.nodes.roots)\r
+                    tree.setSubtreeChecked(root, false);\r
+                validatePage();\r
+            }\r
+        });\r
+        Button selectVisible = new Button(bar, SWT.PUSH);\r
+        selectVisible.setText("&Select Visible");\r
+        selectVisible.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                selectedNodes.addAll(getVisibleNodes());\r
+                refreshAndExpandTree();\r
+                validatePage();\r
+            }\r
+        });\r
+        Button deselectVisible = new Button(bar, SWT.PUSH);\r
+        deselectVisible.setText("&Deselect Visible");\r
+        deselectVisible.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                selectedNodes.removeAll(getVisibleNodes());\r
+                refreshAndExpandTree();\r
+                validatePage();\r
+            }\r
+        });\r
+\r
+        toFileLabel = new Label(container, SWT.NONE);\r
+        toFileLabel.setText("&To file:");\r
+        exportLocation = new CCombo(container, SWT.BORDER);\r
+        {\r
+            exportLocation.setText("");\r
+            GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(exportLocation);\r
+\r
+            for (String path : exportModel.recentLocations) {\r
+                exportLocation.add(path);\r
+            }\r
+\r
+            exportLocationListener = new ModifyListener() {\r
+                @Override\r
+                public void modifyText(ModifyEvent e) {\r
+                    System.out.println("export location changed by user");\r
+                    exportLocationTouchedByUser = true;\r
+                    validatePage();\r
+                }\r
+            };\r
+            exportLocation.addModifyListener(exportLocationListener);\r
+        }\r
+        Button browseFileButton = new Button(container, SWT.PUSH);\r
+        {\r
+            browseFileButton.setText("Browse...");\r
+            browseFileButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));\r
+            browseFileButton.addSelectionListener(new SelectionAdapter() {\r
+                @Override\r
+                public void widgetSelected(SelectionEvent e) {\r
+                    FileDialog dialog = new FileDialog(getShell(), SWT.SAVE);\r
+                    dialog.setFilterExtensions(new String[] { "*.pdf" });\r
+                    dialog.setFilterNames(new String[] { "PDF Document" });\r
+                    String loc = exportLocation.getText();\r
+                    if (loc != null) {\r
+                        IPath p = new Path(loc);\r
+                        File f = p.toFile();\r
+                        if (f.isDirectory()) {\r
+                            dialog.setFilterPath(f.toString());\r
+                        } else if (f.isFile()) {\r
+                            IPath path = p.removeLastSegments(1);\r
+                            String name = p.lastSegment();\r
+                            dialog.setFilterPath(path.toOSString());\r
+                            dialog.setFileName(name);\r
+                        } else {\r
+                            dialog.setFilterPath(f.toString());\r
+                            IPath path = p.removeLastSegments(1);\r
+                            String name = p.lastSegment();\r
+                            f = path.toFile();\r
+                            if (f.isDirectory()) {\r
+                                dialog.setFilterPath(path.toOSString());\r
+                            }\r
+                            dialog.setFileName(name);\r
+                        }\r
+                    }\r
+                    String file = dialog.open();\r
+                    if (file == null)\r
+                        return;\r
+                    exportLocation.setText(file);\r
+                    validatePage();\r
+                }\r
+            });\r
+        }\r
+\r
+        final Button zoomToFitButton = new Button(container, SWT.CHECK);\r
+        GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(zoomToFitButton);\r
+        zoomToFitButton.setText("F&it by content");\r
+        zoomToFitButton.setSelection(exportModel.fitContentToPageMargins);\r
+        zoomToFitButton.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                exportModel.fitContentToPageMargins = zoomToFitButton.getSelection();\r
+            }\r
+        });\r
+\r
+        /*\r
+        final Button attachTGButton = new Button(container, SWT.CHECK);\r
+        GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( attachTGButton );\r
+        attachTGButton.setText("Attach &TG (Importable diagram)");\r
+        attachTGButton.setSelection(exportModel.attachTG);\r
+        attachTGButton.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                exportModel.attachTG = attachTGButton.getSelection();\r
+            }\r
+        });\r
+        */\r
+        \r
+        final Button attachWikiButton = new Button(container, SWT.CHECK);\r
+        GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo( attachWikiButton );\r
+        attachWikiButton.setText("Attach &Wiki page");\r
+        attachWikiButton.setSelection(exportModel.attachWiki);\r
+        attachWikiButton.addSelectionListener(new SelectionAdapter() {\r
+            @Override\r
+            public void widgetSelected(SelectionEvent e) {\r
+                exportModel.attachWiki = attachWikiButton.getSelection();\r
+            }\r
+        });\r
+        \r
+        setControl(container);\r
+        validatePage();\r
+\r
+        scheduleInitializeData(exportModel.selection);\r
+    }\r
+\r
+    private void scheduleInitializeData(final NamedResource modelSelection) {\r
+        display.asyncExec(new Runnable() {\r
+            @Override\r
+            public void run() {\r
+                if (filter.isDisposed())\r
+                    return;\r
+\r
+                try {\r
+                    initializeData(modelSelection);\r
+                } catch (DatabaseException e) {\r
+                    e.printStackTrace();\r
+                } catch (InvocationTargetException e) {\r
+                    e.getTargetException().printStackTrace();\r
+                } catch (InterruptedException e) {\r
+                    e.printStackTrace();\r
+                }\r
+            }\r
+        });\r
+    }\r
+\r
+    private NamedResource getSelectedModel() {\r
+        int sel = modelSelector.getSelectionIndex();\r
+        if (sel != -1) {\r
+            NamedResource nr = (NamedResource) modelSelector.getData("" + sel);\r
+            return nr;\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private void setExportLocationWithoutNotification(String text) {\r
+        exportLocation.removeModifyListener(exportLocationListener);\r
+        exportLocation.setText(text);\r
+        exportLocation.addModifyListener(exportLocationListener);\r
+    }\r
+\r
+    private Collection<Node> getVisibleNodes() {\r
+        Collection<Node> result = new ArrayList<Node>();\r
+\r
+        Deque<TreeItem> todo = new ArrayDeque<TreeItem>();\r
+        for (TreeItem ti : tree.getTree().getItems()) {\r
+            todo.add(ti);\r
+        }\r
+\r
+        while (!todo.isEmpty()) {\r
+            TreeItem item = todo.removeLast();\r
+            Node node = (Node) item.getData();\r
+            result.add(node);\r
+\r
+            for (TreeItem child : item.getItems()) {\r
+                todo.add(child);\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    private void resetFilterString(String filterString) {\r
+        String patternString = filterStrategy.toPatternString(filterString);\r
+        if (patternString == null) {\r
+            matcher = null;\r
+        } else {\r
+            matcher = Pattern.compile(patternString).matcher("");\r
+        }\r
+        refreshAndExpandTree();\r
+    }\r
+\r
+    private void refreshAndExpandTree() {\r
+        tree.refresh();\r
+        tree.expandAll();\r
+    }\r
+\r
+    private void initializeData(final NamedResource modelSelection) throws DatabaseException, InvocationTargetException, InterruptedException {\r
+        if (modelSelection != null) {\r
+            // Process input selection to find the model/state selected by default.\r
+\r
+            // This may take longer than the user wants to wait without\r
+            // notification.\r
+\r
+            // !PROFILE\r
+            long time = System.nanoTime();\r
+\r
+            getWizard().getContainer().run(true, true, new IRunnableWithProgress() {\r
+                @Override\r
+                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {\r
+                    try {\r
+                        final SubMonitor mon = SubMonitor.convert(monitor, "Searching for exportable diagrams...", 100);\r
+                        exportModel.sessionContext.getSession().syncRequest(new ReadRequest() {\r
+                            @Override\r
+                            public void run(ReadGraph graph) throws DatabaseException {\r
+                                exportModel.nodes = DiagramPrinter.browse(mon.newChild(100), graph, new Resource[] { modelSelection.getResource() });\r
+                            }\r
+                        });\r
+                    } catch (DatabaseException e) {\r
+                        throw new InvocationTargetException(e);\r
+                    } finally {\r
+                        monitor.done();\r
+                    }\r
+                }\r
+            });\r
+\r
+            // !PROFILE\r
+            long endTime = System.nanoTime();\r
+            if (exportModel.nodes != null)\r
+                System.out.println("Found " + exportModel.nodes.diagrams.size() + " diagrams in " + ((endTime - time)*1e-9) + " seconds.");\r
+        }\r
+\r
+        // Browsing was canceled by user.\r
+        if (exportModel.nodes == null)\r
+            return;\r
+\r
+        // Setup selected states, select everything by default.\r
+        selectedNodes.clear();\r
+        selectedNodes.addAll(exportModel.nodes.breadthFirstFlatten());\r
+\r
+        tree.setInput(this);\r
+\r
+        for (Node root : exportModel.nodes.roots) {\r
+            tree.setSubtreeChecked(root, true);\r
+        }\r
+\r
+        resetFilterString(filter.getText());\r
+\r
+        modelSelector.removeSelectionListener(modelSelectorListener);\r
+        int selectedIndex = -1;\r
+        for (int i = 0; i < modelSelector.getItemCount(); ++i) {\r
+            Object obj = modelSelector.getData("" + i);\r
+            if (org.simantics.utils.ObjectUtils.objectEquals(obj, modelSelection)) {\r
+                selectedIndex = i;\r
+            }\r
+        }\r
+        if (selectedIndex == -1 && modelSelector.getItemCount() > 0)\r
+            selectedIndex = 0;\r
+        if (selectedIndex != -1)\r
+            modelSelector.select(selectedIndex);\r
+        modelSelector.addSelectionListener(modelSelectorListener);\r
+\r
+        validatePage();\r
+    }\r
+\r
+    void validatePage() {\r
+        //System.out.println("VALIDATE PAGE: " + exportLocationTouchedByUser);\r
+        if (selectedNodes.size() == 0) {\r
+            setMessage("Select the diagrams to export.");\r
+            setErrorMessage(null);\r
+            setPageComplete(false);\r
+            return;\r
+        }\r
+\r
+        if (!exportLocationTouchedByUser) {\r
+            String generatedName = null;\r
+            // Generate file name automatically if user hasn't touched the name manually.\r
+            NamedResource nr = getSelectedModel();\r
+            if (nr != null) {\r
+                if (selectedNodes.size() == 1) {\r
+                    generatedName = nr.getName() + "-" + selectedNodes.iterator().next().getName();\r
+                } else {\r
+                    generatedName = nr.getName();\r
+                }\r
+            }\r
+            //System.out.println("generate name: " + generatedName);\r
+            if (generatedName != null) {\r
+                if (!FileUtils.isValidFileName(generatedName))\r
+                    generatedName = (String) Bindings.STR_VARIANT.createUnchecked(Bindings.STRING, generatedName);\r
+                String name = generatedName + ".pdf";\r
+                \r
+                abu:\r
+                if ( !exportModel.recentLocations.isEmpty() ) {\r
+                       \r
+                       for ( String loc : exportModel.recentLocations )\r
+                       {\r
+                               if ( loc.endsWith(name) && !loc.equals(name) ) {\r
+                                       name = loc;\r
+                                       break abu; \r
+                               }\r
+                       }\r
+                       \r
+                       String firstLine = exportModel.recentLocations.iterator().next();\r
+                       File f = new File(firstLine);    \r
+                       File parentFile = f.getParentFile();\r
+                       if ( parentFile!=null ) {\r
+                               name = new File( f.getParentFile(), name ).getAbsolutePath();\r
+                       }\r
+                }\r
+                setExportLocationWithoutNotification(name);\r
+            }\r
+        }\r
+\r
+        String exportLoc = exportLocation.getText();\r
+        if (exportLoc.isEmpty()) {\r
+            setMessage("Select an export target file.");\r
+            setErrorMessage(null);\r
+            setPageComplete(false);\r
+            return;\r
+        }\r
+        File file = new File(exportLoc);\r
+        if (file.exists()) {\r
+            if (file.isDirectory()) {\r
+                setErrorMessage("The target already exists and it is a directory.");\r
+                setPageComplete(false);\r
+                return;\r
+            }\r
+            if (!file.isFile()) {\r
+                setErrorMessage("The target already exists and it is a not a regular file.");\r
+                setPageComplete(false);\r
+                return;\r
+            }\r
+        }\r
+        exportModel.exportLocation = file;\r
+\r
+        int diagramCount = 0;\r
+        for (Node n : selectedNodes)\r
+            if (n.getDiagramResource() != null)\r
+                ++diagramCount;\r
+\r
+        String msg = diagramCount + " diagrams selected for export.";\r
+\r
+        setMessage(msg);\r
+        setErrorMessage(null);\r
+        setPageComplete(true);\r
+    }\r
+\r
+}\r