/*

# Program description:
The purpose of this file is to be a canvas where the phylogenetic
tree is drawn.  The canvas's principal activities are managing
GUI issues such as translation, zoom, colors, etc.  When the tree
is actually painted, the job is "outsourced" to PhyloCluster.
This is accomplished by the paint method calling the corresponding
paint method in PhyloCluster.
#************************************************************
# Libraries and software support:
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
BEA JRockit(R) (build R27.3.1-1_CR336511-87524-1.5.0_11-20070817-1356-linux-x86_64, compiled mode)
#************************************************************
# Development environment:
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
BEA JRockit(R) (build R27.3.1-1_CR336511-87524-1.5.0_11-20070817-1356-linux-x86_64, compiled mode)

#************************************************************
# System requirements:
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
BEA JRockit(R) (build R27.3.1-1_CR336511-87524-1.5.0_11-20070817-1356-linux-x86_64, compiled mode)
#************************************************************
# Start date:
# Update history:
#            DATE            MODIFICATION
#
#************************************************************
# Test history:
#  TEST PROTOCOL           DATE       TEST RESULTS
#
#************************************************************
# Programmer comments:
1)  Given a PhyloCluster that is taken/assumed to be the root
of a phylogenetic tree, put this canvas in a container.  (See constructor).
This file also contains the implementations for ScaleFrame and
ColorFrame that are windows with GUI controls for scaling and
colors.  The canvas has a PopUpMenu and takes care of additional
GUI activities (such as saving to clipboard/file).
This file also contains implementations of NameFrame which can
be used to rename the clusters. It keeps the original names
in memory to "reset" back to the original names.
#************************************************************
*/

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.Image;
import java.util.*;
import javax.swing.plaf.basic.*;
import com.sun.image.codec.jpeg.*;
import javax.imageio.ImageIO;
import java.awt.datatransfer.*;
import java.awt.geom.*;
import java.applet.*;
import javax.imageio.*;

public class PhyloCanvas extends JPanel implements MouseListener, MouseMotionListener, ActionListener
	{

	//where to possibly write a jpeg file!
	public static File outputJpeg;

	private static AppletContext myAppletContext=null;

	public void setStaticAppletContext(AppletContext ac)
		{
		myAppletContext=ac;
		}

	JApplet ownerApplet=null;


	//reference to root
	PhyloCluster root=null;

	//reference to a random cluster
	PhyloCluster randomCluster=null;

	/*variables to hold the scales
	scale is a general factor used in all drawing
	hscale (for height scale) is used in the spacing *between* the nodes 
		affecting the vertical height/size of the tree
	lscale (for length scale) is used for scalign in drawing the branches affecting 
		how wide the tree is by affecting the drawing of the lengths of the branches*/
	double scale,hscale,lscale;

	//save the scale in a varaible to revert back
	double iscale;

	//variables to keep track of translation of tree
	int tranx;
	int trany;

	//variables to save the original translation to revert back
	int itranx;
	int itrany;

	//flag to know if tree is being dragged around
	boolean isInDrag=false;

	//
	int mouseMode=(-1);

	//log the time when the first time the paint method is called
	long firstPaintTime=0;


	//a newick string representing the loaded p-tree
	String newickString=new String("");

	//reference to a color frame (to color the tree)
	ColorFrame cf=null;

	//reference to a popupmenu
	JPopupMenu myMenu=null;

	//reference to scaleframe
	ScaleFrame sf=null;

	//references to items in the menu
	String[] menuTitles=null;
	JMenuItem[] menuItems=null;
	JRadioButtonMenuItem[] radioMenuButtons=null;
	
	/*keep track of whether or not this is the first time
	paint is called.*/
	boolean firstPaint=true;


	//reference to name frame
	NameFrame nf=null;

	//ref to rerootframe
	RerootFrame rf=null;


	//ref to branchswap frame
	BranchSwapFrame bf=null;

	private int readjusmentPeriod=1000;

	//ref to legendscale frame
	LegendScaleFrame lsf=null;


	//ref to choosenew root frame
	ChooseNewRootFrame cnrFrame=null;

	public static PhyloNode tearOffRootNamedRootNode(PhyloNode entryPoint)
		{
		PhyloNode rootNamedNode=entryPoint.findNodeByName("ROOT");

		if(rootNamedNode==null)
			{
			return entryPoint;
			}
		else
			{
			/*
			the root connected to a node N with three neighbors, the root, and two other nodes.
			what should be done is that the root should be removed and the two-neighbored node
			should be made part of the edge
			*/
			PhyloNode[] rootNeighbors=rootNamedNode.getNeighbors();

			if(rootNeighbors.length!=1)
				{
				System.out.println("rootNeighbors.length is "+rootNeighbors.length);
				for(int n=0;n<rootNeighbors.length;n++)
					{
					System.out.println("Neighbor "+n+" : "+rootNeighbors[n].getName());
					}
				throw new RuntimeException("Error - root has multiple neighbors!!!!");
				}
			else
				{
				PhyloNode root3way=rootNeighbors[0];
				root3way.removeNeighbor(rootNamedNode);
				PhyloNode[] nonad=root3way.getNeighbors();
				if(nonad.length!=2)
					{
					throw new RuntimeException("Error - three-way node didn't delete a neighbor correctly!!!");
					}
				double nonadist=Math.abs(root3way.getDisanceToImmediateNeighbor(nonad[0]));
				nonadist=nonadist+Math.abs(root3way.getDisanceToImmediateNeighbor(nonad[1]));
				root3way.removeNeighbor(nonad[0]);
				root3way.removeNeighbor(nonad[1]);
				nonad[0].addNeighbor(nonad[1],nonadist);				
				return nonad[0];
				}
			}
		}	


	public class ChooseExactRootFrame extends JDialog implements ActionListener,ListSelectionListener,ChangeListener
		{
		
		JList nodePairsList=null;
		JList leftSubTreeList=null;
		JList rightSubTreeList=null;
		JSlider rootPlaceSlider=null;
		JButton placeRootButton=null;
		JTextArea rootDescriptionTA=null;
		PhyloCanvas owner=null;
		PhyloNode[][] nodePairs=null;
		String[] pairDescriptions=null;
		int nearLeft=50;
		int nearRight=50;


		private void fillJList()
			{
			System.out.println("Now filling jlists....");
			System.out.println("The newick string is '"+owner.getNewickString()+"'.");

			PhyloNode root=TreeUtils.buildUnrootedTreeFromNewickString(owner.getNewickString());
			nodePairs=root.getListOfAllAdjacentPairs();
			nodePairs=filterOutRootPairs(nodePairs);

			String[] pairDescriptions=new String[nodePairs.length];
			for(int d=0;d<pairDescriptions.length;d++)
				{
				pairDescriptions[d]=new String("Pair #"+(d+1)+" ");
				}

			leftSubTreeList=new JList();
			rightSubTreeList=new JList();
			rootDescriptionTA=new JTextArea("Put Description here.");
			nodePairsList=new JList(pairDescriptions);
			nodePairsList.addListSelectionListener(this);
			nodePairsList.setSelectedIndex(0);
			}

		public ChooseExactRootFrame(PhyloCanvas o)
			{

			//take care of java stuff FIRST!
			super();

			if(o==null)
				{
				throw new NullPointerException("Error - owner phylocanvas is null!");
				}
			else
				{
				owner=o;
				}

			//prepare lists
			fillJList();

			GridBagConstraints gbc=new GridBagConstraints();
			getContentPane().setLayout(new GridBagLayout());
			gbc.weightx=1.0;
			gbc.weighty=1.0;
			gbc.gridx=0;gbc.gridy=0;
			gbc.fill=gbc.BOTH;
			gbc.anchor=gbc.CENTER;
			gbc.gridwidth=2;
			getContentPane().add(new JLabel("  Select An Adjacent Node Pair Whose Connecting Edge Will Hold The New Root"),gbc);
			gbc.gridy++;
			getContentPane().add(new JScrollPane(nodePairsList),gbc);
			gbc.gridy++;gbc.gridx=0;
			gbc.gridwidth=1;
			getContentPane().add(new JLabel("  Left Subtree"),gbc);
			gbc.gridx=1;
			getContentPane().add(new JLabel("  Right Subtree"),gbc);
			gbc.gridx=0;
			gbc.gridy++;
			getContentPane().add(new JScrollPane(leftSubTreeList),gbc);
			gbc.gridx=1;
			getContentPane().add(new JScrollPane(rightSubTreeList),gbc);
			gbc.gridy++;gbc.gridx=0;gbc.gridwidth=2;
			rootDescriptionTA.setLineWrap(true);
			getContentPane().add(new JScrollPane(rootDescriptionTA),gbc);
			rootPlaceSlider=new JSlider(JSlider.HORIZONTAL);
			rootPlaceSlider.addChangeListener(this);
			stateChanged(new ChangeEvent(rootPlaceSlider));
			gbc.gridy++;
			getContentPane().add(rootPlaceSlider,gbc);
			placeRootButton=new JButton("Place Root Using Slider Position");
			placeRootButton.addActionListener(this);
			gbc.gridy++;gbc.gridx=0;
			gbc.gridwidth=2;
			getContentPane().add(placeRootButton,gbc);
			setTitle(new String("ReRoot The Tree Anywhere"));
			setSize(750,750);
			setVisible(true);
			show();
			}
		
		public void stateChanged(ChangeEvent ce)
			{
			//this handles events for the slider
			//rootDescriptionTA.setText(new String(rootPlaceSlider.toString()));
			nearLeft=rootPlaceSlider.getValue();
			nearRight=100-rootPlaceSlider.getValue();
			String placeString=new String("The distance between the root and the left node will be "+nearLeft+" percent of the connecting arc.  ");
			placeString=placeString+new String("The distance between the root and the right node will be "+nearRight+" percent of the connecting arc.");
			rootDescriptionTA.setText(placeString);
			}

		public void actionPerformed(ActionEvent ae)
			{
			//this handles events for the button
			String message=new String("The tree will be rerooted according to the root placement described.");
			String title=new String("Proceed With ReRooting");
			int cont=JOptionPane.showConfirmDialog((Component)(this),message,title,JOptionPane.OK_CANCEL_OPTION);

			if(cont==JOptionPane.CANCEL_OPTION)
				{
				//cancel rerooting!!!
				message=new String("ReRooting operation cancelled!");
				title=new String("Operation Cancelled!");
				JOptionPane.showMessageDialog((Component)(this),message,title,JOptionPane.OK_OPTION);
				}
			else
				{
				//proceed with rerooting!!!
				PhyloNode leftNode=nodePairs[nodePairsList.getSelectedIndex()][0];
				PhyloNode rightNode=nodePairs[nodePairsList.getSelectedIndex()][1];
				if(!leftNode.isAnImmediateNeighborOf(rightNode))
					{
					throw new RuntimeException("ERROR - it appears that the nodes in the node pairs aren't neighbors!!!!!");
					}		
				//PhyloNode withoutroot=tearOffRootNamedRootNode(leftNode);				
				double totalDist=leftNode.getDisanceToImmediateNeighbor(rightNode);
				double fraction=(double)(rootPlaceSlider.getValue())*0.01;
				double ldist=fraction*totalDist;
				double rdist=(1.0-fraction)*totalDist;
				leftNode.removeNeighbor(rightNode);
				PhyloNode rootPlace=new PhyloNode("");
				rootPlace.addNeighbor(leftNode,ldist);
				rootPlace.addNeighbor(rightNode,rdist);
				PhyloNode rootNode=new PhyloNode("ROOT");
				rootNode.addNeighbor(rootPlace,Math.min(ldist,rdist));						
				PhyloCluster newRoot=new PhyloCluster(rootPlace,rootNode,null,true);
				System.out.println("The new clustering is :");
				System.out.println(newRoot.toString());

				owner.firstPaintTime=0;
				owner.root=newRoot;
				owner.root.spread();
				owner.repaint();
				owner.repaint();

				setVisible(false);
				}
			}

		private PhyloNode[][] filterOutRootPairs(PhyloNode[][] ppairs)
			{
			for(int i=0;i<ppairs.length;i++)
				{
				if(ppairs[i][0].getName().compareTo("ROOT")==0
					||
					ppairs[i][1].getName().compareTo("ROOT")==0)
					{
					ppairs[i]=null;
					}
				}

			PhyloNode[][] okpairs=null;

			for(int j=0;j<ppairs.length;j++)
				{
				if(ppairs[j]!=null)
					{
					okpairs=PhyloNode.addPairToPairList(ppairs[j][0],ppairs[j][1],okpairs);
					}
				}
			return okpairs;
			}



		public void valueChanged(ListSelectionEvent e)
			{
			//based on the selection in the main list, set the contents of the subtree lists
			PhyloNode leftNode=nodePairs[nodePairsList.getSelectedIndex()][0];
			PhyloNode rightNode=nodePairs[nodePairsList.getSelectedIndex()][1];

			PhyloNode[] leftNodes=leftNode.getNodesAwayFrom(rightNode);
			PhyloNode[] rightNodes=rightNode.getNodesAwayFrom(leftNode);
			
			if(leftNodes==null)
				{
				leftNodes=new PhyloNode[1];
				leftNodes[0]=leftNode;
				}
			if(leftNodes.length==0)
				{
				leftNodes=new PhyloNode[1];
				leftNodes[0]=leftNode;
				}
			if(rightNodes==null)
				{
				rightNodes=new PhyloNode[1];
				rightNodes[0]=rightNode;
				}
			if(rightNodes.length==0)
				{
				rightNodes=new PhyloNode[1];
				rightNodes[0]=rightNode;
				}

			String[] leftDescriptions=null;
			for(int ld=0;ld<leftNodes.length;ld++)
				{
				if(leftNodes[ld].getName().length()>1)
					{
					if(leftNodes[ld].getName().compareTo("ROOT")!=0)
						{
						leftDescriptions=TreeUtils.addLine(
												new String(leftNodes[ld].getName()),
												leftDescriptions
												);
						}
					}
				}
			String[] rightDescriptions=null;
			for(int rd=0;rd<rightNodes.length;rd++)
				{
				if(rightNodes[rd].getName().length()>1)
					{
					if(rightNodes[rd].getName().compareTo("ROOT")!=0)
						{
						rightDescriptions=TreeUtils.addLine(
												new String(rightNodes[rd].getName()),
												rightDescriptions
												);
						}
					}
				}
			
			leftSubTreeList.setListData(leftDescriptions);
			rightSubTreeList.setListData(rightDescriptions);		
			
			}
		}



	public class ChooseNewRootFrame extends JDialog implements ActionListener,ListSelectionListener
		{

		JLabel pairsLabel=null;
		JLabel subtreeOneLabel=null;
		JLabel subtreeTwoLabel=null;
		JButton rerootButton=null;
		JList subtreeOneList=null;
		JList subtreeTwoList=null;
		JList pairSelectionList=null;
		PhyloNode[] listOne=null;
		PhyloNode[] listTwo=null;
		PhyloNode[][] pairList=null;
		String[] nameList=null;
		PhyloCanvas owner=null;
		
		public void valueChanged(ListSelectionEvent e)
			{
			
			System.out.println("valueChanged called in ChooseNewRootFrame");
			fillOutSubListsProperly();

			} 

		public void actionPerformed(ActionEvent ae)
			{
			System.out.println("actionPerformed called in ChooseNewRootFrame");

			int answer=JOptionPane.showConfirmDialog(this,"This Will Reset All The Names.  Proceed?","Confirm ReRoot",JOptionPane.YES_NO_OPTION);
			if(!(answer==JOptionPane.YES_OPTION || answer==JOptionPane.OK_OPTION))
				{
				JOptionPane.showMessageDialog(this,"Operation Aborted!");
				return;
				}

			int selected=pairSelectionList.getSelectedIndex();
			//use the selection to access the nodes to reroot the tree!!!!
			PhyloNode firstNode=pairList[selected][0];
			PhyloNode secondNode=pairList[selected][1];

			System.out.println("The Node have been selected!");
			if(firstNode.isAnImmediateNeighborOf(secondNode))
				{
				System.out.println("Happily, they are immediate neighbors!");
				}
			else
				{
				throw new RuntimeException("Error, trying to put new root between two NON-NEIGHBORING nodes!");
				}
			double distBetween=firstNode.getDisanceToImmediateNeighbor(secondNode);
			double midDist=distBetween/2.0;

			PhyloNode userChosenRoot=new PhyloNode("User-SpecifiedRoot");
			PhyloNode userRootIntersection=new PhyloNode("UserSpecifiedRootEntryPoint");
			firstNode.removeNeighbor(secondNode);
			secondNode.removeNeighbor(firstNode);
			userRootIntersection.addNeighbor(firstNode,midDist);
			userRootIntersection.addNeighbor(secondNode,midDist);
			userChosenRoot.addNeighbor(userRootIntersection,midDist/2.0);
					
			PhyloCluster newRoot=new PhyloCluster(userRootIntersection,userChosenRoot,null,true);
			owner.firstPaintTime=0;
			owner.root=newRoot;
			owner.root.spread();
			owner.repaint();
			owner.repaint();
	
			}


		private boolean doesPhyloClusterInListHaveName(PhyloCluster[] pl,String name)
			{
			if(pl==null)
				{
				return false;
				}
			if(pl.length==0)
				{
				return false;
				}	
			for(int x=0;x<pl.length;x++)
				{
				if(name.compareTo(pl[x].getName())==0)
					{
					return true;
					}
				}
			return false;
			}


		private void fillOutSubListsProperly()
			{
			System.out.println("Now filling out lists properly...");
			int selected=pairSelectionList.getSelectedIndex();
			listOne=pairList[selected][0].getNodesAwayFrom(pairList[selected][1]);
			listTwo=pairList[selected][1].getNodesAwayFrom(pairList[selected][0]);
			String[] subtreeOneStringList=null;
			String[] subtreeTwoStringList=null;
			PhyloCluster canvasRoot=owner.getRootCluster();
			PhyloCluster[] leaves=canvasRoot.getLeafChildren();

			System.out.println("Now showing leaves :");
			for(int lc=0;lc<leaves.length;lc++)
				{
				System.out.println("Leaf child #"+lc+" name : "+leaves[lc].getName()+".");
				}

					
			//do first list
			for(int i=0;i<listOne.length;i++)
				{
				System.out.println("Now looking at "+listOne[i].getName()+" for first list.");
				if(doesPhyloClusterInListHaveName(leaves,listOne[i].getName()))
					{
					System.out.println("Now adding "+listOne[i].getName()+".");
					subtreeOneStringList=TreeUtils.addLine(listOne[i].getName(),subtreeOneStringList);
					}
				else
					{
					System.out.println("Not adding.");
					}
				}
			//do second list
			for(int j=0;j<listTwo.length;j++)
				{
				System.out.println("Now looking at "+listTwo[j].getName()+" for second list.");
				if(doesPhyloClusterInListHaveName(leaves,listTwo[j].getName()))
					{
					System.out.println("Now adding it.");
					subtreeTwoStringList=TreeUtils.addLine(listTwo[j].getName(),subtreeTwoStringList);
					}
				else
					{
					System.out.println("Skipping it.");
					}
				}

			//now, put the strings in the lists!!!!!
			if(subtreeOneList==null)
				{
				subtreeOneList=new JList(subtreeOneStringList);
				}
			else
				{
				subtreeOneList.setListData(subtreeOneStringList);
				}
			if(subtreeTwoList==null)
				{
				subtreeTwoList=new JList(subtreeTwoStringList);				
				}
			else
				{
				subtreeTwoList.setListData(subtreeTwoStringList);
				}
			}

		
		private boolean isInList(PhyloNode[][] pairList,PhyloNode first,PhyloNode second)
			{
			if(pairList==null)
				{
				return false;
				}
			else if(pairList.length==0)
				{
				return false;
				}
			else
				{
				for(int p=0;p<pairList.length;p++)
					{
					if(pairList[p][0]==first && pairList[p][1]==second)
						{
						return true;
						}
					if(pairList[p][0]==second && pairList[p][1]==first)
						{
						return true;
						}					
					}	
				}
			return false;
			}


		private PhyloNode[][] addPairToList(PhyloNode[][] existing,PhyloNode first,PhyloNode second)
			{
			if(existing==null)
				{
				existing=new PhyloNode[1][];
				existing[0]=new PhyloNode[2];
				existing[0][0]=first;
				existing[0][1]=second;
				return existing;
				}
			else if(existing.length==0)
				{
				return addPairToList(null,first,second);
				}
			else
				{
				PhyloNode[][] complete=new PhyloNode[existing.length+1][];
				for(int c=0;c<existing.length;c++)
					{
					complete[c]=new PhyloNode[2];
					complete[c][0]=existing[c][0];
					complete[c][1]=existing[c][1];
					}
				complete[complete.length-1]=new PhyloNode[2];
				complete[complete.length-1][0]=first;
				complete[complete.length-1][1]=second;
				return complete;
				}
			}


		public ChooseNewRootFrame(PhyloCanvas o)
			{
			//take care of Java stuff!!!
			super();

			//get the data to populate the GUI!
			owner=o;
			setTitle("ReRoot The Tree!");
			setModal(true);
			String newickString=owner.getNewickString();
			PhyloNode newTreeNode=TreeUtils.buildUnrootedTreeFromNewickString(newickString);
			listOne=newTreeNode.getListOfAllNodes();
			listTwo=newTreeNode.getListOfAllNodes();
			
			for(int l1=0;l1<listOne.length;l1++)
				{
				for(int l2=0;l2<listTwo.length;l2++)
					{
					if(listOne[l1]!=listTwo[l2] && listOne[l1].isAnImmediateNeighborOf(listTwo[l2]) && !isInList(pairList,listOne[l1],listTwo[l2]))
						{
						pairList=addPairToList(pairList,listOne[l1],listTwo[l2]);
						System.out.println("Added '"+listOne[l1].getName()+"' and '"+listTwo[l2].getName()+"' to a list.");
						nameList=TreeUtils.addLine("Internal Node Pair #"+pairList.length+".",nameList);
						System.out.println("The 'name' of the added pair is "+nameList[nameList.length-1]+".");
						}
					}
				}
			pairSelectionList=new JList(nameList);
			pairSelectionList.setSelectedIndex(0);
			fillOutSubListsProperly();
			pairSelectionList.addListSelectionListener(this);
			GridBagConstraints gbc=new GridBagConstraints();
			getContentPane().setLayout(new GridBagLayout());
			pairsLabel=new JLabel("Node Pairs");
			gbc.gridwidth=gbc.REMAINDER;
			gbc.weightx=1.0;gbc.weighty=1.0;
			gbc.gridx=0;gbc.gridy=0;
			gbc.fill=gbc.BOTH;
			gbc.anchor=gbc.CENTER;
			getContentPane().add(pairsLabel,gbc);
			gbc.gridy=1;
			getContentPane().add(new JScrollPane(pairSelectionList),gbc);
			gbc.gridwidth=gbc.REMAINDER;
			subtreeOneLabel=new JLabel("Nodes On One Subtree");
			subtreeTwoLabel=new JLabel("Nodes On The Other");
			gbc.gridy=2;
			gbc.gridwidth=1;
			getContentPane().add(subtreeOneLabel,gbc);
			gbc.gridx=1;
			getContentPane().add(subtreeTwoLabel,gbc);
			gbc.gridy=3;
			gbc.gridx=0;
			getContentPane().add(new JScrollPane(subtreeOneList),gbc);
			gbc.gridx=1;
			getContentPane().add(new JScrollPane(subtreeTwoList),gbc);			
			gbc.gridy=4;
			gbc.gridx=0;
			gbc.gridwidth=gbc.REMAINDER;
			rerootButton=new JButton("Re-Root The Tree At The Selected Location!");
			getContentPane().add(rerootButton,gbc);
			rerootButton.addActionListener(this);

			JTextArea instructions=new JTextArea("This is the 'Re-Root the tree anywhere' window.  The purpose of this window is to allow the user to reroot the tree at any desired location.  To do this, this window presents the user with each pair of adjacent/touching nodes in the tree.  Between any such pair, at the midpoint of the edge connecting the nodes, the user is allowed to reroot the tree.  The user is allowed to select any such pair and reroot the tree.  At the top of the window all of the pairs are selectable/clickable.  Each pair is distinguishable by the set of nodes separated by the pair.  One set of nodes is on one side of the pair and the other on the other side of the pair.  These two sets of nodes are displayed in the lists below.  By examining these lists of nodes, the user can discern where in the tree the adjacent node pair is located.  Once the node pair is selected (using the list at the top of the window), to proceed to reroot the tree, the user should simply click the button above.");
			instructions.setLineWrap(true);
			instructions.setWrapStyleWord(true);
			gbc.gridy=5;
			getContentPane().add(new JScrollPane(instructions),gbc);

			setSize(750,750);
			setResizable(false);
			setVisible(true);

			pairSelectionList.setSelectedIndex(0);	
			}


		}


	public class LegendScaleFrame extends JDialog implements ActionListener
		{

		public JLabel fieldLabel=null;
		public JButton setButton=null;
		public JTextField scaleField=null;
		public PhyloCanvas ownerCanvas=null;

		public LegendScaleFrame(PhyloCanvas o)
			{
			super();
			ownerCanvas=o;
			setTitle(" Set The Scale Length ");
			setModal(true);
			
			fieldLabel=new JLabel("                    Scale Length");
			scaleField=new JTextField(new String(""+ownerCanvas.getDrawScaleLen()+""));
			setButton=new JButton(" Set The Scale Length ");
			setButton.addActionListener(this);

			GridLayout gl=new GridLayout(3,1);
			getContentPane().setLayout(gl);

			getContentPane().add(fieldLabel);
			getContentPane().add(scaleField);
			getContentPane().add(setButton);
			setSize(200,100);
			setResizable(false);
			setVisible(true);
			}

		public void actionPerformed(ActionEvent ae)
			{

			if(ae.getSource()==setButton)
				{
				double oldval=ownerCanvas.getDrawScaleLen();
				double newval;
				try
					{
					newval=Double.parseDouble(scaleField.getText());
					newval=Math.abs(newval);
					scaleField.setText(new String(""+newval+""));
					ownerCanvas.setDrawnScaleLen(newval);
					ownerCanvas.repaint();
					}
				catch(Exception e)
					{
					System.out.println("Exception Caught : "+e.toString());
					e.printStackTrace();
					scaleField.setText(new String(""+oldval+""));
					return;
					}
				}
			}
		}



	
	public class BranchSwapFrame extends JDialog implements ActionListener,ListSelectionListener,WindowListener
		{

		JList clusterList=null;
		JList leafListOne=null;
		JList leafListTwo=null;
		JButton swapButton=null;
		JButton cancelButton=null;
		PhyloCanvas ownerCanvas=null;
		PhyloCluster[] internalClusters=null;
		String[] internalDescriptions=null;

		public void actionPerformed(ActionEvent ae)
			{

			if(ae.getSource()==cancelButton)
				{
				setVisible(false);
				}
			if(ae.getSource()==swapButton)
				{
				int selectedIndex=clusterList.getSelectedIndex();
				internalClusters[selectedIndex].swapChildren();
				initLists();
				ownerCanvas.repaint();
				}


			}


		public String[] getNamesOfLeaves(PhyloCluster p)
			{
			
			String[] names=null;
			PhyloCluster[] leaves=p.getLeafChildren();
			names=new String[leaves.length];
			//public static String[] addLine(String newLine,String[] existing)
			for(int i=0;i<leaves.length;i++)
				{
				names[i]=new String(new String(leaves[i].toString()));
				}
			return names;
			}



		private void initLists()
			{

			if(clusterList==null)
				{
				internalClusters=root.getValidInternalNodes();
				internalDescriptions=new String[internalClusters.length];
				for(int d=0;d<internalDescriptions.length;d++)
					{
					internalDescriptions[d]=new String(" Internal Node "+(d+1)+" ");
					}
				clusterList=new JList(internalDescriptions);
				clusterList.setSelectedIndex(0);
				clusterList.addListSelectionListener(this);
				}

			int selectedIndex=clusterList.getSelectedIndex();

			if(leafListOne==null)
				{
				leafListOne=new JList();
				}
			leafListOne.setListData(getNamesOfLeaves(internalClusters[selectedIndex].getLeftChild()));

			if(leafListTwo==null)
				{
				leafListTwo=new JList();
				}
			leafListTwo.setListData(getNamesOfLeaves(internalClusters[selectedIndex].getRightChild()));
			}
		


		public BranchSwapFrame(PhyloCanvas p)
			{
			ownerCanvas=p;
			setTitle("Swap Branches");
			setModal(true);
			initLists();
			
			
			GridBagConstraints gbc=new GridBagConstraints();
			gbc.gridx=1;
			gbc.gridy=1;
			gbc.weightx=1.0;
			gbc.weighty=1.0/32.0;
			gbc.fill=gbc.BOTH;
			gbc.anchor=gbc.CENTER;
			getContentPane().setLayout(new GridBagLayout());
			getContentPane().add(new JLabel("Select A Cluster Whose Subtrees/Leaves To Swap"),gbc);
			gbc.gridy=2;
			gbc.weighty=9.0/32.0;
			getContentPane().add(new JScrollPane(clusterList),gbc);
			gbc.gridy=3;
			gbc.weighty=1.0/32.0;
			getContentPane().add(new JLabel("Leaves On The Left"),gbc);
			gbc.gridy=4;
			gbc.weighty=9.0/32.0;
			getContentPane().add(new JScrollPane(leafListOne),gbc);
			gbc.gridy=5;
			gbc.weighty=1.0/32.0;
			getContentPane().add(new JLabel(" Leaves On The Right "),gbc);
			gbc.gridy=6;
			gbc.weighty=9.0/32.0;
			getContentPane().add(new JScrollPane(leafListTwo),gbc);
			gbc.weighty=1.0/32.0;
			gbc.gridy=7;
			swapButton=new JButton("Swap");
			swapButton.addActionListener(this);
			getContentPane().add(swapButton,gbc);
			gbc.gridy=8;
			cancelButton=new JButton("Done");
			cancelButton.addActionListener(this);
			getContentPane().add(cancelButton,gbc);
			setSize(600,600);
			setVisible(true);
			

			}


	
		public void valueChanged(ListSelectionEvent e)
			{
			initLists();
			}


		public void windowActivated(WindowEvent e)
			{
			System.out.println("swap windowActivated called....");
			//Invoked when the Window is set to be the active Window. 
			}
          
		public void windowClosed(WindowEvent e)
			{
			System.out.println("swap windowClosed called....");
			this.setVisible(false);
			//Invoked when a window has been closed as the result of calling dispose on the window. 
			}

		public void windowClosing(WindowEvent e)
			{
			this.setVisible(false);
			System.out.println("windowclsoing called on swap....");
			//Invoked when the user attempts to close the window from the window's system menu. 
			}

		public void windowDeactivated(WindowEvent e)
			{
			System.out.println("swap windowdeactivated called....");
			//Invoked when a Window is no longer the active Window. 
			}

		public void windowDeiconified(WindowEvent e)
			{
			System.out.println("swap windowDeiconified called....");
			//Invoked when a window is changed from a minimized to a normal state. 
			}

		public void windowIconified(WindowEvent e)
			{
			System.out.println("swap windowIconified called....");
			//Invoked when a window is changed from a normal to a minimized state. 
			}

		public void windowOpened(WindowEvent e)
			{
			System.out.println("swap WindowOpened called....");
			//Invoked the first time a window is made visible. 
			}
		}


	public class RerootFrame extends JDialog implements ActionListener,ListSelectionListener, WindowListener
		{

		JList clusterList=null;
		JButton cancelButton=null;
		JButton RerootButton=null;
		PhyloCluster[] clusters=null;
		String[] clusterDescriptions=null;
		JTextField childDisplayField=null;
		PhyloCanvas owner;

		public RerootFrame(PhyloCanvas ref)
			{
			addWindowListener(this);
			setModal(true);
			owner=ref;
			clusters=root.getLeafChildren();
			clusterDescriptions=new String[clusters.length];
			for(int d=0;d<clusterDescriptions.length;d++)
				{
				if(clusters[d].name.length()<=0)
					{
					if(d!=0)
						{
						clusterDescriptions[d]=new String(" Anonymous Internal Cluster "+d+" - parent of other clusters");
						}
					else
						{
						clusterDescriptions[d]=new String(" Root Cluster ");
						}					
					}
				else
					{
					clusterDescriptions[d]=new String(" "+ clusters[d].name);
					if(clusters[d].getOriginalName().compareTo(clusters[d].name)!=0)
						{
						clusterDescriptions[d]=clusterDescriptions[d]+new String(" : "+clusters[d].getOriginalName());
						}
					}
				}

			clusterList=new JList(clusterDescriptions);
			clusterList.setSelectedIndex(0);
			clusterList.addListSelectionListener(this);
			GridBagConstraints gbc=new GridBagConstraints();
			gbc.gridx=1;gbc.gridy=1;
			gbc.weightx=1.0;
			gbc.weighty=0.80;



			gbc.fill=gbc.BOTH;
			gbc.anchor=gbc.CENTER;
			getContentPane().setLayout(new GridBagLayout());
			getContentPane().add(new JScrollPane(clusterList),gbc);
			gbc.gridy=2;
			gbc.weighty=0.1;
			RerootButton=new JButton("Reroot The Tree At The Selected Node");
			RerootButton.addActionListener(this);
			getContentPane().add(RerootButton,gbc);
			cancelButton=new JButton("Done");
			cancelButton.addActionListener(this);
			gbc.gridy=3;
			getContentPane().add(cancelButton,gbc);
			setTitle("ReRoot The Tree");
			setSize(500,500);
			}

		public void actionPerformed(ActionEvent ae)
			{
			if(ae.getSource()==cancelButton)
				{
				setVisible(false);
				}
			else if(ae.getSource()==RerootButton)
				{
				int answer=JOptionPane.showConfirmDialog(this,"This Will Reset All The Names.  Proceed?");
				if(!(answer==JOptionPane.YES_OPTION || answer==JOptionPane.OK_OPTION))
					{
					JOptionPane.showMessageDialog(this,"Operation Aborted!");
					return;
					}
				int selectedIndex=clusterList.getSelectedIndex();
				PhyloCluster selectedCluster=clusters[selectedIndex];
				String originalName=selectedCluster.getOriginalName();	
				PhyloCluster newRoot=TreeUtils.getNewRootGivenName(root,originalName,newickString);
				if(newRoot==null)
					{
					JOptionPane.showMessageDialog(this,"Error - failed to find node with name "+originalName+"!!!!");
					}
				else
					{
					owner.firstPaintTime=0;
					owner.root=newRoot;
					owner.root.spread();
					owner.repaint();
					owner.repaint();
					this.setVisible(false);
					}
				}
			}
	
		public void valueChanged(ListSelectionEvent e)
			{

			}


		public void windowActivated(WindowEvent e)
			{
			System.out.println("RerootFrame windowActivated called....");
			//Invoked when the Window is set to be the active Window. 
			}
          
		public void windowClosed(WindowEvent e)
			{
			System.out.println("RerootFrame windowClosed called....");
			this.setVisible(false);
			//Invoked when a window has been closed as the result of calling dispose on the window. 
			}

		public void windowClosing(WindowEvent e)
			{
			this.setVisible(false);
			System.out.println("windowclsoing called on rerootframe....");
			//Invoked when the user attempts to close the window from the window's system menu. 
			}

		public void windowDeactivated(WindowEvent e)
			{
			System.out.println("RerootFrame windowdeactivated called....");
			//Invoked when a Window is no longer the active Window. 
			}

		public void windowDeiconified(WindowEvent e)
			{
			System.out.println("RerootFrame windowDeiconified called....");
			//Invoked when a window is changed from a minimized to a normal state. 
			}

		public void windowIconified(WindowEvent e)
			{
			System.out.println("RerootFrame windowIconified called....");
			//Invoked when a window is changed from a normal to a minimized state. 
			}

		public void windowOpened(WindowEvent e)
			{
			System.out.println("RerootFrame WindowOpened called....");
			//Invoked the first time a window is made visible. 
			}

		}//reroot frame



	public class NameFrame extends JDialog implements ActionListener, WindowListener
		{

		//panel holding jfields (both original and changable)
		JPanel fieldsPanel=null;
		//original fields and  changeable fields
		JTextField[] originalFields=null;
		JTextField[] currentFields=null;
		//jbuttons allowing user to reset or apply names
		JButton applyNamesButton=null;
		JButton resetNamesButton=null;
		//reference to owning canvas
		PhyloCanvas ownerCanvas=null;


		//given a reference to a phylocluster, get the names of the leaves under it.
		private String[] getClusterLeafNamesGivenCluster(PhyloCluster p)
			{
			PhyloCluster[] l=p.getLeafChildren();
			String[] leafNames=new String[l.length];
			for(int i=0;i<leafNames.length;i++)
				{
				leafNames[i]=new String(l[i].name);
				}
			return leafNames;
			}


		//set up a name frame
		public NameFrame(PhyloCanvas initialCanvas)
			{
			//super("Name That Leaf");
			setTitle("Name That Leaf!");
			addWindowListener(this);
			setModal(true);
			setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			ownerCanvas=initialCanvas;
			String[] originalNames=getClusterLeafNamesGivenCluster(root);
			PhyloCluster[] leaves=root.getLeafChildren();
			String[] currentNames=new String[leaves.length];
			for(int in=0;in<leaves.length;in++)
				{
				currentNames[in]=new String(leaves[in].name);
				}
			fieldsPanel=new JPanel();
			fieldsPanel.setLayout(new GridLayout(currentNames.length,2));
			originalFields=new JTextField[currentNames.length];
			currentFields=new JTextField[currentNames.length];
			for(int in1=0;in1<leaves.length;in1++)
				{
				originalNames[in1]=new String(leaves[in1].getOriginalName());
				originalFields[in1]=new JTextField(originalNames[in1]);
				originalFields[in1].setEditable(false);
				fieldsPanel.add(originalFields[in1]);
				currentFields[in1]=new JTextField(leaves[in1].getName());
				currentFields[in1].setEditable(true);
				fieldsPanel.add(currentFields[in1]);
				}


			getContentPane().setLayout(new GridBagLayout());
			
			GridBagConstraints gbc=new GridBagConstraints();
			gbc.gridx=1;
			gbc.gridy=1;
			gbc.weightx=1.0;
			gbc.weighty=0.8;
			gbc.fill=gbc.BOTH;
			gbc.anchor=gbc.CENTER;
			getContentPane().add(new JScrollPane(fieldsPanel),gbc);
			applyNamesButton=new JButton("Apply Names");
			applyNamesButton.addActionListener(this);
			resetNamesButton=new JButton("Reset Names");
			resetNamesButton.addActionListener(this);
			gbc.gridy=2;
			gbc.weighty=0.1;
			getContentPane().add(applyNamesButton,gbc);
			gbc.gridy=3;
			getContentPane().add(resetNamesButton,gbc);

			setSize(680,400);
			}


		//handle button clicks (reset and apply)
		public void actionPerformed(ActionEvent ae)
			{
			if(sf!=null)
				{
				sf.setVisible(false);
				}
			if(cf!=null)
				{
				cf.setVisible(false);
				}
			System.gc();
			PhyloCluster[] leaves=root.getLeafChildren();

			if(ae.getSource()==applyNamesButton)
				{
				for(int l=0;l<leaves.length;l++)
					{
					if(currentFields[l].getText().length()==0)
						{
						//insert a space to prevent 0-length names
						currentFields[l].setText(new String(" "));
						//a zero-length name would mess up a routine in phylocluster, so just put a space instead!
						}
					leaves[l].setName(currentFields[l].getText());
					}
				}//apply
			else if(ae.getSource()==resetNamesButton)
				{
				for(int rn=0;rn<leaves.length;rn++)
					{
					leaves[rn].setName(leaves[rn].getOriginalName());
					currentFields[rn].setText(leaves[rn].getName());
					}
				ae.setSource(applyNamesButton);
				actionPerformed(ae);
				}//reset
			//repaint after making changes (either reset or apply!)
			//this is why the constructor took a reference to a phylocanvas
			ownerCanvas.repaint();
			}


		public void windowActivated(WindowEvent e)
			{
			System.out.println("NameFrame windowActivated called....");
			//Invoked when the Window is set to be the active Window. 
			}
          
		public void windowClosed(WindowEvent e)
			{
			System.out.println("NameFrame windowClosed called....");
			this.setVisible(false);
			//Invoked when a window has been closed as the result of calling dispose on the window. 
			}

		public void windowClosing(WindowEvent e)
			{
			this.setVisible(false);
			System.out.println("WindowClosing called on NameFrame....");
			//Invoked when the user attempts to close the window from the window's system menu. 
			}

		public void windowDeactivated(WindowEvent e)
			{
			System.out.println("NameFrame windowdeactivated called....");
			//Invoked when a Window is no longer the active Window. 
			}

		public void windowDeiconified(WindowEvent e)
			{
			System.out.println("NameFrame windowDeiconified called....");
			//Invoked when a window is changed from a minimized to a normal state. 
			}

		public void windowIconified(WindowEvent e)
			{
			System.out.println("NameFrame windowIconified called....");
			//Invoked when a window is changed from a normal to a minimized state. 
			}

		public void windowOpened(WindowEvent e)
			{
			System.out.println("NameFrame WindowOpened called....");
			//Invoked the first time a window is made visible. 
			}




		}//name frame




	public class ColorFrame extends JDialog implements ActionListener,ListSelectionListener,WindowListener
		{

		JPanel topPanel=null;
		JPanel bottomPanel=null;
		String[] clusterDescriptions=null;
		JList clusterList;
		JList leafList;
		PhyloCanvas pc=null;
		PhyloCluster[] clusters;
		JButton chooseColorButton;
		JButton applyColorButton;
		JButton resetColorsButton;
		JRadioButton selfButton;
		JRadioButton leavesButton;
		JRadioButton allButton;
		JPanel buttonPanel;
		JPanel optionsPanel;
		Color currentColor=new Color(0,0,0);

		//given a reference to a phylocluster, get the names of the leaves under it.
		private String[] getClusterLeafNamesGivenCluster(PhyloCluster p)
			{
			PhyloCluster[] l=p.getLeafChildren();
			String[] leafNames=new String[l.length];
			for(int i=0;i<leafNames.length;i++)
				{
				leafNames[i]=new String(l[i].name);
				}
			return leafNames;
			}


		//when the user clicks a cluster, update the list of leaves
		public void valueChanged(ListSelectionEvent e)
			{
			PhyloCluster selectedCluster=getClusterFromSelectedDescription();
			String[] d=getClusterLeafNamesGivenCluster(selectedCluster);
			if(d!=null)
				{
				for(int x=0;x<d.length;x++)
					{
					d[x]=new String(" "+d[x]);
					}
				leafList.setListData(d);
				}
			}


		//get the currently selected cluster
		private PhyloCluster getClusterFromSelectedDescription()
			{
			Object selectedValue=clusterList.getSelectedValue();
			if(selectedValue==null)
				{
				return null;
				}
			else
				{
				String d=new String((String)(selectedValue));
				for(int c=0;c<clusterDescriptions.length;c++)
					{
					if(clusterDescriptions[c].compareTo(d)==0)
						{
						return clusters[c];
						}
					}
				}
			return null;
			}




		public ColorFrame(PhyloCanvas owner)
			{
			//super(" Cluster Color Applicator ");
			setTitle(" Cluster Color Applicator ");
			setModal(true);
			addWindowListener(this);
			setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			currentColor=new Color(0,0,0);
			pc=owner;
			topPanel=new JPanel(new  GridBagLayout());
			bottomPanel=new JPanel();
			GridBagLayout mgl=new GridBagLayout();
			getContentPane().setLayout(mgl);
			GridBagConstraints mc=new GridBagConstraints();
			mc.gridx=1;
			mc.gridy=1;
			mc.weightx=1.0;
			mc.weighty=0.80;
			mc.fill=mc.BOTH;
			mc.anchor=mc.CENTER;
			getContentPane().add(topPanel,mc);
			mc.weighty=0.20;
			mc.gridy=2;
			getContentPane().add(bottomPanel,mc);

			clusters=root.getSelfAndAllProgeny();
			clusterDescriptions=new String[clusters.length];
			for(int d=0;d<clusterDescriptions.length;d++)
				{
				if(clusters[d].name.length()<=0)
					{
					if(d!=0)
						{
						clusterDescriptions[d]=new String(" Anonymous Internal Cluster "+d+" - parent of other clusters");
						}
					else
						{
						clusterDescriptions[d]=new String(" Root Cluster ");
						}					
					}
				else
					{
					clusterDescriptions[d]=new String(" "+ clusters[d].name);
					}
				}

			clusterList=new JList(clusterDescriptions);
			clusterList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			clusterList.setSelectedValue(clusterDescriptions[0],true);
			String[] leafDesc=getClusterLeafNamesGivenCluster(clusters[0]);
			for(int ld=0;ld<leafDesc.length;ld++)
				{
				leafDesc[ld]=new String(" "+leafDesc[ld]);
				}
			leafList=new JList(leafDesc);
			leafList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			clusterList.addListSelectionListener(this);
			GridBagConstraints gbc=new GridBagConstraints();
			gbc.weightx=1.0;
			gbc.weighty=0.1;
			gbc.fill=gbc.BOTH;
			gbc.gridx=0;
			gbc.gridy=0;
			topPanel.add(new JLabel("   Select A Cluster :"),gbc);
			gbc.gridy=1;
			gbc.weighty=1.0;
			topPanel.add(new JScrollPane(clusterList),gbc);
			gbc.gridy=2;
			gbc.weighty=0.1;
			topPanel.add(new JLabel("   The Selected Cluster's Leaves :"),gbc);
			gbc.weighty=1.0;
			gbc.gridy=3;
			topPanel.add(new JScrollPane(leafList),gbc);

			
			//do buttons
			chooseColorButton=new JButton("Choose A Color");
			chooseColorButton.addActionListener(this);
			applyColorButton=new JButton("Apply Color To Selected Cluster");
			applyColorButton.addActionListener(this);
			resetColorsButton=new JButton("Reset Color");
			resetColorsButton.addActionListener(this);
			buttonPanel=new JPanel();
			buttonPanel.add(chooseColorButton);
			buttonPanel.add(applyColorButton);
			buttonPanel.add(resetColorsButton);
			bottomPanel.setLayout(new GridBagLayout());
			GridBagConstraints bc=new GridBagConstraints();
			bc.weighty=0.15;
			bc.weightx=1.0;
			bc.gridx=1;
			bc.gridy=1;
			bc.fill=bc.BOTH;
			bc.anchor=bc.CENTER;
			bottomPanel.add(buttonPanel,bc);


			//options panel
			optionsPanel=new JPanel();
			selfButton=new JRadioButton("Apply Color To\nSelected Cluster ONLY");
			leavesButton=new JRadioButton("Apply Color To\nSelected Cluster's Leaves ONLY");
			allButton=new JRadioButton("Apply Color To\nEntire Cluster (Leaves, Cluster, and All)",true);
			optionsPanel.add(selfButton);
			optionsPanel.add(leavesButton);
			optionsPanel.add(allButton);
			bc.weighty=0.85;
			bc.gridy=2;
			bottomPanel.add(optionsPanel,bc);
			ButtonGroup rg=new ButtonGroup();
			bottomPanel.setBackground(currentColor);
			optionsPanel.setBackground(currentColor);
			buttonPanel.setBackground(currentColor);
			rg.add(selfButton);rg.add(leavesButton);rg.add(allButton);
			setSize(500,600);
			setResizable(false);
			}

		//this numHits is for the reset color button
		int numHits=5;
		//handle button clicks
		public void actionPerformed(ActionEvent ae)
			{

			//if the choose color button is clicked, then have the user choose a color
			//and paint the backgrounds that color if they selected a color
			if(ae.getSource()==chooseColorButton)
				{
				Color selectedColor=JColorChooser.showDialog(this,new String("Choose A Color!"),currentColor);
				if(selectedColor!=null)
					{
					currentColor=selectedColor;
					bottomPanel.setBackground(currentColor);
					optionsPanel.setBackground(currentColor);
					buttonPanel.setBackground(currentColor);
					repaint();
					}
				}
			/*
			to apply a color, first get refernces to the desired clusters (here called 'target' clusters)
			The definition of 'target' cluster however, depends on the radiobutton currently selected.
			Once the target clusters are known, apply the color to them
			*/
			else if(ae.getSource()==applyColorButton)
				{
				//rg.add(selfButton);rg.add(leavesButton);rg.add(allButton);
				PhyloCluster[] targetClusters=null;
				PhyloCluster selectedCluster=getClusterFromSelectedDescription();
				System.out.println("The selected phylo cluster is : " +selectedCluster.toString()+".");
				System.out.println("The target clusters are being found....");
				if(selfButton.isSelected())
					{
					targetClusters=new PhyloCluster[1];
					targetClusters[0]=selectedCluster;
					}
				else if(leavesButton.isSelected())
					{
					targetClusters=selectedCluster.getLeafChildren();
					}
				else
					{
					targetClusters=selectedCluster.getSelfAndAllProgeny();
					}
				for(int a=0;a<targetClusters.length;a++)
					{
					targetClusters[a].setColor(currentColor);
					}
				for(int tc=0;tc<targetClusters.length;tc++)
					{
					System.out.println("\tTarget Cluster #"+tc+" : "+targetClusters[tc].name+".");
					}
				pc.repaint();
				}
			//reset the color to BLACK.  Call pc.repaint()
			//the business with 'numHits' is to force the
			//black to be propogated until it shows!!!!
			else if(ae.getSource()==resetColorsButton)
				{
				root.setColorRec(Color.BLACK);
				bottomPanel.setBackground(currentColor);
				optionsPanel.setBackground(currentColor);
				buttonPanel.setBackground(currentColor);
				currentColor=Color.BLACK;
				pc.repaint();
				repaint();	
				pc.repaint();
				repaint();
				if(numHits<=5)
					{
					numHits--;
					actionPerformed(ae);
					}
				numHits=5;
				}
			}

		public void windowActivated(WindowEvent e)
			{
			System.out.println("ColorFrame windowActivated called....");
			//Invoked when the Window is set to be the active Window. 
			}
          
		public void windowClosed(WindowEvent e)
			{
			System.out.println("ColorFrame windowClosed called....");
			this.setVisible(false);
			//Invoked when a window has been closed as the result of calling dispose on the window. 
			}

		public void windowClosing(WindowEvent e)
			{
			this.setVisible(false);
			System.out.println("windowclosing called on ColorFrame....");
			//Invoked when the user attempts to close the window from the window's system menu. 
			}

		public void windowDeactivated(WindowEvent e)
			{
			System.out.println("ColorFrame windowdeactivated called....");
			//Invoked when a Window is no longer the active Window. 
			}

		public void windowDeiconified(WindowEvent e)
			{
			System.out.println("ColorFrame windowDeiconified called....");
			//Invoked when a window is changed from a minimized to a normal state. 
			}

		public void windowIconified(WindowEvent e)
			{
			System.out.println("ColorFrame windowIconified called....");
			//Invoked when a window is changed from a normal to a minimized state. 
			}

		public void windowOpened(WindowEvent e)
			{
			System.out.println("ColorFrame WindowOpened called....");
			//Invoked the first time a window is made visible. 
			}





		}//color frame



	//A class for the window to set the scales of the tree
	public class ScaleFrame extends JDialog implements ActionListener, WindowListener
		{

		//save the original values of the scales here
		//0=scale, 1=hscale, 2=lscale
		double[] defaultvals=new double[3];


		//this is used when the values are changed 
		public void updateScaleVals(double s,double l)
			{
			jtf[0].setText(new String(""+s+""));
			defaultvals[2]=l;
			}

		//have a refernce to the canvas to call repaint on it
		PhyloCanvas pc=null;

		public ScaleFrame(double s,double h,double l,PhyloCanvas containerCanvas)
			{
			//super(" Adjust Scale Factors ");
			setTitle(" Adjust Scale Factors ");
			setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			addWindowListener(this);
			pc=containerCanvas;
			

			defaultvals[0]=s;
			defaultvals[1]=h;
			defaultvals[2]=l;

			GridLayout gl=new GridLayout(4,2);
			getContentPane().setLayout(gl);
			ComponentOrientation ltro=ComponentOrientation.LEFT_TO_RIGHT;
			this.applyComponentOrientation(ltro) ;

			for(int p=0;p<jtf.length;p++)
				{
				fl[p]=new JLabel(bs[p]);
				getContentPane().add(fl[p]);
				jtf[p]=new JTextField(new String(""+defaultvals[p]+""));
				getContentPane().add(jtf[p]);
				}
			
			getContentPane().add(resetButton);
			resetButton.addActionListener(this);
			getContentPane().add(applyButton);
			applyButton.addActionListener(this);

			setSize(325,200);

			Dimension d=new Dimension(getWidth(),getHeight());
			setResizable(false);
			}

		String[] bs={" Scale "," HScale "," LScale "};
		JLabel[] fl=new JLabel[3];
		JTextField[] jtf=new JTextField[3];
		JButton resetButton=new JButton(" Reset Values ");
		JButton applyButton=new JButton(" Apply Scale Values ");

		//Handle button clicks
		public void actionPerformed(ActionEvent ae) 
 			{
			if(ae.getSource()==resetButton)
				{
				for(int i=0;i<bs.length;i++)
					{
					jtf[i].setText(new String(""+defaultvals[i]+""));
					}
				ae.setSource(applyButton);
				actionPerformed(ae);
				}
			else if(ae.getSource()==applyButton)
				{
				verifyAndCorrectFieldValues();

				//	double scale,hscale,lscale;
				//String[] bs={" Scale "," HScale "," LScale "};
				scale=Double.parseDouble(jtf[0].getText());
				hscale=Double.parseDouble(jtf[1].getText());
				lscale=Double.parseDouble(jtf[2].getText());				
				}
			pc.repaint();
			}


		//revert to default scale values
		public void setDefaults()
			{
			for(int v=0;v<jtf.length;v++)
				{
				jtf[v].setText("*****.*****");
				}
			ActionEvent ae=new ActionEvent(resetButton,0,new String("issue set default"));
			verifyAndCorrectFieldValues();
			actionPerformed(ae);
			}


		private void verifyAndCorrectFieldValues()
			{
			double d;
			for(int v=0;v<jtf.length;v++)
				{
				try
					{
					d=Double.parseDouble(jtf[v].getText());
					jtf[v].setText(new String(""+d+""));
					}
				catch(NumberFormatException nfe)
					{
					jtf[v].setText(new String(""+defaultvals[v]+""));
					}
				}
			}



		public void windowActivated(WindowEvent e)
			{
			System.out.println("ScaleFrame windowActivated called....");
			//Invoked when the Window is set to be the active Window. 
			}
          
		public void windowClosed(WindowEvent e)
			{
			System.out.println("ScaleFrame windowClosed called....");
			this.setVisible(false);
			//Invoked when a window has been closed as the result of calling dispose on the window. 
			}

		public void windowClosing(WindowEvent e)
			{
			this.setVisible(false);
			System.out.println("WindowClosing called on ScaleFrame....");
			//Invoked when the user attempts to close the window from the window's system menu. 
			}

		public void windowDeactivated(WindowEvent e)
			{
			System.out.println("ScaleFrame windowdeactivated called....");
			//Invoked when a Window is no longer the active Window. 
			}

		public void windowDeiconified(WindowEvent e)
			{
			System.out.println("ScaleFrame windowDeiconified called....");
			//Invoked when a window is changed from a minimized to a normal state. 
			}

		public void windowIconified(WindowEvent e)
			{
			System.out.println("scale frame windowIconified called....");
			//Invoked when a window is changed from a normal to a minimized state. 
			}

		public void windowOpened(WindowEvent e)
			{
			System.out.println("scale frame WindowOpened called....");
			//Invoked the first time a window is made visible. 
			}

		}//ScaleFrame class



	//this is a "setter" method for the newickString
	public String setNewickString(String newnewick)
		{
		if(newnewick!=null)
			{
			newickString=new String(newnewick);
			}
		System.out.println("The canvas's newick string has been set to : '"+newickString+"'.");
		return newickString;
		}

	public String getNewickString()
		{
		return new String(newickString);
		}

    //This class is used to hold an image while on the clipboard.
    public static class ImageSelection implements Transferable
			{
			private Image image;    
			public ImageSelection(Image image)
				{
				this.image = image;
				}    
			// Returns supported flavors
			public DataFlavor[] getTransferDataFlavors()
				{
				return new DataFlavor[]{DataFlavor.imageFlavor};
				}    
			// Returns true if flavor is supported
			public boolean isDataFlavorSupported(DataFlavor flavor)
				{
				return DataFlavor.imageFlavor.equals(flavor);
				}    
			// Returns image
			public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
				{
				if (!DataFlavor.imageFlavor.equals(flavor))
					{
					throw new UnsupportedFlavorException(flavor);
					}
            return image;
			}
		}


	//tell the current translation status of the tree
	public void tellAboutTrans()
		{
		System.out.println("tranx = "+tranx);
		System.out.println("trany = "+trany);
		System.out.println("itranx = "+itranx);
		System.out.println("itrany = "+itrany);
		tellDim();
		}


	//tell the dimensions of the applet
	public void tellDim()
		{
		System.out.println("Dimensions :");
		System.out.println("width : "+getWidth());
		System.out.println("height : "+getHeight());
		}


	//create a phylocanvas given a root to a tree and initial scaling factors

	public PhyloCanvas(PhyloCluster root,double scale,double hval,double lval,boolean f,JApplet mainOwner)
		{
		
		ownerApplet=mainOwner;
		this.isInFrame=f;
		randomCluster=root.chooseRandomCluster(false,3,0.15);
		this.root=root;
		this.hscale=hval;
		this.scale=scale;
		this.lscale=lval;
		iscale=scale;
		initMenu();
		root.spread();
		//setBackground(Color.DARK_GRAY);
		/*sf=new ScaleFrame(scale,hval,lval,this);
		cf=new ColorFrame(this);
		nf=new NameFrame(this);
		rf=new RerootFrame(this);
		bf=new BranchSwapFrame(this);
		lsf=new LegendScaleFrame(this);*/

		
		}


	//set up the translations for drawing the tree at a good spot on the canvas
	public void init()
		{
		if(getWidth()%2==0)
			{	
			itranx=getWidth()/2;
			}
		else
			{
			itranx=((getWidth()+1)/2);
			}
		if(getHeight()%2==0)
			{
			itrany=getHeight()/2;
			}
		else
			{
			itrany=(getHeight()+1)/2;
			}
		tranx=0;
		trany=0;
		tellAboutTrans();
		
		setITrans();
		tellAboutTrans();
		repaint();
		}




	//Make an image of the tree (used for clipboard and file)
	private BufferedImage getImageOfCanvas()
		{
		int w=Math.abs(root.maxx-root.minx)+getWidth()*10;
		int h=Math.abs(root.miny-root.maxy)+getHeight()*10;

		int type=BufferedImage.TYPE_INT_RGB;		
		BufferedImage bi=null;
		Graphics2D g2=null;

		//int treeWidth=Math.abs(root.maxx-root.minx);
		//int treeHeight=Math.abs(root.miny-root.maxy);

		bi=getProperSizedImage(root,scale,lscale,hscale);
		g2=bi.createGraphics();
		g2.setColor(new Color(255,255,255));
		g2.fillRect(0,0,bi.getWidth(),bi.getHeight());
		//g2.translate(0,Math.abs(root.miny-0)+25);
		paintForImage(g2,bi);
		g2.dispose();
		return bi;

		}




	public BufferedImage getProperSizedImage(PhyloCluster rootCluster,double plainScale,double lengthScale,double horizScale)
		{
		//Make an image the proper size!
		System.out.println("******************************");
		System.out.println("getProperSized image called!");
		System.out.println("******************************");

		//Set the scaling values/factors
		double scale=plainScale;
		double hscale=horizScale;
		double lscale=lengthScale;

		//Get the tree maximal distance extension and longest string length
		double maxExt=getMaxExtension(rootCluster);
		int maxExtI=(int)(maxExt*scale*lscale);
		String longest=getLongestString(rootCluster);

		//Use the maximal extension and the longest string width to compute the image
		//add a bit extra width of 25 pixels so the string won't dare look too close to the edge
		int imageWidth=maxExtI+getStringPixelLengthUsingDefaultJPEGBufferedImage(longest)+25;

		//Use the tree size and hscale to compute 
		int imageHeight=
					(int)
						(
						hscale*2.0*
							(
							(double)(rootCluster.getNumProgeny())+2.0
							)
						);
		//Add some space for the scale
		imageHeight+=200;


		//Create an image and return it
		BufferedImage bim=new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB);

		return bim;
		}


	public static String replaceDescs(String newick,File replacementFile) throws Exception
		{
		
		BufferedReader br=new BufferedReader(new FileReader(replacementFile));
		String tempLine;
		String[] tempPieces=null;
		String replaceWith=null;
		while(br.ready())
			{
			tempLine=new String(br.readLine());
			tempLine=tempLine.trim();
			tempPieces=tempLine.split(" ");
			replaceWith=new String(tempLine);
			replaceWith=replaceWith.replaceAll(","," ");
			replaceWith=replaceWith.replaceAll("\\(","[");
			replaceWith=replaceWith.replaceAll("\\)","]");
			replaceWith=replaceWith.trim();
			//System.out.println("I want to replace '"+tempPieces[0]+"' with '"+replaceWith+"'.");
			//System.exit(1);
			newick=newick.replaceAll(tempPieces[0],replaceWith);
			}
		

		return newick;
		}




	public static void main(String[] args)
		{

		try
			{
			File f=new File(args[0]);
			if(!args[1].endsWith(".jpeg"))
				{
				throw new RuntimeException("Error - 2nd argument expected to end in '.jpeg'!");
				}
			outputJpeg=new File(args[1]);
			String fileline=TreeUtils.getFileLinesInOneLine(f.getCanonicalPath());
			fileline=replaceDescs(fileline,new File("/home/esalina/phylostuff/rename.txt"));
			PhyloNode root=TreeUtils.chooseRootAndRootTree(fileline);
			PhyloCluster rootCluster=new PhyloCluster(root,null,null,true);
			//PhyloCanvas myCanvas=new PhyloCanvas(rootCluster,double scale,double hval,double lval,boolean f,JApplet mainOwner)
			PhyloCanvas myCanvas=new PhyloCanvas(rootCluster,1.0,10.0,1.0,true,null);
			JFrame myJFrame=new JFrame("Hello!");
			myJFrame.setSize(1500,600);
			myJFrame.getContentPane().add(myCanvas);
			myJFrame.setDefaultCloseOperation(myJFrame.EXIT_ON_CLOSE);
			myJFrame.show();
			
			}
		catch(Exception e)
			{
			e.printStackTrace();
			}
		}





	//Given a filename and a directory save a jpeg of the tree there
	public void saveJPEGFile(String outputFileName,String directoryInfo) throws Exception
		{
		System.out.println("saveJPEGFile called with output file argument "+outputFileName+".");
		System.out.println("saveJPEGFile called with dirinfo argument "+directoryInfo);
		BufferedImage bi=getImageOfCanvas();
		if(bi==null)
			{
			System.out.println("The received buffered image is null!!!!");
			}
		System.out.println("The file will be "+directoryInfo+outputFileName);
		File f=new File(directoryInfo+outputFileName);
		BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(f));
		if(bos==null)
			{
			System.out.println("The BOS is null!!!!");
			}
		ImageIO.write(bi,"jpg",f);
		/*JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder((OutputStream)(bos));
		if(encoder==null)
			{
			System.out.println("THE ENCODER IS NULL!!!!");
			}
		JPEGEncodeParam param=encoder.getDefaultJPEGEncodeParam(bi);
		param.setQuality(1.0f,true);
		encoder.setJPEGEncodeParam(param);
		encoder.encode(bi);
		bos.flush();
		bos.close();*/
		}






	//init the menu and add it to the canvas
	public void initMenu()
		{
		//System.out.println("******************INIT MENU CALLED!!*********************");
		myMenu=new JPopupMenu("My Menu");
		String[] myMenuTitles={
								"Enter Zoom Mode",//0
								"Show Scaling Window",//1
								"Change Drawn Scale Length",//2
								"Access Cluster Color Applicator",//3
								"Show Name Editing Window",//4
								"Create Tree In New Window",//5
								"ReRoot Tree At A Leaf Node Window",//6
								"ReRoot Tree At Midpoint Of Longest Path",//7
								"ReRoot Tree Anywhere!",//8
								"Branch Swap Window",//9
								"Save Tree Image To File",//10
								"Copy Tree Image To Clipboard",//11
								"Copy Newick-Format Tree String To Clipboard",//12
								"Load New Tree With Newick String"//13
							};
		menuTitles=myMenuTitles;
		menuItems=new JMenuItem[menuTitles.length];
		for(int m=0;m<menuItems.length;m++)
			{
			System.out.println("The value of m is "+m+".");
			if(isInAJFrame())
				{
				System.out.println("Determined to BE in a JFrame!");
				}
			else
				{
				System.out.println("Determined NOT to be in a jframe!!!!!!");
				}
			if(!(isInAJFrame() && m==4))
				{
				System.out.println("Now adding menu item with label "+menuTitles[m]+"....");
				menuItems[m]=new JMenuItem(menuTitles[m]);
				menuItems[m].addActionListener(this);
				myMenu.add(menuItems[m]);
				}
			else
				{
				System.out.println("Skipping adding menu item with label "+menuTitles[m]+"....");
				}

			if(menuItems.length>1 && m==0)
				{
				myMenu.addSeparator();
				}
			if(m==8)
				{
				myMenu.addSeparator();
				}
			if(m==11 && !(isInAJFrame()))
				{
				myMenu.addSeparator();
				myMenu.add(menuItems[m]);
				}
			}// for loop for 'plain' menuitems

		
		ButtonGroup bg=new ButtonGroup();
		radioMenuButtons=new JRadioButtonMenuItem[2];
		radioMenuButtons[0]=new JRadioButtonMenuItem("Click Me!");
		radioMenuButtons[0].setSelected(true);
		radioMenuButtons[0].addActionListener(this);
		radioMenuButtons[1]=new JRadioButtonMenuItem("Click Me Too!");
		radioMenuButtons[1].addActionListener(this);
		JMenu treeTypeMenu=new JMenu("Drawing Modes");
		treeTypeMenu.add(radioMenuButtons[0]);
		treeTypeMenu.add(radioMenuButtons[1]);
		bg.add(radioMenuButtons[0]);
		bg.add(radioMenuButtons[1]);

		//Remove this part from the menu until it's implemented!
		//myMenu.addSeparator();
		//myMenu.add(treeTypeMenu);	

		add(myMenu);
		}



	//Handle menu item selections
	public void actionPerformed(ActionEvent ae)
		{
		JMenuItem menuSource=null;
		System.out.println("actionPerformed called....");

		if(ae.getSource() instanceof JMenuItem)
			{
			System.out.println("Source determined to be a JMenuItem....");
			//System.out.println("received af from menuitem");
			menuSource=(JMenuItem)(ae.getSource());


			if(menuSource==radioMenuButtons[0])
				{
				System.out.println("first radio clicked!");
				}
			if(menuSource==radioMenuButtons[1])
				{
				System.out.println("2nd radio clicked!");
				}


			if(menuSource.getLabel().compareTo(menuTitles[0])==0)
				{
				//enter zoom mode
				System.out.println("Now entering zoom mode....");
				zoomMode=true;
				}
			else if(menuSource.getLabel().compareTo(menuTitles[1])==0)
				{
				System.out.println("Now showing scale frame....");
				sf=new ScaleFrame(scale,hscale,lscale,this);
				sf.setModal(true);
				sf.setVisible(true);
				sf.show();
				}
			else if(menuSource.getLabel().compareTo(menuTitles[2])==0)
				{
				System.out.println("Now showing legend length adjustment window!");
				lsf=new LegendScaleFrame(this);
				}
			else if(menuSource.getLabel().compareTo(menuTitles[3])==0)
				{
				cf=new ColorFrame(this);
				cf.setVisible(true);
				cf.show();
				//System.out.println("Now trying to show applicator....");
				}
			else if(menuSource.getLabel().compareTo(menuTitles[4])==0)
				{
				if(nf!=null)
					{
					nf.setVisible(false);
					nf=null;
					System.gc();
					}
				nf=new NameFrame(this);
				nf.setVisible(true);
				nf.show();
				}
			else if(menuSource.getLabel().compareTo(menuTitles[5])==0)
				{			
				if(!isInAJFrame())
					{
					//disallow frame with canvases to open more frames with canvases!!!
					JFrame phyloFrame=new JFrame();
					//phyloFrame.setSize(this.getWidth(),this.getHeight());
					phyloFrame.setTitle("My Phylogenetic Tree !");
					try
						{
						System.out.println("Trying to load image inti mii....");
						Image mii=ownerApplet.getImage(new URL("http://crick.forensicbiodna.com/~esalina/imgs/favicon.jpg"),"Icon");
						/*Image mii=
						public Image getImage(URL url,
						  String name)
						*/
						System.out.println("\n"+mii.toString()+" : "+mii+".");
						phyloFrame.setIconImage(mii);
						}
					catch(Exception e)
						{
						e.printStackTrace();
						}
					PhyloCanvas newCanvas=new PhyloCanvas(root,scale,hscale,lscale,true,ownerApplet);
					newCanvas.addMouseMotionListener(newCanvas);
					newCanvas.addMouseListener(newCanvas);
					newCanvas.setNewickString(newickString);
					/*pc.addMouseMotionListener(pc);
					pc.addMouseListener(pc);
					getContentPane().add(pc);
					pc.setNewickString(removeDoneFromEnd(newickString));*/
					phyloFrame.resize(Toolkit.getDefaultToolkit().getScreenSize());
					phyloFrame.getContentPane().add(newCanvas);
					phyloFrame.setVisible(true);
					phyloFrame.show();
					}
				}//open in new frame
			else if(menuSource.getLabel().compareTo(menuTitles[6])==0)
				{
				rf=new RerootFrame(this);
				rf.setVisible(true);
				}
			else if(menuSource.getLabel().compareTo(menuTitles[7])==0)
				{
				//reroot at midpoing
				PhyloNode newTreeNode=null;
				PhyloCluster newRoot=null;
				String neickString=null;
				switch(JOptionPane.showConfirmDialog(this,
                                    new String("This will reset the names (to their original values) and reroot the tree at the midpoint of the longest path!  Proceed?"),
                                    new String("Proceed?"),
									JOptionPane.OK_CANCEL_OPTION,
                                    JOptionPane.QUESTION_MESSAGE))
					{
					case JOptionPane.OK_OPTION:
							System.out.println("Now about to attempt to reroot tree at midpoint of longest path!");
							newickString=getNewickString();
							newTreeNode=TreeUtils.makeRootedTreeMidpointMethodFromNewickString(newickString);
							newRoot=new PhyloCluster(newTreeNode,null,null,true);
							System.out.println(newRoot.toString());
							root=newRoot;
							firstPaintTime=0;
							repaint();
						break;
					default:
							JOptionPane.showMessageDialog(this,
                                     new String("Operation Aborted!"),
                                     new String("Operation Aborted!"),
                                     JOptionPane.INFORMATION_MESSAGE);

						break;
					}
                           


				}
			if(menuSource.getLabel().compareTo(menuTitles[8])==0)
				{
				ChooseExactRootFrame cerf=new ChooseExactRootFrame(this);
				//cnrFrame=null;
				//cnrFrame=new ChooseNewRootFrame(this);
				}
			if(menuSource.getLabel().compareTo(menuTitles[9])==0)
				{
				bf=null;
				bf=new BranchSwapFrame(this);
				}
			else if(menuSource.getLabel().compareTo(menuTitles[10])==0)
				{
				//save image!
				System.out.println("Trying to save image to file....");
				try
					{

					Frame f=new Frame("delme");
					FileDialog fd=new FileDialog(f,"Save The Tree As An Image",FileDialog.SAVE);
					fd.setModal(true);
					fd.setFile("myTreeFile.jpg");
					fd.show();
					String outputFileName=fd.getFile();
					System.out.println("The outputfile name is "+outputFileName+".");
					saveJPEGFile(outputFileName,fd.getDirectory());
				 	}
				catch(Exception e)
					{
					JOptionPane.showMessageDialog(this,"Error saving file!","Error Saving file!",JOptionPane.ERROR_MESSAGE);
					System.out.println("Exception caught : "+e);
					e.printStackTrace();
					}
				}
			else if(menuSource.getLabel().compareTo(menuTitles[11])==0)
				{
				System.out.println("Trying to save image to clipboard....");
				//save image to clipboard!
				try
					{
					BufferedImage treeImage=getImageOfCanvas();
					ImageSelection is=new ImageSelection(treeImage);
					Toolkit.getDefaultToolkit().getSystemClipboard().setContents(is, null);
					}
				catch(Exception e)
					{
					JOptionPane.showMessageDialog(this,"Error copying image to clipboard! - "+e.toString(),"Error copying image to clipboard!",JOptionPane.ERROR_MESSAGE);
					System.out.println("Exception caught : "+e);
					e.printStackTrace();
					}
				}
			else if(menuSource.getLabel().compareTo(menuTitles[12])==0)
				{
				//copy newick string to clipboard
				System.out.println("Received request to copy newick string to clipboard....");
				StringSelection ss=new StringSelection(newickString);
				Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
				}
			else if(menuSource.getLabel().compareTo(menuTitles[13])==0)
				{
				System.out.println("New newick string desired!");
				String r=JOptionPane.showInputDialog(this,"Type Or Paste A Newick String","Enter A New Newick String",JOptionPane.PLAIN_MESSAGE);
				System.out.println("Received from input : "+r+".");	
				if(r!=null)
					{
					ImageIcon iu=null;
					try
						{
						iu=new ImageIcon(new URL("http://www.forensicbiodna.com/~esalina/imgs/favicon.jpg"));
						PhyloNode newTreeNode=TreeUtils.makeRootedTreeMidpointMethodFromNewickString(r);
						PhyloCluster newRoot=new PhyloCluster(newTreeNode,null,null,true);
						System.out.println(newRoot.toString());
						root=newRoot;
						firstPaintTime=0;
						repaint();
						this.setNewickString(new String(r));
						}
					catch(Exception e)
						{
						String mString=new String("It appears that there was an error in reading/loading the tree from the newick String!  There was a negative exception : '"+e.toString()+"'.");
						JOptionPane.showMessageDialog(this,mString,"Error Loading Tree From String",JOptionPane.ERROR_MESSAGE);
						/*
						public static void showMessageDialog(Component parentComponent,
                                   Object message,
                                   String title,
                                   int messageType,
                                   Icon icon)
								Brings up a dialog displaying a message, specifying all parameters. 
								Parameters:
								parentComponent - determines the Frame in which the dialog is displayed; if null, or if the parentComponent has no Frame, a default Frame is used
								message - the Object to display
								title - the title string for the dialog
								messageType - the type of message to be displayed: ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
							icon - an icon to display in the dialog that helps the user identify the kind of message that is being displayed 
						*/
						e.printStackTrace();
						}
					}		
				}//12
			}// if instance of jmenuItem
		else
			{

			}
	
		System.gc();
		

		}


	private boolean isInFrame=false;
	public boolean isInAJFrame()
		{
		return isInFrame;
		}


	public void setDrawnScaleLen(double sv)
		{
		drawnscaleval=Math.abs(sv);
		repaint();
		}


	public double getDrawScaleLen()
		{
		return drawnscaleval;
		}

	private double drawnscaleval=0.01;



	//paint the legend/scale
	public void paintScale(Graphics g,Color scaleColor)
		{
		//assumed to be painted WITHOUT any previous translations done!!!!

		if(scaleColor==null)
			{
			scaleColor=Color.BLACK;
			}

		g.setColor(scaleColor);

		int scaleSquareVal=20;
		//put scale in lower-left corner 20 pixes up from bottom and 20 pixels from left-border
		int scaleStartX=scaleSquareVal;
		int scaleStartY=-scaleSquareVal;
		int scaleEndY=scaleStartY;

		double scaleLen=getDrawScaleLen();
		System.out.println("Now inside 'paintScale'.");
		System.out.println("The set value of scaleLen is "+scaleLen+".");
		int scaleEndX=scaleStartX+(int)(scaleLen*lscale*scale);
		g.drawLine(scaleStartX,scaleStartY,scaleEndX,scaleEndY);
		g.drawString(new String(""+scaleLen+""),5,-5);
		}


	//paint for the image
	//tree's position is different 
	public void paintForImage(Graphics2D g,BufferedImage i)
		{		
		g.translate(5,i.getHeight()/2);
		g.translate(tranx,trany);
		root.paint(g,scale,hscale,lscale);
		g.translate(-tranx,-trany);
		g.translate(-5,-(i.getHeight()/2));


		g.translate(25,i.getHeight()-50);
		paintScale( g,null);
		g.translate(-25,-(i.getHeight()-50));
		}


	public void paintComponent(Graphics g)
		{
		//Graphics myCopy=g.create();
		//paint(myCopy);
		}


	//paint the tree (by calling Paint on the cluster)
	//and paint the scale too
	public long lastTimeClear=0;
	public void paint(Graphics g)
		{
		//setBackground(Color.DARK_GRAY);

		Color originalColor=g.getColor();

		g.setColor(new Color(238,238,238));
		g.fillRect(0,0,getWidth(),getHeight());
		g.setColor(originalColor);
		
		root.maxx=-99999;
		root.maxy=-99999;
		root.minx=99999;
		root.miny=99999;

		g=(Graphics2D)(g);

		if(firstPaintTime==0)
			{
			firstPaintTime=System.currentTimeMillis();
			}
		//System.out.println("in paint");
		//tellAboutTrans();
		if(firstPaint)
			{
			setITrans();
			firstPaint=false;
			}
		g.translate(itranx,itrany);
		g.translate(tranx,trany);
		root.paint(g,scale,hscale,lscale);
		g.translate(-tranx,-trany);
		g.translate(-itranx,-itrany);
		root.tellAboutMAM();

		g.translate(25,getHeight()-5);
		paintScale( g,null);
		g.translate(-25,-(getHeight()-5));
		

		int treeWidth=Math.abs(root.maxx-root.minx);
		if(treeWidth>getWidth() && System.currentTimeMillis()<firstPaintTime+readjusmentPeriod)
			{
			lscale=lscale*0.95;
			System.out.println("Calling repaint in shrinking lscale...");
			repaint();
			}
		else if(treeWidth<getWidth() && System.currentTimeMillis()<firstPaintTime+readjusmentPeriod)
			{
			System.out.println("Calling repaint in increasing lscale....");
			lscale=lscale*1.05;
			if(sf!=null)
					{
					sf.updateScaleVals(scale,lscale);
					sf.setDefaults();
					}
			repaint();
			}
		else
			{
			if(outputJpeg!=null)
				{
				try
					{
					saveJPEGFile(outputJpeg.getCanonicalPath(),"");
					}
				catch(Exception e)
					{
					e.printStackTrace();
					}
				System.exit(0);
				}
			}
		}


	//implement dragging and zooming (using the scale)
	public void mouseDragged(MouseEvent e)
		{

		if(System.currentTimeMillis()<firstPaintTime+readjusmentPeriod)
			{
			return;
			}


		zoomMode=false;

		if(isInDrag)
			{
			relx=e.getX()-dragx;
			rely=e.getY()-dragy;

			double drelx=(double)(relx);
			double drely=(double)(rely);

			//double distance=dist;
			double distance=Math.sqrt(drelx*drelx+drely*drely);
			distance=distance/7500.0;
			//System.out.println("The distance is "+distance);
			
			double sign;

			if(relx>=rely)
				{
				sign=1.0;
				}
			else
				{
				sign=(-1.0);
				}

			//System.out.println("relx="+relx+" and rely="+rely);
			//System.out.println("e.getButton() is "+e.getButton());
			//System.out.println("The mousemode is "+mouseMode);

			if(mouseMode==0)
				{
				tranx=starttranx+relx;
				trany=starttrany+rely;
				}
			else if(mouseMode==1)
				{
				scale=startScale+distance*sign*100;
				//System.out.println("Scale is "+scale+".");
				if(scale<=0.0001)
					{
					scale=0.0001;
					}
				if(sf!=null)
					{
					sf.updateScaleVals(scale,lscale);
					}
				}
			repaint();
			}	

		}//mouseDragged


	//set the intial location of the tree on the canvas
	private void setITrans()
		{
		itranx=20;
		itrany=getHeight()/2;
		scale=iscale;
		}



	/*Variables to implement dragging the tree around.*/
	int dragx;int relx;
	int dragy;int rely;
	double startScale;
	int starttranx;
	int starttrany;
	boolean zoomMode=false;	


	/*
	This method is for the MouseListener interface.
	It's where dragging/translation, zooming, and bringing up the menu are taken care of
	*/
	public void mousePressed(MouseEvent e)
		{

		if(System.currentTimeMillis()<firstPaintTime+readjusmentPeriod)
			{
			return;
			}

		//System.out.println("mousePressed called with \ne=");
		//System.out.println(e.toString());
		//System.out.println("In pressed, e.ispopuptrigger() returns "+e.isPopupTrigger()+"!!!");
		if(e.getClickCount()<2 && e.getButton()!=1 && myMenu!=null && !e.isPopupTrigger() && !zoomMode)
			{
			//System.out.println("*************Trying to do Menu Stuff");
			//System.out.println("e.getComponent() returns "+e.getComponent()+"!!!");
			myMenu.show((Component)(this),e.getX(),e.getY());
			return;
			}	
		else if(myMenu!=null)
			{
			//myMenu.setVisible(false);
			}
		if(e.getClickCount()==2)
			{
			tranx=0;
			trany=0;
			scale=iscale;
			repaint();
			return;
			}

		//handle any clickings of names!
		PhyloCluster clicked=root.recIsInBounds(e.getX(),e.getY());
		if(clicked!=null)
			{
			try
				{
				System.out.println("YOU CLICKED : "+clicked.toString()+"!!!!!!");
				String id=new String(clicked.name.substring(0,clicked.name.indexOf(" ")-0));
				String us=new String("http://www.ncbi.nlm.nih.gov/entrez/viewer.fcgi?val="+id+"");
				//String us=new String("http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nucleotide&id="+URLEncoder.encode(id,"UTF-8")+"&rettype=genbank");
				//String us=new String("http://www.google.com/search?q="+URLEncoder.encode(clicked.getName(),"UTF-8")+"&btnGNS=Search+nih.gov&oi=navquery_searchbox&sa=X&as_sitesearch=nih.gov&hl=en");
				//String us=new String("http://www.google.com/search?hl=en&q="++"&btnG=Google+Search");
				URL cu=new URL(us);
				System.out.println("NOW TRYING TO VISIT url "+cu.toString()+".");
				if(myAppletContext!=null)
					{
					myAppletContext.showDocument(cu,"__blank");
					}
				}
			catch(Exception ue)
				{
				System.out.println("EXCEPTION CAUGHT : ");
				ue.printStackTrace();
				}
			}


		//System.out.println("In mousePresseed, e.getButton() returns "+e.getButton());
		//log the coordinates
		isInDrag=true;
		dragx=e.getX();
		dragy=e.getY();
		if(e.getButton()==1)
			{
			mouseMode=0;
			starttranx=tranx;
			starttrany=trany;
			}
		else
			{
			mouseMode=1;
			startScale=scale;
			}
		}

	public void mouseClicked(MouseEvent e)
		{
		//leave blank - just a placeholder  for Interface MouseListener
		}

	public void mouseReleased(MouseEvent e)
		{
		//cease dragging!
		isInDrag=false;
		mouseMode=(-1);
		}

	public void mouseEntered(MouseEvent e) 
		{
		//leave blank - just a placeholder for Interface MouseListener
		}
			  
	public void mouseExited(MouseEvent e) 
		{
		//leave blank - just a placeholder  for Interface MouseListener
		}

	public void mouseMoved(MouseEvent e)
		{
		//leave blank - just a placeholder for Interface MouseMotionListener
		}


	public PhyloCluster getRootCluster()
		{
		return this.root;
		}


	
	public String getLongestString(PhyloCluster r)
		{
		String l=new String("");
		PhyloCluster[] all=r.getSelfAndAllProgeny();
		
		for(int i=0;i<all.length;i++)
			{
			if(l.length()<all[i].getName().length())
				{
				l=new String(all[i].getName());
				}
			}

		return l;

		}



	public int getProgenyCount(PhyloCluster root,boolean left)
		{
		//Get the desired subtree
		PhyloCluster subTree=null;
		if(left)
			{
			subTree=root.getLeftChild();
			}
		else
			{
			subTree=root.getRightChild();
			}
		//return its progeny count
		return subTree.getNumProgeny();
		}





	public double getMaxExtension(PhyloCluster a)
		{

		if(a.getLeftChild()==null && a.getRightChild()==null)
			{
			return a.getClusterDistance();
			}
		else if(a.getLeftChild()==null && a.getRightChild()!=null)
			{
			return Math.max(
							a.getClusterDistance(),
							a.getClusterDistance()+getMaxExtension(a.getRightChild())
							);
			}
		else if(a.getLeftChild()!=null && a.getRightChild()==null)
			{
			return Math.max(
							a.getClusterDistance(),
							a.getClusterDistance()+getMaxExtension(a.getLeftChild())
							);
			}
		else
			{
			double l=a.getClusterDistance()+getMaxExtension(a.getLeftChild());
			double r=a.getClusterDistance()+getMaxExtension(a.getRightChild());
			return Math.max(l,r);
			}
		}



	public int getStringPixelLengthUsingDefaultJPEGBufferedImage(String s)
		{
		//get an image
		BufferedImage bim=new BufferedImage(5,5,BufferedImage.TYPE_INT_RGB);

		//get a graphics context
		Graphics2D g=bim.createGraphics();

		//get the font metrics
		FontMetrics fm=g.getFontMetrics();

		//get the string width
		int width=fm.stringWidth(s);

		return width;
		}





	}
