package org.freeplane.plugin.workspace.nodes;
import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.tree.DefaultTreeCellRenderer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.plugin.workspace.WorkspaceController;
import org.freeplane.plugin.workspace.WorkspaceUtils;
import org.freeplane.plugin.workspace.components.menu.WorkspacePopupMenu;
import org.freeplane.plugin.workspace.components.menu.WorkspacePopupMenuBuilder;
import org.freeplane.plugin.workspace.dnd.IDropAcceptor;
import org.freeplane.plugin.workspace.dnd.IWorkspaceTransferableCreator;
import org.freeplane.plugin.workspace.dnd.WorkspaceTransferable;
import org.freeplane.plugin.workspace.event.IWorkspaceNodeActionListener;
import org.freeplane.plugin.workspace.event.WorkspaceActionEvent;
import org.freeplane.plugin.workspace.io.IFileSystemRepresentation;
import org.freeplane.plugin.workspace.io.annotation.ExportAsAttribute;
import org.freeplane.plugin.workspace.model.AWorkspaceTreeNode;
import org.freeplane.plugin.workspace.model.IMutableLinkNode;
public class FolderLinkNode extends AFolderNode implements IWorkspaceNodeActionListener
, FileAlterationListener
, IWorkspaceTransferableCreator
, IDropAcceptor
, IFileSystemRepresentation
, IMutableLinkNode {
private static final long serialVersionUID = 1L;
private static Icon FOLDER_OPEN_ICON = new ImageIcon(FolderLinkNode.class.getResource("/images/16x16/folder-orange_open.png"));
private static final Icon FOLDER_CLOSED_ICON = new ImageIcon(FolderLinkNode.class.getResource("/images/16x16/folder-orange.png"));
private static WorkspacePopupMenu popupMenu = null;
private URI folderPath;
private boolean doMonitoring = false;
private boolean first;
private boolean orderDescending = false;
public FolderLinkNode() {
this(AFolderNode.FOLDER_TYPE_PHYSICAL);
}
public FolderLinkNode(String id) {
super(id);
}
@ExportAsAttribute(name="path")
public URI getPath() {
return folderPath;
}
public void setPath(URI uri) {
if(isMonitoring()) {
enableMonitoring(false);
this.folderPath = uri;
createIfNeeded(getPath());
enableMonitoring(true);
}
else {
this.folderPath = uri;
createIfNeeded(getPath());
}
}
private void createIfNeeded(URI uri) {
File file = WorkspaceUtils.resolveURI(uri);
if (file != null && !file.exists()) {
file.mkdirs();
}
}
public void initializePopup() {
if (popupMenu == null) {
if (popupMenu == null) {
popupMenu = new WorkspacePopupMenu();
WorkspacePopupMenuBuilder.addActions(popupMenu, new String[] {
WorkspacePopupMenuBuilder.createSubMenu(TextUtils.getRawText("workspace.action.new.label")),
"workspace.action.node.new.folder",
"workspace.action.file.new.mindmap",
//WorkspacePopupMenuBuilder.SEPARATOR,
//"workspace.action.file.new.file",
WorkspacePopupMenuBuilder.endSubMenu(),
WorkspacePopupMenuBuilder.SEPARATOR,
"workspace.action.node.open.location",
WorkspacePopupMenuBuilder.SEPARATOR,
"workspace.action.node.cut",
"workspace.action.node.copy",
"workspace.action.node.paste",
WorkspacePopupMenuBuilder.SEPARATOR,
"workspace.action.node.rename",
"workspace.action.node.remove",
"workspace.action.file.delete",
WorkspacePopupMenuBuilder.SEPARATOR,
"workspace.action.node.physical.sort",
WorkspacePopupMenuBuilder.SEPARATOR,
"workspace.action.node.enable.monitoring",
"workspace.action.node.refresh"
});
}
}
}
public void enableMonitoring(boolean enable) {
if(getPath() == null) {
this.doMonitoring = enable;
}
else {
File file = WorkspaceUtils.resolveURI(getPath());
if(enable != this.doMonitoring) {
this.doMonitoring = enable;
first = true;
if(file == null) {
return;
}
try {
if(enable) {
WorkspaceController.getController().getFileSystemAlterationMonitor().addFileSystemListener(file, this);
}
else {
WorkspaceController.getController().getFileSystemAlterationMonitor().removeFileSystemListener(file, this);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
@ExportAsAttribute(name="monitor")
public boolean isMonitoring() {
return this.doMonitoring;
}
public void orderDescending(boolean enable) {
this.orderDescending = enable;
}
@ExportAsAttribute(name="orderDescending")
public boolean orderDescending() {
return orderDescending;
}
public void handleAction(WorkspaceActionEvent event) {
if (event.getType() == WorkspaceActionEvent.MOUSE_RIGHT_CLICK) {
showPopup( (Component) event.getBaggage(), event.getX(), event.getY());
event.consume();
}
}
public boolean setIcons(DefaultTreeCellRenderer renderer) {
renderer.setOpenIcon(FOLDER_OPEN_ICON);
renderer.setClosedIcon(FOLDER_CLOSED_ICON);
renderer.setLeafIcon(FOLDER_CLOSED_ICON);
return true;
}
public String toString() {
return this.getClass().getSimpleName() + "[id=" + this.getId() + ";name=" + this.getName() + ";path="
+ this.getPath() + "]";
}
public void refresh() {
File folder;
try {
folder = WorkspaceUtils.resolveURI(getPath());
if (folder.isDirectory()) {
WorkspaceUtils.getModel().removeAllElements(this);
WorkspaceController.getController().getFilesystemMgr().scanFileSystem(this, folder);
WorkspaceUtils.getModel().reload(this);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
protected AWorkspaceTreeNode clone(FolderLinkNode node) {
node.setPath(getPath());
return super.clone(node);
}
private void processWorkspaceNodeDrop(List<AWorkspaceTreeNode> nodes, int dropAction) {
try {
File targetDir = WorkspaceUtils.resolveURI(getPath());
for(AWorkspaceTreeNode node : nodes) {
if(node instanceof DefaultFileNode) {
if(targetDir != null && targetDir.isDirectory()) {
if(dropAction == DnDConstants.ACTION_COPY) {
((DefaultFileNode) node).copyTo(targetDir);
}
else if(dropAction == DnDConstants.ACTION_MOVE) {
File oldFile = ((DefaultFileNode) node).getFile();
if(oldFile.equals(targetDir)) return;
((DefaultFileNode) node).moveTo(targetDir);
File newFile = new File(targetDir, ((DefaultFileNode) node).getName());
AWorkspaceTreeNode parent = node.getParent();
WorkspaceUtils.getModel().cutNodeFromParent(node);
parent.refresh();
WorkspaceUtils.getModel().nodeMoved(node, oldFile, newFile);
}
}
}
else if(node instanceof LinkTypeFileNode) {
File srcFile = WorkspaceUtils.resolveURI(((LinkTypeFileNode) node).getLinkPath());
if(targetDir != null && targetDir.isDirectory()) {
FileUtils.copyFileToDirectory(srcFile, targetDir);
if(dropAction == DnDConstants.ACTION_MOVE) {
AWorkspaceTreeNode parent = node.getParent();
WorkspaceUtils.getModel().cutNodeFromParent(node);
parent.refresh();
WorkspaceUtils.getModel().nodeMoved(node, srcFile, new File(targetDir, srcFile.getName()));
}
}
}
}
WorkspaceController.getController().getExpansionStateHandler().addPathKey(this.getKey());
refresh();
}
catch (Exception e) {
LogUtils.warn("DOCEAR@FolderLinkNode.processWorkspaceNodeDrop.1: "+e.getMessage());
}
}
private void processFileListDrop(List<File> files, int dropAction) {
try {
File targetDir = WorkspaceUtils.resolveURI(getPath());
for(File srcFile : files) {
if(srcFile.isDirectory()) {
FileUtils.copyDirectoryToDirectory(srcFile, targetDir);
}
else {
FileUtils.copyFileToDirectory(srcFile, targetDir, true);
}
}
refresh();
}
catch (Exception e) {
LogUtils.warn(e);
}
refresh();
}
private void processUriListDrop(List<URI> uris, int dropAction) {
try {
File targetDir = WorkspaceUtils.resolveURI(getPath());
for(URI uri : uris) {
File srcFile = new File(uri);
if(srcFile == null || !srcFile.exists()) {
continue;
}
if(srcFile.isDirectory()) {
FileUtils.copyDirectoryToDirectory(srcFile, targetDir);
}
else {
FileUtils.copyFileToDirectory(srcFile, targetDir, true);
}
}
refresh();
}
catch (Exception e) {
LogUtils.warn(e);
}
refresh();
}
/***********************************************************************************
* REQUIRED METHODS FOR INTERFACES
**********************************************************************************/
private boolean fsChanges = false;
public void onStart(FileAlterationObserver observer) {
fsChanges = false;
if(first ) return;
// called when the observer starts a check cycle. do nth so far.
}
public void onDirectoryCreate(File directory) {
if(first) return;
fsChanges = true;
}
public void onDirectoryChange(File directory) {
if(first) return;
fsChanges = true;
}
public void onDirectoryDelete(File directory) {
if(first) return;
fsChanges = true;
}
public void onFileCreate(File file) {
if(first) return;
fsChanges = true;
}
public void onFileChange(File file) {
if(first) return;
fsChanges = true;
}
public void onFileDelete(File file) {
if(first) return;
fsChanges = true;
}
public void onStop(FileAlterationObserver observer) {
if(!first && fsChanges) {
fsChanges=false;
refresh();
}
first = false;
}
public WorkspacePopupMenu getContextMenu() {
if (popupMenu == null) {
initializePopup();
}
return popupMenu;
}
public AWorkspaceTreeNode clone() {
FolderLinkNode node = new FolderLinkNode(getType());
return clone(node);
}
public boolean acceptDrop(DataFlavor[] flavors) {
for(DataFlavor flavor : flavors) {
if(WorkspaceTransferable.WORKSPACE_FILE_LIST_FLAVOR.equals(flavor)
|| WorkspaceTransferable.WORKSPACE_URI_LIST_FLAVOR.equals(flavor)
|| WorkspaceTransferable.WORKSPACE_NODE_FLAVOR.equals(flavor)
) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
public boolean processDrop(Transferable transferable, int dropAction) {
try {
if(transferable.isDataFlavorSupported(WorkspaceTransferable.WORKSPACE_NODE_FLAVOR)) {
processWorkspaceNodeDrop((List<AWorkspaceTreeNode>) transferable.getTransferData(WorkspaceTransferable.WORKSPACE_NODE_FLAVOR), dropAction);
}
else if(transferable.isDataFlavorSupported(WorkspaceTransferable.WORKSPACE_FILE_LIST_FLAVOR)) {
processFileListDrop((List<File>) transferable.getTransferData(WorkspaceTransferable.WORKSPACE_FILE_LIST_FLAVOR), dropAction);
}
else if(transferable.isDataFlavorSupported(WorkspaceTransferable.WORKSPACE_URI_LIST_FLAVOR)) {
ArrayList<URI> uriList = new ArrayList<URI>();
String uriString = (String) transferable.getTransferData(WorkspaceTransferable.WORKSPACE_URI_LIST_FLAVOR);
if (!uriString.startsWith("file://")) {
return false;
}
String[] uriArray = uriString.split("\r\n");
for(String singleUri : uriArray) {
try {
uriList.add(URI.create(singleUri));
}
catch (Exception e) {
LogUtils.info("DOCEAR@FolderLinkNode.processDrop.1: "+ e.getMessage());
}
}
processUriListDrop(uriList, dropAction);
}
}
catch (Exception e) {
LogUtils.warn("DOCEAR@FolderLinkNode.processDrop.2<: "+e.getMessage());
}
return true;
}
public boolean processDrop(DropTargetDropEvent event) {
event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
Transferable transferable = event.getTransferable();
if(processDrop(transferable, event.getDropAction())) {
event.dropComplete(true);
return true;
}
event.dropComplete(false);
return false;
}
public Transferable getTransferable() {
WorkspaceTransferable transferable = new WorkspaceTransferable();
try {
URI uri = WorkspaceUtils.absoluteURI(getPath());
transferable.addData(WorkspaceTransferable.WORKSPACE_URI_LIST_FLAVOR, uri.toString());
List<File> fileList = new Vector<File>();
fileList.add(new File(uri));
transferable.addData(WorkspaceTransferable.WORKSPACE_FILE_LIST_FLAVOR, fileList);
if(!this.isSystem()) {
List<AWorkspaceTreeNode> objectList = new ArrayList<AWorkspaceTreeNode>();
objectList.add(this);
transferable.addData(WorkspaceTransferable.WORKSPACE_NODE_FLAVOR, objectList);
}
return transferable;
}
catch (Exception e) {
LogUtils.warn(e);
}
return null;
}
public File getFile() {
return WorkspaceUtils.resolveURI(this.getPath());
}
public boolean changeName(String newName, boolean renameLink) {
assert(newName != null);
assert(newName.trim().length() > 0);
if(renameLink) {
File oldFile = WorkspaceUtils.resolveURI(getPath());
try{
if(oldFile == null) {
throw new Exception("failed to resolve the file for"+getName());
}
File destFile = new File(oldFile.getParentFile(), newName);
if(oldFile.exists() && oldFile.renameTo(destFile)) {
//this.setName(newName);
try {
WorkspaceUtils.getModel().changeNodeName(this, newName);
return true;
}
catch(Exception ex) {
destFile.renameTo(oldFile);
return false;
}
}
else {
LogUtils.warn("cannot rename "+oldFile.getName());
}
}
catch (Exception e) {
LogUtils.warn("cannot rename "+oldFile.getName(), e);
}
}
else {
//this.setName(newName);
try {
WorkspaceUtils.getModel().changeNodeName(this, newName);
return true;
}
catch(Exception ex) {
// do nth.
}
}
return false;
}
}