package org.phylowidget.ui;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.andrewberman.ui.Color;
import org.andrewberman.ui.FontLoader;
import org.andrewberman.ui.unsorted.MethodAndFieldSetter;
import org.phylowidget.PWContext;
import org.phylowidget.PWPlatform;
import org.phylowidget.PhyloTree;
import org.phylowidget.tree.PhyloNode;
import org.phylowidget.tree.RootedTree;
public class PhyloConfig
{
/*
* URL API CONFIGURATION
*
* If you are running a database that works with phylogenetic information, the easiest way to "integrate"
* PhyloWidget with your service is to create "View in PhyloWidget" links using PhyloWidget's simple URL-based
* API. This allows you to customize the look, feel, and functionality of PhyloWidget without having to host
* the applet on your own site.
*
* The API is simple: just choose from one of the configuration parameters listed below, and call the
* PhyloWidget URL with the desired value appended to the end, as you would any normal GET parameters.
* The applet will then read in the configuration parameters and modify its settings accordingly.
*
* Some examples:
* - Set the tree that PhyloWidget shows upon startup.
* http://www.phylowidget.org/lite/index.html?tree="(a,(b,c));"
* - Set the foreground color, using (R,G,B) format
* http://www.phylowidget.org/lite/index.html?foreground="(255,0,0);"
* - Start up PhyloWidget with a preset tree and search string.
* http://www.phylowidget.org/lite/index.html?tree="(a,(b,c));"&search="a"
*
* The configurable parameters are displayed below, generally in order from most to least useful. Enjoy!
*/
public boolean debug = false;
/*
* If you specify a valid URL pointing to a properties file, then PhyloWidget will attempt to load the properties remotely.
* The format for the properties file is the standard .properties style, with a property name followed by an "=", followed
* by the property value.
*
* ### Example.properties ###
* tree="Homo sapiens"
* search="sapiens"
* renderer="Rectangular"
* nodeSize=5.0
*/
public String remoteConfig = "";
/*
* Set the starting tree.
*/
public String tree = DEFAULT_TREE;
/*
* Set the clipboard text.
*/
public String clipboard = "";
/*
* Set the starting search string.
*/
public String search = "";
/*
* Set the starting layout type.
*
* Possible values: Rectangular, Diagonal, Circular, and Unrooted.
*/
public String layout = "Rectangular";
/*
* Choose the preset XML menu files which PhyloWidget will load.
*
* You can load up multiple XML menu files by giving a semicolon-delimited list. See the default
* value for an example of this.
*
* You may also simply let this string be the XML data which you want to load. This is useful for
* demonstration purposes, letting the user edit and change the menu structure in real-time.
*
* Core menu definitions:
* - "context.xml" The context menu which appears when you click a node.
* - "dock.xml" The dock, which holds the arrow, pan, and zoom tools.
* - "toolbar.xml" The toolbar, which sits at the top of the screen and contains
* the menu items and search bar.
* - "none.xml" No menus at all.
*
* Some useful variants:
* - "toolbar-onlysearch.xml" A toolbar with only the search command, nothing else.
* - "dock-onlynav.xml" A dock with only the pan and zoom settings, no arrow.
* - "context-linkout.xml" A context menu which lets you link out to other sites.
* See the XML file for more information.
* - "dock-hidden.xml" A hidden dock. Useful for providing the dock's functionality without
* cluttering up the screen. Users can still use the keyboard shortcuts
* to switch between tools.
* - "toolbar-hidden.xml" Same idea as above, but with the toolbar.
*/
public String menus = "context.xml;dock.xml;toolbar-new.xml;callbacks.xml";
/* Colors: You can modify the foreground and background colors which PhyloWidget uses.
* The new value should be formatted as below; a triplet of integer RGB values enclosed in parentheses.
*/
public String backgroundColor = "(255,255,255)";
public String textColor = "(0,0,0)";
public String nodeColor = "(0,0,0)";
public String branchColor = "(0,0,0)";
public String alignmentColor = "(140,190,50)";
/*
* Node shapes -- usable values are:
* - "square"
* - "circle"
* - "triangle"
* - "x" (star shape)
*/
public String nodeShape = "circle";
/*
* How to handle the angles of rotated nodes. usable values:
* "none" -- keep rotated nodes at all angles (VERY SLOW with large trees)
* "quantize" -- Quantize the rotation in 45-degree increments (a little faster...)
* "level" -- Level all angles to horizontal (much faster!)
*/
public String angleHandling = "level";
/*
* Line style when drawn with the Rectangle renderer:
* "square" -- Regular, square corners
* "round" -- Rounded corners
* "bezier" -- Bezier curve lines
*/
public String lineStyle = "square";
public String font = "Bitstream Vera Sans";
//The following parameters can be set using any numerical value, e.g. "textRotation = 0.25"
public float textRotation = 0f; // Text rotation, in degrees.
public float textScaling = .8f; // Text scaling, where a value of 1.0 is normal size.
public float imageSize = 0.95f; // Image scaling, where a value of 1.0 is normal size.
public float lineWidth = 1f; // Line width. 0 is minimum, 1 is a pretty normal size.
// 10 is as high as you'll want to go.
public float nodeSize = 2f; // Node size. Same range as line width: 0 to 10 is reasonable.
public float innerNodeRatio = 1f; // Ratio between the size of the inner (non-leaf) nodes and the outer (leaf) nodes. Default 1.
public float renderThreshold = 500f; // Maximum number of nodes to render per frame.
public float minTextSize = 8; // Minimum text size for leaf node labels.
// public float branchLengthScaling = 1f; // DEPRECATED.
public float branchScaling = 1f; // Only used with the Cladogram renderer... scales the width.
public float cigarScaling = 10f; // How wide should 1bp of cigar line be, relative to the row height
public float layoutAngle = 0f; // The starting angle for the layout (only applicable for circular and unrooted layouts)
public float animationFrames = 15f; // The number of frames it should take nodes to animate to a new destination. (30 frames ~ 1 sec)
public float viewportX = 0.0f; // The x position of the viewport.
public float viewportY = 0.0f; // Ditto.
public float viewportZoom = 0.8f; // I'll bet you can guess this one.
public boolean showScaleBar = false; // Show a scale bar when showing branch lengths.
public boolean showCladeLabels = false; // Should we show labels of non-leaf nodes?
// public boolean stretchToFit = false; // DEPRECATED.
public boolean useBranchLengths = false; // Should the renderer display the tree using the branch length information?
public boolean showAllLabels = false; // Should the renderer show all labels? This OVERRIDES the minTextSize setting,
// so that labels are shown no matter how small they must be displayed.
public boolean hideAllLabels = false; // Set to TRUE to hide all labels from being drawn. OVERRIDES the showAllLabels setting.
public boolean showAllLeafNodes = false; // Should we always draw the NODES of all LEAF nodes?
public boolean treatNodesAsLabels = false;
public boolean prioritizeDistantLabels = false; // This controls how PhyloWidget prioritizes the display of certain nodes above others.
// If set to "true", then PhyloWidget will first display the nodes that are *farthest* from
// the root, instead of those that are closest (in terms of # of branches to the root).
public boolean alignLabels = false; // When drawing with branch lengths, whether all labels should be aligned.
public boolean useDoubleBuffering = false; // To be honest you probably don't want to mess with this one -- the double buffering really helps!
public boolean antialias = false; // When set to true this slows down the rendering significantly, but looks much better.
public boolean outputAllInnerNodes = false; // Kind of a strange one: if set to true, PhyloWidget will *always* output
// the labels of non-leaf nodes. Sometimes these are just stupid-looking numbers.
public boolean enforceUniqueLabels = false; // Enforce uniqueness of node labels.
public boolean scrapeNaughtyChars = false; // Should we scrape away naughty characters from node labels when exporting the tree file?
public boolean outputFullSizeImages = false; // Output images in the tree at full size, instead of thumbnail (may require LOTS of memory!!)
public boolean useAnimations = true; // Use animated transitions?
public boolean animateNewTree = false; // Try to animate between the current tree and new tree? (EXPERIMENTAL IF SET TO TRUE)
public boolean suppressMessages = false;
public boolean colorHoveredBranch = false;
public boolean respondToMouseWheel = true;
public boolean ignoreAnnotations = false; // ANNOTATIONS: Set to true if you want to globally disable PhyloWidget's display and output of NHX annotations.
public boolean showBootstrapValues = false; // ANNOTATIONS: Should we show NHX-annotated bootstrap values if they exist?
public boolean colorSpecies = true; // ANNOTATIONS: Should we assign colors to different leaf nodes NHX-annotated with a given species or taxon?
public boolean colorDuplications = true; // ANNOTATIONS: Same idea as the others, but for the node duplication coloring.
public boolean colorBootstrap = true; // ANNOTATIONS: ditto for bootstrap values.
/*
*
* END: URL API Configuration
*
* The rest is all just code involved in making these configuration parameters work properly...
*/
private PWContext context;
public PhyloConfig()
{
super();
context = PWPlatform.getInstance().getThisAppContext();
// setRemoteConfig("http://www.phylowidget.org/nhx_test/ensembl.properties");
}
/*
* COLOR CRAP
*/
private Color backgroundC = Color.parseColor(backgroundColor);
public void setBackgroundColor(String s)
{
backgroundC = Color.parseColor(s);
backgroundColor = s;
}
public Color getBackgroundColor()
{
return Color.parseColor(backgroundColor);
}
private Color textC = Color.parseColor(textColor);
public void setTextColor(String s)
{
textC = Color.parseColor(s);
textColor = s;
}
public Color getTextColor()
{
if (textC != null)
return textC;
else
return Color.parseColor(textColor);
}
private Color nodeC = Color.parseColor(nodeColor);
public void setNodeColor(String s)
{
nodeC = Color.parseColor(s);
nodeColor = s;
}
public Color getNodeColor()
{
if (nodeC != null)
return nodeC;
else
return Color.parseColor(nodeColor);
}
private Color branchC = Color.parseColor(branchColor);
public void setBranchColor(String s)
{
branchC = Color.parseColor(s);
branchColor = s;
}
public Color getBranchColor()
{
if (branchC != null)
return branchC;
else
return Color.parseColor(branchColor);
}
private Color alignmentC = Color.parseColor(alignmentColor);
public void setAlignmentColor(String s)
{
alignmentC = Color.parseColor(s);
alignmentColor = s;
}
public Color getAlignmentColor()
{
if (alignmentC != null)
return alignmentC;
else
return Color.parseColor(alignmentColor);
}
/*
* END COLOR CRAP
*/
public void setRespondToMouseWheel(boolean respond)
{
if (!respond)
{
context.trees().camera.makeUnresponsive();
} else
{
context.trees().camera.makeResponsive();
}
}
public void setTree(final String s)
{
new Thread()
{
public void run()
{
context.trees().setTree(s);
tree = s;
}
}.start();
}
public void setClipboard(final String s)
{
new Thread()
{
public void run()
{
context.ui().clipboard.setClipFromJS(s);
}
}.start();
}
public void setUseBranchLengths(boolean useEm)
{
useBranchLengths = useEm;
context.ui().layout();
}
// public void setStretchToFit(boolean fitMe)
// {
// stretchToFit = fitMe;
// }
public void setSearch(String s)
{
this.search = s;
context.ui().search();
}
public void setEnforceUniqueLabels(boolean b)
{
enforceUniqueLabels = b;
RootedTree t = context.trees().getTree();
if (t != null)
t.setEnforceUniqueLabels(b);
}
public void setLayout(String s)
{
if (!layout.equals(s))
layout = s;
s = s.toLowerCase();
if (s.equals("diagonal"))
{
context.trees().diagonalRender();
} else if (s.equals("circular"))
{
context.trees().circleRender();
} else if (s.equals("unrooted"))
{
context.trees().unrootedRender();
} else
{
context.trees().rectangleRender();
}
}
public void setRemoteConfig(String s)
{
try
{
HashMap<String, String> map = new HashMap<String, String>();
URL url = new URL(s);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String line = null;
while ((line = in.readLine()) != null)
{
if (line.startsWith("#"))
continue;
String[] split = line.split("=");
if (split.length >= 2)
{
map.put(split[0], split[1]);
}
}
MethodAndFieldSetter.setMethodsAndFields(this, map);
} catch (Exception e)
{
e.printStackTrace();
}
}
public void setMenus(String menus)
{
this.menus = menus;
new Thread("Menu Loader")
{
@Override
public void run()
{
// System.out.println("PhyloConfig MenuLoader!");
context.ui().setMenus();
}
}.start();
}
public void setShowAllLabels(boolean showAllLabels)
{
this.showAllLabels = showAllLabels;
}
public void setPrioritizeDistantLabels(boolean prioritizeDistanceLabels)
{
this.prioritizeDistantLabels = prioritizeDistanceLabels;
context.ui().layout();
}
public void destroy()
{
tree = null;
}
public void setTextSize(float textSize)
{
this.textScaling = textSize;
// context.ui().layout();
}
public void setLayoutAngle(float layoutAngle)
{
this.layoutAngle = layoutAngle;
context.ui().forceLayout();
}
public void setViewportX(float newX)
{
context.trees().camera.nudgeTo(-newX, context.trees().camera.getY());
}
public void setViewportY(float newY)
{
context.trees().camera.nudgeTo(context.trees().camera.getX(), -newY);
}
public void setViewportZoom(float newZoom)
{
context.trees().camera.zoomTo(newZoom);
}
public void setBranchScaling(float newBranchScaling)
{
this.branchScaling = newBranchScaling;
context.ui().forceLayout();
}
public void setShowScaleBar(boolean show)
{
if (show)
{
context.trees().showScaleBar();
} else
{
context.trees().hideScaleBar();
}
}
public final static String DEFAULT_TREE = "PhyloWidget";
public void setFont(String newFont)
{
FontLoader fl = context.trees().getRenderer().getFontLoader();
fl.setFont(newFont);
this.font = fl.getFontName();
}
public static Map<String, String> getChangedFields(Object a, Object b)
{
Class aClass = a.getClass();
Class bClass = b.getClass();
if (!aClass.equals(bClass))
{
System.out.println("Classes a and b not equal!");
}
HashMap<String, String> changedFields = new HashMap<String, String>();
Field[] fields = aClass.getFields();
for (Field f : fields)
{
try
{
if (f.get(a).equals(f.get(b)))
{
// System.out.println("Equal on field " + f.getName() + f.get(a) + " "+f.get(b));
} else
{
changedFields.put(f.getName(), f.get(a).toString());
}
} catch (Exception e)
{
e.printStackTrace();
}
}
return changedFields;
}
public void setIgnoreAnnotations(boolean ignore)
{
this.ignoreAnnotations = ignore;
context.ui().layout();
}
public static Map<String,String> getConfigSnapshot(PhyloConfig currentConfig)
{
Map<String,String> changed = getChangedFields(currentConfig,new PhyloConfig());
changed.remove("viewportX");
changed.remove("viewportY");
changed.remove("viewportZ");
changed.remove("menus");
return changed;
}
}