/*******************************************************************************
* Copyright (c) 2007, 2008 Gregory Jordan
*
* This file is part of PhyloWidget.
*
* PhyloWidget is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 2 of the License, or (at your option) any later
* version.
*
* PhyloWidget 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 for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* PhyloWidget. If not, see <http://www.gnu.org/licenses/>.
*/
package org.phylowidget.ui;
import org.andrewberman.ui.AbstractUIObject;
import org.andrewberman.ui.StringClipboard;
import org.jgrapht.Graphs;
import org.jgrapht.traverse.BreadthFirstIterator;
import org.phylowidget.PhyloTree;
import org.phylowidget.tree.CachedRootedTree;
import org.phylowidget.tree.DefaultVertex;
import org.phylowidget.tree.PhyloNode;
import org.phylowidget.tree.RootedTree;
import org.phylowidget.tree.TreeIO;
import processing.core.PApplet;
public class TreeClipboard extends AbstractUIObject
{
String newickString;
// String fullNewickString;
RootedTree origTree;
PhyloNode origVertex;
public TreeClipboard(PApplet p)
{
}
public boolean isEmpty()
{
if (newickString == null)
return true;
else
return (newickString.length() == 0);
}
public static final int CLIPBOARD_UPDATED = 123432;
public void clearClipboard()
{
clearTree();
newickString = "";
fireEvent(CLIPBOARD_UPDATED);
}
void clearTree()
{
if (origTree != null)
{
setStateRecursive(origTree, (PhyloNode) origTree.getRoot(),
PhyloNode.NONE);
origTree = null;
}
}
public synchronized void cut(RootedTree tree, PhyloNode cutMe)
{
clearClipboard();
setClip(tree, cutMe);
setStateRecursive(tree, cutMe, PhyloNode.CUT);
}
public synchronized void copy(RootedTree tree, PhyloNode copyMe)
{
clearClipboard();
setClip(tree, copyMe);
setStateRecursive(tree, copyMe, PhyloNode.COPY);
}
public String getClipboardText()
{
return newickString;
}
public void setClip(RootedTree tree, PhyloNode node)
{
setStateRecursive(tree,(PhyloNode) tree.getRoot(),PhyloNode.NONE);
RootedTree clone = tree.cloneSubtree(node);
newickString = TreeIO.createNHXString(clone);
origTree = tree;
origVertex = node;
fireEvent(CLIPBOARD_UPDATED);
}
public void setClipFromJS(String newick)
{
clearTree();
newickString = newick;
origTree = null;
origVertex = null;
}
PhyloTree loadClip()
{
if (newickString == null || newickString.length() == 0)
{
/*
* Try loading a tree from the system clipboard.
*/
newickString = StringClipboard.instance.fromClipboard();
}
if (newickString == null || newickString.length() == 0)
throw new Error("Called TreeClipboard.paste() with empty clipboard");
PhyloTree clipTree = new PhyloTree();
TreeIO.parseNewickString(clipTree, newickString);
if (origTree != null && origVertex != null)
{
setPositionRecursive(clipTree, (PhyloNode) clipTree.getRoot(),
origVertex);
}
return clipTree;
}
public synchronized void swap(RootedTree destTree, PhyloNode destNode)
{
synchronized (destTree)
{
/*
* If we're swapping within the same tree, then it's easy:
*/
if (origTree == destTree && origVertex != null)
{
Object p1 = origTree.getParentOf(origVertex);
Object p2 = origTree.getParentOf(destNode);
if (p1 != null && p2 != null)
{
origTree.removeEdge(p1, origVertex);
origTree.removeEdge(p2, destNode);
origTree.addEdge(p1, destNode);
origTree.addEdge(p2, origVertex);
}
} else
{
/*
* If we're swapping with an "external" clipboard, then it's also easy.
*/
PhyloTree clipTree = loadClip();
setClip(destTree, destNode);
setClipFromJS(newickString);
Object p1 = destTree.getParentOf(destNode);
destTree.deleteSubtree(destNode);
Graphs.addGraph(destTree, clipTree);
if (p1 == null)
{
destTree.setRoot(clipTree.getRoot());
} else
{
destTree.addEdge(p1, clipTree.getRoot());
}
}
}
}
public synchronized void paste(CachedRootedTree destTree, PhyloNode destNode)
{
// Translate the newick string into a RooteTree.
PhyloTree tree = loadClip();
// Add the clone's vertices and edges to the destination tree.
synchronized (destTree)
{
destTree.setHoldCalculations(true);
Graphs.addGraph(destTree, tree);
// Insert the clone's root vertex into the midpoint above destNode.
if (destTree.getParentOf(destNode) == null)
{
destTree.addEdge(destNode, tree.getRoot());
} else
{
DefaultVertex internalVertex = destTree.createAndAddVertex();
((PhyloNode) internalVertex).setPosition(origVertex);
destTree.insertNodeBetween(destTree.getParentOf(destNode),
destNode, internalVertex);
destTree.addEdge(internalVertex, tree.getRoot());
}
destTree.setHoldCalculations(false);
destTree.modPlus();
clearCutNodes();
}
}
void clearCutNodes()
{
if (origTree != null)
{
if (origVertex != null && origVertex.getState() == PhyloNode.CUT)
{
origTree.deleteSubtree(origVertex);
origTree.removeElbowsBelow(origTree.getRoot());
setStateRecursive(origTree, (PhyloNode) origTree.getRoot(),
PhyloNode.NONE);
origVertex.found = false;
origVertex = null;
origTree.modPlus();
}
}
}
void setPositionRecursive(RootedTree tree, PhyloNode base,
PhyloNode positionToMe)
{
BreadthFirstIterator bfi = new BreadthFirstIterator(tree, base);
while (bfi.hasNext())
{
PhyloNode n = (PhyloNode) bfi.next();
n.setPosition(positionToMe);
}
}
void setStateRecursive(RootedTree tree, PhyloNode base, int state)
{
BreadthFirstIterator bfi = new BreadthFirstIterator(tree, base);
while (bfi.hasNext())
{
PhyloNode n = (PhyloNode) bfi.next();
n.setState(state);
}
}
}