X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2Fpdf%2FPDFExportPage.java;fp=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2Fpdf%2FPDFExportPage.java;h=7ef2efb9406f283b578f6bfdcabc7669d91e997a;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=8920decfed1b11f768f3efc259c79c7107c1b221;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git 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 index 8920decfe..7ef2efb94 100644 --- 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 @@ -1,716 +1,716 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 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 - *******************************************************************************/ -package org.simantics.modeling.ui.pdf; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Deque; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.SubMonitor; -import org.eclipse.jface.layout.GridDataFactory; -import org.eclipse.jface.operation.IRunnableWithProgress; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.jface.viewers.CellLabelProvider; -import org.eclipse.jface.viewers.CheckStateChangedEvent; -import org.eclipse.jface.viewers.CheckboxTreeViewer; -import org.eclipse.jface.viewers.ICheckStateListener; -import org.eclipse.jface.viewers.ICheckStateProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerCell; -import org.eclipse.jface.viewers.ViewerComparator; -import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.CCombo; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -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.graphics.Color; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -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.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.TreeItem; -import org.simantics.browsing.ui.common.views.DefaultFilterStrategy; -import org.simantics.browsing.ui.common.views.IFilterStrategy; -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.Node; -import org.simantics.utils.FileUtils; -import org.simantics.utils.strings.AlphanumComparator; -import org.simantics.utils.ui.ISelectionUtils; - -public class PDFExportPage extends WizardPage { - - protected Display display; - - protected PDFExportPlan exportModel; - - protected IFilterStrategy filterStrategy = new DefaultFilterStrategy(); - - protected Combo modelSelector; - protected SelectionListener modelSelectorListener; - - protected Text filter; - - protected Matcher matcher = null; - - protected CheckboxTreeViewer tree; - - protected CCombo exportLocation; - protected ModifyListener exportLocationListener; - - protected Set selectedNodes; - - protected LocalResourceManager resourceManager; - protected Color noDiagramColor; - - protected Label toFileLabel; - - protected boolean exportLocationTouchedByUser = false; - - ICheckStateProvider checkStateProvider = new ICheckStateProvider() { - @Override - public boolean isChecked(Object element) { - Node node = (Node) element; - - // Primarily checked if any children are selected. - Collection children = node.getChildren(); - if (!children.isEmpty()) { - for (Node child : node.getChildren()) - if (isChecked(child)) - return true; - - // No children are checked, not checked. - return false; - } - - // Otherwise checked only if selected. - return selectedNodes.contains(node); - } - @Override - public boolean isGrayed(Object element) { - Node node = (Node) element; - - // Grayed if there are children but not all of them are selected. - Collection children = node.getChildren(); - if (!children.isEmpty()) { - for (Node child : children) - if (!selectedNodes.contains(child)) - return true; - } - - // Grayed if the node itself contains no diagram. - if (node.getDiagramResource() == null) - return true; - - // Otherwise never grayed. - return 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); - } - resourceManager = new LocalResourceManager(JFaceResources.getResources()); - container.addDisposeListener(new DisposeListener() { - @Override - public void widgetDisposed(DisposeEvent e) { - resourceManager.dispose(); - } - }); - noDiagramColor = container.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY); - - 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); - -// Label label = new Label(container, SWT.NONE); -// label.setText("Diagrams to Export:"); -// GridDataFactory.fillDefaults().span(3, 1).applyTo(label); - - Label filterLabel = new Label(container, SWT.NONE); - filterLabel.setText("Fi<er:"); - GridDataFactory.fillDefaults().span(1, 1).applyTo(filterLabel); - filter = new Text(container, SWT.BORDER); - GridDataFactory.fillDefaults().span(2, 1).applyTo(filter); - filter.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - resetFilterString(filter.getText()); - } - }); - - tree = new CheckboxTreeViewer(container, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); - { - tree.setUseHashlookup(true); - GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(tree.getControl()); - tree.getControl().setToolTipText("Selects the diagram to include in the exported document."); - tree.setAutoExpandLevel(TreeViewer.ALL_LEVELS); - tree.addCheckStateListener(new ICheckStateListener(){ - void addOrRemoveSelection(Node node, boolean add) { - if (add) - selectedNodes.add(node); - else - selectedNodes.remove(node); - } - void addOrRemoveSelectionRec(Node node, boolean add) { - addOrRemoveSelection(node, add); - for (Node child : node.getChildren()) - addOrRemoveSelectionRec(child, add); - } - @Override - public void checkStateChanged(CheckStateChangedEvent event) { - final boolean checked = event.getChecked(); - Node checkedNode = (Node) event.getElement(); - - Set nodes = new HashSet(); - Set selection = ISelectionUtils.filterSetSelection(tree.getSelection(), Node.class); - if (selection.contains(checkedNode)) - nodes.addAll(selection); - else - tree.setSelection(StructuredSelection.EMPTY); - nodes.add(checkedNode); - - for (Node node : nodes) { - addOrRemoveSelectionRec(node, checked); - -// tree.setSubtreeChecked(node, checked); -// The checked node is always either checked or not checked, never grayed. -// tree.setGrayed(node, checkStateProvider.isGrayed(node)); - -// Node parent = node.getParent(); -// if (parent != null) { -// tree.setChecked(parent, checkStateProvider.isChecked(parent)); -// tree.setGrayed(parent, checkStateProvider.isGrayed(parent)); -// } - } - - refreshAndExpandTree(); - validatePage(); - } - }); - - tree.setContentProvider(new ITreeContentProvider(){ - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - @Override - public void dispose() { - } - @Override - public Object[] getElements(Object inputElement) { - return exportModel.nodes.roots.toArray(); - } - @Override - public boolean hasChildren(Object element) { - Node n = (Node) element; - if (n.getChildren().isEmpty()) return false; - for (Node c : n.getChildren()) if (hasDiagram(c)) return true; - return false; - - } - @Override - public Object getParent(Object element) { - Node n = (Node) element; - return n.getParent(); - } - @Override - public Object[] getChildren(Object parentElement) { - Node n = (Node) parentElement; - List result = new ArrayList( n.getChildren().size() ); - for (Node c : n.getChildren()) - if (hasDiagram(c)) - result.add(c); - return result.toArray(); - } - - boolean hasDiagram(Node n) - { - if (n.getDiagramResource()!=null) return true; - for (Node c : n.getChildren()) if (hasDiagram(c)) return true; - return false; - } - }); - tree.setLabelProvider(new CellLabelProvider() { - @Override - public void update(ViewerCell cell) { - Object e = cell.getElement(); - if (e instanceof Node) { - Node n = (Node) e; - String name = DiagramPrinter.formDiagramName(n, false); - cell.setText(name); - - if (n.getDiagramResource() == null) - cell.setForeground(noDiagramColor); - else - cell.setForeground(null); - } else { - cell.setText("invalid input: " + e.getClass().getSimpleName()); - } - } - }); - tree.setComparator(new ViewerComparator(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR)); - tree.setFilters(new ViewerFilter[] { - new ViewerFilter() { - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - if (matcher == null) - return true; - - Node node = (Node) element; - // If any children are in sight, show this element. - for (Node child : node.getChildren()) { - if (select(viewer, element, child)) - return true; - } - - return matcher.reset(node.getName().toLowerCase()).matches(); - } - } - }); - tree.setCheckStateProvider(checkStateProvider); - } - - Composite bar = new Composite(container, SWT.NONE); - GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(bar); - bar.setLayout(new RowLayout()); - Button selectAll = new Button(bar, SWT.PUSH); - selectAll.setText("Select &All"); - selectAll.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - selectedNodes.addAll(exportModel.nodes.breadthFirstFlatten()); - for (Node root : exportModel.nodes.roots) - tree.setSubtreeChecked(root, true); - validatePage(); - } - }); - Button clearSelection = new Button(bar, SWT.PUSH); - clearSelection.setText("&Clear Selection"); - clearSelection.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - selectedNodes.clear(); - for (Node root : exportModel.nodes.roots) - tree.setSubtreeChecked(root, false); - validatePage(); - } - }); - Button selectVisible = new Button(bar, SWT.PUSH); - selectVisible.setText("&Select Visible"); - selectVisible.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - selectedNodes.addAll(getVisibleNodes()); - refreshAndExpandTree(); - validatePage(); - } - }); - Button deselectVisible = new Button(bar, SWT.PUSH); - deselectVisible.setText("&Deselect Visible"); - deselectVisible.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - selectedNodes.removeAll(getVisibleNodes()); - refreshAndExpandTree(); - 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(); - } - }); - - setControl(container); - validatePage(); - - scheduleInitializeData(exportModel.selection); - } - - private void scheduleInitializeData(final NamedResource modelSelection) { - display.asyncExec(new Runnable() { - @Override - public void run() { - if (filter.isDisposed()) - return; - - try { - initializeData(modelSelection); - } catch (DatabaseException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.getTargetException().printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - } - - 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 Collection getVisibleNodes() { - Collection result = new ArrayList(); - - Deque todo = new ArrayDeque(); - for (TreeItem ti : tree.getTree().getItems()) { - todo.add(ti); - } - - while (!todo.isEmpty()) { - TreeItem item = todo.removeLast(); - Node node = (Node) item.getData(); - result.add(node); - - for (TreeItem child : item.getItems()) { - todo.add(child); - } - } - - return result; - } - - private void resetFilterString(String filterString) { - String patternString = filterStrategy.toPatternString(filterString); - if (patternString == null) { - matcher = null; - } else { - matcher = Pattern.compile(patternString).matcher(""); - } - refreshAndExpandTree(); - } - - private void refreshAndExpandTree() { - tree.refresh(); - tree.expandAll(); - } - - private void initializeData(final NamedResource modelSelection) throws DatabaseException, InvocationTargetException, InterruptedException { - 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, new IRunnableWithProgress() { - @Override - public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { - try { - final SubMonitor mon = SubMonitor.convert(monitor, "Searching for exportable diagrams...", 100); - exportModel.sessionContext.getSession().syncRequest(new ReadRequest() { - @Override - public void run(ReadGraph graph) throws DatabaseException { - exportModel.nodes = DiagramPrinter.browse(mon.newChild(100), graph, new Resource[] { modelSelection.getResource() }); - } - }); - } catch (DatabaseException e) { - throw new InvocationTargetException(e); - } finally { - monitor.done(); - } - } - }); - - // !PROFILE - long endTime = System.nanoTime(); - if (exportModel.nodes != null) - System.out.println("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(exportModel.nodes.breadthFirstFlatten()); - - tree.setInput(this); - - for (Node root : exportModel.nodes.roots) { - tree.setSubtreeChecked(root, true); - } - - resetFilterString(filter.getText()); - - 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() { - //System.out.println("VALIDATE PAGE: " + exportLocationTouchedByUser); - if (selectedNodes.size() == 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 (selectedNodes.size() == 1) { - generatedName = nr.getName() + "-" + selectedNodes.iterator().next().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; - - int diagramCount = 0; - for (Node n : selectedNodes) - if (n.getDiagramResource() != null) - ++diagramCount; - - String msg = diagramCount + " diagrams selected for export."; - - setMessage(msg); - setErrorMessage(null); - setPageComplete(true); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2010 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 + *******************************************************************************/ +package org.simantics.modeling.ui.pdf; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.viewers.CellLabelProvider; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ICheckStateProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +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.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +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.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.TreeItem; +import org.simantics.browsing.ui.common.views.DefaultFilterStrategy; +import org.simantics.browsing.ui.common.views.IFilterStrategy; +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.Node; +import org.simantics.utils.FileUtils; +import org.simantics.utils.strings.AlphanumComparator; +import org.simantics.utils.ui.ISelectionUtils; + +public class PDFExportPage extends WizardPage { + + protected Display display; + + protected PDFExportPlan exportModel; + + protected IFilterStrategy filterStrategy = new DefaultFilterStrategy(); + + protected Combo modelSelector; + protected SelectionListener modelSelectorListener; + + protected Text filter; + + protected Matcher matcher = null; + + protected CheckboxTreeViewer tree; + + protected CCombo exportLocation; + protected ModifyListener exportLocationListener; + + protected Set selectedNodes; + + protected LocalResourceManager resourceManager; + protected Color noDiagramColor; + + protected Label toFileLabel; + + protected boolean exportLocationTouchedByUser = false; + + ICheckStateProvider checkStateProvider = new ICheckStateProvider() { + @Override + public boolean isChecked(Object element) { + Node node = (Node) element; + + // Primarily checked if any children are selected. + Collection children = node.getChildren(); + if (!children.isEmpty()) { + for (Node child : node.getChildren()) + if (isChecked(child)) + return true; + + // No children are checked, not checked. + return false; + } + + // Otherwise checked only if selected. + return selectedNodes.contains(node); + } + @Override + public boolean isGrayed(Object element) { + Node node = (Node) element; + + // Grayed if there are children but not all of them are selected. + Collection children = node.getChildren(); + if (!children.isEmpty()) { + for (Node child : children) + if (!selectedNodes.contains(child)) + return true; + } + + // Grayed if the node itself contains no diagram. + if (node.getDiagramResource() == null) + return true; + + // Otherwise never grayed. + return 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); + } + resourceManager = new LocalResourceManager(JFaceResources.getResources()); + container.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + resourceManager.dispose(); + } + }); + noDiagramColor = container.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY); + + 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); + +// Label label = new Label(container, SWT.NONE); +// label.setText("Diagrams to Export:"); +// GridDataFactory.fillDefaults().span(3, 1).applyTo(label); + + Label filterLabel = new Label(container, SWT.NONE); + filterLabel.setText("Fi<er:"); + GridDataFactory.fillDefaults().span(1, 1).applyTo(filterLabel); + filter = new Text(container, SWT.BORDER); + GridDataFactory.fillDefaults().span(2, 1).applyTo(filter); + filter.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + resetFilterString(filter.getText()); + } + }); + + tree = new CheckboxTreeViewer(container, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); + { + tree.setUseHashlookup(true); + GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(tree.getControl()); + tree.getControl().setToolTipText("Selects the diagram to include in the exported document."); + tree.setAutoExpandLevel(TreeViewer.ALL_LEVELS); + tree.addCheckStateListener(new ICheckStateListener(){ + void addOrRemoveSelection(Node node, boolean add) { + if (add) + selectedNodes.add(node); + else + selectedNodes.remove(node); + } + void addOrRemoveSelectionRec(Node node, boolean add) { + addOrRemoveSelection(node, add); + for (Node child : node.getChildren()) + addOrRemoveSelectionRec(child, add); + } + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + final boolean checked = event.getChecked(); + Node checkedNode = (Node) event.getElement(); + + Set nodes = new HashSet(); + Set selection = ISelectionUtils.filterSetSelection(tree.getSelection(), Node.class); + if (selection.contains(checkedNode)) + nodes.addAll(selection); + else + tree.setSelection(StructuredSelection.EMPTY); + nodes.add(checkedNode); + + for (Node node : nodes) { + addOrRemoveSelectionRec(node, checked); + +// tree.setSubtreeChecked(node, checked); +// The checked node is always either checked or not checked, never grayed. +// tree.setGrayed(node, checkStateProvider.isGrayed(node)); + +// Node parent = node.getParent(); +// if (parent != null) { +// tree.setChecked(parent, checkStateProvider.isChecked(parent)); +// tree.setGrayed(parent, checkStateProvider.isGrayed(parent)); +// } + } + + refreshAndExpandTree(); + validatePage(); + } + }); + + tree.setContentProvider(new ITreeContentProvider(){ + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + @Override + public void dispose() { + } + @Override + public Object[] getElements(Object inputElement) { + return exportModel.nodes.roots.toArray(); + } + @Override + public boolean hasChildren(Object element) { + Node n = (Node) element; + if (n.getChildren().isEmpty()) return false; + for (Node c : n.getChildren()) if (hasDiagram(c)) return true; + return false; + + } + @Override + public Object getParent(Object element) { + Node n = (Node) element; + return n.getParent(); + } + @Override + public Object[] getChildren(Object parentElement) { + Node n = (Node) parentElement; + List result = new ArrayList( n.getChildren().size() ); + for (Node c : n.getChildren()) + if (hasDiagram(c)) + result.add(c); + return result.toArray(); + } + + boolean hasDiagram(Node n) + { + if (n.getDiagramResource()!=null) return true; + for (Node c : n.getChildren()) if (hasDiagram(c)) return true; + return false; + } + }); + tree.setLabelProvider(new CellLabelProvider() { + @Override + public void update(ViewerCell cell) { + Object e = cell.getElement(); + if (e instanceof Node) { + Node n = (Node) e; + String name = DiagramPrinter.formDiagramName(n, false); + cell.setText(name); + + if (n.getDiagramResource() == null) + cell.setForeground(noDiagramColor); + else + cell.setForeground(null); + } else { + cell.setText("invalid input: " + e.getClass().getSimpleName()); + } + } + }); + tree.setComparator(new ViewerComparator(AlphanumComparator.CASE_INSENSITIVE_COMPARATOR)); + tree.setFilters(new ViewerFilter[] { + new ViewerFilter() { + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + if (matcher == null) + return true; + + Node node = (Node) element; + // If any children are in sight, show this element. + for (Node child : node.getChildren()) { + if (select(viewer, element, child)) + return true; + } + + return matcher.reset(node.getName().toLowerCase()).matches(); + } + } + }); + tree.setCheckStateProvider(checkStateProvider); + } + + Composite bar = new Composite(container, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(bar); + bar.setLayout(new RowLayout()); + Button selectAll = new Button(bar, SWT.PUSH); + selectAll.setText("Select &All"); + selectAll.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + selectedNodes.addAll(exportModel.nodes.breadthFirstFlatten()); + for (Node root : exportModel.nodes.roots) + tree.setSubtreeChecked(root, true); + validatePage(); + } + }); + Button clearSelection = new Button(bar, SWT.PUSH); + clearSelection.setText("&Clear Selection"); + clearSelection.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + selectedNodes.clear(); + for (Node root : exportModel.nodes.roots) + tree.setSubtreeChecked(root, false); + validatePage(); + } + }); + Button selectVisible = new Button(bar, SWT.PUSH); + selectVisible.setText("&Select Visible"); + selectVisible.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + selectedNodes.addAll(getVisibleNodes()); + refreshAndExpandTree(); + validatePage(); + } + }); + Button deselectVisible = new Button(bar, SWT.PUSH); + deselectVisible.setText("&Deselect Visible"); + deselectVisible.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + selectedNodes.removeAll(getVisibleNodes()); + refreshAndExpandTree(); + 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(); + } + }); + + setControl(container); + validatePage(); + + scheduleInitializeData(exportModel.selection); + } + + private void scheduleInitializeData(final NamedResource modelSelection) { + display.asyncExec(new Runnable() { + @Override + public void run() { + if (filter.isDisposed()) + return; + + try { + initializeData(modelSelection); + } catch (DatabaseException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.getTargetException().printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + } + + 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 Collection getVisibleNodes() { + Collection result = new ArrayList(); + + Deque todo = new ArrayDeque(); + for (TreeItem ti : tree.getTree().getItems()) { + todo.add(ti); + } + + while (!todo.isEmpty()) { + TreeItem item = todo.removeLast(); + Node node = (Node) item.getData(); + result.add(node); + + for (TreeItem child : item.getItems()) { + todo.add(child); + } + } + + return result; + } + + private void resetFilterString(String filterString) { + String patternString = filterStrategy.toPatternString(filterString); + if (patternString == null) { + matcher = null; + } else { + matcher = Pattern.compile(patternString).matcher(""); + } + refreshAndExpandTree(); + } + + private void refreshAndExpandTree() { + tree.refresh(); + tree.expandAll(); + } + + private void initializeData(final NamedResource modelSelection) throws DatabaseException, InvocationTargetException, InterruptedException { + 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, new IRunnableWithProgress() { + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + try { + final SubMonitor mon = SubMonitor.convert(monitor, "Searching for exportable diagrams...", 100); + exportModel.sessionContext.getSession().syncRequest(new ReadRequest() { + @Override + public void run(ReadGraph graph) throws DatabaseException { + exportModel.nodes = DiagramPrinter.browse(mon.newChild(100), graph, new Resource[] { modelSelection.getResource() }); + } + }); + } catch (DatabaseException e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + } + }); + + // !PROFILE + long endTime = System.nanoTime(); + if (exportModel.nodes != null) + System.out.println("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(exportModel.nodes.breadthFirstFlatten()); + + tree.setInput(this); + + for (Node root : exportModel.nodes.roots) { + tree.setSubtreeChecked(root, true); + } + + resetFilterString(filter.getText()); + + 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() { + //System.out.println("VALIDATE PAGE: " + exportLocationTouchedByUser); + if (selectedNodes.size() == 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 (selectedNodes.size() == 1) { + generatedName = nr.getName() + "-" + selectedNodes.iterator().next().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; + + int diagramCount = 0; + for (Node n : selectedNodes) + if (n.getDiagramResource() != null) + ++diagramCount; + + String msg = diagramCount + " diagrams selected for export."; + + setMessage(msg); + setErrorMessage(null); + setPageComplete(true); + } + +}