/**
* author: Marcel Genzmehr
* 23.08.2011
*/
package org.docear.plugin.core.workspace.node;
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 javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
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.docear.plugin.core.CoreConfiguration;
import org.docear.plugin.core.DocearController;
import org.docear.plugin.core.event.DocearEvent;
import org.docear.plugin.core.event.DocearEventType;
import org.docear.plugin.core.workspace.actions.DocearProjectEnableMonitoringAction;
import org.docear.plugin.core.workspace.node.config.NodeAttributeObserver;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.mode.Controller;
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.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.nodes.AFolderNode;
import org.freeplane.plugin.workspace.nodes.DefaultFileNode;
import org.freeplane.plugin.workspace.nodes.LinkTypeFileNode;
public class FolderTypeProjectsNode extends AFolderNode implements IWorkspaceNodeActionListener, FileAlterationListener, ChangeListener, IDropAcceptor, IFileSystemRepresentation {
private static final long serialVersionUID = 1L;
private static final Icon DEFAULT_ICON = new ImageIcon(FolderTypeLibraryNode.class.getResource("/images/project-open-2.png"));
private boolean doMonitoring = false;
private URI pathURI = null;
private boolean locked = false;
private static WorkspacePopupMenu popupMenu = null;
private boolean first = true;
private boolean fsChanges = false;
private boolean orderDescending;
/***********************************************************************************
* CONSTRUCTORS
**********************************************************************************/
public FolderTypeProjectsNode() {
this("projects");
}
public FolderTypeProjectsNode(String type) {
super(type);
CoreConfiguration.projectPathObserver.addChangeListener(this);
}
/***********************************************************************************
* METHODS
**********************************************************************************/
public void setPath(URI uri) {
locked = true;
if(isMonitoring()) {
enableMonitoring(false);
this.pathURI = uri;
if (uri != null) {
createPathIfNeeded(getPath());
first = true;
enableMonitoring(true);
}
}
else {
this.pathURI = uri;
createPathIfNeeded(getPath());
}
CoreConfiguration.projectPathObserver.setUri(uri);
DocearEvent event = new DocearEvent(this, DocearEventType.LIBRARY_NEW_PROJECT_INDEXING_REQUEST, this);
DocearController.getController().dispatchDocearEvent(event);
locked = false;
}
@ExportAsAttribute(name="path")
public URI getPath() {
return this.pathURI;
}
public void enableMonitoring(boolean enable) {
if(getPath() == null) {
this.doMonitoring = enable;
}
else {
File file = WorkspaceUtils.resolveURI(getPath());
if(enable != this.doMonitoring) {
this.doMonitoring = enable;
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 boolean setIcons(DefaultTreeCellRenderer renderer) {
renderer.setOpenIcon(DEFAULT_ICON);
renderer.setClosedIcon(DEFAULT_ICON);
renderer.setLeafIcon(DEFAULT_ICON);
return true;
}
public void disassociateReferences() {
CoreConfiguration.projectPathObserver.removeChangeListener(this);
}
protected AWorkspaceTreeNode clone(FolderTypeProjectsNode node) {
node.setPath(getPath());
node.enableMonitoring(isMonitoring());
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();
((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(e);
}
}
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
**********************************************************************************/
public void handleAction(WorkspaceActionEvent event) {
if (event.getType() == WorkspaceActionEvent.MOUSE_RIGHT_CLICK) {
showPopup((Component) event.getBaggage(), event.getX(), event.getY());
}
}
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 void refresh() {
try {
File file = WorkspaceUtils.resolveURI(getPath());
if (file != null) {
WorkspaceUtils.getModel().removeAllElements(this);
WorkspaceController.getController().getFilesystemMgr().scanFileSystem(this, file);
WorkspaceUtils.getModel().reload(this);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
public AWorkspaceTreeNode clone() {
FolderTypeProjectsNode node = new FolderTypeProjectsNode(getType());
return clone(node);
}
public void initializePopup() {
if (popupMenu == null) {
Controller controller = Controller.getCurrentController();
controller.addAction(new DocearProjectEnableMonitoringAction());
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.docear.uri.change",
"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.docear.project.enable.monitoring",
"workspace.action.node.refresh"
});
}
}
public WorkspacePopupMenu getContextMenu() {
if (popupMenu == null) {
initializePopup();
}
return popupMenu;
}
private void createPathIfNeeded(URI uri) {
File file = WorkspaceUtils.resolveURI(uri);
if (file != null) {
if (!file.exists()) {
if (file.mkdirs()) {
}
}
}
else {
LogUtils.warn("no project folder selected!");
}
}
public void stateChanged(ChangeEvent e) {
if(!locked && e.getSource() instanceof NodeAttributeObserver) {
URI uri = ((NodeAttributeObserver) e.getSource()).getUri();
this.setPath(uri);
this.refresh();
}
}
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 - "+ e.getMessage());
}
}
processUriListDrop(uriList, dropAction);
}
}
catch (Exception e) {
LogUtils.warn(e);
}
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 File getFile() {
return WorkspaceUtils.resolveURI(this.getPath());
}
public void orderDescending(boolean enable) {
this.orderDescending = enable;
}
@ExportAsAttribute(name="orderDescending")
public boolean orderDescending() {
return orderDescending;
}
}