/******************************************************************************* * Copyright (c) 2012 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.document.ui.function; import java.util.TreeMap; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.browser.LocationEvent; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.FontDialog; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.simantics.Simantics; import org.simantics.browsing.ui.BuiltinKeys; import org.simantics.browsing.ui.NodeContext; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.request.UnaryRead; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.Logger; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.common.utils.RequestUtil; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.Instances; import org.simantics.db.layer0.request.PossibleModel; import org.simantics.db.layer0.request.PossibleResource; import org.simantics.db.layer0.util.RemoverUtil; import org.simantics.db.layer0.variable.RVI; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.document.DocumentDialect; import org.simantics.document.DocumentResource; import org.simantics.document.DocumentUtils; import org.simantics.document.ui.Activator; import org.simantics.document.ui.CSSEditor; import org.simantics.layer0.Layer0; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.loader.ScenegraphLoaderProcess; import org.simantics.scenegraph.loader.ScenegraphLoaderUtils; import org.simantics.scenegraph.loader.ScenegraphLoaderUtils.ScenegraphPropertyReference; import org.simantics.scl.reflection.annotations.SCLValue; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.FunctionImpl1; import org.simantics.scl.runtime.function.FunctionImpl3; import org.simantics.ui.SimanticsUI; import org.simantics.ui.workbench.ResourceEditorInput2; import org.simantics.ui.workbench.action.DefaultActions; import org.simantics.utils.threads.SWTThread; import org.simantics.utils.ui.workbench.WorkbenchUtils; public class All { private static boolean createDocument(WriteGraph graph, Resource resource, Resource model) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DocumentResource DOC = DocumentResource.getInstance(graph); if(graph.hasStatement(resource, DOC.HasDocumentation)) return true; Instances query = graph.adapt(DOC.DocumentTypeBinding, Instances.class); TreeMap bindings = new TreeMap(); for(Resource binding : query.find(graph, model)) { double priority = graph.getRelatedValue2(binding, DOC.DocumentTypeBinding_priority, Bindings.DOUBLE); Resource type = graph.getSingleObject(binding, DOC.DocumentTypeBinding_HasType); if(graph.isInstanceOf(resource, type)) { bindings.put(priority, binding); } } if(bindings.isEmpty()) return false; Resource binding = bindings.lastEntry().getValue(); Resource documentType = graph.getSingleObject(binding, DOC.DocumentTypeBinding_HasDocumentType); Resource document = graph.newResource(); graph.claim(document, L0.InstanceOf, null, DOC.ScenegraphDocument); graph.claimLiteral(document, L0.HasName, "Documentation"); graph.claim(resource, DOC.HasDocumentation, document); graph.claim(document, L0.PartOf, resource); Resource scenegraph = graph.newResource(); graph.claim(scenegraph, L0.InstanceOf, null, documentType); graph.claimLiteral(scenegraph, L0.HasName, "Scenegraph"); graph.claim(scenegraph, L0.PartOf, document); graph.claim(document, DOC.ScenegraphDocument_scenegraph, scenegraph); return true; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object onCreateDocumentButton(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new FunctionImpl1() { @Override public Object apply(Object _o) { Simantics.async(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { Variable selection = ScenegraphLoaderUtils.getPossibleVariableSelection(graph, context); if(selection == null) return; final Resource input = selection.getRepresents(graph); if(input == null) return; graph.async(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { Resource model = graph.sync(new PossibleModel(input)); if(model == null) return; createDocument(graph, input, model); } }); } }); return true; } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object onDeleteDocumentButton(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new FunctionImpl1() { @Override public Object apply(Object _o) { Simantics.async(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { Variable selection = ScenegraphLoaderUtils.getPossibleVariableSelection(graph, context); if(selection == null) return; final Resource input = selection.getRepresents(graph); if(input == null) return; DocumentResource DOC = DocumentResource.getInstance(graph); if(!graph.hasStatement(input, DOC.HasDocumentation)) return; graph.async(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { DocumentResource DOC = DocumentResource.getInstance(graph); Resource document = graph.getPossibleObject(input, DOC.HasDocumentation); if(document == null) return; RemoverUtil.remove(graph, document); } }); } }); return true; } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> a") public static Object editorLoaded(ReadGraph graph, final Resource resource, final Variable context) throws DatabaseException { return new FunctionImpl3() { @Override public Boolean apply(WriteGraph graph, Variable editor, Variable input) { // try { // return createDocument(graph, input); // } catch (DatabaseException e) { // Logger.defaultLogError(e); // } return true; } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> Boolean") public static Boolean hasDocument(ReadGraph graph, final Resource resource, final Variable context) throws DatabaseException { DocumentResource DOC = DocumentResource.getInstance(graph); Variable selection = ScenegraphLoaderUtils.getPossibleVariableSelection(graph, context); if(selection == null) return false; Resource input = selection.getRepresents(graph); if(input == null) return null; return graph.hasStatement(input, DOC.HasDocumentation); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> a") public static Object viewInputChanged(ReadGraph graph, final Resource resource, final Variable context) throws DatabaseException { return new FunctionImpl1() { @Override public Boolean apply(final Variable viewVariable) { return true; } }; } @SCLValue(type = "ReadGraph -> Variable -> Boolean") public static Boolean isWikitext(ReadGraph graph, Variable context) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DocumentResource DOC = DocumentResource.getInstance(graph); Resource p = context.getPossiblePredicateResource(graph); if (p == null) return Boolean.FALSE; Resource range = graph.getPossibleObject(p, L0.HasRange); return DOC.WikiDocument_WikiText.equals(range); } private static Variable getDefaultSelection(ReadGraph graph, Variable context) throws DatabaseException { DocumentResource DOC = DocumentResource.getInstance(graph); Variable sel = ScenegraphLoaderUtils.getVariableSelection(graph, context); Resource represents = sel.getRepresents(graph); Resource doc = graph.getSingleObject(represents, DOC.HasDocument); Resource scenegraph = graph.getSingleObject(doc, DOC.ScenegraphDocument_scenegraph); Variable runtime = ScenegraphLoaderUtils.getRuntimeVariable(graph, context); INode root = runtime.adapt(graph, INode.class); Variable result = ScenegraphLoaderProcess.getVariable(graph, null, scenegraph, ScenegraphLoaderUtils.getRuntime(graph, context), root); if(result != null) { Variable userDoc = result.getPossibleProperty(graph, "UserDocumentation"); if(userDoc != null) return userDoc; } return null; } private static Variable resolveEditSelection(ReadGraph graph, Variable context, String path) throws DatabaseException { final ScenegraphPropertyReference selectionReference = ScenegraphLoaderUtils.getRelativePropertyReference(SWTThread.getThreadAccess(), graph, context, path); Variable editSelection = selectionReference.getExternalValue(graph); if(editSelection == null) editSelection = getDefaultSelection(graph, context); return editSelection; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> a") public static Object wikitextModifier(ReadGraph graph, final Resource resource, final Variable context) throws DatabaseException { final ScenegraphPropertyReference textReference = ScenegraphLoaderUtils.getRelativePropertyReference(SWTThread.getThreadAccess(), graph, context, ".../TextContainer/Text#text"); return new FunctionImpl1() { @Override public Object apply(final Object event) { final String value = textReference.getValue(); try { Simantics.getSession().sync(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { Variable selection = resolveEditSelection(graph, context, "..../Scroll/Browser#edited"); if (selection != null) selection.setValue(graph, (String)value, Bindings.STRING); else { System.err.println("no selection for resource : " + resource + ", Variable context : " + context + ", value : " + value); } } }); } catch (DatabaseException e) { e.printStackTrace(); } return null; } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") public static String wikitext(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context); return selection.getValue(graph); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") public static String selectedDocumentPart(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { Variable selection = resolveEditSelection(graph, context, ".../Scroll/Browser#edited"); if(selection == null) return null; return selection.getValue(graph); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object documentStructureSelected(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { return new FunctionImpl1() { @Override public Boolean apply(Object _event) { Event event = (Event)_event; final TreeItem item = (TreeItem)event.item; NodeContext context = (NodeContext)item.getData(); final Variable entry = (Variable)context.getConstant(BuiltinKeys.INPUT); ISelectionProvider provider = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart().getSite().getSelectionProvider(); provider.setSelection(new StructuredSelection(entry)); return null; } }; } abstract static class WikiButtonModifier extends FunctionImpl1 { final ScenegraphPropertyReference textReference; final ScenegraphPropertyReference selectionReference; public WikiButtonModifier(ReadGraph graph, Variable context) throws DatabaseException { textReference = ScenegraphLoaderUtils.getRelativePropertyReference(SWTThread.getThreadAccess(), graph, context, ".../TextContainer/Text#text"); selectionReference = ScenegraphLoaderUtils.getRelativePropertyReference(SWTThread.getThreadAccess(), graph, context, ".../TextContainer/Text#selection"); } abstract void perform(String before, String selected, String after, Point selection); @Override public Object apply(Object _event) { String text = textReference.getValue(); Point selection = selectionReference.getValue(); String before = text.substring(0, selection.x); String selected = text.substring(selection.x, selection.y); String after = text.substring(selection.y); perform(before, selected, after, selection); return null; } } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object boldModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { if(selected.isEmpty()) { textReference.setValue(before + "'''bold text'''" + after); } else { textReference.setValue(before + "'''" + selected + "'''" + after); } } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object italicModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { if(selected.isEmpty()) { textReference.setValue(before + "''italic text''" + after); } else { textReference.setValue(before + "''" + selected + "''" + after); } } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object strikethroughModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { if(selected.isEmpty()) { textReference.setValue(before + "strikethrough text" + after); } else { textReference.setValue(before + "" + selected + "" + after); } } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object underlineModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { if(selected.isEmpty()) { textReference.setValue(before + "strikethrough text" + after); } else { textReference.setValue(before + "" + selected + "" + after); } } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object horizontalRulerModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n
\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object indentModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + ":" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object fontModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { private String hex2(int value) { String result = Integer.toHexString(value); if(result.length() == 1) result = "0" + result; return result; } @Override void perform(String before, String selected, String after, Point selection) { FontDialog dialog = new FontDialog(Display.getCurrent().getActiveShell()); FontData data = dialog.open(); if(data == null) return; String family = data.getName(); int size = data.getHeight(); RGB rgb = dialog.getRGB(); String hex = hex2(rgb.red) + hex2(rgb.green) + hex2(rgb.blue); if(selected.isEmpty()) { textReference.setValue(before + "formatted text" + selected + after); } else { textReference.setValue(before + "" + selected + "" + after); } } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object imageModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "[[Image:root://Library/image.png|100px]]" + "\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object header1Modifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n= Header 1 =\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object header2Modifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n== Header 2 ==\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object header3Modifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n=== Header 3 ===\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object header4Modifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n==== Header 4 ====\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object numberedListModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n" + "# Item1\r\n" + "# Item2\r\n" + "## Item2.1\r\n" + "# Item3\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object bulletListModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n" + "* Item1\r\n" + "* Item2\r\n" + "** Item2.1\r\n" + "* Item3\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object tableModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "\r\n" + "{| border=\"1\"\r\n" + "! header\r\n" + "! header2 \r\n" + "|-\r\n" + "| cell || cell2\r\n" + "|-\r\n" + "| cell3\r\n" + "| cell4\r\n" + "|}\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object internalLinkModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "[[Media:root://Documents/Document.pdf|Link to a file within the model]]\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object linkModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { textReference.setValue(before + "[http://www.simantics.org External Website Link]\r\n" + selected + after); } }; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object styleModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { return new WikiButtonModifier(graph, context) { @Override void perform(String before, String selected, String after, Point selection) { Simantics.getSession().asyncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { Variable sel = ScenegraphLoaderUtils.getVariableSelection(graph, context); Resource root = sel.getIndexRoot(graph); String editorId = CSSEditor.EDITOR_ID; RVI rvi = null; PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { try { WorkbenchUtils.openEditor(editorId, new ResourceEditorInput2(editorId, root, root, rvi)); } catch (PartInitException e) { Logger.defaultLogError(e); } } }); } }); } }; } // @SCLValue(type = "ReadGraph -> Resource -> Variable -> a") // public static Object navigate(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { // // return new FunctionImpl1() { // // @Override // public Boolean apply(final String path) { // // Simantics.getSession().asyncRequest(new ReadRequest() { // // @Override // public void run(ReadGraph graph) throws DatabaseException { // // DocumentResource DOC = DocumentResource.getInstance(graph); // Variable sel = ScenegraphLoaderUtils.getVariableSelection(graph, context); // // Resource represents = sel.getRepresents(graph); // Resource doc = graph.getSingleObject(represents, DOC.HasDocument); // Resource scenegraph = graph.getSingleObject(doc, DOC.ScenegraphDocument_scenegraph); // // Variable runtime = ScenegraphLoaderUtils.getRuntimeVariable(graph, context); // INode root = runtime.adapt(graph, INode.class); // // Variable result = ScenegraphLoaderProcess.getVariable(graph, null, scenegraph, ScenegraphLoaderUtils.getRuntime(graph, context), root); // Variable location = result.browse(graph, path); // // ScenegraphPropertyReference editReference = ScenegraphLoaderUtils.getRelativePropertyReference(SWTThread.getThreadAccess(), graph, context, ".#edited"); // editReference.setValue(location); // // } // // }); // // return null; // // } // // }; // // } @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") public static String noDocumentText(ReadGraph graph, final Resource resource, final Variable context) throws DatabaseException { Variable selection = ScenegraphLoaderUtils.getPossibleVariableSelection(graph, context); if(selection == null) return ""; Resource input = selection.getRepresents(graph); if(input == null) return ""; String path = DocumentUtils.indexRootPath(graph, selection); if(!path.isEmpty()) { return "for " + path + selection.getName(graph); } return "for " + NameUtils.getSafeLabel(graph, input); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> Boolean") public static Boolean canCreateDocument(ReadGraph graph, final Resource resource, final Variable context) throws DatabaseException { Variable selection = ScenegraphLoaderUtils.getPossibleVariableSelection(graph, context); if(selection == null) return false; Resource input = selection.getRepresents(graph); if(input == null) return false; return true; } private static class ResolveURI extends UnaryRead { public ResolveURI(String uri) { super(uri); } @Override public Object perform(ReadGraph graph) throws DatabaseException { Object result = graph.syncRequest(new PossibleResource(parameter)); if (result == null) result = Variables.getPossibleVariable(graph, parameter); return result; } } private static final Function1 PERFORM_DEFAULT_ACTION_FOR_URI_RESOURCE = new Function1() { @Override public Boolean apply(Object p0) { LocationEvent le = (LocationEvent) p0; if (le.location.startsWith(DocumentDialect.SIMANTICS_INTERNAL_URI_PREFIX)) { // This is not a valid URL anyway so deny relocation. le.doit = false; // Try to execute default action for the resource or variable // that the URI represents. String uri = le.location.substring(DocumentDialect.SIMANTICS_INTERNAL_URI_PREFIX.length()); try { Session s = Simantics.getSession(); Object input = RequestUtil.trySyncRequest(s, SimanticsUI.UI_THREAD_REQUEST_START_TIMEOUT, SimanticsUI.UI_THREAD_REQUEST_EXECUTION_TIMEOUT, new ResolveURI(uri)); if (input != null) { DefaultActions.asyncPerformDefaultAction(s, input, false, false, false); } } catch (DatabaseException | InterruptedException e) { Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to resolve URI to a database resource or variable: " + uri, e)); } } return true; } }; @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Function1 locationChanging(ReadGraph graph, Resource variable, Variable context) throws DatabaseException { return PERFORM_DEFAULT_ACTION_FOR_URI_RESOURCE; } }