/*
* Copyright 2011 Uwe Krueger.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mandelsoft.mand.tool;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.swing.DropMode;
import javax.swing.TransferHandler;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreePath;
import com.mandelsoft.mand.Environment;
import com.mandelsoft.mand.QualifiedMandelName;
import com.mandelsoft.mand.scan.MandelScanner;
import com.mandelsoft.mand.util.MandelList;
import com.mandelsoft.mand.util.MandelListFolder;
import com.mandelsoft.swing.TreeModelListenerSupport;
/**
*
* @author Uwe Krüger
*/
public abstract class MandelListFolderTreeModelSupport
extends TreeModelListenerSupport
implements MandelListFolderTreeModel {
static private final boolean debug=false;
protected Map<MandelListFolder,MandelListTableModel> listmodels;
//
// temporary internal states
// for event interaction
//
protected boolean moving;
protected MandelListFolderTreeModelSupport()
{
super();
listmodels=new HashMap<MandelListFolder,MandelListTableModel>();
}
public void clear()
{
listmodels.clear();
}
////////////////////////////////////////////////////////////////
//folder tree
////////////////////////////////////////////////////////////////
public void save()
{
try {
System.out.println("save "+getFolderTree());
getFolderTree().save();
}
catch (IOException ex) {
}
}
public MandelListFolder getChild(MandelListFolder parent, String name)
{
for (MandelListFolder f:parent) {
if (f.getName().equals(name)) return f;
}
return null;
}
/////////////////////////////////////////////////////////////////////////
// tree model
/////////////////////////////////////////////////////////////////////////
public Object getChild(Object parent, int index)
{
Object c=((MandelListFolder)parent).get(index);
if (debug) System.out.println("get child for "+parent+": "+c);
return c;
}
public int getChildCount(Object parent)
{
return ((MandelListFolder)parent).size();
}
public int getIndexOfChild(Object parent, Object child)
{
return ((MandelListFolder)parent).indexOf(child);
}
public boolean isLeaf(Object node)
{
MandelListFolder f=(MandelListFolder)node;
return f.isLeaf() || f.size()==0;
}
public void valueForPathChanged(TreePath path, Object newValue)
{
if (newValue==null || newValue.toString().isEmpty()) return;
MandelListFolder f=(MandelListFolder)path.getLastPathComponent();
if (debug) System.out.println(f.getClass()+" value changed: "+path(path)+": "+newValue);
//new Throwable().printStackTrace();
f.setName(newValue.toString());
fireTreeNodesChanged(path,null,null);
try {
f.save();
}
catch (IOException ex) {
System.out.println("cannot save: "+ex);
}
}
public MandelListFolder getRoot()
{
return getFolderTree().getRoot();
}
public MandelListTableModel getRootModel()
{
return getMandelListModel(getRoot());
}
public MandelListFolderTreeModel getEffectiveFolderTreeModel()
{
return this;
}
////////////////////////////////////////////////////////////////
// extended interface
////////////////////////////////////////////////////////////////
public String convertValueToText(Object value, boolean selected,
boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
return ((MandelListFolder)value).getName();
}
public boolean isPathEditable(TreePath path)
{
if (!isModifiable()) return false;
if (path.getPathCount()==1) return false;
return true;
}
public boolean isPathModifiable(TreePath path)
{
MandelListFolder f=(MandelListFolder)path.getLastPathComponent();
return !f.isLeaf() && isModifiable(f);
}
public boolean isPathListModifiable(TreePath path)
{
MandelListFolder f=(MandelListFolder)path.getLastPathComponent();
if (!f.hasMandelList()) return false;
if (!this.getMandelListModel(f).isModifiable()) return false;
return isListModifiable(f); // disallow modification via folder
}
protected boolean isModifiable(MandelListFolder f)
{
return isModifiable();
}
public boolean isPathTransferable(TreePath path)
{
return true;
}
////////////////////////////////////////////////////////////////
// mandel list model handling
////////////////////////////////////////////////////////////////
public MandelListTableModel getMandelListModel(Object folder)
{ MandelListFolder f=(MandelListFolder)folder;
MandelListTableModel m=listmodels.get(f);
if (m==null) {
checkFolder(f);
if (f.getMandelList()==null) return null;
m=createMandelListModel(f);
m.setModifiable(isListModifiable(f));
listmodels.put(f, m);
}
return m;
}
protected boolean isListModifiable(MandelListFolder f)
{
return isModifiable(f);
}
protected void cleanup(MandelListFolder f)
{
listmodels.remove(f);
for (MandelListFolder s:f) {
cleanup(s);
}
}
///////////////////////////////////////////////////////////////////////
// list ops
///////////////////////////////////////////////////////////////////////
public void addAll(MandelListFolder f, QualifiedMandelName[] list)
{
MandelListTableModel m=getMandelListModel(f);
if (m==null)
throw new IllegalArgumentException(" no list supported for folder "+f.getPath());
m.addAll(list);
}
public void add(MandelListFolder f, QualifiedMandelName name)
{
MandelListTableModel m=getMandelListModel(f);
if (m==null)
throw new IllegalArgumentException(" no list supported for folder "+f.getPath());
m.add(name);
}
public void remove(MandelListFolder f, QualifiedMandelName name)
{
MandelListTableModel m=getMandelListModel(f);
if (m==null)
throw new IllegalArgumentException(" no list supported for folder "+f.getPath());
m.remove(name);
}
////////////////////////////////////////////////////////////////
// folder ops
////////////////////////////////////////////////////////////////
public MandelListFolder insertFolder(String name, MandelListFolder parent)
{
MandelListFolder child=parent.createSubFolder(name);
if (child!=null) {
if (debug) System.out.println("created sub folder of "+parent.getName()+
": "+child.getName());
int[] newIndexs=new int[1];
newIndexs[0]=parent.indexOf(child);
foldersWereInserted(parent, newIndexs);
}
return child;
}
public void moveFolders(int index, MandelListFolder[] folders,
MandelListFolder parent)
{
try {
moving=true;
int[] newIndexs=new int[folders.length];
MandelListFolder s;
for (int i=0; i<folders.length; i++) {
checkFolder(folders[i]);
if (folders[i].getParent()==parent) {
//local order changed -> adjust insertion index
if (index>parent.indexOf(folders[i])) index--;
}
_removeFolder(folders[i],true);
}
for (int i=0; i<folders.length; i++) {
if (index<0||index>=parent.size()) {
newIndexs[i]=parent.size();
}
else {
newIndexs[i]=index++;
}
parent.add(newIndexs[i], folders[i]);
}
foldersWereInserted(parent, newIndexs);
}
finally {
moving=false;
}
}
public boolean insertFolders(int index, MandelListFolder[] folders,
MandelListFolder parent)
{
int[] newIndexs=new int[folders.length];
MandelListFolder s;
boolean add=false;
for (int i=0; i<folders.length; i++) {
if (index<0 || index>=parent.size()) {
newIndexs[i]=parent.size();
}
else {
newIndexs[i]=index++;
}
add|=_insertFolder(newIndexs[i],folders[i],parent);
}
if (add) foldersWereInserted(parent, newIndexs);
return add;
}
private boolean _insertFolder(int index, MandelListFolder folder,
MandelListFolder parent)
{
if (debug) System.out.println("insert folder "+folder.getName()+" into "+
parent.getName()+" at "+index);
boolean add=false;
add=true;
MandelListFolder dst=parent.createSubFolder(index, folder.getName());
dst.getMandelList().addAll(folder.getMandelList());
for (MandelListFolder s:folder) {
_insertFolder(dst.size(), s, dst);
}
return add;
}
protected void _removeFolder(MandelListFolder node, boolean moved)
{
MandelListFolder parent=node.getParent();
if (parent==null)
throw new IllegalArgumentException("folder does not have a parent.");
int[] childIndices=new int[1];
Object[] removedChildren=new Object[1];
childIndices[0]=parent.indexOf(node);
removedChildren[0]=node;
parent.remove(childIndices[0]);
foldersWereRemoved(parent,childIndices,removedChildren);
if (!moved) foldersWereDeleted(parent,childIndices,removedChildren);
}
public void removeFolder(MandelListFolder node)
{
_removeFolder(node,false);
}
public void foldersWereInserted(MandelListFolder node, int[] childIndices)
{
if (listenerList!=null&&node!=null&&childIndices!=null&&childIndices.length>0) {
int cCount=childIndices.length;
Object[] newChildren=new Object[cCount];
for (int counter=0; counter<cCount; counter++)
newChildren[counter]=node.get(childIndices[counter]);
fireTreeNodesInserted(getPathToRoot(node), childIndices,
newChildren);
save();
}
}
public void foldersWereRemoved(MandelListFolder node, int[] childIndices,
Object[] removedChildren)
{
for (Object o:removedChildren) {
MandelListFolder f=(MandelListFolder)o;
cleanup(f);
}
if (listenerList!=null&&node!=null&&childIndices!=null&&childIndices.length>0) {
fireTreeNodesRemoved(getPathToRoot(node), childIndices,
removedChildren);
}
save();
}
protected void foldersWereDeleted(MandelListFolder node, int[] childIndices,
Object[] removedChildren)
{
if (listenerList!=null&&node!=null&&childIndices!=null&&childIndices.length>0) {
fireFoldersDeleted(getPathToRoot(node), childIndices,
removedChildren);
}
}
public void setThumbnailName(MandelListFolder folder, QualifiedMandelName name)
{
checkFolder(folder);
folder.setThumbnailName(name);
System.out.println("set thumbnail of "+folder.getPath()+" to "+name);
try {
folder.save();
}
catch (IOException ex) {
System.out.println("cannot save: "+ex);
}
this.fireTreeNodesChanged(getPathToRoot(folder), null, null);
}
public boolean isMoving()
{
return moving;
}
////////////////////////////////////////////////////////////////
// support
////////////////////////////////////////////////////////////////
// public boolean isLocalFolder(MandelListFolder f)
// {
// while (f!=null&&f!=getRoot()) f=f.getParent();
// return f==getRoot();
// }
public boolean isLocalFolder(MandelListFolder f)
{
dump(f);
return f.getMandelListFolderTree()==getFolderTree();
}
protected void checkFolder(MandelListFolder f)
{
if (!isLocalFolder(f))
throw new IllegalArgumentException("none local folder");
}
protected void dump(MandelListFolder f)
{
if (debug) {
while (f!=null) {
System.out.println("* "+f);
f=f.getParent();
}
System.out.println("--");
}
}
////////////////////////////////////////////////////////////////
// list model
protected MandelListTableModel createMandelListModel(MandelListFolder f)
{
return new TreeMandelListModel(f.getMandelList(), getMandelScanner());
}
protected abstract MandelScanner getMandelScanner();
protected class TreeMandelListModel extends DefaultMandelListTableModel {
public TreeMandelListModel(MandelList list, MandelScanner scanner)
{
super(list, scanner);
}
@Override
public void refresh()
{
super.refresh();
fireTreeStructureChanged();
}
@Override
public void refresh(Environment env)
{
super.refresh(env);
fireTreeStructureChanged();
}
private void fireTreeStructureChanged()
{
TreePath path=new TreePath(getRoot());
MandelListFolderTreeModelSupport.this.fireTreeStructureChanged(path);
}
}
////////////////////////////////////////////////////////////////
// drag'n drop support
////////////////////////////////////////////////////////////////
public DropMode getDropMode()
{
return DropMode.ON_OR_INSERT;
}
/**
* We support both copy and move actions.
*/
public int getSourceActions()
{
return TransferHandler.COPY_OR_MOVE;
}
public boolean canImport(TransferSupport info)
{
if (isModifiable()) {
MandelFolderTransferable t;
DropLocation dl=info.getDropLocation();
MandelListFolder folder=(MandelListFolder)(dl.getPath().
getLastPathComponent());
boolean insert=dl.getChildIndex()>=0;
// both drop on and in between
if (info.isDataFlavorSupported(MandelFolderTransferable.folderFlavor)) {
//System.out.println("support folder");
if (!isPathModifiable(dl.getPath())) return false;
if (!insert && folder.isLeaf()) return false;
try {
t=(MandelFolderTransferable)info.getTransferable().
getTransferData(MandelFolderTransferable.folderFlavor);
}
catch (Exception e) {
return false;
}
while (folder!=null) {
for (MandelListFolder f:t.getFolders()) {
if (f==folder) return false;
}
folder=folder.getParent();
}
if (info.getDropAction()==TransferHandler.LINK) {
info.setDropAction(TransferHandler.COPY);
}
return true;
}
// drop on only
if (!insert) {
if (folder.getMandelList()==null) return false;
if (!isPathListModifiable(dl.getPath())) return false;
if (info.isDataFlavorSupported(MandelTransferable.mandelFlavor)) {
return true;
}
if (info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
String data=(String)info.getTransferable().getTransferData(
DataFlavor.stringFlavor);
if (QualifiedMandelName.create(data)!=null) {
if (info.getDropAction()==TransferHandler.LINK) {
info.setDropAction(TransferHandler.COPY);
}
return true;
}
}
catch (Exception ex) {
}
}
}
}
else {
//System.out.println("not active");
}
return false;
}
/**
* Bundle up the selected items in a single list for export.
* Each line is separated by a newline.
*/
public Transferable createTransferable(DragLocation loc)
{
TreePath[] paths=loc.getSelectionPaths();
int cnt=0;
for (int i=0; i<paths.length; i++) {
if (isPathTransferable(paths[i])) cnt++;
}
if (cnt==0) return null;
MandelListFolder[] folders=new MandelListFolder[cnt];
cnt=0;
for (int i=0; i<paths.length; i++) {
if (isPathTransferable(paths[i])) {
folders[cnt++]=(MandelListFolder)paths[i].getLastPathComponent();
}
}
if (debug) System.out.println("create transferable folder");
return new MandelFolderTransferable(getFolderTree(), folders);
}
/**
* Perform the actual import. This demo only supports drag and drop.
*/
public boolean importData(TransferSupport info)
{
DropLocation dl=info.getDropLocation();
TreePath path=dl.getPath();
boolean insert=dl.getChildIndex()>=0;
boolean done=true;
MandelListFolder folder=((MandelListFolder)path.getLastPathComponent());
Transferable t=info.getTransferable();
QualifiedMandelName[] data;
MandelListFolder[] dropfolders;
// if (info.getDropAction()==TransferHandler.LINK) {
// info.setDropAction(TransferHandler.COPY);
// }
// Perform the actual import.
try {
MandelFolderTransferable trans=(MandelFolderTransferable)t.getTransferData(
MandelFolderTransferable.folderFlavor);
dropfolders=trans.getFolders();
if (debug) System.out.println("import folder");
if (trans.getSource()!=getFolderTree()||
info.getDropAction()!=TransferHandler.MOVE) {
// handle foreign folders
insertFolders(dl.getChildIndex(), dropfolders, folder);
}
else {
// handle own folders
moveFolders(dl.getChildIndex(), dropfolders, folder);
done=false;
}
}
catch (Exception e) {
check(e);
try {
MandelTransferable trans;
trans=(MandelTransferable)t.getTransferData(
MandelTransferable.mandelFlavor);
if (trans.getSource()==folder.getMandelList()) {
if (debug) System.out.println("self drop");
return false;
}
data=trans.getNames();
if (debug) System.out.println("import mandel");
addAll(folder, data);
}
catch (Exception ey) {
check(ey);
try {
String name=(String)t.getTransferData(DataFlavor.stringFlavor);
if (debug) System.out.println("import string");
QualifiedMandelName mn=QualifiedMandelName.create(name);
if (mn==null) return false;
add(folder, mn);
}
catch (Exception ex) {
check(ex);
return false;
}
}
}
return done;
}
private void check(Exception e)
{
if (!(e instanceof UnsupportedFlavorException)) {
e.printStackTrace(System.out);
}
}
/**
* Remove the items moved from the list.
*/
public void exportDone(Transferable data, int action)
{
if (debug) System.out.println("action = "+action+"/"+TransferHandler.MOVE);
// skip for internal move
MandelFolderTransferable trans=(MandelFolderTransferable)data;
MandelListFolder[] folders=trans.getFolders();
if (action==TransferHandler.MOVE && trans.getSource()!=getFolderTree()) {
for (int i=folders.length-1; i>=0; i--) {
removeFolder(folders[i]);
}
}
}
/////////////////////////////////////////////////////////////////////////
// util
/////////////////////////////////////////////////////////////////////////
static public String path(TreePath path)
{
String s=((MandelListFolder)path.getLastPathComponent()).getName();
if (path.getParentPath()!=null)
return path(path.getParentPath())+"/"+s;
return s;
}
static public TreePath getPathToRoot(MandelListFolder node)
{
if (node.getParent()==null) return new TreePath(node);
return getPathToRoot(node.getParent()).pathByAddingChild(node);
}
/////////////////////////////////////////////////////////////////////////
//MandelListFolderTreeModel
/////////////////////////////////////////////////////////////////////////
public void addMandelListFolderTreeModelListener(MandelListFolderTreeModelListener l)
{
listenerList.add(MandelListFolderTreeModelListener.class, l);
}
public void removeMandelListFolderTreeModelListener(MandelListFolderTreeModelListener l)
{
listenerList.remove(MandelListFolderTreeModelListener.class, l);
}
public MandelListFolderTreeModelListener[] getMandelListFolderTreeModelListeners()
{
return getListeners(MandelListFolderTreeModelListener.class);
}
protected void fireFoldersDeleted(TreePath path,
int[] childIndices,
Object[] children)
{
// Guaranteed to return a non-null array
Object[] listeners=listenerList.getListenerList();
TreeModelEvent e=null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i=listeners.length-2; i>=0; i-=2) {
if (listeners[i]==TreeModelListener.class) {
// Lazily create the event:
if (e==null)
e=new TreeModelEvent(this, path,
childIndices, children);
((MandelListFolderTreeModelListener)listeners[i+1]).foldersDeleted(e);
}
}
}
protected void fireFoldersDeleted(TreeModelEvent e)
{
// Guaranteed to return a non-null array
Object[] listeners=listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i=listeners.length-2; i>=0; i-=2) {
if (listeners[i]==MandelListFolderTreeModelListener.class) {
// Lazily create the event:
((MandelListFolderTreeModelListener)listeners[i+1]).foldersDeleted(e);
}
}
}
}