/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2015 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.event.ActionEvent;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import net.neilcsmith.praxis.core.ControlAddress;
import net.neilcsmith.praxis.core.info.ArgumentInfo;
import net.neilcsmith.praxis.core.info.ControlInfo;
import net.neilcsmith.praxis.core.types.PString;
import org.netbeans.api.actions.Openable;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.filesystems.FileChangeAdapter;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.util.Exceptions;
class BoundCodeProperty extends BoundArgumentProperty {
private static final Map<String, String> mimeToExt = new HashMap<>();
static {
// include common
mimeToExt.put("text/x-praxis-java", "pxj");
mimeToExt.put("text/x-praxis-script", "pxs");
// GLSL mime types in Praxis core don't match editor mime type (text/x-glsl)
mimeToExt.put("text/x-glsl-frag", "frag");
mimeToExt.put("text/x-glsl-vert", "vert");
}
private final String mimeType;
private final String template;
private final FileListener fileListener;
private final String fileName;
private final Action editAction;
private final Action resetAction;
private FileObject file;
BoundCodeProperty(ControlAddress address, ControlInfo info, String mimeType) {
super(address, info);
this.mimeType = mimeType;
this.template = info.getOutputsInfo()[0].getProperties().getString(ArgumentInfo.KEY_TEMPLATE, "");
this.fileListener = new FileListener();
fileName = safeFileName(address);
String id = address.getID();
editAction = new EditAction(id);
resetAction = new ResetAction(id);
setHidden(true);
}
private String safeFileName(ControlAddress address) {
String name = address.getComponentAddress().getID() + "_" + address.getID();
name = name.replace('-', '_');
return name;
}
@Override
public void restoreDefaultValue() {
super.restoreDefaultValue();
deleteFile();
}
@Override
public void dispose() {
super.dispose();
deleteFile();
}
Action getEditAction() {
return editAction;
}
Action getResetAction() {
return resetAction;
}
private void openEditor() {
try {
if (file == null) {
file = constructFile();
}
DataObject dob = DataObject.find(file);
Openable openable = dob.getLookup().lookup(Openable.class);
if (openable != null) {
openable.open();
}
} catch (Exception ex) {
}
}
private FileObject constructFile() throws Exception {
FileSystem fs = FileUtil.createMemoryFileSystem();
FileObject f;
String ext = findExtension(mimeType);
if (ext != null) {
f = fs.getRoot().createData(fileName, ext);
} else {
f = fs.getRoot().createData(fileName);
}
OutputStreamWriter writer = null;
try {
writer = new OutputStreamWriter(f.getOutputStream());
writer.append(constructFileContent());
} finally {
if (writer != null) {
writer.close();
}
}
f.setAttribute("argumentInfo", getInfo().getOutputsInfo()[0]);
f.addFileChangeListener(fileListener);
return f;
}
private String findExtension(String mimeType) {
if (mimeToExt.containsKey(mimeType)) {
return mimeToExt.get(mimeType);
}
List<String> exts = FileUtil.getMIMETypeExtensions(mimeType);
String ext = null;
if (exts.size() > 0) {
ext = exts.get(0);
}
mimeToExt.put(mimeType, ext);
return ext;
}
private String constructFileContent() {
String s = getValue().toString();
if (s.trim().isEmpty()) {
s = template;
}
return s;
}
private void updateFromFile() {
if (file != null) {
try {
setValue(PString.valueOf(file.asText()));
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
}
private void deleteFile() {
if (file == null) {
return;
}
try {
file.delete();
file = null;
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
class FileListener extends FileChangeAdapter {
@Override
public void fileChanged(final FileEvent fe) {
if (EventQueue.isDispatchThread()) {
if (fe.getFile() == file) {
updateFromFile();
} else {
fe.getFile().removeFileChangeListener(this);
}
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
fileChanged(fe);
}
});
}
}
}
class EditAction extends AbstractAction {
private EditAction(String id) {
super("Edit " + id);
}
@Override
public void actionPerformed(ActionEvent e) {
openEditor();
}
}
class ResetAction extends AbstractAction {
private String id;
private ResetAction(String id) {
super("Reset " + id);
this.id = id;
}
@Override
public void actionPerformed(ActionEvent e) {
NotifyDescriptor nd = new NotifyDescriptor(
"Reset " + id + "? Changes will be lost.",
"Reset " + id + "?",
NotifyDescriptor.YES_NO_OPTION,
NotifyDescriptor.WARNING_MESSAGE,
null,
null);
if (DialogDisplayer.getDefault().notify(nd) == NotifyDescriptor.YES_OPTION) {
restoreDefaultValue();
}
}
}
}