/*
* 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.Transferable;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Stack;
import javax.swing.DropMode;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreePath;
import com.mandelsoft.mand.QualifiedMandelName;
import com.mandelsoft.mand.util.UniqueArrayMandelList;
import com.mandelsoft.mand.util.MandelListFolder;
import com.mandelsoft.mand.util.MandelListFolderTree;
import com.mandelsoft.swing.DnDTreeModel.DragLocation;
import com.mandelsoft.swing.DnDTreeModel.TransferSupport;
import com.mandelsoft.swing.TreeModelListenerSupportBase;
/**
*
* @author Uwe Krüger
*/
public class MandelListFolderBrowserModel extends TreeModelListenerSupportBase
implements MandelListFolderTreeModel {
static private boolean debug=false;
static public final String PROP_MODIFIABLE = "modifiable";
static public final String PROP_MODEL = "model";
static public final String PROP_ACTIVE_NAME = "activeName";
static public final String PROP_ACTIVE_PATH = "activePath";
static public final String PROP_ACTIVE_FOLDER = "activeFolder";
private MandelListFolderTreeModel fmodel;
//
// active list display
//
private TreePath activepath;
private MandelListFolder activefolder;
private String activename;
private MandelListTableModel lmodel;
private MandelListTableModel lempty;
private MandelListFolderTreeModel fempty;
private ForwardingListener listener;
private boolean modifiable;
public MandelListFolderBrowserModel(MandelListFolderTreeModel fmodel)
{
lempty=new DefaultMandelListTableModel(new UniqueArrayMandelList(),null);
fempty=new DefaultMandelListFolderTreeModel("empty",lempty);
listener=new ForwardingListener();
setFolderTreeModel(fmodel);
}
public void clear()
{
setFolderTreeModel(null);
}
public boolean isModifiable()
{
return modifiable;
}
public void setModifiable(boolean modifiable)
{
System.out.println("browser model modifiable: "+modifiable);
//new Throwable().printStackTrace();
if (this.modifiable!=modifiable) {
this.modifiable=modifiable;
firePropertyChange(PROP_MODIFIABLE,!modifiable,modifiable);
}
}
public void setFolderTreeModel(MandelListFolderTreeModel model)
{
if (model==null) model=fempty;
if (model!=fmodel) {
MandelListFolderTreeModel old=fmodel;
if (old!=null) {
old.removeTreeModelListener(listener);
}
this.fmodel=model;
if (fmodel!=null) {
fmodel.addTreeModelListener(listener);
}
firePropertyChange(PROP_MODEL,old,fmodel);
setModifiable(fmodel.isModifiable());
setActivePath(new TreePath(fmodel.getRoot()));
fireTreeStructureChanged(new TreePath(fmodel.getRoot()));
}
}
public MandelListFolderTreeModel getFolderTreeModel()
{
if (fmodel==fempty) return null;
return fmodel;
}
public void setActiveFolder(MandelListFolder f)
{
// System.out.println("*** fmodel: "+getFolderTreeModel());
// System.out.println("folder: "+f.getMandelListFolderTree());
// System.out.println("model: "+getFolderTreeModel().getFolderTree());
if (f.getMandelListFolderTree()!=getFolderTreeModel().getFolderTree()) {
throw new IllegalArgumentException("non matching folder tree");
}
setActivePath(MandelListFolderTreeModelSupport.getPathToRoot(f));
}
public void setActivePath(TreePath path)
{
setActivePath(path,false);
}
private void setActivePath(TreePath path, boolean obsolete)
{
if (path==null) path=new TreePath(fmodel.getRoot());
TreePath oldpath=activepath;
MandelListFolder folder=(MandelListFolder)path.getLastPathComponent();
if (folder!=activefolder) {
if (folder!=null && !fmodel.isLocalFolder(folder)) {
throw new IllegalArgumentException("setting non local folder");
}
MandelListFolder oldfolder=activefolder;
activefolder=folder;
activepath=path;
this.lmodel=folder==null?null:fmodel.getMandelListModel(activefolder);
if (lmodel==null) {
System.out.println("set empty list");
lmodel=lempty;
}
firePropertyChange(PROP_ACTIVE_FOLDER, oldfolder, activefolder);
if (!obsolete && oldfolder!=null) fireTreeNodesChanged(oldpath,null,null);
if (activefolder!=null) fireTreeNodesChanged(activepath,null,null);
}
updateActiveName();
if (!activepath.equals(oldpath)) {
firePropertyChange(PROP_ACTIVE_PATH, oldpath, activepath);
}
}
public TreePath getActivePath()
{
return activepath;
}
public MandelListFolder getActiveFolder()
{
return activefolder;
}
public MandelListTableModel getActiveListModel()
{
return lmodel;
}
public String getActivePathName()
{
return activename;
}
protected void updateActiveName()
{
String name=determineActivePathName();
if (activename==null || !activename.equals(name)) {
String old=activename;
activename=name;
this.firePropertyChange(PROP_ACTIVE_NAME, old, name);
}
}
protected String determineActivePathName()
{
StringBuilder b=new StringBuilder();
String sep="";
for (Object o:getActivePath().getPath()) {
b.append(sep);
sep="/";
b.append(((MandelListFolder)o).getName());
}
return b.toString();
}
private static void dump(String msg, TreePath p)
{
if (debug) {
System.out.println(msg+":");
Object[] oa=p.getPath();
for (Object o:oa) {
System.out.println(" "+o);
}
System.out.println("--");
}
}
private static void dump(String msg, TreeModelEvent e)
{
if (debug) {
System.out.println(msg+":");
Object[] oa=e.getPath();
for (Object o:oa) {
System.out.println(" "+o);
}
oa=e.getChildren();
if (oa!=null&&oa.length>0) {
System.out.println(" children:");
for (Object o:oa) {
System.out.println(" "+o);
}
}
}
}
static public TreePath getParentPath(TreeModelEvent e, TreePath path)
{
TreePath parent=null;
dump("check parent",e);
dump("for",path);
if (e.getTreePath().isDescendant(path)) {
Object[] children=e.getChildren();
int length=e.getTreePath().getPathCount();
if (children!=null&&children.length>0) {
if (length!=path.getPathCount()) {
Object n=path.getPathComponent(length);
for (int i=0; i<children.length; i++) {
if (children[i]==n) {
parent=e.getTreePath().pathByAddingChild(n);
break;
}
}
}
}
else parent=e.getTreePath();
}
return parent;
}
/////////////////////////////////////////////////////////////////////
// util
/////////////////////////////////////////////////////////////////////
private void fireTreeNodesChanged(MandelListFolder f)
{
fireTreeNodesChanged(MandelListFolderTreeModelSupport.getPathToRoot(f),null,null);
}
private boolean contains(TreePath p, MandelListFolder f)
{
while (p!=null) {
if (p.getLastPathComponent()==f) return true;
p=p.getParentPath();
}
return false;
}
private TreePath subPathTo(TreePath p, MandelListFolder f)
{
while (p!=null) {
if (p.getLastPathComponent()==f) return p;
p=p.getParentPath();
}
return null;
}
private TreePath findFolder(TreePath root, MandelListFolder f)
{ Stack<MandelListFolder> path=new Stack<MandelListFolder>();
MandelListFolder r=(MandelListFolder)root.getLastPathComponent();
if (r==f) return root;
for (MandelListFolder c:r) {
if (findFolder(path,c,f)) {
for (MandelListFolder s:path) {
root=root.pathByAddingChild(s);
}
return root;
}
}
return null;
}
private boolean findFolder(Stack<MandelListFolder> path,
MandelListFolder r, MandelListFolder f)
{
path.push(r);
if (r==f) {
return true;
}
for (MandelListFolder c:r) {
if (findFolder(path,c,f))
return true;
}
path.pop();
return false;
}
/////////////////////////////////////////////////////////////////////////
//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);
}
}
}
/////////////////////////////////////////////////////////////////////
// event forwarding
/////////////////////////////////////////////////////////////////////
private class ForwardingListener implements PropertyChangeListener,
TreeModelListener,
MandelListFolderTreeModelListener {
public void propertyChange(PropertyChangeEvent evt)
{
firePropertyChange(evt.getPropertyName(),evt.getOldValue(),
evt.getNewValue());
}
public void foldersDeleted(TreeModelEvent e)
{
fireFoldersDeleted(e.getTreePath(),e.getChildIndices(),e.getChildren());
}
public void treeNodesChanged(TreeModelEvent e)
{
System.out.println("forward TNC ");
fireTreeNodesChanged(e.getTreePath(),e.getChildIndices(),e.getChildren());
MandelListFolder f;
Object[] children=e.getChildren();
f=(MandelListFolder)e.getTreePath().getLastPathComponent();
if (MandelListFolderListModel.getParentPath(e, getActivePath())!=null) {
updateActiveName();
}
}
public void treeNodesInserted(TreeModelEvent e)
{ Object[] children=e.getChildren();
TreePath a=null;
fireTreeNodesInserted(e.getTreePath(),e.getChildIndices(),children);
if (children==null || children.length==0) {
a=findFolder(e.getTreePath(),activefolder);
}
else {
for (int i=0; i<children.length && a==null; i++) {
a=findFolder(e.getTreePath().pathByAddingChild(children[i]),
activefolder);
}
}
if (a!=null) {
// switch active path to new path
// now the model is prepared for a later remove
// !!! Moves must FIRST raise the insert event and
// !!! afterwards the remove event.
setActivePath(a);
}
}
public void treeNodesRemoved(TreeModelEvent e)
{
fireTreeNodesRemoved(e.getTreePath(),e.getChildIndices(),e.getChildren());
if (!isMoving()) handleReorg(e,true);
}
public void treeStructureChanged(TreeModelEvent e)
{
fireTreeStructureChanged(e.getPath(),e.getChildIndices(),e.getChildren());
handleReorg(e,false);
}
private void handleReorg(TreeModelEvent e, boolean remove)
{
TreePath parent=getParentPath(e, activepath);
if (parent!=null) {
// this path has been removed and current folder is
// descendant of this element.
setActivePath(remove?parent.getParentPath():parent,true);
}
}
}
/////////////////////////////////////////////////////////////////////
// regular folder model
/////////////////////////////////////////////////////////////////////
public MandelListFolderTreeModel getEffectiveFolderTreeModel()
{
return getFolderTreeModel();
}
public void valueForPathChanged(TreePath path, Object newValue)
{
fmodel.valueForPathChanged(path, newValue);
}
public void save()
{
fmodel.save();
}
public MandelListFolder getRoot()
{
return fmodel.getRoot();
}
public MandelListTableModel getRootModel()
{
return fmodel.getRootModel();
}
public MandelListTableModel getMandelListModel(Object folder)
{
return fmodel.getMandelListModel(folder);
}
public boolean isPathTransferable(TreePath path)
{
return fmodel.isPathTransferable(path);
}
public boolean isPathModifiable(TreePath path)
{
return fmodel.isPathModifiable(path);
}
public boolean isPathListModifiable(TreePath path)
{
return fmodel.isPathListModifiable(path);
}
public boolean isPathEditable(TreePath path)
{
return fmodel.isPathEditable(path);
}
public boolean isLocalFolder(MandelListFolder f)
{
return fmodel.isLocalFolder(f);
}
public boolean isLeaf(Object node)
{
return fmodel.isLeaf(node);
}
public boolean insertFolders(int index, MandelListFolder[] folders,
MandelListFolder parent)
{
return fmodel.insertFolders(index, folders, parent);
}
public MandelListFolder insertFolder(String name, MandelListFolder parent)
{
return fmodel.insertFolder(name, parent);
}
public void removeFolder(MandelListFolder node)
{
fmodel.removeFolder(node);
}
public void moveFolders(int index, MandelListFolder[] folders,
MandelListFolder parent)
{
fmodel.moveFolders(index, folders, parent);
}
public void setThumbnailName(MandelListFolder folder, QualifiedMandelName name)
{
fmodel.setThumbnailName(folder, name);
}
public boolean isMoving()
{
return fmodel.isMoving();
}
public int getIndexOfChild(Object parent, Object child)
{
return fmodel.getIndexOfChild(parent, child);
}
public MandelListFolderTree getFolderTree()
{
return fmodel.getFolderTree();
}
public int getChildCount(Object parent)
{
return fmodel.getChildCount(parent);
}
public MandelListFolder getChild(MandelListFolder parent, String name)
{
return fmodel.getChild(parent, name);
}
public Object getChild(Object parent, int index)
{
return fmodel.getChild(parent, index);
}
public String convertValueToText(Object value, boolean selected,
boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
return fmodel.convertValueToText(value, selected, expanded, leaf, row,
hasFocus);
}
public void addAll(MandelListFolder f, QualifiedMandelName[] list)
{
fmodel.addAll(f, list);
}
public void add(MandelListFolder f, QualifiedMandelName name)
{
fmodel.add(f, name);
}
public void remove(MandelListFolder f, QualifiedMandelName name)
{
fmodel.remove(f, name);
}
public boolean importData(TransferSupport info)
{
return fmodel.importData(info);
}
public int getSourceActions()
{
return fmodel.getSourceActions();
}
public DropMode getDropMode()
{
return fmodel.getDropMode();
}
public void exportDone(Transferable data, int action)
{
fmodel.exportDone(data, action);
}
public Transferable createTransferable(DragLocation loc)
{
return fmodel.createTransferable(loc);
}
public boolean canImport(TransferSupport info)
{
return fmodel.canImport(info);
}
}