/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2013 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 3 for more details. * * You should have received a copy of the GNU General Public License version 3 * along with this work; if not, see http://www.gnu.org/licenses/ * * * Please visit http://neilcsmith.net if you need additional information or * have any questions. */ package net.neilcsmith.praxis.live.pxr; import java.awt.EventQueue; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Set; import java.util.stream.Collectors; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileNameExtensionFilter; import net.neilcsmith.praxis.core.CallArguments; import net.neilcsmith.praxis.core.ComponentAddress; import net.neilcsmith.praxis.live.core.api.Callback; import net.neilcsmith.praxis.live.core.api.Task; import net.neilcsmith.praxis.live.model.ContainerProxy; import net.neilcsmith.praxis.live.util.AbstractTask; import net.neilcsmith.praxis.live.util.SerialTasks; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.filesystems.FileChooserBuilder; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.RequestProcessor; /** * * @author Neil C Smith */ public class ActionBridge { private final static ActionBridge INSTANCE = new ActionBridge(); private final static RequestProcessor RP = new RequestProcessor(ActionBridge.class); private ActionBridge() { // non instantiable } @Deprecated public void copyToClipboard(ContainerProxy container, Set<String> children) { StringBuilder sb = new StringBuilder(); try { PXRWriter.writeSubGraph((PXRContainerProxy) container, children, sb); SubGraphTransferable tf = new SubGraphTransferable(sb.toString()); getClipboard().setContents(tf, tf); } catch (Exception ex) { Exceptions.printStackTrace(ex); } } public Task createCopyTask(ContainerProxy container, Set<String> children, Runnable preWriteTask, Runnable postWriteTask) { SubGraphTransferable empty = new SubGraphTransferable(""); getClipboard().setContents(empty, empty); SyncTask sync = new SyncTask( children.stream() .map(container::getChild) .filter(cmp -> cmp != null) .collect(Collectors.toSet()), 250); WriteClipboardTask write = new WriteClipboardTask(container, children, preWriteTask, postWriteTask); return new SerialTasks(sync, write); } public Task createExportTask(ContainerProxy container, Set<String> children, Runnable preWriteTask, Runnable postWriteTask) { SyncTask sync = new SyncTask( children.stream() .map(container::getChild) .filter(cmp -> cmp != null) .collect(Collectors.toSet()), 250); ExportTask write = new ExportTask(container, children, preWriteTask, postWriteTask); return new SerialTasks(sync, write); } private static class WriteClipboardTask extends AbstractTask { private final ContainerProxy container; private final Set<String> children; private final Runnable preWriteTask; private final Runnable postWriteTask; WriteClipboardTask(ContainerProxy container, Set<String> children, Runnable preWriteTask, Runnable postWriteTask) { this.container = container; this.children = children; this.preWriteTask = preWriteTask; this.postWriteTask = postWriteTask; } @Override protected void handleExecute() throws Exception { try { if (preWriteTask != null) { preWriteTask.run(); } StringBuilder sb = new StringBuilder(); PXRWriter.writeSubGraph((PXRContainerProxy) container, children, sb); SubGraphTransferable tf = new SubGraphTransferable(sb.toString()); getClipboard().setContents(tf, tf); } finally { if (postWriteTask != null) { postWriteTask.run(); } } updateState(State.COMPLETED); } } private static class ExportTask extends AbstractTask { private final ContainerProxy container; private final Set<String> children; private final Runnable preWriteTask; private final Runnable postWriteTask; ExportTask(ContainerProxy container, Set<String> children, Runnable preWriteTask, Runnable postWriteTask) { this.container = container; this.children = children; this.preWriteTask = preWriteTask; this.postWriteTask = postWriteTask; } @Override protected void handleExecute() throws Exception { if (preWriteTask != null) { preWriteTask.run(); } StringBuilder sb = new StringBuilder(); PXRWriter.writeSubGraph((PXRContainerProxy) container, children, sb); if (postWriteTask != null) { postWriteTask.run(); } File f = new FileChooserBuilder("export-pxg") .addFileFilter(new FileNameExtensionFilter("PXG files", "pxg")) .setAcceptAllFileFilterUsed(false) .showSaveDialog(); if (f == null) { updateState(State.CANCELLED); return; } if (!f.getName().endsWith(".pxg")) { f = new File(f.getParent(), f.getName() + ".pxg"); } File file = f; String export = sb.toString(); RP.post(() -> { if (file.exists()) { EventQueue.invokeLater(() -> { DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("File already exists.", NotifyDescriptor.ERROR_MESSAGE)); updateState(State.ERROR); }); } else { try { Files.write(file.toPath(), export.getBytes(StandardCharsets.UTF_8)); FileUtil.toFileObject(file.getParentFile()).refresh(); EventQueue.invokeLater(() -> { updateState(State.COMPLETED); }); } catch (IOException ex) { Exceptions.printStackTrace(ex); EventQueue.invokeLater(() -> { DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Failed to write file.", NotifyDescriptor.ERROR_MESSAGE)); updateState(State.ERROR); }); } } }); } } public boolean pasteFromClipboard(ContainerProxy container, Callback callback) { Clipboard c = getClipboard(); if (c.isDataFlavorAvailable(DataFlavor.stringFlavor)) { try { String script = (String) c.getData(DataFlavor.stringFlavor); if (script.trim().isEmpty()) { return false; } PXRParser.RootElement fakeRoot = PXRParser.parseInContext(container.getAddress(), script); if (ImportRenameSupport.prepareForPaste(container, fakeRoot)) { PXRBuilder builder = new PXRBuilder(findRootProxy(container), fakeRoot, null); builder.process(callback); return true; } } catch (Exception ex) { Exceptions.printStackTrace(ex); } } return false; } private static Clipboard getClipboard() { Clipboard c = Lookup.getDefault().lookup(Clipboard.class); if (c == null) { c = Toolkit.getDefaultToolkit().getSystemClipboard(); } return c; } public boolean importSubgraph(final ContainerProxy container, final FileObject file, final Callback callback) { if (!file.hasExt("pxg")) { return false; } final ComponentAddress context = container.getAddress(); RP.execute(new Runnable() { @Override public void run() { PXRParser.RootElement r = null; try { r = PXRParser.parseInContext(context, file.asText()); } catch (Exception ex) { Exceptions.printStackTrace(ex); } final PXRParser.RootElement root = r; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { if (root != null) { if (ImportRenameSupport.prepareForImport(container, root)) { PXRBuilder builder = new PXRBuilder(findRootProxy(container), root, null); builder.process(callback); } else { callback.onReturn(CallArguments.EMPTY); } } else { callback.onError(CallArguments.EMPTY); } } catch (Exception ex) { Exceptions.printStackTrace(ex); } } }); } }); return true; } private PXRRootProxy findRootProxy(ContainerProxy container) { while (container != null) { if (container instanceof PXRRootProxy) { return (PXRRootProxy) container; } container = container.getParent(); } throw new IllegalStateException("No root proxy found"); } public static ActionBridge getDefault() { return INSTANCE; } private static class SubGraphTransferable implements Transferable, ClipboardOwner { private String data; private SubGraphTransferable(String data) { this.data = data; } @Override public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[]{ DataFlavor.stringFlavor }; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { if (flavor.equals(DataFlavor.stringFlavor)) { return true; } else { return false; } } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (isDataFlavorSupported(flavor)) { return data; } else { throw new UnsupportedFlavorException(flavor); } } @Override public void lostOwnership(Clipboard clipboard, Transferable contents) { // no op } } }